appmap 0.45.1 → 0.46.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/.travis.yml +10 -0
 - data/CHANGELOG.md +7 -0
 - data/README.md +10 -11
 - data/lib/appmap.rb +0 -1
 - data/lib/appmap/class_map.rb +7 -15
 - data/lib/appmap/config.rb +15 -8
 - data/lib/appmap/event.rb +29 -28
 - data/lib/appmap/handler/function.rb +1 -1
 - data/lib/appmap/handler/rails/request_handler.rb +124 -0
 - data/lib/appmap/handler/rails/sql_handler.rb +152 -0
 - data/lib/appmap/handler/rails/template.rb +149 -0
 - data/lib/appmap/hook/method.rb +1 -1
 - data/lib/appmap/railtie.rb +5 -5
 - data/lib/appmap/trace.rb +46 -6
 - data/lib/appmap/version.rb +1 -1
 - data/package-lock.json +3 -3
 - data/spec/abstract_controller_base_spec.rb +65 -6
 - data/spec/class_map_spec.rb +3 -3
 - data/spec/hook_spec.rb +2 -2
 - metadata +5 -4
 - data/lib/appmap/rails/request_handler.rb +0 -122
 - data/lib/appmap/rails/sql_handler.rb +0 -150
 
    
        data/spec/class_map_spec.rb
    CHANGED
    
    | 
         @@ -5,7 +5,7 @@ require 'spec_helper' 
     | 
|
| 
       5 
5 
     | 
    
         
             
            describe 'AppMap::ClassMap' do
         
     | 
| 
       6 
6 
     | 
    
         
             
              describe '.build_from_methods' do
         
     | 
| 
       7 
7 
     | 
    
         
             
                it 'includes method comment' do
         
     | 
| 
       8 
     | 
    
         
            -
                  map = AppMap.class_map([ 
     | 
| 
      
 8 
     | 
    
         
            +
                  map = AppMap.class_map([ruby_method((method :test_method))])
         
     | 
| 
       9 
9 
     | 
    
         
             
                  function = dig_map(map, 5)[0]
         
     | 
| 
       10 
10 
     | 
    
         
             
                  expect(function).to include(:comment)
         
     | 
| 
       11 
11 
     | 
    
         
             
                end
         
     | 
| 
         @@ -15,8 +15,8 @@ describe 'AppMap::ClassMap' do 
     | 
|
| 
       15 
15 
     | 
    
         
             
                  'test method body'
         
     | 
| 
       16 
16 
     | 
    
         
             
                end
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
                def  
     | 
| 
       19 
     | 
    
         
            -
                  AppMap::Trace:: 
     | 
| 
      
 18 
     | 
    
         
            +
                def ruby_method(method)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  AppMap::Trace::RubyMethod.new AppMap::Config::Package.new, method.receiver.class.name, method, false
         
     | 
| 
       20 
20 
     | 
    
         
             
                end
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                def dig_map(map, depth)
         
     | 
    
        data/spec/hook_spec.rb
    CHANGED
    
    | 
         @@ -250,8 +250,8 @@ describe 'AppMap class Hooking', docker: false do 
     | 
|
| 
       250 
250 
     | 
    
         
             
                _, tracer = invoke_test_file 'spec/fixtures/hook/instance_method.rb' do
         
     | 
| 
       251 
251 
     | 
    
         
             
                  InstanceMethod.new.say_default
         
     | 
| 
       252 
252 
     | 
    
         
             
                end
         
     | 
| 
       253 
     | 
    
         
            -
                expect(tracer.event_methods.to_a.map(&: 
     | 
| 
       254 
     | 
    
         
            -
                expect(tracer.event_methods.to_a.map(&: 
     | 
| 
      
 253 
     | 
    
         
            +
                expect(tracer.event_methods.to_a.map(&:class_name)).to eq([ 'InstanceMethod' ])
         
     | 
| 
      
 254 
     | 
    
         
            +
                expect(tracer.event_methods.to_a.map(&:name)).to eq([ InstanceMethod.public_instance_method(:say_default).name ])
         
     | 
| 
       255 
255 
     | 
    
         
             
              end
         
     | 
| 
       256 
256 
     | 
    
         | 
| 
       257 
257 
     | 
    
         
             
              it 'builds a class map of invoked methods' do
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: appmap
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.46.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Kevin Gilpin
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2021-05- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2021-05-12 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: activesupport
         
     | 
| 
         @@ -348,14 +348,15 @@ files: 
     | 
|
| 
       348 
348 
     | 
    
         
             
            - lib/appmap/event.rb
         
     | 
| 
       349 
349 
     | 
    
         
             
            - lib/appmap/handler/function.rb
         
     | 
| 
       350 
350 
     | 
    
         
             
            - lib/appmap/handler/net_http.rb
         
     | 
| 
      
 351 
     | 
    
         
            +
            - lib/appmap/handler/rails/request_handler.rb
         
     | 
| 
      
 352 
     | 
    
         
            +
            - lib/appmap/handler/rails/sql_handler.rb
         
     | 
| 
      
 353 
     | 
    
         
            +
            - lib/appmap/handler/rails/template.rb
         
     | 
| 
       351 
354 
     | 
    
         
             
            - lib/appmap/hook.rb
         
     | 
| 
       352 
355 
     | 
    
         
             
            - lib/appmap/hook/method.rb
         
     | 
| 
       353 
356 
     | 
    
         
             
            - lib/appmap/metadata.rb
         
     | 
| 
       354 
357 
     | 
    
         
             
            - lib/appmap/middleware/remote_recording.rb
         
     | 
| 
       355 
358 
     | 
    
         
             
            - lib/appmap/minitest.rb
         
     | 
| 
       356 
359 
     | 
    
         
             
            - lib/appmap/open.rb
         
     | 
| 
       357 
     | 
    
         
            -
            - lib/appmap/rails/request_handler.rb
         
     | 
| 
       358 
     | 
    
         
            -
            - lib/appmap/rails/sql_handler.rb
         
     | 
| 
       359 
360 
     | 
    
         
             
            - lib/appmap/railtie.rb
         
     | 
| 
       360 
361 
     | 
    
         
             
            - lib/appmap/record.rb
         
     | 
| 
       361 
362 
     | 
    
         
             
            - lib/appmap/rspec.rb
         
     | 
| 
         @@ -1,122 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            require 'appmap/event'
         
     | 
| 
       4 
     | 
    
         
            -
            require 'appmap/hook'
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
            module AppMap
         
     | 
| 
       7 
     | 
    
         
            -
              module Rails
         
     | 
| 
       8 
     | 
    
         
            -
                module RequestHandler
         
     | 
| 
       9 
     | 
    
         
            -
                  class HTTPServerRequest < AppMap::Event::MethodEvent
         
     | 
| 
       10 
     | 
    
         
            -
                    attr_accessor :normalized_path_info, :request_method, :path_info, :params, :mime_type, :headers, :authorization
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
                    def initialize(request)
         
     | 
| 
       13 
     | 
    
         
            -
                      super AppMap::Event.next_id_counter, :call, Thread.current.object_id
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
                      self.request_method = request.request_method
         
     | 
| 
       16 
     | 
    
         
            -
                      self.normalized_path_info = normalized_path(request)
         
     | 
| 
       17 
     | 
    
         
            -
                      self.mime_type = request.headers['Content-Type']
         
     | 
| 
       18 
     | 
    
         
            -
                      self.headers = AppMap::Util.select_headers(request.env)
         
     | 
| 
       19 
     | 
    
         
            -
                      self.authorization = request.headers['Authorization']
         
     | 
| 
       20 
     | 
    
         
            -
                      self.path_info = request.path_info.split('?')[0]
         
     | 
| 
       21 
     | 
    
         
            -
                      # ActionDispatch::Http::ParameterFilter is deprecated
         
     | 
| 
       22 
     | 
    
         
            -
                      parameter_filter_cls = \
         
     | 
| 
       23 
     | 
    
         
            -
                        if defined?(ActiveSupport::ParameterFilter)
         
     | 
| 
       24 
     | 
    
         
            -
                          ActiveSupport::ParameterFilter
         
     | 
| 
       25 
     | 
    
         
            -
                        else
         
     | 
| 
       26 
     | 
    
         
            -
                          ActionDispatch::Http::ParameterFilter
         
     | 
| 
       27 
     | 
    
         
            -
                        end
         
     | 
| 
       28 
     | 
    
         
            -
                      self.params = parameter_filter_cls.new(::Rails.application.config.filter_parameters).filter(request.params)
         
     | 
| 
       29 
     | 
    
         
            -
                    end
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                    def to_h
         
     | 
| 
       32 
     | 
    
         
            -
                      super.tap do |h|
         
     | 
| 
       33 
     | 
    
         
            -
                        h[:http_server_request] = {
         
     | 
| 
       34 
     | 
    
         
            -
                          request_method: request_method,
         
     | 
| 
       35 
     | 
    
         
            -
                          path_info: path_info,
         
     | 
| 
       36 
     | 
    
         
            -
                          mime_type: mime_type,
         
     | 
| 
       37 
     | 
    
         
            -
                          normalized_path_info: normalized_path_info,
         
     | 
| 
       38 
     | 
    
         
            -
                          authorization: authorization,
         
     | 
| 
       39 
     | 
    
         
            -
                          headers: headers,
         
     | 
| 
       40 
     | 
    
         
            -
                        }.compact
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                        unless params.blank?
         
     | 
| 
       43 
     | 
    
         
            -
                          h[:message] = params.keys.map do |key|
         
     | 
| 
       44 
     | 
    
         
            -
                            val = params[key]
         
     | 
| 
       45 
     | 
    
         
            -
                            {
         
     | 
| 
       46 
     | 
    
         
            -
                              name: key,
         
     | 
| 
       47 
     | 
    
         
            -
                              class: val.class.name,
         
     | 
| 
       48 
     | 
    
         
            -
                              value: self.class.display_string(val),
         
     | 
| 
       49 
     | 
    
         
            -
                              object_id: val.__id__,
         
     | 
| 
       50 
     | 
    
         
            -
                            }.tap do |message|
         
     | 
| 
       51 
     | 
    
         
            -
                              properties = object_properties(val)
         
     | 
| 
       52 
     | 
    
         
            -
                              message[:properties] = properties if properties
         
     | 
| 
       53 
     | 
    
         
            -
                            end
         
     | 
| 
       54 
     | 
    
         
            -
                          end
         
     | 
| 
       55 
     | 
    
         
            -
                        end
         
     | 
| 
       56 
     | 
    
         
            -
                      end
         
     | 
| 
       57 
     | 
    
         
            -
                    end
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
                    private
         
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
                    def normalized_path(request, router = ::Rails.application.routes.router)
         
     | 
| 
       62 
     | 
    
         
            -
                      router.recognize request do |route, _|
         
     | 
| 
       63 
     | 
    
         
            -
                        app = route.app
         
     | 
| 
       64 
     | 
    
         
            -
                        next unless app.matches? request
         
     | 
| 
       65 
     | 
    
         
            -
                        return normalized_path request, app.rack_app.routes.router if app.engine?
         
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
                        return route.path.spec.to_s
         
     | 
| 
       68 
     | 
    
         
            -
                      end
         
     | 
| 
       69 
     | 
    
         
            -
                    end
         
     | 
| 
       70 
     | 
    
         
            -
                  end
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                  class HTTPServerResponse < AppMap::Event::MethodReturnIgnoreValue
         
     | 
| 
       73 
     | 
    
         
            -
                    attr_accessor :status, :mime_type, :headers
         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
                    def initialize(response, parent_id, elapsed)
         
     | 
| 
       76 
     | 
    
         
            -
                      super AppMap::Event.next_id_counter, :return, Thread.current.object_id
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
                      self.status = response.status
         
     | 
| 
       79 
     | 
    
         
            -
                      self.mime_type = response.headers['Content-Type']
         
     | 
| 
       80 
     | 
    
         
            -
                      self.parent_id = parent_id
         
     | 
| 
       81 
     | 
    
         
            -
                      self.elapsed = elapsed
         
     | 
| 
       82 
     | 
    
         
            -
                      self.headers = AppMap::Util.select_headers(response.headers)
         
     | 
| 
       83 
     | 
    
         
            -
                    end
         
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
                    def to_h
         
     | 
| 
       86 
     | 
    
         
            -
                      super.tap do |h|
         
     | 
| 
       87 
     | 
    
         
            -
                        h[:http_server_response] = {
         
     | 
| 
       88 
     | 
    
         
            -
                          status_code: status,
         
     | 
| 
       89 
     | 
    
         
            -
                          mime_type: mime_type,
         
     | 
| 
       90 
     | 
    
         
            -
                          headers: headers
         
     | 
| 
       91 
     | 
    
         
            -
                        }.compact
         
     | 
| 
       92 
     | 
    
         
            -
                      end
         
     | 
| 
       93 
     | 
    
         
            -
                    end
         
     | 
| 
       94 
     | 
    
         
            -
                  end
         
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
                  class HookMethod < AppMap::Hook::Method
         
     | 
| 
       97 
     | 
    
         
            -
                    def initialize
         
     | 
| 
       98 
     | 
    
         
            -
                      # ActionController::Instrumentation has issued start_processing.action_controller and
         
     | 
| 
       99 
     | 
    
         
            -
                      # process_action.action_controller since Rails 3. Therefore it's a stable place to hook
         
     | 
| 
       100 
     | 
    
         
            -
                      # the request. Rails controller notifications can't be used directly because they don't
         
     | 
| 
       101 
     | 
    
         
            -
                      # provide response headers, and we want the Content-Type.
         
     | 
| 
       102 
     | 
    
         
            -
                      super(nil, ActionController::Instrumentation, ActionController::Instrumentation.instance_method(:process_action))
         
     | 
| 
       103 
     | 
    
         
            -
                    end
         
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
     | 
    
         
            -
                    protected
         
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
                    def before_hook(receiver, defined_class, _) # args
         
     | 
| 
       108 
     | 
    
         
            -
                      call_event = HTTPServerRequest.new(receiver.request)
         
     | 
| 
       109 
     | 
    
         
            -
                      # http_server_request events are i/o and do not require a package name.
         
     | 
| 
       110 
     | 
    
         
            -
                      AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
         
     | 
| 
       111 
     | 
    
         
            -
                      [ call_event, TIME_NOW.call ]
         
     | 
| 
       112 
     | 
    
         
            -
                    end
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                    def after_hook(receiver, call_event, start_time, _, _) # return_value, exception
         
     | 
| 
       115 
     | 
    
         
            -
                      elapsed = TIME_NOW.call - start_time
         
     | 
| 
       116 
     | 
    
         
            -
                      return_event = HTTPServerResponse.new receiver.response, call_event.id, elapsed
         
     | 
| 
       117 
     | 
    
         
            -
                      AppMap.tracing.record_event return_event
         
     | 
| 
       118 
     | 
    
         
            -
                    end
         
     | 
| 
       119 
     | 
    
         
            -
                  end
         
     | 
| 
       120 
     | 
    
         
            -
                end
         
     | 
| 
       121 
     | 
    
         
            -
              end
         
     | 
| 
       122 
     | 
    
         
            -
            end
         
     | 
| 
         @@ -1,150 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            require 'appmap/event'
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
            module AppMap
         
     | 
| 
       6 
     | 
    
         
            -
              module Rails
         
     | 
| 
       7 
     | 
    
         
            -
                class SQLHandler
         
     | 
| 
       8 
     | 
    
         
            -
                  class SQLCall < AppMap::Event::MethodCall
         
     | 
| 
       9 
     | 
    
         
            -
                    attr_accessor :payload
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
                    def initialize(payload)
         
     | 
| 
       12 
     | 
    
         
            -
                      super AppMap::Event.next_id_counter, :call, Thread.current.object_id
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                      self.payload = payload
         
     | 
| 
       15 
     | 
    
         
            -
                    end
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                    def to_h
         
     | 
| 
       18 
     | 
    
         
            -
                      super.tap do |h|
         
     | 
| 
       19 
     | 
    
         
            -
                        h[:sql_query] = {
         
     | 
| 
       20 
     | 
    
         
            -
                          sql: payload[:sql],
         
     | 
| 
       21 
     | 
    
         
            -
                          database_type: payload[:database_type]
         
     | 
| 
       22 
     | 
    
         
            -
                        }.tap do |sql_query|
         
     | 
| 
       23 
     | 
    
         
            -
                          %i[server_version].each do |attribute|
         
     | 
| 
       24 
     | 
    
         
            -
                            sql_query[attribute] = payload[attribute] if payload[attribute]
         
     | 
| 
       25 
     | 
    
         
            -
                          end
         
     | 
| 
       26 
     | 
    
         
            -
                        end
         
     | 
| 
       27 
     | 
    
         
            -
                      end
         
     | 
| 
       28 
     | 
    
         
            -
                    end
         
     | 
| 
       29 
     | 
    
         
            -
                  end
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                  class SQLReturn < AppMap::Event::MethodReturnIgnoreValue
         
     | 
| 
       32 
     | 
    
         
            -
                    def initialize(parent_id, elapsed)
         
     | 
| 
       33 
     | 
    
         
            -
                      super AppMap::Event.next_id_counter, :return, Thread.current.object_id
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                      self.parent_id = parent_id
         
     | 
| 
       36 
     | 
    
         
            -
                      self.elapsed = elapsed
         
     | 
| 
       37 
     | 
    
         
            -
                    end
         
     | 
| 
       38 
     | 
    
         
            -
                  end
         
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                  module SQLExaminer
         
     | 
| 
       41 
     | 
    
         
            -
                    class << self
         
     | 
| 
       42 
     | 
    
         
            -
                      def examine(payload, sql:)
         
     | 
| 
       43 
     | 
    
         
            -
                        return unless (examiner = build_examiner)
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
                        payload[:server_version] = examiner.server_version
         
     | 
| 
       46 
     | 
    
         
            -
                        payload[:database_type] = examiner.database_type.to_s
         
     | 
| 
       47 
     | 
    
         
            -
                      end
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                      protected
         
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                      def build_examiner
         
     | 
| 
       52 
     | 
    
         
            -
                        if defined?(Sequel)
         
     | 
| 
       53 
     | 
    
         
            -
                          SequelExaminer.new
         
     | 
| 
       54 
     | 
    
         
            -
                        elsif defined?(ActiveRecord)
         
     | 
| 
       55 
     | 
    
         
            -
                          ActiveRecordExaminer.new
         
     | 
| 
       56 
     | 
    
         
            -
                        end
         
     | 
| 
       57 
     | 
    
         
            -
                      end
         
     | 
| 
       58 
     | 
    
         
            -
                    end
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                    class SequelExaminer
         
     | 
| 
       61 
     | 
    
         
            -
                      def server_version
         
     | 
| 
       62 
     | 
    
         
            -
                        Sequel::Model.db.server_version
         
     | 
| 
       63 
     | 
    
         
            -
                      end
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                      def database_type
         
     | 
| 
       66 
     | 
    
         
            -
                        Sequel::Model.db.database_type.to_sym
         
     | 
| 
       67 
     | 
    
         
            -
                      end
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
                      def execute_query(sql)
         
     | 
| 
       70 
     | 
    
         
            -
                        Sequel::Model.db[sql].all
         
     | 
| 
       71 
     | 
    
         
            -
                      end
         
     | 
| 
       72 
     | 
    
         
            -
                    end
         
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
                    class ActiveRecordExaminer
         
     | 
| 
       75 
     | 
    
         
            -
                      @@db_version_warning_issued = {}
         
     | 
| 
       76 
     | 
    
         
            -
                      
         
     | 
| 
       77 
     | 
    
         
            -
                      def issue_warning
         
     | 
| 
       78 
     | 
    
         
            -
                        db_type = database_type
         
     | 
| 
       79 
     | 
    
         
            -
                        return if @@db_version_warning_issued[db_type]
         
     | 
| 
       80 
     | 
    
         
            -
                        warn("AppMap: Unable to determine database version for #{db_type.inspect}") 
         
     | 
| 
       81 
     | 
    
         
            -
                        @@db_version_warning_issued[db_type] = true
         
     | 
| 
       82 
     | 
    
         
            -
                      end
         
     | 
| 
       83 
     | 
    
         
            -
                      
         
     | 
| 
       84 
     | 
    
         
            -
                      def server_version
         
     | 
| 
       85 
     | 
    
         
            -
                        ActiveRecord::Base.connection.try(:database_version) || issue_warning
         
     | 
| 
       86 
     | 
    
         
            -
                      end
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
                      def database_type
         
     | 
| 
       89 
     | 
    
         
            -
                        type = ActiveRecord::Base.connection.adapter_name.downcase.to_sym
         
     | 
| 
       90 
     | 
    
         
            -
                        type = :postgres if type == :postgresql
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
                        type
         
     | 
| 
       93 
     | 
    
         
            -
                      end
         
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
                      def execute_query(sql)
         
     | 
| 
       96 
     | 
    
         
            -
                        ActiveRecord::Base.connection.execute(sql).inject([]) { |memo, r| memo << r; memo }
         
     | 
| 
       97 
     | 
    
         
            -
                      end
         
     | 
| 
       98 
     | 
    
         
            -
                    end
         
     | 
| 
       99 
     | 
    
         
            -
                  end
         
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
                  def call(_, started, finished, _, payload) # (name, started, finished, unique_id, payload)
         
     | 
| 
       102 
     | 
    
         
            -
                    return if AppMap.tracing.empty?
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
                    reentry_key = "#{self.class.name}#call"
         
     | 
| 
       105 
     | 
    
         
            -
                    return if Thread.current[reentry_key] == true
         
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
                    Thread.current[reentry_key] = true
         
     | 
| 
       108 
     | 
    
         
            -
                    begin
         
     | 
| 
       109 
     | 
    
         
            -
                      sql = payload[:sql].strip
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
                      # Detect whether a function call within a specified filename is present in the call stack.
         
     | 
| 
       112 
     | 
    
         
            -
                      find_in_backtrace = lambda do |file_name, function_name = nil|
         
     | 
| 
       113 
     | 
    
         
            -
                        Thread.current.backtrace.find do |line|
         
     | 
| 
       114 
     | 
    
         
            -
                          tokens = line.split(':')
         
     | 
| 
       115 
     | 
    
         
            -
                          matches_file = tokens.find { |t| t.rindex(file_name) == (t.length - file_name.length) }
         
     | 
| 
       116 
     | 
    
         
            -
                          matches_function = function_name.nil? || tokens.find { |t| t == "in `#{function_name}'" }
         
     | 
| 
       117 
     | 
    
         
            -
                          matches_file && matches_function
         
     | 
| 
       118 
     | 
    
         
            -
                        end
         
     | 
| 
       119 
     | 
    
         
            -
                      end
         
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
                      # Ignore SQL calls which are made while establishing a new connection.
         
     | 
| 
       122 
     | 
    
         
            -
                      #
         
     | 
| 
       123 
     | 
    
         
            -
                      # Example:
         
     | 
| 
       124 
     | 
    
         
            -
                      # /path/to/ruby/2.6.0/gems/sequel-5.20.0/lib/sequel/connection_pool.rb:122:in `make_new'
         
     | 
| 
       125 
     | 
    
         
            -
                      return if find_in_backtrace.call('lib/sequel/connection_pool.rb', 'make_new')
         
     | 
| 
       126 
     | 
    
         
            -
                      # lib/active_record/connection_adapters/abstract/connection_pool.rb:811:in `new_connection'
         
     | 
| 
       127 
     | 
    
         
            -
                      return if find_in_backtrace.call('lib/active_record/connection_adapters/abstract/connection_pool.rb', 'new_connection')
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
                      # Ignore SQL calls which are made while inspecting the DB schema.
         
     | 
| 
       130 
     | 
    
         
            -
                      #
         
     | 
| 
       131 
     | 
    
         
            -
                      # Example:
         
     | 
| 
       132 
     | 
    
         
            -
                      # /path/to/ruby/2.6.0/gems/sequel-5.20.0/lib/sequel/model/base.rb:812:in `get_db_schema'
         
     | 
| 
       133 
     | 
    
         
            -
                      return if find_in_backtrace.call('lib/sequel/model/base.rb', 'get_db_schema')
         
     | 
| 
       134 
     | 
    
         
            -
                      # /usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/model_schema.rb:466:in `load_schema!'
         
     | 
| 
       135 
     | 
    
         
            -
                      return if find_in_backtrace.call('lib/active_record/model_schema.rb', 'load_schema!')
         
     | 
| 
       136 
     | 
    
         
            -
                      return if find_in_backtrace.call('lib/active_model/attribute_methods.rb', 'define_attribute_methods')
         
     | 
| 
       137 
     | 
    
         
            -
                      return if find_in_backtrace.call('lib/active_record/connection_adapters/schema_cache.rb')
         
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
                      SQLExaminer.examine payload, sql: sql
         
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
                      call = SQLCall.new(payload)
         
     | 
| 
       142 
     | 
    
         
            -
                      AppMap.tracing.record_event(call)
         
     | 
| 
       143 
     | 
    
         
            -
                      AppMap.tracing.record_event(SQLReturn.new(call.id, finished - started))
         
     | 
| 
       144 
     | 
    
         
            -
                    ensure
         
     | 
| 
       145 
     | 
    
         
            -
                      Thread.current[reentry_key] = nil
         
     | 
| 
       146 
     | 
    
         
            -
                    end
         
     | 
| 
       147 
     | 
    
         
            -
                  end
         
     | 
| 
       148 
     | 
    
         
            -
                end
         
     | 
| 
       149 
     | 
    
         
            -
              end
         
     | 
| 
       150 
     | 
    
         
            -
            end
         
     |