entitlements 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/deploy-entitlements +10 -1
  4. data/lib/contracts-ruby2/CHANGELOG.markdown +115 -0
  5. data/lib/contracts-ruby2/Gemfile +17 -0
  6. data/lib/contracts-ruby2/LICENSE +23 -0
  7. data/lib/contracts-ruby2/README.md +108 -0
  8. data/lib/contracts-ruby2/Rakefile +8 -0
  9. data/lib/contracts-ruby2/TODO.markdown +6 -0
  10. data/lib/contracts-ruby2/TUTORIAL.md +773 -0
  11. data/lib/contracts-ruby2/benchmarks/bench.rb +67 -0
  12. data/lib/contracts-ruby2/benchmarks/hash.rb +69 -0
  13. data/lib/contracts-ruby2/benchmarks/invariants.rb +91 -0
  14. data/lib/contracts-ruby2/benchmarks/io.rb +62 -0
  15. data/lib/contracts-ruby2/benchmarks/wrap_test.rb +57 -0
  16. data/lib/contracts-ruby2/contracts.gemspec +17 -0
  17. data/lib/contracts-ruby2/cucumber.yml +1 -0
  18. data/lib/contracts-ruby2/dependabot.yml +20 -0
  19. data/lib/contracts-ruby2/features/README.md +17 -0
  20. data/lib/contracts-ruby2/features/basics/functype.feature +71 -0
  21. data/lib/contracts-ruby2/features/basics/pretty-print.feature +241 -0
  22. data/lib/contracts-ruby2/features/basics/simple_example.feature +210 -0
  23. data/lib/contracts-ruby2/features/builtin_contracts/README.md +22 -0
  24. data/lib/contracts-ruby2/features/builtin_contracts/and.feature +103 -0
  25. data/lib/contracts-ruby2/features/builtin_contracts/any.feature +44 -0
  26. data/lib/contracts-ruby2/features/builtin_contracts/args.feature +80 -0
  27. data/lib/contracts-ruby2/features/builtin_contracts/array_of.feature +1 -0
  28. data/lib/contracts-ruby2/features/builtin_contracts/bool.feature +64 -0
  29. data/lib/contracts-ruby2/features/builtin_contracts/enum.feature +1 -0
  30. data/lib/contracts-ruby2/features/builtin_contracts/eq.feature +1 -0
  31. data/lib/contracts-ruby2/features/builtin_contracts/exactly.feature +1 -0
  32. data/lib/contracts-ruby2/features/builtin_contracts/func.feature +1 -0
  33. data/lib/contracts-ruby2/features/builtin_contracts/hash_of.feature +1 -0
  34. data/lib/contracts-ruby2/features/builtin_contracts/int.feature +93 -0
  35. data/lib/contracts-ruby2/features/builtin_contracts/keyword_args.feature +1 -0
  36. data/lib/contracts-ruby2/features/builtin_contracts/maybe.feature +1 -0
  37. data/lib/contracts-ruby2/features/builtin_contracts/nat.feature +115 -0
  38. data/lib/contracts-ruby2/features/builtin_contracts/nat_pos.feature +119 -0
  39. data/lib/contracts-ruby2/features/builtin_contracts/neg.feature +115 -0
  40. data/lib/contracts-ruby2/features/builtin_contracts/none.feature +145 -0
  41. data/lib/contracts-ruby2/features/builtin_contracts/not.feature +1 -0
  42. data/lib/contracts-ruby2/features/builtin_contracts/num.feature +64 -0
  43. data/lib/contracts-ruby2/features/builtin_contracts/or.feature +83 -0
  44. data/lib/contracts-ruby2/features/builtin_contracts/pos.feature +116 -0
  45. data/lib/contracts-ruby2/features/builtin_contracts/range_of.feature +1 -0
  46. data/lib/contracts-ruby2/features/builtin_contracts/respond_to.feature +78 -0
  47. data/lib/contracts-ruby2/features/builtin_contracts/send.feature +147 -0
  48. data/lib/contracts-ruby2/features/builtin_contracts/set_of.feature +1 -0
  49. data/lib/contracts-ruby2/features/builtin_contracts/xor.feature +99 -0
  50. data/lib/contracts-ruby2/features/support/env.rb +6 -0
  51. data/lib/contracts-ruby2/lib/contracts/attrs.rb +24 -0
  52. data/lib/contracts-ruby2/lib/contracts/builtin_contracts.rb +542 -0
  53. data/lib/contracts-ruby2/lib/contracts/call_with.rb +108 -0
  54. data/lib/contracts-ruby2/lib/contracts/core.rb +52 -0
  55. data/lib/contracts-ruby2/lib/contracts/decorators.rb +47 -0
  56. data/lib/contracts-ruby2/lib/contracts/engine/base.rb +136 -0
  57. data/lib/contracts-ruby2/lib/contracts/engine/eigenclass.rb +50 -0
  58. data/lib/contracts-ruby2/lib/contracts/engine/target.rb +70 -0
  59. data/lib/contracts-ruby2/lib/contracts/engine.rb +26 -0
  60. data/lib/contracts-ruby2/lib/contracts/errors.rb +71 -0
  61. data/lib/contracts-ruby2/lib/contracts/formatters.rb +136 -0
  62. data/lib/contracts-ruby2/lib/contracts/invariants.rb +68 -0
  63. data/lib/contracts-ruby2/lib/contracts/method_handler.rb +187 -0
  64. data/lib/contracts-ruby2/lib/contracts/method_reference.rb +100 -0
  65. data/lib/contracts-ruby2/lib/contracts/support.rb +61 -0
  66. data/lib/contracts-ruby2/lib/contracts/validators.rb +139 -0
  67. data/lib/contracts-ruby2/lib/contracts/version.rb +3 -0
  68. data/lib/contracts-ruby2/lib/contracts.rb +281 -0
  69. data/lib/contracts-ruby2/script/docs-release +3 -0
  70. data/lib/contracts-ruby2/script/docs-staging +3 -0
  71. data/lib/contracts-ruby2/script/rubocop.rb +5 -0
  72. data/lib/contracts-ruby2/spec/attrs_spec.rb +119 -0
  73. data/lib/contracts-ruby2/spec/builtin_contracts_spec.rb +461 -0
  74. data/lib/contracts-ruby2/spec/contracts_spec.rb +770 -0
  75. data/lib/contracts-ruby2/spec/fixtures/fixtures.rb +730 -0
  76. data/lib/contracts-ruby2/spec/invariants_spec.rb +17 -0
  77. data/lib/contracts-ruby2/spec/methods_spec.rb +54 -0
  78. data/lib/contracts-ruby2/spec/module_spec.rb +18 -0
  79. data/lib/contracts-ruby2/spec/override_validators_spec.rb +162 -0
  80. data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_1.9.rb +24 -0
  81. data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_2.0.rb +55 -0
  82. data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_2.1.rb +63 -0
  83. data/lib/contracts-ruby2/spec/spec_helper.rb +102 -0
  84. data/lib/contracts-ruby2/spec/support.rb +10 -0
  85. data/lib/contracts-ruby2/spec/support_spec.rb +21 -0
  86. data/lib/contracts-ruby2/spec/validators_spec.rb +47 -0
  87. data/lib/contracts-ruby3/CHANGELOG.markdown +117 -0
  88. data/lib/contracts-ruby3/Gemfile +21 -0
  89. data/lib/contracts-ruby3/LICENSE +23 -0
  90. data/lib/contracts-ruby3/README.md +114 -0
  91. data/lib/contracts-ruby3/Rakefile +10 -0
  92. data/lib/contracts-ruby3/TODO.markdown +6 -0
  93. data/lib/contracts-ruby3/TUTORIAL.md +773 -0
  94. data/lib/contracts-ruby3/benchmarks/bench.rb +67 -0
  95. data/lib/contracts-ruby3/benchmarks/hash.rb +69 -0
  96. data/lib/contracts-ruby3/benchmarks/invariants.rb +91 -0
  97. data/lib/contracts-ruby3/benchmarks/io.rb +62 -0
  98. data/lib/contracts-ruby3/benchmarks/wrap_test.rb +57 -0
  99. data/lib/contracts-ruby3/contracts.gemspec +20 -0
  100. data/lib/contracts-ruby3/cucumber.yml +1 -0
  101. data/lib/contracts-ruby3/dependabot.yml +20 -0
  102. data/lib/contracts-ruby3/features/README.md +17 -0
  103. data/lib/contracts-ruby3/features/basics/functype.feature +71 -0
  104. data/lib/contracts-ruby3/features/basics/pretty-print.feature +241 -0
  105. data/lib/contracts-ruby3/features/basics/simple_example.feature +210 -0
  106. data/lib/contracts-ruby3/features/builtin_contracts/README.md +22 -0
  107. data/lib/contracts-ruby3/features/builtin_contracts/and.feature +103 -0
  108. data/lib/contracts-ruby3/features/builtin_contracts/any.feature +44 -0
  109. data/lib/contracts-ruby3/features/builtin_contracts/args.feature +80 -0
  110. data/lib/contracts-ruby3/features/builtin_contracts/array_of.feature +1 -0
  111. data/lib/contracts-ruby3/features/builtin_contracts/bool.feature +64 -0
  112. data/lib/contracts-ruby3/features/builtin_contracts/enum.feature +1 -0
  113. data/lib/contracts-ruby3/features/builtin_contracts/eq.feature +1 -0
  114. data/lib/contracts-ruby3/features/builtin_contracts/exactly.feature +1 -0
  115. data/lib/contracts-ruby3/features/builtin_contracts/func.feature +1 -0
  116. data/lib/contracts-ruby3/features/builtin_contracts/hash_of.feature +1 -0
  117. data/lib/contracts-ruby3/features/builtin_contracts/int.feature +93 -0
  118. data/lib/contracts-ruby3/features/builtin_contracts/keyword_args.feature +1 -0
  119. data/lib/contracts-ruby3/features/builtin_contracts/maybe.feature +1 -0
  120. data/lib/contracts-ruby3/features/builtin_contracts/nat.feature +115 -0
  121. data/lib/contracts-ruby3/features/builtin_contracts/nat_pos.feature +119 -0
  122. data/lib/contracts-ruby3/features/builtin_contracts/neg.feature +115 -0
  123. data/lib/contracts-ruby3/features/builtin_contracts/none.feature +145 -0
  124. data/lib/contracts-ruby3/features/builtin_contracts/not.feature +1 -0
  125. data/lib/contracts-ruby3/features/builtin_contracts/num.feature +64 -0
  126. data/lib/contracts-ruby3/features/builtin_contracts/or.feature +83 -0
  127. data/lib/contracts-ruby3/features/builtin_contracts/pos.feature +116 -0
  128. data/lib/contracts-ruby3/features/builtin_contracts/range_of.feature +1 -0
  129. data/lib/contracts-ruby3/features/builtin_contracts/respond_to.feature +78 -0
  130. data/lib/contracts-ruby3/features/builtin_contracts/send.feature +147 -0
  131. data/lib/contracts-ruby3/features/builtin_contracts/set_of.feature +1 -0
  132. data/lib/contracts-ruby3/features/builtin_contracts/xor.feature +99 -0
  133. data/lib/contracts-ruby3/features/support/env.rb +8 -0
  134. data/lib/contracts-ruby3/lib/contracts/attrs.rb +26 -0
  135. data/lib/contracts-ruby3/lib/contracts/builtin_contracts.rb +575 -0
  136. data/lib/contracts-ruby3/lib/contracts/call_with.rb +119 -0
  137. data/lib/contracts-ruby3/lib/contracts/core.rb +54 -0
  138. data/lib/contracts-ruby3/lib/contracts/decorators.rb +50 -0
  139. data/lib/contracts-ruby3/lib/contracts/engine/base.rb +137 -0
  140. data/lib/contracts-ruby3/lib/contracts/engine/eigenclass.rb +51 -0
  141. data/lib/contracts-ruby3/lib/contracts/engine/target.rb +72 -0
  142. data/lib/contracts-ruby3/lib/contracts/engine.rb +28 -0
  143. data/lib/contracts-ruby3/lib/contracts/errors.rb +74 -0
  144. data/lib/contracts-ruby3/lib/contracts/formatters.rb +140 -0
  145. data/lib/contracts-ruby3/lib/contracts/invariants.rb +72 -0
  146. data/lib/contracts-ruby3/lib/contracts/method_handler.rb +197 -0
  147. data/lib/contracts-ruby3/lib/contracts/method_reference.rb +102 -0
  148. data/lib/contracts-ruby3/lib/contracts/support.rb +63 -0
  149. data/lib/contracts-ruby3/lib/contracts/validators.rb +143 -0
  150. data/lib/contracts-ruby3/lib/contracts/version.rb +5 -0
  151. data/lib/contracts-ruby3/lib/contracts.rb +290 -0
  152. data/lib/contracts-ruby3/script/docs-release +3 -0
  153. data/lib/contracts-ruby3/script/docs-staging +3 -0
  154. data/lib/contracts-ruby3/script/rubocop.rb +5 -0
  155. data/lib/contracts-ruby3/spec/attrs_spec.rb +119 -0
  156. data/lib/contracts-ruby3/spec/builtin_contracts_spec.rb +457 -0
  157. data/lib/contracts-ruby3/spec/contracts_spec.rb +773 -0
  158. data/lib/contracts-ruby3/spec/fixtures/fixtures.rb +725 -0
  159. data/lib/contracts-ruby3/spec/invariants_spec.rb +17 -0
  160. data/lib/contracts-ruby3/spec/methods_spec.rb +54 -0
  161. data/lib/contracts-ruby3/spec/module_spec.rb +18 -0
  162. data/lib/contracts-ruby3/spec/override_validators_spec.rb +162 -0
  163. data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_1.9.rb +24 -0
  164. data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_2.0.rb +55 -0
  165. data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_2.1.rb +63 -0
  166. data/lib/contracts-ruby3/spec/spec_helper.rb +102 -0
  167. data/lib/contracts-ruby3/spec/support.rb +10 -0
  168. data/lib/contracts-ruby3/spec/support_spec.rb +21 -0
  169. data/lib/contracts-ruby3/spec/validators_spec.rb +47 -0
  170. data/lib/entitlements/data/groups/calculated/yaml.rb +7 -1
  171. data/lib/entitlements/data/people/yaml.rb +9 -1
  172. data/lib/entitlements/extras/ldap_group/rules/ldap_group.rb +5 -1
  173. data/lib/entitlements/extras/orgchart/person_methods.rb +7 -1
  174. data/lib/entitlements.rb +13 -2
  175. data/lib/ruby_version_check.rb +17 -0
  176. metadata +209 -14
@@ -0,0 +1,542 @@
1
+ require "contracts/formatters"
2
+ require "set"
3
+
4
+ # rdoc
5
+ # This module contains all the builtin contracts.
6
+ # If you want to use them, first:
7
+ #
8
+ # import Contracts
9
+ #
10
+ # And then use these or write your own!
11
+ #
12
+ # A simple example:
13
+ #
14
+ # Contract Num, Num => Num
15
+ # def add(a, b)
16
+ # a + b
17
+ # end
18
+ #
19
+ # The contract is <tt>Contract Num, Num, Num</tt>.
20
+ # That says that the +add+ function takes two numbers and returns a number.
21
+ module Contracts
22
+ module Builtin
23
+ # Check that an argument is +Numeric+.
24
+ class Num
25
+ def self.valid? val
26
+ val.is_a? Numeric
27
+ end
28
+ end
29
+
30
+ # Check that an argument is a positive number.
31
+ class Pos
32
+ def self.valid? val
33
+ val && val.is_a?(Numeric) && val > 0
34
+ end
35
+ end
36
+
37
+ # Check that an argument is a negative number.
38
+ class Neg
39
+ def self.valid? val
40
+ val && val.is_a?(Numeric) && val < 0
41
+ end
42
+ end
43
+
44
+ # Check that an argument is an +Integer+.
45
+ class Int
46
+ def self.valid? val
47
+ val && val.is_a?(Integer)
48
+ end
49
+ end
50
+
51
+ # Check that an argument is a natural number (includes zero).
52
+ class Nat
53
+ def self.valid? val
54
+ val && val.is_a?(Integer) && val >= 0
55
+ end
56
+ end
57
+
58
+ # Check that an argument is a positive natural number (excludes zero).
59
+ class NatPos
60
+ def self.valid? val
61
+ val && val.is_a?(Integer) && val > 0
62
+ end
63
+ end
64
+
65
+ # Passes for any argument.
66
+ class Any
67
+ def self.valid? val
68
+ true
69
+ end
70
+ end
71
+
72
+ # Fails for any argument.
73
+ class None
74
+ def self.valid? val
75
+ false
76
+ end
77
+ end
78
+
79
+ # Use this when you are writing your own contract classes.
80
+ # Allows your contract to be called with <tt>[]</tt> instead of <tt>.new</tt>:
81
+ #
82
+ # Old: <tt>Or.new(param1, param2)</tt>
83
+ #
84
+ # New: <tt>Or[param1, param2]</tt>
85
+ #
86
+ # Of course, <tt>.new</tt> still works.
87
+ class CallableClass
88
+ include ::Contracts::Formatters
89
+ def self.[](*vals)
90
+ new(*vals)
91
+ end
92
+ end
93
+
94
+ # Takes a variable number of contracts.
95
+ # The contract passes if any of the contracts pass.
96
+ # Example: <tt>Or[Fixnum, Float]</tt>
97
+ class Or < CallableClass
98
+ def initialize(*vals)
99
+ @vals = vals
100
+ end
101
+
102
+ def valid?(val)
103
+ @vals.any? do |contract|
104
+ res, _ = Contract.valid?(val, contract)
105
+ res
106
+ end
107
+ end
108
+
109
+ def to_s
110
+ @vals[0, @vals.size-1].map do |x|
111
+ InspectWrapper.create(x)
112
+ end.join(", ") + " or " + InspectWrapper.create(@vals[-1]).to_s
113
+ end
114
+ end
115
+
116
+ # Takes a variable number of contracts.
117
+ # The contract passes if exactly one of those contracts pass.
118
+ # Example: <tt>Xor[Fixnum, Float]</tt>
119
+ class Xor < CallableClass
120
+ def initialize(*vals)
121
+ @vals = vals
122
+ end
123
+
124
+ def valid?(val)
125
+ results = @vals.map do |contract|
126
+ res, _ = Contract.valid?(val, contract)
127
+ res
128
+ end
129
+ results.count(true) == 1
130
+ end
131
+
132
+ def to_s
133
+ @vals[0, @vals.size-1].map do |x|
134
+ InspectWrapper.create(x)
135
+ end.join(", ") + " xor " + InspectWrapper.create(@vals[-1]).to_s
136
+ end
137
+ end
138
+
139
+ # Takes a variable number of contracts.
140
+ # The contract passes if all contracts pass.
141
+ # Example: <tt>And[Fixnum, Float]</tt>
142
+ class And < CallableClass
143
+ def initialize(*vals)
144
+ @vals = vals
145
+ end
146
+
147
+ def valid?(val)
148
+ @vals.all? do |contract|
149
+ res, _ = Contract.valid?(val, contract)
150
+ res
151
+ end
152
+ end
153
+
154
+ def to_s
155
+ @vals[0, @vals.size-1].map do |x|
156
+ InspectWrapper.create(x)
157
+ end.join(", ") + " and " + InspectWrapper.create(@vals[-1]).to_s
158
+ end
159
+ end
160
+
161
+ # Takes a variable number of method names as symbols.
162
+ # The contract passes if the argument responds to all
163
+ # of those methods.
164
+ # Example: <tt>RespondTo[:password, :credit_card]</tt>
165
+ class RespondTo < CallableClass
166
+ def initialize(*meths)
167
+ @meths = meths
168
+ end
169
+
170
+ def valid?(val)
171
+ @meths.all? do |meth|
172
+ val.respond_to? meth
173
+ end
174
+ end
175
+
176
+ def to_s
177
+ "a value that responds to #{@meths.inspect}"
178
+ end
179
+ end
180
+
181
+ # Takes a variable number of method names as symbols.
182
+ # Given an argument, all of those methods are called
183
+ # on the argument one by one. If they all return true,
184
+ # the contract passes.
185
+ # Example: <tt>Send[:valid?]</tt>
186
+ class Send < CallableClass
187
+ def initialize(*meths)
188
+ @meths = meths
189
+ end
190
+
191
+ def valid?(val)
192
+ @meths.all? do |meth|
193
+ val.send(meth)
194
+ end
195
+ end
196
+
197
+ def to_s
198
+ "a value that returns true for all of #{@meths.inspect}"
199
+ end
200
+ end
201
+
202
+ # Takes a class +A+. If argument is object of type +A+, the contract passes.
203
+ # If it is a subclass of A (or not related to A in any way), it fails.
204
+ # Example: <tt>Exactly[Numeric]</tt>
205
+ class Exactly < CallableClass
206
+ def initialize(cls)
207
+ @cls = cls
208
+ end
209
+
210
+ def valid?(val)
211
+ val.class == @cls
212
+ end
213
+
214
+ def to_s
215
+ "exactly #{@cls.inspect}"
216
+ end
217
+ end
218
+
219
+ # Takes a list of values, e.g. +[:a, :b, :c]+. If argument is included in
220
+ # the list, the contract passes.
221
+ #
222
+ # Example: <tt>Enum[:a, :b, :c]</tt>?
223
+ class Enum < CallableClass
224
+ def initialize(*vals)
225
+ @vals = vals
226
+ end
227
+
228
+ def valid?(val)
229
+ @vals.include? val
230
+ end
231
+ end
232
+
233
+ # Takes a value +v+. If the argument is +.equal+ to +v+, the contract passes,
234
+ # otherwise the contract fails.
235
+ # Example: <tt>Eq[Class]</tt>
236
+ class Eq < CallableClass
237
+ def initialize(value)
238
+ @value = value
239
+ end
240
+
241
+ def valid?(val)
242
+ @value.equal?(val)
243
+ end
244
+
245
+ def to_s
246
+ "to be equal to #{@value.inspect}"
247
+ end
248
+ end
249
+
250
+ # Takes a variable number of contracts. The contract
251
+ # passes if all of those contracts fail for the given argument.
252
+ # Example: <tt>Not[nil]</tt>
253
+ class Not < CallableClass
254
+ def initialize(*vals)
255
+ @vals = vals
256
+ end
257
+
258
+ def valid?(val)
259
+ @vals.all? do |contract|
260
+ res, _ = Contract.valid?(val, contract)
261
+ !res
262
+ end
263
+ end
264
+
265
+ def to_s
266
+ "a value that is none of #{@vals.inspect}"
267
+ end
268
+ end
269
+
270
+ # @private
271
+ # Takes a collection(responds to :each) type and a contract.
272
+ # The related argument must be of specified collection type.
273
+ # Checks the contract against every element of the collection.
274
+ # If it passes for all elements, the contract passes.
275
+ # Example: <tt>CollectionOf[Array, Num]</tt>
276
+ class CollectionOf < CallableClass
277
+ def initialize(collection_class, contract)
278
+ @collection_class = collection_class
279
+ @contract = contract
280
+ end
281
+
282
+ def valid?(vals)
283
+ return false unless vals.is_a?(@collection_class)
284
+ vals.all? do |val|
285
+ res, _ = Contract.valid?(val, @contract)
286
+ res
287
+ end
288
+ end
289
+
290
+ def to_s
291
+ "a collection #{@collection_class} of #{@contract}"
292
+ end
293
+
294
+ class Factory
295
+ def initialize(collection_class, &before_new)
296
+ @collection_class = collection_class
297
+ @before_new = before_new
298
+ end
299
+
300
+ def new(contract)
301
+ @before_new && @before_new.call
302
+ CollectionOf.new(@collection_class, contract)
303
+ end
304
+
305
+ alias_method :[], :new
306
+ end
307
+ end
308
+
309
+ # Takes a contract. The related argument must be an array.
310
+ # Checks the contract against every element of the array.
311
+ # If it passes for all elements, the contract passes.
312
+ # Example: <tt>ArrayOf[Num]</tt>
313
+ ArrayOf = CollectionOf::Factory.new(Array)
314
+
315
+ # Takes a contract. The related argument must be a set.
316
+ # Checks the contract against every element of the set.
317
+ # If it passes for all elements, the contract passes.
318
+ # Example: <tt>SetOf[Num]</tt>
319
+ SetOf = CollectionOf::Factory.new(Set)
320
+
321
+ # Used for <tt>*args</tt> (variadic functions). Takes a contract
322
+ # and uses it to validate every element passed in
323
+ # through <tt>*args</tt>.
324
+ # Example: <tt>Args[Or[String, Num]]</tt>
325
+ class Args < CallableClass
326
+ attr_reader :contract
327
+ def initialize(contract)
328
+ @contract = contract
329
+ end
330
+
331
+ def to_s
332
+ "Args[#{@contract}]"
333
+ end
334
+ end
335
+
336
+ class Bool
337
+ def self.valid? val
338
+ val.is_a?(TrueClass) || val.is_a?(FalseClass)
339
+ end
340
+ end
341
+
342
+ # Use this to specify a Range object of a particular datatype.
343
+ # Example: <tt>RangeOf[Nat]</tt>, <tt>RangeOf[Date]</tt>, ...
344
+ class RangeOf < CallableClass
345
+ def initialize(contract)
346
+ @contract = contract
347
+ end
348
+
349
+ def valid?(val)
350
+ val.is_a?(Range) &&
351
+ Contract.valid?(val.first, @contract) &&
352
+ Contract.valid?(val.last, @contract)
353
+ end
354
+
355
+ def to_s
356
+ "a range of #{@contract}"
357
+ end
358
+ end
359
+
360
+ # Use this to specify the Hash characteristics. Takes two contracts,
361
+ # one for hash keys and one for hash values.
362
+ # Example: <tt>HashOf[Symbol, String]</tt>
363
+ class HashOf < CallableClass
364
+ INVALID_KEY_VALUE_PAIR = "You should provide only one key-value pair to HashOf contract"
365
+
366
+ def initialize(key, value = nil)
367
+ if value
368
+ @key = key
369
+ @value = value
370
+ else
371
+ validate_hash(key)
372
+ @key = key.keys.first
373
+ @value = key[@key]
374
+ end
375
+ end
376
+
377
+ def valid?(hash)
378
+ return false unless hash.is_a?(Hash)
379
+ keys_match = hash.keys.map { |k| Contract.valid?(k, @key) }.all?
380
+ vals_match = hash.values.map { |v| Contract.valid?(v, @value) }.all?
381
+
382
+ [keys_match, vals_match].all?
383
+ end
384
+
385
+ def to_s
386
+ "Hash<#{@key}, #{@value}>"
387
+ end
388
+
389
+ private
390
+
391
+ def validate_hash(hash)
392
+ fail ArgumentError, INVALID_KEY_VALUE_PAIR unless hash.count == 1
393
+ end
394
+ end
395
+
396
+ # Use this to specify the Hash characteristics. This contracts fails
397
+ # if there are any extra keys that don't have contracts on them.
398
+ # Example: <tt>StrictHash[{ a: String, b: Bool }]</tt>
399
+ class StrictHash < CallableClass
400
+ attr_reader :contract_hash
401
+
402
+ def initialize(contract_hash)
403
+ @contract_hash = contract_hash
404
+ end
405
+
406
+ def valid?(arg)
407
+ return false unless arg.is_a?(Hash)
408
+ return false unless arg.keys.sort.eql?(contract_hash.keys.sort)
409
+
410
+ contract_hash.all? do |key, contract|
411
+ contract_hash.key?(key) && Contract.valid?(arg[key], contract)
412
+ end
413
+ end
414
+ end
415
+
416
+ # Use this for specifying contracts for keyword arguments
417
+ # Example: <tt>KeywordArgs[ e: Range, f: Optional[Num] ]</tt>
418
+ class KeywordArgs < CallableClass
419
+ def initialize(options)
420
+ @options = options
421
+ end
422
+
423
+ def valid?(hash)
424
+ return false unless hash.is_a?(Hash)
425
+ return false unless hash.keys - options.keys == []
426
+ options.all? do |key, contract|
427
+ Optional._valid?(hash, key, contract)
428
+ end
429
+ end
430
+
431
+ def to_s
432
+ "KeywordArgs[#{options}]"
433
+ end
434
+
435
+ def inspect
436
+ to_s
437
+ end
438
+
439
+ private
440
+
441
+ attr_reader :options
442
+ end
443
+
444
+ # Use this for specifying contracts for class arguments
445
+ # Example: <tt>DescendantOf[ e: Range, f: Optional[Num] ]</tt>
446
+ class DescendantOf < CallableClass
447
+ def initialize(parent_class)
448
+ @parent_class = parent_class
449
+ end
450
+
451
+ def valid?(given_class)
452
+ given_class.is_a?(Class) && given_class.ancestors.include?(parent_class)
453
+ end
454
+
455
+ def to_s
456
+ "DescendantOf[#{parent_class}]"
457
+ end
458
+
459
+ def inspect
460
+ to_s
461
+ end
462
+
463
+ private
464
+
465
+ attr_reader :parent_class
466
+ end
467
+
468
+ # Use this for specifying optional keyword argument
469
+ # Example: <tt>Optional[Num]</tt>
470
+ class Optional < CallableClass
471
+ UNABLE_TO_USE_OUTSIDE_OF_OPT_HASH =
472
+ "Unable to use Optional contract outside of KeywordArgs contract"
473
+
474
+ def self._valid?(hash, key, contract)
475
+ return Contract.valid?(hash[key], contract) unless contract.is_a?(Optional)
476
+ contract.within_opt_hash!
477
+ !hash.key?(key) || Contract.valid?(hash[key], contract)
478
+ end
479
+
480
+ def initialize(contract)
481
+ @contract = contract
482
+ @within_opt_hash = false
483
+ end
484
+
485
+ def within_opt_hash!
486
+ @within_opt_hash = true
487
+ self
488
+ end
489
+
490
+ def valid?(value)
491
+ ensure_within_opt_hash
492
+ Contract.valid?(value, contract)
493
+ end
494
+
495
+ def to_s
496
+ "Optional[#{formatted_contract}]"
497
+ end
498
+
499
+ def inspect
500
+ to_s
501
+ end
502
+
503
+ private
504
+
505
+ attr_reader :contract, :within_opt_hash
506
+
507
+ def ensure_within_opt_hash
508
+ return if within_opt_hash
509
+ fail ArgumentError, UNABLE_TO_USE_OUTSIDE_OF_OPT_HASH
510
+ end
511
+
512
+ def formatted_contract
513
+ Formatters::InspectWrapper.create(contract)
514
+ end
515
+ end
516
+
517
+ # Takes a Contract.
518
+ # The contract passes if the contract passes or the given value is nil.
519
+ # Maybe(foo) is equivalent to Or[foo, nil].
520
+ class Maybe < Or
521
+ def initialize(*vals)
522
+ super(*(vals + [nil]))
523
+ end
524
+
525
+ def include_proc?
526
+ @vals.include? Proc
527
+ end
528
+ end
529
+
530
+ # Used to define contracts on functions passed in as arguments.
531
+ # Example: <tt>Func[Num => Num] # the function should take a number and return a number</tt>
532
+ class Func < CallableClass
533
+ attr_reader :contracts
534
+ def initialize(*contracts)
535
+ @contracts = contracts
536
+ end
537
+ end
538
+ end
539
+
540
+ # Users can still include `Contracts::Core` & `Contracts::Builtin`
541
+ include Builtin
542
+ end
@@ -0,0 +1,108 @@
1
+ module Contracts
2
+ module CallWith
3
+ def call_with(this, *args, &blk)
4
+ call_with_inner(false, this, *args, &blk)
5
+ end
6
+
7
+ def call_with_inner(returns, this, *args, &blk)
8
+ args << blk if blk
9
+
10
+ # Explicitly append blk=nil if nil != Proc contract violation anticipated
11
+ nil_block_appended = maybe_append_block!(args, blk)
12
+
13
+ # Explicitly append options={} if Hash contract is present
14
+ maybe_append_options!(args, blk)
15
+
16
+ # Loop forward validating the arguments up to the splat (if there is one)
17
+ (@args_contract_index || args.size).times do |i|
18
+ contract = args_contracts[i]
19
+ arg = args[i]
20
+ validator = @args_validators[i]
21
+
22
+ unless validator && validator[arg]
23
+ data = {:arg => arg,
24
+ :contract => contract,
25
+ :class => klass,
26
+ :method => method,
27
+ :contracts => self,
28
+ :arg_pos => i+1,
29
+ :total_args => args.size,
30
+ :return_value => false}
31
+ return ParamContractError.new("as return value", data) if returns
32
+ return unless Contract.failure_callback(data)
33
+ end
34
+
35
+ if contract.is_a?(Contracts::Func) && blk && !nil_block_appended
36
+ blk = Contract.new(klass, arg, *contract.contracts)
37
+ elsif contract.is_a?(Contracts::Func)
38
+ args[i] = Contract.new(klass, arg, *contract.contracts)
39
+ end
40
+ end
41
+
42
+ # If there is a splat loop backwards to the lower index of the splat
43
+ # Once we hit the splat in this direction set its upper index
44
+ # Keep validating but use this upper index to get the splat validator.
45
+ if @args_contract_index
46
+ splat_upper_index = @args_contract_index
47
+ (args.size - @args_contract_index).times do |i|
48
+ arg = args[args.size - 1 - i]
49
+
50
+ if args_contracts[args_contracts.size - 1 - i].is_a?(Contracts::Args)
51
+ splat_upper_index = i
52
+ end
53
+
54
+ # Each arg after the spat is found must use the splat validator
55
+ j = i < splat_upper_index ? i : splat_upper_index
56
+ contract = args_contracts[args_contracts.size - 1 - j]
57
+ validator = @args_validators[args_contracts.size - 1 - j]
58
+
59
+ unless validator && validator[arg]
60
+ return unless Contract.failure_callback(:arg => arg,
61
+ :contract => contract,
62
+ :class => klass,
63
+ :method => method,
64
+ :contracts => self,
65
+ :arg_pos => i-1,
66
+ :total_args => args.size,
67
+ :return_value => false)
68
+ end
69
+
70
+ if contract.is_a?(Contracts::Func)
71
+ args[args.size - 1 - i] = Contract.new(klass, arg, *contract.contracts)
72
+ end
73
+ end
74
+ end
75
+
76
+ # If we put the block into args for validating, restore the args
77
+ # OR if we added a fake nil at the end because a block wasn't passed in.
78
+ args.slice!(-1) if blk || nil_block_appended
79
+ result = if method.respond_to?(:call)
80
+ # proc, block, lambda, etc
81
+ method.call(*args, &blk)
82
+ else
83
+ # original method name reference
84
+ # Don't reassign blk, else Travis CI shows "stack level too deep".
85
+ target_blk = blk
86
+ target_blk = lambda { |*params| blk.call(*params) } if blk && blk.is_a?(Contract)
87
+ method.send_to(this, *args, &target_blk)
88
+ end
89
+
90
+ unless @ret_validator[result]
91
+ Contract.failure_callback(:arg => result,
92
+ :contract => ret_contract,
93
+ :class => klass,
94
+ :method => method,
95
+ :contracts => self,
96
+ :return_value => true)
97
+ end
98
+
99
+ this.verify_invariants!(method) if this.respond_to?(:verify_invariants!)
100
+
101
+ if ret_contract.is_a?(Contracts::Func)
102
+ result = Contract.new(klass, result, *ret_contract.contracts)
103
+ end
104
+
105
+ result
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,52 @@
1
+ module Contracts
2
+ module Core
3
+ def self.included(base)
4
+ common(base)
5
+ end
6
+
7
+ def self.extended(base)
8
+ common(base)
9
+ end
10
+
11
+ def self.common(base)
12
+ base.extend(MethodDecorators)
13
+
14
+ base.instance_eval do
15
+ def functype(funcname)
16
+ contracts = Engine.fetch_from(self).decorated_methods_for(:class_methods, funcname)
17
+ if contracts.nil?
18
+ "No contract for #{self}.#{funcname}"
19
+ else
20
+ "#{funcname} :: #{contracts[0]}"
21
+ end
22
+ end
23
+ end
24
+
25
+ # NOTE: Workaround for `defined?(super)` bug in ruby 1.9.2
26
+ # source: http://stackoverflow.com/a/11181685
27
+ # bug: https://bugs.ruby-lang.org/issues/6644
28
+ base.class_eval <<-RUBY
29
+ # TODO: deprecate
30
+ # Required when contracts are included in global scope
31
+ def Contract(*args)
32
+ if defined?(super)
33
+ super
34
+ else
35
+ self.class.Contract(*args)
36
+ end
37
+ end
38
+ RUBY
39
+
40
+ base.class_eval do
41
+ def functype(funcname)
42
+ contracts = Engine.fetch_from(self.class).decorated_methods_for(:instance_methods, funcname)
43
+ if contracts.nil?
44
+ "No contract for #{self.class}.#{funcname}"
45
+ else
46
+ "#{funcname} :: #{contracts[0]}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end