I want to share a method amongst all Ruby's data object classes. That is to say the following classes:
Hash
String
Number
Array
Firstly, any classes I've missed out?
Secondly, how can I share my_method amongst all classes at once?
class Hash
def my_method
"hi"
end
end
class String
def my_method
"hi"
end
end
class Number
def my_method
"hi"
end
end
class Array
def my_method
"hi"
end
end
This is what modules are for:
module MyModule
def my_method
"hi"
end
end
class Hash
include MyModule
end
class String
include MyModule
end
...
You can also make it shorter with some metaprograming:
[Hash, String, Number, Array].each do |klass|
klass.instance_eval do
include MyModule
end
end
In newer ruby versions (since ruby 2.1.0), include is no longer private method, hence you can simplify the above to:
[Hash, String, Number, Array].each do |klass|
klass.include MyModule
end
Another way to achieve this is to include it within the parent class, like Object:
class Object
def my_method
"hi"
end
end
{}.my_method
This however will add this method to all the classes and will be available everywhere, so be careful.
Regarding whether you missed any classes - you missed tones of it, it all depends on what are you actually trying to achieve here.
I'm trying to write a ruby module with some meta-programming features, but I'm getting a bit confused.
module MetaModule
extend ActiveSupport::Concern
module ClassMethods
def my_method(attribute)
# Define an attr_accessor for the original class
attr_accessor :test_accessor
# This is clearly wrong, but I don't know what is correct
self.test_accessor ||= []
self.test_accessor << attribute
end
end
end
class MyClass
include MetaModule
my_method :name
my_method :age
my_method :city
end
My desired output is: MyClass.new.test_accessor => [:name, :age, :city]
I think there might be a little bit of a mix up here. It is certainly possible to construct a module that will have your desired output, but ultimately it will look something like this
module MetaModule
extend ActiveSupport::Concern
module ClassMethods
def my_method(attribute)
# Define an attr_accessor for the original class
#class_test_accessor ||= []
#class_test_accessor << attribute
end
def class_test_accessor
#class_test_accessor
end
end
def test_accessor
self.class.class_test_accessor
end
end
But you might notice that ultimately we are adding an instance method that simply accesses a class instance variable. Because my_method is a class method, its value wont change per instance. Therefore I would suggest accessing it simply as self.class.class_test_accessor within an instance. If there is something else you were hoping to accomplish w/ my_method (like seed a class_test_accessor and then modify per instance) let me know and I will try to help.
It is known that in Ruby, class methods get inherited:
class P
def self.mm; puts 'abc' end
end
class Q < P; end
Q.mm # works
However, it comes as a surprise to me that it does not work with mixins:
module M
def self.mm; puts 'mixin' end
end
class N; include M end
M.mm # works
N.mm # does not work!
I know that #extend method can do this:
module X; def mm; puts 'extender' end end
Y = Class.new.extend X
X.mm # works
But I am writing a mixin (or, rather, would like to write) containing both instance methods and class methods:
module Common
def self.class_method; puts "class method here" end
def instance_method; puts "instance method here" end
end
Now what I would like to do is this:
class A; include Common
# custom part for A
end
class B; include Common
# custom part for B
end
I want A, B inherit both instance and class methods from Common module. But, of course, that does not work. So, isn't there a secret way of making this inheritance work from a single module?
It seems inelegant to me to split this into two different modules, one to include, the other to extend. Another possible solution would be to use a class Common instead of a module. But this is just a workaround. (What if there are two sets of common functionalities Common1 and Common2 and we really need to have mixins?) Is there any deep reason why class method inheritance does not work from mixins?
A common idiom is to use included hook and inject class methods from there.
module Foo
def self.included base
base.send :include, InstanceMethods
base.extend ClassMethods
end
module InstanceMethods
def bar1
'bar1'
end
end
module ClassMethods
def bar2
'bar2'
end
end
end
class Test
include Foo
end
Test.new.bar1 # => "bar1"
Test.bar2 # => "bar2"
Here is the full story, explaining the necessary metaprogramming concepts needed to understand why module inclusion works the way it does in Ruby.
What happens when a module is included?
Including a module into a class adds the module to the ancestors of the class. You can look at the ancestors of any class or module by calling its ancestors method:
module M
def foo; "foo"; end
end
class C
include M
def bar; "bar"; end
end
C.ancestors
#=> [C, M, Object, Kernel, BasicObject]
# ^ look, it's right here!
When you call a method on an instance of C, Ruby will look at every item of this ancestor list in order to find an instance method with the provided name. Since we included M into C, M is now an ancestor of C, so when we call foo on an instance of C, Ruby will find that method in M:
C.new.foo
#=> "foo"
Note that the inclusion does not copy any instance or class methods to the class – it merely adds a "note" to the class that it should also look for instance methods in the included module.
What about the "class" methods in our module?
Because inclusion only changes the way instance methods are dispatched, including a module into a class only makes its instance methods available on that class. The "class" methods and other declarations in the module are not automatically copied to the class:
module M
def instance_method
"foo"
end
def self.class_method
"bar"
end
end
class C
include M
end
M.class_method
#=> "bar"
C.new.instance_method
#=> "foo"
C.class_method
#=> NoMethodError: undefined method `class_method' for C:Class
How does Ruby implement class methods?
In Ruby, classes and modules are plain objects – they are instances of the class Class and Module. This means that you can dynamically create new classes, assign them to variables, etc.:
klass = Class.new do
def foo
"foo"
end
end
#=> #<Class:0x2b613d0>
klass.new.foo
#=> "foo"
Also in Ruby, you have the possibility of defining so-called singleton methods on objects. These methods get added as new instance methods to the special, hidden singleton class of the object:
obj = Object.new
# define singleton method
def obj.foo
"foo"
end
# here is our singleton method, on the singleton class of `obj`:
obj.singleton_class.instance_methods(false)
#=> [:foo]
But aren't classes and modules just plain objects as well? In fact they are! Does that mean that they can have singleton methods too? Yes, it does! And this is how class methods are born:
class Abc
end
# define singleton method
def Abc.foo
"foo"
end
Abc.singleton_class.instance_methods(false)
#=> [:foo]
Or, the more common way of defining a class method is to use self within the class definition block, which refers to the class object being created:
class Abc
def self.foo
"foo"
end
end
Abc.singleton_class.instance_methods(false)
#=> [:foo]
How do I include the class methods in a module?
As we just established, class methods are really just instance methods on the singleton class of the class object. Does this mean that we can just include a module into the singleton class to add a bunch of class methods? Yes, it does!
module M
def new_instance_method; "hi"; end
module ClassMethods
def new_class_method; "hello"; end
end
end
class HostKlass
include M
self.singleton_class.include M::ClassMethods
end
HostKlass.new_class_method
#=> "hello"
This self.singleton_class.include M::ClassMethods line does not look very nice, so Ruby added Object#extend, which does the same – i.e. includes a module into the singleton class of the object:
class HostKlass
include M
extend M::ClassMethods
end
HostKlass.singleton_class.included_modules
#=> [M::ClassMethods, Kernel]
# ^ there it is!
Moving the extend call into the module
This previous example is not well-structured code, for two reasons:
We now have to call both include and extend in the HostClass definition to get our module included properly. This can get very cumbersome if you have to include lots of similar modules.
HostClass directly references M::ClassMethods, which is an implementation detail of the module M that HostClass should not need to know or care about.
So how about this: when we call include on the first line, we somehow notify the module that it has been included, and also give it our class object, so that it can call extend itself. This way, it's the module's job to add the class methods if it wants to.
This is exactly what the special self.included method is for. Ruby automatically calls this method whenever the module is included into another class (or module), and passes in the host class object as the first argument:
module M
def new_instance_method; "hi"; end
def self.included(base) # `base` is `HostClass` in our case
base.extend ClassMethods
end
module ClassMethods
def new_class_method; "hello"; end
end
end
class HostKlass
include M
def self.existing_class_method; "cool"; end
end
HostKlass.singleton_class.included_modules
#=> [M::ClassMethods, Kernel]
# ^ still there!
Of course, adding class methods is not the only thing we can do in self.included. We have the class object, so we can call any other (class) method on it:
def self.included(base) # `base` is `HostClass` in our case
base.existing_class_method
#=> "cool"
end
As Sergio mentioned in comments, for guys who are already in Rails (or don’t mind depending on Active Support), Concern is helpful here:
require 'active_support/concern'
module Common
extend ActiveSupport::Concern
def instance_method
puts "instance method here"
end
class_methods do
def class_method
puts "class method here"
end
end
end
class A
include Common
end
You can have your cake and eat it too by doing this:
module M
def self.included(base)
base.class_eval do # do anything you would do at class level
def self.doit #class method
##fred = "Flintstone"
"class method doit called"
end # class method define
def doit(str) #instance method
##common_var = "all instances"
#instance_var = str
"instance method doit called"
end
def get_them
[##common_var,#instance_var,##fred]
end
end # class_eval
end # included
end # module
class F; end
F.include M
F.doit # >> "class method doit called"
a = F.new
b = F.new
a.doit("Yo") # "instance method doit called"
b.doit("Ho") # "instance method doit called"
a.get_them # >> ["all instances", "Yo", "Flintstone"]
b.get_them # >> ["all instances", "Ho", "Flintstone"]
If you intend to add instance, and class variables, you will end up pulling out your hair as you will run into a bunch of broken code unless you do it this way.
possibly I'm not explaining the concept very well, but I'm looking to add class methods to a series of ruby classes to enable them to hold class specific information which will then be called by individual instance methods of the classes.
I can make it work, but it is a bit ugly. Can anyone as it requires 2 modules, one included and the other extended (see example code below).
Can anyone think of a more elegant way of implementing this functionality ?
Thanks
Steve
This module is extended to give class methods but adding an instance member to each class it is included in
module My1
def my_methods (*sym_array)
#my_methods=sym_array
end
def method_list
#my_methods
end
end
This module is included to give instance methods
module My2
def foo
self.class.method_list.each { |m| self.send m }
end
end
Now use the modules - the ugliness is having to use an include and extend statement to allow me to pass a set of symbols to a class method which will then be implemented in an
instance
class Foo
extend My1
include My2
my_methods :baz
def baz
puts "Baz!"
end
end
class Bar
extend My1
include My2
my_methods :frodo
def frodo
puts "Frodo!"
end
end
class Wibble < Bar
extend My1
include My2
my_methods :wobble
def wobble
puts "Wobble!"
end
end
Here is the required output - note that each class has its own instance #my_methods so the behaviour is different for the derived class Wibble < Bar
f=Foo.new
b=Bar.new
w=Wibble.new
f.foo #=> "Bar!"
b.foo #=> "Frodo!"
w.foo #=> "Wobble!"
When a module is included, a hook is called on it. You can use that to do the extend you want.
module M1
def self.included(base)
base.extend(M2)
end
end
People often call that second module M1::ClassMethods. If you're using rails, ActiveSupport::Concern encapsulates this pattern
I would suggest to use a hook from module instead:
module MyModule
def self.included(klass)
klass.extend ClassMethods
end
def foo
self.class.method_list.each{ |m| self.send m }
end
module ClassMethods
attr_reader :method_list
def my_methods(*sym_array)
#method_list = sym_array
end
end
end
So it simplifies to call include only a module whenever you want the functionality to given classes.
class Foo
include MyModule
my_methods :baz
def baz
puts "Baz!"
end
end
I have a Mix-in that reflects on the receiver class to generate some code. This means that I need to execute the class method at the end of the class definition, like in this trivially dumbed down example:
module PrintMethods
module ClassMethods
def print_methods
puts instance_methods
end
end
def self.included(receiver)
receiver.extend ClassMethods
end
end
class Tester
include PrintMethods
def method_that_needs_to_print
end
print_methods
end
I'd like to have the mixin do this for me automatically, but I can't come up with a way. My first thought was to add receiver.print_methods to self.included in the mixin, but that won't work because the method that I want it to reflect on has not been declared yet. I could call include PrintMethods at the end of the class, but that feels like bad form.
Are there any tricks to make this happen so I don't need to call print_methods at the end of the class definition?
First of all, there's no end of class definition. Remember that in Ruby you can reopen the Tester class method after you have 'initialized' it, so the interpreter can't know where the class 'ends'.
The solution I can come up with is to make the class via some helper method, like
module PrintMethods
module ClassMethods
def print_methods
puts instance_methods
end
end
def self.included(receiver)
receiver.extend ClassMethods
end
end
class Object
def create_class_and_print(&block)
klass = Class.new(&block)
klass.send :include, PrintMethods
klass.print_methods
klass
end
end
Tester = create_class_and_print do
def method_that_needs_to_print
end
end
But certainly having to define classes this way makes my eyes hurt.