filterameter 0.9.0 → 0.10.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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -0
  3. data/lib/filterameter/declaration_errors/cannot_be_inline_scope_error.rb +24 -0
  4. data/lib/filterameter/declaration_errors/filter_scope_argument_error.rb +16 -0
  5. data/lib/filterameter/declaration_errors/no_such_attribute_error.rb +16 -0
  6. data/lib/filterameter/declaration_errors/not_a_scope_error.rb +17 -0
  7. data/lib/filterameter/declaration_errors/sort_scope_requires_one_argument_error.rb +16 -0
  8. data/lib/filterameter/declaration_errors/unexpected_error.rb +22 -0
  9. data/lib/filterameter/declaration_errors.rb +11 -0
  10. data/lib/filterameter/declarations_validator.rb +99 -0
  11. data/lib/filterameter/declarative_filters.rb +2 -0
  12. data/lib/filterameter/errors.rb +24 -0
  13. data/lib/filterameter/filter_coordinator.rb +4 -0
  14. data/lib/filterameter/filter_factory.rb +5 -2
  15. data/lib/filterameter/filters/arel_filter.rb +4 -0
  16. data/lib/filterameter/filters/attribute_filter.rb +3 -0
  17. data/lib/filterameter/filters/attribute_validator.rb +18 -0
  18. data/lib/filterameter/filters/conditional_scope_filter.rb +16 -0
  19. data/lib/filterameter/filters/matches_filter.rb +3 -0
  20. data/lib/filterameter/filters/nested_filter.rb +15 -0
  21. data/lib/filterameter/filters/scope_filter.rb +14 -0
  22. data/lib/filterameter/registries/filter_registry.rb +0 -4
  23. data/lib/filterameter/registries/registry.rb +3 -1
  24. data/lib/filterameter/registries/sort_registry.rb +4 -0
  25. data/lib/filterameter/registries/sub_registry.rb +4 -0
  26. data/lib/filterameter/sort_factory.rb +1 -1
  27. data/lib/filterameter/sorts/attribute_sort.rb +3 -0
  28. data/lib/filterameter/sorts/scope_sort.rb +25 -0
  29. data/lib/filterameter/version.rb +1 -1
  30. metadata +42 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f62d4fc49f10adca7c1fec20fb17e430740035d69b154f973d8f300405f64f9c
4
- data.tar.gz: 7369430dbb71a29faf17853a35eaa69f3a35fae52b82d63e85f5c14bd550aa51
3
+ metadata.gz: 929699bdcf63b640acc335ab2b6690e4d57822541e354f93aa9faf71ef0faf3c
4
+ data.tar.gz: 205958bfb5a55418e740382146934b8aa7f3ef122686f903621b6de1f14a2788
5
5
  SHA512:
6
- metadata.gz: e5ee234ae90460282e2618135b96f91882af00361b1c16eda99beb42640cf9c4186d1bb61fa1989235015a1f4dff07f7611ca5dea55ec6277c27dc9239329bc8
7
- data.tar.gz: 6f802f89428a06de2f077d5c4b38464bc8ad1ee4327d334856f3421481cba3af84ef789cb2e85863fe933f8b45f90fe2b7640910d16618bb97e35c65d9bda691
6
+ metadata.gz: dd04092048f8c2d00d4df6f77136e4714e21385b08ca21f0fe1608ba0ff7b12d97d1ca27fc26024f7d52180cb1013546c520131cfe35e90fa688d5c36a185071
7
+ data.tar.gz: 1a065d5dcd20ac179e80b1d507ed52de4be84704651cbba2b7fa68c65c0793db06577eec83c68949e880173bfa8ffae930eca2d0ceec68952d26901be7649c93
data/README.md CHANGED
@@ -340,8 +340,22 @@ If the filter parameters are NOT nested, set this to false. Doing so will restri
340
340
  those that have been declared, meaning undeclared parameters are ignored (and the action_on_undeclared_parameters
341
341
  configuration option does not come into play).
342
342
 
343
+ ### Testing Declarations
343
344
 
345
+ The declarations can be tested for each controller, catching typos, incorrectly defined scopes, or any other issues. Method `declarations_validator` is added to each controller, and a single controller test can be added to validate all the declarations for that controller.
344
346
 
347
+ An RSpec test might look like this:
348
+
349
+ ```ruby
350
+ expect(WidgetsController.declarations_validator).to be_valid
351
+ ```
352
+
353
+ In Minitest it might look like this:
354
+
355
+ ```ruby
356
+ validator = WidgetsController.declarations_validator
357
+ assert_predicate validator, :valid?, -> { validator.errors }
358
+ ```
345
359
 
346
360
  ## Installation
347
361
  Add this line to your application's Gemfile:
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ module DeclarationErrors
5
+ # = CannotBeInlineScopeError
6
+ #
7
+ # Error CannotBeInlineScopeError occurs when an inline scope has been used to define a filter that takes a
8
+ # parameter. This is not valid for use as a Filterameter filter because an inline scope always has an arity of -1
9
+ # meaning the factory cannot tell if it has an argument or not. As such, all inline scopes are assumed to not have
10
+ # arguments and thus be conditional scopes.
11
+ #
12
+ # [The Rails guide](https://guides.rubyonrails.org/active_record_querying.html#passing-in-arguments) provides
13
+ # guidance suggesting scopes that take arguments be written as class methods. This takes that guidance a step
14
+ # further and makes it a requirement for a scope that will be used as a filter.
15
+ class CannotBeInlineScopeError < DeclarationError
16
+ def initialize(model_name, scope_name)
17
+ super(<<~ERROR.chomp)
18
+ #{model_name} scope '#{scope_name}' needs to be written as a class method, not as an inline scope. This is a
19
+ suggestion from the Rails guide but a requirement in order to use a scope that has an argument as a filter.
20
+ ERROR
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ module DeclarationErrors
5
+ # = Filter Scope Argument Error
6
+ #
7
+ # Error FilterScopeArgumentError occurs when a scope used as a filter but does not have either zero or one
8
+ # arument. A conditional scope filter should take zero arguments; other scope filters should take one argument.
9
+ class FilterScopeArgumentError < DeclarationError
10
+ def initialize(model_name, scope_name)
11
+ super("#{model_name} scope '#{scope_name}' takes too many arguments. Scopes for filters can only have either " \
12
+ 'zero (conditional scope) or one argument')
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ module DeclarationErrors
5
+ # = No Such Attribute Error
6
+ #
7
+ # Error NoSuchAttributeError occurs when a filter or sort references an attribute that does not exist on the model.
8
+ # The most likely case of this is a typo. Note that if the typo was supposed to reference a scope, this error is
9
+ # added as attributes are assumed when no matching scopes are found.
10
+ class NoSuchAttributeError < DeclarationError
11
+ def initialize(model_name, attribute_name)
12
+ super("Attribute '#{attribute_name}' does not exist on #{model_name}")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ module DeclarationErrors
5
+ # = Not A Scope Error
6
+ #
7
+ # Error NotAScopeError flags a class method that has been used as a filter but is not a scope. This could occur if
8
+ # there is a class method of the same name an attribute, in which case the class method is going to block the
9
+ # creation of an attribute filter. The work around (if the class method cannot be renamed) is to create a scope
10
+ # that provides a filter on the attribute.
11
+ class NotAScopeError < DeclarationError
12
+ def initialize(model_name, scope_name)
13
+ super("#{model_name} class method '#{scope_name}' is not a scope.")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ module DeclarationErrors
5
+ # = Sort Scope Requires One Argument Error
6
+ #
7
+ # Error SortScopeRequiresOneArgumentError occurs when a sort has been declared for a scope that does not take
8
+ # exactly one argument. Sort scopes must take a single argument and will receive either :asc or :desc to indicate
9
+ # the direction.
10
+ class SortScopeRequiresOneArgumentError < DeclarationError
11
+ def initialize(model_name, scope_name)
12
+ super("#{model_name} scope '#{scope_name}' must take exactly one argument to sort by.")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ module DeclarationErrors
5
+ # = Unexpected Error
6
+ #
7
+ # Error UnexpectedError occurs when the filter or scope factory raises an exception that the validator did not
8
+ # expect.
9
+ class UnexpectedError < DeclarationError
10
+ def initialize(error)
11
+ super(<<~ERROR)
12
+ The previous error was unexpected. It occurred during while building a filter or sort (see below). Please
13
+ report this to the library so that the error can be handled and provide clearer feedback about what is wrong
14
+ with the declaration.
15
+
16
+ #{error.message}
17
+ #{error.backtrace.join("\n\t")}
18
+ ERROR
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ module DeclarationErrors
5
+ # = Declaration Error
6
+ #
7
+ # Error DeclarationError provides a base class for errors from filter or sort declarations.
8
+ class DeclarationError < StandardError
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ # = Declarations Validator
5
+ #
6
+ # Class DeclarationsValidtor fetches each filter and sort from the registry to validate the declaration. This class
7
+ # can be accessed from the controller as `declarations_validator` (via the FilterCoordinator) and be used in tests.
8
+ #
9
+ # Use the `valid?` method to test, then report errors with the `errors` attribute.
10
+ #
11
+ # A test in RSpec might look like this:
12
+ #
13
+ # expect(WidgetsController.declarations_validator).to be_valid
14
+ #
15
+ # In Minitest it might look like this:
16
+ #
17
+ # assert WidgetsController.declarations_validator.valid?, WidgetsController.declarations_validator.errors
18
+ class DeclarationsValidator
19
+ include Filterameter::Errors
20
+
21
+ def initialize(controller_name, model, registry)
22
+ @controller_name = controller_name
23
+ @model = model
24
+ @registry = registry
25
+ end
26
+
27
+ def inspect
28
+ "filter declarations on #{@controller_name.titleize}Controller"
29
+ end
30
+
31
+ def errors
32
+ @errors&.join("\n")
33
+ end
34
+
35
+ private
36
+
37
+ def validate(_)
38
+ @errors.push(*validation_errors_for('filter', fetch_filters))
39
+ @errors.push(*validation_errors_for('sort', fetch_sorts))
40
+ end
41
+
42
+ def fetch_filters
43
+ @registry.filter_parameter_names.index_with { |name| fetch_filter(name) }
44
+ end
45
+
46
+ def fetch_filter(name)
47
+ @registry.fetch_filter(name)
48
+ rescue StandardError => e
49
+ FactoryErrors.new(e)
50
+ end
51
+
52
+ def fetch_sorts
53
+ (@registry.sort_parameter_names - @registry.filter_parameter_names).index_with { |name| fetch_sort(name) }
54
+ end
55
+
56
+ def fetch_sort(name)
57
+ @registry.fetch_sort(name)
58
+ rescue StandardError => e
59
+ FactoryErrors.new(e)
60
+ end
61
+
62
+ def validation_errors_for(type, items)
63
+ items.select { |_name, item| item.respond_to? :valid? }
64
+ .reject { |_name, item| item.valid?(@model) }
65
+ .map { |name, item| error_message(type, name, item.errors) }
66
+ end
67
+
68
+ def error_message(type, name, errors)
69
+ "\nInvalid #{type} for '#{name}':\n #{errors.join("\n ")}"
70
+ end
71
+
72
+ # = Factory Errors
73
+ #
74
+ # Class FactoryErrors is swapped in if the fetch from a factory fails. It is always invalid and provides the reason.
75
+ class FactoryErrors
76
+ attr_reader :errors
77
+
78
+ def initialize(error)
79
+ @errors = [wrap_if_unexpected(error)]
80
+ end
81
+
82
+ def valid?(_)
83
+ false
84
+ end
85
+
86
+ def to_s
87
+ @errors.join("\n")
88
+ end
89
+
90
+ private
91
+
92
+ def wrap_if_unexpected(error)
93
+ return error if error.is_a?(Filterameter::DeclarationErrors::DeclarationError)
94
+
95
+ Filterameter::DeclarationErrors::UnexpectedError.new(error)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -10,6 +10,8 @@ module Filterameter
10
10
  include Filterameter::Sortable
11
11
 
12
12
  class_methods do
13
+ delegate :declarations_validator, to: :filter_coordinator
14
+
13
15
  def filter_model(model_class, query_var_name = nil)
14
16
  filter_coordinator.model_class = model_class
15
17
  filter_query_var_name(query_var_name) if query_var_name.present?
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ # = Errors
5
+ #
6
+ # Module Errors provides `valid?` and `errors` to implementing classes. If the `valid?` method is not overridden,
7
+ # then it returns true.
8
+ #
9
+ # To provide validations rules, override `validate`. If any fail, populate the errors attribute with the
10
+ # reason for the failures.
11
+ module Errors
12
+ attr_reader :errors
13
+
14
+ def valid?(model = nil)
15
+ @errors = []
16
+ validate(model)
17
+ @errors.empty?
18
+ end
19
+
20
+ private
21
+
22
+ def validate(_model); end
23
+ end
24
+ end
@@ -45,6 +45,10 @@ module Filterameter
45
45
  end
46
46
  end
47
47
 
48
+ def declarations_validator
49
+ Filterameter::DeclarationsValidator.new(@controller_name, model_class, registry)
50
+ end
51
+
48
52
  private
49
53
 
50
54
  def model_class
@@ -46,10 +46,13 @@ module Filterameter
46
46
  # Inline scopes return an arity of -1 regardless of arguments, so those will always be assumed to be
47
47
  # conditional scopes. To have a filter that passes a value to a scope, it must be a class method.
48
48
  def build_scope_filter(model, declaration)
49
- if model.method(declaration.name).arity == 1
49
+ number_of_arguments = model.method(declaration.name).arity
50
+ if number_of_arguments < 1
51
+ Filterameter::Filters::ConditionalScopeFilter.new(declaration.name)
52
+ elsif number_of_arguments == 1
50
53
  Filterameter::Filters::ScopeFilter.new(declaration.name)
51
54
  else
52
- Filterameter::Filters::ConditionalScopeFilter.new(declaration.name)
55
+ raise Filterameter::DeclarationErrors::FilterScopeArgumentError.new(model.name, declaration.name)
53
56
  end
54
57
  end
55
58
  end
@@ -6,7 +6,11 @@ module Filterameter
6
6
  #
7
7
  # Class ArelFilter is a base class for arel queries. It does not implement <tt>apply</tt>.
8
8
  class ArelFilter
9
+ include Filterameter::Errors
10
+ include Filterameter::Filters::AttributeValidator
11
+
9
12
  def initialize(model, attribute_name)
13
+ @attribute_name = attribute_name
10
14
  @arel_attribute = model.arel_table[attribute_name]
11
15
  end
12
16
  end
@@ -6,6 +6,9 @@ module Filterameter
6
6
  #
7
7
  # Class AttributeFilter leverages ActiveRecord's where query method to add criteria for an attribute.
8
8
  class AttributeFilter
9
+ include Filterameter::Errors
10
+ include AttributeValidator
11
+
9
12
  def initialize(attribute_name)
10
13
  @attribute_name = attribute_name
11
14
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ module Filters
5
+ # = Attribute Validator
6
+ #
7
+ # Module AttributeValidator validates that the attribute exists on the model.
8
+ module AttributeValidator
9
+ private
10
+
11
+ def validate(model)
12
+ return if model.attribute_method? @attribute_name
13
+
14
+ @errors << Filterameter::DeclarationErrors::NoSuchAttributeError.new(model, @attribute_name)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -6,6 +6,8 @@ module Filterameter
6
6
  #
7
7
  # Class ConditionalScopeFilter applies the scope if the parameter is not false.
8
8
  class ConditionalScopeFilter
9
+ include Filterameter::Errors
10
+
9
11
  def initialize(scope_name)
10
12
  @scope_name = scope_name
11
13
  end
@@ -15,6 +17,20 @@ module Filterameter
15
17
 
16
18
  query.public_send(@scope_name)
17
19
  end
20
+
21
+ private
22
+
23
+ def validate(model)
24
+ validate_is_a_scope(model)
25
+ rescue ArgumentError
26
+ @errors << Filterameter::DeclarationErrors::CannotBeInlineScopeError.new(model.name, @scope_name)
27
+ end
28
+
29
+ def validate_is_a_scope(model)
30
+ return if model.public_send(@scope_name).is_a? ActiveRecord::Relation
31
+
32
+ @errors << Filterameter::DeclarationErrors::NotAScopeError.new(model.name, @scope_name)
33
+ end
18
34
  end
19
35
  end
20
36
  end
@@ -6,6 +6,9 @@ module Filterameter
6
6
  #
7
7
  # Class MatchesFilter uses arel's `matches` to generate a LIKE query.
8
8
  class MatchesFilter
9
+ include Filterameter::Errors
10
+ include Filterameter::Filters::AttributeValidator
11
+
9
12
  def initialize(attribute_name, options)
10
13
  @attribute_name = attribute_name
11
14
  @prefix = options.match_anywhere? ? '%' : nil
@@ -6,6 +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
+ include Filterameter::Errors
10
+
9
11
  def initialize(association_names, association_model, attribute_filter)
10
12
  @joins_values = Filterameter::Helpers::JoinsValuesBuilder.build(association_names)
11
13
  @association_model = association_model
@@ -16,6 +18,19 @@ module Filterameter
16
18
  query.joins(@joins_values)
17
19
  .merge(@attribute_filter.apply(@association_model.all, value))
18
20
  end
21
+
22
+ private
23
+
24
+ def validate(model)
25
+ @errors.push(*@attribute_filter.errors) unless @attribute_filter.valid?(@association_model)
26
+ validate_associations(model)
27
+ end
28
+
29
+ def validate_associations(model)
30
+ model.joins(@joins_values).to_sql
31
+ rescue ActiveRecord::ConfigurationError => e
32
+ @errors << e.message
33
+ end
19
34
  end
20
35
  end
21
36
  end
@@ -6,6 +6,8 @@ module Filterameter
6
6
  #
7
7
  # Class ScopeFilter applies the named scope passing in the parameter value.
8
8
  class ScopeFilter
9
+ include Filterameter::Errors
10
+
9
11
  def initialize(scope_name)
10
12
  @scope_name = scope_name
11
13
  end
@@ -13,6 +15,18 @@ module Filterameter
13
15
  def apply(query, value)
14
16
  query.public_send(@scope_name, value)
15
17
  end
18
+
19
+ private
20
+
21
+ def validate(model)
22
+ validate_is_a_scope(model)
23
+ end
24
+
25
+ def validate_is_a_scope(model)
26
+ return if model.public_send(@scope_name, '42').is_a? ActiveRecord::Relation
27
+
28
+ @errors << Filterameter::DeclarationErrors::NotAScopeError.new(model.name, @scope_name)
29
+ end
16
30
  end
17
31
  end
18
32
  end
@@ -24,10 +24,6 @@ module Filterameter
24
24
  @declarations.values
25
25
  end
26
26
 
27
- def filter_parameter_names
28
- @declarations.keys
29
- end
30
-
31
27
  private
32
28
 
33
29
  # if range is enabled, then in addition to the attribute filter this also adds min and/or max filters
@@ -6,7 +6,9 @@ module Filterameter
6
6
  #
7
7
  # Class Registry records declarations and allows resulting filters and sorts to be fetched from sub-registries.
8
8
  class Registry
9
- delegate :filter_declarations, :filter_parameter_names, :ranges, to: :@filter_registry
9
+ delegate :filter_declarations, :ranges, to: :@filter_registry
10
+ delegate :parameter_names, prefix: :filter, to: :@filter_registry
11
+ delegate :parameter_names, prefix: :sort, to: :@sort_registry
10
12
 
11
13
  def initialize(model_class)
12
14
  @filter_registry = Filterameter::Registries::FilterRegistry.new(Filterameter::FilterFactory.new(model_class))
@@ -7,6 +7,10 @@ module Filterameter
7
7
  # Class SortRegistry is a collection of the sorts. It captures the declarations when classes are loaded,
8
8
  # then uses the injected SortFactory to build the sorts on demand as they are needed.
9
9
  class SortRegistry < SubRegistry
10
+ def sort_parameter_names
11
+ @declarations.keys
12
+ end
13
+
10
14
  private
11
15
 
12
16
  def build_declaration(name, options)
@@ -28,6 +28,10 @@ module Filterameter
28
28
  @registry[name] = @factory.build(@declarations[name])
29
29
  end
30
30
  end
31
+
32
+ def parameter_names
33
+ @declarations.keys
34
+ end
31
35
  end
32
36
  end
33
37
  end
@@ -33,7 +33,7 @@ module Filterameter
33
33
 
34
34
  def build_sort(name, declaration_is_a_scope)
35
35
  if declaration_is_a_scope
36
- Filterameter::Filters::ScopeFilter.new(name)
36
+ Filterameter::Sorts::ScopeSort.new(name)
37
37
  else
38
38
  Filterameter::Sorts::AttributeSort.new(name)
39
39
  end
@@ -6,6 +6,9 @@ module Filterameter
6
6
  #
7
7
  # Class AttributeSort leverages ActiveRecord's `order` query method to add sorting for an attribute.
8
8
  class AttributeSort
9
+ include Filterameter::Errors
10
+ include Filterameter::Filters::AttributeValidator
11
+
9
12
  def initialize(attribute_name)
10
13
  @attribute_name = attribute_name
11
14
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Filterameter
4
+ module Sorts
5
+ # = Scope Sort
6
+ #
7
+ # Class ScopeSort applies the scope with the a param that is either :asc or :desc. A scope that does not take
8
+ # exactly one argument is not valid for sorting.
9
+ class ScopeSort < Filterameter::Filters::ScopeFilter
10
+ private
11
+
12
+ def validate(model)
13
+ validate_is_a_scope(model)
14
+ rescue ArgumentError
15
+ @errors << Filterameter::DeclarationErrors::SortScopeRequiresOneArgumentError.new(model.name, @scope_name)
16
+ end
17
+
18
+ def validate_is_a_scope(model)
19
+ return if model.public_send(@scope_name, :asc).is_a? ActiveRecord::Relation
20
+
21
+ @errors << Filterameter::DeclarationErrors::NotAScopeError.new(model.name, @scope_name)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Filterameter
4
- VERSION = '0.9.0'
4
+ VERSION = '0.10.0'
5
5
  end
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.9.0
4
+ version: 0.10.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-07-18 00:00:00.000000000 Z
11
+ date: 2024-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -150,6 +150,34 @@ dependencies:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
152
  version: '2.25'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop-rspec
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 3.0.3
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 3.0.3
167
+ - !ruby/object:Gem::Dependency
168
+ name: rubocop-rspec_rails
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 2.30.0
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 2.30.0
153
181
  - !ruby/object:Gem::Dependency
154
182
  name: simplecov
155
183
  requirement: !ruby/object:Gem::Requirement
@@ -176,7 +204,16 @@ files:
176
204
  - Rakefile
177
205
  - lib/filterameter.rb
178
206
  - lib/filterameter/configuration.rb
207
+ - lib/filterameter/declaration_errors.rb
208
+ - lib/filterameter/declaration_errors/cannot_be_inline_scope_error.rb
209
+ - lib/filterameter/declaration_errors/filter_scope_argument_error.rb
210
+ - lib/filterameter/declaration_errors/no_such_attribute_error.rb
211
+ - lib/filterameter/declaration_errors/not_a_scope_error.rb
212
+ - lib/filterameter/declaration_errors/sort_scope_requires_one_argument_error.rb
213
+ - lib/filterameter/declaration_errors/unexpected_error.rb
214
+ - lib/filterameter/declarations_validator.rb
179
215
  - lib/filterameter/declarative_filters.rb
216
+ - lib/filterameter/errors.rb
180
217
  - lib/filterameter/exceptions.rb
181
218
  - lib/filterameter/exceptions/cannot_determine_model_error.rb
182
219
  - lib/filterameter/exceptions/collection_association_sort_error.rb
@@ -189,6 +226,7 @@ files:
189
226
  - lib/filterameter/filterable.rb
190
227
  - lib/filterameter/filters/arel_filter.rb
191
228
  - lib/filterameter/filters/attribute_filter.rb
229
+ - lib/filterameter/filters/attribute_validator.rb
192
230
  - lib/filterameter/filters/conditional_scope_filter.rb
193
231
  - lib/filterameter/filters/matches_filter.rb
194
232
  - lib/filterameter/filters/maximum_filter.rb
@@ -211,6 +249,7 @@ files:
211
249
  - lib/filterameter/sort_factory.rb
212
250
  - lib/filterameter/sortable.rb
213
251
  - lib/filterameter/sorts/attribute_sort.rb
252
+ - lib/filterameter/sorts/scope_sort.rb
214
253
  - lib/filterameter/validators/inclusion_validator.rb
215
254
  - lib/filterameter/version.rb
216
255
  homepage: https://github.com/RockSolt/filterameter
@@ -225,7 +264,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
225
264
  requirements:
226
265
  - - ">="
227
266
  - !ruby/object:Gem::Version
228
- version: '0'
267
+ version: 3.1.0
229
268
  required_rubygems_version: !ruby/object:Gem::Requirement
230
269
  requirements:
231
270
  - - ">="