Please explain me why i should define attr_list before attr? I can not understand why i should do that?
class Question
def self.attr_list
[:id, :name]
end
attr *self.attr_list
end
class Question
attr *self.attr_list
def self.attr_list
[:id, :name]
end
end
NoMethodError: undefined method `attr_list' for Question:Class
Unlike a def, a class is executed line by line immediately after you hit return to run your program:
class Dog
x = 10
puts x
end
--output:--
10
...
class Dog
puts x
x=10
end
--output:--
1.rb:2:in `<class:Dog>': undefined local variable or method `x'
In this line:
...
class Dog
def self.greet
puts 'hello'
end
greet
end
--output:--
hello
...
class Dog
greet
def self.greet
puts 'hello'
end
end
--output:--
1.rb:2:in `<class:Dog>': undefined local variable or method `greet'
Similarly, in this line:
attr *self.attr_list
you call self.attr_list(), yet the def comes after that line, so the method doesn't exist yet.
With a def, you can write this:
def do_math()
10/0
end
and you won't get an error until you call the method.
But by the time you create an instance of a class, all the code inside the class has already executed, creating the methods and constants that are defined inside the class.
By the way, you don't ever need to use attr because ruby 1.8.7+ has attr_accessor(reader and writer), attr_reader, and attr_writer.
Related
In Ruby, why can't I set the value of variables directly within a module's self.included method?
For example, the following code outputs NilClass (indicating that the #sound variable has NOT been set):
module Animal
def self.included(klass)
attr_accessor :sound
#sound = "Woof!" # <-- Variable assignment
end
def speak
puts #sound.class
end
end
class Dog
include Animal
end
dog = Dog.new
dog.speak # => NilClass
However, if I set the value of #sound within the module's speak method (instead of inside of self.included), then the variable is set correctly. For example, the following code outputs String:
module Animal
def self.included(klass)
attr_accessor :sound
end
def speak
#sound = "Woof!" # <-- Variable assignment
puts #sound.class
end
end
class Dog
include Animal
end
dog = Dog.new
dog.speak # => String
I would have expected both code samples above to output String.
In first example the receiver in self.included block is Dog class, not it's instance, meaning you are defining class instance variable #sound, not instance variable.
You can check it by running
Dog.instance_variable_get(:#sound) # with first example
speak method returns NilClass because dog does not have #sound instance variable defined.
In second example you are defining an instance variable #sound, thus it works as you expect.
I found this neat delegator based 'tee' implementation on SO:
https://stackoverflow.com/a/6410202/2379703
And I'm curious what is means for #targets (instance variable) means in the context of a class method:
require 'logger'
class MultiDelegator
def initialize(*targets)
#targets = targets
end
def self.delegate(*methods)
methods.each do |m|
define_method(m) do |*args|
#targets.map { |t| t.send(m, *args) }
end
end
self
end
class <<self
alias to new
end
end
log_file = File.open("debug.log", "a")
log = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT, log_file)
I get that it defining the methods write/close but #targets isn't even defined at this point since .to (aliased to new) has yet to be called so I'd assume #targets is nil.
Can anyone give an explanation as to the logistics of how this code works? Does ruby not even attempt to access/resolve #targets until the method in question is attempted to be called, which would be by the logger after it was instantiated?
The define_method method is called on a class to create an instance method. Inside that method, the self (and the instance variable) are instances of the class.
For example:
class Foo
#bar = "CLASS"
def initialize
#bar = "INSTANCE"
end
def self.make_method
define_method :whee do
p #bar
end
end
end
begin
Foo.new.whee
rescue NoMethodError=>e
puts e
end
#=> undefined method `whee' for #<Foo:0x007fc0719794b8 #bar="INSTANCE">
Foo.make_method
Foo.new.whee
#=> "INSTANCE"
It is correct that you can ask about instance variables that have never been created, at any time:
class Bar
def who_dat
puts "#dat is #{#dat.inspect}"
end
end
Bar.new.who_dat
#=> dat is nil
The same is true of other aspects of the language. As long as the code in the method is syntactically valid, it may be defined, even if invoking it causes a runtime error:
class Jim
def say_stuff
stuff!
end
end
puts "Good so far!"
#=> Good so far!
j = Jim.new
begin
j.say_stuff
rescue Exception=>e
puts e
end
#=> undefined method `stuff!' for #<Jim:0x007f9c498852d8>
# Let's add the method now, by re-opening the class
class Jim # this is not a new class
def stuff!
puts "Hello, World!"
end
end
j.say_stuff
#=> "Hello, World!"
In the above I define a say_stuff method that is syntactically valid, but that calls a method that does not exist. This is find. The method is created, but not invoked.
Then I try to invoke the method, and it causes an error (which we catch and handle cleanly).
Then I add the stuff! method to the class. Now I can run the say_stuff method (on the same instance as before!) and it works just fine.
This last example shows how defining a method does not run it, or require that it would even work when it is run. It is dynamically evaluated each time it is invoked (and only at that time).
I was experimenting with making a DSL and ran across something that confuses me. In my call method I wanted to set an initial value for #mymethod before evaulating the block. It works if I assign to the variable directly:
class Test
class << self
attr_accessor :mymethod
end
def self.call(&block)
#mymethod="foo"
class_eval &block
end
end
Test.call do
puts "mymethod returned: #{mymethod}"
mymethod = "bar"
puts "mymethod is now: #{mymethod}"
end
Which returns:
[1] pry(main)> load 'test.rb'
mymethod returned: foo
mymethod is now: bar
=> true
But I feel like this should work and it doesn't. The only thing that has changed is the # has been removed from the assignment to mymethod so I think it should be using the mymethod= method created by attr_accessor:
class Test
class << self
attr_accessor :mymethod
end
def self.call(&block)
mymethod="foo"
class_eval &block
end
end
Test.call do
puts "mymethod returned: #{mymethod}"
mymethod = "bar"
puts "mymethod is now: #{mymethod}"
end
However the assignment to mymethod from within call fails while the same assignment inside the block succeeds:
[1] pry(main)> load 'test.rb'
mymethod returned:
mymethod is now: bar
=> true
What's going on here? Can someone explain to my why the assignment would fail inside the call method?
in your case, mymethod="foo" will define mymethod local variable
rather than call mymethod= method.
use self.mymethod="foo" instead
For example:
class A
def A::f
end
end
What is A::f ?
I suppose it is neither class method nor instance method...
Using the double colons creates a class method. You can see this by doing a simple test:
class A
def A::f
puts "Called A::f"
end
end
puts "A.public_methods:"
puts A.public_methods(false)
puts
a = A.new
puts "a.public_methods:"
puts a.public_methods(false)
puts
puts "Attempt to call f"
A.f
a.f
Output:
A.public_methods:
f
allocate
new
superclass
a.public_methods:
Attempt to call f
Called A::f
NoMethodError: undefined method âfâ for #<A:0x0000010084f020>
http://marcricblog.blogspot.com/2007/11/ruby-double-colon.html
How would I use the parameter value as the instance variable name of an object?
This is the object
Class MyClass
def initialize(ex,ey)
#myvar = ex
#myothervar = ey
end
end
I have the following method
def test(element)
instanceofMyClass.element #this obviously doesnt work
end
How can I have the test method return either myvar or myothervar value depending on the element parameter. I don't want to write an if condition though, I want to pass myvar or myother var via element to the object instance if possible.
def test(element)
instanceofMyClass.send(element.to_sym)
end
You'll get a missing method error if instanceofMyClass doesn't respond to element.
def test(element)
instanceofmyclass.instance_variable_get element
end
test :#myvar # => ex
test :#myothervar # => ey
I like the simplicity of send(), though one bad thing with it is that it can be used to access privates. The issue is still remains solution below, but at least then it's explicitly specified, and reader can see which methods are to be forwarded. The first one just uses delegation, while the second one uses more dynamic way to define methods on the fly.
require 'forwardable'
class A
extend Forwardable
def_delegators :#myinstance, :foo, :bar
class B
def foo
puts 'foo called'
end
def bar
puts 'bar called'
end
def quux
puts 'quux called'
end
def bif
puts 'bif called'
end
end
def initialize
#myinstance = B.new
end
%i(quux bif).each do |meth| # note that only A#quux and A#bif are defined dynamically
define_method meth do |*args_but_we_do_not_have_any|
#myinstance.send(meth)
end
end
end
a = A.new
a.foo
a.bar
a.quux
a.bif