self.included – including class methods from a module in Ruby - ruby-on-rails

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.

Related

Loading module into User Model in Rails

Trying to make available the methods I have stored in a Module which is located in app/models (side note: not sure if this is the correct place for modules?).
Module:
module MyModule
class MyClass
def some_method
# do something
end
end
end
User Model:
class User < ApplicationRecord
include MyModule
def another_method
some_method
end
end
I am getting a NoMethodError:
NoMethodError (undefined method 'some_method' for #<User:0x00007f6a3ce452c0>
You seem to have missunderstood what what modules and classes do in Ruby. In Ruby a module is simply an object that wraps a set of methods and constants.
A module can extend other modules, classes and objects and can be included in classes thus implementing multiple inheritance. Modules in Ruby fill the role that traits, namespaces and singletons do in other languages.
Classes are actually modules (Module is part of the ancestors chain of Class) with the key difference that you can make instances of a class and that class can inherit from a single other class and cannot extend other objects or be included.
The code example here actually doesn't make sense. If you want to declare a method that will be available to classes that include a module you want to declare it in the module itself:
module MyModule
def some_method
# do something
end
end
When you then call User#another_method it will look in the ancestors chain of the User class until it finds the method which is defined in MyModule.
module MyModule
class MyClass
def some_method
# do something
end
end
end
Will actually definte the class MyClass with an instance method that is only available to instances of MyClass. The only thing that the module does here is change the module nesting so that the class is defined in MyModule instead of the global namespace.
If you want to mix in a method from a method into your class then just put the methods directly in the module (without an intermediate class).
Module:
module MyModule
def some_method
# do something
end
end
User Model:
class User < ApplicationRecord
include MyModule
def another_method
some_method
end
end
Have a look at this answer, you need to instantiate your Class first. Or if you want to
class User < ApplicationRecord
include MyModule
def another_method
my_instance = MyClass.new
my_instance.some_method
end
end
As for a place to store your Module, have a look at this guide about service objects, it gave me some inspiration when it comes to different modules.

How to move logic out of a Rails model into a service module?

I want to move some functionality out of a Rails model into a service module.
A concern isn't the correct thing for this as it's only for one model, I just want to tidy up some code elsewhere.
I can't seem to get basic calls on the model to work, here's the set up I have:
/app/models/account.rb
class Account < ApplicationRecord
include SomeService
end
And in a differen't location:
app/services/some_service.rb
module SomeService
def test_code
"abc"
end
def self.test_code_2
"xyz"
end
end
In this case I would then expect Account.first.test_code to output abc and Account.test_code_2 to output zyx.
How do I move functionaity like this out of a model but not into a concern? I feel like I'm very close to this working.
This code doesn't actually define a class method:
module SomeService
def test_code
"abc"
end
def self.test_code_2
"xyz"
end
end
It declares a method on the module itself which you can verify by running SomeService.test_code_2. Thats because self is not a "class method keyword" like static in other languages - its just a reference to the current lexical scope. In this case the module itself.
When you declare methods in a class:
class Foo
def self.bar
"Hello world"
end
end
self is whats known as the singleton class - an instance of the Class class. So it defines a method on the Foo class.
When you include a module in a class you're adding the module to the ancestors chain of the class. It can thus call the instance methods of the module as if it where its own. You can contrast this with extend which imports the methods of the module into the class (test_code becomes a class method).
Thus the ClassMethods pattern which extends the class with an inner module when its included:
module SomeService
def self.included(base)
base.extend ClassMethods
end
def test_code
"abc"
end
module ClassMethods
def test_code_2
"xyz"
end
end
end
How do I move functionaity like this out of a model but not into a concern?
What you're doing is a concern. The term "concern" in Rails really just vaguely means something like "a module thats mixed into classes". The only real definition is that app/models/concerns and app/controllers/concerns are autoloading roots and ActiveSupport::Concern exists which just simplefies the boilerplate code needed when writing mixins. Like for example you can use its class_methods macro to shorten the above code.
There is no actual definition of what a concern should contain or what its role is in an application nor is there any requirement that it be mixed into multiple classes.
But...
Moving logic out of a model (or any class) and into a module actually accomplishes nothing if you then include it back into the model. You're just obscuring the code by shuffling it into multiple files.
The amount of responsibilites and complexity remains the same.
If you actually want to redestribute the responsibilites you want to create an object that can stand on its own and does a unit of work:
class SomeService
def initialize(thing)
#thing = thing
end
def perform
# do something awesome with #thing
end
def self.perform(thing)
new(thing).perform
end
end
This is commonly known as the service object pattern - service modules are AFAIK not a thing. This has a defined set of responsibilites and offloads the model. ActiveJob is an example of this pattern.
What you are doing is known as method extraction - basically just splitting a god like object into modules because modules are good and big classes are evil. Right? Nope. Its still a god class. This became really popular around the time that Rails introduced the concerns folders.
Another solution that should not be overlooked is to look at if the model is actually doing to much and should be split into multiple models with more clearly defined responsibilites.
You can define a ClassMethods module inside your module and include it in the base class. This way, the normal module methods will be available as instance methods and the methods defined inside ClassMethods will be available as class methods in the base class.
app/services/some_service.rb
module SomeService
def self.included(base)
base.extend ClassMethods
end
def test_code
"abc"
end
module ClassMethods
def test_code_2
"xyz"
end
end
end

Nested Modules and Method Hooks

I'm a total newbie at ruby (java background) so I'm sorry if this is a really dumb question.
I'm working through some tutorials on modules and they seem somewhat similar to static classes. The bit I'm having trouble wrapping my head around is why you would do something like the following:
module ExampleModule
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def myMethod
end
end
end
Why wouldn't you just put the methods in ClassMethods into ExampleModule and save adding the method hook. I'm sure I'm missing something really basic but I've been at this for a while now so I feel the need to ask.
It's a ruby idiom. It's useful when you want a module that:
adds some instance methods to a class
adds class methods in the same time /like Java static methods/
in the same time
Example:
module ExampleModule
def self.included(base)
puts 'included'
base.extend(ClassMethods)
end
def foo
puts 'foo!'
end
module ClassMethods
def bar
puts 'bar!'
end
end
end
class ExampleClass
include ExampleModule
end
ExampleClass.bar
ExampleClass.new.foo
If you only want to add class methods, you don't need this idiom, you can just add a method to your module and 'extend' it instead of including it.
When on Rails, this idiom is obsolete and you should use ActiveSupport::Concern instead.
The patter you use here is common when both class methods and instance methods are included via a module in ruby. It gives you the advantage of just having to write
include ExampleModule
for including instance methods and extending class methods instead of
# include instance methods
include ExampleModule
# extend class methods
extend ExampleModule::ClassMethods
So, if used just to extend the class with some methods, my personal preference is to use extend directly.
module ExtensionAtClassLevel
def bla
puts 'foo'
end
end
class A
extend ExtensionAtClassLevel
end
A.bla #=> 'foo'
If both instance and class methods are added, I use the include hook you described.
Some rubyists tend to prefer using extend via the include hook to pure extend, which has no reason if you are just adding class methods like in your example.

Ruby (rails) - including module in model

I'm missing something stupid. Help?
lib/api.rb
require 'httparty'
module API
def self.call_api(query)
base_url = "http://myapi.com"
return HTTParty.get("#{base_url}/#{query}.json")
end
end
models/job.rb
require 'api'
class Job
include API
def self.all(page=1)
self.call_api "jobs?page=#{page}"
end
end
Job::all
NoMethodError: undefined method `call_api' for Job:Class
If I move my "call_api" directly into the Job class, it works. What am I missing?
The other answers have pointed out your issue you should have extend instead of include:
class Job
extend API
You might also like to consider using the following pattern which aside from being useful can also help to alleviate the confusion, since you can tell straight away that your class methods are in the ClassMethods module while your instance methods are directly in the MyModule module.
module MyModule
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def my_class_method
end
end
def my_instance_method
end
end
This uses Ruby's included callback (which is fired every time a module in included in a class), we redefine the callback (which normally doesn't do anything), to extend the class in which the module was included with the ClassMethods sub module. This is a very common metaprogramming pattern in Ruby. If you use this pattern you no longer have to worry about extend, just use include:
class MyClass
include MyModule
then:
MyClass.my_class_method
MyClass.new.my_instance_method
You can also take this pattern a little bit further:
module MyModule
include ClassMethods
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def my_class_method
end
end
def my_instance_method
end
end
Notice that the parent module includes its child ClassMethods module directly. This way my_class_method becomes both an instance and a class method:
class MyClass
include MyModule
MyClass.my_class_method
MyClass.new.my_class_method
You have to be a bit careful if you do that about how you code your method in the ClassMethods child module. But I have found this pattern extremely handy on occasion.
I believe what you need is to define call_api as an instance method in your module, then extend API instead of include API. In general, include is for instance methods and extend is for class methods.
The problem is that you are defining the method call_api directly on the API module, rather than on the inheritance chain of the Job class.
You can call API.call_api just fine, but the problem is that API's "instance methods" are not part of the inheritance chain (not instance methods defined for the receiver of the included module, but API's singleton instance methods - remember API is an instance of the class Module with its own methods)
This is way of doing what you are trying to do:
module API
def self.included(base)
def base.call_api(...)
...
end
end
end
but there may be cleaner ways using extend rather than include on your classes.

Trying to understand how a Controller uses a Plugin/Module

Ok so I am using some module/lib/plugin (not sure of the exact name), let's say its a authentication/authorization plugin like: http://github.com/technoweenie/restful-authentication
class HomeController < ApplicationController
some_module_name_here
end
Now just from adding the code above 'some_module_name_here', I can access to methods from that module.
What is that line of code doing that gives me access to methods/objects from the module?
Is that declaring a variable like in say java/c#:
public SomeModule _someModule;
I know that plugins/modules basically extend the class under the covers, but how does it do this with a single line of code?
Is it called in the constructor somehow?
When you create a ruby plugin, and load it into the rails app via environment.rb, bundler, or a require call, the methods are loaded as "modules" that can be called. The ones that act like you're talking about will have an extra method called acts_as_list or something similar. All that method does is include the methods of the module into the class where that line was called.
Here's an example, which you could include in your app's lib folder and play with:
module Bellmyer
module Pointless
def self.included(base)
base.extend PointlessMethods
end
module PointlessMethods
def acts_as_pointless
unless included_modules.include? InstanceMethods
extend ClassMethods
include InstanceMethods
end
end
end
module ClassMethods
def pointless_class?
true
end
end
module InstanceMethods
def pointless_instance?
true
end
end
end
end
The module is available to any ruby class in your app, but the methods don't actually get loaded until you call acts_as_pointless, which then includes and extends your class with the methods listed. Only the acts_as_pointless method is immediately available to the model. This is the standard pattern for an ActiveRecord plugin.
That's not how it works.
When the plugin or gem is loaded it adds a class method to, in this case, ApplicationController named some_module_name. When you call that methods, a bunch of other class and instance methods are included.
Check out your favourite gem or plugin to see how they do it exactly.

Resources