rails

Railsのform_forとpartial  [rails]  [tips]

form_forを使いつつpartialレンダリングするには、以下の方法でpartial側

formにform_forのブロックパラメータを渡す必要がある。


例: ':locals => {:f => f}'のところ。

<h1><%= _('Create a New Account') %></h1>
<% form_for :user, @user, :url =>{:action => 'signup'} do |f| -%>
  <%= render :partial => 'signup_form', :locals => {:f => f}  %>
  <%= submit_tag _('Sign up') %>
<% end -%>

RubyのYAMLの変な挙動  [ruby]  [rails]  [bug_or_spec]

railsでto_yamlでactiverecordのオブジェクトをyaml化すると、日付関連がこのようになった。

mydate: !timestamp 08/10/10

で、これをrubyのYAML::loadで、読ませると'argument out of range'というエラーになる。

日付で問題なのはなぜか10月(他にもあるかもしれない。)


irb> require 'yaml'
irb> YAML::load("mydate: !timestamp 08/10/10")
ArgumentError: argument out of range
irb> YAML::load("mydate: !timestamp 08/11/10")
=> {"mydate"=>Thu Jan 10 00:00:00 UTC 2008}

(あれ?、よくみると11月も1月になってるし。。。)


irbでDateをyaml化すると、以下のようなフォーマットになる。

irb> Date.new(2008,10,10).to_yaml
=> "--- 2008-10-10\n"

なので、きっとrailsがto_yamlで変なことをしてるんだろう。


いろいろ調べて、environment.rbの記述を変更することで対処できた。

ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.update :default => "%Y/%m/%d"

なぜか、'%y'で設定してたので、'%Y'に変えた。


俺のミスってことだな。


Railsテスト時に日付を固定にする方法 (for Test::Unit)  [ruby]  [rails]  [TDD]

Railsで試験をする時に、日付に依存する機能(次の誕生日までの日数を求めるとか。。)がある時、今までならfixtureに

date: <%= Date.today().to_s %>

とか、動的にfixtureを定義していたのですが、いろいろロジックが複雑になりだしたので、発想を逆にすることにしました。

つまり、テストの日付を固定するという方法です。

基本的に、Date.today()や、Time.now()を上書きしてやるのですが、他のテストに影響を与えないようにやる必要があります。

そこで、Test::Unit::TestCaseのsetup/teardown内で、関数の再定義、もとの定義への戻しをやってやります。

hoge_controller_test.rb

:
#このテストは2008年9月で試験
class Date
  def self.fixed_today
    return Date.new(2008,9,1)
  end
end

class HogeControllerTest < Test::Unit::TestCase 
  def setup
    Date.instance_eval do
      alias :orig_today :today
      alias :today :fixed_today
    end
  end
  def teardown
    Date.instance_eval do
      alias :fixed_today :today
      alias :today :orig_today
    end
  end
:
end

特異メソッドのaliasを作成するので、instance_evalを利用してaliasによる関数の入れ替えをしています。

rails to_json と to_yaml  [rails]  [tips]

to_jsonのときはattributesを取り出してjson化してる。

to_yamlのときはオブジェクトそのものをyaml化してる。

なので、

@items.to_json

@items.attributes.to_yaml

が等価

ということかな。

Rails dbの fixtures形式バックアップ  [rails]  [tips]

こんなのをlibに入れて利用してる。

module YamlFixture
def record_to_yaml_fixture(rec_class_name)
  eval(rec_class_name).find(:all).inject({}){|h, v| h.merge(v.attributes["id"] => v.attributes)}.to_yaml
  end
end

Railsのvalidationの流れをもうちょっと細かく  [rails]  [tips]

ActiveRecord::Errors関係をいろいろいじりたいと思って、まずは、ActiveRecord::Validationをかじってみた。

Validationの流れの参考にでもなれば。

  • ActiveRecord::Baseが、Module ActiveRecord::Validationsをinclude
  • ActiveRecord::Validationsでは、
    • validate, validate_on_create, validate_on_updateのそれぞれのメソッドを定義。
    • これらのメソッド呼び出しにより、個々のvalidatorをCallbackChainに登録できる
      (validate do |record| true;end とやると validate_callback_chainにこのブロックが登録される。)
  • これらのメソッドを利用して
    validates_each, validates_confirmation_of, vaidates_acceptance_of, vaidates_presence_of, validates_length_ofをCallbackChainに登録
  • :save_with_validation :save_with_validation! を定義
    valid?メソッド経由でcallbackchainを順番に呼び出し
  • alias_method_chainによるsaveのリプレース
    • alias_method_chain :save, :validation
    • alias_method_chain :save!, :validation
      これにより
      save => save_with_validation, save! => save_with_validation!
      オリジナルのsave/save!は、
      :save_without_validation , :save_without_validation!
      にalias

最終的に、save/save!を契機にvalidation関連が呼ばれる。

だいたいそんな感じっぽい

参考:

  • validations.rb
    • ActiveRecord::Validations
    • ActiveRecord::Errors
  • callbacks.rb
    • ActiveSupport::Callbacks

Railsのmodelをカスタマイズしてメソッドの呼び出しにオプションを追加する  [rails]  [tips]

Railsである特定のmodelの検索(find)時に必ずsortをかけたいというような場合には、modelのfindをカスタマイズするのが便利。

class Hoge < ActiveRecord::Base
  class << self
    alias_method :find_org, :find
    def find(*params)
      options = {:order => "fuga desc"}
      options.update(params.last.is_a?(::Hash)? params.pop : {})
      find_org(*params.push(options))
    end
  end
end

もとのfind(find_org)を呼び出すときに、paramsを'*'でもとに戻すの忘れてえらい目にあった。