delfos 0.0.1.pre.beta
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 +7 -0
- data/lib/delfos.rb +58 -0
- data/lib/delfos/common_path.rb +60 -0
- data/lib/delfos/distance/calculation.rb +133 -0
- data/lib/delfos/distance/relation.rb +119 -0
- data/lib/delfos/execution_chain.rb +74 -0
- data/lib/delfos/method_logging.rb +71 -0
- data/lib/delfos/method_logging/args.rb +53 -0
- data/lib/delfos/method_logging/code_location.rb +156 -0
- data/lib/delfos/method_logging/klass_determination.rb +16 -0
- data/lib/delfos/neo4j/distance_update.rb +73 -0
- data/lib/delfos/neo4j/execution_persistence.rb +63 -0
- data/lib/delfos/neo4j/informer.rb +110 -0
- data/lib/delfos/neo4j/query_execution.rb +38 -0
- data/lib/delfos/patching.rb +105 -0
- data/lib/delfos/patching_unstubbing_spec_helper.rb +44 -0
- data/lib/delfos/perform_patching.rb +16 -0
- data/lib/delfos/remove_patching.rb +10 -0
- data/lib/delfos/version.rb +4 -0
- metadata +161 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 504300a4ec558fbb0620996b4f1452dfd7f7424c
|
4
|
+
data.tar.gz: cf3adbd6030184d208a52d7586a816cc1d365668
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: be9e77733e7d6af4ac90e62a5bbfac8ae056dbd2b6839e4a16e0c82960e64dd83d93ef8b72787b1b411f992687a67327790a3e9a9367c0dfeabe503c66d931a4
|
7
|
+
data.tar.gz: 6358c9cf62d2a2db3cf12b1fd025edc65e71c3982ad3d1d56a1d8adfb871777111da61f1dcb62ad17a213069666ce59594c24a4a75fc951991610998bf2d8b9b
|
data/lib/delfos.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "delfos/version"
|
3
|
+
require "delfos/method_logging"
|
4
|
+
require "delfos/neo4j/query_execution"
|
5
|
+
require "delfos/neo4j/informer"
|
6
|
+
|
7
|
+
module Delfos
|
8
|
+
class << self
|
9
|
+
def check_setup!
|
10
|
+
raise "Delfos.setup! has not been called" unless neo4j_config && logger
|
11
|
+
end
|
12
|
+
|
13
|
+
def wipe_db!
|
14
|
+
Delfos.setup!(application_directories: [])
|
15
|
+
Delfos::Neo4j::QueryExecution.execute <<-QUERY
|
16
|
+
MATCH (m)-[rel]->(n)
|
17
|
+
DELETE m,rel,n
|
18
|
+
QUERY
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset!
|
22
|
+
@application_directories = []
|
23
|
+
@neo4j_config = nil
|
24
|
+
@logger = nil
|
25
|
+
remove_patching!
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove_patching!
|
29
|
+
load "delfos/remove_patching.rb"
|
30
|
+
begin
|
31
|
+
Delfos::Patching.instance_eval { @added_methods = nil }
|
32
|
+
rescue
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def setup!(
|
38
|
+
connection_type: :server_db,
|
39
|
+
logger: Delfos::Neo4j::Informer.new,
|
40
|
+
host:"http://localhost:7474",
|
41
|
+
auth: { basic_auth: { username: "neo4j", password: "password" } },
|
42
|
+
application_directories: nil)
|
43
|
+
|
44
|
+
@application_directories = application_directories.map{|f| Pathname.new(File.expand_path(f.to_s))}
|
45
|
+
@logger = logger
|
46
|
+
|
47
|
+
@neo4j_config = [connection_type, host, auth]
|
48
|
+
|
49
|
+
perform_patching!
|
50
|
+
end
|
51
|
+
|
52
|
+
def perform_patching!
|
53
|
+
load "delfos/perform_patching.rb"
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_reader :application_directories, :neo4j_config
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
module Delfos
|
5
|
+
module CommonPath
|
6
|
+
class << self
|
7
|
+
SEPARATOR = "/"
|
8
|
+
|
9
|
+
def included_in?(p1, paths)
|
10
|
+
paths = paths.map do |p2|
|
11
|
+
common = common_parent_directory_path(p1, p2)
|
12
|
+
common.to_s.length >= p2.to_s.length
|
13
|
+
end
|
14
|
+
|
15
|
+
paths.compact.detect { |v| v }
|
16
|
+
end
|
17
|
+
|
18
|
+
def common_parent_directory_path(path_a, path_b)
|
19
|
+
dirs = [File.expand_path(path_a.to_s), File.expand_path(path_b.to_s)]
|
20
|
+
|
21
|
+
dir1, dir2 = dirs.minmax.map { |dir| dir.split(SEPARATOR) }
|
22
|
+
|
23
|
+
path_from(dir1, dir2, path_a, path_b)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def path_from(dir1, dir2, path_a, path_b)
|
29
|
+
common_path = common_path(dir1, dir2)
|
30
|
+
common_path, path_a, path_b = append_trailing_slashes!(common_path, path_a, path_b)
|
31
|
+
|
32
|
+
if valid_length?(common_path, path_a, path_b)
|
33
|
+
Pathname.new(common_path)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def valid_length?(common_path, path_a, path_b)
|
38
|
+
(common_path.to_s.length <= path_a.to_s.length) || (common_path.to_s.length <= path_b.to_s.length)
|
39
|
+
end
|
40
|
+
|
41
|
+
def common_path(dir1, dir2)
|
42
|
+
dir1.
|
43
|
+
zip(dir2).
|
44
|
+
take_while { |dn1, dn2| dn1 == dn2 }.
|
45
|
+
map(&:first).
|
46
|
+
join(SEPARATOR)
|
47
|
+
end
|
48
|
+
|
49
|
+
def append_trailing_slashes!(*paths)
|
50
|
+
paths.map do |path|
|
51
|
+
if Pathname.new(path).directory?
|
52
|
+
path += SEPARATOR if path && path.to_s[-1] != SEPARATOR
|
53
|
+
end
|
54
|
+
|
55
|
+
path
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "relation"
|
3
|
+
|
4
|
+
module Delfos
|
5
|
+
module Distance
|
6
|
+
class Calculation
|
7
|
+
attr_reader :path_a, :path_b
|
8
|
+
|
9
|
+
def initialize(path_a, path_b)
|
10
|
+
@path_a = path_a
|
11
|
+
@path_b = path_b
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :traversal_a, :traversal_b
|
15
|
+
|
16
|
+
def traversals
|
17
|
+
result = []
|
18
|
+
path = traversal_path
|
19
|
+
|
20
|
+
path.each_cons(2) do |start, finish|
|
21
|
+
klass = klass_for(start, finish)
|
22
|
+
result.push(klass.new(start, finish))
|
23
|
+
end
|
24
|
+
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def klass_for(a, b)
|
29
|
+
return ChildFile if b + ".." == a
|
30
|
+
Relation
|
31
|
+
end
|
32
|
+
|
33
|
+
def sum_traversals
|
34
|
+
traversals.inject(0) { |a, e| a + e.distance }
|
35
|
+
end
|
36
|
+
|
37
|
+
def sum_possible_traversals
|
38
|
+
traversals.inject(0) { |a, e| a + e.possible_length }
|
39
|
+
end
|
40
|
+
|
41
|
+
def sibling_directories(path)
|
42
|
+
siblings(path).select { |f| File.directory?(f) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def in_start_directory?(path)
|
46
|
+
return false if path.directory?
|
47
|
+
path_a.dirname == path
|
48
|
+
end
|
49
|
+
|
50
|
+
def in_finish_directory?(path)
|
51
|
+
return false if path.directory?
|
52
|
+
|
53
|
+
path_b.dirname == path
|
54
|
+
end
|
55
|
+
|
56
|
+
def traversal_path
|
57
|
+
TraversalPathCalculator.new(path_a, path_b).path
|
58
|
+
end
|
59
|
+
|
60
|
+
class TraversalPathCalculator
|
61
|
+
attr_reader :path_a, :path_b
|
62
|
+
|
63
|
+
def initialize(path_a, path_b)
|
64
|
+
@path_a = path_a
|
65
|
+
@path_b = path_b
|
66
|
+
end
|
67
|
+
|
68
|
+
def path
|
69
|
+
return [path_a, path_b] if same_directory?
|
70
|
+
|
71
|
+
current_path = path_a
|
72
|
+
|
73
|
+
traversal.descend do |p|
|
74
|
+
current_path = full(path_a, p)
|
75
|
+
result.process(current_path)
|
76
|
+
end
|
77
|
+
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
def same_directory?
|
82
|
+
path_a.dirname == path_b.dirname
|
83
|
+
end
|
84
|
+
|
85
|
+
def result
|
86
|
+
@result ||= Result.new([path_a])
|
87
|
+
end
|
88
|
+
|
89
|
+
def traversal
|
90
|
+
path_b.relative_path_from(path_a)
|
91
|
+
end
|
92
|
+
|
93
|
+
def full(start, traversal)
|
94
|
+
start.realpath + Pathname.new(traversal)
|
95
|
+
end
|
96
|
+
|
97
|
+
class Result < Array
|
98
|
+
def initialize(*args)
|
99
|
+
super
|
100
|
+
@in_parent = false
|
101
|
+
end
|
102
|
+
|
103
|
+
def process(i)
|
104
|
+
if @in_parent
|
105
|
+
@in_parent = false
|
106
|
+
remove_parent(i)
|
107
|
+
else
|
108
|
+
add_item(i)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def add_item(i)
|
115
|
+
@in_parent = ((last && last + "..") == i)
|
116
|
+
push i
|
117
|
+
end
|
118
|
+
|
119
|
+
def remove_parent(i)
|
120
|
+
return unless same_dir?(i)
|
121
|
+
|
122
|
+
pop
|
123
|
+
push(i)
|
124
|
+
end
|
125
|
+
|
126
|
+
def same_dir?(i)
|
127
|
+
self[-2] && self[-2].dirname == i.dirname
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Delfos
|
3
|
+
module Distance
|
4
|
+
class Relation
|
5
|
+
attr_reader :start_path, :finish_path
|
6
|
+
|
7
|
+
def initialize(start_path, finish_path)
|
8
|
+
@start_path = start_path
|
9
|
+
@finish_path = finish_path
|
10
|
+
end
|
11
|
+
|
12
|
+
def other_files
|
13
|
+
RelatedPaths.new(start_path).files
|
14
|
+
end
|
15
|
+
|
16
|
+
def other_directories
|
17
|
+
RelatedPaths.new(start_path).directories
|
18
|
+
end
|
19
|
+
|
20
|
+
def distance
|
21
|
+
return traversed_files.length if both_files?
|
22
|
+
return traversed_directories.length if both_directories?
|
23
|
+
|
24
|
+
traversed_files.length + traversed_directories.length
|
25
|
+
end
|
26
|
+
|
27
|
+
def possible_length
|
28
|
+
other_files.length + other_directories.length
|
29
|
+
end
|
30
|
+
|
31
|
+
def traversed_files
|
32
|
+
start_at_end = (start_path.file? && finish_path.directory?)
|
33
|
+
|
34
|
+
subset_to_traverse(collection: other_files,
|
35
|
+
start: start_path,
|
36
|
+
finish: finish_path,
|
37
|
+
start_at_end: start_at_end)
|
38
|
+
end
|
39
|
+
|
40
|
+
def traversed_directories
|
41
|
+
start_at_end = (start_path.file? && finish_path.directory?) || (start_path.directory? && finish_path.file?)
|
42
|
+
|
43
|
+
subset_to_traverse(collection: other_directories,
|
44
|
+
start: start_path,
|
45
|
+
finish: finish_path,
|
46
|
+
start_at_end: start_at_end)
|
47
|
+
end
|
48
|
+
|
49
|
+
def subset_to_traverse(collection:, start:, finish:, start_at_end: true)
|
50
|
+
start_index, finish_index = indexes_from(collection, start, finish, start_at_end)
|
51
|
+
|
52
|
+
Array collection[start_index..finish_index]
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def both_files?
|
58
|
+
start_path.file? && finish_path.file?
|
59
|
+
end
|
60
|
+
|
61
|
+
def both_directories?
|
62
|
+
start_path.directory? && finish_path.directory?
|
63
|
+
end
|
64
|
+
|
65
|
+
def indexes_from(collection, start, finish, start_at_end)
|
66
|
+
start_index = index_from(collection, start, start_at_end: start_at_end)
|
67
|
+
finish_index = index_from(collection, finish, start_at_end: start_at_end, reverse: true)
|
68
|
+
|
69
|
+
if start_index.zero? && finish_index.zero?
|
70
|
+
finish_index = collection.length - 1
|
71
|
+
end
|
72
|
+
|
73
|
+
[start_index, finish_index].sort
|
74
|
+
end
|
75
|
+
|
76
|
+
def index_from(collection, value, reverse: false, start_at_end: false)
|
77
|
+
index = collection.index value
|
78
|
+
|
79
|
+
if index.nil?
|
80
|
+
index = start_at_end && !reverse ? collection.length - 1 : 0
|
81
|
+
end
|
82
|
+
|
83
|
+
index
|
84
|
+
end
|
85
|
+
|
86
|
+
class RelatedPaths
|
87
|
+
attr_reader :path
|
88
|
+
|
89
|
+
def initialize(path)
|
90
|
+
@path = path
|
91
|
+
end
|
92
|
+
|
93
|
+
def files
|
94
|
+
all.select(&:file?)
|
95
|
+
end
|
96
|
+
|
97
|
+
def directories
|
98
|
+
all.select(&:directory?)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def all
|
104
|
+
Dir.glob(path.dirname + "*").map { |f| Pathname.new(f) }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class ChildFile < Relation
|
110
|
+
def other_files
|
111
|
+
RelatedPaths.new(start_path + "*").files
|
112
|
+
end
|
113
|
+
|
114
|
+
def other_directories
|
115
|
+
RelatedPaths.new(start_path + "*").directories
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative "neo4j/execution_persistence"
|
2
|
+
|
3
|
+
module Delfos
|
4
|
+
class ExecutionChain
|
5
|
+
METHOD_CHAIN_MUTEX = Mutex.new
|
6
|
+
|
7
|
+
def self.reset!
|
8
|
+
METHOD_CHAIN_MUTEX.synchronize do
|
9
|
+
Thread.current[:_delfos__execution_chain__method_chain] = nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.method_chain
|
14
|
+
METHOD_CHAIN_MUTEX.synchronize do
|
15
|
+
Thread.current[:_delfos__execution_chain__method_chain] ||= new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.push(method_object)
|
20
|
+
method_chain.push(method_object)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.pop
|
24
|
+
method_chain.pop
|
25
|
+
end
|
26
|
+
|
27
|
+
def push(method_object)
|
28
|
+
self.call_sites.push(method_object)
|
29
|
+
self.stack_depth += 1
|
30
|
+
|
31
|
+
self.execution_count += self.stack_depth == 1 ? 1 : 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def pop
|
35
|
+
popping_empty_stack! if self.stack_depth == 0
|
36
|
+
|
37
|
+
self.stack_depth -= 1
|
38
|
+
|
39
|
+
save_and_reset! if self.stack_depth == 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def stack_depth
|
43
|
+
@stack_depth ||= 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def execution_count
|
47
|
+
@execution_count ||= 0
|
48
|
+
end
|
49
|
+
|
50
|
+
def call_sites
|
51
|
+
@call_sites ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
def step_count
|
55
|
+
call_sites.length
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
class PoppingEmptyStackError < StandardError
|
61
|
+
end
|
62
|
+
|
63
|
+
def popping_empty_stack!
|
64
|
+
raise PoppingEmptyStackError
|
65
|
+
end
|
66
|
+
|
67
|
+
def save_and_reset!
|
68
|
+
Neo4j::ExecutionPersistence.save!(self) if call_sites.length > 0
|
69
|
+
self.call_sites = []
|
70
|
+
end
|
71
|
+
|
72
|
+
attr_writer :stack_depth, :step_count, :execution_count, :call_sites
|
73
|
+
end
|
74
|
+
end
|