2011-09-01 57 views
0

我想在Rails中增強ActiveRecord設置器以確保只保存有效值。需要這樣一個地方是電話號碼。用戶可以在多種格式,如輸入電話號碼,用附加功能包裝ActiveRecord方法

(123) 456-7890 
+1 123-456-7890 

但我只想要存儲的數字,當它到數據庫放棄休息。我現在使用的方法是使用alias_method覆蓋setter方法。此外,我試圖把它放到一個模塊中,以便任何包含電話號碼的模型類都可以包含這個模塊,並定義應該清理的字段。我希望用一種接口是,

# Person has a "phone" attribute to store phone numbers 
class Person < ActiveRecord::Base 
    # first include this module 
    include PhoneSanitizer 

    # then call the class method and tell it which 
    # fields need need to be sanitized 
    sanitize_phone_field :phone 
end 

我我的模型類中做的唯一一件事就是包括PhoneSanitizer模塊(它增加了一個類的方法 - sanitize_phone_fieldPerson類)。該方法現在負責覆蓋設置器phone=方法。這是我沒有得到工作的部分。

module PhoneSanitizer 

    module ClassMethods 
    # wrap each of the passed-in fields with setters that 
    # clean up the phone number value of non-digits. 
    def sanitize_phone(*fields) 
     fields.each do |field| 
     new_method = "original_#{field}=".to_sym 
     original_method = "#{field}=".to_sym 
     alias_method new_method, original_method 
     define_method(original_method) do |value| 
      self.send(new_method, phone_to_number(value)) 
     end 
     end 
    end 
    end 

    def self.included(base) 
    base.extend(ClassMethods) 
    end 

    def phone_to_number(number) 
    number.gsub(/[^\d]/, '') 
    end 

end 

sanitize_phone被調用時,它拋出一個錯誤說:phone=沒有爲Person類中定義,這是很有意義的。我會如何去替代Person的實例的方法?

+0

在我看來,這將是更清潔和更安全簡單在PhoneSanitizer模塊中定義實例方法,將模塊包含在Person類中,然後從before_save回調中調用方法。 – KenB

+0

其實我非常喜歡這個想法。不知道爲什麼我以前沒有考慮過在驗證回調中做這件事。這比使用方法混合玩耍要乾淨得多。我已經實施了這個解決方案。然而,爲了學習和改進我的元編程的業力要點,我仍然有興趣瞭解上面我做錯了什麼。 – Anurag

+0

嗯。我似乎無法重現您的問題:https://gist.github.com/1185316。測試了紅寶石1.8和1.9。雖然我有一個想法,你的問題在哪裏..我認爲你說你得到的錯誤不是Ruby實際告訴你真正的錯誤是。 – Casper

回答

1

我覺得你的錯誤不是undefined method alias_method這是不同的東西,你誤解了它(?)

真正的問題是,在ActiveRecord getter和setter方法動態。在實際的AR對象從數據庫加載之前,不會創建getter和setter方法(即phonephone=)。此時,AR列舉數據庫字段並創建相應的字段方法。

這些字段方法在您的源代碼中定義類時不可用,因此您不能使用alias_method這種不存在的方法。但是你可以做這樣的事情,而不是(未測試):

module PhoneSanitizer 
  module ClassMethods 
    def sanitize_phone(*fields) 
      fields.each do |field| 
        original_method = "#{field}=".to_sym 
        define_method(original_method) do |value| 
      self.write_attribute(field, phone_to_number(value)) 
        end 
      end 
    end 
  end 

    ... 
end 

這應該做到非常多,你本來打算同樣的事情:
http://apidock.com/rails/ActiveRecord/AttributeMethods/Write/write_attribute

+0

錯誤實際上是':phone ='方法未定義,而不是我在我的問題中寫的'alias_method'。我不能使用'write_attribute',因爲它是專門爲'ActiveRecord'定義的,我的一些模型對象是隻包含'ActiveModel'的普通對象。但是,您已正確識別問題,因爲這些方法是通過檢查數據庫字段來動態定義的,並且在我的代碼運行時不可用。 – Anurag