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