I want to create a module which provides some common methods to the classes which are inherited from active record base.
Following is the two-way we can achieve it.
1)
module Commentable
def self.extended(base)
base.class_eval do
include InstanceMethods
extend ClassMethods
end
end
module ClassMethods
def test_commentable_classmethod
puts 'test class method'
end
end
module InstanceMethods
def test_commentable_instance_method
puts 'test instance method'
end
end
end
ActiveRecord::Base.extend(Commentable)
2)
module Commentable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def test_commentable_classmethod
puts 'test class method'
end
end
def test_commentable_instance_method
puts 'test instance methods'
end
end
ActiveRecord::Base.send(:include, Commentable)
Which one is the preferred way to handle this?
And
What to use when?
Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database. It is an implementation of the Active Record pattern which itself is a description of an Object Relational Mapping system.
The active record pattern is an approach to accessing data in a database. A database table or view is wrapped into a class. Thus, an object instance is tied to a single row in the table. After creation of an object, a new row is added to the table upon save. Any object loaded gets its information from the database.
An instance of ActiveRecord::Base is an object that represents a specific row of your database (or might be saved into the database). Whereas an instance of ActiveRecord::Relation is a representation of a query that can be run against your database (but wasn't run yet).
ORM - Object Relational Mapping provides a variety of frameworks and techniques that are helpful in working with Relational Databases such as MySQL, PostgreSQL, etc. “Active record - an object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.”
As of Rails 5, the recommended way is to make a module and include it in the models where it is needed, or everywhere using ApplicationRecord, which all models inherit from. (You can easily implement this pattern from scratch in older versions of Rails.)
# app/models/concerns/my_module.rb
module MyModule
extend ActiveSupport::Concern
module ClassMethods
def has_some_new_fancy_feature(options = {})
...
end
end
end
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
include MyModule
end
Modules are a form of multiple-inheritance, and sometimes add unnecessary complexity. Check if a decorator, service, or other kind of object makes more sense first. Not everything needs be a fancy macro that adds 50 callbacks to your model. You will hate your life if you do this too much.
If you want to monkey-patch (DON'T DO THIS), here is my old answer:
# config/initializers/activerecord_extensions.rb
ActiveRecord::Base.send(:include, MyModule)
Or without monkey-patching (see Mori's response):
# app/models/base_model.rb
class BaseModel < ActiveRecord::Base
self.abstract_class = true
include MyModule
end
Edit: Several months down the road in a large project, I have realized its better to have every model inherit from a new base model class, as Mori explains. The problem with including modules directly into ActiveRecord::Base is this can interfere with third-party code that also relies on ActiveRecord. It is just better not to monkey-patch when you don't have to. In this case, creating a new base class can end up being simpler in the long run.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With