low_type 1.0.0 → 1.0.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.
- checksums.yaml +4 -4
- data/lib/adapters/sinatra_adapter.rb +3 -3
- data/lib/factories/proxy_factory.rb +2 -2
- data/lib/{parser.rb → file_parser.rb} +1 -1
- data/lib/local_types.rb +19 -14
- data/lib/low_type.rb +4 -5
- data/lib/proxies/local_proxy.rb +1 -1
- data/lib/proxies/param_proxy.rb +1 -1
- data/lib/proxies/return_proxy.rb +1 -1
- data/lib/redefiner.rb +36 -17
- data/lib/{subclasses.rb → syntax.rb} +14 -30
- data/lib/type_expression.rb +2 -2
- data/lib/{error_types.rb → types/error_types.rb} +1 -0
- data/lib/version.rb +1 -1
- metadata +5 -5
- /data/lib/{basic_types.rb → types/complex_types.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3541ef39f820d9554a2493481686ad0692f66d36a31920d2c4304a62cb55bbb9
|
|
4
|
+
data.tar.gz: 19bebceb57fff771317130dae93e5caf86962028db3e81321b03855ccf3c42cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 61dace65facc045407b0300157a9e66e6269b14a3414ed943ed3bb5bd817ab313d4c10fd184cad0c2f6803c50a03c6c7d5929253ce68ba89b909a49f55d79fcb
|
|
7
|
+
data.tar.gz: 207f390f476e60af068bffc5bed65c0f76f20bc4997e1d76905496779e63aeaf9022405fe95576d4d8f14ad35107ea6c07f8a98fb4ddfb447d50eff35a1a6921
|
|
@@ -5,7 +5,7 @@ require 'prism'
|
|
|
5
5
|
require_relative '../interfaces/adapter_interface'
|
|
6
6
|
require_relative '../proxies/file_proxy'
|
|
7
7
|
require_relative '../proxies/return_proxy'
|
|
8
|
-
require_relative '../error_types'
|
|
8
|
+
require_relative '../types/error_types'
|
|
9
9
|
|
|
10
10
|
module LowType
|
|
11
11
|
module Adapter
|
|
@@ -27,7 +27,7 @@ module LowType
|
|
|
27
27
|
|
|
28
28
|
pattern = arguments_node.arguments.first.content
|
|
29
29
|
|
|
30
|
-
line =
|
|
30
|
+
line = FileParser.line_number(node: method_call)
|
|
31
31
|
file = FileProxy.new(path: @file_path, line:, scope: "#{@klass}##{method_call.name}")
|
|
32
32
|
next unless (return_proxy = return_proxy(method_node: method_call, pattern:, file:))
|
|
33
33
|
|
|
@@ -38,7 +38,7 @@ module LowType
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def return_proxy(method_node:, pattern:, file:)
|
|
41
|
-
return_type =
|
|
41
|
+
return_type = FileParser.return_type(method_node:)
|
|
42
42
|
return nil if return_type.nil?
|
|
43
43
|
|
|
44
44
|
# Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../proxies/return_proxy'
|
|
4
|
-
require_relative '../
|
|
4
|
+
require_relative '../file_parser'
|
|
5
5
|
require_relative '../type_expression'
|
|
6
6
|
|
|
7
7
|
module LowType
|
|
8
8
|
class ProxyFactory
|
|
9
9
|
class << self
|
|
10
10
|
def return_proxy(method_node:, file:)
|
|
11
|
-
return_type =
|
|
11
|
+
return_type = FileParser.return_type(method_node:)
|
|
12
12
|
return nil if return_type.nil?
|
|
13
13
|
|
|
14
14
|
# Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
|
data/lib/local_types.rb
CHANGED
|
@@ -2,27 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'proxies/file_proxy'
|
|
4
4
|
require_relative 'proxies/local_proxy'
|
|
5
|
+
require_relative 'types/error_types'
|
|
5
6
|
require_relative 'type_expression'
|
|
6
7
|
require_relative 'value_expression'
|
|
7
8
|
|
|
8
|
-
module
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
module LowType
|
|
10
|
+
module LocalTypes
|
|
11
|
+
def type(type_expression)
|
|
12
|
+
value = type_expression.default_value
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
last_caller = caller_locations(1, 1).first
|
|
15
|
+
file = FileProxy.new(path: last_caller.path, line: last_caller.lineno, scope: 'local type')
|
|
16
|
+
proxy = LocalProxy.new(type_expression:, name: self, file:)
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
type_expression.validate!(value:, proxy:)
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
return value.value if value.is_a?(ValueExpression)
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
value
|
|
23
|
+
rescue NoMethodError
|
|
24
|
+
raise ConfigError, "Invalid type expression, likely because you didn't add 'using LowType::Syntax'"
|
|
25
|
+
end
|
|
26
|
+
alias low_type type
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
def value(type)
|
|
29
|
+
LowType.value(type:)
|
|
30
|
+
end
|
|
31
|
+
alias low_value value
|
|
26
32
|
end
|
|
27
|
-
alias low_value value
|
|
28
33
|
end
|
data/lib/low_type.rb
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'adapters/adapter_loader'
|
|
4
|
-
require_relative '
|
|
4
|
+
require_relative 'types/complex_types'
|
|
5
5
|
require_relative 'instance_types'
|
|
6
6
|
require_relative 'local_types'
|
|
7
7
|
require_relative 'redefiner'
|
|
8
|
-
require_relative '
|
|
8
|
+
require_relative 'syntax'
|
|
9
9
|
require_relative 'type_expression'
|
|
10
10
|
require_relative 'value_expression'
|
|
11
11
|
|
|
12
|
-
# Include this module into your class to define and check types.
|
|
13
12
|
module LowType
|
|
14
13
|
# We do as much as possible on class load rather than on instantiation to be thread-safe and efficient.
|
|
15
|
-
def self.included(klass)
|
|
14
|
+
def self.included(klass)
|
|
16
15
|
class << klass
|
|
17
16
|
def low_methods
|
|
18
17
|
@low_methods ||= {}
|
|
@@ -20,7 +19,7 @@ module LowType
|
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
file_path = LowType.file_path(klass:)
|
|
23
|
-
parser =
|
|
22
|
+
parser = FileParser.new(file_path:)
|
|
24
23
|
private_start_line = parser.private_start_line
|
|
25
24
|
|
|
26
25
|
klass.extend InstanceTypes
|
data/lib/proxies/local_proxy.rb
CHANGED
data/lib/proxies/param_proxy.rb
CHANGED
data/lib/proxies/return_proxy.rb
CHANGED
data/lib/redefiner.rb
CHANGED
|
@@ -4,41 +4,57 @@ require_relative 'factories/proxy_factory'
|
|
|
4
4
|
require_relative 'proxies/file_proxy'
|
|
5
5
|
require_relative 'proxies/method_proxy'
|
|
6
6
|
require_relative 'proxies/param_proxy'
|
|
7
|
+
require_relative 'syntax'
|
|
7
8
|
require_relative 'type_expression'
|
|
8
9
|
|
|
9
10
|
module LowType
|
|
10
11
|
# Redefine methods to have their arguments and return values type checked.
|
|
11
12
|
class Redefiner
|
|
13
|
+
using Syntax
|
|
14
|
+
|
|
12
15
|
class << self
|
|
13
|
-
def redefine(method_nodes:, klass:, private_start_line:, file_path:)
|
|
16
|
+
def redefine(method_nodes:, klass:, private_start_line:, file_path:)
|
|
17
|
+
create_proxies(method_nodes:, klass:, file_path:)
|
|
18
|
+
define_methods(method_nodes:, private_start_line:)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def create_proxies(method_nodes:, klass:, file_path:)
|
|
24
|
+
method_nodes.each do |method_node|
|
|
25
|
+
name = method_node.name
|
|
26
|
+
line = FileParser.line_number(node: method_node)
|
|
27
|
+
file = FileProxy.new(path: file_path, line:, scope: "#{klass}##{name}")
|
|
28
|
+
params = param_proxies(method_node:, file:)
|
|
29
|
+
return_proxy = ProxyFactory.return_proxy(method_node:, file:)
|
|
30
|
+
|
|
31
|
+
klass.low_methods[name] = MethodProxy.new(name:, params:, return_proxy:)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def define_methods(method_nodes:, private_start_line:) # rubocop:disable Metrics
|
|
14
36
|
Module.new do
|
|
15
37
|
method_nodes.each do |method_node|
|
|
16
38
|
name = method_node.name
|
|
17
|
-
line = Parser.line_number(node: method_node)
|
|
18
|
-
file = FileProxy.new(path: file_path, line:, scope: "#{klass}##{method_node.name}")
|
|
19
|
-
params = Redefiner.params_with_type_expressions(method_node:, file:)
|
|
20
|
-
return_proxy = ProxyFactory.return_proxy(method_node:, file:)
|
|
21
|
-
|
|
22
|
-
klass.low_methods[name] = MethodProxy.new(name:, params:, return_proxy:)
|
|
23
39
|
|
|
24
40
|
define_method(name) do |*args, **kwargs|
|
|
25
|
-
|
|
41
|
+
method_proxy = instance_of?(Class) ? low_methods[name] : self.class.low_methods[name]
|
|
42
|
+
|
|
43
|
+
method_proxy.params.each do |param_proxy|
|
|
26
44
|
# Get argument value or default value.
|
|
27
45
|
value = param_proxy.position ? args[param_proxy.position] : kwargs[param_proxy.name]
|
|
28
46
|
if value.nil? && param_proxy.type_expression.default_value != :LOW_TYPE_UNDEFINED
|
|
29
47
|
value = param_proxy.type_expression.default_value
|
|
30
48
|
end
|
|
31
|
-
|
|
49
|
+
|
|
32
50
|
param_proxy.type_expression.validate!(value:, proxy: param_proxy)
|
|
33
|
-
# Handle value(type) special case.
|
|
34
51
|
value = value.value if value.is_a?(ValueExpression)
|
|
35
|
-
# Redefine argument value.
|
|
36
52
|
param_proxy.position ? args[param_proxy.position] = value : kwargs[param_proxy.name] = value
|
|
37
53
|
end
|
|
38
54
|
|
|
39
|
-
if return_proxy
|
|
55
|
+
if (return_proxy = method_proxy.return_proxy)
|
|
40
56
|
return_value = super(*args, **kwargs)
|
|
41
|
-
return_proxy.type_expression.validate!(value: return_value, proxy:
|
|
57
|
+
return_proxy.type_expression.validate!(value: return_value, proxy: return_proxy)
|
|
42
58
|
return return_value
|
|
43
59
|
end
|
|
44
60
|
|
|
@@ -50,12 +66,11 @@ module LowType
|
|
|
50
66
|
end
|
|
51
67
|
end
|
|
52
68
|
|
|
53
|
-
def
|
|
69
|
+
def param_proxies(method_node:, file:)
|
|
54
70
|
return [] if method_node.parameters.nil?
|
|
55
71
|
|
|
56
72
|
params = method_node.parameters.slice
|
|
57
|
-
|
|
58
|
-
proxy_method = eval("-> (#{params}) {}", binding, __FILE__, __LINE__) # rubocop:disable Security/Eval
|
|
73
|
+
proxy_method = proxy_method(method_node:)
|
|
59
74
|
required_args, required_kwargs = required_args(proxy_method:)
|
|
60
75
|
|
|
61
76
|
# Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
|
|
@@ -87,7 +102,11 @@ module LowType
|
|
|
87
102
|
raise ArgumentError, "Incorrect param syntax: #{e.message}"
|
|
88
103
|
end
|
|
89
104
|
|
|
90
|
-
|
|
105
|
+
def proxy_method(method_node:)
|
|
106
|
+
params = method_node.parameters.slice
|
|
107
|
+
# Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
|
|
108
|
+
eval("-> (#{params}) {}", binding, __FILE__, __LINE__) # rubocop:disable Security/Eval
|
|
109
|
+
end
|
|
91
110
|
|
|
92
111
|
def required_args(proxy_method:)
|
|
93
112
|
required_args = []
|
|
@@ -1,42 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
# Scoped to the class that includes LowType module.
|
|
3
|
-
class Array < ::Array
|
|
4
|
-
def self.[](*types)
|
|
5
|
-
return LowType::TypeExpression.new(type: [*types]) if types.all? { |type| LowType.type?(type) }
|
|
6
|
-
super
|
|
7
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def ==(other)
|
|
15
|
-
return true if other.class == ::Array
|
|
16
|
-
super
|
|
17
|
-
end
|
|
18
|
-
end
|
|
3
|
+
module LowType
|
|
4
|
+
module Syntax
|
|
5
|
+
refine Array.singleton_class do
|
|
6
|
+
def [](*types)
|
|
7
|
+
return LowType::TypeExpression.new(type: [*types]) if types.all? { |type| LowType.type?(type) }
|
|
19
8
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def self.[](type)
|
|
23
|
-
return LowType::TypeExpression.new(type:) if LowType.type?(type)
|
|
24
|
-
super
|
|
9
|
+
super
|
|
10
|
+
end
|
|
25
11
|
end
|
|
26
12
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end
|
|
13
|
+
refine Hash.singleton_class do
|
|
14
|
+
def [](type)
|
|
15
|
+
return LowType::TypeExpression.new(type:) if LowType.type?(type)
|
|
31
16
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
super
|
|
17
|
+
super
|
|
18
|
+
end
|
|
35
19
|
end
|
|
36
20
|
end
|
|
37
21
|
end
|
|
38
22
|
|
|
39
|
-
#
|
|
23
|
+
# Refine doesn't support inheritence.
|
|
40
24
|
class Object
|
|
41
25
|
# For "Type | [type_expression/type/value]" situations, redirecting to or generating a type expression from types.
|
|
42
26
|
# "|" is not defined on Object class and this is the most compute-efficient way to achieve our goal (world peace).
|
data/lib/type_expression.rb
CHANGED
|
@@ -57,9 +57,9 @@ module LowType
|
|
|
57
57
|
|
|
58
58
|
def valid_types
|
|
59
59
|
types = @types.map do |type|
|
|
60
|
-
# Remove 'LowType::'
|
|
60
|
+
# Remove 'LowType::' namespace in subtypes.
|
|
61
61
|
if type.is_a?(::Array)
|
|
62
|
-
|
|
62
|
+
"[#{type.map { |subtype| subtype.to_s.delete_prefix('LowType::') }.join(', ')}]"
|
|
63
63
|
else
|
|
64
64
|
type.inspect.to_s.delete_prefix('LowType::')
|
|
65
65
|
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: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -19,23 +19,23 @@ extra_rdoc_files: []
|
|
|
19
19
|
files:
|
|
20
20
|
- lib/adapters/adapter_loader.rb
|
|
21
21
|
- lib/adapters/sinatra_adapter.rb
|
|
22
|
-
- lib/basic_types.rb
|
|
23
|
-
- lib/error_types.rb
|
|
24
22
|
- lib/factories/proxy_factory.rb
|
|
23
|
+
- lib/file_parser.rb
|
|
25
24
|
- lib/instance_types.rb
|
|
26
25
|
- lib/interfaces/adapter_interface.rb
|
|
27
26
|
- lib/interfaces/error_interface.rb
|
|
28
27
|
- lib/local_types.rb
|
|
29
28
|
- lib/low_type.rb
|
|
30
|
-
- lib/parser.rb
|
|
31
29
|
- lib/proxies/file_proxy.rb
|
|
32
30
|
- lib/proxies/local_proxy.rb
|
|
33
31
|
- lib/proxies/method_proxy.rb
|
|
34
32
|
- lib/proxies/param_proxy.rb
|
|
35
33
|
- lib/proxies/return_proxy.rb
|
|
36
34
|
- lib/redefiner.rb
|
|
37
|
-
- lib/
|
|
35
|
+
- lib/syntax.rb
|
|
38
36
|
- lib/type_expression.rb
|
|
37
|
+
- lib/types/complex_types.rb
|
|
38
|
+
- lib/types/error_types.rb
|
|
39
39
|
- lib/value_expression.rb
|
|
40
40
|
- lib/version.rb
|
|
41
41
|
homepage: https://codeberg.org/low_ruby/low_type
|
|
File without changes
|