Tuesday, April 23, 2024
HomeRuby On RailsThe standard ActiveModel | Stanko Okay.R.

The standard ActiveModel | Stanko Okay.R.


ActiveModel is one among my most used instruments in Rails purposes. I exploit it in service objects, kind objects and objects that signify exterior entities.

Why? As a result of it supplies a pleasant interface for validating inputs and outcomes, it could have callbacks for pre and post-processing knowledge, and it integrates effectively into varied Rails conventions.

A easy login kind is my go to instance for explaining how good ActiveModel is to work with.

I’ve seen individuals do login flows in controllers, in Consumer fashions and in interactors. However no different method is as simple as making a Login mannequin.

Have a look at this Type object or Exterior entity carried out as a humble mannequin:

# /app/fashions/login.rb
class Login
  embrace ActiveModel::Mannequin

  attr_accessor :username, :password

  validates :username, presence: true
  validates :password, presence: true
  validate :validate_user_exists!
  validate :validate_password_for_user!

  before_validation do
    @consumer = nil
  finish

  def validate_user_exists!
    return if consumer.current?

    errors.add(:username, "not discovered")
  finish

  def validate_password_for_user!
    # `authenticate` comes from Rails
    # Reference:  https://api.rubyonrails.org/courses/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password
    return if consumer&.authenticate(password)

    errors.add(:password, "is invalid")
  finish

  def consumer
    @consumer ||= Consumer.find_by(username: username)
  finish
finish

# app/controllers/logins_controller.rb
class LoginsController < ApplicationController
  def new
    @login = Login.new
  finish

  def create
    @login = Login.new(params.require(:login).allow(:username, :password))

    if @login.legitimate?
      session[:current_user_id] = @login.consumer.id
      redirect_to root_path, discover: "Welcome again!"
    else
      render :new, standing: :unprocessable_entity
    finish
  finish
finish

You need to use form_for with out additional arguments or configuration, I18n works for the attributes and error messages similar to it does for ActiveRecord fashions, and if the login fails the fields get repopulated.

It seems to be and appears like you’re working with an ActiveRecord mannequin, which most of us know and love.

In terms of Service objects, the ActiveModel method provides a solution to talk success and failure via validations. You’ll be able to verify the inputs with legitimate?, or you possibly can verify the end result, and return a pleasant error message.

class Machine::WarehouseReservator < ApplicationModel
  attr_accessor :system, :consumer

  validates :system, presence: true
  validates :consumer, presence: true

  after_initialize do
    self.consumer ||= system.creator
  finish

  def reserve!
    return false if invalid?

    response = HTTP.submit("http://system.stock.com/api/v1/register", knowledge: payload)

    if response.okay?
      errors.add(:system, failed to register)
      return false
    finish

    self.warehouse_reference = system.create_warehouse_reference(reservation_id: JSON.parse(response.physique).fetch(:id))
  finish

  def payload
    { device_id: system.id, title: system.title, reservation_holder: consumer.title }
  finish
finish

reservator = Machine::WarehouseReservator.new(consumer: Consumer.all.pattern, system: Machine.all.pattern.reserver!
reservator.legitimate? # => true
reservator.reserver!
Reservator.warehouse_reference.id # => 1

reservator = Machine::WarehouseReservator.new(consumer: nil, system: nil)
reservator.legitimate? # => false
reservator.reserver! # => false
reservator.errors.full_messages # => ["User missing", "Device missing"]

It is a a lot nicer solution to talk what went mistaken than elevating and catching errors, returning symbols or customized Error objects.

ActiveModel is kind of a flexible device for that alone, however there’s far more that it could do like:


P.S. Many due to Hrvoje S. For reviwing this text.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments