delfos 0.0.1.pre.rc1 → 0.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/delfos.rb +22 -78
- data/lib/delfos/call_stack.rb +39 -0
- data/lib/delfos/call_stack/stack.rb +59 -0
- data/lib/delfos/file_system/common_path.rb +62 -0
- data/lib/delfos/{distance/calculation.rb → file_system/distance_calculation.rb} +4 -15
- data/lib/delfos/file_system/path_determination.rb +37 -0
- data/lib/delfos/{distance → file_system}/relation.rb +1 -1
- data/lib/delfos/method_logging.rb +32 -49
- data/lib/delfos/method_logging/call_site_parsing.rb +74 -0
- data/lib/delfos/method_logging/code_location.rb +34 -98
- data/lib/delfos/method_logging/{args.rb → method_parameters.rb} +17 -18
- data/lib/delfos/neo4j.rb +58 -0
- data/lib/delfos/neo4j/batch/execution.rb +149 -0
- data/lib/delfos/neo4j/{execution_persistence.rb → call_stack_query.rb} +12 -22
- data/lib/delfos/neo4j/distance/call_site_fetcher.rb +19 -0
- data/lib/delfos/neo4j/distance/update.rb +50 -0
- data/lib/delfos/neo4j/informer.rb +62 -34
- data/lib/delfos/neo4j/query_execution/errors.rb +30 -0
- data/lib/delfos/neo4j/query_execution/http.rb +61 -0
- data/lib/delfos/neo4j/query_execution/http_query.rb +67 -0
- data/lib/delfos/neo4j/query_execution/sync.rb +35 -0
- data/lib/delfos/neo4j/query_execution/transactional.rb +68 -0
- data/lib/delfos/neo4j/schema.rb +73 -0
- data/lib/delfos/patching/basic_object.rb +1 -5
- data/lib/delfos/patching/method_cache.rb +83 -0
- data/lib/delfos/patching/method_override.rb +76 -66
- data/lib/delfos/patching/module_defining_methods.rb +105 -0
- data/lib/delfos/patching/unstubber.rb +34 -0
- data/lib/delfos/setup.rb +88 -0
- metadata +42 -15
- data/lib/delfos/common_path.rb +0 -58
- data/lib/delfos/execution_chain.rb +0 -75
- data/lib/delfos/method_logging/added_methods.rb +0 -67
- data/lib/delfos/neo4j/distance_update.rb +0 -73
- data/lib/delfos/neo4j/query_execution.rb +0 -79
- data/lib/delfos/patching/unstubbing_spec_helper.rb +0 -58
@@ -1,29 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require_relative "query_execution"
|
3
|
-
|
4
2
|
module Delfos
|
5
3
|
module Neo4j
|
6
|
-
class
|
7
|
-
def self.save!(chain)
|
8
|
-
new(chain.call_sites, chain.execution_count).save!
|
9
|
-
end
|
10
|
-
|
11
|
-
def save!
|
12
|
-
Neo4j::QueryExecution.execute(query, params)
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
4
|
+
class CallStackQuery
|
17
5
|
def initialize(call_sites, execution_count)
|
18
6
|
@call_sites = call_sites
|
19
7
|
|
20
8
|
@execution_count = execution_count
|
21
9
|
end
|
22
10
|
|
23
|
-
attr_reader :call_sites, :execution_count
|
24
|
-
|
25
11
|
def query
|
26
|
-
map_call_sites do |c,i|
|
12
|
+
map_call_sites do |c, i|
|
27
13
|
call_site_query(c, i)
|
28
14
|
end.join("\n")
|
29
15
|
end
|
@@ -31,21 +17,25 @@ module Delfos
|
|
31
17
|
def params
|
32
18
|
params = {}
|
33
19
|
|
34
|
-
map_call_sites do |c,i|
|
20
|
+
map_call_sites do |c, i|
|
35
21
|
params.merge!(call_site_params(c, i))
|
36
22
|
end
|
37
23
|
|
38
24
|
params
|
39
25
|
end
|
40
26
|
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :call_sites, :execution_count
|
30
|
+
|
41
31
|
def map_call_sites
|
42
32
|
call_sites.compact.map.with_index do |c, i|
|
43
33
|
yield c, i
|
44
34
|
end
|
45
35
|
end
|
46
36
|
|
47
|
-
def call_site_query(
|
48
|
-
|
37
|
+
def call_site_query(_cs, i)
|
38
|
+
<<-QUERY
|
49
39
|
MERGE
|
50
40
|
|
51
41
|
(
|
@@ -70,7 +60,7 @@ module Delfos
|
|
70
60
|
|
71
61
|
(cs#{i}:CallSite {file: {file#{i}}, line_number: {line_number#{i}}})
|
72
62
|
|
73
|
-
MERGE (e#{i}:
|
63
|
+
MERGE (e#{i}:CallStack{number: {execution_count#{i}}})
|
74
64
|
|
75
65
|
MERGE (e#{i})
|
76
66
|
-
|
@@ -82,7 +72,7 @@ module Delfos
|
|
82
72
|
|
83
73
|
def call_site_params(cs, i)
|
84
74
|
{
|
85
|
-
"klass#{i}" => cs.klass,
|
75
|
+
"klass#{i}" => cs.klass.to_s,
|
86
76
|
"method_name#{i}" => cs.method_name,
|
87
77
|
"method_type#{i}" => cs.method_type,
|
88
78
|
"method_definition_file#{i}" => cs.method_definition_file,
|
@@ -90,7 +80,7 @@ module Delfos
|
|
90
80
|
"method_definition_line#{i}" => cs.method_definition_line,
|
91
81
|
"file#{i}" => cs.file,
|
92
82
|
"line_number#{i}" => cs.line_number,
|
93
|
-
"step_number#{i}" => i + 1
|
83
|
+
"step_number#{i}" => i + 1,
|
94
84
|
}
|
95
85
|
end
|
96
86
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Delfos
|
3
|
+
module Neo4j
|
4
|
+
module Distance
|
5
|
+
class CallSiteFetcher
|
6
|
+
def self.perform
|
7
|
+
Neo4j.execute_sync <<-QUERY
|
8
|
+
MATCH
|
9
|
+
(call_site:CallSite) - [:CALLS] -> (called:Method)
|
10
|
+
|
11
|
+
RETURN
|
12
|
+
call_site.file, id(call_site),
|
13
|
+
called.file, id(called)
|
14
|
+
QUERY
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "delfos/file_system/distance_calculation"
|
3
|
+
require_relative "call_site_fetcher"
|
4
|
+
|
5
|
+
module Delfos
|
6
|
+
module Neo4j
|
7
|
+
module Distance
|
8
|
+
class Update
|
9
|
+
def perform
|
10
|
+
results = CallSiteFetcher.perform
|
11
|
+
return if results.length.negative?
|
12
|
+
|
13
|
+
results.each do |start_file, call_site_id, finish_file, called_id|
|
14
|
+
calc = FileSystem::DistanceCalculation.new(start_file, finish_file)
|
15
|
+
|
16
|
+
update(call_site_id, called_id, calc)
|
17
|
+
end
|
18
|
+
|
19
|
+
Neo4j.flush!
|
20
|
+
end
|
21
|
+
|
22
|
+
def update(call_site_id, called_id, calc)
|
23
|
+
Neo4j.execute(
|
24
|
+
query,
|
25
|
+
call_site_id: call_site_id,
|
26
|
+
called_id: called_id,
|
27
|
+
sum_traversals: calc.sum_traversals,
|
28
|
+
sum_possible_traversals: calc.sum_possible_traversals
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def query
|
33
|
+
<<-QUERY
|
34
|
+
START call_site = node({call_site_id}),
|
35
|
+
called = node({called_id})
|
36
|
+
|
37
|
+
MERGE (call_site)
|
38
|
+
-
|
39
|
+
[:EFFERENT_COUPLING {
|
40
|
+
distance: {sum_traversals},
|
41
|
+
possible_distance: {sum_possible_traversals}
|
42
|
+
}
|
43
|
+
]
|
44
|
+
-> (called)
|
45
|
+
QUERY
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,69 +1,101 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "delfos"
|
3
|
-
require_relative "query_execution"
|
4
2
|
|
5
3
|
module Delfos
|
6
4
|
module Neo4j
|
7
5
|
class Informer
|
8
|
-
def
|
9
|
-
|
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)
|
10
9
|
end
|
11
10
|
|
12
|
-
def
|
13
|
-
query = query_for(
|
14
|
-
params =
|
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)
|
15
14
|
|
16
|
-
|
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
|
17
39
|
end
|
18
40
|
|
19
41
|
def query_for(args, call_site, called_code)
|
20
42
|
assign_query_variables(args, call_site, called_code)
|
21
43
|
|
22
|
-
klasses_query = query_variables.map do |
|
23
|
-
"MERGE (#{name}:Class {name: #{
|
44
|
+
klasses_query = query_variables.map do |_klass, name|
|
45
|
+
"MERGE (#{name}:Class {name: {#{name}}})"
|
24
46
|
end.join("\n")
|
25
47
|
|
26
48
|
<<-QUERY
|
27
49
|
#{klasses_query}
|
28
50
|
|
29
|
-
MERGE (#{query_variable(call_site.klass)}) - [:OWNS] ->
|
51
|
+
MERGE (#{query_variable(call_site.klass)}) - [:OWNS] ->
|
52
|
+
#{method_node("m1")}
|
30
53
|
|
31
|
-
MERGE (m1) - [:CONTAINS] ->
|
54
|
+
MERGE (m1) - [:CONTAINS] ->
|
55
|
+
(cs:CallSite
|
56
|
+
{
|
57
|
+
file: {cs_file},
|
58
|
+
line_number: {cs_line_number}
|
59
|
+
}
|
60
|
+
)
|
32
61
|
|
33
|
-
MERGE (#{query_variable(called_code.klass)}) - [:OWNS] ->
|
62
|
+
MERGE (#{query_variable(called_code.klass)}) - [:OWNS] ->
|
63
|
+
#{method_node("m2")}
|
34
64
|
|
35
|
-
MERGE (cs)
|
65
|
+
MERGE (cs) - [:CALLS] -> (m2)
|
36
66
|
|
37
67
|
#{args_query args}
|
38
68
|
QUERY
|
39
69
|
end
|
40
70
|
|
41
71
|
def assign_query_variables(args, call_site, called_code)
|
42
|
-
|
43
|
-
query_variables.assign(called_code.klass, "k")
|
72
|
+
klasses = [call_site.klass, called_code.klass] + args.argument_classes
|
44
73
|
|
45
|
-
|
74
|
+
klasses.uniq.each do |k|
|
46
75
|
query_variables.assign(k, "k")
|
47
76
|
end
|
48
77
|
end
|
49
78
|
|
50
|
-
def method_node(
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
60
90
|
end
|
61
91
|
|
62
92
|
def args_query(args)
|
63
|
-
|
93
|
+
query_text = args.argument_classes.map do |k|
|
64
94
|
name = query_variable(k)
|
65
95
|
"MERGE (cs) - [:ARG] -> (#{name})"
|
66
|
-
end
|
96
|
+
end
|
97
|
+
|
98
|
+
query_text.join("\n")
|
67
99
|
end
|
68
100
|
|
69
101
|
def query_variable(k)
|
@@ -74,10 +106,6 @@ module Delfos
|
|
74
106
|
@query_variables ||= QueryVariables.new
|
75
107
|
end
|
76
108
|
|
77
|
-
def code_execution_query
|
78
|
-
Delfos::Patching.execution_chain
|
79
|
-
end
|
80
|
-
|
81
109
|
class QueryVariables < Hash
|
82
110
|
def initialize(*args)
|
83
111
|
super(*args)
|
@@ -85,7 +113,7 @@ module Delfos
|
|
85
113
|
end
|
86
114
|
|
87
115
|
def assign(klass, prefix)
|
88
|
-
klass = klass.to_s
|
116
|
+
klass = klass.to_s
|
89
117
|
val = self[klass]
|
90
118
|
return val if val
|
91
119
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Delfos
|
3
|
+
module Neo4j
|
4
|
+
module QueryExecution
|
5
|
+
HTTP_ERRORS = [
|
6
|
+
EOFError,
|
7
|
+
Errno::ECONNRESET,
|
8
|
+
Errno::ECONNREFUSED,
|
9
|
+
Errno::EINVAL,
|
10
|
+
Net::HTTPBadResponse,
|
11
|
+
Net::HTTPHeaderSyntaxError,
|
12
|
+
Net::ProtocolError,
|
13
|
+
Timeout::Error,
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
class InvalidQuery < IOError
|
17
|
+
def initialize(errors, query, params)
|
18
|
+
message = errors.map do |e|
|
19
|
+
e.select { |k, _| %w(code message).include?(k) }.inspect
|
20
|
+
end.join("\n")
|
21
|
+
|
22
|
+
super [message, { query: query, params: params }.to_json].join("\n\n")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ConnectionError < IOError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "delfos"
|
3
|
+
require "net/http"
|
4
|
+
|
5
|
+
module Delfos
|
6
|
+
module Neo4j
|
7
|
+
module QueryExecution
|
8
|
+
class Http
|
9
|
+
attr_reader :uri
|
10
|
+
|
11
|
+
def initialize(uri)
|
12
|
+
@uri = uri
|
13
|
+
end
|
14
|
+
|
15
|
+
def post(body)
|
16
|
+
response(build_post(body))
|
17
|
+
end
|
18
|
+
|
19
|
+
def get
|
20
|
+
response(build_get)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def response(request)
|
26
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
27
|
+
|
28
|
+
http.request(request)
|
29
|
+
rescue *HTTP_ERRORS => e
|
30
|
+
raise ConnectionError, e
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_post(body)
|
34
|
+
build_request("Post") do |request|
|
35
|
+
add_headers(request)
|
36
|
+
|
37
|
+
request.body = body
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_get
|
42
|
+
build_request("Get") do |request|
|
43
|
+
add_headers(request)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_request(type)
|
48
|
+
Net::HTTP.const_get(type).new(uri.request_uri).tap do |request|
|
49
|
+
yield request
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_headers(request)
|
54
|
+
request.initialize_http_header("Accept" => "application/json; charset=UTF-8")
|
55
|
+
request.basic_auth(Delfos.neo4j.username, Delfos.neo4j.password)
|
56
|
+
request.content_type = "application/json"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Delfos
|
3
|
+
module Neo4j
|
4
|
+
module QueryExecution
|
5
|
+
module HttpQuery
|
6
|
+
def self.included(base)
|
7
|
+
base.instance_eval do
|
8
|
+
attr_reader :query, :params
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(query, params, uri = nil)
|
13
|
+
@query = query
|
14
|
+
@params = params
|
15
|
+
@uri = uri
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def request_body
|
21
|
+
{
|
22
|
+
"statements": [{ "statement": strip_whitespace(query), "parameters": params }],
|
23
|
+
}.to_json
|
24
|
+
end
|
25
|
+
|
26
|
+
def strip_whitespace(s)
|
27
|
+
s.
|
28
|
+
gsub(/^\s+/, "").
|
29
|
+
gsub(/ +/, " ").
|
30
|
+
gsub("\n\n", "\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
def json
|
34
|
+
JSON.parse response.body
|
35
|
+
end
|
36
|
+
|
37
|
+
def errors?
|
38
|
+
json["errors"].length.positive?
|
39
|
+
end
|
40
|
+
|
41
|
+
def response
|
42
|
+
@response ||= fetch
|
43
|
+
end
|
44
|
+
|
45
|
+
def fetch
|
46
|
+
log_query
|
47
|
+
|
48
|
+
Http.new(uri).post(request_body)
|
49
|
+
end
|
50
|
+
|
51
|
+
def log_query
|
52
|
+
statement = strip_whitespace(query)
|
53
|
+
|
54
|
+
if statement
|
55
|
+
begin
|
56
|
+
params.each { |k, v| statement = statement.gsub("{#{k}}", v.inspect) }
|
57
|
+
rescue
|
58
|
+
byebug
|
59
|
+
end
|
60
|
+
Delfos.logger.debug "sending query: "
|
61
|
+
Delfos.logger.debug statement.gsub(/^/, " ")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|