aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model')
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb11
-rw-r--r--activemodel/lib/active_model/conversion.rb45
-rw-r--r--activemodel/lib/active_model/lint.rb40
-rw-r--r--activemodel/lib/active_model/naming.rb2
-rw-r--r--activemodel/lib/active_model/validations.rb23
-rw-r--r--activemodel/lib/active_model/validations/with.rb9
-rw-r--r--activemodel/lib/active_model/validator.rb18
-rw-r--r--activemodel/lib/active_model/version.rb5
8 files changed, 123 insertions, 30 deletions
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index f7fb66bdfc..c1334069fa 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -21,7 +21,6 @@ module ActiveModel
# A minimal implementation could be:
#
# class Person
- #
# include ActiveModel::AttributeMethods
#
# attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
@@ -44,9 +43,13 @@ module ActiveModel
# def reset_attribute_to_default!(attr)
# send("#{attr}=", "Default Name")
# end
- #
# end
- #
+ #
+ # Please notice that whenever you include ActiveModel::AtributeMethods in your class,
+ # it requires you to implement a <tt>attributes</tt> methods which returns a hash with
+ # each attribute name in your model as hash key and the attribute value as hash value.
+ # Hash keys must be a string.
+ #
module AttributeMethods
extend ActiveSupport::Concern
@@ -85,7 +88,7 @@ module ActiveModel
# AttributePerson.inheritance_column
# # => 'address_id'
def define_attr_method(name, value=nil, &block)
- sing = metaclass
+ sing = singleton_class
sing.send :alias_method, "original_#{name}", name
if block_given?
sing.send :define_method, name, &block
diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb
index c14a07c7dc..585c20dcdf 100644
--- a/activemodel/lib/active_model/conversion.rb
+++ b/activemodel/lib/active_model/conversion.rb
@@ -1,19 +1,44 @@
module ActiveModel
- # If your object is already designed to implement all of the Active Model featurs
- # include this module in your Class.
- #
- # class MyClass
+ # Handle default conversions: to_model, to_key and to_param.
+ #
+ # == Example
+ #
+ # Let's take for example this non persisted object.
+ #
+ # class ContactMessage
# include ActiveModel::Conversion
+ #
+ # # ContactMessage are never persisted in the DB
+ # def persisted?
+ # false
+ # end
# end
- #
- # Returns self to the <tt>:to_model</tt> method.
- #
- # If your model does not act like an Active Model object, then you should define
- # <tt>:to_model</tt> yourself returning a proxy object that wraps your object
- # with Active Model compliant methods.
+ #
+ # cm = ContactMessage.new
+ # cm.to_model == self #=> true
+ # cm.to_key #=> nil
+ # cm.to_param #=> nil
+ #
module Conversion
+ # If your object is already designed to implement all of the Active Model you can use
+ # the default to_model implementation, which simply returns self.
+ #
+ # If your model does not act like an Active Model object, then you should define
+ # <tt>:to_model</tt> yourself returning a proxy object that wraps your object
+ # with Active Model compliant methods.
def to_model
self
end
+
+ # Returns an Enumerable of all (primary) key attributes or nil if persisted? is fakse
+ def to_key
+ persisted? ? [id] : nil
+ end
+
+ # Returns a string representing the object's key suitable for use in URLs,
+ # or nil if persisted? is false
+ def to_param
+ to_key ? to_key.join('-') : nil
+ end
end
end
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
index 7bf0ad712d..13ddb622d1 100644
--- a/activemodel/lib/active_model/lint.rb
+++ b/activemodel/lib/active_model/lint.rb
@@ -13,6 +13,33 @@
module ActiveModel
module Lint
module Tests
+
+ # == Responds to <tt>to_key</tt>
+ #
+ # Returns an Enumerable of all (primary) key attributes
+ # or nil if model.persisted? is false
+ def test_to_key
+ assert model.respond_to?(:to_key), "The model should respond to to_key"
+ def model.persisted?() false end
+ assert model.to_key.nil?
+ end
+
+ # == Responds to <tt>to_param</tt>
+ #
+ # Returns a string representing the object's key suitable for use in URLs
+ # or nil if model.persisted? is false.
+ #
+ # Implementers can decide to either raise an exception or provide a default
+ # in case the record uses a composite primary key. There are no tests for this
+ # behavior in lint because it doesn't make sense to force any of the possible
+ # implementation strategies on the implementer. However, if the resource is
+ # not persisted?, then to_param should always return nil.
+ def test_to_param
+ assert model.respond_to?(:to_param), "The model should respond to to_param"
+ def model.persisted?() false end
+ assert model.to_param.nil?
+ end
+
# == Responds to <tt>valid?</tt>
#
# Returns a boolean that specifies whether the object is in a valid or invalid
@@ -22,21 +49,16 @@ module ActiveModel
assert_boolean model.valid?, "valid?"
end
- # == Responds to <tt>new_record?</tt>
+ # == Responds to <tt>persisted?</tt>
#
# Returns a boolean that specifies whether the object has been persisted yet.
# This is used when calculating the URL for an object. If the object is
# not persisted, a form for that object, for instance, will be POSTed to the
# collection. If it is persisted, a form for the object will put PUTed to the
# URL for the object.
- def test_new_record?
- assert model.respond_to?(:new_record?), "The model should respond to new_record?"
- assert_boolean model.new_record?, "new_record?"
- end
-
- def test_destroyed?
- assert model.respond_to?(:destroyed?), "The model should respond to destroyed?"
- assert_boolean model.destroyed?, "destroyed?"
+ def test_persisted?
+ assert model.respond_to?(:persisted?), "The model should respond to persisted?"
+ assert_boolean model.persisted?, "persisted?"
end
# == Naming
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index b9fb5fe0c8..8cdd3d2fe8 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -42,7 +42,7 @@ module ActiveModel
# To implement, just extend ActiveModel::Naming in your object:
#
# class BookCover
- # exten ActiveModel::Naming
+ # extend ActiveModel::Naming
# end
#
# BookCover.model_name #=> "BookCover"
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 03733a9c89..ba8648f8c9 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/keys'
require 'active_model/errors'
@@ -45,6 +46,9 @@ module ActiveModel
included do
extend ActiveModel::Translation
define_callbacks :validate, :scope => :name
+
+ class_attribute :_validators
+ self._validators = Hash.new { |h,k| h[k] = [] }
end
module ClassMethods
@@ -117,12 +121,23 @@ module ActiveModel
end
set_callback(:validate, *args, &block)
end
-
- private
-
+
+ # List all validators that being used to validate the model using +validates_with+
+ # method.
+ def validators
+ _validators.values.flatten.uniq
+ end
+
+ # List all validators that being used to validate a specific attribute.
+ def validators_on(attribute)
+ _validators[attribute.to_sym]
+ end
+
+ private
+
def _merge_attributes(attr_names)
options = attr_names.extract_options!
- options.merge(:attributes => attr_names)
+ options.merge(:attributes => attr_names.flatten)
end
end
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index db563876af..83d3ea80d6 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -62,6 +62,15 @@ module ActiveModel
args.each do |klass|
validator = klass.new(options, &block)
validator.setup(self) if validator.respond_to?(:setup)
+
+ if validator.respond_to?(:attributes) && !validator.attributes.empty?
+ validator.attributes.each do |attribute|
+ _validators[attribute.to_sym] << validator
+ end
+ else
+ _validators[nil] << validator
+ end
+
validate(validator, options)
end
end
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index ad9729de00..b61f0cb266 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/module/anonymous"
+
module ActiveModel #:nodoc:
# A simple base class that can be used along with
# +ActiveModel::Validations::ClassMethods.validates_with+
@@ -88,11 +90,27 @@ module ActiveModel #:nodoc:
class Validator
attr_reader :options
+ # Returns the kind of the validator.
+ #
+ # == Examples
+ #
+ # PresenceValidator.kind #=> :presence
+ # UniquenessValidator.kind #=> :uniqueness
+ #
+ def self.kind
+ @kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
+ end
+
# Accepts options that will be made availible through the +options+ reader.
def initialize(options)
@options = options
end
+ # Return the kind for this validator.
+ def kind
+ self.class.kind
+ end
+
# Override this method in subclasses with validation logic, adding errors
# to the records +errors+ array where necessary.
def validate(record)
diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb
index d51423ae1b..85d0eed180 100644
--- a/activemodel/lib/active_model/version.rb
+++ b/activemodel/lib/active_model/version.rb
@@ -2,8 +2,9 @@ module ActiveModel
module VERSION #:nodoc:
MAJOR = 3
MINOR = 0
- TINY = "0.beta1"
+ TINY = 0
+ BUILD = "beta1"
- STRING = [MAJOR, MINOR, TINY].join('.')
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
end