Nubish questions about syntax and gems

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 :slight_smile:

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!

···

--
Ross Bamford - rosco@roscopeco.remove.co.uk

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 :slight_smile:

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

···

On Sat, 26 Nov 2005, Ross Bamford wrote:

--
David A. Black
dblack@wobblini.net

I hope can answer some of your questions:

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 = A.new
a.hello

method_name = "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 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.

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

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 :slight_smile:

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).

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 =

  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.
(Note how setting @@models in Schema has changed it for Element).

Personally, I avoid using @@class_variables and use class instance variables
(with accessors) instead - much less chance of accidentally stomping on
another class's state.

Regards,

Sean

···

On 11/26/05, Ross Bamford <rosco@roscopeco.remove.co.uk> wrote:

Ross Bamford wrote:

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 :wink:
Hope someone else can help.

daz

David,

That's a good explanation, thanks for that :slight_smile: 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 :wink:

Thanks again!

···

On Sat, 26 Nov 2005 15:24:36 -0000, David A. Black <dblack@wobblini.net> wrote:

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.

--
Ross Bamford - rosco@roscopeco.remove.co.uk

Hi Sean,

I hope can answer some of your questions:

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 :slight_smile: 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.

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.

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 :slight_smile:

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 :frowning:

I'll probably be asking again after I confuse myself some more :wink:

Cheers for your reply.

···

On Sat, 26 Nov 2005 16:08:19 -0000, Sean O'Halpin <sean.ohalpin@gmail.com> wrote:

On 11/26/05, Ross Bamford <rosco@roscopeco.remove.co.uk> wrote:

--
Ross Bamford - rosco@roscopeco.remove.co.uk

Ross Bamford wrote:

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

Cheers :slight_smile: I was missing quite a bit there...

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

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 :wink:
Hope someone else can help.

daz

Thanks for your reply :slight_smile:

···

On Sat, 26 Nov 2005 16:14:24 -0000, daz <dooby@d10.karoo.co.uk> wrote:

--
Ross Bamford - rosco@roscopeco.remove.co.uk

Hi Sean,

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 :slight_smile:

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, 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 :wink:

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 :frowning:

@@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 = "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).

Regards,

Sean

···

On 11/26/05, Ross Bamford <rosco@roscopeco.remove.co.uk> wrote:

Hi Sean,

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.

I get the feeling that eval is the more general solution. I'll stick with that.

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 :slight_smile:

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 :slight_smile:

[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 :wink:

:slight_smile: 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 :wink:

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 :frowning:

@@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,

···

On Sat, 26 Nov 2005 19:58:57 -0000, Sean O'Halpin <sean.ohalpin@gmail.com> wrote:

On 11/26/05, Ross Bamford <rosco@roscopeco.remove.co.uk> wrote:

--
Ross Bamford - rosco@roscopeco.remove.co.uk

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

···

On Sun, 27 Nov 2005, Sean O'Halpin wrote:

--
David A. Black
dblack@wobblini.net

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

Regards,

Sean

···

On 11/26/05, David A. Black <dblack@wobblini.net> wrote:

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

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

Cheers,

···

On Sat, 26 Nov 2005 21:44:00 -0000, David A. Black <dblack@wobblini.net> wrote:

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

--
Ross Bamford - rosco@roscopeco.remove.co.uk

Hi --

···

On Sun, 27 Nov 2005, Ross Bamford wrote:

On Sat, 26 Nov 2005 21:44:00 -0000, David A. Black <dblack@wobblini.net> > wrote:

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.

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

--
David A. Black
dblack@wobblini.net