filterameter 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -2
- data/lib/filterameter/filter_declaration.rb +2 -2
- data/lib/filterameter/filter_factory.rb +33 -5
- data/lib/filterameter/filter_registry.rb +2 -2
- data/lib/filterameter/filters/nested_collection_filter.rb +15 -0
- data/lib/filterameter/filters/nested_filter.rb +16 -2
- data/lib/filterameter/version.rb +1 -1
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b2ac8a6c211408669122ab5bf2a131ade58736fa145508305f5ebe3dc9c0bdb
|
4
|
+
data.tar.gz: 9216ddecfedab830f9a3a82cf9c1936f040e64664b23b2d16a4ccd71f1f4923e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad640c098660b98a33f54378f16dced7d3c2dd278c6d6b6db120d2026cb7f03bda5f0d5033653e697af153eff4bdb3c9c84e39d263a18eedba5075098b7354ef
|
7
|
+
data.tar.gz: 3e775bbe1d6ed769bdfb1a83ac3f4a74c69128517a5bf6ea3fc140e50a8b025214d87d544c9263ad4bf0495f34d477f7a2128836f81e7b6a82d4ec037b9df938
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
Declarative filter parameters provide clean and clear filters for Rails controllers.
|
8
8
|
|
9
9
|
## Usage
|
10
|
-
Declare filters in controllers to increase readability and reduce boilerplate code. Filters can be declared for attributes
|
10
|
+
Declare filters in controllers to increase readability and reduce boilerplate code. Filters can be declared for attributes or scopes, either directly on the model or on an associated model. Validations can also be assigned.
|
11
11
|
|
12
12
|
```ruby
|
13
13
|
filter :color
|
@@ -28,13 +28,25 @@ If the name of the parameter is different than the name of the attribute or scop
|
|
28
28
|
filter :status, name: :current_status
|
29
29
|
```
|
30
30
|
|
31
|
+
This option can also be helpful with nested filters so that the query parameter can be prefixed with the model name. See the `association` option for an example.
|
32
|
+
|
31
33
|
#### association
|
32
|
-
If the attribute or scope is nested, it can be referenced by naming the association.
|
34
|
+
If the attribute or scope is nested, it can be referenced by naming the association. For example, if the manager_id attribute lives on an employee's department record, use the following:
|
33
35
|
|
34
36
|
```ruby
|
35
37
|
filter :manager_id, association: :department
|
36
38
|
```
|
37
39
|
|
40
|
+
The attribute or scope can be nested more than one level. Declare the filter with an array specifying the associations in order. For example, if an employee belongs to a department and a department belongs to a business unit, use the following to query on the business unit name:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
filter :business_unit_name, name: :name, association: [:department, :business_unit]
|
44
|
+
```
|
45
|
+
|
46
|
+
If an association is a `has_many` [the distinct method](https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-distinct) is called on the query.
|
47
|
+
|
48
|
+
_Limitation:_ If there is more than one association to the same table _and_ both associations can be part of the query, then you cannot use a nested filter directly. Instead, build a scope that disambiguates the associations then build a filter against that scope.
|
49
|
+
|
38
50
|
#### validates
|
39
51
|
If the filter value should be validated, use the `validates` option along with [ActiveModel validations](https://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates). Here's an example of the inclusion validator being used to restrict sizes:
|
40
52
|
|
@@ -16,7 +16,7 @@ module Filterameter
|
|
16
16
|
|
17
17
|
validate_options(options)
|
18
18
|
@name = options.fetch(:name, parameter_name).to_s
|
19
|
-
@association = options[:association]
|
19
|
+
@association = Array.wrap(options[:association]).presence
|
20
20
|
@filter_on_empty = options.fetch(:filter_on_empty, false)
|
21
21
|
@validations = Array.wrap(options[:validates])
|
22
22
|
@raw_partial_options = options.fetch(:partial, false)
|
@@ -24,7 +24,7 @@ module Filterameter
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def nested?
|
27
|
-
|
27
|
+
!@association.nil?
|
28
28
|
end
|
29
29
|
|
30
30
|
def validations?
|
@@ -10,14 +10,23 @@ module Filterameter
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def build(declaration)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
if declaration.nested?
|
14
|
+
build_nested_filter(declaration)
|
15
|
+
else
|
16
|
+
build_filter(@model_class, declaration)
|
17
|
+
end
|
17
18
|
end
|
18
19
|
|
19
20
|
private
|
20
21
|
|
22
|
+
def build_nested_filter(declaration)
|
23
|
+
model = model_from_association(declaration.association)
|
24
|
+
filter = build_filter(model, declaration)
|
25
|
+
clazz = filter_class(declaration.association)
|
26
|
+
|
27
|
+
clazz.new(declaration.association, model, filter)
|
28
|
+
end
|
29
|
+
|
21
30
|
def build_filter(model, declaration) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
22
31
|
# checking dangerous_class_method? excludes any names that cannot be scope names, such as "name"
|
23
32
|
if model.respond_to?(declaration.name) && !model.dangerous_class_method?(declaration.name)
|
@@ -33,9 +42,28 @@ module Filterameter
|
|
33
42
|
end
|
34
43
|
end
|
35
44
|
|
45
|
+
def filter_class(association_names)
|
46
|
+
if any_collections?(association_names)
|
47
|
+
Filters::NestedCollectionFilter
|
48
|
+
else
|
49
|
+
Filters::NestedFilter
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def any_collections?(association_names)
|
54
|
+
association_names.reduce(@model_class) do |model, name|
|
55
|
+
association = model.reflect_on_association(name)
|
56
|
+
return true if association.collection?
|
57
|
+
|
58
|
+
association.klass
|
59
|
+
end
|
60
|
+
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
36
64
|
# TODO: rescue then raise custom error with cause
|
37
65
|
def model_from_association(association)
|
38
|
-
|
66
|
+
association.flatten.reduce(@model_class) { |memo, name| memo.reflect_on_association(name).klass }
|
39
67
|
# rescue StandardError => e
|
40
68
|
end
|
41
69
|
end
|
@@ -51,8 +51,8 @@ module Filterameter
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def add_range_maximum(parameter_name, options)
|
54
|
-
|
55
|
-
@declarations[
|
54
|
+
parameter_name_max = "#{parameter_name}_max"
|
55
|
+
@declarations[parameter_name_max] = Filterameter::FilterDeclaration.new(parameter_name_max,
|
56
56
|
options.merge(range: :max_only))
|
57
57
|
end
|
58
58
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Filterameter
|
4
|
+
module Filters
|
5
|
+
# = Nested Collection Filter
|
6
|
+
#
|
7
|
+
# Class NestedCollectionFilter joins the nested table(s), merges the filter to the association's model, then makes
|
8
|
+
# the results distinct.
|
9
|
+
class NestedCollectionFilter < NestedFilter
|
10
|
+
def apply(*)
|
11
|
+
super.distinct
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -6,8 +6,8 @@ module Filterameter
|
|
6
6
|
#
|
7
7
|
# Class NestedFilter joins the nested table(s) then merges the filter to the association's model.
|
8
8
|
class NestedFilter
|
9
|
-
def initialize(
|
10
|
-
@joins_values =
|
9
|
+
def initialize(association_names, association_model, attribute_filter)
|
10
|
+
@joins_values = build_joins_values_argument(association_names)
|
11
11
|
@association_model = association_model
|
12
12
|
@attribute_filter = attribute_filter
|
13
13
|
end
|
@@ -16,6 +16,20 @@ module Filterameter
|
|
16
16
|
query.joins(@joins_values)
|
17
17
|
.merge(@attribute_filter.apply(@association_model, value))
|
18
18
|
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def build_joins_values_argument(association_names)
|
23
|
+
return association_names.first if association_names.size == 1
|
24
|
+
|
25
|
+
convert_to_nested_hash(association_names)
|
26
|
+
end
|
27
|
+
|
28
|
+
def convert_to_nested_hash(association_names)
|
29
|
+
{}.tap do |nested_hash|
|
30
|
+
association_names.reduce(nested_hash) { |memo, name| memo.store(name, {}) }
|
31
|
+
end
|
32
|
+
end
|
19
33
|
end
|
20
34
|
end
|
21
35
|
end
|
data/lib/filterameter/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filterameter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Todd Kummer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 1.
|
117
|
+
version: '1.64'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 1.
|
124
|
+
version: '1.64'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: rubocop-packaging
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,14 +142,14 @@ dependencies:
|
|
142
142
|
requirements:
|
143
143
|
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: 2.
|
145
|
+
version: '2.25'
|
146
146
|
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: 2.
|
152
|
+
version: '2.25'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: simplecov
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,7 +164,8 @@ dependencies:
|
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0.18'
|
167
|
-
description:
|
167
|
+
description: Declare filters in Rails controllers to increase readability and reduce
|
168
|
+
boilerplate code.
|
168
169
|
email:
|
169
170
|
- todd@rockridgesolutions.com
|
170
171
|
executables: []
|
@@ -191,6 +192,7 @@ files:
|
|
191
192
|
- lib/filterameter/filters/matches_filter.rb
|
192
193
|
- lib/filterameter/filters/maximum_filter.rb
|
193
194
|
- lib/filterameter/filters/minimum_filter.rb
|
195
|
+
- lib/filterameter/filters/nested_collection_filter.rb
|
194
196
|
- lib/filterameter/filters/nested_filter.rb
|
195
197
|
- lib/filterameter/filters/scope_filter.rb
|
196
198
|
- lib/filterameter/log_subscriber.rb
|