entitlements 0.1.8 → 0.2.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 (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