Shortcut for setting initialize() args to attributes?
ruby
Solution
Here's how you could do that. One instance variable and an associated read/write accessor will be created for each of `initialize`'s parameters, with the variable having the same name, preceded by `@`, and each instance variable will be assigned the value of the associated parameter.
Code
class MyClass
def initialize(< arbitrary parameters >)
self.class.params.each { |v|
instance_variable_set("@#{v}", instance_eval("#{v}")) }
< other code >
end
@params = instance_method(:initialize).parameters.map(&:last)
@params.each { |p| instance_eval("attr_accessor :#{p}") }
class << self
attr_reader :params
end
< other code >
end
Example
class MyClass
def initialize(a, b, c)
self.class.params.each { |v|
instance_variable_set("@#{v}", instance_eval("#{v}")) }
end
@params = instance_method(:initialize).parameters.map(&:last)
@params.each { |p| instance_eval("attr_accessor :#{p}") }
class << self
attr_reader :params
end
end
MyClass.methods(false)
#=> [:params]
MyClass.instance_methods(false)
#=> [:a, :a=, :b, :b=, :c, :c=]
m = MyClass.new(1,2,3)
m.a #=> 1
m.b #=> 2
m.c #=> 3
m.a = 4
m.a #=> 4
Explanation
When class `MyClass` is parsed, the class instance variable `@params` is assigned an array whose elements are `initialize`'s parameters. This is possible because the method `initialize` been created when the code beginning `@params = ...` is parsed.
The method Method#parameters is used to obtain `initialize`'s parameters. For the example above,
instance_method(:initialize).parameters
#=> [[:req, :a], [:req, :b], [:req, :c]]
so
@params = instance_method(:initialize).parameters.map(&:last)
#=> [:a, :b, :c]
We then create the read/write accessors:
@params.each { |p| instance_eval("attr_accessor :#{p}") }
and a read accessor for `@params`, for use by `initialize`:
class << self
attr_reader :params
end
When an instance `my_class` of `MyClass` is created, the parameter values passed to `MyClass.new` are passed to `initialize`. `initialize` then loops though the class instance variable `@params` and sets the value of each instance variable. In this example,
MyClass.new(1,2,3)
invokes `initialize(a,b,c)` where
a => 1
b => 2
c => 3
We have:
params = self.class.params
#=> [:a, :b, :c]
params.each { |v| instance_variable_set("@#{v}", instance_eval("#{v}")) }
For the first element of `params` (`:a`), this is:
instance_variable_set("@a", instance_eval(a) }
which is:
instance_variable_set("@a", 1 }
causing `@a` to be assigned `1`.
Note the accessor for `@params` is not essential:
class MyClass
def initialize(a, b, c)
self.class.instance_variable_get(:@params).each { |v|
instance_variable_set("@#{v}", instance_eval("#{v}")) }
end
@params = instance_method(:initialize).parameters.map(&:last)
@params.each { |p| instance_eval("attr_accessor :#{p}") }
end
Problem
This is a common initializer pattern: ``` def initialize(title, val, type) @title, @val, @type = title, val, type end ``` Is there a shortcut that is equivalent to "take every argument, create an attribute of the same name, and set the attribute equal to the argument's value"? I'm looking for a gem-free solution.