Model中“magic”方法的小写条件

时间:2012-02-19 02:18:36

标签: ruby-on-rails activerecord

模型Country有一个属性code,它会被before_save回调自动转换为小写。是否可以在“魔术”方法上强制执行此行为而无需重写大块的ActiveRecord :: Base?

class Country < ActiveRecord::Base
  attr_accessible :code
  validates :code, :presence => true
  validates_uniqueness_of :code, :case_sensitive => false

  before_save do |country|
    country.code.downcase! unless country.code.nil?
  end
end

RSpec的

describe Country do
  describe 'data normalization'
    before :each do
      @country = FactoryGirl.create(:country, :code => 'DE')
    end

    # passes
    it 'should normalize the code to lowercase on insert' do
      @country.code.should eq 'de'
    end

    # fails
    it 'should be agnostic to uppercase finds' do
      country = Country.find_by_code('DE')
      country.should_not be_nil
    end 

    # fails
    it 'should be agnostic to uppercase finds_or_creates' do
      country = Country.find_or_create_by_code('DE')
      country.id.should_not be_nil # ActiveRecord Bug?
    end
end

1 个答案:

答案 0 :(得分:0)

这是我提出的,尽管我真的很讨厌这种方法(正如问题所述)。一个简单的替代方法是将列,表或整个数据库设置为忽略大小写(但这是db dependendt)。

class Country < ActiveRecord::Base
  attr_accessible :code
  validates :code, :presence => true
  validates_uniqueness_of :code, :case_sensitive => false

  before_save do |country|
    country.code.downcase! unless country.code.nil?
  end

  class ActiveRecord::Base
    def self.method_missing_with_code_finders(method_id, *arguments, &block)
      if match = (ActiveRecord::DynamicFinderMatch.match(method_id) || ActiveRecord::DynamicScopeMatch.match(method_id))
        attribute_names = match.attribute_names
        if code_index = attribute_names.find_index('code')
          arguments[code_index].downcase!
        end
      end
      method_missing_without_code_finders(method_id, *arguments, &block)
    end

    class << self
      alias_method_chain(:method_missing, :code_finders)
    end
  end
end