Models
Role of Models
A model is an abstraction on top of data. It should only be responsible of understanding its own data structure and logic.
A model is also part of a collection. Everything related in doing specfic queries to the collection should be in the model. If the query is too complex, it should be a Query Object instead.
Location
app/models/
- Should have no prefix and no suffix.
Usage
- A model can be called from everywhere in the application.
- Try to not call them directly in the Views.
Naming
The model has the name of the SQL table in singular and Capitalzed. Examples:
User
Admin
Post
JobOffer
Company
Structure
A model can handle different behaviors and include plugins. This is the recommended order of declaration in the model class:
include
concerns
relations
:belongs_to
,has_many
,has_one
constantes
validation
: If there is no specific Predicates.callbacks
plugins
scopes
class methods
: custom queries (where
)instance methods
private methods
Things to avoid
A model should be as standalone as possible. These things should be avoided in a model and moved in a better place like Services, Query Objects, Decorators, and Model Concerns:
- Formating: Decorators
- Calling external API's: Services
- Complex logic involving relations: Query Objects
- Callbacks: Use them if it's really necessary (like generating slugs after create or update).
Code
class User < ApplicationRecord
include SecurePasswordConcern
belongs_to :company
has_many :projects
MIN_AGE = 16
validates :first_name, presence: true
validates :email, uniqueness: true
after_create :generate_api_token
after_create :generate_slug
geocode_by :address
scope :actives, -> { where("last_signd_in_at >= ?", 1.day.ago) }
scope :young, -> { where("birthdate >= ?", MIN_AGE.years.ago) }
class << self
def remove_inactives
where("last_signd_in_at < ?", 1.year.ago).destroy_all
end
end
def address
"#{street}, #{postcode} #{city}, #{country}"
end
def active?
self.last_signd_in_at < 1.day.ago
end
private
def generate_api_token
self.api_token = SecureRandom.hex(10)
self.save
end
def generate_slug
self.slug = "#{self.first_name}-#{self.last_name}".parametize
self.save
end
end