Class inside a Method Body

M

Mike Stephens

I have a class that works fine if I declare it outside of anything. If
however I put the code inside a method, Ruby seems to object with a
message "class definition in method body". Can you suggest why Ruby has
this constraint?
 
B

Brian Candler

The class wouldn't be defined until the method is executed, and would be
re-opened each time the method is executed.

That's almost certainly not what you want (*). If you want a private
helper class, I suggest you do it like this:

class MyClass
class MyHelper
...
end
def my_method
MyHelper.new(...)
end
end

Regards,

Brian.

(*) And if you really do, then you can use:

k = Class.new
or
k = Class.new(superclass)
or use 'eval' to define your class.
 
M

Mike Stephens

Brian

Thanks for confirming it is a language feature. I was actually using the
method as a kind of procedure - I just wanted this bit of the whole
program to run. In that kind of procedural view of the world it does
make absolute sense for a class to be declared whenever the procedure
is called.

I think this is part of a difficulty I have with Ruby - it seems to want
to interpret everything in a program, and all the programs called
(required), before it starts executing anything.
 
D

David A. Black

Hi --

Brian

Thanks for confirming it is a language feature. I was actually using the
method as a kind of procedure - I just wanted this bit of the whole
program to run. In that kind of procedural view of the world it does
make absolute sense for a class to be declared whenever the procedure
is called.

I think this is part of a difficulty I have with Ruby - it seems to want
to interpret everything in a program, and all the programs called
(required), before it starts executing anything.

It's more like execution and interpretation are, in effect the same
process -- so, for example:

class C
puts "hi!"
def x
puts "x"
end
puts "bye!"
end

the code is executed as it's encountered, and you get output before
and after the execution of the method definition. (Of course the
method body isn't executed until there's an object to which to send
the "x" method.)


David
 
B

Brian Candler

David said:
the code is executed as it's encountered, and you get output before
and after the execution of the method definition. (Of course the
method body isn't executed until there's an object to which to send
the "x" method.)

Except: when reading in a source file there is an initial parsing step
to build the syntax tree, and if that fails then you get a parse error
and nothing is executed. Mismatched 'end' statements will do that, for
example.

The difference between parsing and execution is most important when
considering local variables.

def x
puts "In method x"
99
end

def meth1
x # << (A)
end

def meth2
x = 123
x # << (B)
end

def meth3
if false
x = 456
end
x # << (C)
end

puts "meth1 returns #{meth1.inspect}"
puts "meth2 returns #{meth2.inspect}"
puts "meth3 returns #{meth3.inspect}"

The decision as to whether x is a local variable or a method call is
made statically at *parse* time, before anything is executed. This is
why at point (C), x is determined to a local variable: there was an
assignment to x earlier in the method. However at run time that line is
never actually run so its value is nil.
 
R

Robert Klemme

2009/11/17 Mike Stephens said:
Thanks for confirming it is a language feature. I was actually using the
method as a kind of procedure - I just wanted this bit of the whole
program to run. In that kind of procedural view of the world it does
make absolute sense for a class to be =A0declared whenever the procedure
is called.

Can you describe under which circumstances you believe this makes
sense? After all since the class definition would not change you
would get the same class definition over and over again. To me that
does not sound useful.

If you want to make sure the class is only created when the method is
executed, you could put it in a separate file which you require in
that method. Alternatively you can use autoload.
I think this is part of a difficulty I have with Ruby - it seems to want
to interpret everything in a program, and all the programs called
(required), before it starts executing anything.

If you have a difficulty here I can see two possible explanations: 1.
there is an issue with Ruby's object model, 2. you are not using the
language properly. While I don't want to exclude option 1, I tend to
believe option 2 is more likely. :)

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
M

Mike Stephens

My concern here is when you use libraries. When your user just wants to
get a list of product categories from the web server, you seem to risk
Ruby parsing the whole portfolio of library capabilities despite the
fact you emphatically won't use 99% of them on that transaction.

Autoload does look as though it addresses this.

The other solution you mentioned was you put require inside a method and
then Ruby won't execute it until control passes to the method, so if you
don't need that method, you don't incur the cost of interpreting it.
Presumably you can't add classes this way and, from a glance at The Ruby
Programming Language (Flanagan & Matsumoto), there is a risk the
included code gets treated as belonging to a different scope to the
calling method (although that might be a plus point).
 
M

Marnen Laibow-Koser

Mike said:
My concern here is when you use libraries. When your user just wants to
get a list of product categories from the web server, you seem to risk
Ruby parsing the whole portfolio of library capabilities despite the
fact you emphatically won't use 99% of them on that transaction.

Not at all. This is what require is for. If you don't require a file,
it won't be read.
Autoload does look as though it addresses this.

Autoload just automates require AFAIK.
The other solution you mentioned was you put require inside a method and
then Ruby won't execute it until control passes to the method, so if you
don't need that method, you don't incur the cost of interpreting it.
Presumably you can't add classes this way

Of course you can! Why do you think you can't?
and, from a glance at The Ruby
Programming Language (Flanagan & Matsumoto), there is a risk the
included code gets treated as belonging to a different scope to the
calling method (although that might be a plus point).

If you wrap it in a class or module, you get to specify the scope.


Best,
 
R

Robert Klemme

Not at all. This is what require is for. If you don't require a file,
it won't be read.

Mike's point is, that if you require a library which requires other
files in turn you might end up loading 50 files although you just need 3
of them. That overhead can be significant for short lived programs.

He has a valid point: basically his question is, how do I declare file
dependencies in complex libraries without immediately "executing" them
(means: reading all those files)? A good approach here is to use
autoload alone or a combination of autoload and require as I have done
in the Muppet Laboratories project (see link below).
Autoload just automates require AFAIK.

Yes, but this is crucial here: autoload will require a file once a
constant is accessed the first time. And you can even nest it, i.e. use
it inside a module. That way, if a class is never used at all, you
won't read that file.

You can find an example in my project:
http://github.com/rklemme/muppet-laboratories/blob/master/lib/animal.rb
Of course you can! Why do you think you can't?

Exactly. The required file can contain anything - from zero to multiple
classes.
If you wrap it in a class or module, you get to specify the scope.

Hmm... I am not aware of any scoping issues right now but you could
avoid them by anchoring the class name:

class ::Foo
end

That class is always defined in the root namespace.

Kind regards

robert
 
M

Marnen Laibow-Koser

Robert said:
Mike's point is, that if you require a library which requires other
files in turn you might end up loading 50 files although you just need 3
of them. That overhead can be significant for short lived programs.
True.


He has a valid point: basically his question is, how do I declare file
dependencies in complex libraries without immediately "executing" them
(means: reading all those files)? A good approach here is to use
autoload alone or a combination of autoload and require as I have done
in the Muppet Laboratories project (see link below).


Yes, but this is crucial here: autoload will require a file once a
constant is accessed the first time. And you can even nest it, i.e. use
it inside a module. That way, if a class is never used at all, you
won't read that file.

With a better explanation of the library example, now I understand what
the OP was getting at. You're right.
You can find an example in my project:
http://github.com/rklemme/muppet-laboratories/blob/master/lib/animal.rb


Exactly. The required file can contain anything - from zero to multiple
classes.


Hmm... I am not aware of any scoping issues right now but you could
avoid them by anchoring the class name:

class ::Foo
end

That class is always defined in the root namespace.

Indeed. I'd be surprised if this were actually necessary, though.
Kind regards

robert


Best,
 
M

Mike Stephens

Of course you can! Why do you think you can't?

Exactly. The required file can contain anything - from zero to multiple
classes.

If I put a class inside a method I get an error, so I assumed if I
insert code with the word 'class' in it, the same would happen.
 
M

Marnen Laibow-Koser

Mike said:
If I put a class inside a method I get an error, so I assumed if I
insert code with the word 'class' in it, the same would happen.

In that light, it's a reasonable assumption, but I don't think it's
right. require is not *exactly* the same as literal inclusion. Have
you had a chance to try it yet?


Best,
 
P

pharrington

If I put a class inside a method I get an error, so I assumed if I
insert code with the word 'class' in it, the same would happen.


Because

def asdf; class Edgar; end; end

is a syntax error is all; of course nothing in Ruby is stopping you
from creating a new class within a method call. If you really want or
need to is a different question entirely.
 
G

Gary Wright

In that light, it's a reasonable assumption, but I don't think it's
right. require is not *exactly* the same as literal inclusion. Have
you had a chance to try it yet?

In fact it is very different from textual inclusion and it will only
serve to confuse if you try to draw analogies to textual inclusion.

Gary Wright
 
D

David A. Black

Except: when reading in a source file there is an initial parsing step
to build the syntax tree, and if that fails then you get a parse error
and nothing is executed. Mismatched 'end' statements will do that, for
example.

I hadn't gotten into parsing just to keep it simple -- but in fact, it
does appear to be a parse-time/syntax error, rather than a runtime
error, which I hadn't noticed.


David
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top