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