delfos 0.0.1.pre.beta → 0.0.1.pre.rc1

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