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を’*‘でもとに戻すの忘れてえらい目にあった。
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
こんなのを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
to_jsonのときはattributesを取り出してjson化してる。
to_yamlのときはオブジェクトそのものをyaml化してる。
なので、
@items.to_json
と
@items.attributes.to_yaml
が等価
ということかな。
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_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’に変えた。
俺のミスってことだな。
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 -%>