contracts 0.16.1 → 0.17.1

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.
@@ -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)