delfos 0.0.1.pre.beta → 0.0.1.pre.rc1
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 +63 -21
- data/lib/delfos/common_path.rb +1 -3
- data/lib/delfos/execution_chain.rb +15 -14
- data/lib/delfos/method_logging.rb +18 -20
- data/lib/delfos/method_logging/added_methods.rb +67 -0
- data/lib/delfos/method_logging/args.rb +24 -23
- data/lib/delfos/method_logging/code_location.rb +25 -28
- data/lib/delfos/neo4j/distance_update.rb +26 -26
- data/lib/delfos/neo4j/execution_persistence.rb +59 -24
- data/lib/delfos/neo4j/informer.rb +16 -26
- data/lib/delfos/neo4j/query_execution.rb +58 -17
- data/lib/delfos/patching/basic_object.rb +18 -0
- data/lib/delfos/patching/basic_object_remove.rb +7 -0
- data/lib/delfos/patching/method_override.rb +138 -0
- data/lib/delfos/patching/unstubbing_spec_helper.rb +58 -0
- metadata +40 -58
- data/lib/delfos/method_logging/klass_determination.rb +0 -16
- data/lib/delfos/patching.rb +0 -105
- data/lib/delfos/patching_unstubbing_spec_helper.rb +0 -44
- data/lib/delfos/perform_patching.rb +0 -16
- data/lib/delfos/remove_patching.rb +0 -10
- data/lib/delfos/version.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 942050257d2df0f604039c76a5503f892118ba8c
|
4
|
+
data.tar.gz: bbafde8bed085d9c67c75d6ff9d065f578b8d8b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26f22aa726e32d4aaaffc19aebcfad37d0a19150bb85cd4aa709d358d9733258954168837adcc31af470440cbd69d7ae8106ff6616f20277507c0d58ed3db6cf
|
7
|
+
data.tar.gz: 9aa2570f3afda8ec7aa4e9abd998385adc0d3dedd0acc32344b2758815d38ed2b2f326a880490310b0264c20caf00642679a665c45d7694a45ac092a195ab44f
|
data/lib/delfos.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "delfos/version"
|
3
|
-
require "delfos/method_logging"
|
4
|
-
require "delfos/neo4j/query_execution"
|
5
2
|
require "delfos/neo4j/informer"
|
6
3
|
|
7
4
|
module Delfos
|
8
5
|
class << self
|
6
|
+
attr_writer :application_directories
|
7
|
+
|
9
8
|
def check_setup!
|
10
9
|
raise "Delfos.setup! has not been called" unless neo4j_config && logger
|
11
10
|
end
|
12
11
|
|
13
12
|
def wipe_db!
|
14
|
-
Delfos.
|
13
|
+
Delfos.setup_neo4j!
|
14
|
+
|
15
|
+
require "delfos/neo4j/query_execution"
|
15
16
|
Delfos::Neo4j::QueryExecution.execute <<-QUERY
|
16
17
|
MATCH (m)-[rel]->(n)
|
17
18
|
DELETE m,rel,n
|
@@ -20,39 +21,80 @@ module Delfos
|
|
20
21
|
|
21
22
|
def reset!
|
22
23
|
@application_directories = []
|
23
|
-
@
|
24
|
+
@method_logging = nil
|
25
|
+
@neo4j = nil
|
24
26
|
@logger = nil
|
27
|
+
|
28
|
+
if defined? Delfos::ExecutionChain
|
29
|
+
Delfos::ExecutionChain.reset!
|
30
|
+
end
|
31
|
+
|
32
|
+
# unstubbing depends upon AddedMethods being still defined
|
33
|
+
# so this order is important
|
34
|
+
unstub_all!
|
35
|
+
remove_added_methods!
|
36
|
+
|
25
37
|
remove_patching!
|
26
38
|
end
|
27
39
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
Delfos::Patching.
|
32
|
-
|
33
|
-
|
40
|
+
def unstub_all!
|
41
|
+
if defined? Delfos::Patching::MethodOverride
|
42
|
+
|
43
|
+
if Delfos::Patching::MethodOverride.respond_to?(:unstub_all!)
|
44
|
+
Delfos::Patching::MethodOverride.unstub_all!
|
45
|
+
end
|
34
46
|
end
|
35
47
|
end
|
36
48
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
49
|
+
def remove_added_methods!
|
50
|
+
if defined? Delfos::MethodLogging::AddedMethods
|
51
|
+
Delfos::MethodLogging::AddedMethods.instance_eval { @instance = nil }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove_patching!
|
56
|
+
load "delfos/patching/basic_object_remove.rb"
|
57
|
+
end
|
58
|
+
|
59
|
+
def setup!(logger: Delfos::Neo4j::Informer.new,
|
60
|
+
neo4j_url: nil,
|
61
|
+
neo4j_username: nil,
|
62
|
+
neo4j_password: nil,
|
63
|
+
application_directories: nil)
|
64
|
+
application_directories ||= %w(app lib)
|
65
|
+
|
66
|
+
@application_directories = if application_directories.is_a?(Proc)
|
67
|
+
application_directories
|
68
|
+
else
|
69
|
+
Array(application_directories).map { |f| Pathname.new(File.expand_path(f.to_s)) }
|
70
|
+
end
|
43
71
|
|
44
|
-
@application_directories = application_directories.map{|f| Pathname.new(File.expand_path(f.to_s))}
|
45
72
|
@logger = logger
|
46
73
|
|
47
|
-
|
74
|
+
setup_neo4j!(neo4j_url, neo4j_username, neo4j_password)
|
48
75
|
|
49
76
|
perform_patching!
|
50
77
|
end
|
51
78
|
|
79
|
+
def setup_neo4j!(url = nil, username = nil, password = nil)
|
80
|
+
url ||= ENV["NEO4J_URL"] || "http://localhost:7474"
|
81
|
+
username ||= ENV["NEO4J_USERNAME"] || "neo4j"
|
82
|
+
password ||= ENV["NEO4J_PASSWORD"] || "password"
|
83
|
+
@neo4j = Neo4jOptions.new(url, username, password)
|
84
|
+
end
|
85
|
+
|
52
86
|
def perform_patching!
|
53
|
-
load "delfos/
|
87
|
+
load "delfos/patching/basic_object.rb"
|
88
|
+
end
|
89
|
+
|
90
|
+
attr_reader :neo4j
|
91
|
+
|
92
|
+
Neo4jOptions = Struct.new(:url, :username, :password) do
|
93
|
+
def to_s
|
94
|
+
" url: #{url}\n username: #{username}\n password: #{password}"
|
95
|
+
end
|
54
96
|
end
|
55
97
|
|
56
|
-
attr_reader :application_directories, :
|
98
|
+
attr_reader :application_directories, :neo4j_host, :neo4j_username, :neo4j_password
|
57
99
|
end
|
58
100
|
end
|
data/lib/delfos/common_path.rb
CHANGED
@@ -29,9 +29,7 @@ module Delfos
|
|
29
29
|
common_path = common_path(dir1, dir2)
|
30
30
|
common_path, path_a, path_b = append_trailing_slashes!(common_path, path_a, path_b)
|
31
31
|
|
32
|
-
if valid_length?(common_path, path_a, path_b)
|
33
|
-
Pathname.new(common_path)
|
34
|
-
end
|
32
|
+
Pathname.new(common_path) if valid_length?(common_path, path_a, path_b)
|
35
33
|
end
|
36
34
|
|
37
35
|
def valid_length?(common_path, path_a, path_b)
|
@@ -1,42 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require_relative "neo4j/execution_persistence"
|
2
3
|
|
3
4
|
module Delfos
|
4
5
|
class ExecutionChain
|
5
|
-
|
6
|
+
EXECUTION_CHAIN_MUTEX = Mutex.new
|
6
7
|
|
7
8
|
def self.reset!
|
8
|
-
|
9
|
-
Thread.current[:
|
9
|
+
EXECUTION_CHAIN_MUTEX.synchronize do
|
10
|
+
Thread.current[:_delfos__execution_chain] = nil
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
13
|
-
def self.
|
14
|
-
|
15
|
-
Thread.current[:
|
14
|
+
def self.execution_chain
|
15
|
+
EXECUTION_CHAIN_MUTEX.synchronize do
|
16
|
+
Thread.current[:_delfos__execution_chain] ||= new
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
19
20
|
def self.push(method_object)
|
20
|
-
|
21
|
+
execution_chain.push(method_object)
|
21
22
|
end
|
22
23
|
|
23
24
|
def self.pop
|
24
|
-
|
25
|
+
execution_chain.pop
|
25
26
|
end
|
26
27
|
|
27
28
|
def push(method_object)
|
28
|
-
|
29
|
+
call_sites.push(method_object)
|
29
30
|
self.stack_depth += 1
|
30
31
|
|
31
32
|
self.execution_count += self.stack_depth == 1 ? 1 : 0
|
32
33
|
end
|
33
34
|
|
34
35
|
def pop
|
35
|
-
popping_empty_stack! if self.stack_depth
|
36
|
+
popping_empty_stack! if self.stack_depth.zero?
|
36
37
|
|
37
38
|
self.stack_depth -= 1
|
38
39
|
|
39
|
-
save_and_reset! if self.stack_depth
|
40
|
+
save_and_reset! if self.stack_depth.zero?
|
40
41
|
end
|
41
42
|
|
42
43
|
def stack_depth
|
@@ -55,6 +56,8 @@ module Delfos
|
|
55
56
|
call_sites.length
|
56
57
|
end
|
57
58
|
|
59
|
+
attr_writer :stack_depth, :step_count, :execution_count, :call_sites
|
60
|
+
|
58
61
|
private
|
59
62
|
|
60
63
|
class PoppingEmptyStackError < StandardError
|
@@ -65,10 +68,8 @@ module Delfos
|
|
65
68
|
end
|
66
69
|
|
67
70
|
def save_and_reset!
|
68
|
-
Neo4j::ExecutionPersistence.save!(self)
|
71
|
+
Neo4j::ExecutionPersistence.save!(self) if call_sites.length.positive?
|
69
72
|
self.call_sites = []
|
70
73
|
end
|
71
|
-
|
72
|
-
attr_writer :stack_depth, :step_count, :execution_count, :call_sites
|
73
74
|
end
|
74
75
|
end
|
@@ -1,9 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "pathname"
|
3
|
-
require "forwardable"
|
3
|
+
require "forwardable" unless defined? Forwardable
|
4
4
|
require "binding_of_caller"
|
5
5
|
require_relative "common_path"
|
6
|
-
require_relative "method_logging/klass_determination"
|
7
6
|
require_relative "method_logging/code_location"
|
8
7
|
require_relative "method_logging/args"
|
9
8
|
require_relative "execution_chain"
|
@@ -11,6 +10,11 @@ require_relative "execution_chain"
|
|
11
10
|
module Delfos
|
12
11
|
class << self
|
13
12
|
attr_accessor :logger, :application_directories
|
13
|
+
attr_writer :method_logging
|
14
|
+
|
15
|
+
def method_logging
|
16
|
+
@method_logging ||= ::Delfos::MethodLogging
|
17
|
+
end
|
14
18
|
end
|
15
19
|
|
16
20
|
class ApplicationDirectoriesNotDefined < StandardError
|
@@ -25,32 +29,21 @@ module Delfos
|
|
25
29
|
raise Delfos::ApplicationDirectoriesNotDefined unless Delfos.application_directories
|
26
30
|
end
|
27
31
|
|
28
|
-
def log(called_object,
|
29
|
-
args, keyword_args, _block,
|
30
|
-
class_method,
|
31
|
-
stack, call_site_binding,
|
32
|
-
called_method)
|
32
|
+
def log(call_site, called_object, called_method, class_method, arguments)
|
33
33
|
check_setup!
|
34
|
-
|
35
|
-
call_site = CodeLocation.from_call_site(stack, call_site_binding)
|
36
|
-
Delfos::ExecutionChain.push(call_site)
|
37
|
-
|
38
|
-
return unless call_site
|
39
|
-
|
40
|
-
args = Args.new(args.dup, keyword_args.dup)
|
41
|
-
|
34
|
+
arguments = Args.new(arguments)
|
42
35
|
called_code = CodeLocation.from_called(called_object, called_method, class_method)
|
43
36
|
|
44
|
-
Delfos.logger.debug(
|
37
|
+
Delfos.logger.debug(arguments, call_site, called_code)
|
45
38
|
end
|
46
39
|
|
47
40
|
def include_any_path_in_logging?(paths)
|
48
|
-
paths.inject(false) do |result, path|
|
41
|
+
Array(paths).inject(false) do |result, path|
|
49
42
|
result || include_file_in_logging?(path)
|
50
43
|
end
|
51
44
|
end
|
52
45
|
|
53
|
-
def
|
46
|
+
def exclude?(method)
|
54
47
|
file, _line_number = method.source_location
|
55
48
|
return true unless file
|
56
49
|
|
@@ -63,8 +56,13 @@ module Delfos
|
|
63
56
|
|
64
57
|
def exclude_file_from_logging?(file)
|
65
58
|
check_setup!
|
66
|
-
path = Pathname.new(File.expand_path
|
67
|
-
|
59
|
+
path = Pathname.new(File.expand_path(file))
|
60
|
+
|
61
|
+
if Delfos.application_directories.is_a? Proc
|
62
|
+
Delfos.application_directories.call(path)
|
63
|
+
else
|
64
|
+
!CommonPath.included_in?(path, Delfos.application_directories)
|
65
|
+
end
|
68
66
|
end
|
69
67
|
end
|
70
68
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "Forwardable" unless defined? Forwardable
|
3
|
+
|
4
|
+
module Delfos
|
5
|
+
module MethodLogging
|
6
|
+
class AddedMethods
|
7
|
+
class << self
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :instance,
|
11
|
+
:all_method_sources_for,
|
12
|
+
:method_source_for,
|
13
|
+
:append,
|
14
|
+
:find
|
15
|
+
|
16
|
+
def instance
|
17
|
+
@instance ||= new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@added_methods = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :added_methods
|
26
|
+
|
27
|
+
def all_method_sources_for(klass)
|
28
|
+
fetch(klass).values.map(&:source_location)
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_source_for(klass, key)
|
32
|
+
meth = find(klass, key)
|
33
|
+
|
34
|
+
if meth
|
35
|
+
meth.source_location
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def append(klass, key, original_method)
|
40
|
+
m = fetch(klass)[key]
|
41
|
+
|
42
|
+
if m.nil?
|
43
|
+
fetch(klass)[key] = original_method
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def find(klass, key)
|
48
|
+
fetch(klass)[key]
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def fetch(klass)
|
54
|
+
# Find method definitions defined in klass or its ancestors
|
55
|
+
super_klass = klass.ancestors.detect do |k|
|
56
|
+
(fetch_without_default(k) || {}).values.length.positive?
|
57
|
+
end
|
58
|
+
|
59
|
+
added_methods[(super_klass || klass).to_s] ||= {}
|
60
|
+
end
|
61
|
+
|
62
|
+
def fetch_without_default(klass)
|
63
|
+
added_methods[klass.to_s]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,52 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require_relative "../../delfos"
|
3
3
|
require_relative "../common_path"
|
4
|
-
require_relative "
|
4
|
+
require_relative "./added_methods"
|
5
5
|
|
6
6
|
module Delfos
|
7
7
|
module MethodLogging
|
8
8
|
class Args
|
9
|
-
|
9
|
+
attr_reader :block
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
@raw_args = args
|
13
|
-
@raw_keyword_args = keyword_args
|
11
|
+
def initialize(arguments)
|
12
|
+
@raw_args = arguments.args
|
13
|
+
@raw_keyword_args = arguments.keyword_args
|
14
|
+
@block = arguments.block
|
14
15
|
end
|
15
16
|
|
16
17
|
def args
|
17
18
|
@args ||= calculate_args(@raw_args)
|
18
19
|
end
|
19
20
|
|
20
|
-
def calculate_args(args)
|
21
|
-
klass_file_locations(args).select do |_klass, locations|
|
22
|
-
Delfos::MethodLogging.include_any_path_in_logging?(locations)
|
23
|
-
end.map(&:first)
|
24
|
-
end
|
25
|
-
|
26
21
|
def keyword_args
|
27
22
|
@keyword_args ||= calculate_args(@raw_keyword_args.values)
|
28
23
|
end
|
29
24
|
|
30
|
-
|
31
|
-
files = method_sources(klass)
|
25
|
+
private
|
32
26
|
|
33
|
-
|
27
|
+
def calculate_args(arguments)
|
28
|
+
arguments.
|
29
|
+
map { |o| o.is_a?(Class) ? o : o.class }.
|
30
|
+
select { |k| keep?(k) }
|
34
31
|
end
|
35
32
|
|
36
|
-
|
33
|
+
def keep?(klass)
|
34
|
+
files_for(klass).
|
35
|
+
select { |f| record?(f) }.length.positive?
|
36
|
+
end
|
37
37
|
|
38
|
-
def
|
39
|
-
|
38
|
+
def files_for(klass)
|
39
|
+
source_files(klass).
|
40
|
+
flatten.
|
41
|
+
compact.
|
42
|
+
uniq
|
40
43
|
end
|
41
44
|
|
42
|
-
def
|
43
|
-
|
44
|
-
klass_for(k)
|
45
|
-
end
|
45
|
+
def source_files(klass)
|
46
|
+
AddedMethods.all_method_sources_for(klass).map(&:first)
|
46
47
|
end
|
47
48
|
|
48
|
-
def
|
49
|
-
|
49
|
+
def record?(f)
|
50
|
+
Delfos.method_logging.include_any_path_in_logging?(f)
|
50
51
|
end
|
51
52
|
end
|
52
53
|
end
|
@@ -1,11 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require_relative "klass_determination"
|
3
2
|
|
4
3
|
module Delfos
|
5
4
|
module MethodLogging
|
6
5
|
class CodeLocation
|
7
|
-
include KlassDetermination
|
8
|
-
|
9
6
|
class << self
|
10
7
|
def from_call_site(stack, call_site_binding)
|
11
8
|
CallSiteParsing.new(stack, call_site_binding).perform
|
@@ -21,18 +18,6 @@ module Delfos
|
|
21
18
|
def method_type_from(class_method)
|
22
19
|
class_method ? "ClassMethod" : "InstanceMethod"
|
23
20
|
end
|
24
|
-
|
25
|
-
def method_definition_for(klass, class_method, method_name)
|
26
|
-
key = key_from(class_method, method_name)
|
27
|
-
|
28
|
-
Delfos::Patching.method_definition_for(klass, key)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def key_from(class_method, name)
|
34
|
-
"#{method_type_from(class_method)}_#{name}"
|
35
|
-
end
|
36
21
|
end
|
37
22
|
|
38
23
|
attr_reader :object, :method_name, :class_method, :method_type, :file, :line_number
|
@@ -44,7 +29,6 @@ module Delfos
|
|
44
29
|
@method_type = self.class.method_type_from class_method
|
45
30
|
@line_number = line_number.to_i
|
46
31
|
@file = file
|
47
|
-
|
48
32
|
end
|
49
33
|
|
50
34
|
def file
|
@@ -71,7 +55,7 @@ module Delfos
|
|
71
55
|
end
|
72
56
|
|
73
57
|
def klass
|
74
|
-
|
58
|
+
object.is_a?(Class) ? object : object.class
|
75
59
|
end
|
76
60
|
|
77
61
|
def klass_name
|
@@ -80,11 +64,21 @@ module Delfos
|
|
80
64
|
end
|
81
65
|
|
82
66
|
def method_definition_file
|
83
|
-
method_definition
|
67
|
+
if method_definition
|
68
|
+
method_definition[0].to_s
|
69
|
+
else
|
70
|
+
#TODO fix edge case when block
|
71
|
+
"#{@file} in block"
|
72
|
+
end
|
84
73
|
end
|
85
74
|
|
86
75
|
def method_definition_line
|
87
|
-
method_definition
|
76
|
+
if method_definition
|
77
|
+
method_definition[1].to_i
|
78
|
+
else
|
79
|
+
#TODO fix edge case when block
|
80
|
+
0
|
81
|
+
end
|
88
82
|
end
|
89
83
|
|
90
84
|
private
|
@@ -94,23 +88,22 @@ module Delfos
|
|
94
88
|
end
|
95
89
|
|
96
90
|
def method_definition
|
97
|
-
|
91
|
+
@method_definition ||= ::Delfos::MethodLogging::AddedMethods.method_source_for(klass, method_key)
|
98
92
|
end
|
99
93
|
end
|
100
94
|
|
101
95
|
class CallSiteParsing
|
102
|
-
# This magic number is
|
103
|
-
#
|
104
|
-
#
|
105
|
-
# is to be extracted into another method we will get a failing test and have to increment
|
106
|
-
# the value
|
96
|
+
# This magic number is based on the implementation within this file.
|
97
|
+
# If the line with `call_site_binding.of_caller(stack_index + STACK_OFFSET).receiver`
|
98
|
+
# is moved up or down the call stack a test fails and we have to change `STACK_OFFSET`
|
107
99
|
STACK_OFFSET = 5
|
108
100
|
|
109
101
|
attr_reader :stack, :call_site_binding
|
110
102
|
|
111
|
-
def initialize(stack, call_site_binding)
|
103
|
+
def initialize(stack, call_site_binding, stack_offset: nil)
|
112
104
|
@stack = stack
|
113
105
|
@call_site_binding = call_site_binding
|
106
|
+
@stack_offset = stack_offset
|
114
107
|
end
|
115
108
|
|
116
109
|
def perform
|
@@ -129,12 +122,16 @@ module Delfos
|
|
129
122
|
def current
|
130
123
|
stack.detect do |s|
|
131
124
|
file = s.split(":")[0]
|
132
|
-
Delfos
|
125
|
+
Delfos.method_logging.include_file_in_logging?(file)
|
133
126
|
end
|
134
127
|
end
|
135
128
|
|
136
129
|
def object
|
137
|
-
@object ||= call_site_binding.of_caller(stack_index +
|
130
|
+
@object ||= call_site_binding.of_caller(stack_index + stack_offset).receiver
|
131
|
+
end
|
132
|
+
|
133
|
+
def stack_offset
|
134
|
+
@stack_offset ||= STACK_OFFSET
|
138
135
|
end
|
139
136
|
|
140
137
|
def stack_index
|