active_record-sql_analyzer 0.2.2 → 0.2.3
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/.rubocop.yml +12 -0
- data/.travis.yml +3 -5
- data/Gemfile +6 -5
- data/Rakefile +10 -3
- data/lib/active_record/sql_analyzer/cli_processor.rb +2 -2
- data/lib/active_record/sql_analyzer/configuration.rb +2 -2
- data/lib/active_record/sql_analyzer/monkeypatches/query.rb +70 -49
- data/lib/active_record/sql_analyzer/redacted_logger.rb +4 -4
- data/lib/active_record/sql_analyzer/version.rb +1 -1
- data/spec/active_record/sql_analyzer/end_to_end_spec.rb +38 -9
- data/spec/support/db_connection.rb +1 -0
- metadata +4 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 77bac67b331f9db59ff2dbc8f57ec2bca04505c0
         | 
| 4 | 
            +
              data.tar.gz: 870a62bd36006b4098d8e88a3a3af1cd3fa310b2
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 88f4ef3bbe57d34b89194e045e2845f4fa4bd9d5800e47943b8e9b2561fc2cb9025a870d976fc36e29c43564bc98c3c3baa7497ea6bd5ae2e894b505205ab3bd
         | 
| 7 | 
            +
              data.tar.gz: 7042bf53a81900be2c19a3bfa4e5564d2c2ca0d9fa23d3af22838e6322dd584e460db80236a5f35f1a755efba7a72781516d7c33ac9c7c3c24d2660518126cd8
         | 
    
        data/.rubocop.yml
    ADDED
    
    
    
        data/.travis.yml
    CHANGED
    
    | @@ -1,15 +1,13 @@ | |
| 1 1 | 
             
            bundler_args: --without development
         | 
| 2 | 
            -
            env:
         | 
| 3 | 
            -
              global:
         | 
| 4 | 
            -
                - JRUBY_OPTS="$JRUBY_OPTS --debug"
         | 
| 5 2 | 
             
            language: ruby
         | 
| 6 3 | 
             
            rvm:
         | 
| 7 | 
            -
              - 2.1
         | 
| 8 4 | 
             
              - 2.2
         | 
| 9 | 
            -
              - 2.3. | 
| 5 | 
            +
              - 2.3.3
         | 
| 6 | 
            +
              - 2.4.0-preview3
         | 
| 10 7 | 
             
              - ruby-head
         | 
| 11 8 | 
             
            matrix:
         | 
| 12 9 | 
             
              allow_failures:
         | 
| 10 | 
            +
                - rvm: 2.4.0-preview3
         | 
| 13 11 | 
             
                - rvm: ruby-head
         | 
| 14 12 | 
             
              fast_finish: true
         | 
| 15 13 | 
             
            sudo: false
         | 
    
        data/Gemfile
    CHANGED
    
    | @@ -1,13 +1,14 @@ | |
| 1 1 | 
             
            source "https://rubygems.org"
         | 
| 2 2 |  | 
| 3 | 
            +
            gem "pry-byebug"
         | 
| 3 4 | 
             
            gem "rake"
         | 
| 4 5 |  | 
| 5 6 | 
             
            group :test do
         | 
| 6 | 
            -
              gem  | 
| 7 | 
            -
              gem  | 
| 8 | 
            -
              gem  | 
| 9 | 
            -
              gem  | 
| 10 | 
            -
              gem  | 
| 7 | 
            +
              gem "mysql2", "~> 0.4", ">= 0.4.0"
         | 
| 8 | 
            +
              gem "rspec", "~> 3.4"
         | 
| 9 | 
            +
              gem "rubocop", "~> 0.30"
         | 
| 10 | 
            +
              gem "sql-parser", git: "https://github.com/nerdrew/sql-parser.git"
         | 
| 11 | 
            +
              gem "timecop", "~> 0.8"
         | 
| 11 12 | 
             
            end
         | 
| 12 13 |  | 
| 13 14 | 
             
            gemspec
         | 
    
        data/Rakefile
    CHANGED
    
    | @@ -1,6 +1,13 @@ | |
| 1 1 | 
             
            require 'bundler/gem_tasks'
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 3 | 
            +
            begin
         | 
| 4 | 
            +
              require "rubocop/rake_task"
         | 
| 5 | 
            +
              require "rspec/core/rake_task"
         | 
| 5 6 |  | 
| 6 | 
            -
             | 
| 7 | 
            +
              RuboCop::RakeTask.new
         | 
| 8 | 
            +
              RSpec::Core::RakeTask.new(:spec)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              task default: [:rubocop, :spec]
         | 
| 11 | 
            +
            rescue LoadError
         | 
| 12 | 
            +
              warn "rubocop, rspec only available in development"
         | 
| 13 | 
            +
            end
         | 
| @@ -35,7 +35,7 @@ module ActiveRecord | |
| 35 35 | 
             
                    logs.each { |l| queue << l }
         | 
| 36 36 |  | 
| 37 37 | 
             
                    # Spin up threads to start processing the queue
         | 
| 38 | 
            -
                    threads = concurrency | 
| 38 | 
            +
                    threads = Array.new(concurrency) do
         | 
| 39 39 | 
             
                      Thread.new(queue) do |t_queue|
         | 
| 40 40 | 
             
                        # Create a local copy of each definitions then merge them in
         | 
| 41 41 | 
             
                        CLIProcessor.process_queue(t_queue) do |local_definitions, line|
         | 
| @@ -63,7 +63,7 @@ module ActiveRecord | |
| 63 63 | 
             
                    logs.each { |l| queue << l }
         | 
| 64 64 |  | 
| 65 65 | 
             
                    # Spin up threads to start processing the queue
         | 
| 66 | 
            -
                    threads = concurrency | 
| 66 | 
            +
                    threads = Array.new(concurrency) do
         | 
| 67 67 | 
             
                      Thread.new(queue) do |t_queue|
         | 
| 68 68 | 
             
                        # Create a local copy of the usage for each SHA then merge it in at the end
         | 
| 69 69 | 
             
                        CLIProcessor.process_queue(t_queue) do |local_usage, line|
         | 
| @@ -108,7 +108,7 @@ module ActiveRecord | |
| 108 108 |  | 
| 109 109 | 
             
                  # How many total lines to log when the caller is ambiguous
         | 
| 110 110 | 
             
                  def ambiguous_backtrace_lines(lines)
         | 
| 111 | 
            -
                    if !lines.is_a?( | 
| 111 | 
            +
                    if !lines.is_a?(Integer)
         | 
| 112 112 | 
             
                      raise ArgumentError, "Lines must be a Fixnum"
         | 
| 113 113 | 
             
                    elsif lines <= 1
         | 
| 114 114 | 
             
                      raise ArgumentError, "Lines cannot be <= 1"
         | 
| @@ -129,7 +129,7 @@ module ActiveRecord | |
| 129 129 |  | 
| 130 130 | 
             
                  private
         | 
| 131 131 |  | 
| 132 | 
            -
                  def check_proc(proc,  | 
| 132 | 
            +
                  def check_proc(proc, _arity, msg)
         | 
| 133 133 | 
             
                    if !proc.is_a?(Proc)
         | 
| 134 134 | 
             
                      raise ArgumentError, "You must pass a proc"
         | 
| 135 135 | 
             
                    elsif proc.arity != 1
         | 
| @@ -8,23 +8,30 @@ module ActiveRecord | |
| 8 8 |  | 
| 9 9 | 
             
                    def execute(sql, *args)
         | 
| 10 10 | 
             
                      return super unless SqlAnalyzer.config
         | 
| 11 | 
            +
                      safe_sql = nil
         | 
| 12 | 
            +
                      query_analyzer_call = nil
         | 
| 11 13 |  | 
| 12 | 
            -
                       | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 14 | 
            +
                      # Record "full" transactions (see below for more information about "full")
         | 
| 15 | 
            +
                      if @_query_analyzer_private_in_transaction
         | 
| 16 | 
            +
                        if @_query_analyzer_private_record_transaction
         | 
| 17 | 
            +
                          safe_sql ||= sql.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
         | 
| 18 | 
            +
                          query_analyzer_call ||= QueryAnalyzerCall.new(safe_sql, caller)
         | 
| 19 | 
            +
                          @_query_analyzer_private_transaction_queue << query_analyzer_call
         | 
| 20 | 
            +
                        end
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                      # Record interesting queries
         | 
| 24 | 
            +
                      SqlAnalyzer.config[:analyzers].each do |analyzer|
         | 
| 25 | 
            +
                        if SqlAnalyzer.config[:should_log_sample_proc].call(analyzer[:name])
         | 
| 26 | 
            +
                          # This is here rather than above intentionally.
         | 
| 27 | 
            +
                          # We assume we're not going to be analyzing 100% of queries and want to only re-encode
         | 
| 28 | 
            +
                          # when it's actually relevant.
         | 
| 29 | 
            +
                          safe_sql ||= sql.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
         | 
| 30 | 
            +
                          query_analyzer_call ||= QueryAnalyzerCall.new(safe_sql, caller)
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                          if safe_sql =~ analyzer[:table_regex]
         | 
| 33 | 
            +
                            SqlAnalyzer.background_processor <<
         | 
| 34 | 
            +
                            _query_analyzer_private_query_stanza([query_analyzer_call], analyzer)
         | 
| 28 35 | 
             
                          end
         | 
| 29 36 | 
             
                        end
         | 
| 30 37 | 
             
                      end
         | 
| @@ -32,30 +39,62 @@ module ActiveRecord | |
| 32 39 | 
             
                      super
         | 
| 33 40 | 
             
                    end
         | 
| 34 41 |  | 
| 42 | 
            +
                    def begin_db_transaction
         | 
| 43 | 
            +
                      @_query_analyzer_private_in_transaction = true
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                      record_transaction = SqlAnalyzer.config[:analyzers].any? do |analyzer|
         | 
| 46 | 
            +
                        SqlAnalyzer.config[:should_log_sample_proc].call(analyzer[:name])
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
                      if record_transaction
         | 
| 49 | 
            +
                        @_query_analyzer_private_transaction_queue ||= []
         | 
| 50 | 
            +
                        @_query_analyzer_private_record_transaction = true
         | 
| 51 | 
            +
                      else
         | 
| 52 | 
            +
                        @_query_analyzer_private_record_transaction = nil
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                      super
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    def commit_db_transaction
         | 
| 58 | 
            +
                      _query_analyzer_private_drain_transaction_queue("COMMIT")
         | 
| 59 | 
            +
                      super
         | 
| 60 | 
            +
                    ensure
         | 
| 61 | 
            +
                      @_query_analyzer_private_in_transaction = false
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    def exec_rollback_db_transaction
         | 
| 65 | 
            +
                      _query_analyzer_private_drain_transaction_queue("ROLLBACK")
         | 
| 66 | 
            +
                      super
         | 
| 67 | 
            +
                    ensure
         | 
| 68 | 
            +
                      @_query_analyzer_private_in_transaction = false
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    # "private" methods for this monkeypatch
         | 
| 72 | 
            +
             | 
| 35 73 | 
             
                    # Drain the transaction query queue. Log the current transaction out to any logger that samples it.
         | 
| 36 | 
            -
                    def _query_analyzer_private_drain_transaction_queue
         | 
| 74 | 
            +
                    def _query_analyzer_private_drain_transaction_queue(last_query)
         | 
| 75 | 
            +
                      return unless @_query_analyzer_private_record_transaction
         | 
| 76 | 
            +
             | 
| 37 77 | 
             
                      reencoded_calls = nil
         | 
| 38 78 |  | 
| 39 79 | 
             
                      SqlAnalyzer.config[:analyzers].each do |analyzer|
         | 
| 40 | 
            -
                         | 
| 41 | 
            -
                          # Again, trying to only re-encode and join strings if the transaction is actually
         | 
| 42 | 
            -
                          # sampled.
         | 
| 43 | 
            -
                          reencoded_calls ||= @_query_analyzer_private_transaction_queue.map do |call|
         | 
| 44 | 
            -
                            QueryAnalyzerCall.new(call.sql.encode(Encoding::UTF_8, invalid: :replace, undef: :replace), call.caller)
         | 
| 45 | 
            -
                          end
         | 
| 46 | 
            -
             | 
| 47 | 
            -
                          has_matching_calls = reencoded_calls.any? { |call| call.sql =~ analyzer[:table_regex] }
         | 
| 80 | 
            +
                        reencoded_calls ||= @_query_analyzer_private_transaction_queue << QueryAnalyzerCall.new(last_query, caller)
         | 
| 48 81 |  | 
| 49 | 
            -
             | 
| 50 | 
            -
                            matching_calls = reencoded_calls.select do |call|
         | 
| 51 | 
            -
                              (call.sql =~ /^(BEGIN|COMMIT|ROLLBACK|UPDATE|INSERT|DELETE)/) || (call.sql =~ analyzer[:table_regex])
         | 
| 52 | 
            -
                            end
         | 
| 82 | 
            +
                        has_matching_calls = reencoded_calls.any? { |call| call.sql =~ analyzer[:table_regex] }
         | 
| 53 83 |  | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 84 | 
            +
                        # Record "full" transactions
         | 
| 85 | 
            +
                        # Record all INSERT, UPDATE, and DELETE
         | 
| 86 | 
            +
                        # Record all queries that match the analyzer's table_regex
         | 
| 87 | 
            +
                        if has_matching_calls
         | 
| 88 | 
            +
                          matching_calls = reencoded_calls.select do |call|
         | 
| 89 | 
            +
                            (call.sql =~ /^(BEGIN|COMMIT|ROLLBACK|UPDATE|INSERT|DELETE)/) || (call.sql =~ analyzer[:table_regex])
         | 
| 56 90 | 
             
                          end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                          SqlAnalyzer.background_processor << _query_analyzer_private_query_stanza(matching_calls, analyzer)
         | 
| 57 93 | 
             
                        end
         | 
| 58 94 | 
             
                      end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                      @_query_analyzer_private_transaction_queue.clear
         | 
| 97 | 
            +
                      @_query_analyzer_private_record_transaction = nil
         | 
| 59 98 | 
             
                    end
         | 
| 60 99 |  | 
| 61 100 | 
             
                    # Helper method to construct the event for a query or transaction.
         | 
| @@ -70,24 +109,6 @@ module ActiveRecord | |
| 70 109 | 
             
                        request_path: Thread.current[:_ar_analyzer_request_path],
         | 
| 71 110 | 
             
                      }
         | 
| 72 111 | 
             
                    end
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                    def transaction(requires_new: nil, isolation: nil, joinable: true)
         | 
| 75 | 
            -
                      must_clear_transaction = false
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                      if SqlAnalyzer.config[:consolidate_transactions]
         | 
| 78 | 
            -
                        if @_query_analyzer_private_transaction_queue.nil?
         | 
| 79 | 
            -
                          must_clear_transaction = true
         | 
| 80 | 
            -
                          @_query_analyzer_private_transaction_queue = []
         | 
| 81 | 
            -
                        end
         | 
| 82 | 
            -
                      end
         | 
| 83 | 
            -
             | 
| 84 | 
            -
                      super
         | 
| 85 | 
            -
                    ensure
         | 
| 86 | 
            -
                      if must_clear_transaction
         | 
| 87 | 
            -
                        _query_analyzer_private_drain_transaction_queue
         | 
| 88 | 
            -
                        @_query_analyzer_private_transaction_queue = nil
         | 
| 89 | 
            -
                      end
         | 
| 90 | 
            -
                    end
         | 
| 91 112 | 
             
                  end
         | 
| 92 113 | 
             
                end
         | 
| 93 114 | 
             
              end
         | 
| @@ -22,10 +22,10 @@ module ActiveRecord | |
| 22 22 |  | 
| 23 23 | 
             
                  def filter_caller(kaller)
         | 
| 24 24 | 
             
                    kaller = if config[:ambiguous_tracers].any? { |regex| kaller.first =~ regex }
         | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 25 | 
            +
                               kaller[0, config[:ambiguous_backtrace_lines]].join(", ")
         | 
| 26 | 
            +
                             else
         | 
| 27 | 
            +
                               kaller.first
         | 
| 28 | 
            +
                             end
         | 
| 29 29 |  | 
| 30 30 | 
             
                    return '' unless kaller
         | 
| 31 31 |  | 
| @@ -216,6 +216,36 @@ RSpec.describe "End to End" do | |
| 216 216 | 
             
                  "COMMIT;")
         | 
| 217 217 | 
             
              end
         | 
| 218 218 |  | 
| 219 | 
            +
              context "ActiveRecord generated transactions" do
         | 
| 220 | 
            +
                before do
         | 
| 221 | 
            +
                  stub_const("Matching", Class.new(ActiveRecord::Base) do
         | 
| 222 | 
            +
                    self.table_name = "matching_table"
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                    after_commit { self.class.last }
         | 
| 225 | 
            +
                  end)
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                  stub_const("NonMatching", Class.new(ActiveRecord::Base) do
         | 
| 228 | 
            +
                    self.table_name = "nonmatching_table"
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                    after_commit { self.class.last }
         | 
| 231 | 
            +
                  end)
         | 
| 232 | 
            +
                end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                it "Logs the matching statements in the transaction and logs after_commit hooks outside the transaction" do
         | 
| 235 | 
            +
                  Matching.transaction do
         | 
| 236 | 
            +
                    Matching.create!
         | 
| 237 | 
            +
                    NonMatching.create
         | 
| 238 | 
            +
                    NonMatching.last
         | 
| 239 | 
            +
                  end
         | 
| 240 | 
            +
                  NonMatching.create
         | 
| 241 | 
            +
             | 
| 242 | 
            +
                  expect(log_def_hash.map { |_hash, data| data["sql"] }).to match([
         | 
| 243 | 
            +
                    "INSERT INTO `matching_table` VALUES ()",
         | 
| 244 | 
            +
                    "BEGIN; INSERT INTO `matching_table` VALUES (); INSERT INTO `nonmatching_table` VALUES (); COMMIT;",
         | 
| 245 | 
            +
                    "SELECT `matching_table`.* FROM `matching_table` ORDER BY `matching_table`.`id` DESC"
         | 
| 246 | 
            +
                  ])
         | 
| 247 | 
            +
                end
         | 
| 248 | 
            +
              end
         | 
| 219 249 |  | 
| 220 250 | 
             
              it "Logs mixed matching-nonmatching with inserts correctly" do
         | 
| 221 251 | 
             
                transaction do
         | 
| @@ -278,15 +308,14 @@ RSpec.describe "End to End" do | |
| 278 308 | 
             
                    execute "SELECT * FROM matching_table WHERE id > 4 and id < 8"
         | 
| 279 309 | 
             
                  end
         | 
| 280 310 |  | 
| 281 | 
            -
             | 
| 282 | 
            -
             | 
| 283 | 
            -
             | 
| 284 | 
            -
             | 
| 285 | 
            -
             | 
| 286 | 
            -
                    " | 
| 287 | 
            -
                    "SELECT * FROM matching_table WHERE id = '[REDACTED]' | 
| 288 | 
            -
             | 
| 289 | 
            -
                    "COMMIT;")
         | 
| 311 | 
            +
                  expect(log_def_hash.map { |_hash, data| data["sql"] }).to match([
         | 
| 312 | 
            +
                    "SELECT * FROM matching_table WHERE id = '[REDACTED]'",
         | 
| 313 | 
            +
                    "BEGIN; "\
         | 
| 314 | 
            +
                    "SELECT * FROM matching_table WHERE id = '[REDACTED]'; "\
         | 
| 315 | 
            +
                    "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; "\
         | 
| 316 | 
            +
                    "COMMIT;",
         | 
| 317 | 
            +
                    "SELECT * FROM matching_table WHERE id = '[REDACTED]' and id = '[REDACTED]'"
         | 
| 318 | 
            +
                  ])
         | 
| 290 319 | 
             
                end
         | 
| 291 320 | 
             
              end
         | 
| 292 321 |  | 
| @@ -19,6 +19,7 @@ class DBConnection | |
| 19 19 | 
             
              end
         | 
| 20 20 |  | 
| 21 21 | 
             
              def self.setup_db
         | 
| 22 | 
            +
                ActiveRecord::Base.raise_in_transactional_callbacks = true
         | 
| 22 23 | 
             
                conn = ActiveRecord::Base.establish_connection(configuration)
         | 
| 23 24 | 
             
                conn.connection.execute <<-SQL
         | 
| 24 25 | 
             
                  CREATE DATABASE IF NOT EXISTS ar_sql_analyzer_test
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: active_record-sql_analyzer
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.2. | 
| 4 | 
            +
              version: 0.2.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Zachary Anker
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2016- | 
| 12 | 
            +
            date: 2016-12-16 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: activerecord
         | 
| @@ -56,6 +56,7 @@ extra_rdoc_files: [] | |
| 56 56 | 
             
            files:
         | 
| 57 57 | 
             
            - ".gitignore"
         | 
| 58 58 | 
             
            - ".rspec"
         | 
| 59 | 
            +
            - ".rubocop.yml"
         | 
| 59 60 | 
             
            - ".travis.yml"
         | 
| 60 61 | 
             
            - CONTRIBUTING.md
         | 
| 61 62 | 
             
            - Gemfile
         | 
| @@ -110,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 110 111 | 
             
                  version: '0'
         | 
| 111 112 | 
             
            requirements: []
         | 
| 112 113 | 
             
            rubyforge_project: 
         | 
| 113 | 
            -
            rubygems_version: 2. | 
| 114 | 
            +
            rubygems_version: 2.5.1
         | 
| 114 115 | 
             
            signing_key: 
         | 
| 115 116 | 
             
            specification_version: 4
         | 
| 116 117 | 
             
            summary: Logs a subset of ActiveRecord queries and dumps them for analyses.
         |