How to use read_attribute when manually defining g

2019-06-09 19:45发布

问题:

I set up a search model and want to require at least one field is filled out. I found a question that helps with the validation, Rails: how to require at least one field not to be blank. (I tried all answers, but Voyta's seems the best.)

The validation is working, except when I want to redefine the getter/setter via attr_accessor or attr_writer. (I have virtual attributes on the form that need to be apart of the validation.) To figure out what the problem is, I tested with an attribute that is a regular attribute item_length. If I add attr_accessor :item_length, the validation stops to work. So, I guess the question is how to I read an attribute's value without using dot notation. Since the validation uses strings, I can't use the normal way of reading.

Here is a snippet:

if %w(keywords 
      item_length 
      item_length_feet 
      item_length_inches).all?{|attr| read_attribute(attr).blank?}
    errors.add(:base, "Please fill out at least one field")
  end

Like I said, the virtual attrbutes (length_inches and length_feet) do not work at all, and the normal attribute (length) works except if I redefine the getter/setter.

回答1:

As stated in comment, use send

array.all? {|attr| send(attr).blank?}

For those wondering if send is ok in this case, yes it is: object calls its own instance methods.

But send is a sharp tool, so whenever you use with other objects, ensure you use their public api with public_send



回答2:

You should consider read_attribute as a private method for reading Active Record columns. Otherwise you should always use readers directly.

self.read_attribute(:item_length) # does not work
self.item_length # ok

Since you are trying to call this dynamically, you can use generic ruby method public_send to call the specified method

self.public_send(:item_length) # the same as self.item_length