Railsのテストフレームワークには、Mockを利用する仕組みがあります。 しかし、通常の方法でMockクラスを作成すると、オリジナルのクラスを 完全に置き換えてしまうため、すべての実装をテスト用に 書き直さなければならなくなってしまいます。

そこで、テスト用に改変したい場所だけMockで上書きし、その他の 動作はオリジナルに委譲する方法を紹介します。

   1  require_dependency 'models/foo'
   2  
   3  class Foo
   4    def bar
   5      puts 'baz'
   6    end
   7  end

上記のように、require_dependencyを使ってapp ディレクトリ以下のファイルを参照することで、Fooクラスのbar メソッドの挙動だけを置き換えることができるようになります。

posted by Png genki on Fri 1 Feb 2008 at 12:16

UnitTestやFunctionalTestの中から参照するファイルをどこに置くべきか という疑問について、明確な回答を用意できていなかったのですが、 Railsのソースの中で答えらしきものを見つけたので紹介します。

actionpack/lib/action_controller/test_process.rb L325

   1    # Usage example, within a functional test:
   2    #   post :change_avatar, :avatar => ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + '/files/spongebob.png', 'image/png')

このExampleによると、test/fixtures/filesというディレクトリを作り、 その中にファイルを置いているようです。

posted by Png genki on Fri 1 Feb 2008 at 00:33

[Ruby] [:name, :yomi] から {:name=>"maiha", :yomi=>"maiha"} を作りたい

最初に思いついたのは、inject

array = [:name, :yomi]
array.inject({}){|h,i| h[i] = "maiha"; h}
=> {:name=>"maiha", :yomi=>"maiha"}

"; h" が冗長で悔しいのよね。

injectでもこんな感じに書けばシンプルにできますよ。

   1  >> [:name, :yomi].inject({}){|h,k| h.merge(k => "maiha")}
   2  => {:name=>"maiha", :yomi=>"maiha"}

Hashinjectmergeと相性がいい。

posted by Png genki on Sun 27 Jan 2008 at 06:45

ActiveSupportでは、メソッドの途中で戻り値を指定するための returningというメソッドがObjectクラスに定義されています。 これを使うと、以下のようにメソッドの最後の式の値が何になるかを 気にせずに記述することができます。

   1  def foo
   2    returning Article.find(parmas[:id]) do |article|
   3      article.foo = 'bar'
   4      article.save
   5    end
   6  end

これと同様のことを、Rubyにあらかじめ用意されているensure を使っても実現可能です。

   1  def foo
   2    article = Article.find(params[:id])
   3  ensure
   4    article.foo = 'bar'
   5    article.save
   6  end

ブロックの値がensureの値を無視する事を利用しています。

posted by Png genki on Sat 26 Jan 2008 at 20:13

RubyでバイナリデータをBase64エンコードする場合、 require 'base64'をしてからBase64.encode64(binary)をするか、 以下のようにpackmテンプレートを使うことができます。

   1  [binary].pack('m') #=> Base64 encoded string

しかし、アップロードしたファイル用のテンポラリなファイル名 などに使用する場合、Base64でエンコードした文字列は ファイル名に使えない(可能性が高い)文字を含んでいるため、 そのような場合にはbase64url形式でエンコードします。

base64url って何?

base64url とは、base64 を基に RFC4648 で規定された変換方式で、url とファイル名に使用しても安全になるように設計されています。変更点は、base64 では + と / が使用されていますが、それを - と _ を使用するようにします。具体的には、base64では「ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/」の表で変換しますが、base64url では「ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_」を使用します。

ということで、通常のBase64エンコードした結果に対して、 +-に、/_に置き換えればいいだけなので、 以下のようにすればOKです。

   1  [binary].pack('m').tr('+/', '-_')

posted by Png genki on Thu 24 Jan 2008 at 06:02

annotate_models は、テーブルの情報をModelとFixtureのファイルに コメントとして書き込んでくれる非常に便利なプラグインです。

永く愛用しているプラグインの一つなのですが、 しばらくメンテナンスされていないようなので、 欲しいと思っていたindexに関する情報も出力するようにしてみました。

http://svn.s21g.com/public/rails/plugins/annotate_models_with_index/

こんな感じに、インデックス情報をカラムごとに付加します。

   1  # == Schema Information
   2  # Schema version: 48
   3  #
   4  # Table name: users
   5  #
   6  #  id           :integer(11)   not null, primary key
   7     (* snip *)
   8  #  mobile_email :string(255)   index_users_on_mobile_email(unique)

posted by Png genki on Thu 24 Jan 2008 at 01:09

irbを使っているときに、オブジェクトにどんなメソッドが 定義されているのかをpublic_methodsなどで調べることが 出来ますが、たいていの場合大量のメソッドがリストアップされて わかりにくくなってしまいます。

今回は、そんなときに良くやる方法を紹介します。

   1  >> Time.public_methods.sort - Object.public_methods
   2  => ["_load", "at", "days_in_month", "gm", "httpdate", "iso8601", "local", "local_time", "mktime", "now", "parse", "rfc2822", "rfc822", "time_with_datetime_fallback", "times", "today", "utc", "utc_time", "xmlschema", "yaml_new", "zone_offset"]

public_methodsからObjectpublic_methodsを取り除いたものを 表示しています。Objectの代わりに適当な親クラスを指定することで、 クラス階層の特定の領域で定義されたメソッドを表示することも できますね。

posted by Png genki on Mon 21 Jan 2008 at 14:14

Now, we are working on ShootingStar-4.0.0

It will be working on Windows by using WaitForMultipleObject API. I loved it since more than 10 years ago and thought it as is one of a beautiful aspect of Windows.

It may be shipped on next month.

posted by Png genki on Mon 21 Jan 2008 at 06:46

Rails勉強会@東京#26

Genki Takiuchi

Recent Situation:

  • Founded a small company.
    • And it has already survived 3 months, Yay!
  • Shipped ShootingStar 3.2.6
  • Jogging and swimming.
  • Finally I bought e-mobile (D02HW)
  • And also being with Rails 2.0.2

Favorite Things:

  • C/C++ Template, JavaScript, Ruby.
  • Thinking algorithm, Rendering (CG) and 3D.
  • Vim7, Zsh, GNU screen, rails.vim, ZenTest.

Thank you!

posted by Png genki on Sun 20 Jan 2008 at 14:33

以前紹介した Hash#only にちょっとした修正。

lib/hash_ext.rb

   1  class Hash
   2    def only(*args)
   3      args = *args if args[0].is_a? Array
   4      args.inject({}){|hash, key| hash[key] = self[key] if include? key; hash}
   5    end
   6  
   7    def only!(*args)
   8      args = *args if args[0].is_a? Array
   9      args.inject({}){|hash, key| hash[key] = self[key]; hash}
  10    end

以前のHash#onlyは、指定したキーが存在しない場合でも

   1  {:foo => 1}.only(:bar) #=> {:bar => nil}

というように、nilを指すHashを返していましたが、 キーが存在しない場合に空のHashを返すようにしてみました。 従来の挙動をするメソッドは、Hash#only!と感嘆符付きの メソッドにしています。 修正後の挙動は以下の通り。

   1  {:foo => 1}.only(:bar) #=> {}
   2  {:foo => 1}.only!(:bar) #=> {:bar => nil}

posted by Png genki on Sat 19 Jan 2008 at 13:30