5

Tôi có một mô hình có nhiều thuộc tính ngày. Tôi muốn có thể đặt và nhận các giá trị dưới dạng chuỗi. Tôi quá cưỡi một trong những phương pháp (bill_date) như sau:Sử dụng phương pháp bị thiếu trong Rails

def bill_date_human 
    date = self.bill_date || Date.today 
    date.strftime('%b %d, %Y') 
    end 
    def bill_date_human=(date_string) 
    self.bill_date = Date.strptime(date_string, '%b %d, %Y') 
    end 

này thực hiện rất tốt cho nhu cầu của tôi, nhưng tôi muốn làm điều tương tự trong vài ngày các thuộc tính khác ... thế nào tôi sẽ tận dụng lợi thế của phương pháp thiếu để bất kỳ thuộc tính ngày tháng có thể được thiết lập/nhận được như vậy?

+1

'method_missing' là về loại rơm cuối cùng bạn nên uống. Phương pháp xác định thực tế sạch hơn nhiều, dẫn đến thiết kế mã tốt hơn với sự phân tách rõ ràng các mối quan tâm, dễ hiểu hơn và cũng nhanh hơn. Vì vậy, nếu bạn có thể xác định phương pháp của bạn, bạn nên luôn luôn làm điều đó. –

+0

Như đã học từ KL-7 có cách tiếp cận tốt hơn method_missing, nhưng xem xét tôi có 4 thuộc tính ngày khác nhau cho mô hình này, việc xác định thủ công từng loại không phải là giải pháp. DRY – tybro0103

+0

Vâng, phương pháp của KL-7 thực sự là một phương pháp ưu tiên ở đây. Bởi vì anh ta đề xuất chính xác những gì tôi cũng có nghĩa là: xác định các phương pháp. –

Trả lời

10

Như bạn đã biết chữ ký của các phương pháp mong muốn, có thể tốt hơn là xác định chúng thay vì sử dụng method_missing. Bạn có thể làm điều đó như thế (bên trong định nghĩa lớp bạn):

[:bill_date, :registration_date, :some_other_date].each do |attr| 
    define_method("#{attr}_human") do 
    (send(attr) || Date.today).strftime('%b %d, %Y') 
    end 

    define_method("#{attr}_human=") do |date_string| 
    self.send "#{attr}=", Date.strptime(date_string, '%b %d, %Y') 
    end 
end 

Nếu liệt kê tất cả các thuộc tính ngày không phải là một vấn đề phương pháp này là tốt hơn khi bạn đang đối phó với phương pháp thông thường thay vì một số ma thuật bên method_missing.

Nếu bạn muốn áp dụng được cho tất cả các thuộc tính có tên kết thúc với _date bạn có thể lấy chúng như thế (bên trong định nghĩa lớp học của bạn):

column_names.grep(/_date$/) 

Và đây là method_missing giải pháp (không kiểm tra, mặc dù trước đó người ta không được kiểm tra một trong hai):

def method_missing(method_name, *args, &block) 
    # delegate to superclass if you're not handling that method_name 
    return super unless /^(.*)_date(=?)/ =~ method_name 

    # after match we have attribute name in $1 captured group and '' or '=' in $2 
    if $2.blank? 
    (send($1) || Date.today).strftime('%b %d, %Y') 
    else 
    self.send "#{$1}=", Date.strptime(args[0], '%b %d, %Y') 
    end 
end 

bên cạnh đó là tốt đẹp để ghi đè respond_to? phương pháp và trở true cho tên phương pháp, mà bạn xử lý bên trong method_missing (trong 1.9 bạn nên ghi đè respond_to_missing? thay thế).

+0

oooo tốt đẹp! Tôi đã không đi qua define_method trước :) Tôi vẫn muốn nhìn thấy phương pháp thiếu thực hiện, nhưng 1 cho một giải pháp tốt hơn – tybro0103

+1

Added 'method_missing' phiên bản, nhưng không sử dụng nó nếu bạn có thể xác định các phương pháp này. –

+1

@ KL-7 Trong 'method_missing' của bạn, bạn nên quay trở lại trực tiếp với siêu khi không xử lý phương thức. Ngay bây giờ, việc xử lý thuộc tính bên dưới luôn được thực thi. –

5

Bạn có thể quan tâm đến mô-đun AttributeMethods của ActiveModel (bản ghi hoạt động đã sử dụng cho một loạt nội dung), gần như (nhưng không hoàn toàn) những gì bạn cần.

Tóm lại bạn sẽ có thể để làm

class MyModel < ActiveRecord::Base 

    attribute_method_suffix '_human' 

    def attribute_human(attr_name) 
    date = self.send(attr_name) || Date.today 
    date.strftime('%b %d, %Y') 
    end 
end 

Sau khi làm điều này, my_instance.bill_date_human sẽ gọi attribute_human với attr_name thiết lập để 'bill_date'. ActiveModel sẽ xử lý những thứ như method_missing, respond_to cho bạn. Nhược điểm duy nhất là các phương thức _human này sẽ tồn tại cho tất cả các cột.