Definition of methods: self

In Why's poignant guide to Ruby some examples appear where he defines a
class "class LotteryTicket", and then he defines a method "def
self.new_random", or "def LotteryDraw.buy(..)"; that is, before the name
of the method, the name of the class appears (the name or the keyword
"self"). However, in other methods in the other classes, the class name
is not set before the name of the method. Explanations? :smiley:

···

--
Posted via http://www.ruby-forum.com/.

Um...wait...Do I have to set the name of the class before the name of
the method when this method is a class method (except the "initialize")?

···

--
Posted via http://www.ruby-forum.com/.

Right, but no exception: initialize is an instance method. new is a
class method and after a new instance is allocated initialize is
called on that instance:

$ ruby -e 'set_trace_func lambda {|*a| p a}; Array.new'
["line", "-e", 1, nil, #<Binding:0x100f6938>, false]
["c-call", "-e", 1, :new, #<Binding:0x100f68f0>, Class]
["c-call", "-e", 1, :initialize, #<Binding:0x100f6698>, Array]
["c-return", "-e", 1, :initialize, #<Binding:0x100f6650>, Array]
["c-return", "-e", 1, :new, #<Binding:0x100f6518>, Class]

Note, there is also allocate (class method) that will create an empty
instance of the correct type. So new can be imagined as being
implemented like this:

class Class
  def new(*a,&b)
    o = allocate
    o.initialize(*a,&b)
    o
  end
end

Kind regards

robert

···

2006/7/8, Damaris Fuentes <dfl_maradentro@yahoo.es>:

Um...wait...Do I have to set the name of the class before the name of
the method when this method is a class method (except the "initialize")?

--
Have a look: Robert K. | Flickr

Damaris Fuentes wrote:

Um...wait...Do I have to set the name of the class before the name of the method when this method is a class method (except the "initialize")?

Yes. this is the class method declaration syntax. Within class body definition the default receiver is the class itself, so the snippets beneath are equivalent:

class A
     def A.meth
     end
end

class A
     def self.meth
     end
end

If there are more class methods in a class it is easier to switch to class instance:

class B
     class << self

         def meth1
         end

  # more methods
     end
end

or even:

class B
     instance_eval do

         def meth1
         end

  # more methods
     end
end

lopex

Ok, thanks for the explanations!
But...now that you mention the concatenator... I will comment another
problem in this entry. (I write all the code, ven though perhaps you
don't need it)

The problem here is that the re- definition ot a class (LotteryDraw)
doesn't seem to see the attributes of the first definition.

I have the class ot the lottery ticket:

···

***********************
class LotteryTicket
  NUMERIC_RANGE = 1..25
  attr_reader :picks

  def initialize( *picks )
    [...]
    @picks = picks
  end

  def score( final )
    count = 0
    final.picks.each do |note|
      count +=1 if picks.include? note
    end
    count
  end

end

*****************************
Now I have the lottery draw, where I store the buyers of the tickets:
**********************
class LotteryDraw
  @@tickets = {}
   def LotteryDraw.buy( customer, *tickets )
     unless @@tickets.has_key?( customer )
       @@tickets[customer] = []
     end
     @@tickets[customer] += tickets
   end
end
*******
And NOW, I REDEFINE THE CLASS with the concatenator, adding a new class
method:
********************
class << LotteryDraw
   def play
     final = LotteryTicket.new_random
     winners = {}
     @@tickets.each do |buyer, ticket_list|
       ticket_list.each do |ticket|
         score = ticket.score( final )
         next if score.zero?
         winners[buyer] ||= []
         winners[buyer] << [ ticket, score ]
       end
     end
     @@tickets.clear
     winners
   end
end
********

And now I execute the following:
************************
LotteryDraw.buy('JuanMi',
     LotteryTicket.new( 12, 6, 19 ),
     LotteryTicket.new( 5, 1, 3 ),
     LotteryTicket.new( 24, 6, 8 ))

LotteryDraw.buy('Da',
     LotteryTicket.new( 18, 4, 7 ),
     LotteryTicket.new( 21, 25, 3 ),
     LotteryTicket.new( 12, 17, 19 ))

LotteryDraw.play.each do |winner, tickets|
  puts winner + "won on " + tickets.length + " ticket(s)!"
    tickets.each do |ticket, score|
      puts "\t" + ticket.picks.join( ', ' ) + ": " + score
    end
end
************************************

And an exception arises:
in `play': uninitialized class variable @@tickets in Object.
This exception appears in the trace:
- LotteryDraw.play.each do |winner, tickets|,
- @@tickets.each do |buyer, ticket_list| (in LotteryDraw.play)

What is the problem? Thanks (and sorry if this is very long :()

--
Posted via http://www.ruby-forum.com/.

Damaris Fuentes wrote:

Ok, thanks for the explanations!
But...now that you mention the concatenator... I will comment another problem in this entry. (I write all the code, ven though perhaps you don't need it)

The problem here is that the re- definition ot a class (LotteryDraw) doesn't seem to see the attributes of the first definition.

I have the class ot the lottery ticket:
***********************
class LotteryTicket
  NUMERIC_RANGE = 1..25
  attr_reader :picks

  def initialize( *picks )
    [...]
    @picks = picks
  end

  def score( final )
    count = 0
    final.picks.each do |note|
      count +=1 if picks.include? note
    end
    count
  end

end

*****************************
Now I have the lottery draw, where I store the buyers of the tickets:
**********************
class LotteryDraw
  @@tickets = {}
   def LotteryDraw.buy( customer, *tickets )
     unless @@tickets.has_key?( customer )
       @@tickets[customer] =
     end
     @@tickets[customer] += tickets
   end
end
*******
And NOW, I REDEFINE THE CLASS with the concatenator, adding a new class method:
********************
class << LotteryDraw
   def play
     final = LotteryTicket.new_random
     winners = {}
     @@tickets.each do |buyer, ticket_list|
       ticket_list.each do |ticket|
         score = ticket.score( final )
         next if score.zero?
         winners[buyer] ||=
         winners[buyer] << [ ticket, score ]
       end
     end
     @@tickets.clear
     winners
   end
end
********

And now I execute the following:
************************
LotteryDraw.buy('JuanMi',
     LotteryTicket.new( 12, 6, 19 ),
     LotteryTicket.new( 5, 1, 3 ),
     LotteryTicket.new( 24, 6, 8 ))

LotteryDraw.buy('Da',
     LotteryTicket.new( 18, 4, 7 ),
     LotteryTicket.new( 21, 25, 3 ),
     LotteryTicket.new( 12, 17, 19 ))

LotteryDraw.play.each do |winner, tickets|
  puts winner + "won on " + tickets.length + " ticket(s)!"
    tickets.each do |ticket, score|
      puts "\t" + ticket.picks.join( ', ' ) + ": " + score
    end
end
************************************

And an exception arises:
in `play': uninitialized class variable @@tickets in Object.
This exception appears in the trace:
- LotteryDraw.play.each do |winner, tickets|,
- @@tickets.each do |buyer, ticket_list| (in LotteryDraw.play)

What is the problem? Thanks (and sorry if this is very long :()

Without looking too closely at your code I'm guessing that it's because your class methods are in the singleton class of LotteryDraw - and so lookups for class variables are done in *that* hierarchy and not in the one of LotteryDraw.

I generally recommend to not use class variables because they introduce all sorts of weirdnesses. I'd rather use a normal instance variable of the class instance. That's accessible for sure in the singleton method.

Kind regards

  robert

I agree with Robert - avoid @@class_variables wherever possible. The
problem is the way @@class_variables are looked up - they are shared
within a class hierarchy. Also, class << LotteryDraw does not
introduce a new class definition scope (even though you may think it
looks like it should).

The following examples demonstrate the way lookup for class variables
works in the context you're using:

class A
  @@a = 42
end

# add a class variable to Object - without this, the next puts @@a
would cause an error
@@a = 24
class << A
  puts @@a # this refers to the toplevel Object @@a, not A's
end

class A
  puts @@a # this does refer to A's @@a
end

p @@a

__END__
24
42
24

(By the way, if you had run ruby with -w you would have got the warning:
  class variable access from toplevel singleton method
)

Also, the order in which you define @@class_variables matters. Look at
this example:

@@a = 24 # add a class variable to Object
class A
  @@a = 42
  # because @@a already exists in the class hierarchy (A inherits from Object)
  # this assignment updates it
end

class << A
  puts @@a # there is only one @@a now
end

class A
  puts @@a
end

p @@a

__END__
42

My advice is steer clear of them!

Regards,

Sean

Yes, thank you all, boys. I had set the class<< LotteryDraw as a new
class definition. Now, I've set the class<<LotteryDrwa inside the
LotteryDraw itself and it already works. Thanks. And thanks for the
other comments.

···

--
Posted via http://www.ruby-forum.com/.