lowkey 0.3.2 → 0.4.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/adapter_loader.rb +1 -1
- data/lib/adapters/sinatra_adapter.rb +15 -8
- data/lib/factories/proxy_factory.rb +41 -8
- data/lib/factories/source_factory.rb +83 -0
- data/lib/interfaces/proxy.rb +13 -5
- data/lib/lowkey.rb +8 -6
- data/lib/models/source.rb +39 -0
- data/lib/proxies/body_proxy.rb +11 -0
- data/lib/proxies/class_proxy.rb +5 -40
- data/lib/proxies/file_proxy.rb +14 -23
- data/lib/proxies/method_proxy.rb +6 -6
- data/lib/proxies/module_proxy.rb +44 -0
- data/lib/proxies/param_proxy.rb +4 -5
- data/lib/proxies/return_proxy.rb +3 -4
- data/lib/queries/query.rb +11 -0
- data/lib/version.rb +1 -1
- data/lib/visitors/class_visitor.rb +31 -0
- data/lib/visitors/method_call_visitor.rb +23 -6
- data/lib/visitors/method_def_visitor.rb +15 -18
- data/lib/visitors/module_visitor.rb +40 -0
- data/lib/visitors/visitor.rb +15 -8
- metadata +7 -2
- data/lib/visitors/method_class_visitor.rb +0 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a2bcc0b4b60c158051ab7da59abb915078011d30cf4a231db29ddc03f87a9af4
|
|
4
|
+
data.tar.gz: 50ed70e6d38d529f0b9d8c39cd31e3af9df2fd0f04100bf4739235d81daadb59
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2585284e511997be690bffcee538d8e8e97150ce9338b803867aafc4680f1e8caa70b4d34d8d8c4952cf8ba2cd141462ae74d7b0fad0194b5e22d3cb90ed7fb5
|
|
7
|
+
data.tar.gz: eb4a250a62dfd1cf13110095f8a4b831b9b47c282944c38f2edd642bb3f4817ddfd9fe838acd5241d0088ab93ec3d2e56c3ae6cec45dfbda2de0b84b389c019b
|
|
@@ -9,7 +9,7 @@ module Lowkey
|
|
|
9
9
|
def load(file_proxy:)
|
|
10
10
|
class_proxies = file_proxy.definitions.values.filter { |definition| definition.instance_of?(ClassProxy) }
|
|
11
11
|
class_proxies.each do |class_proxy|
|
|
12
|
-
SinatraAdapter.new(class_proxy:).load
|
|
12
|
+
SinatraAdapter.new(file_proxy:, class_proxy:).load
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../factories/source_factory'
|
|
3
4
|
require_relative '../interfaces/adapter'
|
|
4
5
|
|
|
5
6
|
module Lowkey
|
|
6
7
|
class SinatraAdapter < Adapter
|
|
7
|
-
def initialize(class_proxy:)
|
|
8
|
+
def initialize(file_proxy:, class_proxy:)
|
|
9
|
+
@file_proxy = file_proxy
|
|
8
10
|
@class_proxy = class_proxy
|
|
9
|
-
@file_path = class_proxy.file_path
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def load # rubocop:disable Metrics/AbcSize
|
|
@@ -20,19 +21,25 @@ module Lowkey
|
|
|
20
21
|
pattern = arguments_node.arguments.first.content
|
|
21
22
|
route = "#{method_call_node.name.upcase} #{pattern}"
|
|
22
23
|
name = route
|
|
23
|
-
|
|
24
|
-
start_line = method_call_node.start_line
|
|
24
|
+
source = SourceFactory.method_call_source(method_call_node:, arguments_node:, file_path:, lines:)
|
|
25
25
|
|
|
26
|
-
next unless (return_proxy = ProxyFactory.return_proxy(method_node: method_call_node, name:,
|
|
26
|
+
next unless (return_proxy = ProxyFactory.return_proxy(method_node: method_call_node, name:, source:))
|
|
27
27
|
|
|
28
|
-
param_proxies = [ParamProxy.new(
|
|
28
|
+
param_proxies = [ParamProxy.new(source:, name:, type: :pos_req, position: 0)]
|
|
29
|
+
method_proxy = MethodProxy.new(source:, name:, param_proxies:, return_proxy:)
|
|
29
30
|
|
|
30
|
-
@class_proxy.keyed_methods[route] =
|
|
31
|
+
@class_proxy.keyed_methods[route] = method_proxy
|
|
31
32
|
end
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
private
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
def file_path
|
|
38
|
+
@file_proxy.file_path
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def lines
|
|
42
|
+
@file_proxy.lines
|
|
43
|
+
end
|
|
37
44
|
end
|
|
38
45
|
end
|
|
@@ -1,13 +1,48 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../factories/source_factory'
|
|
4
|
+
|
|
5
|
+
require_relative '../proxies/body_proxy'
|
|
6
|
+
require_relative '../proxies/class_proxy'
|
|
7
|
+
require_relative '../proxies/file_proxy'
|
|
3
8
|
require_relative '../proxies/method_proxy'
|
|
9
|
+
require_relative '../proxies/module_proxy'
|
|
4
10
|
require_relative '../proxies/param_proxy'
|
|
5
11
|
require_relative '../proxies/return_proxy'
|
|
6
12
|
|
|
7
13
|
module Lowkey
|
|
8
14
|
class ProxyFactory
|
|
9
15
|
class << self
|
|
10
|
-
def
|
|
16
|
+
def file_proxy(root_node:, file_path:)
|
|
17
|
+
source = SourceFactory.file_source(root_node:, file_path:)
|
|
18
|
+
FileProxy.new(root_node:, source:)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def module_proxy(node:, namespace:, file_path:, lines:)
|
|
22
|
+
source = SourceFactory.module_source(node:, namespace:, file_path:, lines:)
|
|
23
|
+
name = node.respond_to?(:name) ? node.name : 'Object'
|
|
24
|
+
ModuleProxy.new(node:, name:, namespace:, source:)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def class_proxy(node:, namespace:, file_path:, lines:)
|
|
28
|
+
source = SourceFactory.class_source(node:, file_path:, lines:)
|
|
29
|
+
ClassProxy.new(node:, name: node.name, namespace:, source:)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def method_proxy(method_node:, file_proxy:)
|
|
33
|
+
name = method_node.name
|
|
34
|
+
|
|
35
|
+
method_source = SourceFactory.method_source(method_node:, file_path: file_proxy.file_path, lines: file_proxy.lines)
|
|
36
|
+
body_source = SourceFactory.body_source(method_source:)
|
|
37
|
+
|
|
38
|
+
param_proxies = param_proxies(parameters_node: method_node.parameters, file_path: file_proxy.file_path, source: method_source)
|
|
39
|
+
body_proxy = BodyProxy.new(name:, source: body_source)
|
|
40
|
+
return_proxy = return_proxy(name:, method_node:, source: method_source)
|
|
41
|
+
|
|
42
|
+
MethodProxy.new(name:, source: method_source, param_proxies:, body_proxy:, return_proxy:)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def param_proxies(parameters_node:, source:, file_path:)
|
|
11
46
|
return [] if parameters_node.nil?
|
|
12
47
|
|
|
13
48
|
param_types = {
|
|
@@ -19,24 +54,22 @@ module Lowkey
|
|
|
19
54
|
|
|
20
55
|
params = [*parameters_node.requireds, *parameters_node.optionals, *parameters_node.keywords]
|
|
21
56
|
params.map.with_index do |param, position|
|
|
22
|
-
type = param_types[param.class]
|
|
23
57
|
name = param.name
|
|
24
|
-
|
|
25
|
-
|
|
58
|
+
source = SourceFactory.param_source(param_node: param, file_path:, lines: source.lines)
|
|
59
|
+
type = param_types[param.class]
|
|
26
60
|
value = param.respond_to?(:value) ? param.value.slice : ':LOWKEY_UNDEFINED'
|
|
27
61
|
|
|
28
|
-
ParamProxy.new(
|
|
62
|
+
ParamProxy.new(name:, source:, type:, position:, value:)
|
|
29
63
|
end
|
|
30
64
|
end
|
|
31
65
|
|
|
32
|
-
def return_proxy(method_node:, name:,
|
|
66
|
+
def return_proxy(method_node:, name:, source:)
|
|
33
67
|
return_node = find_return_node(method_node:)
|
|
34
68
|
return nil if return_node.nil?
|
|
35
69
|
|
|
36
|
-
start_line = return_node.start_line
|
|
37
70
|
value = return_node.body.slice
|
|
38
71
|
|
|
39
|
-
ReturnProxy.new(name:,
|
|
72
|
+
ReturnProxy.new(name:, source:, value:)
|
|
40
73
|
end
|
|
41
74
|
|
|
42
75
|
private
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../models/source'
|
|
4
|
+
|
|
5
|
+
module Lowkey
|
|
6
|
+
class SourceFactory
|
|
7
|
+
class << self
|
|
8
|
+
def file_source(root_node:, file_path:)
|
|
9
|
+
end_line = root_node.respond_to?(:end_line) ? root_node.end_line : nil
|
|
10
|
+
|
|
11
|
+
Source.new(file_path:, scope: 'file', lines: root_node.script_lines, start_line: 0, end_line:)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def module_source(node:, namespace:, file_path:, lines:)
|
|
15
|
+
start_line = node.respond_to?(:class_keyword_loc) ? node.class_keyword_loc.start_line : 0
|
|
16
|
+
end_line = node.respond_to?(:end_keyword_loc) ? node.end_keyword_loc.end_line : start_line
|
|
17
|
+
end_line = node.end_line if namespace == 'Object'
|
|
18
|
+
scope = node.respond_to?(:name) ? node.name : 'Object'
|
|
19
|
+
|
|
20
|
+
Source.new(file_path:, scope:, lines:, start_line:, end_line:)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def class_source(node:, file_path:, lines:)
|
|
24
|
+
start_line = node.respond_to?(:class_keyword_loc) ? node.class_keyword_loc.start_line : 0
|
|
25
|
+
end_line = node.respond_to?(:end_keyword_loc) ? node.end_keyword_loc.end_line : start_line
|
|
26
|
+
|
|
27
|
+
Source.new(file_path:, scope: node.name, lines:, start_line:, end_line:)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def method_source(method_node:, file_path:, lines:)
|
|
31
|
+
scope = method_node.name
|
|
32
|
+
start_line = method_node.start_line
|
|
33
|
+
end_line = method_node.end_line
|
|
34
|
+
end_line = end_line_from_indent(method_node:, lines:) if end_line > lines.count
|
|
35
|
+
|
|
36
|
+
Source.new(file_path:, scope:, lines:, start_line:, end_line:)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def param_source(param_node:, file_path:, lines:)
|
|
40
|
+
scope = param_node.name
|
|
41
|
+
start_line = param_node.start_line
|
|
42
|
+
end_line = param_node.end_line
|
|
43
|
+
|
|
44
|
+
Source.new(file_path:, scope:, lines:, start_line:, end_line:)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def body_source(method_source:)
|
|
48
|
+
# TODO: Handle multi-line return proxy which increases body start line.
|
|
49
|
+
start_line = method_source.start_line + 1
|
|
50
|
+
end_line = method_source.end_line - 1
|
|
51
|
+
|
|
52
|
+
Source.new(file_path: method_source.file_path, scope: method_source.scope, lines: method_source.lines, start_line:, end_line:)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def method_call_source(method_call_node:, arguments_node:, file_path:, lines:)
|
|
56
|
+
pattern = arguments_node.arguments.first.content
|
|
57
|
+
scope = "#{method_call_node.name.upcase} #{pattern}"
|
|
58
|
+
start_line = method_call_node.start_line
|
|
59
|
+
end_line = method_call_node.end_line
|
|
60
|
+
|
|
61
|
+
Source.new(file_path:, scope:, lines:, start_line:, end_line:)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# TODO: end_line = start_line if it's an endless method like "def method = expression".
|
|
67
|
+
def end_line_from_indent(method_node:, lines:)
|
|
68
|
+
end_index = nil
|
|
69
|
+
|
|
70
|
+
index = method_node.start_line - 1
|
|
71
|
+
indent = lines[index].split('def').first
|
|
72
|
+
|
|
73
|
+
while end_index.nil?
|
|
74
|
+
end_index = index if lines[index].nil?
|
|
75
|
+
end_index = index if lines[index].start_with?("#{indent}end")
|
|
76
|
+
index += 1
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end_index + 1
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/interfaces/proxy.rb
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
|
|
3
5
|
module Lowkey
|
|
6
|
+
# Proxy and Source abstract away nodes and avoid referencing the node on the proxy itself. Will this change?
|
|
7
|
+
# The goal is to make an API that's indiependent of the AST, that can manipulate source code line by line.
|
|
4
8
|
class Proxy
|
|
5
|
-
|
|
9
|
+
extend Forwardable
|
|
10
|
+
|
|
11
|
+
attr_reader :name
|
|
12
|
+
|
|
13
|
+
def_delegators :@source, :file_path, :lines, :start_line, :end_line, :scope
|
|
14
|
+
def_delegators :@source, :wrap, :export
|
|
6
15
|
|
|
7
|
-
def initialize(
|
|
8
|
-
@
|
|
9
|
-
@
|
|
10
|
-
@scope = scope
|
|
16
|
+
def initialize(name:, source:)
|
|
17
|
+
@name = name
|
|
18
|
+
@source = source
|
|
11
19
|
end
|
|
12
20
|
end
|
|
13
21
|
end
|
data/lib/lowkey.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require 'prism'
|
|
4
4
|
|
|
5
5
|
require_relative 'adapters/adapter_loader'
|
|
6
|
+
require_relative 'factories/proxy_factory'
|
|
7
|
+
require_relative 'maps/parent_map'
|
|
6
8
|
require_relative 'visitors/visitor'
|
|
7
9
|
require_relative 'proxies/file_proxy'
|
|
8
10
|
|
|
@@ -16,9 +18,9 @@ module Lowkey
|
|
|
16
18
|
keys[key]
|
|
17
19
|
end
|
|
18
20
|
|
|
19
|
-
def load(file_path)
|
|
21
|
+
def load(file_path, cache: true)
|
|
20
22
|
root_node = Prism.parse_file(file_path).value
|
|
21
|
-
file_proxy =
|
|
23
|
+
file_proxy = ProxyFactory.file_proxy(root_node:, file_path:)
|
|
22
24
|
|
|
23
25
|
parent_map = ParentMap.new(root_node:)
|
|
24
26
|
visitor = Visitor.new(file_proxy:, parent_map:)
|
|
@@ -26,7 +28,7 @@ module Lowkey
|
|
|
26
28
|
|
|
27
29
|
AdapterLoader.load(file_proxy:)
|
|
28
30
|
|
|
29
|
-
if Lowkey.config.cache
|
|
31
|
+
if Lowkey.config.cache && cache
|
|
30
32
|
map_file_path(file_proxy:)
|
|
31
33
|
map_definitions(file_proxy:)
|
|
32
34
|
end
|
|
@@ -50,11 +52,11 @@ module Lowkey
|
|
|
50
52
|
private
|
|
51
53
|
|
|
52
54
|
def map_file_path(file_proxy:)
|
|
53
|
-
keys[file_proxy.
|
|
55
|
+
keys[file_proxy.file_path] = file_proxy
|
|
54
56
|
|
|
55
57
|
# Map absolute paths to project root/relative paths.
|
|
56
|
-
project_path = file_proxy.
|
|
57
|
-
keys[project_path] = file_proxy if project_path != file_proxy.
|
|
58
|
+
project_path = file_proxy.file_path.delete_prefix(Dir.pwd).delete_prefix('/')
|
|
59
|
+
keys[project_path] = file_proxy if project_path != file_proxy.file_path
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
def map_definitions(file_proxy:)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lowkey
|
|
4
|
+
class Source
|
|
5
|
+
attr_reader :file_path, :scope
|
|
6
|
+
attr_accessor :lines, :start_line, :end_line
|
|
7
|
+
|
|
8
|
+
def initialize(file_path:, scope:, lines:, start_line:, end_line: nil)
|
|
9
|
+
@file_path = file_path
|
|
10
|
+
@scope = scope
|
|
11
|
+
|
|
12
|
+
@start_line = start_line
|
|
13
|
+
@end_line = end_line
|
|
14
|
+
@lines = lines
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def wrap(prefix:, suffix:)
|
|
18
|
+
start_line = lines[start_index]
|
|
19
|
+
indent = start_line[/^\s+/]
|
|
20
|
+
lines[start_index] = indent + prefix.to_s + start_line.lstrip
|
|
21
|
+
|
|
22
|
+
lines[end_index] = lines[end_index] + suffix.to_s
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def export
|
|
26
|
+
lines.join
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def start_index
|
|
32
|
+
start_line - 1
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def end_index
|
|
36
|
+
end_line - 1
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/proxies/class_proxy.rb
CHANGED
|
@@ -1,50 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '
|
|
3
|
+
require_relative 'module_proxy'
|
|
4
4
|
|
|
5
5
|
module Lowkey
|
|
6
|
-
class ClassProxy
|
|
7
|
-
|
|
6
|
+
class ClassProxy < ModuleProxy
|
|
7
|
+
attr_accessor :instance_methods
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
def initialize(node:, name:, namespace:, source:)
|
|
10
|
+
super(node:, name:, namespace:, source:)
|
|
11
11
|
|
|
12
|
-
def initialize(node:, namespace:, file_proxy:)
|
|
13
|
-
@node = node
|
|
14
|
-
@namespace = namespace
|
|
15
|
-
@file_proxy = file_proxy
|
|
16
|
-
|
|
17
|
-
@start_line = node.respond_to?(:class_keyword_loc) ? node.class_keyword_loc.start_line : 0
|
|
18
|
-
@end_line = node.respond_to?(:end_keyword_loc) ? node.end_keyword_loc.end_line : @start_line
|
|
19
|
-
@end_line = file_proxy.end_line if namespace == 'Object'
|
|
20
|
-
@private_start_line = nil
|
|
21
|
-
|
|
22
|
-
@keyed_methods = {}
|
|
23
|
-
@class_methods = {}
|
|
24
12
|
@instance_methods = {}
|
|
25
|
-
|
|
26
|
-
@method_calls = []
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def [](key)
|
|
30
|
-
key.start_with?('.') ? query(node: @node, namespace: nil, name: key.delete_prefix('.')) : @keyed_methods[key]
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def file_path
|
|
34
|
-
@file_proxy.path
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
class << self
|
|
38
|
-
def class_method?(method_node:, parent_map:)
|
|
39
|
-
return true if method_node.is_a?(::Prism::DefNode) && method_node.receiver.instance_of?(Prism::SelfNode) # self.method_name
|
|
40
|
-
return true if method_node.is_a?(::Prism::SingletonClassNode) # class << self
|
|
41
|
-
|
|
42
|
-
if (parent_node = parent_map[method_node])
|
|
43
|
-
return class_method?(method_node: parent_node, parent_map:)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
false
|
|
47
|
-
end
|
|
48
13
|
end
|
|
49
14
|
end
|
|
50
15
|
end
|
data/lib/proxies/file_proxy.rb
CHANGED
|
@@ -1,50 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '../
|
|
3
|
+
require_relative '../interfaces/proxy'
|
|
4
4
|
require_relative '../proxies/class_proxy'
|
|
5
5
|
require_relative '../queries/query'
|
|
6
6
|
|
|
7
7
|
module Lowkey
|
|
8
|
-
class FileProxy
|
|
8
|
+
class FileProxy < Proxy
|
|
9
9
|
include Query
|
|
10
10
|
|
|
11
|
-
attr_reader :
|
|
11
|
+
attr_reader :root_node
|
|
12
12
|
attr_accessor :definitions, :dependencies
|
|
13
13
|
|
|
14
|
-
def initialize(
|
|
15
|
-
|
|
16
|
-
@root_node = root_node
|
|
14
|
+
def initialize(root_node:, source:)
|
|
15
|
+
super(name: nil, source:)
|
|
17
16
|
|
|
18
|
-
@
|
|
19
|
-
@end_line = root_node.respond_to?(:end_line) ? root_node.end_line : nil
|
|
17
|
+
@root_node = root_node
|
|
20
18
|
|
|
21
19
|
@definitions = {}
|
|
22
|
-
@dependencies =
|
|
20
|
+
@dependencies = Set.new
|
|
23
21
|
end
|
|
24
22
|
|
|
25
23
|
def [](keypath)
|
|
26
|
-
namespace, *
|
|
27
|
-
|
|
24
|
+
namespace, *file_path = keypath.split('.')
|
|
25
|
+
file_path.empty? ? @definitions[namespace] : query(node: @root_node, namespace:, name: file_path.join)
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
def []=(keypath, value)
|
|
31
29
|
# TODO: Slice the lines in a file and replace with the output of the class proxy.
|
|
32
30
|
end
|
|
33
31
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
namespace
|
|
37
|
-
@definitions[namespace] ||= ClassProxy.new(node:, namespace:, file_proxy: self)
|
|
32
|
+
def upsert_definition(module_proxy:)
|
|
33
|
+
# TODO: Merge duplicate class with existing class.
|
|
34
|
+
@definitions[module_proxy.namespace] ||= module_proxy
|
|
38
35
|
end
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def namespace(node:, parent_map:, namespace: [])
|
|
43
|
-
return namespace if parent_map[node].nil?
|
|
44
|
-
|
|
45
|
-
namespace << node.constant_path.name.to_s if node.respond_to?(:constant_path)
|
|
46
|
-
|
|
47
|
-
namespace(node: parent_map[node], parent_map:, namespace:)
|
|
37
|
+
def upsert_dependency(namespace:)
|
|
38
|
+
@dependencies.add(namespace)
|
|
48
39
|
end
|
|
49
40
|
end
|
|
50
41
|
end
|
data/lib/proxies/method_proxy.rb
CHANGED
|
@@ -7,17 +7,17 @@ module Lowkey
|
|
|
7
7
|
class MethodProxy < Proxy
|
|
8
8
|
include Query
|
|
9
9
|
|
|
10
|
-
attr_reader :
|
|
10
|
+
attr_reader :params, :body, :return_proxy
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
super(file_path:, start_line:, scope:)
|
|
12
|
+
def initialize(name:, source:, param_proxies: [], body_proxy: nil, return_proxy: nil)
|
|
13
|
+
super(name:, source:)
|
|
15
14
|
|
|
16
|
-
@name = name
|
|
17
15
|
@params = param_proxies
|
|
16
|
+
@body = body_proxy
|
|
17
|
+
@return_proxy = return_proxy
|
|
18
|
+
|
|
18
19
|
@named_params = name_params
|
|
19
20
|
@tagged_params = tag_params
|
|
20
|
-
@return_proxy = return_proxy
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def [](key)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/proxy'
|
|
4
|
+
require_relative '../queries/query'
|
|
5
|
+
|
|
6
|
+
module Lowkey
|
|
7
|
+
class ModuleProxy < Proxy
|
|
8
|
+
include Query
|
|
9
|
+
|
|
10
|
+
attr_reader :namespace
|
|
11
|
+
attr_accessor :private_start_line, :keyed_methods, :class_methods, :method_calls
|
|
12
|
+
|
|
13
|
+
def initialize(node:, name:, namespace:, source:)
|
|
14
|
+
super(name:, source:)
|
|
15
|
+
|
|
16
|
+
@node = node
|
|
17
|
+
@namespace = namespace
|
|
18
|
+
|
|
19
|
+
@private_start_line = nil
|
|
20
|
+
|
|
21
|
+
@keyed_methods = {}
|
|
22
|
+
@class_methods = {}
|
|
23
|
+
|
|
24
|
+
@method_calls = []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def [](key)
|
|
28
|
+
key.start_with?('.') ? query(node: @node, namespace: nil, name: key.delete_prefix('.')) : @keyed_methods[key]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
def class_method?(method_node:, parent_map:)
|
|
33
|
+
return true if method_node.is_a?(::Prism::DefNode) && method_node.receiver.instance_of?(Prism::SelfNode) # self.method_name
|
|
34
|
+
return true if method_node.is_a?(::Prism::SingletonClassNode) # class << self
|
|
35
|
+
|
|
36
|
+
if (parent_node = parent_map[method_node])
|
|
37
|
+
return class_method?(method_node: parent_node, parent_map:)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
false
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/proxies/param_proxy.rb
CHANGED
|
@@ -4,17 +4,16 @@ require_relative '../interfaces/proxy'
|
|
|
4
4
|
|
|
5
5
|
module Lowkey
|
|
6
6
|
class ParamProxy < Proxy
|
|
7
|
-
attr_reader :
|
|
7
|
+
attr_reader :type, :position, :value
|
|
8
8
|
attr_accessor :expression
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
super(file_path:, start_line:, scope:)
|
|
10
|
+
def initialize(name:, source:, type:, position: nil, value: :LOWKEY_UNDEFINED, expression: nil) # rubocop:disable Metrics/ParameterLists
|
|
11
|
+
super(name:, source:)
|
|
13
12
|
|
|
14
|
-
@name = name
|
|
15
13
|
@type = type
|
|
16
14
|
@position = position
|
|
17
15
|
@value = value
|
|
16
|
+
|
|
18
17
|
@expression = expression
|
|
19
18
|
end
|
|
20
19
|
|
data/lib/proxies/return_proxy.rb
CHANGED
|
@@ -4,13 +4,12 @@ require_relative '../interfaces/proxy'
|
|
|
4
4
|
|
|
5
5
|
module Lowkey
|
|
6
6
|
class ReturnProxy < Proxy
|
|
7
|
-
attr_reader :
|
|
7
|
+
attr_reader :value
|
|
8
8
|
attr_accessor :expression
|
|
9
9
|
|
|
10
|
-
def initialize(
|
|
11
|
-
super(
|
|
10
|
+
def initialize(name:, source:, value: :LOWKEY_UNDEFINED, expression: nil)
|
|
11
|
+
super(name:, source:)
|
|
12
12
|
|
|
13
|
-
@name = name
|
|
14
13
|
@value = value
|
|
15
14
|
@expression = expression
|
|
16
15
|
end
|
data/lib/queries/query.rb
CHANGED
|
@@ -8,5 +8,16 @@ module Lowkey
|
|
|
8
8
|
n.respond_to?(:name) && n.name == name.to_sym
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
|
+
|
|
12
|
+
def namespace(node:, parent_map:, namespace: [])
|
|
13
|
+
if parent_map[node].nil?
|
|
14
|
+
namespace << 'Object' if namespace.empty?
|
|
15
|
+
return namespace.reverse.join('::')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
namespace << node.constant_path.name.to_s if node.respond_to?(:constant_path)
|
|
19
|
+
|
|
20
|
+
namespace(node: parent_map[node], parent_map:, namespace:)
|
|
21
|
+
end
|
|
11
22
|
end
|
|
12
23
|
end
|
data/lib/version.rb
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../factories/proxy_factory'
|
|
4
|
+
require_relative '../queries/query'
|
|
5
|
+
|
|
6
|
+
module Lowkey
|
|
7
|
+
class ClassVisitor
|
|
8
|
+
include Query
|
|
9
|
+
|
|
10
|
+
def initialize(file_proxy:, parent_map:)
|
|
11
|
+
@file_proxy = file_proxy
|
|
12
|
+
@parent_map = parent_map
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def visit(node)
|
|
16
|
+
namespace = namespace(node:, parent_map:)
|
|
17
|
+
class_proxy = ProxyFactory.class_proxy(node:, namespace:, file_path:, lines:)
|
|
18
|
+
@file_proxy.upsert_definition(module_proxy: class_proxy)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_reader :parent_map
|
|
22
|
+
|
|
23
|
+
def file_path
|
|
24
|
+
@file_proxy.file_path
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def lines
|
|
28
|
+
@file_proxy.lines
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../queries/query'
|
|
4
|
+
|
|
3
5
|
module Lowkey
|
|
4
6
|
class MethodCallVisitor
|
|
5
|
-
|
|
7
|
+
include Query
|
|
6
8
|
|
|
7
9
|
def initialize(file_proxy:, parent_map:)
|
|
8
10
|
@file_proxy = file_proxy
|
|
@@ -10,13 +12,28 @@ module Lowkey
|
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
def visit(node)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
namespace = namespace(node:, parent_map:)
|
|
16
|
+
module_proxy = file_proxy[namespace]
|
|
17
|
+
|
|
18
|
+
module_proxy.method_calls << node
|
|
19
|
+
upsert_dependency(node:, namespace:)
|
|
15
20
|
|
|
16
|
-
return unless node.name == :private && node.respond_to?(:start_line) &&
|
|
17
|
-
return unless node.start_line >
|
|
21
|
+
return unless node.name == :private && node.respond_to?(:start_line) && module_proxy.start_line && module_proxy.end_line
|
|
22
|
+
return unless node.start_line > module_proxy.start_line && node.start_line < module_proxy.end_line
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
module_proxy.private_start_line = node.start_line
|
|
20
25
|
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def upsert_dependency(node:, namespace:)
|
|
30
|
+
return unless %i[include extend].include?(node.name)
|
|
31
|
+
|
|
32
|
+
dependency_name = node.arguments.arguments.first.name.to_s
|
|
33
|
+
dependency_name = "#{namespace}::#{dependency_name}" unless dependency_name.start_with?('::')
|
|
34
|
+
file_proxy.upsert_dependency(namespace: dependency_name)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
attr_reader :file_proxy, :parent_map
|
|
21
38
|
end
|
|
22
39
|
end
|
|
@@ -1,39 +1,36 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../factories/source_factory'
|
|
3
4
|
require_relative '../factories/proxy_factory'
|
|
5
|
+
require_relative '../proxies/method_proxy'
|
|
6
|
+
require_relative '../queries/query'
|
|
4
7
|
|
|
5
8
|
module Lowkey
|
|
6
9
|
class MethodDefVisitor
|
|
7
|
-
|
|
10
|
+
include Query
|
|
8
11
|
|
|
9
12
|
def initialize(file_proxy:, parent_map:)
|
|
10
13
|
@file_proxy = file_proxy
|
|
11
14
|
@parent_map = parent_map
|
|
12
15
|
end
|
|
13
16
|
|
|
14
|
-
def visit(method_node)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
def visit(method_node)
|
|
18
|
+
namespace = namespace(node: method_node, parent_map:)
|
|
19
|
+
module_proxy = file_proxy[namespace]
|
|
20
|
+
method_proxy = ProxyFactory.method_proxy(method_node:, file_proxy:)
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
return_proxy = ProxyFactory.return_proxy(name:, method_node:, file_path:, scope:)
|
|
21
|
-
method_proxy = MethodProxy.new(file_path:, start_line: method_node.start_line, scope:, name:, param_proxies:, return_proxy:)
|
|
22
|
+
module_proxy.keyed_methods[method_node.name] = method_proxy
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
else
|
|
29
|
-
class_proxy.instance_methods[method_node.name] = method_proxy
|
|
24
|
+
# TODO: Implemented as tagged methods similar to tagged params.
|
|
25
|
+
if ModuleProxy.class_method?(method_node:, parent_map:)
|
|
26
|
+
module_proxy.class_methods[method_node.name] = method_proxy
|
|
27
|
+
elsif module_proxy.class <= ClassProxy
|
|
28
|
+
module_proxy.instance_methods[method_node.name] = method_proxy
|
|
30
29
|
end
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
private
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
@file_proxy.path
|
|
37
|
-
end
|
|
34
|
+
attr_reader :file_proxy, :parent_map
|
|
38
35
|
end
|
|
39
36
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../factories/proxy_factory'
|
|
4
|
+
require_relative '../queries/query'
|
|
5
|
+
|
|
6
|
+
module Lowkey
|
|
7
|
+
class ModuleVisitor
|
|
8
|
+
include Query
|
|
9
|
+
|
|
10
|
+
def initialize(file_proxy:, parent_map:)
|
|
11
|
+
@file_proxy = file_proxy
|
|
12
|
+
@parent_map = parent_map
|
|
13
|
+
|
|
14
|
+
create_root_module
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def visit(node)
|
|
18
|
+
namespace = namespace(node:, parent_map:)
|
|
19
|
+
module_proxy = ProxyFactory.module_proxy(node:, namespace:, file_path:, lines:)
|
|
20
|
+
@file_proxy.upsert_definition(module_proxy:)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def create_root_module
|
|
26
|
+
module_proxy = ProxyFactory.module_proxy(node: @file_proxy.root_node, namespace: 'Object', file_path:, lines:)
|
|
27
|
+
@file_proxy.upsert_definition(module_proxy:)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
attr_reader :parent_map
|
|
31
|
+
|
|
32
|
+
def file_path
|
|
33
|
+
@file_proxy.file_path
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def lines
|
|
37
|
+
@file_proxy.lines
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/visitors/visitor.rb
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '
|
|
4
|
-
require_relative '
|
|
3
|
+
require_relative 'class_visitor'
|
|
4
|
+
require_relative 'module_visitor'
|
|
5
5
|
require_relative 'method_def_visitor'
|
|
6
|
+
require_relative 'method_call_visitor'
|
|
6
7
|
|
|
7
8
|
module Lowkey
|
|
8
9
|
class Visitor < Prism::Visitor
|
|
9
10
|
def initialize(file_proxy:, parent_map:)
|
|
10
|
-
@
|
|
11
|
-
@
|
|
11
|
+
@class_visitor = ClassVisitor.new(file_proxy:, parent_map:)
|
|
12
|
+
@module_visitor = ModuleVisitor.new(file_proxy:, parent_map:)
|
|
12
13
|
@method_def_visitor = MethodDefVisitor.new(file_proxy:, parent_map:)
|
|
14
|
+
@method_call_visitor = MethodCallVisitor.new(file_proxy:, parent_map:)
|
|
13
15
|
end
|
|
14
16
|
|
|
15
|
-
def
|
|
16
|
-
@
|
|
17
|
+
def visit_class_node(node)
|
|
18
|
+
@class_visitor.visit(node)
|
|
17
19
|
super
|
|
18
20
|
end
|
|
19
21
|
|
|
20
|
-
def
|
|
21
|
-
@
|
|
22
|
+
def visit_module_node(node)
|
|
23
|
+
@module_visitor.visit(node)
|
|
22
24
|
super
|
|
23
25
|
end
|
|
24
26
|
|
|
@@ -26,5 +28,10 @@ module Lowkey
|
|
|
26
28
|
@method_def_visitor.visit(node)
|
|
27
29
|
super
|
|
28
30
|
end
|
|
31
|
+
|
|
32
|
+
def visit_call_node(node)
|
|
33
|
+
@method_call_visitor.visit(node)
|
|
34
|
+
super
|
|
35
|
+
end
|
|
29
36
|
end
|
|
30
37
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lowkey
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -34,20 +34,25 @@ files:
|
|
|
34
34
|
- lib/adapters/adapter_loader.rb
|
|
35
35
|
- lib/adapters/sinatra_adapter.rb
|
|
36
36
|
- lib/factories/proxy_factory.rb
|
|
37
|
+
- lib/factories/source_factory.rb
|
|
37
38
|
- lib/interfaces/adapter.rb
|
|
38
39
|
- lib/interfaces/proxy.rb
|
|
39
40
|
- lib/lowkey.rb
|
|
40
41
|
- lib/maps/parent_map.rb
|
|
42
|
+
- lib/models/source.rb
|
|
43
|
+
- lib/proxies/body_proxy.rb
|
|
41
44
|
- lib/proxies/class_proxy.rb
|
|
42
45
|
- lib/proxies/file_proxy.rb
|
|
43
46
|
- lib/proxies/method_proxy.rb
|
|
47
|
+
- lib/proxies/module_proxy.rb
|
|
44
48
|
- lib/proxies/param_proxy.rb
|
|
45
49
|
- lib/proxies/return_proxy.rb
|
|
46
50
|
- lib/queries/query.rb
|
|
47
51
|
- lib/version.rb
|
|
52
|
+
- lib/visitors/class_visitor.rb
|
|
48
53
|
- lib/visitors/method_call_visitor.rb
|
|
49
|
-
- lib/visitors/method_class_visitor.rb
|
|
50
54
|
- lib/visitors/method_def_visitor.rb
|
|
55
|
+
- lib/visitors/module_visitor.rb
|
|
51
56
|
- lib/visitors/visitor.rb
|
|
52
57
|
homepage: https://github.com/low-rb/lowkey
|
|
53
58
|
licenses: []
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Lowkey
|
|
4
|
-
class MethodClassVisitor
|
|
5
|
-
attr_reader :parent_map
|
|
6
|
-
|
|
7
|
-
def initialize(file_proxy:, parent_map:)
|
|
8
|
-
@file_proxy = file_proxy
|
|
9
|
-
@parent_map = parent_map
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def visit(node)
|
|
13
|
-
@file_proxy.upsert_class_proxy(node:, parent_map:)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|