appmap 0.37.0 → 0.40.0
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.
- checksums.yaml +4 -4
- data/.dockerignore +1 -1
- data/.rubocop.yml +12 -1
- data/.travis.yml +2 -23
- data/CHANGELOG.md +24 -0
- data/CONTRIBUTING.md +22 -0
- data/README.md +79 -50
- data/Rakefile +3 -3
- data/lib/appmap/class_map.rb +25 -8
- data/lib/appmap/config.rb +22 -10
- data/lib/appmap/event.rb +16 -1
- data/lib/appmap/hook.rb +11 -3
- data/lib/appmap/hook/method.rb +18 -12
- data/lib/appmap/rails/request_handler.rb +25 -4
- data/lib/appmap/railtie.rb +1 -5
- data/lib/appmap/trace.rb +18 -7
- data/lib/appmap/version.rb +2 -2
- data/spec/abstract_controller_base_spec.rb +132 -67
- data/spec/fixtures/hook/instance_method.rb +4 -0
- data/spec/fixtures/hook/labels.rb +6 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/.dockerignore +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/.gitignore +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/.rspec +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/.ruby-version +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/Dockerfile +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/Dockerfile.pg +0 -0
- data/spec/fixtures/rails5_users_app/Gemfile +50 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/Rakefile +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/app/controllers/api/users_controller.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/app/controllers/application_controller.rb +0 -0
- data/spec/fixtures/{rails4_users_app/app/assets/images → rails5_users_app/app/controllers/concerns}/.keep +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/app/controllers/health_controller.rb +0 -0
- data/spec/fixtures/rails5_users_app/app/controllers/users_controller.rb +13 -0
- data/spec/fixtures/{rails4_users_app/app/models → rails5_users_app/app/models/activerecord}/user.rb +0 -0
- data/spec/fixtures/{rails4_users_app/app/controllers → rails5_users_app/app/models}/concerns/.keep +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/app/models/sequel/user.rb +0 -0
- data/spec/fixtures/{rails4_users_app → rails5_users_app}/app/views/layouts/application.html.haml +0 -0
- data/spec/fixtures/{rails4_users_app → rails5_users_app}/app/views/users/index.html.haml +0 -0
- data/spec/fixtures/rails5_users_app/appmap.yml +4 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/appmap +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/byebug +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/gli +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/htmldiff +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/ldiff +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/nokogiri +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/rackup +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/rails +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/rake +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/rspec +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/ruby-parse +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/ruby-rewrite +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/sequel +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/setup +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/sprockets +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/thor +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/bin/update +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config.ru +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/application.rb +2 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/boot.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/credentials.yml.enc +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/database.yml +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/environment.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/environments/development.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/environments/production.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/environments/test.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/initializers/application_controller_renderer.rb +0 -0
- data/spec/fixtures/{rails4_users_app → rails5_users_app}/config/initializers/backtrace_silencers.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/initializers/cors.rb +0 -0
- data/spec/fixtures/{rails4_users_app → rails5_users_app}/config/initializers/filter_parameter_logging.rb +0 -0
- data/spec/fixtures/{rails4_users_app → rails5_users_app}/config/initializers/inflections.rb +0 -0
- data/spec/fixtures/{rails4_users_app → rails5_users_app}/config/initializers/mime_types.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/initializers/record_button.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/initializers/wrap_parameters.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/config/locales/en.yml +0 -0
- data/spec/fixtures/{rails4_users_app → rails5_users_app}/config/routes.rb +1 -2
- data/spec/fixtures/{rails_users_app → rails5_users_app}/create_app +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/db/migrate/20190728211408_create_users.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/db/schema.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/docker-compose.yml +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/features/api_users.feature +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/features/support/env.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/features/support/hooks.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/features/support/steps.rb +0 -0
- data/spec/fixtures/{rails4_users_app/app/mailers → rails5_users_app/lib/tasks}/.keep +0 -0
- data/spec/fixtures/{rails4_users_app/app/models → rails5_users_app/log}/.keep +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/public/robots.txt +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/spec/controllers/users_controller_api_spec.rb +1 -1
- data/spec/fixtures/rails5_users_app/spec/controllers/users_controller_spec.rb +27 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/spec/models/user_spec.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/spec/rails_helper.rb +0 -0
- data/spec/fixtures/{rails4_users_app → rails5_users_app}/spec/spec_helper.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails5_users_app}/users_app/.gitignore +0 -0
- data/spec/fixtures/rails6_users_app/.dockerignore +1 -0
- data/spec/fixtures/rails6_users_app/.gitignore +39 -0
- data/spec/fixtures/rails6_users_app/.rspec +1 -0
- data/spec/fixtures/rails6_users_app/.ruby-version +1 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/Dockerfile +3 -4
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/Dockerfile.pg +1 -1
- data/spec/fixtures/{rails_users_app → rails6_users_app}/Gemfile +3 -4
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/Rakefile +1 -1
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/app/controllers/api/users_controller.rb +1 -1
- data/spec/fixtures/rails6_users_app/app/controllers/application_controller.rb +2 -0
- data/spec/fixtures/{rails4_users_app/app/models → rails6_users_app/app/controllers}/concerns/.keep +0 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/app/controllers/health_controller.rb +1 -1
- data/spec/fixtures/rails6_users_app/app/controllers/users_controller.rb +13 -0
- data/spec/fixtures/{rails_users_app → rails6_users_app}/app/models/activerecord/user.rb +0 -0
- data/spec/fixtures/{rails4_users_app/lib/assets → rails6_users_app/app/models/concerns}/.keep +0 -0
- data/spec/fixtures/rails6_users_app/app/models/sequel/user.rb +25 -0
- data/spec/fixtures/{rails_users_app → rails6_users_app}/app/views/layouts/application.html.haml +0 -0
- data/spec/fixtures/{rails_users_app → rails6_users_app}/app/views/users/index.html.haml +0 -0
- data/spec/fixtures/rails6_users_app/appmap.yml +5 -0
- data/spec/fixtures/rails6_users_app/bin/appmap +29 -0
- data/spec/fixtures/rails6_users_app/bin/byebug +29 -0
- data/spec/fixtures/rails6_users_app/bin/gli +29 -0
- data/spec/fixtures/rails6_users_app/bin/htmldiff +29 -0
- data/spec/fixtures/rails6_users_app/bin/ldiff +29 -0
- data/spec/fixtures/rails6_users_app/bin/nokogiri +29 -0
- data/spec/fixtures/rails6_users_app/bin/rackup +29 -0
- data/spec/fixtures/rails6_users_app/bin/rails +4 -0
- data/spec/fixtures/rails6_users_app/bin/rake +29 -0
- data/spec/fixtures/rails6_users_app/bin/rspec +29 -0
- data/spec/fixtures/rails6_users_app/bin/ruby-parse +29 -0
- data/spec/fixtures/rails6_users_app/bin/ruby-rewrite +29 -0
- data/spec/fixtures/rails6_users_app/bin/sequel +29 -0
- data/spec/fixtures/rails6_users_app/bin/setup +25 -0
- data/spec/fixtures/rails6_users_app/bin/sprockets +29 -0
- data/spec/fixtures/rails6_users_app/bin/thor +29 -0
- data/spec/fixtures/rails6_users_app/bin/update +25 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/config.ru +2 -1
- data/spec/fixtures/rails6_users_app/config/application.rb +53 -0
- data/spec/fixtures/rails6_users_app/config/boot.rb +3 -0
- data/spec/fixtures/rails6_users_app/config/credentials.yml.enc +1 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/config/database.yml +2 -2
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/config/environment.rb +1 -1
- data/spec/fixtures/rails6_users_app/config/environments/development.rb +40 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/config/environments/production.rb +19 -30
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/config/environments/test.rb +5 -11
- data/spec/fixtures/rails6_users_app/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/fixtures/{rails_users_app → rails6_users_app}/config/initializers/backtrace_silencers.rb +0 -0
- data/spec/fixtures/rails6_users_app/config/initializers/cors.rb +16 -0
- data/spec/fixtures/{rails_users_app → rails6_users_app}/config/initializers/filter_parameter_logging.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails6_users_app}/config/initializers/inflections.rb +0 -0
- data/spec/fixtures/{rails_users_app → rails6_users_app}/config/initializers/mime_types.rb +0 -0
- data/spec/fixtures/rails6_users_app/config/initializers/record_button.rb +3 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/config/initializers/wrap_parameters.rb +1 -6
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/config/locales/en.yml +10 -0
- data/spec/fixtures/{rails_users_app → rails6_users_app}/config/routes.rb +1 -1
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/create_app +7 -3
- data/spec/fixtures/rails6_users_app/db/migrate/20190728211408_create_users.rb +9 -0
- data/spec/fixtures/rails6_users_app/db/schema.rb +23 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/docker-compose.yml +3 -1
- data/spec/fixtures/rails6_users_app/features/api_users.feature +13 -0
- data/spec/fixtures/rails6_users_app/features/support/env.rb +4 -0
- data/spec/fixtures/rails6_users_app/features/support/hooks.rb +11 -0
- data/spec/fixtures/rails6_users_app/features/support/steps.rb +18 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/lib/tasks/.keep +0 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/log/.keep +0 -0
- data/spec/fixtures/rails6_users_app/public/robots.txt +1 -0
- data/spec/fixtures/rails6_users_app/spec/controllers/users_controller_api_spec.rb +29 -0
- data/spec/fixtures/rails6_users_app/spec/controllers/users_controller_spec.rb +27 -0
- data/spec/fixtures/rails6_users_app/spec/models/user_spec.rb +39 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app}/spec/rails_helper.rb +3 -32
- data/spec/fixtures/{rails_users_app → rails6_users_app}/spec/spec_helper.rb +0 -0
- data/spec/fixtures/{rails4_users_app → rails6_users_app/users_app}/.gitignore +8 -1
- data/spec/hook_spec.rb +111 -57
- data/spec/rails_spec_helper.rb +5 -5
- data/spec/railtie_spec.rb +31 -32
- data/spec/record_sql_rails_pg_spec.rb +62 -63
- data/spec/remote_recording_spec.rb +90 -89
- data/spec/rspec_feature_metadata_spec.rb +17 -18
- data/spec/spec_helper.rb +1 -0
- metadata +148 -139
- data/spec/abstract_controller4_base_spec.rb +0 -67
- data/spec/fixtures/rails4_users_app/.rbenv-gemsets +0 -2
- data/spec/fixtures/rails4_users_app/.ruby-version +0 -1
- data/spec/fixtures/rails4_users_app/Gemfile +0 -77
- data/spec/fixtures/rails4_users_app/README.rdoc +0 -28
- data/spec/fixtures/rails4_users_app/app/assets/javascripts/application.js +0 -16
- data/spec/fixtures/rails4_users_app/app/assets/stylesheets/application.css +0 -15
- data/spec/fixtures/rails4_users_app/app/controllers/application_controller.rb +0 -5
- data/spec/fixtures/rails4_users_app/app/controllers/users_controller.rb +0 -5
- data/spec/fixtures/rails4_users_app/app/helpers/application_helper.rb +0 -2
- data/spec/fixtures/rails4_users_app/appmap.yml +0 -3
- data/spec/fixtures/rails4_users_app/bin/rails +0 -9
- data/spec/fixtures/rails4_users_app/bin/setup +0 -29
- data/spec/fixtures/rails4_users_app/bin/spring +0 -17
- data/spec/fixtures/rails4_users_app/config/application.rb +0 -26
- data/spec/fixtures/rails4_users_app/config/boot.rb +0 -3
- data/spec/fixtures/rails4_users_app/config/environments/development.rb +0 -41
- data/spec/fixtures/rails4_users_app/config/initializers/assets.rb +0 -11
- data/spec/fixtures/rails4_users_app/config/initializers/cookies_serializer.rb +0 -3
- data/spec/fixtures/rails4_users_app/config/initializers/session_store.rb +0 -3
- data/spec/fixtures/rails4_users_app/config/initializers/to_time_preserves_timezone.rb +0 -10
- data/spec/fixtures/rails4_users_app/config/secrets.yml +0 -22
- data/spec/fixtures/rails4_users_app/db/migrate/20191127112304_create_users.rb +0 -10
- data/spec/fixtures/rails4_users_app/db/schema.rb +0 -26
- data/spec/fixtures/rails4_users_app/db/seeds.rb +0 -7
- data/spec/fixtures/rails4_users_app/public/404.html +0 -67
- data/spec/fixtures/rails4_users_app/public/422.html +0 -67
- data/spec/fixtures/rails4_users_app/public/500.html +0 -66
- data/spec/fixtures/rails4_users_app/public/favicon.ico +0 -0
- data/spec/fixtures/rails4_users_app/public/robots.txt +0 -5
- data/spec/fixtures/rails4_users_app/spec/controllers/users_controller_api_spec.rb +0 -49
- data/spec/fixtures/rails4_users_app/test/fixtures/users.yml +0 -9
- data/spec/fixtures/rails_users_app/app/controllers/concerns/.keep +0 -0
- data/spec/fixtures/rails_users_app/app/controllers/users_controller.rb +0 -5
- data/spec/fixtures/rails_users_app/app/models/concerns/.keep +0 -0
- data/spec/fixtures/rails_users_app/appmap.yml +0 -3
- data/spec/fixtures/rails_users_app/lib/tasks/.keep +0 -0
- data/spec/fixtures/rails_users_app/log/.keep +0 -0
- data/spec/record_sql_rails4_pg_spec.rb +0 -76
data/lib/appmap/event.rb
CHANGED
|
@@ -89,10 +89,25 @@ module AppMap
|
|
|
89
89
|
else
|
|
90
90
|
mc.path = [ defined_class, static ? '.' : '#', method.name ].join
|
|
91
91
|
end
|
|
92
|
+
|
|
93
|
+
# Check if the method has key parameters. If there are any they'll always be last.
|
|
94
|
+
# If yes, then extract it from arguments.
|
|
95
|
+
has_key = [[:dummy], *method.parameters].last.first.to_s.start_with?('key') && arguments[-1].is_a?(Hash)
|
|
96
|
+
kwargs = has_key && arguments[-1].dup || {}
|
|
97
|
+
|
|
92
98
|
mc.parameters = method.parameters.map.with_index do |method_param, idx|
|
|
93
99
|
param_type, param_name = method_param
|
|
94
100
|
param_name ||= 'arg'
|
|
95
|
-
value =
|
|
101
|
+
value = case param_type
|
|
102
|
+
when :keyrest
|
|
103
|
+
kwargs
|
|
104
|
+
when /^key/
|
|
105
|
+
kwargs.delete param_name
|
|
106
|
+
when :rest
|
|
107
|
+
arguments[idx..(has_key ? -2 : -1)]
|
|
108
|
+
else
|
|
109
|
+
arguments[idx]
|
|
110
|
+
end
|
|
96
111
|
{
|
|
97
112
|
name: param_name,
|
|
98
113
|
class: value.class.name,
|
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 =
|
|
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
|
|
data/lib/appmap/hook/method.rb
CHANGED
|
@@ -39,6 +39,7 @@ module AppMap
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
defined_class = @defined_class
|
|
42
|
+
hook_package = self.hook_package
|
|
42
43
|
hook_method = self.hook_method
|
|
43
44
|
before_hook = self.method(:before_hook)
|
|
44
45
|
after_hook = self.method(:after_hook)
|
|
@@ -48,29 +49,34 @@ module AppMap
|
|
|
48
49
|
hook_class.instance_eval do
|
|
49
50
|
hook_method_def = Proc.new do |*args, &block|
|
|
50
51
|
instance_method = hook_method.bind(self).to_proc
|
|
52
|
+
call_instance_method = -> { instance_method.call(*args, &block) }
|
|
51
53
|
|
|
52
54
|
# We may not have gotten the class for the method during
|
|
53
55
|
# initialization (e.g. for a singleton method on an embedded
|
|
54
56
|
# struct), so make sure we have it now.
|
|
55
|
-
defined_class,
|
|
57
|
+
defined_class, = Hook.qualify_method_name(hook_method) unless defined_class
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
reentrant = Thread.current[HOOK_DISABLE_KEY]
|
|
60
|
+
disabled_by_shallow_flag = \
|
|
61
|
+
-> { hook_package&.shallow? && AppMap.tracing.last_package_for_current_thread == hook_package }
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
enabled = true if AppMap.tracing.enabled? && !reentrant && !disabled_by_shallow_flag.call
|
|
64
|
+
|
|
65
|
+
return call_instance_method.call unless enabled
|
|
66
|
+
|
|
67
|
+
call_event, start_time = with_disabled_hook.call do
|
|
68
|
+
before_hook.call(self, defined_class, args)
|
|
63
69
|
end
|
|
64
70
|
return_value = nil
|
|
65
71
|
exception = nil
|
|
66
72
|
begin
|
|
67
|
-
return_value =
|
|
73
|
+
return_value = call_instance_method.call
|
|
68
74
|
rescue
|
|
69
75
|
exception = $ERROR_INFO
|
|
70
76
|
raise
|
|
71
77
|
ensure
|
|
72
|
-
with_disabled_hook.
|
|
73
|
-
after_hook.(self, call_event, start_time, return_value, exception)
|
|
78
|
+
with_disabled_hook.call do
|
|
79
|
+
after_hook.call(self, call_event, start_time, return_value, exception)
|
|
74
80
|
end
|
|
75
81
|
end
|
|
76
82
|
end
|
|
@@ -87,7 +93,7 @@ module AppMap
|
|
|
87
93
|
[ call_event, TIME_NOW.call ]
|
|
88
94
|
end
|
|
89
95
|
|
|
90
|
-
def after_hook(
|
|
96
|
+
def after_hook(_receiver, call_event, start_time, return_value, exception)
|
|
91
97
|
require 'appmap/event'
|
|
92
98
|
elapsed = TIME_NOW.call - start_time
|
|
93
99
|
return_event = \
|
|
@@ -95,12 +101,12 @@ module AppMap
|
|
|
95
101
|
AppMap.tracing.record_event return_event
|
|
96
102
|
end
|
|
97
103
|
|
|
98
|
-
def with_disabled_hook(&
|
|
104
|
+
def with_disabled_hook(&function)
|
|
99
105
|
# Don't record functions, such as to_s and inspect, that might be called
|
|
100
106
|
# by the fn. Otherwise there can be a stack overflow.
|
|
101
107
|
Thread.current[HOOK_DISABLE_KEY] = true
|
|
102
108
|
begin
|
|
103
|
-
|
|
109
|
+
function.call
|
|
104
110
|
ensure
|
|
105
111
|
Thread.current[HOOK_DISABLE_KEY] = false
|
|
106
112
|
end
|
|
@@ -7,22 +7,31 @@ module AppMap
|
|
|
7
7
|
module Rails
|
|
8
8
|
module RequestHandler
|
|
9
9
|
class HTTPServerRequest < AppMap::Event::MethodEvent
|
|
10
|
-
attr_accessor :request_method, :path_info, :params
|
|
10
|
+
attr_accessor :normalized_path_info, :request_method, :path_info, :params
|
|
11
11
|
|
|
12
12
|
def initialize(request)
|
|
13
13
|
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
|
14
14
|
|
|
15
15
|
@request_method = request.request_method
|
|
16
|
+
@normalized_path_info = normalized_path request
|
|
16
17
|
@path_info = request.path_info.split('?')[0]
|
|
17
|
-
|
|
18
|
+
# ActionDispatch::Http::ParameterFilter is deprecated
|
|
19
|
+
parameter_filter_cls = \
|
|
20
|
+
if defined?(ActiveSupport::ParameterFilter)
|
|
21
|
+
ActiveSupport::ParameterFilter
|
|
22
|
+
else
|
|
23
|
+
ActionDispatch::Http::ParameterFilter
|
|
24
|
+
end
|
|
25
|
+
@params = parameter_filter_cls.new(::Rails.application.config.filter_parameters).filter(request.params)
|
|
18
26
|
end
|
|
19
27
|
|
|
20
28
|
def to_h
|
|
21
29
|
super.tap do |h|
|
|
22
30
|
h[:http_server_request] = {
|
|
23
31
|
request_method: request_method,
|
|
24
|
-
path_info: path_info
|
|
25
|
-
|
|
32
|
+
path_info: path_info,
|
|
33
|
+
normalized_path_info: normalized_path_info
|
|
34
|
+
}.compact
|
|
26
35
|
|
|
27
36
|
h[:message] = params.keys.map do |key|
|
|
28
37
|
val = params[key]
|
|
@@ -35,6 +44,18 @@ module AppMap
|
|
|
35
44
|
end
|
|
36
45
|
end
|
|
37
46
|
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
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
|
|
58
|
+
end
|
|
38
59
|
end
|
|
39
60
|
|
|
40
61
|
class HTTPServerResponse < AppMap::Event::MethodReturnIgnoreValue
|
data/lib/appmap/railtie.rb
CHANGED
|
@@ -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'
|
|
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
|
data/lib/appmap/trace.rb
CHANGED
|
@@ -15,34 +15,38 @@ module AppMap
|
|
|
15
15
|
|
|
16
16
|
class Tracing
|
|
17
17
|
def initialize
|
|
18
|
-
@
|
|
18
|
+
@tracers = []
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def empty?
|
|
22
|
-
@
|
|
22
|
+
@tracers.empty?
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def trace(enable: true)
|
|
26
26
|
Tracer.new.tap do |tracer|
|
|
27
|
-
@
|
|
27
|
+
@tracers << tracer
|
|
28
28
|
tracer.enable if enable
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def enabled?
|
|
33
|
-
@
|
|
33
|
+
@tracers.any?(&:enabled?)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def last_package_for_current_thread
|
|
37
|
+
@tracers.first&.last_package_for_current_thread
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def record_event(event, package: nil, defined_class: nil, method: nil)
|
|
37
|
-
@
|
|
41
|
+
@tracers.each do |tracer|
|
|
38
42
|
tracer.record_event(event, package: package, defined_class: defined_class, method: method)
|
|
39
43
|
end
|
|
40
44
|
end
|
|
41
45
|
|
|
42
46
|
def delete(tracer)
|
|
43
|
-
return unless @
|
|
47
|
+
return unless @tracers.member?(tracer)
|
|
44
48
|
|
|
45
|
-
@
|
|
49
|
+
@tracers.delete(tracer)
|
|
46
50
|
tracer.disable
|
|
47
51
|
end
|
|
48
52
|
end
|
|
@@ -52,6 +56,7 @@ module AppMap
|
|
|
52
56
|
# Records the events which happen in a program.
|
|
53
57
|
def initialize
|
|
54
58
|
@events = []
|
|
59
|
+
@last_package_for_thread = {}
|
|
55
60
|
@methods = Set.new
|
|
56
61
|
@enabled = false
|
|
57
62
|
end
|
|
@@ -75,11 +80,17 @@ module AppMap
|
|
|
75
80
|
def record_event(event, package: nil, defined_class: nil, method: nil)
|
|
76
81
|
return unless @enabled
|
|
77
82
|
|
|
83
|
+
@last_package_for_thread[Thread.current.object_id] = package if package
|
|
78
84
|
@events << event
|
|
79
85
|
@methods << Trace::ScopedMethod.new(package, defined_class, method, event.static) \
|
|
80
86
|
if package && defined_class && method && (event.event == :call)
|
|
81
87
|
end
|
|
82
88
|
|
|
89
|
+
# Gets the last package which was observed on the current thread.
|
|
90
|
+
def last_package_for_current_thread
|
|
91
|
+
@last_package_for_thread[Thread.current.object_id]
|
|
92
|
+
end
|
|
93
|
+
|
|
83
94
|
# Gets a unique list of the methods that were invoked by the program.
|
|
84
95
|
def event_methods
|
|
85
96
|
@methods.to_a
|
data/lib/appmap/version.rb
CHANGED
|
@@ -1,85 +1,150 @@
|
|
|
1
1
|
require 'rails_spec_helper'
|
|
2
2
|
|
|
3
3
|
describe 'AbstractControllerBase' do
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
%w[5 6].each do |rails_major_version| # rubocop:disable Metrics/BlockLength
|
|
5
|
+
context "in Rails #{rails_major_version}" do
|
|
6
|
+
include_context 'Rails app pg database', "spec/fixtures/rails#{rails_major_version}_users_app"
|
|
7
|
+
def run_spec(spec_name)
|
|
8
|
+
FileUtils.rm_rf tmpdir
|
|
9
|
+
FileUtils.mkdir_p tmpdir
|
|
10
|
+
cmd = <<~CMD.gsub "\n", ' '
|
|
11
|
+
docker-compose run --rm -e APPMAP=true
|
|
12
|
+
-v #{File.absolute_path tmpdir}:/app/tmp app ./bin/rspec #{spec_name}
|
|
13
|
+
CMD
|
|
14
|
+
run_cmd cmd, chdir: fixture_dir
|
|
15
|
+
end
|
|
6
16
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
cmd = "docker-compose run --rm -e APPMAP=true -v #{File.absolute_path tmpdir}:/app/tmp app ./bin/rspec spec/controllers/users_controller_api_spec.rb:8"
|
|
11
|
-
run_cmd cmd, chdir: @fixture_dir
|
|
17
|
+
def tmpdir
|
|
18
|
+
'tmp/spec/AbstractControllerBase'
|
|
19
|
+
end
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
21
|
+
let(:appmap) { JSON.parse File.read File.join tmpdir, 'appmap/rspec', appmap_json_file }
|
|
22
|
+
let(:events) { appmap['events'] }
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
describe 'testing with rspec' do
|
|
25
|
+
describe 'creating a user' do
|
|
26
|
+
before(:all) { run_spec 'spec/controllers/users_controller_api_spec.rb:8' }
|
|
27
|
+
let(:appmap_json_file) do
|
|
28
|
+
'Api_UsersController_POST_api_users_with_required_parameters_creates_a_user.appmap.json'
|
|
29
|
+
end
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
end
|
|
31
|
+
it 'inventory file is printed' do
|
|
32
|
+
expect(File).to exist(File.join(tmpdir, 'appmap/rspec/Inventory.appmap.json'))
|
|
33
|
+
end
|
|
23
34
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
it 'message fields are recorded in the appmap' do
|
|
36
|
+
expect(events).to include(
|
|
37
|
+
hash_including(
|
|
38
|
+
'http_server_request' => hash_including(
|
|
39
|
+
'request_method' => 'POST',
|
|
40
|
+
'path_info' => '/api/users'
|
|
41
|
+
),
|
|
42
|
+
'message' => include(
|
|
43
|
+
hash_including(
|
|
44
|
+
'name' => 'login',
|
|
45
|
+
'class' => 'String',
|
|
46
|
+
'value' => 'alice',
|
|
47
|
+
'object_id' => Integer
|
|
48
|
+
),
|
|
49
|
+
hash_including(
|
|
50
|
+
'name' => 'password',
|
|
51
|
+
'class' => 'String',
|
|
52
|
+
'value' => '[FILTERED]',
|
|
53
|
+
'object_id' => Integer
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
),
|
|
57
|
+
hash_including(
|
|
58
|
+
'http_server_response' => {
|
|
59
|
+
'status' => 201,
|
|
60
|
+
'mime_type' => 'application/json; charset=utf-8'
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
end
|
|
27
65
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
66
|
+
it 'properly captures method parameters in the appmap' do
|
|
67
|
+
expect(events).to include hash_including(
|
|
68
|
+
'event' => 'call',
|
|
69
|
+
'thread_id' => Integer,
|
|
70
|
+
'defined_class' => 'Api::UsersController',
|
|
71
|
+
'method_id' => 'build_user',
|
|
72
|
+
'path' => 'app/controllers/api/users_controller.rb',
|
|
73
|
+
'lineno' => 23,
|
|
74
|
+
'static' => false,
|
|
75
|
+
'parameters' => include(
|
|
76
|
+
'name' => 'params',
|
|
77
|
+
'class' => 'ActiveSupport::HashWithIndifferentAccess',
|
|
78
|
+
'object_id' => Integer,
|
|
79
|
+
'value' => '{"login"=>"alice"}',
|
|
80
|
+
'kind' => 'req'
|
|
81
|
+
),
|
|
82
|
+
'receiver' => anything
|
|
83
|
+
)
|
|
84
|
+
end
|
|
35
85
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
86
|
+
it 'returns a minimal event' do
|
|
87
|
+
expect(events).to include hash_including(
|
|
88
|
+
'event' => 'return',
|
|
89
|
+
'return_value' => Hash,
|
|
90
|
+
'id' => Integer,
|
|
91
|
+
'thread_id' => Integer,
|
|
92
|
+
'parent_id' => Integer,
|
|
93
|
+
'elapsed' => Numeric
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
42
97
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
98
|
+
describe 'showing a user' do
|
|
99
|
+
before(:all) { run_spec 'spec/controllers/users_controller_spec.rb:22' }
|
|
100
|
+
let(:appmap_json_file) do
|
|
101
|
+
'UsersController_GET_users_login_shows_the_user.appmap.json'
|
|
102
|
+
end
|
|
48
103
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
104
|
+
it 'records the normalized path info' do
|
|
105
|
+
expect(events).to include(
|
|
106
|
+
hash_including(
|
|
107
|
+
'http_server_request' => {
|
|
108
|
+
'request_method' => 'GET',
|
|
109
|
+
'path_info' => '/users/alice',
|
|
110
|
+
'normalized_path_info' => '/users/:id(.:format)'
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
55
116
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
117
|
+
describe 'listing users' do
|
|
118
|
+
before(:all) { run_spec 'spec/controllers/users_controller_spec.rb:11' }
|
|
119
|
+
let(:appmap_json_file) { 'UsersController_GET_users_lists_the_users.appmap.json' }
|
|
59
120
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
class: ActiveSupport::HashWithIndifferentAccess
|
|
71
|
-
object_id: .*
|
|
72
|
-
value: '{"login"=>"alice"}'
|
|
73
|
-
kind: req
|
|
74
|
-
receiver:
|
|
75
|
-
CREATE_CALL
|
|
76
|
-
end
|
|
121
|
+
it 'records and labels view rendering' do
|
|
122
|
+
expect(events).to include hash_including(
|
|
123
|
+
'event' => 'call',
|
|
124
|
+
'thread_id' => Numeric,
|
|
125
|
+
'defined_class' => 'ActionView::Renderer',
|
|
126
|
+
'method_id' => 'render',
|
|
127
|
+
'path' => String,
|
|
128
|
+
'lineno' => Integer,
|
|
129
|
+
'static' => false
|
|
130
|
+
)
|
|
77
131
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
132
|
+
expect(appmap['classMap']).to include hash_including(
|
|
133
|
+
'name' => 'action_view',
|
|
134
|
+
'children' => include(hash_including(
|
|
135
|
+
'name' => 'ActionView',
|
|
136
|
+
'children' => include(hash_including(
|
|
137
|
+
'name' => 'Renderer',
|
|
138
|
+
'children' => include(hash_including(
|
|
139
|
+
'name' => 'render',
|
|
140
|
+
'labels' => ['view']
|
|
141
|
+
))
|
|
142
|
+
))
|
|
143
|
+
))
|
|
144
|
+
)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
83
148
|
end
|
|
84
149
|
end
|
|
85
150
|
end
|