For example we have:
module X
def test
"X"
end
end
module Y
def test
"Y"
end
end
class L
include X
end
L.new.test #=> "X"
class L
include Y
end
L.new.test #=> "Y"
class L
include X
end
L.new.test #=> "Y"
Here's the strange thing. Why the last include didn't change the method to X's method?
Ruby does not allow to include one module twice, attempt to do it second time is just ignored.
From documentation of Module#append_features which is used by Module#include under the covers:
Ruby's default implementation is to add the constants, methods, and module variables of this module to mod if this module has not already been added to mod or one of its ancestors.
Related
In Ruby, constant lookup is affected by nesting and methods retain their nesting.
For example, if I have these modules:
module A
X = 1
end
module B
X = 2
end
module Foo
end
I can define a method Foo.a that has a nesting of [A, Foo]:
module Foo
module ::A
Module.nesting #=> [A, Foo]
def Foo.a
X
end
end
end
Foo.a #=> 1
And a method Foo.b that has a nesting of [Foo, B]:
module B
module ::Foo
Module.nesting #=> [Foo, B]
def Foo.b
X
end
end
end
Foo.b #=> 2
The difference becomes apparent if I define Foo::X:
Foo::X = 3
Foo.a #=> 1 <- still resolves to A::X
Foo.b #=> 3 <- now resolves to Foo::X
But how do I determine the nesting of a given method?
This would work:
Foo.method(:a).to_proc.binding.eval('Module.nesting')
#=> [A, Foo]
Foo.method(:b).to_proc.binding.eval('Module.nesting')
#=> [Foo, B]
Tested with Ruby 2.2.1 and 2.3.1.
It doesn't work with Ruby 2.1.5.
You are thinking about it the wrong way. There is no such thing as "nesting of methods". Constants are nested somewhere. Nesting has path resolution connected to names of modules and classes. Methods are contained within a module/class (be it "normal" or singleton).
Where a method is placed is semantical. There is a concept, similar to self, which determines where a method will be defined, called default definee. There is no keyword for it, but it's roughly equivalent to:
kind_of?(Module) ? name : self.class.name
Where a constant is placed/searched for is purely syntactical. When you refer to X, it does not care the least bit if you placed it a method or not:
DEEP_MIND = Object.new
module Foo
X = 42
end
module Foo
module Bar
def DEEP_MIND.talk
p X
end
end
end
DEEP_MIND.talk # => 42
module Foo::Bar
def DEEP_MIND.talk
p X
end
end
DEEP_MIND.talk # => uninitialized constant
All it cares about is what the "current nesting" on the line of the code where you tried to reference it.
Now, if you actually wanted to find "the current nesting inside the body of the method", then you need some way to pretend you are actually there.
Sadly, I don't think there is any other way other than the one showed in #Eric's answer. Using block with instance_eval/instance_exec will give you the nesting of where the block was defined.
How can I write a ruby module that imposes some conditions on classes in which it is included, which must be met the end of currently opened class definition?
To be concrete, suppose the condition is "a class variable ##foo should be defined to be >0 "
I'd like to write a module that looks something like this:
module NeedPositiveFoo
module ClassMethods
def validate_positive_foo
raise unless defined?(##foo) && ##foo > 0
end
end
def included(other)
other.extend(ClassMethods)
end
end
Then this class definition would be valid:
class ValidClass
include NeedPositiveFoo
##foo = 3
end
But these class definitions would raise after their closing end's:
class InvalidClass1
include NeedPositiveFoo
# ##foo is not defined
end
class InvalidClass2
include NeedPositiveFoo
##foo = -2
end
While the answer by Uri Agassi perfectly works when you are allowed to put includes in the very end of class definition, the code below will work despite where include was placed.
def included(other)
TracePoint.new(:end) do |tp|
if tp.self == other
tp.disable
raise unless defined?(other.class_variable_get(:##foo)) # checks
end
end.enable
other.extend(ClassMethods)
end
TracePoint documentation.
You can't hook the end of a class definition, because there isn't one - a class can be declared in different files, at different times, and in different libraries even.
What you can do, is check the condition when the module is included, and declare the inclusion at the end of your definition:
module NeedPositiveFoo
def included(other)
raise unless defined?(##foo) && ##foo > 0
end
end
class ValidClass
##foo = 3
include NeedPositiveFoo
end
class InvalidClass1
# ##foo is not defined
include NeedPositiveFoo
end
class InvalidClass2
##foo = -2
include NeedPositiveFoo
end
class InvalidClass3
include NeedPositiveFoo
##foo = 4 # declared after inclusion - not a valid state...
end
When asking questions like this, it is often useful to see how the Ruby core library does it. There are two well-known mixins in the Ruby core library which place certain conditions on the classes they are being mixed into:
Enumerable requires that the class have an each method that can be called with zero arguments and takes a block, to which it yields all the elements of the collection successively.
Comparable requires that the class have a <=> method that can be called with a single argument and responds with either -1, 0, or 1, depending on whether the argument is considered to be greater than, equal, or less than the receiver.
In both of these cases, the requirements are simply stated in the documentation, not in code. It is up to the class author to make sure they are met.
In fact, in the Enumerable case, the requirements are not really stated at all, it is just assumed that any competent Ruby programmer knows them.
I would follow this style set out by the authors of the core library, since it is what Rubyists are used to.
module Api
module V1
class ABC
end
end
end
class Api::V1::ABC
end
Any different between these to declare a class?
Any pros and cons?
Yes, there is a difference while you will be doing constant lookups. Module::nesting will be helpful here to things get cleared for you.
module Api
module V1
class ABC
p Module.nesting
end
end
end
# >> [Api::V1::ABC, Api::V1, Api]
module Api
module V1
end
end
class Api::V1::ABC
p Module.nesting
end
# >> [Api::V1::ABC]
Module.nesting returns the lexically enclosing classes/modules which are searched before Ruby walks up the class/module hierarchy to find the constant.
It means, with the following code :
module Api
module V1
X = 12
end
end
X = 10
class Api::V1::ABC
p X
end
Api::V1::ABC.superclass # => Object
# >> 10
While constant looks-up will be happening, it will first search to the array of constants, which has been given by Module.nesting, if not found then, up to the ancestor chains/included modules. It means following to the Api::V1::ABC.ancestors outputs.
Now in the above example, you can see that value of X printed, which is defined in the Object, not inside the API::V1. The reason as I said above.
Now coming to another example below :-
X = 10
module Api
module V1
X = 12
class ABC
p X
end
end
end
# >> 12
Here the constant look up will follow through the array [Api::V1::ABC, Api::V1, Api]. You can see the output found as 12, as Api::V1 has a constant X defined into it.
Thus we can say - Yes there is a difference between the 2 declarations.
I've been doing some 'monkey-patching' (ahem excuse me superman-patching), like so, adding the below code and more to file(s) in my "#{Rails.root}/initializers/" folder:
module RGeo
module Geographic
class ProjectedPointImpl
def to_s
coords = self.as_text.split("(").last.split(")").first.split(" ")
"#{coords.last}, #{coords.first}"
end# of to_s
def google_link
url = "https://maps.google.com/maps?hl=en&q=#{self.to_s}"
end
end# of ProjectedPointImpl class
end# of Geographic module
end
I ended up realizing that there were two different _Point_ instances which I wanted to utilize these methods on (both were strings with the same formatting, i.e. Well-Known Text (WKT)), and added an exact copy of the above two methods to a certain RGeo::Geos::CAPIPointImpl class space.
I then, in my youthful, unexperienced way, after thinking about DRY (don't repeat yourself) principles, proceeded to create an ad hoc class which I presumed I might be able to inherit from for both
class Arghhh
def to_s
coords = self.as_text.split("(").last.split(")").first.split(" ")
"#{coords.last}, #{coords.first}"
end# of to_s
def google_link
url = "https://maps.google.com/maps?hl=en&q=#{self.to_s}"
end
end
and told my classes to inherit from it i.e.: ProjectedPointImpl < Arghhh
I was promptly responded to by ruby with this error when I stopped and then tried to reload my rails console:
`<module:Geos>': superclass mismatch for class CAPIPointImpl (TypeError)
...
I think my naivete in trying to get CAPIPointImpl (in this case) to inherit from another class than its parent also highlights my knowledge gap on this subject very explicitly
What methods might I be able to use to practically graft extra shared methods onto two classes coming from otherwise separate parents? Does ruby allow for these types of abstract exceptions?
What you need to do is to define the new methods in a module which you then "mix into" the existing classes. Here's a rough sketch:
# Existing definition of X
class X
def test
puts 'X.test'
end
end
# Existing definition of Y
class Y
def test
puts 'Y.test'
end
end
module Mixin
def foo
puts "#{self.class.name}.foo"
end
def bar
puts "#{self.class.name}.bar"
end
end
# Reopen X and include Mixin module
class X
include Mixin
end
# Reopen Y and include Mixin module
class Y
include Mixin
end
x = X.new
x.test # => 'X.test'
x.foo # => 'X.foo'
x.bar # => 'X.bar'
y = Y.new
y.test # => 'Y.test'
y.foo # => 'Y.foo'
y.bar # => 'Y.bar'
In this example we have two existing classes X and Y. We define the methods we would like to add to both X and Y in a module which I've called Mixin. We can then reopen both X and Y and include the module Mixin into them. Once that's done, both X and Y have their original methods plus the methods from Mixin.
I cannot figure out how come the object is so smart to disregard the following block of code even if performed for the first time.
The method it is searching for is on each module
class A
include B,C,D,B
end
Does ruby keeps an array of module names on the side (as it obviously calls D)?
I'm not 100% I understand your question but I try my best...
Ruby actually remembers which modules are included into a class and merges these modules into the lookup path for methods. You can ask a class for its included methods my using A.included_modules
Methods from included modules are put on top of the modules defined in the current class. Please see this example:
class X
def foo
puts 'Hi from X'
end
end
module AnotherFoo
def foo
puts "Hi from AnotherFoo"
super
end
end
class Y < X
include AnotherFoo
end
y = Y.new
y.foo
# Hi from another foo
# Hi from X
class Y < X
include AnotherFoo
def foo
puts "Hi from Y"
super
end
end
y.foo
# Hi from Y
# Hi from another foo
# Hi from X
You can see the method resolution order: the child class -> the included modules -> the parent class. You can also see that modules are always included only once. So if a class already includes a module, it will not be re-included again.