ratnikov-shoulda 2.0.6.3 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. data/README.rdoc +3 -2
  2. data/Rakefile +1 -1
  3. data/lib/shoulda/active_record/assertions.rb +10 -31
  4. data/lib/shoulda/active_record/helpers.rb +40 -0
  5. data/lib/shoulda/active_record/macros.rb +171 -325
  6. data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +83 -0
  7. data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +102 -0
  8. data/lib/shoulda/active_record/matchers/association_matcher.rb +226 -0
  9. data/lib/shoulda/active_record/matchers/ensure_inclusion_of_matcher.rb +87 -0
  10. data/lib/shoulda/active_record/matchers/ensure_length_of_matcher.rb +141 -0
  11. data/lib/shoulda/active_record/matchers/have_db_column_matcher.rb +169 -0
  12. data/lib/shoulda/active_record/matchers/have_index_matcher.rb +105 -0
  13. data/lib/shoulda/active_record/matchers/have_named_scope_matcher.rb +125 -0
  14. data/lib/shoulda/active_record/matchers/have_readonly_attribute_matcher.rb +59 -0
  15. data/lib/shoulda/active_record/matchers/validate_acceptance_of_matcher.rb +41 -0
  16. data/lib/shoulda/active_record/matchers/validate_numericality_of_matcher.rb +39 -0
  17. data/lib/shoulda/active_record/matchers/validate_presence_of_matcher.rb +60 -0
  18. data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +148 -0
  19. data/lib/shoulda/active_record/matchers/validation_matcher.rb +56 -0
  20. data/lib/shoulda/active_record/matchers.rb +42 -0
  21. data/lib/shoulda/active_record.rb +4 -0
  22. data/lib/shoulda/assertions.rb +12 -0
  23. data/lib/shoulda/autoload_macros.rb +46 -0
  24. data/lib/shoulda/rails.rb +1 -8
  25. data/lib/shoulda/rspec.rb +5 -0
  26. data/lib/shoulda/tasks/list_tests.rake +6 -1
  27. data/lib/shoulda/test_unit.rb +19 -0
  28. data/lib/shoulda.rb +5 -17
  29. data/rails/init.rb +1 -1
  30. data/test/README +2 -2
  31. data/test/fail_macros.rb +2 -2
  32. data/test/matchers/allow_mass_assignment_of_matcher_test.rb +68 -0
  33. data/test/matchers/allow_value_matcher_test.rb +41 -0
  34. data/test/matchers/association_matcher_test.rb +258 -0
  35. data/test/matchers/ensure_inclusion_of_matcher_test.rb +80 -0
  36. data/test/matchers/ensure_length_of_matcher_test.rb +158 -0
  37. data/test/matchers/have_db_column_matcher_test.rb +169 -0
  38. data/test/matchers/have_index_matcher_test.rb +74 -0
  39. data/test/matchers/have_named_scope_matcher_test.rb +65 -0
  40. data/test/matchers/have_readonly_attributes_matcher_test.rb +29 -0
  41. data/test/matchers/validate_acceptance_of_matcher_test.rb +44 -0
  42. data/test/matchers/validate_numericality_of_matcher_test.rb +52 -0
  43. data/test/matchers/validate_presence_of_matcher_test.rb +86 -0
  44. data/test/matchers/validate_uniqueness_of_matcher_test.rb +141 -0
  45. data/test/model_builder.rb +61 -0
  46. data/test/other/autoload_macro_test.rb +18 -0
  47. data/test/other/helpers_test.rb +58 -0
  48. data/test/rails_root/config/database.yml +1 -1
  49. data/test/rails_root/config/environments/{sqlite3.rb → test.rb} +0 -0
  50. data/test/rails_root/test/shoulda_macros/custom_macro.rb +6 -0
  51. data/test/rails_root/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +6 -0
  52. data/test/rails_root/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +6 -0
  53. data/test/test_helper.rb +3 -1
  54. data/test/unit/address_test.rb +1 -1
  55. data/test/unit/dog_test.rb +1 -1
  56. data/test/unit/post_test.rb +4 -4
  57. data/test/unit/product_test.rb +2 -2
  58. data/test/unit/tag_test.rb +2 -1
  59. data/test/unit/user_test.rb +8 -7
  60. metadata +49 -3
@@ -0,0 +1,148 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensures that the model is invalid if the given attribute is not unique.
6
+ #
7
+ # Internally, this uses values from existing records to test validations,
8
+ # so this will always fail if you have not saved at least one record for
9
+ # the model being tested, like so:
10
+ #
11
+ # describe User do
12
+ # before(:each) { User.create!(:email => 'address@example.com') }
13
+ # it { should validate_uniqueness_of(:email) }
14
+ # end
15
+ #
16
+ # Options:
17
+ #
18
+ # * <tt>with_message</tt> - value the test expects to find in
19
+ # <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
20
+ # Defaults to the translation for <tt>:taken</tt>.
21
+ # * <tt>scoped_to</tt> - field(s) to scope the uniqueness to.
22
+ # * <tt>case_insensitive</tt> - ensures that the validation does not
23
+ # check case. Off by default. Ignored by non-text attributes.
24
+ #
25
+ # Examples:
26
+ # it { should validate_uniqueness_of(:keyword) }
27
+ # it { should validate_uniqueness_of(:keyword).with_message(/dup/) }
28
+ # it { should validate_uniqueness_of(:email).scoped_to(:name) }
29
+ # it { should validate_uniqueness_of(:email).
30
+ # scoped_to(:first_name, :last_name) }
31
+ # it { should validate_uniqueness_of(:keyword).case_insensitive }
32
+ #
33
+ def validate_uniqueness_of(attr)
34
+ ValidateUniquenessOfMatcher.new(attr)
35
+ end
36
+
37
+ class ValidateUniquenessOfMatcher < ValidationMatcher # :nodoc:
38
+ include Helpers
39
+
40
+ def initialize(attribute)
41
+ @attribute = attribute
42
+ end
43
+
44
+ def scoped_to(*scopes)
45
+ @scopes = [*scopes].flatten
46
+ self
47
+ end
48
+
49
+ def with_message(message)
50
+ @expected_message = message
51
+ self
52
+ end
53
+
54
+ def case_insensitive
55
+ @case_insensitive = true
56
+ self
57
+ end
58
+
59
+ def description
60
+ result = "require "
61
+ result << "case sensitive " unless @case_insensitive
62
+ result << "unique value for #{@attribute}"
63
+ result << " scoped to #{@scopes.join(', ')}" unless @scopes.blank?
64
+ result
65
+ end
66
+
67
+ def matches?(subject)
68
+ @subject = subject.class.new
69
+ @expected_message ||= :taken
70
+ find_existing &&
71
+ set_scoped_attributes &&
72
+ validate_attribute &&
73
+ validate_after_scope_change
74
+ end
75
+
76
+ private
77
+
78
+ def find_existing
79
+ if @existing = @subject.class.find(:first)
80
+ @failure_message = "Can't find first #{class_name}"
81
+ true
82
+ else
83
+ false
84
+ end
85
+ end
86
+
87
+ def set_scoped_attributes
88
+ unless @scopes.blank?
89
+ @scopes.each do |scope|
90
+ setter = :"#{scope}="
91
+ unless @subject.respond_to?(setter)
92
+ @failure_message =
93
+ "#{class_name} doesn't seem to have a #{scope} attribute."
94
+ return false
95
+ end
96
+ @subject.send("#{scope}=", @existing.send(scope))
97
+ end
98
+ end
99
+ true
100
+ end
101
+
102
+ def validate_attribute
103
+ disallows_value_of(existing_value, @expected_message)
104
+ end
105
+
106
+ # TODO: There is a chance that we could change the scoped field
107
+ # to a value that's already taken. An alternative implementation
108
+ # could actually find all values for scope and create a unique
109
+ def validate_after_scope_change
110
+ if @scopes.blank?
111
+ true
112
+ else
113
+ @scopes.all? do |scope|
114
+ previous_value = @existing.send(scope)
115
+
116
+ # Assume the scope is a foreign key if the field is nil
117
+ previous_value ||= 0
118
+
119
+ next_value = previous_value.next
120
+
121
+ @subject.send("#{scope}=", next_value)
122
+
123
+ if allows_value_of(existing_value, @expected_message)
124
+ @negative_failure_message <<
125
+ " (with different value of #{scope})"
126
+ true
127
+ else
128
+ @failure_message << " (with different value of #{scope})"
129
+ false
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def class_name
136
+ @subject.class.name
137
+ end
138
+
139
+ def existing_value
140
+ value = @existing.send(@attribute)
141
+ value.swapcase! if @case_insensitive && value.respond_to?(:swapcase!)
142
+ value
143
+ end
144
+ end
145
+
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,56 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ class ValidationMatcher # :nodoc:
6
+
7
+ attr_reader :failure_message
8
+
9
+ def initialize(attribute)
10
+ @attribute = attribute
11
+ end
12
+
13
+ def negative_failure_message
14
+ @negative_failure_message || @failure_message
15
+ end
16
+
17
+ def matches?(subject)
18
+ @subject = subject
19
+ false
20
+ end
21
+
22
+ private
23
+
24
+ def allows_value_of(value, message = nil)
25
+ allow = AllowValueMatcher.
26
+ new(value).
27
+ for(@attribute).
28
+ with_message(message)
29
+ if allow.matches?(@subject)
30
+ @negative_failure_message = allow.failure_message
31
+ true
32
+ else
33
+ @failure_message = allow.negative_failure_message
34
+ false
35
+ end
36
+ end
37
+
38
+ def disallows_value_of(value, message = nil)
39
+ disallow = AllowValueMatcher.
40
+ new(value).
41
+ for(@attribute).
42
+ with_message(message)
43
+ if disallow.matches?(@subject)
44
+ @failure_message = disallow.negative_failure_message
45
+ false
46
+ else
47
+ @negative_failure_message = disallow.failure_message
48
+ true
49
+ end
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
@@ -0,0 +1,42 @@
1
+ require 'shoulda/active_record/helpers'
2
+ require 'shoulda/active_record/matchers/validation_matcher'
3
+ require 'shoulda/active_record/matchers/allow_value_matcher'
4
+ require 'shoulda/active_record/matchers/ensure_length_of_matcher'
5
+ require 'shoulda/active_record/matchers/ensure_inclusion_of_matcher'
6
+ require 'shoulda/active_record/matchers/validate_presence_of_matcher'
7
+ require 'shoulda/active_record/matchers/validate_uniqueness_of_matcher'
8
+ require 'shoulda/active_record/matchers/validate_acceptance_of_matcher'
9
+ require 'shoulda/active_record/matchers/validate_numericality_of_matcher'
10
+ require 'shoulda/active_record/matchers/association_matcher'
11
+ require 'shoulda/active_record/matchers/have_db_column_matcher'
12
+ require 'shoulda/active_record/matchers/have_index_matcher'
13
+ require 'shoulda/active_record/matchers/have_readonly_attribute_matcher'
14
+ require 'shoulda/active_record/matchers/allow_mass_assignment_of_matcher'
15
+ require 'shoulda/active_record/matchers/have_named_scope_matcher'
16
+
17
+
18
+ module Shoulda # :nodoc:
19
+ module ActiveRecord # :nodoc:
20
+ # = Matchers for your active record models
21
+ #
22
+ # These matchers will test most of the validations and associations for your
23
+ # ActiveRecord models.
24
+ #
25
+ # describe User do
26
+ # it { should validate_presence_of(:name) }
27
+ # it { should validate_presence_of(:phone_number) }
28
+ # %w(abcd 1234).each do |value|
29
+ # it { should_not allow_value(value).for(:phone_number) }
30
+ # end
31
+ # it { should allow_value("(123) 456-7890").for(:phone_number) }
32
+ # it { should_not allow_mass_assignment_of(:password) }
33
+ # it { should have_one(:profile) }
34
+ # it { should have_many(:dogs) }
35
+ # it { should have_many(:messes).through(:dogs) }
36
+ # it { should belong_to(:lover) }
37
+ # end
38
+ #
39
+ module Matchers
40
+ end
41
+ end
42
+ end
@@ -1,10 +1,14 @@
1
1
  require 'shoulda'
2
+ require 'shoulda/active_record/helpers'
3
+ require 'shoulda/active_record/matchers'
2
4
  require 'shoulda/active_record/assertions'
3
5
  require 'shoulda/active_record/macros'
4
6
 
5
7
  module Test # :nodoc: all
6
8
  module Unit
7
9
  class TestCase
10
+ include Shoulda::ActiveRecord::Helpers
11
+ include Shoulda::ActiveRecord::Matchers
8
12
  include Shoulda::ActiveRecord::Assertions
9
13
  extend Shoulda::ActiveRecord::Macros
10
14
  end
@@ -43,5 +43,17 @@ module Shoulda # :nodoc:
43
43
  assert(!collection.include?(x), msg)
44
44
  end
45
45
  end
46
+
47
+ # Asserts that the given matcher returns true when +target+ is passed to #matches?
48
+ def assert_accepts(matcher, target)
49
+ success = matcher.matches?(target)
50
+ assert_block(matcher.failure_message) { success }
51
+ end
52
+
53
+ # Asserts that the given matcher returns false when +target+ is passed to #matches?
54
+ def assert_rejects(matcher, target)
55
+ success = !matcher.matches?(target)
56
+ assert_block(matcher.negative_failure_message) { success }
57
+ end
46
58
  end
47
59
  end
@@ -0,0 +1,46 @@
1
+ module Shoulda # :nodoc:
2
+ # Call autoload_macros when you want to load test macros automatically in a non-Rails
3
+ # project (it's done automatically for Rails projects).
4
+ # You don't need to specify ROOT/test/shoulda_macros explicitly. Your custom macros
5
+ # are loaded automatically when you call autoload_macros.
6
+ #
7
+ # The first argument is the path to you application's root directory.
8
+ # All following arguments are directories relative to your root, which contain
9
+ # shoulda_macros subdirectories. These directories support the same kinds of globs as the
10
+ # Dir class.
11
+ #
12
+ # Basic usage (from a test_helper):
13
+ # Shoulda.autoload_macros(File.dirname(__FILE__) + '/..')
14
+ # will load everything in
15
+ # - your_app/test/shoulda_macros
16
+ #
17
+ # To load vendored macros as well:
18
+ # Shoulda.autoload_macros(APP_ROOT, 'vendor/*')
19
+ # will load everything in
20
+ # - APP_ROOT/vendor/*/shoulda_macros
21
+ # - APP_ROOT/test/shoulda_macros
22
+ #
23
+ # To load macros in an app with a vendor directory laid out like Rails':
24
+ # Shoulda.autoload_macros(APP_ROOT, 'vendor/{plugins,gems}/*')
25
+ # or
26
+ # Shoulda.autoload_macros(APP_ROOT, 'vendor/plugins/*', 'vendor/gems/*')
27
+ # will load everything in
28
+ # - APP_ROOT/vendor/plugins/*/shoulda_macros
29
+ # - APP_ROOT/vendor/gems/*/shoulda_macros
30
+ # - APP_ROOT/test/shoulda_macros
31
+ #
32
+ # If you prefer to stick testing dependencies away from your production dependencies:
33
+ # Shoulda.autoload_macros(APP_ROOT, 'vendor/*', 'test/vendor/*')
34
+ # will load everything in
35
+ # - APP_ROOT/vendor/*/shoulda_macros
36
+ # - APP_ROOT/test/vendor/*/shoulda_macros
37
+ # - APP_ROOT/test/shoulda_macros
38
+ def self.autoload_macros(root, *dirs)
39
+ dirs << File.join('test')
40
+ complete_dirs = dirs.map{|d| File.join(root, d, 'shoulda_macros')}
41
+ all_files = complete_dirs.inject([]){ |files, dir| files + Dir[File.join(dir, '*.rb')] }
42
+ all_files.each do |file|
43
+ require file
44
+ end
45
+ end
46
+ end
data/lib/shoulda/rails.rb CHANGED
@@ -8,12 +8,5 @@ require 'shoulda/action_mailer' if defined? ActionMailer::Base
8
8
 
9
9
  if defined?(RAILS_ROOT)
10
10
  # load in the 3rd party macros from vendorized plugins and gems
11
- Dir[File.join(RAILS_ROOT, "vendor", "{plugins,gems}", "*", "shoulda_macros", "*.rb")].each do |macro_file_path|
12
- require macro_file_path
13
- end
14
-
15
- # load in the local application specific macros
16
- Dir[File.join(RAILS_ROOT, "test", "shoulda_macros", "*.rb")].each do |macro_file_path|
17
- require macro_file_path
18
- end
11
+ Shoulda.autoload_macros RAILS_ROOT, File.join("vendor", "{plugins,gems}", "*")
19
12
  end
@@ -0,0 +1,5 @@
1
+ require 'shoulda/active_record/matchers'
2
+
3
+ Spec::Runner.configure do |config|
4
+ config.include Shoulda::ActiveRecord::Matchers, :type => :model
5
+ end
@@ -13,7 +13,12 @@ namespace :shoulda do
13
13
  test_files = Dir.glob(File.join('test', '**', '*_test.rb'))
14
14
  test_files.each do |file|
15
15
  load file
16
- klass = File.basename(file, '.rb').classify.constantize
16
+ klass = File.basename(file, '.rb').classify
17
+ unless Object.const_defined?(klass.to_s)
18
+ puts "Skipping #{klass} because it doesn't map to a Class"
19
+ next
20
+ end
21
+ klass = klass.constantize
17
22
 
18
23
  puts klass.name.gsub('Test', '')
19
24
 
@@ -0,0 +1,19 @@
1
+ require 'shoulda/context'
2
+ require 'shoulda/proc_extensions'
3
+ require 'shoulda/assertions'
4
+ require 'shoulda/macros'
5
+ require 'shoulda/helpers'
6
+ require 'shoulda/autoload_macros'
7
+ require 'shoulda/rails' if defined? RAILS_ROOT
8
+
9
+ module Test # :nodoc: all
10
+ module Unit
11
+ class TestCase
12
+ extend Shoulda::ClassMethods
13
+ include Shoulda::Assertions
14
+ extend Shoulda::Macros
15
+ include Shoulda::Helpers
16
+ end
17
+ end
18
+ end
19
+
data/lib/shoulda.rb CHANGED
@@ -1,21 +1,9 @@
1
- require 'shoulda/context'
2
- require 'shoulda/proc_extensions'
3
- require 'shoulda/assertions'
4
- require 'shoulda/macros'
5
- require 'shoulda/helpers'
6
- require 'shoulda/rails' if defined? RAILS_ROOT
7
-
8
1
  module Shoulda
9
- VERSION = "2.0.6.3"
2
+ VERSION = "2.9.0"
10
3
  end
11
4
 
12
- module Test # :nodoc: all
13
- module Unit
14
- class TestCase
15
- extend Shoulda::ClassMethods
16
- include Shoulda::Assertions
17
- extend Shoulda::Macros
18
- include Shoulda::Helpers
19
- end
20
- end
5
+ if defined? Spec
6
+ require 'shoulda/rspec'
7
+ else
8
+ require 'shoulda/test_unit'
21
9
  end
data/rails/init.rb CHANGED
@@ -1 +1 @@
1
- require 'shoulda/rails'
1
+ require 'shoulda/rails' if RAILS_ENV == 'test'
data/test/README CHANGED
@@ -6,7 +6,7 @@ The test directory contains the following files and subdirectories:
6
6
 
7
7
  * rails_root - contains the stripped down rails application that the tests run against. The rails root contains:
8
8
  ** the models, controllers, and views defined under app/
9
- ** the sqlite3.rb environment file
9
+ ** the test.rb environment file
10
10
  ** a migration file for each model
11
11
  ** a shoulda initializer that simulates loading the plugin but without relying on vendor/plugins
12
12
  * fixtures - contain the sample DB data for each model
@@ -14,7 +14,7 @@ The test directory contains the following files and subdirectories:
14
14
  * unit - model tests for each of the models under rails_root/app
15
15
  * other - tests for the shoulda contexts, should statements, and assertions
16
16
  * test_helper.rb - responsible for initializing the test environment
17
- ** sets the rails_env to sqlite3
17
+ ** sets the rails_env to test
18
18
  ** sets the rails_root
19
19
  ** runs all the migrations against the in-memory sqlite3 db
20
20
  ** adds some magic to load the right fixture files
data/test/fail_macros.rb CHANGED
@@ -8,8 +8,8 @@ module Shoulda
8
8
  # example, to ensure that a set of test macros should fail, do this:
9
9
  #
10
10
  # should_fail do
11
- # should_require_attributes :comments
12
- # should_protect_attributes :name
11
+ # should_validate_presence_of :comments
12
+ # should_not_allow_mass_assignment_of :name
13
13
  # end
14
14
  def should_fail(&block)
15
15
  context "should fail when trying to run:" do
@@ -0,0 +1,68 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class AllowMassAssignmentOfMatcherTest < Test::Unit::TestCase # :nodoc:
4
+
5
+ context "an attribute that is blacklisted from mass-assignment" do
6
+ setup do
7
+ define_model :example, :attr => :string do
8
+ attr_protected :attr
9
+ end
10
+ @model = Example.new
11
+ end
12
+
13
+ should "reject being mass-assignable" do
14
+ assert_rejects allow_mass_assignment_of(:attr), @model
15
+ end
16
+ end
17
+
18
+ context "an attribute that is not whitelisted for mass-assignment" do
19
+ setup do
20
+ define_model :example, :attr => :string, :other => :string do
21
+ attr_accessible :other
22
+ end
23
+ @model = Example.new
24
+ end
25
+
26
+ should "reject being mass-assignable" do
27
+ assert_rejects allow_mass_assignment_of(:attr), @model
28
+ end
29
+ end
30
+
31
+ context "an attribute that is whitelisted for mass-assignment" do
32
+ setup do
33
+ define_model :example, :attr => :string do
34
+ attr_accessible :attr
35
+ end
36
+ @model = Example.new
37
+ end
38
+
39
+ should "accept being mass-assignable" do
40
+ assert_accepts allow_mass_assignment_of(:attr), @model
41
+ end
42
+ end
43
+
44
+ context "an attribute not included in the mass-assignment blacklist" do
45
+ setup do
46
+ define_model :example, :attr => :string, :other => :string do
47
+ attr_protected :other
48
+ end
49
+ @model = Example.new
50
+ end
51
+
52
+ should "accept being mass-assignable" do
53
+ assert_accepts allow_mass_assignment_of(:attr), @model
54
+ end
55
+ end
56
+
57
+ context "an attribute on a class with no protected attributes" do
58
+ setup do
59
+ define_model :example, :attr => :string
60
+ @model = Example.new
61
+ end
62
+
63
+ should "accept being mass-assignable" do
64
+ assert_accepts allow_mass_assignment_of(:attr), @model
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,41 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class AllowValueMatcherTest < Test::Unit::TestCase # :nodoc:
4
+
5
+ context "an attribute with a format validation" do
6
+ setup do
7
+ define_model :example, :attr => :string do
8
+ validates_format_of :attr, :with => /abc/
9
+ end
10
+ @model = Example.new
11
+ end
12
+
13
+ should "allow a good value" do
14
+ assert_accepts allow_value("abcde").for(:attr), @model
15
+ end
16
+
17
+ should "not allow a bad value" do
18
+ assert_rejects allow_value("xyz").for(:attr), @model
19
+ end
20
+ end
21
+
22
+ context "an attribute with a format validation and a custom message" do
23
+ setup do
24
+ define_model :example, :attr => :string do
25
+ validates_format_of :attr, :with => /abc/, :message => 'bad value'
26
+ end
27
+ @model = Example.new
28
+ end
29
+
30
+ should "allow a good value" do
31
+ assert_accepts allow_value('abcde').for(:attr).with_message(/bad/),
32
+ @model
33
+ end
34
+
35
+ should "not allow a bad value" do
36
+ assert_rejects allow_value('xyz').for(:attr).with_message(/bad/),
37
+ @model
38
+ end
39
+ end
40
+
41
+ end