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 +4 -4
- data/Gemfile.lock +5 -2
- data/README.md +103 -26
- data/lib/rokaki/filterable.rb +38 -5
- data/lib/rokaki/version.rb +1 -1
- data/rokaki.gemspec +2 -1
- metadata +33 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd7982d23aa428243393be242a44e66655f3805cd23e2cb75e9f9ac1d9f6e8a3
|
4
|
+
data.tar.gz: 07d183020e8234b19d253cbde9172a3d802a705be0e535d982c6be06087ab3a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a22d6928e09c881f021e16b92bffa57f43f8a872528ab49f1b25f5ffc0c0a5918898944ea41439ed58d5e1fa1a40fbc16cbc6fa628f7380680cc2cc1bc3abe2
|
7
|
+
data.tar.gz: 8e9ac460a085d3bc32b72cb9b9d1c6198da65bae3e6d76a03a916708faa2e5862c3102d66407b795c23c93e4b527ea5fbda190d93ae876a583253d8b18d08ce7
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rokaki (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.
|
107
|
+
2.1.3
|
data/README.md
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
# Rokaki
|
2
2
|
[](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
|
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
|
-
|
23
|
+
### `#define_filter_keys`
|
24
|
+
#### A Simple Example
|
23
25
|
|
24
26
|
A simple example might be:-
|
25
27
|
|
26
28
|
```ruby
|
27
|
-
|
28
|
-
|
29
|
+
class FilterArticles
|
30
|
+
include Rokaki::Filterable
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
def initialize(filters:)
|
33
|
+
@filters = filters
|
34
|
+
@articles = Article
|
35
|
+
end
|
34
36
|
|
35
|
-
|
37
|
+
attr_accessor :filters
|
36
38
|
|
37
|
-
|
39
|
+
define_filter_keys :date, author: [:first_name, :last_name]
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
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
|
-
|
49
|
-
|
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
|
-
|
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: {
|
data/lib/rokaki/filterable.rb
CHANGED
@@ -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};
|
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
|
-
|
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
|
-
|
95
|
+
yield _keys
|
63
96
|
end
|
64
97
|
end
|
65
98
|
|
data/lib/rokaki/version.rb
CHANGED
data/rokaki.gemspec
CHANGED
@@ -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.
|
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:
|
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:
|
227
|
+
version: 1.3.1
|
214
228
|
requirements: []
|
215
|
-
rubygems_version: 3.
|
229
|
+
rubygems_version: 3.1.2
|
216
230
|
signing_key:
|
217
231
|
specification_version: 4
|
218
232
|
summary: A web request filtering library
|