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,5 @@
1
+ README.markdown
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,28 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+ Icon*
4
+
5
+ ## TEXTMATE
6
+ *.tmproj
7
+ tmtags
8
+
9
+ ## EMACS
10
+ *~
11
+ \#*
12
+ .\#*
13
+
14
+ ## VIM
15
+ *.swp
16
+
17
+ ## RUBINIUS
18
+ *.rbc
19
+
20
+ ## PROJECT::GENERAL
21
+ .yardoc
22
+ coverage
23
+ doc
24
+ measurements
25
+ pkg
26
+ rdoc
27
+
28
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Anthony Williams
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,99 @@
1
+ # Ward
2
+
3
+ _Ward is not ready for production use just yet. 0.1 should be considered a preview release only._
4
+
5
+ ## What Is It?
6
+
7
+ Object validation inspired by RSpec. As it turns out, Ward looks very little like RSpec, but that's where the inspiration came from nonetheless.
8
+
9
+ The aim is to provide an expressive DSL which allows you to compose validations without the need to create special validation methods.
10
+
11
+ class Person
12
+ include Ward::Validation
13
+
14
+ validate do |person|
15
+ person.subdomain.length.is(2..50)
16
+ person.has.at_least(5).posts
17
+ person.owner.name.is("Michael Scarn")
18
+ end
19
+ end
20
+
21
+ Note: The syntax described above isn't currently supported, though you can achieve a very similar end result with 0.1:
22
+
23
+ class Person
24
+ cattr_accessor :validators
25
+
26
+ # ...
27
+ end
28
+
29
+ Person.validators = Ward::ValidationSet.build do |person|
30
+ person.subdomain.length.is(2..50)
31
+ person.has.at_least(5).posts
32
+ person.owner.name.is("Michael Scarn")
33
+ end
34
+
35
+ Person.validators.valid?(Person.new)
36
+ # => false
37
+
38
+ Person.validators.validate(Person.new)
39
+ # => Ward::Support::Result
40
+
41
+ ### Current Status & Roadmap
42
+
43
+ **Planned for 0.2:**
44
+
45
+ * Add the Ward::Validation module allowing for more conventional validation
46
+ (defining the validations _on_ the object to be validated, as is the case
47
+ with ActiveRecord and dm-validations).
48
+
49
+ * Documentation is severely lacking, and will be improved. In the
50
+ meantime, Ward makes extensive use of Cucumber features (/features) where
51
+ you will find many examples of how to use the library in your own
52
+ applications.
53
+
54
+ * Ward error messages are currently in English only, but full i18n support is
55
+ planned.
56
+
57
+ **Planned for 0.3:**
58
+
59
+ * Compatibility modules will be added to support at least ActiveRecord and
60
+ DataMapper; and perhaps Sequel and other ORMs after that, depending on
61
+ demand.
62
+
63
+ **Later:**
64
+
65
+ * The DSL is subject to change prior to 1.0.
66
+
67
+ ### Compatibility
68
+
69
+ Ward specs are run against:
70
+
71
+ * Ruby (MRI) 1.8.6 p399,
72
+ * Ruby (MRI) 1.8.7 p249,
73
+ * Ruby (YARV) 1.9.1 p378,
74
+ * JRuby 1.4.0,
75
+ * Rubinius RC3.
76
+
77
+ Ward depends on ActiveSupport 3.0 to provide support for inflections in error messages, and to add Ruby 1.9-style String interpolation. However, only the bare minimum is included from ActiveSupport in order to minimise the impact on your runtime environment (see `lib/ward.rb` for specifics).
78
+
79
+ ### Note on Patches/Pull Requests
80
+
81
+ * Fork the project, taking care not to get any in your eyes.
82
+
83
+ * Make your feature addition or bug fix.
84
+
85
+ * Add tests for it. This is especially important not only because it helps
86
+ ensure that I don't unintentionally break it in a future version, but also
87
+ since it appeases Phyllis --- the goddess of Cucumbers --- who has been
88
+ known to rain showers of fresh vegetables on those who don't write tests.
89
+
90
+ * Commit, but do not mess with the Rakefile, VERSION, or history. If you want
91
+ to have your own version, that is fine, but bump version in a commit by
92
+ itself so that I can ignore it when I pull.
93
+
94
+ * Send me a pull request. Bonus points for topic branches. But we all know
95
+ everything is made up and the points don't matter.
96
+
97
+ ### Copyright
98
+
99
+ Copyright (c) 2010 Anthony Williams, MIT Licensed.
@@ -0,0 +1,47 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+
4
+ require File.expand_path('../lib/ward/version', __FILE__)
5
+
6
+ CLOBBER.include ['pkg', '*.gem', 'doc', 'coverage', 'measurements']
7
+
8
+ begin
9
+ require 'jeweler'
10
+ Jeweler::Tasks.new do |gem|
11
+ gem.name = 'ward'
12
+ gem.summary = 'Ward'
13
+ gem.homepage = 'http://github.com/antw/ward'
14
+ gem.description = 'Object validation inspired by RSpec.'
15
+
16
+ gem.author = 'Anthony Williams'
17
+ gem.email = 'hi@antw.me'
18
+
19
+ gem.platform = Gem::Platform::RUBY
20
+ gem.has_rdoc = false
21
+
22
+ # Dependencies.
23
+ gem.add_dependency 'activesupport', '>= 3.0.0.beta'
24
+
25
+ # Development dependencies.
26
+ gem.add_development_dependency 'rspec', '>= 1.3.0'
27
+ gem.add_development_dependency 'cucumber', '>= 0.3'
28
+ gem.add_development_dependency 'yard', '>= 0.5'
29
+
30
+ gem.post_install_message =
31
+ "************************************************************\n" \
32
+ "\n" \
33
+ "Thank you for installing ward-#{Ward::VERSION}\n" \
34
+ "\n" \
35
+ "Please note that 0.1 is a preview release and considered\n" \
36
+ "unsuitable for use in a production environment.\n" \
37
+ "\n" \
38
+ "************************************************************\n" \
39
+ end
40
+
41
+ Jeweler::GemcutterTasks.new
42
+ rescue LoadError
43
+ puts 'Jeweler (or a dependency) not available. Install it with: gem ' \
44
+ 'install jeweler'
45
+ end
46
+
47
+ FileList['tasks/**/*.rake'].each { |task| import task }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,78 @@
1
+ Feature: Acceptance matcher
2
+ In order to validate that a user has accepted an attribute
3
+ I want to be able to ensure collections have a certain number of members
4
+
5
+ Background:
6
+ Given a class with a 'terms' attribute
7
+
8
+ Scenario Outline: When the attribute value is accepted
9
+ When using a validation set like
10
+ """
11
+ object.terms.is.accepted
12
+
13
+ """
14
+ And the instance 'terms' attribute is '<value>'
15
+ Then the validation set should pass
16
+
17
+ Examples:
18
+ | value |
19
+ | true |
20
+ | "true" |
21
+ | "t" |
22
+ | "yes" |
23
+ | "y" |
24
+ | 1 |
25
+ | "1" |
26
+
27
+ Scenario Outline: When the attribute value is not accepted
28
+ When using a validation set like
29
+ """
30
+ object.terms.is.accepted
31
+
32
+ """
33
+ And the instance 'terms' attribute is '<value>'
34
+ Then the validation set should fail
35
+
36
+ Examples:
37
+ | value |
38
+ | false |
39
+ | nil |
40
+ | "" |
41
+ | "false" |
42
+ | "f" |
43
+ | "no" |
44
+ | "n" |
45
+ | 0 |
46
+ | "0" |
47
+
48
+ Scenario: When setting a single custom expectation value
49
+ When using a validation set like
50
+ """
51
+ object.terms.is.accepted("absolutely")
52
+
53
+ """
54
+ And the instance 'terms' attribute is 'absolutely'
55
+ Then the validation set should pass
56
+
57
+ Scenario: When setting a single custom expectation value and the attribute is not accepted
58
+ When using a validation set like
59
+ """
60
+ object.terms.is.accepted("absolutely")
61
+
62
+ """
63
+ And the instance 'terms' attribute is 'yes'
64
+ Then the validation set should fail
65
+
66
+ Scenario Outline: When setting several custom expectation values
67
+ When using a validation set like
68
+ """
69
+ object.terms.is.accepted(%w( absolutely certainly ))
70
+
71
+ """
72
+ And the instance 'terms' attribute is '<value>'
73
+ Then the validation set should pass
74
+
75
+ Examples:
76
+ | value |
77
+ | absolutely |
78
+ | certainly |
@@ -0,0 +1,13 @@
1
+ Feature: Validating objects with an "attribute" keyword
2
+ In order validate attributes whose name conflicts with DSL methods
3
+ I want to be able to use the attribute keyword
4
+
5
+ Scenario: With no arguments, a matcher, and a valid value
6
+ Given a class with a 'message' attribute
7
+ When using a validation set like
8
+ """
9
+ object.attribute(:message).is.equal_to('Wow! Signal')
10
+
11
+ """
12
+ And the instance 'message' attribute is 'Wow! Signal'
13
+ Then the validation set should pass
@@ -0,0 +1,130 @@
1
+ Feature: CloseTo matcher
2
+ In order to validate values which may vary slightly at runtime
3
+ I want to be able to use the close_to matcher to set an acceptable range of values
4
+
5
+ Background:
6
+ Given a class with an 'estimate' attribute
7
+
8
+ Scenario Outline: When the attribute value is within the specified integer delta
9
+ When using a validation set like
10
+ """
11
+ object.estimate.is.close_to(50, 10)
12
+
13
+ """
14
+ And the instance 'estimate' attribute is '<value>'
15
+ Then the validation set should pass
16
+
17
+ Examples:
18
+ | value |
19
+ | 40 |
20
+ | 50 |
21
+ | 55.0 |
22
+ | 60 |
23
+
24
+ Scenario Outline: When the attribute value is outside the specified integer delta
25
+ When using a validation set like
26
+ """
27
+ object.estimate.is.close_to(50, 10)
28
+
29
+ """
30
+ And the instance 'estimate' attribute is '<value>'
31
+ Then the validation set should fail
32
+ And the error on 'estimate' should be 'Estimate should be within 10 of 50'
33
+
34
+ Examples:
35
+ | value |
36
+ | 0 |
37
+ | 39 |
38
+ | 61 |
39
+
40
+ Scenario Outline: When the attribute value is within the specified float delta
41
+ When using a validation set like
42
+ """
43
+ object.estimate.is.close_to(2, 0.5)
44
+
45
+ """
46
+ And the instance 'estimate' attribute is '<value>'
47
+ Then the validation set should pass
48
+
49
+ Examples:
50
+ | value |
51
+ | 1.5 |
52
+ | 2 |
53
+ | 2.0 |
54
+ | 2.5 |
55
+
56
+ Scenario Outline: When the attribute value is outside the specified float delta
57
+ When using a validation set like
58
+ """
59
+ object.estimate.is.close_to(2, 0.5)
60
+
61
+ """
62
+ And the instance 'estimate' attribute is '<value>'
63
+ Then the validation set should fail
64
+ And the error on 'estimate' should be 'Estimate should be within 0.5 of 2'
65
+
66
+ Examples:
67
+ | value |
68
+ | 0 |
69
+ | 1.49 |
70
+ | 2.51 |
71
+
72
+ Scenario Outline: When the attribute value is a date inside the delta
73
+ When using a validation set like
74
+ """
75
+ object.estimate.is.close_to(Date.civil(2010, 3, 1), 1)
76
+
77
+ """
78
+ And the instance 'estimate' attribute is '<value>'
79
+ Then the validation set should pass
80
+
81
+ Examples:
82
+ | value |
83
+ | Date.civil(2010, 3, 1) - 1 |
84
+ | Date.civil(2010, 3, 1) |
85
+ | Date.civil(2010, 3, 1) + 1 |
86
+
87
+ Scenario Outline: When the attribute value is a date outside the delta
88
+ When using a validation set like
89
+ """
90
+ object.estimate.is.close_to(Date.civil(2010, 3, 1), 1)
91
+
92
+ """
93
+ And the instance 'estimate' attribute is '<value>'
94
+ Then the validation set should fail
95
+ And the error on 'estimate' should be 'Estimate should be within 1 of 2010-03-01'
96
+
97
+ Examples:
98
+ | value |
99
+ | Date.civil(2010, 3, 1) - 2 |
100
+ | Date.civil(2010, 3, 1) + 2 |
101
+
102
+ Scenario Outline: When the attribute value is a date inside the delta
103
+ When using a validation set like
104
+ """
105
+ object.estimate.is.close_to(Time.parse('2010-03-01 12:00'), 30)
106
+
107
+ """
108
+ And the instance 'estimate' attribute is '<value>'
109
+ Then the validation set should pass
110
+
111
+ Examples:
112
+ | value |
113
+ | Time.parse('2010-03-01 12:00') - 30 |
114
+ | Time.parse('2010-03-01 12:00') |
115
+ | Time.parse('2010-03-01 12:00') + 30 |
116
+
117
+ Scenario Outline: When the attribute value is a date outside the delta
118
+ When using a validation set like
119
+ """
120
+ object.estimate.is.close_to(Time.parse('2010-03-01 12:00'), 30)
121
+
122
+ """
123
+ And the instance 'estimate' attribute is '<value>'
124
+ Then the validation set should fail
125
+ And the error on 'estimate' should be '/^Estimate should be within 30 of/'
126
+
127
+ Examples:
128
+ | value |
129
+ | Time.parse('2010-03-01 12:00') - 31 |
130
+ | Time.parse('2010-03-01 12:00') + 31 |
@@ -0,0 +1,47 @@
1
+ Feature: Validating values which require an argument or block
2
+
3
+ Scenario: Supplying an argument to a context
4
+ Given the class has behaviour like
5
+ """
6
+ def name(yes = false)
7
+ if yes then "True" else "False" end
8
+ end
9
+
10
+ """
11
+ And using a validation set like
12
+ """
13
+ object.name(true).equal_to("True")
14
+
15
+ """
16
+ Then the validation set should pass
17
+
18
+ Scenario: Supplying an argument to a context when using scenarios
19
+ Given the class has behaviour like
20
+ """
21
+ def name(yes = false)
22
+ if yes then "True" else "False" end
23
+ end
24
+
25
+ """
26
+ And using a validation set like
27
+ """
28
+ object.name(true).equal_to("True")
29
+ object.name(false).equal_to("False").scenario(:negative)
30
+
31
+ """
32
+ Then the validation set should pass when using the 'negative' scenario
33
+
34
+ Scenario: Supplying a block to a context
35
+ Given the class has behaviour like
36
+ """
37
+ def name(&block)
38
+ block.call.reverse
39
+ end
40
+
41
+ """
42
+ And using a validation set like
43
+ """
44
+ object.name { "My value" }.equal_to("eulav yM")
45
+
46
+ """
47
+ Then the validation set should pass