filterameter 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -0
- data/lib/filterameter/filter_declaration.rb +11 -1
- data/lib/filterameter/filter_factory.rb +8 -5
- data/lib/filterameter/filters/matches_filter.rb +22 -0
- data/lib/filterameter/options/partial_options.rb +81 -0
- data/lib/filterameter/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8110e021be072bc1f37dd324c42e590e3c91a94fbadd934fdc0758f1f85d700
|
4
|
+
data.tar.gz: 7ee31589a1fb66717647a1085658ab4dd5b9ba7deb58d847102fc72be1d9dffe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83117da59a74ba92bf255cdb8ccd025f071f5549dfeb315af5894c12dc8829928fef6aae0eb6d94eaec7abfd5f2a85aa1d085bd7b3ec26d0b476d0506febd7a4
|
7
|
+
data.tar.gz: 3f777e21518ce18d2f62f36912ba3b532839a9f5d03bc73dd7796648d1fcec81992701b07ad639dbe021f7765ab5d5baa66d4d82bf92edc7264a2463df4044ff
|
data/README.md
CHANGED
@@ -39,6 +39,24 @@ filter :size, validates: { inclusion: { in: %w[Small Medium Large] }, unless: ->
|
|
39
39
|
|
40
40
|
Note that the `inclusion` validator does not allow arrays to be specified. If the filter should allow multiple values to be specified, then the validation needs to be disabled when the value an array.
|
41
41
|
|
42
|
+
#### partial
|
43
|
+
Specify the partial option if the filter should do a partial search (SQL's `LIKE`). The partial option accepts a hash to specify the search behavior. Here are the available options:
|
44
|
+
- match: anywhere (default), from_start, dynamic
|
45
|
+
- case_sensitive: true, false (default)
|
46
|
+
|
47
|
+
There are two shortcuts: : the partial option can be declared with `true`, which just uses the defaults; or the partial option can be declared with the match option directly, such as `partial: :from_start`.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
filter :description, partial: true
|
51
|
+
filter :department_name, partial: :from_start
|
52
|
+
filter :reason, partial: { match: :dynamic, case_sensitive: true }
|
53
|
+
```
|
54
|
+
|
55
|
+
The `match` options defines where you are searching (which then controls where the wildcard(s) appear):
|
56
|
+
- anywhere: adds wildcards at the start and end, for example '%blue%'
|
57
|
+
- from_start: adds a wildcard at the end, for example 'blue%'
|
58
|
+
- dynamic: adds no wildcards; this enables the client to fully control the search string
|
59
|
+
|
42
60
|
### Configuring Controllers
|
43
61
|
|
44
62
|
Rails conventions are used to determine the controller's model as well as the name of the instance variable to apply the filters to. For example, the PhotosController will use the variable `@photos` to store a query against the Photo model. If the conventions do not provide the correct info, they can be overridden with the following two methods:
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_support/core_ext/array/wrap'
|
4
|
+
require 'filterameter/options/partial_options'
|
4
5
|
|
5
6
|
module Filterameter
|
6
7
|
# = Filter Declaration
|
@@ -17,6 +18,7 @@ module Filterameter
|
|
17
18
|
@association = options[:association]
|
18
19
|
@filter_on_empty = options.fetch(:filter_on_empty, false)
|
19
20
|
@validations = Array.wrap(options[:validates])
|
21
|
+
@raw_partial_options = options.fetch(:partial, false)
|
20
22
|
end
|
21
23
|
|
22
24
|
def nested?
|
@@ -31,10 +33,18 @@ module Filterameter
|
|
31
33
|
@filter_on_empty
|
32
34
|
end
|
33
35
|
|
36
|
+
def partial_search?
|
37
|
+
partial_options.present?
|
38
|
+
end
|
39
|
+
|
40
|
+
def partial_options
|
41
|
+
@partial_options ||= @raw_partial_options ? Options::PartialOptions.new(@raw_partial_options) : nil
|
42
|
+
end
|
43
|
+
|
34
44
|
private
|
35
45
|
|
36
46
|
def validate_options(options)
|
37
|
-
options.assert_valid_keys(:name, :association, :filter_on_empty, :validates)
|
47
|
+
options.assert_valid_keys(:name, :association, :filter_on_empty, :validates, :partial)
|
38
48
|
end
|
39
49
|
end
|
40
50
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'filterameter/filters/attribute_filter'
|
4
4
|
require 'filterameter/filters/conditional_scope_filter'
|
5
|
+
require 'filterameter/filters/matches_filter'
|
5
6
|
require 'filterameter/filters/nested_filter'
|
6
7
|
require 'filterameter/filters/scope_filter'
|
7
8
|
|
@@ -16,19 +17,21 @@ module Filterameter
|
|
16
17
|
|
17
18
|
def build(declaration)
|
18
19
|
model = declaration.nested? ? model_from_association(declaration.association) : @model_class
|
19
|
-
filter = build_filter(model, declaration
|
20
|
+
filter = build_filter(model, declaration)
|
20
21
|
|
21
22
|
declaration.nested? ? Filterameter::Filters::NestedFilter.new(declaration.association, model, filter) : filter
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
25
26
|
|
26
|
-
def build_filter(model,
|
27
|
+
def build_filter(model, declaration)
|
27
28
|
# checking dangerous_class_method? excludes any names that cannot be scope names, such as "name"
|
28
|
-
if model.respond_to?(name) && !model.dangerous_class_method?(name)
|
29
|
-
Filterameter::Filters::ScopeFilter.new(name)
|
29
|
+
if model.respond_to?(declaration.name) && !model.dangerous_class_method?(declaration.name)
|
30
|
+
Filterameter::Filters::ScopeFilter.new(declaration.name)
|
31
|
+
elsif declaration.partial_search?
|
32
|
+
Filterameter::Filters::MatchesFilter.new(declaration.name, declaration.partial_options)
|
30
33
|
else
|
31
|
-
Filterameter::Filters::AttributeFilter.new(name)
|
34
|
+
Filterameter::Filters::AttributeFilter.new(declaration.name)
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Filterameter
|
4
|
+
module Filters
|
5
|
+
# = Matches Filter
|
6
|
+
#
|
7
|
+
# Class MatchesFilter uses arel's `matches` to generate a LIKE query.
|
8
|
+
class MatchesFilter
|
9
|
+
def initialize(attribute_name, options)
|
10
|
+
@attribute_name = attribute_name
|
11
|
+
@prefix = options.match_anywhere? ? '%' : nil
|
12
|
+
@suffix = options.match_anywhere? || options.match_from_start? ? '%' : nil
|
13
|
+
@case_sensitive = options.case_sensitive?
|
14
|
+
end
|
15
|
+
|
16
|
+
def apply(query, value)
|
17
|
+
arel = query.arel_table[@attribute_name].matches("#{@prefix}#{value}#{@suffix}", false, @case_sensitive)
|
18
|
+
query.where(arel)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Options
|
4
|
+
# = Partial Options
|
5
|
+
#
|
6
|
+
# Class PartialOptions parses the options passed in as partial, then exposes those. Here are the options along with
|
7
|
+
# their valid values:
|
8
|
+
# - match: anywhere (default), from_start, dynamic
|
9
|
+
# - case_sensitive: true, false (default)
|
10
|
+
#
|
11
|
+
# Options may be specified by passing a hash with the option keys:
|
12
|
+
#
|
13
|
+
# partial: { match: :from_start, case_sensitive: true }
|
14
|
+
#
|
15
|
+
# There are two shortcuts: the partial option can be declared with `true`, which just uses the defaults; or the
|
16
|
+
# partial option can be declared with the match option directly, such as partial: :from_start.
|
17
|
+
class PartialOptions
|
18
|
+
VALID_OPTIONS = %i[match case_sensitive].freeze
|
19
|
+
VALID_MATCH_OPTIONS = %w[anywhere from_start dynamic].freeze
|
20
|
+
|
21
|
+
def initialize(options)
|
22
|
+
@match = 'anywhere'
|
23
|
+
@case_sensitive = false
|
24
|
+
|
25
|
+
if options.is_a?(TrueClass)
|
26
|
+
nil
|
27
|
+
elsif options.is_a? Hash
|
28
|
+
evaluate_hash(options)
|
29
|
+
elsif options.is_a?(String) || options.is_a?(Symbol)
|
30
|
+
assign_match(options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def case_sensitive?
|
35
|
+
@case_sensitive
|
36
|
+
end
|
37
|
+
|
38
|
+
def match_anywhere?
|
39
|
+
@match == 'anywhere'
|
40
|
+
end
|
41
|
+
|
42
|
+
def match_from_start?
|
43
|
+
@match == 'from_start'
|
44
|
+
end
|
45
|
+
|
46
|
+
def match_dynamically?
|
47
|
+
@match == 'dynamic'
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def evaluate_hash(options)
|
53
|
+
options.assert_valid_keys(:match, :case_sensitive)
|
54
|
+
assign_match(options[:match]) if options.key?(:match)
|
55
|
+
assign_case_sensitive(options[:case_sensitive]) if options.key?(:case_sensitive)
|
56
|
+
end
|
57
|
+
|
58
|
+
def assign_match(value)
|
59
|
+
validate_match(value)
|
60
|
+
@match = value.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_match(value)
|
64
|
+
return if VALID_MATCH_OPTIONS.include? value.to_s
|
65
|
+
|
66
|
+
raise ArgumentError,
|
67
|
+
"Invalid match option for partial: #{value}. Valid options are #{VALID_MATCH_OPTIONS.to_sentence}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def assign_case_sensitive(value)
|
71
|
+
validate_case_sensitive(value)
|
72
|
+
@case_sensitive = value
|
73
|
+
end
|
74
|
+
|
75
|
+
def validate_case_sensitive(value)
|
76
|
+
return if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
77
|
+
|
78
|
+
raise ArgumentError, "Invalid case_sensitive option for partial: #{value}. Valid options are true and false."
|
79
|
+
end
|
80
|
+
end
|
81
|
+
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.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Todd Kummer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -144,9 +144,11 @@ files:
|
|
144
144
|
- lib/filterameter/filter_factory.rb
|
145
145
|
- lib/filterameter/filters/attribute_filter.rb
|
146
146
|
- lib/filterameter/filters/conditional_scope_filter.rb
|
147
|
+
- lib/filterameter/filters/matches_filter.rb
|
147
148
|
- lib/filterameter/filters/nested_filter.rb
|
148
149
|
- lib/filterameter/filters/scope_filter.rb
|
149
150
|
- lib/filterameter/log_subscriber.rb
|
151
|
+
- lib/filterameter/options/partial_options.rb
|
150
152
|
- lib/filterameter/parameters_base.rb
|
151
153
|
- lib/filterameter/version.rb
|
152
154
|
- lib/tasks/filterameter_tasks.rake
|