filterameter 0.3.0 → 0.4.1

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: c5ff39d6e3e772f3943678ecc748ca3f9d9234417958fc9254071ee14764ed31
4
- data.tar.gz: 22e0d442436d0cae884e60a60066eea859c7de22251d6471e05c289af50f3da1
3
+ metadata.gz: 65c8829537f7ee2affcc40a56fb5de2b58d4789fc6d1e9811b33607c86c9c01b
4
+ data.tar.gz: e731021c3cda4e0330b2a7bc499b90ceb49cd918ddd635d57a37c4bf2ce3a9bc
5
5
  SHA512:
6
- metadata.gz: 67cd4b48cf4f50e0681e34003f0f9ebe7ae753b703f83121d017daaee7185c846c4e2e467d2b140fc14ced8e11b07f88b155a1dda4b6e0a9104906348f077a53
7
- data.tar.gz: f80c40fcd3a438682bd95c7b3c282dae7cc199c57375372314d0720c234c53aa0b4609c47cccc2dcb85505a023f74e9ba6c6e2125714a5d48710c87bff20a6d5
6
+ metadata.gz: 20d94a91e915e4f7048db9d11b9608c6f7eaa3d47c018a5ff5132d63736c54e1be96b5ce7f1050325d610c7009e1b64582a2f7253f0cd6a8b37e1e65f4f0f37f
7
+ data.tar.gz: f5aa757afd07c03e2654f11cd34b348c3d0d810231ac9831c82b04e936bb9009fb20f6a8fd93b2193ddfe344481053c0b608d2a074843972d6a35b37f194329a
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![Maintainability](https://api.codeclimate.com/v1/badges/d9d87f9ce8020eb6e656/maintainability)](https://codeclimate.com/github/RockSolt/filterameter/maintainability)
5
5
 
6
6
  # Filterameter
7
- Declarative filter parameters provide provide clean and clear filters for queries.
7
+ Declarative filter parameters provide clean and clear filters for queries.
8
8
 
9
9
  ## Usage
10
10
  Declare filters in query classes or controllers to increase readability and reduce boilerplate code. Filters can be declared for attributes, scopes, or attributes from singular associations (`belongs_to` or `has_one`). Validations can also be assigned.
@@ -170,22 +170,42 @@ Or install it yourself as:
170
170
  $ gem install filterameter
171
171
  ```
172
172
 
173
+ ## Forms and Query Parameters
174
+
175
+ The controller mixin will look for filter parameters nested under the `filter` key. For example, here's what the query parameters might look like for size and color:
176
+
177
+ ```
178
+ ?filter[size]=large&filter[color]=blue
179
+ ```
180
+
181
+ On [a generic search form](https://guides.rubyonrails.org/form_helpers.html#a-generic-search-form), the [`form_with` form helper takes the option `scope`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with) that allows parameters to be grouped:
182
+
183
+ ```erb
184
+ <%= form_with url: "/search", scope: :filter, method: :get do |form| %>
185
+ <%= form.label :size, "Size:" %>
186
+ <%= form.text_field :size %>
187
+ <%= form.label :color, "Color:" %>
188
+ <%= form.text_field :color %>
189
+ <%= form.submit "Search" %>
190
+ <% end %>
191
+ ```
192
+
173
193
 
174
194
  ## Running Tests
175
195
 
176
- Tests are written in RSpec and the dummy app uses a docker database. First, start the database and prepare it from the dummy folder.
196
+ Tests are written in RSpec and the dummy app uses a docker database. The script `bin/start_db.sh` starts and prepares the test
197
+ database. It is a one-time step before running the tests.
177
198
 
178
199
  ```bash
179
- cd spec/dummy
180
- docker-compose up -d
181
- bundle exec rails db:test:prepare
182
- cd ../..
200
+ bin/start_db.rb
201
+ bundle exec rspec
183
202
  ```
184
203
 
185
- Run the tests from the main directory
204
+ The tests can also be run across all the ruby and Rails combinations using appraisal. The install is also a one-time step.
186
205
 
187
206
  ```bash
188
- bundle exec rspec
207
+ bundle exec appraisal install
208
+ bundle exec appraisal rspec
189
209
  ```
190
210
 
191
211
  ## License
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'filterameter/filter_declaration'
4
- require 'filterameter/filter_factory'
5
- require 'filterameter/filter_registry'
6
- require 'filterameter/log_subscriber'
7
- require 'filterameter/parameters_base'
8
- require 'filterameter/query_builder'
9
-
10
3
  module Filterameter
11
4
  module Coordinators
12
5
  # = Coordinators Base
@@ -7,8 +7,6 @@ require 'action_dispatch'
7
7
  require 'action_controller/metal/live'
8
8
  require 'action_controller/metal/strong_parameters'
9
9
 
10
- require 'filterameter/coordinators/base'
11
-
12
10
  module Filterameter
13
11
  module Coordinators
14
12
  # = Controller Filters
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'filterameter/filterable'
4
- require 'filterameter/coordinators/controller_coordinator'
5
-
6
3
  module Filterameter
7
4
  # = Declarative Controller Filters
8
5
  #
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'filterameter/coordinators/query_coordinator'
4
- require 'filterameter/filterable'
5
- require 'filterameter/query_builder'
6
-
7
3
  module Filterameter
8
4
  # = Declarative Filters
9
5
  #
@@ -9,8 +9,8 @@ module Filterameter
9
9
  # explicitly by adding a call to `filter_model`.
10
10
  class CannotDetermineModelError < FilterameterError
11
11
  def initialize(name, path)
12
- super "Cannot determine model name from controller name #{value_and_classify(name)} " \
13
- "or path #{value_and_classify(path)}. Declare the model explicitly with filter_model."
12
+ super("Cannot determine model name from controller name #{value_and_classify(name)} " \
13
+ "or path #{value_and_classify(path)}. Declare the model explicitly with filter_model.")
14
14
  end
15
15
 
16
16
  private
@@ -6,7 +6,3 @@ module Filterameter
6
6
  end
7
7
  end
8
8
  end
9
-
10
- require 'filterameter/exceptions/cannot_determine_model_error'
11
- require 'filterameter/exceptions/validation_error'
12
- require 'filterameter/exceptions/undeclared_parameter_error'
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_support/core_ext/array/wrap'
4
- require 'filterameter/options/partial_options'
5
4
 
6
5
  module Filterameter
7
6
  # = Filter Declaration
@@ -1,14 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'filterameter/filters/arel_filter'
4
- require 'filterameter/filters/attribute_filter'
5
- require 'filterameter/filters/conditional_scope_filter'
6
- require 'filterameter/filters/matches_filter'
7
- require 'filterameter/filters/maximum_filter'
8
- require 'filterameter/filters/minimum_filter'
9
- require 'filterameter/filters/nested_filter'
10
- require 'filterameter/filters/scope_filter'
11
-
12
3
  module Filterameter
13
4
  # = Filter Factory
14
5
  #
@@ -1,82 +1,84 @@
1
1
  # frozen_string_literal: true
2
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
3
+ module Filterameter
4
+ module Options
5
+ # = Partial Options
6
+ #
7
+ # Class PartialOptions parses the options passed in as partial, then exposes those. Here are the options along with
8
+ # their valid values:
9
+ # - match: anywhere (default), from_start, dynamic
10
+ # - case_sensitive: true, false (default)
11
+ #
12
+ # Options may be specified by passing a hash with the option keys:
13
+ #
14
+ # partial: { match: :from_start, case_sensitive: true }
15
+ #
16
+ # There are two shortcuts: the partial option can be declared with `true`, which just uses the defaults; or the
17
+ # partial option can be declared with the match option directly, such as partial: :from_start.
18
+ class PartialOptions
19
+ VALID_OPTIONS = %i[match case_sensitive].freeze
20
+ VALID_MATCH_OPTIONS = %w[anywhere from_start dynamic].freeze
20
21
 
21
- def initialize(options)
22
- @match = 'anywhere'
23
- @case_sensitive = false
22
+ def initialize(options)
23
+ @match = 'anywhere'
24
+ @case_sensitive = false
24
25
 
25
- case options
26
- when TrueClass
27
- nil
28
- when Hash
29
- evaluate_hash(options)
30
- when String, Symbol
31
- assign_match(options)
26
+ case options
27
+ when TrueClass
28
+ nil
29
+ when Hash
30
+ evaluate_hash(options)
31
+ when String, Symbol
32
+ assign_match(options)
33
+ end
32
34
  end
33
- end
34
35
 
35
- def case_sensitive?
36
- @case_sensitive
37
- end
36
+ def case_sensitive?
37
+ @case_sensitive
38
+ end
38
39
 
39
- def match_anywhere?
40
- @match == 'anywhere'
41
- end
40
+ def match_anywhere?
41
+ @match == 'anywhere'
42
+ end
42
43
 
43
- def match_from_start?
44
- @match == 'from_start'
45
- end
44
+ def match_from_start?
45
+ @match == 'from_start'
46
+ end
46
47
 
47
- def match_dynamically?
48
- @match == 'dynamic'
49
- end
48
+ def match_dynamically?
49
+ @match == 'dynamic'
50
+ end
50
51
 
51
- private
52
+ private
52
53
 
53
- def evaluate_hash(options)
54
- options.assert_valid_keys(:match, :case_sensitive)
55
- assign_match(options[:match]) if options.key?(:match)
56
- assign_case_sensitive(options[:case_sensitive]) if options.key?(:case_sensitive)
57
- end
54
+ def evaluate_hash(options)
55
+ options.assert_valid_keys(:match, :case_sensitive)
56
+ assign_match(options[:match]) if options.key?(:match)
57
+ assign_case_sensitive(options[:case_sensitive]) if options.key?(:case_sensitive)
58
+ end
58
59
 
59
- def assign_match(value)
60
- validate_match(value)
61
- @match = value.to_s
62
- end
60
+ def assign_match(value)
61
+ validate_match(value)
62
+ @match = value.to_s
63
+ end
63
64
 
64
- def validate_match(value)
65
- return if VALID_MATCH_OPTIONS.include? value.to_s
65
+ def validate_match(value)
66
+ return if VALID_MATCH_OPTIONS.include? value.to_s
66
67
 
67
- raise ArgumentError,
68
- "Invalid match option for partial: #{value}. Valid options are #{VALID_MATCH_OPTIONS.to_sentence}"
69
- end
68
+ raise ArgumentError,
69
+ "Invalid match option for partial: #{value}. Valid options are #{VALID_MATCH_OPTIONS.to_sentence}"
70
+ end
70
71
 
71
- def assign_case_sensitive(value)
72
- validate_case_sensitive(value)
73
- @case_sensitive = value
74
- end
72
+ def assign_case_sensitive(value)
73
+ validate_case_sensitive(value)
74
+ @case_sensitive = value
75
+ end
75
76
 
76
- def validate_case_sensitive(value)
77
- return if value.is_a?(TrueClass) || value.is_a?(FalseClass)
77
+ def validate_case_sensitive(value)
78
+ return if value.is_a?(TrueClass) || value.is_a?(FalseClass)
78
79
 
79
- raise ArgumentError, "Invalid case_sensitive option for partial: #{value}. Valid options are true and false."
80
+ raise ArgumentError, "Invalid case_sensitive option for partial: #{value}. Valid options are true and false."
81
+ end
80
82
  end
81
83
  end
82
84
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'active_model/attribute_assignment'
4
4
  require 'active_model/validations'
5
- require 'filterameter/validators/inclusion_validator'
6
5
 
7
6
  module Filterameter
8
7
  # = Parameters
@@ -64,15 +64,7 @@ module Filterameter
64
64
  raise Filterameter::Exceptions::ValidationError, validator.errors
65
65
  end
66
66
 
67
- filter_params.except(*invalid_attributes(validator.errors).map(&:to_s))
68
- end
69
-
70
- def invalid_attributes(errors)
71
- if errors.respond_to? :attribute_names
72
- errors.attribute_names
73
- else # pre rails 6.1
74
- errors.keys
75
- end
67
+ filter_params.except(*validator.errors.attribute_names.map(&:to_s))
76
68
  end
77
69
 
78
70
  def validator_class
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Filterameter
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.1'
5
5
  end
data/lib/filterameter.rb CHANGED
@@ -1,17 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'filterameter/configuration'
4
- require 'filterameter/declarative_controller_filters'
5
- require 'filterameter/declarative_filters'
6
- require 'filterameter/exceptions'
3
+ require 'zeitwerk'
4
+
5
+ loader = Zeitwerk::Loader.for_gem
6
+ loader.setup # ready!
7
7
 
8
8
  # = Filterameter
9
- #
10
- # Module Filterameter can be mixed into a controller to provide the DSL to describe each controller's filters.
11
- #
12
- # The model class must be declared if it cannot be derived. It can be derived if (A) the model is not namespaced and its
13
- # name matches the controller name (for example BrandsController -> Brand) or (B) both the controller and model share
14
- # the same namespace and name.
15
9
  module Filterameter
16
10
  class << self
17
11
  attr_writer :configuration
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.3.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Todd Kummer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-30 00:00:00.000000000 Z
11
+ date: 2024-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 5.2.2
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 5.2.2
26
+ version: '6.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: appraisal
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 2.4.1
33
+ version: 2.5.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 2.4.1
40
+ version: 2.5.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: guard
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -86,56 +86,70 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 1.1.4
89
+ version: 1.5.4
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 1.1.4
96
+ version: 1.5.4
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rspec-rails
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '3.9'
103
+ version: '6.1'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '3.9'
110
+ version: '6.1'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: rubocop
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 1.31.1
117
+ version: 1.60.2
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.31.1
124
+ version: 1.60.2
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-packaging
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.5.2
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.5.2
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: rubocop-rails
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
143
  - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: 2.15.1
145
+ version: 2.23.1
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
- version: 2.15.1
152
+ version: 2.23.1
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: simplecov
141
155
  requirement: !ruby/object:Gem::Requirement
@@ -157,7 +171,6 @@ executables: []
157
171
  extensions: []
158
172
  extra_rdoc_files: []
159
173
  files:
160
- - MIT-LICENSE
161
174
  - README.md
162
175
  - Rakefile
163
176
  - lib/filterameter.rb
@@ -208,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
208
221
  - !ruby/object:Gem::Version
209
222
  version: '0'
210
223
  requirements: []
211
- rubygems_version: 3.1.6
224
+ rubygems_version: 3.4.19
212
225
  signing_key:
213
226
  specification_version: 4
214
227
  summary: Declarative Filter Parameters
data/MIT-LICENSE DELETED
@@ -1,20 +0,0 @@
1
- Copyright 2019 Todd Kummer
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.