紳士なブログ

紳士すぎてすみません

ruby script/runnerのお話

以前ruby script/runner関連で意味不明なエラーが出たのでちょっと調べてみました。
今の自分にはわからないことが多かったので、雑多にまとめてみようと思います。


RailsアプリでActiveRecordを使ったバッチ処理 その2」

http://higelog.brassworks.jp/?p=12

  • ruby on railsではscript/runnerを使えばWebアプリ内からではなくてもメソッドを実行することができる
  • script/runnerはRails環境を読み込んだうえでワンライナーを実行するためのスクリプト
  • このコマンドをcronなどで定期的に実行するようにする



「script/console と script/runner の環境の指定方法の違い」

http://d.hatena.ne.jp/takihiro/20080507/1210163583

script/console と script/runner で環境の指定の仕方が違うとのこと。

% ruby script/runner -e production
% ruby script/console production


使い方のヘルプはそれぞれこんな感じ。

% ruby script/runner -h
Usage: script/runner [options] ('Some.ruby(code)' or a filename)

    -e, --environment=name           ...


% ruby script/console --help
Usage: console [environment] [options]


環境を設定するコードも全然違うみたい。

  • rails-2.3.14/lib/commands/runner.rb
require 'optparse'

options = { :environment => (ENV['RAILS_ENV'] || "development").dup }
code_or_file = nil

ARGV.clone.options do |opts|
  opts.on("-e", "--environment=name", String,
          "Specifies the environment for the runner to operate under (test/development/production).",
          "Default: development") { |v| options[:environment] = v }
end

ENV["RAILS_ENV"] = options[:environment]
RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV)


  • rails-2.3.14/lib/commands/console.rb
ENV['RAILS_ENV'] = case ARGV.first  # (1)
  when "p": "production"
  when "d": "development"
  when "t": "test"
  else
    ARGV.first || ENV['RAILS_ENV'] || 'development'
end


Rails task: script/runner or rake? - Stack Overflow

http://stackoverflow.com/questions/591503/rails-task-script-runner-or-rake

scrpt/runner some_useful_thing と rake some:other_useful_thingどちらが好まれるかという話。


script/runner がRailsを呼び出すのに対し、 rakeは :environment を指定したりして呼び出すことを明確にしない限りRailsを呼び出さない。
Railsを呼び出すのはコストがかかるので、避けられるに越したことはないという話になります。

とは言ってもほとんど同じとのこと。どちらも使うことはあるし、むしろ script/runner を使う人もいるようです。


script/runner OR script/rails runnerがクソ重い。どうしてくれんのよ?

http://d.hatena.ne.jp/babie/20100506/1273157249

Railsでのバッチ処理(script/runner)は環境を全部ロードするのでスタートアップがとても重いとのこと。
daemon_generatorなどを使う手もある。

他にもたくさん方法はあるみたいです。

http://higelog.brassworks.jp/?p=15


でもちょっと大げさ、というときのための手軽な方法があるようです。

actionに処理内容を書いて、ローカルのスクリプトからHTTPアクセスして叩く、という方法。
手動やcron/crontabで処理しようと思っていたなら使える方法です。

  • app/controllers/hoge_controller.rb
class HogeController < ActionController::Base
  def batch
    # IP制限しないと怖いですよ……
    raise NotFoundError.new unless request.remote_ip == "127.0.0.1"

    # 処理内容を書く
    ...

    render :text => "ok", :status => 200
  end
end

IPアドレスを制限するのがポイントとのこと。


じゃあバッチスクリプトはどう書くかというと、

  • script/batch.rb
#!/usr/bin/env ruby

require "net/http"
Net::HTTP.version_1_2

if __FILE__ == $0
  Net::HTTP.start('127.0.0.1', 80) do |http|
    response = http.get("/hoge/batch")
    unless response == Net::HTTPSuccess
      # log にエラーを吐くとか……適当に。
    end
  end
end

こんな感じで、cron/crontabに設定しておけばよいようです。
NotFoundErrorのところ、ちゃんと定義しておく必要がある(名前は任意)みたい。


  • app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  ...

  class NotFoundError < StandardError; end
  rescue_from NotFoundError, :with => :record_not_found

  def record_not_found
    render :file => File.join(RAILS_ROOT, 'public', '404.html'), :status => 404
  end
end

すでにロードされているので激速とのこと。今度試してみる。



railsのmodelメソッドを簡単にdaemon化する方法」

http://doruby.kbmj.com/red/20100707/rails___model___daemon__


Railsで普段開発する際には、ruby script/console や ruby script/runnerは重宝する。

これらのコマンドは、Webアプリを作成するために作ったmodelのメソッドを、簡単にbatchやコマンドラインのインタラクティブアプリに変更することができ、cron化やデバッグにとても重宝される。


最後までお読みいただきありがとうございます。