Rails extending ActiveRecord::Base
I've done some reading about how to extend ActiveRecord:Base class so my models would have some special methods. What is the easy way to extend it (step by step tutorial)?
Solution 1:
There are several approaches :
Using ActiveSupport::Concern (Preferred)
Read the ActiveSupport::Concern documentation for more details.
Create a file called active_record_extension.rb
in the lib
directory.
require 'active_support/concern'
module ActiveRecordExtension
extend ActiveSupport::Concern
# add your instance methods here
def foo
"foo"
end
# add your static(class) methods here
class_methods do
#E.g: Order.top_ten
def top_ten
limit(10)
end
end
end
# include the extension
ActiveRecord::Base.send(:include, ActiveRecordExtension)
Create a file in the config/initializers
directory called extensions.rb
and add the following line to the file:
require "active_record_extension"
Inheritance (Preferred)
Refer to Toby's answer.
Monkey patching (Should be avoided)
Create a file in the config/initializers
directory called active_record_monkey_patch.rb
.
class ActiveRecord::Base
#instance method, E.g: Order.new.foo
def foo
"foo"
end
#class method, E.g: Order.top_ten
def self.top_ten
limit(10)
end
end
The famous quote about Regular expressions by Jamie Zawinski can be re-purposed to illustrate the problems associated with monkey-patching.
Some people, when confronted with a problem, think “I know, I'll use monkey patching.” Now they have two problems.
Monkey patching is easy and quick. But, the time and effort saved is always extracted back sometime in the future; with compound interest. These days I limit monkey patching to quickly prototype a solution in the rails console.
Solution 2:
You can just extend the class and simply use inheritance.
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
Solution 3:
You can also use ActiveSupport::Concern
and be more Rails core idiomatic like:
module MyExtension
extend ActiveSupport::Concern
def foo
end
module ClassMethods
def bar
end
end
end
ActiveRecord::Base.send(:include, MyExtension)
[Edit] following the comment from @daniel
Then all your models will have the method foo
included as an instance method and the methods in ClassMethods
included as class methods. E.g. on a FooBar < ActiveRecord::Base
you will have: FooBar.bar
and FooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html