I would like to expand the functionality of some class using class_eval. I would like to force the class to inherit some methods from some other class.
I.e.:
SomeClass.class_eval do
# force inheritence from some other class
end
What's the best way to achieve it?
If overriding existing functionality is a hard requirement here, you need to have those existing methods defined in a module that's also included.
class SomeClass
include DefaultBehaviour
end
module DefaultBehaviour
def run
puts "ran default"
end
end
module AlternateBehaviour
def run
puts "ran alternate"
end
end
SomeClass.class_eval {
include AlternateBehaviour
}
SomeClass.new.run #=> "ran alternate"
The reason for this is because of ruby's method lookup path.
It starts off as SomeClass -> Object.
When you include AlternateBehaviour, it becomes SomeClass -> AlternateBehaviour -> Object. So methods defined directly on SomeClass still take precedence.
However, if those methods are defined on DefaultBehaviour, the lookup path becomes SomeClass -> AlternateBehaviour -> DefaultBehaviour -> Object, so your alternate method takes priority. Whichever module was included most recently is the highest priority.
In the case where you do not have control of the original class, you can do instead:
module AlternateBehaviour
def self.included(base)
base.send(:remove_method, :run)
end
def run
puts "ran alternate"
end
end
Though at this point, one starts to wonder whether you might be better off by just doing
SomeClass.class_eval {
def run
"ran alternate"
end
end
Try using include and extend, both explained here. They only work with modules; you just can't modify/add superclasses of a class in Ruby after it has already been created.
Only one problem: you can't override already existing methods in a class for the explained in the third comment to this post.
Also see this topic for more information.
Related
2 main techniques for creating class methods (without the obvious "def self.method") are:
Defining them in "class << self" block
Defining ClassMethod module and extending it later
I personally prefer second way, seems cleaner. Does anyone has any reason to prefer one technique over the other?
There's also "class_method" method, but I never used it, it has quite complex implementation and seem to do a lot more than previous 2.
self.method is the simplest option when you just need to create one method without dependencies or related logic.
class << self allows you to do far more than define methods on the metaclass. This is useful when you're defining methods which need to work with other parts of the metaclass (eg. aliasing existing method names).
For instance:
class Logger
class << self
attr_reader :payload
def log(message)
#payload = message
end
end
end
The module extension method comes in handy for method reuse and to group multiple coherent methods.
For instance:
module QueryMethods
def find
end
def where
end
end
module FactoryMethods
def build
end
def create
end
end
class BusinessModel
extend QueryMethods
extend FactoryMethods
end
First, the class << foo syntax opens up foo's singleton class (eigenclass). This allows you to specialise the behaviour of methods called on that specific object.
a = 'foo'
class << a
def inspect
'"bar"'
end
end
a.inspect # => "bar"
a = 'foo' # new object, new singleton class
a.inspect # => "foo"
class << self opens up self's singleton class, so that methods can be redefined for the current self object (which inside a class or module body is the class or module itself). Usually, this is used to define class/module ("static") methods
class << self is good at keeping all of your class methods in the same block. If methods are being added in def self.method from then there's no guarantee (other than convention and wishful thinking) that there won't be an extra class method tucked away later in the file.
def self.method is good at explicitly stating that a method is a class method, whereas with class << self you have to go and find the container yourself.
Which of these is more important to you is a subjective decision, and also depends on things like how many other people are working on the code and what their preferences are.
Pros of “class << self” style
Before voting for closing due to question duplication I want to say that my question is really simple one (not asked in above mentioned questions).
There are two modules, one defines module method using extend self, another defines mixin method.
module A
extend self
def module_a_meth
"Called module_a_meth"
end
end
module B
def module_b_meth
"Called module_b_meth"
end
end
There is a class, where I both include and extend these modules:
class Test
include A
extend A
include B
extend B
end
When we includeing module, its methods become class' instance methods, when extending - class methods.
Question:
it doesn't matter for class, if methods in module defined as module methods or mixin methods, right? I mean, when included - EVERY method (either module methods or mixin methods) become instance methods, and when extended - either become class methods.
If I'm wrong - where is the difference?
obj = Test.new
puts obj.module_a_meth
puts obj.module_b_meth
puts Test.module_a_meth
puts Test.module_b_meth
#=> Called module_a_meth
#=> Called module_b_meth
#=> Called module_a_meth
#=> Called module_b_meth
EDIT
Please start your answer with Yes or No, since my question implies this type of answer :).
Regardless of whether you are using extend or include you are always copying over instance methods. The difference is where those instance methods live.
When you call Class#include you are "copying" all of the instance methods in the module to be instance methods in the class. It's similar to how inheritance work, and if you call Class#ancestors you'll see the module there.
When you call Object#extend you are copying all of the instance methods of the module to the object's singleton class. This is a class reserved just for this object instance that is created at runtime. This is how you get "class methods" (e.g. MyClass.hello_world); by adding them to the class's singleton. You can also do things like extend a particular object instance (e.g. s = String.new; s.extend(SomeModule); s.hello_world)
There are some other differences too. The context binding is different depending on whether you use extend or include. extend doesn't cause the module to show up in the ancestor chain while include does.
When trying to add both "class" and instance methods, one common pattern you'll see is doing things like this which uses the included callback to extend the base class with a ClassMethods module:
module MyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def hello_world
end
end
end
ActiveSupport::Concerns also abstracts this pattern allowing you to add both instance and "class" methods in one call.
I personally prefer having modules only work with instance methods and using singleton methods (e.g. def self.my_method) to have scoped methods (sort of like how you would use private methods). This allows consumers to use either extend or include however they want and have it work as expected.
I'm not sure if that answers your question or not, but there's some info for you
Let's look at this in steps.
module A
puts "self = #{self}"
extend self
def module_a_meth
"Called module_a_meth"
end
end
class Test
end
Test.include A
#-> self = Test
Test.instance_methods.include?(:module_a_meth)
#=> true
Test.methods.include?(:module_a_meth)
#=> false - no class method
So include includes :module_a_meth as an instance method. As self is Test, the line:
extend self
is equivalent to:
extend Test
which of course makes no reference to the module. Now we extend and obtain the expected result:
Test.extend A
#=> true
Test.methods.include?(:module_a_meth)
#=> true
including and extending B is normal:
module B
def module_b_meth
"Called module_b_meth"
end
end
Test.include B
Test.instance_methods.include?(:module_b_meth)
#=> true
Test.extend B
Test.methods.include?(:module_b_meth)
#=> true
First of all, regarding the actual question: No :).
Class (or any other object) cares how methods are defined in a module you're including. Basically, method's in a module you've described are defined as mixin methods. extend self doesn't redefine methods to be a module methods, but, basically, duplicates them to both contexts.
It's pretty much a question about how does extend work, it's just a tricky case.
First of all, think of extend as an include in object's singleton class context. Those two definitions are equal:
module SomeModule
def hi
'hi'
end
end
class SomeClass
extend SomeModule
end
class SomeClass
class << self
include SomeModule
end
end
Given that, by using extend self in a module you're saying: Take all of the mixin methods I've defined and extend module's singleton class with them. This magic is a result of ruby's nature: an ability to re-open any definition. Here's how a verbose version of extend self would look like:
module Module1
def hi
'hi'
end
end
module Module1
extend Module1 # which is self
#### now "hi" is both here:
# def hi; end
#### and here:
# class << self; def hi; end
end
Module1.hi # => 'hi'
class SomeClass; include Module1; end;
SomeClass.new.hi # => 'hi'
__ EDIT __
Just a quick proof that object cares about how methods in a module are defined:
module SomeModule
def self.hi
'hi'
end
end
object = 'some string'
class << object
include SomeModule
end
object.hi # => NoMethodError: undefined method
Module#refine method takes a class and a block and returns a refinement module, so I thought I could define:
class Class
def include_refined(klass)
_refinement = Module.new do
include refine(klass) {
yield if block_given?
}
end
self.send :include, _refinement
end
end
and the following test passes
class Base
def foo
"foo"
end
end
class Receiver
include_refined(Base) {
def foo
"refined " + super
end
}
end
describe Receiver do
it { should respond_to(:foo) }
its(:foo) { should eq("refined foo") }
end
So, using refinements, I can turn a class into a module, refine its behaviour on the fly, and include it in other classes.
Is there a simpler way to turn a class into a module in Ruby (say in ruby < 2)?
In the C-implementation of rb_mod_refine
we see
refinement = rb_module_new();
RCLASS_SET_SUPER(refinement, klass);
Is this just setting the superclass of refinement to klass that copies the implementation of the class inside the refinement module?
I am aware that multiple inheritance IS
done via Modules, but what would the community think of the above Class#include_refined?
Would it be reasonable to extract this aspect out of refinements?
"Locally" patching inside a Class instead of using "using" switches to activate refinements?
I am happy indeed with Ruby 2.1 (and later) class-level "private" scope of refinements. My example above can be rephrased as:
# spec/modulify_spec.rb
module Modulify
refine(Class) do
def include_refined(klass)
_refined = Module.new do
include refine(klass) { yield if block_given? }
end
include _refined
end
end
end
class A
def a
"I am an 'a'"
end
end
class B
using Modulify
include_refined(A) do
def a
super + " and not a 'b'"
end
end
def b
"I cannot say: " + a
end
end
RSpec.describe B do
it "can use refined methods from A" do
expect(subject.b).to eq "I cannot say: I am an 'a' and not a 'b'"
end
end
and suits as solution for the original problem.
Andrea, thank you for the info in comment. Excuse my lack of knowledge to understand this is really necessary though it sounds doable as per your research.
I don't think we need to go so low level to do something in Rails.
If I'm going to do similar on Engine, I will try the following ideas, from easy to hard.
In routes.rb, mount the whole engine in right route.
I'm afraid this most common usage can't fit your need
In routes.rb, Customize engine's route for specific controllers in application route.
Devise, as an engine, can do easily. But I know not every engine could do this.
In routes.rb, redirect specific or whole set of routes to engine's routes
In your application's action, redirect to specific engine's action in application's action.
This should be customized enough for specific action
class FoosController < ApplicationController
def foo
redirect_to some_engine_path if params[:foo] == 'bar'
end
Inherit the engine's controller - for a set of actions, and if all above can't fit
*The engine's classes are available in all application, you can inherit a controller from them, instead of normal ApplicationController.
# class FoosController < ApplicationController
class FoosController < BarEngine::BarsController
*Since most engine's controller inherit from ApplicationController, this inheritance still allows you to use your own things from ApplicationController, no bad effect at all.
If all above can't do, I can try to serve a customized locally or from my github repo.
In conclusion, the above should be able to solve most of cases, and I myself prefer #5 when possible and needed.
I'm designing/building a system of classes that all derive from a single base class.
The goal is to have easy-to-use inherited macro methods that look something like this:
class Something < A::Base
full_name 'Something that goes bump in the night.'
end
Any code should be able to ask the class for this information (or, likely, normalized/derived infomation) later on via class-level accessor method(s).
puts Something.full_name
# => "Some kind of calculated value that may or may not go bump in the night."
Given that A::Base includes/extends/somehow-otherwise-mixes-in both a module with the macro method that works something like this:
module MacroMethods
private
def full_name(full_name)
# non-trivial, one-time-only set-up code exists here in actual usage
end
end
and a module with the class-level accessor method that works something like this:
module AccessorMethods
public
def full_name
# a non-trivial, runtime-calculated value is returned here in actual usage
end
end
no matter how I mix them in, I'm continually running into naming conflicts (i.e. ‘wrong number of arguments (1 for 0) (ArgumentError)’) between the two.
Note: full_name is the simplest example of what is needed; other, more-complex macros/accessors ensure the non-flexible constraints of macro methods needing to be declared inside the class and needing to be set once-and-only-once.
My question is two-fold:
Is there a way to make this all work inside of the A::Base class?
Is this the right way to do this in Ruby? Is there a better way go about it, achieving the same result?
Options that have been considered:
Calling either the macro or accessor method(s) something else.
(e.g. in Something class: set_up_full_name 'Something that …')
Downside is that the naming is confusing and unconventional.
Making the accessor method(s) instance-level instead of class-level.
(e.g. puts a_something.full_name')
Downside is that the traits set up by the macros are inherent to the class, not to each instance (in some cases, only a reference to the class may be available, not an instance).
Creating a single method that handles both macro and accessor functionality.
(e.g. in A::Base class: def self.full_name(*args) …)
Downside is that the macro methods can no longer be private and the RDoc looks like sh*t.
Using abstact/virtual-ish methods instead.
(e.g. in Something class: def self.full_name; 'Something that …'; end)
Downside is that this is more code in sub-classes and is more of a Objective-C (or C++, or Java, …) thing than a good Ruby paradigm.
Slipp, I read your question carefully. There is no way you can have 2 different methods called full_name defined on the same object at the same time. BUT, you could do something like this:
module MacroMethods
private
def full_name(full_name)
# non-trivial, one-time-only set-up code exists here in actual usage
# this method can only be called once in the definition of any given class,
# because after the first call, it will be redefined!
extend AccessorMethods
end
end
module AccessorMethods
public
def full_name
# a non-trivial, runtime-calculated value is returned here in actual usage
end
end
class Base
extend MacroMethods
end
class Something < Base
full_name 'after this call, I will get a new full_name method!'
end
class SomethingElse < Base
full_name 'so will I!'
end
If you want to have class macros available to certain classes, then a common base class is not the Ruby solution. Instead, you create a module that extends the base classes with the functionality you want them to have:
module Extensions
def self.included(base_class)
base_class.extend ClassMethods
end
module ClassMethods
attr_accessor :full_name
end
end
class Something
include Extensions
self.full_name = "Something that goes bump in the night"
end
puts Something.full_name # => Something that goes bump in the night
thing = Something.new
puts thing.full_name # Error
This overrides a hook method in Extensions called Module#included that passes any class that includes the module as an argument. The new method then calls Object#extend on the base class to put the methods available in ClassMethods directly onto that class as class methods. This works the same way as defining class methods, but this runs dynamically. This frees you of needing to use your only base class on a class that provides macros. Note that the methods are not defined on instances of classes that include the module.
It looks like most of the other answers have the right idea, but are lacking the getter method for #full_name. This example might be what you're looking for:
class Thing
class << self
attr_writer :full_name
def full_name
"My full name is #{#full_name}"
end
end
end
With this you can do something like this:
> Thing.full_name = 'Thing Class'
=> "Thing Class"
> Thing.full_name
=> "My full name is Thing Class"
This seems needlessly complex. Why not just use an attribute on the parent class?
class Base
class << self
attr_accessor :full_name
end
end
class A < Base; end
class B < Base; end
A.full_name = "The full name of A"
B.full_name = "The full name of B"
puts A.full_name # "The full name of A"
puts B.full_name # "The full name of B"
i've got a module that wants to use data provided by the class that included it - but at the class level, not the instance level.
the goal is to have class 'metadata' provided to a module that the class includes, so that the module can use the metadata during the included call.
this works:
module Bar
def value
#value
end
def baz
puts "the value is: #{value}"
end
end
module Foo
def self.included(mod)
mod.extend(Bar)
mod.baz
end
end
class MyClass
#value = "my class defined this"
include Foo
end
the output of this code is
the value is: my class defined this
i'm not sure if the use of #value is good or not... it seems odd to me that i require this to be set before the include Foo happens, not from a technical perspective (i know why it's required to be done in this order) but from an idiomatic or usability perspective.
... is there a better way / more idiomatic way of accomplishing this?
If you really want to use the class metadata in the moment you're including a module, given the 'included' method runs on its own scope, it's best to have a class method providing the metadata to it.
Also, if the metadata is not going to be manipulated, its better to declare it as a constant.
module Bar
def self.included(base)
puts "the value is: #{base.metadata}"
end
end
class MyClass
VALUE = "MyClass metadata"
def self.metadata
VALUE
end
include Bar
end
class OtherClass
VALUE = "OtherClass metadata"
def self.metadata
VALUE
end
include Bar
end
Of course you can declare the metadata anyway you want, as long as its accessible by a class method to your Module.
Also, its not common to do these kind of metadata manipulation in the module's 'included' method and the necessity of ordering your statements on the class level is a bit brittle, so you might want to try to find a different solution to your original problem instead.
If you want to the class to pass an argument to the mixin, then why not use one of the Ruby constructs that actually does allow passing an argument?
class Object
private
def Bar(metadata)
Module.new do
include Bar
define_singleton_method(:included) do |base|
puts "the value is: #{metadata}"
end
end
end
end
module Bar
# put common behavior here
end
class MyClass
include Bar 'MyClass metadata'
end
class OtherClass
include Bar 'OtherClass metadata'
end
This is a pretty common idiom that is for example used by the delegate library in the stdlib.