low_type 1.0.1 → 1.0.3
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/file_parser.rb +26 -9
- data/lib/low_type.rb +14 -14
- data/lib/redefiner.rb +13 -6
- data/lib/syntax/syntax.rb +19 -0
- data/lib/{syntax.rb → syntax/union_types.rb} +7 -23
- data/lib/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5eeeaaa196eb1b1b9d4f4408ec91e5716c6d9d19599e78d9f3f1ddaff9c08181
|
|
4
|
+
data.tar.gz: 47259a30a8855c530f530ba10f03c298c29dbf5018146b5432a4b4c0b95ac491
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4331980b4a8ab95a1bcf806a575580c05a9f8386da572cc16e0e978b68376f5609fadac585436e072bbd3d1dc77e09892e53abec6710dff77dbe67f165fb0eff
|
|
7
|
+
data.tar.gz: 6c62cd38cf093e6beef4c66bfa0cad1286da8d86feaa5bf6f2f229124ece2bcddb6ffc9acb216cc6b6c24f9b4935deeca39726c9f29ca27f38d0ed8ca97a508d
|
data/lib/file_parser.rb
CHANGED
|
@@ -4,25 +4,25 @@ require 'prism'
|
|
|
4
4
|
|
|
5
5
|
module LowType
|
|
6
6
|
class FileParser
|
|
7
|
-
attr_reader :parent_map, :instance_methods, :class_methods, :
|
|
7
|
+
attr_reader :parent_map, :instance_methods, :class_methods, :line_numbers
|
|
8
8
|
|
|
9
|
-
def initialize(file_path:)
|
|
9
|
+
def initialize(klass:, file_path:)
|
|
10
10
|
@root_node = Prism.parse_file(file_path).value
|
|
11
11
|
|
|
12
12
|
parent_mapper = ParentMapper.new
|
|
13
13
|
parent_mapper.visit(@root_node)
|
|
14
14
|
@parent_map = parent_mapper.parent_map
|
|
15
15
|
|
|
16
|
-
method_visitor = MethodDefVisitor.new(@parent_map)
|
|
16
|
+
method_visitor = MethodDefVisitor.new(root_node: @root_node, parent_map:, klass:)
|
|
17
17
|
@root_node.accept(method_visitor)
|
|
18
18
|
|
|
19
19
|
@instance_methods = method_visitor.instance_methods
|
|
20
20
|
@class_methods = method_visitor.class_methods
|
|
21
|
-
@
|
|
21
|
+
@line_numbers = method_visitor.line_numbers
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def method_calls(method_names:)
|
|
25
|
-
block_visitor = MethodCallVisitor.new(parent_map
|
|
25
|
+
block_visitor = MethodCallVisitor.new(parent_map:, method_names:)
|
|
26
26
|
@root_node.accept(block_visitor)
|
|
27
27
|
block_visitor.method_calls
|
|
28
28
|
end
|
|
@@ -54,14 +54,15 @@ module LowType
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
class MethodDefVisitor < Prism::Visitor
|
|
57
|
-
attr_reader :class_methods, :instance_methods, :
|
|
57
|
+
attr_reader :class_methods, :instance_methods, :line_numbers
|
|
58
58
|
|
|
59
|
-
def initialize(parent_map)
|
|
59
|
+
def initialize(root_node:, parent_map:, klass:)
|
|
60
60
|
@parent_map = parent_map
|
|
61
|
+
@klass = klass
|
|
61
62
|
|
|
62
63
|
@instance_methods = []
|
|
63
64
|
@class_methods = []
|
|
64
|
-
@
|
|
65
|
+
@line_numbers = { class_start: 0, class_end: root_node.end_line }
|
|
65
66
|
end
|
|
66
67
|
|
|
67
68
|
def visit_def_node(node)
|
|
@@ -75,7 +76,23 @@ module LowType
|
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
def visit_call_node(node)
|
|
78
|
-
|
|
79
|
+
start_line = node.name == :private && node.respond_to?(:start_line) && node.start_line || nil
|
|
80
|
+
class_start = @line_numbers[:class_start]
|
|
81
|
+
class_end = @line_numbers[:class_end]
|
|
82
|
+
|
|
83
|
+
if start_line && class_start && class_end && start_line > class_start && start_line < class_end
|
|
84
|
+
@line_numbers[:private_start] = node.start_line
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
super
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def visit_class_node(node)
|
|
91
|
+
if node.name == @klass.to_s.to_sym
|
|
92
|
+
@line_numbers = { class_start: node.class_keyword_loc.start_line, class_end: node.end_keyword_loc.end_line }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
super
|
|
79
96
|
end
|
|
80
97
|
|
|
81
98
|
private
|
data/lib/low_type.rb
CHANGED
|
@@ -5,27 +5,29 @@ 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 'syntax'
|
|
8
|
+
require_relative 'syntax/syntax'
|
|
9
9
|
require_relative 'type_expression'
|
|
10
10
|
require_relative 'value_expression'
|
|
11
11
|
|
|
12
12
|
module LowType
|
|
13
13
|
# We do as much as possible on class load rather than on instantiation to be thread-safe and efficient.
|
|
14
14
|
def self.included(klass)
|
|
15
|
+
require_relative 'syntax/union_types' if LowType.config.union_type_expressions
|
|
16
|
+
|
|
15
17
|
class << klass
|
|
16
18
|
def low_methods
|
|
17
19
|
@low_methods ||= {}
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
|
|
21
|
-
file_path = LowType.file_path
|
|
22
|
-
parser = FileParser.new(file_path:)
|
|
23
|
-
|
|
23
|
+
file_path = LowType.file_path
|
|
24
|
+
parser = FileParser.new(klass:, file_path:)
|
|
25
|
+
line_numbers = parser.line_numbers
|
|
24
26
|
|
|
25
27
|
klass.extend InstanceTypes
|
|
26
28
|
klass.include LocalTypes
|
|
27
|
-
klass.prepend LowType::Redefiner.redefine(method_nodes: parser.instance_methods, klass:,
|
|
28
|
-
klass.singleton_class.prepend LowType::Redefiner.redefine(method_nodes: parser.class_methods, klass:,
|
|
29
|
+
klass.prepend LowType::Redefiner.redefine(method_nodes: parser.instance_methods, klass:, line_numbers:, file_path:)
|
|
30
|
+
klass.singleton_class.prepend LowType::Redefiner.redefine(method_nodes: parser.class_methods, klass:, line_numbers:, file_path:)
|
|
29
31
|
|
|
30
32
|
if (adapter = Adapter::Loader.load(klass:, parser:, file_path:))
|
|
31
33
|
adapter.process
|
|
@@ -37,8 +39,8 @@ module LowType
|
|
|
37
39
|
# Public API.
|
|
38
40
|
|
|
39
41
|
def config
|
|
40
|
-
config = Struct.new(:deep_type_check, :severity_level)
|
|
41
|
-
@config ||= config.new(false, :error)
|
|
42
|
+
config = Struct.new(:deep_type_check, :severity_level, :union_type_expressions)
|
|
43
|
+
@config ||= config.new(false, :error, true)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
def configure
|
|
@@ -47,11 +49,9 @@ module LowType
|
|
|
47
49
|
|
|
48
50
|
# Internal API.
|
|
49
51
|
|
|
50
|
-
def file_path
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# The first class found regardless of namespace will be the class that did the include.
|
|
54
|
-
caller.find { |callee| callee.end_with?("<class:#{class_name}>'") }.split(':').first
|
|
52
|
+
def file_path
|
|
53
|
+
includer_file = caller.find { |callee| callee.end_with?("in 'Module#include'") || callee.end_with?("in 'include'") }
|
|
54
|
+
includer_file.split(':').first
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
# TODO: Unit test.
|
|
@@ -60,7 +60,7 @@ module LowType
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def basic_type?(type:)
|
|
63
|
-
type.
|
|
63
|
+
type.class == Class
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def complex_type?(type:)
|
data/lib/redefiner.rb
CHANGED
|
@@ -4,7 +4,7 @@ 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
|
+
require_relative 'syntax/syntax'
|
|
8
8
|
require_relative 'type_expression'
|
|
9
9
|
|
|
10
10
|
module LowType
|
|
@@ -13,9 +13,9 @@ module LowType
|
|
|
13
13
|
using Syntax
|
|
14
14
|
|
|
15
15
|
class << self
|
|
16
|
-
def redefine(method_nodes:, klass:,
|
|
16
|
+
def redefine(method_nodes:, klass:, line_numbers:, file_path:)
|
|
17
17
|
create_proxies(method_nodes:, klass:, file_path:)
|
|
18
|
-
define_methods(method_nodes:,
|
|
18
|
+
define_methods(method_nodes:, line_numbers:)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
private
|
|
@@ -32,13 +32,20 @@ module LowType
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
def define_methods(method_nodes:,
|
|
35
|
+
def define_methods(method_nodes:, line_numbers:) # rubocop:disable Metrics
|
|
36
|
+
class_start = line_numbers[:class_start]
|
|
37
|
+
class_end = line_numbers[:class_end]
|
|
38
|
+
private_start = line_numbers[:private_start]
|
|
39
|
+
|
|
36
40
|
Module.new do
|
|
37
41
|
method_nodes.each do |method_node|
|
|
42
|
+
method_start = method_node.start_line
|
|
43
|
+
next unless method_start > class_start && method_node.end_line <= class_end
|
|
44
|
+
|
|
38
45
|
name = method_node.name
|
|
39
46
|
|
|
40
47
|
define_method(name) do |*args, **kwargs|
|
|
41
|
-
method_proxy = instance_of?(Class) ? low_methods[name] : self.class.low_methods[name]
|
|
48
|
+
method_proxy = instance_of?(Class) ? low_methods[name] : self.class.low_methods[name] || Object.low_methods[name]
|
|
42
49
|
|
|
43
50
|
method_proxy.params.each do |param_proxy|
|
|
44
51
|
# Get argument value or default value.
|
|
@@ -61,7 +68,7 @@ module LowType
|
|
|
61
68
|
super(*args, **kwargs)
|
|
62
69
|
end
|
|
63
70
|
|
|
64
|
-
private name if
|
|
71
|
+
private name if private_start && method_start > private_start
|
|
65
72
|
end
|
|
66
73
|
end
|
|
67
74
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
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) }
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
refine Hash.singleton_class do
|
|
13
|
+
def [](type)
|
|
14
|
+
return LowType::TypeExpression.new(type:) if LowType.type?(type)
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,26 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
super
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
refine Hash.singleton_class do
|
|
14
|
-
def [](type)
|
|
15
|
-
return LowType::TypeExpression.new(type:) if LowType.type?(type)
|
|
16
|
-
|
|
17
|
-
super
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Refine doesn't support inheritence.
|
|
1
|
+
###
|
|
2
|
+
# Type expressions from union types.
|
|
3
|
+
#
|
|
4
|
+
# The "|" pipe syntax requires a monkey-patch but can be disabled if you don't need union types with default values.
|
|
5
|
+
# This is the only monkey-patch in the entire library and is a relatively harmless one.
|
|
6
|
+
# @see LowType.config.union_type_expressions
|
|
7
|
+
###
|
|
24
8
|
class Object
|
|
25
9
|
# For "Type | [type_expression/type/value]" situations, redirecting to or generating a type expression from types.
|
|
26
10
|
# "|" is not defined on Object class and this is the most compute-efficient way to achieve our goal (world peace).
|
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.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -32,7 +32,8 @@ files:
|
|
|
32
32
|
- lib/proxies/param_proxy.rb
|
|
33
33
|
- lib/proxies/return_proxy.rb
|
|
34
34
|
- lib/redefiner.rb
|
|
35
|
-
- lib/syntax.rb
|
|
35
|
+
- lib/syntax/syntax.rb
|
|
36
|
+
- lib/syntax/union_types.rb
|
|
36
37
|
- lib/type_expression.rb
|
|
37
38
|
- lib/types/complex_types.rb
|
|
38
39
|
- lib/types/error_types.rb
|