Share methods between ActiveModels - ruby-on-rails

I have a simple problem, but I don't know what the best approach is to do it.
I have two ActiveModel classes (say SimpleUser and User). I'd like to share some method implementations between them (say a full_name method that aggregates first name and last name, to stay simple).
In a classical OO language, I'd have used an abstract class that my two classes would have extended. But in Ruby (and even Rails), what's the best approach?

You can put your code in the app/models/concerns and include them in both models.
For example:
app/models/concerns/user/shared_methods.rb
module Concerns::User::SharedMethods
extend ActiveSupport::Concern
def full_name
...
end
end
app/models/user.rb
class User < ActiveRecord::Base
include Concerns::User::SharedMethods
...
end
app/models/simple_user.rb
class SimpleUser < ActiveRecord::Base
include Concerns::User::SharedMethods
...
end

There are several options:
You could create a module, e.g UserAttributes, which would be included in each User class
Or you could create a base class as in conventional object-oriented language and use it as an abstract class. If you want to make sure that no one misuses it, you could make it's constructor private.
It would look like this:
class User
private_class_method :new
end
Note: In ActiveRecord (not ActiveModel) there is a built-in way to make class abstract.
self.abstract_class = true

Related

How to create common model to inherit from in Rails

I would like to convert all my models in a rails app to use uuid as id and maybe if I need, add some fields that will be common to all my models. To do this I am thinking of, instead of doing this:
class MyModel < ActiveRecord::Base
end
doing this
class CommonUUIDModel < ActiveRecord::Base
some_uuid_related_information
end
class MyModel < CommonUUIDModel
end
my questions are:
How can I achieve this (what should be in the place of some_uuid_related_information and is this a good practice for Rails applications?
Prefer extension (mixing in modules) to inheritance (subclasses).
In other words, make a module that alters the models in a uniform way and include that module in all classes that use it.
This is especially true of ActiveRecord-backed models, since subclassing often signals STI (single table inheritance)

Rails & ActiveRecord: Appending methods to models that inherit from ActiveRecord::Base

I have a standard ActiveRecord model with the following:
class MyModel < ActiveRecord::Base
custom_method :first_field, :second_field
end
At the moment, that custom_method is picked up by a module sent to ActiveRecord::Base. The functionality basically works, but of course, it attaches itself to every model class, not just MyModel. So if I have MyModel and MyOtherModel in the same action, it'll assume MyOtherModel has custom_method :first_field, :second_field as well.
So, my question is: How do I attach a method (eg: def custom_method(*args)) to every class that inherits from ActiveRecord::Base, but not by attaching it to ActiveRecord::Base itself?
Any ideas appreciated.
===
Edit
The custom_method is currently attached to ActiveRecord::Base by the following:
module MyCustomModule
def self.included(base)
base.extend(self)
end
def custom_method(*args)
# Zippity doo dah - code goes here
end
end
ActiveRecord::Base.send(:include, MyCustomModule)
Do you know about descendants?
ActiveRecord::Base.descendants
You have to be sure to touch the models before calling it.
See excellent discussion here:
Is there a way to get a collection of all the Models in your Rails app?
I concur with the commentors above that you may want to consider adding your methods to the meta class, or an intermediary class, or a Module mixin.

Is there something similar to application_helper or application_controller for models?

I have some functions that I use in every single model and I'd like to place these in something like ActiveRecord::Base so I won't have to name the same functions in all of my models.
I'm not even sure if something like this is according to best practices. Perhaps some Rails pros could show me something better.
Write a module that contains your required methods, and include MyModule as needed.
You certainly can do as #derekerdmann suggested and create an abstract base class for your models:
class MyBaseModel < ActiveRecord::Base
abstract_class = true
def my_method(*args)
#code goes here
end
end
class MyModel < MyBaseModel
end
Just don't forget the abstract_class = true line or single table inheritance is assumed.
Personally, I prefer the mixin methodology, because if your models ever diverge in common functionality, you can group common functions into separate modules and include them as needed.
Remember that you can still use standard object-oriented practices in Rails. Make a class that extends ActiveRecord::Base with all your common functionality, and then extend that class for each of your real ActiveRecord models.

Refactoring ActiveRecord models with a base class versus a base module

Class A and B are identical:
class A < ActiveRecord::Base
def foo
puts "foo"
end
end
class B < ActiveRecord::Base
def foo
puts "foo"
end
end
What's the difference between refactoring like this with a base class:
class Base < ActiveRecord::Base
def foo
puts "foo"
end
end
class A < Base
end
class B < Base
end
versus like this using a base module:
module Base
def foo
puts "foo"
end
end
class A < ActiveRecord::Base
include Base
end
class B < ActiveRecord::Base
include Base
end
Is one way preferable over another?
There's a fundamental difference between those two methods that all the other answers are missing, and that's rails' implementation of STIs (Single Table Inheritance):
http://api.rubyonrails.org/classes/ActiveRecord/Base.html (Find the "Single Table Inheritance" section)
Basically, if you refactor your Base class like this:
class Base < ActiveRecord::Base
def foo
puts "foo"
end
end
class A < Base
end
class B < Base
end
Then, you are supposed to have a database table called "bases", with a column called "type", which should have a value of "A" or "B". The columns on this table will be the same across all your models, and if you have a column that belongs to only one of the models, your "bases" table will be denormalized.
Whereas, if you refactor your Base class like this:
Module Base
def foo
puts "foo"
end
end
class A < ActiveRecord::Base
include Base
end
class B < ActiveRecord::Base
include Base
end
Then there will be no table "bases". Instead, there will be a table "as" and a table "bs". If they have the same attributes, the columns will have to be duplicated across both tables, but if there are differences, they won't be denomarlized.
So, if one is preferable over the other, yes, but that's specific to your application. As a rule of thumb, if they have the exact same properties or a big overlap, use STI (1st example), else, use Modules (2nd example).
Both of these methods will work. When deciding to use a module or a class, the question I have is does the class fit into the object hierarchy, or are these just methods I am looking to reuse. If I am just trying to factor out common code for DRY reasons, that sounds like a module. If there really is a class that fits into the hierarchy that makes sense on its own, I use a class.
Coming from a Java background, it is refreshing I can choose to make these decisions.
You have more flexibility with the module. The module's intent is to span across different types of classes. With the other method you are locking yourself into Base. Other than that, there isn't much difference.
Ruby's answer to multiple inheritance is mixins. Since your classes are already inheriting from Rails specific classes, they can no longer inherit from your custom classes.
So your choice is to chain together in a long chain, or use a mixin which is much cleaner, and easier to understand.
The module gives you more flexibility in that 1) you can only inherit from one class, but you can include multiple modules, and 2) you can't inherit from a base class without inheriting its superclasses, but you can include a module all by itself (e.g. you might want to add the "foo" method to another class that isn't an active record model).
Another difference is that within the methods in the class Base you could call things from ActiveRecord::Base, but you couldn't do that from the module.
It depends on what you are really trying to do.
Overriding or adding methods to ActiveRecord::Base: Do this if you want every ActiveRecord model in your app to respond_to foo.
Subclass ActiveRecord::Base, and have every model inherit from your subclass: Achieves the same as 1, but every model in your app needs to extend an unconventional class, so why go through the trouble.
include module: This works great if only some number of models need access to foo. This is pretty much what all those acts_as_<whatever> plugins do.
Bottom line, if you want every single model to have a different behavior to what ActiveRecord::Base already provides, use option 1. If only a handful of your models require the behavior, create a module and include it in your models (option 3).

Where to put model "utility" functions in Ruby on Rails

I have a rails app with several models.
I have a function that I want to access from several models.
What's the best place to put this code and how can I make it accessible from the models that need to get at it?
My understanding is that helpers are just for views. Is this correct?
It seems wrong to create a plug-in and put it in the vendor folder - this is my code and integral to my app. Is this correct?
Thanks.
The simplest solution would be to create a module under lib and mix this into the models that need it, for instance, in lib/fooable.rb:
module Fooable
def do_foo
end
end
And then in your various models:
class MyModel < ActiveRecord::Base
include Fooable
end
No need to require fooable.rb, the Rails autoloading mechanism will find it for you as long as it's named using correct conventions.
In order to lessen the repetition of code, you could also create a main class which would include that module and the simply inherit from it from every model you'd like to share the behaviour.
Something like:
module Fooable
def do_foo
end
end
class ParentModel < ActiveRecord::Base
include Fooable
end
class Product < ParentModel end
class User < ParentModel end
class Item < ActiveRecord::Base end
Thus, in that example, both Product and User would share the do_foo functionality and Item would not.

Resources