How do you list included Modules in a Ruby Class? - ruby

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.)

Related

How to get a class name given that self could be a class or an instance?

I have a module that provides logging functionality. Some classes extend it, others include it. As part of the logging, I pass in a class name.
If I do this:
global_logger.call(level, self, msg)
It could log either:
WARN -- Some::Class: some msg
OR
WARN -- #<Some::OtherClass:0x00007fdc04907710>: some msg
based on if the module was extended or included. I can call self.class instead, but then the the other one turns into Class.
Is there a way to get a class name(without the #<...:0x00007fdc04907710>), given that you don't know if self is a class or an instance?
There are a number of things you could do, but one of the easiest is probably to see if the passed object is a class before trying to extract other information about it. For example:
Object.kind_of? Class #=> true
Object.new.kind_of? Class #=> false
You can then decide what methods are appropriate to call based on whether or not it's a class. For example:
p Object.new.class.name #=> "Object"
P Object.name #=> "Object"
We know that, for a given module M, you can determine if a class C has included M by writing
C.included_modules.include?(M)
To determine if a class C has extended M you may execute
C.singleton_class.included_modules.include?(M)
because extending a module to a class is equivalent to including the same module to the class' singleton class.
See Module#included_modules and Object#singleton_class.
Here is an example.
module M
def m; end
end
class C
extend M
end
a = C.singleton_class.included_modules
#=> [M, Kernel]
a.include?(M)
#=> true

Clarification about constants in ruby

I'm trying to build a rest client and since a lot of is almost the same I thought I'd put the actions into a module and just extend the module to get that set of actions and keep the unique bits separate by defining them as a constant, the same constant but each route has it set differently so the end result is a different URL but the same action.
module Common
def list
"some_url/#{Route_Name}.json"
end
end
class Posts
extend Common
Route_Name = 'posts'
end
class Comments
extend Common
Route_Name = 'comments'
end
Comments.list
#=> "some_url/comments.json" what I expect to be outputted
But it just errors, in this example it would error uninitialized constant Common::Route_Name.
How do I get Route_Name to be what I expect it to be?
EDIT:
I kind of solved the issue by changing Route_Name to #route_name, but the problem is that Route_Name is constant, it will never change so it doesn't feel right using an instance variable, even if it does work.
Ruby does constant lookup first by namespace (roughly: where you've nested them) and then by ancestors (what you've inherited from). Because list is declared in Common, lookup starts with Common::Route_Name and can't go any further.
But, when list is called, it's called on Comments or Posts, giving you access to those via self. Instead of leaving Ruby to look for the constant itself, you can use self::Route_Name to make lookup start where you want it.
module Common
def list
"some_url/#{self::Route_Name}.json"
end
end
class Posts
extend Common
Route_Name = 'posts'
end
class Comments
extend Common
Route_Name = 'comments'
end
puts Comments.list #=> some_url/comments.json
puts Posts.list #=> some_url/posts.json
Use const_get instead. It's a method call (compared to lookup by static name). Meaning, that it will start the constant lookup from the bottom of the ancestor hierarchy.
module Common
def list
"some_url/#{const_get('Route_Name')}.json"
end
end
class Posts
extend Common
Route_Name = 'posts'
end
class Comments
extend Common
Route_Name = 'comments'
end
Comments.list # => "some_url/comments.json"
#mtamhankar has a good point about deriving routes from class names. Something like this, perhaps (uses tableize from ActiveSupport)
module Common
def list
"some_url/#{name.tableize}.json"
end
end
I've done something similar to this, where I've defined several controller actions (index, create, etc..) in a module, then just include MyModule in the controller in which I want to use them.
At this point, in my Module I can call self.controller_name to get where I actually am coming from.
So instead of trying to set the Route_Name, you could derive it with something like self.controller_name.tableize or self.class.tableize
#route_name as you mentioned is an instance variable and will work, but I think what you're actually looking for is ##route_name which would make it a class level variable (one per class, not per instance).
A ruby method looks up constants from that method's owner, not the receiver or the class of the receiver. When you access a constant, ruby interpreter looks up the constant along Module.nesting of the calling module or class.
X = 0
module A
X = 1
module B
p Module.nesting #=> [A::B, A]
X = 2
def self.x
X
end
end
end
A::B.x # Try commenting out any 'X = ?' to see the difference
When you call A::B.x, ruby tries to find the constant X in the namespace A::B (i.e. tries to find A::B::X), if found, it stops the lookup and returns that value immediately. If not found, then ruby tries to find X inside A, if still not fount, ruby tries to find X inside the top level namespace, which is Object.
Since classes and modules have a class/module method name which returns the name of the class/module, you can refactor your Common module like this:
module Common
def list
"some_url/#{name.downcase}.json"
end
end
And annihilate all Route_Name = 'xxx'

How does 'defining a method' work semantically?

Background:
Here is what I understand about the object model (relative to my question below):
self always references the receiver in the current stack frame.
When you are in the top level and you say def someMethod the implicit receiver is self and you are creating a method which sits in the anonymous class associated with self. This anonymous class happens to sit just under Object (self is an instance of the Object class) so when you call someMethod, Ruby "takes a step to the right", and it lands in the anonymous class, thus finding and invoking your method.
This is similar to what goes on when you define methods inside of class definitions. If, when inside a class definition, you say: def self.classMethod you are creating a method in an anonymous class that sits just underneath the Class class. Methods of this class, existing "to the right" of the class currently being defined will not be visible to instances of the new class.
My Question:
How does "defining a method in a class" happen in the first place? (semantically)
Class objects aren't supposed to be different from normal objects, right?
From what I understand about message handling, Class objects have a table as a part of their state, presumable meaning it is an instance variable, that has the names of all of its instance methods. This is how the method look up works. (If Ruby doesn't find it , it goes up one and again, presumably the directions to the next link up the chain are a part of the state of the current Class object.)
Since Ruby doesn't really care about object type, I presume it doesn't care that it's looking in Class objects specifically when doing method look up. Rather, it's just following references and looking for bits of state with certain names. So, could I create my own "class objects" without using the class keyword that don't inherit from the Class class?
If that question doesn't make any sense, then I apologize. I just want to know what happens when the interpreter encounters the def keyword.
When you write 'def something' in ruby you are adding a method to a module. Sometimes that module is a 'class' (a type of module). It all depends on what 'self' is at the time:
class Foo
# right now self is 'Foo'
class << self
# right now self is 'Class:Foo'
end
def self.bar
# right now self is 'Foo'
end
end
def Foo.buz
# right now self is 'Foo'
end
obj = Foo.new
def obj.baz
# right now self is 'Foo:0x007fe8a632fa78' (an instance)
end
A class is just a type of module. Subclassing is one way of creating a pointer from One module up to another:
class Foo
end
class Bar < Foo
end
> Bar.ancestors
=> [Bar, Foo, Object, Kernel, BasicObject]
Another way is including mixins:
module Mixin
end
class Foo
include Mixin
end
> Foo.ancestors
=> [Foo, Mixin, Object, Kernel, BasicObject]
Method dispatch works on what exists in the inheritance chain. It's a list (not a tree) of parent modules and is ordered based on when the inheritance was created:
# bar.rb
module MixinA
def something
puts "MixinA"
super
end
end
module MixinB
def something
puts "MixinB"
end
end
class Base
def something
puts "Base"
super
end
end
class Sub < Base
include MixinB
include MixinA
def something
puts "Sub"
super
end
end
obj = Sub.new
obj.something
Run:
$ ruby bar.rb
Sub
MixinA
MixinB
Inspecting the chain:
> Sub.ancestors
=> [Sub, MixinA, MixinB, Base, Object, Kernel, BasicObject]
When a method call happens in walks this list looking for the method in question. If none of the modules in the chain have the method then the search starts over at the top but instead calls method_missing. In either case, the first resolution found wins.
Yehuda Katz wrote a good article on this stuff in 2009:
http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/

Referencing constants from Module

In a tutorial, there are examples querying constants using const_get:
class This
end
p Module.const_get('This').new
and
A_CONSTANT = 42
p Module.const_get('A_CONSTANT')
I can't see any module definition. Why is Module used? Why isn't const_get('This') or self.const_get('This') enough? The class where all this is written in is Object, ancestor of BasicObject.
Does the fact that This is a constant mean that it holds the class definition inside of it? Like as it just could hold the number 42? Or to be clearer, is it the same as:
This = class
end
like an unnamed method so I can write p This and get the class definition?
Some nice knowledge you have acquired. But I don't know why are you mentioning metaprogramming in your title, when your question is all about constants. Metaprogramming is something else.
In any case, #const_get is an instance method of Module class, so it won't work at the top level, where the implicit receiver is of Object class. When you write
class Foo; end
Constant Foo gets added to Object:
Object::Foo #=> Foo
Object::Bar #=> error (we didn't define constant Bar)
This constant is available also in other classes, such as
Module::Foo #=> Foo, but with a warning
Array::Foo #=> same Foo with the same warning
Fixnum::Foo #=> ditto
In other words, your Foo defined at toplevel is visible from almost every module. But accessing toplevel constants like this is frowned upon, as this is often not what the programmer intended. You can indeed see that the constant is defined on Object:
Object.constants( false ).include? :Foo #=> true
and not on eg. Module:
Module.constants( false ).include? :Foo #=> false
However, if you use #const_get, the warning message is suppressed and you may come to think that Module::Foo actually exists:
Module.const_get( :Foo ) #=> Foo
It doesn't.

How do I tell which modules have been mixed into a class?

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

Resources