ward 0.1.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 (92) hide show
  1. data/.document +5 -0
  2. data/.gitignore +28 -0
  3. data/LICENSE +19 -0
  4. data/README.markdown +99 -0
  5. data/Rakefile +47 -0
  6. data/VERSION +1 -0
  7. data/features/acceptance_matcher.feature +78 -0
  8. data/features/attribute_keyword.feature +13 -0
  9. data/features/close_to_matcher.feature +130 -0
  10. data/features/context_arguments.feature +47 -0
  11. data/features/equal_to_matcher.feature +25 -0
  12. data/features/error_messages.feature +69 -0
  13. data/features/external_validation.feature +15 -0
  14. data/features/has_matcher.feature +72 -0
  15. data/features/has_matcher_initialized_with_expectation.feature +94 -0
  16. data/features/has_matcher_relativities.feature +171 -0
  17. data/features/include_matcher.feature +28 -0
  18. data/features/is_keyword.feature +42 -0
  19. data/features/is_not_keyword.feature +62 -0
  20. data/features/match_matcher.feature +49 -0
  21. data/features/multiple_validators.feature +29 -0
  22. data/features/nil_matcher.feature +25 -0
  23. data/features/predicate_matcher.feature +23 -0
  24. data/features/present_matcher.feature +59 -0
  25. data/features/satisfy_matcher.feature +80 -0
  26. data/features/scenario_validation.feature +81 -0
  27. data/features/step_definitions/external_validation_steps.rb +69 -0
  28. data/features/step_definitions/generic_validation_steps.rb +33 -0
  29. data/features/step_definitions/object_definition_steps.rb +43 -0
  30. data/features/support/env.rb +12 -0
  31. data/features/support/object_builder.rb +33 -0
  32. data/features/support/struct.rb +38 -0
  33. data/lang/en.yml +56 -0
  34. data/lib/ward.rb +26 -0
  35. data/lib/ward/context.rb +70 -0
  36. data/lib/ward/context_chain.rb +87 -0
  37. data/lib/ward/dsl.rb +7 -0
  38. data/lib/ward/dsl/validation_block.rb +73 -0
  39. data/lib/ward/dsl/validation_builder.rb +190 -0
  40. data/lib/ward/errors.rb +213 -0
  41. data/lib/ward/matchers.rb +97 -0
  42. data/lib/ward/matchers/acceptance.rb +43 -0
  43. data/lib/ward/matchers/close_to.rb +60 -0
  44. data/lib/ward/matchers/equal_to.rb +33 -0
  45. data/lib/ward/matchers/has.rb +283 -0
  46. data/lib/ward/matchers/include.rb +54 -0
  47. data/lib/ward/matchers/match.rb +29 -0
  48. data/lib/ward/matchers/matcher.rb +68 -0
  49. data/lib/ward/matchers/nil.rb +30 -0
  50. data/lib/ward/matchers/predicate.rb +31 -0
  51. data/lib/ward/matchers/present.rb +56 -0
  52. data/lib/ward/matchers/satisfy.rb +65 -0
  53. data/lib/ward/spec.rb +17 -0
  54. data/lib/ward/spec/matcher_matcher.rb +114 -0
  55. data/lib/ward/support.rb +7 -0
  56. data/lib/ward/support/basic_object.rb +55 -0
  57. data/lib/ward/support/result.rb +49 -0
  58. data/lib/ward/validator.rb +147 -0
  59. data/lib/ward/validator_set.rb +115 -0
  60. data/lib/ward/version.rb +3 -0
  61. data/spec/lib/has_matcher_relativity_examples.rb +15 -0
  62. data/spec/lib/have_public_method_defined.rb +22 -0
  63. data/spec/rcov.opts +8 -0
  64. data/spec/spec.opts +4 -0
  65. data/spec/spec_helper.rb +19 -0
  66. data/spec/ward/context_chain_spec.rb +178 -0
  67. data/spec/ward/context_spec.rb +57 -0
  68. data/spec/ward/dsl/validation_block_spec.rb +27 -0
  69. data/spec/ward/dsl/validation_builder_spec.rb +212 -0
  70. data/spec/ward/errors_spec.rb +149 -0
  71. data/spec/ward/matchers/acceptance_spec.rb +16 -0
  72. data/spec/ward/matchers/close_to_spec.rb +57 -0
  73. data/spec/ward/matchers/equal_to_spec.rb +16 -0
  74. data/spec/ward/matchers/has_spec.rb +175 -0
  75. data/spec/ward/matchers/include_spec.rb +41 -0
  76. data/spec/ward/matchers/match_spec.rb +21 -0
  77. data/spec/ward/matchers/matcher_spec.rb +54 -0
  78. data/spec/ward/matchers/nil_spec.rb +16 -0
  79. data/spec/ward/matchers/predicate_spec.rb +19 -0
  80. data/spec/ward/matchers/present_spec.rb +16 -0
  81. data/spec/ward/matchers/satisfy_spec.rb +68 -0
  82. data/spec/ward/matchers_spec.rb +51 -0
  83. data/spec/ward/spec/have_public_method_defined_spec.rb +31 -0
  84. data/spec/ward/spec/matcher_matcher_spec.rb +217 -0
  85. data/spec/ward/validator_set_spec.rb +178 -0
  86. data/spec/ward/validator_spec.rb +264 -0
  87. data/tasks/features.rake +15 -0
  88. data/tasks/rcov.rake +24 -0
  89. data/tasks/spec.rake +18 -0
  90. data/tasks/yard.rake +9 -0
  91. data/ward.gemspec +176 -0
  92. metadata +239 -0
@@ -0,0 +1,149 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe Ward::Errors do
4
+ subject { Ward::Errors }
5
+
6
+ it 'should be enumerable' do
7
+ Ward::Errors.ancestors.should include(Enumerable)
8
+ end
9
+
10
+ it { should have_public_method_defined(:each) }
11
+
12
+ describe '#each' do
13
+ it 'should yield each invalid attribute' do
14
+ errors = Ward::Errors.new
15
+ errors.add(:one, 'Whoops')
16
+ errors.add(:two, 'Whoops')
17
+
18
+ attributes = errors.map { |attribute, _| attribute }
19
+ attributes.should include(:one)
20
+ attributes.should include(:two)
21
+ end
22
+ end
23
+
24
+ #
25
+ # add
26
+ #
27
+
28
+ describe '#add' do
29
+ it 'should return the message which was set' do
30
+ Ward::Errors.new.add(:name, 'Whoops').should == 'Whoops'
31
+ end
32
+ end
33
+
34
+ #
35
+ # on
36
+ #
37
+
38
+ it { should have_public_method_defined(:on) }
39
+
40
+ describe '#on' do
41
+
42
+ describe 'when there is one error keyed on an attribute name' do
43
+ before(:all) do
44
+ @errors = Ward::Errors.new
45
+ @errors.add(:name, 'Name should not fail')
46
+ end
47
+
48
+ it 'should return nil when no error is present on the attribute' do
49
+ @errors.on(:__invalid__).should be_nil
50
+ end
51
+
52
+ it 'return a string when an error is present on the attribute' do
53
+ @errors.on(:name).should == ['Name should not fail']
54
+ end
55
+ end # when there is one error keyed on an attribute name
56
+
57
+ describe 'when there is one error keyed on a single context' do
58
+ before(:all) do
59
+ @errors = Ward::Errors.new
60
+ @errors.add(Ward::Context.new(:name), 'Name should not fail')
61
+ end
62
+
63
+ it 'should return nil when no error is present on the attribute' do
64
+ @errors.on(:__invalid__).should be_nil
65
+ end
66
+
67
+ it 'return a string when an error is present on the attribute' do
68
+ @errors.on(:name).should == ['Name should not fail']
69
+ end
70
+ end # when there is one error keyed on a single context
71
+
72
+ describe 'when there is one error keyed on a context chain' do
73
+ before(:all) do
74
+ chain = Ward::ContextChain.new
75
+ chain.push(Ward::Context.new(:name))
76
+ chain.push(Ward::Context.new(:length))
77
+
78
+ @errors = Ward::Errors.new
79
+ @errors.add(chain, 'Name should not fail')
80
+ end
81
+
82
+ it 'should return nil when no error is present on the attribute' do
83
+ @errors.on(:__invalid__).should be_nil
84
+ end
85
+
86
+ it 'return a string when an error is present on the attribute' do
87
+ @errors.on(:name).should == ['Name should not fail']
88
+ end
89
+ end # when there is one error keyed on a context chain
90
+
91
+ end # on
92
+
93
+ # Class methods ============================================================
94
+
95
+ describe '.message' do
96
+ it 'should return the message specified' do
97
+ message = Ward::Errors.message('has.eql.positive')
98
+ message.should == '%{context} should have %{expected} %{collection}'
99
+ end
100
+
101
+ it 'should return the first matching message key' do
102
+ message = Ward::Errors.message(
103
+ 'does.not.exist', 'has.eql.negative', 'has.eql.positive')
104
+
105
+ message.should == '%{context} should not have %{expected} %{collection}'
106
+ end
107
+
108
+ it 'should return nil when no matching message is found' do
109
+ Ward::Errors.message('does.not.exist').should be_nil
110
+ end
111
+ end
112
+
113
+ describe '.format_exclusive_list' do
114
+ it 'should return an empty string when given an empty Array' do
115
+ Ward::Errors.format_exclusive_list([]).should == ''
116
+ end
117
+
118
+ it 'should return "1" when given [1]' do
119
+ Ward::Errors.format_exclusive_list([1]).should == '1'
120
+ end
121
+
122
+ it 'should return "1 or 2" when given [1, 2]' do
123
+ Ward::Errors.format_exclusive_list([1, 2]).should == '1 or 2'
124
+ end
125
+
126
+ it 'should return "1, 2, or 3" when given [1, 2, 3]' do
127
+ Ward::Errors.format_exclusive_list([1, 2, 3]).should == '1, 2, or 3'
128
+ end
129
+ end
130
+
131
+ describe '.format_inclusive_list' do
132
+ it 'should return an empty string when given an empty Array' do
133
+ Ward::Errors.format_inclusive_list([]).should == ''
134
+ end
135
+
136
+ it 'should return "1" when given [1]' do
137
+ Ward::Errors.format_inclusive_list([1]).should == '1'
138
+ end
139
+
140
+ it 'should return "1 or 2" when given [1, 2]' do
141
+ Ward::Errors.format_inclusive_list([1, 2]).should == '1 and 2'
142
+ end
143
+
144
+ it 'should return "1, 2, or 3" when given [1, 2, 3]' do
145
+ Ward::Errors.format_inclusive_list([1, 2, 3]).should == '1, 2, and 3'
146
+ end
147
+ end
148
+
149
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe Ward::Matchers::Acceptance do
4
+
5
+ it 'should be registered with :accepted' do
6
+ matcher = Ward::Matchers.matchers[:accepted]
7
+ matcher.should == Ward::Matchers::Acceptance
8
+ end
9
+
10
+ #
11
+ # matches?
12
+ #
13
+
14
+ # #matches? tests can be found in features/acceptance_matcher.feature
15
+
16
+ end
@@ -0,0 +1,57 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe Ward::Matchers::CloseTo do
4
+
5
+ it 'should be registered with :close_to' do
6
+ matcher = Ward::Matchers.matchers[:close_to]
7
+ matcher.should == Ward::Matchers::CloseTo
8
+ end
9
+
10
+ #
11
+ # initialize
12
+ #
13
+
14
+ describe '#initialize' do
15
+ describe 'when given a numeric as the first and second argument' do
16
+ it 'should not raise an error' do
17
+ running_this = lambda { Ward::Matchers::CloseTo.new(1, 1) }
18
+ running_this.should_not raise_exception
19
+ end
20
+ end
21
+
22
+ describe 'when given a numeric as neither argument' do
23
+ it 'should raise an error' do
24
+ running_this = lambda { Ward::Matchers::CloseTo.new('', '') }
25
+ running_this.should raise_exception(ArgumentError)
26
+ end
27
+ end
28
+
29
+ describe 'when given a numeric as the first, but not second, argument' do
30
+ it 'should raise an error' do
31
+ running_this = lambda { Ward::Matchers::CloseTo.new(1, '') }
32
+ running_this.should raise_exception(ArgumentError)
33
+ end
34
+ end
35
+
36
+ describe 'when given a numeric as the second, but not first, argument' do
37
+ it 'should raise an error' do
38
+ running_this = lambda { Ward::Matchers::CloseTo.new('', 1) }
39
+ running_this.should raise_exception(ArgumentError)
40
+ end
41
+ end
42
+
43
+ describe 'when given only one argument' do
44
+ it 'should raise an error' do
45
+ running_this = lambda { Ward::Matchers::CloseTo.new(1) }
46
+ running_this.should raise_exception(ArgumentError)
47
+ end
48
+ end
49
+ end
50
+
51
+ #
52
+ # matches?
53
+ #
54
+
55
+ # #matches? tests can be found in features/close_to_matcher.feature
56
+
57
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe Ward::Matchers::EqualTo do
4
+
5
+ it 'should be registered with :equal_to' do
6
+ matcher = Ward::Matchers.matchers[:equal_to]
7
+ matcher.should == Ward::Matchers::EqualTo
8
+ end
9
+
10
+ #
11
+ # matches?
12
+ #
13
+
14
+ # #matches? tests can be found in features/equal_to_matcher.feature
15
+
16
+ end
@@ -0,0 +1,175 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe Ward::Matchers::Has do
4
+
5
+ it 'should be registered with :has' do
6
+ matcher = Ward::Matchers.matchers[:has]
7
+ matcher.should == Ward::Matchers::Has
8
+ end
9
+
10
+ it 'should be registered with :have' do
11
+ matcher = Ward::Matchers.matchers[:have]
12
+ matcher.should == Ward::Matchers::Has
13
+ end
14
+
15
+ #
16
+ # Collections.
17
+ #
18
+
19
+ describe 'when setting a collection' do
20
+ before(:all) do
21
+ @matcher = Ward::Matchers::Has.new(1)
22
+ @return = @matcher.author
23
+ end
24
+
25
+ it 'should return the matcher' do
26
+ @return.should == @matcher
27
+ end
28
+
29
+ it 'should set the collection name' do
30
+ @matcher.collection_name.should == :author
31
+ end
32
+ end
33
+
34
+ #
35
+ # Relativities.
36
+ #
37
+
38
+ [:lt, :less_than, :fewer_than].each do |method|
39
+ describe "##{method}" do
40
+ # lt sets the relativity to :lte, and decreases the expectation by
41
+ # such that is is within the :lte range.
42
+ before(:all) { @method, @relativity = method, :lte }
43
+
44
+ it_should_behave_like 'Has matcher relativity method'
45
+
46
+ it 'should set the expected value' do
47
+ @matcher.expected.should == 4
48
+ end
49
+ end
50
+ end
51
+
52
+ [:lte, :at_most].each do |method|
53
+ describe "##{method}" do
54
+ before(:all) { @method, @relativity = method, :lte }
55
+
56
+ it_should_behave_like 'Has matcher relativity method'
57
+
58
+ it 'should set the expected value' do
59
+ @matcher.expected.should == 5
60
+ end
61
+ end
62
+ end
63
+
64
+ [:eql, :exactly].each do |method|
65
+ describe "##{method}" do
66
+ before(:all) { @method, @relativity = method, :eql }
67
+
68
+ it_should_behave_like 'Has matcher relativity method'
69
+
70
+ it 'should set the expected value' do
71
+ @matcher.expected.should == 5
72
+ end
73
+ end
74
+ end
75
+
76
+ [:gte, :at_least].each do |method|
77
+ describe "##{method}" do
78
+ before(:all) { @method, @relativity = method, :gte }
79
+
80
+ it_should_behave_like 'Has matcher relativity method'
81
+
82
+ it 'should set the expected value' do
83
+ @matcher.expected.should == 5
84
+ end
85
+ end
86
+ end
87
+
88
+ [:gt, :greater_than, :more_than].each do |method|
89
+ # gt sets the relativity to :gte, and increases the expectation by 1 such
90
+ # that is is within the :gte range.
91
+ describe "##{method}" do
92
+ before(:all) { @method, @relativity = method, :gte }
93
+
94
+ it_should_behave_like 'Has matcher relativity method'
95
+
96
+ it 'should set the expected value' do
97
+ @matcher.expected.should == 6
98
+ end
99
+ end
100
+ end
101
+
102
+ describe '#between' do
103
+ before(:all) { @method, @relativity = :between, :between }
104
+
105
+ describe 'when given a range as the expectation' do
106
+ before(:all) { @expectation = [(1..5)] }
107
+
108
+ it_should_behave_like 'Has matcher relativity method'
109
+
110
+ it 'should set the expected value' do
111
+ @matcher.expected.should == (1..5)
112
+ end
113
+ end
114
+
115
+ describe 'when given a two numerics as the expectation' do
116
+ before(:all) { @expectation = [1, 5] }
117
+
118
+ it_should_behave_like 'Has matcher relativity method'
119
+
120
+ it 'should set the expected value' do
121
+ @matcher.expected.should == (1..5)
122
+ end
123
+ end
124
+
125
+ it 'should raise an error when given a single numeric' do
126
+ running_this = lambda { Ward::Matchers::Has.new.between(1) }
127
+ running_this.should raise_exception(ArgumentError, /upper boundary/)
128
+ end
129
+ end
130
+
131
+ it 'should raise an error if setting a relativity when one is already set' do
132
+ matcher = Ward::Matchers::Has.new.at_least(1)
133
+ running_this = lambda { matcher.at_most(6) }
134
+ running_this.should raise_exception(RuntimeError, /already set/)
135
+ end
136
+
137
+ #
138
+ # matches?
139
+ #
140
+
141
+ describe '#matches?' do
142
+ # Detailed #matches? tests can be found in features/has_matcher*.feature
143
+
144
+ it 'should raise an error if the collection is nil' do
145
+ matcher = Ward::Matchers::Has.new.eql(5)
146
+
147
+ running_this = lambda { matcher.should pass_matcher_with(nil) }
148
+ running_this.should raise_exception(RuntimeError, /not a collection/)
149
+ end
150
+
151
+ it 'should call #length if available' do
152
+ actual = mock()
153
+ actual.should_receive(:length).once.and_return(5)
154
+ actual.should_not_receive(:size)
155
+
156
+ Ward::Matchers::Has.new.eql(5).should pass_matcher_with(actual)
157
+ end
158
+
159
+ it 'should call #size if #length is not available' do
160
+ actual = mock()
161
+ actual.should_receive(:size).once.and_return(5)
162
+
163
+ Ward::Matchers::Has.new.eql(5).should pass_matcher_with(actual)
164
+ end
165
+
166
+ it 'should raise an error if the collection does not respond to ' \
167
+ '#size or #length' do
168
+ matcher = Ward::Matchers::Has.new.eql(5)
169
+
170
+ running_this = lambda { matcher.should pass_matcher_with(mock()) }
171
+ running_this.should raise_exception(RuntimeError, /not a collection/)
172
+ end
173
+ end # matches?
174
+
175
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe Ward::Matchers::Include do
4
+
5
+ it 'should be registered with :included_in' do
6
+ matcher = Ward::Matchers.matchers[:included_in]
7
+ matcher.should == Ward::Matchers::Include
8
+ end
9
+
10
+ it 'should be registered with :one_of' do
11
+ matcher = Ward::Matchers.matchers[:one_of]
12
+ matcher.should == Ward::Matchers::Include
13
+ end
14
+
15
+ #
16
+ # initialize
17
+ #
18
+
19
+ describe '#initialize' do
20
+ describe 'when given an object which responds to #include?' do
21
+ it 'should not raise an error' do
22
+ running_this = lambda { Ward::Matchers::Include.new([]) }
23
+ running_this.should_not raise_exception
24
+ end
25
+ end
26
+
27
+ describe 'when given an object which does not respond to #include?' do
28
+ it 'should raise an ArgumentError' do
29
+ running_this = lambda { Ward::Matchers::Include.new(nil) }
30
+ running_this.should raise_exception(ArgumentError)
31
+ end
32
+ end
33
+ end
34
+
35
+ #
36
+ # matches?
37
+ #
38
+
39
+ # #matches? tests can be found in features/include_matcher.feature
40
+
41
+ end