読者です 読者をやめる 読者になる 読者になる

月曜日までに考えておきます

ITネタとゲームネタ中心に興味のあること色々書きます。

Heroku&Ruby1.9.3&Rails3.2で「つぶやかレシピ」を作って公開したときの技術的な話

IT ruby

先日公開した「つぶやかレシピ」の技術的な内容について書きます。 使っている技術はざっくり言うと以下のような内容なので興味のある部分があれば参考にしてください。

  • Ruby1.9.3+Rails3.2.3
  • Ruby Gem Twitterを用いてTwitterAPIを利用
  • Nokogiriを用いでCookpadのサイトから情報の一部を引っ張ってきている
  • 公開はHeroku

■Herokuへのアップロードについて

  1. Herokuのアカウントを取る
  2. ローカルPCでHerokuにログインする(Terminalで)
  3. Railsのアプリ作る
  4. gitでpushする

$ gem install heroku
$ heroku login (emailとパスワード入れる)
$ rails new [application name]
$ cd [application name]
$ git init
$ git add .
$ git commit -m “first commit”
--- アプリ作成 とgit add & commitを繰り返す

完成したら


$ heroku create --stack cedar
$ git push heroku master

でデプロイできます。 なお、Rails3.1からProductionモードで動かした時にassetsファイルの読み込み先がdevelopmentモードと違ってて動かなかったりCSS反映してくれなかったりするので、ローカルでもproductionモードで動かしておくのが吉です。

 

 

■つぶやかレシピの話

■やってること

  • 定期的にTwitterをクックパッドのURL "cookpad.com"で検索
  • 見つかったものを1つぶやき1レコードでDBに入れる
  • ユーザーがアクセスしてきたらDBを集計して返す

■短縮URLへの対応

Twitter検索は、つぶやきが短縮URLになっていても検索時はフルのURLとして検索対象にしてくれる。 しかし、取得してきた文字は短縮URLなので、短縮を展開する必要がある。 短縮URLは Net::HTTPRedirection が返るので以下の様な感じで展開処理を書いています。

def self.getExpandUri(uri)
  expandUri = nil
  errCd = nil
  begin
    response = Net::HTTP::get_response(URI.parse(uri))
    case response
    when Net::HTTPSuccess then
    #通常URIの場合
    expandUri = uri
    when Net::HTTPRedirection then
    #短縮URIの場合
    expandUri = getExpandUri(response['location'])
    else #その他
    response.error!
    end
  rescue => errCd
    #ログに書く
    logger.error "ERR_CD:#{errCd},uri:#{uri}"
  end
  return expandUri
end

あと、URLの展開は以下のようにやっているのですが、URL直後に半角英数がくっつていると一緒に持ってきちゃって意図しないURLになります。 (r.textがURLを含んだツイート内容です)


uri = URI.extract(r.text, %w[http]).first

これを外す処理がこれ。


def self.format_uri(uri)
  return if uri == nil
  case uri.last
  when ':', '.', '(', ')', '@'
    uri[-1] = ''
    self.format_uri(uri)
  end
  uri.gsub!(/[?].*/, '')
  return uri
end

展開後にも、?… というアクセス解析のためのクエリストリングが付いていることがあるので展開後にももう一度やる必要があります。

 

■定期的なつぶやきの取得 Herokuは無料だとCronが1日1回しか使えません。 cookpadのURLつぶやきは多そうなので1日1回だと不足だと思いました。 やったこと。

  • Delayed_jobをつかう→H有料プランじゃないと使えない機能でした。
  • 更新用のURLを作って踏む→Herokuは15秒ぐらい以上掛かる処理はアプリケーションエラーになる
  • 更新用のURLを作ってすこしずつ取得→うまく行った

少しずつ、というのは6分に1回、10件ずつ処理しています。 具体的には update?token={秘密の文字列} みたいなURLを作って、秘密の文字列が一致していた時のみ更新処理を行うようにしています。 そして、そのURLに対して自分のLinuxマシンから6分毎にcurlしています。


def update_task
  @result = false
  if params[:token] != ENV['token']
    logger.error "authenticate error:#{params[:token]}:#{ENV['token']} does not much"
    return
  end
  #更新処理
  Recipe.collect(10, 100, 10)
  @result = true
end

※ENVの説明は後でします。 GoogleAppEngineはCronを任意のタイミングで実行できるようなので、そちらも使ってみるとWebサービスだけでうまく処理が完結してくれるのかな、と思います。 ただ、あくまで処理時間を10秒ぐらいで抑えているのでこの対応でうまくいくだけなので、1日に1回とか1時間に1回、数秒とかでは終わらず数分とかかかるような処理をさせるのであればやはりHerokuのCronを使う必要があるのだと思います。

環境変数ENV

パスワードとかAPIキーとか、ソースはgithubに公開したいけど公開されると困る情報というのがあると思います。 そういうのをソースコード内で ENV['hoge'] としておいて読めるようにする機能。 たとえば上の ENV['token']ですが、こんな感じでHerokuに設定しています。

$ heroku config:add token="homuhomuhomuhomuhomuhomuhomuhomuhomuhomuhomuhomu"

こうしておくと、Rubyのコードでは ENV['token'] で"homuhomu(略)"が返るわけです。 ただし、このENVというのはあくまでHerokuの機能なので単純にやるとローカル環境でのテストや実行が通らなくと思います。 その対応として、秘密の文字列を追加したファイルを作る。(.envなど)そしてそのファイルを.gitignoreなどで構成管理に入らないようにする。 中身はこんな感じで token=homuhomuhomuhomu・・・ RAILS_ROOT/config/initializes/ 以下にこれを読み出してグローバル変数ENVに格納する処理を書けば、ローカルでもENVの部分を解釈してくれるようになります。 例

envfile = Rails.root.join(".env")
if envfile.exist?
    envfile.open("r").each do |line|
      key, val = line.split("=", 2)
      ENV[key] = val.gsub("\n", "") if val != nil
  end
end

JSONやYAMLCSVで書いてもいいと思います。

■参考リンク

お世話になったサイト