/active_record_decorator

This ruby gem provides additional functionality for active record in rails

Primary LanguageRubyMIT LicenseMIT

active_record_decorator

This gem is still in developement! contributions are welcome!

The idea bedind this gem is to additional functionality for active record ORM module in rails to make day-to-day application development easy. ActiveRecord is a very powerful tool and extensively used in most Rails apps. When working on application developement we might hit upon certain situation where we felt like "It would be great if Activerecord would have given us this". ActiveRecord as a framework solved many generic cases like scopes, callbacks, relationships to larger extent. But as app application developement evolves we might face some intuitive features which does not make sense at framework level but it does make sense at application development level. Moreover ActiveRecord cannot provide all exhaustive features the whole world wants. But it definitely has the building blocks on which develeopers like us can write abstraction upon which helps others by sharing

Installation

gem install active_record_decorator

Usage

Most of us would have stumbled up following similar use case

#conditional_scopes

class User < ActiveRecord::Base
   scope :active_users, lambda {
     where(status: 1)
   }
end

# Controller Action

def users
  if params[:is_active]
    User.active_users
  else
    User.all
  end
end

To make it simple

class User < ActiveRecord::Base
   include ActiveRecordDecorator
   scope :active_users, lambda {
     where(status: 1)
   }
end

# Controller Action
def users
  User.conditional_scopes(params[:is_active], :active_users)
end

#conditional_includes

To conditionally add includes

class User < ActiveRecord::Base
   include ActiveRecordDecorator
   has_one :image
end

# Controller Action
def users
  User.conditional_includes(params[:include] == 'image', :image)
end

#on_has_one_update

Trigger callback on parent model when has_one child is updated

  class User < ActiveRecord::Base
   include ActiveRecordDecorator
   has_one :image
   
   on_has_one_update :image, :on_image_last_update
   
   def on_image_last_update
     update(:image_last_updated, Time.now)
   end
   
  end
   
  class Image < ActiveRecord::Base
    include ActiveRecordDecorator
    belongs_to :user
  end

#condition_alias

we regularly define conditions in models coupling with column values as below

def order_delivered?
  status == 'delivered'
end
def user_active?
  status = 1
end

The above can be now simplified as

  class Order < ActiveRecord::Base
   
   include ActiveRecordDecorator
   condition_alias :order_delivered?, attr: :status, value: 1
  end
  => order.condition_match?(:order_delivered?)

  class User < ActiveRecord::Base
   
   include ActiveRecordDecorator
   condition_alias :user_active?, attr: :status, value: 1
  end
  => user.condition_match?(:user_active?)

#assign_operation

We often do update operation in place. Over period this will be spread across the codebases. I see couple of problems here. It hinders the readability of developers, by switching route to understand what this update operation means in business domain sense.

If any change in column definition, you can foresee a mammoth task that I am gonna refer here as fellow devs.

Instead do it yourself approach, lets define the operation and tell to perform it as below

  class Order < ActiveRecord::Base
    include ActiveRecordDecorator
    assign_operation :mark_as_delivered, attr: :status, value: 1
    assign_operation :payment_status, attr: :status, value: 1
  end
  
  => order.mark_as_delivered.save
  => order.mark_as_paid.save
  class User < ActiveRecord::Base
   
   include ActiveRecordDecorator
   assign_operation :make_as_active, attr: :status, value: 1
  end
  
  => user.make_as_active.save

#in_batches_by_column We have got handy batch processing utils in rails. But one caveat here is it works on only ID column We might stumble upon use case where we need to process on another column which might be foreign keys or any column with more distinct values. One prerequisite is column should be indexed :) for efficient retrieval. In case if we want to get users who placed orders

class Order < ActiveRecord::Base 
include ActiveRecordDecorator
end

Order.in_batches_by_column(column: :user_id,batch_size: 100, start:1) do |user_ids|
  send_email_for_users(user_ids)
end