low_type 0.3.0 → 0.4.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: b01f96320aa0543e44d8b4b40430903af4966174fb9ae1533ddf7240c1919ec1
4
- data.tar.gz: 87094ba39f9755fce4befa350b7ce33d7bd807a9774f1366340a0c9003788933
3
+ metadata.gz: efc284f18360245938c679ce476f0588ea31560da0b75a1076d080d8d687e07c
4
+ data.tar.gz: 7839a82ee075a6436203aaf991be31267a180e9b35f94a4a444365506f4700d8
5
5
  SHA512:
6
- metadata.gz: 2b4ff1cca9f7763084b3283a13b34955863a81074a6161703c3993aa6d0916180804f8b6058ee35779be159eec7000d9c2f676c656254a67039cb3548073f10d
7
- data.tar.gz: 39aad0e4a488963333b31c8ec21fc6586e78e5d0ebf76a6546997183ada9ce6baacbd0107ecd0ed71ce5e5c9fa14d8597936b5db874edc48fdc6c082651ea72e
6
+ metadata.gz: b2260394d54e93340450d2d4cfccd93e3d462f91c39fec4778205c52699590f4467639ac847cf9faa697b5e468308ee1d917c077670b30a4e9d41ca3413bc7b7
7
+ data.tar.gz: 521ee18436fac4d04caa4a2787e93b65367ccbe0fdd69eee7b7479316e2d7642e0a99628fedc8fc93996c28f2620a91d51407143c4ec1baa02040a0478d5a4e8
data/lib/low_type.rb CHANGED
@@ -16,12 +16,20 @@ module LowType
16
16
  # Hash[] class method returns a type expression only for the duration of this "included" hook.
17
17
  hash_class_method = Hash.method('[]').unbind
18
18
  Hash.define_singleton_method('[]') do |expression|
19
+ # Support Pry which uses Hash[].
20
+ unless LowType.type?(expression)
21
+ Hash.define_singleton_method('[]', hash_class_method)
22
+ result = Hash[expression]
23
+ Hash.method('[]').unbind
24
+ return result
25
+ end
26
+
19
27
  TypeExpression.new(type: expression)
20
28
  end
21
29
 
22
30
  class << klass
23
- def low_params
24
- @low_params ||= {}
31
+ def low_methods
32
+ @low_methods ||= {}
25
33
  end
26
34
 
27
35
  def type(expression)
@@ -51,7 +59,7 @@ module LowType
51
59
  end
52
60
 
53
61
  def type?(expression)
54
- expression.respond_to?(:new) || expression == Integer
62
+ expression.respond_to?(:new) || expression == Integer || (expression.is_a?(Hash) && expression.keys.first.respond_to?(:new) && expression.values.first.respond_to?(:new))
55
63
  end
56
64
 
57
65
  def value?(expression)
data/lib/parser.rb CHANGED
@@ -18,6 +18,15 @@ module LowType
18
18
  @class_methods = method_visitor.class_methods
19
19
  @private_start_line = method_visitor.private_start_line
20
20
  end
21
+
22
+ def self.return_node(method_node:)
23
+ # Only a lambda defined immediately after a method's parameters is considered a return type expression.
24
+ node = method_node.compact_child_nodes.find { |node| node.is_a?(Prism::StatementsNode) }.body.first
25
+
26
+ return node if node.is_a?(Prism::LambdaNode)
27
+
28
+ nil
29
+ end
21
30
  end
22
31
 
23
32
  class MethodVisitor < Prism::Visitor
data/lib/redefiner.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require_relative 'method_proxy'
1
2
  require_relative 'param_proxy'
2
3
  require_relative 'parser'
3
4
  require_relative 'type_expression'
@@ -8,31 +9,52 @@ module LowType
8
9
  def redefine_methods(method_nodes:, private_start_line:, klass:)
9
10
  Module.new do
10
11
  method_nodes.each do |method_node|
11
- klass.low_params[method_node.name] = Redefiner.param_proxy_with_type_expressions(method_node)
12
- next if klass.low_params[method_node.name].empty?
13
-
14
- define_method(method_node.name) do |*args, **kwargs|
15
- klass.low_params[method_node.name].each do |param_proxy|
16
- arg = param_proxy.position ? args[param_proxy.position] : kwargs[param_proxy.name]
17
- arg = param_proxy.type_expression.default_value if arg.nil? && param_proxy.type_expression.default_value != :LOW_TYPE_UNDEFINED
18
- param_proxy.type_expression.validate!(arg:, name: param_proxy.name)
19
- param_proxy.position ? args[param_proxy.position] = arg : kwargs[param_proxy.name] = arg
12
+ name = method_node.name
13
+ params = Redefiner.params_with_type_expressions(method_node:)
14
+ return_expression = Redefiner.return_type_expression(method_node:)
15
+
16
+ klass.low_methods[name] = MethodProxy.new(name:, params:, return_expression:)
17
+
18
+ define_method(name) do |*args, **kwargs|
19
+ klass.low_methods[name].params.each do |param_proxy|
20
+ value = param_proxy.position ? args[param_proxy.position] : kwargs[param_proxy.name]
21
+ value = param_proxy.type_expression.default_value if value.nil? && param_proxy.type_expression.default_value != :LOW_TYPE_UNDEFINED
22
+ param_proxy.type_expression.validate!(value:, name: param_proxy.name)
23
+ param_proxy.position ? args[param_proxy.position] = value : kwargs[param_proxy.name] = value
24
+ end
25
+
26
+ if return_expression
27
+ return_value = super(*args, **kwargs)
28
+ return_expression.validate!(value: return_value, name:)
29
+ return return_value
20
30
  end
21
31
 
22
32
  super(*args, **kwargs)
23
33
  end
24
34
 
25
35
  if private_start_line && method_node.start_line > private_start_line
26
- private method_node.name
36
+ private name
27
37
  end
28
38
  end
29
39
  end
30
40
  end
31
41
 
32
- def param_proxy_with_type_expressions(method_node)
42
+ def return_type_expression(method_node:)
43
+ return_node = Parser.return_node(method_node:)
44
+ return nil if return_node.nil?
45
+
46
+ expression = eval(return_node.slice).call
47
+
48
+ return expression if expression.class == TypeExpression
49
+ TypeExpression.new(type: expression)
50
+ end
51
+
52
+ def params_with_type_expressions(method_node:)
53
+ return [] if method_node.parameters.nil?
54
+
33
55
  params = method_node.parameters.slice
34
56
  proxy_method = eval("-> (#{params}) {}")
35
- required_args, required_kwargs = Redefiner.required_args(proxy_method)
57
+ required_args, required_kwargs = Redefiner.required_args(proxy_method:)
36
58
 
37
59
  typed_method = eval(
38
60
  <<~RUBY
@@ -65,7 +87,7 @@ module LowType
65
87
  raise ArgumentError, "Incorrect param syntax"
66
88
  end
67
89
 
68
- def required_args(proxy_method)
90
+ def required_args(proxy_method:)
69
91
  required_args = []
70
92
  required_kwargs = {}
71
93
 
@@ -1,10 +1,10 @@
1
1
  module LowType
2
2
  class TypeExpression
3
- attr_reader :type, :default_value
3
+ attr_reader :types, :default_value
4
4
 
5
- def initialize(type:)
5
+ def initialize(type:, default_value: :LOW_TYPE_UNDEFINED)
6
6
  @types = [type]
7
- @default_value = :LOW_TYPE_UNDEFINED
7
+ @default_value = default_value
8
8
  end
9
9
 
10
10
  def |(expression)
@@ -24,21 +24,22 @@ module LowType
24
24
  @default_value == :LOW_TYPE_UNDEFINED
25
25
  end
26
26
 
27
- def validate!(arg:, name:)
28
- if arg.nil? && required?
29
- raise ArgumentError, "Missing required argument of type '#{@types.join(', ')}' for '#{name}'"
27
+ def validate!(value:, name:)
28
+ if value.nil?
29
+ return true if @default_value.nil?
30
+ raise ArgumentError, "Missing required argument of type '#{@types.join(', ')}' for '#{name}'" if required?
30
31
  end
31
32
 
32
33
  @types.each do |type|
33
- return true if LowType.type?(type) && type == arg.class
34
+ return true if LowType.type?(type) && type == value.class
34
35
  # TODO: Shallow validation of enumerables could be made deeper with user config.
35
- return true if type.class == Array && arg.class == Array && type.first == arg.first.class
36
- if type.class == Hash && arg.class == Hash && type.keys[0] == arg.keys[0].class && type.values[0] == arg.values[0].class
36
+ return true if type.class == Array && value.class == Array && type.first == value.first.class
37
+ if type.class == Hash && value.class == Hash && type.keys[0] == value.keys[0].class && type.values[0] == value.values[0].class
37
38
  return true
38
39
  end
39
40
  end
40
41
 
41
- raise TypeError, "Invalid type '#{arg.class}' for '#{name}'"
42
+ raise TypeError, "Invalid type '#{value.class}' for '#{name}'"
42
43
  end
43
44
  end
44
45
  end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LowType
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: low_type
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi