aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model')
-rw-r--r--activemodel/lib/active_model/attributes.rb25
-rw-r--r--activemodel/lib/active_model/base.rb8
-rw-r--r--activemodel/lib/active_model/conversion.rb8
-rw-r--r--activemodel/lib/active_model/observing.rb97
-rw-r--r--activemodel/lib/active_model/serializers/json.rb1
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb1
-rw-r--r--activemodel/lib/active_model/validations.rb2
7 files changed, 102 insertions, 40 deletions
diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb
deleted file mode 100644
index ea8c8d5f72..0000000000
--- a/activemodel/lib/active_model/attributes.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require 'active_support/core_ext/object/instance_variables'
-
-module ActiveModel
- module Attributes
- def self.append_features(base)
- unless base.instance_methods.include?('attributes')
- super
- else
- false
- end
- end
-
- def attributes
- instance_values
- end
-
- def read_attribute(attr_name)
- instance_variable_get(:"@#{attr_name}")
- end
-
- def write_attribute(attr_name, value)
- instance_variable_set(:"@#{attr_name}", value)
- end
- end
-end
diff --git a/activemodel/lib/active_model/base.rb b/activemodel/lib/active_model/base.rb
deleted file mode 100644
index a500adfdf1..0000000000
--- a/activemodel/lib/active_model/base.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module ActiveModel
- class Base
- include Observing
- # disabled, until they're tested
- # include Callbacks
- # include Validations
- end
-end \ No newline at end of file
diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb
new file mode 100644
index 0000000000..d5c65920f6
--- /dev/null
+++ b/activemodel/lib/active_model/conversion.rb
@@ -0,0 +1,8 @@
+module ActiveModel
+ # Include ActiveModel::Conversion if your object "acts like an ActiveModel model".
+ module Conversion
+ def to_model
+ self
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb
index 7bad2397ae..3b230c43b9 100644
--- a/activemodel/lib/active_model/observing.rb
+++ b/activemodel/lib/active_model/observing.rb
@@ -1,7 +1,8 @@
require 'observer'
require 'singleton'
-require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/array/wrap'
+require 'active_support/core_ext/module/aliasing'
+require 'active_support/core_ext/string/inflections'
module ActiveModel
module Observing
@@ -39,8 +40,25 @@ module ActiveModel
observers.each { |o| instantiate_observer(o) }
end
+ # Wraps methods with before and after notifications.
+ #
+ # wrap_with_notifications :create, :save, :update, :destroy
+ def wrap_with_notifications(*methods)
+ methods.each do |method|
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{method}_with_notifications(*args, &block)
+ notify_observers(:before_#{method})
+ result = #{method}_without_notifications(*args, &block)
+ notify_observers(:after_#{method})
+ result
+ end
+ EOS
+ alias_method_chain(method, :notifications)
+ end
+ end
+
protected
- def instantiate_observer(observer)
+ def instantiate_observer(observer) #:nodoc:
# string/symbol
if observer.respond_to?(:to_sym)
observer = observer.to_s.camelize.constantize.instance
@@ -60,12 +78,72 @@ module ActiveModel
end
private
- def notify(method) #:nodoc:
+ # Fires notifications to model's observers
+ #
+ # def save
+ # notify_observers(:before_save)
+ # ...
+ # notify_observers(:after_save)
+ # end
+ def notify_observers(method)
self.class.changed
self.class.notify_observers(method, self)
end
end
+ # Observer classes respond to lifecycle callbacks to implement trigger-like
+ # behavior outside the original class. This is a great way to reduce the
+ # clutter that normally comes when the model class is burdened with
+ # functionality that doesn't pertain to the core responsibility of the
+ # class. Example:
+ #
+ # class CommentObserver < ActiveModel::Observer
+ # def after_save(comment)
+ # Notifications.deliver_comment("admin@do.com", "New comment was posted", comment)
+ # end
+ # end
+ #
+ # This Observer sends an email when a Comment#save is finished.
+ #
+ # class ContactObserver < ActiveModel::Observer
+ # def after_create(contact)
+ # contact.logger.info('New contact added!')
+ # end
+ #
+ # def after_destroy(contact)
+ # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
+ # end
+ # end
+ #
+ # This Observer uses logger to log when specific callbacks are triggered.
+ #
+ # == Observing a class that can't be inferred
+ #
+ # Observers will by default be mapped to the class with which they share a name. So CommentObserver will
+ # be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
+ # differently than the class you're interested in observing, you can use the Observer.observe class method which takes
+ # either the concrete class (Product) or a symbol for that class (:product):
+ #
+ # class AuditObserver < ActiveModel::Observer
+ # observe :account
+ #
+ # def after_update(account)
+ # AuditTrail.new(account, "UPDATED")
+ # end
+ # end
+ #
+ # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
+ #
+ # class AuditObserver < ActiveModel::Observer
+ # observe :account, :balance
+ #
+ # def after_update(record)
+ # AuditTrail.new(record, "UPDATED")
+ # end
+ # end
+ #
+ # The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
+ #
class Observer
include Singleton
@@ -77,6 +155,15 @@ module ActiveModel
define_method(:observed_classes) { models }
end
+ # Returns an array of Classes to observe.
+ #
+ # You can override this instead of using the +observe+ helper.
+ #
+ # class AuditObserver < ActiveModel::Observer
+ # def self.observed_classes
+ # [AccountObserver, BalanceObserver]
+ # end
+ # end
def observed_classes
Array.wrap(observed_class)
end
@@ -97,7 +184,7 @@ module ActiveModel
observed_classes.each { |klass| add_observer!(klass) }
end
- def observed_classes
+ def observed_classes #:nodoc:
self.class.observed_classes
end
@@ -114,7 +201,7 @@ module ActiveModel
end
protected
- def add_observer!(klass)
+ def add_observer!(klass) #:nodoc:
klass.add_observer(self)
end
end
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index adf200597d..e94512fd64 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -5,7 +5,6 @@ module ActiveModel
module Serializers
module JSON
extend ActiveSupport::Concern
- include ActiveModel::Attributes
included do
extend ActiveModel::Naming
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 7cdd281223..76a0e54a56 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -5,7 +5,6 @@ module ActiveModel
module Serializers
module Xml
extend ActiveSupport::Concern
- include ActiveModel::Attributes
class Serializer < ActiveModel::Serializer #:nodoc:
class Attribute #:nodoc:
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 5223cea135..54a869396d 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -1,5 +1,7 @@
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/keys'
+require 'active_support/concern'
+require 'active_support/callbacks'
module ActiveModel
module Validations