class Collie
def speak
puts dog_generic
end
end
class Greyhound
def speak
puts dog_generic
end
end
class Labrador
def speak
puts dog_generic
end
end
dog_generic = "Woof"
chep = Collie.new
wrex = Collie.new
speedy = Greyhound.new
faithful = Labrador.new
chep.speak #=> Woof
wrex.speak #=> Woof
speedy.speak #=> Woof
faithful.speak #=> Woof
I'd like those last three methods to all return "Woof". However, this code will call an undefined variable dog_generic error. This seems to be because even global variables aren't available to objects. If I were to change all instances of dog_generic to ##dog_generic, it would work, but ## variables are rarely used, and based on that alone, I can't help thinking I'd be doing it wrong.
How can I share one variable amongst several objects?
And no, I don't want to pass in a string of "Woof" to every single object as a parameter.
Typically, one would use inheritance to provide this sort of behavior:
class Dog
def speak
puts "Woof"
end
end
class Collie < Dog
# whatever behavior that is specific to Collie here
end
chep = Collie.new
chep.speak #=> Woof
You seem to have some confusion about what a global variable in ruby is. You have to explicitly make them global, with the $ sigil, a la
$dog_generic = 'Woof'
and
def speak
puts $dog_generic
end
That said, using a global is probably your worst approach here. Defining either a generic dog class and having your specific types inherit, or else creating a dog mixin and including it would both be better solutions IMHO.
Make a constant. Any variable starting with a capital letter is a constant. Including classes, so constants can be scoped globally. class Foo; end is a constant that points to a class, you could also write this as Foo = Class.new.
class Collie
def speak
puts DOG_GENERIC
end
end
class Greyhound
def speak
puts DOG_GENERIC
end
end
class Labrador
def speak
puts DOG_GENERIC
end
end
DOG_GENERIC = "Woof"
chep = Collie.new
wrex = Collie.new
speedy = Greyhound.new
faithful = Labrador.new
chep.speak #=> Woof
wrex.speak #=> Woof
speedy.speak #=> Woof
faithful.speak #=> Woof
I agree with #perimosocordiae, you should probably use inheritance. I would disagree with him though on use of a class for inheritance. Modules in Ruby are included in the inheritance chain, however, they do not have constructors (you can think of them as abstract classes, but you can inherit multiple modules).
module Speach
WOOF = "Woof"
QUACK = "Quack"
module Dog
def speak
puts WOOF
end
end
module Duck
def speak
puts QUACK
end
end
end
class Collie
include Speach::Dog
end
class Greyhound
include Speach::Dog
# can overwrite ancestor
def speak
puts "Ruff"
end
end
class Mallard
include Speach::Duck
end
Collie.new.speak # => "Woof"
Greyhound.new.speak # => "Ruff"
Mallard.new.speak # => "Quack"
While others answered your question about subclassing Dog, I would like to introduce you to a better way of naming your instances: NameMagic. Type gem install y_support in your command line, and require NameMagic it by:
require 'y_support/name_magic'
And then:
class Dog
include NameMagic
def speak
puts "Woof"
end
end
Collie = Class.new Dog
Greyhound = Class.new Dog
Labrador = Class.new Dog
Chep = Collie.new
Wrex = Collie.new
Speedy = Greyhound.new
Faithful = Labrador.new
And then:
Dog.instances.each &:speak
#=> Woof
#=> Woof
#=> Woof
#=> Woof
NameMagic also allows you to query instances and their names:
Dog.instances
#=> [#<Collie:0xb90e3c38>, #<Collie:0xb90e1adc>, #<Greyhound:0xb90e7860>, #<Labrador:0xb90e5574>]
Collie.instances
#=> [#<Collie:0xb90e3c38>, #<Collie:0xb90e1adc>]
Labrador.instances
#=> [#<Labrador:0xb90e5574>]
Dog.instance_names
#=> [:Chep, :Wrex, :Speedy, :Faithful]
Collie.instance_names
#=> [:Chep, :Wrex]
Related
When working in a class defined in a module, I'd like to access another class defined in the same module.
When using this code
module SomeModule
class Foo
def self.get_bar
Bar
end
end
end
module SomeModule
class Bar
end
end
# works and returns SomeModule::Bar
SomeModule::Foo.get_bar
Looking up Bar from SomeModule::Foo works. It is not found in local scope SomeModule::Foo, so it looks one level up to SomeModule and finds SomeModule::Bar.
But when using the shortcut notation class A::B to define the classes, the lookup does not work anymore:
module SomeModule
end
class SomeModule::Foo
def self.get_bar
Bar
end
end
class SomeModule::Bar
end
# does not work and raises a NameError
SomeModule::Foo.get_bar
It produces the error NameError: uninitialized constant SomeModule::Foo::Bar. But for me both codes look identical and should produce the same output. I'm obvisouly missing a key concept here.
Can someone explain why the lookup works in one case and not the other? And is it possible to know in advance if the lookup will work or fail by introspecting the class?
There is an awesome post explaining how it works in detail. The summary will be that Ruby's constant lookup is based on lexical scope.
There is a method Module.nesting which returns an array of constants where Ruby is looking for a required const first.
module SomeModule
class Buzz
def self.get_bar
p Module.nesting #=> [SomeModule::Buzz, SomeModule]
Bar
end
end
end
class SomeModule::Foo
def self.get_bar
p Module.nesting #=> [SomeModule::Foo]
Bar rescue puts "Oops, can not find it"
end
end
class SomeModule::Bar
end
SomeModule::Buzz.get_bar
SomeModule::Foo.get_bar
Ruby constant lookup is based on the lexical scope, and these two examples are quite different with regards to that. Take a look:
module SomeModule
puts Module.nesting.inspect #=> [SomeModule]
puts Module.nesting.map(&:constants).inspect # => [[]], we didn't define Foo yet
class Foo
puts Module.nesting.inspect #=> [SomeModule::Foo, SomeModule]
puts Module.nesting.map(&:constants).inspect #=> [[], [:Foo]], at this point SomeModule is already "aware" of Foo
def self.get_bar
Bar
end
end
end
module SomeModule
puts Module.nesting.inspect #=> [SomeModule]
puts Module.nesting.map(&:constants).inspect #=> [[:Foo]], we didn't define Bar yet
class Bar
puts Module.nesting.inspect #=> [SomeModule::Bar, SomeModule]
puts Module.nesting.map(&:constants).inspect #=> [[], [:Foo, :Bar]]
end
end
and the second one
module SomeModule
puts Module.nesting.inspect #=> [SomeModule]
puts Module.nesting.map(&:constants).inspect #=> [[]]
end
class SomeModule::Foo
puts Module.nesting.inspect #=> [SomeModule:Foo]
puts Module.nesting.map(&:constants).inspect #=> [[]]
def self.get_bar
Bar
end
end
class SomeModule::Bar
puts Module.nesting.inspect #=> [SomeModule:Bar]
puts Module.nesting.map(&:constants).inspect #=> [[]]
end
As you see, Bar in 1st and 2nd case is being resolved in quite different lexical scopes which in turn leads to quite different result.
Regarding your question
is it possible to know in advance if the lookup will work or fail by introspecting the class
It is possible for isolated piece of code, but application-wide I wouldn't rely on that. When in doubt just specify the nesting explicitly starting from the outermost context (::SomeModule::Bar)...
Suppose one wished to create a class having a class instance variable, a getter for that variable, a class method and an instance method. One might write the following.
class C
#v = 1
def hi
'hi'
end
class << self
attr_reader :v
def ho
'ho'
end
end
end
C.methods(false)
#=> [:v, :ho]
C.ho
#=> "ho"
C.v
#=> 1
This could be written many other ways, of course. For example:
class C
#v = 1
def hi
'hi'
end
def self.ho
'ho'
end
singleton_class.class_eval do
attr_reader :v
end
end
C.methods(false)
#=> [:v, :ho]
C.ho
#=> "ho"
C.v
#=> 1
In both cases, class << self and singleton_class.class_eval cause self to change from C to C.singleton_class. Suppose there were a keyword singleton_class that did the same thing, allowing us to write the following.
class C
#v = 1
def hi
'hi'
end
singleton_class
attr_reader :v
def ho
'ho'
end
end
C.methods(false)
#=> [:v, :ho]
C.ho
#=> "ho"
C.v
#=> 1
One could have another keyword that would permit self to flip back and forth between C and its singleton class, but it might be simplest to require everything after singleton_class to be related to the singleton class.
It would not seem that conflicts would arise between the use of the keyword singleton_class and local variables or methods of the same name. Consider, for example, the use of the keyword private: (After writing the following I learned that my long-standing belief that private is a keyword was incorrect; it is the method Module#private used without an argument. I am leaving this here as it is referenced in the comments.)
class C
def m
private = 'hi'
private
end
private :m
private
def private
'ho'
end
end
c = C.new
c.m
#=> NoMethodError (private method `m' called...
c.send :m
#=> "hi"
c.private
#=> NoMethodError (private method `private' called...
c.send :private
#=> "ho"
My question is given in the title. Feel free to also offer an opinion as to whether Ruby would benefit from the introduction of a keyword to be used in this way.
Yes. Introducing a new keyword always breaks existing code.
Before, whatever you introduce was a valid identifier, now it is not.
That is why keywords are very "expensive" and should be used very sparingly. (Ruby already has way too many of them, IMO).
I have two classes (I can't combine them), each in their own file.
File number 1:
class A
def say_hi
puts "Hi"
end
end
File number 2:
class B
def say_bye
puts "bye"
end
end
I can do:
apple = A.new
apple.say_hi
Or:
baby = B.new
apple.say_bye
But what if I want to do:
apple = A.new
apple.say_bye
Or:
baby = B.new
baby.say_hi
Is there a simple way to do that without restructuring my classes?
If it's a case of "which class I should instantiate?" and all you know is you want method "say_hi" then you could do...
method = :say_hi
if A.new.respond_to?(method)
runner_object = A.new
else
runner_object = B.new
end
active support in Rails lets you do...
result = A.new.try(:say_hi) || B.new.try(:say_hi)
If I understand correctly, you need to call these different methods from a flow which gets either one of these classes.
The easiest way to do this is by using alias_method:
class A
def say_hi
puts "Hi"
end
alias_method :say, :say_hi
end
class B
def say_bye
puts "bye"
end
alias_method :say, :say_bye
end
apple = A.new
apple.say
# => "Hi"
baby = B.new
baby.say
# => "bye"
You can even do it if you can't actually change the class code, by using class_eval:
A.class_eval { alias_method :say, :say_hi }
B.class_eval { alias_method :say, :say_bye }
For example:
class Animal
def make_noise
print NOISE
end
end
class Dog < Animal
NOISE = "bark"
end
d = Dog.new
d.make_noise # I want this to print "bark"
How do I accomplish the above? Currently it says
uninitialized constant Animal::NOISE
I think that you don't really want a constant; I think that you want an instance variable on the class:
class Animal
#noise = "whaargarble"
class << self
attr_accessor :noise
end
def make_noise
puts self.class.noise
end
end
class Dog < Animal
#noise = "bark"
end
a = Animal.new
d = Dog.new
a.make_noise #=> "whaargarble"
d.make_noise #=> "bark"
Dog.noise = "WOOF"
d.make_noise #=> "WOOF"
a.make_noise #=> "whaargarble"
However, if you are sure that you want a constant:
class Animal
def make_noise
puts self.class::NOISE
# or self.class.const_get(:NOISE)
end
end
one way to do it without class instance variables:
class Animal
def make_noise
print self.class::NOISE
end
end
class Dog < Animal
NOISE = "bark"
end
d = Dog.new
d.make_noise # prints bark
I think you have the wrong concept here. Classes in Ruby are similar to classes in Java, Smalltalk, C#, ... and all are templates for their instances. So the class defines the structure and the behavior if its instances, and the parts of the structure and behavior of the instances of its subclasses but not vice versae.
So direct access from a superclass to a constant in a subclass is not possible at all, and that is a good thing. See below how to fix it. For your classes defined, the following things are true:
class Animal defines the method make_noise.
instances of class Animal may call the method make_noise.
class Dogdefines the constant NOISE with its value.
instances of Dog and the class Dog itself may use the constant NOISE.
What is not possible:
Instances of Animal or the class Animal itself have access to constants of the class Dog.
You may fix that by the following change:
class Animal
def make_noise
print Dog::NOISE
end
end
But this is bad style, because now, your superclass (which is an abstraction about Dog and other animals) knows now something that belongs to Dog.
A better solution would be:
Define an abstract method in class Animal which defines that make_noise should be defined. See the answer https://stackoverflow.com/a/6792499/41540.
Define in your concrete classes the method again, but now with the reference to the constant.
If you're doing this to configure your sub classes so that the base class has access to the constants then you can create a DSL for them like this:
module KlassConfig
def attr_config(attribute)
define_singleton_method(attribute) do |*args|
method_name = "config_#{attribute}"
define_singleton_method method_name do
args.first
end
define_method method_name do
args.first
end
end
end
end
class Animal
extend KlassConfig
attr_config :noise
def make_noise
puts config_noise
end
end
class Dog < Animal
noise 'bark'
end
This way is a bit more performant as on each method call you don't have to introspect the class to reach back (or is it forward?) for the constant.
If you want it the Object-Oriented Way (TM), then I guess you want:
class Animal
# abstract animals cannot make a noise
end
class Dog < Animal
def make_noise
print "bark"
end
end
class Cat < Animal
def make_noise
print "meow"
end
end
d = Dog.new
d.make_noise # prints bark
c = Cat.new
c.make_noise # prints meow
If you want to refactor to prevent duplicating the code for print:
class Animal
def make_noise
print noise
end
end
class Dog < Animal
def noise
"bark"
end
end
class Cat < Animal
def noise
if friendly
"meow"
else
"hiss"
end
end
end
d = Dog.new
d.make_noise # prints bark
c = Cat.new
c.make_noise # prints meow or hiss
Just for fun, again, but is it possible to take a block that contains method definitions and add those to an object, somehow? The following doesn't work (I never expected it to), but just so you get the idea of what I'm playing around with.
I do know that I can reopen a class with class << existing_object and add methods that way, but is there a way for code to pass that information in a block?
I guess I'm trying to borrow a little Java thinking here.
def new(cls)
obj = cls.new
class << obj
yield
end
obj
end
class Cat
def meow
puts "Meow"
end
end
cat = new(Cat) {
def purr
puts "Prrrr..."
end
}
cat.meow
# => Meow
# Not working
cat.purr
# => Prrrr...
EDIT | Here's the working version of the above, based on edgerunner's answer:
def new(cls, &block)
obj = cls.new
obj.instance_eval(&block)
obj
end
class Cat
def meow
puts "Meow"
end
end
cat = new(Cat) {
def purr
puts "Prrrr..."
end
}
cat.meow
# => Meow
cat.purr
# => Prrrr...
You can use class_eval(also aliased as module_eval) or instance_eval to evaluate a block in the context of a class/module or an object instance respectively.
class Cat
def meow
puts "Meow"
end
end
Cat.module_eval do
def purr
puts "Purr"
end
end
kitty = Cat.new
kitty.meow #=> Meow
kitty.purr #=> Purr
kitty.instance_eval do
def purr
puts "Purrrrrrrrrr!"
end
end
kitty.purr #=> Purrrrrrrrrr!
Yes
I suspect you thought of this and were looking for some other way, but just in case...
class A
def initialize
yield self
end
end
o = A.new do |o|
class << o
def purr
puts 'purr...'
end
end
end
o.purr
=> purr...
For the record, this isn't the usual way to dynamically add a method. Typically, a dynamic method starts life as a block itself, see, for example, *Module#define_method*.