I include and extend some modules into my class.
I put binding.pry then step into it. Use self.methods to list all methods I have.
But the list is too long , How could I list the methods only in the modules I include. Excepts the methods in my self.class and the methods from ancestors like inspect to_s , ...
include SnapshotsHelper
extend SnapshotsHelper
Thanks
I think something like below you are looking for :
when you will include modules
module A
def meth1;end
def meth2;end
end
module B
def meth11;end
def meth21;end
end
class C
include A,B
end
list = C.included_modules.each_with_object({}) do |m,ob|
module_arry = m.instance_methods
ob[m]=C.instance_methods.select{|e| module_arry.include?(e)} if m != Kernel
end
p list
# >> {A=>[:meth1, :meth2], B=>[:meth11, :meth21]}
when you will extend modules
module A
def meth1;end
def meth2;end
end
module B
def meth11;end
def meth21;end
end
class C
extend A,B
end
list = C.singleton_class.included_modules.each_with_object({}) do |m,ob|
module_arry = m.instance_methods
class_methods = C.methods - C.instance_methods
ob[m]= class_methods.select{|e| module_arry.include?(e)} if m != Kernel
end
p list
# >> {A=>[:meth1, :meth2], B=>[:meth11, :meth21]}
I suppose you want only methods from modules included in the very child you're in, filtering out methods from modules included in parents. If you also want modules included from parents, #Arup answer is the good one.
There's no provided core method to do what you want, but you can create one if you need this often.
Prerequisite knowledge
There are three sources of methods for a given class :
methods defined in the class itself and its monkey patches
methods from parent
methods from modules
Method from modules may be inserted in class itself or from parents (or even by hook in modules, but let's keep that aside).
Note also that Class inherits from Module, so a Class is a Module.
How to retrieve modules
Now, having all of that, what you want to do is to get all modules included in your current class, remove parent modules and get methods. The basic formula is thus :
methods_from_module_included_in_current_class = ( modules_in_class - modules_in_parent ).methods
To get all module in class, you can use the #ancestors method. It will list all classes and modules which has been mixed in current class (remember : a class is just a module).
modules_in_class = MyClass.ancestors.reject { |ancestor| Class === ancestor }
Here, we reject what is a Class from the list (Class === ancestor is the same than doing ancestor.is_a? Class), so we know we only have modules. Again, we can't just select modules, since Class are Module.
Finally, we can apply the same pattern to get modules from parent :
module_in_parent = MyClass.superclass.ancestors.reject { |ancestor| Class === ancestor }
Getting methods
So, to get all your methods from modules included in current class :
( MyClass.ancestors.reject { |ancestor| Class === ancestor } - MyClass.superclass.ancestors.reject { |ancestor| Class === ancestor } ).map( &:instance_methods ).flatten
To get class methods (thus methods added using #extend) rather, replace instance_methods with methods.
Note you may still have a few unfamiliar methods here if some anonymous module is declared on class initialization, like rails does on ActiveRecord::Base.
Factoring that
If you find yourself using this frequently, you can factorize it in a method :
class Object
def inner_module_methods
( self.class.ancestors.reject { |ancestor| Class === ancestor } - self.class.superclass.ancestors.reject { |ancestor| Class === ancestor } ).map( &:instance_methods ).flatten
end
end
You can now do :
obj.inner_module_methods
Or within pry :
inner_module_methods
Alternative
You may also want to filter methods by source file. If find this being more effective and volatile, in the long run.
Related
During long run I prepend foreign classes with my anonymous modules (that are being created on the fly.)
At some point, given an instance of arbitrary class, I need to list my anonymous modules, prepended to it. Unfortunately, Module#new, unlike Class#new, does not accept a parent parameter (in other words, they cannot all have a same common dummy parent, that I could use to sieve my modules out of all ancestors.)
I could define a specific my_litmus_dummy_method on each of my anonymous modules and check for it while ancestors.select, but this is ugly.
I could include DummyLitmusModule in each of my anonymous modules and check for it being an ancestor while ancestors.select, but this is ugly either.
So, my question is: is there any elegant way to mark my anonymous modules so that later on I could easily sieve them out of all modules list?
UPD Code example
def patch_class klazz
klazz.send :prepend, Module.new { define_method(:my_method) {} }
end
...
# load list of classes from somewhere and patch them
classes = my_parse_yaml('config.yml')
classes.each do |c|
patch_class c
end
...
# somewhere in other place
instance.patches # ⇐ I want to implement this
At this point I need^W want to know, what methods were patched. I kinda reimplement alias_method_chain from Rails and I want to report the “usages” to my interface user.
What about patching Module#prepend?
class Module
attr_reader :mods_prepended
alias :old_prepend :prepend
def prepend(*mods)
#mods_prepended =|| {}
(#mods_prepended[self] =|| []).concat(mods)
end
old_prepend(*mods)
end
class C
def patches
Module.mods_prepended[self.class]
end
end
Then the desired information is given by:
C.new.patches
Store your anonymous modules somewhere and then you will be able to find them:
patches = [Module.new] # some other module
c = Class.new
m = Module.new
c.prepend m
patches << m
instance = c.new
used_patches = patches.select{|p| instance.is_a? p}
Suppose I have a module called Flight with both class and instance methods. I can get its methods into a class using include, extend, or both:
class Bat < Mammal
# Add Flight's class methods to Bat.
extend Flight
# Add Flight's instance methods to Bat.
include Flight
...
end
include will add Flight to Bat.ancestors, but extend will not.
My question is, why is this different for modules than for classes? When I subclass Mammal, I always get both class and instance methods at once. However, when I mix in a module, I cannot get both class and instance methods at once (unless I use the self.included hook or something like ActiveSupport::Concern).
Is there a language-design issue behind this difference?
Both Module#include and Object#extend are used to add the instance methods of a Module to an Object.
Given the module:
module Flight
def can_fly?
true
end
end
Module#include is used to add (or mix in) the instance methods of a module to the instance methods of a class or a module:
class Bat < Mammal
include Flight
end
a = Bat.new()
a.can_fly? # true
It actually affects the Object#is_a? method, so:
a.is_a? Flight # true
Module#include is a private method, so it can only be called with function notation when defining a class or another module:
class Bat < Mammal
self.include Flight # NoMethodError: private method called
end
Object#extend adds the instance methods of a module as singleton methods to the object on which it's called, so you can do this:
b = Mammal.new()
b.extend Flight
b.can_fly? # true
b.is_a? Flight # true
c = Mammal.new()
c.can_fly? # NoMethodError: undefined method
And only b will have the instance methods from Flight; other Mammal objects won't.
When calling Object#extend inside a class definition, the methods are added to the eigenclass of the class you're defining.
This is the important difference between the two methods when using them inside a class definition, because the methods are added as class methods:
class Bat < Mammal
extend Flight
end
Bat.can_fly? # true
d = Bat.new
d.can_fly? # NoMethodError: undefined method
I would like to address one part of your question:
include will add Flight to Bat.ancestors, but extend will not.
extend is not the same as include so it does something different obviously... You can think of extend being equal to an include on the class' metaclass.
Have a look at the following example:
module M
end
class A
include M
end
# then you will see M within A's ancestors as you know
A.ancestors # => [A, M, Object...]
class B
# the following is roughly the same as extend M:
class <<self
include M
end
end
# then you will see M within B's metaclass' ancestors
MetaclassOfB = class <<B; self; end
MetaclassOfB.ancestors # => [M, Class, Module...]
So, since extend is like an include on the metaclass, you see the extended modules showing up in the metaclass' ancestor chain...
"When I subclass Mammal, I always get both class and instance methods at once"
That's because Bat class as an object also inherited the instance methods from Mammal singleton class.
Including a module into a class changes the method look up chain. So actually the class doesn't inherit any instance methods.
Extending a class with a module is same as extending any object. The class simply acquires the module's instance methods as class instance methods (ie. methods on the class object itself).
Is there a way to override a class's operator, by creating a new operator method inside a module, then mixing that module into the class?
eg, this overrides Fixnum's + operator:
class Fixnum
def +(x)
product = x
product = product * self
return product
end
end
p 3 + 3
# => 9
This does not override Fixnum's + operator:
module NewOperators
def +(x)
product = x
product = product * self
return product
end
end
class Fixnum
include NewOperators
end
p 3 + 3
# => 6
Your question led me to this interesting article which describes the problem:
Fixing Ruby’s Inheritance Model with Metamorph
This is the ultimate problem with Ruby’s inheritance model, in my opinion. Because mixins always take lower priority than methods defined directly in a class body, A#foo can not be overridden by mixin inclusion. Moreover, because mixins take lower priority than methods, A#foo is now violating encapsulation [...]
And his solution is to transparently define all new methods inside an anonymous inner module:
class Object
def self.method_added(name)
return if name == :initialize
const_set(:InstanceMethods, Module.new) unless defined?(self::InstanceMethods)
meth = instance_method(name)
self::InstanceMethods.send(:define_method, name) {|*args, &block| meth.bind(self).call(*args, &block) }
remove_method(name)
include self::InstanceMethods
end
end
This is also conveniently packaged in a library called metamorph, which allows you to have mixin methods override class methods by just requiring it.
No, because in a method lookup you don't get a chance to pull in methods that were defined in mixins or parent classes until you've actually looked at the current class.
That said, you can create a method that, when called on a class, will create methods in that class. This can give you a way to inject operators into classes easily.
Be warned that actually doing this is a good recipe for surprise. This is doubly true when you are changing standard modules whose behavior is relied upon by others. See http://avdi.org/devblog/2008/02/23/why-monkeypatching-is-destroying-ruby/ for context.
How would you list out the modules that have been included in a specific class in a class hierarchy in Ruby? Something like this:
module SomeModule
end
class ParentModel < Object
include SomeModule
end
class ChildModel < ParentModel
end
p ChildModel.included_modules #=> [SomeModule]
p ChildModel.included_modules(false) #=> []
Listing the ancestors makes the module appear higher in the tree:
p ChildModel.ancestors #=> [ChildModel, ParentModel, SomeModule, Object, Kernel]
As far as I understand your question, something like this is what you are looking for:
class Class
def mixin_ancestors(include_ancestors=true)
ancestors.take_while {|a| include_ancestors || a != superclass }.
select {|ancestor| ancestor.instance_of?(Module) }
end
end
However, I don't fully understand your testcases: why is SomeModule listed as an included module of ChildModel even though it isn't actually included in ChildModel but in ParentModel? And conversely, why is Kernel not listed as an included module, even though it is just as much in the ancestors chain as SomeModule? And what does the boolean argument to the method mean?
(Note that boolean arguments are always bad design: a method should do exactly one thing. If it takes a boolean argument, it does by definition two things, one if the argument is true, another is the argument is false. Or, if it does only one thing, then this can only mean that it ignores its argument, in which case it shouldn't take it to begin with.)
I have a class that has a number of modules that are mixed in with it based on some runtime criteria.
I want to be able to get a list of which modules have been mixed into this class. How can you do that?
UPDATE
So when I said class I meant object as it is the object that is being extended at runtime using:
obj.extend(MyModule)
obj.included_modules and obj.ancestors don't exist so you can't get the modules that have been mixed in from there.
Try:
MyClass.ancestors.select {|o| o.class == Module }
for example:
>> Array.ancestors.select {|o| o.class == Module}
=> [Enumerable, Kernel]
UPDATE
To get the modules mixed into an object instance at runtime you'll need to retrieve the eigenclass of the instance. There is no clean way to do this in Ruby, but a reasonably common idiom is the following:
(class << obj; self; end).included_modules
If you find yourself using this a lot, you can make it generally available:
module Kernel
def eigenclass
class << self
self
end
end
end
and the solution is then:
obj.eigenclass.included_modules
This might be a better idea:
MyClass.included_modules
irb(main):001:0> Array.included_modules
=> [Enumerable, Kernel]
If you're looking for the whole list, Swanand's answer is your best bet.
If, on the other hand, you want to check whether a class includes a particular module, the < operator is your friend:
module Foo
end
class Bar
include Foo
end
Bar < Foo
# => true
obj.singleton_class.included_modules
And if you want to check if module included:
obj.singleton_class.include? MyModule
Here is another effective way to see if a module has been included or extended by a class.
As others have mentioned, you can determine whether a module is included on a class by checking the included_modules class method that exists on all classes in ruby.
So if you had a class named MyClass and you wanted to see if it included the Comparable module you could do something like this:
# will return true if MyClass.include(Comparable)
MyClass.included_modules.include?(Comparable)
If you want to determine whether your class has extended the ActiveRecord::Querying module, as all rails model classes do, you can actually use this:
# will return true if MyClass.extend(ActiveRecord::Querying)
MyClass.kind_of?(ActiveRecord::Querying)
Why does this work? To quote the book Eloquent Ruby, by Russ Olsen:
When you mix a module into a class, Ruby rewires the class hierarchy a bit, inserting the module as a sort of pseudo superclass of the class.
This also means that another way to determine whether a module has been included into your class is to do something like this (although I still prefer the included_modules method):
# will also return true if MyClass.include(Comparable)
MyClass.new.kind_of?(Comparable)
Only classes can story methods and when you add a method to an instance you are actualy adding it to the objects metaclass. The module you are looking for will be in this metaclass ancestors list.
module TestModule; end
obj = "test"
obj.extend(TestModule)
class Object
def metaclass
class << self; self; end
end
end
obj.metaclass.ancestors
# => [TestModule, String, Comparable, Object, Kernel, BasicObject]
I like #AlexParamonov's solution, but I played around and found out that this works just as well:
obj.class.include? MyModule
MyClass.include? MyModule
http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-include-3F