rspec-activerecord-expectations 1.0.0 → 1.3.0

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 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