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:

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”)?

2006/7/8, Damaris F. [email protected]:

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”)?

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

Damaris F. 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 :()

Damaris F. wrote:

class LotteryTicket
final.picks.each do |note|
class LotteryDraw
method:
winners[buyer] << [ ticket, score ]
LotteryDraw.buy(‘JuanMi’,
puts winner + “won on " + tickets.length + " ticket(s)!”

  • @@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
42
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.