required_query_attributes 0.1.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 +7 -0
- data/.circleci/config.yml +14 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +7 -0
- data/.ruby-version +1 -0
- data/Gemfile +6 -0
- data/LICENSE.md +21 -0
- data/README.md +68 -0
- data/Rakefile +8 -0
- data/lib/required_query_attributes.rb +13 -0
- data/lib/required_query_attributes/model.rb +32 -0
- data/lib/required_query_attributes/relation.rb +29 -0
- data/lib/required_query_attributes/version.rb +5 -0
- data/required_query_attributes.gemspec +40 -0
- metadata +173 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 9983089c7aaeb16d4d63ecf5054937faaeefd2c9
|
|
4
|
+
data.tar.gz: 0b58629ae1b2a85a0f8343fc0ad1657e5ac9624c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c17fd158de571d0cf5e2ede9cc015a6adb58ff28eef54b1a7584b782a43ec398d1b9aee8508fe0b2a0588224647cd6d3a55e9acace2ebf49e77f837bab00d298
|
|
7
|
+
data.tar.gz: 392a7d28d5a63c04b19cea264625357b961f296ef1d90345dc1761e810489f5d7ff933d7552771fa763e19ee424a114fc55cc9911cbb4241163a3f48b5feecc9
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Ruby CircleCI 2.0 configuration file
|
|
2
|
+
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
|
|
3
|
+
version: 2
|
|
4
|
+
jobs:
|
|
5
|
+
build:
|
|
6
|
+
docker:
|
|
7
|
+
- image: circleci/ruby:2.4.3
|
|
8
|
+
working_directory: ~/repo
|
|
9
|
+
steps:
|
|
10
|
+
- checkout
|
|
11
|
+
- run: bundle install --jobs=4 --retry=3 --path vendor/bundle
|
|
12
|
+
- run: bundle exec bundle-audit update && bundle exec bundle-audit check
|
|
13
|
+
- run: bundle exec rubocop
|
|
14
|
+
- run: bundle exec rspec
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.4.3
|
data/Gemfile
ADDED
data/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Wealthsimple
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, 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,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# RequiredQueryAttributes [](https://circleci.com/gh/wealthsimple/required_query_attributes)
|
|
2
|
+
|
|
3
|
+
Enforce presence of specific columns in ActiveRecord queries.
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
If you have a `records` table like this:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
create_table 'records' do |t|
|
|
11
|
+
t.date 'date'
|
|
12
|
+
t.string 'text'
|
|
13
|
+
end
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
You might want to make sure that `SELECT` queries always provide a predicate condition on `date`.
|
|
17
|
+
|
|
18
|
+
It could be because you have a very large table, and your only good index is on `date`.
|
|
19
|
+
|
|
20
|
+
Or maybe your data is partitioned by `date`, and thus not providing it will trigger full table scan.
|
|
21
|
+
|
|
22
|
+
`RequiredQueryAttributes` ensures all `SELECT` queries provide at least one predicate condition on the desired columns, or they will fail with an exception:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
RequiredQueryAttributes::RequiredAttributeMissing:
|
|
26
|
+
Required attributes ["date"] missing from query:
|
|
27
|
+
SELECT "records".* FROM "records" WHERE "records"."text" = 'string_to_find'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
Add this line to your application's `Gemfile` and execute `bundle`:
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
gem 'required_query_attributes'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
Using the same `records` example:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
create_table 'records' do |t|
|
|
44
|
+
t.date 'date'
|
|
45
|
+
t.string 'text'
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Configure your class:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
class Record < ActiveRecord::Base
|
|
53
|
+
include RequiredQueryAttributes
|
|
54
|
+
|
|
55
|
+
require_query_attribute :date
|
|
56
|
+
|
|
57
|
+
# Add additional attributes if desired
|
|
58
|
+
# require_query_attribute :text
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Profit.
|
|
63
|
+
|
|
64
|
+
## FYI
|
|
65
|
+
|
|
66
|
+
This gem tries to do its job as cleanly as possible, only changing `ActiveRecord` behaviour when necessary.
|
|
67
|
+
|
|
68
|
+
There are no global changes to `ActiveRecord`: only models which you choose to install `RequiredQueryAttributes` are affected.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_record'
|
|
4
|
+
|
|
5
|
+
require 'required_query_attributes/model'
|
|
6
|
+
require 'required_query_attributes/relation'
|
|
7
|
+
require 'required_query_attributes/version'
|
|
8
|
+
|
|
9
|
+
module RequiredQueryAttributes
|
|
10
|
+
def self.included(base)
|
|
11
|
+
base.send :extend, Model::ClassMethods
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module RequiredQueryAttributes
|
|
2
|
+
# Extensions to `ActiveRecord::Base`.
|
|
3
|
+
# We want to have the smallest possible footprint here.
|
|
4
|
+
module Model
|
|
5
|
+
module ClassMethods
|
|
6
|
+
# Declare this in your model to enforce that the provided attribute
|
|
7
|
+
# must be used in at least one condition on SELECT statements.
|
|
8
|
+
#
|
|
9
|
+
# Parameters:
|
|
10
|
+
#
|
|
11
|
+
# - name - Attribute to enforce presence in queries.
|
|
12
|
+
#
|
|
13
|
+
# @api public
|
|
14
|
+
def require_query_attribute(attribute)
|
|
15
|
+
@required_query_attributes ||= []
|
|
16
|
+
@required_query_attributes << attribute.to_s
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
attr_reader :required_query_attributes
|
|
20
|
+
|
|
21
|
+
def self.extended(base)
|
|
22
|
+
# Attach our own RequiredQueryAttributes::Relation
|
|
23
|
+
# to existing delegate factories
|
|
24
|
+
base.instance_eval do
|
|
25
|
+
@relation_delegate_cache.values.each do |klass|
|
|
26
|
+
klass.include Relation
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module RequiredQueryAttributes
|
|
2
|
+
class RequiredAttributeMissing < ActiveRecord::ActiveRecordError; end
|
|
3
|
+
|
|
4
|
+
module Relation
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
# Intercepts calls to 'load', validating for required attributes before execution
|
|
8
|
+
def load(*)
|
|
9
|
+
required_query_attributes = @klass.required_query_attributes
|
|
10
|
+
check_required_query_attributes(required_query_attributes) if required_query_attributes
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def check_required_query_attributes(required_query_attributes)
|
|
17
|
+
predicate_attributes = where_clause.send(:predicates).
|
|
18
|
+
select { |node| node.left.relation.name == table_name }.
|
|
19
|
+
map { |node| node.left.name }
|
|
20
|
+
|
|
21
|
+
missing_names = required_query_attributes - predicate_attributes
|
|
22
|
+
|
|
23
|
+
if missing_names.present?
|
|
24
|
+
raise RequiredAttributeMissing,
|
|
25
|
+
"Required attributes #{missing_names} missing from query: #{to_sql.truncate(200)}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'required_query_attributes/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'required_query_attributes'
|
|
9
|
+
spec.version = RequiredQueryAttributes::VERSION
|
|
10
|
+
spec.authors = ['Marco Costa']
|
|
11
|
+
spec.email = ['marco@marcotc.com']
|
|
12
|
+
spec.license = "MIT"
|
|
13
|
+
|
|
14
|
+
spec.summary = 'Enforce presence of specific columns in ActiveRecord queries.'
|
|
15
|
+
spec.description = <<~DESC
|
|
16
|
+
Enforces the presence of specific columns in SELECT queries. Useful when you have
|
|
17
|
+
performance restrictions when specific columns are not included in your WHERE clauses.
|
|
18
|
+
Indexed columns and partition keys are common examples of such columns.
|
|
19
|
+
DESC
|
|
20
|
+
spec.homepage = 'https://github.com/wealthsimple/required_query_attributes'
|
|
21
|
+
|
|
22
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
23
|
+
f.match(%r{^(test|spec|features)/})
|
|
24
|
+
end
|
|
25
|
+
spec.bindir = 'exe'
|
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
27
|
+
spec.require_paths = ['lib']
|
|
28
|
+
|
|
29
|
+
spec.required_ruby_version = '>= 2.4'
|
|
30
|
+
|
|
31
|
+
spec.add_dependency 'activerecord', '~> 5'
|
|
32
|
+
|
|
33
|
+
spec.add_development_dependency 'bundler', '~> 1.15'
|
|
34
|
+
spec.add_development_dependency 'bundler-audit', '~> 0'
|
|
35
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
36
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
37
|
+
spec.add_development_dependency 'rubocop', '0.49.1' # Temporarily necessary to avoid breaking ws-style
|
|
38
|
+
spec.add_development_dependency 'sqlite3', '~> 1'
|
|
39
|
+
spec.add_development_dependency 'ws-style', '~> 0'
|
|
40
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: required_query_attributes
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Marco Costa
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-03-07 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activerecord
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '5'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '5'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: bundler
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.15'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.15'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler-audit
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '10.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '10.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rubocop
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - '='
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 0.49.1
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - '='
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: 0.49.1
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: sqlite3
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '1'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '1'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: ws-style
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
description: |
|
|
126
|
+
Enforces the presence of specific columns in SELECT queries. Useful when you have
|
|
127
|
+
performance restrictions when specific columns are not included in your WHERE clauses.
|
|
128
|
+
Indexed columns and partition keys are common examples of such columns.
|
|
129
|
+
email:
|
|
130
|
+
- marco@marcotc.com
|
|
131
|
+
executables: []
|
|
132
|
+
extensions: []
|
|
133
|
+
extra_rdoc_files: []
|
|
134
|
+
files:
|
|
135
|
+
- ".circleci/config.yml"
|
|
136
|
+
- ".gitignore"
|
|
137
|
+
- ".rspec"
|
|
138
|
+
- ".rubocop.yml"
|
|
139
|
+
- ".ruby-version"
|
|
140
|
+
- Gemfile
|
|
141
|
+
- LICENSE.md
|
|
142
|
+
- README.md
|
|
143
|
+
- Rakefile
|
|
144
|
+
- lib/required_query_attributes.rb
|
|
145
|
+
- lib/required_query_attributes/model.rb
|
|
146
|
+
- lib/required_query_attributes/relation.rb
|
|
147
|
+
- lib/required_query_attributes/version.rb
|
|
148
|
+
- required_query_attributes.gemspec
|
|
149
|
+
homepage: https://github.com/wealthsimple/required_query_attributes
|
|
150
|
+
licenses:
|
|
151
|
+
- MIT
|
|
152
|
+
metadata: {}
|
|
153
|
+
post_install_message:
|
|
154
|
+
rdoc_options: []
|
|
155
|
+
require_paths:
|
|
156
|
+
- lib
|
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
158
|
+
requirements:
|
|
159
|
+
- - ">="
|
|
160
|
+
- !ruby/object:Gem::Version
|
|
161
|
+
version: '2.4'
|
|
162
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
167
|
+
requirements: []
|
|
168
|
+
rubyforge_project:
|
|
169
|
+
rubygems_version: 2.6.14
|
|
170
|
+
signing_key:
|
|
171
|
+
specification_version: 4
|
|
172
|
+
summary: Enforce presence of specific columns in ActiveRecord queries.
|
|
173
|
+
test_files: []
|