Ruby - Can we use include statement anywhere within the class? - ruby-on-rails

Can we use include statement to include a module anywhere within the class or does it has to be at the beginning of the class?
If I include the module at the beginning of my class declaration, method overriding works as expected. Why is it not working if i include at the end as described below?
# mym.rb
module Mym
def hello
puts "am in the module"
end
end
# myc.rb
class Myc
require 'mym'
def hello
puts "am in class"
end
include Mym
end
Myc.new.hello
=> am in class

When you include a module, its methods do NOT replace methods defined in this class, but rather they are injected into inheritance chain. So, when you call super, method from included module will get called.
They will behave almost the same way with other modules. When a module gets included, it is placed right above the class in inheritance chain, with existing modules placed above it. See example:
module Mym
def hello
puts "am in the module"
end
end
module Mym2
def hello
puts "am in the module2"
super
end
end
class Myc
include Mym
include Mym2
def hello
puts "im in a class"
super
end
end
puts Myc.new.hello
# im in a class
# am in the module2
# am in the module
For more info see this post.
Also read this: http://rhg.rubyforge.org/chapter04.html

Related

Ruby include/extend Module: a class method - Beginner

I've been reading this article on the difference between include & extend in ruby.
If I have this module, I understand how the first and second methods of the module will be used in the class. What I don't understand is how the class << self will be used by include or extend.
module Direction
def straight
puts "going straight!"
end
def turn
puts "turning!"
end
class << self
def stop
puts "stopping!"
end
end
end
# This will work because `include` brings them in as instance methods
class Car
include Direction
end
Car.new.straight
Car.new.turn
# ---------------------
# Now this will also work because `extend` brings them in as class methods
class Car
extend Direction
end
Car.straight
Car.turn
# ---------------------
Now, the issue is, doing Car.stop or Car.new.stop will always result in an error:
/Users/<name>/Projects/ruby-testing/main.rb:34:in `<main>': undefined method `stop' for Car:Class (NoMethodError)
Why are class methods not carried over via include and extend?
I started thinking about this because of my research into the [forwardable source code at line 119].(https://github.com/ruby/ruby/blob/master/lib/forwardable.rb#L119)
Thank you for any help you may have!
Update from Answer Below
The following was an example given:
module Direction
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
def straight
puts "going straight!"
end
def turn
puts "turning!"
end
end
class Car
include Direction
end
This I understand now, and I understand how I can implement class methods from a module into a class using def self.included(base). My question is, if we used extend inside of Car instead of include, would we still be able to get at those class methods using def self.included(base)?
When you define a method with class << self you are defining a class method. It's the same as defining the methed like this:
class Foo
def self.foo
puts 'foo'
end
# the above definition is the same as doing:
class << self
def foo
puts 'foo'
end
end
end
The above shows 2 ways of defining class methods which are called directly on the class and not on instances of the class. You might use the 2nd syntax if you want to define only class methods or several of them inside of the class << self block. But either style has the same result.
Since you've defined a class method on the Direction module, include or extend will not inherit the class method of that module. This is the expected behavior.
If you want to use inheritance with class methods from a module, you should do it like this which is explained further down in the article you've linked
module Direction
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
def straight
puts "going straight!"
end
def turn
puts "turning!"
end
end
class Car
include Direction
end
Now calling class methods on Car will inherit as defined in the Direction class.
Car.stop
stopping!
=>nil # calling a method will return nil unless the method returns a value.
However always be careful using inheritance of any kind as Ruby is a dynamic language. So if you do the above code and then later redefine this method:
module Direction
module ClassMethods
def stop
puts 'go!'
end
end
end
Guess what will happen if you do this:
Car.stop
Since the method was defined inside Direction module, when the method gets called on Car it will be calling the method from the Direction module.
Car.stop
go!
=>nil
Updated based on comments:
If you prefer to use extend vs include you would need to do this instead:
module Direction
def self.extended(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
end
class Car
extend Direction
end
In this example, all the methods which were inherited from the module are "copied" to the class extending them. This avoids the problem of possible result of redefining the module method which I warned about when using include previously in my answer.
But you may want to look at answers to this question for ideas about when and why to use either case.

self.included – including class methods from a module in Ruby

I read this post: Ruby modules - included do end block – but still don't get when you would use the self.included do ... end block in a module.
The post says that code in the block will be ran when you include the module, but what's the point of that if a module's sole purpose is to be included? Wouldn't that code need to be run anyway? That block doesn't need to be there in order for that code to be run, right?
What would be the difference between the two below:
module M
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
module ClassMethods
...
end
end
vs.
module M
def self.some_class_method
...
end
scope :disabled, -> { where(disabled: true) }
end
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.
module M
# self.included is the hook which automatically runs when this module is included
def self.included(base)
puts 'this will be printed when this module will be included inside a class'
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
def print_object_class
self.class.name # here self will be the object of class which includes this module
end
module ClassMethods
def print_class_name
self.name # Here self would be the class which is including this module
end
end
end
I tried to modify the above module to help you understand how a module(concern) can be helpful in code reusability
self.included is a hook which runs automatically when a module is included inside a class.
any method declare in the module ClassMethods will become class methods for the class including this module
any method declare outside the module ClassMethods will become instance methods for the class including this module
For Ex suppose there is a class Product and you have included the module in it
class Product < ActiveRecord::Base
include M
puts 'after including M'
end
If you try this example in you rails console you will notice that as soon as the module M gets included in class Product included hook of module run and
this will be printed when this module will be included inside a class this is printed on console
after that after including M this will be printed on the console.
Also you can try following commands
Product.disabled # a scope with name 'disabled' is avaialble because of including the module M
Product.print_class_name # Outputs => 'Product' This method is available to class with the help of module M
Product.new.print_object_class #Outputs => 'Product'
It also offers reusability, include this module M in any class and that class gets access to all those methods described in the module.
Whereas your second example is a mere example of basic module
module N
def self.abc
puts 'basic module'
end
end
Now abc method define in module can be accessible using only this module
N.abc # outputs 'basic module'
class Product < ActiveRecord::Base
include N
end
Product.abc #raises exception, No method found on class Product
Product.new.abc #raises exception, No method found on object of class Product
I hope this may help you understand the concept of module better.
Please let me know if you still have any doubts.

Overriding a concern of a gem - Rails

I am trying to modify a gem (Devise token auth to be precise) to suit my needs. For that I want to override certain functions inside the concern SetUserByToken.
The problem is how do I override that?
I don't want to change the gem files. Is there an easy/standard way of doing that?
Bear in mind here that a "concern" in Rails is just a module with a few programmer conveniences from ActiveSupport::Concern.
When you include a module in a class the methods defined in the class itself will have priority over the included module.
module Greeter
def hello
"hello world"
end
end
class LeetSpeaker
include Greeter
def hello
super.tr("e", "3").tr("o", "0")
end
end
LeetSpeaker.new.hello # => "h3ll0 w0rld"
So you can quite simply redefine the needed methods in ApplicationController or even compose a module of your own of your own without monkey patching the library:
module Greeter
extend ActiveSupport::Concern
def hello
"hello world"
end
class_methods do
def foo
"bar"
end
end
end
module BetterGreeter
extend ActiveSupport::Concern
def hello
super.titlecase
end
# we can override class methods as well.
class_methods do
def foo
"baz"
end
end
end
class Person
include Greeter # the order of inclusion matters
include BetterGreeter
end
Person.new.hello # => "Hello World"
Person.foo # => "baz"
See Monkey patching: the good, the bad and the ugly for a good explanation why it often is better to overlay your custom code on top of a framework or library rather than modifying a library component at runtime.
You can monkey patch concerns like any other module:
module DeviseTokenAuth::Concerns::SetUserByToken
# Your code here
end
If you want to extend the behavior of an existing method, try using an around alias:
http://rubyquicktips.com/post/18427759343/around-alias

Modules in rails lib directory

module MyModule
def self.my_method
#instance_variable ||= "some string"
end
end
Consider another module OtherModule that includes MyModule. When code in OtherModule calls my_method multiple times will #instance_variable be instantiated multiple times? Or just once?
updated answer:
if the delegate module call the method, you will get an error.
if the original module call the method, the class instance variable only instanced 1 time.
if you want to define "class methods" in an module, you can't define self.xx then simply extend/include it. you should either "extend" it, or "include it to the eigen class", e.g.
module SomeModule
def self.aim_to_be_class_method ; end;
def aim_to_be_instance_method ; end;
end
# class methods: []
# instance methods: ["aim_to_be_instance_method"]
class SomeClassIncludingTheModule
include SomeModule
end
# class methods: ["aim_to_be_instance_method"]
# instance methods: []
class SomeClassExtendingTheModule
extend SomeModule
end
# class methods: ["aim_to_be_instance_method"]
# instance methods: []
class SomeClassMadeEigenClassIncludingModule
class << self
include SomeModule
end
end
your code is an example of "class instance variables". according to the book << metaprogramming ruby>>, page 127, you can consider "class instance variables" as Java's static fields. so, I think most of the case, it should run once.
for more details, please write an unit test and see what happens? I will update my answer after a short while with my own unit test code written.
UPDATED: my unit test and result:
# all of the code is placed in a file: class_instance_variable_test.rb
# define a class so that we see its initialize process
class Apple
def initialize
puts "Apple initialized~"
end
end
# define an original_module that calls Apple.new
module OriginalModule
def self.original_method
#var ||= Apple.new
end
end
# define another module to call the original_module
module DelegateModule
include OriginalModule
end
# now the test begins
require 'test/unit'
class ClassInstanceTest < Test::Unit::TestCase
# output of this test case:
# NoMethodError: undefined method `original_method' for DelegateModule:Module
def test_if_delegate_module_could_call_original_method_by_including_original_module
DelegateModule.original_method
end
# output of this test case:
# ----- calling from original_method, 3 times called, how many times initialized?
# Apple initialized~
# ------ ends
def test_if_we_could_call_original_method_from_original_module
puts "----- calling from original_method, 3 times called, how many times initialized? "
OriginalModule.original_method
OriginalModule.original_method
OriginalModule.original_method
puts "------ ends "
end
end

Ruby method_added callback not trigger including Modules

I wanted to write a little "Deprecate-It" lib and used the "method_added" callback a lot.
But now I noticed that this callback is not triggered, when including a module.
Are there any callbacks or workarounds, to get class "Foobar" informed when somewhing is included to itself?
Small Demo to demonstrate:
# Including Moduls won't trigger method_added callback
module InvisibleMethod
def invisible
"You won't get a callback from me"
end
end
class Foobar
def self.method_added(m)
puts "InstanceMethod: '#{m}' added to '#{self}'"
end
def visible
"You will get a callback from me"
end
include InvisibleMethod
end
[:invisible, :visible, :wont_exist].each do |meth|
puts "#{meth}: #{Foobar.public_method_defined? meth}"
end
That's the result:
InstanceMethod: 'visible' added to 'Foobar'
invisible: true
visible: true
wont_exist: false
Additional Information:
I really need to use a hook like method_added.
ActiveModel is adding public_instance_methods to Class during runtime though anonymous Modules.
The problem is that including modules doesn't add methods to the classes - it only changes the method call chain. This chain defines which classes/module will be searched for a method, that is not defined for the class in question. What happens when you include a module is an addition of an entry in that chain.
This is exactly the same as when you add a method in a superclass - this doesn't call method_added since it is not defined in the superclass. It would be very strange if a subclass could change the behavior of a superclass.
You could fix that by manually calling method added for an included module, by redefining include for your class:
class Foobar
def self.include(included_module)
included_module.instance_methods.each{|m| self.method_added(m)}
super
end
end
And it is much safer than redefining the included method in Module - the change is narrowed only to the classes, that you have defined yourself.
As suggested by one of the comments, you could use some other hook to get the behavior you want. For example, try to add this at the beginning of your code:
class Module
def included(klass)
if klass.respond_to?(:method_added)
self.instance_methods.each do |method|
klass.method_added(method)
end
end
end
end
Whenever a module is included in a class, all instance methods of that module will be notified to the class, as long as it defines the method method_added. By running your code with the change above I get this result:
InstanceMethod: 'visible' added to 'Foobar'
InstanceMethod: 'invisible' added to 'Foobar'
invisible: true
visible: true
wont_exist: false
Which I think is the behavior you want.
I think deprecation is not big isue to require a library. it is implemented like this in datamapper. About method_added hook; it is working as expected because methods already added to module not class. Only you can get your expected result monkey patching included hook.
# got from https://github.com/datamapper/dm-core/blob/master/lib/dm-core/support/deprecate.rb
module Deprecate
def deprecate(old_method, new_method)
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{old_method}(*args, &block)
warn "\#{self.class}##{old_method} is deprecated, use \#{self.class}##{new_method} instead (\#{caller.first})"
send(#{new_method.inspect}, *args, &block)
end
RUBY
end
end # module Deprecate
class MyClass
extend Deprecate
def old_method
p "I am old"
end
deprecate :old_method, :new_method
def new_method
p "I am new"
end
end
m = MyClass.new
m.old_method
# MyClass#old_method is deprecated, use MyClass#new_method instead (pinger.rb:27:in `<main>')
# "I am new"

Resources