Module instance methods on later defined class - ruby

I want to note that this is COMPLETELY a made up question. I am aware that there are other ways to accomplish this.
I want to declare a module like so
module Foo
# some logic here to
# get instance method 'foo' on
# a later defined class
end
then later I want to declare a class like:
class Foo::Bar
end
Then WITHOUT using include or extend be able to do this:
Foo::Bar.new.foo
and have it call the foo method I defined in module Foo

module Foo
class Bar
def foo
puts "erik is a dummy"
end
end
end
Foo::Bar.new.foo
=> erik is a dummy

Related

Include a Ruby module but change the root module reference

Given a Module Foo, I would like to extend this module, but not preserve the module name Foo.
# sscce (irb)
module Foo
class Foo; end
end
module Bar
include Foo
end
> Foo::Foo
Foo::Foo
> Bar.ancestors
[Bar, Foo]
> Bar::Foo
Foo::Foo
How can I get Bar::Foo to be Bar::Foo (not Foo::Foo) without redefining the class?
I've attempted include and prepend as well as include Foo.clone with the same result. I think it's safe to assume that it's referencing the original class definition, fine, but I'd still like to have the class Foo actually belong to Bar.
maybe something like this could work?
module Foo
class Foo
def foo
puts "inside: #{self.class}"
end
end
end
module Bar
class Foo < ::Foo::Foo; end
end
now when you do:
a = Foo::Foo.new
a.foo # inside: Foo::Foo
and if you do
b = Bar::Foo.new
b.foo # inside: Bar::Foo
instead of trying to include/extend Foo in a tricky way just create a new class inside the Bar module and rely on inheritance?
You cannot make a class belong to a module by inclusion. Even more - a class doesn't belong to a module even without inclusion (only a reference to a class is available as a module constant). When you import Foo to Bar, you bring there the constants and instance methods from Foo, that's why you have access to Foo::Foo by using the Bar::Foo constant and it points to the uniquely identified class Foo::Foo.
If you want to have the class Bar::Foo, it will be a different class uniquely identified by this "address". And if you want to have it identical to Foo::Foo, you'll have to use the included hook in module Foo and then do some heavy metaprogramming stuff to create a new class from the old one and assign it to a constant with the same name from the new module.
Or, maybe it could be enough just to clone the class:
module Foo
class Foo; end
def included(mod)
mod.const_set('Foo', self::Foo.clone)
end
end
module Bar
include ::Foo
end
obj1 = Foo::Foo.new
obj2 = Bar::Foo.new
Foo::Foo == Bar::Foo # false
But I'm not sure if cloning is enough for all the cases.

Namespace collision with top-level methods

I have the following Ruby namespace problem:
I have a number of top-level methods in a "library", among these I have a get_name(param) method.
The problem is that I want to use this get_name(param) method inside a class that has its own get_name() method (without param!).
Of course this results in a problem as the 2 methods are different and the new one in the class overrides the top-level one.
Is there a way to do something like this:
def get_name(param)
puts param
end
class Foo
def get_name()
puts "name"
end
def bar()
self.get_name() #should work and print name, i.e. it uses the get_name method of Foo
get_name("abc") #should work and print abc, i.e. it uses the top-level method
end
end
I would like to know if there is an easy way to achieve this without having to alter the name or scope of the top-level methods. (The goal is to have those available without modification as global methods, but still be able to call instance methods of Foo without errors.
If the library methods are really top-level methods and are not part of any class or module, then, they will get defined as private methods of Object class.
You can do following to invoke it:
def bar()
self.get_name()
Object.send(:get_name, "abc") # Will print "abc" to console
end
The so called "top level method" is only a method of main object, which is only an Object. If you want that method to be able to be called (almost) anywhere, you should make it a kernel method.
module Kernel
def get_name(param)
puts param
end
end
Note that Kernel is almost at the tail of any classes ancestors chain (before BasicObject), which means almost all the objects are a Kernel. So in your Foo class, you can override it like this:
class Foo
def get_name(*args)
return super if args.any?
puts 'name'
end
end
Edit
If you can't make those top level methods kernel methods, you can store the main object in a constant or a global variable, and call those top level methods on it.
$main = self
class Foo
def get_name(*args)
return $main.send(:get_name, *args) if args.any?
puts 'name'
end
end
First off: there is no such thing as a "top-level method". There is exactly one kind of method in Ruby: instance methods.
The method you are referring to is a private instance method of Object. Since Object is a superclass of almost any other class (excluding Object and its superclasses), if you are sure that there are no other methods with the same name somewhere else in the inheritance hierarchy, you could use Method#super_method to get access to the method and then call it:
def get_name(param)
puts param
end
class Foo
def get_name
puts 'name'
end
def bar
get_name #should work and print name, i.e. it uses the get_name method of Foo
method(:get_name).super_method.('abc') #should work and print abc, i.e. it uses the top-level method
end
end
Foo.new.bar
# name
# abc
Alternatively, if you don't know whether there are any other methods by the same name within the inheritance hierarchy, you could grab a reference to the UnboundMethod directly from Object and then bind and call it:
class Foo
def bar
get_name #should work and print name, i.e. it uses the get_name method of Foo
Object.instance_method(:get_name).bind(self).('abc') #should work and print abc, i.e. it uses the top-level method
end
end
Foo.new.bar
# name
# abc

Module and class variable scope in ruby

I am still trying to clearly understand Module/Class/Instance variables ...
My code currently looks something like this ...
module Foo
##var1 ={}
##var2 =[]
##var3 = nil
def m1(value)
##var2 << value
end
def m2(value)
##var1[##var3]=value
end
end
class Bar
include Foo
p ##var1
end
class Bar2
include Foo
p #var1
end
I am trying to create a module that contains a class-wide configuration for how each class will behave. The configuration is stored in ##var1 and ##var2. Using this code the variables are shared across ALL classes that include the module. This is not the desire result, I want each class to have it's own behavior configuration.
I have also tried creating a single class that includes the module and also creates the variables but then the variables are not accessible by the module.
module Foo
def m1(value)
##var2 << value
end
def m2(value)
##var1[##var3]=value
end
end
class T
##var1 ={}
##var2 =[]
##var3 = nil
include foo
end
class Bar < T
p ##var1
end
class Bar2 < T
p #var1
end
I have also read that having modules with class variables is not good coding style but I cannot think of a way to achieve my functionality with this ...
Thanks in advance for any help
Firstly - class variables are evil and should be avoided (because they are also inherited by all subclasses and usually causes more harm than good.
You want to create a class instance variable (not class variable) on a class or module which is including given module. It is easy to do with included method:
module Foo
#default_settings = {}
module ClassMethods
def foo_settings
#foo_settings
end
end
def self.included(target)
target.instance_variable_set('#foo_settings', #default_settings.dup)
target.extend ClassMethods
end
end

What is the difference between "include module" and "extend module" in Ruby? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
What is the difference between include and extend in Ruby?
Given:
module my_module
def foo
...
end
end
Question 1
What is the difference between:
class A
include my_module
end
and
class A
extend my_module
end
Question 2
Will foo be considered an instance method or a class method ?
In other words, is this equivalent to:
class A
def foo
...
end
end
or to:
class A
def self.foo
...
end
end
?
I wrote a blog posting about this a long time ago here.
When you're "including" a module, the module is included as if the methods were defined at the class that's including them, you could say that it's copying the methods to the including class.
When you're "extending" a module, you're saying "add the methods of this module to this specific instance". When you're inside a class definition and say "extend" the "instance" is the class object itself, but you could also do something like this (as in my blog post above):
module MyModule
def foo
puts "foo called"
end
end
class A
end
object = A.new
object.extend MyModule
object.foo #prints "foo called"
So, it's not exactly a class method, but a method to the "instance" which you called "extend". As you're doing it inside a class definition and the instance in there is the class itself, it "looks like" a class method.
1) include adds methods, constants, and variables on instances of class A; extend adds those things to the instance of the Class instance A (effectively defining class methods).
include my_module will allow this: A.new.foo
extend my_module will allow this: A.foo
More generally, include only makes sense on a Class or Module, while extend can be used to add methods to any Object.
2) In effect: when using include, foo is an instance method of A ... when using extend, foo is a class method.

How can I call a static method in a Ruby module from a class that includes the module?

Is it possible to declare static methods in a module in ruby?
module Software
def self.exit
puts "exited"
end
end
class Windows
include Software
def self.start
puts "started"
self.exit
end
end
Windows.start
The example above will not print out "exited".
Is it only possible to have instance methods in a module?
Define your module like this (i.e. make exit an instance method in the module):
module Software
def exit
puts "exited"
end
end
and then use extend rather than include
class Windows
extend Software
# your self.start method as in the question
end
In use:
irb(main):016:0> Windows.start
started
exited
=> nil
Explanation
obj.extend(module, ...) adds to
obj the instance methods from each module given as a parameter
...so when used within the context of a class definition (with the class itself as the receiver) the methods become class methods.
Put your class methods in a nested module, and then override the "included" hook. This hook is called anytime your module is included. Inside the hook, add the class methods to whomever did the include:
module Foo
def self.included(o)
o.extend(ClassMethods)
end
module ClassMethods
def foo
'foo'
end
end
end
Now any class including Foo gets a class method named foo:
class MyClass
include Foo
end
p MyClass.foo # "foo"
Any non-class methods may be defined in Foo as usual.
Two things need to change to be able to call Windows.exit:
Software#exit needs to be an instance method
Windows needs to extend Software, not include it.
This is because extending another module puts that module's instance methods as the current module's class methods, whereas includeing a module puts the methods as new instance methods.
module Software
def exit
puts "exited"
end
end
class Windows
extend Software
def self.start
puts "started"
self.exit
end
end
Windows.start
Output is:
started
exited
It is possible to include static methods in a module:
module Software
def self.exit
puts "exited"
end
end
Software.exit
Running this prints 'exited' as expected.

Resources