Railsでツイッターのツイート検索アプリを作成する

# はじめに
今回はTwitter APIを利用して、Rails5でツイッターのツイートを検索するアプリを作成します。


# 事前準備
実際にコードを書く前に何点か事前準備をします。

## Consumer Keyの取得
Twitter APIを利用するために、まずは今回作成するアプリをTwitterに登録し、Consumer Keyを取得します。アプリケーションの登録は以下のURLから行います。
Consumer Keyの取得方法は細かく説明しませんが、一点だけ補足すると、WebsiteやCallback URLを記述する必要があるのですが、サンプルアプリとして利用するのであれば、`http://0.0.0.0:3000/`で問題ありません。
なお今回はツイッターのツイートを検索するだけなので、ユーザー認証は必要ありません。ですので、アクセストークンについては取得する必要がありません。

https://apps.twitter.com/



## プロジェクトの雛形の作成

まずは`rails new`でプロジェクトを作成します。

```
$ rail new twitter-search
```

Twitter gemを利用するのでGemfileに以下を追記します。

```config/Gemfile
gem 'twitter'
```

`bundle`を実行し、gemをインストールします。

```
$ bundle
```

Rails generateのcontrollerオプションを使って、コントローラーとビューを作成します。

```
$ rails g controller tweets
```

# モックの作成

次に、モックを作成します。

## Routesの編集

今回は http://localhost:3000/tweets/search のURLに検索ページを作成するので、以下のようにルーティングを設定します。

```routes.rb
Rails.application.routes.draw do
  get "/tweets/search" => 'tweets#search'
end
```

## Controllerの作成

ルーティングに対応するメソッド(search)を作成します。

```tweets_controller.rb
class TweetsController < ApplicationController
  def search
    respond_to do |format|
      format.html # show.html.erb
    end
  end
end
```

## Viewの作成

searchメソッドによって呼ばれるビューを作成します。
今回は検索キーワードを送るだけの単純な検索窓を作成し、このフォームは何のモデルにも基づかないため、`form_tag`、`text_field_tag`を利用しています。[[1]](http://rails3try.blogspot.jp/2011/06/rails3-textfield-textfieldtag.html)[[2]](http://qiita.com/shunsuke227ono/items/7accec12eef6d89b0aa9)
なお、`text_field_tag`にある`nil`は`placeholder`で指定している文字列をデフォルトで薄字で表示させるためのものです。[[3]](http://apidock.com/rails/ActionView/Helpers/FormTagHelper/text_field_tag)
`<%= render partial: 'tweet_list' %>`の部分は実際に検索結果を表示するViewで、別のerbファイルから呼び出すようにします。(検索結果を表示するViewの作成は後述)


```app/views/tweets/search.html.erb
<div class='content'>
  <div id="search-box">
    <%= form_tag('/tweets/search', method: :get) do %>
      <%= text_field_tag :tweet, nil,  id: "tweet_search", name: "keyword", placeholder: "キーワードを打ち込んでください", style: "width: 200px;"%><button title="検索" type="submit">検索</button>
    <% end %>
  </div>

  <div id='tweet_list'>
     <%= render partial: 'tweet_list' %>
  </div>
</div>

```

## モックの検証

サーバーを起動して正しく画面が表示されているか検証していきます。

```
$ rails s
```

http://localhost:3000/tweets/search にアクセスすると以下のような画面が表示されると思います。

![スクリーンショット 2017-01-02 1.33.51.png](https://qiita-image-store.s3.amazonaws.com/0/140792/dc1f321e-ab07-40fa-6ea3-ec5ab13b8577.png)

# Twitterのツイートを取得する機能の作成

上記で画面は作成できたので、実際にツイート検索と取得を行う機能を作成していきます。

## Modelの作成

検索したツイートを格納させるためのモデルを作成します。
今回のモデルはDBへのアクセスが必要ないので[[4]](http://qiita.com/tatsuki-m/items/043f14e7d4504277c947)、以下のようになります。

```models/tweet.rb
class Tweet
  # プロパティの設定
  attr_accessor :contents

  def initialize(contents)
    @contents = contents
  end
end
```

## Controllerの編集

Controllerは以下のようになります。

```tweets_controller.rb
class TweetsController < ApplicationController
  def search
    client = Twitter::REST::Client.new do |config|
      # 事前準備で取得したキーのセット
      config.consumer_key         = "xxxxxxxxxxxxxxxxxxxxxxx"
      config.consumer_secret      = "yyyyyyyyyyyyyyyyyyyyyyyyyyyy"
    end
    @tweets = []
    since_id = nil
    # 検索ワードが存在していたらツイートを取得
    if params[:keyword].present?
      # リツイートを除く、検索ワードにひっかかった最新10件のツイートを取得する
      tweets = client.search(params[:keyword], count: 10, result_type: "recent", exclude: "retweets", since_id: since_id)
      # 取得したツイートをモデルに渡す
      tweets.take(10).each do |tw|
        tweet = Tweet.new(tw.full_text)
        @tweets << tweet
      end
    end
    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @tweets } # jsonを指定した場合、jsonフォーマットで返す
    end
  end
end
```

## Consumer(API) Keyのリファクタリング

上記のように直接ControllerにConsumer Keyを記述してもいいのですが、Railsの場合、アクセスキーなどは`secret.yml`に書いておいたほうがセキュリティ的に良いです。[[5]](http://blogs.zealot.co.jp/archives/810)
ですので、以下のように`secret.yml`の新規作成と、`tweets_controller.rb`の編集を行います。


```secrets.yml
default: &default
  twitter_consumer_key: "xxxxxxxxxxxxxxxxxxxxxxx"
  twitter_consumer_secret: "yyyyyyyyyyyyyyyyyyyyyyyyyyyy"

development:
  <<: *default

test:
  <<: *default

production:
  twitter_consumer_key:  <%= ENV["TWITTER_CONSUMER_KEY"] %>
  twitter_consumer_secret:  <%= ENV["TWITTER_CONSUMER_SECRET"] %>
```

```diff:tweets_controller.rb
def search
  client = Twitter::REST::Client.new do |config|
-    config.consumer_key         = "xxxxxxxxxxxxxxxxxxxxxxx"
-    config.consumer_secret      = "yyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+    config.consumer_key         = Rails.application.secrets.twitter_consumer_key
+    config.consumer_secret      = Rails.application.secrets.twitter_consumer_secret
  end
```

## 検索結果を表示するViewの追加

`search.html.erb`から呼ばれる検索結果を表示するViewを作成していきます。
コントローラーで`@tweets`に検索結果のツイートが格納されたものをループで一件づつ表示させています。

```app/views/tweets/_tweet_list.html.erb
<% if @tweets.present? %>
  <h2>検索結果</h2>
  <ul>
    <% @tweets.each_with_index do |tweet, i| %>
    <div class="list">
      <li>Tweet<%= i %>: <%= tweet.contents %> </li>
    </div>
    <% end %>
  </ul>
<% end %>
```

## アプリの検証

サーバーを起動して正しくツイート検索ができるか検証していきます。

```
$ rails s
```

http://localhost:3000/tweets/search にアクセスして実際に検索ワードを入れて結果が表示されるか確認してみてください。
以下は「正月」というキーワードで検索した結果です。

![スクリーンショット 2017-01-02 1.33.38.png](https://qiita-image-store.s3.amazonaws.com/0/140792/fd987c85-0a7e-bd4a-9a98-a076efbdca10.png)



json形式のデータが欲しい場合はエンドポイントを`.json`と指定します。
例えば「正月」というキーワードで検索したい場合のリクストURIは以下のようになります。

http://localhost:3000/tweets/search.json?keyword=正月


# 参考
[1] [[Rails3] form_for と form_tag、text_field と text_field_tag の違い](http://rails3try.blogspot.jp/2011/06/rails3-textfield-textfieldtag.html)
[2] [【Rails】form_for/form_tagの違い・使い分けをまとめた](http://qiita.com/shunsuke227ono/items/7accec12eef6d89b0aa9)
[3] [text_field_tag](http://apidock.com/rails/ActionView/Helpers/FormTagHelper/text_field_tag)
[4] [【Rails】Amazon APIで書籍検索アプリ作成](http://qiita.com/tatsuki-m/items/043f14e7d4504277c947)
[5] [Rails環境でTwitterとFacebookを使ってサイトの拡散をするため自動投稿させるアプリを作成してみた](http://blogs.zealot.co.jp/archives/810)