Posts Tagged ‘Rails’

, | 2 Comments | 2月 10th, 2009

Rails&Lighttpdの環境で動画とか大容量ファイルを高速にダウンロードさせるとき、Rails の send_file を使わずに Lighttpd の X-sendfile という仕組みを使うと良いようです。

lighttpd-1.4.15 で試しました。

通常のsend_file

send_file '/tmp/movie.wmv'

Lighttpdを使ったx-sendfile

# X_SENDFILE する
path = '/tmp/movie.wmv'
response.headers['Content-Type'] = "application/force-download"
response.headers['Content-Disposition'] = "attachment; filename=\"123.mmv\""  # 好きな名前でダウンロードさせる
response.headers["X-LIGHTTPD-SEND-FILE"] = path
response.headers['Content-length'] = File.size(path)
render :nothing => true

これだけではダメで、lighttpdの設定も変更する必要がある。

lighttpdにパスが通ってる環境では lighttpd が優先的に起動されるが、初回起動時に config/lighttpd.conf が生成されるはずで、以下のようなくだりがあるはず。

fastcgi.server      = ( ".fcgi" => ( "localhost" => (
  "min-procs"       => 1,
  "max-procs"       => 1,
  "socket"          => CWD + "/tmp/sockets/fcgi.socket",
  "bin-path"        => CWD + "/public/dispatch.fcgi",
  "bin-environment" => ( "RAILS_ENV" => "development" )
) ) )

ここに、 "allow-x-send-file" => "enable" を追加すると先ほどの X-LIGHTTPD-SEND-FILE が使えるようになります。追加しないと、エラーは起きないけど、1byteの空ファイルしかダウンロードされません。

設定すると、以下のようになります。

fastcgi.server      = ( ".fcgi" => ( "localhost" => (
  "allow-x-send-file" => "enable",
  "min-procs"       => 1,
  "max-procs"       => 1,
  "socket"          => CWD + "/tmp/sockets/fcgi.socket",
  "bin-path"        => CWD + "/public/dispatch.fcgi",
  "bin-environment" => ( "RAILS_ENV" => "development" )
) ) )

x-send-file の注意点ではないですが、IEでは ヘッダに Cache-Control: no-cache がついていた場合、SSLでファイルのダウンロードが出来ないという仕様があります。こういうときは、SSLを経由しないようにするか、no-cache をやめる必要があります。どういう仕様なんだろう…。

どういう条件かわからないが、ファイルが変更されても304 Not Modified が返されてしまうことがあることが発覚。強制的に、200を返してあげれば最新のファイルがダウンロードされます。逆に変更されていない場合は304を返したいので、そういう割り当てをすれば良いのだが。

render :status => 200

とかで、強制的に200を返せるようだ。

| No Comments | 1月 29th, 2009

準備

% sudo gem install diff-lcs redgreen ruby-snarl ZenTest launchy rspec webrat autotest_screen --no-ri --no-rdoc

インストール

%  script/plugin install git://github.com/dchelimsky/rspec.git
% script/plugin install git://github.com/dchelimsky/rspec-rails.git
% script/generate rspec

Railsのバージョンを上げたら、上記のコマンドを実行しないとエラーが出るメソッドがあるので注意。

Rails2.2.2でエラーで動かなかった

vendor/plugins/rspec_on_rails/lib/spec/rails/example/rails_example_group.rb:4:
undefined method `cache_template_extensions=' for
ActionView::Base:Class (NoMethodError)

というエラーが出てテストが実行できなかった。
該当箇所をコメントアウトしても今のところ問題は起きていないので、このまま続けてみようと思う。

#ActionView::Base.cache_template_extensions = false

Growlと連携

まず、Growlの設定で「ネットワーク」の「受信される通知を開く」にチェックを入れる。

あと、growlnotify をインストールしていなかったらインストールする。Growl-1.1.4.dmg をマウントしたら、

% cd /Volumes/Growl-1.1.4/Extras/growlnotify
% sudo ./install.sh

という感じでインストールできます。

あとは、autotest の設定ファイルを ~/.autotest に記述する。growlとautotestを連携させる – Hello, world! – s21g の通りに設定させてもらう。必要あるか分からないけど、一応 Growl の再起動をして試したところ無事動作しました。

動かなかったら、export RSPEC=true をする必要がある。

, | No Comments | 1月 8th, 2009

MacBookもSSDに入れ替えて完全にSSD厨な感じだけど、環境構築してたら MySQL + Ruby でつまづいたのでメモしとく。

Ruby は、MacPorts で 1.8.7 をインストール。MySQL は、MySQL公式のダウンロードページ から、Mac OS X 10.5 (x86_64) をインストールした(これが良くなかったみたい)。

このあと、いつものように

% sudo gem install mysql -- --with-mysql-config /usr/local/mysql/bin/
mysql_config
Building native extensions.  This could take a while...
Successfully installed mysql-2.7
1 gem installed

ってやってインストールするんだけど、

% script/console
=> Booting WEBrick...
dyld: lazy symbol binding failed: Symbol not found: _mysql_init
  Referenced from: /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle
  Expected in: dynamic lookup
 
dyld: Symbol not found: _mysql_init
  Referenced from: /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle
  Expected in: dynamic lookup

って出てしまう。

ぐぐってみると、同じ症状が出てる人がいた。Leopard MacbookにMySQLをインストール〜Railsで使えるようにするまで
(http://blog.digital-squad.net/article/92963519.html) を参考にさせて頂いて、試してみる

% sudo env ARCHFLAGS="-arch x86_64" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Building native extensions.  This could take a while...
Successfully installed mysql-2.7
1 gem installed

しかし・・・

% script/console
Loading development environment (Rails 2.0.2)
/Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle: dlopen(/Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle, 9): no suitable image found.  Did find: (LoadError)
        /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle: mach-o, but wrong architecture - /Library/Ruby/Gems/1.8/gems/mysql-2.7/lib/mysql.bundle       from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in `require'
        from /Library/Ruby/Gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:496:in `require'
        from /Library/Ruby/Gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
        from /Library/Ruby/Gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:496:in `require'
        from /Users/chu/rails/viena/config/environment.rb:97
        from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
        from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in `require'
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/irb/init.rb:253:in `load_modules'
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/irb/init.rb:251:in `each'
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/irb/init.rb:251:in `load_modules'
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/irb/init.rb:21:in `setup'
        from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/irb.rb:54:in `start'
        from /usr/bin/irb:13

違うエラーになった。

下記を試せばいいっていう書き込みもあったけど、どうやら、x86_x64 のMySQL を使うとこれが出るっぽい。

% sudo env ARCHFLAGS="-arch i386" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Building native extensions.  This could take a while...
Successfully installed mysql-2.7
1 gem installed

というわけで、Mac OS X 10.5 (x86) を改めて入れ直して、

% sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Building native extensions.  This could take a while...
Successfully installed mysql-2.7
1 gem installed

ってやったら普通に動いた。うーん。

友達曰く、port で入れれば普通に動くのに。らしい(‘A`)

| No Comments | 12月 3rd, 2008

 複数プロセスを立ち上げていても、script/runner 等で叩くときも並行して同じ処理を走らせないようにするとき、ファイルのロックを使うのが簡単です。

ソース

#{RAILS_ROOT}/lib/batch_lock.rb という名前で下記を保存する。

# 同一処理を並行で走らせない様にするモジュール
class BatchLockException < Exception; end
module BatchLock
  # 処理を排他ロックして走らせる
  def self.run(batch_name)
    FileUtils.mkdir(RAILS_ROOT + '/tmp/batch_lock') unless File.exists?(RAILS_ROOT + '/tmp/batch_lock')
    # batch_name をスコープとして排他処理される。
    File.open("#{RAILS_ROOT}/tmp/batch_lock/#{batch_name}", 'w') do |f|
      if f.flock(File::LOCK_EX | File::LOCK_NB)
        yield
      else
        raise BatchLockException.new
      end
    end
  end
 
  # 処理が走っているか確認する
  def self.running?(batch_name)
    FileUtils.mkdir(RAILS_ROOT + '/tmp/batch_lock') unless File.exists?(RAILS_ROOT + '/tmp/batch_lock')
    # ロック用のファイル名は、バッチごとにユニークにする。
    File.open("#{RAILS_ROOT}/tmp/batch_lock/#{batch_name}", 'w') do |f|
      ! f.flock(File::LOCK_EX | File::LOCK_NB)
    end
  end
end

使い方

同時に走らせたくない処理をブロックで実行する。

begin
  BatchLock.run(:go_to_park) do
    # ユーザがみんな公園に行く
    User.find(all).each(&:go_to_park)
  end
rescue BatchLockException
  logger.error "既にバッチが走っていた…。"
end

今バッチが走ってるか確認する。

p BatchLock.running?(:go_to_park) ? 'バッチ進行中' : 'バッチは走ってない'

| No Comments | 10月 27th, 2008

 Railsで、ユーザが500万件居たりすると User.find(:all) とかやっただけでかなりきつい。こういうのをやるときって、バッチ処理だと思うんだけど、結構工夫してループ内で何回も find したりしても、メモリ足りなくなったりするから、結局 script/runner で Rails の起動コストを引いても シェルスクリプト回したほうが安全っていう結論になる。

 50000件ずつループで処理して、Railsを再起動みたいな感じになる。

#!/bin/sh
limit=50000
count=50000
while [ $count -le 5000000 ];
do
  script/runner "User.find(:all, :offset => $count,  :limit => $limit, :conditions => ['migration_complete = false']).each(&:check_email)"
 
  echo "$count"
  count=`expr $count + 5000`
done
download the hurt locker  
UA-682965-1