rokaki 0.7.0 → 0.8.0.pre.pre

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: 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