Skip to content

feat(concern): add declarative default filters - #14

Merged
jplot merged 2 commits into
mainfrom
feat/default-filters
Jul 3, 2026
Merged

feat(concern): add declarative default filters#14
jplot merged 2 commits into
mainfrom
feat/default-filters

Conversation

@jplot

@jplot jplot commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

Last major item from the gem-landscape backlog (has_scope's default: transposed at the engine level): default_filters sort: '-value_date' declares default filter params covering default sort and default filter values in one mechanism — opt-in only, never an implicit fallback.

Changes

  • default_filters(hash) + inherited default_filter_params registry on Filterable::Concern; defaults are merged under the request's filters, so an absent key falls back to its default and a present key — even blank — suppresses it (the caller expressed intent for that key)
  • a Proc default is evaluated lazily at each call (Date.current computed at query time, not at declaration — the classic frozen-time bug); a Proc returning nil withdraws its default
  • the whitelist holds for free: defaults flow through the same fold as request params, so a default on an undeclared name narrows nothing
  • filterable and applied_filters now share a single effective_filters helper — the report always reflects exactly what the fold received, and Model.filterable({}) applies the declared defaults
  • uniformity side effect: filters now always receive a HashWithIndifferentAccess (previously a raw ActionController::Parameters could reach custom filters); all built-in filters already handled both, the whole suite passes unchanged

How to Test

  1. bundle exec rake
  2. model.default_filters(sort: '-value_date') puis model.filterable({}) → relation triée desc ; model.filterable(filters: { sort: '+value_date' }) → la requête gagne ; model.applied_filters({}){ 'sort' => '-value_date' }

jplot added 2 commits July 3, 2026 13:59
- `default_filters sort: '-value_date'` declares default filter
  params, merged under the request's filters: an absent key falls back
  to its default, a present key — even blank — suppresses it
- a Proc default is evaluated lazily at each call (Date.current is
  computed at query time, not at declaration), returning nil withdraws
  the default; defaults go through the same whitelisted fold, so a
  default on an undeclared name narrows nothing
- filterable and applied_filters now share a single effective_filters
  helper, so the report always reflects what the fold received; both
  hand filters a HashWithIndifferentAccess uniformly (previously raw
  ActionController::Parameters could reach custom filters)
- opt-in only: with no declared defaults the engine behaves as before
Copilot AI review requested due to automatic review settings July 3, 2026 12:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@fluence-ci

fluence-ci Bot commented Jul 3, 2026

Copy link
Copy Markdown

📊 Code Quality Report

Tool Base PR Δ
Reek 40 45 smells 🔴 +5
Flog total 577.7 603.8 🔴 +26.1
Flay total 521 521 ±0

🔍 7 new reek smells introduced — review below.

🔍 7 new reek smells introduced by this PR
Smell File Context Lines Message
DuplicateMethodCall lib/filterable/concern.rb Filterable::Concern#effective_filters 124, 124 calls 'params[:filters]' 2 times
FeatureEnvy lib/filterable/concern.rb Filterable::Concern#effective_filters 123, 123, 124, 124 refers to 'params' more than self (maybe move it to another class?)
FeatureEnvy lib/filterable/concern.rb Filterable::Concern#evaluated_defaults 135, 135 refers to 'value' more than self (maybe move it to another class?)
ManualDispatch lib/filterable/concern.rb Filterable::Concern#default_filter_params 48 manually dispatches method call
ManualDispatch lib/filterable/concern.rb Filterable::Concern#effective_filters 125 manually dispatches method call
ManualDispatch lib/filterable/concern.rb Filterable::Concern#evaluated_defaults 135 manually dispatches method call
NilCheck lib/filterable/concern.rb Filterable::Concern#evaluated_defaults 136 performs a nil-check
✅ 2 reek smells resolved by this PR

The exact list isn't shown — these smells are gone, that's what matters.

🔍 All 45 reek smells on this PR
Smell File Context Lines Message
DataClump lib/filterable/equatable.rb Filterable::Equatable 18, 31, 43, 57 takes parameters ['params', 'scope'] to 4 methods
DataClump lib/filterable/scopable.rb Filterable::Scopable 17, 30, 44 takes parameters ['params', 'scope'] to 3 methods
DataClump lib/filterable/sortable.rb Filterable::Sortable 19, 33, 44 takes parameters ['params', 'scope'] to 3 methods
DataClump lib/filterable/togglable.rb Filterable::Togglable 24, 37, 51 takes parameters ['params', 'scope'] to 3 methods
DuplicateMethodCall lib/filterable/concern.rb Filterable::Concern#effective_filters 124, 124 calls 'params[:filters]' 2 times
DuplicateMethodCall lib/filterable/scopable.rb Filterable::Scopable#accepted 50, 52 calls 'declared[name]' 2 times
DuplicateMethodCall lib/filterable/sortable.rb Filterable::Sortable#terms 45, 47 calls 'params[:sort]' 2 times
DuplicateMethodCall lib/filterable/togglable.rb Filterable::Togglable#accepted 56, 58 calls 'declared[name]' 2 times
DuplicateMethodCall lib/filterable/togglable.rb Filterable::Togglable#toggled_on? 73, 75 calls 'TRUE_VALUES.include?(value)' 2 times
FeatureEnvy lib/filterable/concern.rb Filterable::Concern#effective_filters 123, 123, 124, 124 refers to 'params' more than self (maybe move it to another class?)
FeatureEnvy lib/filterable/concern.rb Filterable::Concern#evaluated_defaults 135, 135 refers to 'value' more than self (maybe move it to another class?)
ManualDispatch lib/filterable/concern.rb Filterable::Concern#add_filter 33 manually dispatches method call
ManualDispatch lib/filterable/concern.rb Filterable::Concern#applied_filters 99 manually dispatches method call
ManualDispatch lib/filterable/concern.rb Filterable::Concern#default_filter_params 48 manually dispatches method call
ManualDispatch lib/filterable/concern.rb Filterable::Concern#effective_filters 125 manually dispatches method call
ManualDispatch lib/filterable/concern.rb Filterable::Concern#evaluated_defaults 135 manually dispatches method call
ManualDispatch lib/filterable/concern.rb Filterable::Concern#filters 21 manually dispatches method call
ManualDispatch lib/filterable/concern.rb Filterable::Concern#indifferent? 147 manually dispatches method call
ManualDispatch lib/filterable/concerns/datable.rb Filterable::Concerns::Datable#datable_attribute_names 27 manually dispatches method call
ManualDispatch lib/filterable/concerns/equatable.rb Filterable::Concerns::Equatable#equatable_attribute_names 25 manually dispatches method call
ManualDispatch lib/filterable/concerns/rangeable.rb Filterable::Concerns::Rangeable#rangeable_attribute_names 26 manually dispatches method call
ManualDispatch lib/filterable/concerns/scopable.rb Filterable::Concerns::Scopable#scopable_scope_names 24 manually dispatches method call
ManualDispatch lib/filterable/concerns/sortable.rb Filterable::Concerns::Sortable#sortable_attribute_names 24 manually dispatches method call
ManualDispatch lib/filterable/concerns/togglable.rb Filterable::Concerns::Togglable#togglable_scope_names 25 manually dispatches method call
ManualDispatch lib/filterable/datable.rb Filterable::Datable#bounds 38 manually dispatches method call
ManualDispatch lib/filterable/declarations_validator.rb Filterable::DeclarationsValidator#column_errors 59 manually dispatches method call
ManualDispatch lib/filterable/declarations_validator.rb Filterable::DeclarationsValidator#scope_errors 76, 79 manually dispatches method call
ManualDispatch lib/filterable/equatable.rb Filterable::Equatable#values 59 manually dispatches method call
ManualDispatch lib/filterable/rangeable.rb Filterable::Rangeable#bounds 36 manually dispatches method call
ManualDispatch lib/filterable/scopable.rb Filterable::Scopable#accepted 47, 50 manually dispatches method call
ManualDispatch lib/filterable/togglable.rb Filterable::Togglable#accepted 54, 56 manually dispatches method call
NestedIterators lib/filterable/datable.rb Filterable::Datable#accepted 55 contains iterators nested 2 deep
NestedIterators lib/filterable/rangeable.rb Filterable::Rangeable#accepted 53 contains iterators nested 2 deep
NilCheck lib/filterable/concern.rb Filterable::Concern#evaluated_defaults 136 performs a nil-check
NilCheck lib/filterable/equatable.rb Filterable::Equatable#accepted 46 performs a nil-check
NilCheck lib/filterable/scopable.rb Filterable::Scopable#accepted 50 performs a nil-check
NilCheck lib/filterable/togglable.rb Filterable::Togglable#toggled_on? 70 performs a nil-check
TooManyStatements lib/filterable/concern.rb Filterable::Concern#applied_filters 94 has approx 6 statements
TooManyStatements lib/filterable/datable.rb Filterable::Datable#accepted 52 has approx 6 statements
TooManyStatements lib/filterable/rangeable.rb Filterable::Rangeable#accepted 50 has approx 6 statements
TooManyStatements lib/filterable/scopable.rb Filterable::Scopable#accepted 44 has approx 7 statements
TooManyStatements lib/filterable/sortable.rb Filterable::Sortable#terms 44 has approx 6 statements
TooManyStatements lib/filterable/togglable.rb Filterable::Togglable#accepted 51 has approx 6 statements
TooManyStatements lib/filterable/value_normalization.rb Filterable::ValueNormalization#normalize 15 has approx 6 statements
UtilityFunction lib/filterable/concern.rb Filterable::Concern#indifferent? 146 doesn't depend on instance state (maybe move it to another class?)
📊 Flog complexity breakdown
603.8: flog total
     7.0: flog/method average

    28.7: main#none
    18.8: Filterable::Railtie::initializer#filterable.active_record lib/filterable/railtie.rb:19-29
    15.8: Filterable::Scopable#accepted    lib/filterable/scopable.rb:44-54
    15.7: Filterable::Datable::Range#call  lib/filterable/datable/range.rb:17-23
    14.8: Filterable::Concern#applied_filters lib/filterable/concern.rb:94-101
    14.4: Filterable::Concern#effective_filters lib/filterable/concern.rb:122-127
    12.8: Filterable::Togglable#accepted   lib/filterable/togglable.rb:51-60
    12.8: Filterable::Sortable#terms       lib/filterable/sortable.rb:44-53
    11.5: Filterable::Concern#evaluated_defaults lib/filterable/concern.rb:133-138
    11.2: Filterable::Datable::After#call  lib/filterable/datable/after.rb:16-21
    11.2: Filterable::Datable::Before#call lib/filterable/datable/before.rb:16-21
    11.2: Filterable::Datable::Since#call  lib/filterable/datable/since.rb:16-21
    11.2: Filterable::Rangeable::Maximum#call lib/filterable/rangeable/maximum.rb:16-21
    11.2: Filterable::Rangeable::Minimum#call lib/filterable/rangeable/minimum.rb:16-21
    10.4: Filterable::Datable#accepted     lib/filterable/datable.rb:52-60
    10.4: Filterable::Rangeable#accepted   lib/filterable/rangeable.rb:50-58
    10.3: Filterable::ValueNormalization#normalize lib/filterable/value_normalization.rb:15-25
    10.1: Filterable::Concern#filterable   lib/filterable/concern.rb:76-81
     9.0: Filterable::DeclarationsValidator#column_errors lib/filterable/declarations_validator.rb:57-66
     9.0: Filterable::Concerns::Datable#none
     8.8: Filterable::Concern#add_filter   lib/filterable/concern.rb:30-38
     8.6: Filterable::Concern#default_filter_params lib/filterable/concern.rb:46-53
     8.6: Filterable::Concerns::Togglable#togglable_scope_names lib/filterable/concerns/togglable.rb:23-30
     8.6: Filterable::Concerns::Sortable#sortable_attribute_names lib/filterable/concerns/sortable.rb:22-29
     8.6: Filterable::Concerns::Scopable#scopable_scope_names lib/filterable/concerns/scopable.rb:22-29
     8.6: Filterable::Concerns::Rangeable#rangeable_attribute_names lib/filterable/concerns/rangeable.rb:24-31
     8.6: Filterable::Concerns::Datable#datable_attribute_names lib/filterable/concerns/datable.rb:25-32
     8.6: Filterable::Concerns::Equatable#equatable_attribute_names lib/filterable/concerns/equatable.rb:23-30
     8.3: Filterable::Sortable#call        lib/filterable/sortable.rb:19-25
     8.3: Filterable::Rangeable#bounds     lib/filterable/rangeable.rb:34-38
     8.3: Filterable::Datable#bounds       lib/filterable/datable.rb:36-40
     8.3: Filterable::Datable::Since#applied lib/filterable/datable/since.rb:29-34
📊 Flay duplication breakdown
Total score (lower is better) = 521

1) Similar code found in :module (mass = 245)
  lib/filterable/datable/after.rb:4
  lib/filterable/datable/before.rb:4
  lib/filterable/datable/since.rb:4
  lib/filterable/rangeable/maximum.rb:4
  lib/filterable/rangeable/minimum.rb:4

2) Similar code found in :module (mass = 164)
  lib/filterable/concerns/equatable.rb:4
  lib/filterable/concerns/scopable.rb:4
  lib/filterable/concerns/sortable.rb:4
  lib/filterable/concerns/togglable.rb:4

3) Similar code found in :defn (mass = 66)
  lib/filterable/datable.rb:52
  lib/filterable/rangeable.rb:50

4) Similar code found in :defn (mass = 46)
  lib/filterable/datable.rb:36
  lib/filterable/rangeable.rb:34

@fluence-ci

fluence-ci Bot commented Jul 3, 2026

Copy link
Copy Markdown

🛡️ Documentation Quality: 100.0% (0.0%)

████████████████████ 100.0% — 750/750 checks passing

Base PR Δ
Coverage 100.0% 100.0% ±0%
Failing 0 0 ±0

Documentation quality above threshold.

Changed files

File Failing Δ vs base
🟢 lib/filterable/concern.rb 0 ±0

@fluence-ci

fluence-ci Bot commented Jul 3, 2026

Copy link
Copy Markdown

🛡️ Coverage Report: 100.0% (0.0%)

████████████████████ 100.0% — 411/411 lines covered

Base PR Δ
Total 100.0% 100.0% ±0%

All changes are tested.

Changed files

File Coverage Δ vs base Lines
🟢 lib/filterable/concern.rb 100.0% ±0% 44/44

@fluence-ci

fluence-ci Bot commented Jul 3, 2026

Copy link
Copy Markdown

🛡️ RuboCop: 0 offenses (0)

Base PR Δ
Offenses 0 0 ±0

No RuboCop offenses.

@jplot
jplot merged commit 0455f51 into main Jul 3, 2026
16 checks passed
@jplot
jplot deleted the feat/default-filters branch July 3, 2026 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants