├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── lib ├── rspec-query-limit.rb └── rspec │ ├── query_limit.rb │ └── query_limit │ ├── matchers.rb │ ├── query_counter.rb │ └── version.rb └── rspec-query-limit.gemspec /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in query_limit.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 Monterail.com LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rspec custom matchers for Rails query counter [![Gem Version](https://badge.fury.io/rb/rspec-query-limit.png)](http://badge.fury.io/rb/rspec-query-limit) 2 | 3 | ## Features 4 | 5 | - Prevent N+1 problem 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'rspec-query-limit' 13 | ``` 14 | 15 | And then execute: 16 | ```bash 17 | $ bundle 18 | ``` 19 | 20 | Support for rspec versions 2 and 3 21 | 22 | ## Usage 23 | 24 | Add to your ```spec/spec_helper.rb``` 25 | 26 | ```ruby 27 | require 'rspec/query_limit' 28 | ``` 29 | 30 | And then you can use matchers 31 | ```ruby 32 | describe UserEmails do 33 | describe '#primary_emails' do 34 | users = User.all 35 | expect { UserEmails.primary_emails(users) }.to query_limit_eq(2) 36 | end 37 | end 38 | ``` 39 | 40 | ## List of query limit matchers 41 | Numbers of queries must be equal 42 | 43 | ``` 44 | query_limit_eq(number) 45 | ``` 46 | 47 | ## Inspiration from 48 | [Stackoverflow](http://stackoverflow.com/questions/5490411/counting-the-number-of-queries-performed) 49 | 50 | ## Contributing 51 | 52 | 1. Fork it 53 | 2. Create your feature branch (`git checkout -b my-new-feature`) 54 | 3. Commit your changes (`git commit -am 'Add some feature'`) 55 | 4. Push to the branch (`git push origin my-new-feature`) 56 | 5. Create a new Pull Request 57 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | -------------------------------------------------------------------------------- /lib/rspec-query-limit.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/query_limit' 2 | -------------------------------------------------------------------------------- /lib/rspec/query_limit.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/query_limit/version' 2 | require 'rspec/query_limit/query_counter' 3 | require 'rspec/query_limit/matchers' 4 | -------------------------------------------------------------------------------- /lib/rspec/query_limit/matchers.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/matchers' 2 | 3 | module Rspec 4 | module QueryLimit 5 | RSpec::Matchers.define :query_limit_eq do |expected| 6 | 7 | match do |block| 8 | query_count(&block) == expected 9 | end 10 | 11 | if self.respond_to?(:failure_message) 12 | failure_message do |actual| 13 | failure_text 14 | end 15 | 16 | failure_message_when_negated do |actual| 17 | failure_text_negated 18 | end 19 | else 20 | failure_message_for_should do |actual| 21 | failure_text 22 | end 23 | 24 | failure_message_for_should_not do |actual| 25 | failure_text_negated 26 | end 27 | end 28 | 29 | def query_count(&block) 30 | @counter = ActiveRecord::QueryCounter.new 31 | ActiveSupport::Notifications.subscribed(@counter.to_proc, 'sql.active_record', &block) 32 | @counter.query_count 33 | end 34 | 35 | def supports_block_expectations? 36 | true 37 | end 38 | 39 | def failure_text 40 | "Expected to run exactly #{expected} queries, got #{@counter.query_count}" 41 | end 42 | 43 | def failure_text_negated 44 | "Expected to run other than #{expected} queries, got #{@counter.query_count}" 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/rspec/query_limit/query_counter.rb: -------------------------------------------------------------------------------- 1 | module Rspec 2 | module QueryLimit 3 | module ActiveRecord 4 | class QueryCounter 5 | 6 | attr_reader :query_count 7 | 8 | def initialize 9 | @query_count = 0 10 | end 11 | 12 | def to_proc 13 | lambda(&method(:callback)) 14 | end 15 | 16 | def callback(name, start, finish, message_id, values) 17 | @query_count += 1 unless %w(CACHE SCHEMA).include?(values[:name]) 18 | end 19 | 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/rspec/query_limit/version.rb: -------------------------------------------------------------------------------- 1 | module Rspec 2 | module QueryLimit 3 | VERSION = "0.1.2" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /rspec-query-limit.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'rspec/query_limit/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "rspec-query-limit" 8 | spec.version = Rspec::QueryLimit::VERSION 9 | spec.authors = ["Jakub Cieslar"] 10 | spec.email = ["cieslar.jakub@gmail.com"] 11 | spec.summary = %q{Rspec custom matchers for Rails query counter} 12 | spec.description = %q{Rspec custom matchers for Rails query counter} 13 | spec.homepage = "https://github.com/monterail/rspec-query-limit" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files -z`.split("\x0") 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_dependency 'rspec-rails' 22 | 23 | spec.add_development_dependency "bundler", "~> 1.3" 24 | spec.add_development_dependency "rake" 25 | end 26 | --------------------------------------------------------------------------------