Nubish questions about syntax and gems

R

Ross Bamford

Hi folks.

I'm getting on really well with Ruby (I've not enjoyed programming this
much for ages :)) but I've a few (probably simple) questions I'm hoping
you might be able to help with.

1) Is this the right way to define a method with a dynamic name?

eval <<-EOC
def #{method_name}
'You called #{method_name}'
end
EOC

it seems to(?) work, but I wonder if theres a better way?

2) I'm struggling with boolean attributes, and question marks.
attr_accessor (etc) won't allow me to pass those names in (I guess because
they're illegal instance var names). I tried:

attr_accessor :is_green
alias is_green? is_green

which works, but again it strikes me theres probably a better way that
I've missed.

3) Does this

class MyClass
@field = "one"
end

create a class or instance variable? And from that, does

class MyClass
@@field = "one"
end

create a class variable on MyClass, Class, or something else? I've tried a
few experiments and am more confused than when I started :)

4) And finally, not exactly a Ruby question, but relevant here I hope?
With a RubyGem, is it possible to install additional files besides
binaries? I'd like to install some manpages along with libs and bin. I
considered misusing the C extension build, but I don't know if it'd work
and it seems a bit nasty...

Thanks in advance!
 
D

David A. Black

Hi --

Hi folks.

I'm getting on really well with Ruby (I've not enjoyed programming this much
for ages :)) but I've a few (probably simple) questions I'm hoping you might
be able to help with.

Let me zero in on one of your questions (I'm sure you'll get multiple
replies :)
3) Does this

class MyClass
@field = "one"
end

create a class or instance variable?

@var is always an instance variable; @@var is always a class variable.
Let me stick to the former.

There's a simple way to get a handle on instance variables:

At every moment during runtime, there is a current or default object
-- "self". Every time you see:

@var

you are seeing an instance variable that belongs to "self", the
current object. There's never any ambiguity.

"self", in turn, can be any object, including a Class object. So,
when in doubt, ask what "self" is:

class C
puts "In class definition body scope"
puts self # C (the class object)
@var = 1
puts @var # 1

def some_method
puts "In instance method scope"
puts self # an instance of C
puts @var # nil
end
end

c = C.new
c.some_method

As you'll see from the output, the fact that the object C (the Class
object) assigns to its instance variable @var does not affect the @var
of an *instance* of C (which is uninitialized and therefore nil).

You can think of class objects as wearing two hats: the "special case"
hat of being an object factory, and the "civilian" hat of just being
an object. It's in "civilian" mode that class objects possess
instance variables of their own, on the same footing as all other
objects.


David
 
R

Ross Bamford

There's a simple way to get a handle on instance variables:

At every moment during runtime, there is a current or default object
-- "self". Every time you see:

@var

you are seeing an instance variable that belongs to "self", the
current object. There's never any ambiguity.

"self", in turn, can be any object, including a Class object. So,
when in doubt, ask what "self" is:

class C
puts "In class definition body scope"
puts self # C (the class object)
@var = 1
puts @var # 1

def some_method
puts "In instance method scope"
puts self # an instance of C
puts @var # nil
end
end

c = C.new
c.some_method

As you'll see from the output, the fact that the object C (the Class
object) assigns to its instance variable @var does not affect the @var
of an *instance* of C (which is uninitialized and therefore nil).

You can think of class objects as wearing two hats: the "special case"
hat of being an object factory, and the "civilian" hat of just being
an object. It's in "civilian" mode that class objects possess
instance variables of their own, on the same footing as all other
objects.

David,

That's a good explanation, thanks for that :) So I guess it makes sense
that I can't access, say @var, in the class as @@var in instance methods.
I think I was adding extra confusion for myself with that assumption.

Theres a lot of scope in these nuances, my mind is starting to work
overtime ;)

Thanks again!
 
S

Sean O'Halpin

1) Is this the right way to define a method with a dynamic name?

eval <<-EOC
def #{method_name}
'You called #{method_name}'
end
EOC

it seems to(?) work, but I wonder if theres a better way?

You can also use define_method, but it has limitations:

class A
def hello
puts "hello"
end
end

a =3D A.new
a.hello

method_name =3D "hello"
A.class_eval {
define_method method_name do
puts "You called #{method_name}"
end
}

a.hello
-- OUTPUT --
hello
You called hello

You can't define methods that take blocks (as you can't pass a block
argument to a block) which means that you need to use your original method
(eval + string) if you want to dynamically create a method that takes
a block argument.

Also note the differences in scope between using "def ... end" and a
define_method + a closure.
2) I'm struggling with boolean attributes, and question marks.
attr_accessor (etc) won't allow me to pass those names in (I guess becaus= e
they're illegal instance var names). I tried:

attr_accessor :is_green
alias is_green? is_green

which works, but again it strikes me theres probably a better way that
I've missed.

I can't think of a standard way to do this that is quicker but you could
do this:

class Module
def predicate(*names)
names.each do |name|
attr_accessor name
alias_method "#{name}?", name
end
end
end

class A
predicate :is_green
end

a =3D A.new
a.is_green =3D true
p a.is_green?
-- OUTPUT --
true
3) Does this

class MyClass
@field =3D "one"
end

create a class or instance variable? And from that, does

class MyClass
@@field =3D "one"
end

create a class variable on MyClass, Class, or something else? I've tried = a
few experiments and am more confused than when I started :)

Just to add a comment to David's explanation.

class A
@foo =3D "A's foo"
end
p A.instance_eval { @foo }

A.instance_eval { @foo =3D "A's foo again" }

class A
puts @foo
end
-- OUTPUT --
A's foo
A's foo again

The thing to get your head around is that a class is an object in its own r=
ight
(i.e. it is an instance of class Class).

When you open a class (with say "class A"), all the subsequent statements
are executed in the context of that class instance object.

Regarding @@class_variables - they work a bit like globals within a
class hierarchy. For example:

class Model
@@models =3D []

def models
@@models
end
end

class Element < Model
@@models =3D ['Hello from Element']
end

class Schema < Model
@@models =3D ['Hello from Schema']
end

e =3D Element.new
p e.models
__END__
["Hello from Schema"]

In other words, they do not belong to a class as such but a class hierarchy=
 
D

daz

Ross said:
Hi folks.

I'm getting on really well with Ruby (I've not enjoyed programming this
much for ages :)) but I've a few (probably simple) questions I'm hoping
you might be able to help with.

1) Is this the right way to define a method with a dynamic name?

http://www.outerbody.com/ruby/ri.do?class=Module&method=define_method


2) I'm struggling with boolean attributes, and question marks.
attr_accessor (etc) won't allow me to pass those names in (I guess because
they're illegal instance var names). I tried:

attr_accessor :is_green
alias is_green? is_green

Suggest to stick with something like this:

class Roo
attr_accessor :colour
def initialize
@colour = :white
end

def is_green?
@colour == :green
end
end

r = Roo.new
p r.is_green? #-> false
r.colour = :green
p r.is_green? #-> true

3) Does this

Awesome answer from Dr. Black !
4) And finally, not exactly a Ruby question, but relevant here I hope?
With a RubyGem, [...]

Pass on 4 ;)
Hope someone else can help.


daz
 
R

Ross Bamford

Hi Sean,

1) Is this the right way to define a method with a dynamic name?

eval <<-EOC
def #{method_name}
'You called #{method_name}'
end
EOC

it seems to(?) work, but I wonder if theres a better way?

You can also use define_method, but it has limitations:

define_method method_name do
puts "You called #{method_name}"
end

Ahh, I should have known to check Module :) I'm still getting used to
methods coming from all angles.
You can't define methods that take blocks (as you can't pass a block
argument to a block) which means that you need to use your original
method
(eval + string) if you want to dynamically create a method that takes
a block argument.

Interesting, I didn't know that (about block args to blocks). I guess if I
wanted to pass a block in that way, I'd have to get a Proc for the
closure? I've seen code like:

some_method(proc { |s| s.do_something })

which seems a bit nasty to me.
Also note the differences in scope between using "def ... end" and a
define_method + a closure.

Okay, I'm with you there.
I can't think of a standard way to do this that is quicker but you could
do this:

class Module
def predicate(*names)
names.each do |name|
attr_accessor name
alias_method "#{name}?", name
end
end
end

class A
predicate :is_green
end

a = A.new
a.is_green = true
p a.is_green?
-- OUTPUT --
true

Nice one. Gotta get used to this 'effectively adding keywords' thing too.
I've gotten around the fact that class definitions can call methods, now I
need to get used to it being _my_ methods :)
Just to add a comment to David's explanation.

class A
@foo = "A's foo"
end
p A.instance_eval { @foo }

A.instance_eval { @foo = "A's foo again" }

class A
puts @foo
end
-- OUTPUT --
A's foo
A's foo again

The thing to get your head around is that a class is an object in its
own right
(i.e. it is an instance of class Class).

This is where I am going around in circles. It's an instance of Class, but
it also has a class, Class. So, Class.class == Class. Is that right ?
When you open a class (with say "class A"), all the subsequent statements
are executed in the context of that class instance object.

Yes, I kind of got that, but I wasn't thinking about it properly. I've
been playing around with bindings and instance_eval a bit, and the 'self =
current object' idea I have to say makes a lot of sense. I see now though,
that theres more depth here than I first thought when it comes to the
class definition.
Regarding @@class_variables - they work a bit like globals within a
class hierarchy. For example:

class Model
@@models = []

def models
@@models
end
end

class Element < Model
@@models = ['Hello from Element']
end

class Schema < Model
@@models = ['Hello from Schema']
end

e = Element.new
p e.models
__END__
["Hello from Schema"]

In other words, they do not belong to a class as such but a class
hierarchy

Hmm, I think I need to go back to the book on class variables. I can't
quite see how they fit in, especially with respect to singleton class and
stuff. Thanks for your explanation, though - it's just me :(

I'll probably be asking again after I confuse myself some more ;)

Cheers for your reply.
 
S

Sean O'Halpin


Hi Ross,
I guess if I
wanted to pass a block in that way, I'd have to get a Proc for the
closure? I've seen code like:

some_method(proc { |s| s.do_something })

which seems a bit nasty to me.

Yes - you'd have to pass it as a regular argument.
Nice one. Gotta get used to this 'effectively adding keywords' thing too.
I've gotten around the fact that class definitions can call methods, now = I
need to get used to it being _my_ methods :)

A class definition is executable code. The ability to define methods
that execute within the class definition is one of the most powerful
techniques available in Ruby.

[snip stuff about a class being an instance of Class]
This is where I am going around in circles. It's an instance of Class, bu= t
it also has a class, Class. So, Class.class =3D=3D Class. Is that right ?

Yes. If it's any consolation, it took me a fair bit of head scratching
before the 'aha' moment ;)
Hmm, I think I need to go back to the book on class variables. I can't
quite see how they fit in, especially with respect to singleton class and
stuff. Thanks for your explanation, though - it's just me :(

@@class_variables seem to be a bit of a hack to me (at least in 1.8.x).
The double '@' is deliberately ugly. They obey different scoping rules
to other variables in that they look for the nearest enclosing class
regardless of self.

This is a little test that (to me) shows how wacky the scoping rules
are for class variables:

class A
@@foo =3D "class variable foo"
@foo =3D "class instance variable foo"
def self.evaluate(txt)
instance_eval txt
end
def self.eval_block(&block)
instance_eval &block
end
end

begin
p A.instance_eval { @@foo }
rescue =3D> e
puts e
end

@@foo =3D "hi"

p A.instance_eval { [self, @@foo, @foo] }
p A.evaluate("[self, @@foo, @foo]")
p A.eval_block { [self, @@foo, @foo] }

class B
@@foo =3D "B's class variable foo"
@foo =3D "B instance foo"
def self.eval_block(&block)
instance_eval &block
end
end

p B.eval_block { [self, @@foo, @foo] }
__END__
uninitialized class variable @@foo in Object
[A, "hi", "class instance variable foo"]
[A, "class variable foo", "class instance variable foo"]
[A, "hi", "class instance variable foo"]
[B, "B's class variable foo", "B instance foo"]

I'd be grateful if anyone can explain this behaviour (i.e. why the
call to B.eval_block gets B's class variable but the call to A gets
the outer scope).

Regards,

Sean
 
R

Ross Bamford


Cheers :) I was missing quite a bit there...
Suggest to stick with something like this:

class Roo
attr_accessor :colour
def initialize
@colour = :white
end

def is_green?
@colour == :green
end
end

r = Roo.new
p r.is_green? #-> false
r.colour = :green
p r.is_green? #-> true

Definitely agree, I prefer to keep as much immutable as possible (too many
years of Java I guess :p). I just thought there might be a convenient way
to do setters, and keep the Rdoc [RW] or whatever identifiers correct at
the same time.
3) Does this

Awesome answer from Dr. Black !
4) And finally, not exactly a Ruby question, but relevant here I hope?
With a RubyGem, [...]

Pass on 4 ;)
Hope someone else can help.


daz

Thanks for your reply :)
 
R

Ross Bamford

Hi Ross,


Yes - you'd have to pass it as a regular argument.

I get the feeling that eval is the more general solution. I'll stick with
that.
A class definition is executable code. The ability to define methods
that execute within the class definition is one of the most powerful
techniques available in Ruby.

I'm starting to see that now. The flexibility has really shaken me up, I
must say :)
[snip stuff about a class being an instance of Class]
This is where I am going around in circles. It's an instance of Class,
but
it also has a class, Class. So, Class.class == Class. Is that right ?

Yes. If it's any consolation, it took me a fair bit of head scratching
before the 'aha' moment ;)

:) Don't worry, I've been falling around in the dark for a few weeks now -
it's only the past few days that I've started getting these moments of
clarity. It's probably no coincidence that I've developed a reflex where i
type 'irb' a lot ;)
@@class_variables seem to be a bit of a hack to me (at least in 1.8.x).
The double '@' is deliberately ugly. They obey different scoping rules
to other variables in that they look for the nearest enclosing class
regardless of self.

Have to agree, so far they seem pretty counter-intuitive to me.
This is a little test that (to me) shows how wacky the scoping rules
are for class variables:
I'd be grateful if anyone can explain this behaviour (i.e. why the
call to B.eval_block gets B's class variable but the call to A gets
the outer scope).

The scoping issues are beyond me right now, but it seems pretty bizarre
that outer scope is ignored there. I noticed that if you modify it thus:

class A
@@foo = "class variable foo"
@foo = "class instance variable foo"
def self.evaluate(txt)
instance_eval txt
end
def self.eval_block(&block)
instance_eval &block
end
end

begin
p A.instance_eval { @@foo }
rescue => e
puts e
end

@@foo = "hi"

p A.instance_eval { [self, @@foo, @foo] }
p A.evaluate("[self, @@foo, @foo]")
p A.eval_block { [self, @@foo, @foo] }

class B
@@foo = "B's class variable foo"
@foo = "B instance foo"
def self.eval_block(&block)
instance_eval &block
end
end

p B.eval_block { [self, @@foo, @foo] }

class C
@@foo = "C's class variable foo"
@foo = "C instance foo"
def self.eval_block(&block)
instance_eval &block
end
end

# N.B. Not C, just B again
p B.eval_block { [self, @@foo, @foo] }

__END__
uninitialized class variable @@foo in Object
[A, "hi", "class instance variable foo"]
[A, "class variable foo", "class instance variable foo"]
[A, "hi", "class instance variable foo"]
[B, "B's class variable foo", "B instance foo"]
[B, "C's class variable foo", "B instance foo"]

To me, that seems stranger still. It seems that it's getting the last
defined @@foo, no matter what?

Cheers,
 
D

David A. Black

Hi --

This is a little test that (to me) shows how wacky the scoping rules
are for class variables:

class A
@@foo = "class variable foo"
@foo = "class instance variable foo"
def self.evaluate(txt)
instance_eval txt
end
def self.eval_block(&block)
instance_eval &block
end
end

begin
p A.instance_eval { @@foo }
rescue => e
puts e
end

@@foo = "hi"

p A.instance_eval { [self, @@foo, @foo] }
p A.evaluate("[self, @@foo, @foo]")
p A.eval_block { [self, @@foo, @foo] }

class B
@@foo = "B's class variable foo"
@foo = "B instance foo"
def self.eval_block(&block)
instance_eval &block
end
end

p B.eval_block { [self, @@foo, @foo] }
__END__
uninitialized class variable @@foo in Object
[A, "hi", "class instance variable foo"]
[A, "class variable foo", "class instance variable foo"]
[A, "hi", "class instance variable foo"]
[B, "B's class variable foo", "B instance foo"]

I'd be grateful if anyone can explain this behaviour (i.e. why the
call to B.eval_block gets B's class variable but the call to A gets
the outer scope).

What you've got basically is:

class A
@@foo = "A's class variable foo"
end
@@foo = "Object's class variable foo"
class B
@@foo = "B's class variable foo"
end

Since B is a subclass of Object, setting B's @@foo also sets Object's
@@foo. The reason A's @@foo (when you see it via the instance eval)
does not change is that if you create a subclass's class variable
*first*, and then the superclass's (in this case, A and Object,
respectively), they are separate:

irb(main):002:0> class A; end; class B < A; @@foo = 1; end; class A;
@@foo = 2; puts @@foo; end
2
=> nil
irb(main):003:0> class B; puts @@foo; end
1


David
 
S

Sean O'Halpin

Since B is a subclass of Object, setting B's @@foo also sets Object's
@@foo.

Thanks David - that explains it. (Obvious of course now you point it out).
Makes me even less likely to use class variables! :)
They are far too fragile.

Regards,

Sean
 
R

Ross Bamford

What you've got basically is:

class A
@@foo = "A's class variable foo"
end
@@foo = "Object's class variable foo"
class B
@@foo = "B's class variable foo"
end

Since B is a subclass of Object, setting B's @@foo also sets Object's
@@foo. The reason A's @@foo (when you see it via the instance eval)
does not change is that if you create a subclass's class variable
*first*, and then the superclass's (in this case, A and Object,
respectively), they are separate:

irb(main):002:0> class A; end; class B < A; @@foo = 1; end; class A;
@@foo = 2; puts @@foo; end
2
=> nil
irb(main):003:0> class B; puts @@foo; end
1
David

That's pretty subtle. Definitely one for the notebook.

Cheers,
 
D

David A. Black

Hi --

That's pretty subtle. Definitely one for the notebook.

Keep in mind too that class variable behavior is due to change in 2.0
(and I guess 1.9 too). The new behavior is supposed to be more
strictly class-scoped, rather than hierarchy scoped.


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,774
Messages
2,569,598
Members
45,152
Latest member
LorettaGur
Top