diff --git a/activemodel/README b/activemodel/README index c20f732a12a3d..1d987605c0f22 100644 --- a/activemodel/README +++ b/activemodel/README @@ -1,21 +1,33 @@ -Active Model -============== += Active Model - defined interfaces for Rails -Totally experimental library that aims to extract common model mixins from -ActiveRecord for use in ActiveResource (and other similar libraries). -This is in a very rough state (no autotest or spec rake tasks set up yet), -so please excuse the mess. +Prior to Rails 3.0, if a plugin or gem developer wanted to be able to have +an object interact with Action Pack helpers, it was required to either +copy chunks of code from Rails, or monkey patch entire helpers to make them +handle objects that did not look like Active Record. This generated code +duplication and fragile applications that broke on upgrades. -Here's what I plan to extract: - * ActiveModel::Observing - * ActiveModel::Callbacks - * ActiveModel::Validations +Active Model is a solution for this problem. - # for ActiveResource params and ActiveRecord options - * ActiveModel::Scoping +Active Model provides a known set of interfaces that your objects can implement +to then present a common interface to the Action Pack helpers. - # to_json, to_xml, etc - * ActiveModel::Serialization +You can include functionality from the following modules: + +* Callbacks + + class MyClass + extend ActiveModel::Callbacks + define_model_callbacks :create + + def create + _run_create_callbacks do + # Your create action methods here + end + end + end + + ...gives you before_create, around_create and after_create class methods that + wrap your create method. + + {Learn more}[link:classes/ActiveModel/CallBacks.html] -I'm trying to keep ActiveRecord compatibility where possible, but I'm -annotating the spots where I'm diverging a bit. \ No newline at end of file diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb index f66a1ddcaa847..a7e0cf90c1baf 100644 --- a/activemodel/lib/active_model/callbacks.rb +++ b/activemodel/lib/active_model/callbacks.rb @@ -1,6 +1,52 @@ require 'active_support/callbacks' module ActiveModel + # == Active Model Callbacks + # + # Provides an interface for any class to have Active Record like callbacks. + # + # Like the Active Record methods, the call back chain is aborted as soon as + # one of the methods in the chain returns false. + # + # First, extend ActiveModel::Callbacks from the class you are creating: + # + # class MyModel + # extend ActiveModel::Callbacks + # end + # + # Then define a list of methods that you want call backs attached to: + # + # define_model_callbacks :create, :update + # + # This will provide all three standard callbacks (before, around and after) around + # both the :create and :update methods. To implement, you need to wrap the methods + # you want call backs on in a block so that the call backs get a chance to fire: + # + # def create + # _run_create_callbacks do + # # Your create action methods here + # end + # end + # + # The _run__callbacks methods are dynamically created when you extend + # the ActiveModel::Callbacks module. + # + # Then in your class, you can use the +before_create+, +after_create+ and +around_create+ + # methods, just as you would in an Active Record module. + # + # before_create :action_before_create + # + # def action_before_create + # # Your code here + # end + # + # You can choose not to have all three callbacks by passing an hash to the + # define_model_callbacks method. + # + # define_model_callbacks :create, :only => :after, :before + # + # Would only create the after_create and before_create callback methods in your + # class. module Callbacks def self.extended(base) base.class_eval do @@ -8,43 +54,39 @@ def self.extended(base) end end - # Define callbacks similar to ActiveRecord ones. It means: - # - # * The callback chain is aborted whenever the block given to - # _run_callbacks returns false. - # - # * If a class is given to the fallback, it will search for - # before_create, around_create and after_create methods. - # - # == Usage - # - # First you need to define which callbacks your model will have: + # define_model_callbacks accepts all options define_callbacks does, in case you + # want to overwrite a default. Besides that, it also accepts an :only option, + # where you can choose if you want all types (before, around or after) or just some. # + # define_model_callbacks :initializer, :only => :after + # + # Note, the :only => hash will apply to all callbacks defined on + # that method call. To get around this you can call the define_model_callbacks + # method as many times as you need. + # + # define_model_callbacks :create, :only => :after + # define_model_callbacks :update, :only => :before + # define_model_callbacks :destroy, :only => :around + # + # Would create +after_create+, +before_update+ and +around_destroy+ methods only. + # + # You can pass in a class to before_, after_ and around_, in which + # case the call back will call that class's _ method passing the object + # that the callback is being called on. + # # class MyModel + # extend ActiveModel::Callbacks # define_model_callbacks :create + # + # before_create AnotherClass # end - # - # This will define three class methods: before_create, around_create, - # and after_create. They accept a symbol, a string, an object or a block. - # - # After you create a callback, you need to tell when they are executed. - # For example, you could do: - # - # def create - # _run_create_callbacks do - # super + # + # class AnotherClass + # def self.before_create( obj ) + # # obj is the MyModel instance that the callback is being called on # end # end - # - # == Options - # - # define_model_callbacks accepts all options define_callbacks does, in - # case you want to overwrite a default. Besides that, it also accepts - # an :only option, where you can choose if you want all types (before, - # around or after) or just some: - # - # define_model_callbacks :initializer, :only => :after - # + # def define_model_callbacks(*callbacks) options = callbacks.extract_options! options = { :terminator => "result == false", :scope => [:kind, :name] }.merge(options)