aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
authorYehuda Katz <wycats@gmail.com>2009-08-29 01:49:18 -0500
committerYehuda Katz <wycats@gmail.com>2009-08-29 01:49:18 -0500
commitdbf20c2dbb5d1f2640517c468aa7c269d93414b9 (patch)
tree79fdbbd46f2a2c7ccf1601fbacf781cae387a739 /activemodel
parent353157ccd9960c806a6f17537dcbeddd2d1d17c8 (diff)
downloadrails-dbf20c2dbb5d1f2640517c468aa7c269d93414b9.tar.gz
rails-dbf20c2dbb5d1f2640517c468aa7c269d93414b9.tar.bz2
rails-dbf20c2dbb5d1f2640517c468aa7c269d93414b9.zip
Initial AMo Lint implementation
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/lib/active_model.rb1
-rw-r--r--activemodel/lib/active_model/lint.rb96
-rw-r--r--activemodel/test/cases/lint_test.rb50
3 files changed, 147 insertions, 0 deletions
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 244f3a546e..5bb931be7f 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -31,6 +31,7 @@ module ActiveModel
autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods'
autoload :Dirty, 'active_model/dirty'
autoload :Errors, 'active_model/errors'
+ autoload :Lint, 'active_model/lint'
autoload :Name, 'active_model/naming'
autoload :Naming, 'active_model/naming'
autoload :Observer, 'active_model/observing'
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/test/cases/lint_test.rb b/activemodel/test/cases/lint_test.rb
new file mode 100644
index 0000000000..165c353045
--- /dev/null
+++ b/activemodel/test/cases/lint_test.rb
@@ -0,0 +1,50 @@
+require "cases/helper"
+
+class TestLint < Test::Unit::TestCase
+ class CompliantObject
+ def to_model
+ self
+ end
+
+ def valid?() true end
+ def new_record?() true end
+ def destroyed?() true end
+
+ def errors
+ obj = Object.new
+ def obj.[](key) [] end
+ def obj.full_messages() [] end
+ obj
+ end
+ end
+
+ def assert_output(object, failures, errors, *test_names)
+ ActiveModel::Lint.test(object, 3, output = StringIO.new)
+ regex = %r{#{failures} failures, #{errors} errors}
+ assert_match regex, output.string
+
+ test_names.each do |test_name|
+ assert_match test_name, output.string
+ end
+ end
+
+ def test_valid
+ assert_output(CompliantObject.new, 0, 0, /test_valid/)
+ end
+
+ def test_new_record
+ assert_output(CompliantObject.new, 0, 0, /test_new_record?/)
+ end
+
+ def test_destroyed
+ assert_output(CompliantObject.new, 0, 0, /test_destroyed/)
+ end
+
+ def test_errors_aref
+ assert_output(CompliantObject.new, 0, 0, /test_errors_aref/)
+ end
+
+ def test_errors_full_messages
+ assert_output(CompliantObject.new, 0, 0, /test_errors_aref/)
+ end
+end \ No newline at end of file