appmap 0.49.0 → 0.51.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/appmap.gemspec +3 -0
  4. data/exe/appmap-agent-setup +47 -0
  5. data/lib/appmap.rb +74 -7
  6. data/lib/appmap/command/init.rb +42 -0
  7. data/lib/appmap/config.rb +94 -28
  8. data/lib/appmap/handler/rails/template.rb +19 -5
  9. data/lib/appmap/minitest.rb +8 -2
  10. data/lib/appmap/railtie.rb +7 -0
  11. data/lib/appmap/rspec.rb +8 -2
  12. data/lib/appmap/service/guesser.rb +26 -0
  13. data/lib/appmap/trace.rb +4 -2
  14. data/lib/appmap/util.rb +21 -0
  15. data/lib/appmap/version.rb +4 -1
  16. data/spec/abstract_controller_base_spec.rb +57 -18
  17. data/spec/config_spec.rb +21 -0
  18. data/spec/fixtures/rails5_users_app/config/application.rb +0 -8
  19. data/spec/fixtures/rails5_users_app/spec/rails_helper.rb +0 -2
  20. data/spec/fixtures/rails6_users_app/config/application.rb +0 -8
  21. data/spec/fixtures/rails6_users_app/spec/rails_helper.rb +0 -2
  22. data/spec/hook_spec.rb +2 -2
  23. data/spec/record_net_http_spec.rb +1 -1
  24. data/test/cli_test.rb +37 -0
  25. metadata +9 -29
  26. data/lib/appmap/algorithm/prune_class_map.rb +0 -67
  27. data/lib/appmap/algorithm/stats.rb +0 -91
  28. data/lib/appmap/command/record.rb +0 -38
  29. data/lib/appmap/command/stats.rb +0 -14
  30. data/lore/pages/2019-05-21-install-and-record/index.pug +0 -51
  31. data/lore/pages/2019-05-21-install-and-record/install_example_appmap.png +0 -0
  32. data/lore/pages/2019-05-21-install-and-record/metadata.yml +0 -5
  33. data/lore/pages/layout.pug +0 -66
  34. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.css +0 -1912
  35. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.css.map +0 -1
  36. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.min.css +0 -7
  37. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.min.css.map +0 -1
  38. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.css +0 -331
  39. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.css.map +0 -1
  40. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.min.css +0 -8
  41. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.min.css.map +0 -1
  42. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.css +0 -9030
  43. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.css.map +0 -1
  44. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.min.css +0 -7
  45. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.min.css.map +0 -1
  46. data/lore/public/stylesheets/style.css +0 -8
  47. data/package-lock.json +0 -1064
  48. data/package.json +0 -24
  49. data/spec/fixtures/rails5_users_app/config/initializers/record_button.rb +0 -3
  50. data/spec/fixtures/rails6_users_app/config/initializers/record_button.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '02814a0f9d0927d19c2d341a22d8f8ec66b83bc022833a09a7777ce81ec4edfc'
4
- data.tar.gz: 11b2786d88e360fc78e046675ae799e7106625dd02b4a3070c3835bfe8feea05
3
+ metadata.gz: 5de4c6db2c2be91ba2d0c844ef4fbed14ea1b65b10abde9ef99ccbe50cd96639
4
+ data.tar.gz: 7b51407f7b49e3aa492b85fa439a55f593e0a2b772796bba0ccbd18bb4188149
5
5
  SHA512:
6
- metadata.gz: c852ae464d52f9f29ef9222fa1627f99b4f5baf1907b1cd54cbfb969ff2323cb19e0d5b53bad1356518ec21a07243ea71237ad831a62ffe825a2601cca1a5cf7
7
- data.tar.gz: 49c5ba0d0c20d8bee6e4c768544ab6abe0f0f0d71a1a59bc04fcf867e93bc743eda4cc94f7505c0a1194c07e95e2b8e7f875a42b16aa3da2a21068a61ba42a10
6
+ metadata.gz: 13662be867000e4239ed4f8587d0108d0af04b63d7727d51c3f9fa16b7284e233a590ffad301375d97d6155289c17c267eb0b8003899b8489f5afa11fd296d70
7
+ data.tar.gz: 228adedfd313b54d6b6b4f03c143d94366c8815d0daac8793d70f2e8da909712504f371f5c13f43a22d647828e4d8bb83f1dfb5c023149f8380816b8df0a8819
data/CHANGELOG.md CHANGED
@@ -1,3 +1,48 @@
1
+ ## [0.51.3](https://github.com/applandinc/appmap-ruby/compare/v0.51.2...v0.51.3) (2021-06-22)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Remove outdate lore, command, and algorithm code ([d899989](https://github.com/applandinc/appmap-ruby/commit/d8999896c611c16f51a092f5f7afb3d7203d7e72))
7
+
8
+ ## [0.51.2](https://github.com/applandinc/appmap-ruby/compare/v0.51.1...v0.51.2) (2021-06-22)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Be less verbose when logging config messages ([fba2fd0](https://github.com/applandinc/appmap-ruby/commit/fba2fd01dbb7b1830194b49285654d6657d1c786))
14
+ * Method objects must support eql? and hash to ensure they are unique in a Set ([f4d5b11](https://github.com/applandinc/appmap-ruby/commit/f4d5b11db90aa50bdd1f768e039927833e83c30f))
15
+ * Require rails, then appmap/railtie ([07967a1](https://github.com/applandinc/appmap-ruby/commit/07967a14609891544a7dd874c648b7ef5a505f21))
16
+ * Use a hybrid strategy to auto-requiring appmap modules ([6fb09b8](https://github.com/applandinc/appmap-ruby/commit/6fb09b8c0bd55b1e29967d459ce1e2bd5b1ba9fe))
17
+
18
+ ## [0.51.1](https://github.com/applandinc/appmap-ruby/compare/v0.51.0...v0.51.1) (2021-06-21)
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * Add missing require 'yaml' ([1187a02](https://github.com/applandinc/appmap-ruby/commit/1187a023243caaab8cd48de5cbbddefa361636ad))
24
+
25
+ # [0.51.0](https://github.com/applandinc/appmap-ruby/compare/v0.50.0...v0.51.0) (2021-06-21)
26
+
27
+
28
+ ### Features
29
+
30
+ * Provide default appmap.yml settings ([7fa8159](https://github.com/applandinc/appmap-ruby/commit/7fa8159b5020e35f13379017b44906d671e62e64))
31
+
32
+ # [0.50.0](https://github.com/applandinc/appmap-ruby/compare/v0.49.0...v0.50.0) (2021-06-17)
33
+
34
+
35
+ ### Bug Fixes
36
+
37
+ * Remove appmap configuration in test cases which now occurs automatically ([7391c4c](https://github.com/applandinc/appmap-ruby/commit/7391c4c36ed80f98a6b82ccd43f05de488e7cd2f))
38
+
39
+
40
+ ### Features
41
+
42
+ * Direct minitest and rspec startup messages to the Rails log, when available ([15f6444](https://github.com/applandinc/appmap-ruby/commit/15f6444b0fad3ce7d9e91273b6a1116e470c2a89))
43
+ * Enroll railtie, rspec, and minitest helpers automatically ([1709374](https://github.com/applandinc/appmap-ruby/commit/1709374ee7b5183482c55cf4c7386266fa517262))
44
+ * railtie enrolls the app in remote recording ([3a1f8aa](https://github.com/applandinc/appmap-ruby/commit/3a1f8aac1d83c4df04b5da55ed33d418235e348b))
45
+
1
46
  # [0.49.0](https://github.com/applandinc/appmap-ruby/compare/v0.48.2...v0.49.0) (2021-06-16)
2
47
 
3
48
 
data/appmap.gemspec CHANGED
@@ -22,6 +22,9 @@ Gem::Specification.new do |spec|
22
22
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
23
  spec.files = `git ls-files --no-deleted`.split("
24
24
  ")
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+
25
28
  spec.extensions << "ext/appmap/extconf.rb"
26
29
 
27
30
  spec.require_paths = ['lib']
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'gli'
5
+
6
+ require 'appmap'
7
+
8
+ class App
9
+ extend GLI::App
10
+
11
+ program_desc 'CLI tool to be used by IDEs for AppMap setup and configuration'
12
+
13
+ version AppMap::VERSION
14
+
15
+ subcommand_option_handling :normal
16
+ arguments :strict
17
+ preserve_argv true
18
+
19
+ desc 'AppMap configuration file name'
20
+ default_value ENV['APPMAP_CONFIG'] || AppMap::DEFAULT_CONFIG_FILE_PATH
21
+ arg_name 'filename'
22
+ flag %i[c config]
23
+
24
+ desc 'Creates base configuration file for the current project'
25
+ command :init do |c|
26
+ c.action do
27
+ require 'appmap/command/init'
28
+ AppMap::Command::Init.new(@config_file).perform
29
+ end
30
+ end
31
+
32
+ pre do |global, _, _, _|
33
+ @config_file = global[:config]
34
+ @config = interpret_config_option(@config_file) if File.exist?(@config_file)
35
+ true
36
+ end
37
+
38
+ class << self
39
+ protected
40
+
41
+ def interpret_config_option(fname)
42
+ AppMap::Config.load_from_file fname
43
+ end
44
+ end
45
+ end
46
+
47
+ exit App.run(ARGV)
data/lib/appmap.rb CHANGED
@@ -27,7 +27,7 @@ module AppMap
27
27
  # Gets the configuration. If there is no configuration, the default
28
28
  # configuration is initialized.
29
29
  def configuration
30
- @configuration ||= initialize
30
+ @configuration ||= initialize_configuration
31
31
  end
32
32
 
33
33
  # Sets the configuration. This is only expected to happen once per
@@ -38,19 +38,34 @@ module AppMap
38
38
  @configuration = config
39
39
  end
40
40
 
41
- # Configures AppMap for recording. Default behavior is to configure from "appmap.yml".
41
+ def default_config_file_path
42
+ ENV['APPMAP_CONFIG_FILE'] || 'appmap.yml'
43
+ end
44
+
45
+ # Configures AppMap for recording. Default behavior is to configure from
46
+ # APPMAP_CONFIG_FILE, or 'appmap.yml'. If no config file is available, a
47
+ # configuration will be automatically generated and used - and the user is prompted
48
+ # to create the config file.
49
+ #
42
50
  # This method also activates the code hooks which record function calls as trace events.
43
51
  # Call this function before the program code is loaded by the Ruby VM, otherwise
44
52
  # the load events won't be seen and the hooks won't activate.
45
- def initialize(config_file_path = 'appmap.yml')
46
- raise "AppMap configuration file #{config_file_path} does not exist" unless ::File.exists?(config_file_path)
47
- warn "Configuring AppMap from path #{config_file_path}"
53
+ def initialize_configuration(config_file_path = default_config_file_path)
54
+ startup_message "Configuring AppMap from path #{config_file_path}"
48
55
  Config.load_from_file(config_file_path).tap do |configuration|
49
56
  self.configuration = configuration
50
57
  Hook.new(configuration).enable
51
58
  end
52
59
  end
53
60
 
61
+ def info(msg)
62
+ if defined?(::Rails) && defined?(::Rails.logger)
63
+ ::Rails.logger.info msg
64
+ else
65
+ warn msg
66
+ end
67
+ end
68
+
54
69
  # Used to start tracing, stop tracing, and record events.
55
70
  def tracing
56
71
  @tracing ||= Trace::Tracing.new
@@ -94,8 +109,60 @@ module AppMap
94
109
  @metadata ||= Metadata.detect.freeze
95
110
  @metadata.deep_dup
96
111
  end
112
+
113
+ def startup_message(msg)
114
+ if defined?(::Rails) && defined?(::Rails.logger) && ::Rails.logger
115
+ ::Rails.logger.debug msg
116
+ elsif ENV['DEBUG'] == 'true'
117
+ warn msg
118
+ end
119
+ end
97
120
  end
98
121
  end
99
122
 
100
- require 'appmap/railtie' if defined?(::Rails::Railtie)
101
- AppMap.initialize if ENV['APPMAP'] == 'true'
123
+ lambda do
124
+ Initializer = Struct.new(:class_name, :module_name, :gem_module_name)
125
+
126
+ INITIALIZERS = {
127
+ 'Rails::Railtie' => Initializer.new('AppMap::Railtie', 'appmap/railtie', 'railtie'),
128
+ 'RSpec' => Initializer.new('AppMap::RSpec', 'appmap/rspec', 'rspec-core'),
129
+ 'Minitest::Unit::TestCase' => Initializer.new('AppMap::Minitest', 'appmap/minitest', 'minitest')
130
+ }
131
+
132
+ TracePoint.new(:class) do |tp|
133
+ cls_name = tp.self.name
134
+ initializers = INITIALIZERS.delete(cls_name)
135
+ if initializers
136
+ initializers = [ initializers ] unless initializers.is_a?(Array)
137
+ next if Object.const_defined?(initializers.first.class_name)
138
+
139
+ gem_module_name = initializers.first.gem_module_name
140
+
141
+ AppMap.startup_message AppMap::Util.color(<<~LOAD_MSG, :magenta)
142
+ When 'appmap' was loaded, '#{gem_module_name}' had not been loaded yet. Now '#{gem_module_name}' has
143
+ just been loaded, so the following AppMap modules will be automatically required:
144
+
145
+ #{initializers.map(&:module_name).join("\n")}
146
+
147
+ To suppress this message, ensure '#{gem_module_name}' appears before 'appmap' in your Gemfile.
148
+ LOAD_MSG
149
+ initializers.each do |init|
150
+ require init.module_name
151
+ end
152
+ end
153
+ end.enable
154
+
155
+ if defined?(::Rails::Railtie)
156
+ require 'appmap/railtie'
157
+ end
158
+
159
+ if defined?(::RSpec)
160
+ require 'appmap/rspec'
161
+ end
162
+
163
+ if defined?(::Minitest)
164
+ require 'appmap/minitest'
165
+ end
166
+ end.call
167
+
168
+ AppMap.initialize_configuration if ENV['APPMAP'] == 'true'
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'appmap/service/guesser'
5
+ require 'appmap/util'
6
+
7
+ module AppMap
8
+ module Command
9
+ InitStruct = Struct.new(:config_file)
10
+
11
+ class Init < InitStruct
12
+ def perform
13
+ if File.exist?(config_file)
14
+ puts AppMap::Util.color(%(The AppMap config file #{config_file} already exists.), :magenta)
15
+ return
16
+ end
17
+
18
+ ensure_directory_exists
19
+
20
+ config = {
21
+ 'name' => Service::Guesser.guess_name,
22
+ 'packages' => Service::Guesser.guess_paths.map { |path| { 'path' => path } }
23
+ }
24
+ content = YAML.dump(config).gsub("---\n", '')
25
+
26
+ File.write(config_file, content)
27
+ puts AppMap::Util.color(
28
+ %(The following AppMap config file #{config_file} has been created:),
29
+ :green
30
+ )
31
+ puts content
32
+ end
33
+
34
+ private
35
+
36
+ def ensure_directory_exists
37
+ dirname = File.dirname(config_file)
38
+ FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
39
+ end
40
+ end
41
+ end
42
+ end
data/lib/appmap/config.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'yaml'
3
4
  require 'appmap/handler/net_http'
4
5
  require 'appmap/handler/rails/template'
6
+ require 'appmap/service/guesser'
5
7
 
6
8
  module AppMap
7
9
  class Config
@@ -223,10 +225,14 @@ module AppMap
223
225
  'JSON::Ext::Generator::State' => TargetMethods.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.generate])),
224
226
  }.freeze
225
227
 
226
- attr_reader :name, :packages, :exclude, :hooked_methods, :builtin_hooks
228
+ attr_reader :name, :appmap_dir, :packages, :exclude, :hooked_methods, :builtin_hooks
227
229
 
228
- def initialize(name, packages, exclude: [], functions: [])
230
+ def initialize(name,
231
+ packages: [],
232
+ exclude: [],
233
+ functions: [])
229
234
  @name = name
235
+ @appmap_dir = AppMap::DEFAULT_APPMAP_DIR
230
236
  @packages = packages
231
237
  @hook_paths = Set.new(packages.map(&:path))
232
238
  @exclude = exclude
@@ -253,38 +259,98 @@ module AppMap
253
259
  class << self
254
260
  # Loads configuration data from a file, specified by the file name.
255
261
  def load_from_file(config_file_name)
256
- require 'yaml'
257
- load YAML.safe_load(::File.read(config_file_name))
262
+ logo = lambda do
263
+ Util.color(<<~LOGO, :magenta)
264
+ ___ __ ___
265
+ / _ | ___ ___ / |/ /__ ____
266
+ / __ |/ _ \\/ _ \\/ /|_/ / _ `/ _ \\
267
+ /_/ |_/ .__/ .__/_/ /_/\\_,_/ .__/
268
+ /_/ /_/ /_/
269
+ LOGO
270
+ end
271
+
272
+ config_present = true if File.exists?(config_file_name)
273
+
274
+ config_data = if config_present
275
+ YAML.safe_load(::File.read(config_file_name))
276
+ else
277
+ warn logo.()
278
+ warn ''
279
+ warn Util.color(%Q|NOTICE: The AppMap config file #{config_file_name} was not found!|, :magenta, bold: true)
280
+ warn ''
281
+ warn Util.color(<<~MISSING_FILE_MSG, :magenta)
282
+ AppMap uses this file to customize its behavior. For example, you can use
283
+ the 'packages' setting to indicate which local file paths and dependency
284
+ gems you want to include in the AppMap. Since you haven't provided specific
285
+ settings, the appmap gem will try and guess some reasonable defaults.
286
+ To suppress this message, create the file:
287
+
288
+ #{Pathname.new(config_file_name).expand_path}
289
+
290
+ Here are the default settings that will be used in the meantime. You can
291
+ copy and paste this example to start your appmap.yml.
292
+ MISSING_FILE_MSG
293
+ {}
294
+ end
295
+ load(config_data).tap do |config|
296
+ config_yaml = {
297
+ 'name' => config.name,
298
+ 'packages' => config.packages.select{|p| p.path}.map do |pkg|
299
+ { 'path' => pkg.path }
300
+ end,
301
+ 'exclude' => []
302
+ }.compact
303
+ unless config_present
304
+ warn Util.color(YAML.dump(config_yaml), :magenta)
305
+ warn logo.()
306
+ end
307
+ end
258
308
  end
259
309
 
260
310
  # Loads configuration from a Hash.
261
311
  def load(config_data)
262
- functions = (config_data['functions'] || []).map do |function_data|
263
- package = function_data['package']
264
- cls = function_data['class']
265
- functions = function_data['function'] || function_data['functions']
266
- raise 'AppMap class configuration should specify package, class and function(s)' unless package && cls && functions
267
- functions = Array(functions).map(&:to_sym)
268
- labels = function_data['label'] || function_data['labels']
269
- labels = Array(labels).map(&:to_s) if labels
270
- Function.new(package, cls, labels, functions)
312
+ name = config_data['name'] || Service::Guesser.guess_name
313
+ config_params = {
314
+ exclude: config_data['exclude']
315
+ }.compact
316
+
317
+ if config_data['functions']
318
+ config_params[:functions] = config_data['functions'].map do |function_data|
319
+ package = function_data['package']
320
+ cls = function_data['class']
321
+ functions = function_data['function'] || function_data['functions']
322
+ raise %q(AppMap config 'function' element should specify 'package', 'class' and 'function' or 'functions') unless package && cls && functions
323
+
324
+ functions = Array(functions).map(&:to_sym)
325
+ labels = function_data['label'] || function_data['labels']
326
+ labels = Array(labels).map(&:to_s) if labels
327
+ Function.new(package, cls, labels, functions)
328
+ end
271
329
  end
272
- packages = (config_data['packages'] || []).map do |package|
273
- gem = package['gem']
274
- path = package['path']
275
- raise 'AppMap package configuration should specify gem or path, not both' if gem && path
276
-
277
- if gem
278
- shallow = package['shallow']
279
- # shallow is true by default for gems
280
- shallow = true if shallow.nil?
281
- Package.build_from_gem(gem, exclude: package['exclude'] || [], shallow: shallow)
330
+
331
+ config_params[:packages] = \
332
+ if config_data['packages']
333
+ config_data['packages'].map do |package|
334
+ gem = package['gem']
335
+ path = package['path']
336
+ raise %q(AppMap config 'package' element should specify 'gem' or 'path', not both) if gem && path
337
+
338
+ if gem
339
+ shallow = package['shallow']
340
+ # shallow is true by default for gems
341
+ shallow = true if shallow.nil?
342
+ Package.build_from_gem(gem, exclude: package['exclude'] || [], shallow: shallow)
343
+ else
344
+ Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow'])
345
+ end
346
+ end.compact
282
347
  else
283
- Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow'])
348
+ Array(Service::Guesser.guess_paths).map do |path|
349
+ Package.build_from_path(path)
350
+ end
284
351
  end
285
- end.compact
286
- exclude = config_data['exclude'] || []
287
- Config.new config_data['name'], packages, exclude: exclude, functions: functions
352
+
353
+ Config.new name, config_params
288
354
  end
289
355
  end
290
356
 
@@ -294,7 +360,7 @@ module AppMap
294
360
  packages: packages.map(&:to_h),
295
361
  functions: @functions.map(&:to_h),
296
362
  exclude: exclude
297
- }
363
+ }.compact
298
364
  end
299
365
 
300
366
  # Determines if methods defined in a file path should possibly be hooked.
@@ -14,16 +14,30 @@ module AppMap
14
14
  # The class name is generated from the template path. The package name is
15
15
  # 'app/views', and the method name is 'render'. The source location of the method
16
16
  # is, of course, the path to the view template.
17
- TemplateMethod = Struct.new(:path) do
18
- private_instance_methods :path
17
+ class TemplateMethod
19
18
  attr_reader :class_name
20
-
19
+
20
+ attr_reader :path
21
+ private_instance_methods :path
22
+
21
23
  def initialize(path)
22
- super
24
+ @path = path
23
25
 
24
26
  @class_name = path.parameterize.underscore
25
27
  end
26
-
28
+
29
+ def id
30
+ [ package, path, name ]
31
+ end
32
+
33
+ def hash
34
+ id.hash
35
+ end
36
+
37
+ def eql?(other)
38
+ other.is_a?(TemplateMethod) && id.eql?(other.id)
39
+ end
40
+
27
41
  def package
28
42
  'app/views'
29
43
  end