require 'delegate' module Pardner # A base class for creating an active model that decorate an active record # model to add behavior. Common active record persistence methods are # available and delegated to the decorated active record instance. # # To use: # # 1. create a subclass of Pardner::Base # 2. call .pardner_config to configure things # 3. override methods, add validations, add callbacks etc to get custom # behavior # # For example: # # class BookDecorator < Pardner::Base # pardner_config Book # # after_save :send_email # # private # # def send_email # BookMailer.saved_book(self).deliver_now # end # end # class Base < SimpleDelegator extend ActiveModel::Callbacks include ActiveModel::Model include ActiveModel::Validations include ActiveModel::Validations::Callbacks class_attribute :pardner_config define_model_callbacks \ :destroy, :save, :validation # Configure the decorator by calling this. Pass in one or many classes # that will be delegated to. Define a block which'll be passed a # configuration object to configure more things. def self.howdy_pardner(decorated_class) self.pardner_config = pardner_config ? pardner_config.deep_dup : Config.new pardner_config.decorated_class = decorated_class nil end def initialize(decorated_record) __setobj__ decorated_record end # Returns the decorated record def decorated_record __getobj__ end # Returns the decorated record deeply, ignoring any nested decorators. def decorated_record_deep __getobj__.is_a?(Pardner::Base) ? __getobj__.decorated_record_deep : __getobj__ end def self.model_name if pardner_config.try(:decorated_class) pardner_config.decorated_class.model_name else super end end def [](attr) send(attr) end def []=(attr, value) send("#{attr}=", value) end def attributes=(attrs = {}) attrs.each do |attr, value| public_send "#{attr}=", value end end def save valid? or return false status = ActiveRecord::Base.transaction do run_callbacks(:save) { super } end status == true end def save! save or fail InvalidModel, "Validation failed: #{errors.full_messages.join(',')}" end def destroy ActiveRecord::Base.transaction do run_callbacks(:destroy) { super } end end def update(attrs = {}) self.attributes = attrs save end alias_method :update_attributes, :update def update!(attrs = {}) update attrs or fail InvalidModel, "Validation failed: #{errors.full_messages.join(',')}" end alias_method :update_attributes!, :update! def valid? run_callbacks :validation do if super && __getobj__.valid? true else false end end end def persisted? decorated_record.persisted? end def new_record? decorated_record.new_record? end def errors(*args, &block) decorated_record.errors(*args, &block) end end end