low_type 0.4.1 → 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 +4 -4
- data/lib/low_type.rb +14 -6
- data/lib/parser.rb +3 -1
- data/lib/redefiner.rb +28 -14
- data/lib/type_expression.rb +10 -5
- data/lib/value_expression.rb +11 -0
- data/lib/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5862d1077a767f4d1df0df6f535a3b8c5d0de646a23088ea9679316a11a57f9e
|
|
4
|
+
data.tar.gz: ae4b3a7e0d57ab5e088e8d666de25df1b121d248b832a343a13565f1cb44d6b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ddf2c76d224b5dc51a5503f7635d9c6f0e2de8ee3c9dccdf9cc1e7eb6105fe88cb1045438a2a40322ceb78128c6a8a5604cc2db5d1f8bb71eeaf23d28f0ccc99
|
|
7
|
+
data.tar.gz: ceb481d5488ce8b217b5715f43991280359b846acee3d7f0fa205ab485f17e4c1da48c914db3b293a37a2ee21b5516ab5334b8240c69a9ab69730bbbfdc7cca7
|
data/lib/low_type.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'redefiner'
|
|
4
4
|
require_relative 'type_expression'
|
|
5
|
+
require_relative 'value_expression'
|
|
5
6
|
|
|
6
7
|
module LowType
|
|
7
8
|
# We do as much as possible on class load rather than on instantiation to be thread-safe and efficient.
|
|
@@ -28,19 +29,22 @@ module LowType
|
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
class << klass
|
|
31
|
-
|
|
32
|
-
@low_methods ||= {}
|
|
33
|
-
end
|
|
34
|
-
|
|
32
|
+
# Public API.
|
|
35
33
|
def type(expression)
|
|
36
34
|
# TODO: Runtime type expression for the supplied variable.
|
|
37
35
|
end
|
|
38
36
|
alias_method :low_type, :type
|
|
39
37
|
|
|
38
|
+
# Public API.
|
|
40
39
|
def value(expression)
|
|
41
|
-
|
|
40
|
+
::LowType.value(expression)
|
|
42
41
|
end
|
|
43
42
|
alias_method :low_value, :value
|
|
43
|
+
|
|
44
|
+
# Internal API.
|
|
45
|
+
def low_methods
|
|
46
|
+
@low_methods ||= {}
|
|
47
|
+
end
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
parser = Parser.new(file_path: LowType.file_path(klass:))
|
|
@@ -53,6 +57,7 @@ module LowType
|
|
|
53
57
|
Hash.define_singleton_method('[]', hash_class_method)
|
|
54
58
|
end
|
|
55
59
|
|
|
60
|
+
# Internal API.
|
|
56
61
|
class << self
|
|
57
62
|
def file_path(klass:)
|
|
58
63
|
caller.find { |callee| callee.end_with?("<class:#{klass}>'") }.split(':').first
|
|
@@ -65,8 +70,11 @@ module LowType
|
|
|
65
70
|
def value?(expression)
|
|
66
71
|
!expression.respond_to?(:new) && expression != Integer
|
|
67
72
|
end
|
|
73
|
+
|
|
74
|
+
def value(type)
|
|
75
|
+
TypeExpression.new(default_value: ValueExpression.new(value: type))
|
|
76
|
+
end
|
|
68
77
|
end
|
|
69
78
|
|
|
70
|
-
class ValueExpression; end
|
|
71
79
|
class Boolean; end # TrueClass or FalseClass
|
|
72
80
|
end
|
data/lib/parser.rb
CHANGED
|
@@ -21,8 +21,10 @@ module LowType
|
|
|
21
21
|
|
|
22
22
|
def self.return_node(method_node:)
|
|
23
23
|
# Only a lambda defined immediately after a method's parameters is considered a return type expression.
|
|
24
|
-
|
|
24
|
+
statements_node = method_node.compact_child_nodes.find { |node| node.is_a?(Prism::StatementsNode) }
|
|
25
|
+
return nil if statements_node.nil? # Sometimes developers define methods without code inside them.
|
|
25
26
|
|
|
27
|
+
node = statements_node.body.first
|
|
26
28
|
return node if node.is_a?(Prism::LambdaNode)
|
|
27
29
|
|
|
28
30
|
nil
|
data/lib/redefiner.rb
CHANGED
|
@@ -4,6 +4,8 @@ require_relative 'parser'
|
|
|
4
4
|
require_relative 'type_expression'
|
|
5
5
|
|
|
6
6
|
module LowType
|
|
7
|
+
class ReturnError < StandardError; end
|
|
8
|
+
|
|
7
9
|
class Redefiner
|
|
8
10
|
class << self
|
|
9
11
|
def redefine_methods(method_nodes:, private_start_line:, klass:)
|
|
@@ -17,15 +19,20 @@ module LowType
|
|
|
17
19
|
|
|
18
20
|
define_method(name) do |*args, **kwargs|
|
|
19
21
|
klass.low_methods[name].params.each do |param_proxy|
|
|
22
|
+
# Get argument value or default value.
|
|
20
23
|
value = param_proxy.position ? args[param_proxy.position] : kwargs[param_proxy.name]
|
|
21
24
|
value = param_proxy.type_expression.default_value if value.nil? && param_proxy.type_expression.default_value != :LOW_TYPE_UNDEFINED
|
|
22
|
-
|
|
25
|
+
# Validate argument type.
|
|
26
|
+
param_proxy.type_expression.validate!(value:, name: param_proxy.name, error_type: ArgumentError, error_keyword: 'required')
|
|
27
|
+
# Handle value(type) special case.
|
|
28
|
+
value = value.value if value.is_a?(ValueExpression)
|
|
29
|
+
# Redefine argument value.
|
|
23
30
|
param_proxy.position ? args[param_proxy.position] = value : kwargs[param_proxy.name] = value
|
|
24
31
|
end
|
|
25
32
|
|
|
26
33
|
if return_expression
|
|
27
34
|
return_value = super(*args, **kwargs)
|
|
28
|
-
return_expression.validate!(value: return_value, name:)
|
|
35
|
+
return_expression.validate!(value: return_value, name:, error_type: ReturnError, error_keyword: 'return')
|
|
29
36
|
return return_value
|
|
30
37
|
end
|
|
31
38
|
|
|
@@ -39,22 +46,12 @@ module LowType
|
|
|
39
46
|
end
|
|
40
47
|
end
|
|
41
48
|
|
|
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
49
|
def params_with_type_expressions(method_node:)
|
|
53
50
|
return [] if method_node.parameters.nil?
|
|
54
51
|
|
|
55
52
|
params = method_node.parameters.slice
|
|
56
53
|
proxy_method = eval("-> (#{params}) {}")
|
|
57
|
-
required_args, required_kwargs =
|
|
54
|
+
required_args, required_kwargs = required_args(proxy_method:)
|
|
58
55
|
|
|
59
56
|
typed_method = eval(
|
|
60
57
|
<<~RUBY
|
|
@@ -84,9 +81,21 @@ module LowType
|
|
|
84
81
|
|
|
85
82
|
# TODO: Write spec for this.
|
|
86
83
|
rescue ArgumentError => e
|
|
87
|
-
raise ArgumentError, "Incorrect param syntax"
|
|
84
|
+
raise ArgumentError, "Incorrect param syntax: #{e.message}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def return_type_expression(method_node:)
|
|
88
|
+
return_node = Parser.return_node(method_node:)
|
|
89
|
+
return nil if return_node.nil?
|
|
90
|
+
|
|
91
|
+
expression = eval(return_node.slice).call
|
|
92
|
+
|
|
93
|
+
return expression if expression.class == TypeExpression
|
|
94
|
+
TypeExpression.new(type: expression)
|
|
88
95
|
end
|
|
89
96
|
|
|
97
|
+
private
|
|
98
|
+
|
|
90
99
|
def required_args(proxy_method:)
|
|
91
100
|
required_args = []
|
|
92
101
|
required_kwargs = {}
|
|
@@ -104,6 +113,11 @@ module LowType
|
|
|
104
113
|
|
|
105
114
|
[required_args, required_kwargs]
|
|
106
115
|
end
|
|
116
|
+
|
|
117
|
+
# Type expressions are eval()'d in the context of this module class (the instance doesn't exist yet) so alias API.
|
|
118
|
+
def value(expression)
|
|
119
|
+
::LowType.value(expression)
|
|
120
|
+
end
|
|
107
121
|
end
|
|
108
122
|
end
|
|
109
123
|
end
|
data/lib/type_expression.rb
CHANGED
|
@@ -2,8 +2,9 @@ module LowType
|
|
|
2
2
|
class TypeExpression
|
|
3
3
|
attr_reader :types, :default_value
|
|
4
4
|
|
|
5
|
-
def initialize(type
|
|
6
|
-
@types = [
|
|
5
|
+
def initialize(type: nil, default_value: :LOW_TYPE_UNDEFINED)
|
|
6
|
+
@types = []
|
|
7
|
+
@types << type unless type.nil?
|
|
7
8
|
@default_value = default_value
|
|
8
9
|
end
|
|
9
10
|
|
|
@@ -24,10 +25,10 @@ module LowType
|
|
|
24
25
|
@default_value == :LOW_TYPE_UNDEFINED
|
|
25
26
|
end
|
|
26
27
|
|
|
27
|
-
def validate!(value:, name:)
|
|
28
|
+
def validate!(value:, name:, error_type:, error_keyword:)
|
|
28
29
|
if value.nil?
|
|
29
30
|
return true if @default_value.nil?
|
|
30
|
-
raise
|
|
31
|
+
raise error_type, "Missing #{error_keyword} value of type '#{@types.join(', ')}' for '#{name}'" if required?
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
@types.each do |type|
|
|
@@ -39,20 +40,24 @@ module LowType
|
|
|
39
40
|
end
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
raise TypeError, "Invalid type '#{value.class}' for '#{name}'"
|
|
43
|
+
raise TypeError, "Invalid type '#{value.class}' for '#{name}'. Valid types: [#{@types.join(', ')}]"
|
|
43
44
|
end
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
class Object
|
|
48
49
|
class << self
|
|
50
|
+
# For "Type | [type_expression/type/value]" situations, redirecting to or generating a type expression from types.
|
|
49
51
|
# "|" is not defined on Object class and this is the most compute-efficient way to achieve our goal (world peace).
|
|
52
|
+
# "|" is overridable by any child object. While we could def/undef this method, this approach is actually lighter.
|
|
50
53
|
# "|" bitwise operator on Integer is not defined when the receiver is an Integer class, so we are not in conflict.
|
|
51
54
|
def |(expression)
|
|
52
55
|
if expression.class == ::LowType::TypeExpression
|
|
56
|
+
# We pass our type into their type expression.
|
|
53
57
|
expression | self
|
|
54
58
|
expression
|
|
55
59
|
else
|
|
60
|
+
# We turn our type into a type expression and pass in their [type_expression/type/value].
|
|
56
61
|
type_expression = ::LowType::TypeExpression.new(type: self)
|
|
57
62
|
type_expression | expression
|
|
58
63
|
end
|
data/lib/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -23,6 +23,7 @@ files:
|
|
|
23
23
|
- lib/parser.rb
|
|
24
24
|
- lib/redefiner.rb
|
|
25
25
|
- lib/type_expression.rb
|
|
26
|
+
- lib/value_expression.rb
|
|
26
27
|
- lib/version.rb
|
|
27
28
|
homepage: https://codeberg.org/low_ruby/low_type
|
|
28
29
|
licenses: []
|