1

I have a question about the ActiveRecord's validates

For example, with this code:

class Person < ApplicationRecord
  validates :name, presence: true

  def name
    'hello world'
  end
end

There is a getter method name to return a string as the name. There is also a column in the database table, whose name is name.

I understand that, when we call person.name, the method (not the db record) is used. However, for the validates, do we use the method's return value or the db records to check?

I tried to read the ActiveRecord source code, but quickly got lost :-( Any help or pointer is much appreciated!

Sheng
  • 3,467
  • 1
  • 17
  • 21
  • Could just try it… – Dave Newton Feb 09 '23 at 01:39
  • There is no such thing as the "getter method" and the "db record". Without the definition of `name` `ActiveModel` uses `method_missing` to determine if the method name is actually an attribute and if so it returns its value. When you define `name` the method is no longer missing and thus you get the return value from that method call. In both cases the validation hooks simply call the method. e.g. `obj.send(:name)` [See Here](https://github.com/rails/rails/blob/v7.0.0/activemodel/lib/active_model/validations.rb#L402) – engineersmnky Feb 09 '23 at 18:04

1 Answers1

1

It uses the getter method. It's straight-forward to test:

class Person < ApplicationRecord
  validates :name, presence: true

  def name
    binding.irb
  end
end

Then in the Rails console:

irb(main):002:0> Person.create!

From: /Users/foo/app/models/person.rb @ line 5 :

    3: 
    4:   def name
 => 5:     binding.irb
    6:   end
    7: 

And if you simply exit from there (implying nil) you'll receive:

Validation failed: Name can't be blank (ActiveRecord::RecordInvalid)
anothermh
  • 9,815
  • 3
  • 33
  • 52
  • Thanks @anothermh! Is there a beautiful way to let the validates to use the db records, instead of getter? – Sheng Feb 09 '23 at 06:20
  • Use `self[:name]` from within the getter. See https://stackoverflow.com/a/28362793/3784008 for more. – anothermh Feb 09 '23 at 07:53
  • Yes, that works and actually I am using that in my code :-) My question is actually, how can I not involve the getter function in the `validates` path at all? Is it doable? – Sheng Feb 09 '23 at 08:03
  • You probably want to make a [custom validator](https://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations) that can make the calls necessary to return what you want. – anothermh Feb 09 '23 at 09:54