How does ActiveRecord define methods compared to attr_accessor?
When you use attr_accessor in your class that does not inherit from another class, there is, by definition, no method by the same name in a "parent" class. Therefore, super has nowhere to go to find a same-named method. (Well, your class inherits from Object, but Object does not define a method named some_attribute.)
On the other hand, ActiveRecord does define a getter and a setter for your attributes. Therefore, when you define them again in your class (that inherits from ActiveRecord::Base), then Ruby has somewhere to go (ActiveRecord::Base) when you invoke super.
Contrasting attr_accessor and the (many) methods that ActiveRecord generates for your table columns is a bit of an apples and oranges kind of question. ActiveRecord does all sorts of things with the attributes on the underlying table, including--but not limited to--creating getters and setters for the table columns.
(Note on the above: ActiveRecord works mostly by harnessing the power of method_missing, so many or most of the methods defined on your table attributes are actually implemented through the method_missing method. super does in fact invoke method_missing, if it exists, in parent classes, so that is how you can successfully invoke super on some_attribute when you inherit from ActiveRecord::Base.)
Difference between attr_accessor and attr_accessible
attr_accessor
is a Ruby method that makes a getter and a setter. attr_accessible
is a Rails method that allows you to pass in values to a mass assignment: new(attrs)
or update_attributes(attrs)
.
Here's a mass assignment:
Order.new({ :type => 'Corn', :quantity => 6 })
You can imagine that the order might also have a discount code, say :price_off
. If you don't tag :price_off
as attr_accessible
you stop malicious code from being able to do like so:
Order.new({ :type => 'Corn', :quantity => 6, :price_off => 30 })
Even if your form doesn't have a field for :price_off
, if it's in your model it's available by default. This means a crafted POST could still set it. Using attr_accessible
white lists those things that can be mass assigned.
Why is attr_accessor making my methods private?
As described in the examples from the documentation, Class.new
is passed a block, so I would do as below:
klass = Class.new do
attr_accessor :name
end
instance = klass.new
instance.name = "Foo"
instance.name #=> "Foo"
Rails - attr_accessor :- read the attribute value instead of the function overriding it
attr_accessor
defines a instance variable and a method to access it (read/write).
So the easy way is to write:
def open_ledger_account
create_ledger_account!(opening_balance: @opening_balance)
end
The read_attribute
would only work if opening_balance
was an attribute in the database on Customer
.
What happens when attr_accessor is within a class method?
When in a class method, self
is the class itself. Therefore, the following are equivalent in what they ultimately do:
class Test
def self.abc
# `self` in this context is Test.
# This sends the message `:attr_accessor, :john` to `Test`
attr_accessor :john
end
end
class Test
# `self` in this context is Test.
# This sends the message `:attr_accessor, :john` to `Test`
attr_accessor :john
end
However, as you noted, Test::abc
isn't executed when the class is parsed, so attr_accessor
isn't called, and the instance methods aren't added. It is perfectly valid to do this at runtime, and in fact, is the basis of much of the metaprogramming performed in Rails.
Typically, if you expect to add accessors via a class method, you might call that class method after definition but still during class declaration:
class Test
def self.abc
attr_accessor :john
end
abc
end
This will actually run, and properly declare the accessors on the class!
As to your question:
How come a running of a class method dynamically add methods to already created objects?
This is because instantiating a class doesn't instantiate a "snapshot" of the class at the time of instantiation - it creates an object which delegates much of its functionality (including discovery of the instance methods on it) to the class associated with it. Note that it is possible to define new methods on an instance which don't extend back to the class, too:
class Test
attr_accessor :foo
end
t1 = Test.new
t2 = Test.new
Test.send(:define_method, :bar) {}
puts t1.respond_to? :foo # => true
puts t2.respond_to? :foo # => true
puts t1.respond_to? :bar # => true
puts t2.respond_to? :bar # => true
t1.define_singleton_method(:baz) {}
puts t1.respond_to? :baz # => true
puts t2.respond_to? :baz # => false
Understanding Ruby on Rails ActiveRecord model Accessors
Because Rails' ORM uses the ActiveRecord pattern, two methods are created automatically for each column in the database associated with that table: columnname, and columnname=. This happens "automatically" as a result of your model inheriting from ActiveRecord::Base. These methods are defined using ruby's metaprogramming facilities and are created dynamically at the time of class creation.
For more info as to exactly what's going on, I would take a look at the Rails source. However, the above is probably enough to give you a working understanding of what is happening.
What is attr_accessor in Ruby?
Let's say you have a class Person
.
class Person
end
person = Person.new
person.name # => no method error
Obviously we never defined method name
. Let's do that.
class Person
def name
@name # simply returning an instance variable @name
end
end
person = Person.new
person.name # => nil
person.name = "Dennis" # => no method error
Aha, we can read the name, but that doesn't mean we can assign the name. Those are two different methods. The former is called reader and latter is called writer. We didn't create the writer yet so let's do that.
class Person
def name
@name
end
def name=(str)
@name = str
end
end
person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"
Awesome. Now we can write and read instance variable @name
using reader and writer methods. Except, this is done so frequently, why waste time writing these methods every time? We can do it easier.
class Person
attr_reader :name
attr_writer :name
end
Even this can get repetitive. When you want both reader and writer just use accessor!
class Person
attr_accessor :name
end
person = Person.new
person.name = "Dennis"
person.name # => "Dennis"
Works the same way! And guess what: the instance variable @name
in our person object will be set just like when we did it manually, so you can use it in other methods.
class Person
attr_accessor :name
def greeting
"Hello #{@name}"
end
end
person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"
That's it. In order to understand how attr_reader
, attr_writer
, and attr_accessor
methods actually generate methods for you, read other answers, books, ruby docs.
How to iterate ActiveRecord Attributes, including attr_accessor methods
You can't really solve this. You can approximate a hack, but it's not something that will ever work nicely.
model.attribute_names
should get you all the ActiveRecord ones, but the attr_accessor
fields are not fields. They are just ordinary ruby methods, and the only way to get them is with model.instance_methods
.
Idea 1
You could do model.attribute_names + model.instance_methods
, but then you'd have to filter out all your other normal ruby methods initialize, save
, etc which would be impractical.
To help filter the instance_methods
you could match them up against model.instance_variables
(you'd have to account for the @
sign in the instance variables manually), but the problem with this is that instance variables don't actually exist at all until they are first assigned.
Idea 2
In your environment.rb
, before anything else ever gets loaded, define your own self.attr_accessor
in ActiveRecord::Base
. This could then wrap the underlying attr_accessor
but also save the attribute names to a private list. Then you'd be able to pull out of this list later on. However I'd advise against this... monkey-patching core language facilities like attr_accessor
is guaranteed to bring you a lot of pain.
Idea 3
Define your own custom_attr_accessor
in ActiveRecord::Base
, which does the same thing as Idea 2, and use it in your code where you want to be able to retrieve the attribute names. This would be safe as you won't be clobbering the built-in attr_accessor
method any more, but you'll have to change all your code to use custom_attr_accessor
where neccessary
I guess in summary, what are you trying to do that needs to know about all the attr_accessor
fields? Try look at your problem from a different angle if you can.
Related Topics
All Possible Combinations of Selected Character Substitution in a String in Ruby
Couldn't Find User with Id=Sign_Out
Ruby on Rails Incompatible Library
Rails Shows "Warning: Can't Verify Csrf Token Authenticity" from a Restkit Post
Why Don't Numbers Support .Dup
How to Stop Rails' Built-In Server from Listening on 0.0.0.0 by Default
How to Batch Convert Mp4 Files to Ogg with Ffmpeg Using a Bash Command or Ruby
Mismatched Bundler Version - Bundler 2, Ruby 2.6
What Is the Standalone Splat Operator (*) Used for in Ruby
Rails Routes: Wrong Singular for Resources
Rails 3.1 - Changing Default Scaffold Views and Template
Using Compass from Ruby (Not Shell)