bahuvrihi-tap 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/History +69 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +119 -0
  4. data/bin/tap +114 -0
  5. data/cmd/console.rb +42 -0
  6. data/cmd/destroy.rb +16 -0
  7. data/cmd/generate.rb +16 -0
  8. data/cmd/run.rb +126 -0
  9. data/doc/Class Reference +362 -0
  10. data/doc/Command Reference +153 -0
  11. data/doc/Tutorial +237 -0
  12. data/lib/tap.rb +32 -0
  13. data/lib/tap/app.rb +720 -0
  14. data/lib/tap/constants.rb +8 -0
  15. data/lib/tap/env.rb +640 -0
  16. data/lib/tap/file_task.rb +547 -0
  17. data/lib/tap/generator/base.rb +109 -0
  18. data/lib/tap/generator/destroy.rb +37 -0
  19. data/lib/tap/generator/generate.rb +61 -0
  20. data/lib/tap/generator/generators/command/command_generator.rb +21 -0
  21. data/lib/tap/generator/generators/command/templates/command.erb +32 -0
  22. data/lib/tap/generator/generators/config/config_generator.rb +26 -0
  23. data/lib/tap/generator/generators/config/templates/doc.erb +12 -0
  24. data/lib/tap/generator/generators/config/templates/nodoc.erb +8 -0
  25. data/lib/tap/generator/generators/file_task/file_task_generator.rb +27 -0
  26. data/lib/tap/generator/generators/file_task/templates/file.txt +11 -0
  27. data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
  28. data/lib/tap/generator/generators/file_task/templates/task.erb +33 -0
  29. data/lib/tap/generator/generators/file_task/templates/test.erb +29 -0
  30. data/lib/tap/generator/generators/root/root_generator.rb +55 -0
  31. data/lib/tap/generator/generators/root/templates/Rakefile +86 -0
  32. data/lib/tap/generator/generators/root/templates/gemspec +27 -0
  33. data/lib/tap/generator/generators/root/templates/tapfile +8 -0
  34. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +3 -0
  35. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +5 -0
  36. data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
  37. data/lib/tap/generator/generators/task/task_generator.rb +27 -0
  38. data/lib/tap/generator/generators/task/templates/task.erb +14 -0
  39. data/lib/tap/generator/generators/task/templates/test.erb +21 -0
  40. data/lib/tap/generator/manifest.rb +14 -0
  41. data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
  42. data/lib/tap/patches/rake/testtask.rb +55 -0
  43. data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
  44. data/lib/tap/patches/ruby19/parsedate.rb +16 -0
  45. data/lib/tap/root.rb +581 -0
  46. data/lib/tap/support/aggregator.rb +55 -0
  47. data/lib/tap/support/assignments.rb +172 -0
  48. data/lib/tap/support/audit.rb +418 -0
  49. data/lib/tap/support/batchable.rb +47 -0
  50. data/lib/tap/support/batchable_class.rb +107 -0
  51. data/lib/tap/support/class_configuration.rb +194 -0
  52. data/lib/tap/support/command_line.rb +98 -0
  53. data/lib/tap/support/comment.rb +270 -0
  54. data/lib/tap/support/configurable.rb +114 -0
  55. data/lib/tap/support/configurable_class.rb +296 -0
  56. data/lib/tap/support/configuration.rb +122 -0
  57. data/lib/tap/support/constant.rb +70 -0
  58. data/lib/tap/support/constant_utils.rb +127 -0
  59. data/lib/tap/support/declarations.rb +111 -0
  60. data/lib/tap/support/executable.rb +111 -0
  61. data/lib/tap/support/executable_queue.rb +82 -0
  62. data/lib/tap/support/framework.rb +71 -0
  63. data/lib/tap/support/framework_class.rb +199 -0
  64. data/lib/tap/support/instance_configuration.rb +147 -0
  65. data/lib/tap/support/lazydoc.rb +428 -0
  66. data/lib/tap/support/manifest.rb +89 -0
  67. data/lib/tap/support/run_error.rb +39 -0
  68. data/lib/tap/support/shell_utils.rb +71 -0
  69. data/lib/tap/support/summary.rb +30 -0
  70. data/lib/tap/support/tdoc.rb +404 -0
  71. data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
  72. data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
  73. data/lib/tap/support/templater.rb +180 -0
  74. data/lib/tap/support/validation.rb +410 -0
  75. data/lib/tap/support/versions.rb +97 -0
  76. data/lib/tap/task.rb +259 -0
  77. data/lib/tap/tasks/dump.rb +56 -0
  78. data/lib/tap/tasks/rake.rb +93 -0
  79. data/lib/tap/test.rb +37 -0
  80. data/lib/tap/test/env_vars.rb +29 -0
  81. data/lib/tap/test/file_methods.rb +377 -0
  82. data/lib/tap/test/script_methods.rb +144 -0
  83. data/lib/tap/test/subset_methods.rb +420 -0
  84. data/lib/tap/test/tap_methods.rb +237 -0
  85. data/lib/tap/workflow.rb +187 -0
  86. metadata +145 -0
@@ -0,0 +1,38 @@
1
+ require 'rdoc/generators/html_generator'
2
+
3
+ # Defines a specialized generator so it can be called for using a --fmt option.
4
+ class TDocHTMLGenerator < Generators::HTMLGenerator # :nodoc:
5
+ end
6
+
7
+ module Generators # :nodoc:
8
+ const_set(:RubyToken, RDoc::RubyToken)
9
+
10
+ class HtmlClass < ContextUser # :nodoc:
11
+ alias tdoc_original_value_hash value_hash
12
+
13
+ def value_hash
14
+ # split attributes into configurations and regular attributes
15
+ configurations, attributes = @context.attributes.partition do |attribute|
16
+ attribute.kind_of?(Tap::Support::TDoc::ConfigAttr)
17
+ end
18
+
19
+ # set the context attributes to JUST the regular
20
+ # attributes and process as usual.
21
+ @context.attributes.clear.concat attributes
22
+ values = tdoc_original_value_hash
23
+
24
+ # set the context attributes to the configurations
25
+ # and echo the regular processing to produce a list
26
+ # of configurations
27
+ @context.attributes.clear.concat configurations
28
+ @context.sections.each_with_index do |section, i|
29
+ secdata = values["sections"][i]
30
+
31
+ al = build_attribute_list(section)
32
+ secdata["configurations"] = al unless al.empty?
33
+ end
34
+
35
+ values
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ require 'rdoc/generators/template/html/html'
2
+
3
+ #
4
+ # Add a template for documenting configurations. Do so by inserting in the
5
+ # template into the content regions used to template html.
6
+ # (see 'rdoc/generators/html_generator' line 864)
7
+ #
8
+ [
9
+ RDoc::Page::BODY,
10
+ RDoc::Page::FILE_PAGE,
11
+ RDoc::Page::METHOD_LIST].each do |content|
12
+
13
+ # this substitution method duplicates the attribute template for configurations
14
+ # (see rdoc\generators\template\html line 523)
15
+ #
16
+ #IF:attributes
17
+ # <div id="attribute-list">
18
+ # <h3 class="section-bar">Attributes</h3>
19
+ #
20
+ # <div class="name-list">
21
+ # <table>
22
+ #START:attributes
23
+ # <tr class="top-aligned-row context-row">
24
+ # <td class="context-item-name">%name%</td>
25
+ #IF:rw
26
+ # <td class="context-item-value">&nbsp;[%rw%]&nbsp;</td>
27
+ #ENDIF:rw
28
+ #IFNOT:rw
29
+ # <td class="context-item-value">&nbsp;&nbsp;</td>
30
+ #ENDIF:rw
31
+ # <td class="context-item-desc">%a_desc%</td>
32
+ # </tr>
33
+ #END:attributes
34
+ # </table>
35
+ # </div>
36
+ # </div>
37
+ #ENDIF:attributes
38
+ #
39
+ content.gsub!(/IF:attributes.*?ENDIF:attributes/m) do |match|
40
+ match + "\n\n" + match.gsub(/attributes/, 'configurations').gsub(/Attributes/, 'Configurations')
41
+ end
42
+ end
@@ -0,0 +1,180 @@
1
+ require 'ostruct'
2
+ require 'erb'
3
+
4
+ module Tap
5
+ module Support
6
+
7
+ # Templater is a convenience class for creating ERB templates. As
8
+ # an OpenStruct, attributes can be assigned/unassigned at will to
9
+ # a Templater. When the template is built, all the method of
10
+ # Templater (and hence all the assigned attributes) are available
11
+ # in the template.
12
+ #
13
+ # t = Templater.new( "key: <%= value %>")
14
+ # t.value = "default"
15
+ # t.build # => "key: default"
16
+ #
17
+ # t.value = "another"
18
+ # t.build # => "key: another"
19
+ #
20
+ # Templater includes the Templater::Utils utility methods.
21
+ #
22
+ # === ERB Redirection
23
+ #
24
+ # Templater hooks into the ERB templating mechanism by providing itself
25
+ # as the ERB output target (_erbout). ERB concatenates each line of an
26
+ # ERB template to _erbout, as can be seen when you look at the src code
27
+ # evaluated by ERB:
28
+ #
29
+ # e = ERB.new("<%= 1 + 2 %>")
30
+ # e.src # => "_erbout = ''; _erbout.concat(( 1 + 2 ).to_s); _erbout"
31
+ #
32
+ # By setting itself as _erbout, instances of Templater can redirect the
33
+ # output to a temporary target which can then be used in string
34
+ # transformations. For example, redirection allows indentation of
35
+ # nested content:
36
+ #
37
+ # template = %Q{
38
+ # # Un-nested content
39
+ # <% redirect do |target| %>
40
+ # # Nested content
41
+ # <% module_nest("Nesting::Module") { target } %>
42
+ # <% end %>
43
+ # }
44
+ #
45
+ # t = Templater.new(template)
46
+ # t.build
47
+ # # => %Q{
48
+ # # # Un-nested content
49
+ # # module Nesting
50
+ # # module Module
51
+ # # # Nested content
52
+ # #
53
+ # # end
54
+ # # end}
55
+ #
56
+ class Templater < OpenStruct
57
+
58
+ # Utility methods for Templater; mostly string manipulations
59
+ # useful in creating documentation.
60
+ module Utils
61
+
62
+ # yamlize converts the object to YAML (using to_yaml), omitting
63
+ # the header and final newline:
64
+ #
65
+ # {'key' => 'value'}.to_yaml # => "--- \nkey: value\n"
66
+ # yamlize {'key' => 'value'} # => "key: value"
67
+ def yamlize(object)
68
+ object.to_yaml[5...-1]
69
+ end
70
+
71
+ # Nest the return of the block in the nesting lines.
72
+ #
73
+ # nest([["\nmodule Some", "end\n"],["module Nested", "end"]]) { "class Const\nend" }
74
+ # # => %Q{
75
+ # # module Some
76
+ # # module Nested
77
+ # # class Const
78
+ # # end
79
+ # # end
80
+ # # end
81
+ # # }
82
+ #
83
+ def nest(nesting, indent=" ", line_sep="\n")
84
+ content = yield
85
+ return content if nesting.empty?
86
+
87
+ depth = nesting.length
88
+ lines = [indent * depth + content.gsub(/#{line_sep}/, line_sep + indent * depth)]
89
+
90
+ nesting.reverse_each do |(start_line, end_line)|
91
+ depth -= 1
92
+ lines.unshift(indent * depth + start_line)
93
+ lines << (indent * depth + end_line)
94
+ end
95
+
96
+ lines.join(line_sep)
97
+ end
98
+
99
+ # Nest the return of the block in the nesting module.
100
+ #
101
+ # module_nest('Some::Nested') { "class Const\nend" }
102
+ # # => %Q{
103
+ # # module Some
104
+ # # module Nested
105
+ # # class Const
106
+ # # end
107
+ # # end
108
+ # # end
109
+ # # }.strip
110
+ #
111
+ def module_nest(const_name, indent=" ", line_sep="\n")
112
+ nesting = const_name.split(/::/).collect do |name|
113
+ ["module #{name}", "end"]
114
+ end
115
+
116
+ nest(nesting, indent, line_sep) { yield }
117
+ end
118
+ end
119
+
120
+ include Utils
121
+
122
+ # Initialized a new Templater. An ERB or String may be provided as the
123
+ # template. If a String is provided, it will be used to initialize an
124
+ # ERB with a trim_mode of "<>".
125
+ def initialize(template, attributes={})
126
+ @template = case template
127
+ when ERB
128
+ if template.instance_variable_get(:@src).index('_erbout =') != 0
129
+ raise ArgumentError, "Templater does not work with ERB templates where eoutvar != '_erbout'"
130
+ end
131
+ template
132
+ when String then ERB.new(template, nil, "<>")
133
+ else raise ArgumentError, "cannot convert #{template.class} into an ERB template"
134
+ end
135
+
136
+ src = @template.instance_variable_get(:@src)
137
+ @template.instance_variable_set(:@src, "self." + src)
138
+
139
+ super(attributes)
140
+ end
141
+
142
+ # Returns self (not the underlying erbout storage that actually receives
143
+ # the output lines). In the ERB context, this method directs erb outputs
144
+ # to Templater#concat and into the redirect mechanism.
145
+ def _erbout
146
+ self
147
+ end
148
+
149
+ # Sets the underlying erbout storage to input.
150
+ def _erbout=(input)
151
+ @_erbout = input
152
+ end
153
+
154
+ # Redirects output of erb to the redirected_erbout string
155
+ # for the duration of the block. When redirect completes,
156
+ # the redirected_erbout is concatenated to the main
157
+ # erbout storage.
158
+ def redirect # :yields: redirected_erbout
159
+ current = @_erbout
160
+ @_erbout = ""
161
+ result = yield(@_erbout)
162
+ @_erbout = current
163
+ concat(result)
164
+ end
165
+
166
+ # Concatenates the specified input to the underlying erbout storage.
167
+ def concat(input)
168
+ @_erbout << input
169
+ end
170
+
171
+ # Build the template. All methods of self will be
172
+ # accessible in the template.
173
+ def build
174
+ @template.result(binding)
175
+ @_erbout
176
+ end
177
+
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,410 @@
1
+ module Tap
2
+ module Support
3
+
4
+ # Validation generates blocks for common validations and transformations of
5
+ # configurations set through Configurable. In general these blocks allow
6
+ # configurations to be set to objects of a particular class, or to a string
7
+ # that can be loaded as YAML into such an object.
8
+ #
9
+ # integer = Validation.integer
10
+ # integer.class # => Proc
11
+ # integer.call(1) # => 1
12
+ # integer.call('1') # => 1
13
+ # integer.call(nil) # => ValidationError
14
+ #
15
+ #--
16
+ # Note the unusual syntax for declaring constants that are blocks
17
+ # defined by lambda... ex:
18
+ #
19
+ # block = lambda {}
20
+ # CONST = block
21
+ #
22
+ # This syntax plays well with RDoc, which otherwise gets jacked
23
+ # when you do it all in one step.
24
+ #++
25
+ module Validation
26
+
27
+ # Raised when Validation blocks fail.
28
+ class ValidationError < ArgumentError
29
+ def initialize(input, validations)
30
+ super case
31
+ when validations.empty?
32
+ "no validations specified"
33
+ else
34
+ validation_str = PP.singleline_pp(validations, "")
35
+ PP.singleline_pp(input, "expected #{validation_str} but was: ")
36
+ end
37
+ end
38
+ end
39
+
40
+ # Raised when yamlization fails.
41
+ class YamlizationError < ArgumentError
42
+ def initialize(input, error)
43
+ super "#{error} ('#{input}')"
44
+ end
45
+ end
46
+
47
+ module_function
48
+
49
+ # Yaml conversion and checker. Valid if any of the validations
50
+ # match in a case statement. Otherwise raises an error.
51
+
52
+ # Returns input if any of the validations match any of the
53
+ # inputs, as in a case statement. Raises a ValidationError
54
+ # otherwise. For example:
55
+ #
56
+ # validate(10, [Integer, nil])
57
+ #
58
+ # Does the same as:
59
+ #
60
+ # case 10
61
+ # when Integer, nil then input
62
+ # else raise ValidationError.new(...)
63
+ # end
64
+ #
65
+ # Note the validations input must be an Array or nil;
66
+ # validate will raise an ArgumentError otherwise.
67
+ # All inputs are considered VALID if validations == nil.
68
+ def validate(input, validations)
69
+ case validations
70
+ when Array
71
+
72
+ case input
73
+ when *validations then input
74
+ else raise ValidationError.new(input, validations)
75
+ end
76
+
77
+ when nil then input
78
+ else raise ArgumentError.new("validations must be nil, or an array of valid inputs")
79
+ end
80
+ end
81
+
82
+ # Attempts to load the input as YAML. Raises a YamlizationError
83
+ # for errors.
84
+ def yamlize(input)
85
+ begin
86
+ YAML.load(input)
87
+ rescue
88
+ raise YamlizationError.new(input, $!.message)
89
+ end
90
+ end
91
+
92
+ # Returns a block that calls validate using the block input
93
+ # and the input validations. Raises an error if no validations
94
+ # are specified.
95
+ def check(*validations)
96
+ raise ArgumentError.new("no validations specified") if validations.empty?
97
+ lambda {|input| validate(input, validations) }
98
+ end
99
+
100
+ # Returns a block that loads input strings as YAML, then
101
+ # calls validate with the result and the input validations.
102
+ # Non-string inputs are not converted.
103
+ #
104
+ # b = yaml(Integer, nil)
105
+ # b.class # => Proc
106
+ # b.call(1) # => 1
107
+ # b.call("1") # => 1
108
+ # b.call(nil) # => nil
109
+ # b.call("str") # => ValidationError
110
+ #
111
+ # If no validations are specified, the result will be
112
+ # returned without validation.
113
+ def yaml(*validations)
114
+ lambda do |input|
115
+ res = input.kind_of?(String) ? yamlize(input) : input
116
+ validations.empty? ? res : validate(res, validations)
117
+ end
118
+ end
119
+
120
+ # Returns a block loads a String input as YAML then
121
+ # validates the result is valid using the input
122
+ # validations. If the input is not a String, the
123
+ # input is validated directly.
124
+ def yamlize_and_check(*validations)
125
+ lambda do |input|
126
+ input = yamlize(input) if input.kind_of?(String)
127
+ validate(input, validations)
128
+ end
129
+ end
130
+
131
+ # Returns a block that checks the input is a string.
132
+ # Moreover, strings are re-evaluated as string
133
+ # literals using %Q.
134
+ #
135
+ # string.class # => Proc
136
+ # string.call('str') # => 'str'
137
+ # string.call('\n') # => "\n"
138
+ # string.call("\n") # => "\n"
139
+ # string.call("%s") # => "%s"
140
+ # string.call(nil) # => ValidationError
141
+ # string.call(:sym) # => ValidationError
142
+ #
143
+ def string(); STRING; end
144
+ string_validation_block = lambda do |input|
145
+ input = validate(input, [String])
146
+ eval %Q{"#{input}"}
147
+ end
148
+ STRING = string_validation_block
149
+
150
+ # Same as string but allows nil. Note the special
151
+ # behavior of the nil string '~' -- rather than
152
+ # being treated as a string, it is processed as nil
153
+ # to be consistent with the other [class]_or_nil
154
+ # methods.
155
+ #
156
+ # string_or_nil.call('~') # => nil
157
+ # string_or_nil.call(nil) # => nil
158
+ def string_or_nil(); STRING_OR_NIL; end
159
+ string_or_nil_validation_block = lambda do |input|
160
+ input = validate(input, [String, nil])
161
+ case input
162
+ when nil, '~' then nil
163
+ else eval %Q{"#{input}"}
164
+ end
165
+ end
166
+ STRING_OR_NIL = string_or_nil_validation_block
167
+
168
+ # Returns a block that checks the input is a symbol.
169
+ # String inputs are loaded as yaml first.
170
+ #
171
+ # symbol.class # => Proc
172
+ # symbol.call(:sym) # => :sym
173
+ # symbol.call(':sym') # => :sym
174
+ # symbol.call(nil) # => ValidationError
175
+ # symbol.call('str') # => ValidationError
176
+ #
177
+ def symbol(); SYMBOL; end
178
+ SYMBOL = yamlize_and_check(Symbol)
179
+
180
+ # Same as symbol but allows nil:
181
+ #
182
+ # symbol_or_nil.call('~') # => nil
183
+ # symbol_or_nil.call(nil) # => nil
184
+ def symbol_or_nil(); SYMBOL_OR_NIL; end
185
+ SYMBOL_OR_NIL = yamlize_and_check(Symbol, nil)
186
+
187
+ # Returns a block that checks the input is true, false or nil.
188
+ # String inputs are loaded as yaml first.
189
+ #
190
+ # boolean.class # => Proc
191
+ # boolean.call(true) # => true
192
+ # boolean.call(false) # => false
193
+ # boolean.call(nil) # => nil
194
+ #
195
+ # boolean.call('true') # => true
196
+ # boolean.call('yes') # => true
197
+ # boolean.call('FALSE') # => false
198
+ #
199
+ # boolean.call(1) # => ValidationError
200
+ # boolean.call("str") # => ValidationError
201
+ #
202
+ def boolean(); BOOLEAN; end
203
+ BOOLEAN = yamlize_and_check(true, false, nil)
204
+
205
+ def switch(); SWITCH; end
206
+ SWITCH = yamlize_and_check(true, false, nil)
207
+
208
+ def flag(); FLAG; end
209
+ FLAG = yamlize_and_check(true, false, nil)
210
+
211
+ # Returns a block that checks the input is an array.
212
+ # String inputs are loaded as yaml first.
213
+ #
214
+ # array.class # => Proc
215
+ # array.call([1,2,3]) # => [1,2,3]
216
+ # array.call('[1, 2, 3]') # => [1,2,3]
217
+ # array.call(nil) # => ValidationError
218
+ # array.call('str') # => ValidationError
219
+ #
220
+ def array(); ARRAY; end
221
+ ARRAY = yamlize_and_check(Array)
222
+
223
+ # Same as array but allows nil:
224
+ #
225
+ # array_or_nil.call('~') # => nil
226
+ # array_or_nil.call(nil) # => nil
227
+ def array_or_nil(); ARRAY_OR_NIL; end
228
+ ARRAY_OR_NIL = yamlize_and_check(Array, nil)
229
+
230
+ def list(); LIST; end
231
+ list_block = lambda do |input|
232
+ if input.kind_of?(String)
233
+ input = case processed_input = yamlize(input)
234
+ when Array then processed_input
235
+ else input.split(/,/).collect {|arg| yamlize(arg) }
236
+ end
237
+ end
238
+
239
+ validate(input, [Array])
240
+ end
241
+ LIST = list_block
242
+
243
+ # Returns a block that checks the input is a hash.
244
+ # String inputs are loaded as yaml first.
245
+ #
246
+ # hash.class # => Proc
247
+ # hash.call({'key' => 'value'}) # => {'key' => 'value'}
248
+ # hash.call('key: value') # => {'key' => 'value'}
249
+ # hash.call(nil) # => ValidationError
250
+ # hash.call('str') # => ValidationError
251
+ #
252
+ def hash(); HASH; end
253
+ HASH = yamlize_and_check(Hash)
254
+
255
+ # Same as hash but allows nil:
256
+ #
257
+ # hash_or_nil.call('~') # => nil
258
+ # hash_or_nil.call(nil) # => nil
259
+ def hash_or_nil(); HASH_OR_NIL; end
260
+ HASH_OR_NIL = yamlize_and_check(Hash, nil)
261
+
262
+ # Returns a block that checks the input is an integer.
263
+ # String inputs are loaded as yaml first.
264
+ #
265
+ # integer.class # => Proc
266
+ # integer.call(1) # => 1
267
+ # integer.call('1') # => 1
268
+ # integer.call(1.1) # => ValidationError
269
+ # integer.call(nil) # => ValidationError
270
+ # integer.call('str') # => ValidationError
271
+ #
272
+ def integer(); INTEGER; end
273
+ INTEGER = yamlize_and_check(Integer)
274
+
275
+ # Same as integer but allows nil:
276
+ #
277
+ # integer_or_nil.call('~') # => nil
278
+ # integer_or_nil.call(nil) # => nil
279
+ def integer_or_nil(); INTEGER_OR_NIL; end
280
+ INTEGER_OR_NIL = yamlize_and_check(Integer, nil)
281
+
282
+ # Returns a block that checks the input is a float.
283
+ # String inputs are loaded as yaml first.
284
+ #
285
+ # float.class # => Proc
286
+ # float.call(1.1) # => 1.1
287
+ # float.call('1.1') # => 1.1
288
+ # float.call('1.0e+6') # => 1e6
289
+ # float.call(1) # => ValidationError
290
+ # float.call(nil) # => ValidationError
291
+ # float.call('str') # => ValidationError
292
+ #
293
+ def float(); FLOAT; end
294
+ FLOAT = yamlize_and_check(Float)
295
+
296
+ # Same as float but allows nil:
297
+ #
298
+ # float_or_nil.call('~') # => nil
299
+ # float_or_nil.call(nil) # => nil
300
+ def float_or_nil(); FLOAT_OR_NIL; end
301
+ FLOAT_OR_NIL = yamlize_and_check(Float, nil)
302
+
303
+ # Returns a block that checks the input is a number.
304
+ # String inputs are loaded as yaml first.
305
+ #
306
+ # num.class # => Proc
307
+ # num.call(1.1) # => 1.1
308
+ # num.call(1) # => 1
309
+ # num.call(1e6) # => 1e6
310
+ # num.call('1.1') # => 1.1
311
+ # num.call('1.0e+6') # => 1e6
312
+ # num.call(nil) # => ValidationError
313
+ # num.call('str') # => ValidationError
314
+ #
315
+ def num(); NUMERIC; end
316
+ NUMERIC = yamlize_and_check(Numeric)
317
+
318
+ # Same as num but allows nil:
319
+ #
320
+ # num_or_nil.call('~') # => nil
321
+ # num_or_nil.call(nil) # => nil
322
+ def num_or_nil(); NUMERIC_OR_NIL; end
323
+ NUMERIC_OR_NIL = yamlize_and_check(Numeric, nil)
324
+
325
+ # Returns a block that checks the input is a regexp.
326
+ # String inputs are converted to regexps using
327
+ # Regexp#new.
328
+ #
329
+ # regexp.class # => Proc
330
+ # regexp.call(/regexp/) # => /regexp/
331
+ # regexp.call('regexp') # => /regexp/
332
+ #
333
+ # # use of ruby-specific flags can turn on/off
334
+ # # features like case insensitive matching
335
+ # regexp.call('(?i)regexp') # => /(?i)regexp/
336
+ #
337
+ def regexp(); REGEXP; end
338
+ regexp_block = lambda do |input|
339
+ input = Regexp.new(input) if input.kind_of?(String)
340
+ validate(input, [Regexp])
341
+ end
342
+ REGEXP = regexp_block
343
+
344
+ # Same as regexp but allows nil. Note the special
345
+ # behavior of the nil string '~' -- rather than
346
+ # being converted to a regexp, it is processed as
347
+ # nil to be consistent with the other [class]_or_nil
348
+ # methods.
349
+ #
350
+ # regexp_or_nil.call('~') # => nil
351
+ # regexp_or_nil.call(nil) # => nil
352
+ def regexp_or_nil(); REGEXP_OR_NIL; end
353
+ regexp_or_nil_block = lambda do |input|
354
+ input = case input
355
+ when nil, '~' then nil
356
+ when String then Regexp.new(input)
357
+ else input
358
+ end
359
+
360
+ validate(input, [Regexp, nil])
361
+ end
362
+ REGEXP_OR_NIL = regexp_or_nil_block
363
+
364
+ # Returns a block that checks the input is a range.
365
+ # String inputs are split into a beginning and
366
+ # end if possible, where each part is loaded as
367
+ # yaml before being used to construct a Range.a
368
+ #
369
+ # range.class # => Proc
370
+ # range.call(1..10) # => 1..10
371
+ # range.call('1..10') # => 1..10
372
+ # range.call('a..z') # => 'a'..'z'
373
+ # range.call('-10...10') # => -10...10
374
+ # range.call(nil) # => ValidationError
375
+ # range.call('1.10') # => ValidationError
376
+ # range.call('a....z') # => ValidationError
377
+ #
378
+ def range(); RANGE; end
379
+ range_block = lambda do |input|
380
+ if input.kind_of?(String) && input =~ /^([^.]+)(\.{2,3})([^.]+)$/
381
+ input = Range.new(yamlize($1), yamlize($3), $2.length == 3)
382
+ end
383
+ validate(input, [Range])
384
+ end
385
+ RANGE = range_block
386
+
387
+ # Same as range but allows nil:
388
+ #
389
+ # range_or_nil.call('~') # => nil
390
+ # range_or_nil.call(nil) # => nil
391
+ def range_or_nil(); RANGE_OR_NIL; end
392
+ range_or_nil_block = lambda do |input|
393
+ input = case input
394
+ when nil, '~' then nil
395
+ when String
396
+ if input =~ /^([^.]+)(\.{2,3})([^.]+)$/
397
+ Range.new(yamlize($1), yamlize($3), $2.length == 3)
398
+ else
399
+ input
400
+ end
401
+ else input
402
+ end
403
+
404
+ validate(input, [Range, nil])
405
+ end
406
+ RANGE_OR_NIL = range_or_nil_block
407
+
408
+ end
409
+ end
410
+ end