How can I overwrite a getter method in an ActiveRecord model?
The Rails Style Guide recommends using self[:attr]
over read_attribute(:attr)
.
You can use it like this:
def name
name_trans || self[:name]
end
rails override default getter for a relationship (belongs_to)
alias_method is your friend here.
alias_method :original_bar, :bar
def bar
self.original_bar || Bar.last
end
The way this works is that you alias the default "bar" method as "original bar" and then implement your own version of "bar". If the call to original_bar returns nil then you return the last Bar instance instead.
What is the right way to override a setter method in Ruby on Rails?
===========================================================================
Update: July 19, 2017
Now the Rails documentation is also suggesting to use super
like this:
class Model < ActiveRecord::Base
def attribute_name=(value)
# custom actions
###
super(value)
end
end
===========================================================================
Original Answer
If you want to override the setter methods for columns of a table while accessing through models, this is the way to do it.
class Model < ActiveRecord::Base
attr_accessible :attribute_name
def attribute_name=(value)
# custom actions
###
write_attribute(:attribute_name, value)
# this is same as self[:attribute_name] = value
end
end
See Overriding default accessors in the Rails documentation.
So, your first method is the correct way to override column setters in Models of Ruby on Rails. These accessors are already provided by Rails to access the columns of the table as attributes of the model. This is what we call ActiveRecord ORM mapping.
Also keep in mind that the attr_accessible
at the top of the model has nothing to do with accessors. It has a completely different functionlity (see this question)
But in pure Ruby, if you have defined accessors for a class and want to override the setter, you have to make use of instance variable like this:
class Person
attr_accessor :name
end
class NewPerson < Person
def name=(value)
# do something
@name = value
end
end
This will be easier to understand once you know what attr_accessor
does. The code attr_accessor :name
is equivalent to these two methods (getter and setter)
def name # getter
@name
end
def name=(value) # setter
@name = value
end
Also your second method fails because it will cause an infinite loop as you are calling the same method attribute_name=
inside that method.
ActiveRecord: How to grab record column value if I overwrite the getter method?
Use read_attribute
:
def email
guest? ? guest_email : read_attribute(:email)
end
Alternatively, you can use self[:attribute]
as pointed out by @Stefan:
def email
guest? ? guest_email : self[:email]
end
Overwrite has_many getter
Technically you can add an alias to make sure the rest of the code doesn't break. The idea is to rename your existing has_many
method to something else and use the existing key_phrases
as a normal method.
This way you don't have to change anywhere else in the codebase and this will work with minimum changes.
Read more here about alias_attribute
class Metric < ApplicationRecord
alias_attribute :phrases, :key_phrases #NOTE the alias_attribute should be before `key_phrases`
has_many key_phrases
def key_phrases
# your logic goes into this method
if phrases.empty?
# create
else
phrases
end
end
end
However... I personally create a more meaning full method and leave the "has_many key_phrases" as it is, the reason is, this implies and gets and if you are trying to create records in the same method it's a bit confusing.
So, I would do something like this
class Metric < ApplicationRecord
has_many key_phrases
def get_or_create_key_phrases(*params)
if phrases.empty?
# create
else
phrases
end
end
end
and then change all the places that call key_phrases
to get_or_create_key_phrases
, which I personally think more explicit. But as per the downside, you'll have to change more places in the code.
Overriding a has_many association getter
Use super:
class User < ActiveRecord::Base
has_many :cars
def cars
# Pretend this is complex logic
super.order(:num_wheels)
end
end
when you use a macro like has_many, Rails dynamically creates a module(which could be accessed by User.generated_association_methods).In your case, define the accessors and readers(such as "cars" in your case, which could be accessed by User.generated_association_methods.instance_methods). This module becomes the ancestor of your User class, so you can access the reader method(cars) by "super" in your own cars method.
How do I override an attr_accessor getter from a module?
Are you sure you want the attr_accessor
? Wouldn't an attr_writer
suffice?
require 'active_support/all'
module HasUrl
extend ActiveSupport::Concern
included do
attr_writer :bar
end
def bar
0
end
end
class Foo
include HasUrl
end
p Foo.new.bar
Anyhow, if you really want to use attr_accessor
, this should work:
require 'active_support/all'
module HasUrl
extend ActiveSupport::Concern
included do
attr_accessor :bar
define_method :bar do
0
end
end
end
class Foo
include HasUrl
end
p Foo.new.bar
Hash getter and setter on ActiveRecord object
First to say: I dont think thats the best solution. When you touch this code again in lets say 3 years it will be like "WHAAAAAT HAVE I DONE?"... It whould the best solution to replace all the snippets in your code with other code.
You can prepend the method_missing
method of the object after removing this line serialize :state
to fetch all calls that want to access the not anymore existing serialized field of the object. Its explained here:
http://blog.enriquez.me/2010/2/21/dont-forget-about-respond-to-when-implementing-method-missing/
Its called metaprogramming. Thats the "rails magic" that makes all the find_by_attribute_name
stuff working without defining each of these methods. Can be cool stuff but you need to be very carefull and you need to know what your doing.
Overrriding ActiveRecord attribute getter doesn't produce consistent results
You might want to try using an enum in your model, like below:
enum priority: { high: 1, normal: 2, low: 3}
see the documentation here http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html
Related Topics
How to Sort a String's Characters Alphabetically
Modify Ruby Hash in Place( Rails Strong Params)
Ruby on Rails Error "Cannot Load Such File -- Less"
What Does "Wrong Number of Arguments (1 for 0)" Mean in Ruby
Param Is Missing or the Value Is Empty: User Rails 4
How to Get Attributes That Were Defined Through Attr_Reader or Attr_Accessor
How to Parse JSON Request Body in Sinatra Just Once and Expose It to All Routes
Ruby on Rails Upload File Problem Odd Utf8 Conversion Error
How to Determine the Md5 Digest of a Given Asset in the Rails Asset Pipeline
How to Use Dot Syntax for Ruby Hash
How to Execute Ruby Template Files (Erb) Without a Web Server from Command Line
Get Class Location from Class Object
What Would a Default Getter and Setter Look Like in Rails
Ruby Gem Not Found Although It Is Installed