Instance_eval does not work with do/end block, only with {}-blocks
It's because when you pass block with curly braces, it is passed to instance_eval
method. But if you pass it with do-end
, it's passed to puts
method, so instance_eval
doesn't get block and raises an error.
Instance_eval does not work with do/end block, only with {}-blocks
It's because when you pass block with curly braces, it is passed to instance_eval
method. But if you pass it with do-end
, it's passed to puts
method, so instance_eval
doesn't get block and raises an error.
Ruby Block Scope with instance_eval
When you define @value
in the lexical scope (your main source file), you're defining an instance variable in the global interpreter. For example:
self #=> main
# "self" here refers to the main interpreter, which is of class Object
self.instance_variable_get(:@value) #=> 1
# "example" is your instance above
example.instance_variable_get(:@value) #=> "a"
# "self" has been changed to refer to "example" using instance_eval
example.instance_eval { self.instance_variable_get(:@value) } #=> "a"
# this syntax is just a shortcut for the line above
example.instance_eval { @value } #=> "a"
With instance_eval
, all you're doing is replacing the main interpreter self
with the object you've called instance_eval
on.
instance_eval's block argument(s)- documented? purpose?
Reading Ruby's source code, what I can interpret is:
instance_eval is executing this:
return specific_eval(argc, argv, klass, self)
which in turn runs:
if (rb_block_given_p()) {
if (argc > 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
}
return yield_under(klass, self, Qundef);
}
You can see they pass Qundef
for the VALUES argument.
if (values == Qundef) {
return vm_yield_with_cref(th, 1, &self, cref);
}
In that particular line of code, they set manually argc (argument count) to 1 and the argument as "self". Later on the code that prepares the block sets the arguments to the block to these arguments, hence the first argument = "self" and the rest are nil.
The code that sets up the block arguments is doing :
arg0 = argv[0];
... bunch of code ...
else {
argv[0] = arg0;
}
for (i=argc; i<m; i++) {
argv[i] = Qnil;
}
Resulting in:
1.9.3p194 :006 > instance_eval do |x, y, z, a, b, c, d| x.class end
=> Object
1.9.3p194 :008 > instance_eval do |x, y, z, a, b, c, d| y.class end
=> NilClass
Why ? I have no idea but the code seems to be intentional. Would be nice to ask the question to the implementers and see what they have to say about it.
[Edit]
This probably is like that because the blocks you pass to instance_eval may or may not be crafted for it (code that depends on self being set to the class you want the block to modify), instead they may assume you are going to pass them the instance you want them to modify as an argument and in this way they would work with instance_eval as well.
irb(main):001:0> blk = Proc.new do |x| x.class end
#<Proc:0x007fd2018447b8@(irb):1>
irb(main):002:0> blk.call
NilClass
irb(main):003:0> instance_eval &blk
Object
Of course this is only a theory and without official documentation I can only guess.
rspec `its` syntax with dynamic conditions
You are almost answering yourself, use a let
assignment before you set the subject. Then you can reference it everywhere:
context "as a user" do
let(:user) { Factory(:user) }
subject { user }
its(:name) { should == user.contact.name }
end
I'm a context
, subject
, its
lover too !
How to change self in a block like instance_eval method do?
You can write a method that accepts a proc argument, and then pass that as a proc argument to instance_eval.
class Foo
def bar(&b)
# Do something here first.
instance_eval &b
# Do something else here afterward, call it again, etc.
end
end
Foo.new.bar { puts self }
Yields
#<Foo:0x100329f00>
Ruby object initialization using instance_eval
Ruby setters cannot be called without an explicit receiver since local variables take a precedence over method calls.
You don’t need to experiment with such an overcomplicated example, the below won’t work as well:
class Person
attr_accessor :name
def set_name(new_name)
name = new_name
end
end
only this will:
class Person
attr_accessor :name
def set_name(new_name)
# name = new_name does not call `#name=`
self.name = new_name
end
end
For your example, you must explicitly call the method on a receiver:
person = Person.new do
self.first_name = "Adam"
end
Passing around procs between different objects
You are looking for:
instance_eval(&data)
object.instance_eval
evaluates block, but replaces self
within that block (which would normally be self
of the context block was created in) with object
:
whoami = proc { self }
whoami.call => main
1.instance_eval(&whoami) => 1
Note however, that instance_eval
also passes an object as an argument to the block, which might be problematic if you want to pass a lambda:
whoami = lambda { self }
1.instance_eval(&whoami) #=> ArgumentError (wrong number of arguments (given 1, expected 0))
There is another similar method: instance_exec
. Not only it does not pass self as an argument:
whoami = lambda { self }
1.instance_exec(&whoami) #=> 1
but it additionally allows you to pass other arguments:
add_number = lambda { |number| self + number }
1.instance_exec(3, &add_number) #=> 4
Naturally, both methods need to be used with extra caution and very sparingly - they are nice when you want to declare class-wide hooks in a declarative manner to be executed on each instance. They should not be used as a mean of interaction between two objects, unless you really know what you are ding and can justify it does not validate encapsulation.
Related Topics
How to Convert Character Code to What I Want
Errno::Enoent No Such File or Directory Rails 4
Return Values Were Not Extracted in Ruby
Ruby: Length of a Line of a File in Bytes
Use [].Replace to Make a Copy of an Array
"Bad Ecpoint" Ssl Error on Fresh Rvm Ruby 1.9.3 Install on Osx Mountain Lion
Shading Mask Algorithm for Radiation Calculations
How to Install Ruby System-Wide Using Rbenv
How to Count Duplicates Hash Itens in Ruby 1.8.5 ( Sketchup Ruby API )
How to Iterate Through This JSON Document Using Ruby
How to Deal with a Page Which Fails to Load and Continue Testing in Watir-Webdriver
Run Ruby Script That Is Stored on Internet
Ruby Gem Cucumber Ssl Error and Gem Sources
Flatten a Ruby Array Without Using Built-In 'Flatten' Method