filterameter 1.0.3 → 1.1.0

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: 50c824121830196b05ccb71f43495ad4d5197f85fe9d31456edc549fd2462a59
4
- data.tar.gz: 4bb268b89c285e6542b0347bb3c52ca903f4d9be517f33b6303a76200b36f6c6
3
+ metadata.gz: 02daf1402e83a27c8b13ed7e548dfe269b7a1e6c7c5085f9aad2220509793253
4
+ data.tar.gz: 1ddcce714232b71be40272ac3e84f116e38af380928407936a082ade5395c743
5
5
  SHA512:
6
- metadata.gz: 21e92b0610890ed0ccbd8e3bdba68864de0c28d1c1ea42af40e51e696b0950c0ed6dd2d085673e493f400ca875d0307eeaaf9b844d021f1ae0d2fe11d53420c7
7
- data.tar.gz: 77edd1277a98e49ab6c6425fe387404176477afc9050bafbb4d3d0eb34268d0e0e4a1f2983fb3b4951a0c9e7c88a0309464ab11112d67ae401ebcb75f7a74617
6
+ metadata.gz: b33c74905694f9a1682a4c7faefb6e11cbeff81a7c2bae32dd3a39f25393d50a7eb86fc766825f9296c2ce19440aed296eab17bebfd6f1eac77a41aa749dbeeb
7
+ data.tar.gz: 36f85f875a0c15258988cbbf97062caa75e1e47de1142484eaa96b8bae7f757d5a94e5fd5ac07b2137db7f1d8cb146adb76b3d6aa2849ad3507707e22b87b783
data/README.md CHANGED
@@ -42,6 +42,7 @@ Simplify and speed development of Rails controllers by making filter parameters
42
42
  - [Partial](#partial)
43
43
  - [Range](#range)
44
44
  - [Sortable](#sortable)
45
+ - [Converters](#converters)
45
46
  - [Scope Filters](#scope-filters)
46
47
  - [Sorting](#sorting)
47
48
  - [Building the Query](#building-the-query)
@@ -82,6 +83,9 @@ Include module `Filterameter::DeclarativeFilters` in the controller to provide t
82
83
  filter :brand_name, association: :brand, name: :name
83
84
  filter :on_sale, association: :price, validates: [{ numericality: { greater_than: 0 } },
84
85
  { numericality: { less_than: 100 } }]
86
+ filter :amount do |value|
87
+ value.is_a?(String) ? value.delete(",") : value
88
+ end
85
89
  ```
86
90
 
87
91
  Filters without options can be declared all at once with `filters`:
@@ -185,6 +189,17 @@ The following filters are not sortable:
185
189
  - scope filters (see [_Sorting with a Scope_](#sorting-with-a-scope))
186
190
  - filters with collection associations
187
191
 
192
+ #### Converters
193
+
194
+ If the filter value needs to be converted before being applied to the query, a converter block can be provided. The block should take the parameter value as an argument and return the converted value.
195
+
196
+ For example, if the amount filter should remove commas from the value before applying it to the query, the declaration would look like this:
197
+
198
+ ```ruby
199
+ filter :amount do |value|
200
+ value.is_a?(String) ? value.delete(",") : value
201
+ end
202
+ ```
188
203
 
189
204
  ### Scope Filters
190
205
 
@@ -61,8 +61,8 @@ module Filterameter
61
61
  # filter :department_name, partial: :from_start
62
62
  # filter :reason, partial: { match: :dynamic, case_sensitive: true }
63
63
  # filter :price, range: true
64
- def filter(name, options = {})
65
- filter_coordinator.add_filter(name, options)
64
+ def filter(name, options = {}, &converter)
65
+ filter_coordinator.add_filter(name, options.merge(converter:))
66
66
  end
67
67
 
68
68
  # Declares a list of filters without options. Filters that require options must be declared with `filter`.
@@ -16,7 +16,7 @@ module Filterameter
16
16
  class FilterDeclaration
17
17
  VALID_RANGE_OPTIONS = [true, :min_only, :max_only].freeze
18
18
 
19
- attr_reader :name, :parameter_name, :association, :validations
19
+ attr_reader :name, :parameter_name, :association, :validations, :converter
20
20
 
21
21
  def initialize(parameter_name, options, range_type: nil)
22
22
  @parameter_name = parameter_name.to_s
@@ -29,6 +29,7 @@ module Filterameter
29
29
  @raw_range = options[:range]
30
30
  @range_type = range_type
31
31
  @sortable = options.fetch(:sortable, true)
32
+ @converter = options[:converter]
32
33
  end
33
34
 
34
35
  def nested?
@@ -88,7 +89,7 @@ module Filterameter
88
89
  private
89
90
 
90
91
  def validate_options(options)
91
- options.assert_valid_keys(:name, :association, :validates, :partial, :range, :sortable)
92
+ options.assert_valid_keys(:name, :association, :validates, :partial, :range, :sortable, :converter)
92
93
  validate_range(options[:range]) if options.key?(:range)
93
94
  end
94
95
 
@@ -15,7 +15,7 @@ module Filterameter
15
15
  if declaration.nested?
16
16
  build_nested_filter(declaration, context)
17
17
  else
18
- build_filter(@model_class, declaration, context.scope?)
18
+ build_filter_or_scope_filter(@model_class, declaration, context.scope?)
19
19
  end
20
20
  end
21
21
 
@@ -23,23 +23,29 @@ module Filterameter
23
23
 
24
24
  def build_nested_filter(declaration, context)
25
25
  model = context.model_from_association
26
- filter = build_filter(model, declaration, context.scope?)
26
+ filter = build_filter_or_scope_filter(model, declaration, context.scope?)
27
27
  nested_filter_class = context.any_collections? ? Filters::NestedCollectionFilter : Filters::NestedFilter
28
28
 
29
29
  nested_filter_class.new(declaration.association, model, filter)
30
30
  end
31
31
 
32
- def build_filter(model, declaration, declaration_is_a_scope) # rubocop:disable Metrics/MethodLength
32
+ def build_filter_or_scope_filter(model, declaration, declaration_is_a_scope)
33
33
  if declaration_is_a_scope
34
34
  build_scope_filter(model, declaration)
35
- elsif declaration.partial_search?
36
- Filterameter::Filters::MatchesFilter.new(declaration.name, declaration.partial_options)
35
+ else
36
+ build_filter(model, declaration)
37
+ end
38
+ end
39
+
40
+ def build_filter(model, declaration)
41
+ if declaration.partial_search?
42
+ Filterameter::Filters::MatchesFilter.new(declaration.name, declaration.partial_options, &declaration.converter)
37
43
  elsif declaration.minimum_range?
38
- Filterameter::Filters::MinimumFilter.new(model, declaration.name)
44
+ Filterameter::Filters::MinimumFilter.new(model, declaration.name, &declaration.converter)
39
45
  elsif declaration.maximum_range?
40
- Filterameter::Filters::MaximumFilter.new(model, declaration.name)
46
+ Filterameter::Filters::MaximumFilter.new(model, declaration.name, &declaration.converter)
41
47
  else
42
- Filterameter::Filters::AttributeFilter.new(declaration.name)
48
+ Filterameter::Filters::AttributeFilter.new(declaration.name, &declaration.converter)
43
49
  end
44
50
  end
45
51
 
@@ -48,9 +54,9 @@ module Filterameter
48
54
  def build_scope_filter(model, declaration)
49
55
  number_of_arguments = model.method(declaration.name).arity
50
56
  if number_of_arguments < 1
51
- Filterameter::Filters::ConditionalScopeFilter.new(declaration.name)
57
+ Filterameter::Filters::ConditionalScopeFilter.new(declaration.name, &declaration.converter)
52
58
  elsif number_of_arguments == 1
53
- Filterameter::Filters::ScopeFilter.new(declaration.name)
59
+ Filterameter::Filters::ScopeFilter.new(declaration.name, &declaration.converter)
54
60
  else
55
61
  raise Filterameter::DeclarationErrors::FilterScopeArgumentError.new(model.name, declaration.name)
56
62
  end
@@ -10,9 +10,10 @@ module Filterameter
10
10
  include Filterameter::Errors
11
11
  include Filterameter::Filters::AttributeValidator
12
12
 
13
- def initialize(model, attribute_name)
13
+ def initialize(model, attribute_name, &converter)
14
14
  @attribute_name = attribute_name
15
15
  @arel_attribute = model.arel_table[attribute_name]
16
+ @converter = converter
16
17
  end
17
18
  end
18
19
  end
@@ -9,11 +9,13 @@ module Filterameter
9
9
  include Filterameter::Errors
10
10
  include AttributeValidator
11
11
 
12
- def initialize(attribute_name)
12
+ def initialize(attribute_name, &converter)
13
13
  @attribute_name = attribute_name
14
+ @converter = converter
14
15
  end
15
16
 
16
17
  def apply(query, value)
18
+ value = @converter.call(value) if @converter
17
19
  query.where(@attribute_name => value)
18
20
  end
19
21
  end
@@ -8,11 +8,13 @@ module Filterameter
8
8
  class ConditionalScopeFilter
9
9
  include Filterameter::Errors
10
10
 
11
- def initialize(scope_name)
11
+ def initialize(scope_name, &converter)
12
12
  @scope_name = scope_name
13
+ @converter = converter
13
14
  end
14
15
 
15
16
  def apply(query, value)
17
+ value = @converter.call(value) if @converter
16
18
  return query unless ActiveModel::Type::Boolean.new.cast(value)
17
19
 
18
20
  query.public_send(@scope_name)
@@ -9,14 +9,16 @@ module Filterameter
9
9
  include Filterameter::Errors
10
10
  include Filterameter::Filters::AttributeValidator
11
11
 
12
- def initialize(attribute_name, options)
12
+ def initialize(attribute_name, options, &converter)
13
13
  @attribute_name = attribute_name
14
14
  @prefix = options.match_anywhere? ? '%' : nil
15
15
  @suffix = options.match_anywhere? || options.match_from_start? ? '%' : nil
16
16
  @case_sensitive = options.case_sensitive?
17
+ @converter = converter
17
18
  end
18
19
 
19
20
  def apply(query, value)
21
+ value = @converter.call(value) if @converter
20
22
  arel = query.arel_table[@attribute_name].matches("#{@prefix}#{value}#{@suffix}", false, @case_sensitive)
21
23
  query.where(arel)
22
24
  end
@@ -7,6 +7,7 @@ module Filterameter
7
7
  # Class MaximumFilter adds criteria for all values greater than or equal to a maximum.
8
8
  class MaximumFilter < ArelFilter
9
9
  def apply(query, value)
10
+ value = @converter.call(value) if @converter
10
11
  query.where(@arel_attribute.lteq(value))
11
12
  end
12
13
  end
@@ -7,6 +7,7 @@ module Filterameter
7
7
  # Class MinimumFilter adds criteria for all values greater than or equal to a minimum.
8
8
  class MinimumFilter < ArelFilter
9
9
  def apply(query, value)
10
+ value = @converter.call(value) if @converter
10
11
  query.where(@arel_attribute.gteq(value))
11
12
  end
12
13
  end
@@ -8,11 +8,13 @@ module Filterameter
8
8
  class ScopeFilter
9
9
  include Filterameter::Errors
10
10
 
11
- def initialize(scope_name)
11
+ def initialize(scope_name, &converter)
12
12
  @scope_name = scope_name
13
+ @converter = converter
13
14
  end
14
15
 
15
16
  def apply(query, value)
17
+ value = @converter.call(value) if @converter
16
18
  query.public_send(@scope_name, value)
17
19
  end
18
20
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Filterameter
4
- VERSION = '1.0.3'
4
+ VERSION = '1.1.0'
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filterameter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Todd Kummer
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-03-31 00:00:00.000000000 Z
10
+ date: 2026-06-19 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -196,7 +196,8 @@ files:
196
196
  homepage: https://github.com/RockSolt/filterameter
197
197
  licenses:
198
198
  - MIT
199
- metadata: {}
199
+ metadata:
200
+ rubygems_mfa_required: 'true'
200
201
  rdoc_options: []
201
202
  require_paths:
202
203
  - lib