aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/examples/validations.rb29
-rw-r--r--activemodel/lib/active_model.rb3
-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
-rw-r--r--activemodel/lib/activemodel.rb1
-rw-r--r--activemodel/test/cases/attributes_test.rb30
-rw-r--r--activemodel/test/cases/observing_test.rb7
-rw-r--r--activemodel/test/cases/serializeration/json_serialization_test.rb4
-rw-r--r--activemodel/test/cases/serializeration/xml_serialization_test.rb4
14 files changed, 146 insertions, 74 deletions
diff --git a/activemodel/examples/validations.rb b/activemodel/examples/validations.rb
new file mode 100644
index 0000000000..3f8311ff96
--- /dev/null
+++ b/activemodel/examples/validations.rb
@@ -0,0 +1,29 @@
+require 'activemodel'
+
+class Person
+ include ActiveModel::Conversion
+ include ActiveModel::Validations
+
+ validates_presence_of :name
+
+ attr_accessor :name
+
+ def initialize(attributes = {})
+ @name = attributes[:name]
+ end
+
+ def persist
+ @persisted = true
+ end
+
+ def new_record?
+ @persisted
+ end
+end
+
+person1 = Person.new
+p person1.valid?
+person1.errors
+
+person2 = Person.new(:name => "matz")
+p person2.valid?
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index f988cd71b8..2de19597b1 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -26,8 +26,7 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path)
require 'active_support'
module ActiveModel
- autoload :Attributes, 'active_model/attributes'
- autoload :Base, 'active_model/base'
+ autoload :Conversion, 'active_model/conversion'
autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods'
autoload :Errors, 'active_model/errors'
autoload :Name, 'active_model/naming'
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
diff --git a/activemodel/lib/activemodel.rb b/activemodel/lib/activemodel.rb
new file mode 100644
index 0000000000..da3133103b
--- /dev/null
+++ b/activemodel/lib/activemodel.rb
@@ -0,0 +1 @@
+require 'active_model'
diff --git a/activemodel/test/cases/attributes_test.rb b/activemodel/test/cases/attributes_test.rb
deleted file mode 100644
index 5f3ea839a4..0000000000
--- a/activemodel/test/cases/attributes_test.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require 'cases/helper'
-
-class AttributesTest < ActiveModel::TestCase
- class Person
- include ActiveModel::Attributes
- attr_accessor :name
- end
-
- test "reads attribute" do
- p = Person.new
- assert_equal nil, p.read_attribute(:name)
-
- p.name = "Josh"
- assert_equal "Josh", p.read_attribute(:name)
- end
-
- test "writes attribute" do
- p = Person.new
- assert_equal nil, p.name
-
- p.write_attribute(:name, "Josh")
- assert_equal "Josh", p.name
- end
-
- test "returns all attributes" do
- p = Person.new
- p.name = "Josh"
- assert_equal({"name" => "Josh"}, p.attributes)
- end
-end
diff --git a/activemodel/test/cases/observing_test.rb b/activemodel/test/cases/observing_test.rb
index 564451fa2f..fbf93c19ef 100644
--- a/activemodel/test/cases/observing_test.rb
+++ b/activemodel/test/cases/observing_test.rb
@@ -1,6 +1,8 @@
require 'cases/helper'
-class ObservedModel < ActiveModel::Base
+class ObservedModel
+ include ActiveModel::Observing
+
class Observer
end
end
@@ -17,7 +19,8 @@ class FooObserver < ActiveModel::Observer
end
end
-class Foo < ActiveModel::Base
+class Foo
+ include ActiveModel::Observing
end
class ObservingTest < ActiveModel::TestCase
diff --git a/activemodel/test/cases/serializeration/json_serialization_test.rb b/activemodel/test/cases/serializeration/json_serialization_test.rb
index 3e69db110e..6227aedc39 100644
--- a/activemodel/test/cases/serializeration/json_serialization_test.rb
+++ b/activemodel/test/cases/serializeration/json_serialization_test.rb
@@ -3,6 +3,10 @@ require 'models/contact'
class Contact
include ActiveModel::Serializers::JSON
+
+ def attributes
+ instance_values
+ end
end
class JsonSerializationTest < ActiveModel::TestCase
diff --git a/activemodel/test/cases/serializeration/xml_serialization_test.rb b/activemodel/test/cases/serializeration/xml_serialization_test.rb
index 57792e900e..e459f6433a 100644
--- a/activemodel/test/cases/serializeration/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializeration/xml_serialization_test.rb
@@ -3,6 +3,10 @@ require 'models/contact'
class Contact
include ActiveModel::Serializers::Xml
+
+ def attributes
+ instance_values
+ end
end
class XmlSerializationTest < ActiveModel::TestCase