module NPlusOneControl
RSpec
and Minitest matchers to prevent N+1 queries problem.
Constants
- EXTRACT_TABLE_RXP
Used to extract a table name from a query
- FAILURE_MESSAGES
- QUERY_PART_TO_TYPE
Used to convert a query part extracted by the regexp above to the corresponding human-friendly type
- VERSION
Attributes
backtrace_cleaner[RW]
backtrace_length[RW]
default_matching[R]
default_scale_factors[RW]
event[RW]
ignore[RW]
show_table_stats[RW]
truncate_query_size[RW]
verbose[RW]
Public Class Methods
default_matching=(val)
click to toggle source
# File lib/n_plus_one_control.rb, line 70 def default_matching=(val) unless val @default_matching = nil return end @default_matching = if val.is_a?(Regexp) val else Regexp.new(val, Regexp::MULTILINE | Regexp::IGNORECASE) end end
failure_message(type, queries)
click to toggle source
# File lib/n_plus_one_control.rb, line 31 def failure_message(type, queries) # rubocop:disable Metrics/MethodLength msg = ["#{FAILURE_MESSAGES[type]}, but got:\n"] queries.each do |(scale, data)| msg << " #{data.size} for N=#{scale}\n" end msg.concat(table_usage_stats(queries.map(&:last))) if show_table_stats if verbose queries.each do |(scale, data)| msg << "Queries for N=#{scale}\n" msg << data.map { |sql| " #{truncate_query(sql)}\n" }.join.to_s end end msg.join end
table_usage_stats(runs)
click to toggle source
# File lib/n_plus_one_control.rb, line 49 def table_usage_stats(runs) # rubocop:disable Metrics/MethodLength msg = ["Unmatched query numbers by tables:\n"] before, after = runs.map do |queries| queries.group_by do |query| matches = query.match(EXTRACT_TABLE_RXP) next unless matches " #{matches[2]} (#{QUERY_PART_TO_TYPE[matches[1].downcase]})" end.transform_values(&:count) end before.keys.each do |k| next if before[k] == after[k] msg << "#{k}: #{before[k]} != #{after[k]}\n" end msg end
Private Class Methods
truncate_query(sql)
click to toggle source
# File lib/n_plus_one_control.rb, line 86 def truncate_query(sql) return sql unless truncate_query_size # Only truncate query, leave tracing (if any) as is parts = sql.split(/(\s+↳)/) parts[0] = if truncate_query_size < 4 "..." else parts[0][0..(truncate_query_size - 4)] + "..." end parts.join end