1

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.

Community
  • 1
  • 1
d_rail
  • 4,109
  • 32
  • 37

2 Answers2

8

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
Simon Perepelitsa
  • 20,350
  • 8
  • 55
  • 74
6

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

apneadiving
  • 114,565
  • 26
  • 219
  • 213
  • prefer `public_send` to respect encapsulation – BrendanDean May 19 '15 at 17:36
  • @BrendanDean Which encapsulation? Everything happens within the same class, whats the problem? Using `public_send` would prevent instance from using some of its methods – apneadiving May 19 '15 at 20:56
  • I mean object encapsulation: "encapsulation…A language mechanism for restricting access to some of the object's components." - [Wikipedia](http://en.wikipedia.org/wiki/Encapsulation_%28object-oriented_programming%29) `send` ignores access specifiers like `private`. `send_public` respects them. – BrendanDean May 20 '15 at 22:25
  • @BrendanDean Ok but within a class its useless, hopefuly you can call private methods within public methods – apneadiving May 21 '15 at 05:44
  • Basically a class encapsulates methods and properties, its exposes a public api. But the object itself, in its internals should not be restricted to the public api, it can access everything within itself. `send` is 100% legit here. It wouldnt be if you start using it to access one object internals from another class though – apneadiving May 21 '15 at 07:01
  • You're right. I'll switch to an upvote. I like to encourage use of `public_send` unless you *must* use `send`, because `public_send` has the correct default behavior. But that's a stylistic concern—your answer is correct. – BrendanDean May 21 '15 at 15:21
  • ah I guess I need to edit, you should be good. I completely agree with you about the need to push people towards `public_send` – apneadiving May 21 '15 at 16:41