delfos 0.0.1 → 0.0.2.pre.rc2

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/delfos.rb +72 -13
  3. data/lib/delfos/call_stack.rb +9 -9
  4. data/lib/delfos/call_stack/stack.rb +18 -18
  5. data/lib/delfos/file_system.rb +16 -0
  6. data/lib/delfos/file_system/app_directories.rb +42 -0
  7. data/lib/delfos/file_system/common_path.rb +17 -11
  8. data/lib/delfos/file_system/distance_calculation.rb +12 -3
  9. data/lib/delfos/file_system/path_determination.rb +4 -1
  10. data/lib/delfos/file_system/relation.rb +1 -0
  11. data/lib/delfos/method_trace.rb +59 -0
  12. data/lib/delfos/method_trace/call_handler.rb +48 -0
  13. data/lib/delfos/method_trace/code_location.rb +35 -0
  14. data/lib/delfos/method_trace/code_location/call_site.rb +38 -0
  15. data/lib/delfos/method_trace/code_location/container_method_factory.rb +71 -0
  16. data/lib/delfos/method_trace/code_location/eval_in_caller.rb +17 -0
  17. data/lib/delfos/method_trace/code_location/filename_helpers.rb +46 -0
  18. data/lib/delfos/method_trace/code_location/method.rb +58 -0
  19. data/lib/delfos/neo4j.rb +4 -4
  20. data/lib/delfos/neo4j/batch/execution.rb +30 -88
  21. data/lib/delfos/neo4j/batch/retryable.rb +101 -0
  22. data/lib/delfos/neo4j/call_site_query.rb +89 -0
  23. data/lib/delfos/neo4j/distance/call_site_fetcher.rb +1 -0
  24. data/lib/delfos/neo4j/distance/update.rb +22 -5
  25. data/lib/delfos/neo4j/live/call_site_logger.rb +17 -0
  26. data/lib/delfos/neo4j/offline/call_site_logger.rb +39 -0
  27. data/lib/delfos/neo4j/offline/importer.rb +68 -0
  28. data/lib/delfos/neo4j/query_execution/errors.rb +13 -3
  29. data/lib/delfos/neo4j/query_execution/http.rb +1 -0
  30. data/lib/delfos/neo4j/query_execution/http_query.rb +20 -15
  31. data/lib/delfos/neo4j/query_execution/sync.rb +1 -0
  32. data/lib/delfos/neo4j/query_execution/transactional.rb +24 -13
  33. data/lib/delfos/neo4j/schema.rb +5 -19
  34. data/lib/delfos/setup.rb +76 -44
  35. metadata +77 -18
  36. data/lib/delfos/method_logging.rb +0 -52
  37. data/lib/delfos/method_logging/call_site_parsing.rb +0 -74
  38. data/lib/delfos/method_logging/code_location.rb +0 -89
  39. data/lib/delfos/method_logging/method_parameters.rb +0 -53
  40. data/lib/delfos/neo4j/call_stack_query.rb +0 -88
  41. data/lib/delfos/neo4j/informer.rb +0 -128
  42. data/lib/delfos/patching/basic_object.rb +0 -14
  43. data/lib/delfos/patching/basic_object_remove.rb +0 -7
  44. data/lib/delfos/patching/method_cache.rb +0 -83
  45. data/lib/delfos/patching/method_override.rb +0 -148
  46. data/lib/delfos/patching/module_defining_methods.rb +0 -105
  47. data/lib/delfos/patching/unstubber.rb +0 -34
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
- require "binding_of_caller"
3
-
4
- module Delfos
5
- module MethodLogging
6
- class CallSiteParsing
7
- # This magic number is based on the implementation within this file.
8
- # If the line with `call_site_binding.of_caller(stack_index + STACK_OFFSET).receiver`
9
- # is moved up or down the call stack a test fails and we have to change `STACK_OFFSET`
10
- STACK_OFFSET = 5
11
-
12
- attr_reader :stack, :call_site_binding
13
-
14
- def initialize(stack, call_site_binding, stack_offset: nil)
15
- @stack = stack
16
- @call_site_binding = call_site_binding
17
- @stack_offset = stack_offset
18
- end
19
-
20
- def perform
21
- file, line_number, method_name = method_details
22
- return unless current && file && line_number && method_name
23
-
24
- CodeLocation.new(object: object,
25
- method_name: method_name.to_s,
26
- class_method: class_method,
27
- file: file,
28
- line_number: line_number)
29
- end
30
-
31
- private
32
-
33
- def class_method
34
- object.is_a? Module
35
- end
36
-
37
- def current
38
- stack.detect do |s|
39
- file = s.split(":")[0]
40
- Delfos::MethodLogging.include_file?(file)
41
- end
42
- end
43
-
44
- def object
45
- @object ||= call_site_binding.of_caller(stack_index + stack_offset).receiver
46
- end
47
-
48
- def stack_offset
49
- @stack_offset ||= STACK_OFFSET
50
- end
51
-
52
- def stack_index
53
- stack.index { |c| c == current }
54
- end
55
-
56
- METHOD_NAME_REGEX = /`(.*)'$/
57
-
58
- def method_details
59
- return unless current
60
- file, line_number, rest, more = current.split(":")
61
-
62
- rest = more.nil? ? rest : "#{rest}:#{more}"
63
- method_name = rest.match(METHOD_NAME_REGEX)&.[](1)
64
-
65
- return unless method_name && file && line_number
66
-
67
- method_name = method_name.delete("`")
68
- method_name = method_name.delete("'")
69
-
70
- [file, line_number.to_i, method_name]
71
- end
72
- end
73
- end
74
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "call_site_parsing"
3
-
4
- module Delfos
5
- module MethodLogging
6
- class CodeLocation
7
- class << self
8
- def from_call_site(stack, call_site_binding)
9
- CallSiteParsing.new(stack, call_site_binding).perform
10
- end
11
-
12
- def from_called(object, called_method, class_method)
13
- file, line_number = called_method.source_location
14
- return unless file && line_number
15
-
16
- new(object: object, method_name: called_method.name.to_s,
17
- class_method: class_method, file: file, line_number: line_number)
18
- end
19
-
20
- end
21
-
22
- attr_reader :object, :method_name, :class_method, :line_number
23
-
24
- def initialize(object:, method_name:, class_method:, file:, line_number:)
25
- @object = object
26
- @method_name = method_name
27
- @class_method = class_method
28
- @line_number = line_number.to_i
29
- @file = file
30
- end
31
-
32
- def file
33
- relative_filename @file
34
- end
35
-
36
- def klass
37
- object.is_a?(Class) ? object : object.class
38
- end
39
-
40
- def method_definition_file
41
- relative_filename(method_definition&.first || fallback_method_definition_file)
42
- end
43
-
44
- def method_definition_line
45
- method_definition&.last&.to_i || fallback_method_definition_line_number
46
- end
47
-
48
- def method_type
49
- class_method ? "ClassMethod" : "InstanceMethod"
50
- end
51
-
52
- private
53
-
54
- def relative_filename(f)
55
- return unless f
56
- file = f.to_s
57
-
58
- Delfos.application_directories.map do |d|
59
- file = relative_path(file, d)
60
- end
61
-
62
- file
63
- end
64
-
65
- def relative_path(file, dir)
66
- match = dir.to_s.split("/")[0..-2].join("/")
67
-
68
- if file[match]
69
- file = file.gsub(match, "").
70
- gsub(%r{^/}, "")
71
- end
72
-
73
- file
74
- end
75
-
76
- def fallback_method_definition_file
77
- @file
78
- end
79
-
80
- def fallback_method_definition_line_number
81
- 0
82
- end
83
-
84
- def method_definition
85
- @method_definition ||= Patching::MethodCache.find(klass: klass, method_name: method_name, class_method: class_method)&.source_location
86
- end
87
- end
88
- end
89
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
- require "delfos/patching/method_cache"
3
-
4
- module Delfos
5
- module MethodLogging
6
- class MethodParameters
7
- attr_reader :block
8
-
9
- def initialize(args=[], keyword_args=nil, block=nil)
10
- @raw_args = args
11
- @raw_keyword_args = keyword_args
12
- @block = block
13
- end
14
-
15
- def args
16
- @args ||= calculate_args(@raw_args)
17
- end
18
-
19
- def argument_classes
20
- (args + keyword_args).uniq
21
- end
22
-
23
- def keyword_args
24
- @keyword_args ||= calculate_args(@raw_keyword_args.values)
25
- end
26
-
27
- def as_json(*params)
28
- {arguments: args, keyword_arguments: keyword_args}
29
- end
30
-
31
- private
32
-
33
- def calculate_args(arguments)
34
- arguments.
35
- map { |o| o.is_a?(Class) ? o : o.class }.
36
- select { |k| keep?(k) }
37
- end
38
-
39
- def keep?(klass)
40
- files_for(klass).
41
- any? { |f| record?(f) }
42
- end
43
-
44
- def files_for(klass)
45
- Patching::MethodCache.files_for(klass)
46
- end
47
-
48
- def record?(f)
49
- Delfos::MethodLogging.include_file?(f)
50
- end
51
- end
52
- end
53
- end
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
- module Delfos
3
- module Neo4j
4
- class CallStackQuery
5
- def initialize(call_sites, execution_count)
6
- @call_sites = call_sites
7
-
8
- @execution_count = execution_count
9
- end
10
-
11
- def query
12
- map_call_sites do |c, i|
13
- call_site_query(c, i)
14
- end.join("\n")
15
- end
16
-
17
- def params
18
- params = {}
19
-
20
- map_call_sites do |c, i|
21
- params.merge!(call_site_params(c, i))
22
- end
23
-
24
- params
25
- end
26
-
27
- private
28
-
29
- attr_reader :call_sites, :execution_count
30
-
31
- def map_call_sites
32
- call_sites.compact.map.with_index do |c, i|
33
- yield c, i
34
- end
35
- end
36
-
37
- def call_site_query(_cs, i)
38
- <<-QUERY
39
- MERGE
40
-
41
- (
42
- k#{i}:Class { name: {klass#{i}} }
43
- )
44
-
45
- - [:OWNS] ->
46
-
47
- (
48
- m#{i} :Method {
49
- type: {method_type#{i}},
50
- name: {method_name#{i}},
51
- file: {method_definition_file#{i}},
52
- line_number: {method_definition_line#{i}}}
53
- )
54
-
55
- MERGE
56
-
57
- (m#{i})
58
-
59
- -[:CONTAINS]->
60
-
61
- (cs#{i}:CallSite {file: {file#{i}}, line_number: {line_number#{i}}})
62
-
63
- MERGE (e#{i}:CallStack{number: {execution_count#{i}}})
64
-
65
- MERGE (e#{i})
66
- -
67
- [:STEP {number: {step_number#{i}}}]
68
- ->
69
- (cs#{i})
70
- QUERY
71
- end
72
-
73
- def call_site_params(cs, i)
74
- {
75
- "klass#{i}" => cs.klass.to_s,
76
- "method_name#{i}" => cs.method_name,
77
- "method_type#{i}" => cs.method_type,
78
- "method_definition_file#{i}" => cs.method_definition_file,
79
- "execution_count#{i}" => execution_count,
80
- "method_definition_line#{i}" => cs.method_definition_line,
81
- "file#{i}" => cs.file,
82
- "line_number#{i}" => cs.line_number,
83
- "step_number#{i}" => i + 1,
84
- }
85
- end
86
- end
87
- end
88
- end
@@ -1,128 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Delfos
4
- module Neo4j
5
- class Informer
6
- def save_call_stack(call_sites, execution_number)
7
- q = Neo4j::CallStackQuery.new(call_sites, execution_number)
8
- Neo4j.execute(q.query, q.params)
9
- end
10
-
11
- def log(args, call_site, called_code)
12
- query = query_for(args, call_site, called_code)
13
- params = params_for(args, call_site, called_code)
14
-
15
- Neo4j.execute(query, params)
16
- end
17
-
18
- def params_for(args, call_site, called_code)
19
- assign_query_variables(args, call_site, called_code)
20
-
21
- params = query_variables.each_with_object({}) do |(klass, name), object|
22
- object[name] = klass.to_s
23
- end
24
-
25
- params["m1_type"] = call_site.method_type
26
- params["m1_name"] = call_site.method_name
27
- params["m1_file"] = call_site.method_definition_file
28
- params["m1_line_number"] = call_site.method_definition_line
29
-
30
- params["cs_file"] = call_site.file
31
- params["cs_line_number"] = call_site.line_number
32
-
33
- params["m2_type"] = called_code.method_type
34
- params["m2_name"] = called_code.method_name
35
- params["m2_file"] = called_code.method_definition_file
36
- params["m2_line_number"] = called_code.method_definition_line
37
-
38
- params
39
- end
40
-
41
- def query_for(args, call_site, called_code)
42
- assign_query_variables(args, call_site, called_code)
43
-
44
- klasses_query = query_variables.map do |_klass, name|
45
- "MERGE (#{name}:Class {name: {#{name}}})"
46
- end.join("\n")
47
-
48
- <<-QUERY
49
- #{klasses_query}
50
-
51
- MERGE (#{query_variable(call_site.klass)}) - [:OWNS] ->
52
- #{method_node("m1")}
53
-
54
- MERGE (m1) - [:CONTAINS] ->
55
- (cs:CallSite
56
- {
57
- file: {cs_file},
58
- line_number: {cs_line_number}
59
- }
60
- )
61
-
62
- MERGE (#{query_variable(called_code.klass)}) - [:OWNS] ->
63
- #{method_node("m2")}
64
-
65
- MERGE (cs) - [:CALLS] -> (m2)
66
-
67
- #{args_query args}
68
- QUERY
69
- end
70
-
71
- def assign_query_variables(args, call_site, called_code)
72
- klasses = [call_site.klass, called_code.klass] + args.argument_classes
73
-
74
- klasses.uniq.each do |k|
75
- query_variables.assign(k, "k")
76
- end
77
- end
78
-
79
- def method_node(id)
80
- <<-NODE
81
- (#{id}:Method
82
- {
83
- type: {#{id}_type},
84
- name: {#{id}_name},
85
- file: {#{id}_file},
86
- line_number: {#{id}_line_number}
87
- }
88
- )
89
- NODE
90
- end
91
-
92
- def args_query(args)
93
- query_text = args.argument_classes.map do |k|
94
- name = query_variable(k)
95
- "MERGE (cs) - [:ARG] -> (#{name})"
96
- end
97
-
98
- query_text.join("\n")
99
- end
100
-
101
- def query_variable(k)
102
- query_variables[k.to_s]
103
- end
104
-
105
- def query_variables
106
- @query_variables ||= QueryVariables.new
107
- end
108
-
109
- class QueryVariables < Hash
110
- def initialize(*args)
111
- super(*args)
112
- @counters = Hash.new(1)
113
- end
114
-
115
- def assign(klass, prefix)
116
- klass = klass.to_s
117
- val = self[klass]
118
- return val if val
119
-
120
- "#{prefix}#{@counters[prefix]}".tap do |v|
121
- self[klass] = v
122
- @counters[prefix] += 1
123
- end
124
- end
125
- end
126
- end
127
- end
128
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "method_override"
3
-
4
- class BasicObject
5
- def self.method_added(name)
6
- ::Delfos::Patching::MethodOverride.setup(self, name, private_instance_methods, class_method: false)
7
- end
8
-
9
- def self.singleton_method_added(name)
10
- return if %i(define_method extended included inherited method_added singleton_method_added).include?(name)
11
-
12
- ::Delfos::Patching::MethodOverride.setup(self, name, private_methods, class_method: true)
13
- end
14
- end