aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2009-07-03 13:01:39 +0100
committerPratik Naik <pratiknaik@gmail.com>2009-07-03 13:01:39 +0100
commit2fe263bb328c20539f2970057f31e567ec4ab7c8 (patch)
tree63e85164fb09aca6beb78e1a5c52424fa49ed098 /activemodel/lib
parentbf5ac9965f12840d469ef2a4a16e8205dbbe5253 (diff)
parenta4bdc00fec623f72592e663e6d7830eea0bc6ea4 (diff)
downloadrails-2fe263bb328c20539f2970057f31e567ec4ab7c8.tar.gz
rails-2fe263bb328c20539f2970057f31e567ec4ab7c8.tar.bz2
rails-2fe263bb328c20539f2970057f31e567ec4ab7c8.zip
Merge commit 'mainstream/master'
Conflicts: actionpack/lib/action_controller.rb actionpack/lib/action_controller/base/base.rb actionpack/lib/action_view/template/path.rb activesupport/lib/active_support/json/encoders/hash.rb
Diffstat (limited to 'activemodel/lib')
-rw-r--r--activemodel/lib/active_model.rb20
-rw-r--r--activemodel/lib/active_model/attributes.rb17
-rw-r--r--activemodel/lib/active_model/deprecated_error_methods.rb4
-rw-r--r--activemodel/lib/active_model/errors.rb23
-rw-r--r--activemodel/lib/active_model/naming.rb25
-rw-r--r--activemodel/lib/active_model/observing.rb116
-rw-r--r--activemodel/lib/active_model/serializers/json.rb38
-rw-r--r--activemodel/lib/active_model/validations.rb16
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb4
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb4
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb4
-rw-r--r--activemodel/lib/active_model/validations/format.rb4
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb4
-rw-r--r--activemodel/lib/active_model/validations/length.rb4
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb4
-rw-r--r--activemodel/lib/active_model/validations/presence.rb6
16 files changed, 203 insertions, 90 deletions
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 803f5b0157..544121c593 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -21,21 +21,27 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-activesupport_path = "#{File.dirname(__FILE__)}/../../../activesupport/lib"
+activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
$:.unshift(activesupport_path) if File.directory?(activesupport_path)
require 'active_support'
-require 'active_support/inflector'
-
module ActiveModel
+ autoload :Attributes, 'active_model/attributes'
autoload :Base, 'active_model/base'
- autoload :Observing, 'active_model/observing'
- autoload :Validations, 'active_model/validations'
- autoload :Errors, 'active_model/errors'
autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods'
- autoload :TestCase, 'active_model/test_case'
+ autoload :Errors, 'active_model/errors'
+ autoload :Name, 'active_model/naming'
+ autoload :Naming, 'active_model/naming'
+ autoload :Observer, 'active_model/observing'
+ autoload :Observing, 'active_model/observing'
autoload :StateMachine, 'active_model/state_machine'
+ autoload :TestCase, 'active_model/test_case'
+ autoload :Validations, 'active_model/validations'
autoload :ValidationsRepairHelper, 'active_model/validations_repair_helper'
+
+ module Serializers
+ autoload :JSON, 'active_model/serializers/json'
+ end
end
I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb
new file mode 100644
index 0000000000..4665525281
--- /dev/null
+++ b/activemodel/lib/active_model/attributes.rb
@@ -0,0 +1,17 @@
+require 'active_support/core_ext/object/instance_variables'
+
+module ActiveModel
+ module Attributes
+ 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/deprecated_error_methods.rb b/activemodel/lib/active_model/deprecated_error_methods.rb
index 433de8931a..dd8050c549 100644
--- a/activemodel/lib/active_model/deprecated_error_methods.rb
+++ b/activemodel/lib/active_model/deprecated_error_methods.rb
@@ -19,7 +19,7 @@ module ActiveModel
ActiveSupport::Deprecation.warn "Errors#add_to_base(msg) has been deprecated, use Errors#[:base] << msg instead"
self[:base] << msg
end
-
+
def invalid?(attribute)
ActiveSupport::Deprecation.warn "Errors#invalid?(attribute) has been deprecated, use Errors#[attribute].any? instead"
self[attribute].any?
@@ -30,4 +30,4 @@ module ActiveModel
to_a.each { |error| yield error }
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 4be91d0505..a4cf700231 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/string/inflections'
+
module ActiveModel
class Errors < Hash
include DeprecatedErrorMethods
@@ -23,7 +25,7 @@ module ActiveModel
end
def each
- each_key do |attribute|
+ each_key do |attribute|
self[attribute].each { |error| yield attribute, error }
end
end
@@ -66,7 +68,7 @@ module ActiveModel
# Will add an error message to each of the attributes in +attributes+ that is empty.
def add_on_empty(attributes, custom_message = nil)
[attributes].flatten.each do |attribute|
- value = @base.get_attribute_value(attribute)
+ value = @base.send(attribute)
is_empty = value.respond_to?(:empty?) ? value.empty? : false
add(attribute, :empty, :default => custom_message) unless !value.nil? && !is_empty
end
@@ -75,7 +77,7 @@ module ActiveModel
# Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
def add_on_blank(attributes, custom_message = nil)
[attributes].flatten.each do |attribute|
- value = @base.get_attribute_value(attribute)
+ value = @base.send(attribute)
add(attribute, :blank, :default => custom_message) if value.blank?
end
end
@@ -94,6 +96,7 @@ module ActiveModel
full_messages = []
each do |attribute, messages|
+ messages = Array.wrap(messages)
next if messages.empty?
if attribute == :base
@@ -111,15 +114,15 @@ module ActiveModel
end
# Translates an error message in it's default scope (<tt>activemodel.errrors.messages</tt>).
- # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there,
- # it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the
- # default message (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name,
+ # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there,
+ # it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the
+ # default message (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name,
# translated attribute name and the value are available for interpolation.
#
# When using inheritence in your models, it will check all the inherited models too, but only if the model itself
# hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt>
# error +message+ for the <tt>title</tt> +attribute+, it looks for these translations:
- #
+ #
# <ol>
# <li><tt>activemodel.errors.models.admin.attributes.title.blank</tt></li>
# <li><tt>activemodel.errors.models.admin.blank</tt></li>
@@ -135,7 +138,7 @@ module ActiveModel
klass_ancestors += @base.class.ancestors.reject {|x| x.is_a?(Module)}
defaults = klass_ancestors.map do |klass|
- [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
+ [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
:"models.#{klass.name.underscore}.#{message}" ]
end
@@ -143,7 +146,7 @@ module ActiveModel
defaults = defaults.compact.flatten << :"messages.#{message}"
key = defaults.shift
- value = @base.get_attribute_value(attribute)
+ value = @base.send(attribute)
options = { :default => defaults,
:model => @base.class.name.humanize,
@@ -155,4 +158,4 @@ module ActiveModel
I18n.translate(key, options)
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
new file mode 100644
index 0000000000..ffb44e3824
--- /dev/null
+++ b/activemodel/lib/active_model/naming.rb
@@ -0,0 +1,25 @@
+require 'active_support/inflector'
+
+module ActiveModel
+ class Name < String
+ attr_reader :singular, :plural, :element, :collection, :partial_path
+ alias_method :cache_key, :collection
+
+ def initialize(name)
+ super
+ @singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze
+ @plural = ActiveSupport::Inflector.pluralize(@singular).freeze
+ @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
+ @collection = ActiveSupport::Inflector.tableize(self).freeze
+ @partial_path = "#{@collection}/#{@element}".freeze
+ end
+ end
+
+ module Naming
+ # Returns an ActiveModel::Name object for module. It can be
+ # used to retrieve all kinds of naming-related information.
+ def model_name
+ @_model_name ||= ActiveModel::Name.new(name)
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb
index d3c6d8e482..7bad2397ae 100644
--- a/activemodel/lib/active_model/observing.rb
+++ b/activemodel/lib/active_model/observing.rb
@@ -1,69 +1,90 @@
require 'observer'
require 'singleton'
+require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/array/wrap'
module ActiveModel
module Observing
+ extend ActiveSupport::Concern
+
+ included do
+ extend Observable
+ end
+
module ClassMethods
- def observers
- @observers ||= []
- end
-
+ # Activates the observers assigned. Examples:
+ #
+ # # Calls PersonObserver.instance
+ # ActiveRecord::Base.observers = :person_observer
+ #
+ # # Calls Cacher.instance and GarbageCollector.instance
+ # ActiveRecord::Base.observers = :cacher, :garbage_collector
+ #
+ # # Same as above, just using explicit class references
+ # ActiveRecord::Base.observers = Cacher, GarbageCollector
+ #
+ # Note: Setting this does not instantiate the observers yet. +instantiate_observers+ is
+ # called during startup, and before each development request.
def observers=(*values)
@observers = values.flatten
end
-
+
+ # Gets the current observers.
+ def observers
+ @observers ||= []
+ end
+
+ # Instantiate the global Active Record observers.
def instantiate_observers
observers.each { |o| instantiate_observer(o) }
end
-
- protected
- def instantiate_observer(observer)
- # string/symbol
- if observer.respond_to?(:to_sym)
- observer = observer.to_s.camelize.constantize.instance
- elsif observer.respond_to?(:instance)
- observer.instance
- else
- raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"
+
+ protected
+ def instantiate_observer(observer)
+ # string/symbol
+ if observer.respond_to?(:to_sym)
+ observer = observer.to_s.camelize.constantize.instance
+ elsif observer.respond_to?(:instance)
+ observer.instance
+ else
+ raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"
+ end
+ end
+
+ # Notify observers when the observed class is subclassed.
+ def inherited(subclass)
+ super
+ changed
+ notify_observers :observed_class_inherited, subclass
end
- end
-
- # Notify observers when the observed class is subclassed.
- def inherited(subclass)
- super
- changed
- notify_observers :observed_class_inherited, subclass
- end
- end
-
- def self.included(receiver)
- receiver.extend Observable, ClassMethods
end
+
+ private
+ def notify(method) #:nodoc:
+ self.class.changed
+ self.class.notify_observers(method, self)
+ end
end
class Observer
include Singleton
- attr_writer :observed_classes
class << self
- attr_accessor :models
# Attaches the observer to the supplied model classes.
def observe(*models)
- @models = models.flatten
- @models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
+ models.flatten!
+ models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
+ define_method(:observed_classes) { models }
end
- def observed_class_name
- @observed_class_name ||=
- if guessed_name = name.scan(/(.*)Observer/)[0]
- @observed_class_name = guessed_name[0]
- end
+ def observed_classes
+ Array.wrap(observed_class)
end
# The class observed by default is inferred from the observer's class name:
- # assert_equal [Person], PersonObserver.observed_class
+ # assert_equal Person, PersonObserver.observed_class
def observed_class
- if observed_class_name
+ if observed_class_name = name[/(.*)Observer/, 1]
observed_class_name.constantize
else
nil
@@ -73,8 +94,11 @@ module ActiveModel
# Start observing the declared classes and their subclasses.
def initialize
- self.observed_classes = self.class.models if self.class.models
- observed_classes.each { |klass| klass.add_observer(self) }
+ observed_classes.each { |klass| add_observer!(klass) }
+ end
+
+ def observed_classes
+ self.class.observed_classes
end
# Send observed_method(object) if the method exists.
@@ -86,12 +110,12 @@ module ActiveModel
# Passes the new subclass.
def observed_class_inherited(subclass) #:nodoc:
self.class.observe(observed_classes + [subclass])
- subclass.add_observer(self)
+ add_observer!(subclass)
end
- protected
- def observed_classes
- @observed_classes ||= [self.class.observed_class]
- end
+ protected
+ def add_observer!(klass)
+ klass.add_observer(self)
+ end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
new file mode 100644
index 0000000000..60b5cbe948
--- /dev/null
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -0,0 +1,38 @@
+require 'active_support/json'
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/hash/slice'
+
+module ActiveModel
+ module Serializers
+ module JSON
+ extend ActiveSupport::Concern
+ include ActiveModel::Attributes
+
+ included do
+ cattr_accessor :include_root_in_json, :instance_writer => false
+ end
+
+ def encode_json(encoder)
+ options = encoder.options || {}
+
+ hash = if options[:only]
+ only = Array.wrap(options[:only]).map { |attr| attr.to_s }
+ attributes.slice(*only)
+ elsif options[:except]
+ except = Array.wrap(options[:except]).map { |attr| attr.to_s }
+ attributes.except(*except)
+ else
+ attributes
+ end
+
+ hash = { self.class.model_name.element => hash } if include_root_in_json
+ ActiveSupport::JSON.encode(hash)
+ end
+
+ def as_json(options = nil)
+ self
+ end
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 336c2757fc..5223cea135 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -1,3 +1,6 @@
+require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/hash/keys'
+
module ActiveModel
module Validations
extend ActiveSupport::Concern
@@ -61,7 +64,7 @@ module ActiveModel
# Declare the validation.
send(validation_method(options[:on]), options) do |record|
attrs.each do |attr|
- value = record.get_attribute_value(attr)
+ value = record.send(attr)
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
yield record, attr, value
end
@@ -69,10 +72,9 @@ module ActiveModel
end
private
-
- def validation_method(on)
- :validate
- end
+ def validation_method(on)
+ :validate
+ end
end
# Returns the Errors object that holds all information about attribute error messages.
@@ -91,10 +93,6 @@ module ActiveModel
def invalid?
!valid?
end
-
- def get_attribute_value(attribute)
- respond_to?(attribute.to_sym) ? send(attribute.to_sym) : instance_variable_get(:"@#{attribute}")
- end
end
end
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 0c9ef51726..b65c9b933d 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -39,10 +39,10 @@ module ActiveModel
validates_each(attr_names,configuration) do |record, attr_name, value|
unless value == configuration[:accept]
- record.errors.add(attr_name, :accepted, :default => configuration[:message])
+ record.errors.add(attr_name, :accepted, :default => configuration[:message])
end
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index b9823172f7..d414224dd2 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -36,10 +36,10 @@ module ActiveModel
validates_each(attr_names, configuration) do |record, attr_name, value|
unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
- record.errors.add(attr_name, :confirmation, :default => configuration[:message])
+ record.errors.add(attr_name, :confirmation, :default => configuration[:message])
end
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index 0aa9848ee1..2cfdec97a5 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -29,10 +29,10 @@ module ActiveModel
validates_each(attr_names, configuration) do |record, attr_name, value|
if enum.include?(value)
- record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value)
+ record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value)
end
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index 8efce8ba2b..6f3b668bf0 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -32,10 +32,10 @@ module ActiveModel
validates_each(attr_names, configuration) do |record, attr_name, value|
unless value.to_s =~ configuration[:with]
- record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
+ record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
end
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index a4bc8fe035..0d7dc5cd64 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -29,10 +29,10 @@ module ActiveModel
validates_each(attr_names, configuration) do |record, attr_name, value|
unless enum.include?(value)
- record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value)
+ record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value)
end
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index bb9a269a02..db0439d447 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -81,7 +81,7 @@ module ActiveModel
validates_each(attrs, options) do |record, attr, value|
value = options[:tokenizer].call(value) if value.kind_of?(String)
unless !value.nil? and value.size.method(validity_checks[option])[option_value]
- record.errors.add(attr, key, :default => custom_message, :count => option_value)
+ record.errors.add(attr, key, :default => custom_message, :count => option_value)
end
end
end
@@ -90,4 +90,4 @@ module ActiveModel
alias_method :validates_size_of, :validates_length_of
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 79fca2f1ea..ada6e28594 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -71,7 +71,7 @@ module ActiveModel
case option
when :odd, :even
unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
- record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
+ record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
end
else
unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
@@ -83,4 +83,4 @@ module ActiveModel
end
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index 518bc8a952..72d6b1c6f0 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/blank'
+
module ActiveModel
module Validations
module ClassMethods
@@ -16,7 +18,7 @@ module ActiveModel
#
# Configuration options:
# * <tt>message</tt> - A custom error message (default is: "can't be blank").
- # * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>,
+ # * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>,
# <tt>:update</tt>).
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
@@ -36,4 +38,4 @@ module ActiveModel
end
end
end
-end \ No newline at end of file
+end