Here is an example:
class MyClass
end
obj = MyClass.new
obj.instance_eval do
def hello
"hello"
end
end
obj.hello
# => "hello"
obj.methods.grep "hello"
# => ["hello"]
MyClass.instance_methods.grep "hello"
# => []
MyClass's instance methods don't contain 'hello' method, so My question is where Ruby stores the method defined in instance_eval()?
Look at this:
obj = MyClass.new
def obj.hello
"hello"
end
obj.hello #=> "hello"
obj.singleton_methods #=> [:hello]
obj.methods.grep :hello #=> [:hello]
obj.instance_eval do
def hello2 ; end
end #
obj.singleton_methods #=> [:hello, :hello2]
As you can see instead of using instance_eval you can also define a method directly on an object. In both cases they end up in the object's singleton class (eigenclass), which can be accessed via obj.singleton_class in Ruby 1.9 and the class << self ; self; end idiom in Ruby 1.8.
Related
I wrote some code to learn about the class_eval method. My understanding is that Foo.class_eval will set self to "Foo" and evaluate the block below in that context, just as saying "class Foo" will do. However, the code below shows that setting a class variable using class_eval will assign the class variable at the toplevel: to "Object". Why does this happen?
class Foo
end
class Foo
puts self
# => Foo
##class_var = "hello"
end
puts Object.class_variables
# => []
Foo.class_eval do
puts self
# => Foo
##class_var = "hi"
# => warning: class variable access from toplevel
end
puts Object.class_variables
# => ##class_var
As pointed out in comments a single # would do what you want, now if you were running the code from inside another class ## would work but it would set a class variable for both Foo, and whatever class you running from:
class Foo
puts self
# => Foo
##class_var = "hello"
end
class Bar
def self.main
puts Object.class_variables
# => []
Foo.class_eval do
puts self
# => Foo
##class_var = "hi"
end
end
end
Bar.main
p [Bar.class_variables]
#[[:##class_var]]
p [Foo.class_variables]
#[[:##class_var]]
That has to do with proc or block scope, I was expecting it to set only ##class_var only on Bar class but it actually sets on both, weird.
Is there a concise way to limit method visibility within the module while including it? In other words, I'd like to limit polluting the class with helper methods only used in the included module.
module Bar
def call
hide_me
end
private
# make this method only callable within this module
def hide_me
'visible'
end
end
class Foo
include Bar
def unrelated_method
hide_me
end
end
# that's ok
Foo.new.call #=> 'visible'
# that's not
Foo.new.unrelated_method #=> 'visible'
I'm ok with calling it via Bar.instance_method(:hide_me).bind(self).call, I just don't want to worry about accessing or redefining a helper method from some module.
You can wrap a class into the module and use private methods within the class, like so:
module Bar
def call
BarClass.new.call
end
class BarClass
def call
hide_me
end
private
def hide_me
puts "invisible"
end
end
end
class Foo
include Bar
def call_bar
call
end
def this_method_fails
hide_me
end
end
One can do what you want by including the module to the class and then undefining the unwanted included methods.
First construct a module.
module M
def cat
puts "meow"
end
def dog
puts "woof"
end
def owl
puts "who?"
end
private
def frog
puts "ribbit"
end
def pig
puts "oink"
end
end
Confirm the methods now exist.
M.instance_methods(false)
#=> [:cat, :dog, :owl]
M.private_instance_methods(false)
#=> [:frog, :pig]
Create the class, including the module M.
class C
def froggie
frog
end
def cat
puts "meow-meow"
end
include M
end
Check the instance methods.
C.instance_methods & [:cat, :dog, :owl, :froggie]
#=> [:cat, :froggie, :dog, :owl]
C.private_instance_methods & [:frog, :pig]
#=> [:frog, :pig]
and confirm :cat is owned by C and not by M.
C.instance_method(:cat).owner
#=> C
Now use the method Module#undef_method to undefine the unwanted methods from the module.
class C
[:cat, :owl, :pig].each { |m|
undef_method(m) unless instance_method(m).owner == self }
end
The unless... clause is needed so that the instance method :cat defined in C is not undefined.
Confirm the methods were undefined.
C.instance_methods & [[:cat, :dog, :owl, :froggie]
#=> [:cat, :froggie, :dog]
C.private_instance_methods & [:frog, :pig]
#=> [:frog]
Execute the methods.
c = C.new
c.cat
#=> "meow-meow"
c.dog
#=> "woof"
c.froggie
#=> "ribbit"
class Test
class << self
attr_accessor :some
def set_some
puts self.inspect
some = 'some_data'
end
def get_some
puts self.inspect
some
end
end
end
Test.set_some => Test
puts Test.get_some.inspect => Test nil
Here above I could find self as Test itself but not returning the some_data as output.
But while I modified in following way it returns expected output
class Test
class << self
attr_accessor :some
def set_some
puts self.inspect
self.some = 'some_data'
end
def get_some
puts self.inspect
self.some
end
end
end
Test.set_some => Test
puts Test.get_some.inspect => Test some_data
What is the differences?
EDIT
Now in the first example if I set as get some method as
Test.some = 'new_data'
puts Test.some.inspect #=> new_data
Test.set_some
puts Test.get_some.inspect => new_data
Now it made me much more confused.
some = :foo makes ruby think it should create a new local variable with name some. If you want to call some=(), you have to use an explicit reciever - as in self.some = :foo. I once lost a bet on that... :-/
It's (local) variable in the first example
In the first example some is a local variable.
In the second one, some is a method of self. Why? Because attr_accessor :some is the same as:
def some= (val)
#some = val
end
def some
return #some
end
So, you have created the getter and setter methods for the instance variable #some (it's an instance variable of the object Test, as every class is also an object of class Class).
in the first method
def set_some
puts self.inspect
some = 'some_data'
end
some is a local variable.. its not the same as #some that is a instance variable (in this case a class instance variable) so the value disappears when the method ends.
if you want to call the setter method some or set #some to something then do this
#some = 'some_data'
or
self.some = 'some_data'
in the second method
def get_some
puts self.inspect
self.some
end
your calling the method some. which returns the instace variable #some.. and since at this point #some has no value.. returns nil..
Example 1 with no method override and no local variable
class Foo
def initialize
#foo = 'foo'
end
def print_foo
print #foo
print self.foo
print foo
end
end
#foo, self.foo, and foo will access instance variable #foo within the instance method:
Foo.new.print_foo #=> foofoofoo
Example 2 with method override
class Foo
def initialize
#foo = 'foo'
end
def foo
return 'bar'
end
def print_foo
print #foo
print self.foo
print foo
end
end
#foo will access the instance variable, but self.foo and foo will call the foo override method:
Foo.new.print_foo #=> foobarbar
Example 3 with method override and local variable
class Foo
def initialize
#foo = 'foo'
end
def foo
return 'bar'
end
def print_foo
foo = 'baz'
print #foo
print self.foo
print foo
end
end
#foo accesses instance variable, self.foo accesses override method, and foo accesses local variable:
Foo.new.print_foo #=> foobarbaz
Is there any difference if you define Foo with instance_eval: . . .
class Foo
def initialize(&block)
instance_eval(&block) if block_given?
end
end
. . . or with 'yield self':
class Foo
def initialize
yield self if block_given?
end
end
In either case you can do this:
x = Foo.new { def foo; 'foo'; end }
x.foo
So 'yield self' means that the block after Foo.new is always evaluated in the context of the Foo class.
Is this correct?
Your two pieces of code do very different things. By using instance_eval you're evaluating the block in the context of your object. This means that using def will define methods on that object. It also means that calling a method without a receiver inside the block will call it on your object.
When yielding self you're passing self as an argument to the block, but since your block doesn't take any arguments, it is simply ignored. So in this case yielding self does the same thing as yielding nothing. The def here behaves exactly like a def outside the block would, yielding self does not actually change what you define the method on. What you could do is:
class Foo
def initialize
yield self if block_given?
end
end
x = Foo.new {|obj| def obj.foo() 'foo' end}
x.foo
The difference to instance_eval being that you have to specify the receiver explicitly.
Edit to clarify:
In the version with yield, obj in the block will be the object that is yielded, which in this case is is the newly created Foo instance. While self will have the same value it had outside the block. With the instance_eval version self inside the block will be the newly created Foo instance.
They are different. yield(self) does not change the value of self inside the block, while instance_eval(&block) does.
class Foo
def with_yield
yield(self)
end
def with_instance_eval(&block)
instance_eval(&block)
end
end
f = Foo.new
f.with_yield do |arg|
p self
# => main
p arg
# => #<Foo:0x100124b10>
end
f.with_instance_eval do |arg|
p self
# => #<Foo:0x100124b10>
p arg
# => #<Foo:0x100124b10>
end
You just can drop the self keyword
class Foo
def initialize
yield if block_given?
end
end
Update from comments
Using yield there is a bit new to my taste, specially when used outside irb.
However there is a big and significant difference between instance_eval approach and yield approach, check this snippet:
class Foo
def initialize(&block)
instance_eval(&block) if block_given?
end
end
x = Foo.new { def foo; 'foo'; end }
#=> #<Foo:0xb800f6a0>
x.foo #=> "foo"
z = Foo.new #=> #<Foo:0xb800806c>
z.foo #=>NoMethodError: undefined method `foo' for #<Foo:0xb800806c>
Check this one as well:
class Foo2
def initialize
yield if block_given?
end
end
x = Foo2.new { def foo; 'foo'; end } #=> #<Foo:0xb7ff1bb4>
x.foo #=> private method `foo' called for #<Foo2:0xb8004930> (NoMethodError)
x.send :foo => "foo"
z = Foo.new #=> #<Foo:0xb800806c>
z.send :foo => "foo"
As you can see the difference is that the former one is adding a singleton method foo to the object being initialized, while the later is adding a private method to all instances of Object class.
In Ruby, is there a way to redefine a method of a particular instance of a class using a proc? For example:
class Foo
def bar()
return "hello"
end
end
x = Foo.new
y = Foo.new
(Something like):
y.method(:bar) = lambda { return "goodbye" }
x.bar
y.bar
Producing:
hello
goodbye
Thanks.
def define_singleton_method_by_proc(obj, name, block)
metaclass = class << obj; self; end
metaclass.send(:define_method, name, block)
end
p = proc { "foobar!" }
define_singleton_method_by_proc(y, :bar, p)
or, if you want to monkey-patch Object to make it easy
class Object
# note that this method is already defined in Ruby 1.9
def define_singleton_method(name, callable = nil, &block)
block ||= callable
metaclass = class << self; self; end
metaclass.send(:define_method, name, block)
end
end
p = proc { "foobar!" }
y.define_singleton_method(:bar, p)
#or
y.define_singleton_method(:bar) do
"foobar!"
end
or, if you want to define your proc inline, this may be more readable
class << y
define_method(:bar, proc { "foobar!" })
end
or,
class << y
define_method(:bar) { "foobar!" }
end
this is the most readable, but probably doesn't fit your needs
def y.bar
"goodbye"
end
This question is highly related
I'm not sure what version of Ruby this was added in (at least 1.8.7), but there seems to be an even simpler way of doing this:
str1 = "Hello"
str2 = "Goodbye"
def str1.to_spanish
"Hola"
end
puts str1 # => Hello
puts str1.to_spanish # => Hola
puts str2 # => Goodbye
puts str2.to_spanish # => Throws a NoMethodError
Learnt about this whilst reading the Ruby Koans (about_class_methods.rb lesson).
I'm still not entirely sure what the purpose of this is since it seems a bit dangerous to me.
You can use the syntax class <<object to get an object's "singleton class" (that's a special parent class belonging only to that object) and define methods only for that instance. For example:
str1 = "Hello"
str2 = "Foo"
class <<str1
def to_spanish
'Hola'
end
end
Now if you do str1.to_spanish, it will return "Hola", but str2.to_spanish will give you a NoMethodFound exception.