search_cop 1.0.9 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +50 -0
- data/.rubocop.yml +134 -0
- data/CHANGELOG.md +29 -7
- data/Gemfile +4 -17
- data/README.md +69 -3
- data/Rakefile +0 -1
- data/docker-compose.yml +18 -0
- data/gemfiles/rails5.gemfile +13 -0
- data/gemfiles/rails6.gemfile +13 -0
- data/gemfiles/rails7.gemfile +13 -0
- data/lib/search_cop/grammar_parser.rb +3 -4
- data/lib/search_cop/hash_parser.rb +20 -18
- data/lib/search_cop/helpers.rb +15 -0
- data/lib/search_cop/query_builder.rb +3 -5
- data/lib/search_cop/query_info.rb +0 -2
- data/lib/search_cop/search_scope.rb +2 -4
- data/lib/search_cop/version.rb +1 -1
- data/lib/search_cop/visitors/mysql.rb +4 -2
- data/lib/search_cop/visitors/postgres.rb +5 -3
- data/lib/search_cop/visitors/visitor.rb +7 -5
- data/lib/search_cop/visitors.rb +0 -2
- data/lib/search_cop.rb +15 -13
- data/lib/search_cop_grammar/attributes.rb +53 -34
- data/lib/search_cop_grammar/nodes.rb +0 -2
- data/lib/search_cop_grammar.rb +13 -4
- data/lib/search_cop_grammar.treetop +6 -4
- data/search_cop.gemspec +9 -11
- data/test/and_test.rb +6 -8
- data/test/boolean_test.rb +7 -9
- data/test/database.yml +4 -1
- data/test/date_test.rb +38 -12
- data/test/datetime_test.rb +45 -12
- data/test/default_operator_test.rb +51 -0
- data/test/error_test.rb +2 -4
- data/test/float_test.rb +16 -11
- data/test/fulltext_test.rb +6 -8
- data/test/hash_test.rb +32 -34
- data/test/integer_test.rb +9 -11
- data/test/not_test.rb +6 -8
- data/test/or_test.rb +8 -10
- data/test/scope_test.rb +11 -13
- data/test/search_cop_test.rb +42 -36
- data/test/string_test.rb +67 -19
- data/test/test_helper.rb +28 -18
- data/test/visitor_test.rb +4 -6
- metadata +25 -42
- data/.travis.yml +0 -45
- data/Appraisals +0 -21
- data/gemfiles/3.2.gemfile +0 -25
- data/gemfiles/4.0.gemfile +0 -25
- data/gemfiles/4.1.gemfile +0 -25
- data/gemfiles/4.2.gemfile +0 -25
- data/gemfiles/5.0.gemfile +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 07623ce75206a9b6b4455141697c09782649b05f1073ec27c965a7a4fdf2862a
|
4
|
+
data.tar.gz: 5adef700f6a6514d0d1de0b1f17958d03ecacc0a0f83df57de5aff480bc96da5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbe73b6ec59b87263d9bfd7d4740618deab1dd7d4867dcb39a864e75db8614f08beed34518477791dd3d66f10ee8461a1282a3d81d35ee17b2a7de26d8c995cf
|
7
|
+
data.tar.gz: c131ea06253e994bc5550570708cc5dc973b5392ca208020de08c3384153492c0bc9820438fc0d32c596ec46f27c9982b445d98b80363a89764b6461a4a35706
|
@@ -0,0 +1,50 @@
|
|
1
|
+
name: test
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
build:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
strategy:
|
7
|
+
fail-fast: false
|
8
|
+
matrix:
|
9
|
+
ruby: ["2.6", "2.7", "3.0", "3.1"]
|
10
|
+
rails: ["rails5", "rails6", "rails7"]
|
11
|
+
database: ["sqlite", "postgres", "mysql"]
|
12
|
+
exclude:
|
13
|
+
- ruby: "3.0"
|
14
|
+
rails: "rails5"
|
15
|
+
- ruby: "3.1"
|
16
|
+
rails: "rails5"
|
17
|
+
- ruby: "2.6"
|
18
|
+
rails: "rails7"
|
19
|
+
services:
|
20
|
+
postgres:
|
21
|
+
image: postgres
|
22
|
+
env:
|
23
|
+
POSTGRES_USER: search_cop
|
24
|
+
POSTGRES_PASSWORD: secret
|
25
|
+
POSTGRES_DB: search_cop
|
26
|
+
ports:
|
27
|
+
- 5432:5432
|
28
|
+
mysql:
|
29
|
+
image: mysql
|
30
|
+
env:
|
31
|
+
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
32
|
+
MYSQL_ROOT_PASSWORD: ""
|
33
|
+
MYSQL_DATABASE: search_cop
|
34
|
+
ports:
|
35
|
+
- 3306:3306
|
36
|
+
steps:
|
37
|
+
- uses: actions/checkout@v1
|
38
|
+
- uses: ruby/setup-ruby@v1
|
39
|
+
with:
|
40
|
+
ruby-version: ${{ matrix.ruby }}
|
41
|
+
- name: test
|
42
|
+
env:
|
43
|
+
DATABASE: ${{ matrix.database }}
|
44
|
+
run: |
|
45
|
+
gem install bundler
|
46
|
+
bundle config set --local gemfile "gemfiles/${{ matrix.rails }}.gemfile"
|
47
|
+
bundle config set --local path "../vendor/bundle"
|
48
|
+
bundle install
|
49
|
+
bundle exec rake test
|
50
|
+
bundle exec rubocop
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
|
4
|
+
Gemspec/RequireMFA:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Style/FetchEnvVar:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Gemspec/RequiredRubyVersion:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Style/CaseLikeIf:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Style/RedundantArgument:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Lint/EmptyBlock:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Layout/EmptyLineBetweenDefs:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/FrozenStringLiteralComment:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Lint/RedundantRequireStatement:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Layout/ArgumentAlignment:
|
32
|
+
EnforcedStyle: with_fixed_indentation
|
33
|
+
|
34
|
+
Layout/FirstArrayElementIndentation:
|
35
|
+
EnforcedStyle: consistent
|
36
|
+
|
37
|
+
Style/PercentLiteralDelimiters:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/SpecialGlobalVars:
|
41
|
+
EnforcedStyle: use_english_names
|
42
|
+
|
43
|
+
Security/Eval:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Style/WordArray:
|
47
|
+
EnforcedStyle: brackets
|
48
|
+
|
49
|
+
Style/ClassAndModuleChildren:
|
50
|
+
Enabled: false
|
51
|
+
|
52
|
+
Style/TrivialAccessors:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
Style/Alias:
|
56
|
+
Enabled: false
|
57
|
+
|
58
|
+
Style/StringLiteralsInInterpolation:
|
59
|
+
EnforcedStyle: double_quotes
|
60
|
+
|
61
|
+
Metrics/ClassLength:
|
62
|
+
Enabled: false
|
63
|
+
|
64
|
+
Naming/MethodParameterName:
|
65
|
+
Enabled: false
|
66
|
+
|
67
|
+
Style/SymbolArray:
|
68
|
+
EnforcedStyle: brackets
|
69
|
+
|
70
|
+
Layout/RescueEnsureAlignment:
|
71
|
+
Enabled: false
|
72
|
+
|
73
|
+
Layout/LineLength:
|
74
|
+
Enabled: false
|
75
|
+
|
76
|
+
Metrics/MethodLength:
|
77
|
+
Enabled: false
|
78
|
+
|
79
|
+
Metrics/ModuleLength:
|
80
|
+
Enabled: false
|
81
|
+
|
82
|
+
Style/ZeroLengthPredicate:
|
83
|
+
Enabled: false
|
84
|
+
|
85
|
+
Metrics/PerceivedComplexity:
|
86
|
+
Enabled: false
|
87
|
+
|
88
|
+
Metrics/AbcSize:
|
89
|
+
Enabled: false
|
90
|
+
|
91
|
+
Metrics/CyclomaticComplexity:
|
92
|
+
Enabled: false
|
93
|
+
|
94
|
+
Metrics/BlockLength:
|
95
|
+
Enabled: false
|
96
|
+
|
97
|
+
Metrics/BlockNesting:
|
98
|
+
Enabled: false
|
99
|
+
|
100
|
+
Style/NumericPredicate:
|
101
|
+
Enabled: false
|
102
|
+
|
103
|
+
Naming/AccessorMethodName:
|
104
|
+
Enabled: false
|
105
|
+
|
106
|
+
Naming/MemoizedInstanceVariableName:
|
107
|
+
Enabled: false
|
108
|
+
|
109
|
+
Style/StringLiterals:
|
110
|
+
EnforcedStyle: double_quotes
|
111
|
+
|
112
|
+
Style/Documentation:
|
113
|
+
Enabled: false
|
114
|
+
|
115
|
+
Naming/ConstantName:
|
116
|
+
Enabled: false
|
117
|
+
|
118
|
+
Style/MutableConstant:
|
119
|
+
Enabled: false
|
120
|
+
|
121
|
+
Layout/MultilineMethodCallIndentation:
|
122
|
+
EnforcedStyle: indented
|
123
|
+
|
124
|
+
Layout/ParameterAlignment:
|
125
|
+
EnforcedStyle: with_fixed_indentation
|
126
|
+
|
127
|
+
Lint/UnusedMethodArgument:
|
128
|
+
Enabled: false
|
129
|
+
|
130
|
+
Style/IfUnlessModifier:
|
131
|
+
Enabled: false
|
132
|
+
|
133
|
+
Style/RedundantBegin:
|
134
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,14 +1,36 @@
|
|
1
1
|
|
2
2
|
# Changelog
|
3
3
|
|
4
|
+
Version 1.2.1:
|
5
|
+
|
6
|
+
* Fix use of `table_name` to work with inherited models
|
7
|
+
* Fix linter, add ruby 3 and rails 7 to ci pipeline
|
8
|
+
|
9
|
+
Version 1.2.0:
|
10
|
+
|
11
|
+
* Added support for disabling the right wildcard
|
12
|
+
* Fixed escaping of wildcard chars (`_`, `%`)
|
13
|
+
|
14
|
+
Version 1.1.0:
|
15
|
+
|
16
|
+
* Adds customizable default operator to concatenate conditions (#49)
|
17
|
+
* Make the postgis adapter use the postgres extensions
|
18
|
+
|
19
|
+
Version 1.0.9:
|
20
|
+
|
21
|
+
* Use `[:blank:]` instead of `\s` for space (#46)
|
22
|
+
* Updated `SearchCop::Visitors::Visitor` to check the connection's `adapter_name` when extending. (#47)
|
23
|
+
* Fix for negative numeric values
|
24
|
+
* allow searching for relative dates, like hours, days, weeks, months or years ago
|
25
|
+
|
4
26
|
Version 1.0.8:
|
5
27
|
|
6
28
|
* No longer add search scope methods globally #34
|
7
29
|
|
8
30
|
Version 1.0.7:
|
9
31
|
|
10
|
-
* Bugfix regarding NOT queries in fulltext mode #32
|
11
|
-
* Safely handle NULL values for match queries
|
32
|
+
* Bugfix regarding `NOT` queries in fulltext mode #32
|
33
|
+
* Safely handle `NULL` values for match queries
|
12
34
|
* Added coalesce option
|
13
35
|
|
14
36
|
Version 1.0.6:
|
@@ -46,7 +68,7 @@ Version 1.0.0:
|
|
46
68
|
|
47
69
|
Version 0.0.5:
|
48
70
|
|
49
|
-
* Supporting
|
71
|
+
* Supporting `:default => false`
|
50
72
|
* Datetime/Date greater operator fix
|
51
73
|
* Use reflection to find associated models
|
52
74
|
* Providing reflection
|
@@ -55,16 +77,16 @@ Version 0.0.4:
|
|
55
77
|
|
56
78
|
* Fixed date attributes
|
57
79
|
* Fail softly for mixed datatype attributes
|
58
|
-
* Support custom table, class and alias names via attr_searchable_alias
|
80
|
+
* Support custom table, class and alias names via `attr_searchable_alias`
|
59
81
|
|
60
82
|
Version 0.0.3:
|
61
83
|
|
62
|
-
* belongs_to association fixes
|
84
|
+
* `belongs_to` association fixes
|
63
85
|
|
64
86
|
Version 0.0.2:
|
65
87
|
|
66
88
|
* Arel abstraction layer added
|
67
|
-
* count() queries resulting in "Cannot visit AttrSearchableGrammar::Nodes..." fixed
|
89
|
+
* `count()` queries resulting in "Cannot visit AttrSearchableGrammar::Nodes..." fixed
|
68
90
|
* Better error messages
|
69
|
-
* Model#unsafe_search added
|
91
|
+
* `Model#unsafe_search` added
|
70
92
|
|
data/Gemfile
CHANGED
@@ -1,23 +1,10 @@
|
|
1
|
-
source
|
1
|
+
source "https://rubygems.org"
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in search_cop.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
platforms :jruby do
|
7
|
-
gem 'activerecord-jdbcmysql-adapter'
|
8
|
-
gem 'activerecord-jdbcsqlite3-adapter'
|
9
|
-
gem 'activerecord-jdbcpostgresql-adapter'
|
10
|
-
end
|
11
|
-
|
12
6
|
platforms :ruby do
|
13
|
-
gem
|
14
|
-
gem
|
15
|
-
gem
|
7
|
+
gem "mysql2"
|
8
|
+
gem "pg"
|
9
|
+
gem "sqlite3"
|
16
10
|
end
|
17
|
-
|
18
|
-
platforms :rbx do
|
19
|
-
gem 'racc'
|
20
|
-
gem 'rubysl', '~> 2.0'
|
21
|
-
gem 'psych'
|
22
|
-
end
|
23
|
-
|
data/README.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# SearchCop
|
2
2
|
|
3
|
-
[![Build Status](https://
|
3
|
+
[![Build Status](https://github.com/mrkamel/search_cop/workflows/test/badge.svg?branch=master)](https://github.com/mrkamel/search_cop/actions?query=workflow%3Atest)
|
4
4
|
[![Code Climate](https://codeclimate.com/github/mrkamel/search_cop.png)](https://codeclimate.com/github/mrkamel/search_cop)
|
5
|
-
[![Dependency Status](https://gemnasium.com/mrkamel/search_cop.png?travis)](https://gemnasium.com/mrkamel/search_cop)
|
6
5
|
[![Gem Version](https://badge.fury.io/rb/search_cop.svg)](http://badge.fury.io/rb/search_cop)
|
7
6
|
|
8
7
|
![search_cop](https://raw.githubusercontent.com/mrkamel/search_cop_logo/master/search_cop.png)
|
@@ -118,6 +117,24 @@ or post-process the search results in every possible way:
|
|
118
117
|
Book.where(available: true).search("Harry Potter").order("books.id desc").paginate(page: params[:page])
|
119
118
|
```
|
120
119
|
|
120
|
+
## Security
|
121
|
+
|
122
|
+
When you pass a query string to SearchCop, it gets parsed, analyzed and mapped
|
123
|
+
to finally build up an SQL query. To be more precise, when SearchCop parses the
|
124
|
+
query, it creates objects (nodes), which represent the query expressions (And-,
|
125
|
+
Or-, Not-, String-, Date-, etc Nodes). To build the SQL query, SearchCop uses
|
126
|
+
the concept of visitors like e.g. used in
|
127
|
+
[Arel](https://github.com/rails/arel), such that, for every node there must be
|
128
|
+
a [visitor](https://github.com/mrkamel/search_cop/blob/master/lib/search_cop/visitors/visitor.rb),
|
129
|
+
which transforms the node to SQL. When there is no visitor, an exception is
|
130
|
+
raised when the query builder tries to "visit" the node. The visitors are
|
131
|
+
responsible for sanitizing the user supplied input. This is primilarly done via
|
132
|
+
quoting (string-, table-name-, column-quoting, etc). SearchCop is using the
|
133
|
+
methods provided by the ActiveRecord connection adapter for sanitizing/quoting
|
134
|
+
to prevent SQL injection. While we can never be 100% safe from security issues,
|
135
|
+
SearchCop takes security issues seriously. Please report responsibly via
|
136
|
+
security at flakks dot com in case you find any security related issues.
|
137
|
+
|
121
138
|
## Fulltext index capabilities
|
122
139
|
|
123
140
|
By default, i.e. if you don't tell SearchCop about your fulltext indices,
|
@@ -283,7 +300,7 @@ For more details about PostgreSQL fulltext indices visit
|
|
283
300
|
|
284
301
|
In case you expose non-fulltext attributes to search queries (price, stock,
|
285
302
|
etc.), the respective queries, like `Book.search("stock > 0")`, will profit
|
286
|
-
from
|
303
|
+
from the usual non-fulltext indices. Thus, you should add a usual index on
|
287
304
|
every column you expose to search queries plus a fulltext index for every
|
288
305
|
fulltext attribute.
|
289
306
|
|
@@ -312,6 +329,51 @@ User.search("admin")
|
|
312
329
|
# ... WHERE users.username LIKE 'admin%'
|
313
330
|
```
|
314
331
|
|
332
|
+
Similarly, you can disable the right wildcard as well:
|
333
|
+
|
334
|
+
```ruby
|
335
|
+
search_scope :search do
|
336
|
+
attributes :username
|
337
|
+
|
338
|
+
options :username, right_wildcard: false
|
339
|
+
end
|
340
|
+
```
|
341
|
+
|
342
|
+
## Default operator
|
343
|
+
|
344
|
+
When you define multiple fields on a search scope, SearcCop will use
|
345
|
+
by default the AND operator to concatenate the conditions, e.g:
|
346
|
+
|
347
|
+
```ruby
|
348
|
+
class User < ActiveRecord::Base
|
349
|
+
include SearchCop
|
350
|
+
|
351
|
+
search_scope :search do
|
352
|
+
attributes :username, :fullname
|
353
|
+
end
|
354
|
+
|
355
|
+
# ...
|
356
|
+
end
|
357
|
+
```
|
358
|
+
|
359
|
+
So a search like `User.search("something")` will generate a query
|
360
|
+
with the following conditions:
|
361
|
+
|
362
|
+
```sql
|
363
|
+
... WHERE username LIKE '%something%' AND fullname LIKE '%something%'
|
364
|
+
```
|
365
|
+
|
366
|
+
However, there are cases where using AND as the default operator is not desired,
|
367
|
+
so SearchCop allows you to override it and use OR as the default operator instead.
|
368
|
+
A query like `User.search("something", default_operator: :or)` will
|
369
|
+
generate the query using OR to concatenate the conditions
|
370
|
+
|
371
|
+
```sql
|
372
|
+
... WHERE username LIKE '%something%' OR fullname LIKE '%something%'
|
373
|
+
```
|
374
|
+
|
375
|
+
Finally, please note that you can apply it to fulltext indices/queries as well.
|
376
|
+
|
315
377
|
## Associations
|
316
378
|
|
317
379
|
If you specify searchable attributes from another model, like
|
@@ -454,6 +516,10 @@ SearchCop also provides the ability to define custom operators by defining a
|
|
454
516
|
search. This is useful when you want to use database operators that are not
|
455
517
|
supported by SearchCop.
|
456
518
|
|
519
|
+
Please note, when using generators, you are responsible for sanitizing/quoting
|
520
|
+
the values (see example below). Otherwise your generator will allow SQL
|
521
|
+
injection. Thus, please only use generators if you know what you're doing.
|
522
|
+
|
457
523
|
For example, if you wanted to perform a `LIKE` query where a book title starts
|
458
524
|
with a string, you can define the search scope like so:
|
459
525
|
|
data/Rakefile
CHANGED
data/docker-compose.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
version: '2'
|
3
|
+
services:
|
4
|
+
mysql:
|
5
|
+
image: percona:5.7
|
6
|
+
environment:
|
7
|
+
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
|
8
|
+
- MYSQL_ROOT_PASSWORD=
|
9
|
+
ports:
|
10
|
+
- 3306:3306
|
11
|
+
postgres:
|
12
|
+
image: postgres:9.6.6
|
13
|
+
environment:
|
14
|
+
POSTGRES_DB: search_cop
|
15
|
+
POSTGRES_USER: search_cop
|
16
|
+
POSTGRES_PASSWORD: secret
|
17
|
+
ports:
|
18
|
+
- 5432:5432
|
@@ -1,8 +1,7 @@
|
|
1
|
-
|
2
1
|
require "search_cop_grammar"
|
3
2
|
require "treetop"
|
4
3
|
|
5
|
-
Treetop.load File.expand_path("
|
4
|
+
Treetop.load File.expand_path("../search_cop_grammar.treetop", __dir__)
|
6
5
|
|
7
6
|
module SearchCop
|
8
7
|
class GrammarParser
|
@@ -12,11 +11,11 @@ module SearchCop
|
|
12
11
|
@query_info = query_info
|
13
12
|
end
|
14
13
|
|
15
|
-
def parse(string)
|
14
|
+
def parse(string, query_options)
|
16
15
|
node = SearchCopGrammarParser.new.parse(string) || raise(ParseError)
|
17
16
|
node.query_info = query_info
|
17
|
+
node.query_options = query_options
|
18
18
|
node.evaluate
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
class SearchCop::HashParser
|
3
2
|
attr_reader :query_info
|
4
3
|
|
@@ -6,23 +5,25 @@ class SearchCop::HashParser
|
|
6
5
|
@query_info = query_info
|
7
6
|
end
|
8
7
|
|
9
|
-
def parse(hash)
|
8
|
+
def parse(hash, query_options = {})
|
9
|
+
default_operator = SearchCop::Helpers.sanitize_default_operator(query_options)
|
10
|
+
|
10
11
|
res = hash.collect do |key, value|
|
11
12
|
case key
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
when :and
|
14
|
+
value.collect { |val| parse val }.inject(:and)
|
15
|
+
when :or
|
16
|
+
value.collect { |val| parse val }.inject(:or)
|
17
|
+
when :not
|
18
|
+
parse(value).not
|
19
|
+
when :query
|
20
|
+
SearchCop::Parser.parse(value, query_info)
|
21
|
+
else
|
22
|
+
parse_attribute(key, value)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
|
-
res.inject
|
26
|
+
res.inject(default_operator)
|
26
27
|
end
|
27
28
|
|
28
29
|
private
|
@@ -33,14 +34,15 @@ class SearchCop::HashParser
|
|
33
34
|
if value.is_a?(Hash)
|
34
35
|
raise(SearchCop::ParseError, "Unknown operator #{value.keys.first}") unless collection.valid_operator?(value.keys.first)
|
35
36
|
|
36
|
-
|
37
|
-
|
37
|
+
generator = collection.generator_for(value.keys.first)
|
38
|
+
|
39
|
+
if generator
|
40
|
+
collection.generator(generator, value.values.first)
|
38
41
|
else
|
39
|
-
collection.send
|
42
|
+
collection.send(value.keys.first, value.values.first.to_s)
|
40
43
|
end
|
41
44
|
else
|
42
|
-
collection.send
|
45
|
+
collection.send(:matches, value.to_s)
|
43
46
|
end
|
44
47
|
end
|
45
48
|
end
|
46
|
-
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module SearchCop
|
2
|
+
module Helpers
|
3
|
+
def self.sanitize_default_operator(query_options)
|
4
|
+
return "and" unless query_options.key?(:default_operator)
|
5
|
+
|
6
|
+
default_operator = query_options[:default_operator].to_s.downcase
|
7
|
+
|
8
|
+
unless ["and", "or"].include?(default_operator)
|
9
|
+
raise(SearchCop::UnknownDefaultOperator, "Unknown default operator value #{default_operator}")
|
10
|
+
end
|
11
|
+
|
12
|
+
default_operator
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,19 +1,18 @@
|
|
1
|
-
|
2
1
|
module SearchCop
|
3
2
|
class QueryBuilder
|
4
3
|
attr_accessor :query_info, :scope, :sql
|
5
4
|
|
6
|
-
def initialize(model, query, scope)
|
5
|
+
def initialize(model, query, scope, query_options)
|
7
6
|
self.scope = scope
|
8
7
|
self.query_info = QueryInfo.new(model, scope)
|
9
8
|
|
10
|
-
arel = SearchCop::Parser.parse(query, query_info).optimize!
|
9
|
+
arel = SearchCop::Parser.parse(query, query_info, query_options).optimize!
|
11
10
|
|
12
11
|
self.sql = SearchCop::Visitors::Visitor.new(model.connection).visit(arel)
|
13
12
|
end
|
14
13
|
|
15
14
|
def associations
|
16
|
-
all_associations - [query_info.model.
|
15
|
+
all_associations - [query_info.model.table_name.to_sym]
|
17
16
|
end
|
18
17
|
|
19
18
|
private
|
@@ -32,4 +31,3 @@ module SearchCop
|
|
32
31
|
end
|
33
32
|
end
|
34
33
|
end
|
35
|
-
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
module SearchCop
|
3
2
|
class Reflection
|
4
3
|
attr_accessor :attributes, :options, :aliases, :scope, :generators
|
@@ -11,11 +10,11 @@ module SearchCop
|
|
11
10
|
end
|
12
11
|
|
13
12
|
def default_attributes
|
14
|
-
keys = options.select { |
|
13
|
+
keys = options.select { |_key, value| value[:default] == true }.keys
|
15
14
|
keys = attributes.keys.reject { |key| options[key] && options[key][:default] == false } if keys.empty?
|
16
15
|
keys = keys.to_set
|
17
16
|
|
18
|
-
attributes.select { |key,
|
17
|
+
attributes.select { |key, _value| keys.include? key }
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
@@ -64,4 +63,3 @@ module SearchCop
|
|
64
63
|
end
|
65
64
|
end
|
66
65
|
end
|
67
|
-
|
data/lib/search_cop/version.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
|
2
1
|
module SearchCop
|
3
2
|
module Visitors
|
4
3
|
module Mysql
|
4
|
+
# rubocop:disable Naming/MethodName
|
5
|
+
|
5
6
|
class FulltextQuery < Visitor
|
6
7
|
def visit_SearchCopGrammar_Nodes_MatchesFulltextNot(node)
|
7
8
|
node.right.split(/[\s+'"<>()~-]+/).collect { |word| "-#{word}" }.join(" ")
|
@@ -38,6 +39,7 @@ module SearchCop
|
|
38
39
|
"MATCH(#{visit node.collection}) AGAINST(#{visit FulltextQuery.new(connection).visit(node.node)} IN BOOLEAN MODE)"
|
39
40
|
end
|
40
41
|
end
|
42
|
+
|
43
|
+
# rubocop:enable Naming/MethodName
|
41
44
|
end
|
42
45
|
end
|
43
|
-
|