Problems creating a method to define a class with its method

B

Benny

Dear list,

I want to create a method that creates certain kinds of classes for me.
The following doesn't work:

### start pseudo code
module My_Module
class Bigclass
attr_accessor :test
def Bigclass.run(para)
@test = para
puts "your parameter is " + @test
end
def Bigclass.check
puts "I'm here"
end
end

def My_Module.subclass(name, &code)
eval <<-EOS
class #{name} < Bigclass
def #{name}.run(p)
super(p)
#{code.call}
end
end
EOS
end
end

My_Module::subclass("Newclass"){
puts "doesn't work" + @test.to_s
check()
}

My_Module::Newclass.run("my parameter")
### end pseudo code

the problem is that the #{code.call} is executed before
the class is defined, so @test isn't filled yet (super(p)
isn't executed at this time.

how can I change the code, so that »code« is part of the definition
of the class (i.e. part of its method »run«)?
(the code can be very long, so putting it in a string is
not very comfortable regarding quoting etc. (I also want
the code to be highlighted in an editor).

benny
 
M

Mauricio Fernández

def My_Module.subclass(name, &code)
eval <<-EOS
class #{name} < Bigclass
def #{name}.run(p)
super(p)
#{code.call}
end
end
EOS
end
end [...]
the problem is that the #{code.call} is executed before
the class is defined, so @test isn't filled yet (super(p)
isn't executed at this time.

how can I change the code, so that »code« is part of the definition
of the class (i.e. part of its method »run«)

batsman@tux-chan:/tmp$ cat ghfdgh54yiyh.rb
module My_Module
class Bigclass
attr_accessor :test
def Bigclass.run(para)
@test = para
puts "your parameter is " + @test
end
def Bigclass.check
puts "I'm here"
end
end

def My_Module.subclass(name, &code)
k = Class.new(Bigclass)
class << k; self end.send:)define_method, "run") do |p|
super(p)
instance_eval(&code)
end
My_Module.const_set(name, k)
k
end
end

My_Module.subclass("Newclass") do
puts "does work work now: " + @test.to_s
check
end

My_Module::Newclass.run "blah"
batsman@tux-chan:/tmp$ ruby ghfdgh54yiyh.rb
your parameter is blah
does work work now: blah
I'm here

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

"You, sir, are nothing but a pathetically lame salesdroid!
I fart in your general direction!"
-- Randseed on #Linux
 
B

Benny

Mauricio said:
your parameter is blah
does work work now: blah
I'm here
whow!! thank you. its seems to be a bit more complicated
than I thought. I would never have found the solution on my own!!

now I know for what reason we've got "send", "define_method",
"instance_eval" and "const_set". can you explain me why "eval" was a
thought in the wrong direction?

(btw: the construct with k = Class.new... const_set(name, k) seems less
elegant than eval("class #{name} < Bigclass..") at first sight)

benny
 
M

Mauricio Fernández

whow!! thank you. its seems to be a bit more complicated
than I thought. I would never have found the solution on my own!!

now I know for what reason we've got "send", "define_method",
"instance_eval" and "const_set". can you explain me why "eval" was a
thought in the wrong direction?

Your code was

def My_Module.subclass(name, &code)
eval <<-EOS
class #{name} < Bigclass
def #{name}.run(p)
super(p)
#{code.call}
end
end
EOS
end

the basic problem is that you're opening a new scope when you do class X;
... end or def foo; ... end, so you cannot access the 'code' variable
directly; if it were possible to turn it into a literal (that is, if
it were a number, string, array, etc) you could use #{code.inspect}
to interpolate the value, but it's not possible in this case since it's
a Proc. In order to be able to access code from the method definition,
you have to use a closure, i.e. define_method.
(btw: the construct with k = Class.new... const_set(name, k) seems less
elegant than eval("class #{name} < Bigclass..") at first sight)

I tend to avoid eval <<-EOF for a number of reasons:
* syntax errors aren't caught at load time
* the editor doesn't indent/highlight the code
* (when defining classes/methods with class/def) no closures
* interpolating values in the code feels cheap (and will only work for
some classes)
* it's eval :) using it is a bit like recognizing the language wasn't
powerful enough to express what I wanted 'statically'

To me, eval'ing a text with lots of #{whatever} seems less elegant, for
the reasons stated above. I prefer one of the block forms (instance_eval{}
or module_eval{ }) or define_method when it makes sense.

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Why are there always boycotts? Shouldn't there be girlcotts too?
-- argon on #Linux
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top