Newbie Question

Hi,

I am new to Ruby. This is the very first class I am writing and it
doesn’t seem that Ruby is designed to handle what I thought should work.
Why doesn’t the below work?

class Song < ActiveRecord::Base
@@dcounts = []

def Song.count(difficulty, artistid)
@@dcounts[difficulty][artistid]
end

def Song.count=(difficulty, artistid, val)
@@dcounts[difficulty] ||= {}
@@dcounts[difficulty][artistid] = val
end
end

Song.count(1, 3547) = 10

test.rb:14: parse error, unexpected ‘=’, expecting $
Song.count(1,3547) = 10
^

On 3/1/07, Dick [email protected] wrote:

@@dcounts[difficulty][artistid]

test.rb:14: parse error, unexpected ‘=’, expecting $
Song.count(1,3547) = 10

You can’t have more then one argument to setter (var=)

change it to

def Song.set_count(difficulty, artistid, val)
@@dcounts[difficulty] ||= {}
@@dcounts[difficulty][artistid] = val
end

Song.set_count(1, 3547,10)

Then, maybe you can create classes for each of the subhashes to
encapsulate the functionality - but it depends on the purpose and the
data.

It’s possible to write the code that you could write:

Song.difficulty(1).artist(3547) = 10

On Thu, Mar 01, 2007 at 09:15:40PM +0900, Dick wrote:

                ^

Unfortunately, a ‘foo=’ method can only take one argument, which is the
expression to the right of the equals sign.

You can create a regular method, e.g.

def Song.set_difficulty(difficulty, artistid, val)

Song.set_difficultly(1, 3547, 10)

However I wonder if perhaps you’re going about this the wrong way.
You’re
using a class variable, @@dcounts, which implies that you plan to create
multiple instances of class ‘Song’. So maybe what you really need is an
accessor on the individual Song object to set its difficulty, which
could
also update your @@dcounts index for you.

Also, consider having a separate SongIndex class, to which individual
Songs
are added. At the time when you add them (or at some later time) you can
index its difficulty.

This approach is cleaner because you get away from the class variable;
you
can just use a regular instance variable of the SongIndex class. It is
also
more flexible - it allows you to create individual instances of Song
which
are not in the global index.

HTH,

Brian.

You can’t have more then one argument to setter (var=)

oddly enough, you can define such a method, but you can’t call it
without jumping through some hoops:

irb(main):006:0> class Test
irb(main):007:1> def set= a,b
irb(main):008:2> @a=a
irb(main):009:2> @b=b
irb(main):010:2> end
irb(main):011:1> end
irb(main):012:0> t = Test.new
=> #Test:0x40205cac
irb(main):013:0> t.set=1,2
ArgumentError: wrong number of arguments (1 for 2)

irb(main):016:0> t.send “set=”, 1, 1
=> 1
irb(main):017:0> t
=> #<Test:0x40205cac @b=1, @a=1>

Weird behaviour,
-tim

On Thu, Mar 01, 2007 at 10:22:39PM +0900, Brian C. wrote:

end
end

However I wonder if perhaps you’re going about this the wrong way. You’re
using a class variable, @@dcounts, which implies that you plan to create
multiple instances of class ‘Song’. So maybe what you really need is an
accessor on the individual Song object to set its difficulty, which could
also update your @@dcounts index for you.

Are you trying to count, for each artist, the number of songs of a
particular difficulty level? Then you can make the the difficulty an
attribute of the song:

class Song
@@dcounts = []

def initialize(artistid, name, diff=nil)
@artistid = artistid
@name = name
self.difficulty=(diff)
end

def difficulty=(diff)
# decrement count at old difficulty (if any)
if @difficulty
@@dcounts[@difficulty][@artistid] -= 1
end

# increment count at new difficulty
if diff
  @@dcounts[diff] ||= {}
  @@dcounts[diff][@artistid] += 1
end

# remember difficulty
@difficulty = diff

end
end

However if you are doing this with ActiveRecord, i.e. you have a SQL
database on the backend, then I’d forget the class variable and let the
database do the work for you. Store the difficulty as an attribute of
the
song, then you just need something like (untested)

def Song.count(difficulty, artistid)
Song.count_by_sql([
“SELECT COUNT(*) FROM songs WHERE id=? AND difficulty=?”,
artistid, difficulty])
end

Or using :group conditions you could get all the songs grouped by
difficulty
and artistid, effectively populating your @@dcounts variable in one go.
I’m
I’m not sure if AR provides a method which will accept the output from a
SELECT foo,COUNT(*) GROUP BY foo, but at worst you can send it directly
to
the underlying database.

HTH,

Brian.

On 3/1/07, Dick [email protected] wrote:

@@dcounts[difficulty][artistid]

end

You’ve already stimulated discussion on the restrictions on x= methods
in Ruby.

Since you say this is your first ruby class, I feel the need to
critique the use of class variables.

While there might be some uses of class variables, I tend to see a
little red light go off every time I see @@ in ruby code. It’s not
likely that a class variable will do what the programmer thinks it
does, since class variables are shared between classes and subclasses.
In most cases what you really want are class INSTANCE variables which
look just like instance variables except that they appear in the
context of a class method.

I’d rewrite that beginning code as something like

class Song < ActiveRecord::Base
# This defines an accessor for the dcounts class instance variable
# note that this is a class method
# it could also have been defined as def self.dcounts, but I
usually
# prefer naming the class as a matter of style.
def Song.dcounts
# if @dcounts is nil initialize it to an empty array
# in any case return it.
# This is a standard ruby idiom for lazy initialization
@dcounts ||= []
end

  # Another class method, I used the accessor so that
  # @dcounts will be properly intialized, other references should
  # do the same.
  def Song.count(difficulty, artistid)
      dcounts[difficulty][artistid]
 end

HTH


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Mar 1, 2007, at 1:20 PM, [email protected] wrote:

This is one thing that really bothers me about Ruby. Does anyone know
if this will be addressed in further Ruby versions? The class
instance variable to me is a bit of a “work around” and I’d like to
see a more elegant way to have class variables that are inherited into
sub-classes, yet the sub-class version does not point directly to the
exact same object (i.e. same memory space) as parent class.

I think you are asking for something which is quite easy:

class A
class <<self
attr_accessor :per_class
end
end

class B < A
end

A.per_class = 1
B.per_class = 2
puts A.per_class # 1
puts B.per_class # 2

Is this what you are looking for or something else?

Gary W.

On Mar 1, 9:37 am, “Rick DeNatale” [email protected] wrote:


While there might be some uses of class variables, I tend to see a
little red light go off every time I see @@ in ruby code. It’s not
likely that a class variable will do what the programmer thinks it
does, since class variables are shared between classes and subclasses.

This is one thing that really bothers me about Ruby. Does anyone know
if this will be addressed in further Ruby versions? The class
instance variable to me is a bit of a “work around” and I’d like to
see a more elegant way to have class variables that are inherited into
sub-classes, yet the sub-class version does not point directly to the
exact same object (i.e. same memory space) as parent class.

I can see the use for “@@” as it works now; however, the usage would
be far less than a class instance variable (at least for me).

On Mar 1, 11:35 am, Gary W. [email protected] wrote:


I think you are asking for something which is quite easy:

class A
class <<self
attr_accessor :per_class
end
end

Yes, this is kind of what I’m referring to; however, my gripe is that
this is somewhat esoteric. The beauty of other parts of Ruby is that
the code is terse and readable. Also, I think that the work-around
that you mentioned (and I’ve used too) does not truly inherit:

class A
class <<self
attr_accessor :per_class
end
end

A.per_class #=> 1

class B < A
end

B.per_class #=> nil

Something that is unique to a class but inherits with nice syntax is
what I’d like to see (and I’m fairly sure is the behavior used in
other OOP languages). For example, assume that the behavior that I’m
referring to uses a triple “@” prefix to initialize ("@@@"). Then:

class A
@@@per_class = 1
attr_accessor :per_class
end

A.per_class #=> 1

class B < A
end

B.per_class #=> 1
B.per_class = 2 #=> 2
A.per_class #=> 1

I’ve read about this in postings before; however, I’m not aware of any
efforts made to support this behavior natively. I’m hoping that this
feature is being considered or hopefully worked on. I’ve had the need
for this on more than one occasion.

Thanks,
-James

On Fri, Mar 02, 2007 at 05:20:11AM +0900, [email protected]
wrote:

A.per_class #=> 1

class B < A
end

B.per_class #=> 1
B.per_class = 2 #=> 2
A.per_class #=> 1

So how would it work? B.per_class and A.per_class are clearly different,
since they end up pointing at different objects. Does the fact that
@@@per_class is undefined in B mean that the value in A is used?

In that case, you need something like a class hierarchy search to find
the
instance variable. How about this:

class A
def self.per_class
self.ancestors.each { |k|
k.instance_eval { return @per_class if defined? @per_class }
}
end
def self.per_class=(x)
@per_class=x
end
end

puts “hello”
A.per_class = 1

class B<A
end

puts A.per_class # 1
puts B.per_class # 1
B.per_class = 2
puts A.per_class # 1
puts B.per_class # 2

Clearly, this could be wrapped up as

class_attr_accessor :per_class

or similar.

If this gets rid of the @@ syntax from the language, then I’m in favour
:slight_smile:

Regards,

Brian.

On 3/1/07, [email protected] [email protected] wrote:

Yes, this is kind of what I’m referring to; however, my gripe is that
this is somewhat esoteric. The beauty of other parts of Ruby is that
the code is terse and readable.

Go back and look at my earlier post.

Just what does @var mean?

In a method it means that this is an instance variable of the object
executing the method.

[email protected]:/public/rubyscripts$ cat civ.rb
class C

def instance_var
@instance_var
end

def instance_var=(val)
@instance_var = val
end

def C.class_instance_var
@class_instance_var
end

def C.class_instance_var=(val)
@class_instance_var = val
end
end

class D < C
end

c = C.new
c1 = C.new

c.instance_var = ‘c’
c1.instance_var = ‘c1’

puts “c.instance_var=#{c.instance_var}”
puts “c1.instance_var=#{c1.instance_var}”

C.class_instance_var=“C”
puts “C.class_instance_var=#{C.class_instance_var}”
puts “D.class_instance_var=#{D.class_instance_var}”
D.class_instance_var=“D”
puts “C.class_instance_var=#{C.class_instance_var}”
puts “D.class_instance_var=#{D.class_instance_var}”
[email protected]:/public/rubyscripts$ ruby civ.rb
c.instance_var=c
c1.instance_var=c1
C.class_instance_var=C
D.class_instance_var=
C.class_instance_var=C
D.class_instance_var=D
[email protected]:/public/rubyscripts$

All a class instance variable is, is an instance variable of the
class. so @class_inst_var is a class instance variable of C because
it’s in class methods of C.

Gary’s example looks a bit esoteric perhaps, but the attr_accessor
method is just a metamethod which generates the two methods for each
of my variables. My code could be replaced with the equivalent:

class C

attr_accessor :instance_var

class << C # or class << self
attr_accessor :class_instance_var
end

end

class D < C
end

The class<<self (within the definition of C) or class << C (which
could also be outside) simply puts us in a context where self is the
class C, which allows us to invoke the private method attr_accessor.

To put this in the context of another older OO language, Smalltalk has
instance variables, class instance variables and class variables, but
unlike in ruby the existance of these has to be declared, with
something like

Object subclass: #C
instanceVariables: ‘instanceVariable’
classInstanceVariables: ‘classInstanceVariable’
classVariables: ‘ClassVariable’

The difference between Ruby and Smalltalk springs from Matz’ decision
to use sigils* to differentiate between the variable types rather than
requiring declaration.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Fri, Mar 02, 2007 at 06:12:40AM +0900, Brian C. wrote:

def self.per_class
self.ancestors.each { |k|
k.instance_eval { return @per_class if defined? @per_class }
}
end

P.S. I first wrote this as:

def self.per_class
self.ancestors.each { |k|
return k.instance_variable_get(:@per_class) if
k.instance_variables.include?("@per_class")
}
end

I note that Object#instance_variables appears to return an array of
strings,
not an array of symbols as I would have expected.

Also, I couldn’t find anything like ‘instance_variable_defined?’, which
is
why I ended up having to search through the whole array of all instance
variables. So the instance_eval form looked to be the better bet.

Regards,

Brian.

On Mar 1, 2007, at 3:20 PM, [email protected] wrote:

Yes, this is kind of what I’m referring to; however, my gripe is that
this is somewhat esoteric. The beauty of other parts of Ruby is that
the code is terse and readable. Also, I think that the work-around
that you mentioned (and I’ve used too) does not truly inherit:

I think your use of ‘inherit’ is misleading. Inheritance generally
refers to methods and not to data. I think ‘sharing’ would be a better
word for what you are describing.

So you want to share data between a class and all its subclasses:

class A
class <<self
def a_tree
@@a_tree
end
def a_tree=(val)
@@a_tree = val
end
end
end

A.a_tree = 1

class B < A; end

puts B.a_tree # 1

B.a_tree = 2

class C < A; end

puts C.a_tree # 2
puts A.a_tree # 2
puts A.new.class.a_tree # 2
puts C.new.class.a_tree # 2

Personally I’d avoid this sort of thing if at all possible. If you
are going to use class variables I think it is best to wrap the
access to
them in methods. It is often difficult to understand the lexical scope
of ‘@@var’ and so if you only use the ‘@@’ syntax when writing your
setters and getters (and thus limit yourself to a single lexical
scope), you’ll
be able to reason about how the data is shared more easily. It also
forces
all access to the shared state through well defined setters/getters,
otherwise
access to class variables is almost as bad as using global variables.

Of course you don’t even need class variables to do this:

class A
class <<self
a_tree = nil
define_method(:a_tree) { a_tree }
define_method(:a_tree=) { |val| a_tree = val }
end
end

This works by forcing the setter and getters to share the
scope generated by the class<<self;end block, which is uniquely
associated with the class object A and uses a local variable,
a_tree, defined within that scope to reference the shared data.

Gary W.

On Fri, Mar 02, 2007 at 08:06:59AM +0900, Gary W. wrote:

I think your use of ‘inherit’ is misleading. Inheritance generally
refers to methods and not to data. I think ‘sharing’ would be a better
word for what you are describing.

The word “scope” comes to mind for me.

Go back and look at my earlier post…

OK, I did… and I also ran the code that you provided in the second
example. I really do understand the concept of class instance
variables. But I’m trying to show how they can be preset and have the
preset values inherited. Maybe I’ll take your example and show you
what I’m looking for. Take your example and insert:

@class_instance_var = 1

as the last line in the class definition of C. I’m looking for
functionality where the first two lines of the class instance variable
printing that you have in your example would look like this:

C.class_instance_var=C
D.class_instance_var=1

Because D inherits from C and C had previously preset
@class_instance_var to 1, then I’d expect D to inherit that. At first
glance, you’d think that I was asking for a class variable, but it
would be useful (in the majority of the time IMHO) to NOT to have it
share the exact same namespace as C as is how class variables work
now.

Brian’s response seems to do what I’m looking for… check out his
response. Now it would be nice to have this integrated into the
language itself (again, IMHO). I’m pretty sure that I’ve seen this
behavior noted before in newsgroups… I’ll try to find references and
post.

For now, I’ll probably put together an accessor as Brian noted.

On Mar 1, 3:04 pm, [email protected] wrote:

… I’m pretty sure that I’ve seen this behavior noted before in
newsgroups… I’ll try to find references and post.

And here is the reference for those interested; with comments in the
post from none other than Matz himself… unfortunately, he just
describes the behavior of class variables and does not express a
desire to add true inheritance of variables between classes.

http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/13c6275b3da69be6/dc6bcceed21db7d2

Wouldn’t it just be nice to have a way to inherit variables instead of
jumping through hoops? So that leads us back to my original question:
do later versions of Ruby plan on addressing this? I think that the
answer for now is “no” as I haven’t heard anything to the contrary.

Thanks,
-James

On Fri, Mar 02, 2007 at 08:10:10AM +0900, [email protected]
wrote:

Go back and look at my earlier post…

OK, I did… and I also ran the code that you provided in the second
example. I really do understand the concept of class instance
variables. But I’m trying to show how they can be preset and have the
preset values inherited.

Maybe this is where the confusion arises. Here you’re talking about
inheritance of values, which implies the value from class A is
copied to
class B. The trouble with that approach is, when exactly do you copy the
values? When class B is first created? But that assumes that class A has
already set its instance variable. If it hasn’t, then there’s nothing
for
class B to copy.

That’s why my suggestion was to set the value in the class’s own
instance
variable space only, and not copy it, but at the point when you read
the
value, if you don’t have it in your own instance variable space then
walk
upwards.

However, I’m really not sure what problem we’re trying to solve - i.e.
in
what circumstances this behaviour would actually be useful. If class A
and
class B both set their own values, then they behave just like class
instance
variables do now. And if class B wants to ‘share’ a value with class A,
then
it would have to ask class A explicitly to set it:

class B

A.per_class = whatever

end

in which case, again, this is just a class instance variable in A, and
it
could read it using the same syntax:

class B

foo = A.per_class

end

I don’t see where you’d need the magic that B.per_class returns the same
as
A.per_class, except for the case where B.per_class has set its own value
to
override it.

B.

On Mar 1, 2007, at 6:50 PM, [email protected] wrote:

Wouldn’t it just be nice to have a way to inherit variables instead of
jumping through hoops? So that leads us back to my original question:
do later versions of Ruby plan on addressing this? I think that the
answer for now is “no” as I haven’t heard anything to the contrary.

It is not clear at all (to me) what you mean by ‘inheriting variables’.
As Chad and myself pointed out, you seem to be suggesting that you
want to share access to some state but the exact scope of the sharing
isn’t really clear. I’ve suggested a couple of ideas, and I understand
that they are not as simple as ‘attr :foo’, but I’m not sure if it is
the
syntax or the semantics that is problematic for you.

Gary W.

On 3/1/07, Dick [email protected] wrote:

@@dcounts[difficulty][artistid]

test.rb:14: parse error, unexpected ‘=’, expecting $
Song.count(1,3547) = 10
^


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

You could do something like:
def Song.count=(*args)
@@dcounts[args[0]] ||= {}
@@dcounts[args[0]][args[1]] = args[2]
end

Song.count 1, 3547, 10

On 3/2/07, Brian C. [email protected] wrote:

class B. The trouble with that approach is, when exactly do you copy the
values? When class B is first created? But that assumes that class A has
already set its instance variable. If it hasn’t, then there’s nothing for
class B to copy.

That’s why my suggestion was to set the value in the class’s own instance
variable space only, and not copy it, but at the point when you read the
value, if you don’t have it in your own instance variable space then walk
upwards.

It seems like this is approaching the notions of delegation rather
than inheritance.

Some years ago there was a raging debate over delegation vs.
inheritance in the OO community. (Do a google search on oopsla
delegation inheritance).

Some languages unified instance variables with methods. Self did
this. Self had no explicit classes, objects have named slots which
contain values, some of which are executable. And these named slots
were resolved by walking up a chain of objects (rather than having a
superclass you delegate to another object).

Ruby actually uses somewhat similar mechanisms, but it separates
methods from instance variables, it puts the tables which are used to
find methods into a class rather than in the instance itself (even for
singleton methods which get put in singleton classes). Although Ruby
instance variables are implemented by name lookup in an instance
variable table owned by the instance, there’s no instance chaining so
there’s no place to search upwards for instance variables.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs