query_helper 0.2.21 → 0.2.30
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 +4 -4
- data/.github/workflows/brakeman.yml +26 -0
- data/.github/workflows/ci.yml +24 -0
- data/.github/workflows/publish_rubygems.yml +20 -0
- data/.gitignore +1 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile.lock +8 -8
- data/README.md +33 -2
- data/lib/query_helper/filter.rb +4 -2
- data/lib/query_helper/query_helper_concern.rb +4 -3
- data/lib/query_helper/sql_filter.rb +15 -4
- data/lib/query_helper/sql_manipulator.rb +47 -3
- data/lib/query_helper/sql_parser.rb +14 -1
- data/lib/query_helper/sql_sort.rb +46 -28
- data/lib/query_helper/version.rb +1 -1
- data/lib/query_helper.rb +4 -0
- data/query_helper.gemspec +5 -4
- metadata +24 -22
- data/.travis.yml +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03de8ed1f4ad37b3fd8eb07edfedd5829c06e3502e78450fcbea037841a55bd7
|
4
|
+
data.tar.gz: 79598bdaca7ee9073f90858c906a5492c6eec8e4e6e514ddaa8397d0cf0afcc1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75cf73dd094f480095b4d0e2b8d9ae941d54a817e172dcbc3ddcc270f5c97e8c64e8303d5df8b3b0d876cb3ae5c230426920f329659892574db5889cb0ab1cd4
|
7
|
+
data.tar.gz: 25e7609715272868b5f6baf61a229d704b21cbc97f6a71be5a28126f763f9472088bc396498d60208492a2b582ac17d1994bbd2e8293c685b423c199da864e31
|
@@ -0,0 +1,26 @@
|
|
1
|
+
name: Brakeman
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
brakeman:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
steps:
|
9
|
+
- uses: actions/checkout@v2
|
10
|
+
|
11
|
+
- name: Set up Ruby 2.7.2
|
12
|
+
uses: ruby/setup-ruby@v1
|
13
|
+
with:
|
14
|
+
# Not needed with a .ruby-version file
|
15
|
+
ruby-version: 2.7.2
|
16
|
+
# runs 'bundle install' and caches installed gems automatically
|
17
|
+
bundler-cache: true
|
18
|
+
|
19
|
+
- name: Brakeman install
|
20
|
+
run: gem install brakeman
|
21
|
+
|
22
|
+
- name: brakeman
|
23
|
+
id: brakeman
|
24
|
+
run: |
|
25
|
+
brakeman --color --force -q
|
26
|
+
if [ $? -ne 0 ]; then return 1; fi
|
@@ -0,0 +1,24 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
env:
|
6
|
+
RAILS_ENV: test
|
7
|
+
GEMFILE_RUBY_VERSION: 2.7.2
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
|
15
|
+
- name: Set up Ruby 2.7.2
|
16
|
+
uses: ruby/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
# Not needed with a .ruby-version file
|
19
|
+
ruby-version: 2.7.2
|
20
|
+
# runs 'bundle install' and caches installed gems automatically
|
21
|
+
bundler-cache: true
|
22
|
+
|
23
|
+
- name: Run tests
|
24
|
+
run: bundle exec rspec spec
|
@@ -0,0 +1,20 @@
|
|
1
|
+
name: Publish Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags:
|
6
|
+
- v*
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v1
|
13
|
+
|
14
|
+
- name: Release Gem
|
15
|
+
if: contains(github.ref, 'refs/tags/v')
|
16
|
+
uses: cadwallion/publish-rubygems-action@master
|
17
|
+
env:
|
18
|
+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
19
|
+
RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
|
20
|
+
RELEASE_COMMAND: rake release
|
data/.gitignore
CHANGED
data/CODE_OF_CONDUCT.md
CHANGED
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
|
|
55
55
|
## Enforcement
|
56
56
|
|
57
57
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
-
reported by contacting the project team at
|
58
|
+
reported by contacting the project team at opensource@pattern.com. All
|
59
59
|
complaints will be reviewed and investigated and will result in a response that
|
60
60
|
is deemed necessary and appropriate to the circumstances. The project team is
|
61
61
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
query_helper (0.2.
|
4
|
+
query_helper (0.2.30)
|
5
5
|
activerecord (> 5)
|
6
6
|
activesupport (> 5)
|
7
|
+
sqlite3
|
7
8
|
|
8
9
|
GEM
|
9
10
|
remote: https://rubygems.org/
|
@@ -46,12 +47,12 @@ GEM
|
|
46
47
|
crass (~> 1.0.2)
|
47
48
|
nokogiri (>= 1.5.9)
|
48
49
|
method_source (1.0.0)
|
49
|
-
mini_portile2 (2.
|
50
|
+
mini_portile2 (2.8.0)
|
50
51
|
minitest (5.14.4)
|
51
|
-
nokogiri (1.
|
52
|
-
mini_portile2 (~> 2.
|
52
|
+
nokogiri (1.13.3)
|
53
|
+
mini_portile2 (~> 2.8.0)
|
53
54
|
racc (~> 1.4)
|
54
|
-
racc (1.
|
55
|
+
racc (1.6.0)
|
55
56
|
rack (2.2.3)
|
56
57
|
rack-test (1.1.0)
|
57
58
|
rack (>= 1.0, < 3)
|
@@ -88,7 +89,7 @@ GEM
|
|
88
89
|
rspec-mocks (~> 3.10)
|
89
90
|
rspec-support (~> 3.10)
|
90
91
|
rspec-support (3.10.2)
|
91
|
-
sqlite3 (1.
|
92
|
+
sqlite3 (1.4.2)
|
92
93
|
thor (1.1.0)
|
93
94
|
tzinfo (2.0.4)
|
94
95
|
concurrent-ruby (~> 1.0)
|
@@ -107,7 +108,6 @@ DEPENDENCIES
|
|
107
108
|
rake (~> 13.0)
|
108
109
|
rspec (~> 3.0)
|
109
110
|
rspec-rails
|
110
|
-
sqlite3 (~> 1.3.6)
|
111
111
|
|
112
112
|
BUNDLED WITH
|
113
|
-
2.2.
|
113
|
+
2.2.29
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# QueryHelper
|
2
|
-
[](https://travis-ci.org/iserve-products/query_helper)
|
3
2
|
[](https://badge.fury.io/rb/query_helper)
|
3
|
+
[](https://github.com/patterninc/query_helper/actions)
|
4
4
|
|
5
5
|
QueryHelper is a ruby gem used to paginate, sort, and filter your API calls in Ruby on Rails using URL params in your HTTP requests. It currently only supports Postgres.
|
6
6
|
|
@@ -88,6 +88,37 @@ Multiple Sorts: `http://www.example.com/resources?sort=resource_name:desc,resour
|
|
88
88
|
|
89
89
|
Lowercase Sort: `http://www.example.com/resources?sort=resource_name:desc:lowercase`
|
90
90
|
|
91
|
+
Custom Sort: `http://www.example.com/resources?custom_sort=resource_name:desc`
|
92
|
+
Example:
|
93
|
+
Custom Sort is basically used for enum based column.
|
94
|
+
```
|
95
|
+
class Customer < ApplicationRecord
|
96
|
+
enum customer_type: {
|
97
|
+
enum1: 0,
|
98
|
+
enum2: 1,
|
99
|
+
enum3: 3
|
100
|
+
}
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
Usage at Controller
|
105
|
+
|
106
|
+
```
|
107
|
+
class SomeController
|
108
|
+
|
109
|
+
def index
|
110
|
+
sort_column, sort_direction = params[:custom_sort]&.split(':')
|
111
|
+
|
112
|
+
column_sort_order = {
|
113
|
+
column_name: sort_column,
|
114
|
+
direction: sort_direction,
|
115
|
+
sort_values: Customer.send(sort_column.pluralize).values
|
116
|
+
}
|
117
|
+
|
118
|
+
@query_helper.update(query: query, column_sort_order: column_sort_order)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
```
|
91
122
|
#### Filtering
|
92
123
|
|
93
124
|
`filter[column][operator_code]=value`
|
@@ -316,7 +347,7 @@ or
|
|
316
347
|
|
317
348
|
## Contributing
|
318
349
|
|
319
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
350
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/patterninc/query_helper. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
320
351
|
|
321
352
|
## License
|
322
353
|
|
data/lib/query_helper/filter.rb
CHANGED
@@ -3,18 +3,20 @@ require "query_helper/invalid_query_error"
|
|
3
3
|
class QueryHelper
|
4
4
|
class Filter
|
5
5
|
|
6
|
-
attr_accessor :operator, :criterion, :comparate, :operator_code, :bind_variable, :aggregate
|
6
|
+
attr_accessor :operator, :criterion, :comparate, :operator_code, :bind_variable, :aggregate, :qualify_clause
|
7
7
|
|
8
8
|
def initialize(
|
9
9
|
operator_code:,
|
10
10
|
criterion:,
|
11
11
|
comparate:,
|
12
|
-
aggregate: false
|
12
|
+
aggregate: false,
|
13
|
+
qualify_clause: false
|
13
14
|
)
|
14
15
|
@operator_code = operator_code
|
15
16
|
@criterion = criterion # Converts to a string to be inserted into sql.
|
16
17
|
@comparate = comparate
|
17
18
|
@aggregate = aggregate
|
19
|
+
@qualify_clause = qualify_clause
|
18
20
|
@bind_variable = ('a'..'z').to_a.shuffle[0,20].join.to_sym
|
19
21
|
|
20
22
|
translate_operator_code()
|
@@ -26,12 +26,13 @@ class QueryHelper
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def create_query_helper_filter
|
29
|
-
|
29
|
+
# Here we get dynamic filter variable and hence we can't define permit.
|
30
|
+
filter_values = params[:filter].to_unsafe_h
|
30
31
|
QueryHelper::SqlFilter.new(filter_values: filter_values)
|
31
32
|
end
|
32
33
|
|
33
34
|
def create_query_helper_sort
|
34
|
-
QueryHelper::SqlSort.new(sort_string: params[:sort], sort_tiebreak: params[:sort_tiebreak])
|
35
|
+
QueryHelper::SqlSort.new(sort_string: params[:sort], sort_tiebreak: params[:sort_tiebreak], column_sort_order: params[:column_sort_order])
|
35
36
|
end
|
36
37
|
|
37
38
|
def create_query_helper_associations
|
@@ -48,7 +49,7 @@ class QueryHelper
|
|
48
49
|
def query_helper_params_no_pagination
|
49
50
|
helpers = {}
|
50
51
|
helpers[:sql_filter] = create_query_helper_filter() if params[:filter]
|
51
|
-
helpers[:sql_sort] = create_query_helper_sort() if params[:sort] || params[:sort_tiebreak]
|
52
|
+
helpers[:sql_sort] = create_query_helper_sort() if params[:sort] || params[:sort_tiebreak] || params[:custom_sort]
|
52
53
|
helpers[:associations] = create_query_helper_associations() if params[:include]
|
53
54
|
helpers[:search_string] = params[:search_for] if params[:search_for]
|
54
55
|
helpers
|
@@ -3,11 +3,13 @@ require "query_helper/invalid_query_error"
|
|
3
3
|
class QueryHelper
|
4
4
|
class SqlFilter
|
5
5
|
|
6
|
-
attr_accessor :filter_values, :column_maps
|
6
|
+
attr_accessor :filter_values, :column_maps, :options, :qualify_filters
|
7
7
|
|
8
|
-
def initialize(filter_values: [], column_maps: [])
|
8
|
+
def initialize(filter_values: [], column_maps: [], qualify_filters: [], options: {})
|
9
9
|
@column_maps = column_maps
|
10
10
|
@filter_values = filter_values
|
11
|
+
@options = options
|
12
|
+
@qualify_filters = qualify_filters
|
11
13
|
end
|
12
14
|
|
13
15
|
def create_filters
|
@@ -23,13 +25,22 @@ class QueryHelper
|
|
23
25
|
operator_code: criteria.keys.first,
|
24
26
|
criterion: criteria.values.first,
|
25
27
|
comparate: map.sql_expression,
|
26
|
-
aggregate: map.aggregate
|
28
|
+
aggregate: map.aggregate,
|
29
|
+
qualify_clause: aggregated_attribute?(comparate: map.sql_expression)
|
27
30
|
)
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
34
|
+
def aggregated_attribute?(comparate:)
|
35
|
+
@options['qualify_clause'] && qualify_filters.include?(comparate)
|
36
|
+
end
|
37
|
+
|
38
|
+
def qualify_clauses
|
39
|
+
@filters.select{ |f| aggregated_attribute?(comparate: f.comparate) }.map(&:sql_string)
|
40
|
+
end
|
41
|
+
|
31
42
|
def where_clauses
|
32
|
-
@filters.select{ |f| f.aggregate == false }.map(&:sql_string)
|
43
|
+
@filters.select{ |f| f.aggregate == false && !f.qualify_clause }.map(&:sql_string)
|
33
44
|
end
|
34
45
|
|
35
46
|
def having_clauses
|
@@ -9,14 +9,17 @@ class QueryHelper
|
|
9
9
|
sql:,
|
10
10
|
where_clauses: nil,
|
11
11
|
having_clauses: nil,
|
12
|
+
qualify_clauses: nil,
|
12
13
|
order_by_clauses: nil,
|
13
14
|
include_limit_clause: false,
|
14
15
|
additional_select_clauses: []
|
15
16
|
)
|
17
|
+
sql = remove_qualified_count(sql)
|
16
18
|
@parser = SqlParser.new(sql)
|
17
19
|
@sql = @parser.sql.dup
|
18
20
|
@where_clauses = where_clauses
|
19
21
|
@having_clauses = having_clauses
|
22
|
+
@qualify_clauses = qualify_clauses
|
20
23
|
@order_by_clauses = order_by_clauses
|
21
24
|
@include_limit_clause = include_limit_clause
|
22
25
|
@additional_select_clauses = additional_select_clauses
|
@@ -24,28 +27,69 @@ class QueryHelper
|
|
24
27
|
|
25
28
|
def build
|
26
29
|
insert_having_clauses()
|
30
|
+
insert_qualify_clauses()
|
27
31
|
insert_where_clauses()
|
28
32
|
insert_select_clauses()
|
29
33
|
insert_order_by_and_limit_clause()
|
34
|
+
insert_qualified_count_clauses if (qualify_clause_applicable? || @qualify_present) && @include_limit_clause
|
35
|
+
|
30
36
|
@sql.squish
|
31
37
|
end
|
32
38
|
|
33
39
|
private
|
34
40
|
|
35
41
|
def insert_select_clauses
|
36
|
-
|
37
|
-
@additional_select_clauses << total_count_clause if @include_limit_clause
|
42
|
+
@additional_select_clauses << count_sql if @include_limit_clause && !(qualify_clause_applicable? || @qualify_present)
|
38
43
|
@sql.insert(@parser.insert_select_index, " , #{@additional_select_clauses.join(", ")} ") if @additional_select_clauses.length > 0
|
39
44
|
end
|
40
45
|
|
46
|
+
def count_sql
|
47
|
+
"count(*) over () as _query_full_count"
|
48
|
+
end
|
49
|
+
|
50
|
+
def qualify_clauses(index)
|
51
|
+
if index == 0
|
52
|
+
"qualified_results AS ( "
|
53
|
+
else
|
54
|
+
", qualified_results AS ( "
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# If modifying something please check remove_qualified_count method too
|
59
|
+
def insert_qualified_count_clauses
|
60
|
+
@sql.insert(@parser.select_index, qualify_clauses(@parser.select_index))
|
61
|
+
@sql.insert(@sql.length, ") SELECT qualified_results.*, #{count_sql} FROM qualified_results")
|
62
|
+
end
|
63
|
+
|
64
|
+
# If modifying something please check insert_qualified_count_clauses method too
|
65
|
+
def remove_qualified_count(unparsed_sql)
|
66
|
+
@qualify_present = unparsed_sql.include?("qualified_results") || unparsed_sql.include?(" qualify ")
|
67
|
+
return unparsed_sql unless @qualify_present
|
68
|
+
|
69
|
+
unparsed_sql = unparsed_sql.gsub(/[\,\s]?qualified_results AS \( /, '')
|
70
|
+
unparsed_sql = unparsed_sql.gsub(") SELECT qualified_results.*, count(*) over () as _query_full_count FROM qualified_results", '')
|
71
|
+
unparsed_sql
|
72
|
+
end
|
73
|
+
|
41
74
|
def insert_where_clauses
|
42
75
|
return unless @where_clauses.length > 0
|
43
76
|
begin_string = @parser.where_included? ? "and" : "where"
|
44
77
|
filter_string = @where_clauses.join(" and ")
|
45
|
-
" #{begin_string} #{filter_string} "
|
46
78
|
@sql.insert(@parser.insert_where_index, " #{begin_string} #{filter_string} ")
|
47
79
|
end
|
48
80
|
|
81
|
+
def insert_qualify_clauses
|
82
|
+
return unless qualify_clause_applicable?
|
83
|
+
|
84
|
+
begin_string = @parser.qualify_included? ? "and" : "qualify"
|
85
|
+
filter_string = @qualify_clauses.join(" and ")
|
86
|
+
@sql.insert(@parser.insert_qualify_index, " #{begin_string} #{filter_string} ")
|
87
|
+
end
|
88
|
+
|
89
|
+
def qualify_clause_applicable?
|
90
|
+
@qualify_clauses.length > 0
|
91
|
+
end
|
92
|
+
|
49
93
|
def insert_having_clauses
|
50
94
|
return unless @having_clauses.length > 0
|
51
95
|
begin_string = @parser.having_included? ? "and" : "having"
|
@@ -48,6 +48,11 @@ class QueryHelper
|
|
48
48
|
find_index(regex, position)
|
49
49
|
end
|
50
50
|
|
51
|
+
def qualify_index(position=:start)
|
52
|
+
regex = / [Qq][Uu][Aa][Ll][Ii][Ff][Yy] /
|
53
|
+
find_index(regex, position)
|
54
|
+
end
|
55
|
+
|
51
56
|
def group_by_index(position=:start)
|
52
57
|
regex = / [Gg][Rr][Oo][Uu][Pp] [Bb][Yy] /
|
53
58
|
find_index(regex, position)
|
@@ -80,6 +85,10 @@ class QueryHelper
|
|
80
85
|
!where_index.nil?
|
81
86
|
end
|
82
87
|
|
88
|
+
def qualify_included?
|
89
|
+
!qualify_index.nil?
|
90
|
+
end
|
91
|
+
|
83
92
|
def group_by_included?
|
84
93
|
!group_by_index.nil?
|
85
94
|
end
|
@@ -108,6 +117,10 @@ class QueryHelper
|
|
108
117
|
group_by_index() || order_by_index() || limit_index() || @sql.length
|
109
118
|
end
|
110
119
|
|
120
|
+
def insert_qualify_index
|
121
|
+
order_by_index() || limit_index() || @sql.length
|
122
|
+
end
|
123
|
+
|
111
124
|
def insert_having_index
|
112
125
|
# raise InvalidQueryError.new("Cannot calculate insert_having_index because the query has no group by clause") unless group_by_included?
|
113
126
|
order_by_index() || limit_index() || @sql.length
|
@@ -173,7 +186,7 @@ class QueryHelper
|
|
173
186
|
ColumnMap.new(
|
174
187
|
alias_name: sql_alias,
|
175
188
|
sql_expression: sql_expression.squish,
|
176
|
-
aggregate: /(array_agg|avg|bit_and|bit_or|bool_and|bool_or|count|every|json_agg|jsonb_agg|json_object_agg|jsonb_object_agg|max|min|string_agg|sum|xmlagg)\((.*)\)/.match?(sql_expression)
|
189
|
+
aggregate: /(array_agg|avg|bit_and|bit_or|bool_and|bool_or|boolor_agg|booland_agg|count|every|json_agg|jsonb_agg|json_object_agg|jsonb_object_agg|max|min|string_agg|sum|xmlagg)\((.*)\)/.match?(sql_expression)
|
177
190
|
) if sql_alias
|
178
191
|
end
|
179
192
|
column_maps.compact
|
@@ -3,17 +3,18 @@ require "query_helper/invalid_query_error"
|
|
3
3
|
class QueryHelper
|
4
4
|
class SqlSort
|
5
5
|
|
6
|
-
attr_accessor :column_maps, :select_strings, :sort_tiebreak
|
6
|
+
attr_accessor :column_maps, :select_strings, :sort_tiebreak, :column_sort_order
|
7
7
|
|
8
|
-
def initialize(sort_string: "", sort_tiebreak: "", column_maps: [])
|
8
|
+
def initialize(sort_string: "", sort_tiebreak: "", column_maps: [], column_sort_order: {})
|
9
9
|
@sort_string = sort_string
|
10
10
|
@column_maps = column_maps
|
11
11
|
@sort_tiebreak = sort_tiebreak
|
12
|
+
@column_sort_order = column_sort_order
|
12
13
|
@select_strings = []
|
13
14
|
end
|
14
15
|
|
15
16
|
def parse_sort_string
|
16
|
-
return [] if @sort_string.blank? && @sort_tiebreak.blank?
|
17
|
+
return [] if @sort_string.blank? && @sort_tiebreak.blank? && @column_sort_order.blank?
|
17
18
|
|
18
19
|
return attributes_sql_expression(@sort_tiebreak) if @sort_string.blank?
|
19
20
|
|
@@ -25,38 +26,55 @@ class QueryHelper
|
|
25
26
|
|
26
27
|
def attributes_sql_expression(sort_attribute)
|
27
28
|
sql_strings = []
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
29
|
+
if sort_attribute.present?
|
30
|
+
sorts = sort_attribute.split(",")
|
31
|
+
sorts.each_with_index do |sort, index|
|
32
|
+
sort_alias = sort.split(":")[0]
|
33
|
+
direction = sort.split(":")[1]
|
34
|
+
modifier = sort.split(":")[2]
|
35
|
+
begin
|
36
|
+
sql_expression = @column_maps.find{ |m| m.alias_name.casecmp?(sort_alias) }.sql_expression
|
37
|
+
rescue NoMethodError => e
|
38
|
+
raise InvalidQueryError.new("Sorting not allowed on column '#{sort_alias}'")
|
39
|
+
end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
if direction == "desc"
|
42
|
+
case ActiveRecord::Base.connection.adapter_name
|
43
|
+
when "SQLite" # SQLite is used in the test suite
|
44
|
+
direction = "desc"
|
45
|
+
else
|
46
|
+
direction = "desc nulls last"
|
47
|
+
end
|
43
48
|
else
|
44
|
-
direction = "
|
49
|
+
direction = "asc"
|
45
50
|
end
|
46
|
-
else
|
47
|
-
direction = "asc"
|
48
|
-
end
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
case modifier
|
53
|
+
when "lowercase"
|
54
|
+
sql_expression = "lower(#{sql_expression})"
|
55
|
+
# When select distincts are used, the order by clause must be included in the select clause
|
56
|
+
@select_strings << sql_expression
|
57
|
+
end
|
56
58
|
|
57
|
-
|
59
|
+
sql_strings << "#{sql_expression} #{direction}"
|
60
|
+
end
|
58
61
|
end
|
62
|
+
sql_strings << parse_custom_sort_string if @column_sort_order.present? && @column_sort_order[:sort_values].present?
|
59
63
|
sql_strings
|
60
64
|
end
|
65
|
+
|
66
|
+
# This method is used for sorting enum based column
|
67
|
+
def parse_custom_sort_string
|
68
|
+
sort_column = @column_sort_order[:column_name]
|
69
|
+
sort_values = @column_sort_order[:sort_values]
|
70
|
+
direction = @column_sort_order[:direction]
|
71
|
+
|
72
|
+
sql_expression = '(CASE'
|
73
|
+
sort_values.each_with_index do |value, index|
|
74
|
+
sql_expression << " WHEN #{sort_column}=#{value} THEN #{index}"
|
75
|
+
end
|
76
|
+
sql_expression << " END) #{direction}"
|
77
|
+
sql_expression
|
78
|
+
end
|
61
79
|
end
|
62
80
|
end
|
data/lib/query_helper/version.rb
CHANGED
data/lib/query_helper.rb
CHANGED
@@ -67,6 +67,7 @@ class QueryHelper
|
|
67
67
|
sql_filter: nil,
|
68
68
|
sql_sort: nil,
|
69
69
|
sort_tiebreak: nil,
|
70
|
+
column_sort_order: nil,
|
70
71
|
page: nil,
|
71
72
|
per_page: nil,
|
72
73
|
search_string: nil,
|
@@ -85,6 +86,7 @@ class QueryHelper
|
|
85
86
|
@sql_filter = sql_filter if sql_filter
|
86
87
|
@sql_sort = sql_sort if sql_sort
|
87
88
|
@sql_sort.sort_tiebreak = sort_tiebreak if sort_tiebreak
|
89
|
+
@sql_sort.column_sort_order = column_sort_order if column_sort_order
|
88
90
|
@search_string = search_string if search_string
|
89
91
|
@page = determine_page(page: page, per_page: per_page) if page
|
90
92
|
@per_page = determine_per_page(page: page, per_page: per_page) if per_page
|
@@ -119,6 +121,7 @@ class QueryHelper
|
|
119
121
|
|
120
122
|
having_clauses = @sql_filter.having_clauses
|
121
123
|
where_clauses = @sql_filter.where_clauses
|
124
|
+
qualify_clauses = @sql_filter.qualify_clauses
|
122
125
|
|
123
126
|
if @search_string
|
124
127
|
search_filter = search_filter(column_maps)
|
@@ -138,6 +141,7 @@ class QueryHelper
|
|
138
141
|
sql: @query,
|
139
142
|
where_clauses: where_clauses,
|
140
143
|
having_clauses: having_clauses,
|
144
|
+
qualify_clauses: qualify_clauses,
|
141
145
|
order_by_clauses: @sql_sort.parse_sort_string,
|
142
146
|
include_limit_clause: @page && @per_page ? true : false,
|
143
147
|
additional_select_clauses: @sql_sort.select_strings
|
data/query_helper.gemspec
CHANGED
@@ -6,12 +6,12 @@ require "query_helper/version"
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "query_helper"
|
8
8
|
spec.version = QueryHelper::VERSION
|
9
|
-
spec.authors = ["
|
10
|
-
spec.email = ["
|
9
|
+
spec.authors = ["Patterninc"]
|
10
|
+
spec.email = ["jason@pattern.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{Ruby Gem to help with pagination and data formatting at Pattern, Inc.}
|
13
13
|
spec.description = %q{Ruby gem developed to help with pagination, filtering, sorting, and including associations on both active record queries and custom sql queries}
|
14
|
-
spec.homepage = "https://github.com/
|
14
|
+
spec.homepage = "https://github.com/patterninc/query_helper"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
17
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
@@ -35,11 +35,11 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.bindir = "exe"
|
36
36
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
37
|
spec.require_paths = ["lib"]
|
38
|
+
spec.required_ruby_version = ">= 2.6.0"
|
38
39
|
|
39
40
|
spec.add_development_dependency "bundler", "~> 2.2.27"
|
40
41
|
spec.add_development_dependency "rake", "~> 13.0"
|
41
42
|
spec.add_development_dependency "rspec", "~> 3.0"
|
42
|
-
spec.add_development_dependency "sqlite3", "~> 1.3.6"
|
43
43
|
spec.add_development_dependency "faker", "~> 1.9.3"
|
44
44
|
spec.add_development_dependency "byebug"
|
45
45
|
spec.add_development_dependency 'rspec-rails'
|
@@ -48,4 +48,5 @@ Gem::Specification.new do |spec|
|
|
48
48
|
|
49
49
|
spec.add_dependency "activerecord", "> 5"
|
50
50
|
spec.add_dependency "activesupport", "> 5"
|
51
|
+
spec.add_dependency "sqlite3"
|
51
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: query_helper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.30
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Patterninc
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: sqlite3
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 1.3.6
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 1.3.6
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: faker
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,17 +150,33 @@ dependencies:
|
|
164
150
|
- - ">"
|
165
151
|
- !ruby/object:Gem::Version
|
166
152
|
version: '5'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: sqlite3
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
167
|
description: Ruby gem developed to help with pagination, filtering, sorting, and including
|
168
168
|
associations on both active record queries and custom sql queries
|
169
169
|
email:
|
170
|
-
-
|
170
|
+
- jason@pattern.com
|
171
171
|
executables: []
|
172
172
|
extensions: []
|
173
173
|
extra_rdoc_files: []
|
174
174
|
files:
|
175
|
+
- ".github/workflows/brakeman.yml"
|
176
|
+
- ".github/workflows/ci.yml"
|
177
|
+
- ".github/workflows/publish_rubygems.yml"
|
175
178
|
- ".gitignore"
|
176
179
|
- ".rspec"
|
177
|
-
- ".travis.yml"
|
178
180
|
- CODE_OF_CONDUCT.md
|
179
181
|
- Gemfile
|
180
182
|
- Gemfile.lock
|
@@ -195,7 +197,7 @@ files:
|
|
195
197
|
- lib/query_helper/sql_sort.rb
|
196
198
|
- lib/query_helper/version.rb
|
197
199
|
- query_helper.gemspec
|
198
|
-
homepage: https://github.com/
|
200
|
+
homepage: https://github.com/patterninc/query_helper
|
199
201
|
licenses:
|
200
202
|
- MIT
|
201
203
|
metadata: {}
|
@@ -207,14 +209,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
207
209
|
requirements:
|
208
210
|
- - ">="
|
209
211
|
- !ruby/object:Gem::Version
|
210
|
-
version:
|
212
|
+
version: 2.6.0
|
211
213
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
212
214
|
requirements:
|
213
215
|
- - ">="
|
214
216
|
- !ruby/object:Gem::Version
|
215
217
|
version: '0'
|
216
218
|
requirements: []
|
217
|
-
rubygems_version: 3.
|
219
|
+
rubygems_version: 3.1.4
|
218
220
|
signing_key:
|
219
221
|
specification_version: 4
|
220
222
|
summary: Ruby Gem to help with pagination and data formatting at Pattern, Inc.
|