delfos 0.0.1.pre.rc1 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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