153

An example of what I'm talking about:

class Person < ActiveRecord::Base
  def name=(name)
    super(name.capitalize)
  end
  def name
    super().downcase  # not sure why you'd do this; this is just an example
  end
end

This seems to work, but I was just read the section on overriding attribute methods in the ActiveRecord::Base docs, and it suggests using the read_attribute and write_attribute methods. I thought there must be something wrong with what I'm doing in the example above; otherwise, why would they bless these methods as the "right way" to override attribute methods? They're also forcing a much uglier idiom, so there must be a good reason...

My real question: Is there something wrong with this example?

Ajedi32
  • 45,670
  • 22
  • 127
  • 172

4 Answers4

215

Echoing Gareth's comments... your code will not work as written. It should be rewritten this way:

def name=(name)
  write_attribute(:name, name.capitalize)
end

def name
  read_attribute(:name).downcase  # No test for nil?
end
Aaron Longwell
  • 8,153
  • 5
  • 21
  • 10
  • 19
    This is no longer true. Either super or this works now. I've not tested the hash notation, however. – heartpunk Apr 21 '11 at 17:32
  • 2
    In rails 3, the reader method specified here by Aaron works, but the writer that the original poster specified (feeding the name to super) works fine, and IMHO is cleaner than manually writing the attribute as Aaron suggests. – Batkins Nov 23 '11 at 20:46
  • 1
    I have tested the hash method given by mipadi below and it works like a charm (Rails v 3.2.6) – almathie Jul 23 '12 at 10:45
  • Isn't it the right answer? Thanks @Aaron, worked for me as well. – Sadiksha Gautam Dec 03 '15 at 09:31
  • 1
    FYI, the [Rails Style Guide](https://github.com/bbatsov/rails-style-guide) (unofficial community guide, but ~5500 stars) has [encouraged `self[:attr]` over `read_attribute(:attr)` since early 2013](https://github.com/bbatsov/rails-style-guide/commit/8de8a73fadcbfd49edd239d3d4e4216eb6796577#diff-04c6e90faac2675aa89e2176d2eec7d8R278). – Ryan Lue May 02 '18 at 04:49
  • Still valid for rails 6, see [docu](https://apidock.com/rails/v6.0.0/ActiveRecord/AttributeMethods/Read/read_attribute) – estani Oct 16 '19 at 09:26
  • 2
    This was removed in Rails 6.1: https://github.com/rails/rails/commit/e14e78bf447fbe1420d87212e833535a72783cd2 – Alec Jul 30 '21 at 12:53
95

As an extension to Aaron Longwell's answer, you can also use a "hash notation" to access attributes that have overridden accessors and mutators:

def name=(name)
  self[:name] = name.capitalize
end

def name
  self[:name].downcase
end
mipadi
  • 398,885
  • 90
  • 523
  • 479
7

There is some great information available on this topic at http://errtheblog.com/posts/18-accessor-missing.

The long and short of it is that ActiveRecord does correctly handle super calls for ActiveRecord attribute accessors.

jcnnghm
  • 7,426
  • 7
  • 32
  • 38
-3

I have a rails plugin that makes attribute overriding work with super as you would expect. You can find it on github.

To install:

./script/plugin install git://github.com/chriseppstein/has_overrides.git

To use:

class Post < ActiveRecord::Base

  has_overrides

  module Overrides
    # put your getter and setter overrides in this module.
    def title=(t)
      super(t.titleize)
    end
  end
end

Once you've done that things just work:

$ ./script/console 
Loading development environment (Rails 2.3.2)
>> post = Post.new(:title => "a simple title")
=> #<Post id: nil, title: "A Simple Title", body: nil, created_at: nil, updated_at: nil>
>> post.title = "another simple title"
=> "another simple title"
>> post.title
=> "Another Simple Title"
>> post.update_attributes(:title => "updated title")
=> true
>> post.title
=> "Updated Title"
>> post.update_attribute(:title, "singly updated title")
=> true
>> post.title
=> "Singly Updated Title"
chriseppstein
  • 10,453
  • 1
  • 25
  • 17