Class inheritance and class constants

Correct, since the method is being called in the context of A, it will search starting from A. You can verify this by printing out the Module.nesting value which is used for constant lookup searching:

···

On Aug 14, 2011, at 9:31 AM, Iñaki Baz Castillo wrote:

------------------------
class A
HELLO = "Hello I'm A"

def hello
   puts HELLO
end
end

class B < A
HELLO = "Hello I'm B"

def hello2
   puts HELLO
end
end
------------------------

B.new.hello
=> "Hello I'm A" (ouch...)

B.new.hello2
=> "Hello I'm B"

-------------------------
class A
HELLO = "Hello I'm A"

def hello
   p Module.nesting
   puts HELLO
end
end
-------------------------

This will output [A].

So, if a contstant appears within a method, its value is evaluated for
the class in which such method is defined ("hello" is defined in class
A not in B, so it takes HELLO from class A).
I don't like this behavior (just my opinion). Do I miss something?

This behavior can fortunately be overridden by simply overriding the hello method in the B class, meaning that it will use the constant defined in B instead:

--------------------------
class A
HELLO = "Hello I'm A"

def hello
   puts HELLO
end
end

class B < A
HELLO = "Hello I'm B"

def hello
  puts HELLO
end

def hello2
   puts HELLO
end
end

B.new.hello
--------------------------

Regards,
Chris White
http://www.twitter.com/cwgem

Does Ruby violate the DRY principle here?

···

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

class A
  class << self
    attr_accessor :hello
  end
  @hello = "Hello I'm A"

  def hello
    puts self.class.hello
  end
end

class B < A
  @hello = "Hello I'm B"

  def hello2
    puts self.class.hello
  end
end

B.new.hello
#=> "Hello I'm B"

B.new.hello2
#=> "Hello I'm B"

···

On Aug 14, 2011, at 10:31 AM, Iñaki Baz Castillo wrote:

So, if a contstant appears within a method, its value is evaluated for
the class in which such method is defined ("hello" is defined in class
A not in B, so it takes HELLO from class A).
I don't like this behavior (just my opinion). Do I miss something?

"Iñaki Baz Castillo" <ibc@aliax.net> wrote in post #1016621:

------------------------
class A
  HELLO = "Hello I'm A"

  def hello
    puts HELLO
  end
end

class B < A
  HELLO = "Hello I'm B"

  def hello2
    puts HELLO
  end
end

Change
  puts HELLO
to
  puts self.class::HELLO

and it should work the way you want.

Ruby's constant resolution is static, unless you force it to be dynamic
this way.

···

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

Yes, but it requires duplicating code. In my case I've a server class:

class Server
  def self.foo
    puts "I listen in IP #{IP}"
  end
end

and then some classes inhereting from it:

class UdpServer < Server
  IP = "1.2.3.4"
end

class TcpServer < Server
  IP = "1.2.3.5"
end

Of course "TcpServer.foo" fails:

  NameError: uninitialized constant Server::IP

So I must use workarounds like:

class Server
  def self.foo
    puts "I listen in IP #{self::IP}"
  end
end

class UdpServer < Server
  IP = "1.2.3.4"
end

class TcpServer < Server
  IP = "1.2.3.5"
end

Then it works:

TcpServer.foo
=> I listen in IP 1.2.3.5

UdpServer.foo
=> I listen in IP 1.2.3.4

···

2011/8/14 Chris White <cwprogram@live.com>:

This behavior can fortunately be overridden by simply overriding the hello method in the B class, meaning that it will use the constant defined in B instead

--
Iñaki Baz Castillo
<ibc@aliax.net>

I may be late to the party, since it seems like you've already solved
the problem, but Ruby has a const_missing method, which works just
like method_missing. You could use it to search for constants in
various places.

http://www.ruby-doc.org/core/classes/Module.html#M000489

-- Matma Rex

Yes, I've also used this mechanism (self.class::HELLO). And also using
class instance variables.

···

2011/8/15 Brian Candler <b.candler@pobox.com>:

Change
puts HELLO
to
puts self.class::HELLO

and it should work the way you want.

Ruby's constant resolution is static, unless you force it to be dynamic

--
Iñaki Baz Castillo
<ibc@aliax.net>

What about Modules?

Create a Server module with Server logic, and mix that into your classes.

···

On Sun, Aug 14, 2011 at 8:40 PM, Iñaki Baz Castillo <ibc@aliax.net> wrote:

Yes, but it requires duplicating code. In my case I've a server class:

--
Phillip Gawlowski

phgaw.posterous.com | twitter.com/phgaw | gplus.to/phgaw

A method of solution is perfect if we can forsee from the start,
and even prove, that following that method we shall attain our aim.
-- Leibniz

I suppose the question then becomes why they have to be constants at that level. By using a module and doing a mixin (as I notice that Philip just mentioned as I'm writing this):

···

On Aug 14, 2011, at 11:40 AM, Iñaki Baz Castillo wrote:

2011/8/14 Chris White <cwprogram@live.com>:

This behavior can fortunately be overridden by simply overriding the hello method in the B class, meaning that it will use the constant defined in B instead

Yes, but it requires duplicating code. In my case I've a server class:

class Server
def self.foo
   puts "I listen in IP #{IP}"
end
end

and then some classes inhereting from it:

class UdpServer < Server
IP = "1.2.3.4"
end

class TcpServer < Server
IP = "1.2.3.5"
end

Of course "TcpServer.foo" fails:

NameError: uninitialized constant Server::IP

-------------------------------------
module Server
  def listen(ip)
    # Would most likely set instance variable @ip for later use
    @ip = ip
    puts "Listening on #@ip"
  end
end

class UdpServer
include Server

def listen(ip)
  puts "UDP Listen"
  super
end
end

class TcpServer
include Server
end

TcpServer.new.listen("1.2.3.4")
UdpServer.new.listen("1.2.3.5")
-------------------------------------

The listen logic is centralized, and as shown in the UdpServer class, you can override the Server listen and still call its core functionality through `super`. This works out because including modules inserts into the object hierarchy as shown by doing a p TcpServer.ancestors:

[TcpServer, Server, Object, Kernel, BasicObject]

Regards,
Chris White
http://www.twitter.com/cwgem

Thanks, but I don't think that mechanism is as efficient as using
class instance variables (this is, @attributes belonging to the class
itself).

Thanks a lot.

···

2011/8/14 Bartosz Dziewoński <matma.rex@gmail.com>:

I may be late to the party, since it seems like you've already solved
the problem, but Ruby has a const_missing method, which works just
like method_missing. You could use it to search for constants in
various places.

--
Iñaki Baz Castillo
<ibc@aliax.net>

It's the very same:

module M
  def foo
    puts "HELLO = #{HELLO}"
  end
end

class A
  HELLO = "I'm A"
  extend M
end

A.foo
=> NameError: uninitialized constant M::HELLO

···

2011/8/14 Phillip Gawlowski <cmdjackryan@gmail.com>:

What about Modules?

Create a Server module with Server logic, and mix that into your classes.

--
Iñaki Baz Castillo
<ibc@aliax.net>

Hi. My code uses classes rather than instance of classes. Each class
(i.e. UdpServer, TcpServer) has different attributes (listenting IP,
port, transport, and so) and I don't want (due to efficience) to
assign the same value in initialize method for every instance.

···

2011/8/14 Chris White <cwprogram@live.com>:

The listen logic is centralized, and as shown in the UdpServer class, you can override the Server listen and still call its core functionality through `super`

--
Iñaki Baz Castillo
<ibc@aliax.net>

If they need to stay as classes and not instances, then I recommend the use of a class instance variable:

···

On Aug 14, 2011, at 12:19 PM, Iñaki Baz Castillo wrote:

2011/8/14 Chris White <cwprogram@live.com>:

The listen logic is centralized, and as shown in the UdpServer class, you can override the Server listen and still call its core functionality through `super`

Hi. My code uses classes rather than instance of classes. Each class
(i.e. UdpServer, TcpServer) has different attributes (listenting IP,
port, transport, and so) and I don't want (due to efficience) to
assign the same value in initialize method for every instance.

-------------------------------
class Server
  def self.listen
    puts "Listening on #@ip"
  end
end

class UdpServer < Server
  @ip = "1.2.3.4"
end

class TcpServer < Server
  @ip = "5.6.7.8"
end

UdpServer.listen
TcpServer.listen
-------------------------------

What's happening here is that an instance variable is created for both UdpServer and TcpServer. Classes can have instance variables the same as their instances since classes are themselves instances of Class. When listen is called on the class in question, the "self" value is set to the class itself, not Server, and so @ip is the instance variable for the specific server object in question.

Regards,
Chris White
http://www.twitter.com/cwgem

Yes, I also us that commonly. However I didn't know that class
instance variables can be defined out of a method. I expected:

class TcpServer < Server
  def self.init_variables
    @ip="5.6.7.8"
  end
end

Good to know that it's not needed :slight_smile:

···

2011/8/14 Chris White <cwprogram@live.com>:

-------------------------------
class Server
def self.listen
puts "Listening on #@ip"
end
end

class UdpServer < Server
@ip = "1.2.3.4"
end

class TcpServer < Server
@ip = "5.6.7.8"
end

UdpServer.listen
TcpServer.listen
-------------------------------

--
Iñaki Baz Castillo
<ibc@aliax.net>

Yes, I also us that commonly. However I didn't know that class
instance variables can be defined out of a method. I expected:

class TcpServer < Server
def self.init_variables
   @ip="5.6.7.8"
end
end

Good to know that it's not needed :slight_smile:

Yup, and the reason why it works is because class definitions are expressions, where the last expression in the class body is the return of the expression. Since in most cases it's a method definition, the result is generally nil. So what happens is:

class TcpServer < Server
def self.init_variables

Notice we have to use self here so it works when calling TcpServer.init_variables. This is because we're at the class declaration stage, so self is set to TcpServer. This binds the method to the specific TcpServer class instead of the instances of the class. So @ip outside of a method declaration is the same principal, as when instance variables are created and accessed, they're bound to the value of self (You can see this at the bytecode / c level here if you want http://blog.rubychest.net/2011/08/know-they-self-instance-variables.html ).

Regards,
Chris White
http://www.twitter.com/cwgem