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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/delfos.rb +22 -78
  3. data/lib/delfos/call_stack.rb +39 -0
  4. data/lib/delfos/call_stack/stack.rb +59 -0
  5. data/lib/delfos/file_system/common_path.rb +62 -0
  6. data/lib/delfos/{distance/calculation.rb → file_system/distance_calculation.rb} +4 -15
  7. data/lib/delfos/file_system/path_determination.rb +37 -0
  8. data/lib/delfos/{distance → file_system}/relation.rb +1 -1
  9. data/lib/delfos/method_logging.rb +32 -49
  10. data/lib/delfos/method_logging/call_site_parsing.rb +74 -0
  11. data/lib/delfos/method_logging/code_location.rb +34 -98
  12. data/lib/delfos/method_logging/{args.rb → method_parameters.rb} +17 -18
  13. data/lib/delfos/neo4j.rb +58 -0
  14. data/lib/delfos/neo4j/batch/execution.rb +149 -0
  15. data/lib/delfos/neo4j/{execution_persistence.rb → call_stack_query.rb} +12 -22
  16. data/lib/delfos/neo4j/distance/call_site_fetcher.rb +19 -0
  17. data/lib/delfos/neo4j/distance/update.rb +50 -0
  18. data/lib/delfos/neo4j/informer.rb +62 -34
  19. data/lib/delfos/neo4j/query_execution/errors.rb +30 -0
  20. data/lib/delfos/neo4j/query_execution/http.rb +61 -0
  21. data/lib/delfos/neo4j/query_execution/http_query.rb +67 -0
  22. data/lib/delfos/neo4j/query_execution/sync.rb +35 -0
  23. data/lib/delfos/neo4j/query_execution/transactional.rb +68 -0
  24. data/lib/delfos/neo4j/schema.rb +73 -0
  25. data/lib/delfos/patching/basic_object.rb +1 -5
  26. data/lib/delfos/patching/method_cache.rb +83 -0
  27. data/lib/delfos/patching/method_override.rb +76 -66
  28. data/lib/delfos/patching/module_defining_methods.rb +105 -0
  29. data/lib/delfos/patching/unstubber.rb +34 -0
  30. data/lib/delfos/setup.rb +88 -0
  31. metadata +42 -15
  32. data/lib/delfos/common_path.rb +0 -58
  33. data/lib/delfos/execution_chain.rb +0 -75
  34. data/lib/delfos/method_logging/added_methods.rb +0 -67
  35. data/lib/delfos/neo4j/distance_update.rb +0 -73
  36. data/lib/delfos/neo4j/query_execution.rb +0 -79
  37. 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 ExecutionPersistence
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(cs, i)
48
- <<-QUERY
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}:ExecutionChain{number: {execution_count#{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 debug(args, call_site, called_code)
9
- execute_query(args, call_site, called_code)
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 execute_query(*args)
13
- query = query_for(*args)
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
- QueryExecution.execute(query, params)
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 |klass, name|
23
- "MERGE (#{name}:Class {name: #{klass.inspect}})"
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] -> #{method_node(call_site, "m1")}
51
+ MERGE (#{query_variable(call_site.klass)}) - [:OWNS] ->
52
+ #{method_node("m1")}
30
53
 
31
- MERGE (m1) - [:CONTAINS] -> (cs:CallSite{file: "#{call_site.file}", line_number: #{call_site.line_number}})
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] -> #{method_node(called_code, "m2")}
62
+ MERGE (#{query_variable(called_code.klass)}) - [:OWNS] ->
63
+ #{method_node("m2")}
34
64
 
35
- MERGE (cs) - [:CALLS] -> m2
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
- query_variables.assign(call_site.klass, "k")
43
- query_variables.assign(called_code.klass, "k")
72
+ klasses = [call_site.klass, called_code.klass] + args.argument_classes
44
73
 
45
- (args.args + args.keyword_args).uniq.each do |k|
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(code, id)
51
- if code.method_definition_file.length.positive? && code.method_definition_line.positive?
52
- <<-NODE
53
- (#{id}:Method{type: "#{code.method_type}", name: "#{code.method_name}", file: #{code.method_definition_file.inspect}, line_number: #{code.method_definition_line}})
54
- NODE
55
- else
56
- <<-NODE
57
- (#{id}:Method{type: "#{code.method_type}", name: "#{code.method_name}"})
58
- NODE
59
- end
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
- (args.args + args.keyword_args).map do |k|
93
+ query_text = args.argument_classes.map do |k|
64
94
  name = query_variable(k)
65
95
  "MERGE (cs) - [:ARG] -> (#{name})"
66
- end.join("\n")
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.tr(":", "_")
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