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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 504300a4ec558fbb0620996b4f1452dfd7f7424c
4
- data.tar.gz: cf3adbd6030184d208a52d7586a816cc1d365668
3
+ metadata.gz: 942050257d2df0f604039c76a5503f892118ba8c
4
+ data.tar.gz: bbafde8bed085d9c67c75d6ff9d065f578b8d8b1
5
5
  SHA512:
6
- metadata.gz: be9e77733e7d6af4ac90e62a5bbfac8ae056dbd2b6839e4a16e0c82960e64dd83d93ef8b72787b1b411f992687a67327790a3e9a9367c0dfeabe503c66d931a4
7
- data.tar.gz: 6358c9cf62d2a2db3cf12b1fd025edc65e71c3982ad3d1d56a1d8adfb871777111da61f1dcb62ad17a213069666ce59594c24a4a75fc951991610998bf2d8b9b
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.setup!(application_directories: [])
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
- @neo4j_config = nil
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 remove_patching!
29
- load "delfos/remove_patching.rb"
30
- begin
31
- Delfos::Patching.instance_eval { @added_methods = nil }
32
- rescue
33
- nil
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 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)
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
- @neo4j_config = [connection_type, host, auth]
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/perform_patching.rb"
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, :neo4j_config
98
+ attr_reader :application_directories, :neo4j_host, :neo4j_username, :neo4j_password
57
99
  end
58
100
  end
@@ -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
- METHOD_CHAIN_MUTEX = Mutex.new
6
+ EXECUTION_CHAIN_MUTEX = Mutex.new
6
7
 
7
8
  def self.reset!
8
- METHOD_CHAIN_MUTEX.synchronize do
9
- Thread.current[:_delfos__execution_chain__method_chain] = nil
9
+ EXECUTION_CHAIN_MUTEX.synchronize do
10
+ Thread.current[:_delfos__execution_chain] = nil
10
11
  end
11
12
  end
12
13
 
13
- def self.method_chain
14
- METHOD_CHAIN_MUTEX.synchronize do
15
- Thread.current[:_delfos__execution_chain__method_chain] ||= new
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
- method_chain.push(method_object)
21
+ execution_chain.push(method_object)
21
22
  end
22
23
 
23
24
  def self.pop
24
- method_chain.pop
25
+ execution_chain.pop
25
26
  end
26
27
 
27
28
  def push(method_object)
28
- self.call_sites.push(method_object)
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 == 0
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 == 0
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) if call_sites.length > 0
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(args, call_site, called_code)
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 exclude_from_logging?(method)
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 file)
67
- !CommonPath.included_in?(path, Delfos.application_directories)
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 "klass_determination"
4
+ require_relative "./added_methods"
5
5
 
6
6
  module Delfos
7
7
  module MethodLogging
8
8
  class Args
9
- include KlassDetermination
9
+ attr_reader :block
10
10
 
11
- def initialize(args, keyword_args)
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
- def klass_locations(klass)
31
- files = method_sources(klass)
25
+ private
32
26
 
33
- files.flatten.compact.uniq
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
- private
33
+ def keep?(klass)
34
+ files_for(klass).
35
+ select { |f| record?(f) }.length.positive?
36
+ end
37
37
 
38
- def klass_file_locations(args)
39
- klasses(args).each_with_object({}) { |k, result| result[k] = klass_locations(k) }
38
+ def files_for(klass)
39
+ source_files(klass).
40
+ flatten.
41
+ compact.
42
+ uniq
40
43
  end
41
44
 
42
- def klasses(args)
43
- args.map do |k|
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 method_sources(klass)
49
- (Delfos::Patching.added_methods[klass.to_s] || {}).values.map(&:first)
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
- klass_for(object)
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[0].to_s
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[1].to_i
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
- (@method_definition ||= self.class.method_definition_for(klass, class_method, method_name)) || {}
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 determined based on the specific implementation now
103
- # E.g. if the line
104
- # where we call this `call_site_binding.of_caller(stack_index + STACK_OFFSET).eval('self')`
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::MethodLogging.include_file_in_logging?(file)
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 + STACK_OFFSET).receiver
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