appmap 0.77.1 → 0.77.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -7
  3. data/CHANGELOG.md +21 -0
  4. data/Rakefile +29 -3
  5. data/lib/appmap/config.rb +9 -1
  6. data/lib/appmap/handler/function.rb +8 -8
  7. data/lib/appmap/handler/net_http.rb +19 -18
  8. data/lib/appmap/handler/rails/request_handler.rb +3 -4
  9. data/lib/appmap/handler/rails/template.rb +68 -62
  10. data/lib/appmap/hook/method/ruby2.rb +56 -0
  11. data/lib/appmap/hook/method/ruby3.rb +56 -0
  12. data/lib/appmap/hook/method.rb +42 -98
  13. data/lib/appmap/hook.rb +2 -2
  14. data/lib/appmap/swagger/rake_tasks.rb +6 -1
  15. data/lib/appmap/version.rb +1 -1
  16. data/spec/fixtures/rails7_users_app/.dockerignore +1 -0
  17. data/spec/fixtures/rails7_users_app/.gitattributes +7 -0
  18. data/spec/fixtures/rails7_users_app/.gitignore +31 -0
  19. data/spec/fixtures/rails7_users_app/.rspec +1 -0
  20. data/spec/fixtures/rails7_users_app/.ruby-version +1 -0
  21. data/spec/fixtures/rails7_users_app/Dockerfile +30 -0
  22. data/spec/fixtures/rails7_users_app/Dockerfile.pg +3 -0
  23. data/spec/fixtures/rails7_users_app/Gemfile +99 -0
  24. data/spec/fixtures/rails7_users_app/README.md +24 -0
  25. data/spec/fixtures/rails7_users_app/Rakefile +6 -0
  26. data/spec/fixtures/rails7_users_app/app/assets/config/manifest.js +4 -0
  27. data/spec/fixtures/rails7_users_app/app/assets/images/.keep +0 -0
  28. data/spec/fixtures/rails7_users_app/app/assets/stylesheets/application.css +15 -0
  29. data/spec/fixtures/rails7_users_app/app/channels/application_cable/channel.rb +4 -0
  30. data/spec/fixtures/rails7_users_app/app/channels/application_cable/connection.rb +4 -0
  31. data/spec/fixtures/rails7_users_app/app/controllers/application_controller.rb +2 -0
  32. data/spec/fixtures/rails7_users_app/app/controllers/concerns/.keep +0 -0
  33. data/spec/fixtures/rails7_users_app/app/helpers/application_helper.rb +2 -0
  34. data/spec/fixtures/rails7_users_app/app/javascript/application.js +3 -0
  35. data/spec/fixtures/rails7_users_app/app/javascript/controllers/application.js +9 -0
  36. data/spec/fixtures/rails7_users_app/app/javascript/controllers/hello_controller.js +7 -0
  37. data/spec/fixtures/rails7_users_app/app/javascript/controllers/index.js +11 -0
  38. data/spec/fixtures/rails7_users_app/app/jobs/application_job.rb +7 -0
  39. data/spec/fixtures/rails7_users_app/app/mailers/application_mailer.rb +4 -0
  40. data/spec/fixtures/rails7_users_app/app/models/application_record.rb +3 -0
  41. data/spec/fixtures/rails7_users_app/app/models/concerns/.keep +0 -0
  42. data/spec/fixtures/rails7_users_app/app/models/instance.rb +7 -0
  43. data/spec/fixtures/rails7_users_app/app/models/instructor.rb +7 -0
  44. data/spec/fixtures/rails7_users_app/app/views/layouts/application.html.erb +16 -0
  45. data/spec/fixtures/rails7_users_app/app/views/layouts/mailer.html.erb +13 -0
  46. data/spec/fixtures/rails7_users_app/app/views/layouts/mailer.text.erb +1 -0
  47. data/spec/fixtures/rails7_users_app/appmap.yml +3 -0
  48. data/spec/fixtures/rails7_users_app/bin/bundle +114 -0
  49. data/spec/fixtures/rails7_users_app/bin/importmap +4 -0
  50. data/spec/fixtures/rails7_users_app/bin/rails +4 -0
  51. data/spec/fixtures/rails7_users_app/bin/rake +4 -0
  52. data/spec/fixtures/rails7_users_app/bin/setup +33 -0
  53. data/spec/fixtures/rails7_users_app/config/application.rb +22 -0
  54. data/spec/fixtures/rails7_users_app/config/boot.rb +4 -0
  55. data/spec/fixtures/rails7_users_app/config/cable.yml +10 -0
  56. data/spec/fixtures/rails7_users_app/config/credentials.yml.enc +1 -0
  57. data/spec/fixtures/rails7_users_app/config/database.yml +86 -0
  58. data/spec/fixtures/rails7_users_app/config/environment.rb +5 -0
  59. data/spec/fixtures/rails7_users_app/config/environments/development.rb +70 -0
  60. data/spec/fixtures/rails7_users_app/config/environments/production.rb +93 -0
  61. data/spec/fixtures/rails7_users_app/config/environments/test.rb +60 -0
  62. data/spec/fixtures/rails7_users_app/config/importmap.rb +7 -0
  63. data/spec/fixtures/rails7_users_app/config/initializers/assets.rb +12 -0
  64. data/spec/fixtures/rails7_users_app/config/initializers/content_security_policy.rb +26 -0
  65. data/spec/fixtures/rails7_users_app/config/initializers/filter_parameter_logging.rb +8 -0
  66. data/spec/fixtures/rails7_users_app/config/initializers/inflections.rb +16 -0
  67. data/spec/fixtures/rails7_users_app/config/initializers/permissions_policy.rb +11 -0
  68. data/spec/fixtures/rails7_users_app/config/locales/en.yml +33 -0
  69. data/spec/fixtures/rails7_users_app/config/puma.rb +43 -0
  70. data/spec/fixtures/rails7_users_app/config/routes.rb +6 -0
  71. data/spec/fixtures/rails7_users_app/config/storage.yml +34 -0
  72. data/spec/fixtures/rails7_users_app/config.ru +6 -0
  73. data/spec/fixtures/rails7_users_app/create_app +31 -0
  74. data/spec/fixtures/rails7_users_app/db/migrate/20220328093141_create_instances.rb +8 -0
  75. data/spec/fixtures/rails7_users_app/db/migrate/20220328093154_create_instructors.rb +8 -0
  76. data/spec/fixtures/rails7_users_app/db/schema.rb +27 -0
  77. data/spec/fixtures/rails7_users_app/db/seeds.rb +7 -0
  78. data/spec/fixtures/rails7_users_app/docker-compose.yml +31 -0
  79. data/spec/fixtures/rails7_users_app/lib/assets/.keep +0 -0
  80. data/spec/fixtures/rails7_users_app/lib/tasks/.keep +0 -0
  81. data/spec/fixtures/rails7_users_app/log/.keep +0 -0
  82. data/spec/fixtures/rails7_users_app/public/404.html +67 -0
  83. data/spec/fixtures/rails7_users_app/public/422.html +67 -0
  84. data/spec/fixtures/rails7_users_app/public/500.html +66 -0
  85. data/spec/fixtures/rails7_users_app/public/apple-touch-icon-precomposed.png +0 -0
  86. data/spec/fixtures/rails7_users_app/public/apple-touch-icon.png +0 -0
  87. data/spec/fixtures/rails7_users_app/public/favicon.ico +0 -0
  88. data/spec/fixtures/rails7_users_app/public/robots.txt +1 -0
  89. data/spec/fixtures/rails7_users_app/storage/.keep +0 -0
  90. data/spec/fixtures/rails7_users_app/test/application_system_test_case.rb +5 -0
  91. data/spec/fixtures/rails7_users_app/test/channels/application_cable/connection_test.rb +11 -0
  92. data/spec/fixtures/rails7_users_app/test/controllers/.keep +0 -0
  93. data/spec/fixtures/rails7_users_app/test/fixtures/files/.keep +0 -0
  94. data/spec/fixtures/rails7_users_app/test/fixtures/instances.yml +11 -0
  95. data/spec/fixtures/rails7_users_app/test/fixtures/instructors.yml +11 -0
  96. data/spec/fixtures/rails7_users_app/test/helpers/.keep +0 -0
  97. data/spec/fixtures/rails7_users_app/test/integration/.keep +0 -0
  98. data/spec/fixtures/rails7_users_app/test/mailers/.keep +0 -0
  99. data/spec/fixtures/rails7_users_app/test/models/.keep +0 -0
  100. data/spec/fixtures/rails7_users_app/test/models/instance_test.rb +6 -0
  101. data/spec/fixtures/rails7_users_app/test/models/instructor_test.rb +7 -0
  102. data/spec/fixtures/rails7_users_app/test/system/.keep +0 -0
  103. data/spec/fixtures/rails7_users_app/test/test_helper.rb +13 -0
  104. data/spec/hook_spec.rb +2 -2
  105. data/spec/rails_recording_spec.rb +6 -4
  106. data/spec/rails_spec_helper.rb +7 -0
  107. data/spec/rails_test_spec.rb +45 -0
  108. data/spec/swagger/swagger_spec.rb +2 -2
  109. metadata +93 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b186892e87c9138a6bd540c82dd899e5d9b4671748ba7219c7d685175509d625
4
- data.tar.gz: 0f917bd67d06fab65b568b55c68405206fdf942cd72854a6cff845e021970d80
3
+ metadata.gz: 4a3775abc9bff6d949fa377e2da5121414500735c37d9c5b9d6f03a7a2ae31a7
4
+ data.tar.gz: 7254e2e5c3f25f431cdb25e8d0512d55a155afd8b64647b255b3a8223b663f3f
5
5
  SHA512:
6
- metadata.gz: afb2787068a4b7dca1c7b5226f82864c1ce8e5d32c88db7caf554f7c9d6cfbcd1d817e37f1ac691a573d36da4da53ea057c4c3d39c0e985e0969cfed9c63f7ae
7
- data.tar.gz: 3b7f117ebe7f4b732cc81bd412bea46dd51b356db78a228561b3b9cf5c581b05d91e081b6cd699ad31d33e82fbd3d31548ccb91478c0fe7648357c947d6ef18a
6
+ metadata.gz: cf54fd237e40750eff85fb4723651a747ba6e69214ea04acae1a95964a09e4a800ea1de31dbc4f7dec7d26c585ef7fb9167a3e97eb36e9b0c14003240696d6d6
7
+ data.tar.gz: 81f413c5e21379b145a99f9882db9faea3d27e75ef56df7a8a928ce0b74f141c26025300cf0449fbf00128eb4991d99c94316f4fb522c5bd25042fb6ad2cfff3
data/.travis.yml CHANGED
@@ -33,13 +33,6 @@ before_install:
33
33
  && nvm use --lts \
34
34
  && npm i -g yarn
35
35
 
36
- jobs:
37
- include:
38
- - stage: test
39
- env:
40
- # GEM_ALTERNATIVE_NAME only needed for deployment
41
- - GEM_ALTERNATIVE_NAME=''
42
-
43
36
  before_deploy:
44
37
  - |
45
38
  npm i -g \
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## [0.77.4](https://github.com/applandinc/appmap-ruby/compare/v0.77.3...v0.77.4) (2022-04-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Update Rails request handler to the new hook architecture ([595b39a](https://github.com/applandinc/appmap-ruby/commit/595b39abb030c1dcf85c83e4717c25d4c5177d4d))
7
+
8
+ ## [0.77.3](https://github.com/applandinc/appmap-ruby/compare/v0.77.2...v0.77.3) (2022-03-29)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Rescue exceptions when calling Class#to_s ([f59f2f6](https://github.com/applandinc/appmap-ruby/commit/f59f2f6b39664ff050486c88ff1b859ca0db48d8))
14
+
15
+ ## [0.77.2](https://github.com/applandinc/appmap-ruby/compare/v0.77.1...v0.77.2) (2022-03-25)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * Pass the proper openapi-template arg ([05cbfde](https://github.com/applandinc/appmap-ruby/commit/05cbfdebdf80e3df2105a943ad892d5a7df614d7))
21
+
1
22
  ## [0.77.1](https://github.com/applandinc/appmap-ruby/compare/v0.77.0...v0.77.1) (2022-03-24)
2
23
 
3
24
 
data/Rakefile CHANGED
@@ -2,6 +2,21 @@ $: << File.join(__dir__, 'lib')
2
2
  require 'appmap/version'
3
3
  GEM_VERSION = AppMap::VERSION
4
4
 
5
+ # Make sure the local version is not behind the one on
6
+ # rubygems.org (it's ok if they're the same).
7
+ #
8
+ # If it is behind, the fixture images won't get updated with the gem
9
+ # built from the local source, so you'll wind up testing the rubygems
10
+ # version instead.
11
+ unless ENV['SKIP_VERSION_CHECK']
12
+ require 'json'
13
+ require 'net/http'
14
+ rubygems_version = JSON.parse(Net::HTTP.get(URI.parse('https://rubygems.org/api/v1/gems/appmap.json')))['version']
15
+ if Gem::Version.new(GEM_VERSION) < Gem::Version.new(rubygems_version)
16
+ raise "#{GEM_VERSION} < #{rubygems_version}. Rebase to avoid build issues."
17
+ end
18
+ end
19
+
5
20
  require 'rake/testtask'
6
21
  require 'rdoc/task'
7
22
 
@@ -24,7 +39,7 @@ RUBY_VERSIONS=%w[2.6 2.7 3.0 3.1].select do |version|
24
39
 
25
40
  false
26
41
  end
27
- FIXTURE_APPS=%w[rack_users_app rails6_users_app rails5_users_app]
42
+ FIXTURE_APPS=[:rack_users_app, :rails6_users_app, :rails5_users_app, :rails7_users_app => {:ruby_version => '>= 2.7'}]
28
43
 
29
44
  def run_cmd(*cmd)
30
45
  $stderr.puts "Running: #{cmd}"
@@ -83,7 +98,17 @@ namespace :build do
83
98
  RUBY_VERSIONS.each do |ruby_version|
84
99
  namespace ruby_version do
85
100
  desc "build:fixtures:#{ruby_version}"
86
- FIXTURE_APPS.each do |app|
101
+ FIXTURE_APPS.each do |app_spec|
102
+ app = if app_spec.instance_of?(Hash)
103
+ app_spec = app_spec.flatten
104
+ version_rqt = Gem::Requirement.create(app_spec[1][:ruby_version])
105
+ next unless version_rqt =~ (Gem::Version.new(ruby_version))
106
+ app = app_spec[0]
107
+ else
108
+ app = app_spec
109
+ end.to_s
110
+
111
+
87
112
  desc app
88
113
  task app => ["base:#{ruby_version}"] do
89
114
  build_app_image(app, ruby_version)
@@ -109,12 +134,13 @@ def run_specs(ruby_version, task_args)
109
134
  # description), because it's not intended to be invoked directly
110
135
  RSpec::Core::RakeTask.new("rspec_#{ruby_version}", [:specs]) do |task, args|
111
136
  task.exclude_pattern = 'spec/fixtures/**/*_spec.rb'
137
+ task.rspec_opts = '-f doc'
112
138
  if args.count > 0
113
139
  # There doesn't appear to be a value for +pattern+ that will
114
140
  # cause it to be ignored. Setting it to '' or +nil+ causes an
115
141
  # empty argument to get passed to rspec, which confuses it.
116
142
  task.pattern = 'never match this'
117
- task.rspec_opts = args.to_a.join(' ')
143
+ task.rspec_opts += " " + args.to_a.join(' ')
118
144
  end
119
145
  end
120
146
 
data/lib/appmap/config.rb CHANGED
@@ -450,7 +450,15 @@ module AppMap
450
450
 
451
451
  # Hook a method which is specified by class and method name.
452
452
  def package_for_code_object
453
- class_name = cls.to_s.index('#<Class:') == 0 ? cls.to_s['#<Class:'.length...-1] : cls.name
453
+ class_name = begin
454
+ cls.to_s.index('#<Class:') == 0 ? cls.to_s['#<Class:'.length...-1] : cls.name
455
+ rescue
456
+ # Calling #to_s on some Rails classes
457
+ # (e.g. those generated to represent
458
+ # associations) will raise an exception. Fall
459
+ # back to using the class name.
460
+ cls.name
461
+ end
454
462
  Array(config.gem_hooks[class_name])
455
463
  .find { |hook| hook.include_method?(method.name) }
456
464
  &.package
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'appmap/event'
4
+ require 'appmap/hook/method'
4
5
 
5
6
  module AppMap
6
7
  module Handler
7
- module Function
8
- class << self
9
- def handle_call(defined_class, hook_method, receiver, args)
10
- AppMap::Event::MethodCall.build_from_invocation(defined_class, hook_method, receiver, args)
11
- end
8
+ # Base handler class, will emit method call and return events.
9
+ class Function < Hook::Method
10
+ def handle_call(receiver, args)
11
+ AppMap::Event::MethodCall.build_from_invocation(defined_class, hook_method, receiver, args)
12
+ end
12
13
 
13
- def handle_return(call_event_id, elapsed, return_value, exception)
14
- AppMap::Event::MethodReturn.build_from_invocation(call_event_id, return_value, exception, elapsed: elapsed)
15
- end
14
+ def handle_return(call_event_id, elapsed, return_value, exception)
15
+ AppMap::Event::MethodReturn.build_from_invocation(call_event_id, return_value, exception, elapsed: elapsed)
16
16
  end
17
17
  end
18
18
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'appmap/event'
4
+ require 'appmap/hook/method'
4
5
  require 'appmap/util'
5
6
  require 'rack'
6
7
 
@@ -81,29 +82,29 @@ module AppMap
81
82
  end
82
83
  end
83
84
 
84
- class NetHTTP
85
- class << self
86
- def copy_headers(obj)
87
- {}.tap do |headers|
88
- obj.each_header do |key, value|
89
- key = key.split('-').map(&:capitalize).join('-')
90
- headers[key] = value
91
- end
85
+ # Handler class for HTTP requests.
86
+ # Emits HTTP request events instead of method calls.
87
+ class NetHTTP < Hook::Method
88
+ def self.copy_headers(obj)
89
+ {}.tap do |headers|
90
+ obj.each_header do |key, value|
91
+ key = key.split('-').map(&:capitalize).join('-')
92
+ headers[key] = value
92
93
  end
93
94
  end
95
+ end
94
96
 
95
- def handle_call(defined_class, hook_method, receiver, args)
96
- # request will call itself again in a start block if it's not already started.
97
- return unless receiver.started?
97
+ def handle_call(receiver, args)
98
+ # request will call itself again in a start block if it's not already started.
99
+ return unless receiver.started?
98
100
 
99
- http = receiver
100
- request = args.first
101
- HTTPClientRequest.new(http, request)
102
- end
101
+ http = receiver
102
+ request = args.first
103
+ HTTPClientRequest.new(http, request)
104
+ end
103
105
 
104
- def handle_return(call_event_id, elapsed, return_value, exception)
105
- HTTPClientResponse.new(return_value, call_event_id, elapsed)
106
- end
106
+ def handle_return(call_event_id, elapsed, return_value, exception)
107
+ HTTPClientResponse.new(return_value, call_event_id, elapsed)
107
108
  end
108
109
  end
109
110
  end
@@ -100,15 +100,14 @@ module AppMap
100
100
 
101
101
  protected
102
102
 
103
- def before_hook(receiver, defined_class, _) # args
103
+ def before_hook(receiver, *)
104
104
  call_event = HTTPServerRequest.new(receiver.request)
105
105
  # http_server_request events are i/o and do not require a package name.
106
106
  AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
107
- [ call_event, TIME_NOW.call ]
107
+ call_event
108
108
  end
109
109
 
110
- def after_hook(receiver, call_event, start_time, _, _) # return_value, exception
111
- elapsed = TIME_NOW.call - start_time
110
+ def after_hook(receiver, call_event, elapsed, *)
112
111
  return_event = HTTPServerResponse.new receiver.response, call_event.id, elapsed
113
112
  AppMap.tracing.record_event return_event
114
113
  end
@@ -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