Skip to content

Commit

Permalink
Initial AMo Lint implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
wycats committed Aug 29, 2009
1 parent 353157c commit dbf20c2
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 0 deletions.
1 change: 1 addition & 0 deletions activemodel/lib/active_model.rb
Expand Up @@ -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'
Expand Down
96 changes: 96 additions & 0 deletions 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
50 changes: 50 additions & 0 deletions 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

1 comment on commit dbf20c2

@jeroenvandijk
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting.

I think I saw one small error on line 62 in active_model/lint.rb:

    assert @object.respond_to?(:new_record?), "The model should respond to destroyed?"

Should be
assert @object.respond_to?(:destroyed?), "The model should respond to destroyed?"

Btw, I was wondering why you put seperate parts in different modules while still being in the same file? Is that just conceptual or has it other uses as well?

Cheers,
Jeroen

Please sign in to comment.