appmap 0.39.0 → 0.41.2

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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -16
  3. data/CHANGELOG.md +28 -2
  4. data/CONTRIBUTING.md +22 -0
  5. data/README.md +113 -51
  6. data/Rakefile +3 -3
  7. data/lib/appmap/class_map.rb +25 -8
  8. data/lib/appmap/config.rb +48 -28
  9. data/lib/appmap/event.rb +14 -4
  10. data/lib/appmap/hook.rb +18 -3
  11. data/lib/appmap/hook/method.rb +1 -1
  12. data/lib/appmap/rails/request_handler.rb +8 -3
  13. data/lib/appmap/railtie.rb +1 -5
  14. data/lib/appmap/version.rb +1 -1
  15. data/spec/abstract_controller_base_spec.rb +2 -2
  16. data/spec/config_spec.rb +1 -0
  17. data/spec/fixtures/hook/exclude.rb +15 -0
  18. data/spec/fixtures/hook/labels.rb +6 -0
  19. data/spec/fixtures/rails5_users_app/Gemfile +2 -3
  20. data/spec/fixtures/rails5_users_app/appmap.yml +4 -1
  21. data/spec/fixtures/rails5_users_app/config/application.rb +2 -0
  22. data/spec/fixtures/rails5_users_app/docker-compose.yml +3 -0
  23. data/spec/fixtures/rails6_users_app/Gemfile +2 -3
  24. data/spec/fixtures/rails6_users_app/appmap.yml +4 -1
  25. data/spec/fixtures/rails6_users_app/config/application.rb +2 -0
  26. data/spec/fixtures/rails6_users_app/docker-compose.yml +3 -0
  27. data/spec/hook_spec.rb +41 -41
  28. data/spec/record_sql_rails_pg_spec.rb +1 -1
  29. data/spec/rspec_feature_metadata_spec.rb +1 -1
  30. data/spec/spec_helper.rb +1 -0
  31. data/test/fixtures/gem_test/appmap.yml +1 -1
  32. data/test/fixtures/gem_test/test/parser_test.rb +12 -0
  33. data/test/gem_test.rb +4 -4
  34. metadata +6 -69
  35. data/spec/abstract_controller4_base_spec.rb +0 -66
  36. data/spec/fixtures/rails4_users_app/.gitignore +0 -13
  37. data/spec/fixtures/rails4_users_app/.rbenv-gemsets +0 -2
  38. data/spec/fixtures/rails4_users_app/.ruby-version +0 -1
  39. data/spec/fixtures/rails4_users_app/Dockerfile +0 -30
  40. data/spec/fixtures/rails4_users_app/Dockerfile.pg +0 -3
  41. data/spec/fixtures/rails4_users_app/Gemfile +0 -77
  42. data/spec/fixtures/rails4_users_app/README.rdoc +0 -28
  43. data/spec/fixtures/rails4_users_app/Rakefile +0 -6
  44. data/spec/fixtures/rails4_users_app/app/assets/images/.keep +0 -0
  45. data/spec/fixtures/rails4_users_app/app/assets/javascripts/application.js +0 -16
  46. data/spec/fixtures/rails4_users_app/app/assets/stylesheets/application.css +0 -15
  47. data/spec/fixtures/rails4_users_app/app/controllers/api/users_controller.rb +0 -27
  48. data/spec/fixtures/rails4_users_app/app/controllers/application_controller.rb +0 -5
  49. data/spec/fixtures/rails4_users_app/app/controllers/concerns/.keep +0 -0
  50. data/spec/fixtures/rails4_users_app/app/controllers/health_controller.rb +0 -5
  51. data/spec/fixtures/rails4_users_app/app/controllers/users_controller.rb +0 -5
  52. data/spec/fixtures/rails4_users_app/app/helpers/application_helper.rb +0 -2
  53. data/spec/fixtures/rails4_users_app/app/mailers/.keep +0 -0
  54. data/spec/fixtures/rails4_users_app/app/models/.keep +0 -0
  55. data/spec/fixtures/rails4_users_app/app/models/concerns/.keep +0 -0
  56. data/spec/fixtures/rails4_users_app/app/models/user.rb +0 -18
  57. data/spec/fixtures/rails4_users_app/app/views/layouts/application.html.haml +0 -7
  58. data/spec/fixtures/rails4_users_app/app/views/users/index.html.haml +0 -7
  59. data/spec/fixtures/rails4_users_app/appmap.yml +0 -3
  60. data/spec/fixtures/rails4_users_app/bin/rails +0 -9
  61. data/spec/fixtures/rails4_users_app/bin/setup +0 -29
  62. data/spec/fixtures/rails4_users_app/bin/spring +0 -17
  63. data/spec/fixtures/rails4_users_app/config.ru +0 -4
  64. data/spec/fixtures/rails4_users_app/config/application.rb +0 -26
  65. data/spec/fixtures/rails4_users_app/config/boot.rb +0 -3
  66. data/spec/fixtures/rails4_users_app/config/database.yml +0 -18
  67. data/spec/fixtures/rails4_users_app/config/environment.rb +0 -5
  68. data/spec/fixtures/rails4_users_app/config/environments/development.rb +0 -41
  69. data/spec/fixtures/rails4_users_app/config/environments/production.rb +0 -79
  70. data/spec/fixtures/rails4_users_app/config/environments/test.rb +0 -42
  71. data/spec/fixtures/rails4_users_app/config/initializers/assets.rb +0 -11
  72. data/spec/fixtures/rails4_users_app/config/initializers/backtrace_silencers.rb +0 -7
  73. data/spec/fixtures/rails4_users_app/config/initializers/cookies_serializer.rb +0 -3
  74. data/spec/fixtures/rails4_users_app/config/initializers/filter_parameter_logging.rb +0 -4
  75. data/spec/fixtures/rails4_users_app/config/initializers/inflections.rb +0 -16
  76. data/spec/fixtures/rails4_users_app/config/initializers/mime_types.rb +0 -4
  77. data/spec/fixtures/rails4_users_app/config/initializers/session_store.rb +0 -3
  78. data/spec/fixtures/rails4_users_app/config/initializers/to_time_preserves_timezone.rb +0 -10
  79. data/spec/fixtures/rails4_users_app/config/initializers/wrap_parameters.rb +0 -14
  80. data/spec/fixtures/rails4_users_app/config/locales/en.yml +0 -23
  81. data/spec/fixtures/rails4_users_app/config/routes.rb +0 -12
  82. data/spec/fixtures/rails4_users_app/config/secrets.yml +0 -22
  83. data/spec/fixtures/rails4_users_app/create_app +0 -23
  84. data/spec/fixtures/rails4_users_app/db/migrate/20191127112304_create_users.rb +0 -10
  85. data/spec/fixtures/rails4_users_app/db/schema.rb +0 -26
  86. data/spec/fixtures/rails4_users_app/db/seeds.rb +0 -7
  87. data/spec/fixtures/rails4_users_app/docker-compose.yml +0 -26
  88. data/spec/fixtures/rails4_users_app/lib/assets/.keep +0 -0
  89. data/spec/fixtures/rails4_users_app/lib/tasks/.keep +0 -0
  90. data/spec/fixtures/rails4_users_app/log/.keep +0 -0
  91. data/spec/fixtures/rails4_users_app/public/404.html +0 -67
  92. data/spec/fixtures/rails4_users_app/public/422.html +0 -67
  93. data/spec/fixtures/rails4_users_app/public/500.html +0 -66
  94. data/spec/fixtures/rails4_users_app/public/favicon.ico +0 -0
  95. data/spec/fixtures/rails4_users_app/public/robots.txt +0 -5
  96. data/spec/fixtures/rails4_users_app/spec/controllers/users_controller_api_spec.rb +0 -49
  97. data/spec/fixtures/rails4_users_app/spec/rails_helper.rb +0 -95
  98. data/spec/fixtures/rails4_users_app/spec/spec_helper.rb +0 -96
  99. data/spec/fixtures/rails4_users_app/test/fixtures/users.yml +0 -9
  100. data/spec/record_sql_rails4_pg_spec.rb +0 -75
  101. data/test/fixtures/gem_test/test/to_param_test.rb +0 -14
data/lib/appmap/config.rb CHANGED
@@ -16,20 +16,20 @@ module AppMap
16
16
  end
17
17
 
18
18
  def build_from_gem(gem, shallow: true, package_name: nil, exclude: [], labels: [])
19
- gem_paths(gem).map do |gem_path|
20
- Package.new(gem_path, gem, package_name, exclude, labels, shallow)
19
+ if %w[method_source activesupport].member?(gem)
20
+ warn "WARNING: #{gem} cannot be AppMapped because it is a dependency of the appmap gem"
21
+ return
21
22
  end
23
+ Package.new(gem_path(gem), gem, package_name, exclude, labels, shallow)
22
24
  end
23
25
 
24
26
  private_class_method :new
25
27
 
26
28
  protected
27
29
 
28
- def gem_paths(gem)
30
+ def gem_path(gem)
29
31
  gemspec = Gem.loaded_specs[gem] or raise "Gem #{gem.inspect} not found"
30
- gemspec.source_paths.map do |path|
31
- File.join(gemspec.gem_dir, path)
32
- end
32
+ gemspec.gem_dir
33
33
  end
34
34
  end
35
35
 
@@ -57,32 +57,36 @@ module AppMap
57
57
  # Methods that should always be hooked, with their containing
58
58
  # package and labels that should be applied to them.
59
59
  HOOKED_METHODS = {
60
- 'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support', package_name: 'active_support', labels: %w[security crypto])),
61
- 'ActionView::Renderer' => Hook.new(:render, Package.build_from_path('action_view', package_name: 'action_view', labels: %w[view]))
60
+ 'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support', labels: %w[provider.secure_compare])),
61
+ 'ActionView::Renderer' => Hook.new(:render, Package.build_from_path('action_view', labels: %w[mvc.view])),
62
+ 'ActionDispatch::Cookies::CookieJar' => Hook.new(%i[[]= clear update delete recycle], Package.build_from_path('action_pack', labels: %w[provider.http.cookie])),
63
+ 'ActionDispatch::Cookies::EncryptedCookieJar' => Hook.new(%i[[]=], Package.build_from_path('action_pack', labels: %w[provider.http.cookie crypto])),
64
+ 'CanCan::ControllerAdditions' => Hook.new(%i[authorize! can? cannot?], Package.build_from_path('cancancan', labels: %w[provider.authorization])),
65
+ 'CanCan::Ability' => Hook.new(%i[authorize!], Package.build_from_path('cancancan', labels: %w[provider.authorization])),
62
66
  }.freeze
63
67
 
64
68
  BUILTIN_METHODS = {
65
69
  'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGES),
66
- 'Digest::Instance' => Hook.new(:digest, OPENSSL_PACKAGES),
67
70
  'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGES),
68
71
  'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES),
69
72
  'OpenSSL::Cipher' => Hook.new(%i[encrypt decrypt final], OPENSSL_PACKAGES),
70
73
  'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGES),
71
- 'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[http io])),
72
- 'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[smtp email io])),
73
- 'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[pop pop3 email io])),
74
- 'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[imap email io])),
75
- 'Marshal' => Hook.new(%i[dump load], Package.build_from_path('marshal', labels: %w[serialization marshal])),
76
- 'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[serialization yaml])),
77
- 'JSON::Ext::Parser' => Hook.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[serialization json])),
78
- 'JSON::Ext::Generator::State' => Hook.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[serialization json])),
74
+ 'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http io])),
75
+ 'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[protocol.smtp protocol.email io])),
76
+ 'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[protocol.pop protocol.email io])),
77
+ 'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.imap protocol.email io])),
78
+ 'Marshal' => Hook.new(%i[dump load], Package.build_from_path('marshal', labels: %w[format.marshal provider.serialization])),
79
+ 'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml provider.serialization])),
80
+ 'JSON::Ext::Parser' => Hook.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json provider.serialization])),
81
+ 'JSON::Ext::Generator::State' => Hook.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json provider.serialization])),
79
82
  }.freeze
80
83
 
81
- attr_reader :name, :packages
84
+ attr_reader :name, :packages, :exclude
82
85
 
83
- def initialize(name, packages = [])
86
+ def initialize(name, packages = [], exclude = [])
84
87
  @name = name
85
88
  @packages = packages
89
+ @exclude = exclude
86
90
  end
87
91
 
88
92
  class << self
@@ -105,44 +109,60 @@ module AppMap
105
109
  shallow = true if shallow.nil?
106
110
  Package.build_from_gem(gem, exclude: package['exclude'] || [], shallow: shallow)
107
111
  else
108
- [ Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow']) ]
112
+ Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow'])
109
113
  end
110
- end.flatten
111
- Config.new config_data['name'], packages
114
+ end.compact
115
+ Config.new config_data['name'], packages, config_data['exclude'] || []
112
116
  end
113
117
  end
114
118
 
115
119
  def to_h
116
120
  {
117
121
  name: name,
118
- packages: packages.map(&:to_h)
122
+ packages: packages.map(&:to_h),
123
+ exclude: exclude
119
124
  }
120
125
  end
121
126
 
127
+ # package_for_method finds the Package, if any, which configures the hook
128
+ # for a method.
122
129
  def package_for_method(method)
130
+ package_hooked_by_class(method) || package_hooked_by_source_location(method)
131
+ end
132
+
133
+ def package_hooked_by_class(method)
123
134
  defined_class, _, method_name = ::AppMap::Hook.qualify_method_name(method)
124
- package = find_package(defined_class, method_name)
125
- return package if package
135
+ return find_package(defined_class, method_name)
136
+ end
126
137
 
138
+ def package_hooked_by_source_location(method)
127
139
  location = method.source_location
128
140
  location_file, = location
129
141
  return unless location_file
130
142
 
131
143
  location_file = location_file[Dir.pwd.length + 1..-1] if location_file.index(Dir.pwd) == 0
132
- packages.find do |pkg|
144
+ packages.select { |pkg| pkg.path }.find do |pkg|
133
145
  (location_file.index(pkg.path) == 0) &&
134
146
  !pkg.exclude.find { |p| location_file.index(p) }
135
147
  end
136
148
  end
137
149
 
138
- def included_by_location?(method)
139
- !!package_for_method(method)
150
+ def never_hook?(method)
151
+ defined_class, separator, method_name = ::AppMap::Hook.qualify_method_name(method)
152
+ return true if exclude.member?(defined_class) || exclude.member?([ defined_class, separator, method_name ].join)
140
153
  end
141
154
 
155
+ # always_hook? indicates a method that should always be hooked.
142
156
  def always_hook?(defined_class, method_name)
143
157
  !!find_package(defined_class, method_name)
144
158
  end
145
159
 
160
+ # included_by_location? indicates a method whose source location matches a method definition that has been
161
+ # configured for inclusion.
162
+ def included_by_location?(method)
163
+ !!package_for_method(method)
164
+ end
165
+
146
166
  def find_package(defined_class, method_name)
147
167
  hook = find_hook(defined_class)
148
168
  return nil unless hook
data/lib/appmap/event.rb CHANGED
@@ -38,6 +38,15 @@ module AppMap
38
38
 
39
39
  protected
40
40
 
41
+ # Heuristic for dynamically defined class whose name can be nil
42
+ def best_class_name(value)
43
+ value_cls = value.class
44
+ while value_cls.name.nil?
45
+ value_cls = value_cls.superclass
46
+ end
47
+ value_cls.name
48
+ end
49
+
41
50
  def custom_display_string(value)
42
51
  case value
43
52
  when File
@@ -77,6 +86,7 @@ module AppMap
77
86
 
78
87
  class << self
79
88
  def build_from_invocation(mc = MethodCall.new, defined_class, method, receiver, arguments)
89
+ defined_class ||= 'Class'
80
90
  mc.tap do
81
91
  static = receiver.is_a?(Module)
82
92
  mc.defined_class = defined_class
@@ -110,14 +120,14 @@ module AppMap
110
120
  end
111
121
  {
112
122
  name: param_name,
113
- class: value.class.name,
123
+ class: best_class_name(value),
114
124
  object_id: value.__id__,
115
125
  value: display_string(value),
116
126
  kind: param_type
117
127
  }
118
128
  end
119
129
  mc.receiver = {
120
- class: receiver.class.name,
130
+ class: best_class_name(receiver),
121
131
  object_id: receiver.__id__,
122
132
  value: display_string(receiver)
123
133
  }
@@ -172,7 +182,7 @@ module AppMap
172
182
  mr.tap do |_|
173
183
  if return_value
174
184
  mr.return_value = {
175
- class: return_value.class.name,
185
+ class: best_class_name(return_value),
176
186
  value: display_string(return_value),
177
187
  object_id: return_value.__id__
178
188
  }
@@ -183,7 +193,7 @@ module AppMap
183
193
  while next_exception
184
194
  exception_backtrace = next_exception.backtrace_locations.try(:[], 0)
185
195
  exceptions << {
186
- class: next_exception.class.name,
196
+ class: best_class_name(next_exception),
187
197
  message: next_exception.message,
188
198
  object_id: next_exception.__id__,
189
199
  path: exception_backtrace&.path,
data/lib/appmap/hook.rb CHANGED
@@ -6,6 +6,9 @@ module AppMap
6
6
  class Hook
7
7
  LOG = (ENV['APPMAP_DEBUG'] == 'true' || ENV['DEBUG'] == 'true')
8
8
 
9
+ OBJECT_INSTANCE_METHODS = %i[! != !~ <=> == === =~ __id__ __send__ class clone define_singleton_method display dup enum_for eql? equal? extend freeze frozen? hash inspect instance_eval instance_exec instance_of? instance_variable_defined? instance_variable_get instance_variable_set instance_variables is_a? itself kind_of? method methods nil? object_id private_methods protected_methods public_method public_methods public_send remove_instance_variable respond_to? send singleton_class singleton_method singleton_methods taint tainted? tap then to_enum to_s to_h to_a trust untaint untrust untrusted? yield_self].freeze
10
+ OBJECT_STATIC_METHODS = %i[! != !~ < <= <=> == === =~ > >= __id__ __send__ alias_method allocate ancestors attr attr_accessor attr_reader attr_writer autoload autoload? class class_eval class_exec class_variable_defined? class_variable_get class_variable_set class_variables clone const_defined? const_get const_missing const_set constants define_method define_singleton_method deprecate_constant display dup enum_for eql? equal? extend freeze frozen? hash include include? included_modules inspect instance_eval instance_exec instance_method instance_methods instance_of? instance_variable_defined? instance_variable_get instance_variable_set instance_variables is_a? itself kind_of? method method_defined? methods module_eval module_exec name new nil? object_id prepend private_class_method private_constant private_instance_methods private_method_defined? private_methods protected_instance_methods protected_method_defined? protected_methods public_class_method public_constant public_instance_method public_instance_methods public_method public_method_defined? public_methods public_send remove_class_variable remove_instance_variable remove_method respond_to? send singleton_class singleton_class? singleton_method singleton_methods superclass taint tainted? tap then to_enum to_s trust undef_method untaint untrust untrusted? yield_self].freeze
11
+
9
12
  @unbound_method_arity = ::UnboundMethod.instance_method(:arity)
10
13
  @method_arity = ::Method.instance_method(:arity)
11
14
 
@@ -42,12 +45,17 @@ module AppMap
42
45
  tp = TracePoint.new(:end) do |trace_point|
43
46
  cls = trace_point.self
44
47
 
45
- instance_methods = cls.public_instance_methods(false)
46
- class_methods = cls.singleton_class.public_instance_methods(false) - instance_methods
48
+ instance_methods = cls.public_instance_methods(false) - OBJECT_INSTANCE_METHODS
49
+ class_methods = cls.singleton_class.public_instance_methods(false) - instance_methods - OBJECT_STATIC_METHODS
47
50
 
48
51
  hook = lambda do |hook_cls|
49
52
  lambda do |method_id|
50
- method = hook_cls.public_instance_method(method_id)
53
+ method = begin
54
+ hook_cls.public_instance_method(method_id)
55
+ rescue NameError
56
+ warn "AppMap: Method #{hook_cls} #{method.name} is not accessible" if LOG
57
+ return
58
+ end
51
59
 
52
60
  warn "AppMap: Examining #{hook_cls} #{method.name}" if LOG
53
61
 
@@ -55,6 +63,8 @@ module AppMap
55
63
  # Skip methods that have no instruction sequence, as they are obviously trivial.
56
64
  next unless disasm
57
65
 
66
+ next if config.never_hook?(method)
67
+
58
68
  next unless \
59
69
  config.always_hook?(hook_cls, method.name) ||
60
70
  config.included_by_location?(method)
@@ -76,6 +86,8 @@ module AppMap
76
86
  tp.enable(&block)
77
87
  end
78
88
 
89
+ # hook_builtins builds hooks for code that is built in to the Ruby standard library.
90
+ # No TracePoint events are emitted for builtins, so a separate hooking mechanism is needed.
79
91
  def hook_builtins
80
92
  return unless self.class.lock_builtins
81
93
 
@@ -89,6 +101,7 @@ module AppMap
89
101
  require hook.package.package_name if hook.package.package_name
90
102
  Array(hook.method_names).each do |method_name|
91
103
  method_name = method_name.to_sym
104
+
92
105
  cls = class_from_string.(class_name)
93
106
  method = \
94
107
  begin
@@ -97,6 +110,8 @@ module AppMap
97
110
  cls.method(method_name) rescue nil
98
111
  end
99
112
 
113
+ next if config.never_hook?(method)
114
+
100
115
  if method
101
116
  Hook::Method.new(hook.package, cls, method).activate
102
117
  else
@@ -35,7 +35,7 @@ module AppMap
35
35
  else
36
36
  "#{hook_method.name} (class resolution deferred)"
37
37
  end
38
- warn "AppMap: Hooking " + msg
38
+ warn "AppMap: Hooking #{msg} at line #{(hook_method.source_location || []).join(':')}"
39
39
  end
40
40
 
41
41
  defined_class = @defined_class
@@ -47,9 +47,14 @@ module AppMap
47
47
 
48
48
  private
49
49
 
50
- def normalized_path(request)
51
- route = ::Rails.application.routes.router.enum_for(:recognize, request).first
52
- route.first.path.spec.to_s if route
50
+ def normalized_path(request, router = ::Rails.application.routes.router)
51
+ router.recognize request do |route, _|
52
+ app = route.app
53
+ next unless app.matches? request
54
+ return normalized_path request, app.rack_app.routes.router if app.engine?
55
+
56
+ return route.path.spec.to_s
57
+ end
53
58
  end
54
59
  end
55
60
 
@@ -5,13 +5,9 @@ module AppMap
5
5
  class Railtie < ::Rails::Railtie
6
6
  config.appmap = ActiveSupport::OrderedOptions.new
7
7
 
8
- initializer 'appmap.init' do |_| # params: app
9
- require 'appmap'
10
- end
11
-
12
8
  # appmap.subscribe subscribes to ActiveSupport Notifications so that they can be recorded as
13
9
  # AppMap events.
14
- initializer 'appmap.subscribe', after: 'appmap.init' do |_| # params: app
10
+ initializer 'appmap.subscribe' do |_| # params: app
15
11
  require 'appmap/rails/sql_handler'
16
12
  require 'appmap/rails/request_handler'
17
13
  ActiveSupport::Notifications.subscribe 'sql.sequel', AppMap::Rails::SQLHandler.new
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.39.0'
6
+ VERSION = '0.41.2'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.4'
9
9
  end
@@ -8,7 +8,7 @@ describe 'AbstractControllerBase' do
8
8
  FileUtils.rm_rf tmpdir
9
9
  FileUtils.mkdir_p tmpdir
10
10
  cmd = <<~CMD.gsub "\n", ' '
11
- docker-compose run --rm -e APPMAP=true
11
+ docker-compose run --rm -e RAILS_ENV=test -e APPMAP=true
12
12
  -v #{File.absolute_path tmpdir}:/app/tmp app ./bin/rspec #{spec_name}
13
13
  CMD
14
14
  run_cmd cmd, chdir: fixture_dir
@@ -137,7 +137,7 @@ describe 'AbstractControllerBase' do
137
137
  'name' => 'Renderer',
138
138
  'children' => include(hash_including(
139
139
  'name' => 'render',
140
- 'labels' => ['view']
140
+ 'labels' => ['mvc.view']
141
141
  ))
142
142
  ))
143
143
  ))
data/spec/config_spec.rb CHANGED
@@ -7,6 +7,7 @@ require 'appmap/config'
7
7
  describe AppMap::Config, docker: false do
8
8
  it 'loads from a Hash' do
9
9
  config_data = {
10
+ exclude: [],
10
11
  name: 'test',
11
12
  packages: [
12
13
  {
@@ -0,0 +1,15 @@
1
+ class ExcludeTest
2
+ def instance_method
3
+ 'instance_method'
4
+ end
5
+
6
+ class << self
7
+ def singleton_method
8
+ 'singleton_method'
9
+ end
10
+ end
11
+
12
+ def self.cls_method
13
+ 'class_method'
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ # @label has-cls-label
2
+ class ClassWithLabel
3
+ # @label has-fn-label
4
+ def fn_with_label
5
+ end
6
+ end
@@ -33,11 +33,10 @@ appmap_options = \
33
33
  { path: appmap_path }
34
34
  else
35
35
  {}
36
- end.merge(require: %w[appmap appmap/railtie])
37
-
38
- gem 'appmap', appmap_options
36
+ end.merge(require: %w[appmap])
39
37
 
40
38
  group :development, :test do
39
+ gem 'appmap', appmap_options
41
40
  gem 'cucumber-rails', require: false
42
41
  gem 'rspec-rails'
43
42
  # Required for Sequel, since without ActiveRecord, the Rails transactional fixture support
@@ -1,4 +1,7 @@
1
1
  name: rails5_users_app
2
2
  packages:
3
- - path: app
3
+ - path: app/models
4
+ labels: [ mvc.model ]
5
+ - path: app/controllers
6
+ labels: [ mvc.controller ]
4
7
  - gem: sequel
@@ -19,6 +19,8 @@ when 'activerecord'
19
19
  require 'active_record/railtie'
20
20
  end
21
21
 
22
+ require 'appmap/railtie' if defined?(AppMap)
23
+
22
24
  # require "active_storage/engine"
23
25
  # require "action_mailer/railtie"
24
26
  # require "action_cable/engine"
@@ -19,6 +19,9 @@ services:
19
19
  environment:
20
20
  RAILS_ENV:
21
21
  ORM_MODULE:
22
+ PGHOST: pg
23
+ PGPORT: '5432'
24
+ DATABASE_URL: postgres://postgres@pg
22
25
  APPMAP:
23
26
  volumes:
24
27
  - .:/src/app
@@ -34,11 +34,10 @@ appmap_options = \
34
34
  { path: appmap_path }
35
35
  else
36
36
  {}
37
- end.merge(require: %w[appmap appmap/railtie])
38
-
39
- gem 'appmap', appmap_options
37
+ end.merge(require: %w[appmap])
40
38
 
41
39
  group :development, :test do
40
+ gem 'appmap', appmap_options
42
41
  gem 'cucumber-rails', require: false
43
42
  gem 'rspec-rails'
44
43
  # Required for Sequel, since without ActiveRecord, the Rails transactional fixture support
@@ -1,5 +1,8 @@
1
1
  name: rails6_users_app
2
2
  packages:
3
- - path: app
3
+ - path: app/models
4
+ labels: [ mvc.model ]
5
+ - path: app/controllers
6
+ labels: [ mvc.controller ]
4
7
  - gem: sequel
5
8