aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model
diff options
context:
space:
mode:
authorEmilio Tagua <miloops@gmail.com>2009-09-01 15:45:43 -0300
committerEmilio Tagua <miloops@gmail.com>2009-09-01 15:45:43 -0300
commit39e4e76d15233bb1cb0b778d920f54efe86bb4f0 (patch)
tree7e9cab343139c79acc36715f4f24c579a207fbc6 /activemodel/lib/active_model
parent6b67df70ab1bc42d9a05571144cdf5614a7d4a6a (diff)
parentda636809daca9c338200811d3590e446f57c8e81 (diff)
downloadrails-39e4e76d15233bb1cb0b778d920f54efe86bb4f0.tar.gz
rails-39e4e76d15233bb1cb0b778d920f54efe86bb4f0.tar.bz2
rails-39e4e76d15233bb1cb0b778d920f54efe86bb4f0.zip
Merge commit 'rails/master'
Conflicts: activerecord/lib/active_record/associations.rb
Diffstat (limited to 'activemodel/lib/active_model')
-rw-r--r--activemodel/lib/active_model/dirty.rb14
-rw-r--r--activemodel/lib/active_model/lint.rb96
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb21
-rw-r--r--activemodel/lib/active_model/version.rb9
4 files changed, 139 insertions, 1 deletions
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index 624c3647ca..735c61df74 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -72,12 +72,26 @@ module ActiveModel
changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h }
end
+ # Map of attributes that were changed when the model was saved.
+ # person.name # => 'bob'
+ # person.name = 'robert'
+ # person.save
+ # person.previous_changes # => {'name' => ['bob, 'robert']}
+ def previous_changes
+ previously_changed_attributes
+ end
+
private
# Map of change <tt>attr => original value</tt>.
def changed_attributes
@changed_attributes ||= {}
end
+ # Map of fields that were changed when the model was saved
+ def previously_changed_attributes
+ @previously_changed || {}
+ end
+
# Handle <tt>*_changed?</tt> for +method_missing+.
def attribute_changed?(attr)
changed_attributes.include?(attr)
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
new file mode 100644
index 0000000000..46af8ca9de
--- /dev/null
+++ b/activemodel/lib/active_model/lint.rb
@@ -0,0 +1,96 @@
+require "test/unit"
+require "test/unit/ui/console/testrunner"
+
+# You can test whether an object is compliant with the ActiveModel API by
+# calling ActiveModel::Compliance.test(object). It will emit a Test::Unit
+# output that tells you whether your object is fully compliant, or if not,
+# which aspects of the API are not implemented.
+#
+# These tests do not attempt to determine the semantic correctness of the
+# returned values. For instance, you could implement valid? to always
+# return true, and the tests would pass. It is up to you to ensure that
+# the values are semantically meaningful.
+#
+# Objects you pass in are expected to return a compliant object from a
+# call to to_model. It is perfectly fine for to_model to return self.
+
+module ActiveModel
+ module Lint
+ def self.test(object, verbosity = 2, output = STDOUT)
+ test_class = Class.new(::Test::Unit::TestCase) do
+ include Test
+
+ define_method(:setup) do
+ assert object.respond_to?(:to_model), "The object should respond_to :to_model"
+ @object = object.to_model
+ super
+ end
+ end
+
+ ::Test::Unit::UI::Console::TestRunner.new(test_class, verbosity, output).start
+ end
+
+ module Test
+ def assert_boolean(name, result)
+ assert result == true || result == false, "#{name} should be a boolean"
+ end
+
+ # valid?
+ # ------
+ #
+ # Returns a boolean that specifies whether the object is in a valid or invalid
+ # state.
+ def test_valid?
+ assert @object.respond_to?(:valid?), "The model should respond to valid?"
+ assert_boolean "valid?", @object.valid?
+ end
+
+ # new_record?
+ # -----------
+ #
+ # 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 @object.respond_to?(:new_record?), "The model should respond to new_record?"
+ assert_boolean "new_record?", @object.new_record?
+ end
+
+ def test_destroyed?
+ assert @object.respond_to?(:new_record?), "The model should respond to destroyed?"
+ assert_boolean "destroyed?", @object.destroyed?
+ end
+
+ # errors
+ # ------
+ #
+ # Returns an object that has :[] and :full_messages defined on it. See below
+ # for more details.
+ def setup
+ assert @object.respond_to?(:errors), "The model should respond to errors"
+ @errors = @object.errors
+ end
+
+ # This module tests the #errors object
+ module Errors
+ # Returns an Array of Strings that are the errors for the attribute in
+ # question. If localization is used, the Strings should be localized
+ # for the current locale. If no error is present, this method should
+ # return an empty Array.
+ def test_errors_aref
+ assert @errors[:hello].is_a?(Array), "errors#[] should return an Array"
+ end
+
+ # Returns an Array of all error messages for the object. Each message
+ # should contain information about the field, if applicable.
+ def test_errors_full_messages
+ assert @errors.full_messages.is_a?(Array), "errors#full_messages should return an Array"
+ end
+ end
+
+ include Errors
+ end
+ end
+end \ No newline at end of file
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index ada6e28594..32dbcd82d0 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -31,6 +31,21 @@ module ActiveModel
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
+ #
+ # The following checks can also be supplied with a proc or a symbol which corresponds to a method:
+ # * <tt>:greater_than</tt>
+ # * <tt>:greater_than_or_equal_to</tt>
+ # * <tt>:equal_to</tt>
+ # * <tt>:less_than</tt>
+ # * <tt>:less_than_or_equal_to</tt>
+ #
+ # class Person < ActiveRecord::Base
+ # validates_numericality_of :width, :less_than => Proc.new { |person| person.height }
+ # validates_numericality_of :width, :greater_than => :minimum_weight
+ # end
+ #
+ #
+
def validates_numericality_of(*attr_names)
configuration = { :only_integer => false, :allow_nil => false }
configuration.update(attr_names.extract_options!)
@@ -38,7 +53,8 @@ module ActiveModel
numericality_options = ALL_NUMERICALITY_CHECKS.keys & configuration.keys
(numericality_options - [ :odd, :even ]).each do |option|
- raise ArgumentError, ":#{option} must be a number" unless configuration[option].is_a?(Numeric)
+ value = configuration[option]
+ raise ArgumentError, ":#{option} must be a number, a symbol or a proc" unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
end
validates_each(attr_names,configuration) do |record, attr_name, value|
@@ -74,6 +90,9 @@ module ActiveModel
record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
end
else
+ configuration[option] = configuration[option].call(record) if configuration[option].is_a? Proc
+ configuration[option] = record.method(configuration[option]).call if configuration[option].is_a? Symbol
+
unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option])
end
diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb
new file mode 100644
index 0000000000..0c233b7b4f
--- /dev/null
+++ b/activemodel/lib/active_model/version.rb
@@ -0,0 +1,9 @@
+module ActiveModel
+ module VERSION #:nodoc:
+ MAJOR = 3
+ MINOR = 0
+ TINY = "pre"
+
+ STRING = [MAJOR, MINOR, TINY].join('.')
+ end
+end