How to get attributes that were defined through attr_reader or attr_accessor
Use introspection, Luke!
class A
attr_accessor :x, :y
def initialize(*args)
@x, @y = args
end
def attrs
instance_variables.map{|ivar| instance_variable_get ivar}
end
end
a = A.new(5,10)
a.x # => 5
a.y # => 10
a.attrs # => [5, 10]
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
.
Can I get a list of the symbols passed to attr_accessor in a class when inspecting?
There's no built-in method. Your solution of storing the method names at creation time will work, as long as you know in advance and can control what the method names are.
In my answer to a different but similar question, I showed how to get the names of the methods dynamically, after the fact, using TracePoint
. I've updated it below to include :attr_reader
and :attr_writer
.
module MethodTracer
TracePoint.trace(:c_call) do |t|
if %i[attr_accessor attr_writer attr_reader].include?(t.method_id)
t.self.extend(MethodTracer)
methods = t.self::Methods ||= []
MethodTracer.send(:define_method, :method_added) {|m| methods << m }
end
end
TracePoint.trace(:c_return) do |t|
if %i[attr_accessor attr_writer attr_reader].include?(t.method_id)
MethodTracer.send(:remove_method, :method_added)
end
end
end
class Foo
attr_accessor :a
attr_reader :b
attr_writer :c
def foo; end
end
Foo::Methods # => [:a, :a=, :b, :c=]
I've stored the method names in the Methods
constant, but obviously you can store them wherever is most convenient for you.
Defining/removing method_added
on MethodTracer
ensures that you don't clobber any Foo.method_added
you've defined yourself. This methodology, however, does require that if you define Foo.method_added
before your calls to attr_*
, you will need to call super
inside it. Otherwise you will skip the temporary method_added
defined by MethodTracer
.
Attr_accessor on class variables
attr_accessor
defines accessor methods for an instance. If you want class level auto-generated accessors you could use it on the metaclass
class Parent
@things = []
class << self
attr_accessor :things
end
end
Parent.things #=> []
Parent.things << :car
Parent.things #=> [:car]
but note that this creates a class level instance variable not a class variable. This is likely what you want anyway, as class variables behave differently than you might expect when dealing w/ inheritance. See "Class and Instance Variables In Ruby".
If attr_accessor includes an element, eg. ':name', is there any need for the 'name=' method?
I would argue that you should not use attr_accessor
and then override the setter.
I prefer using attr_reader
with custom setter methods:
attr_accessor :name
attr_reader :color
def color=(color)
# ...
end
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.
Regarding attr_accessor and def initialize
attr_accessor is just an attribute and by default it is nil. You cannot assign any value to it. If you need it to an array then initialize it as an array before using it.
Ruby attr_accessor with method name != instance variable name
No, there is not. All attr_accessor does is define two methods, one called value (in this case) and the other called value= that set an instance variable of the same name. Since you should only be accessing the instance variable via the getter/setter methods, it shouldn't matter what it is called internally.
If you are inheriting, you can use a call to super to ensure constancy:
class Walrus
attr_accessor :bubbles
end
class Harold < Walrus
def bubbles
# do Harold related things here
super
end
def bubbles=(value)
# do Harold related things here
super(value)
end
end
EDIT
If you really want to do it then you can define your own method on Class:
class Class
def attr_accessor2(method_name, attribute_name)
define_method(method_name) do
instance_variable_get("@#{attribute_name}")
end
define_method("#{method_name}=") do |value|
instance_variable_set("@#{attribute_name}", value)
end
end
end
I haven't tested it, but something like that should work. I'm not saying it's a good idea, but that is what you're looking for.
EDIT2
Here is how it works in practice:
1.9.3p0 :012 > class Derp
1.9.3p0 :013?> attr_accessor2 :herp, :meep
1.9.3p0 :014?> end
=> #<Proc:0x007fc75b02e958@(irb):7 (lambda)>
1.9.3p0 :015 > d = Derp.new
=> #<Derp:0x007fc75b027e00>
1.9.3p0 :016 > d.herp
=> nil
1.9.3p0 :017 > d.herp = 10
=> 10
1.9.3p0 :018 > d.herp
=> 10
1.9.3p0 :019 > d.instance_variable_get("@meep")
=> 10
Related Topics
Configure Webrick to Use Automatically Generated Self-Signed Ssl/Https Certificate
Rails Render of Partial and Layout in Controller
Getting Ruby Function Object Itself
What Do 'Def +@' and 'Def -@' Mean
Get Response Headers from Curb
Where to Put a Before_Filter Shared Between Multiple Controllers
Converting String "2½" (Two and a Half) into 2.5
Rails: Detecting User Agent Works in Development But Not Production
How to Delete a Range of Values from an Array
How to Make Xcode Use the Correct Version of Ruby When Running a Script
Split Seeds.Rb into Multiple Sections
How to Use Regex for Utf8 in Ruby
Accessing Variables from Included Files in Ruby
Generate a Nested JSON Array in Jbuilder
Generate Array of Numbers That Fit to a Probability Distribution in Ruby