[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

class_eval and using a block instead of a String?

Mariano Kamp

12/19/2006 8:47:00 PM

Hi,

given the following contrived example I am wondering how to use a
block with class_eval instead of using the string like in the code
below?!

Any ideas?

Cheers,
Mariano

require 'test/unit'
include Test::Unit::Assertions

class Base
class << self
def enhance(*attributes)
class_eval do
attr_reader *attributes
end

initializer = "def initialize(#{attributes.join(", ")})"
attributes.each {|a| initializer << "@#{a}=#{a}\n"}
initializer << "end"

# ------
class_eval initializer # <-------
# ------
end
end
end
class Derived < Base; end

Derived.enhance(:a, :b, :c)
d = Derived.new("a", "b", "c")

assert_equal "a", d.a
assert_equal "b", d.b
assert_equal "c", d.c

3 Answers

Luke A. Kanies

12/19/2006 9:37:00 PM

0

On Dec 19, 2006, at 2:47 PM, Mariano Kamp wrote:

> Hi,
>
> given the following contrived example I am wondering how to use a
> block with class_eval instead of using the string like in the code
> below?!
>
> Any ideas?
>
> Cheers,
> Mariano
>
> require 'test/unit'
> include Test::Unit::Assertions
>
> class Base
> class << self
> def enhance(*attributes)
> class_eval do
> attr_reader *attributes
> end
>
> initializer = "def initialize(#{attributes.join(", ")})"
> attributes.each {|a| initializer << "@#{a}=#{a}\n"}
> initializer << "end"
>
> # ------
> class_eval initializer # <-------
> # ------
> end
> end
> end
> class Derived < Base; end
>
> Derived.enhance(:a, :b, :c)
> d = Derived.new("a", "b", "c")
>
> assert_equal "a", d.a
> assert_equal "b", d.b
> assert_equal "c", d.c

Something like thie following should work:

require 'test/unit'
include Test::Unit::Assertions

class Base
class << self
def enhance(*attributes)
attr_accessor *attributes

define_method(:initialize) do |*ary|
attributes.each do |a|
send(a.to_s + "=", ary.shift)
end
end
end
end
end
class Derived < Base; end

class Testing < Test::Unit::TestCase
def test_initialize
Derived.enhance(:a, :b, :c)
d = Derived.new("a", "b", "c")

assert_equal "a", d.a
assert_equal "b", d.b
assert_equal "c", d.c
end
end

I didn't really do what you want, because I'm not actually using
class_eval here at all, but the assertions pass, anyway, which I
assume is the real goal.

Note that I'm using attr_accessor here instead of attr_reader,
because it's much easier to set the attributes this way. You could
just about as easily use instance_variable_set or whatever the method
is, but I prefer this for my own code.

Note also that if you call enhance() multiple times on the same
class, you'll recreate the initialize() method each time, which is
probaby a bad thing; as long as this is just for testing, you should
be fine, but I'd be wary of putting this into production anywhere.

One of the things I love about ruby is I can usually use a block
instead of a string with the eval methods, but what I love even more
is being able to skip the eval methods entirely.

--
Some people are afraid of heights. I'm afraid of widths.
-- Stephen Wright
---------------------------------------------------------------------
Luke Kanies | http://reducti... | http://m...



Mariano Kamp

12/20/2006 11:27:00 AM

0

On Dec 19, 2006, at 10:36 PM, Luke Kanies wrote:
> Something like thie following should work:
>
> require 'test/unit'
> include Test::Unit::Assertions
>
> class Base
> class << self
> def enhance(*attributes)
> attr_accessor *attributes
>
> define_method(:initialize) do |*ary|
> attributes.each do |a|
> send(a.to_s + "=", ary.shift)
> end
> end
> end
> end
> end
> class Derived < Base; end
>
> class Testing < Test::Unit::TestCase
> def test_initialize
> Derived.enhance(:a, :b, :c)
> d = Derived.new("a", "b", "c")
>
> assert_equal "a", d.a
> assert_equal "b", d.b
> assert_equal "c", d.c
> end
> end
>
> I didn't really do what you want, because I'm not actually using
> class_eval here at all, but the assertions pass, anyway, which I
> assume is the real goal.
>
> Note that I'm using attr_accessor here instead of attr_reader,
> because it's much easier to set the attributes this way. You could
> just about as easily use instance_variable_set or whatever the
> method is, but I prefer this for my own code.
>
> Note also that if you call enhance() multiple times on the same
> class, you'll recreate the initialize() method each time, which is
> probaby a bad thing; as long as this is just for testing, you
> should be fine, but I'd be wary of putting this into production
> anywhere.
>
> One of the things I love about ruby is I can usually use a block
> instead of a string with the eval methods, but what I love even
> more is being able to skip the eval methods entirely.

Hi Luke,

thanks. That should work for my example.

Well, generally speaking I love the idea that you can transform
one concept into another and if this works nicely it most of the time
looks elegant to me. So I was hoping that I just didn't get how to do
that with code in strings as opposed to blocks.

So it seems, that there are things that you can do with one of the
concepts but not with the other.

I am currently trying to figure out what the hard rules are on
when to use a String and when to use a block.

I like the String approach as it is pretty close to how you would
write the method by hand, but I hate about it that it is just heap of
characters. But the latter might be just me as my thoughts are often
still in the Java world, where you like to do things explicitly and
give the compiler/editor a chance to help you.

Coming back to the example it seems that the two approaches do not
provide the same functionality. When using the String approach the
parameters to the initialize method are explicit. On consequence is
that if I would call Derived.new ("a", "b") I would get an error
saying that I just provided two of three parameters. This doesn't
happen with the block approach. Of course I could always explicitly
code to check for the ary length, but this is kind of redundant.


Cheers,
Mariano

dblack

12/20/2006 1:46:00 PM

0