How do I include a module using a method in Ruby? - ruby-on-rails

I have this module that is a part of a gem I'm writing. I currently use it as follows:
gem 'foobar' # Gemfile
class Baz < ActiveRecord::Base
include Foo::Bar
say
end
module Foo
module Bar
module ClassMethods
def say
"hello"
end
end
extend ClassMethods
end
end
To make say work, I have to include Foo::Bar before calling it. Is there anyway to call say without first having to include the module? (Have it do the include for me?) I see other gems just magically add methods to classes without using include--just a matter of adding the gem and running bundle. How does this happen?

If you want the say method to be general and not specific to objects, make it a class method:
module Foo
module Bar
def self.say
"hello"
end
end
end
Then you can call it directly:
class Baz < ActiveRecord::Base
Foo::Bar.say
end
Edit: To answer your new question (regarding the gem), you could re-open the ActiveRecord::Base class and define the methods there, although doing it with a separate module is the best way (cleaner and semantically correct).

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.

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.

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.

What should I not include in the `included do ... end` block?

I am using Ruby on Rails 3.2.2. I am implementing a module and including that in a my class by using the RoR ActiveSupport::Concern feature. It makes available the included do ... end block making code stated inside that to be executed in the class context of the class where the module is included.
My doubt is: What should I not include in the included do ... end block? That is, for instance, is it a "common" / "good" practice to make the following?
module MyModule
extend ActiveSupport::Concern
class MyModuleClass
attr_accessor :attr1, :attr2, :attr3
def initialize(attrs)
#attr1 = attrs[:attr1]
#attr2 = attrs[:attr2]
#attr3 = attrs[:attr3]
end
end
included do
#my_module_class = MyModuleClass.new(some_attrs)
end
end
More, will be the #my_module_class variable available as an attribute in the including class of MyModule (BTW: I would like to make the #my_module_class to be "visible" only internally to MyModule since it is intended to be used only in that module)? Are there some "advanced" examples or tutorials on how to handle situations like that I am trying to instantiate in the included do ... end block of the above code? What do you advice about?
#my_class will be an instance of MyClass and not MyModule. If you want to make all instances of MyClass be an instance of MyModule you should write:
include MyModule
inside the class definition.
I think my answer makes sense if you look at the original version of this question before it was edited.
EDIT 1:
Let's add on to your example and say you have a class called Foo:
class Foo
include MyModule
end
You want to make an instance of MyModuleClass that is associated with Foo but it sounds like you don't really want to modify Foo or give it access to the MyModuleClass. I propose that you use a hash table:
module MyModule
# ...
#hash = {}
class << self
attr_accessor :hash
end
included do
MyModule.hash[self] = MyModuleClass.new(some_attrs)
end
end
I think that will work, and it avoids adding an instance variable to to the Foo class object. Technically, any part of the ruby code can access MyModule.hash but you should put a comment in the source code telling people NOT to do that, and don't advertise that the hash exists.

inject/access methods from a parent module inside a model

In Rails I have the following structure
#.../models/A.rb
module A
def m
end
end
#.../models/a/B.rb
class A::B < ActiveRecord::Base
end
This automatically places A as a parent of B. Is there a way to do something like B.m without modifying B? I know that I could do something like B.parent.m and from there create aliases, but then i would have to change B.
I'm looking to somehow inject a code present in A into B, but I don't know where this automatic association is done behind the scenes.
Something like
module A
module C
def mc
end
end
def placed_as_parent (child) # supposing this is the method called to put this module as a parent
super child
child.include(C) #this is what I would like to do
end
end
The question behind it is that I have a module which is already being shared among several models of that folder and I would like to put some common stuff for the models in there without have to manually include/extend a module in each of my models
[[EDITED]]
I'm not being clear with my question. In rails 3 if you do
rails generate active_record:model A::B
it will generate the files
#.../models/A.rb
module A
def self.table_name_prefix
'a_'
end
end
#.../models/a/B.rb
class A::B < ActiveRecord::Base
end
So if I open a console and type
A::B.table_name # -> 'a_b'
A::B.table_name_prefix # -> ''
A::B.parent # -> A
A.table_name_prefix # 'a_'
This happens automatically without any include/extend in the model B. What I want is to include more stuff in A and access it from B, without changing anything on B as i described earlier.
To be honest I'm not sure I fully understand your question but I'll give it a shot anyway.
There is a hook in the Module class that allows you to get a reference to the class the module is being included into. Thus, you could then do virtually anything with it.
An example:
module A
# you can change the class' behavior here
def self.included(klass)
puts "included in #{klass}"
end
end
And then to use it:
class B
include A #this causes the included hook in the module to be called
end
Is this what you're after?
The OP wrote:
The question behind it is that I have a module which is already being shared among several models of that folder and I would like to put some common stuff for the models in there without have to manually include/extend a module in each of my models
Here's what I would do:
module Stuff1
...
end
module Stuff2
...
end
module StuffIWantInSeveralModels
include Stuff1, Stuff2
end
class X < ActiveRecord::Base
include StuffIWantInSeveralModels
end
class Y < ActiveRecord::Base
include StuffIWantInSeveralModels
end
Then when you want to add a new module to several of your models, you only have to write an "include" statement in one place (in the StuffIWantInSeveralModels module).
Each module should be in its own file in the lib directory, with the file name matching the name of the module so Rails auto-loading will work (e.g. stuff_i_want_in_several_models.rb).
Does this achieve what you wanted?

Resources