contracts 0.16.1 → 0.17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f008c5c8ab0f0e8d60c14f29413e2b6b262f53b53d9bdc34a618d438a3065a2
4
- data.tar.gz: 812d80e1696cfe92350c4402d5a2368589d7ecca4b1849f6ca110a8afe239d18
3
+ metadata.gz: cbf4bff58a593998c5902920fff3c0a808ca5eb97ec9cad3646df370435421c7
4
+ data.tar.gz: 02e5cffce164a25ae463b22e9f8b85c810d50c9c2875d09a3e83751afebac012
5
5
  SHA512:
6
- metadata.gz: 37e8c58c78748244de26e797301d7510a16263a1373a45b69b01749f34d8a0990c60b9ec02adf49b745abf1493911e61596b90d37af60663abd84e8f5c5273d5
7
- data.tar.gz: 502421a171fc8284295c9253edb405321c05ef871d0aafb52b80e9b7cf5099f216b7f71ca3dce696e582854feedaad80faa00394a0fb903760fa732c34cd87a0
6
+ metadata.gz: c6911f4383b0140c40bc08c838a3044897c3c9ac3c577fae4b3806879cbfed12f044f8ee1336305b1599c10af1bfb5aabe9ae2e8f1f9ffd94774d4de789e8402
7
+ data.tar.gz: 25c8ecfe04e846ba55d87b33a43e0a38422440d0a963066ac67217edd8ebc396cbb2309b50704ea6f4dc4609bacbfb3fb3b501ef0a40210fe1877df5c6fbb7f8
@@ -22,7 +22,7 @@ jobs:
22
22
  os:
23
23
  - ubuntu
24
24
  ruby:
25
- - "2.7"
25
+ - "3.0"
26
26
  runs-on: ${{ matrix.os }}-latest
27
27
  steps:
28
28
  - name: Checkout
@@ -22,17 +22,11 @@ jobs:
22
22
  os:
23
23
  - ubuntu
24
24
  ruby:
25
- - "2.1"
26
- - "2.2"
27
- - "2.3"
28
- - "2.4"
29
- - "2.5"
30
- - "2.6"
31
- - "2.7"
25
+ - "3.0"
32
26
  test_command: ["bundle exec rspec && bundle exec cucumber"]
33
27
  include:
34
28
  - os: ubuntu
35
- ruby: "2.4.2"
29
+ ruby: "3.0"
36
30
  test_command: "bundle exec rspec"
37
31
  runs-on: ${{ matrix.os }}-latest
38
32
  steps:
data/CHANGELOG.markdown CHANGED
@@ -1,6 +1,8 @@
1
1
 
2
2
  ## [v0.16.1] - 2021-04-17
3
3
 
4
+ [v0.16.1]: https://github.com/egonSchiele/contracts.ruby/compare/v0.16.0...v0.16.1
5
+
4
6
  - Enhancement: Pretty-print contracts in error messages - [Corey Farwell](https://github.com/frewsxcv) [#289](https://github.com/egonSchiele/contracts.ruby/pull/289)
5
7
  - Bugfix: Fix `attr_accessor_with_contract` with multiple attribute names input - [Kevin Yeh](https://github.com/kyeah) [#259](https://github.com/egonSchiele/contracts.ruby/pull/259)
6
8
  - Bugfix: Fix "stack level too deep" in CI builds - [md-work](https://github.com/md-work) [#283](https://github.com/egonSchiele/contracts.ruby/pull/283)
data/Gemfile CHANGED
@@ -1,17 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gemspec
4
6
 
5
7
  group :test do
6
- gem "rspec"
7
8
  gem "aruba"
8
9
  gem "cucumber", "~> 1.3.20"
9
- gem "rubocop", "~> 0.41.2" if RUBY_VERSION >= "2"
10
+ gem "rspec"
11
+
12
+ gem "rubocop", ">= 1.0.0"
13
+ gem "rubocop-performance", ">= 1.0.0"
10
14
  end
11
15
 
12
16
  group :development do
13
- gem "relish"
14
17
  gem "method_profiler"
15
- gem "ruby-prof"
16
18
  gem "rake"
19
+ gem "relish"
20
+ gem "ruby-prof"
17
21
  end
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  task :default => [:spec]
2
4
 
3
5
  task :add_tag do
data/contracts.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path(File.join(__FILE__, "../lib/contracts/version"))
2
4
 
3
5
  Gem::Specification.new do |s|
@@ -10,6 +12,7 @@ Gem::Specification.new do |s|
10
12
  s.files = `git ls-files`.split("\n")
11
13
  s.homepage = "https://github.com/egonSchiele/contracts.ruby"
12
14
  s.license = "BSD-2-Clause"
15
+ s.required_ruby_version = [">= 3.0", "< 4"]
13
16
  s.post_install_message = "
14
17
  0.16.x will be the supporting Ruby 2.x and be feature frozen (only fixes will be released)
15
18
  For Ruby 3.x use 0.17.x or later (might not be released yet)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "aruba/cucumber"
2
4
  require "aruba/jruby" if RUBY_PLATFORM == "java"
3
5
 
data/lib/contracts.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "contracts/attrs"
2
4
  require "contracts/builtin_contracts"
3
5
  require "contracts/decorators"
@@ -51,7 +53,9 @@ class Contract < Contracts::Decorator
51
53
  end
52
54
 
53
55
  attr_reader :args_contracts, :ret_contract, :klass, :method
56
+
54
57
  def initialize(klass, method, *contracts)
58
+ super(klass, method)
55
59
  unless contracts.last.is_a?(Hash)
56
60
  unless contracts.one?
57
61
  fail %{
@@ -93,17 +97,17 @@ class Contract < Contracts::Decorator
93
97
  last_contract = args_contracts.last
94
98
  penultimate_contract = args_contracts[-2]
95
99
  @has_options_contract = if @has_proc_contract
96
- penultimate_contract.is_a?(Hash) || penultimate_contract.is_a?(Contracts::Builtin::KeywordArgs)
100
+ penultimate_contract.is_a?(Contracts::Builtin::KeywordArgs)
97
101
  else
98
- last_contract.is_a?(Hash) || last_contract.is_a?(Contracts::Builtin::KeywordArgs)
102
+ last_contract.is_a?(Contracts::Builtin::KeywordArgs)
99
103
  end
100
104
  # ===
101
105
 
102
106
  @klass, @method = klass, method
103
107
  end
104
108
 
105
- def pretty_contract c
106
- c.is_a?(Class) ? c.name : c.class.name
109
+ def pretty_contract contract
110
+ contract.is_a?(Class) ? contract.name : contract.class.name
107
111
  end
108
112
 
109
113
  def to_s
@@ -130,15 +134,15 @@ class Contract < Contracts::Decorator
130
134
  expected_prefix = "Expected: "
131
135
  expected_value = Contracts::Support.indent_string(
132
136
  Contracts::Formatters::Expected.new(data[:contract]).contract.pretty_inspect,
133
- expected_prefix.length
137
+ expected_prefix.length,
134
138
  ).strip
135
- expected_line = expected_prefix + expected_value + ","
139
+ expected_line = "#{expected_prefix}#{expected_value},"
136
140
 
137
141
  # Actual
138
142
  actual_prefix = "Actual: "
139
143
  actual_value = Contracts::Support.indent_string(
140
144
  data[:arg].pretty_inspect,
141
- actual_prefix.length
145
+ actual_prefix.length,
142
146
  ).strip
143
147
  actual_line = actual_prefix + actual_value
144
148
 
@@ -157,16 +161,19 @@ class Contract < Contracts::Decorator
157
161
  position_value = Contracts::Support.method_position(data[:method])
158
162
  position_line = position_prefix + position_value
159
163
 
160
- header +
161
- "\n" +
164
+ [
165
+ header,
162
166
  Contracts::Support.indent_string(
163
- [expected_line,
164
- actual_line,
165
- value_line,
166
- contract_line,
167
- position_line].join("\n"),
168
- indent_amount
169
- )
167
+ [
168
+ expected_line,
169
+ actual_line,
170
+ value_line,
171
+ contract_line,
172
+ position_line,
173
+ ].join("\n"),
174
+ indent_amount,
175
+ ),
176
+ ].join("\n")
170
177
  end
171
178
 
172
179
  # Callback for when a contract fails. By default it raises
@@ -182,7 +189,7 @@ class Contract < Contracts::Decorator
182
189
  # puts failure_msg(data)
183
190
  # exit
184
191
  # end
185
- def self.failure_callback(data, use_pattern_matching = true)
192
+ def self.failure_callback(data, use_pattern_matching: true)
186
193
  if data[:contracts].pattern_match? && use_pattern_matching
187
194
  return DEFAULT_FAILURE_CALLBACK.call(data)
188
195
  end
@@ -242,19 +249,21 @@ class Contract < Contracts::Decorator
242
249
  # returns true if it appended nil
243
250
  def maybe_append_block! args, blk
244
251
  return false unless @has_proc_contract && !blk &&
245
- (@args_contract_index || args.size < args_contracts.size)
252
+ (@args_contract_index || args.size < args_contracts.size)
253
+
246
254
  args << nil
247
255
  true
248
256
  end
249
257
 
250
258
  # Same thing for when we have named params but didn't pass any in.
251
259
  # returns true if it appended nil
252
- def maybe_append_options! args, blk
260
+ def maybe_append_options! args, kargs, blk
253
261
  return false unless @has_options_contract
254
- if @has_proc_contract && (args_contracts[-2].is_a?(Hash) || args_contracts[-2].is_a?(Contracts::Builtin::KeywordArgs)) && !args[-2].is_a?(Hash)
255
- args.insert(-2, {})
256
- elsif (args_contracts[-1].is_a?(Hash) || args_contracts[-1].is_a?(Contracts::Builtin::KeywordArgs)) && !args[-1].is_a?(Hash)
257
- args << {}
262
+
263
+ if @has_proc_contract && args_contracts[-2].is_a?(Contracts::Builtin::KeywordArgs)
264
+ args.insert(-2, kargs)
265
+ elsif args_contracts[-1].is_a?(Contracts::Builtin::KeywordArgs)
266
+ args << kargs
258
267
  end
259
268
  true
260
269
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  module Attrs
3
5
  def attr_reader_with_contract(*names, contract)
@@ -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
 
@@ -96,6 +98,7 @@ module Contracts
96
98
  # Example: <tt>Or[Fixnum, Float]</tt>
97
99
  class Or < CallableClass
98
100
  def initialize(*vals)
101
+ super()
99
102
  @vals = vals
100
103
  end
101
104
 
@@ -107,9 +110,11 @@ 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
 
@@ -118,6 +123,7 @@ module Contracts
118
123
  # Example: <tt>Xor[Fixnum, Float]</tt>
119
124
  class Xor < CallableClass
120
125
  def initialize(*vals)
126
+ super()
121
127
  @vals = vals
122
128
  end
123
129
 
@@ -130,9 +136,11 @@ 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
 
@@ -141,6 +149,7 @@ module Contracts
141
149
  # Example: <tt>And[Fixnum, 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,19 @@
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
15
  # Explicitly append options={} if Hash contract is present
14
- maybe_append_options!(args, blk)
16
+ kargs_appended = maybe_append_options!(args, kargs, blk)
15
17
 
16
18
  # Loop forward validating the arguments up to the splat (if there is one)
17
19
  (@args_contract_index || args.size).times do |i|
@@ -20,14 +22,16 @@ module Contracts
20
22
  validator = @args_validators[i]
21
23
 
22
24
  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}
25
+ data = {
26
+ arg: arg,
27
+ contract: contract,
28
+ class: klass,
29
+ method: method,
30
+ contracts: self,
31
+ arg_pos: i+1,
32
+ total_args: args.size,
33
+ return_value: false,
34
+ }
31
35
  return ParamContractError.new("as return value", data) if returns
32
36
  return unless Contract.failure_callback(data)
33
37
  end
@@ -57,14 +61,18 @@ module Contracts
57
61
  validator = @args_validators[args_contracts.size - 1 - j]
58
62
 
59
63
  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)
64
+ # rubocop:disable Style/SoleNestedConditional
65
+ return unless Contract.failure_callback({
66
+ :arg => arg,
67
+ :contract => contract,
68
+ :class => klass,
69
+ :method => method,
70
+ :contracts => self,
71
+ :arg_pos => i - 1,
72
+ :total_args => args.size,
73
+ :return_value => false,
74
+ })
75
+ # rubocop:enable Style/SoleNestedConditional
68
76
  end
69
77
 
70
78
  if contract.is_a?(Contracts::Func)
@@ -76,24 +84,27 @@ module Contracts
76
84
  # If we put the block into args for validating, restore the args
77
85
  # OR if we added a fake nil at the end because a block wasn't passed in.
78
86
  args.slice!(-1) if blk || nil_block_appended
87
+ args.slice!(-1) if kargs_appended
79
88
  result = if method.respond_to?(:call)
80
89
  # proc, block, lambda, etc
81
- method.call(*args, &blk)
90
+ method.call(*args, **kargs, &blk)
82
91
  else
83
92
  # original method name reference
84
93
  # Don't reassign blk, else Travis CI shows "stack level too deep".
85
94
  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)
95
+ target_blk = lambda { |*params| blk.call(*params) } if blk.is_a?(Contract)
96
+ method.send_to(this, *args, **kargs, &target_blk)
88
97
  end
89
98
 
90
99
  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)
100
+ Contract.failure_callback({
101
+ arg: result,
102
+ contract: ret_contract,
103
+ class: klass,
104
+ method: method,
105
+ contracts: self,
106
+ return_value: true,
107
+ })
97
108
  end
98
109
 
99
110
  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
  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
  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
  # @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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pp"
2
4
 
3
5
  module Contracts
@@ -7,18 +9,19 @@ module Contracts
7
9
  class Expected
8
10
  # @param full [Boolean] if false only unique `to_s` values will be output,
9
11
  # non unique values become empty string.
10
- def initialize(contract, full = true)
12
+ def initialize(contract, full: true)
11
13
  @contract, @full = contract, full
12
14
  end
13
15
 
14
16
  # Formats any type of Contract.
15
17
  def contract(contract = @contract)
16
- if contract.is_a?(Hash)
18
+ case contract
19
+ when Hash
17
20
  hash_contract(contract)
18
- elsif contract.is_a?(Array)
21
+ when Array
19
22
  array_contract(contract)
20
23
  else
21
- InspectWrapper.create(contract, @full)
24
+ InspectWrapper.create(contract, full: @full)
22
25
  end
23
26
  end
24
27
 
@@ -26,14 +29,14 @@ module Contracts
26
29
  def hash_contract(hash)
27
30
  @full = true # Complex values output completely, overriding @full
28
31
  hash.inject({}) do |repr, (k, v)|
29
- repr.merge(k => InspectWrapper.create(contract(v), @full))
32
+ repr.merge(k => InspectWrapper.create(contract(v), full: @full))
30
33
  end
31
34
  end
32
35
 
33
36
  # Formats Array contracts.
34
37
  def array_contract(array)
35
38
  @full = true
36
- array.map { |v| InspectWrapper.create(contract(v), @full) }
39
+ array.map { |v| InspectWrapper.create(contract(v), full: @full) }
37
40
  end
38
41
  end
39
42
 
@@ -42,8 +45,8 @@ module Contracts
42
45
  module InspectWrapper
43
46
  # InspectWrapper is a factory, will never be an instance
44
47
  # @return [ClassInspectWrapper, ObjectInspectWrapper]
45
- def self.create(value, full = true)
46
- if value.class == Class
48
+ def self.create(value, full: true)
49
+ if value.instance_of?(Class)
47
50
  ClassInspectWrapper
48
51
  else
49
52
  ObjectInspectWrapper
@@ -66,6 +69,7 @@ module Contracts
66
69
  return @value.inspect if empty_val?
67
70
  return @value.to_s if plain?
68
71
  return delim(@value.to_s) if useful_to_s?
72
+
69
73
  useful_inspect
70
74
  end
71
75
 
@@ -96,7 +100,7 @@ module Contracts
96
100
  end
97
101
 
98
102
  def useful_to_s?
99
- # Useless to_s value or no custom to_s behavious defined
103
+ # Useless to_s value or no custom to_s behaviour defined
100
104
  !empty_to_s? && custom_to_s?
101
105
  end
102
106
 
@@ -125,7 +129,7 @@ module Contracts
125
129
  include InspectWrapper
126
130
 
127
131
  def custom_to_s?
128
- !@value.to_s.match(/#\<\w+:.+\>/)
132
+ !@value.to_s.match(/#<\w+:.+>/)
129
133
  end
130
134
 
131
135
  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)
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  # Handles class and instance methods addition
3
5
  # Represents single such method
4
6
  class MethodHandler
5
7
  METHOD_REFERENCE_FACTORY = {
6
8
  :class_methods => SingletonMethodReference,
7
- :instance_methods => MethodReference
9
+ :instance_methods => MethodReference,
8
10
  }
9
11
 
10
12
  RAW_METHOD_STRATEGY = {
11
13
  :class_methods => lambda { |target, name| target.method(name) },
12
- :instance_methods => lambda { |target, name| target.instance_method(name) }
14
+ :instance_methods => lambda { |target, name| target.instance_method(name) },
13
15
  }
14
16
 
15
17
  # Creates new instance of MethodHandler
@@ -78,11 +80,13 @@ module Contracts
78
80
 
79
81
  def pattern_matching?
80
82
  return @_pattern_matching if defined?(@_pattern_matching)
83
+
81
84
  @_pattern_matching = decorated_methods.any? { |x| x.method != method_reference }
82
85
  end
83
86
 
84
87
  def mark_pattern_matching_decorators
85
88
  return unless pattern_matching?
89
+
86
90
  decorated_methods.each(&:pattern_match!)
87
91
  end
88
92
 
@@ -107,13 +111,13 @@ module Contracts
107
111
  current_engine = engine
108
112
 
109
113
  # We are gonna redefine original method here
110
- method_reference.make_definition(target) do |*args, &blk|
114
+ method_reference.make_definition(target) do |*args, **kargs, &blk|
111
115
  engine = current_engine.nearest_decorated_ancestor
112
116
 
113
117
  # If we weren't able to find any ancestor that has decorated methods
114
118
  # FIXME : this looks like untested code (commenting it out doesn't make specs red)
115
119
  unless engine
116
- fail "Couldn't find decorator for method " + self.class.name + ":#{name}.\nDoes this method look correct to you? If you are using contracts from rspec, rspec wraps classes in it's own class.\nLook at the specs for contracts.ruby as an example of how to write contracts in this case."
120
+ fail "Couldn't find decorator for method #{self.class.name}:#{name}.\nDoes this method look correct to you? If you are using contracts from rspec, rspec wraps classes in it's own class.\nLook at the specs for contracts.ruby as an example of how to write contracts in this case."
117
121
  end
118
122
 
119
123
  # Fetch decorated methods out of the contracts engine
@@ -130,17 +134,20 @@ module Contracts
130
134
  last_error = nil
131
135
 
132
136
  decorated_methods.each do |decorated_method|
133
- result = decorated_method.call_with_inner(true, self, *args, &blk)
137
+ result = decorated_method.call_with_inner(true, self, *args, **kargs, &blk)
134
138
  return result unless result.is_a?(ParamContractError)
139
+
135
140
  last_error = result
136
141
  end
137
142
 
138
143
  begin
139
- if ::Contract.failure_callback(last_error.data, false)
140
- decorated_methods.last.call_with_inner(false, self, *args, &blk)
144
+ if ::Contract.failure_callback(last_error&.data, use_pattern_matching: false)
145
+ decorated_methods.last.call_with_inner(false, self, *args, **kargs, &blk)
141
146
  end
147
+ # rubocop:disable Naming/RescuedExceptionsVariableName
142
148
  rescue expected_error => final_error
143
149
  raise final_error.to_contract_error
150
+ # rubocop:enable Naming/RescuedExceptionsVariableName
144
151
  end
145
152
  end
146
153
  end
@@ -173,7 +180,8 @@ https://github.com/egonSchiele/contracts.ruby/issues
173
180
 
174
181
  return if matched.empty?
175
182
 
176
- fail ContractError.new(%{
183
+ fail ContractError.new(
184
+ %{
177
185
  It looks like you are trying to use pattern-matching, but
178
186
  multiple definitions for function '#{method_name}' have the same
179
187
  contract for input parameters:
@@ -181,7 +189,9 @@ contract for input parameters:
181
189
  #{(matched + [decorator]).map(&:to_s).join("\n")}
182
190
 
183
191
  Each definition needs to have a different contract for the parameters.
184
- }, {})
192
+ },
193
+ {},
194
+ )
185
195
  end
186
196
  end
187
197
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  # MethodReference represents original method reference that was
3
5
  # decorated by contracts.ruby. Used for instance methods.
@@ -39,8 +41,8 @@ module Contracts
39
41
 
40
42
  # Calls original method on specified `this` argument with
41
43
  # specified arguments `args` and block `&blk`.
42
- def send_to(this, *args, &blk)
43
- this.send(aliased_name, *args, &blk)
44
+ def send_to(this, *args, **kargs, &blk)
45
+ this.send(aliased_name, *args, **kargs, &blk)
44
46
  end
45
47
 
46
48
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  module Support
3
5
  class << self
@@ -8,7 +10,7 @@ module Contracts
8
10
  if file.nil? || line.nil?
9
11
  ""
10
12
  else
11
- file + ":" + line.to_s
13
+ "#{file}:#{line}"
12
14
  end
13
15
  end
14
16
 
@@ -45,7 +47,7 @@ module Contracts
45
47
  def indent_string(string, amount)
46
48
  string.gsub(
47
49
  /^(?!$)/,
48
- (string[/^[ \t]/] || " ") * amount
50
+ (string[/^[ \t]/] || " ") * amount,
49
51
  )
50
52
  end
51
53
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
4
  module Validators
3
5
  DEFAULT_VALIDATOR_STRATEGIES = {
@@ -9,6 +11,7 @@ module Contracts
9
11
  Array => lambda do |contract|
10
12
  lambda do |arg|
11
13
  return false unless arg.is_a?(Array) && arg.length == contract.length
14
+
12
15
  arg.zip(contract).all? do |_arg, _contract|
13
16
  Contract.valid?(_arg, _contract)
14
17
  end
@@ -19,6 +22,7 @@ module Contracts
19
22
  Hash => lambda do |contract|
20
23
  lambda do |arg|
21
24
  return false unless arg.is_a?(Hash)
25
+
22
26
  contract.keys.all? do |k|
23
27
  Contract.valid?(arg[k], contract[k])
24
28
  end
@@ -59,7 +63,7 @@ module Contracts
59
63
 
60
64
  :default => lambda do |contract|
61
65
  lambda { |arg| contract == arg }
62
- end
66
+ end,
63
67
  }.freeze
64
68
 
65
69
  # Allows to override validator with custom one.
@@ -90,7 +94,7 @@ module Contracts
90
94
  else
91
95
  if contract.respond_to? :valid?
92
96
  :valid
93
- elsif klass == Class || klass == Module
97
+ elsif [Class, Module].include?(klass)
94
98
  :class
95
99
  else
96
100
  :default
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Contracts
2
- VERSION = "0.16.1"
4
+ VERSION = "0.17"
3
5
  end
@@ -376,10 +376,6 @@ RSpec.describe "Contracts:" do
376
376
  fails { @o.hash_keywordargs(:hash => nil) }
377
377
  fails { @o.hash_keywordargs(:hash => 1) }
378
378
  end
379
-
380
- it "should pass if a method is overloaded with non-KeywordArgs" do
381
- passes { @o.person_keywordargs("name", 10) }
382
- end
383
379
  end
384
380
 
385
381
  describe "Optional:" do
@@ -405,15 +401,15 @@ RSpec.describe "Contracts:" do
405
401
  end
406
402
 
407
403
  context "given a fulfilled contract" do
408
- it { expect(@o.gives_max_value(:panda => 1, :bamboo => 2)).to eq(2) }
409
- it { expect(@o.pretty_gives_max_value(:panda => 1, :bamboo => 2)).to eq(2) }
404
+ it { expect(@o.gives_max_value({ :panda => 1, :bamboo => 2 })).to eq(2) }
405
+ it { expect(@o.pretty_gives_max_value({ :panda => 1, :bamboo => 2 })).to eq(2) }
410
406
  end
411
407
 
412
408
  context "given an unfulfilled contract" do
413
- it { fails { @o.gives_max_value(:panda => "1", :bamboo => "2") } }
409
+ it { fails { @o.gives_max_value({ :panda => "1", :bamboo => "2" }) } }
414
410
  it { fails { @o.gives_max_value(nil) } }
415
411
  it { fails { @o.gives_max_value(1) } }
416
- it { fails { @o.pretty_gives_max_value(:panda => "1", :bamboo => "2") } }
412
+ it { fails { @o.pretty_gives_max_value({ :panda => "1", :bamboo => "2" }) } }
417
413
  end
418
414
 
419
415
  describe "#to_s" do
@@ -430,25 +426,25 @@ RSpec.describe "Contracts:" do
430
426
  describe "StrictHash:" do
431
427
  context "when given an exact correct input" do
432
428
  it "does not raise an error" do
433
- passes { @o.strict_person(:name => "calvin", :age => 10) }
429
+ passes { @o.strict_person({ :name => "calvin", :age => 10 }) }
434
430
  end
435
431
  end
436
432
 
437
433
  context "when given an input with correct keys but wrong types" do
438
434
  it "raises an error" do
439
- fails { @o.strict_person(:name => "calvin", :age => "10") }
435
+ fails { @o.strict_person({ :name => "calvin", :age => "10" }) }
440
436
  end
441
437
  end
442
438
 
443
439
  context "when given an input with missing keys" do
444
440
  it "raises an error" do
445
- fails { @o.strict_person(:name => "calvin") }
441
+ fails { @o.strict_person({ :name => "calvin" }) }
446
442
  end
447
443
  end
448
444
 
449
445
  context "when given an input with extra keys" do
450
446
  it "raises an error" do
451
- fails { @o.strict_person(:name => "calvin", :age => 10, :soft => true) }
447
+ fails { @o.strict_person({ :name => "calvin", :age => 10, :soft => true }) }
452
448
  end
453
449
  end
454
450
 
@@ -349,19 +349,19 @@ RSpec.describe "Contracts:" do
349
349
 
350
350
  describe "Hashes" do
351
351
  it "should pass for exact correct input" do
352
- expect { @o.person(:name => "calvin", :age => 10) }.to_not raise_error
352
+ expect { @o.person({ :name => "calvin", :age => 10 }) }.to_not raise_error
353
353
  end
354
354
 
355
355
  it "should pass even if some keys don't have contracts" do
356
- expect { @o.person(:name => "calvin", :age => 10, :foo => "bar") }.to_not raise_error
356
+ expect { @o.person({ :name => "calvin", :age => 10, :foo => "bar" }) }.to_not raise_error
357
357
  end
358
358
 
359
359
  it "should fail if a key with a contract on it isn't provided" do
360
- expect { @o.person(:name => "calvin") }.to raise_error(ContractError)
360
+ expect { @o.person({ :name => "calvin" }) }.to raise_error(ContractError)
361
361
  end
362
362
 
363
363
  it "should fail for incorrect input" do
364
- expect { @o.person(:name => 50, :age => 10) }.to raise_error(ContractError)
364
+ expect { @o.person({ :name => 50, :age => 10 }) }.to raise_error(ContractError)
365
365
  end
366
366
  end
367
367
 
@@ -612,16 +612,19 @@ RSpec.describe "Contracts:" do
612
612
 
613
613
  it "should contain to_s representation within a Hash contract" do
614
614
  expect do
615
- @o.hash_complex_contracts(:rigged => "bad")
615
+ @o.hash_complex_contracts({ :rigged => "bad" })
616
616
  end.to raise_error(ContractError, not_s(delim "TrueClass or FalseClass"))
617
617
  end
618
618
 
619
619
  it "should contain to_s representation within a nested Hash contract" do
620
620
  expect do
621
- @o.nested_hash_complex_contracts(:rigged => true,
622
- :contents => {
623
- :kind => 0,
624
- :total => 42 })
621
+ @o.nested_hash_complex_contracts({
622
+ :rigged => true,
623
+ :contents => {
624
+ :kind => 0,
625
+ :total => 42,
626
+ },
627
+ })
625
628
  end.to raise_error(ContractError, not_s(delim "String or Symbol"))
626
629
  end
627
630
 
@@ -120,16 +120,11 @@ class GenericExample
120
120
  end
121
121
 
122
122
  Contract C::KeywordArgs[:name => String, :age => Fixnum] => nil
123
- def person_keywordargs(data)
124
- end
125
-
126
- # Testing overloaded method
127
- Contract String, Fixnum => nil
128
- def person_keywordargs(name, age)
123
+ def person_keywordargs(name: "name", age: 10)
129
124
  end
130
125
 
131
126
  Contract C::KeywordArgs[:hash => C::HashOf[Symbol, C::Num]] => nil
132
- def hash_keywordargs(data)
127
+ def hash_keywordargs(hash:)
133
128
  end
134
129
 
135
130
  Contract (/foo/) => nil
@@ -30,15 +30,15 @@ RSpec.describe Contract do
30
30
  obj = klass.new
31
31
 
32
32
  expect do
33
- obj.something(:a => 35, :b => "hello")
33
+ obj.something({ :a => 35, :b => "hello" })
34
34
  end.to raise_error(ContractError)
35
35
 
36
36
  expect do
37
- obj.something(
37
+ obj.something({
38
38
  :a => 35,
39
39
  :b => "hello",
40
40
  :it_is_a_hash => true
41
- )
41
+ })
42
42
  end.not_to raise_error
43
43
  end
44
44
 
@@ -1,10 +1,10 @@
1
1
  class GenericExample
2
- Contract C::Args[String], { repeat: C::Maybe[C::Num] } => C::ArrayOf[String]
2
+ Contract C::Args[String], C::KeywordArgs[ repeat: C::Maybe[C::Num] ] => C::ArrayOf[String]
3
3
  def splat_then_optional_named(*vals, repeat: 2)
4
4
  vals.map { |v| v * repeat }
5
5
  end
6
6
 
7
- Contract ({foo: C::Nat}) => nil
7
+ Contract C::KeywordArgs[ foo: C::Nat ] => nil
8
8
  def nat_test_with_kwarg(foo: 10)
9
9
  end
10
10
 
@@ -34,7 +34,7 @@ RSpec.describe "Contract validators" do
34
34
 
35
35
  describe "within a hash" do
36
36
  it "should pass for a matching string" do
37
- expect { o.hash_containing_foo(:host => "foo.example.org") }.to_not raise_error
37
+ expect { o.hash_containing_foo({ :host => "foo.example.org" }) }.to_not raise_error
38
38
  end
39
39
  end
40
40
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contracts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.1
4
+ version: '0.17'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aditya Bhargava
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-17 00:00:00.000000000 Z
11
+ date: 2021-05-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: This library provides contracts for Ruby. Contracts let you clearly express
14
14
  how your code behaves, and free you from writing tons of boilerplate, defensive
@@ -121,7 +121,10 @@ required_ruby_version: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: '3.0'
125
+ - - "<"
126
+ - !ruby/object:Gem::Version
127
+ version: '4'
125
128
  required_rubygems_version: !ruby/object:Gem::Requirement
126
129
  requirements:
127
130
  - - ">="