ar-query-matchers 0.5.2.pre.6 → 0.7.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: a03e79681fb7644c916a5f3c019a5e9484f8b7176e21b6c91beae9bd2d9eb520
4
- data.tar.gz: 1a1d6f97aab26efa604ae32dd2d4b56164e4616ff87b2ffbfb84812e87e57a75
3
+ metadata.gz: e73f1af783ac0aea8a1342b20e01d139106e29c2c4048bf3d30086a13675f204
4
+ data.tar.gz: 64b309608ae8f52ba8d7a998ea0e7658eebb2ee7d11122d8e1c0934f042131b6
5
5
  SHA512:
6
- metadata.gz: 960fb79a70f3875f606d67364227ad6b0af6440b18fed4e885a8a44614d61f484ad7cdc33e6040b114b568d320edcc045a212a9db0c570577a039bb7a04b09c6
7
- data.tar.gz: 379ef8320c724772bcf9fc38dda46a86fa8a24a8690a9547a13973065dcebef9fae17f77a636e059463f91afbba2aa341164ddadbe900e58fb625d3f1b64a5c4
6
+ metadata.gz: 13b5fe44e89c7e0df944882d4d15dac2ff72b8fe3a679591c46b15015587159246c4e2344d2e604b6a31a0ee6dc5a450eacd85ef638d90d578f1b4073fca2f58
7
+ data.tar.gz: 61d48528375466023a71bf17ad2e453ec62ce5520f43200f88de94ff2f116522be3694fe2c0116f83093fa5f1087cf4774471143e63deb8f465dd03173bda8cd
data/CHANGELOG.md CHANGED
@@ -6,9 +6,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
- ## [0.5.2] - 2021-05-18
9
+ ## [0.7.0] - 2022-01-27
10
+ ### Added
11
+ - A new matcher, `only_load_at_most_models`, will do a less-than-or-equal-to (<=) check on model counts. This is a method that makes tests less noisy as performance gets better.
12
+
13
+ ## [0.6.0] - 2022-01-05
14
+ ### Changed
15
+ - Support Rails 7
16
+
17
+ ## [0.5.3] - 2021-05-26
18
+ ### Changed
19
+ - Re-release of previous version due to accidental premature release
20
+
21
+ ## [0.5.2] - 2021-05-26
10
22
  ### Changed
11
- - Removes 'SELECT' from MODEL_SQL_PATTERN to allow for more granular SQL statement matching
23
+ - Update MODEL_SQL_PATTERN to allow for more accurate matches against SQL strings
12
24
 
13
25
  ## [0.5.1] - 2020-11-19
14
26
  ### Changed
@@ -23,7 +23,7 @@ module ArQueryMatchers
23
23
  # for inserts, name is always 'SQL', we have to rely on pattern matching the query string.
24
24
  select_from_table = sql.match(TABLE_NAME_SQL_PATTERN)
25
25
 
26
- [TableName.new(select_from_table[:table_name])] if select_from_table
26
+ TableName.new(select_from_table[:table_name]) if select_from_table
27
27
  end
28
28
  end
29
29
  end
@@ -15,15 +15,24 @@ module ArQueryMatchers
15
15
  end
16
16
 
17
17
  class LoadQueryFilter < Queries::QueryFilter
18
+ # Matches named SQL operations like the following:
19
+ # 'User Load'
20
+ MODEL_LOAD_PATTERN = /\A(?<model_name>[\w:]+) (Load|Exists)\Z/.freeze
21
+
18
22
  # Matches unnamed SQL operations like the following:
19
- # "SELECT * FROM `users` ..."
23
+ # "SELECT COUNT(*) FROM `users` ..."
20
24
  MODEL_SQL_PATTERN = /SELECT (?:(?!SELECT).)* FROM [`"](?<table_name>[^`"]+)[`"]/.freeze
21
25
 
22
- def filter_map(_name, sql)
23
- # Pattern-matching on the table name in a SELECT ... FROM and looking
26
+ def filter_map(name, sql)
27
+ # First check for a `SELECT * FROM` query that ActiveRecord has
28
+ # helpfully named for us in the payload
29
+ match = name.match(MODEL_LOAD_PATTERN)
30
+ return ModelName.new(match[:model_name]) if match
31
+
32
+ # Fall back to pattern-matching on the table name in a COUNT and looking
24
33
  # up the table name from ActiveRecord's loaded descendants.
25
- selects_from_table = sql.scan(MODEL_SQL_PATTERN)
26
- selects_from_table.map { |(table_name)| TableName.new(table_name) } unless selects_from_table.empty?
34
+ select_from_table = sql.match(MODEL_SQL_PATTERN)
35
+ TableName.new(select_from_table[:table_name]) if select_from_table
27
36
  end
28
37
  end
29
38
  end
@@ -81,13 +81,9 @@ module ArQueryMatchers
81
81
  # Given a `sql.active_record` event, figure out which model is being
82
82
  # accessed. Some of the simpler queries have a :name key that makes this
83
83
  # really easy. Others require parsing the SQL by hand.
84
- results = @query_filter.filter_map(payload[:name] || '', payload[:sql] || '')
85
-
86
- # Round to microseconds
87
- results&.each do |result|
88
- model_name = result.model_name
89
- next unless model_name
84
+ model_name = @query_filter.filter_map(payload[:name] || '', payload[:sql] || '')&.model_name
90
85
 
86
+ if model_name
91
87
  comment = payload[:sql].match(MARGINALIA_SQL_COMMENT_PATTERN)
92
88
  queries[model_name][:lines] << comment[:line] if comment
93
89
  queries[model_name][:count] += 1
@@ -22,7 +22,7 @@ module ArQueryMatchers
22
22
  def filter_map(_name, sql)
23
23
  # for updates, name is always 'SQL', we have to rely on pattern matching on the query string instead.
24
24
  select_from_table = sql.match(TABLE_NAME_SQL_PATTERN)
25
- [TableName.new(select_from_table[:table_name])] if select_from_table
25
+ TableName.new(select_from_table[:table_name]) if select_from_table
26
26
  end
27
27
  end
28
28
  end
@@ -97,6 +97,49 @@ module ArQueryMatchers
97
97
  end
98
98
  end
99
99
 
100
+ # The following will fail because the call to `User` is not expected, even
101
+ # though the Payroll count is correct:
102
+ #
103
+ # expect {
104
+ # Payroll.count
105
+ # Payroll.count
106
+ # User.count
107
+ # }.to only_load_at_most_models(
108
+ # 'Payroll' => 2,
109
+ # )
110
+ #
111
+ # The following will succeed because the counts are exact:
112
+ #
113
+ # expect {
114
+ # Payroll.count
115
+ # Payroll.count
116
+ # User.count
117
+ # }.to only_load_at_most_models(
118
+ # 'Payroll' => 2,
119
+ # 'User' => 1,
120
+ # )
121
+ #
122
+ RSpec::Matchers.define(:only_load_at_most_models) do |expected = {}|
123
+ include MatcherConfiguration
124
+ include MatcherErrors
125
+
126
+ match do |block|
127
+ @query_stats = Queries::LoadCounter.instrument(&block)
128
+ expected_queries = Utility.remove_superfluous_expectations(expected)
129
+ actual_queries = @query_stats.query_counts
130
+
131
+ all_models = expected_queries.keys | actual_queries.keys
132
+
133
+ all_models.each do |model|
134
+ expect(actual_queries[model] || 0).to be <= expected_queries[model]
135
+ end
136
+ end
137
+
138
+ def failure_text
139
+ expectation_failed_message('load at most')
140
+ end
141
+ end
142
+
100
143
  RSpec::Matchers.define(:not_load_any_models) do
101
144
  include MatcherConfiguration
102
145
  include MatcherErrors
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ar-query-matchers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2.pre.6
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matan Zruya
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-26 00:00:00.000000000 Z
11
+ date: 2022-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -17,9 +17,6 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '4.0'
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '7.0'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +24,6 @@ dependencies:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '4.0'
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '7.0'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: activesupport
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -37,9 +31,6 @@ dependencies:
37
31
  - - ">="
38
32
  - !ruby/object:Gem::Version
39
33
  version: '4.0'
40
- - - "<"
41
- - !ruby/object:Gem::Version
42
- version: '7.0'
43
34
  type: :runtime
44
35
  prerelease: false
45
36
  version_requirements: !ruby/object:Gem::Requirement
@@ -47,9 +38,6 @@ dependencies:
47
38
  - - ">="
48
39
  - !ruby/object:Gem::Version
49
40
  version: '4.0'
50
- - - "<"
51
- - !ruby/object:Gem::Version
52
- version: '7.0'
53
41
  - !ruby/object:Gem::Dependency
54
42
  name: rspec
55
43
  requirement: !ruby/object:Gem::Requirement
@@ -92,6 +80,20 @@ dependencies:
92
80
  - - ">="
93
81
  - !ruby/object:Gem::Version
94
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
95
97
  - !ruby/object:Gem::Dependency
96
98
  name: rake
97
99
  requirement: !ruby/object:Gem::Requirement
@@ -186,9 +188,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
186
188
  version: '0'
187
189
  required_rubygems_version: !ruby/object:Gem::Requirement
188
190
  requirements:
189
- - - ">"
191
+ - - ">="
190
192
  - !ruby/object:Gem::Version
191
- version: 1.3.1
193
+ version: '0'
192
194
  requirements: []
193
195
  rubygems_version: 3.0.3.1
194
196
  signing_key: