d_rail
d_rail

Reputation: 4119

How to use read_attribute when manually defining getter/setter (attr_accessor or attr_writers)

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.

Upvotes: 1

Views: 8025

Answers (2)

apneadiving
apneadiving

Reputation: 115531

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

Upvotes: 6

Simon Perepelitsa
Simon Perepelitsa

Reputation: 20639

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

Upvotes: 8

Related Questions