rokaki 0.8.1.2 → 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.ruby-version +1 -0
- data/Gemfile.lock +31 -27
- data/README.md +87 -2
- data/lib/rokaki.rb +2 -3
- data/lib/rokaki/filter_model.rb +49 -38
- data/lib/rokaki/filter_model/basic_filter.rb +3 -2
- data/lib/rokaki/filter_model/deep_assign_struct.rb +57 -0
- data/lib/rokaki/filter_model/join_map.rb +105 -0
- data/lib/rokaki/filter_model/like_keys.rb +24 -22
- data/lib/rokaki/filter_model/nested_filter.rb +11 -11
- data/lib/rokaki/filter_model/nested_like_filters.rb +241 -0
- data/lib/rokaki/filterable.rb +14 -4
- data/lib/rokaki/version.rb +1 -1
- data/rokaki.gemspec +8 -4
- metadata +45 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b14c1377cb6ea9d8aa3571f310ffc884f027c495067e08adfce02caf1f7d0d2
|
4
|
+
data.tar.gz: 5326391f9ed786eb26f9cb5573d2afc78862e72b5ab64f8b88f26b8ff18fdcfe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5c2e4a0b0fea5a627db0136291654aa6df25e6a2f548ba3c42fc1ebc8b3e427c3fcc53e0488c1df6e99f8bb6fd44e752c3ccdabe1f4be506a5fd7d53878cf3c
|
7
|
+
data.tar.gz: 79934522b0dba27889946208f47a8b2d769362016ae914ce99c7796c0b6899f6de8d96e25569e9ecc5086f0b0aeae23f0a6e21b6fe410db022eb89d6c6c15dd7
|
data/.gitignore
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.7.4
|
data/Gemfile.lock
CHANGED
@@ -1,38 +1,42 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rokaki (0.8.1
|
4
|
+
rokaki (0.8.3.1)
|
5
5
|
activesupport
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activemodel (6.
|
11
|
-
activesupport (= 6.
|
12
|
-
activerecord (6.
|
13
|
-
activemodel (= 6.
|
14
|
-
activesupport (= 6.
|
15
|
-
activesupport (6.
|
10
|
+
activemodel (6.1.3)
|
11
|
+
activesupport (= 6.1.3)
|
12
|
+
activerecord (6.1.3)
|
13
|
+
activemodel (= 6.1.3)
|
14
|
+
activesupport (= 6.1.3)
|
15
|
+
activesupport (6.1.3)
|
16
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
|
-
i18n (>=
|
18
|
-
minitest (
|
19
|
-
tzinfo (~>
|
20
|
-
zeitwerk (~> 2.
|
17
|
+
i18n (>= 1.6, < 2)
|
18
|
+
minitest (>= 5.1)
|
19
|
+
tzinfo (~> 2.0)
|
20
|
+
zeitwerk (~> 2.3)
|
21
21
|
byebug (11.1.3)
|
22
22
|
coderay (1.1.3)
|
23
|
-
concurrent-ruby (1.1.
|
23
|
+
concurrent-ruby (1.1.8)
|
24
|
+
database_cleaner (1.8.5)
|
25
|
+
database_cleaner-active_record (1.8.0)
|
26
|
+
activerecord
|
27
|
+
database_cleaner (~> 1.8.0)
|
24
28
|
diff-lcs (1.3)
|
25
29
|
factory_bot (6.0.2)
|
26
30
|
activesupport (>= 5.0.0)
|
27
|
-
ffi (1.
|
28
|
-
formatador (0.
|
29
|
-
guard (2.
|
31
|
+
ffi (1.15.3)
|
32
|
+
formatador (0.3.0)
|
33
|
+
guard (2.18.0)
|
30
34
|
formatador (>= 0.2.4)
|
31
35
|
listen (>= 2.7, < 4.0)
|
32
36
|
lumberjack (>= 1.0.12, < 2.0)
|
33
37
|
nenv (~> 0.1)
|
34
38
|
notiffany (~> 0.0)
|
35
|
-
pry (>= 0.
|
39
|
+
pry (>= 0.13.0)
|
36
40
|
shellany (~> 0.0)
|
37
41
|
thor (>= 0.18.1)
|
38
42
|
guard-compat (1.2.1)
|
@@ -40,14 +44,14 @@ GEM
|
|
40
44
|
guard (~> 2.1)
|
41
45
|
guard-compat (~> 1.1)
|
42
46
|
rspec (>= 2.99.0, < 4.0)
|
43
|
-
i18n (1.8.
|
47
|
+
i18n (1.8.9)
|
44
48
|
concurrent-ruby (~> 1.0)
|
45
|
-
listen (3.
|
49
|
+
listen (3.5.1)
|
46
50
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
47
51
|
rb-inotify (~> 0.9, >= 0.9.10)
|
48
|
-
lumberjack (1.2.
|
52
|
+
lumberjack (1.2.8)
|
49
53
|
method_source (1.0.0)
|
50
|
-
minitest (5.14.
|
54
|
+
minitest (5.14.4)
|
51
55
|
nenv (0.3.0)
|
52
56
|
notiffany (0.1.3)
|
53
57
|
nenv (~> 0.1)
|
@@ -60,7 +64,7 @@ GEM
|
|
60
64
|
byebug (~> 11.0)
|
61
65
|
pry (~> 0.13.0)
|
62
66
|
rake (13.0.1)
|
63
|
-
rb-fsevent (0.
|
67
|
+
rb-fsevent (0.11.0)
|
64
68
|
rb-inotify (0.10.1)
|
65
69
|
ffi (~> 1.0)
|
66
70
|
rspec (3.9.0)
|
@@ -78,11 +82,10 @@ GEM
|
|
78
82
|
rspec-support (3.9.3)
|
79
83
|
shellany (0.0.1)
|
80
84
|
sqlite3 (1.4.2)
|
81
|
-
thor (1.0
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
zeitwerk (2.3.0)
|
85
|
+
thor (1.1.0)
|
86
|
+
tzinfo (2.0.4)
|
87
|
+
concurrent-ruby (~> 1.0)
|
88
|
+
zeitwerk (2.4.2)
|
86
89
|
|
87
90
|
PLATFORMS
|
88
91
|
ruby
|
@@ -90,6 +93,7 @@ PLATFORMS
|
|
90
93
|
DEPENDENCIES
|
91
94
|
activerecord
|
92
95
|
bundler (~> 2.0)
|
96
|
+
database_cleaner-active_record
|
93
97
|
factory_bot
|
94
98
|
guard
|
95
99
|
guard-rspec
|
@@ -102,4 +106,4 @@ DEPENDENCIES
|
|
102
106
|
sqlite3
|
103
107
|
|
104
108
|
BUNDLED WITH
|
105
|
-
2.
|
109
|
+
2.2.3
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# Rokaki
|
2
|
+
|
2
3
|
[![Gem Version](https://badge.fury.io/rb/rokaki.svg)](https://badge.fury.io/rb/rokaki)
|
3
4
|
|
4
5
|
This gem was born out of a desire to dry up filtering services in Rails apps or any Ruby app that uses the concept of "filters" or "facets".
|
@@ -46,7 +47,8 @@ class FilterArticles
|
|
46
47
|
|
47
48
|
def filter_results
|
48
49
|
@articles = @articles.where(date: date) if date
|
49
|
-
@articles = @articles.joins(:author).where(
|
50
|
+
@articles = @articles.joins(:author).where(authors: { first_name: author_first_name }) if author_first_name
|
51
|
+
@articles = @articles.joins(:author).where(authors: { last_name: author_last_name }) if author_last_name
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
@@ -98,6 +100,7 @@ advanced_filterable.advanced__filter_key_4__deep_leaf_array == [1,2,3,4]
|
|
98
100
|
advanced_filterable.advanced__filter_key_1__filter_key_3__deep_node == 'NODE'
|
99
101
|
```
|
100
102
|
### `#define_filter_map`
|
103
|
+
The define_filter_map method is more suited to classic "search", where you might want to search multiple fields on a model or across a graph. See the section on [filter_map](https://github.com/tevio/rokaki#2-the-filter_map-command-syntax) with OR for more on this kind of application.
|
101
104
|
|
102
105
|
This method takes a single field in the passed in filters hash and maps it to fields named in the second param, this is useful if you want to search for a single value across many different fields or associated tables simultaneously.
|
103
106
|
|
@@ -115,7 +118,7 @@ class FilterMap
|
|
115
118
|
define_filter_map :query, :mapped_a, association: :field
|
116
119
|
end
|
117
120
|
|
118
|
-
filter_map = FilterMap.new(
|
121
|
+
filter_map = FilterMap.new(fylterz: { query: 'H2O' })
|
119
122
|
|
120
123
|
filter_map.mapped_a == 'H2O'
|
121
124
|
filter_map.association_field = 'H2O'
|
@@ -227,6 +230,88 @@ filtered_authors = AuthorFilter.new(filters: filters).results
|
|
227
230
|
|
228
231
|
In the above example we search for authors who have written articles containing the word "Jiddu" in the title that also have reviews containing the sames word in their titles.
|
229
232
|
|
233
|
+
The above example performs an "ALL" like query, where all fields must satisfy the query term. Conversly you can use `or` to perform an "ANY", where any of the fields within the `or` will satisfy the query term, like so:-
|
234
|
+
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
class AuthorFilter
|
238
|
+
include Rokaki::FilterModel
|
239
|
+
|
240
|
+
filter_map :author, :query,
|
241
|
+
like: {
|
242
|
+
articles: {
|
243
|
+
title: :circumfix,
|
244
|
+
or: { # the or is aware of the join and will generate a compound join aware or query
|
245
|
+
reviews: {
|
246
|
+
title: :circumfix
|
247
|
+
}
|
248
|
+
}
|
249
|
+
},
|
250
|
+
}
|
251
|
+
|
252
|
+
attr_accessor :filters, :model
|
253
|
+
|
254
|
+
def initialize(filters:)
|
255
|
+
@filters = filters
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
filters = { query: "Lao" }
|
260
|
+
filtered_authors = AuthorFilter.new(filters: filters).results
|
261
|
+
```
|
262
|
+
|
263
|
+
## CAVEATS
|
264
|
+
Active record OR over a join may require you to add something like the following in an initializer in order for it to function properly:-
|
265
|
+
|
266
|
+
### #structurally_incompatible_values_for_or
|
267
|
+
|
268
|
+
``` ruby
|
269
|
+
module ActiveRecord
|
270
|
+
module QueryMethods
|
271
|
+
def structurally_incompatible_values_for_or(other)
|
272
|
+
Relation::SINGLE_VALUE_METHODS.reject { |m| send("#{m}_value") == other.send("#{m}_value") } +
|
273
|
+
(Relation::MULTI_VALUE_METHODS - [:joins, :eager_load, :references, :extending]).reject { |m| send("#{m}_values") == other.send("#{m}_values") } +
|
274
|
+
(Relation::CLAUSE_METHODS - [:having, :where]).reject { |m| send("#{m}_clause") == other.send("#{m}_clause") }
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
```
|
279
|
+
|
280
|
+
### A has one relation to a model called Or
|
281
|
+
If you happen to have a model/table named 'Or' then you can override the `or:` key syntax by specifying a special `or_key`:-
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
class AuthorFilter
|
285
|
+
include Rokaki::FilterModel
|
286
|
+
|
287
|
+
or_key :my_or
|
288
|
+
filter_map :author, :query,
|
289
|
+
like: {
|
290
|
+
articles: {
|
291
|
+
title: :circumfix,
|
292
|
+
my_or: { # the or is aware of the join and will generate a compound join aware or query
|
293
|
+
or: { # The Or model has a title field
|
294
|
+
title: :circumfix
|
295
|
+
}
|
296
|
+
}
|
297
|
+
},
|
298
|
+
}
|
299
|
+
|
300
|
+
attr_accessor :filters, :model
|
301
|
+
|
302
|
+
def initialize(filters:)
|
303
|
+
@filters = filters
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
filters = { query: "Syntaxes" }
|
308
|
+
filtered_authors = AuthorFilter.new(filters: filters).results
|
309
|
+
```
|
310
|
+
|
311
|
+
|
312
|
+
See [this issue](https://github.com/rails/rails/issues/24055) for details.
|
313
|
+
|
314
|
+
|
230
315
|
#### 3. The porcelain command syntax
|
231
316
|
|
232
317
|
In this syntax you will need to provide three keywords:- `filters`, `like` and `filter_model` if you are not passing in the model type and assigning it to `@model`
|
data/lib/rokaki.rb
CHANGED
@@ -3,13 +3,12 @@
|
|
3
3
|
require 'rokaki/version'
|
4
4
|
require 'rokaki/filterable'
|
5
5
|
require 'rokaki/filter_model'
|
6
|
+
require 'rokaki/filter_model/join_map'
|
6
7
|
require 'rokaki/filter_model/like_keys'
|
7
8
|
require 'rokaki/filter_model/basic_filter'
|
8
9
|
require 'rokaki/filter_model/nested_filter'
|
10
|
+
require 'rokaki/filter_model/nested_like_filters'
|
9
11
|
|
10
12
|
module Rokaki
|
11
13
|
class Error < StandardError; end
|
12
|
-
|
13
|
-
# include this module for filters dsl in an object
|
14
|
-
#
|
15
14
|
end
|
data/lib/rokaki/filter_model.rb
CHANGED
@@ -8,7 +8,7 @@ module Rokaki
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def prepare_terms(param, mode)
|
11
|
-
if param
|
11
|
+
if Array === param
|
12
12
|
return param.map { |term| "%#{term}%" } if mode == :circumfix
|
13
13
|
return param.map { |term| "%#{term}" } if mode == :prefix
|
14
14
|
return param.map { |term| "#{term}%" } if mode == :suffix
|
@@ -56,18 +56,39 @@ module Rokaki
|
|
56
56
|
|
57
57
|
@_chain_filters ||= []
|
58
58
|
filter_keys.each do |filter_key|
|
59
|
-
|
60
59
|
# TODO: does the key need casting to an array here?
|
61
60
|
_chain_filter(filter_key) unless filter_key.is_a? Hash
|
62
|
-
|
63
61
|
_chain_nested_filter(filter_key) if filter_key.is_a? Hash
|
62
|
+
end
|
63
|
+
|
64
|
+
define_results # writes out all the generated filters
|
65
|
+
end
|
64
66
|
|
67
|
+
def like_filters(like_keys, term_type: :like)
|
68
|
+
if @filter_map_query_key
|
69
|
+
define_filter_map(@filter_map_query_key, *like_keys.call)
|
70
|
+
else
|
71
|
+
define_filter_keys(*like_keys.call)
|
65
72
|
end
|
66
73
|
|
74
|
+
@_chain_filters ||= []
|
75
|
+
filter_map = []
|
76
|
+
|
77
|
+
nested_like_filter = NestedLikeFilters.new(
|
78
|
+
filter_key_object: like_keys,
|
79
|
+
prefix: filter_key_prefix,
|
80
|
+
infix: filter_key_infix,
|
81
|
+
db: @_filter_db,
|
82
|
+
type: term_type,
|
83
|
+
or_key: or_key
|
84
|
+
)
|
85
|
+
nested_like_filter.call
|
86
|
+
|
87
|
+
_chain_nested_like_filter(nested_like_filter)
|
67
88
|
define_results # writes out all the generated filters
|
68
89
|
end
|
69
90
|
|
70
|
-
def
|
91
|
+
def _build_basic_filter(key)
|
71
92
|
basic_filter = BasicFilter.new(
|
72
93
|
keys: [key],
|
73
94
|
prefix: filter_key_prefix,
|
@@ -77,13 +98,17 @@ module Rokaki
|
|
77
98
|
db: @_filter_db
|
78
99
|
)
|
79
100
|
basic_filter.call
|
101
|
+
basic_filter
|
102
|
+
end
|
80
103
|
|
104
|
+
def _chain_filter(key)
|
105
|
+
basic_filter = _build_basic_filter(key)
|
81
106
|
class_eval basic_filter.filter_method, __FILE__, __LINE__ - 2
|
82
107
|
|
83
108
|
@_chain_filters << basic_filter.filter_template
|
84
109
|
end
|
85
110
|
|
86
|
-
def
|
111
|
+
def _build_nested_filter(filters_object)
|
87
112
|
nested_filter = NestedFilter.new(
|
88
113
|
filter_key_object: filters_object,
|
89
114
|
prefix: filter_key_prefix,
|
@@ -93,6 +118,21 @@ module Rokaki
|
|
93
118
|
db: @_filter_db
|
94
119
|
)
|
95
120
|
nested_filter.call
|
121
|
+
nested_filter
|
122
|
+
end
|
123
|
+
|
124
|
+
def _chain_nested_like_filter(filters_object)
|
125
|
+
filters_object.filter_methods.each do |filter_method|
|
126
|
+
class_eval filter_method, __FILE__, __LINE__ - 2
|
127
|
+
end
|
128
|
+
|
129
|
+
filters_object.templates.each do |filter_template|
|
130
|
+
@_chain_filters << filter_template
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def _chain_nested_filter(filters_object)
|
135
|
+
nested_filter = _build_nested_filter(filters_object)
|
96
136
|
|
97
137
|
nested_filter.filter_methods.each do |filter_method|
|
98
138
|
class_eval filter_method, __FILE__, __LINE__ - 2
|
@@ -103,10 +143,6 @@ module Rokaki
|
|
103
143
|
end
|
104
144
|
end
|
105
145
|
|
106
|
-
# def associated_table(association)
|
107
|
-
# @model.reflect_on_association(association).klass.table_name
|
108
|
-
# end
|
109
|
-
|
110
146
|
def filter_model(model_class)
|
111
147
|
@model = (model_class.is_a?(Class) ? model_class : Object.const_get(model_class.capitalize))
|
112
148
|
class_eval "def set_model; @model ||= #{@model}; end;"
|
@@ -116,41 +152,16 @@ module Rokaki
|
|
116
152
|
raise ArgumentError, 'argument mush be a hash' unless args.is_a? Hash
|
117
153
|
@_like_semantics = (@_like_semantics || {}).merge(args)
|
118
154
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
filters(*keys)
|
155
|
+
like_keys = LikeKeys.new(args)
|
156
|
+
like_filters(like_keys, term_type: :like)
|
123
157
|
end
|
124
158
|
|
125
159
|
def ilike(args)
|
126
160
|
raise ArgumentError, 'argument mush be a hash' unless args.is_a? Hash
|
127
161
|
@i_like_semantics = (@i_like_semantics || {}).merge(args)
|
128
162
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
filters(*keys)
|
133
|
-
end
|
134
|
-
|
135
|
-
def deep_chain(keys, value)
|
136
|
-
if value.is_a? Hash
|
137
|
-
value.keys.map do |key|
|
138
|
-
_keys = keys.dup << key
|
139
|
-
deep_chain(_keys, value[key])
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
if value.is_a? Array
|
144
|
-
value.each do |av|
|
145
|
-
_keys = keys.dup << av
|
146
|
-
_build_deep_chain(_keys)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
if value.is_a? Symbol
|
151
|
-
_keys = keys.dup << value
|
152
|
-
_build_deep_chain(_keys)
|
153
|
-
end
|
163
|
+
like_keys = LikeKeys.new(args)
|
164
|
+
like_filters(like_keys, term_type: :ilike)
|
154
165
|
end
|
155
166
|
|
156
167
|
# the model method is called to instatiate @model from the
|
@@ -10,8 +10,9 @@ module Rokaki
|
|
10
10
|
@like_semantics = like_semantics
|
11
11
|
@i_like_semantics = i_like_semantics
|
12
12
|
@db = db
|
13
|
+
@filter_query = nil
|
13
14
|
end
|
14
|
-
attr_reader :keys, :prefix, :infix, :like_semantics, :i_like_semantics, :db
|
15
|
+
attr_reader :keys, :prefix, :infix, :like_semantics, :i_like_semantics, :db, :filter_query
|
15
16
|
attr_accessor :filter_method, :filter_template
|
16
17
|
|
17
18
|
def call
|
@@ -56,7 +57,7 @@ module Rokaki
|
|
56
57
|
query = "@model.where(#{key}: #{filter})"
|
57
58
|
end
|
58
59
|
|
59
|
-
query
|
60
|
+
@filter_query = query
|
60
61
|
end
|
61
62
|
|
62
63
|
def build_like_query(type:, query:, filter:, mode:, key:)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rokaki
|
4
|
+
module FilterModel
|
5
|
+
class DeepAssignStruct
|
6
|
+
def initialize(keys:, value:, struct: nil)
|
7
|
+
@keys = keys
|
8
|
+
@value = value
|
9
|
+
@struct = struct
|
10
|
+
end
|
11
|
+
attr_reader :keys, :value
|
12
|
+
attr_accessor :struct
|
13
|
+
|
14
|
+
def call
|
15
|
+
base_keys = keys
|
16
|
+
i = base_keys.length - 1
|
17
|
+
|
18
|
+
base_keys.reverse_each.reduce (value) do |struc,key|
|
19
|
+
i -= 1
|
20
|
+
cur_keys = base_keys[0..i]
|
21
|
+
|
22
|
+
if struct
|
23
|
+
val = struct.dig(*cur_keys)
|
24
|
+
val[key] = struc
|
25
|
+
p val
|
26
|
+
return val
|
27
|
+
else
|
28
|
+
if key.is_a?(Integer)
|
29
|
+
struct = [struc]
|
30
|
+
else
|
31
|
+
{ key=>struc }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def deep_construct(keys, value)
|
40
|
+
|
41
|
+
if keys.last.is_a?(Integer)
|
42
|
+
rstruct = struct[keys.last] = value
|
43
|
+
else
|
44
|
+
rstruct = { keys.last => value }
|
45
|
+
end
|
46
|
+
|
47
|
+
keys[0..-2].reverse_each.reduce (rstruct) do |struc,key|
|
48
|
+
if key.is_a?(Integer)
|
49
|
+
[struc]
|
50
|
+
else
|
51
|
+
{ key=>struc }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# DOUBLE SPLAT HASHES TO MAKE ARG LISTS!
|
4
|
+
#
|
5
|
+
# Array#dig could be useful
|
6
|
+
#
|
7
|
+
# Array#intersection could be useful
|
8
|
+
#
|
9
|
+
# Array#difference could be useful
|
10
|
+
#
|
11
|
+
|
12
|
+
module Rokaki
|
13
|
+
module FilterModel
|
14
|
+
class JoinMap
|
15
|
+
def initialize(key_paths)
|
16
|
+
@key_paths = key_paths
|
17
|
+
@result = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :key_paths
|
21
|
+
attr_accessor :result
|
22
|
+
|
23
|
+
def call
|
24
|
+
key_paths.uniq.each do |key_path|
|
25
|
+
current_key_path = []
|
26
|
+
previous_key = nil
|
27
|
+
|
28
|
+
if Symbol === key_path
|
29
|
+
if key_paths.length == 1
|
30
|
+
@result = key_paths
|
31
|
+
else
|
32
|
+
result[key_path] = {} unless result.keys.include? key_path
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if Array === key_path
|
37
|
+
key_path.each do |key|
|
38
|
+
current_path_length = current_key_path.length
|
39
|
+
|
40
|
+
if current_path_length > 0 && result.dig(current_key_path).nil?
|
41
|
+
|
42
|
+
if current_path_length == 1
|
43
|
+
parent_result = result[previous_key]
|
44
|
+
|
45
|
+
if Symbol === parent_result && parent_result != key
|
46
|
+
result[previous_key] = [parent_result, key]
|
47
|
+
elsif Array === parent_result
|
48
|
+
|
49
|
+
parent_result.each_with_index do |array_item, index|
|
50
|
+
if array_item == key
|
51
|
+
current_key_path << index
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
else
|
56
|
+
result[previous_key] = key unless result[previous_key] == key
|
57
|
+
end
|
58
|
+
|
59
|
+
else
|
60
|
+
previous_key_path = current_key_path - [previous_key]
|
61
|
+
previous_path_length = previous_key_path.length
|
62
|
+
p current_key_path
|
63
|
+
|
64
|
+
if previous_path_length == 1
|
65
|
+
res = result.dig(*previous_key_path)
|
66
|
+
|
67
|
+
if Symbol === res
|
68
|
+
result[previous_key_path.first] = { previous_key => key }
|
69
|
+
end
|
70
|
+
elsif previous_path_length > 1
|
71
|
+
res = result.dig(*previous_key_path)
|
72
|
+
|
73
|
+
if Symbol === res
|
74
|
+
base = previous_key_path.pop
|
75
|
+
result.dig(*previous_key_path)[base] = { previous_key => key }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
else
|
81
|
+
end
|
82
|
+
|
83
|
+
previous_key = key
|
84
|
+
current_key_path << key
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
result
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
@@ -9,43 +9,45 @@ module Rokaki
|
|
9
9
|
class LikeKeys
|
10
10
|
def initialize(args)
|
11
11
|
@args = args
|
12
|
-
@
|
12
|
+
@keys = []
|
13
|
+
@key_paths = []
|
13
14
|
end
|
14
15
|
|
15
|
-
attr_reader :args, :
|
16
|
+
attr_reader :args, :keys, :key_paths
|
16
17
|
|
17
18
|
def call
|
18
19
|
args.keys.each do |key|
|
19
|
-
|
20
|
+
map_keys(key: key, value: args[key])
|
20
21
|
end
|
21
|
-
|
22
|
+
keys
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
25
26
|
|
26
|
-
def map_keys(value
|
27
|
-
key_result = {}
|
27
|
+
def map_keys(key:, value:, key_path: [])
|
28
28
|
|
29
|
-
if value.is_a?
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
if sub_value.is_a? Symbol
|
34
|
-
if key_result[key].is_a? Array
|
35
|
-
key_result[key] << sub_key
|
36
|
-
else
|
37
|
-
key_result[key] = [ sub_key ]
|
38
|
-
end
|
39
|
-
|
40
|
-
elsif sub_value.is_a? Hash
|
41
|
-
key_result[key] = map_keys(sub_value, sub_key)
|
29
|
+
if value.is_a?(Hash)
|
30
|
+
key_path << key
|
31
|
+
value.keys.each do |key|
|
32
|
+
map_keys(key: key, value: value[key], key_path: key_path.dup)
|
42
33
|
end
|
43
34
|
end
|
44
|
-
|
45
|
-
|
35
|
+
|
36
|
+
if value.is_a?(Symbol)
|
37
|
+
keys << (key_path.empty? ? key : deep_assign(key_path, key))
|
38
|
+
key_path << key
|
39
|
+
key_paths << key_path
|
46
40
|
end
|
47
41
|
|
48
|
-
|
42
|
+
key_path
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
# Many thanks Cary Swoveland
|
47
|
+
# https://stackoverflow.com/questions/56634950/ruby-dig-set-assign-values-using-hashdig/56635124
|
48
|
+
#
|
49
|
+
def deep_assign(keys, value)
|
50
|
+
keys[0..-2].reverse_each.reduce ({ keys.last => value }) { |h,key| { key=>h } }
|
49
51
|
end
|
50
52
|
end
|
51
53
|
end
|
@@ -66,7 +66,7 @@ module Rokaki
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def _build_deep_chain(keys)
|
69
|
-
name = ''
|
69
|
+
name = ''
|
70
70
|
count = keys.size - 1
|
71
71
|
|
72
72
|
joins_before = []
|
@@ -75,13 +75,13 @@ module Rokaki
|
|
75
75
|
where_before = []
|
76
76
|
where_after = []
|
77
77
|
out = ''
|
78
|
-
|
78
|
+
search_mode = nil
|
79
79
|
type = nil
|
80
80
|
leaf = nil
|
81
81
|
|
82
|
-
if
|
82
|
+
if search_mode = find_like_key(keys)
|
83
83
|
type = 'LIKE'
|
84
|
-
elsif
|
84
|
+
elsif search_mode = find_i_like_key(keys)
|
85
85
|
type = 'ILIKE'
|
86
86
|
end
|
87
87
|
leaf = keys.pop
|
@@ -114,12 +114,12 @@ module Rokaki
|
|
114
114
|
joins = joins.join
|
115
115
|
where = where.join
|
116
116
|
|
117
|
-
if
|
117
|
+
if search_mode
|
118
118
|
query = build_like_query(
|
119
119
|
type: type,
|
120
120
|
query: '',
|
121
121
|
filter: "#{prefix}#{name}",
|
122
|
-
|
122
|
+
search_mode: search_mode,
|
123
123
|
key: keys.last.to_s.pluralize,
|
124
124
|
leaf: leaf
|
125
125
|
)
|
@@ -136,15 +136,15 @@ module Rokaki
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
139
|
-
def build_like_query(type:, query:, filter:,
|
139
|
+
def build_like_query(type:, query:, filter:, search_mode:, key:, leaf:)
|
140
140
|
if db == :postgres
|
141
141
|
query = "where(\"#{key}.#{leaf} #{type} ANY (ARRAY[?])\", "
|
142
|
-
query += "prepare_terms(#{filter}, :#{
|
142
|
+
query += "prepare_terms(#{filter}, :#{search_mode}))"
|
143
143
|
else
|
144
144
|
query = "where(\"#{key}.#{leaf} #{type} :query\", "
|
145
|
-
query += "query: \"%\#{#{filter}}%\")" if
|
146
|
-
query += "query: \"%\#{#{filter}}\")" if
|
147
|
-
query += "query: \"\#{#{filter}}%\")" if
|
145
|
+
query += "query: \"%\#{#{filter}}%\")" if search_mode == :circumfix
|
146
|
+
query += "query: \"%\#{#{filter}}\")" if search_mode == :prefix
|
147
|
+
query += "query: \"\#{#{filter}}%\")" if search_mode == :suffix
|
148
148
|
end
|
149
149
|
|
150
150
|
query
|
@@ -0,0 +1,241 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/inflector'
|
3
|
+
|
4
|
+
module Rokaki
|
5
|
+
module FilterModel
|
6
|
+
class NestedLikeFilters
|
7
|
+
def initialize(filter_key_object:, prefix:, infix:, db:, mode: :and, or_key: :or, type: :like)
|
8
|
+
@filter_key_object = filter_key_object
|
9
|
+
@prefix = prefix
|
10
|
+
@infix = infix
|
11
|
+
@db = db
|
12
|
+
@mode = mode
|
13
|
+
@or_key = or_key
|
14
|
+
@type = type
|
15
|
+
|
16
|
+
@names = []
|
17
|
+
@filter_methods = []
|
18
|
+
@templates = []
|
19
|
+
@filter_queries = []
|
20
|
+
@method_names = []
|
21
|
+
@filter_names = []
|
22
|
+
@join_key_paths = []
|
23
|
+
@key_paths = []
|
24
|
+
@search_modes = []
|
25
|
+
@modes = []
|
26
|
+
end
|
27
|
+
attr_reader :filter_key_object, :prefix, :infix, :like_semantics, :i_like_semantics,
|
28
|
+
:db, :mode, :or_key, :filter_queries, :type
|
29
|
+
attr_accessor :filter_methods, :templates, :method_names, :filter_names, :names, :join_key_paths, :key_paths, :search_modes, :modes
|
30
|
+
|
31
|
+
def call
|
32
|
+
build_filters_data
|
33
|
+
compound_filters
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_filters_data
|
37
|
+
results = filter_key_object.key_paths.each do |key_path|
|
38
|
+
if key_path.is_a?(Symbol)
|
39
|
+
build_filter_data(key_path)
|
40
|
+
else
|
41
|
+
if key_path.include? or_key
|
42
|
+
build_filter_data(key_path.dup, mode: or_key)
|
43
|
+
else
|
44
|
+
build_filter_data(key_path.dup)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def compound_filters
|
51
|
+
# key_paths represents a structure like
|
52
|
+
# [
|
53
|
+
# [ # this is an or
|
54
|
+
# [:articles, :title],
|
55
|
+
# [:articles, :authors, :first_name],
|
56
|
+
# [:articles, :authors, :reviews, :title],
|
57
|
+
# [:articles, :authors, :reviews, :content]
|
58
|
+
# ],
|
59
|
+
# [:articles, :content] # this is an and
|
60
|
+
# ]
|
61
|
+
#
|
62
|
+
# Each item in the array represents a compounded filter
|
63
|
+
#
|
64
|
+
key_paths.each_with_index do |key_path_item, index|
|
65
|
+
base_names = get_name(index)
|
66
|
+
join_map = JoinMap.new(join_key_paths[index])
|
67
|
+
join_map.call
|
68
|
+
|
69
|
+
if key_path_item.first.is_a?(Array)
|
70
|
+
item_search_modes = search_modes[index]
|
71
|
+
|
72
|
+
base_name = base_names.shift
|
73
|
+
method_name = prefix.to_s + ([:filter].push base_name).compact.join(infix.to_s)
|
74
|
+
method_name += (infix.to_s+'or'+infix.to_s) + (base_names).join(infix.to_s+'or'+infix.to_s)
|
75
|
+
item_filter_names = [prefix.to_s + base_name]
|
76
|
+
|
77
|
+
base_names.each do |filter_base_name|
|
78
|
+
item_filter_names << (prefix.to_s + filter_base_name)
|
79
|
+
end
|
80
|
+
|
81
|
+
base_modes = modes[index]
|
82
|
+
key_path_item.each_with_index do |key_path, kp_index|
|
83
|
+
|
84
|
+
build_query(keys: key_path.dup, join_map: join_map.result, mode: base_modes[kp_index], filter_name: item_filter_names[kp_index], search_mode: item_search_modes[kp_index])
|
85
|
+
end
|
86
|
+
|
87
|
+
item_filter_queries = filter_queries[index]
|
88
|
+
first_query = item_filter_queries.shift
|
89
|
+
|
90
|
+
ored = item_filter_queries.map do |query|
|
91
|
+
".or(#{query})"
|
92
|
+
end
|
93
|
+
|
94
|
+
filter_conditions = item_filter_names.join(' || ')
|
95
|
+
|
96
|
+
@filter_methods << "def #{method_name}; #{first_query + ored.join}; end;"
|
97
|
+
@templates << "@model = #{method_name} if #{filter_conditions};"
|
98
|
+
else
|
99
|
+
|
100
|
+
base_name = get_name(index)
|
101
|
+
filter_name = "#{prefix}#{get_filter_name(index)}"
|
102
|
+
|
103
|
+
method_name = ([prefix, :filter, base_name]).compact.join(infix.to_s)
|
104
|
+
|
105
|
+
build_query(keys: key_path_item.dup, join_map: join_map.result, filter_name: filter_name, search_mode: search_modes[index])
|
106
|
+
|
107
|
+
@filter_methods << "def #{method_name}; #{filter_queries[index]}; end;"
|
108
|
+
@templates << "@model = #{method_name} if #{filter_name};"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def get_name(index)
|
116
|
+
names[index]
|
117
|
+
end
|
118
|
+
|
119
|
+
def get_filter_name(index)
|
120
|
+
filter_names[index]
|
121
|
+
end
|
122
|
+
|
123
|
+
def find_mode_key(keys)
|
124
|
+
current_like_key = @filter_key_object.args.dup
|
125
|
+
keys.each do |key|
|
126
|
+
current_like_key = current_like_key[key]
|
127
|
+
end
|
128
|
+
current_like_key
|
129
|
+
end
|
130
|
+
|
131
|
+
def build_filter_data(key_path, mode: :and)
|
132
|
+
# if key_path.is_a?(Symbol)
|
133
|
+
# search_mode = @filter_key_object.args[key_path]
|
134
|
+
|
135
|
+
# name = key_path
|
136
|
+
# filter_name = (prefix.to_s + key_path.to_s)
|
137
|
+
# @names << name
|
138
|
+
# @filter_names << filter_name
|
139
|
+
# @key_paths << key_path
|
140
|
+
# @search_modes << search_mode
|
141
|
+
# @modes << mode
|
142
|
+
# else
|
143
|
+
search_mode = find_mode_key(key_path)
|
144
|
+
|
145
|
+
key_path.delete(mode)
|
146
|
+
|
147
|
+
name = key_path.join(infix.to_s)
|
148
|
+
filter_name = key_path.compact.join(infix.to_s)
|
149
|
+
|
150
|
+
if mode == or_key
|
151
|
+
@names << [@names.pop, name].flatten
|
152
|
+
@filter_names << [@filter_names.pop, filter_name].flatten
|
153
|
+
|
154
|
+
or_key_paths = @key_paths.pop
|
155
|
+
if or_key_paths.first.is_a?(Array)
|
156
|
+
@key_paths << [*or_key_paths] + [key_path.dup]
|
157
|
+
else
|
158
|
+
@key_paths << [or_key_paths] + [key_path.dup]
|
159
|
+
end
|
160
|
+
|
161
|
+
@search_modes << [@search_modes.pop, search_mode].flatten
|
162
|
+
@modes << [@modes.pop, mode].flatten
|
163
|
+
|
164
|
+
else
|
165
|
+
@names << name
|
166
|
+
@filter_names << filter_name
|
167
|
+
@key_paths << key_path.dup # having this wrapped in an array is messy for single items
|
168
|
+
@search_modes << search_mode
|
169
|
+
@modes << mode
|
170
|
+
end
|
171
|
+
|
172
|
+
join_key_path = key_path.dup
|
173
|
+
|
174
|
+
leaf = join_key_path.pop
|
175
|
+
if mode == or_key
|
176
|
+
or_join_key_paths = @join_key_paths.pop
|
177
|
+
if or_join_key_paths.first.is_a?(Array)
|
178
|
+
@join_key_paths << [*or_join_key_paths] + [join_key_path.dup]
|
179
|
+
else
|
180
|
+
@join_key_paths << [or_join_key_paths] + [join_key_path.dup]
|
181
|
+
end
|
182
|
+
else
|
183
|
+
if join_key_path.length == 1
|
184
|
+
@join_key_paths << join_key_path
|
185
|
+
else
|
186
|
+
@join_key_paths << [join_key_path.dup]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
# end
|
190
|
+
end
|
191
|
+
|
192
|
+
# DOUBLE SPLAT HASHES TO MAKE ARG LISTS!
|
193
|
+
def build_query(keys: , join_map:, mode: :and, filter_name:, search_mode:)
|
194
|
+
leaf = nil
|
195
|
+
leaf = keys.pop
|
196
|
+
|
197
|
+
|
198
|
+
query = build_like_query(
|
199
|
+
type: type,
|
200
|
+
query: '',
|
201
|
+
filter: filter_name,
|
202
|
+
search_mode: search_mode,
|
203
|
+
key: keys.last,
|
204
|
+
leaf: leaf
|
205
|
+
)
|
206
|
+
|
207
|
+
if join_map.empty?
|
208
|
+
filter_query = "@model.#{query}"
|
209
|
+
elsif join_map.is_a?(Array)
|
210
|
+
filter_query = "@model.joins(*#{join_map}).#{query}"
|
211
|
+
else
|
212
|
+
filter_query = "@model.joins(**#{join_map}).#{query}"
|
213
|
+
end
|
214
|
+
|
215
|
+
if mode == or_key
|
216
|
+
@filter_queries << [@filter_queries.pop, filter_query].flatten
|
217
|
+
else
|
218
|
+
@filter_queries << filter_query
|
219
|
+
end
|
220
|
+
filter_query
|
221
|
+
end
|
222
|
+
|
223
|
+
def build_like_query(type:, query:, filter:, search_mode:, key:, leaf:)
|
224
|
+
key_leaf = key ? "#{key.to_s.pluralize}.#{leaf}" : leaf
|
225
|
+
if db == :postgres
|
226
|
+
query = "where(\"#{key_leaf} #{type.to_s.upcase} ANY (ARRAY[?])\", "
|
227
|
+
query += "prepare_terms(#{filter}, :#{search_mode}))"
|
228
|
+
else
|
229
|
+
query = "where(\"#{key_leaf} #{type.to_s.upcase} :query\", "
|
230
|
+
query += "query: \"%\#{#{filter}}%\")" if search_mode == :circumfix
|
231
|
+
query += "query: \"%\#{#{filter}}\")" if search_mode == :prefix
|
232
|
+
query += "query: \"\#{#{filter}}%\")" if search_mode == :suffix
|
233
|
+
end
|
234
|
+
|
235
|
+
query
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
data/lib/rokaki/filterable.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rokaki
|
4
|
+
# include this module for rokaki's filtering dsl in any object
|
5
|
+
#
|
2
6
|
module Filterable
|
3
7
|
def self.included(base)
|
4
8
|
base.extend(ClassMethods)
|
@@ -11,15 +15,15 @@ module Rokaki
|
|
11
15
|
|
12
16
|
def define_filter_keys(*filter_keys)
|
13
17
|
filter_keys.each do |filter_key|
|
14
|
-
_build_filter([filter_key]) unless filter_key
|
15
|
-
_nested_key filter_key if filter_key
|
18
|
+
_build_filter([filter_key]) unless Hash === filter_key
|
19
|
+
_nested_key filter_key if Hash === filter_key
|
16
20
|
end
|
17
21
|
end
|
18
22
|
|
19
23
|
def define_filter_map(query_field, *filter_keys)
|
20
24
|
filter_keys.each do |filter_key|
|
21
|
-
_map_filters(query_field, [filter_key]) unless filter_key
|
22
|
-
_nested_map query_field, filter_key if filter_key
|
25
|
+
_map_filters(query_field, [filter_key]) unless Hash === filter_key
|
26
|
+
_nested_map query_field, filter_key if Hash === filter_key
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
@@ -35,11 +39,16 @@ module Rokaki
|
|
35
39
|
@filter_key_infix ||= infix
|
36
40
|
end
|
37
41
|
|
42
|
+
def or_key(or_key = :or)
|
43
|
+
@or_key ||= or_key
|
44
|
+
end
|
45
|
+
|
38
46
|
def filterable_object_name(name = 'filters')
|
39
47
|
@filterable_object_name ||= name
|
40
48
|
end
|
41
49
|
|
42
50
|
def _build_filter(keys)
|
51
|
+
keys.delete(or_key)
|
43
52
|
name = @filter_key_prefix.to_s
|
44
53
|
count = keys.size - 1
|
45
54
|
|
@@ -52,6 +61,7 @@ module Rokaki
|
|
52
61
|
end
|
53
62
|
|
54
63
|
def _map_filters(query_field, keys)
|
64
|
+
keys.delete(or_key)
|
55
65
|
name = @filter_key_prefix.to_s
|
56
66
|
count = keys.size - 1
|
57
67
|
|
data/lib/rokaki/version.rb
CHANGED
data/rokaki.gemspec
CHANGED
@@ -34,13 +34,17 @@ Gem::Specification.new do |spec|
|
|
34
34
|
|
35
35
|
spec.add_development_dependency 'activerecord'
|
36
36
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
37
|
-
spec.add_development_dependency 'factory_bot'
|
38
|
-
spec.add_development_dependency 'guard'
|
39
|
-
spec.add_development_dependency 'guard-rspec'
|
40
|
-
spec.add_development_dependency 'pg'
|
41
37
|
spec.add_development_dependency 'pry'
|
42
38
|
spec.add_development_dependency 'pry-byebug'
|
43
39
|
spec.add_development_dependency 'rake', '~> 13.0'
|
40
|
+
|
41
|
+
spec.add_development_dependency 'guard'
|
42
|
+
spec.add_development_dependency 'guard-rspec'
|
44
43
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
44
|
+
spec.add_development_dependency 'factory_bot'
|
45
|
+
|
46
|
+
spec.add_development_dependency 'pg'
|
45
47
|
spec.add_development_dependency 'sqlite3'
|
48
|
+
spec.add_development_dependency 'database_cleaner-active_record'
|
49
|
+
|
46
50
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rokaki
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Martin
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: pry
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: pry-byebug
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
@@ -81,21 +81,21 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rake
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
89
|
+
version: '13.0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
96
|
+
version: '13.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: guard
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -109,7 +109,7 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: guard-rspec
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
@@ -123,7 +123,21 @@ dependencies:
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: factory_bot
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
128
142
|
requirements:
|
129
143
|
- - ">="
|
@@ -137,35 +151,35 @@ dependencies:
|
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: '0'
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
154
|
+
name: pg
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
142
156
|
requirements:
|
143
|
-
- - "
|
157
|
+
- - ">="
|
144
158
|
- !ruby/object:Gem::Version
|
145
|
-
version: '
|
159
|
+
version: '0'
|
146
160
|
type: :development
|
147
161
|
prerelease: false
|
148
162
|
version_requirements: !ruby/object:Gem::Requirement
|
149
163
|
requirements:
|
150
|
-
- - "
|
164
|
+
- - ">="
|
151
165
|
- !ruby/object:Gem::Version
|
152
|
-
version: '
|
166
|
+
version: '0'
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
168
|
+
name: sqlite3
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
156
170
|
requirements:
|
157
|
-
- - "
|
171
|
+
- - ">="
|
158
172
|
- !ruby/object:Gem::Version
|
159
|
-
version: '
|
173
|
+
version: '0'
|
160
174
|
type: :development
|
161
175
|
prerelease: false
|
162
176
|
version_requirements: !ruby/object:Gem::Requirement
|
163
177
|
requirements:
|
164
|
-
- - "
|
178
|
+
- - ">="
|
165
179
|
- !ruby/object:Gem::Version
|
166
|
-
version: '
|
180
|
+
version: '0'
|
167
181
|
- !ruby/object:Gem::Dependency
|
168
|
-
name:
|
182
|
+
name: database_cleaner-active_record
|
169
183
|
requirement: !ruby/object:Gem::Requirement
|
170
184
|
requirements:
|
171
185
|
- - ">="
|
@@ -187,6 +201,7 @@ extra_rdoc_files: []
|
|
187
201
|
files:
|
188
202
|
- ".gitignore"
|
189
203
|
- ".rspec"
|
204
|
+
- ".ruby-version"
|
190
205
|
- ".travis.yml"
|
191
206
|
- CODE_OF_CONDUCT.md
|
192
207
|
- Gemfile
|
@@ -200,9 +215,12 @@ files:
|
|
200
215
|
- lib/rokaki.rb
|
201
216
|
- lib/rokaki/filter_model.rb
|
202
217
|
- lib/rokaki/filter_model/basic_filter.rb
|
218
|
+
- lib/rokaki/filter_model/deep_assign_struct.rb
|
203
219
|
- lib/rokaki/filter_model/filter_chain.rb
|
220
|
+
- lib/rokaki/filter_model/join_map.rb
|
204
221
|
- lib/rokaki/filter_model/like_keys.rb
|
205
222
|
- lib/rokaki/filter_model/nested_filter.rb
|
223
|
+
- lib/rokaki/filter_model/nested_like_filters.rb
|
206
224
|
- lib/rokaki/filterable.rb
|
207
225
|
- lib/rokaki/version.rb
|
208
226
|
- rokaki.gemspec
|
@@ -211,7 +229,7 @@ licenses:
|
|
211
229
|
- MIT
|
212
230
|
metadata:
|
213
231
|
homepage_uri: https://github.com/tevio/rokaki
|
214
|
-
post_install_message:
|
232
|
+
post_install_message:
|
215
233
|
rdoc_options: []
|
216
234
|
require_paths:
|
217
235
|
- lib
|
@@ -226,8 +244,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
226
244
|
- !ruby/object:Gem::Version
|
227
245
|
version: '0'
|
228
246
|
requirements: []
|
229
|
-
rubygems_version: 3.1.
|
230
|
-
signing_key:
|
247
|
+
rubygems_version: 3.1.6
|
248
|
+
signing_key:
|
231
249
|
specification_version: 4
|
232
250
|
summary: A web request filtering library
|
233
251
|
test_files: []
|