contracts 0.16.1 → 0.17.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "contracts/formatters"
2
4
  require "set"
3
5
 
@@ -30,35 +32,35 @@ module Contracts
30
32
  # Check that an argument is a positive number.
31
33
  class Pos
32
34
  def self.valid? val
33
- val && val.is_a?(Numeric) && val > 0
35
+ val.is_a?(Numeric) && val.positive?
34
36
  end
35
37
  end
36
38
 
37
39
  # Check that an argument is a negative number.
38
40
  class Neg
39
41
  def self.valid? val
40
- val && val.is_a?(Numeric) && val < 0
42
+ val.is_a?(Numeric) && val.negative?
41
43
  end
42
44
  end
43
45
 
44
46
  # Check that an argument is an +Integer+.
45
47
  class Int
46
48
  def self.valid? val
47
- val && val.is_a?(Integer)
49
+ val.is_a?(Integer)
48
50
  end
49
51
  end
50
52
 
51
53
  # Check that an argument is a natural number (includes zero).
52
54
  class Nat
53
55
  def self.valid? val
54
- val && val.is_a?(Integer) && val >= 0
56
+ val.is_a?(Integer) && val >= 0
55
57
  end
56
58
  end
57
59
 
58
60
  # Check that an argument is a positive natural number (excludes zero).
59
61
  class NatPos
60
62
  def self.valid? val
61
- val && val.is_a?(Integer) && val > 0
63
+ val.is_a?(Integer) && val.positive?
62
64
  end
63
65
  end
64
66
 
@@ -93,9 +95,10 @@ module Contracts
93
95
 
94
96
  # Takes a variable number of contracts.
95
97
  # The contract passes if any of the contracts pass.
96
- # Example: <tt>Or[Fixnum, Float]</tt>
98
+ # Example: <tt>Or[Integer, Float]</tt>
97
99
  class Or < CallableClass
98
100
  def initialize(*vals)
101
+ super()
99
102
  @vals = vals
100
103
  end
101
104
 
@@ -107,17 +110,20 @@ module Contracts
107
110
  end
108
111
 
109
112
  def to_s
113
+ # rubocop:disable Style/StringConcatenation
110
114
  @vals[0, @vals.size-1].map do |x|
111
115
  InspectWrapper.create(x)
112
116
  end.join(", ") + " or " + InspectWrapper.create(@vals[-1]).to_s
117
+ # rubocop:enable Style/StringConcatenation
113
118
  end
114
119
  end
115
120
 
116
121
  # Takes a variable number of contracts.
117
122
  # The contract passes if exactly one of those contracts pass.
118
- # Example: <tt>Xor[Fixnum, Float]</tt>
123
+ # Example: <tt>Xor[Integer, Float]</tt>
119
124
  class Xor < CallableClass
120
125
  def initialize(*vals)
126
+ super()
121
127
  @vals = vals
122
128
  end
123
129
 
@@ -130,17 +136,20 @@ module Contracts
130
136
  end
131
137
 
132
138
  def to_s
139
+ # rubocop:disable Style/StringConcatenation
133
140
  @vals[0, @vals.size-1].map do |x|
134
141
  InspectWrapper.create(x)
135
142
  end.join(", ") + " xor " + InspectWrapper.create(@vals[-1]).to_s
143
+ # rubocop:enable Style/StringConcatenation
136
144
  end
137
145
  end
138
146
 
139
147
  # Takes a variable number of contracts.
140
148
  # The contract passes if all contracts pass.
141
- # Example: <tt>And[Fixnum, Float]</tt>
149
+ # Example: <tt>And[Integer, Float]</tt>
142
150
  class And < CallableClass
143
151
  def initialize(*vals)
152
+ super()
144
153
  @vals = vals
145
154
  end
146
155
 
@@ -152,9 +161,11 @@ module Contracts
152
161
  end
153
162
 
154
163
  def to_s
164
+ # rubocop:disable Style/StringConcatenation
155
165
  @vals[0, @vals.size-1].map do |x|
156
166
  InspectWrapper.create(x)
157
167
  end.join(", ") + " and " + InspectWrapper.create(@vals[-1]).to_s
168
+ # rubocop:enable Style/StringConcatenation
158
169
  end
159
170
  end
160
171
 
@@ -164,6 +175,7 @@ module Contracts
164
175
  # Example: <tt>RespondTo[:password, :credit_card]</tt>
165
176
  class RespondTo < CallableClass
166
177
  def initialize(*meths)
178
+ super()
167
179
  @meths = meths
168
180
  end
169
181
 
@@ -185,6 +197,7 @@ module Contracts
185
197
  # Example: <tt>Send[:valid?]</tt>
186
198
  class Send < CallableClass
187
199
  def initialize(*meths)
200
+ super()
188
201
  @meths = meths
189
202
  end
190
203
 
@@ -204,11 +217,12 @@ module Contracts
204
217
  # Example: <tt>Exactly[Numeric]</tt>
205
218
  class Exactly < CallableClass
206
219
  def initialize(cls)
220
+ super()
207
221
  @cls = cls
208
222
  end
209
223
 
210
224
  def valid?(val)
211
- val.class == @cls
225
+ val.instance_of?(@cls)
212
226
  end
213
227
 
214
228
  def to_s
@@ -222,6 +236,7 @@ module Contracts
222
236
  # Example: <tt>Enum[:a, :b, :c]</tt>?
223
237
  class Enum < CallableClass
224
238
  def initialize(*vals)
239
+ super()
225
240
  @vals = vals
226
241
  end
227
242
 
@@ -235,6 +250,7 @@ module Contracts
235
250
  # Example: <tt>Eq[Class]</tt>
236
251
  class Eq < CallableClass
237
252
  def initialize(value)
253
+ super()
238
254
  @value = value
239
255
  end
240
256
 
@@ -252,6 +268,7 @@ module Contracts
252
268
  # Example: <tt>Not[nil]</tt>
253
269
  class Not < CallableClass
254
270
  def initialize(*vals)
271
+ super()
255
272
  @vals = vals
256
273
  end
257
274
 
@@ -275,12 +292,14 @@ module Contracts
275
292
  # Example: <tt>CollectionOf[Array, Num]</tt>
276
293
  class CollectionOf < CallableClass
277
294
  def initialize(collection_class, contract)
295
+ super()
278
296
  @collection_class = collection_class
279
297
  @contract = contract
280
298
  end
281
299
 
282
300
  def valid?(vals)
283
301
  return false unless vals.is_a?(@collection_class)
302
+
284
303
  vals.all? do |val|
285
304
  res, _ = Contract.valid?(val, @contract)
286
305
  res
@@ -298,7 +317,7 @@ module Contracts
298
317
  end
299
318
 
300
319
  def new(contract)
301
- @before_new && @before_new.call
320
+ @before_new&.call
302
321
  CollectionOf.new(@collection_class, contract)
303
322
  end
304
323
 
@@ -324,7 +343,9 @@ module Contracts
324
343
  # Example: <tt>Args[Or[String, Num]]</tt>
325
344
  class Args < CallableClass
326
345
  attr_reader :contract
346
+
327
347
  def initialize(contract)
348
+ super()
328
349
  @contract = contract
329
350
  end
330
351
 
@@ -343,6 +364,7 @@ module Contracts
343
364
  # Example: <tt>RangeOf[Nat]</tt>, <tt>RangeOf[Date]</tt>, ...
344
365
  class RangeOf < CallableClass
345
366
  def initialize(contract)
367
+ super()
346
368
  @contract = contract
347
369
  end
348
370
 
@@ -364,6 +386,7 @@ module Contracts
364
386
  INVALID_KEY_VALUE_PAIR = "You should provide only one key-value pair to HashOf contract"
365
387
 
366
388
  def initialize(key, value = nil)
389
+ super()
367
390
  if value
368
391
  @key = key
369
392
  @value = value
@@ -376,6 +399,7 @@ module Contracts
376
399
 
377
400
  def valid?(hash)
378
401
  return false unless hash.is_a?(Hash)
402
+
379
403
  keys_match = hash.keys.map { |k| Contract.valid?(k, @key) }.all?
380
404
  vals_match = hash.values.map { |v| Contract.valid?(v, @value) }.all?
381
405
 
@@ -400,6 +424,7 @@ module Contracts
400
424
  attr_reader :contract_hash
401
425
 
402
426
  def initialize(contract_hash)
427
+ super()
403
428
  @contract_hash = contract_hash
404
429
  end
405
430
 
@@ -417,12 +442,14 @@ module Contracts
417
442
  # Example: <tt>KeywordArgs[ e: Range, f: Optional[Num] ]</tt>
418
443
  class KeywordArgs < CallableClass
419
444
  def initialize(options)
445
+ super()
420
446
  @options = options
421
447
  end
422
448
 
423
449
  def valid?(hash)
424
450
  return false unless hash.is_a?(Hash)
425
451
  return false unless hash.keys - options.keys == []
452
+
426
453
  options.all? do |key, contract|
427
454
  Optional._valid?(hash, key, contract)
428
455
  end
@@ -445,6 +472,7 @@ module Contracts
445
472
  # Example: <tt>DescendantOf[ e: Range, f: Optional[Num] ]</tt>
446
473
  class DescendantOf < CallableClass
447
474
  def initialize(parent_class)
475
+ super()
448
476
  @parent_class = parent_class
449
477
  end
450
478
 
@@ -473,11 +501,13 @@ module Contracts
473
501
 
474
502
  def self._valid?(hash, key, contract)
475
503
  return Contract.valid?(hash[key], contract) unless contract.is_a?(Optional)
504
+
476
505
  contract.within_opt_hash!
477
506
  !hash.key?(key) || Contract.valid?(hash[key], contract)
478
507
  end
479
508
 
480
509
  def initialize(contract)
510
+ super()
481
511
  @contract = contract
482
512
  @within_opt_hash = false
483
513
  end
@@ -506,6 +536,7 @@ module Contracts
506
536
 
507
537
  def ensure_within_opt_hash
508
538
  return if within_opt_hash
539
+
509
540
  fail ArgumentError, UNABLE_TO_USE_OUTSIDE_OF_OPT_HASH
510
541
  end
511
542
 
@@ -531,7 +562,9 @@ module Contracts
531
562
  # Example: <tt>Func[Num => Num] # the function should take a number and return a number</tt>
532
563
  class Func < CallableClass
533
564
  attr_reader :contracts
565
+
534
566
  def initialize(*contracts)
567
+ super()
535
568
  @contracts = contracts
536
569
  end
537
570
  end
@@ -1,17 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  module CallWith
3
- def call_with(this, *args, &blk)
4
- call_with_inner(false, this, *args, &blk)
5
+ def call_with(this, *args, **kargs, &blk)
6
+ call_with_inner(false, this, *args, **kargs, &blk)
5
7
  end
6
8
 
7
- def call_with_inner(returns, this, *args, &blk)
9
+ def call_with_inner(returns, this, *args, **kargs, &blk)
8
10
  args << blk if blk
9
11
 
10
12
  # Explicitly append blk=nil if nil != Proc contract violation anticipated
11
13
  nil_block_appended = maybe_append_block!(args, blk)
12
14
 
13
- # Explicitly append options={} if Hash contract is present
14
- maybe_append_options!(args, blk)
15
+ if @kargs_validator && !@kargs_validator[kargs]
16
+ data = {
17
+ arg: kargs,
18
+ contract: kargs_contract,
19
+ class: klass,
20
+ method: method,
21
+ contracts: self,
22
+ arg_pos: :keyword,
23
+ total_args: args.size,
24
+ return_value: false,
25
+ }
26
+ return ParamContractError.new("as return value", data) if returns
27
+ return unless Contract.failure_callback(data)
28
+ end
15
29
 
16
30
  # Loop forward validating the arguments up to the splat (if there is one)
17
31
  (@args_contract_index || args.size).times do |i|
@@ -20,14 +34,16 @@ module Contracts
20
34
  validator = @args_validators[i]
21
35
 
22
36
  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}
37
+ data = {
38
+ arg: arg,
39
+ contract: contract,
40
+ class: klass,
41
+ method: method,
42
+ contracts: self,
43
+ arg_pos: i+1,
44
+ total_args: args.size,
45
+ return_value: false,
46
+ }
31
47
  return ParamContractError.new("as return value", data) if returns
32
48
  return unless Contract.failure_callback(data)
33
49
  end
@@ -57,14 +73,18 @@ module Contracts
57
73
  validator = @args_validators[args_contracts.size - 1 - j]
58
74
 
59
75
  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)
76
+ # rubocop:disable Style/SoleNestedConditional
77
+ return unless Contract.failure_callback({
78
+ :arg => arg,
79
+ :contract => contract,
80
+ :class => klass,
81
+ :method => method,
82
+ :contracts => self,
83
+ :arg_pos => i - 1,
84
+ :total_args => args.size,
85
+ :return_value => false,
86
+ })
87
+ # rubocop:enable Style/SoleNestedConditional
68
88
  end
69
89
 
70
90
  if contract.is_a?(Contracts::Func)
@@ -78,22 +98,24 @@ module Contracts
78
98
  args.slice!(-1) if blk || nil_block_appended
79
99
  result = if method.respond_to?(:call)
80
100
  # proc, block, lambda, etc
81
- method.call(*args, &blk)
101
+ method.call(*args, **kargs, &blk)
82
102
  else
83
103
  # original method name reference
84
104
  # Don't reassign blk, else Travis CI shows "stack level too deep".
85
105
  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)
106
+ target_blk = lambda { |*params| blk.call(*params) } if blk.is_a?(Contract)
107
+ method.send_to(this, *args, **kargs, &target_blk)
88
108
  end
89
109
 
90
110
  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)
111
+ Contract.failure_callback({
112
+ arg: result,
113
+ contract: ret_contract,
114
+ class: klass,
115
+ method: method,
116
+ contracts: self,
117
+ return_value: true,
118
+ })
97
119
  end
98
120
 
99
121
  this.verify_invariants!(method) if this.respond_to?(:verify_invariants!)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  module Core
3
5
  def self.included(base)
@@ -25,7 +27,7 @@ module Contracts
25
27
  # NOTE: Workaround for `defined?(super)` bug in ruby 1.9.2
26
28
  # source: http://stackoverflow.com/a/11181685
27
29
  # bug: https://bugs.ruby-lang.org/issues/6644
28
- base.class_eval <<-RUBY
30
+ base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
29
31
  # TODO: deprecate
30
32
  # Required when contracts are included in global scope
31
33
  def Contract(*args)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  module MethodDecorators
3
5
  def self.extended(klass)
@@ -25,6 +27,7 @@ module Contracts
25
27
  class << self; attr_accessor :decorators; end
26
28
 
27
29
  def self.inherited(klass)
30
+ super
28
31
  name = klass.name.gsub(/^./) { |m| m.downcase }
29
32
 
30
33
  return if name =~ /^[^A-Za-z_]/ || name =~ /[^0-9A-Za-z_]/
@@ -33,11 +36,11 @@ module Contracts
33
36
  # make a new method that is the name of your decorator.
34
37
  # that method accepts random args and a block.
35
38
  # inside, `decorate` is called with those params.
36
- MethodDecorators.module_eval <<-ruby_eval, __FILE__, __LINE__ + 1
39
+ MethodDecorators.module_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
37
40
  def #{klass}(*args, &blk)
38
41
  ::Contracts::Engine.fetch_from(self).decorate(#{klass}, *args, &blk)
39
42
  end
40
- ruby_eval
43
+ RUBY_EVAL
41
44
  end
42
45
 
43
46
  def initialize(klass, method)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  module Engine
3
5
  # Contracts engine
@@ -90,7 +92,7 @@ module Contracts
90
92
  def nearest_decorated_ancestor
91
93
  current = klass
92
94
  current_engine = self
93
- ancestors = current.ancestors[1..-1]
95
+ ancestors = current.ancestors[1..]
94
96
 
95
97
  while current && current_engine && !current_engine.decorated_methods?
96
98
  current = ancestors.shift
@@ -109,8 +111,7 @@ module Contracts
109
111
  end
110
112
 
111
113
  # No-op because it is safe to add decorators to normal classes
112
- def validate!
113
- end
114
+ def validate!; end
114
115
 
115
116
  def pop_decorators
116
117
  decorators.tap { clear_decorators }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  module Engine
3
5
  # Special case of contracts engine for eigenclasses
@@ -27,8 +29,7 @@ module Contracts
27
29
  end
28
30
 
29
31
  # No-op for eigenclasses
30
- def set_eigenclass_owner
31
- end
32
+ def set_eigenclass_owner; end
32
33
 
33
34
  # Fetches just eigenclasses decorators
34
35
  def all_decorators
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  module Engine
3
5
  # Represents class in question
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "contracts/engine/base"
2
4
  require "contracts/engine/target"
3
5
  require "contracts/engine/eigenclass"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # @private
2
4
  # Base class for Contract errors
3
5
  #
@@ -65,6 +67,7 @@ module Contracts
65
67
  alias_method :to_s, :message
66
68
 
67
69
  def initialize(message = DEFAULT_MESSAGE)
70
+ super
68
71
  @message = message
69
72
  end
70
73
  end
@@ -1,4 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Lint/RedundantRequireStatement
4
+ # If removed, spec/module_spec fails
1
5
  require "pp"
6
+ # rubocop:enable Lint/RedundantRequireStatement
2
7
 
3
8
  module Contracts
4
9
  # A namespace for classes related to formatting.
@@ -7,18 +12,19 @@ module Contracts
7
12
  class Expected
8
13
  # @param full [Boolean] if false only unique `to_s` values will be output,
9
14
  # non unique values become empty string.
10
- def initialize(contract, full = true)
15
+ def initialize(contract, full: true)
11
16
  @contract, @full = contract, full
12
17
  end
13
18
 
14
19
  # Formats any type of Contract.
15
20
  def contract(contract = @contract)
16
- if contract.is_a?(Hash)
21
+ case contract
22
+ when Hash
17
23
  hash_contract(contract)
18
- elsif contract.is_a?(Array)
24
+ when Array
19
25
  array_contract(contract)
20
26
  else
21
- InspectWrapper.create(contract, @full)
27
+ InspectWrapper.create(contract, full: @full)
22
28
  end
23
29
  end
24
30
 
@@ -26,14 +32,14 @@ module Contracts
26
32
  def hash_contract(hash)
27
33
  @full = true # Complex values output completely, overriding @full
28
34
  hash.inject({}) do |repr, (k, v)|
29
- repr.merge(k => InspectWrapper.create(contract(v), @full))
35
+ repr.merge(k => InspectWrapper.create(contract(v), full: @full))
30
36
  end
31
37
  end
32
38
 
33
39
  # Formats Array contracts.
34
40
  def array_contract(array)
35
41
  @full = true
36
- array.map { |v| InspectWrapper.create(contract(v), @full) }
42
+ array.map { |v| InspectWrapper.create(contract(v), full: @full) }
37
43
  end
38
44
  end
39
45
 
@@ -42,8 +48,8 @@ module Contracts
42
48
  module InspectWrapper
43
49
  # InspectWrapper is a factory, will never be an instance
44
50
  # @return [ClassInspectWrapper, ObjectInspectWrapper]
45
- def self.create(value, full = true)
46
- if value.class == Class
51
+ def self.create(value, full: true)
52
+ if value.instance_of?(Class)
47
53
  ClassInspectWrapper
48
54
  else
49
55
  ObjectInspectWrapper
@@ -66,6 +72,7 @@ module Contracts
66
72
  return @value.inspect if empty_val?
67
73
  return @value.to_s if plain?
68
74
  return delim(@value.to_s) if useful_to_s?
75
+
69
76
  useful_inspect
70
77
  end
71
78
 
@@ -96,7 +103,7 @@ module Contracts
96
103
  end
97
104
 
98
105
  def useful_to_s?
99
- # Useless to_s value or no custom to_s behavious defined
106
+ # Useless to_s value or no custom to_s behaviour defined
100
107
  !empty_to_s? && custom_to_s?
101
108
  end
102
109
 
@@ -125,7 +132,7 @@ module Contracts
125
132
  include InspectWrapper
126
133
 
127
134
  def custom_to_s?
128
- !@value.to_s.match(/#\<\w+:.+\>/)
135
+ !@value.to_s.match(/#<\w+:.+>/)
129
136
  end
130
137
 
131
138
  def useful_inspect
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  module Invariants
3
5
  def self.included(base)
@@ -46,10 +48,12 @@ module Contracts
46
48
  def check_on(target, method)
47
49
  return if target.instance_eval(&@condition)
48
50
 
49
- self.class.failure_callback(:expected => expected,
50
- :actual => false,
51
- :target => target,
52
- :method => method)
51
+ self.class.failure_callback({
52
+ expected: expected,
53
+ actual: false,
54
+ target: target,
55
+ method: method,
56
+ })
53
57
  end
54
58
 
55
59
  def self.failure_callback(data)