rspec-activerecord-expectations 1.0.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8090634964dbd7f2f5f8a386cfd0de07257d6f08cd6f9b14606982f983489815
4
- data.tar.gz: 007eef50aa9f1eb61da08fba2a6e7eb6a4f4114c5e0b4fa68f2cb629d4ef86c6
3
+ metadata.gz: 92f14ca413e9e716ea9b3046abc13d3929893185cfef8cdeaf14b7bebc93755c
4
+ data.tar.gz: 93bfa962b7954844560961d66d080fa2c45a3188b8c9b92420d1852e36ce718b
5
5
  SHA512:
6
- metadata.gz: 6c7072fc9e686e66c750e0e117515094d4b6cde5e764a8b07c750f3d8fa64cfd210f010c8816bbef6a6e0edd6c5af7983f6886e1c0fefe05de19b9a4bf2cf0af
7
- data.tar.gz: 8196d1a03e6e7b1c73ab3b8524a759cadf554db7fee528a8a350df601661b0b21b7bb1cc3078ea852634aea64c5b6ba9c755ba9ce0232bcef46735e959dd57b1
6
+ metadata.gz: bb81903bbb00357ab9613a59fb2684bfaad7671fbca955f9876d503f5b179f8ead6d36f248ea24909f30db3ddb6f4fc307c509a181d2fe85d33d1e04006856da
7
+ data.tar.gz: f813c1e8d6e320f6810cc33620fe702c379f47124b6add4bc1bdabde81c4e12c60a6aac5711caf8c23672f838d6185106ffb3695770a7c4002d04404c87929cb
@@ -2,26 +2,66 @@ name: CI
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [ master ]
5
+ branches: [ main ]
6
6
  pull_request:
7
- branches: [ master ]
7
+ branches: [ main ]
8
8
 
9
9
  jobs:
10
- test:
10
+ test_modern:
11
11
  strategy:
12
12
  matrix:
13
13
  os: [ubuntu-latest, macos-latest]
14
- ruby-version: [3.0, 2.7, 2.6, 2.5]
14
+ ruby_version: ['3.0', '2.7']
15
+ rails: ['5.0', '5.1', '5.2', '6.0', '6.1', '7.0']
16
+
15
17
  runs-on: ${{ matrix.os }}
18
+ name: ruby ${{ matrix.ruby_version }}, rails ${{ matrix.rails }}, ${{ matrix.os }}
16
19
 
17
20
  steps:
18
21
  - uses: actions/checkout@v2
19
- - name: Set up Ruby ${{ matrix.ruby-version }}
22
+
23
+ - name: Set up Ruby ${{ matrix.ruby_version }}
20
24
  uses: ruby/setup-ruby@v1
21
25
  with:
22
- ruby-version: ${{ matrix.ruby-version }}
26
+ ruby_version: ${{ matrix.ruby_version }}
23
27
  bundler-cache: true
24
- - name: Install dependencies
25
- run: bundle install
28
+
29
+ - name: Install Gems w/ Rails ${{ matrix.rails }}
30
+ env:
31
+ MATRIX_RAILS_VERSION: ${{ matrix.rails }}
32
+ run: |
33
+ export BUNDLE_GEMFILE="${GITHUB_WORKSPACE}/gemfiles/rails_${MATRIX_RAILS_VERSION}.gemfile"
34
+ gem install bundler
35
+ bundle install --jobs 4 --retry 3
36
+
37
+ - name: Run tests
38
+ run: bundle exec rspec
39
+
40
+ test_legacy:
41
+ strategy:
42
+ matrix:
43
+ ruby_version: ['2.6', '2.5', '2.4', '2.3']
44
+ rails: ['5.0', '5.1', '5.2']
45
+
46
+ runs-on: ubuntu-latest
47
+ name: legacy ruby ${{ matrix.ruby_version }}, rails ${{ matrix.rails }}
48
+
49
+ steps:
50
+ - uses: actions/checkout@v2
51
+
52
+ - name: Set up Ruby ${{ matrix.ruby_version }}
53
+ uses: ruby/setup-ruby@v1
54
+ with:
55
+ ruby_version: ${{ matrix.ruby_version }}
56
+ bundler-cache: true
57
+
58
+ - name: Install Gems w/ Rails ${{ matrix.rails }}
59
+ env:
60
+ MATRIX_RAILS_VERSION: ${{ matrix.rails }}
61
+ run: |
62
+ export BUNDLE_GEMFILE="${GITHUB_WORKSPACE}/gemfiles/rails_${MATRIX_RAILS_VERSION}.gemfile"
63
+ gem install bundler
64
+ bundle install --jobs 4 --retry 3
65
+
26
66
  - name: Run tests
27
67
  run: bundle exec rspec
data/.gitignore CHANGED
@@ -10,4 +10,7 @@
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
12
 
13
- # this is a gem, don't track the specific deps
13
+ # this is a gem, don't track the specific deps or build artifact
14
+ Gemfile.lock
15
+ rails_*.gemfile.lock
16
+ rspec-activerecord-expectations-*.gem
data/Appraisals ADDED
@@ -0,0 +1,23 @@
1
+ appraise "rails-7.0" do
2
+ gem "rails", "~> 7.0.0"
3
+ end
4
+
5
+ appraise "rails-6.1" do
6
+ gem "rails", "~> 6.1.0"
7
+ end
8
+
9
+ appraise "rails-6.0" do
10
+ gem "rails", "~> 6.0.3", '>= 6.0.3.2'
11
+ end
12
+
13
+ appraise "rails-5.2" do
14
+ gem "rails", "~> 5.2.4", '>= 5.2.4.3'
15
+ end
16
+
17
+ appraise "rails-5.1" do
18
+ gem "rails", "~> 5.1.7"
19
+ end
20
+
21
+ appraise "rails-5.0" do
22
+ gem "rails", "~> 5.0.7", '>= 5.0.7.2'
23
+ end
data/CHANGELOG.md CHANGED
@@ -1,7 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.3.0] - 2020-12-31
4
+ - Add `repeatedly_load` matcher
5
+ - Add query type matchers for `load_queries`, `schema_queries`, `transaction_queries`, `destroy_queries`
6
+ - Allow singular version of all query types (e.g. `transaction_queries` vs `transaction_query`)
7
+ - Fix failure message for `execute.exactly` matcher
8
+
9
+ ## [1.2.0] - 2020-12-31
10
+ - Add `query` as a synonym for `queries`
11
+ - Ignore schema and transaction queries in query count
12
+ - Add beginning of recording specific query types
13
+ - Add query count matcher for `exactly`
14
+
15
+ ## [1.1.0] - 2020-12-30
16
+ - Add query count matchers for e.g. `less_than_or_equal_to`, `greater_than`
17
+
18
+ ## [1.0.1] - 2020-12-30
19
+ - Pin all the dependencies to a proper working subset
20
+ - Expand testing to many rails / ruby combinations
21
+
3
22
  ## [1.0.0] - 2020-12-30
4
- - Basic gem w/ all the fixins and README and such.
5
- - Add `less_than` comparison, and `queries` type.
6
- - Basic tests in place, and not bad tbh.
23
+ - Basic gem w/ all the fixins and README and such
24
+ - Add `less_than` comparison, and `queries` type
25
+ - Basic tests in place, and not bad tbh
7
26
 
data/README.md CHANGED
@@ -91,11 +91,78 @@ That's it! Refactor your report to be more efficient and then leave the test in
91
91
  place to make sure that future developers don't accidentally cause a
92
92
  performance regression.
93
93
 
94
- ## Supported Matchers
94
+ ## Preventing Repeated Load (N+1) Queries
95
+
96
+ Reloading, whether by e.g. `Album.find` or `album.tracks` are both antipatterns
97
+ within your code. They will load from the database for every iteration in a
98
+ loop, unless you load records outside the loop, cache responses, or use an
99
+ eager loading mechanism like `includes`. These sorts of queries are often
100
+ referred to as N+1 queries.
101
+
102
+ This sort of query can be prevented using the `repeatedly_load` expectation.
95
103
 
96
104
  ```ruby
105
+ expect {}.not_to repeatedly_load('SomeActiveRecordClass')
106
+ ```
107
+
108
+ This matcher will track ActiveRecord's built in load methods to prevent those
109
+ N+1 situations. Using eager loading (e.g. `Track.all.includes(:album)`) will
110
+ allow these expectations to pass as expected!
111
+
112
+ **Note:** At the moment, this expectation will fail if you use a mechanism
113
+ that loads records in batches, such as with `find_in_batches`. This will cause
114
+ records to be "repeatedly loaded", but this is actually expected behavior in
115
+ this case. If your tests load a relatively small number of records (which is
116
+ probable), your tests won't fail. But it is possible.
117
+
118
+ ## Counting Queries
119
+
120
+ ### Types of Comparisons
121
+
122
+ Several comparison types are available, along with some aliases to allow for
123
+ easier to read tests.
124
+
125
+ ```ruby
126
+ expect {}.to execute.less_than(20).queries
97
127
  expect {}.to execute.fewer_than(20).queries
98
- expect {}.to execute.less_than(20).queries # alias for fewer_than
128
+
129
+ expect {}.to execute.less_than_or_equal_to(20).queries
130
+ expect {}.to execute.at_most(20).queries
131
+
132
+ expect {}.to execute.greater_than(20).queries
133
+ expect {}.to execute.more_than(20).queries
134
+
135
+ expect {}.to execute.greater_than_or_equal_to(20).queries
136
+ expect {}.to execute.at_least(20).queries
137
+
138
+ expect {}.to execute.exactly(20).queries
139
+
140
+ expect {}.to execute.at_least(2).queries
141
+ expect {}.to execute.at_least(1).query # singular form also accepted
142
+ ```
143
+
144
+ ### Specific Query Types
145
+
146
+ You can of course make assertions for the total number of queries executed, but
147
+ sometimes it's more valuable to assert particular _types_ of queries, such as
148
+ inserts or find statements. Matchers are supported for this as well!
149
+
150
+ **Note:** Transaction (for example, `ROLLBACK`) queries are not counted in any of these
151
+ categories, nor are queries that load the DB schema.
152
+
153
+ **Note:** Destroy and delete queries are both condensed into the matcher for
154
+ `destroy_queries`.
155
+
156
+ ```ruby
157
+ expect {}.to execute.exactly(20).queries
158
+
159
+ expect {}.to execute.exactly(20).insert_queries
160
+ expect {}.to execute.exactly(20).load_queries
161
+ expect {}.to execute.exactly(20).destroy_queries
162
+ expect {}.to execute.exactly(20).exists_queries
163
+
164
+ expect {}.to execute.exactly(20).schema_queries
165
+ expect {}.to execute.exactly(20).transaction_queries
99
166
  ```
100
167
 
101
168
  ## Future Planned Functionality
@@ -103,17 +170,11 @@ expect {}.to execute.less_than(20).queries # alias for fewer_than
103
170
  This gem still has lots of future functionality. See below.
104
171
 
105
172
  ```ruby
106
- expect {}.to execute.more_than(20).activerecord_queries
107
- expect {}.to execute.fewer_than(20).insert_queries
108
- expect {}.to execute.exactly(5).queries
109
- expect {}.to execute.at_least(5).handrolled_queries
110
-
111
- expect {}.to execute.less_than(20).delete_queries
112
- expect {}.to execute.less_than(20).load_queries
113
- expect {}.to execute.less_than(20).schema_queries
114
- expect {}.to execute.less_than(20).exists_queries
115
- expect {}.to execute.less_than(20).queries
116
- expect {}.to execute.less_than(20).queries_of_type("Audited::Audit Load")
173
+ expect {}.to execute.at_least(2).queries_of_type("Audited::Audit Load")
174
+ expect {}.to execute.at_least(2).load_queries("Audited::Audit")
175
+
176
+ expect {}.to execute.at_least(2).activerecord_queries
177
+ expect {}.to execute.at_least(2).hand_rolled_queries
117
178
 
118
179
  expect {}.not_to rollback_transaction.exactly(5).times
119
180
  expect {}.not_to commit_transaction.once
@@ -123,18 +184,10 @@ expect {}.to create.exactly(5).of_type(User)
123
184
  expect {}.to insert.exactly(5).subscription_changes
124
185
  expect {}.to update.exactly(2).of_any_type
125
186
  expect {}.to delete.exactly(2).of_any_type
126
-
127
- expect {}.not_to repeatedly_load(Audited::Audit)
128
-
129
- at_least, more_than, at_most, less_than, lteq?
130
- exactly(1) => once?
131
187
  ```
132
188
 
133
- - ignore transactionals (begin / rollback)
134
- - `name: Foo Load`
135
- - differentiate AR queries from generic ones? arbitrary execution somehow?
136
- - warn about warmup
137
- - make sure we don't smite any built in methods (or from other libs)
189
+ - warn if we smite any built in methods (or methods from other libs)
190
+ - support Rails 6 bulk insert (still one query)
138
191
 
139
192
  ## Development
140
193
 
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_RETRY: "1"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 12.0"
6
+ gem "rspec", "~> 3.0"
7
+ gem "rails", "~> 5.0.7", ">= 5.0.7.2"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 12.0"
6
+ gem "rspec", "~> 3.0"
7
+ gem "rails", "~> 5.1.7"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 12.0"
6
+ gem "rspec", "~> 3.0"
7
+ gem "rails", "~> 5.2.4", ">= 5.2.4.3"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 12.0"
6
+ gem "rspec", "~> 3.0"
7
+ gem "rails", "~> 6.0.3", ">= 6.0.3.2"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 12.0"
6
+ gem "rspec", "~> 3.0"
7
+ gem "rails", "~> 6.1.0"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 12.0"
6
+ gem "rspec", "~> 3.0"
7
+ gem "rails", "~> 7.0.0"
8
+
9
+ gemspec path: "../"
@@ -1,23 +1,42 @@
1
1
  module RSpec::ActiveRecord::Expectations
2
2
  class Collector
3
3
  def initialize
4
- @counts = {
5
- queries: 0
6
- }
4
+ @inspector = QueryInspector.new
5
+ @by_name = {}
6
+ @counts = QueryInspector.valid_query_types.each_with_object({}) do |query_type, hash|
7
+ hash[query_type] = 0
8
+ end
7
9
 
8
- ActiveSupport::Notifications.subscribe("sql.active_record", method(:record_query))
10
+ @subscription = ActiveSupport::Notifications.subscribe("sql.active_record", method(:record_query))
11
+ end
12
+
13
+ def finalize
14
+ ActiveSupport::Notifications.unsubscribe(@subscription)
9
15
  end
10
16
 
11
17
  def queries_of_type(type)
12
- @counts.fetch(type)
18
+ @counts[type] || (raise ArgumentError, "Sorry, #{type} is not a valid kind of query")
13
19
  end
14
20
 
15
21
  def valid_type?(type)
16
22
  @counts.include? type
17
23
  end
18
24
 
25
+ def calls_by_name(name)
26
+ @by_name.fetch(name, 0)
27
+ end
28
+
19
29
  def record_query(*_unused, data)
20
- @counts[:queries] += 1
30
+ categories = @inspector.categorize(data)
31
+
32
+ categories.each do |category|
33
+ @counts[category] += 1
34
+ rescue NoMethodError
35
+ raise "tried to add to to #{category} but it doesn't exist"
36
+ end
37
+
38
+ @by_name[data[:name]] ||= 0
39
+ @by_name[data[:name]] += 1
21
40
  end
22
41
  end
23
42
  end
@@ -0,0 +1,29 @@
1
+ module RSpec::ActiveRecord::Expectations
2
+ module Matchers
3
+ class LoadMatcher
4
+ def initialize(klass)
5
+ @collector = Collector.new
6
+ @klass = klass.to_s
7
+ end
8
+
9
+ def supports_block_expectations?
10
+ true
11
+ end
12
+
13
+ def matches?(block)
14
+ block.call
15
+
16
+ @count = @collector.calls_by_name("#{@klass} Load")
17
+ @count > 1
18
+ end
19
+
20
+ def failure_message
21
+ "expected block to repeatedly load #{@klass}, but it was loaded #{@count} times"
22
+ end
23
+
24
+ def failure_message_when_negated
25
+ "expected block not to repeatedly load #{@klass}, but it was loaded #{@count} times"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,141 @@
1
+ module RSpec::ActiveRecord::Expectations
2
+ module Matchers
3
+ class QueryCountMatcher
4
+ attr_reader :failure_message, :failure_message_when_negated
5
+
6
+ def initialize
7
+ @collector = Collector.new
8
+
9
+ @match_method = nil
10
+ @comparison = nil
11
+ @query_type = nil
12
+ end
13
+
14
+ def supports_block_expectations?
15
+ true
16
+ end
17
+
18
+ def matches?(block)
19
+ raise NoComparisonError unless @match_method
20
+ raise NoQueryTypeError unless @collector.valid_type?(@query_type)
21
+
22
+ block.call
23
+ result = @match_method.call
24
+ @collector.finalize
25
+
26
+ result
27
+ end
28
+
29
+ # COMPARISON TYPES
30
+
31
+ def less_than(n)
32
+ @comparison = n
33
+ @match_method = method(:compare_less_than)
34
+ self
35
+ end
36
+ alias_method :fewer_than, :less_than
37
+
38
+ def less_than_or_equal_to(n)
39
+ @comparison = n
40
+ @match_method = method(:compare_less_than_or_equal_to)
41
+ self
42
+ end
43
+ alias_method :at_most, :less_than_or_equal_to
44
+
45
+ def greater_than(n)
46
+ @comparison = n
47
+ @match_method = method(:compare_greater_than)
48
+ self
49
+ end
50
+ alias_method :more_than, :greater_than
51
+
52
+ def greater_than_or_equal_to(n)
53
+ @comparison = n
54
+ @match_method = method(:compare_greater_than_or_equal_to)
55
+ self
56
+ end
57
+ alias_method :at_least, :greater_than_or_equal_to
58
+
59
+ def exactly(n)
60
+ @comparison = n
61
+ @match_method = method(:compare_exactly)
62
+ self
63
+ end
64
+
65
+ # TARGET QUERY TYPES
66
+
67
+ RSpec::ActiveRecord::Expectations::QueryInspector.valid_query_types.each do |type|
68
+ define_method type do
69
+ @query_type = type
70
+ self
71
+ end
72
+
73
+ singularized_type = type.to_s.gsub('queries', 'query')
74
+ if singularized_type != type.to_s
75
+ define_method singularized_type do
76
+ @query_type = type
77
+ self
78
+ end
79
+ end
80
+ end
81
+
82
+ # TODO singularize everything
83
+ alias_method :query, :queries
84
+
85
+ private
86
+
87
+ def humanized_query_type
88
+ @query_type.to_s.gsub("_", " ")
89
+ end
90
+
91
+ # MATCHERS
92
+
93
+ def compare_less_than
94
+ count = @collector.queries_of_type(@query_type)
95
+
96
+ @failure_message = "expected block to execute fewer than #{@comparison} #{humanized_query_type}, but it executed #{count}"
97
+ @failure_message_when_negated = "expected block not to execute fewer than #{@comparison} #{humanized_query_type}, but it executed #{count}"
98
+
99
+ count < @comparison
100
+ end
101
+
102
+ def compare_less_than_or_equal_to
103
+ count = @collector.queries_of_type(@query_type)
104
+
105
+ # boy this negated operator is confusing. don't use that plz.
106
+ @failure_message = "expected block to execute at most #{@comparison} #{humanized_query_type}, but it executed #{count}"
107
+ @failure_message_when_negated = "expected block not to execute any less than #{@comparison} #{humanized_query_type}, but it executed #{count}"
108
+
109
+ count <= @comparison
110
+ end
111
+
112
+ def compare_greater_than
113
+ count = @collector.queries_of_type(@query_type)
114
+
115
+ @failure_message = "expected block to execute greater than #{@comparison} #{humanized_query_type}, but it executed #{count}"
116
+ @failure_message_when_negated = "expected block not to execute greater than #{@comparison} #{humanized_query_type}, but it executed #{count}"
117
+
118
+ count > @comparison
119
+ end
120
+
121
+ def compare_greater_than_or_equal_to
122
+ count = @collector.queries_of_type(@query_type)
123
+
124
+ # see above, negating this matcher is so confusing.
125
+ @failure_message = "expected block to execute at least #{@comparison} #{humanized_query_type}, but it executed #{count}"
126
+ @failure_message_when_negated = "expected block not to execute any more than #{@comparison} #{humanized_query_type}, but it executed #{count}"
127
+
128
+ count >= @comparison
129
+ end
130
+
131
+ def compare_exactly
132
+ count = @collector.queries_of_type(@query_type)
133
+
134
+ @failure_message = "expected block to execute exactly #{@comparison} #{humanized_query_type}, but it executed #{count}"
135
+ @failure_message_when_negated = "expected block not to execute exactly #{@comparison} #{humanized_query_type}, but it executed #{count}"
136
+
137
+ count == @comparison
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,28 @@
1
+ module RSpec::ActiveRecord::Expectations
2
+ class QueryInspector
3
+ def self.valid_query_types
4
+ [:queries, :schema_queries, :transaction_queries, :insert_queries,
5
+ :load_queries, :destroy_queries, :exists_queries]
6
+ end
7
+
8
+ def categorize(query)
9
+ if query[:name] == "SCHEMA"
10
+ [:schema_queries]
11
+ elsif query[:name] == "TRANSACTION"
12
+ [:transaction_queries]
13
+ elsif query[:name] =~ /Create$/
14
+ [:queries, :insert_queries]
15
+ elsif query[:name] =~ /Load$/
16
+ [:queries, :load_queries]
17
+ elsif query[:name] =~ /Destroy$/
18
+ [:queries, :destroy_queries]
19
+ elsif query[:name] =~ /Delete All$/
20
+ [:queries, :destroy_queries]
21
+ elsif query[:name] =~ /Exists\?$/
22
+ [:queries, :exists_queries]
23
+ else
24
+ [:queries]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -4,10 +4,16 @@ module RSpec
4
4
  def execute
5
5
  Matchers::QueryCountMatcher.new
6
6
  end
7
+
8
+ def repeatedly_load(klass)
9
+ Matchers::LoadMatcher.new(klass)
10
+ end
7
11
  end
8
12
  end
9
13
  end
10
14
 
11
15
  require_relative 'expectations/errors'
16
+ require_relative 'expectations/query_inspector'
12
17
  require_relative 'expectations/collector'
13
- require_relative 'expectations/matchers/query_count'
18
+ require_relative 'expectations/matchers/query_count_matcher'
19
+ require_relative 'expectations/matchers/load_matcher'
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "rspec-activerecord-expectations"
3
- spec.version = '1.0.0'
3
+ spec.version = '1.3.0'
4
4
  spec.authors = ["Joseph Mastey"]
5
5
  spec.email = ["hello@joemastey.com"]
6
6
 
@@ -22,8 +22,9 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
24
 
25
- spec.add_dependency "activerecord", ">= 5.0", "< 7.0" # TODO run tests to loosen me
25
+ spec.add_dependency "activerecord", ">= 5.0.0", "< 7.1.0"
26
26
  spec.add_dependency "sqlite3", "~> 1.0"
27
27
 
28
28
  spec.add_development_dependency "pry", "~> 0.0"
29
+ spec.add_development_dependency "appraisal", "~> 2"
29
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-activerecord-expectations
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joseph Mastey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-30 00:00:00.000000000 Z
11
+ date: 2022-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: 5.0.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '7.0'
22
+ version: 7.1.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '5.0'
29
+ version: 5.0.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '7.0'
32
+ version: 7.1.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: sqlite3
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -58,6 +58,20 @@ dependencies:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '0.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: appraisal
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2'
61
75
  description: Adds new matchers to rspec to help you test whether your code is executing
62
76
  an unreasonable number of queries.
63
77
  email:
@@ -71,16 +85,26 @@ files:
71
85
  - ".rspec"
72
86
  - ".ruby-version"
73
87
  - ".travis.yml"
88
+ - Appraisals
74
89
  - CHANGELOG.md
75
90
  - CODE_OF_CONDUCT.md
76
91
  - Gemfile
77
92
  - LICENSE.txt
78
93
  - README.md
94
+ - gemfiles/.bundle/config
95
+ - gemfiles/rails_5.0.gemfile
96
+ - gemfiles/rails_5.1.gemfile
97
+ - gemfiles/rails_5.2.gemfile
98
+ - gemfiles/rails_6.0.gemfile
99
+ - gemfiles/rails_6.1.gemfile
100
+ - gemfiles/rails_7.0.gemfile
79
101
  - lib/rspec/activerecord-expectations.rb
80
102
  - lib/rspec/activerecord/expectations.rb
81
103
  - lib/rspec/activerecord/expectations/collector.rb
82
104
  - lib/rspec/activerecord/expectations/errors.rb
83
- - lib/rspec/activerecord/expectations/matchers/query_count.rb
105
+ - lib/rspec/activerecord/expectations/matchers/load_matcher.rb
106
+ - lib/rspec/activerecord/expectations/matchers/query_count_matcher.rb
107
+ - lib/rspec/activerecord/expectations/query_inspector.rb
84
108
  - rspec-activerecord-expectations.gemspec
85
109
  homepage: https://github.com/jmmastey/rspec-activerecord-expectations
86
110
  licenses:
@@ -1,57 +0,0 @@
1
- module RSpec::ActiveRecord::Expectations
2
- module Matchers
3
- class QueryCountMatcher
4
- attr_reader :failure_message, :failure_message_when_negated
5
-
6
- def initialize
7
- @collector = Collector.new
8
-
9
- @match_method = nil
10
- @comparison = nil
11
- @query_type = nil
12
- end
13
-
14
- def supports_block_expectations?
15
- true
16
- end
17
-
18
- def matches?(block)
19
- raise NoComparisonError unless @match_method
20
- raise NoQueryTypeError unless @collector.valid_type?(@query_type)
21
-
22
- result = block.call
23
-
24
- !!@match_method.call
25
- end
26
-
27
- # COMPARISON TYPES
28
-
29
- def fewer_than(n)
30
- @comparison = n
31
- @match_method = method(:match_fewer_than)
32
- self
33
- end
34
- alias_method :less_than, :fewer_than
35
-
36
- # TARGET QUERY TYPES
37
-
38
- def queries
39
- @query_type = :queries
40
- self
41
- end
42
-
43
- private
44
-
45
- # MATCHERS
46
-
47
- def match_fewer_than
48
- count = @collector.queries_of_type(@query_type)
49
-
50
- @failure_message = "expected block to execute fewer than #{@comparison} queries, but it executed #{count}"
51
- @failure_message_when_negated = "expected block not to execute fewer than #{@comparison} queries, but it executed #{count}"
52
-
53
- count < @comparison
54
- end
55
- end
56
- end
57
- end