delivered 0.3.0 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2cc2a244302acba122bf449caa221233453955568ca1a7c42f9a4c66e75093a
4
- data.tar.gz: 2c6033b4adee7756b8fb7bbb9ff0cac83bbe7f42e35220eb9a24857c17e2aa7e
3
+ metadata.gz: b22fc67aefea021f5a096de509e51e3ab69eb3b256388eebd764d194c23ed72d
4
+ data.tar.gz: 9fe11a083f14b7dba924435d66fce6aff92d93819b7e7ef5a5c1b0c41fb38c9c
5
5
  SHA512:
6
- metadata.gz: 3adf4d4bb048a39a00e331e598814998fffe28673702d08bba181cd574f521b3f2d164725c5fe61d349c85c960c32a59d98bb8855ff0f26db6ad5bd74123ae4e
7
- data.tar.gz: 735bde97671a2e2d69238b5363de35971ecb1a7ab4003e2352c560231bd461b712a3e652bd01c9b02cf067d5d8b2a856baf6b8709621c5f8e34b0eb94c692e9f
6
+ metadata.gz: a07fe7c33a96cccc120ec61e999e45704c771c15c170f6b90e263e831bd0f80a93e76d75ff07277447eac18cd9d5defa14d7ea816f6767ecacc7f794eea393ca
7
+ data.tar.gz: 0fcf19ef982ddb7eae98cedac03723d4a66eda8b88411840e4435137bccf467141db2e4b180ed1876e660caaca7d9746e380c55afa79553d559b925840ccb409
data/Gemfile CHANGED
@@ -13,6 +13,5 @@ group :test do
13
13
  end
14
14
 
15
15
  group :development, :test do
16
- gem 'amazing_print'
17
16
  gem 'debug', platforms: %i[mri mingw x64_mingw]
18
17
  end
data/Gemfile.lock CHANGED
@@ -1,62 +1,66 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- delivered (0.3.0)
4
+ delivered (0.5.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- amazing_print (1.6.0)
10
9
  ast (2.4.2)
11
- debug (1.9.2)
10
+ date (3.4.1)
11
+ debug (1.10.0)
12
12
  irb (~> 1.10)
13
13
  reline (>= 0.3.8)
14
- io-console (0.7.2)
15
- irb (1.13.1)
14
+ io-console (0.8.0)
15
+ irb (1.15.1)
16
+ pp (>= 0.6.0)
16
17
  rdoc (>= 4.0.0)
17
18
  reline (>= 0.4.2)
18
- json (2.7.2)
19
- language_server-protocol (3.17.0.3)
20
- parallel (1.24.0)
21
- parser (3.3.1.0)
19
+ json (2.10.1)
20
+ language_server-protocol (3.17.0.4)
21
+ lint_roller (1.1.0)
22
+ parallel (1.26.3)
23
+ parser (3.3.7.1)
22
24
  ast (~> 2.4.1)
23
25
  racc
24
- psych (5.1.2)
26
+ pp (0.6.2)
27
+ prettyprint
28
+ prettyprint (0.2.0)
29
+ psych (5.2.3)
30
+ date
25
31
  stringio
26
- racc (1.7.3)
32
+ racc (1.8.1)
27
33
  rainbow (3.1.1)
28
- rdoc (6.6.3.1)
34
+ rdoc (6.12.0)
29
35
  psych (>= 4.0.0)
30
- regexp_parser (2.9.2)
31
- reline (0.5.7)
36
+ regexp_parser (2.10.0)
37
+ reline (0.6.0)
32
38
  io-console (~> 0.5)
33
- rexml (3.2.8)
34
- strscan (>= 3.0.9)
35
- rubocop (1.63.5)
39
+ rubocop (1.72.2)
36
40
  json (~> 2.3)
37
- language_server-protocol (>= 3.17.0)
41
+ language_server-protocol (~> 3.17.0.2)
42
+ lint_roller (~> 1.1.0)
38
43
  parallel (~> 1.10)
39
44
  parser (>= 3.3.0.2)
40
45
  rainbow (>= 2.2.2, < 4.0)
41
- regexp_parser (>= 1.8, < 3.0)
42
- rexml (>= 3.2.5, < 4.0)
43
- rubocop-ast (>= 1.31.1, < 2.0)
46
+ regexp_parser (>= 2.9.3, < 3.0)
47
+ rubocop-ast (>= 1.38.0, < 2.0)
44
48
  ruby-progressbar (~> 1.7)
45
- unicode-display_width (>= 2.4.0, < 3.0)
46
- rubocop-ast (1.31.3)
49
+ unicode-display_width (>= 2.4.0, < 4.0)
50
+ rubocop-ast (1.38.0)
47
51
  parser (>= 3.3.1.0)
48
52
  ruby-progressbar (1.13.0)
49
- stringio (3.1.0)
50
- strscan (3.1.0)
51
- sus (0.25.0)
52
- unicode-display_width (2.5.0)
53
+ stringio (3.1.5)
54
+ sus (0.32.0)
55
+ unicode-display_width (3.1.4)
56
+ unicode-emoji (~> 4.0, >= 4.0.4)
57
+ unicode-emoji (4.0.4)
53
58
 
54
59
  PLATFORMS
55
60
  arm64-darwin-22
56
61
  ruby
57
62
 
58
63
  DEPENDENCIES
59
- amazing_print
60
64
  debug
61
65
  delivered!
62
66
  rubocop
data/README.md CHANGED
@@ -26,6 +26,23 @@ end
26
26
  If an invalid argument is given to `User#create`, for example, if `age` is a `String` instead of
27
27
  the required `Integer`, a `Delivered::ArgumentError` exception will be raised.
28
28
 
29
+ Delivered also provides a handy `verify!` method to verify the type of any value.
30
+
31
+ ```ruby
32
+ Delivered.verify! 1, Integer
33
+ Delivered.verify! :yes, T.Any(:yes, :no)
34
+ ```
35
+
36
+ ### Single and Double Splat Arguments
37
+
38
+ You can use single and double splats in your method signatures, and Delivered will pass them through
39
+ without checking, while still checking the other named positional and keyword arguments.
40
+
41
+ ```ruby
42
+ sig String
43
+ def create(name, *args, foo:, **kwargs); end
44
+ ```
45
+
29
46
  ### Return Types
30
47
 
31
48
  You can also check the return value of the method by passing a Hash with an Array as the key, and
@@ -82,6 +99,38 @@ sig name: T.RespondTo(:to_s)
82
99
  def create(name:); end
83
100
  ```
84
101
 
102
+ #### `RangeOf`
103
+
104
+ Value **MUST** be a Range of the given type
105
+
106
+ ```ruby
107
+ sig name: T.RangeOf(Integer)
108
+ def create(name: 1...2); end
109
+ ```
110
+
111
+ #### `ArrayOf`
112
+
113
+ Value **MUST** be an Array of the given type
114
+
115
+ ```ruby
116
+ sig names: T.ArrayOf(String)
117
+ def create(names: ['Joel', 'Ash']); end
118
+ ```
119
+
120
+ #### `Enumerable`
121
+
122
+ Value **MUST** be an Enumerable of the optional given type
123
+
124
+ ```ruby
125
+ sig users: T.Enumerable
126
+ def create(users: [1, 2]); end
127
+ ```
128
+
129
+ ```ruby
130
+ sig users: T.Enumerable(User)
131
+ def create(users: User.all); end
132
+ ```
133
+
85
134
  #### `Any`
86
135
 
87
136
  Value **MUST** be any of the given list of values, that is, the value must be one of the given list.
@@ -3,8 +3,6 @@
3
3
  module Delivered
4
4
  module Signature
5
5
  def sig(*sig_args, **sig_kwargs, &return_blk)
6
- # ap [sig_args, sig_kwargs, return_blk]
7
-
8
6
  # Block return
9
7
  returns = return_blk&.call
10
8
 
@@ -20,32 +18,26 @@ module Delivered
20
18
  sig_kwargs = sig_args.pop if sig_args.last.is_a?(Hash)
21
19
  end
22
20
 
23
- # ap [sig_args, sig_kwargs, returns]
24
-
25
21
  meta = class << self; self; end
26
22
  sig_check = lambda do |klass, class_method, name, *args, **kwargs, &block| # rubocop:disable Metrics/BlockLength
27
- cname = if class_method
28
- "#{klass.name}.#{name}"
29
- else
30
- "#{klass.class.name}##{name}"
31
- end
23
+ cname = class_method ? "#{klass.name}.#{name}" : "#{klass.class.name}##{name}"
32
24
 
33
25
  sig_args.each.with_index do |arg, i|
34
26
  args[i] => ^arg
35
- rescue NoMatchingPatternError => e
27
+ rescue NoMatchingPatternError
36
28
  raise Delivered::ArgumentError,
37
- "`#{cname}` expected #{arg.inspect} as argument #{i}, but received " \
38
- "`#{args[i].inspect}`",
39
- caller, cause: e
29
+ "#{cname} expected #{arg.inspect} as argument #{i}, but received " \
30
+ "`#{args[i].inspect}`"
40
31
  end
41
32
 
42
- kwargs.each do |key, value|
43
- value => ^(sig_kwargs[key])
44
- rescue NoMatchingPatternError => e
45
- raise Delivered::ArgumentError,
46
- "`#{cname}` expected #{sig_kwargs[key].inspect} as keyword argument :#{key}, " \
47
- "but received `#{value.inspect}`",
48
- caller, cause: e
33
+ unless sig_kwargs.empty?
34
+ kwargs.each do |key, value|
35
+ value => ^(sig_kwargs[key])
36
+ rescue NoMatchingPatternError
37
+ raise Delivered::ArgumentError,
38
+ "#{cname} expected #{sig_kwargs[key].inspect} as keyword argument :#{key}, " \
39
+ "but received `#{value.inspect}`"
40
+ end
49
41
  end
50
42
 
51
43
  result = if block
@@ -56,16 +48,16 @@ module Delivered
56
48
 
57
49
  begin
58
50
  result => ^returns unless returns.nil?
59
- rescue NoMatchingPatternError => e
51
+ rescue NoMatchingPatternError
60
52
  raise Delivered::ArgumentError,
61
53
  "`#{cname}` expected to return #{returns.inspect}, " \
62
- "but returned `#{result.inspect}`",
63
- caller, cause: e
54
+ "but returned `#{result.inspect}`"
64
55
  end
65
56
 
66
57
  result
67
58
  end
68
59
 
60
+ # Instance method redefinition
69
61
  meta.send :define_method, :method_added do |name|
70
62
  meta.send :remove_method, :method_added
71
63
  meta.send :remove_method, :singleton_method_added
@@ -76,6 +68,7 @@ module Delivered
76
68
  end
77
69
  end
78
70
 
71
+ # Class method redefinition
79
72
  meta.send :define_method, :singleton_method_added do |name|
80
73
  next if name == :singleton_method_added
81
74
 
@@ -13,6 +13,57 @@ module Delivered
13
13
  end
14
14
  end
15
15
 
16
+ class EnumerableType
17
+ def initialize(type)
18
+ @type = type
19
+ end
20
+
21
+ def inspect = "Enumerable(#{@type.inspect})"
22
+
23
+ if Delivered::EXPENSIVE_TYPE_CHECKS
24
+ def ===(value)
25
+ Enumerable === value && value.all? { |item| @type === item }
26
+ end
27
+ else
28
+ def ===(value)
29
+ Enumerable === value && (value.empty? || @type === value.first)
30
+ end
31
+ end
32
+ end
33
+
34
+ class ArrayOfType
35
+ def initialize(type)
36
+ @type = type
37
+ end
38
+
39
+ def inspect = "ArrayOf(#{@type.inspect})"
40
+
41
+ if Delivered::EXPENSIVE_TYPE_CHECKS
42
+ def ===(value)
43
+ Array === value && value.all? { |item| @type === item }
44
+ end
45
+ else
46
+ def ===(value)
47
+ Array === value && (value.empty? || @type === value[0])
48
+ end
49
+ end
50
+ end
51
+
52
+ class RangeOfType
53
+ def initialize(type)
54
+ @type = type
55
+ end
56
+
57
+ def inspect = "Range(#{@type&.inspect})"
58
+
59
+ def ===(value)
60
+ Range === value && (
61
+ (@type === value.begin && (nil === value.end || @type === value.end)) ||
62
+ (@type === value.end && nil === value.begin)
63
+ )
64
+ end
65
+ end
66
+
16
67
  class RespondToType
17
68
  def initialize(*methods)
18
69
  @methods = methods
@@ -53,6 +104,9 @@ module Delivered
53
104
  module_function
54
105
 
55
106
  def Nilable(type = nil) = NilableType.new(type)
107
+ def Enumerable(type = nil) = EnumerableType.new(type)
108
+ def ArrayOf(type) = ArrayOfType.new(type)
109
+ def RangeOf(type) = RangeOfType.new(type)
56
110
  def RespondTo(*methods) = RespondToType.new(*methods)
57
111
  def Any(*types) = AnyType.new(*types)
58
112
  def Boolean = BooleanType.new
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Delivered
4
- VERSION = '0.3.0'
4
+ VERSION = '0.5.0'
5
5
  end
data/lib/delivered.rb CHANGED
@@ -1,9 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Delivered
4
- class ArgumentError < ArgumentError
5
- end
4
+ EXPENSIVE_TYPE_CHECKS = ENV['DELIVERED_EXPENSIVE_TYPE_CHECKS'] != 'false'
5
+
6
+ class ArgumentError < ArgumentError; end
7
+ class VerifyError < StandardError; end
6
8
 
7
9
  autoload :Signature, 'delivered/signature'
8
10
  autoload :Types, 'delivered/types'
11
+
12
+ module_function
13
+
14
+ def verify!(value, type)
15
+ value => ^type
16
+ rescue NoMatchingPatternError => e
17
+ raise Delivered::VerifyError, "Expected `#{value.inspect}` to be #{type.inspect}", cause: e
18
+ end
9
19
  end
metadata CHANGED
@@ -1,16 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delivered
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Moss
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-05-17 00:00:00.000000000 Z
10
+ date: 2025-02-24 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description:
14
12
  email:
15
13
  - joel@developwithstyle.com
16
14
  executables: []
@@ -34,7 +32,6 @@ metadata:
34
32
  source_code_uri: https://github.com/joelmoss/delivered
35
33
  changelog_uri: https://github.com/joelmoss/delivered/releases
36
34
  rubygems_mfa_required: 'true'
37
- post_install_message:
38
35
  rdoc_options: []
39
36
  require_paths:
40
37
  - lib
@@ -49,8 +46,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
46
  - !ruby/object:Gem::Version
50
47
  version: '0'
51
48
  requirements: []
52
- rubygems_version: 3.5.9
53
- signing_key:
49
+ rubygems_version: 3.6.5
54
50
  specification_version: 4
55
51
  summary: Simple runtime type checking for Ruby method signatures
56
52
  test_files: []