aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/CHANGELOG.md15
-rw-r--r--activemodel/lib/active_model.rb2
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb4
-rw-r--r--activemodel/lib/active_model/callbacks.rb2
-rw-r--r--activemodel/lib/active_model/conversion.rb2
-rw-r--r--activemodel/lib/active_model/dirty.rb1
-rw-r--r--activemodel/lib/active_model/naming.rb5
-rw-r--r--activemodel/lib/active_model/observer_array.rb152
-rw-r--r--activemodel/lib/active_model/observing.rb374
-rwxr-xr-xactivemodel/lib/active_model/serializers/xml.rb7
-rw-r--r--activemodel/lib/active_model/validations.rb3
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb6
-rw-r--r--activemodel/lib/active_model/validations/length.rb16
-rw-r--r--activemodel/test/cases/observer_array_test.rb220
-rw-r--r--activemodel/test/cases/observing_test.rb181
-rw-r--r--activemodel/test/cases/railtie_test.rb7
-rw-r--r--activemodel/test/cases/serializers/json_serialization_test.rb5
-rwxr-xr-xactivemodel/test/cases/serializers/xml_serialization_test.rb12
-rw-r--r--activemodel/test/cases/validations/length_validation_test.rb39
-rw-r--r--activemodel/test/models/observers.rb27
20 files changed, 95 insertions, 985 deletions
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 133bb558a9..aacef97858 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,5 +1,20 @@
## Rails 4.0.0 (unreleased) ##
+* Observers was extracted from Active Model as `rails-observers` gem.
+
+ *Rafael Mendonça França*
+
+* Specify type of singular association during serialization *Steve Klabnik*
+
+* Fixed length validator to correctly handle nil values. Fixes #7180.
+
+ *Michal Zima*
+
+* Removed dispensable `require` statements. Make sure to require `active_model` before requiring
+ individual parts of the framework.
+
+ *Yves Senn*
+
* Use BCrypt's MIN_COST in the test environment for speedier tests when using `has_secure_pasword`.
*Brian Cardarella + Jeremy Kemper + Trevor Turk*
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index f757ba9843..0a32c5af05 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -40,8 +40,6 @@ module ActiveModel
autoload :DeprecatedMassAssignmentSecurity
autoload :Name, 'active_model/naming'
autoload :Naming
- autoload :Observer, 'active_model/observing'
- autoload :Observing
autoload :SecurePassword
autoload :Serialization
autoload :TestCase
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 86eaad830e..af11da1351 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -8,7 +8,7 @@ module ActiveModel
#
# user = User.first
# user.pets.select(:id).first.user_id
- # # => ActiveModel::MissingAttributeError: missing attribute: user_id
+ # # => ActiveModel::MissingAttributeError: missing attribute: user_id
class MissingAttributeError < NoMethodError
end
# == Active \Model Attribute Methods
@@ -202,7 +202,7 @@ module ActiveModel
# person.name # => "Bob"
# person.nickname # => "Bob"
# person.name_short? # => true
- # person.nickname_short? # => true
+ # person.nickname_short? # => true
def alias_attribute(new_name, old_name)
self.attribute_aliases = attribute_aliases.merge(new_name.to_s => old_name.to_s)
attribute_method_matchers.each do |matcher|
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index eab94554cc..c52e4947ae 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -1,5 +1,3 @@
-require 'active_support/callbacks'
-
module ActiveModel
# == Active \Model \Callbacks
#
diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb
index 42de32e63e..1f5d23dd8e 100644
--- a/activemodel/lib/active_model/conversion.rb
+++ b/activemodel/lib/active_model/conversion.rb
@@ -1,5 +1,3 @@
-require 'active_support/inflector'
-
module ActiveModel
# == Active \Model Conversions
#
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index 9d09353ef2..d47c3ae1bb 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -1,4 +1,3 @@
-require 'active_model/attribute_methods'
require 'active_support/hash_with_indifferent_access'
require 'active_support/core_ext/object/duplicable'
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 264880eecd..6887f6d781 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -1,4 +1,3 @@
-require 'active_support/inflector'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/module/introspection'
@@ -56,8 +55,8 @@ module ActiveModel
# end
#
# BlogPost.model_name <=> 'BlogPost' # => 0
- # BlogPost.model_name <=> 'Blog' # => 1
- # BlogPost.model_name <=> 'BlogPosts' # => -1
+ # BlogPost.model_name <=> 'Blog' # => 1
+ # BlogPost.model_name <=> 'BlogPosts' # => -1
##
# :method: =~
diff --git a/activemodel/lib/active_model/observer_array.rb b/activemodel/lib/active_model/observer_array.rb
deleted file mode 100644
index 77bc0f71e3..0000000000
--- a/activemodel/lib/active_model/observer_array.rb
+++ /dev/null
@@ -1,152 +0,0 @@
-require 'set'
-
-module ActiveModel
- # Stores the enabled/disabled state of individual observers for
- # a particular model class.
- class ObserverArray < Array
- attr_reader :model_class
- def initialize(model_class, *args) #:nodoc:
- @model_class = model_class
- super(*args)
- end
-
- # Returns +true+ if the given observer is disabled for the model class,
- # +false+ otherwise.
- def disabled_for?(observer) #:nodoc:
- disabled_observers.include?(observer.class)
- end
-
- # Disables one or more observers. This supports multiple forms:
- #
- # ORM.observers.disable :all
- # # => disables all observers for all models subclassed from
- # # an ORM base class that includes ActiveModel::Observing
- # # e.g. ActiveRecord::Base
- #
- # ORM.observers.disable :user_observer
- # # => disables the UserObserver
- #
- # User.observers.disable AuditTrail
- # # => disables the AuditTrail observer for User notifications.
- # # Other models will still notify the AuditTrail observer.
- #
- # ORM.observers.disable :observer_1, :observer_2
- # # => disables Observer1 and Observer2 for all models.
- #
- # User.observers.disable :all do
- # # all user observers are disabled for
- # # just the duration of the block
- # end
- def disable(*observers, &block)
- set_enablement(false, observers, &block)
- end
-
- # Enables one or more observers. This supports multiple forms:
- #
- # ORM.observers.enable :all
- # # => enables all observers for all models subclassed from
- # # an ORM base class that includes ActiveModel::Observing
- # # e.g. ActiveRecord::Base
- #
- # ORM.observers.enable :user_observer
- # # => enables the UserObserver
- #
- # User.observers.enable AuditTrail
- # # => enables the AuditTrail observer for User notifications.
- # # Other models will not be affected (i.e. they will not
- # # trigger notifications to AuditTrail if previously disabled)
- #
- # ORM.observers.enable :observer_1, :observer_2
- # # => enables Observer1 and Observer2 for all models.
- #
- # User.observers.enable :all do
- # # all user observers are enabled for
- # # just the duration of the block
- # end
- #
- # Note: all observers are enabled by default. This method is only
- # useful when you have previously disabled one or more observers.
- def enable(*observers, &block)
- set_enablement(true, observers, &block)
- end
-
- protected
-
- def disabled_observers #:nodoc:
- @disabled_observers ||= Set.new
- end
-
- def observer_class_for(observer) #:nodoc:
- return observer if observer.is_a?(Class)
-
- if observer.respond_to?(:to_sym) # string/symbol
- observer.to_s.camelize.constantize
- else
- raise ArgumentError, "#{observer} was not a class or a " +
- "lowercase, underscored class name as expected."
- end
- end
-
- def start_transaction #:nodoc:
- disabled_observer_stack.push(disabled_observers.dup)
- each_subclass_array do |array|
- array.start_transaction
- end
- end
-
- def disabled_observer_stack #:nodoc:
- @disabled_observer_stack ||= []
- end
-
- def end_transaction #:nodoc:
- @disabled_observers = disabled_observer_stack.pop
- each_subclass_array do |array|
- array.end_transaction
- end
- end
-
- def transaction #:nodoc:
- start_transaction
-
- begin
- yield
- ensure
- end_transaction
- end
- end
-
- def each_subclass_array #:nodoc:
- model_class.descendants.each do |subclass|
- yield subclass.observers
- end
- end
-
- def set_enablement(enabled, observers) #:nodoc:
- if block_given?
- transaction do
- set_enablement(enabled, observers)
- yield
- end
- else
- observers = ActiveModel::Observer.descendants if observers == [:all]
- observers.each do |obs|
- klass = observer_class_for(obs)
-
- unless klass < ActiveModel::Observer
- raise ArgumentError.new("#{obs} does not refer to a valid observer")
- end
-
- if enabled
- disabled_observers.delete(klass)
- else
- disabled_observers << klass
- end
- end
-
- each_subclass_array do |array|
- array.set_enablement(enabled, observers)
- end
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb
deleted file mode 100644
index 5f1c99ce62..0000000000
--- a/activemodel/lib/active_model/observing.rb
+++ /dev/null
@@ -1,374 +0,0 @@
-require 'singleton'
-require 'active_model/observer_array'
-require 'active_support/core_ext/module/aliasing'
-require 'active_support/core_ext/module/remove_method'
-require 'active_support/core_ext/string/inflections'
-require 'active_support/core_ext/enumerable'
-require 'active_support/core_ext/object/try'
-require 'active_support/descendants_tracker'
-
-module ActiveModel
- # == Active \Model Observers Activation
- module Observing
- extend ActiveSupport::Concern
-
- included do
- extend ActiveSupport::DescendantsTracker
- end
-
- module ClassMethods
- # Activates the observers assigned.
- #
- # class ORM
- # include ActiveModel::Observing
- # end
- #
- # # Calls PersonObserver.instance
- # ORM.observers = :person_observer
- #
- # # Calls Cacher.instance and GarbageCollector.instance
- # ORM.observers = :cacher, :garbage_collector
- #
- # # Same as above, just using explicit class references
- # ORM.observers = Cacher, GarbageCollector
- #
- # Note: Setting this does not instantiate the observers yet.
- # <tt>instantiate_observers</tt> is called during startup, and before
- # each development request.
- def observers=(*values)
- observers.replace(values.flatten)
- end
-
- # Gets an array of observers observing this model. The array also provides
- # +enable+ and +disable+ methods that allow you to selectively enable and
- # disable observers (see ActiveModel::ObserverArray.enable and
- # ActiveModel::ObserverArray.disable for more on this).
- #
- # class ORM
- # include ActiveModel::Observing
- # end
- #
- # ORM.observers = :cacher, :garbage_collector
- # ORM.observers # => [:cacher, :garbage_collector]
- # ORM.observers.class # => ActiveModel::ObserverArray
- def observers
- @observers ||= ObserverArray.new(self)
- end
-
- # Returns the current observer instances.
- #
- # class Foo
- # include ActiveModel::Observing
- #
- # attr_accessor :status
- # end
- #
- # class FooObserver < ActiveModel::Observer
- # def on_spec(record, *args)
- # record.status = true
- # end
- # end
- #
- # Foo.observers = FooObserver
- # Foo.instantiate_observers
- #
- # Foo.observer_instances # => [#<FooObserver:0x007fc212c40820>]
- def observer_instances
- @observer_instances ||= []
- end
-
- # Instantiate the global observers.
- #
- # class Foo
- # include ActiveModel::Observing
- #
- # attr_accessor :status
- # end
- #
- # class FooObserver < ActiveModel::Observer
- # def on_spec(record, *args)
- # record.status = true
- # end
- # end
- #
- # Foo.observers = FooObserver
- #
- # foo = Foo.new
- # foo.status = false
- # foo.notify_observers(:on_spec)
- # foo.status # => false
- #
- # Foo.instantiate_observers # => [FooObserver]
- #
- # foo = Foo.new
- # foo.status = false
- # foo.notify_observers(:on_spec)
- # foo.status # => true
- def instantiate_observers
- observers.each { |o| instantiate_observer(o) }
- end
-
- # Add a new observer to the pool. The new observer needs to respond to
- # <tt>update</tt>, otherwise it raises an +ArgumentError+ exception.
- #
- # class Foo
- # include ActiveModel::Observing
- # end
- #
- # class FooObserver < ActiveModel::Observer
- # end
- #
- # Foo.add_observer(FooObserver.instance)
- #
- # Foo.observers_instance
- # # => [#<FooObserver:0x007fccf55d9390>]
- def add_observer(observer)
- unless observer.respond_to? :update
- raise ArgumentError, "observer needs to respond to 'update'"
- end
- observer_instances << observer
- end
-
- # Fires notifications to model's observers.
- #
- # def save
- # notify_observers(:before_save)
- # ...
- # notify_observers(:after_save)
- # end
- #
- # Custom notifications can be sent in a similar fashion:
- #
- # notify_observers(:custom_notification, :foo)
- #
- # This will call <tt>custom_notification</tt>, passing as arguments
- # the current object and <tt>:foo</tt>.
- def notify_observers(*args)
- observer_instances.each { |observer| observer.update(*args) }
- end
-
- # Returns the total number of instantiated observers.
- #
- # class Foo
- # include ActiveModel::Observing
- #
- # attr_accessor :status
- # end
- #
- # class FooObserver < ActiveModel::Observer
- # def on_spec(record, *args)
- # record.status = true
- # end
- # end
- #
- # Foo.observers = FooObserver
- # Foo.observers_count # => 0
- # Foo.instantiate_observers
- # Foo.observers_count # => 1
- def observers_count
- observer_instances.size
- end
-
- # <tt>count_observers</tt> is deprecated. Use #observers_count.
- def count_observers
- msg = "count_observers is deprecated in favor of observers_count"
- ActiveSupport::Deprecation.warn msg
- observers_count
- end
-
- protected
- def instantiate_observer(observer) #:nodoc:
- # string/symbol
- if observer.respond_to?(:to_sym)
- observer = observer.to_s.camelize.constantize
- end
- if observer.respond_to?(:instance)
- observer.instance
- else
- raise ArgumentError,
- "#{observer} must be a lowercase, underscored class name (or " +
- "the class itself) responding to the method :instance. " +
- "Example: Person.observers = :big_brother # calls " +
- "BigBrother.instance"
- end
- end
-
- # Notify observers when the observed class is subclassed.
- def inherited(subclass) #:nodoc:
- super
- notify_observers :observed_class_inherited, subclass
- end
- end
-
- # Notify a change to the list of observers.
- #
- # class Foo
- # include ActiveModel::Observing
- #
- # attr_accessor :status
- # end
- #
- # class FooObserver < ActiveModel::Observer
- # def on_spec(record, *args)
- # record.status = true
- # end
- # end
- #
- # Foo.observers = FooObserver
- # Foo.instantiate_observers # => [FooObserver]
- #
- # foo = Foo.new
- # foo.status = false
- # foo.notify_observers(:on_spec)
- # foo.status # => true
- #
- # See ActiveModel::Observing::ClassMethods.notify_observers for more
- # information.
- def notify_observers(method, *extra_args)
- self.class.notify_observers(method, self, *extra_args)
- end
- end
-
- # == Active \Model Observers
- #
- # Observer classes respond to life cycle 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.
- #
- # class CommentObserver < ActiveModel::Observer
- # def after_save(comment)
- # Notifications.comment('admin@do.com', 'New comment was posted', comment).deliver
- # end
- # end
- #
- # This Observer sends an email when a <tt>Comment#save</tt> 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 <tt>CommentObserver</tt> will be tied to observing <tt>Comment</tt>,
- # <tt>ProductManagerObserver</tt> to <tt>ProductManager</tt>, and so on. If
- # you want to name your observer differently than the class you're interested
- # in observing, you can use the <tt>Observer.observe</tt> class method which
- # takes either the concrete class (<tt>Product</tt>) or a symbol for that
- # class (<tt>:product</tt>):
- #
- # 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 <tt>AuditObserver</tt> will now act on both updates to <tt>Account</tt>
- # and <tt>Balance</tt> by treating them both as records.
- #
- # If you're using an Observer in a Rails application with Active Record, be
- # sure to read about the necessary configuration in the documentation for
- # ActiveRecord::Observer.
- class Observer
- include Singleton
- extend ActiveSupport::DescendantsTracker
-
- class << self
- # Attaches the observer to the supplied model classes.
- #
- # class AuditObserver < ActiveModel::Observer
- # observe :account, :balance
- # end
- #
- # AuditObserver.observed_classes # => [Account, Balance]
- def observe(*models)
- models.flatten!
- models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
- singleton_class.redefine_method(:observed_classes) { models }
- end
-
- # Returns an array of Classes to observe.
- #
- # AccountObserver.observed_classes # => [Account]
- #
- # You can override this instead of using the +observe+ helper.
- #
- # class AuditObserver < ActiveModel::Observer
- # def self.observed_classes
- # [Account, Balance]
- # end
- # end
- def observed_classes
- Array(observed_class)
- end
-
- # Returns the class observed by default. It's inferred from the observer's
- # class name.
- #
- # PersonObserver.observed_class # => Person
- # AccountObserver.observed_class # => Account
- def observed_class
- name[/(.*)Observer/, 1].try :constantize
- end
- end
-
- # Start observing the declared classes and their subclasses.
- # Called automatically by the instance method.
- def initialize #:nodoc:
- observed_classes.each { |klass| add_observer!(klass) }
- end
-
- def observed_classes #:nodoc:
- self.class.observed_classes
- end
-
- # Send observed_method(object) if the method exists and
- # the observer is enabled for the given object's class.
- def update(observed_method, object, *extra_args, &block) #:nodoc:
- return if !respond_to?(observed_method) || disabled_for?(object)
- send(observed_method, object, *extra_args, &block)
- end
-
- # Special method sent by the observed class when it is inherited.
- # Passes the new subclass.
- def observed_class_inherited(subclass) #:nodoc:
- self.class.observe(observed_classes + [subclass])
- add_observer!(subclass)
- end
-
- protected
- def add_observer!(klass) #:nodoc:
- klass.add_observer(self)
- end
-
- # Returns true if notifications are disabled for this object.
- def disabled_for?(object) #:nodoc:
- klass = object.class
- return false unless klass.respond_to?(:observers)
- klass.observers.disabled_for?(self)
- end
- end
-end
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index fb6093cce5..4a17a63e20 100755
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -149,7 +149,12 @@ module ActiveModel
end
else
merged_options[:root] = association.to_s
- records.to_xml(merged_options)
+
+ unless records.class.to_s.underscore == association.to_s
+ merged_options[:type] = records.class.name
+ end
+
+ records.to_xml merged_options
end
end
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 2524b8d065..2db4a25f61 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -1,9 +1,6 @@
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/except'
-require 'active_model/errors'
-require 'active_model/validations/callbacks'
-require 'active_model/validator'
module ActiveModel
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index a8fb4fdfc2..e28ad2841b 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -1,5 +1,3 @@
-require 'active_support/callbacks'
-
module ActiveModel
module Validations
# == Active \Model Validation Callbacks
@@ -85,8 +83,8 @@ module ActiveModel
# person = Person.new
# person.name = ''
# person.valid? # => false
- # person.status # => false
- #  person.name = 'bob'
+ # person.status # => false
+ # person.name = 'bob'
# person.valid? # => true
# person.status # => true
def after_validation(*args, &block)
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index 70ef589cd7..675fb5f1e5 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -14,6 +14,10 @@ module ActiveModel
options[:minimum], options[:maximum] = range.min, range.max
end
+ if options[:allow_blank] == false && options[:minimum].nil? && options[:is].nil?
+ options[:minimum] = 1
+ end
+
super
end
@@ -40,7 +44,10 @@ module ActiveModel
CHECKS.each do |key, validity_check|
next unless check_value = options[key]
- next if value_length.send(validity_check, check_value)
+
+ if !value.nil? || skip_nil_check?(key)
+ next if value_length.send(validity_check, check_value)
+ end
errors_options[:count] = check_value
@@ -58,6 +65,10 @@ module ActiveModel
options[:tokenizer].call(value)
end || value
end
+
+ def skip_nil_check?(key)
+ key == :maximum && options[:allow_nil].nil? && options[:allow_blank].nil?
+ end
end
module HelperMethods
@@ -79,7 +90,8 @@ module ActiveModel
#
# Configuration options:
# * <tt>:minimum</tt> - The minimum size of the attribute.
- # * <tt>:maximum</tt> - The maximum size of the attribute.
+ # * <tt>:maximum</tt> - The maximum size of the attribute. Allows +nil+ by
+ # default if not used with :minimum.
# * <tt>:is</tt> - The exact size of the attribute.
# * <tt>:within</tt> - A range specifying the minimum and maximum size of
# the attribute.
diff --git a/activemodel/test/cases/observer_array_test.rb b/activemodel/test/cases/observer_array_test.rb
deleted file mode 100644
index fc5f18008b..0000000000
--- a/activemodel/test/cases/observer_array_test.rb
+++ /dev/null
@@ -1,220 +0,0 @@
-require 'cases/helper'
-require 'models/observers'
-
-class ObserverArrayTest < ActiveModel::TestCase
- def teardown
- ORM.observers.enable :all
- Budget.observers.enable :all
- Widget.observers.enable :all
- end
-
- def assert_observer_notified(model_class, observer_class)
- observer_class.instance.before_save_invocations.clear
- model_instance = model_class.new
- model_instance.save
- assert_equal [model_instance], observer_class.instance.before_save_invocations
- end
-
- def assert_observer_not_notified(model_class, observer_class)
- observer_class.instance.before_save_invocations.clear
- model_instance = model_class.new
- model_instance.save
- assert_equal [], observer_class.instance.before_save_invocations
- end
-
- test "all observers are enabled by default" do
- assert_observer_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "can disable individual observers using a class constant" do
- ORM.observers.disable WidgetObserver
-
- assert_observer_not_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "can enable individual observers using a class constant" do
- ORM.observers.disable :all
- ORM.observers.enable AuditTrail
-
- assert_observer_not_notified Widget, WidgetObserver
- assert_observer_not_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "can disable individual observers using a symbol" do
- ORM.observers.disable :budget_observer
-
- assert_observer_notified Widget, WidgetObserver
- assert_observer_not_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "can enable individual observers using a symbol" do
- ORM.observers.disable :all
- ORM.observers.enable :audit_trail
-
- assert_observer_not_notified Widget, WidgetObserver
- assert_observer_not_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "can disable multiple observers at a time" do
- ORM.observers.disable :widget_observer, :budget_observer
-
- assert_observer_not_notified Widget, WidgetObserver
- assert_observer_not_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "can enable multiple observers at a time" do
- ORM.observers.disable :all
- ORM.observers.enable :widget_observer, :budget_observer
-
- assert_observer_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_not_notified Widget, AuditTrail
- assert_observer_not_notified Budget, AuditTrail
- end
-
- test "can disable all observers using :all" do
- ORM.observers.disable :all
-
- assert_observer_not_notified Widget, WidgetObserver
- assert_observer_not_notified Budget, BudgetObserver
- assert_observer_not_notified Widget, AuditTrail
- assert_observer_not_notified Budget, AuditTrail
- end
-
- test "can enable all observers using :all" do
- ORM.observers.disable :all
- ORM.observers.enable :all
-
- assert_observer_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "can disable observers on individual models without affecting those observers on other models" do
- Widget.observers.disable :all
-
- assert_observer_not_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_not_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "can enable observers on individual models without affecting those observers on other models" do
- ORM.observers.disable :all
- Budget.observers.enable AuditTrail
-
- assert_observer_not_notified Widget, WidgetObserver
- assert_observer_not_notified Budget, BudgetObserver
- assert_observer_not_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "can disable observers for the duration of a block" do
- yielded = false
- ORM.observers.disable :budget_observer do
- yielded = true
- assert_observer_notified Widget, WidgetObserver
- assert_observer_not_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- assert yielded
- assert_observer_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "can enable observers for the duration of a block" do
- yielded = false
- Widget.observers.disable :all
-
- Widget.observers.enable :all do
- yielded = true
- assert_observer_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- assert yielded
- assert_observer_not_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_not_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "raises an appropriate error when a developer accidentally enables or disables the wrong class (i.e. Widget instead of WidgetObserver)" do
- assert_raise ArgumentError do
- ORM.observers.enable :widget
- end
-
- assert_raise ArgumentError do
- ORM.observers.enable Widget
- end
-
- assert_raise ArgumentError do
- ORM.observers.disable :widget
- end
-
- assert_raise ArgumentError do
- ORM.observers.disable Widget
- end
- end
-
- test "allows #enable at the superclass level to override #disable at the subclass level when called last" do
- Widget.observers.disable :all
- ORM.observers.enable :all
-
- assert_observer_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- test "allows #disable at the superclass level to override #enable at the subclass level when called last" do
- Budget.observers.enable :audit_trail
- ORM.observers.disable :audit_trail
-
- assert_observer_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_not_notified Widget, AuditTrail
- assert_observer_not_notified Budget, AuditTrail
- end
-
- test "can use the block form at different levels of the hierarchy" do
- yielded = false
- Widget.observers.disable :all
-
- ORM.observers.enable :all do
- yielded = true
- assert_observer_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-
- assert yielded
- assert_observer_not_notified Widget, WidgetObserver
- assert_observer_notified Budget, BudgetObserver
- assert_observer_not_notified Widget, AuditTrail
- assert_observer_notified Budget, AuditTrail
- end
-end
-
diff --git a/activemodel/test/cases/observing_test.rb b/activemodel/test/cases/observing_test.rb
deleted file mode 100644
index ade6026602..0000000000
--- a/activemodel/test/cases/observing_test.rb
+++ /dev/null
@@ -1,181 +0,0 @@
-require 'cases/helper'
-
-class ObservedModel
- include ActiveModel::Observing
-
- class Observer
- end
-end
-
-class FooObserver < ActiveModel::Observer
- class << self
- public :new
- end
-
- attr_accessor :stub
-
- def on_spec(record, *args)
- stub.event_with(record, *args) if stub
- end
-
- def around_save(record)
- yield :in_around_save
- end
-end
-
-class Foo
- include ActiveModel::Observing
-end
-
-class ObservingTest < ActiveModel::TestCase
- def setup
- ObservedModel.observers.clear
- end
-
- test "initializes model with no cached observers" do
- assert ObservedModel.observers.empty?, "Not empty: #{ObservedModel.observers.inspect}"
- end
-
- test "stores cached observers in an array" do
- ObservedModel.observers << :foo
- assert ObservedModel.observers.include?(:foo), ":foo not in #{ObservedModel.observers.inspect}"
- end
-
- test "flattens array of assigned cached observers" do
- ObservedModel.observers = [[:foo], :bar]
- assert ObservedModel.observers.include?(:foo), ":foo not in #{ObservedModel.observers.inspect}"
- assert ObservedModel.observers.include?(:bar), ":bar not in #{ObservedModel.observers.inspect}"
- end
-
- test "uses an ObserverArray so observers can be disabled" do
- ObservedModel.observers = [:foo, :bar]
- assert ObservedModel.observers.is_a?(ActiveModel::ObserverArray)
- end
-
- test "instantiates observer names passed as strings" do
- ObservedModel.observers << 'foo_observer'
- FooObserver.expects(:instance)
- ObservedModel.instantiate_observers
- end
-
- test "instantiates observer names passed as symbols" do
- ObservedModel.observers << :foo_observer
- FooObserver.expects(:instance)
- ObservedModel.instantiate_observers
- end
-
- test "instantiates observer classes" do
- ObservedModel.observers << ObservedModel::Observer
- ObservedModel::Observer.expects(:instance)
- ObservedModel.instantiate_observers
- end
-
- test "raises an appropriate error when a developer accidentally adds the wrong class (i.e. Widget instead of WidgetObserver)" do
- assert_raise ArgumentError do
- ObservedModel.observers = ['string']
- ObservedModel.instantiate_observers
- end
- assert_raise ArgumentError do
- ObservedModel.observers = [:string]
- ObservedModel.instantiate_observers
- end
- assert_raise ArgumentError do
- ObservedModel.observers = [String]
- ObservedModel.instantiate_observers
- end
- end
-
- test "passes observers to subclasses" do
- FooObserver.instance
- bar = Class.new(Foo)
- assert_equal Foo.observers_count, bar.observers_count
- end
-end
-
-class ObserverTest < ActiveModel::TestCase
- def setup
- ObservedModel.observers = :foo_observer
- FooObserver.singleton_class.instance_eval do
- alias_method :original_observed_classes, :observed_classes
- end
- end
-
- def teardown
- FooObserver.singleton_class.instance_eval do
- undef_method :observed_classes
- alias_method :observed_classes, :original_observed_classes
- end
- end
-
- test "guesses implicit observable model name" do
- assert_equal Foo, FooObserver.observed_class
- end
-
- test "tracks implicit observable models" do
- instance = FooObserver.new
- assert_equal [Foo], instance.observed_classes
- end
-
- test "tracks explicit observed model class" do
- FooObserver.observe ObservedModel
- instance = FooObserver.new
- assert_equal [ObservedModel], instance.observed_classes
- end
-
- test "tracks explicit observed model as string" do
- FooObserver.observe 'observed_model'
- instance = FooObserver.new
- assert_equal [ObservedModel], instance.observed_classes
- end
-
- test "tracks explicit observed model as symbol" do
- FooObserver.observe :observed_model
- instance = FooObserver.new
- assert_equal [ObservedModel], instance.observed_classes
- end
-
- test "calls existing observer event" do
- foo = Foo.new
- FooObserver.instance.stub = stub
- FooObserver.instance.stub.expects(:event_with).with(foo)
- Foo.notify_observers(:on_spec, foo)
- end
-
- test "calls existing observer event from the instance" do
- foo = Foo.new
- FooObserver.instance.stub = stub
- FooObserver.instance.stub.expects(:event_with).with(foo)
- foo.notify_observers(:on_spec)
- end
-
- test "passes extra arguments" do
- foo = Foo.new
- FooObserver.instance.stub = stub
- FooObserver.instance.stub.expects(:event_with).with(foo, :bar)
- Foo.send(:notify_observers, :on_spec, foo, :bar)
- end
-
- test "skips nonexistent observer event" do
- foo = Foo.new
- Foo.notify_observers(:whatever, foo)
- end
-
- test "update passes a block on to the observer" do
- yielded_value = nil
- FooObserver.instance.update(:around_save, Foo.new) do |val|
- yielded_value = val
- end
- assert_equal :in_around_save, yielded_value
- end
-
- test "observe redefines observed_classes class method" do
- class BarObserver < ActiveModel::Observer
- observe :foo
- end
-
- assert_equal [Foo], BarObserver.observed_classes
-
- BarObserver.observe(ObservedModel)
- assert_equal [ObservedModel], BarObserver.observed_classes
- end
-end
diff --git a/activemodel/test/cases/railtie_test.rb b/activemodel/test/cases/railtie_test.rb
index f89a288f8f..a0cd1402b1 100644
--- a/activemodel/test/cases/railtie_test.rb
+++ b/activemodel/test/cases/railtie_test.rb
@@ -5,10 +5,11 @@ class RailtieTest < ActiveModel::TestCase
include ActiveSupport::Testing::Isolation
def setup
- require 'rails/all'
+ require 'active_model/railtie'
- @app ||= Class.new(::Rails::Application).tap do |app|
- app.config.eager_load = false
+ @app ||= Class.new(::Rails::Application) do
+ config.eager_load = false
+ config.logger = Logger.new(STDOUT)
end
end
diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb
index fd4d068354..9134c4980c 100644
--- a/activemodel/test/cases/serializers/json_serialization_test.rb
+++ b/activemodel/test/cases/serializers/json_serialization_test.rb
@@ -157,11 +157,8 @@ class JsonSerializationTest < ActiveModel::TestCase
test "as_json should keep the default order in the hash" do
json = @contact.as_json
- keys = json.keys
- %w(name age created_at awesome preferences).each_with_index do |field, index|
- assert_equal keys.index(field), index
- end
+ assert_equal %w(name age created_at awesome preferences), json.keys
end
test "from_json should work without a root (class attribute)" do
diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb
index 90ddf8ff0c..99a9c1fe33 100755
--- a/activemodel/test/cases/serializers/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializers/xml_serialization_test.rb
@@ -6,12 +6,12 @@ require 'ostruct'
class Contact
include ActiveModel::Serializers::Xml
- attr_accessor :address, :friends
+ attr_accessor :address, :friends, :contact
remove_method :attributes if method_defined?(:attributes)
def attributes
- instance_values.except("address", "friends")
+ instance_values.except("address", "friends", "contact")
end
end
@@ -56,6 +56,9 @@ class XmlSerializationTest < ActiveModel::TestCase
@contact.address.zip = 11111
@contact.address.apt_number = 35
@contact.friends = [Contact.new, Contact.new]
+ @related_contact = SerializableContact.new
+ @related_contact.name = "related"
+ @contact.contact = @related_contact
end
test "should serialize default root" do
@@ -256,4 +259,9 @@ class XmlSerializationTest < ActiveModel::TestCase
assert_match %r{<address>}, xml
assert_match %r{<apt-number type="integer">}, xml
end
+
+ test "association with sti" do
+ xml = @contact.to_xml(include: :contact)
+ assert xml.include?(%(<contact type="SerializableContact">))
+ end
end
diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb
index 113bfd6337..1a40ca8efc 100644
--- a/activemodel/test/cases/validations/length_validation_test.rb
+++ b/activemodel/test/cases/validations/length_validation_test.rb
@@ -375,4 +375,43 @@ class LengthValidationTest < ActiveModel::TestCase
t.author_name = "A very long author name that should still be valid." * 100
assert t.valid?
end
+
+ def test_validates_length_of_using_maximum_should_not_allow_nil_when_nil_not_allowed
+ Topic.validates_length_of :title, :maximum => 10, :allow_nil => false
+ t = Topic.new
+ assert t.invalid?
+ end
+
+ def test_validates_length_of_using_maximum_should_not_allow_nil_and_empty_string_when_blank_not_allowed
+ Topic.validates_length_of :title, :maximum => 10, :allow_blank => false
+ t = Topic.new
+ assert t.invalid?
+
+ t.title = ""
+ assert t.invalid?
+ end
+
+ def test_validates_length_of_using_both_minimum_and_maximum_should_not_allow_nil
+ Topic.validates_length_of :title, :minimum => 5, :maximum => 10
+ t = Topic.new
+ assert t.invalid?
+ end
+
+ def test_validates_length_of_using_minimum_0_should_not_allow_nil
+ Topic.validates_length_of :title, :minimum => 0
+ t = Topic.new
+ assert t.invalid?
+
+ t.title = ""
+ assert t.valid?
+ end
+
+ def test_validates_length_of_using_is_0_should_not_allow_nil
+ Topic.validates_length_of :title, :is => 0
+ t = Topic.new
+ assert t.invalid?
+
+ t.title = ""
+ assert t.valid?
+ end
end
diff --git a/activemodel/test/models/observers.rb b/activemodel/test/models/observers.rb
deleted file mode 100644
index 3729b3435e..0000000000
--- a/activemodel/test/models/observers.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-class ORM
- include ActiveModel::Observing
-
- def save
- notify_observers :before_save
- end
-
- class Observer < ActiveModel::Observer
- def before_save_invocations
- @before_save_invocations ||= []
- end
-
- def before_save(record)
- before_save_invocations << record
- end
- end
-end
-
-class Widget < ORM; end
-class Budget < ORM; end
-class WidgetObserver < ORM::Observer; end
-class BudgetObserver < ORM::Observer; end
-class AuditTrail < ORM::Observer
- observe :widget, :budget
-end
-
-ORM.instantiate_observers