class ActiveRecordFormatterHelpers::Collector

Constants

SKIP_QUERIES

Attributes

active_groups[R]
group_counts[R]
groups_encountered[R]
objects_count[R]
query_count[R]
query_names[R]
total_objects[R]
total_queries[R]

Public Class Methods

new() click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 10
def initialize
  #@unnamed_queries = []
  @query_count    = 0
  @groups_encountered = 0
  @objects_count  = 0
  @total_queries  = 0
  @total_objects  = 0
  @query_names    = Hash.new(0)
  @group_counts   = Hash.new(0)
  @active_groups  = []

  ActiveSupport::Notifications.subscribe("sql.active_record", method(:record_query))
end

Public Instance Methods

group_finished(group) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 53
def group_finished(group)
  active_groups.delete(group_path(group))
end
group_started(group) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 45
def group_started(group)
  @groups_encountered += 1

  return unless group.parent_groups.length > 1

  active_groups.push(group_path(group))
end
most_common_query_names() click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 32
def most_common_query_names
  query_names.sort_by(&:last).reverse
end
most_expensive_groups() click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 36
def most_expensive_groups
  group_counts.sort_by(&:last).reverse
end
record_query(*_unused, data) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 24
def record_query(*_unused, data)
  return if SKIP_QUERIES.any? { |q| data[:sql].index(q) == 0 }

  inc_query
  inc_object if query_is_an_insert?(data[:sql])
  inc_query_name(data)
end
reset_example(_) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 40
def reset_example(_)
  @query_count   = 0
  @objects_count = 0
end

Protected Instance Methods

group_path(group) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 114
def group_path(group)
  group.parent_groups.reverse.map(&:description).join(' ')
end
inc_object() click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 59
def inc_object
  @objects_count  += 1
  @total_objects  += 1

  active_groups.each do |group|
    @group_counts[group] += 1
  end
end
inc_query() click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 68
def inc_query
  @query_count    += 1
  @total_queries  += 1
end
inc_query_name(data) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 73
def inc_query_name(data)
  name = data[:name] || "Unnamed"

  # In older versions of Rails, insert statements are just counted as SQL
  # queries, which means that all the queries are just bunchedup at the top.
  # Makes this data pretty useless. So anyway, try to suss out a name for
  # at least those insertions (which are much more frequent than, say,
  # updates in a test suite anyway).
  if data[:name] == "SQL" && query_is_an_insert?(data[:sql])
    table = data[:sql].scan(/INSERT INTO "(\w+)"/).first.first
    name = "#{table} Create"
  elsif query_is_a_full_table_delete?(data[:sql])
    table = data[:sql].scan(/DELETE FROM "(\w+)"/).first.first
    name = "Full Delete Table"
  # TODO: truncate table
  elsif query_is_transaction_management?(data[:sql])
    name = "Transaction Management"
  elsif query_is_schema_detection?(data[:sql])
    name = "SCHEMA"
  elsif query_is_trigger_management?(data[:sql])
    name = "Trigger Management"
  elsif query_refreshes_materialized_view?(data[:sql])
    name = "Refresh Materialized View"
  end


  # In older versions of Rails, insert statements are just counted as SQL
  # queries, which means that all the queries are just bunchedup at the top.
  # Makes this data pretty useless. So anyway, try to suss out a name for
  # at least those insertions (which are much more frequent than, say,
  # updates in a test suite anyway).
  #if data[:name].nil? && query_is_a_delete?(data[:sql])
  #  table = data[:sql].scan(/DELETE FROM "(\w+)"/).first.first
  #  name = "#{table} Delete"
  #end

  #@unnamed_queries << data if name == "Unnamed"

  query_names[name] += 1
end
query_is_a_delete?(query) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 124
def query_is_a_delete?(query)
  query =~ /^DELETE FROM/
end
query_is_a_full_table_delete?(query) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 128
def query_is_a_full_table_delete?(query)
  query =~ /^DELETE FROM [a-z_\."]*;$/i
end
query_is_an_insert?(query) click to toggle source

TODO: what happens if we try to create many records at once? TODO: are there any false positives we need to worry about? false negatives?

# File lib/rspec/activerecord/helpers/collector.rb, line 120
def query_is_an_insert?(query)
  query =~ /^INSERT INTO/
end
query_is_schema_detection?(query) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 136
def query_is_schema_detection?(query)
  query =~ /SELECT .* FROM pg_tables/m ||
    query =~ /SELECT .* FROM information_schema.views/
end
query_is_transaction_management?(query) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 132
def query_is_transaction_management?(query)
  query =~ /^(COMMIT|BEGIN|ROLLBACK|SAVEPOINT|RELEASE SAVEPOINT)/
end
query_is_trigger_management?(query) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 141
def query_is_trigger_management?(query)
  query =~ /(DISABLE|ENABLE) TRIGGER ALL/m
end
query_refreshes_materialized_view?(query) click to toggle source
# File lib/rspec/activerecord/helpers/collector.rb, line 145
def query_refreshes_materialized_view?(query)
  query =~ /REFRESH MATERIALIZED VIEW/m
end