1.21 jigowatts

Great Scott!

Ruby MongoDB イン・アクション のTweetArchiverを作成する

概要

MongoDBについてはマニュアルを読むのが一番いいと思いますが、日本語で読めるMongoDB イン・アクションを進めています。

MongoDBイン・アクション

MongoDBイン・アクション

ただ、さすがに情報が古くなっていますね。Rubyでツイートを検索しMongoDBに登録・ブラウザに表示するサンプルアプリケーションが載っているのですが、ライブラリのAPIが変わっていたりとそのままでは動きませんでした。ということで動くように少し内容を更新してみます。

環境

OS: CentOS 7
Ruby: 2.3.1
MongoDB: 3.2

セットアップ

ライブラリのインストール

以下の3つ。ここは変わりませんね。

$ gem install mongo
$ gem install twitter
$ gem install sinatra

バージョンは以下。

設定ファイル

MongoDBのRubyドライバに変更が。書籍ではデータベース名を設定してましたが、接続文字列に変更。
Twitter APIでも4つのキーが必要になっているため設定ファイルに指定できるようにしておきました。キーの取得は以前Twitter Botを作成したときと同じでOKでした。
config.rb

CONNECTION_STRING = "mongodb://localhost/twitter-archive"
COLLECTION_NAME = "tweets"
TAGS = ["mongodb","ruby"]

CONSUMER_KEY = "your_consumer_key"
CONSUMER_SECRET = "your_consumer_secret"
ACCESS_TOKEN = "your_access_token"
ACCESS_TOKEN_SECRET = "your_access_token_secret"

ツイートの保存

MongoDBへの接続やインデックスの作成を変更しました。Twitter APIもconfigからキーを指定して呼び出すように変更。サンプルコードを見ていると、当時はキー指定とかなくてただAPI呼んでるだけ。シンプルで手軽だったんですね。
archiver.rb

require 'rubygems'
require 'mongo'
require 'twitter'

require './config'

class TweetArchiver

  def initialize(tag)
    db = Mongo::Client.new(CONNECTION_STRING)
    @tweet = db[COLLECTION_NAME]

   @tweet.indexes.create_many([
      {:key => {id: 1}, unique: true},
      {:key => {tags: 1,id: -1}}
    ])

    @tag = tag
    @tweet_found = 0
  end

  def update
    puts "Starting Twitter search for '#{@tag}'..."
    save_tweets_for(@tag)
    print "#{@tweet_found} tweets saved.\n\n"
  end

  def save_tweets_for(term)
    twitter_client = Twitter::REST::Client.new do |config|
      config.consumer_key        = CONSUMER_KEY
      config.consumer_secret     = CONSUMER_SECRET
      config.access_token        = ACCESS_TOKEN
      config.access_token_secret = ACCESS_TOKEN_SECRET
    end

    twitter_client.search(term).each do |tweet|
      @tweet_found += 1
      tweet_with_tag = tweet.to_hash.merge!({"tags" => [term]})
      @tweet.insert_one(tweet_with_tag)
    end

  end

end


TweetArchiverクラスの実行スクリプトには変更なし。
update.rb

require './config'
require './archiver'

TAGS.each do |tag|
  archiver = TweetArchiver.new(tag)
  archiver.update
end

こいつを実行すればタグに指定した内容で検索して結果をMongoDBに登録してくれます。ツイートだけでなくユーザ名やプロフィールの内容にも引っかかるみたいですけど、今回その辺の細かいことは気にしないようにします。

$ ruby update.rb

ブラウザに表示

Sinatra?知らない子ですねぇ。RubyのWebアプリケーションフレームワークといえばRails一択だろ。その程度の認識しかありませんでした。
小規模なWebアプリであればSinatraの方が簡単、お手軽に作成できるらしい。初めて触ってみたんですがいいですねSinatra

ここもMongoDBの接続とソートクエリを少し変更。
viewer.rb

require 'rubygems'
require 'mongo'
require 'sinatra'

require './config'

configure do
  db = Mongo::Client.new(CONNECTION_STRING)
  TWEETS = db[COLLECTION_NAME]
end

get '/' do 
  if params['tag']
    selector = {:tags => params['tag']}
  else
    selector = {}
  end

  @tweets = TWEETS.find(selector).sort({"id" => -1})

  erb :tweets
end

viewsサブディレクトリにerbファイルを作成します。DOCTYPE宣言をHTML5に。それから一部のtweetプロパティも変わっていたので変更しました。
views/tweets.erb

<!DOCTYPE>
<html>

<head>
  <style>
    body{
      width:1000px;
      margin: 50px auto;
    }
    h2{
      margin-top:2em;
    }
  </style>
    <title>Tweet Archive</title>
</head>

<body>
  <h1>Tweet Archive</h1>

  <% TAGS.each do |tag| %>
    <a href="/?tag=<%= tag %>"><%= tag %></a>
  <% end %>

  <% @tweets.each do |tweet| %>
    <h2><%= tweet['text'] %></h2>
    <p>
      <a href="http://twitter.com/<%= tweet['user']['screen_name'] %>">
        <%= tweet['user']['name'] %>
      </a>
      on <%= tweet['created_at'] %>
    </p>

    <img src="<%= tweet['user']['profile_image_url'] %>" width="48" />
  <% end %>

</body>

</html>

これでたぶんOKなはず。
あとはアプリケーションを実行してブラウザでhttp://localhost:4567へアクセス!

$ ruby viewer.rb