パフォーマンスのため、RMagickを使わずImageMagickを使う

37signalsのBascampは、イメージのサイズをこの方法で変更している。わざわざRMagickをインストールするのではなく、ImageMagickに渡すのである。

def thumbnail(temp, target)
  system(
    "/usr/local/bin/convert #{escape(temp)} -resize 48x48! #{escape(target)}"
  )
end

実践 Rails -強力なWebアプリケーションをすばやく構築するテクニック

確かに、下手にRMagickを使うよりも、素直にImageMagickに任せた方がいい場合も多そうだ。

ActiveRecordでオブジェクトを作成しないSelect

ActiveRecordでオブジェクトを作成しないSelect。
オーバーヘッドが少ないので、オブジェクトが必要ない場合はパフォーマンスが向上する。

select_value は最初の一件を返す。
値は文字列で返される。

>> ActiveRecord::Base.connection.select_value('SELECT zip_code FROM addresses')
=> "1000000"

select_valuesは配列で返す。
値は文字列で返される。

>> ActiveRecord::Base.connection.select_values('SELECT zip_code FROM addresses LIMIT 3')
=> ["1000000", "1020072", "1020082"]

実践 Rails -強力なWebアプリケーションをすばやく構築するテクニック』に紹介されていた方法。
メソッドを一つ追加して、select_valuesを使いやすくする。

class << ActiveRecord::Base
  def select_values(sql)
    connection.select_values(sanitize_sql(sql))
  end
end

sql = %(SELECT id FROM people WHERE last_name = ?)
last_name = %(O'Reilly)
Person.select_values [sql, last_name] #=> ["12", "42"]

Ruby on Railsで複数のデータベースを使用する方法

実践 Rails -強力なWebアプリケーションをすばやく構築するテクニック』に紹介されていたRuby on Railsで複数のデータベースを使用する方法。

database.ymlファイルに複数のデータベース環境を指定する。

legary:
  adapter: mysql
  database: my_db
  username: user
  password: pass
  host: legary_host

new:
  adapter: mysql
  database: my_db
  username: user
  password: pass
  host: new_host

ActiveRecordのクラス定義で、接続するデータベースを指定する。

class LegacyClient < ActiveRecord::Base
  establish_connection "legary"
end

class Client < ActiveRecord::Base
  establish_connection "new"
end

以上だ。

ActiveRecordの設定をDRYにするDrySQLライブラリ

実践 Rails -強力なWebアプリケーションをすばやく構築するテクニック』にDrySQLというライブラリが紹介されていた。

DrySQLを使うと、スキーマ情報を元にテーブルのリレーションシップや検証ルールを自動的に設定してくれる。

たとえば、従来は次のように設定する必要があった。
(DrySQLのサイトの例より)

class Employee < ActiveRecord::Base
  set_table_name "EMP123"
  set_primary_key "EMP_REF_ID"
  belongs_to :department, :foreign_key=>'DEP_NAME'
  has_many :projects, :foreign_key=>'PROJECT_NAME'
  has_one :location
  has_many :whatevers, :through=>projects
  validates_length_of :NAME, :allow_nil=>true, :maximum=>20
  validates_numericality_of :EMP_REF_ID, :allow_nil=>false, only_integer=>true
end 

DrySQLを使うと、次のように簡略化できる。
残りの設定は、DrySQLが自動的に設定してくれるのだ。

class Employee < ActiveRecord::Base
  set_table_name "EMP123"
end

これで、テーブルの設計を変更したときに、ソースコードを修正する必要がなくなる。

Ruby on Rails 2.3でテストを実行すると、undefined method `use_transactional_fixtures=’というエラーになるときの対策

Ruby on Rails 2.3でテストを実行すると

./test/unit/../test_helper.rb:23: undefined method `use_transactional_fixtures=’ for Test::Unit::TestCase:Class (NoMethodError)

というエラーになるときの対策です。

Ruby on Rails 2.3では、TestCaseクラスをTest::Unit::TestCaseからActiveSupport::TestCaseに変更する必要があります。

RAILS_ROOT/test/test_helper.rbを修正します。

#class Test::Unit::TestCase #Ruby on Rails 2.2以前
class ActiveSupport::TestCase #Ruby on Rails 2.3以降

RAILS_ROOT/test/unit/にある*.test.rbもすべて修正します。

#class SampleTest < Test::Unit::TestCase #Ruby on Rails 2.2以前
class SampleTest < ActiveSupport::TestCase #Ruby on Rails 2.3以降

Ruby on Rails 2.0で、ActionController::TestCaseのsetupにバグがあったため、Ruby on Rails 1.2と同じようにFunctionalTestにもTest::Unit::TestCaseを使っているケースがあります。
ActionController::TestCaseのsetupにバグは、Ruby on Rails 2.1で修正されているようですから、ActionController::TestCaseを使うようにします。

RAILS_ROOT/test/functional/にある*.test.rbもすべて修正します。

#class SampleTest < Test::Unit::TestCase #Ruby on Rails 2.2以前
class SampleTest < ActionController::TestCase #Ruby on Rails 2.3以降

Ruby on Rails 2.3でDEPRECATION WARNING: protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect. という警告が出るの対策

Ruby on Rails 2.3でDEPRECATION WARNING: protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect. という警告が出る場合の対策です。

Ruby on Rails 2.3から、protect_from_forgeryへの:digest、:secretオプションは非推奨になりました。つけても何も起こりません。

:digest、:secretオプションがあると警告が出ますから、削除しましょう。

WindowsでRuby1.8.7を使ってRuby on Railsをインストールする

One-Click InstallerにRuby 1.8.7がないので、茨の道を進むことになった。

Ruby 1.8.7のインストール

http://www.garbagecollect.jp/ruby/mswin32/ja/」から1.8.7の最新リリース版をダウンロードする。

ダウンロードが完了したら、ファイルを展開し、適当なフォルダに配置する。
ここではC:\ruby-1.8.7-p72-i386-mswinに配置したとする。

環境変数の「Path」を編集して「C:\ruby-1.8.7-p72-i386-mswin\bin」にパスを通す。

RubyGemsのインストール

RubyGems」のページから「rubygems-x.x.x.zip」をダウンロードする。

ダウンロードが完了したら、ファイルを展開する。

コマンドプロンプトから展開したフォルダに移動して、次のコマンドを入力し、RubyGemsをインストールする。

ruby setup.rb

Ruby on Railsのインストール

Porting Libraries to Win32」から「openssl-xxx-mswin32.zip」をダウンロードする。
ダウンロードが完了したら、ファイルを展開し、binフォルダにある「libeay32.dll」と「ssleay32.dll」をruby.exeと同じフォルダ(C:\ruby-1.8.7-p72-i386-mswin\bin)にコピーする。

Porting Libraries to Win32」から「zlib-xxx-mswin32.zip」をダウンロードする。
ダウンロードが完了したら、ファイルを展開し、binフォルダにある「zlib.dll」をruby.exeと同じフォルダ(C:\ruby-1.8.7-p72-i386-mswin\bin)にコピーする。

これで、RubyGemsが使用できるようになる。

次のコマンドを入力し、Ruby on Railsをインストールする。

gem install rails

「msvcr90.dllが見つからない。」というエラーが発生する場合は、「Microsoft Visual C++ 2008 再頒布可能パッケージ」からダウンロードしてインストールする。

Ruby on Railsに必要なファイルのインストール

iconv-1.8.win32.zipをダウンロードして展開する。
libフォルダにある「iconv.dll」をruby.exeと同じフォルダ(C:\ruby-1.8.7-p72-i386-mswin\bin)にコピーする。

これで、rakeがインストールできるようになる。

次のコマンドを入力し、rakeをインストールする。

gem intall rake

Porting Libraries to Win32」から「http://jarp.does.notwork.org/win32/readline-4.3-2-mswin32.zip」をダウンロードする。
libフォルダにある「readline.dll」をruby.exeと同じフォルダ(C:\ruby-1.8.7-p72-i386-mswin\bin)にコピーする。

これで、「ruby script\console」でエラーが発生しなくなる。

SQLite/Rubyのページを更新しました

情報が古く、ファイルがダウンロードできなくなっている、
というメールをいただきましたので、
3年ぶりにSQLite/Rubyのページを更新しました。

お知らせありがとうございました。

他のRuby関連のページもずいぶんと古くなってしまいました。
何とかしなければ。

Ruby on Railsによるプルダウンメニューの連動

質問をいただきました。

質問お願いします。
2つのプルダウンメニューを連動させたいのですがいい方法はありませんか?

Ruby on Railsによるプルダウンメニューの連動の簡単なサンプルを作りました。
サンプルということで、わかりやすさを重視しています。

Viewの方でプルダウンメニューを操作するJavaScriptの関数を用意しておき、
ControllerからJavaScriptの関数を呼び出すようにしました。
このあたりの処理は、いろいろなやり方があると思います。

index.html.erb

<%= javascript_include_tag(:defaults) %>
<script>
//プルダウンメニューが選択されたときに呼び出される関数
function change_item() {
  <%= remote_function(:url => {:action => 'call_ajax'}, 
                      :with => "'id=' + $('item1').value") %>
}
//プルダウンメニューの選択肢を削除する関数
function clear_item() {
  $('item2').length = 0;
}
//ブルダウンメニューに選択肢を追加する関数
function update_menu(name, value) {
  $('item2').options[$('item2').options.length] = new Option(name, value);
}
</script>
<% form_tag do %>
<%= select_tag('item1',
               options_for_select([['x','1'],['y','2'],['z','3']]), 
               :onchange => 'change_item()') %>
<%= select_tag('item2',options_for_select([])) %>
<% end %>

index_controller.rb

class IndexController < ApplicationController
  def index
  end

  def call_ajax
    # 選択肢のデータを取得
    item_list = {
      '1' => [['x1', '1'], ['x2', '2'], ['x3', '3']],
      '2' => [['y1', '4'], ['y2', '5'], ['y3', '6']],
      '3' => [['z1', '7'], ['z2', '8'], ['z3', '9']]
    }
    items = item_list[params['id']]
    # Javascriptの呼び出し
    render :update do |page|
      #選択肢を削除
      page.call 'clear_item'
      #選択肢を追加
      items.each { |item| page.call 'update_menu', item[0], item[1] }
    end
  end
end

Ruby on Railsでファイルのダウンロードの機能テスト

Ruby on Railsでファイルをダウンロードするには、send_data()を使用する。

send_data(csv_data, :type => 'text/csv', :filename => 'filename.csv')

さて、機能テスト(functional test)するには、どうすればいいか。

@response.bodyを参照すると、csv_dataが入っているので、動作のテストができる。

get :download_csv
assert_equal('期待する値', @response.body)