Rails: How to effectively use self.inherited - ruby-on-rails

I have an AbstractRecord model from which some of the concrete models (which have their own tables) descend. Following is the inheritance.
AbstractRecord < ActiveRecord::Base
Blog < AbstractRecord
Post < AbstractRecord
....
....
In order for Rails to look for the proper tables if there is inheritance, API docs say to define a class method abstract_class? that returns true so that rails won't look for its table. In my case, in order for rails to look for blogs table (instead of abstract_records table, which is typically the case as in STI) I defined method abstract_class? in AbstractRecord that returns true. All the queries seems to work fine. But I see whenever I instantiate Blog, rails shows it as Blog (abstract) in the console as its parent class returns true. In order to avoid this, I could again define abstract_class? that returns false in Blog class.
But I was thinking instead of defining abstract_class? in all the child models, if I could somehow make use of self.inherited and define that method in AbstractClass itself. I tried to use several approaches (following) none seems to work.
class AbstractRecord < ActiveRecord::Base
def self.abstract_class?
true
end
def self.inherited(subclass)
super
subclass.instance_eval do
define_method(:abstract_class?) { false }
end
end
end
class AbstractRecord < ActiveRecord::Base
def self.abstract_class?
true
end
def self.inherited(subclass)
super
subclass.class_eval do
define_method(:abstract_class?) { false }
end
end
end
class AbstractRecord < ActiveRecord::Base
def self.abstract_class?
true
end
def self.inherited(subclass)
super
subclass.instance_eval do
def abstract_class?
false
end
end
end
end
class AbstractRecord < ActiveRecord::Base
def self.abstract_class?
true
end
def self.inherited(subclass)
super
subclass.class_eval do
def abstract_class?
false
end
end
end
end
Any advise on what I am doing wrong is appreciated?

Try this:
def self.inherited(subclass)
super
def subclass.abstract_class?
false
end
end
Or:
def self.inherited(subclass)
super
subclass.class_eval do
def self.abstract_class?
# You lost the 'self' part, so you had just defined an instant method for the subclass
false
end
end
end

class Account < Foo::Bar::Base
end
module Foo
module Bar
class Base < ::ActiveRecord::Base
def self.abstract_class?
true
end
end
end
end
That works fine for me. It results in table name "accounts" as it have to be.

Related

How to include multiple model in rails lib

In my /lib i have this class below:
module Application
class Post < ActiveRecord::Base
attr_accessor :id
def artilce_content
post.articles.content
end
private
def post
Post.find(id)
end
end
end
But the problem is article is undefined.
NoMethodError: undefined method ziptag_type' for #<Application::Api::V2::Ziptag:0x00000008edc120>
The question is, how can I use or include multiple models in /lib? I tried adding class User < ActiveRecord::Base; end above module
class Article < ActiveRecord::Base; end
module Application
class Post < ActiveRecord::Base; end
:
:
:
end
end
but is doesn't worked.
The best way here is to use require.
require 'your_model'
In your case, it should be:
require 'article'
module Application
class Post < ActiveRecord::Base
attr_accessor :id
def artilce_content
post.articles.content
end
private
def post
Post.find(id)
end
end
end
I've got the answer. It should be:
class Article < ActiveRecord::Base; attr_accessor :column1, :other_column end
Not:
class Article < ActiveRecord::Base; end
Now, it's working fine on me.

How do I add global Rails helpers like has_many and devise_for?

In Rails, I'd like to apply functionality to many models at the same time, having the ability to add it to other models in the future.
I'm looking for something like...
class Stuff < ActiveRecord::Base
some_tag
end
class Thing < ActiveRecord::Base
some_tag
end
to give those and any other models with
some_tag
the functionality of
class Functionality
has_many :other_things, polymorphic: true
def does_something
end
end
First step: Create a module with the method you want to add to your classes:
module FooFunction
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def does_something(options = {})
...
end
end
end
Next step: Include that module into the class
# in config/initializers/foo_function.rb
class Object
include FooFunction
end

What is the preferred way to add some method in active record base?

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?
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.
Another way is make your own base class by inheriting from ActiveRecord::Base and then letting your models inherit from that base class. This has the advantage of making it clear that your models aren't running on vanilla ActiveRecord:
class MyBase < ActiveRecord::Base
self.abstract_class = true
def self.a_class_method
end
def an_instance_method
end
end
class Foo < MyBase
end
Foo.a_class_method
Foo.new.an_instance_method
reffering with Mori's answer...you can do something like:-
Module ActiveRecordUtilities
class MyBase < ActiveRecord::Base
self.abstract_class = true
def self.a_class_method
end
def an_instance_method
end
end
end##class ends
end##module ends
and can use it ...suppose in user.rb
include ActiveRecordUtilities::MyBase
User.a_class_method
#user.instance_method
============================OR====================
module MyUtils
def do_something_funky
# Some exciting code
end
end
class Account < ActiveRecord::Base
belongs_to :person, :extend => MyUtils
end
And then call it like this:
#account = Account.first
#account.person.do_something_funky

Inherit active_record in rails

Right now my classes are look like this.
class BalanceName < ActiveRecord
def before_validation
set_blank_attributes_to_nil(#attributes)
end
end
class Balance < ActiveRecord
def before_validation
set_blank_attributes_to_nil(#attributes)
end
end
I want to inherite activer record into one class and than want to inherite that class into other classes like.
I want something like this.
class CommonActiveRecord < ActiveRecord::Base
def before_validation
set_blank_attributes_to_nil(#attributes)
end
end
class BalanceName < CommonActiveRecord
def before_validation
super
end
end
class Balance < CommonActiveRecord
def before_validation
super
end
end
You can do exactly as you have done except you do not need to redefine the before_validation methods in your subclasses (though I guess these may be here prior to being filled with more specific validation).
You will also need to indicate to rails that your CommonActiveRecord class is abstract and therefore is not persisted by adding:
class CommonActiveRecord < ActiveRecord::Base
self.abstract_class = true
end
You can create module (e.g. lib/common_active_record.rb):
module CommonActiveRecord
def before_validation
set_blank_attributes_to_nil(#attributes)
end
end
And then in your model simply include it:
class BalanceName < ActiveRecord::Base
include CommonActiveRecord
end
class Balance < ActiveRecord::Base
include CommonActiveRecord
end

adding class methods to ActiveRecord::Base

I have created an instance method which is also a callback (if that makes sense) which does some stuff thats irrelevant. I would love to be able to just call:
class Model < ActiveRecord::Base
fix_camelcase_columns
end
Instead at the moment I have this:
def after_find
self.class.columns.each do |column|
self.instance_eval("def #{column.name.to_underscore}; self.#{column.name}; end;")
end
end
I would love to abstract this and use it on other classes. Any pointers?
Well, you can open up ActiveRecord::Base and throw a method there:
class ActiveRecord::Base
def self.fix_camelcase_columns
define_method :after_find do
...
end
end
end
For a cleaner way, create a module:
module CamelcaseFixer
def self.included(base)
base.extend(self)
end
def fix_camelcase_columns
define_method :after_find do
...
end
end
end
and then in your model do
class Model < ActiveRecord::Base
include CamelcaseFixer
fix_camelcase_columns
end
Didn't test the code, see if it works.

Resources