ward 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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