Ruby: Module, Mixins and Blocks confusing?
By the way: in Ruby 2.0, there are two features which help you with both your problems.
Module#prepend
prepends a mixin to the inheritance chain, so that methods defined in the mixin override methods defined in the module/class it is being mixed into.
Refinements allow lexically scoped monkeypatching.
Here they are in action (you can get a current build of YARV 2.0 via RVM or ruby-build easily):
module Sum
def sum(initial=0)
inject(initial, :+)
end
end
module ArrayWithSum
refine Array do
prepend Sum
end
end
class Foo
using ArrayWithSum
p [1, 2, 3].sum
# 6
end
p [1, 2, 3].sum
# NoMethodError: undefined method `sum' for [1, 2, 3]:Array
using ArrayWithSum
p [1, 2, 3].sum
# 6
idiomatic RSpec testing with mixins and blocks?
First, the operations provided by FileUtils should be tested separately, in a FileUtils test.
Then you should only validate that the block you provide to the UserDataFile is actually used. There are 2 cases - one just yields the instance, while the other uses instance_eval. You need to validate both.
For the first case you can use the yield RSpec matcher. It will look like:
expect { |b| MyModule::UserDataFile.new(client, &b) }.to yield_with_args(kind_of(UserDataFile)) }
The second one is more tricky. You can pass a block and add expectation for something done within a block. A classical example is to throw
from the block and expect that symbol to be thrown:
my_proc = proc { throw :my_proc_was_called }
expect { MyModule::UserDataFile.new(client, &my_proc) }.to throw_symbol :my_proc_was_called
As for the reason you get NoMethodError
- you are stubbing the add_data
on the class, but the code is calling it on the instance. You will have to use allow_any_instance_of
to stub the way you are trying to do
To break up Ruby class into separate files by mixins or plain definitions?
Are your support methods general enough that they might be useful for other totally unrelated classes? If so, a mixin is the proper way to do this, since it lets you easily reuse the support code.
However if your support methods are very specific to ReallyBigClass and are unlikely to work if included somewhere else, reopening the class is the way to go. Using a mixin there could give the appearance that the methods are more general than they really are, when really they should only be used with instances of a specific class.
That being said, I think your question indicates a larger design problem. If you are in the former case (general methods), you should be designing more general modules in the first place to avoid tight coupling. A module called ReallyBigClassFileB
gives off some strong code smell. In the latter case (very specific methods) if your class is so big that its file is unmanageably large you probably need to refactor something. Maybe your class is responsible for too much? Maybe it could use some subclasses (which make sense in separate files)?
Why does including this module not override a dynamically-generated method?
Let's do an experiment:
class A; def x; 'hi' end end
module B; def x; super + ' john' end end
A.class_eval { include B }
A.new.x
=> "hi" # oops
Why is that? The answer is simple:
A.ancestors
=> [A, B, Object, Kernel, BasicObject]
B
is before A
in the ancestors chain (you can think of this as B
being inside A
). Therefore A.x
always takes priority over B.x
.
However, this can be worked around:
class A
def x
'hi'
end
end
module B
# Define a method with a different name
def x_after
x_before + ' john'
end
# And set up aliases on the inclusion :)
# We can use `alias new_name old_name`
def self.included(klass)
klass.class_eval {
alias :x_before :x
alias :x :x_after
}
end
end
A.class_eval { include B }
A.new.x #=> "hi john"
With ActiveSupport (and therefore Rails) you have this pattern implemented as alias_method_chain(target, feature)
http://apidock.com/rails/Module/alias_method_chain:
module B
def self.included(base)
base.alias_method_chain :x, :feature
end
def x_with_feature
x_without_feature + " John"
end
end
Update Ruby 2 comes with Module#prepend, which does override the methods of A
, making this alias
hack unnecessary for most use cases.
What's the hierarchical relationship between a Module and a Class?
Modules can be used for two things in Ruby: as namespaces for constants and as mixins.
The namespace usage is quite similar to C# namespaces: a module can contain constants (and all constants are contained in modules, even if you don't explicitly see it, in which case they belong to Object
) which are namespaced to that module. So, Foo::BAR
and Baz::BAR
are two different constants even though they have the same name.
Mixins are a bit trickier. One way to look at a mixin, is that a mixin is a class which doesn't know its superclass (or a class that is parameterized by its superclass). Okay, this sounds a bit convoluted. Let's look at an example.
Imagine, something like this were possible in C#:
interface IEachable<E> {
IEachable<E> Each(Action<E> block);
}
class Enumerable<S, E> : S where S : IEachable<E> {
List<T> Map(Func<E, T> block) {
var res = new List<T>();
this.Each(e => {†res.Add(block(e));});
return res;
}
}
So, you have a generic class Enumerable
which inherits from its type parameter S
(the superclass). This is actually a pretty accurate description of the Enumerable
mixin in Ruby. Everytime you instantiate the generic class into a concrete type, it ends with a different superclass.
So, the same class appears multiple times in the inheritance hierarchy, each time with a different superclass, but always just one.
This is different from multiple inheritance, where the same class appears once in the inheritance hierarchy, with multiple superclasses.
This property, that the class always has just one superclass, is called linearization and it solves a lot of the problems that traditional multiple inheritance has, which are usually related to the fact that there are multiple possible paths between two classes in the inheritance DAG, whereas with mixin linearization, there is just an inheritance tree, so there is always just one possible path.
In particular, this is what happens, when you mix a module M
into a class C
with superclass S
:
module M; end
class C < S
include M
end
When you call include
, Ruby will
- create a new class
M'
whose method table pointer, constant table pointer, class variable table pointer and instance variable table pointer point toM
's method table, constant table, class variable table and instance variable table - set
M'
's superclass toC
's current superclass (i.e.S
) - set
C
's superclass toM'
So that the inheritance hierarchy now looks like this:
C < M' < S
[Note: Actually include
delegates to append_features
, which really does the above work, and just like almost everything else in Ruby, you can actually alter this behavior by overriding append_features
, but that is advanced meta-magic.]
So, what are the practical implications of having a class that can be used in multiple places in the inheritance hierarchy? Well, you can use it to implement common behavior for unrelated classes.
Again, look at the Enumerable
mixin: it provides common behavior for any object that conforms to the following protocol: it must have an each
method that yield
s all elements one-by-one and returns self
. That's it. There doesn't need to be any sort of inheritance relationship between the classes of those objects. All that is required is that they respond to each
in an appropriate manner.
Which leads us to your question about interfaces: that's an interface. Although in Ruby, we usually call it protocol. Protocols in Ruby are very much like interfaces in C#, however, there is one crucial difference: there is no such thing as a protocol in Ruby. Protocols are latent, not manifest, they only exist in the programmer's head, in documentation, but not in the source code, and thus they cannot be checked by the language.
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 in ClassMethods
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.
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 do I use the Enumerable mixin in my class?
It's easy, just include the Enumerable
module and define an each
instance method, which more often than not will just use some other class's each
method. Here's a really simplified example:
class ATeam
include Enumerable
def initialize(*members)
@members = members
end
def each(&block)
@members.each do |member|
block.call(member)
end
# or
# @members.each(&block)
end
end
ateam = ATeam.new("Face", "B.A. Barracus", "Murdoch", "Hannibal")
#use any Enumerable method from here on
p ateam.map(&:downcase)
For further info, I recommend the following article: Ruby Enumerable Magic: The Basics.
In the context of your question, if what you expose through an accessor already is a collection, you probably don't need to bother with including Enumerable
.
Related Topics
Getting a Rogue Iteration from My .Each Loop
How to Upgrade Rvm When the Official Way Doesn't Work
Shading Mask Algorithm for Radiation Calculations
Ruby on Rails: Create Confirmation View Before Creating the Object
Rubocop, How to Disable/Enable Cops on Blocks of Code
Currying a Proc with Keyword Arguments in Ruby
Which Global Variable Is for Last Expression
Algorithm to Shuffle an Array Randomly Based on Different Weights
Working with Multiple Processes in Ruby
Calling a Method of a Ruby Singleton Without the Reference of 'Instance'
Hardcoded "Require 'Debug'" Can't Find the Sourcefile
Splitting a String into Words and Punctuation with Ruby
How to Install Ruby System-Wide Using Rbenv
How to Add Iedriverserver to Path
How to Pass Multi Value Query Params in Swagger
How Does Sinatra Define and Invoke the Get Method
Official Expansion of ||= Conditional Assignment Operator
Uml Sequence Diagram - How to Represent Method Arguments That Instantiate Objects