appmap 0.77.3 → 0.79.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.travis.yml +4 -23
  4. data/CHANGELOG.md +21 -0
  5. data/{spec/fixtures/rails5_users_app/Dockerfile.pg → Dockerfile.pg} +0 -0
  6. data/README.md +14 -44
  7. data/README_CI.md +0 -7
  8. data/Rakefile +12 -150
  9. data/appmap.gemspec +3 -2
  10. data/docker-compose.yml +10 -0
  11. data/ext/appmap/appmap.c +21 -2
  12. data/lib/appmap/builtin_hooks/ruby.yml +6 -3
  13. data/lib/appmap/handler/eval.rb +41 -0
  14. data/lib/appmap/handler/function.rb +8 -8
  15. data/lib/appmap/handler/net_http.rb +19 -18
  16. data/lib/appmap/handler/rails/request_handler.rb +3 -4
  17. data/lib/appmap/handler/rails/template.rb +68 -62
  18. data/lib/appmap/hook/method/ruby2.rb +56 -0
  19. data/lib/appmap/hook/method/ruby3.rb +56 -0
  20. data/lib/appmap/hook/method.rb +42 -98
  21. data/lib/appmap/hook.rb +2 -2
  22. data/lib/appmap/version.rb +1 -1
  23. data/spec/config_spec.rb +1 -1
  24. data/spec/depends/api_spec.rb +13 -5
  25. data/spec/depends/spec_helper.rb +0 -9
  26. data/spec/fixtures/database.yml +11 -0
  27. data/spec/fixtures/rails5_users_app/config/database.yml +1 -0
  28. data/spec/fixtures/rails6_users_app/Gemfile +1 -25
  29. data/spec/fixtures/rails6_users_app/config/database.yml +1 -0
  30. data/spec/fixtures/rails7_users_app/Gemfile +1 -25
  31. data/spec/fixtures/rails7_users_app/config/database.yml +1 -0
  32. data/spec/handler/eval_spec.rb +66 -0
  33. data/spec/hook_spec.rb +3 -3
  34. data/spec/rails_recording_spec.rb +4 -20
  35. data/spec/rails_spec_helper.rb +76 -63
  36. data/spec/rails_test_spec.rb +7 -17
  37. data/spec/railtie_spec.rb +4 -18
  38. data/spec/record_sql_rails_pg_spec.rb +44 -75
  39. data/spec/remote_recording_spec.rb +18 -30
  40. data/spec/spec_helper.rb +1 -0
  41. data/spec/swagger/swagger_spec.rb +1 -16
  42. data/spec/util_spec.rb +1 -1
  43. metadata +25 -21
  44. data/Dockerfile.appmap +0 -5
  45. data/spec/fixtures/rack_users_app/Dockerfile +0 -32
  46. data/spec/fixtures/rack_users_app/docker-compose.yml +0 -9
  47. data/spec/fixtures/rails5_users_app/Dockerfile +0 -29
  48. data/spec/fixtures/rails5_users_app/config/database.yml +0 -18
  49. data/spec/fixtures/rails5_users_app/create_app +0 -33
  50. data/spec/fixtures/rails5_users_app/docker-compose.yml +0 -31
  51. data/spec/fixtures/rails6_users_app/.ruby-version +0 -1
  52. data/spec/fixtures/rails6_users_app/Dockerfile +0 -44
  53. data/spec/fixtures/rails6_users_app/Dockerfile.pg +0 -3
  54. data/spec/fixtures/rails6_users_app/config/database.yml +0 -18
  55. data/spec/fixtures/rails6_users_app/create_app +0 -33
  56. data/spec/fixtures/rails6_users_app/docker-compose.yml +0 -31
  57. data/spec/fixtures/rails7_users_app/.ruby-version +0 -1
  58. data/spec/fixtures/rails7_users_app/Dockerfile +0 -30
  59. data/spec/fixtures/rails7_users_app/Dockerfile.pg +0 -3
  60. data/spec/fixtures/rails7_users_app/config/database.yml +0 -86
  61. data/spec/fixtures/rails7_users_app/create_app +0 -31
  62. data/spec/fixtures/rails7_users_app/docker-compose.yml +0 -31
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'appmap/handler/function'
3
4
  require 'appmap/event'
4
5
 
5
6
  module AppMap
@@ -9,7 +10,7 @@ module AppMap
9
10
  LOG = (ENV['APPMAP_TEMPLATE_DEBUG'] == 'true' || ENV['DEBUG'] == 'true')
10
11
 
11
12
  # All the code which is touched by the AppMap is recorded in the classMap.
12
- # This duck-typed 'method' is used to represent a view template as a package,
13
+ # This duck-typed 'method' is used to represent a view template as a package,
13
14
  # class, and method in the classMap.
14
15
  # The class name is generated from the template path. The package name is
15
16
  # 'app/views', and the method name is 'render'. The source location of the method
@@ -41,31 +42,31 @@ module AppMap
41
42
  def package
42
43
  'app/views'
43
44
  end
44
-
45
+
45
46
  def name
46
47
  'render'
47
48
  end
48
-
49
+
49
50
  def source_location
50
51
  path
51
52
  end
52
-
53
+
53
54
  def static
54
55
  true
55
56
  end
56
-
57
+
57
58
  def comment
58
59
  nil
59
60
  end
60
-
61
+
61
62
  def labels
62
63
  [ 'mvc.template' ]
63
64
  end
64
65
  end
65
-
66
+
66
67
  # TemplateCall is a type of function call which is specialized to view template rendering. Since
67
68
  # there isn't really a perfect method in Rails to hook, this one is synthesized from the available
68
- # information.
69
+ # information.
69
70
  class TemplateCall < AppMap::Event::MethodEvent
70
71
  # This is basically the +self+ parameter.
71
72
  attr_reader :render_instance
@@ -75,19 +76,19 @@ module AppMap
75
76
  attr_accessor :ready
76
77
 
77
78
  alias ready? ready
78
-
79
+
79
80
  def initialize(render_instance)
80
81
  super :call
81
-
82
+
82
83
  AppMap::Event::MethodEvent.build_from_invocation(:call, event: self)
83
84
  @ready = false
84
85
  @render_instance = render_instance
85
86
  end
86
-
87
+
87
88
  def static?
88
89
  true
89
90
  end
90
-
91
+
91
92
  def to_h
92
93
  super.tap do |h|
93
94
  h[:defined_class] = path ? path.parameterize.underscore : 'inline_template'
@@ -103,71 +104,76 @@ module AppMap
103
104
  end.compact
104
105
  end
105
106
  end
106
-
107
+
107
108
  TEMPLATE_RENDERER = 'appmap.handler.rails.template.renderer'
108
109
 
109
110
  # Hooks the ActionView::Resolver methods +find_all+, +find_all_anywhere+. The resolver is used
110
111
  # during template rendering to lookup the template file path from parameters such as the
111
112
  # template name, prefix, and partial (boolean).
112
- class ResolverHandler
113
- class << self
114
- # Handled as a normal function call.
115
- def handle_call(defined_class, hook_method, receiver, args)
116
- name, prefix, partial = args
117
- warn "Resolver: #{{ name: name, prefix: prefix, partial: partial }}" if LOG
118
-
119
- AppMap::Handler::Function.handle_call(defined_class, hook_method, receiver, args)
120
- end
113
+ class ResolverHandler < AppMap::Handler::Function
114
+ def handle_call(receiver, args)
115
+ name, prefix, partial = args
116
+ warn "Resolver: #{{ name: name, prefix: prefix, partial: partial }}" if LOG
121
117
 
122
- # When the resolver returns, look to see if there is template rendering underway.
123
- # If so, populate the template path. In all cases, add a TemplateMethod so that the
124
- # template will be recorded in the classMap.
125
- def handle_return(call_event_id, elapsed, return_value, exception)
126
- renderer = Array(Thread.current[TEMPLATE_RENDERER]).last
127
- path_obj = Array(return_value).first
128
-
129
- warn "Resolver return: #{path_obj}" if LOG
130
-
131
- if path_obj
132
- path = if path_obj.respond_to?(:identifier) && path_obj.inspect.index('#<')
133
- path_obj.identifier
134
- else
135
- path_obj.inspect
136
- end
137
- path = path[Dir.pwd.length + 1..-1] if path.index(Dir.pwd) == 0
138
- AppMap.tracing.record_method(TemplateMethod.new(path))
139
- renderer.path ||= path if renderer
140
- end
118
+ super
119
+ end
141
120
 
142
- AppMap::Handler::Function.handle_return(call_event_id, elapsed, return_value, exception)
143
- end
121
+ # When the resolver returns, look to see if there is template rendering underway.
122
+ # If so, populate the template path. In all cases, add a TemplateMethod so that the
123
+ # template will be recorded in the classMap.
124
+ def handle_return(call_event_id, elapsed, return_value, exception)
125
+ renderer = Array(Thread.current[TEMPLATE_RENDERER]).last
126
+ path_obj = Array(return_value).first
127
+
128
+ warn "Resolver return: #{path_obj}" if LOG
129
+
130
+ record_template_path renderer, path_obj
131
+
132
+ super
133
+ end
134
+
135
+ def record_template_path(renderer, path_obj)
136
+ return unless path_obj
137
+
138
+ path = path_from_obj path_obj
139
+ AppMap.tracing.record_method(TemplateMethod.new(path))
140
+ renderer.path ||= path if renderer
141
+ end
142
+
143
+ def path_from_obj(path_obj)
144
+ path =
145
+ if path_obj.respond_to?(:identifier) && path_obj.inspect.index('#<')
146
+ path_obj.identifier
147
+ else
148
+ path_obj.inspect
149
+ end
150
+ path = path[Dir.pwd.length + 1..] if path.index(Dir.pwd) == 0
151
+ path
144
152
  end
145
153
  end
146
154
 
147
155
  # Hooks the ActionView::Renderer method +render+. This method is used by Rails to perform
148
156
  # template rendering. The TemplateCall event which is emitted by this handler has a
149
- # +path+ parameter, which is nil until it's filled in by a ResolverHandler.
150
- class RenderHandler
151
- class << self
152
- def handle_call(defined_class, hook_method, receiver, args)
153
- # context, options
154
- _, options = args
155
-
156
- warn "Renderer: #{options}" if LOG
157
-
158
- TemplateCall.new(receiver).tap do |call|
159
- Thread.current[TEMPLATE_RENDERER] ||= []
160
- Thread.current[TEMPLATE_RENDERER] << call
161
- end
162
- end
163
-
164
- def handle_return(call_event_id, elapsed, return_value, exception)
165
- template_call = Array(Thread.current[TEMPLATE_RENDERER]).pop
166
- template_call.ready = true
157
+ # +path+ parameter, which is nil until it's filled in by a ResolverHandler.
158
+ class RenderHandler < AppMap::Hook::Method
159
+ def handle_call(receiver, args)
160
+ # context, options
161
+ _, options = args
162
+
163
+ warn "Renderer: #{options}" if LOG
167
164
 
168
- AppMap::Event::MethodReturnIgnoreValue.build_from_invocation(call_event_id, elapsed: elapsed)
165
+ TemplateCall.new(receiver).tap do |call|
166
+ Thread.current[TEMPLATE_RENDERER] ||= []
167
+ Thread.current[TEMPLATE_RENDERER] << call
169
168
  end
170
169
  end
170
+
171
+ def handle_return(call_event_id, elapsed, _return_value, _exception)
172
+ template_call = Array(Thread.current[TEMPLATE_RENDERER]).pop
173
+ template_call.ready = true
174
+
175
+ AppMap::Event::MethodReturnIgnoreValue.build_from_invocation(call_event_id, elapsed: elapsed)
176
+ end
171
177
  end
172
178
  end
173
179
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ def ruby2_keywords(*); end unless respond_to?(:ruby2_keywords, true)
4
+
5
+ module AppMap
6
+ class Hook
7
+ # Delegation methods for Ruby 2.
8
+ # cf. https://eregon.me/blog/2019/11/10/the-delegation-challenge-of-ruby27.html
9
+ class Method
10
+ ruby2_keywords def call(receiver, *args, &block)
11
+ return do_call(receiver, *args, &block) unless trace?
12
+
13
+ call_event = with_disabled_hook { before_hook receiver, *args }
14
+ trace_call call_event, receiver, *args, &block
15
+ end
16
+
17
+ protected
18
+
19
+ def before_hook(receiver, *args)
20
+ call_event = handle_call(receiver, args)
21
+ if call_event
22
+ AppMap.tracing.record_event \
23
+ call_event,
24
+ package: hook_package,
25
+ defined_class: defined_class,
26
+ method: hook_method
27
+ end
28
+ call_event
29
+ end
30
+
31
+ ruby2_keywords def do_call(receiver, *args, &block)
32
+ hook_method.bind(receiver).call(*args, &block)
33
+ end
34
+
35
+ ruby2_keywords def trace_call(call_event, receiver, *args, &block)
36
+ start_time = gettime
37
+ begin
38
+ return_value = do_call(receiver, *args, &block)
39
+ rescue # rubocop:disable Style/RescueStandardError
40
+ exception = $ERROR_INFO
41
+ raise
42
+ ensure
43
+ with_disabled_hook { after_hook receiver, call_event, gettime - start_time, return_value, exception } \
44
+ if call_event
45
+ end
46
+ end
47
+
48
+ def hook_method_def
49
+ this = self
50
+ proc { |*args, &block| this.call self, *args, &block }.tap do |hook|
51
+ hook.ruby2_keywords if hook.respond_to? :ruby2_keywords
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppMap
4
+ class Hook
5
+ # Delegation methods for Ruby 3.
6
+ class Method
7
+ def call(receiver, *args, **kwargs, &block)
8
+ return do_call(receiver, *args, **kwargs, &block) unless trace?
9
+
10
+ call_event = with_disabled_hook { before_hook receiver, *args, **kwargs }
11
+ trace_call call_event, receiver, *args, **kwargs, &block
12
+ end
13
+
14
+ protected
15
+
16
+ def before_hook(receiver, *args, **kwargs)
17
+ args = [*args, kwargs] if !kwargs.empty? || keyrest?
18
+ call_event = handle_call(receiver, args)
19
+ if call_event
20
+ AppMap.tracing.record_event \
21
+ call_event,
22
+ package: hook_package,
23
+ defined_class: defined_class,
24
+ method: hook_method
25
+ end
26
+ call_event
27
+ end
28
+
29
+ def keyrest?
30
+ @keyrest ||= parameters.map(&:last).include? :keyrest
31
+ end
32
+
33
+ def do_call(receiver, *args, **kwargs, &block)
34
+ hook_method.bind_call(receiver, *args, **kwargs, &block)
35
+ end
36
+
37
+ def trace_call(call_event, receiver, *args, **kwargs, &block)
38
+ start_time = gettime
39
+ begin
40
+ return_value = do_call(receiver, *args, **kwargs, &block)
41
+ rescue # rubocop:disable Style/RescueStandardError
42
+ exception = $ERROR_INFO
43
+ raise
44
+ ensure
45
+ with_disabled_hook { after_hook receiver, call_event, gettime - start_time, return_value, exception } \
46
+ if call_event
47
+ end
48
+ end
49
+
50
+ def hook_method_def
51
+ this = self
52
+ proc { |*args, **kwargs, &block| this.call self, *args, **kwargs, &block }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,17 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppMap
4
- NEW_RUBY = Util.ruby_minor_version >= 2.7
5
- if NEW_RUBY && !Proc.instance_methods.include?(:ruby2_keywords)
6
- warn "Ruby is #{RUBY_VERSION}, but Procs don't respond to #ruby2_keywords"
7
- end
8
-
9
4
  class Hook
10
5
  SIGNATURES = {}
11
-
12
6
  LOOKUP_SIGNATURE = lambda do |id|
13
7
  method = super(id)
14
-
8
+
15
9
  signature = SIGNATURES[[ method.owner, method.name ]]
16
10
  if signature
17
11
  method.singleton_class.module_eval do
@@ -20,36 +14,24 @@ module AppMap
20
14
  end
21
15
  end
22
16
  end
23
-
17
+
24
18
  method
25
19
  end
26
-
27
- class Method
28
- attr_reader :hook_package, :hook_class, :hook_method
29
20
 
30
- # +method_display_name+ may be nil if name resolution gets
31
- # deferred until runtime (e.g. for a singleton method on an
32
- # embedded Struct).
33
- attr_reader :method_display_name
21
+ # Single hooked method.
22
+ # Call #activate to override the original.
23
+ class Method
24
+ attr_reader :hook_package, :hook_class, :hook_method, :parameters, :arity
34
25
 
35
26
  HOOK_DISABLE_KEY = 'AppMap::Hook.disable'
36
27
  private_constant :HOOK_DISABLE_KEY
37
28
 
38
- # Grab the definition of Time.now here, to avoid interfering
39
- # with the method we're hooking.
40
- TIME_NOW = Time.method(:now)
41
- private_constant :TIME_NOW
42
-
43
- ARRAY_OF_EMPTY_HASH = [{}.freeze].freeze
44
-
45
29
  def initialize(hook_package, hook_class, hook_method)
46
30
  @hook_package = hook_package
47
31
  @hook_class = hook_class
48
32
  @hook_method = hook_method
49
-
50
- # Get the class for the method, if it's known.
51
- @defined_class, method_symbol = Hook.qualify_method_name(@hook_method)
52
- @method_display_name = [@defined_class, method_symbol, @hook_method.name].join if @defined_class
33
+ @parameters = hook_method.parameters
34
+ @arity = hook_method.arity
53
35
  end
54
36
 
55
37
  def activate
@@ -62,64 +44,6 @@ module AppMap
62
44
  warn "AppMap: Hooking #{msg} at line #{(hook_method.source_location || []).join(':')}"
63
45
  end
64
46
 
65
- defined_class = @defined_class
66
- hook_package = self.hook_package
67
- hook_method = self.hook_method
68
- before_hook = self.method(:before_hook)
69
- after_hook = self.method(:after_hook)
70
- with_disabled_hook = self.method(:with_disabled_hook)
71
-
72
- is_array_containing_empty_hash = ->(obj) {
73
- obj.is_a?(Array) && obj.length == 1 && obj[0].is_a?(Hash) && obj[0].size == 0
74
- }
75
-
76
- call_instance_method = lambda do |receiver, args, &block|
77
- # https://github.com/applandinc/appmap-ruby/issues/153
78
- if NEW_RUBY && is_array_containing_empty_hash.(args) && hook_method.arity == 1
79
- hook_method.bind_call(receiver, {}, &block)
80
- else
81
- if NEW_RUBY
82
- hook_method.bind_call(receiver, *args, &block)
83
- else
84
- hook_method.bind(receiver).call(*args, &block)
85
- end
86
- end
87
- end
88
-
89
- hook_method_def = Proc.new do |*args, &block|
90
- # We may not have gotten the class for the method during
91
- # initialization (e.g. for a singleton method on an embedded
92
- # struct), so make sure we have it now.
93
- defined_class, = Hook.qualify_method_name(hook_method) unless defined_class
94
-
95
- reentrant = Thread.current[HOOK_DISABLE_KEY]
96
- disabled_by_shallow_flag = \
97
- -> { hook_package&.shallow? && AppMap.tracing.last_package_for_current_thread == hook_package }
98
-
99
- enabled = true if AppMap.tracing.enabled? && !reentrant && !disabled_by_shallow_flag.call
100
-
101
- enabled = false if %i[instance_eval instance_exec].member?(hook_method.name) && args.empty?
102
-
103
- return call_instance_method.call(self, args, &block) unless enabled
104
-
105
- call_event, start_time = with_disabled_hook.call do
106
- before_hook.call(self, defined_class, args)
107
- end
108
- return_value = nil
109
- exception = nil
110
- begin
111
- return_value = call_instance_method.call(self, args, &block)
112
- rescue
113
- exception = $ERROR_INFO
114
- raise
115
- ensure
116
- with_disabled_hook.call do
117
- after_hook.call(self, call_event, start_time, return_value, exception) if call_event
118
- end
119
- end
120
- end
121
- hook_method_def = hook_method_def.ruby2_keywords if hook_method_def.respond_to?(:ruby2_keywords)
122
-
123
47
  hook_method_parameters = hook_method.parameters.dup.freeze
124
48
  SIGNATURES[[ hook_class, hook_method.name ]] = hook_method_parameters
125
49
 
@@ -140,28 +64,42 @@ module AppMap
140
64
 
141
65
  protected
142
66
 
143
- def before_hook(receiver, defined_class, args)
144
- call_event = hook_package.handler_class.handle_call(defined_class, hook_method, receiver, args)
145
- AppMap.tracing.record_event(call_event, package: hook_package, defined_class: defined_class, method: hook_method) if call_event
146
- [ call_event, TIME_NOW.call ]
67
+ def gettime
68
+ Process.clock_gettime Process::CLOCK_MONOTONIC
69
+ end
70
+
71
+ def trace?
72
+ return false unless AppMap.tracing.enabled?
73
+ return false if Thread.current[HOOK_DISABLE_KEY]
74
+ return false if hook_package&.shallow? && AppMap.tracing.last_package_for_current_thread == hook_package
75
+
76
+ true
147
77
  end
148
78
 
149
- def after_hook(_receiver, call_event, start_time, return_value, exception)
150
- elapsed = TIME_NOW.call - start_time
151
- return_event = hook_package.handler_class.handle_return(call_event.id, elapsed, return_value, exception)
79
+ def method_display_name
80
+ return @method_display_name if @method_display_name
81
+
82
+ return @method_display_name = [defined_class, method_symbol, hook_method.name].join if defined_class
83
+
84
+ "#{hook_method.name} (class resolution deferred)"
85
+ end
86
+
87
+ def defined_class
88
+ @defined_class ||= Hook.qualify_method_name(hook_method)&.first
89
+ end
90
+
91
+ def after_hook(_receiver, call_event, elapsed, return_value, exception)
92
+ return_event = handle_return(call_event.id, elapsed, return_value, exception)
152
93
  AppMap.tracing.record_event(return_event) if return_event
153
- nil
154
94
  end
155
95
 
156
- def with_disabled_hook(&function)
96
+ def with_disabled_hook
157
97
  # Don't record functions, such as to_s and inspect, that might be called
158
98
  # by the fn. Otherwise there can be a stack overflow.
159
99
  Thread.current[HOOK_DISABLE_KEY] = true
160
- begin
161
- function.call
162
- ensure
163
- Thread.current[HOOK_DISABLE_KEY] = false
164
- end
100
+ yield
101
+ ensure
102
+ Thread.current[HOOK_DISABLE_KEY] = false
165
103
  end
166
104
  end
167
105
  end
@@ -189,3 +127,9 @@ unless ENV['APPMAP_NO_PATCH_MODULE'] == 'true'
189
127
  prepend AppMap::ModuleMethods
190
128
  end
191
129
  end
130
+
131
+ if RUBY_VERSION < '3'
132
+ require 'appmap/hook/method/ruby2'
133
+ else
134
+ require 'appmap/hook/method/ruby3'
135
+ end
data/lib/appmap/hook.rb CHANGED
@@ -118,7 +118,7 @@ module AppMap
118
118
  cls, method = entry
119
119
  return false if config.never_hook?(cls, method)
120
120
 
121
- Hook::Method.new(hook.package, cls, method).activate
121
+ hook.package.handler_class.new(hook.package, cls, method).activate
122
122
  end
123
123
 
124
124
  methods = []
@@ -209,7 +209,7 @@ module AppMap
209
209
  package = config.lookup_package(hook_cls, method)
210
210
  next unless package
211
211
 
212
- Hook::Method.new(package, hook_cls, method).activate
212
+ package.handler_class.new(package, hook_cls, method).activate
213
213
  end
214
214
  end
215
215
 
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.77.3'
6
+ VERSION = '0.79.0'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.5.1'
9
9
 
data/spec/config_spec.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require 'rails_spec_helper'
4
4
  require 'appmap/config'
5
5
 
6
- describe AppMap::Config, docker: false do
6
+ describe AppMap::Config do
7
7
  it 'loads as expected' do
8
8
  config_data = {
9
9
  name: 'test',
@@ -33,7 +33,7 @@ module AppMap
33
33
  end
34
34
  end
35
35
  end
36
- end
36
+ end
37
37
 
38
38
  describe 'Depends API' do
39
39
  let(:api) { AppMap::Depends::API.new(ENV['DEBUG'] == 'true') }
@@ -112,12 +112,12 @@ describe 'Depends API' do
112
112
  around do |test|
113
113
  @minitest_test_command_method = AppMap.configuration.depends_config.minitest_test_command_method
114
114
  AppMap.configuration.depends_config.minitest_test_command_method = 'AppMap::Depends::APISpec.minitest_test_command'
115
-
115
+
116
116
  test.call
117
117
  ensure
118
118
  AppMap.configuration.depends_config.minitest_test_command_method = @minitest_test_command
119
119
  end
120
-
120
+
121
121
  it 'passes a smoke test' do
122
122
  run_tests
123
123
  end
@@ -175,10 +175,18 @@ describe 'Depends API' do
175
175
  # At this point, we would run tests to bring the AppMaps up to date
176
176
  # Then once the tests have finished, remove any AppMaps that weren't refreshed
177
177
  removed = api.remove_out_of_date_appmaps(since, appmap_dir: DEPENDS_TEST_DIR, base_dir: DEPENDS_BASE_DIR)
178
- expect(removed).to eq([ appmap_path.split('.')[0] ])
178
+ expect(removed).to eq([ appmap_path.split('.')[0] ])
179
179
  ensure
180
- File.write(appmap_path, appmap)
180
+ File.write(appmap_path, appmap)
181
181
  end
182
182
  end
183
183
  end
184
+
185
+ before do
186
+ Dir.glob("#{DEPENDS_TEST_DIR}/*.appmap.json").each { |fname| FileUtils.touch fname }
187
+ update_appmap_index
188
+
189
+ FileUtils.rm_rf 'spec/tmp'
190
+ FileUtils.mkdir_p 'spec/tmp'
191
+ end
184
192
  end
@@ -16,12 +16,3 @@ def update_appmap_index
16
16
  system cmd.join(' ') or raise "Failed to update AppMap index in #{DEPENDS_TEST_DIR}"
17
17
  end
18
18
 
19
- RSpec.configure do |rspec|
20
- rspec.before do
21
- Dir.glob("#{DEPENDS_TEST_DIR}/*.appmap.json").each { |fname| FileUtils.touch fname }
22
- update_appmap_index
23
-
24
- FileUtils.rm_rf 'spec/tmp'
25
- FileUtils.mkdir_p 'spec/tmp'
26
- end
27
- end
@@ -0,0 +1,11 @@
1
+ default: &default
2
+ url: <%= ENV['DATABASE_URL'] %>
3
+ adapter: postgresql
4
+ database: <%= ENV['TEST_DATABASE'] || 'appland-rails6-test' %>
5
+
6
+ development:
7
+ <<: *default
8
+ test:
9
+ <<: *default
10
+ production:
11
+ <<: *default
@@ -0,0 +1 @@
1
+ ../../database.yml
@@ -11,32 +11,8 @@ gem 'sequel', '>= 5.43.0', require: false
11
11
  gem 'sequel-rails', require: false
12
12
  gem 'sequel_secure_password', require: false
13
13
 
14
- appmap_path = \
15
- # Support debugging inside the container with volume-mounted source
16
- if File.directory?('/src/appmap-ruby')
17
- '/src/appmap-ruby'
18
- elsif File.exist?('../../../appmap.gemspec')
19
- '../../..'
20
- end
21
-
22
- if appmap_path
23
- # Set the branch parameter, so that 'bundle config local.appmap' will work
24
- appmap_branch = Dir.chdir appmap_path do
25
- `git rev-parse --abbrev-ref HEAD`.strip
26
- end
27
- end
28
-
29
- appmap_options = \
30
- if appmap_path && appmap_branch
31
- { git: appmap_path, branch: appmap_branch }
32
- elsif appmap_path
33
- { path: appmap_path }
34
- else
35
- {}
36
- end.merge(require: %w[appmap])
37
-
38
14
  group :development, :test do
39
- gem 'appmap', appmap_options
15
+ gem 'appmap', path: '../../..'
40
16
  gem 'cucumber-rails', require: false
41
17
  gem 'rspec-rails'
42
18
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
@@ -0,0 +1 @@
1
+ ../../database.yml