The original class from the "Soft delete your ActiveRecord" example:
class Person < ActiveRecord::Base attr_accessible :name validates :active, :inclusion => { :in => [true, false] } before_validation :activate, :on => :create, :if => Proc.new {|r| r.active.nil? } def self.active where :active => true end def activate self.active = true self end def deactivate self.active = false self end endcan be refactored to include the soft deletion feature by composition. Starting with the module structure in the new created file lib/activerecord_extentions.rb:
module ActiveRecordExtensions module Deactivateable extend ActiveSupport::Concern self.included do end end endThe module Deactivateable was scoped by ActiveRecordExtensions to be more clear about its intention.
Using the Ruby on Rails ActiveSupport::Concern module helps to handle module dependencies gracefully.
The self.included block contains all the stuff, that has to be done immediately when the module was included. Until now it contains not a bit logic, but can already be included:
class Person < ActiveRecord::Base include ActiveRecordExtensions::Deactivateable attr_accessible :name validates :active, :inclusion => { :in => [true, false] } before_validation :activate, :on => :create, :if => Proc.new {|r| r.active.nil? } def self.active where :active => true end def activate self.active = true self end def deactivate self.active = false self end endExtracting the instance methods and the putting them into the module:
module ActiveRecordExtensions module Deactivateable extend ActiveSupport::Concern self.included do end def activate self.active = true self end def deactivate self.active = false self end end endThe second step is to move the validator and the default setter on creation into the self.included block:
module ActiveRecordExtensions module Deactivateable extend ActiveSupport::Concern self.included do validates :active, :inclusion => { :in => [true, false] } before_validation :activate, :on => :create, :if => Proc.new {|r| r.active.nil? } end def activate self.active = true self end def deactivate self.active = false self end end endBoth the validator and the callback are processed right at including time on the target.
At least the class method is moved into the class scope of the prospectively enriched class:
module ActiveRecordExtensions module Deactivateable extend ActiveSupport::Concern self.included do validates :active, :inclusion => { :in => [true, false] } before_validation :activate, :on => :create, :if => Proc.new {|r| r.active.nil? } end def activate self.active = true self end def deactivate self.active = false self end module ClassMethods def active where :active => true end end end endFinally, let's take a look at the target class Person after moving the soft delete feature into the module:
class Person < ActiveRecord::Base include ActiveRecordExtensions::Deactivateable attr_accessible :name endand benefit from it by sharing it with other classes:
class Task < ActiveRecord::Base include ActiveRecordExtensions::Deactivateable endThe benefits are:
- Modularized features (means concise code blocks)
- Maintainable code
- Avoids the downsides of inheritance
Supported by Ruby 2.1.1 and Ruby on Rails 3.2.17
Keine Kommentare:
Kommentar veröffentlichen