rokaki 0.7.0 → 0.8.0.pre.pre

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a46db4e821b0a8fb7f661ed539c18c6509b0a644b3bc0f790c0619ab72ff4108
4
- data.tar.gz: f6ae9809a6a82c9a1de24ecf7d79b9f0cc6cab530579751df919832c0fa3593e
3
+ metadata.gz: cd7982d23aa428243393be242a44e66655f3805cd23e2cb75e9f9ac1d9f6e8a3
4
+ data.tar.gz: 07d183020e8234b19d253cbde9172a3d802a705be0e535d982c6be06087ab3a5
5
5
  SHA512:
6
- metadata.gz: 55b11eee1380defcbd8b56e584b4d4ea145571718da3fa82e5445b2233d779bf169c173d19f5afbf5e7eb6dc69a070a8a08ff64fa3dc41fc4d29690ab2de0556
7
- data.tar.gz: 88869cad8ba114234d2581661277963d3eb2effd4aecded8be6d2b7140dcb8331b68d529b4e388518c8ca503deff5f5b8ede87d33bb98152c8e29f676eb268ba
6
+ metadata.gz: 8a22d6928e09c881f021e16b92bffa57f43f8a872528ab49f1b25f5ffc0c0a5918898944ea41439ed58d5e1fa1a40fbc16cbc6fa628f7380680cc2cc1bc3abe2
7
+ data.tar.gz: 8e9ac460a085d3bc32b72cb9b9d1c6198da65bae3e6d76a03a916708faa2e5862c3102d66407b795c23c93e4b527ea5fbda190d93ae876a583253d8b18d08ce7
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rokaki (0.6.0)
4
+ rokaki (0.7.0)
5
5
  activesupport
6
6
 
7
7
  GEM
@@ -22,6 +22,8 @@ GEM
22
22
  coderay (1.1.2)
23
23
  concurrent-ruby (1.1.5)
24
24
  diff-lcs (1.3)
25
+ factory_bot (5.1.1)
26
+ activesupport (>= 4.2.0)
25
27
  ffi (1.11.1)
26
28
  formatador (0.2.5)
27
29
  guard (2.15.0)
@@ -90,6 +92,7 @@ PLATFORMS
90
92
  DEPENDENCIES
91
93
  activerecord
92
94
  bundler (~> 2.0)
95
+ factory_bot
93
96
  guard
94
97
  guard-rspec
95
98
  pg
@@ -101,4 +104,4 @@ DEPENDENCIES
101
104
  sqlite3
102
105
 
103
106
  BUNDLED WITH
104
- 2.0.2
107
+ 2.1.3
data/README.md CHANGED
@@ -1,10 +1,9 @@
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:
@@ -17,42 +16,118 @@ And then execute:
17
16
 
18
17
  $ bundle
19
18
 
20
- ## Usage
19
+ ## `Rokaki::Filterable` - Usage
20
+
21
+ To use the DSL first include the `Rokaki::Filterable` module in your [por](http://blog.jayfields.com/2007/10/ruby-poro.html) class.
21
22
 
22
- To use the basic DSL include the `Rokaki::Filterable` module
23
+ ### `#define_filter_keys`
24
+ #### A Simple Example
23
25
 
24
26
  A simple example might be:-
25
27
 
26
28
  ```ruby
27
- class FilterArticles
28
- include Rokaki::Filterable
29
+ class FilterArticles
30
+ include Rokaki::Filterable
29
31
 
30
- def initialize(filters:)
31
- @filters = filters
32
- @articles = Article
33
- end
32
+ def initialize(filters:)
33
+ @filters = filters
34
+ @articles = Article
35
+ end
34
36
 
35
- attr_accessor :filters
37
+ attr_accessor :filters
36
38
 
37
- define_filter_keys :date, author: [:first_name, :last_name]
39
+ define_filter_keys :date, author: [:first_name, :last_name]
38
40
 
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
41
+ def filter_results
42
+ @articles = @articles.where(date: date) if date
43
+ @articles = @articles.joins(:author).where(author: { first_name: author_first_name }) if author_first_name
43
44
  end
45
+ end
46
+
47
+ article_filter = FilterArticles.new(filters: {
48
+ date: '10-10-10',
49
+ author: {
50
+ first_name: 'Steve',
51
+ last_name: 'Martin'
52
+ }})
53
+ article_filter.author_first_name == 'Steve'
54
+ article_filter.author_last_name == 'Martin'
55
+ article_filter.date == '10-10-10'
56
+ ```
57
+
58
+ 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.
59
+
60
+ #### A More Complex Example
61
+
62
+ ```ruby
63
+ class AdvancedFilterable
64
+ include Rokaki::Filterable
65
+
66
+ def initialize(filters:)
67
+ @fyltrz = filters
68
+ end
69
+ attr_accessor :fyltrz
70
+
71
+ filterable_object_name :fyltrz
72
+ filter_key_infix :__
73
+ define_filter_keys :basic, advanced: {
74
+ filter_key_1: [:filter_key_2, { filter_key_3: :deep_node }],
75
+ filter_key_4: :deep_leaf_array
76
+ }
77
+ end
78
+
79
+
80
+ advanced_filterable = AdvancedFilterable.new(filters: {
81
+ basic: 'ABC',
82
+ advanced: {
83
+ filter_key_1: {
84
+ filter_key_2: '123',
85
+ filter_key_3: { deep_node: 'NODE' }
86
+ },
87
+ filter_key_4: { deep_leaf_array: [1,2,3,4] }
88
+ }
89
+ })
90
+
91
+ advanced_filterable.advanced__filter_key_4__deep_leaf_array == [1,2,3,4]
92
+ advanced_filterable.advanced__filter_key_1__filter_key_3__deep_node == 'NODE'
44
93
  ```
94
+ ### `#define_filter_map`
45
95
 
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' } }`.
96
+ 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
97
 
48
- ## Additional options
49
- You can specify a `filter_key_prefix` and a `filter_key_infix` to change the structure of the accessors.
98
+ #### A Simple Example
99
+ ```ruby
100
+ class FilterMap
101
+ include Rokaki::Filterable
102
+
103
+ def initialize(fylterz:)
104
+ @fylterz = fylterz
105
+ end
106
+ attr_accessor :fylterz
107
+
108
+ filterable_object_name :fylterz
109
+ define_filter_map :query, :mapped_a, association: :field
110
+ end
111
+
112
+ filter_map = FilterMap.new(fytlerz: { query: 'H2O' })
113
+
114
+ filter_map.mapped_a == 'H2O'
115
+ filter_map.association_field = 'H2O'
116
+ ```
117
+
118
+ #### Additional `Filterable` options
119
+ 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
120
 
51
121
  `filter_key_prefix :__` would result in key accessors like `__author_first_name`
52
122
 
53
123
  `filter_key_infix :__` would result in key accessors like `author__first_name`
54
124
 
55
- ## ActiveRecord
125
+ `filterable_object_name :fylterz` would use an internal filter state object named `@fyltrz` instead of the default `@filters`
126
+
127
+
128
+ ## `Rokaki::FilterModel` - Usage
129
+
130
+ ### ActiveRecord
56
131
  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
132
 
58
133
  ```ruby
@@ -67,7 +142,7 @@ end
67
142
 
68
143
 
69
144
  class ArticleFilter
70
- include FilterModel
145
+ include Rokaki::FilterModel
71
146
 
72
147
  filters :date, :title, author: [:first_name, :last_name]
73
148
 
@@ -96,7 +171,7 @@ You can use `like` (or, if you use postgres, the case insensitive `ilike`) to pe
96
171
 
97
172
  ```ruby
98
173
  class ArticleFilter
99
- include FilterModel
174
+ include Rokaki::FilterModel
100
175
 
101
176
  filter :article,
102
177
  like: { # you can use ilike here instead if you use postgres and want case insensitive results
@@ -122,7 +197,7 @@ In this syntax you will need to provide three keywords:- `filters`, `like` and `
122
197
 
123
198
  ```ruby
124
199
  class ArticleFilter
125
- include FilterModel
200
+ include Rokaki::FilterModel
126
201
 
127
202
  filters :date, :title, author: [:first_name, :last_name]
128
203
  like title: :circumfix
@@ -141,7 +216,7 @@ Or without the model in the initializer
141
216
 
142
217
  ```ruby
143
218
  class ArticleFilter
144
- include FilterModel
219
+ include Rokaki::FilterModel
145
220
 
146
221
  filters :date, :title, author: [:first_name, :last_name]
147
222
  like title: :circumfix
@@ -165,7 +240,7 @@ Would produce a query with a LIKE which circumfixes '%' around the filter term,
165
240
  You can filter joins both with basic matching and partial matching
166
241
  ```ruby
167
242
  class ArticleFilter
168
- include FilterModel
243
+ include Rokaki::FilterModel
169
244
 
170
245
  filter :author,
171
246
  like: {
@@ -189,6 +264,8 @@ You can pass array params (and partially match them), to filters (search multipl
189
264
 
190
265
  ```ruby
191
266
  class ArticleFilter
267
+ include Rokaki::FilterModel
268
+
192
269
  filter :article,
193
270
  like: {
194
271
  author: {
@@ -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,20 +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
82
  if value.is_a? Array
54
83
  value.each do |av|
84
+ if av.is_a? Symbol
55
85
  _keys = keys.dup << av
56
- _build_filter(_keys)
86
+ yield _keys
87
+ else
88
+ deep_map(keys, av, &Proc.new)
89
+ end
57
90
  end
58
91
  end
59
92
 
60
93
  if value.is_a? Symbol
61
94
  _keys = keys.dup << value
62
- _build_filter(_keys)
95
+ yield _keys
63
96
  end
64
97
  end
65
98
 
@@ -1,3 +1,3 @@
1
1
  module Rokaki
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0-pre"
3
3
  end
@@ -34,12 +34,13 @@ 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'
37
38
  spec.add_development_dependency 'guard'
38
39
  spec.add_development_dependency 'guard-rspec'
40
+ spec.add_development_dependency 'pg'
39
41
  spec.add_development_dependency 'pry'
40
42
  spec.add_development_dependency 'pry-byebug'
41
43
  spec.add_development_dependency 'rake', '~> 10.0'
42
44
  spec.add_development_dependency 'rspec', '~> 3.0'
43
45
  spec.add_development_dependency 'sqlite3'
44
- spec.add_development_dependency 'pg'
45
46
  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.7.0
4
+ version: 0.8.0.pre.pre
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-11-09 00:00:00.000000000 Z
11
+ date: 2020-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: factory_bot
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: guard
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,20 @@ dependencies:
80
94
  - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pg
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: pry
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -150,20 +178,6 @@ dependencies:
150
178
  - - ">="
151
179
  - !ruby/object:Gem::Version
152
180
  version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: pg
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: '0'
160
- type: :development
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - ">="
165
- - !ruby/object:Gem::Version
166
- version: '0'
167
181
  description: A dsl for filtering data in web requests
168
182
  email:
169
183
  - steve@martian.media
@@ -208,11 +222,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
208
222
  version: '0'
209
223
  required_rubygems_version: !ruby/object:Gem::Requirement
210
224
  requirements:
211
- - - ">="
225
+ - - ">"
212
226
  - !ruby/object:Gem::Version
213
- version: '0'
227
+ version: 1.3.1
214
228
  requirements: []
215
- rubygems_version: 3.0.4
229
+ rubygems_version: 3.1.2
216
230
  signing_key:
217
231
  specification_version: 4
218
232
  summary: A web request filtering library