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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32447c431fd5565b44ba41a641dbf1081619def560b139fbb090902a281827f6
4
- data.tar.gz: 98e73906e4657e135ab0b5e4b9c81d9b4b898d34caff0162c77155859c68d3b8
3
+ metadata.gz: a2bcc0b4b60c158051ab7da59abb915078011d30cf4a231db29ddc03f87a9af4
4
+ data.tar.gz: 50ed70e6d38d529f0b9d8c39cd31e3af9df2fd0f04100bf4739235d81daadb59
5
5
  SHA512:
6
- metadata.gz: 8eaed237ab5aaadffdef5e5a2b9cd0a5fd882b5d5f1591aeb64c7bebbe7c0407e3d4c5626dd3dbb86343391dbfa49750f889877f7a404e4536bfd3034ce22586
7
- data.tar.gz: 9118dada97ff5c4f245d4e3479cd64771956b4cecf28f0ad5b475dc46cba4cdd49d3f00e2cc25cf1ae6ed7283416dc74e6ce4cbd4322880f593e47224e1301f7
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
- scope = route
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:, file_path:, scope:))
26
+ next unless (return_proxy = ProxyFactory.return_proxy(method_node: method_call_node, name:, source:))
27
27
 
28
- param_proxies = [ParamProxy.new(file_path:, start_line:, scope:, name:, type: :pos_req, position: 0)]
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] = MethodProxy.new(file_path:, start_line:, scope:, name:, param_proxies:, return_proxy:)
31
+ @class_proxy.keyed_methods[route] = method_proxy
31
32
  end
32
33
  end
33
34
 
34
35
  private
35
36
 
36
- attr_reader :file_path
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 param_proxies(parameters_node:, file_path:, scope:)
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
- scope = name
25
- start_line = param.start_line
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(file_path:, start_line:, scope:, name:, type:, position:, value:)
62
+ ParamProxy.new(name:, source:, type:, position:, value:)
29
63
  end
30
64
  end
31
65
 
32
- def return_proxy(method_node:, name:, file_path:, scope:)
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:, file_path:, start_line:, scope:, value:)
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
@@ -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
- attr_reader :file_path, :start_line, :scope
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(file_path:, start_line:, scope:)
8
- @file_path = file_path
9
- @start_line = start_line
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 = FileProxy.new(path: file_path, root_node:)
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.path] = 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.path.delete_prefix(Dir.pwd).delete_prefix('/')
57
- keys[project_path] = file_proxy if project_path != file_proxy.path
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
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../interfaces/proxy'
4
+
5
+ module Lowkey
6
+ class BodyProxy < Proxy
7
+ def initialize(name:, source:)
8
+ super(name:, source:)
9
+ end
10
+ end
11
+ end
@@ -1,50 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../queries/query'
3
+ require_relative 'module_proxy'
4
4
 
5
5
  module Lowkey
6
- class ClassProxy
7
- include Query
6
+ class ClassProxy < ModuleProxy
7
+ attr_accessor :instance_methods
8
8
 
9
- attr_reader :namespace, :start_line, :end_line
10
- attr_accessor :private_start_line, :keyed_methods, :class_methods, :instance_methods, :method_calls
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
@@ -1,50 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../maps/parent_map'
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 :path, :start_line, :end_line
11
+ attr_reader :root_node
12
12
  attr_accessor :definitions, :dependencies
13
13
 
14
- def initialize(path:, root_node:)
15
- @path = path
16
- @root_node = root_node
14
+ def initialize(root_node:, source:)
15
+ super(name: nil, source:)
17
16
 
18
- @start_line = 0
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, *path = keypath.split('.')
27
- path.empty? ? @definitions[namespace] : query(node: @root_node, namespace:, name: path.join)
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 upsert_class_proxy(node:, parent_map:)
35
- namespace = namespace(node:, parent_map:).reverse.join('::')
36
- namespace = 'Object' if namespace.empty?
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
- private
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
@@ -7,17 +7,17 @@ module Lowkey
7
7
  class MethodProxy < Proxy
8
8
  include Query
9
9
 
10
- attr_reader :file_path, :start_line, :scope, :name, :params, :return_proxy
10
+ attr_reader :params, :body, :return_proxy
11
11
 
12
- # TODO: Refactor file path, start line and scope into "scope" model.
13
- def initialize(file_path:, start_line:, scope:, name:, param_proxies: [], return_proxy: nil) # rubocop:disable Metrics/ParameterLists
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
@@ -4,17 +4,16 @@ require_relative '../interfaces/proxy'
4
4
 
5
5
  module Lowkey
6
6
  class ParamProxy < Proxy
7
- attr_reader :name, :type, :position, :value
7
+ attr_reader :type, :position, :value
8
8
  attr_accessor :expression
9
9
 
10
- # TODO: Refactor file path, start line and scope into "scope" model.
11
- def initialize(file_path:, start_line:, scope:, name:, type:, value: :LOWKEY_UNDEFINED, position: nil, expression: nil) # rubocop:disable Metrics/ParameterLists
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
 
@@ -4,13 +4,12 @@ require_relative '../interfaces/proxy'
4
4
 
5
5
  module Lowkey
6
6
  class ReturnProxy < Proxy
7
- attr_reader :name, :value
7
+ attr_reader :value
8
8
  attr_accessor :expression
9
9
 
10
- def initialize(file_path:, start_line:, scope:, name:, value: :LOWKEY_UNDEFINED, expression: nil) # rubocop:disable Metrics/ParameterLists
11
- super(file_path:, start_line:, scope:)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lowkey
4
- VERSION = '0.3.2'
4
+ VERSION = '0.4.1'
5
5
  end
@@ -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
- attr_reader :parent_map
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
- class_proxy = @file_proxy.upsert_class_proxy(node:, parent_map:)
14
- class_proxy.method_calls << node
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) && class_proxy.start_line && class_proxy.end_line
17
- return unless node.start_line > class_proxy.start_line && node.start_line < class_proxy.end_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
- class_proxy.private_start_line = node.start_line
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
- attr_reader :parent_map
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) # rubocop:disable Metrics/AbcSize
15
- class_proxy = @file_proxy.upsert_class_proxy(node: method_node, parent_map:)
16
- name = method_node.name
17
- scope = name
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
- param_proxies = ProxyFactory.param_proxies(parameters_node: method_node.parameters, file_path:, scope:)
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
- class_proxy.keyed_methods[method_node.name] = method_proxy
24
-
25
- # TODO: Implemented as sorted methods similar to sorted params.
26
- if ClassProxy.class_method?(method_node:, parent_map:)
27
- class_proxy.class_methods[method_node.name] = method_proxy
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
- def file_path
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
@@ -1,24 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'method_call_visitor'
4
- require_relative 'method_class_visitor'
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
- @method_call_visitor = MethodCallVisitor.new(file_proxy:, parent_map:)
11
- @method_class_visitor = MethodClassVisitor.new(file_proxy:, parent_map:)
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 visit_call_node(node)
16
- @method_call_visitor.visit(node)
17
+ def visit_class_node(node)
18
+ @class_visitor.visit(node)
17
19
  super
18
20
  end
19
21
 
20
- def visit_class_node(node)
21
- @method_class_visitor.visit(node)
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.3.2
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