Why isn't this simple test class's method inherited in Ruby? - ruby

Consider this very simple logging class:
class MockLog
# ...
end
Let's add logging to all our classes:
class Module
def has_logging()
class_eval {
#log = MockLog.new
def log
self.class.instance_variable_get :#log
end
}
end
end
Now, why doesn't this work?
class Foo
has_logging
end
Foo.new.log.nil? # => false, as expected
class Bar < Foo
end
Bar.new.log.nil? # => true?! Why wasn't the `log` method inherited?

The log method was inherited (if it wasn't, you would get a NoMethodError), but your class level instance variable wasn't, so instance_variable_get returned nil. You should declare a regular class variable like:
class Module
def has_logging()
class_eval {
##log = MockLog.new
def log
##log
end
}
end
end
class Foo
has_logging
end
class Bar < Foo
end
Now it will be inherited. See Class and Instance Variables In Ruby

Related

When to use self for Ruby Class

Can somebody help me distinguish When we create methods inside class << self block and when we define normal methods.
I saw somewhere code like this, but I don't know concisely the use cases of them
class Foo
def initialize
end
def bar
end
class << self
def foobar
end
end
end
The methods defined right inside a class block are instance methods:
class Foo
def bar
end
end
Methods defined within class << self inside a class block are class methods:
class Foo
class << self
def baz
end
end
end
Instance methods become available to any instance of a given class:
foo = Foo.new
foo.bar
Whereas class methods can be called directly on the class:
Foo.baz
Attempting to call instance methods on the class or vice versa results in an error:
Foo.bar #=> NoMethodError: undefined method `bar' for Foo:Class
foo.baz #=> NoMethodError: undefined method `baz' for #<Foo:0x00007ffe20055a20>
Another way to define class methods is by prefixing the method name with self.:
class Foo
def self.baz
end
end
You could also define them outside the class block, although this is rarely seen:
def Foo.baz
end
Or likewise:
class << Foo
def baz
end
end
Note that defining methods this way is not limited to classes. You can add methods to arbitrary objects, e.g.:
o = Object.new
def o.hello
"hello from o"
end
o.hello
#=> "hello from o"
Or via:
class << o
def hello
"hello from o"
end
end
Internally, these methods are added to the object's singleton class. It's a special purpose class to hold methods for just that instance:
o.singleton_class.instance_methods(false)
#=> [:hello]
For the Foo class above:
Foo.instance_methods(false) #=> [:bar]
Foo.singleton_class.instance_methods(false) #=> [:baz]
So technically, a class method is just an instance method defined on the class' singleton class.
You may need to read up on Ruby's instance and class methods.
But personally, I'd do
class Foo
class << self
def foobar
end
end
end
instead of
class Foo
def self.foobar
end
end
whenever I want to add some class level attributes, or make a method private etc as
class Foo
private
def self.foobar
end
end
wouldn't work the same as
class Foo
class << self
private
def foobar
end
end
end

Is it possible to access a method without creating an instance?

I have a class within a module and it has methods:
module D
class Dog
#name = 'pluto'
def setName( n )
#name = n
end
def getName ()
return #name
end
end
end
Can I access getName without creating an instance of Dog like the static method in C++? Something like:
D::Dog.getName ()
instead of:
d = D::Dog.new
d.getName()
I believe you're looking for what is known as a class method in Ruby:
module SomeModule
class SomeClass
#class_variable = "some_string" # An instance variable on a class
def self.some_class_method
#class_variable # Return can be omitted in Ruby
end
# This is how setter methods are usually written in Ruby
def self.some_class_method= new_value
#class_variable = new_value
end
end
end
SomeModule::SomeClass.some_class_method
#=> "some_string"

can i initialize when inherited with ruby?

I want to self-initialize a class when it is inherited, but it seems inherited gets called before the inherited class is loaded. is this perhaps bad form?
class Foo
def self.inherited klass
klass.new
end
end
class Bar < Foo
def initialize
puts 'initialize'
end
end

How to call super class method within class

How can I call super class method within class like:
class A
def foo
puts "lol"
end
end
class B < A
foo
end
You're trying to call an instance method from within the context of a class. This is not valid.
What would work:
class A
def self.foo
puts "lol"
end
end
class B < A
foo
end

Ruby scope question, option parsing

So I'm using the awesome trollop gem to do option parsing, but I'm having a general problem with the scope of the variables it's setting.
require 'trollop'
class MyClass
opts = Trollop::options do
opt :thing, "does something", default: "blah", type: String
end
def my_method
puts opts[:thing]
end
end
But I get:
undefined local variable or method `opts' for #<MyClass:0x0000010203c840> (NameError)
Any ideas what I'm doing wrong with my scope?
There are about six options here: instance variable, class instance variable, class variable, class constant, global variable, global constant. Which to use depends on your needs.
Instance Variable - each MyClass instance gets its own options:
class MyClass
def initialize
#opts = ...
end
def my_method
puts #opts[:thing]
end
end
Class Instance Variable - single value across the class that can be reassigned:
class MyClass
#opts = ...
class << self
attr_accessor :opts
end
def my_method
puts self.class.opts[:thing]
end
end
Class Variable - each MyClass and all subclasses share the same value (convenient syntax, but rarely a good idea):
class MyClass
##opts = ...
def my_method
puts ##opts[:thing]
end
end
Class Constant - single object that may be mutated, but not re-assigned. Easily accessed from this class, accessible from others via MyClass::OPTS:
class MyClass
OPTS = ...
def my_method
puts OPTS[:thing]
end
end
Global Variable - you can only have one of these in your entire app; often global variables are ill-advised, but perhaps appropriate for a standalone application's options:
$opts = ...
class MyClass
def my_method
puts $opts[:thing]
end
end
Global Constant - accessed from many classes, can't be set to a new value, but may be mutated:
OPTS = ...
class MyClass
def my_method
puts OPTS[:thing]
end
end
Shouldn't you just use instance variable?
require 'trollop'
class MyClass
def initialize
#opts = Trollop::options do
opt :thing, "does something", default: "blah", type: String
end
end
def my_method
puts #opts[:thing]
end
end
You are defining 'opts' as a local variable inside your class. Instances methods (like my_method) will not be able to access it. Is opts supposed to be "global" for the whole class? In that case:
class MyClass
##opts = Trollop::options...
def my_method
puts ##opts[:thing]
end
end
Or is there supposed to be a unique one for each instance of the class?
class MyClass
def initialize
#opts = Trollop::options...
end
def my_method
puts #opts[:thing]
end
end
This might be a good read: http://sporkmonger.com/2007/2/19/instance-variables-class-variables-and-inheritance-in-ruby
You'd want to make it either a class variable or an instance variable, depending on your needs.

Resources