Ruby: class C includes module M; including module N in M does not affect C. What gives?
In writing my question, inevitably, I came across an answer. Here's what I came up with. Let me know if I missed an obvious, much simpler solution.
The problem seems to be that a module inclusion flattens the ancestors of the included module, and includes that. Thus, method lookup is not fully dynamic, the ancestor chain of included modules is never inspected.
In practice, Array
knows Enumerable
is an ancestor, but it doesn't care about what's currently included in Enumerable
.
The good thing is that you can include
modules again, and it'll recompute the module ancestor chain, and include the entire thing. So, after defining and including Narf
, you can reopen Array
and include Enumerable
again, and it'll get Narf
too.
class Array
include Enumerable
end
p Array.ancestors
# => [Array, Enumerable, Narf, Object, Kernel]
Now let's generalize that:# Narf here again just to make this example self-contained
module Narf
def narf?
puts "(from #{self.class}) ZORT!"
end
end
# THIS IS THE IMPORTANT BIT
# Imbue provices the magic we need
class Module
def imbue m
include m
# now that self includes m, find classes that previously
# included self and include it again, so as to cause them
# to also include m
ObjectSpace.each_object(Class) do |k|
k.send :include, self if k.include? self
end
end
end
# imbue will force Narf down on every existing Enumerable
module Enumerable
imbue Narf
end
# Behold!
p Array.ancestors
Array.new.narf?
# => [Array, Enumerable, Narf, Object, Kernel]
# => (from Array) ZORT!
Now on GitHub and Gemcutter for extra fun. Inheriting class methods from modules / mixins in Ruby
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"
How to expose included private methods as public class methods?
It seems like the instance methods on FileUtils
are all private (as mentioned in another answer here, that means they can only be called without an explicit receiver). And what you get when you include or extend is the instance methods. For example:
require 'fileutils'
class A
include FileUtils
end
A.new.pwd #=> NoMethodError: private method `pwd' called for #<A:0x0000000150e5a0>
o = Object.new
o.extend FileUtils
o.pwd #=> NoMethodError: private method `pwd' called for #<Object:0x00000001514068>
It turns out all the methods we want on FileUtils
are there twice, as private instance methods and also as public class methods (aka singleton methods).Based on this answer I came up with this code which basically copies all the class methods from FileUtils
to FileManager
:
require 'fileutils'
module FileManager
class << self
FileUtils.singleton_methods.each do |m|
define_method m, FileUtils.method(m).to_proc
end
end
end
FileManager.pwd #=> "/home/scott"
It's not pretty, but it does the job (as far as I can tell). Add class to ruby module in runtime
This question is concerned specifically with adding a class at runtime. The class is to be created within an existing module, but, as will be seen, that is almost incidental and is of questionable utility.
Adding a class to a module at runtime
To assist in the construction of classes within a given module at runtime we might construct a method class_factory
.
def class_factory(mod, class_name, consts, meths, instance_meths,
accessors)
class_obj = mod.const_set(class_name, Class.new)
consts.each { |const,val| class_obj.const_set(const,val) }
meths.each do |name,body|
class_obj.singleton_class.
instance_eval("define_method(:#{name}) #{body}")
end
instance_meths.each do |name,body|
class_obj.instance_eval("define_method(:#{name}) #{body}")
end
accessors.each do |accessor,inst_var|
class_obj.public_send(accessor, inst_var)
end
class_obj
end
Let's try it.module M
end
class_obj = class_factory(
M,
"B",
{ 'A'=>7, 'D'=>'cat' },
{ greeting: '{ |n| "Cat\'s have #{n} lives" }' },
{ initialize: '{ |name| @name = name }',
say_name: '{ "My name is #{@name}" }' },
{ attr_accessor: "name" }
)
#=> M::B
class_obj == M::B
#=> true
M::B.constants
#=> [:A, :D]
class_obj.methods(false)
#=> [:greeting]
M::B.instance_methods(false)
#=> [:say_name, :name=, :name]
class_obj.greeting(9)
#=> "Cat's have 9 lives"
M::B.greeting(5)
#=> "Cat's have 5 lives"
instance = M::B.new "Lola" # or class_obj.new "Lola"
#=> #<M::B:0x000056cb6e766840 @name="Lola">
instance.say_name
#=> "My name is Lola"
instance.name
#=> "Lola"
instance.name = "Lo"
#=> "Lo"
instance.name
#=> "Lo"
It could be that your code may contain static expressions such as these, and the only dynamic part is the construction of the class.On the other hand, the class may be used dynamically as well. For example:
mod = "M"
cl = "B"
name = "Lola"
meth = "say_name"
Then:Object.const_get("#{mod}::#{cl}").new(name).public_send(meth)
#=> "My name is Lola"
orclass_obj.new(name).public_send(meth)
#=> "My name is Lola"
How best to reference dynamically-created classesWe have just seen various ways to reference a dynamically-created class. Depending on requirements, M::B
versus class_obj
and Object.const_get("#{mod}::#{cl}")
versus class_obj
. Clearly, the use of class_obj
is simplest in both cases and has the additional advantage that references to class_obj
need not be changed if, in future, M
or B
in M::B
are changed.
Are the advantages to creating classes dynamically that are members of a module?
Recall that the main reason to create a class within a module is create a namespace, so that, for example, M1::C
and M2::C
do not create a name conflict. However, if we reference a dynamically-created class by its (unique) object (rather than its name, a constant) that is held by a variable (here class_obj
) there is no need for the namespace. So the answer to the question I posed in this section is "no".
Moreover, if we reference a dynamically-created class by its object, there is no reason to assign a name to the class. We therefore could modify class_factory
as follows:
def class_factory(consts, meths, instance_meths, accessors)
Class.new do
consts.each { |const,val| const_set(const,val) }
meths.each do |name,body|
singleton_class.
instance_eval("define_method(:#{name}) #{body}")
end
instance_meths.each do |name,body|
instance_eval("define_method(:#{name}) #{body}")
end
accessors.each do |accessor,inst_var|
public_send(accessor, inst_var)
end
end
end
class_obj = class_factory(
{ 'A'=>7, 'D'=>'cat' },
{ greeting: '{ |n| "Cat\'s have #{n} lives" }' },
{ initialize: '{ |name| @name = name }',
say_name: '{ "My name is #{@name}" }' },
{ attr_accessor: "name" }
)
#=> #<Class:0x000056cb6eaeefd0>
The object held by class_obj
is called an anonymous class because it has no name (that is a constant).class_obj.constants
#=> [:A, :D]
class_obj.methods(false)
#=> [:greeting]
class_obj.instance_methods(false)
#=> [:say_name, :name=, :name]
instance = class_obj.new "Billy-Bob"
#=> #<#<Class:0x000056cb6eaeefd0>:
# 0x000056cb6eb183d0 @name="Billy-Bob">
instance.say_name
#=> "My name is Billy-Bob"
instance.name
#=> "Billy-Bob"
instance.name = "BB"
#=> "BB"
Method-lookup path for Ruby
You can use ancestor reflection:
class C
def report
my_ancestors = self.class.ancestors
puts "my ancestors are: #{my_ancestors}"
method = my_ancestors[2].instance_method(:report)
method.bind(self).call
end
end
C.new.report
=> my ancestors are: [C, N, M, Object, PP::ObjectMixin, Kernel, BasicObject]
=> 'report' method in module M
Calling Ruby class methods from C++
First off, go
is, as you've defined it, not a class method, but an instance method.
As an object oriented language, all ruby methods require a receiver, that is, an object that the method is invoked on. For instance methods, the receiver is an instance of the class, for class methods, the receiver is the class object itself.
The ? placeholder you have is the slot for the receiver of the method call.
If you want to leave it as an instance method, then you need to do this:
rb_funcall(a_CallTest_instance, rb_intern("go"), 0);
where a_CallTest_instance
was an instance of CallTest you created using rb_class_new_instance()
.If you make it into a class method:
class CallTest
def self.go
# ...
end
end
Then you need to use the CallTest
class itself as the receiver:rb_funcall(klass, rb_intern("go"), 0);
You can get a reference to the CallTest
class using rb_const_get()
VALUE klass = rb_const_get(rb_cObject, rb_intern('CallTest'));
Use rb_cObject
there, since CallTest
is defined in the global context.I'd suggest reading the chapter in the Pickaxe about extending ruby.
self.included – including class methods from a module in Ruby
What's the difference between the two examples?
The first code block adds the class methods inClassMethods
to the including class and calls the scope
method on it as well. The second one does neither of these things and will result in a NoMethodError
because the module has no scope
class method. self.some_class_method
will not be available on the including class once the module is included.For the full story on how module inclusion works in Ruby, read my answer here:
Inheriting class methods from modules / mixins in Ruby
What's the point of self.included
if a module's sole purpose is to be included?
Inclusion is not the only purpose of modules. They are also used for other things such as namespacing or simply storing various class methods that are then callable on the module itself.Why doesn't Ruby include class methods automatically?
Theoretically Ruby could automatically add all class methods defined in a module to the including class, but in practice that would be a bad idea, because you would not get to choose anymore whether you want to include class methods — all class methods would be included every time, whether or not they are intended to be included. Consider this example:module M
def self.class_method
"foo"
end
def self.configure_module
# add configuration for this module
end
end
class C
include M
end
Here, the configure_module
method is obviously not supposed to be added to C
, as its purpose is to set the configuration for the module object. Yet, if we had auto-inclusion for class methods, you would not be able to prevent it from being included.But all instance methods are already included! How is that okay then?
Instance methods in a module are only really useful if they are included into a class, since modules cannot have instances, only classes can. So in a module every instance method is expected to be included somewhere to work.
A "class" method on a module is different, because it can be called on the module itself, so it can be used just fine regardless of whether it's also added to the including class. That is why it is better that you have a choice there.
Ruby: Raise error in Module if class method not found
Sounds like you want to make use of method_missing
and define_method
.
If you do use method_missing
don't forget to:
- call
super
for unhandled cases. - also implement a
respond_to?
method
Update:
It sounds the goal is to do static method checking like Java or c++ does. This is not really meaningful in ruby :-(
Since in ruby:
- Each instance of an object has its own eigenclass. A given object may have the necessary methods mixed in at runtime. So just because
Foo
does not have a method at class load time is meaningless. - Frameworks like RoR hooks method_missing and dynamically create methods needed for the database query methods, so the method may exist (or not) when it is needed.
class Foo
p "Hi"
end
You will see "Hi" the first and only the first time Foo is used. This is how things like devise hook into do their magic.class User < ActiveRecord::Base
# **CALL 'devise' method**
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
# **CALL attr_accessible method**
attr_accessible :email, :password, :password_confirmation
end
So maybe by private convention have developers add a check_class
method call to the bottom of the classes in question?I understand the intent but it seems like fighting the way ruby is designed to work.
As a mostly Java person I appreciate the frustration. Let me guess: repeated cases of code getting pushed to production that had missing methods? :-P
Update2:
wrt onload
In ruby barring use of frozen a class get new methods defined all the time. ( Or an instance can get new methods defined just for that instance. ) so checking for a method's nonexistence is only a snapshot check and not as definitive a check as a static language brings to the table. This is ruby's very own Halting problem
Is extend self the same as module_function?
module_function
makes the given instance methods private, then duplicates and puts them into the module's metaclass as public methods. extend self
adds all instance methods to the module's singleton, leaving their visibilities unchanged.
module M
extend self
def a; end
private
def b; end
end
module N
def c; end
private
def d; end
module_function :c, :d
end
class O
include M
include N
end
M.a
M.b # NoMethodError: private method `b' called for M:Module
N.c
N.d
O.new.a
O.new.b # NoMethodError: private method `b' called for O
O.new.c # NoMethodError: private method `c' called for O
O.new.d # NoMethodError: private method `d' called for O
Related Topics
How to Send a Keep-Alive Packet Through Websocket in Ruby on Rails
Rails on Netbeans: Uncaught Exception: No Such File to Load - Script/Server or Script/Console
How to Generate The Form for an Existing Model in Rails
How to Get Records Created at The Current Month
Binding to Networking Interfaces in Ruby
How to Remove Header and Footer from Some of The Pages in Ruby on Rails
Rails How to Create Data Schema Seed Data
Converting Ruby Hashes to Arrays
Starting with Redmine Locally - How Easy Is Migration to Server Later
Can Sunspot Search Inside Array
Extend Model in Plugin with "Has_Many" Using a Module
Sha1 Hashes Not Matching Between My Rails and Cocoa Apps
Importing CSV Data into a Ruby Array/Variable
How to Find Word with The Greatest Number of Repeated Letters
Understanding The Behaviour of Inject Used with a Lambda in Ruby
Error Installing Gems: Cannot Load Such File - Zlib
How to Compare Xml Output in a Cucumber Step Using a Multiline String Example