diff options
author | Pratik Naik <pratiknaik@gmail.com> | 2009-07-03 13:01:39 +0100 |
---|---|---|
committer | Pratik Naik <pratiknaik@gmail.com> | 2009-07-03 13:01:39 +0100 |
commit | 2fe263bb328c20539f2970057f31e567ec4ab7c8 (patch) | |
tree | 63e85164fb09aca6beb78e1a5c52424fa49ed098 /activemodel/lib/active_model | |
parent | bf5ac9965f12840d469ef2a4a16e8205dbbe5253 (diff) | |
parent | a4bdc00fec623f72592e663e6d7830eea0bc6ea4 (diff) | |
download | rails-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/active_model')
-rw-r--r-- | activemodel/lib/active_model/attributes.rb | 17 | ||||
-rw-r--r-- | activemodel/lib/active_model/deprecated_error_methods.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/errors.rb | 23 | ||||
-rw-r--r-- | activemodel/lib/active_model/naming.rb | 25 | ||||
-rw-r--r-- | activemodel/lib/active_model/observing.rb | 116 | ||||
-rw-r--r-- | activemodel/lib/active_model/serializers/json.rb | 38 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations.rb | 16 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/acceptance.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/confirmation.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/exclusion.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/format.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/inclusion.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/length.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/numericality.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/presence.rb | 6 |
15 files changed, 190 insertions, 83 deletions
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 |