rokaki 0.5.1 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 53244b10ca9f4942f648d825156616dfc9057cf8b5c39afa4f995152f8f56091
4
- data.tar.gz: 30b6db3fe0f4c6e68a6b4f628e254c85dcc7b72aa387d1ec91b1f5cd3c674010
3
+ metadata.gz: 8a4312a80ff21e5c59187f2677c803626aa2d9bd2331316dea46de60461c50fa
4
+ data.tar.gz: f2d1b2b2aeefb8f82cf7be3ecfe5f058472701ad23064a8909fe09e6c289e4d7
5
5
  SHA512:
6
- metadata.gz: 13df19ccfa76fc0265e7f12c0dacbb7c4dba8b07169db9a3c08c7368fe07cc9f50cbd2ea056490f0c6cff1dbafe550d28998c94ac339b0316d868a95e350a569
7
- data.tar.gz: 164ea95d95555b85ac86cc81fd2b2b9dca2b2245246241c6d52b7f16c8357aaa9e6c67d446b2f79d1213162aa35ece8b420f10545cd448517e32a8802d500e93
6
+ metadata.gz: c4d9d19c3d0f008721cfe2cbae3364f29e46fc094edd03af7738999a5cbe3680606f9201d884d4ce1316d5ad3da54b1f572d8bec7bcc3a00cc60696e36a0db11
7
+ data.tar.gz: 2c83644c0338d9024422de614edb92d336ec542261f3453b07f5713e676e8c0c6c5947e8e75fb85bfde6f862185f56a9eac44664ea6769767dda2e901937b9ea
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
12
  tags
13
+ *.gem
@@ -1,28 +1,32 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rokaki (0.5.1)
4
+ rokaki (0.8.1)
5
+ activesupport
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
9
- activemodel (6.0.0)
10
- activesupport (= 6.0.0)
11
- activerecord (6.0.0)
12
- activemodel (= 6.0.0)
13
- activesupport (= 6.0.0)
14
- activesupport (6.0.0)
10
+ activemodel (6.0.3.2)
11
+ activesupport (= 6.0.3.2)
12
+ activerecord (6.0.3.2)
13
+ activemodel (= 6.0.3.2)
14
+ activesupport (= 6.0.3.2)
15
+ activesupport (6.0.3.2)
15
16
  concurrent-ruby (~> 1.0, >= 1.0.2)
16
17
  i18n (>= 0.7, < 2)
17
18
  minitest (~> 5.1)
18
19
  tzinfo (~> 1.1)
19
- zeitwerk (~> 2.1, >= 2.1.8)
20
- coderay (1.1.2)
21
- concurrent-ruby (1.1.5)
20
+ zeitwerk (~> 2.2, >= 2.2.2)
21
+ byebug (11.1.3)
22
+ coderay (1.1.3)
23
+ concurrent-ruby (1.1.6)
22
24
  diff-lcs (1.3)
23
- ffi (1.11.1)
25
+ factory_bot (6.0.2)
26
+ activesupport (>= 5.0.0)
27
+ ffi (1.13.1)
24
28
  formatador (0.2.5)
25
- guard (2.15.0)
29
+ guard (2.16.2)
26
30
  formatador (>= 0.2.4)
27
31
  listen (>= 2.7, < 4.0)
28
32
  lumberjack (>= 1.0.12, < 2.0)
@@ -36,47 +40,49 @@ GEM
36
40
  guard (~> 2.1)
37
41
  guard-compat (~> 1.1)
38
42
  rspec (>= 2.99.0, < 4.0)
39
- i18n (1.6.0)
43
+ i18n (1.8.3)
40
44
  concurrent-ruby (~> 1.0)
41
- listen (3.1.5)
42
- rb-fsevent (~> 0.9, >= 0.9.4)
43
- rb-inotify (~> 0.9, >= 0.9.7)
44
- ruby_dep (~> 1.2)
45
- lumberjack (1.0.13)
46
- method_source (0.9.2)
47
- minitest (5.11.3)
45
+ listen (3.2.1)
46
+ rb-fsevent (~> 0.10, >= 0.10.3)
47
+ rb-inotify (~> 0.9, >= 0.9.10)
48
+ lumberjack (1.2.6)
49
+ method_source (1.0.0)
50
+ minitest (5.14.1)
48
51
  nenv (0.3.0)
49
52
  notiffany (0.1.3)
50
53
  nenv (~> 0.1)
51
54
  shellany (~> 0.0)
52
- pry (0.12.2)
53
- coderay (~> 1.1.0)
54
- method_source (~> 0.9.0)
55
- rake (10.5.0)
56
- rb-fsevent (0.10.3)
57
- rb-inotify (0.10.0)
55
+ pg (1.2.3)
56
+ pry (0.13.1)
57
+ coderay (~> 1.1)
58
+ method_source (~> 1.0)
59
+ pry-byebug (3.9.0)
60
+ byebug (~> 11.0)
61
+ pry (~> 0.13.0)
62
+ rake (13.0.1)
63
+ rb-fsevent (0.10.4)
64
+ rb-inotify (0.10.1)
58
65
  ffi (~> 1.0)
59
- rspec (3.8.0)
60
- rspec-core (~> 3.8.0)
61
- rspec-expectations (~> 3.8.0)
62
- rspec-mocks (~> 3.8.0)
63
- rspec-core (3.8.2)
64
- rspec-support (~> 3.8.0)
65
- rspec-expectations (3.8.4)
66
+ rspec (3.9.0)
67
+ rspec-core (~> 3.9.0)
68
+ rspec-expectations (~> 3.9.0)
69
+ rspec-mocks (~> 3.9.0)
70
+ rspec-core (3.9.2)
71
+ rspec-support (~> 3.9.3)
72
+ rspec-expectations (3.9.2)
66
73
  diff-lcs (>= 1.2.0, < 2.0)
67
- rspec-support (~> 3.8.0)
68
- rspec-mocks (3.8.1)
74
+ rspec-support (~> 3.9.0)
75
+ rspec-mocks (3.9.1)
69
76
  diff-lcs (>= 1.2.0, < 2.0)
70
- rspec-support (~> 3.8.0)
71
- rspec-support (3.8.2)
72
- ruby_dep (1.5.0)
77
+ rspec-support (~> 3.9.0)
78
+ rspec-support (3.9.3)
73
79
  shellany (0.0.1)
74
- sqlite3 (1.4.1)
75
- thor (0.20.3)
80
+ sqlite3 (1.4.2)
81
+ thor (1.0.1)
76
82
  thread_safe (0.3.6)
77
- tzinfo (1.2.5)
83
+ tzinfo (1.2.7)
78
84
  thread_safe (~> 0.1)
79
- zeitwerk (2.1.9)
85
+ zeitwerk (2.3.0)
80
86
 
81
87
  PLATFORMS
82
88
  ruby
@@ -84,13 +90,16 @@ PLATFORMS
84
90
  DEPENDENCIES
85
91
  activerecord
86
92
  bundler (~> 2.0)
93
+ factory_bot
87
94
  guard
88
95
  guard-rspec
96
+ pg
89
97
  pry
90
- rake (~> 10.0)
98
+ pry-byebug
99
+ rake (~> 13.0)
91
100
  rokaki!
92
101
  rspec (~> 3.0)
93
102
  sqlite3
94
103
 
95
104
  BUNDLED WITH
96
- 2.0.2
105
+ 2.1.4
data/Guardfile CHANGED
@@ -36,6 +36,7 @@ guard :rspec, cmd: "bundle exec rspec" do
36
36
  watch(rspec.spec_support) { rspec.spec_dir }
37
37
  watch(rspec.spec_files)
38
38
 
39
+ watch(%r{^lib/rokaki/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
39
40
  # Ruby files
40
41
  ruby = dsl.ruby
41
42
  dsl.watch_spec_files_for(ruby.lib_files)
@@ -19,3 +19,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  THE SOFTWARE.
22
+
23
+ f007539360
data/README.md CHANGED
@@ -1,14 +1,19 @@
1
1
  # Rokaki
2
2
  [![Gem Version](https://badge.fury.io/rb/rokaki.svg)](https://badge.fury.io/rb/rokaki)
3
3
 
4
- This gem was born out of a desire to dry up filtering services in Rails or any ruby app that uses the concept of `filters`.
5
-
6
- It's a simple gem that just provides you with a basic dsl based on the filter params that you might pass through from a web request.
4
+ 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".
7
5
 
6
+ There are two modes of use `Filterable` and `FilterModel` that can be activated through the use of two mixins respectively, `include Rokaki::Filterable` or `include Rokaki::FilterModel`.
8
7
  ## Installation
9
8
 
10
9
  Add this line to your application's Gemfile:
11
10
 
11
+ You can install from Rubygems:
12
+ ```
13
+ gem 'rokaki'
14
+ ```
15
+ Or from github
16
+
12
17
  ```ruby
13
18
  gem 'rokaki', git: 'https://github.com/tevio/rokaki.git'
14
19
  ```
@@ -17,42 +22,118 @@ And then execute:
17
22
 
18
23
  $ bundle
19
24
 
20
- ## Usage
25
+ ## `Rokaki::Filterable` - Usage
26
+
27
+ To use the DSL first include the `Rokaki::Filterable` module in your [por](http://blog.jayfields.com/2007/10/ruby-poro.html) class.
21
28
 
22
- To use the basic DSL include the `Rokaki::Filterable` module
29
+ ### `#define_filter_keys`
30
+ #### A Simple Example
23
31
 
24
32
  A simple example might be:-
25
33
 
26
34
  ```ruby
27
- class FilterArticles
28
- include Rokaki::Filterable
35
+ class FilterArticles
36
+ include Rokaki::Filterable
29
37
 
30
- def initialize(filters:)
31
- @filters = filters
32
- @articles = Article
33
- end
38
+ def initialize(filters:)
39
+ @filters = filters
40
+ @articles = Article
41
+ end
34
42
 
35
- attr_accessor :filters
43
+ attr_accessor :filters
36
44
 
37
- define_filter_keys :date, author: [:first_name, :last_name]
45
+ define_filter_keys :date, author: [:first_name, :last_name]
38
46
 
39
- def filter_results
40
- @articles = @articles.where(date: date) if date
41
- @articles = @articles.joins(:author).where(author: { first_name: author_first_name }) if author_first_name
42
- end
47
+ def filter_results
48
+ @articles = @articles.where(date: date) if date
49
+ @articles = @articles.joins(:author).where(author: { first_name: author_first_name }) if author_first_name
43
50
  end
51
+ end
52
+
53
+ article_filter = FilterArticles.new(filters: {
54
+ date: '10-10-10',
55
+ author: {
56
+ first_name: 'Steve',
57
+ last_name: 'Martin'
58
+ }})
59
+ article_filter.author_first_name == 'Steve'
60
+ article_filter.author_last_name == 'Martin'
61
+ article_filter.date == '10-10-10'
62
+ ```
63
+
64
+ In this example Rokaki maps the "flat" attribute "keys" `date`, `author_first_name` and `author_last_name` to a `@filters` object with the expected deep structure `{ date: '10-10-10', author: { first_name: 'Steve' } }`, to make it simple to use them in filter queries.
65
+
66
+ #### A More Complex Example
67
+
68
+ ```ruby
69
+ class AdvancedFilterable
70
+ include Rokaki::Filterable
71
+
72
+ def initialize(filters:)
73
+ @fyltrz = filters
74
+ end
75
+ attr_accessor :fyltrz
76
+
77
+ filterable_object_name :fyltrz
78
+ filter_key_infix :__
79
+ define_filter_keys :basic, advanced: {
80
+ filter_key_1: [:filter_key_2, { filter_key_3: :deep_node }],
81
+ filter_key_4: :deep_leaf_array
82
+ }
83
+ end
84
+
85
+
86
+ advanced_filterable = AdvancedFilterable.new(filters: {
87
+ basic: 'ABC',
88
+ advanced: {
89
+ filter_key_1: {
90
+ filter_key_2: '123',
91
+ filter_key_3: { deep_node: 'NODE' }
92
+ },
93
+ filter_key_4: { deep_leaf_array: [1,2,3,4] }
94
+ }
95
+ })
96
+
97
+ advanced_filterable.advanced__filter_key_4__deep_leaf_array == [1,2,3,4]
98
+ advanced_filterable.advanced__filter_key_1__filter_key_3__deep_node == 'NODE'
44
99
  ```
100
+ ### `#define_filter_map`
45
101
 
46
- This maps attributes `date`, `author_first_name` and `author_last_name` to a filters object with the structure `{ date: '10-10-10', author: { first_name: 'Shteeve' } }`.
102
+ 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.
47
103
 
48
- ## Additional options
49
- You can specify a `filter_key_prefix` and a `filter_key_infix` to change the structure of the accessors.
104
+ #### A Simple Example
105
+ ```ruby
106
+ class FilterMap
107
+ include Rokaki::Filterable
108
+
109
+ def initialize(fylterz:)
110
+ @fylterz = fylterz
111
+ end
112
+ attr_accessor :fylterz
113
+
114
+ filterable_object_name :fylterz
115
+ define_filter_map :query, :mapped_a, association: :field
116
+ end
117
+
118
+ filter_map = FilterMap.new(fytlerz: { query: 'H2O' })
119
+
120
+ filter_map.mapped_a == 'H2O'
121
+ filter_map.association_field = 'H2O'
122
+ ```
123
+
124
+ #### Additional `Filterable` options
125
+ You can specify several configuration options, for example a `filter_key_prefix` and a `filter_key_infix` to change the structure of the generated filter accessors.
50
126
 
51
127
  `filter_key_prefix :__` would result in key accessors like `__author_first_name`
52
128
 
53
129
  `filter_key_infix :__` would result in key accessors like `author__first_name`
54
130
 
55
- ## ActiveRecord
131
+ `filterable_object_name :fylterz` would use an internal filter state object named `@fyltrz` instead of the default `@filters`
132
+
133
+
134
+ ## `Rokaki::FilterModel` - Usage
135
+
136
+ ### ActiveRecord
56
137
  Include `Rokaki::FilterModel` in any ActiveRecord model (only AR >= 6.0.0 tested so far) you can generate the filter keys and the actual filter lookup code using the `filters` keyword on a model like so:-
57
138
 
58
139
  ```ruby
@@ -67,7 +148,7 @@ end
67
148
 
68
149
 
69
150
  class ArticleFilter
70
- include FilterModel
151
+ include Rokaki::FilterModel
71
152
 
72
153
  filters :date, :title, author: [:first_name, :last_name]
73
154
 
@@ -84,16 +165,19 @@ filter = ArticleFilter.new(filters: params[:filters])
84
165
  filtered_results = filter.results
85
166
 
86
167
  ```
168
+ ### Arrays of params
169
+ You can also filter collections of fields, simply pass an array of filter values instead of a single value, eg:- `{ date: '10-10-10', author: { first_name: ['Author1', 'Author2'] } }`.
170
+
87
171
 
88
172
  ### Partial matching
89
- You can use `like` (or, if you use postgres, the case insensitive `ilike`) to perform a partial match on a specific key, there are 3 options:- `:prefix`, `:circumfix` and `:suffix`. There are two syntaxes you can use for this:-
173
+ You can use `like` (or, if you use postgres, the case insensitive `ilike`) to perform a partial match on a specific field, there are 3 options:- `:prefix`, `:circumfix` and `:suffix`. There are two syntaxes you can use for this:-
90
174
 
91
175
  #### 1. The `filter` command syntax
92
176
 
93
177
 
94
178
  ```ruby
95
179
  class ArticleFilter
96
- include FilterModel
180
+ include Rokaki::FilterModel
97
181
 
98
182
  filter :article,
99
183
  like: { # you can use ilike here instead if you use postgres and want case insensitive results
@@ -112,14 +196,45 @@ end
112
196
  ```
113
197
  Or
114
198
 
115
- #### 2. The porcelain command syntax
199
+ #### 2. The `filter_map` command syntax
200
+ `filter_map` takes the model name, then a single 'query' field and maps it to fields named in the options, this is useful if you want to search for a single value across many different fields or associated tables simultaneously. (builds on `define_filter_map`)
201
+
202
+
203
+ ```ruby
204
+ class AuthorFilter
205
+ include Rokaki::FilterModel
206
+
207
+ filter_map :author, :query,
208
+ like: {
209
+ articles: {
210
+ title: :circumfix,
211
+ reviews: {
212
+ title: :circumfix
213
+ }
214
+ },
215
+ }
216
+
217
+ attr_accessor :filters, :model
218
+
219
+ def initialize(filters:)
220
+ @filters = filters
221
+ end
222
+ end
223
+
224
+ filters = { query: "Jiddu" }
225
+ filtered_authors = AuthorFilter.new(filters: filters).results
226
+ ```
227
+
228
+ 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
+
230
+ #### 3. The porcelain command syntax
116
231
 
117
232
  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`
118
233
 
119
234
 
120
235
  ```ruby
121
236
  class ArticleFilter
122
- include FilterModel
237
+ include Rokaki::FilterModel
123
238
 
124
239
  filters :date, :title, author: [:first_name, :last_name]
125
240
  like title: :circumfix
@@ -138,7 +253,7 @@ Or without the model in the initializer
138
253
 
139
254
  ```ruby
140
255
  class ArticleFilter
141
- include FilterModel
256
+ include Rokaki::FilterModel
142
257
 
143
258
  filters :date, :title, author: [:first_name, :last_name]
144
259
  like title: :circumfix
@@ -158,6 +273,64 @@ Would produce a query with a LIKE which circumfixes '%' around the filter term,
158
273
  @model = @model.where('title LIKE :query', query: "%#{title}%")
159
274
  ```
160
275
 
276
+ ### Deep nesting
277
+ You can filter joins both with basic matching and partial matching
278
+ ```ruby
279
+ class ArticleFilter
280
+ include Rokaki::FilterModel
281
+
282
+ filter :author,
283
+ like: {
284
+ articles: {
285
+ reviews: {
286
+ title: :circumfix
287
+ }
288
+ },
289
+ }
290
+
291
+ attr_accessor :filters
292
+
293
+ def initialize(filters:)
294
+ @filters = filters
295
+ end
296
+ end
297
+ ```
298
+
299
+ ### Array params
300
+ You can pass array params (and partially match them), to filters (search multiple matches) in databases that support it (postgres) by passing the `db` param to the filter keyword, and passing an array of search terms at runtine
301
+
302
+ ```ruby
303
+ class ArticleFilter
304
+ include Rokaki::FilterModel
305
+
306
+ filter :article,
307
+ like: {
308
+ author: {
309
+ first_name: :circumfix,
310
+ last_name: :circumfix
311
+ }
312
+ },
313
+ match: %i[title created_at],
314
+ db: :postgres
315
+
316
+ attr_accessor :filters
317
+
318
+ def initialize(filters:)
319
+ @filters = filters
320
+ end
321
+ end
322
+
323
+ filterable = ArticleFilter.new(filters:
324
+ {
325
+ author: {
326
+ first_name: ['Match One', 'Match Two']
327
+ }
328
+ }
329
+ )
330
+
331
+ filterable.results
332
+ ```
333
+
161
334
 
162
335
  ## Development
163
336
 
@@ -4,6 +4,8 @@ require 'rokaki/version'
4
4
  require 'rokaki/filterable'
5
5
  require 'rokaki/filter_model'
6
6
  require 'rokaki/filter_model/like_keys'
7
+ require 'rokaki/filter_model/basic_filter'
8
+ require 'rokaki/filter_model/nested_filter'
7
9
 
8
10
  module Rokaki
9
11
  class Error < StandardError; end
@@ -7,120 +7,96 @@ module Rokaki
7
7
  base.extend(ClassMethods)
8
8
  end
9
9
 
10
+ def prepare_terms(param, mode)
11
+ if param.is_a? Array
12
+ return param.map { |term| "%#{term}%" } if mode == :circumfix
13
+ return param.map { |term| "%#{term}" } if mode == :prefix
14
+ return param.map { |term| "#{term}%" } if mode == :suffix
15
+ else
16
+ return ["%#{param}%"] if mode == :circumfix
17
+ return ["%#{param}"] if mode == :prefix
18
+ return ["#{param}%"] if mode == :suffix
19
+ end
20
+ end
21
+
22
+
10
23
  module ClassMethods
11
24
  include Filterable::ClassMethods
12
25
 
13
26
  private
14
27
 
28
+ def filter_map(model, query_key, options)
29
+ filter_model(model)
30
+ @_query_key = query_key
31
+
32
+ @_filter_db = options[:db] || :postgres
33
+ like(options[:like]) if options[:like]
34
+ ilike(options[:ilike]) if options[:ilike]
35
+ filters(*options[:match]) if options[:match]
36
+ end
37
+
15
38
  def filter(model, options)
16
39
  filter_model(model)
17
40
 
41
+ @_filter_db = options[:db] || :postgres
18
42
  like(options[:like]) if options[:like]
19
43
  ilike(options[:ilike]) if options[:ilike]
20
44
  filters(*options[:match]) if options[:match]
21
45
  end
22
46
 
23
47
  def filters(*filter_keys)
24
- define_filter_keys *filter_keys
48
+ if @_query_key
49
+ define_filter_map(@_query_key, *filter_keys)
50
+ else
51
+ define_filter_keys(*filter_keys)
52
+ end
25
53
 
26
54
  @_chain_filters ||= []
27
55
  filter_keys.each do |filter_key|
28
- _chain_filter([filter_key]) unless filter_key.is_a? Hash
29
- _chain_nested(filter_key) if filter_key.is_a? Hash
30
- end
31
56
 
32
- define_results
33
- end
57
+ # TODO: does the key need casting to an array here?
58
+ _chain_filter(filter_key) unless filter_key.is_a? Hash
34
59
 
35
- def _chain_filter(keys)
36
- first_key = keys.shift
37
- filter = "#{filter_key_prefix}#{first_key}"
38
- name = first_key
60
+ _chain_nested_filter(filter_key) if filter_key.is_a? Hash
39
61
 
40
- keys.each do |key|
41
- filter += "#{filter_key_infix}#{key}"
42
- name += "#{filter_key_infix}#{key}"
43
62
  end
44
63
 
45
- filter_method = "def #{filter_key_prefix}filter_#{name};" \
46
- "#{_chain_filter_type(name)} end;"
47
-
48
- class_eval filter_method, __FILE__, __LINE__ - 2
49
-
50
- @_chain_filters << "@model = #{filter_key_prefix}filter_#{name} if #{filter};"
64
+ define_results # writes out all the generated filters
51
65
  end
52
66
 
53
- def _chain_filter_type(key)
54
- filter = "#{filter_key_prefix}#{key}"
55
- query = ''
56
-
57
- if @_like_semantics && mode = @_like_semantics[key]
58
- query = like_semantics(
59
- type: 'LIKE',
60
- query: query,
61
- filter: filter,
62
- mode: mode,
63
- key: key
64
- )
65
-
66
- elsif @i_like_semantics && mode = @i_like_semantics[key]
67
- query = like_semantics(
68
- type: 'ILIKE',
69
- query: query,
70
- filter: filter,
71
- mode: mode,
72
- key: key
73
- )
74
-
75
- else
76
- query = "@model.where(#{key}: #{filter})"
77
- end
67
+ def _chain_filter(key)
68
+ basic_filter = BasicFilter.new(
69
+ keys: [key],
70
+ prefix: filter_key_prefix,
71
+ infix: filter_key_infix,
72
+ like_semantics: @_like_semantics,
73
+ i_like_semantics: @i_like_semantics,
74
+ db: @_filter_db
75
+ )
76
+ basic_filter.call
78
77
 
79
- query
80
- end
78
+ class_eval basic_filter.filter_method, __FILE__, __LINE__ - 2
81
79
 
82
- def like_semantics(type:, query:, filter:, mode:, key:)
83
- query = "@model.where(\"#{key} #{type} :query\", "
84
- query += "query: \"%\#{#{filter}}%\")" if mode == :circumfix
85
- query += "query: \"%\#{#{filter}}\")" if mode == :prefix
86
- query += "query: \"\#{#{filter}}%\")" if mode == :suffix
87
- query
80
+ @_chain_filters << basic_filter.filter_template
88
81
  end
89
82
 
90
- def _build_deep_chain(keys)
91
- name = filter_key_prefix.to_s
92
- count = keys.size - 1
93
-
94
- joins = ''
95
- where = ''
96
- out = ''
97
-
98
- leaf = keys.pop
99
-
100
- keys.each_with_index do |key, _i|
101
- next unless keys.length == 1
102
- name = "#{filter_key_prefix}#{key}#{filter_key_infix}#{leaf}"
103
- joins = ":#{key}"
104
-
105
- where = "{ #{key.to_s.pluralize}: { #{leaf}: #{name} } }"
83
+ def _chain_nested_filter(filters_object)
84
+ nested_filter = NestedFilter.new(
85
+ filter_key_object: filters_object,
86
+ prefix: filter_key_prefix,
87
+ infix: filter_key_infix,
88
+ like_semantics: @_like_semantics,
89
+ i_like_semantics: @i_like_semantics,
90
+ db: @_filter_db
91
+ )
92
+ nested_filter.call
93
+
94
+ nested_filter.filter_methods.each do |filter_method|
95
+ class_eval filter_method, __FILE__, __LINE__ - 2
106
96
  end
107
97
 
108
- joins = joins += out
109
- where = where += out
110
-
111
- # chain filter here?
112
- #
113
- filter_method = "def #{filter_key_prefix}filter_#{name};"\
114
- "@model.joins(#{joins}).where(#{where}); end;"
115
-
116
- class_eval filter_method, __FILE__, __LINE__ - 2
117
-
118
- @_chain_filters << "@model = #{filter_key_prefix}filter_#{name} if #{name};"
119
- end
120
-
121
- def _chain_nested(filters_object)
122
- filters_object.keys.each do |key|
123
- deep_chain([key], filters_object[key])
98
+ nested_filter.filter_templates.each do |filter_template|
99
+ @_chain_filters << filter_template
124
100
  end
125
101
  end
126
102
 
@@ -128,9 +104,9 @@ module Rokaki
128
104
  @model.reflect_on_association(association).klass.table_name
129
105
  end
130
106
 
131
- def filter_model(model)
132
- @model = (model.is_a?(Class) ? model : Object.const_get(model.capitalize))
133
- class_eval "def model; @model ||= #{@model}; end;"
107
+ def filter_model(model_class)
108
+ @model = (model_class.is_a?(Class) ? model_class : Object.const_get(model_class.capitalize))
109
+ class_eval "def set_model; @model ||= #{@model}; end;"
134
110
  end
135
111
 
136
112
  def like(args)
@@ -138,8 +114,9 @@ module Rokaki
138
114
  @_like_semantics = (@_like_semantics || {}).merge(args)
139
115
 
140
116
  key_builder = LikeKeys.new(args)
117
+ keys = key_builder.call
141
118
 
142
- filters(*key_builder.call)
119
+ filters(*keys)
143
120
  end
144
121
 
145
122
  def ilike(args)
@@ -147,8 +124,9 @@ module Rokaki
147
124
  @i_like_semantics = (@i_like_semantics || {}).merge(args)
148
125
 
149
126
  key_builder = LikeKeys.new(args)
127
+ keys = key_builder.call
150
128
 
151
- filters(*key_builder.call)
129
+ filters(*keys)
152
130
  end
153
131
 
154
132
  def deep_chain(keys, value)
@@ -176,7 +154,7 @@ module Rokaki
176
154
  # filter_model method
177
155
  #
178
156
  def define_results
179
- results_def = 'def results;model;'
157
+ results_def = 'def results; @model || set_model;'
180
158
  @_chain_filters.each do |item|
181
159
  results_def += item
182
160
  end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rokaki
4
+ module FilterModel
5
+ class BasicFilter
6
+ def initialize(keys:, prefix:, infix:, like_semantics:, i_like_semantics:, db:)
7
+ @keys = keys
8
+ @prefix = prefix
9
+ @infix = infix
10
+ @like_semantics = like_semantics
11
+ @i_like_semantics = i_like_semantics
12
+ @db = db
13
+ end
14
+ attr_reader :keys, :prefix, :infix, :like_semantics, :i_like_semantics, :db
15
+ attr_accessor :filter_method, :filter_template
16
+
17
+ def call
18
+ first_key = keys.shift
19
+ filter = "#{prefix}#{first_key}"
20
+ name = first_key
21
+
22
+ keys.each do |key|
23
+ filter += "#{infix}#{key}"
24
+ name += "#{infix}#{key}"
25
+ end
26
+
27
+ @filter_method = "def #{prefix}filter_#{name};" \
28
+ "#{_chain_filter_type(name)} end;"
29
+
30
+ # class_eval filter_method, __FILE__, __LINE__ - 2
31
+
32
+ @filter_template = "@model = #{prefix}filter_#{name} if #{filter};"
33
+ end
34
+
35
+ def _chain_filter_type(key)
36
+ filter = "#{prefix}#{key}"
37
+ query = ''
38
+
39
+ if like_semantics && mode = like_semantics[key]
40
+ query = build_like_query(
41
+ type: 'LIKE',
42
+ query: query,
43
+ filter: filter,
44
+ mode: mode,
45
+ key: key
46
+ )
47
+ elsif i_like_semantics && mode = i_like_semantics[key]
48
+ query = build_like_query(
49
+ type: 'ILIKE',
50
+ query: query,
51
+ filter: filter,
52
+ mode: mode,
53
+ key: key
54
+ )
55
+ else
56
+ query = "@model.where(#{key}: #{filter})"
57
+ end
58
+
59
+ query
60
+ end
61
+
62
+ def build_like_query(type:, query:, filter:, mode:, key:)
63
+ if db == :postgres
64
+ query = "@model.where(\"#{key} #{type} ANY (ARRAY[?])\", "
65
+ query += "prepare_terms(#{filter}, :#{mode}))"
66
+ else
67
+ query = "@model.where(\"#{key} #{type} :query\", "
68
+ query += "query: \"%\#{#{filter}}%\")" if mode == :circumfix
69
+ query += "query: \"%\#{#{filter}}\")" if mode == :prefix
70
+ query += "query: \"\#{#{filter}}%\")" if mode == :suffix
71
+ end
72
+
73
+ query
74
+ end
75
+ end
76
+ end
77
+ end
78
+
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rokaki
4
+ module FilterModel
5
+ class FilterChain
6
+ def initialize(keys)
7
+ @keys = keys
8
+ @filter_chain = []
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Rokaki
4
4
  module FilterModel
5
+ # Converts deep hashes into keys
6
+ # effectively drops the leaf values and make's their
7
+ # keys the leaves
8
+ #
5
9
  class LikeKeys
6
10
  def initialize(args)
7
11
  @args = args
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+ require 'active_support/inflector'
3
+
4
+ module Rokaki
5
+ module FilterModel
6
+ class NestedFilter
7
+ def initialize(filter_key_object:, prefix:, infix:, like_semantics:, i_like_semantics:, db:)
8
+ @filter_key_object = filter_key_object
9
+ @prefix = prefix
10
+ @infix = infix
11
+ @like_semantics = like_semantics
12
+ @i_like_semantics = i_like_semantics
13
+ @filter_methods = []
14
+ @filter_templates = []
15
+ @db = db
16
+ end
17
+ attr_reader :filter_key_object, :prefix, :infix, :like_semantics, :i_like_semantics, :db
18
+ attr_accessor :filter_methods, :filter_templates
19
+
20
+ def call # _chain_nested_filter
21
+ filter_key_object.keys.each do |key|
22
+ deep_chain([key], filter_key_object[key])
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def deep_chain(keys, value)
29
+ if value.is_a? Hash
30
+ value.keys.map do |key|
31
+ _keys = keys.dup << key
32
+ deep_chain(_keys, value[key])
33
+ end
34
+ end
35
+
36
+ if value.is_a? Array
37
+ value.each do |av|
38
+ _keys = keys.dup << av
39
+ _build_deep_chain(_keys)
40
+ end
41
+ end
42
+
43
+ if value.is_a? Symbol
44
+ _keys = keys.dup << value
45
+ _build_deep_chain(_keys)
46
+ end
47
+ end
48
+
49
+ def find_like_key(keys)
50
+ return nil unless like_semantics && like_semantics.keys.any?
51
+ current_like_key = like_semantics
52
+ keys.each do |key|
53
+ current_like_key = current_like_key[key]
54
+ end
55
+ current_like_key
56
+ end
57
+
58
+ def find_i_like_key(keys)
59
+ return nil unless like_semantics && i_like_semantics.keys.any?
60
+ current_like_key = i_like_semantics
61
+ keys.each do |key|
62
+ current_like_key = current_like_key[key]
63
+ end
64
+ current_like_key
65
+ end
66
+
67
+ def _build_deep_chain(keys)
68
+ name = '' # prefix.to_s
69
+ count = keys.size - 1
70
+
71
+ joins_before = []
72
+ joins_after = []
73
+ joins = ''
74
+ where_before = []
75
+ where_after = []
76
+ out = ''
77
+ mode = nil
78
+ type = nil
79
+ leaf = nil
80
+
81
+ if mode = find_like_key(keys)
82
+ type = 'LIKE'
83
+ elsif mode = find_i_like_key(keys)
84
+ type = 'ILIKE'
85
+ end
86
+ leaf = keys.pop
87
+
88
+ keys.each_with_index do |key, i|
89
+ if keys.length == 1
90
+ joins_before << ":#{key}"
91
+ else
92
+ if i == 0
93
+ joins_before << "#{key}: "
94
+ elsif (keys.length-1) == i
95
+ joins_before << " :#{key}"
96
+ else
97
+ joins_before << "{ #{key}:"
98
+ joins_after << " }"
99
+ end
100
+ end
101
+
102
+ name += "#{key}#{infix}"
103
+ where_before.push("{ #{key.to_s.pluralize}: ")
104
+ where_after.push(" }")
105
+ end
106
+
107
+ joins = joins_before + joins_after
108
+
109
+ name += "#{leaf}"
110
+ where_middle = ["{ #{leaf}: #{prefix}#{name} }"]
111
+
112
+ where = where_before + where_middle + where_after
113
+ joins = joins.join
114
+ where = where.join
115
+
116
+ if mode
117
+ query = build_like_query(
118
+ type: type,
119
+ query: '',
120
+ filter: "#{prefix}#{name}",
121
+ mode: mode,
122
+ key: keys.last.to_s.pluralize,
123
+ leaf: leaf
124
+ )
125
+
126
+ @filter_methods << "def #{prefix}filter#{infix}#{name};"\
127
+ "@model.joins(#{joins}).#{query}; end;"
128
+
129
+ @filter_templates << "@model = #{prefix}filter#{infix}#{name} if #{prefix}#{name};"
130
+ else
131
+ @filter_methods << "def #{prefix}filter#{infix}#{name};"\
132
+ "@model.joins(#{joins}).where(#{where}); end;"
133
+
134
+ @filter_templates << "@model = #{prefix}filter#{infix}#{name} if #{prefix}#{name};"
135
+ end
136
+ end
137
+
138
+ def build_like_query(type:, query:, filter:, mode:, key:, leaf:)
139
+ if db == :postgres
140
+ query = "where(\"#{key}.#{leaf} #{type} ANY (ARRAY[?])\", "
141
+ query += "prepare_terms(#{filter}, :#{mode}))"
142
+ else
143
+ query = "where(\"#{key}.#{leaf} #{type} :query\", "
144
+ query += "query: \"%\#{#{filter}}%\")" if mode == :circumfix
145
+ query += "query: \"%\#{#{filter}}\")" if mode == :prefix
146
+ query += "query: \"\#{#{filter}}%\")" if mode == :suffix
147
+ end
148
+
149
+ query
150
+ end
151
+
152
+ end
153
+ end
154
+ end
155
+
@@ -16,6 +16,13 @@ module Rokaki
16
16
  end
17
17
  end
18
18
 
19
+ def define_filter_map(query_field, *filter_keys)
20
+ filter_keys.each do |filter_key|
21
+ _map_filters(query_field, [filter_key]) unless filter_key.is_a? Hash
22
+ _nested_map query_field, filter_key if filter_key.is_a? Hash
23
+ end
24
+ end
25
+
19
26
  def filter_key_prefix(prefix = nil)
20
27
  @filter_key_prefix ||= prefix
21
28
  end
@@ -24,6 +31,10 @@ module Rokaki
24
31
  @filter_key_infix ||= infix
25
32
  end
26
33
 
34
+ def filterable_object_name(name = 'filters')
35
+ @filterable_object_name ||= name
36
+ end
37
+
27
38
  def _build_filter(keys)
28
39
  name = @filter_key_prefix.to_s
29
40
  count = keys.size - 1
@@ -33,12 +44,30 @@ module Rokaki
33
44
  name += filter_key_infix.to_s unless count == i
34
45
  end
35
46
 
36
- class_eval "def #{name}; filters.dig(*#{keys}); end;", __FILE__, __LINE__
47
+ class_eval "def #{name}; #{filterable_object_name}.dig(*#{keys}); end;", __FILE__, __LINE__
48
+ end
49
+
50
+ def _map_filters(query_field, keys)
51
+ name = @filter_key_prefix.to_s
52
+ count = keys.size - 1
53
+
54
+ keys.each_with_index do |key, i|
55
+ name += key.to_s
56
+ name += filter_key_infix.to_s unless count == i
57
+ end
58
+
59
+ class_eval "def #{name}; #{filterable_object_name}.dig(:#{query_field}); end;", __FILE__, __LINE__
37
60
  end
38
61
 
39
62
  def _nested_key(filters_object)
40
63
  filters_object.keys.each do |key|
41
- deep_map([key], filters_object[key])
64
+ deep_map([key], filters_object[key]) { |keys| _build_filter(keys) }
65
+ end
66
+ end
67
+
68
+ def _nested_map(query_field, filters_object)
69
+ filters_object.keys.each do |key|
70
+ deep_map([key], filters_object[key]) { |keys| _map_filters(query_field, keys) }
42
71
  end
43
72
  end
44
73
 
@@ -46,21 +75,24 @@ module Rokaki
46
75
  if value.is_a? Hash
47
76
  value.keys.map do |key|
48
77
  _keys = keys.dup << key
49
- deep_map(_keys, value[key])
78
+ deep_map(_keys, value[key], &Proc.new)
50
79
  end
51
80
  end
52
81
 
53
-
54
82
  if value.is_a? Array
55
83
  value.each do |av|
84
+ if av.is_a? Symbol
56
85
  _keys = keys.dup << av
57
- _build_filter(_keys)
86
+ yield _keys
87
+ else
88
+ deep_map(keys, av, &Proc.new)
89
+ end
58
90
  end
59
91
  end
60
92
 
61
93
  if value.is_a? Symbol
62
94
  _keys = keys.dup << value
63
- _build_filter(_keys)
95
+ yield _keys
64
96
  end
65
97
  end
66
98
 
@@ -1,3 +1,3 @@
1
1
  module Rokaki
2
- VERSION = "0.5.1"
2
+ VERSION = "0.8.1"
3
3
  end
@@ -1,39 +1,46 @@
1
- lib = File.expand_path("lib", __dir__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require "rokaki/version"
5
+ require 'rokaki/version'
4
6
 
5
7
  Gem::Specification.new do |spec|
6
- spec.name = "rokaki"
8
+ spec.name = 'rokaki'
7
9
  spec.version = Rokaki::VERSION
8
- spec.authors = ["Steve Martin"]
9
- spec.email = ["steve@martian.media"]
10
+ spec.authors = ['Steve Martin']
11
+ spec.email = ['steve@martian.media']
10
12
 
11
- spec.summary = %q{A web request filtering library}
12
- spec.description = %q{A dsl for filtering data in web requests}
13
- spec.homepage = "https://github.com/tevio/rokaki"
14
- spec.license = "MIT"
13
+ spec.summary = 'A web request filtering library'
14
+ spec.description = 'A dsl for filtering data in web requests'
15
+ spec.homepage = 'https://github.com/tevio/rokaki'
16
+ spec.license = 'MIT'
15
17
 
16
18
  # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
17
19
 
18
- spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata['homepage_uri'] = spec.homepage
19
21
  # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
20
22
  # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
21
23
 
22
24
  # Specify which files should be added to the gem when it is released.
23
25
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
26
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
27
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
28
  end
27
- spec.bindir = "exe"
29
+ spec.bindir = 'exe'
28
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
- spec.require_paths = ["lib"]
30
-
31
- spec.add_development_dependency "bundler", "~> 2.0"
32
- spec.add_development_dependency "rake", "~> 10.0"
33
- spec.add_development_dependency "rspec", "~> 3.0"
34
- spec.add_development_dependency "guard"
35
- spec.add_development_dependency "pry"
36
- spec.add_development_dependency "guard-rspec"
37
- spec.add_development_dependency "activerecord"
38
- spec.add_development_dependency "sqlite3"
31
+ spec.require_paths = ['lib']
32
+
33
+ spec.add_dependency 'activesupport'
34
+
35
+ spec.add_development_dependency 'activerecord'
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
+ spec.add_development_dependency 'pry'
42
+ spec.add_development_dependency 'pry-byebug'
43
+ spec.add_development_dependency 'rake', '~> 13.0'
44
+ spec.add_development_dependency 'rspec', '~> 3.0'
45
+ spec.add_development_dependency 'sqlite3'
39
46
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rokaki
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Martin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-10-17 00:00:00.000000000 Z
11
+ date: 2020-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: bundler
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -25,35 +53,35 @@ dependencies:
25
53
  - !ruby/object:Gem::Version
26
54
  version: '2.0'
27
55
  - !ruby/object:Gem::Dependency
28
- name: rake
56
+ name: factory_bot
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
- - - "~>"
59
+ - - ">="
32
60
  - !ruby/object:Gem::Version
33
- version: '10.0'
61
+ version: '0'
34
62
  type: :development
35
63
  prerelease: false
36
64
  version_requirements: !ruby/object:Gem::Requirement
37
65
  requirements:
38
- - - "~>"
66
+ - - ">="
39
67
  - !ruby/object:Gem::Version
40
- version: '10.0'
68
+ version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
- name: rspec
70
+ name: guard
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
- - - "~>"
73
+ - - ">="
46
74
  - !ruby/object:Gem::Version
47
- version: '3.0'
75
+ version: '0'
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
- - - "~>"
80
+ - - ">="
53
81
  - !ruby/object:Gem::Version
54
- version: '3.0'
82
+ version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
- name: guard
84
+ name: guard-rspec
57
85
  requirement: !ruby/object:Gem::Requirement
58
86
  requirements:
59
87
  - - ">="
@@ -67,7 +95,7 @@ dependencies:
67
95
  - !ruby/object:Gem::Version
68
96
  version: '0'
69
97
  - !ruby/object:Gem::Dependency
70
- name: pry
98
+ name: pg
71
99
  requirement: !ruby/object:Gem::Requirement
72
100
  requirements:
73
101
  - - ">="
@@ -81,7 +109,7 @@ dependencies:
81
109
  - !ruby/object:Gem::Version
82
110
  version: '0'
83
111
  - !ruby/object:Gem::Dependency
84
- name: guard-rspec
112
+ name: pry
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
115
  - - ">="
@@ -95,7 +123,7 @@ dependencies:
95
123
  - !ruby/object:Gem::Version
96
124
  version: '0'
97
125
  - !ruby/object:Gem::Dependency
98
- name: activerecord
126
+ name: pry-byebug
99
127
  requirement: !ruby/object:Gem::Requirement
100
128
  requirements:
101
129
  - - ">="
@@ -108,6 +136,34 @@ dependencies:
108
136
  - - ">="
109
137
  - !ruby/object:Gem::Version
110
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rake
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '13.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '13.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rspec
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '3.0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '3.0'
111
167
  - !ruby/object:Gem::Dependency
112
168
  name: sqlite3
113
169
  requirement: !ruby/object:Gem::Requirement
@@ -143,7 +199,10 @@ files:
143
199
  - bin/setup
144
200
  - lib/rokaki.rb
145
201
  - lib/rokaki/filter_model.rb
202
+ - lib/rokaki/filter_model/basic_filter.rb
203
+ - lib/rokaki/filter_model/filter_chain.rb
146
204
  - lib/rokaki/filter_model/like_keys.rb
205
+ - lib/rokaki/filter_model/nested_filter.rb
147
206
  - lib/rokaki/filterable.rb
148
207
  - lib/rokaki/version.rb
149
208
  - rokaki.gemspec
@@ -167,7 +226,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
226
  - !ruby/object:Gem::Version
168
227
  version: '0'
169
228
  requirements: []
170
- rubygems_version: 3.0.4
229
+ rubygems_version: 3.1.2
171
230
  signing_key:
172
231
  specification_version: 4
173
232
  summary: A web request filtering library