Ezra Zygmuntowicz
8/24/2007 12:46:00 AM
Hi~
On Aug 23, 2007, at 4:40 PM, Thomas Gantner wrote:
> Hi
>
> I stumbled upon a problem with 'instance_eval' (using ruby 1.8.5 on
> linux-x86_64). Simplified code example:
>
> irb:01> S = Struct.new(:a)
> => S
> irb:02> s = S.new(1)
> => #<struct S a=1>
> irb:03> s.a
> => 1
> irb:04> s.a = 2
> => 2
> irb:05> s
> => #<struct S a=2>
> irb:06> s.instance_eval { puts a }
> 2
> => nil
> irb:07> s.instance_eval { a = 3 }
> => 3
> irb:08> s
> => #<struct S a=2>
> irb:09> s.instance_eval { self.a = 3 }
> => 3
> irb:10> s
> => #<struct S a=3>
>
> My problem is in line 07: it seems that the 'a' in the block is
> treated as a
> local variable, therefore all the writer-accessor methods ('a=')
> are hidden
> within the block. I get the same result when instance_eval-ing the
> string "a = 3" instead of the block variant.
>
> Is there a way to avoid this behaviour without using 'self.a' or
> passing the
> struct as a parameter into the block (which quite obsoletes the
> meaning of
> instance_eval)?
>
> My real world use of this pattern is using a block with 'initialize
> ()':
>
> class C
> def initialize(&blk)
> @format = Struct.new( ... many, many elements with default
> values ...)
> @format.instance_eval(&blk)
> end
> end
>
> To explicitly override some default format-values I want to use
>
> c = C.new { elem8 = 8; elem17 = 'test'; ... }
>
> This won't work as explained above. I know I can use
>
> c = C.new {|format| format.elem8 = 8; format.elem17 = 'test'; ... }
>
> or
>
> c = C.new { self.elem8 = 8; self.elem17 = 'test'; ... }
>
> but both seem somewhat redundant and need too much typing (yeah I
> know I'm
> lazy ;-).
>
> Thanks for any suggestions.
>
> -Thomas
>
> --
> <sig. under construction>
No unfortunately you are stuck with the behavior you observed.
Whenever ruby sees 'a = b' it assumes it is a local variable
assignment. This happens at parse time so there is no way to affect
it at runtime. It's a tradeoff in order to allow local variables to
look like method calls and visca versca. So you have to prefix an
assignment with self. or another object, otherwise it is a local
variable assignment and you cannot work around it since it happens at
parse time.
Cheers-
-- Ezra Zygmuntowicz-- Founder & Ruby Hacker
-- ez@engineyard.com
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)