カスタム validation
うーむ。フレイムワークのソースを見ざるを得ない訳で。
直前エントリにおいて、AR#save メソド発行時、戻りが false (UPDATE 失敗) だったのですが、その所以はドコなんだ、とゆー事でバックトレイスを。
app/models/comment.rb (の一部再掲)
class Comment < ActiveRecord::Base belongs_to :article validates_presence_of :author, :body validates_against_spamdb :body, :url, :ip validates_age_of :article_id
上記の 2 番目の validate でヒッカカっている可能性もあるが、試験 (test_reject_article_age メソド) において、@article3 から @article1 に変更カケて save を発行しているあたりで article_id に違いない、という非常に適当な判断より validates_age_of メソドを確認。以下。
def validates_age_of(*attr_names) configuration = { :message => "points to an item that is no longer avail able for interaction"} configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) validates_each(attr_names, configuration) do |record, attr_name, value| next unless value.to_i > 0 record.errors.add(attr_name, configuration[:message]) if SpamProtection.new.article_closed?(record) end end
もう何が何だかワケ分かりませんな。とりあえず以下にポイントを。
- 仮引数の *attr_names は可変長引数を配列として受けとりまっせ、という合図
- で configuration という名前の Hash を初期化
- いっちゃんケツの引数が Hash だったら引数から pop して configuration に追加
(このあたりまでは validate の決まり文句的な手続きみたい)
ちなみに irb (というか script/console) で試してみたのが以下。
> configuration = { :message => "blocked by SpamProtection" } => {:message=>"blocked by SpamProtection"} > configuration.update({:attr => "value"}) => {:attr=>"value", :message=>"blocked by SpamProtection"}
で、次の validates_each が微妙。内容について別途。ぱっと見では、引数に指定されたシンボルについての検査を、という風に読める。SpamProtection#article_clised? は同じソースで定義されており、以下のようになっている。
def article_closed?(record) if config['sp_article_auto_close'].to_i > 0 if record.article.created_at.to_i < config['sp_article_auto_close'].to_i.d ays.ago.to_i logger.info("[SP] Blocked interaction with #{record.article.title}") return true end end end
デフォルトでは config['sp_article_auto_close'] は 300 らしい。現時点での試験用 DB も以下のようになっている。
mysql> select * from settings ; +----+---------+------------------------+-----------+ | id | user_id | name | value | +----+---------+------------------------+-----------+ | 1 | NULL | login | tobi | | 2 | NULL | password | whatever | | 3 | NULL | blog_name | test blog | | 4 | NULL | default_allow_pings | 1 | | 5 | NULL | default_allow_comments | 1 | | 6 | NULL | sp_url_limit | 3 | | 7 | NULL | sp_article_auto_close | 300 | | 8 | NULL | sp_global | 1 | | 9 | NULL | text_filter | textile | +----+---------+------------------------+-----------+ 9 rows in set (0.02 sec) mysql>
articles テーブルの created_at が今日から 300 日より前だったら true を返却、みたい。との事で @article1 の created_at を今年の正月とかに修正したら、試験パス。まだもう数件の Failure/Error が残存。メモを纏めたら作業続行予定。
validates_each について
定義は activerecord-1.14.2/lib/active_record/validations.rb との事。中身は以下。(activerecord 1.14.2 です)
def validates_each(*attrs) options = attrs.last.is_a?(Hash) ? attrs.pop.symbolize_keys : {} attrs = attrs.flatten # Declare the validation. send(validation_method(options[:on] || :save)) do |record| # Don't validate when there is an :if condition and that condition is false unless options[:if] && !evaluate_condition(options[:if], record) attrs.each do |attr| value = record.send(attr) next if value.nil? && options[:allow_nil] yield record, attr, value end end end end
うーん...
これも同様にいっちゃんケツの引数が Hash だったら options に格納している。ちなみに symbolize_keys は ActiveSupport のナニらしい。Rails API ドキュメントによると
Destructively convert all keys to symbols.
Rails API ドキュメントより引用
とある。単に pop するのとどう違うのか、は要調査、という事で。
あと、send とゆーのが何なのかさっぱり分からん。これも調べてみます。(弱
なんとなく record は AR なインスタンスを参照してて attr は列名で value はその値っつー気がするんですが根拠ゼロ。
send
わからん。
send(validation_method(options[:on] || :save)) do |record|
って何だー。
てか、|record| に AR オブジェクトの参照が渡される根拠は??
とりあえず保留。意味的には大体掴めてると思うし。(とほほほ