kiroshi 0.1.1 → 0.3.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 +4 -4
- data/.codacy.yml +13 -0
- data/.markdownlint.json +6 -0
- data/.rubocop_todo.yml +4 -8
- data/README.md +62 -68
- data/lib/kiroshi/filter.rb +46 -24
- data/lib/kiroshi/filter_query/exact.rb +3 -3
- data/lib/kiroshi/filter_query/like.rb +30 -6
- data/lib/kiroshi/filter_query.rb +10 -14
- data/lib/kiroshi/filter_runner.rb +47 -35
- data/lib/kiroshi/filters/class_methods.rb +172 -0
- data/lib/kiroshi/filters.rb +89 -78
- data/lib/kiroshi/version.rb +1 -1
- data/lib/kiroshi.rb +3 -23
- data/spec/lib/kiroshi/filter_query/exact_spec.rb +41 -7
- data/spec/lib/kiroshi/filter_query/like_spec.rb +53 -12
- data/spec/lib/kiroshi/filter_runner_spec.rb +51 -26
- data/spec/lib/kiroshi/filter_spec.rb +9 -12
- data/spec/lib/kiroshi/filters/class_methods_spec.rb +103 -0
- data/spec/lib/kiroshi/filters_spec.rb +315 -9
- data/spec/spec_helper.rb +1 -0
- data/spec/support/db/schema.rb +1 -0
- metadata +6 -2
@@ -12,7 +12,12 @@ module Kiroshi
|
|
12
12
|
#
|
13
13
|
# @example Creating and running a filter
|
14
14
|
# filter = Kiroshi::Filter.new(:name, match: :like)
|
15
|
-
# runner = Kiroshi::FilterRunner.new(filter: filter, scope: User.all,
|
15
|
+
# runner = Kiroshi::FilterRunner.new(filter: filter, scope: User.all, value: 'John')
|
16
|
+
# result = runner.apply
|
17
|
+
#
|
18
|
+
# @example Creating and running a filter with specific value
|
19
|
+
# filter = Kiroshi::Filter.new(:status)
|
20
|
+
# runner = Kiroshi::FilterRunner.new(filter: filter, scope: User.all, value: 'active')
|
16
21
|
# result = runner.apply
|
17
22
|
#
|
18
23
|
# @since 0.1.0
|
@@ -21,13 +26,13 @@ module Kiroshi
|
|
21
26
|
#
|
22
27
|
# @param filter [Kiroshi::Filter] the filter configuration
|
23
28
|
# @param scope [ActiveRecord::Relation] the scope to filter
|
24
|
-
# @param
|
29
|
+
# @param value [Object, nil] the specific value to use for filtering, defaults to nil
|
25
30
|
#
|
26
|
-
# @since 0.
|
27
|
-
def initialize(filter:, scope:,
|
31
|
+
# @since 0.2.0
|
32
|
+
def initialize(filter:, scope:, value: nil)
|
28
33
|
@filter = filter
|
29
34
|
@scope = scope
|
30
|
-
@
|
35
|
+
@value = value
|
31
36
|
end
|
32
37
|
|
33
38
|
# Applies the filter logic to the scope
|
@@ -38,41 +43,49 @@ module Kiroshi
|
|
38
43
|
# @return [ActiveRecord::Relation] the filtered scope
|
39
44
|
#
|
40
45
|
# @example Applying exact match filter
|
41
|
-
# runner = FilterRunner.new(filter: filter, scope: scope,
|
46
|
+
# runner = FilterRunner.new(filter: filter, scope: scope, value: 'John')
|
42
47
|
# runner.apply
|
43
48
|
#
|
44
49
|
# @example Applying LIKE filter
|
45
|
-
# runner = FilterRunner.new(filter: filter, scope: scope,
|
50
|
+
# runner = FilterRunner.new(filter: filter, scope: scope, value: 'Ruby')
|
51
|
+
# runner.apply
|
52
|
+
#
|
53
|
+
# @example With specific value provided
|
54
|
+
# runner = FilterRunner.new(filter: filter, scope: scope, value: 'specific_value')
|
46
55
|
# runner.apply
|
47
56
|
#
|
48
|
-
# @example With no
|
49
|
-
# runner = FilterRunner.new(filter: filter, scope: scope,
|
57
|
+
# @example With no value (returns unchanged scope)
|
58
|
+
# runner = FilterRunner.new(filter: filter, scope: scope, value: nil)
|
50
59
|
# runner.apply
|
51
60
|
# # Returns the original scope unchanged
|
52
61
|
#
|
53
62
|
# @since 0.1.1
|
54
63
|
def apply
|
55
|
-
return scope unless
|
64
|
+
return scope unless value.present?
|
56
65
|
|
57
66
|
query_strategy = FilterQuery.for(filter.match).new(self)
|
58
67
|
query_strategy.apply
|
59
68
|
end
|
60
69
|
|
61
|
-
|
70
|
+
attr_reader :scope, :value
|
71
|
+
|
72
|
+
# @!method scope
|
73
|
+
# @api private
|
62
74
|
#
|
63
|
-
#
|
75
|
+
# Returns the current scope being filtered
|
64
76
|
#
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
end
|
77
|
+
# @return [ActiveRecord::Relation] the scope
|
78
|
+
#
|
79
|
+
# @since 0.1.1
|
69
80
|
|
70
|
-
#
|
81
|
+
# @!method value
|
82
|
+
# @api private
|
71
83
|
#
|
72
|
-
#
|
84
|
+
# Returns the filter value for the current filter
|
73
85
|
#
|
74
|
-
#
|
75
|
-
|
86
|
+
# @return [Object] the filter value or nil if not present
|
87
|
+
#
|
88
|
+
# @since 0.2.0
|
76
89
|
|
77
90
|
# Returns the table name to use for the filter
|
78
91
|
#
|
@@ -84,12 +97,12 @@ module Kiroshi
|
|
84
97
|
#
|
85
98
|
# @example With filter table_name specified
|
86
99
|
# filter = Kiroshi::Filter.new(:name, table: 'tags')
|
87
|
-
# runner = FilterRunner.new(filter: filter, scope: Document.joins(:tags),
|
100
|
+
# runner = FilterRunner.new(filter: filter, scope: Document.joins(:tags), value: 'ruby')
|
88
101
|
# runner.table_name # => 'tags'
|
89
102
|
#
|
90
103
|
# @example Without filter table_name (fallback to scope)
|
91
104
|
# filter = Kiroshi::Filter.new(:name)
|
92
|
-
# runner = FilterRunner.new(filter: filter, scope: Document.all,
|
105
|
+
# runner = FilterRunner.new(filter: filter, scope: Document.all, value: 'test')
|
93
106
|
# runner.table_name # => 'documents'
|
94
107
|
#
|
95
108
|
# @since 0.1.1
|
@@ -104,9 +117,16 @@ module Kiroshi
|
|
104
117
|
#
|
105
118
|
# @return [ActiveRecord::Relation] the scope
|
106
119
|
|
120
|
+
# @!method value
|
121
|
+
# @api private
|
122
|
+
#
|
123
|
+
# Returns the filter value for the current filter
|
124
|
+
#
|
125
|
+
# @return [Object, nil] the filter value or nil if not present
|
126
|
+
|
107
127
|
private
|
108
128
|
|
109
|
-
attr_reader :filter
|
129
|
+
attr_reader :filter
|
110
130
|
|
111
131
|
# @!method filter
|
112
132
|
# @api private
|
@@ -116,24 +136,16 @@ module Kiroshi
|
|
116
136
|
#
|
117
137
|
# @return [Kiroshi::Filter] the filter configuration
|
118
138
|
|
119
|
-
|
120
|
-
# @api private
|
121
|
-
# @private
|
122
|
-
#
|
123
|
-
# Returns the hash of filter values
|
124
|
-
#
|
125
|
-
# @return [Hash] the hash of filter values
|
126
|
-
|
127
|
-
delegate :attribute, to: :filter
|
139
|
+
delegate :column, to: :filter
|
128
140
|
delegate :table_name, to: :scope, prefix: true
|
129
141
|
delegate :table_name, to: :filter, prefix: true
|
130
142
|
|
131
|
-
# @!method
|
143
|
+
# @!method column
|
132
144
|
# @api private
|
133
145
|
#
|
134
|
-
# Returns the
|
146
|
+
# Returns the column name to use in database queries
|
135
147
|
#
|
136
|
-
# @return [Symbol] the
|
148
|
+
# @return [Symbol] the column name to use in database queries
|
137
149
|
|
138
150
|
# @!method scope_table_name
|
139
151
|
# @api private
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kiroshi
|
4
|
+
class Filters
|
5
|
+
# @api public
|
6
|
+
# Class-level methods for configuring filters in Kiroshi::Filters
|
7
|
+
#
|
8
|
+
# This module provides the DSL methods that allow filter classes to
|
9
|
+
# define their filtering behavior using class-level method calls.
|
10
|
+
# These methods are automatically available when extending Kiroshi::Filters.
|
11
|
+
#
|
12
|
+
# The primary interface is the {.filter_by} method, which registers
|
13
|
+
# filters that will be applied when {Filters#apply} is called on
|
14
|
+
# instances of the filter class.
|
15
|
+
#
|
16
|
+
# @example Basic filter configuration
|
17
|
+
# class DocumentFilters < Kiroshi::Filters
|
18
|
+
# filter_by :name, match: :like
|
19
|
+
# filter_by :status
|
20
|
+
# filter_by :category, table: :documents
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @example Accessing filter configurations
|
24
|
+
# DocumentFilters.filter_configs.keys # => ["name", "status", "category"]
|
25
|
+
# DocumentFilters.filter_configs["name"].match # => :like
|
26
|
+
#
|
27
|
+
# @since 0.2.0
|
28
|
+
# @author darthjee
|
29
|
+
module ClassMethods
|
30
|
+
# Defines a filter for the current filter class
|
31
|
+
#
|
32
|
+
# This method is used at the class level to configure filters that will
|
33
|
+
# be applied when {Filters#apply} is called. Each call creates a new {Filter}
|
34
|
+
# instance with the specified configuration and stores it in the class's
|
35
|
+
# filter registry for later use during filtering operations.
|
36
|
+
#
|
37
|
+
# The method supports various matching strategies and table qualification
|
38
|
+
# options to handle complex database queries with joins and ambiguous
|
39
|
+
# column names.
|
40
|
+
#
|
41
|
+
# @overload filter_by(filter_key, **options)
|
42
|
+
# @param filter_key [Symbol] the filter key name to identify this filter
|
43
|
+
# @param options [Hash] additional options passed to {Filter#initialize}
|
44
|
+
# @option options [Symbol] :match (:exact) the matching type
|
45
|
+
# - +:exact+ for exact matching (default)
|
46
|
+
# - +:like+ for partial matching using SQL LIKE with wildcards
|
47
|
+
# @option options [String, Symbol, nil] :table (nil) the table name to qualify the column
|
48
|
+
# when dealing with joined tables that have conflicting column names
|
49
|
+
# @option options [Symbol, nil] :column (nil) the column name to use in database queries,
|
50
|
+
# defaults to filter_key if not specified
|
51
|
+
#
|
52
|
+
# @return (see Filters.filter_by)
|
53
|
+
# @example (see Filters.filter_by)
|
54
|
+
# @note (see Filters.filter_by)
|
55
|
+
# @see (see Filters.filter_by)
|
56
|
+
# @since (see Filters.filter_by)
|
57
|
+
def filter_by(filter_key, **options)
|
58
|
+
Filter.new(filter_key, **options).tap do |filter|
|
59
|
+
filter_configs[filter_key.to_s] = filter
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @api private
|
64
|
+
# Returns the filter configuration for a specific filter key
|
65
|
+
#
|
66
|
+
# This method provides a convenient way to retrieve a specific filter
|
67
|
+
# by its filter key name. It's a shorthand for accessing the filter_configs
|
68
|
+
# hash directly and is used internally by the filtering system.
|
69
|
+
#
|
70
|
+
# @param filter_key [Symbol, String] the filter key name to look up
|
71
|
+
#
|
72
|
+
# @return [Filter, nil] the filter instance for the given filter key,
|
73
|
+
# or nil if no filter is configured for that filter key
|
74
|
+
#
|
75
|
+
# @example Retrieving a specific filter
|
76
|
+
# class MyFilters < Kiroshi::Filters
|
77
|
+
# filter_by :name, match: :like
|
78
|
+
# filter_by :status
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# MyFilters.filter_for(:name) # => #<Kiroshi::Filter:0x... @filter_key=:name @match=:like>
|
82
|
+
# MyFilters.filter_for(:status) # => #<Kiroshi::Filter:0x... @filter_key=:status @match=:exact>
|
83
|
+
# MyFilters.filter_for(:unknown) # => nil
|
84
|
+
#
|
85
|
+
# @see .filter_configs for accessing the complete filter registry
|
86
|
+
# @see Filters#apply for how this method is used during filtering
|
87
|
+
#
|
88
|
+
# @since 0.3.0
|
89
|
+
def filter_for(filter_key)
|
90
|
+
filter_key_string = filter_key.to_s
|
91
|
+
filter_configs[filter_key_string] || inherited_filter_for(filter_key_string)
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# @api private
|
97
|
+
# @private
|
98
|
+
#
|
99
|
+
# Searches for a filter in the inheritance chain
|
100
|
+
#
|
101
|
+
# This method looks up the inheritance chain to find a filter configuration
|
102
|
+
# for the given filter key. It only searches in superclasses that inherit
|
103
|
+
# from Kiroshi::Filters, stopping when it reaches a non-Filters class.
|
104
|
+
#
|
105
|
+
# @param filter_key_string [String] the filter key name to look up
|
106
|
+
# @return [Filter, nil] the filter instance from a parent class, or nil if not found
|
107
|
+
#
|
108
|
+
# @since 0.3.0
|
109
|
+
def inherited_filter_for(filter_key_string)
|
110
|
+
return nil unless superclass < Kiroshi::Filters
|
111
|
+
|
112
|
+
superclass.filter_for(filter_key_string)
|
113
|
+
end
|
114
|
+
|
115
|
+
# @api private
|
116
|
+
# @private
|
117
|
+
#
|
118
|
+
# Returns the hash of configured filters for this filter class
|
119
|
+
#
|
120
|
+
# This method provides access to the internal registry of filters
|
121
|
+
# that have been configured using {.filter_by}. The returned hash
|
122
|
+
# contains {Filter} instances keyed by their filter key names, allowing
|
123
|
+
# for efficient O(1) lookup during filter application.
|
124
|
+
#
|
125
|
+
# This method is primarily used internally by {Filters#apply} to
|
126
|
+
# iterate through and apply all configured filters to a scope.
|
127
|
+
# While marked as private API, it may be useful for introspection
|
128
|
+
# and testing purposes.
|
129
|
+
#
|
130
|
+
# @return [Hash<String, Filter>] hash of {Filter} instances configured
|
131
|
+
# for this filter class, keyed by filter key name for efficient access
|
132
|
+
#
|
133
|
+
# @example Accessing configured filters for introspection
|
134
|
+
# class MyFilters < Kiroshi::Filters
|
135
|
+
# filter_by :name, match: :like
|
136
|
+
# filter_by :status
|
137
|
+
# filter_by :category, table: :categories
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# MyFilters.filter_configs.length # => 3
|
141
|
+
# MyFilters.filter_configs.keys # => ["name", "status", "category"]
|
142
|
+
# MyFilters.filter_configs["name"].attribute # => :name
|
143
|
+
# MyFilters.filter_configs["name"].match # => :like
|
144
|
+
# MyFilters.filter_configs["status"].match # => :exact
|
145
|
+
# MyFilters.filter_configs["category"].table_name # => :categories
|
146
|
+
#
|
147
|
+
# @example Using in tests to verify filter configuration
|
148
|
+
# RSpec.describe ProductFilters do
|
149
|
+
# it 'configures the expected filters' do
|
150
|
+
# expect(described_class.filter_configs).to have_key("name")
|
151
|
+
# expect(described_class.filter_configs["name"].match).to eq(:like)
|
152
|
+
# end
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# @note This method returns a reference to the actual internal hash.
|
156
|
+
# Modifying the returned hash directly will affect the filter class
|
157
|
+
# configuration. Use {.filter_by} for proper filter registration.
|
158
|
+
#
|
159
|
+
# @note The hash is lazily initialized on first access and persists
|
160
|
+
# for the lifetime of the class. Each filter class maintains its
|
161
|
+
# own separate filter_configs hash.
|
162
|
+
#
|
163
|
+
# @see .filter_by for adding filters to this configuration
|
164
|
+
# @see Filters#apply for how these configurations are used
|
165
|
+
#
|
166
|
+
# @since 0.2.0
|
167
|
+
def filter_configs
|
168
|
+
@filter_configs ||= {}
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/lib/kiroshi/filters.rb
CHANGED
@@ -36,78 +36,83 @@ module Kiroshi
|
|
36
36
|
#
|
37
37
|
# @since 0.1.0
|
38
38
|
class Filters
|
39
|
-
|
40
|
-
# Defines a filter for the current filter class
|
41
|
-
#
|
42
|
-
# This method is used at the class level to configure filters that will
|
43
|
-
# be applied when {#apply} is called. Each call creates a new {Filter}
|
44
|
-
# instance with the specified configuration.
|
45
|
-
#
|
46
|
-
# @overload filter_by(attribute, **options)
|
47
|
-
# @param attribute [Symbol] the attribute name to filter by
|
48
|
-
# @param options [Hash] additional options passed to {Filter#initialize}
|
49
|
-
# @option options [Symbol] :match (:exact) the matching type
|
50
|
-
# - +:exact+ for exact matching (default)
|
51
|
-
# - +:like+ for partial matching using SQL LIKE
|
52
|
-
# @option options [String, Symbol, nil] :table (nil) the table name to qualify the attribute
|
53
|
-
#
|
54
|
-
# @return [Filter] the new filter instance
|
55
|
-
#
|
56
|
-
# @example Defining exact match filters
|
57
|
-
# class ProductFilters < Kiroshi::Filters
|
58
|
-
# filter_by :category
|
59
|
-
# filter_by :brand
|
60
|
-
# end
|
61
|
-
#
|
62
|
-
# @example Defining partial match filters
|
63
|
-
# class SearchFilters < Kiroshi::Filters
|
64
|
-
# filter_by :title, match: :like
|
65
|
-
# filter_by :description, match: :like
|
66
|
-
# end
|
67
|
-
#
|
68
|
-
# @example Mixed filter types
|
69
|
-
# class OrderFilters < Kiroshi::Filters
|
70
|
-
# filter_by :customer_name, match: :like
|
71
|
-
# filter_by :status, match: :exact
|
72
|
-
# filter_by :payment_method
|
73
|
-
# end
|
74
|
-
#
|
75
|
-
# @example Filter with table qualification
|
76
|
-
# class DocumentTagFilters < Kiroshi::Filters
|
77
|
-
# filter_by :name, table: :tags
|
78
|
-
# end
|
79
|
-
#
|
80
|
-
# @since 0.1.0
|
81
|
-
def filter_by(attribute, **)
|
82
|
-
Filter.new(attribute, **).tap do |filter|
|
83
|
-
filter_configs << filter
|
84
|
-
end
|
85
|
-
end
|
39
|
+
autoload :ClassMethods, 'kiroshi/filters/class_methods'
|
86
40
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
41
|
+
extend ClassMethods
|
42
|
+
|
43
|
+
# @method self.filter_by(filter_key, **options)
|
44
|
+
# @api public
|
45
|
+
# @param filter_key [Symbol] the filter key name to identify this filter
|
46
|
+
# @param options [Hash] additional options passed to {Filter#initialize}
|
47
|
+
# @option options [Symbol] :match (:exact) the matching type
|
48
|
+
# - +:exact+ for exact matching (default)
|
49
|
+
# - +:like+ for partial matching using SQL LIKE with wildcards
|
50
|
+
# @option options [String, Symbol, nil] :table (nil) the table name to qualify the column
|
51
|
+
# when dealing with joined tables that have conflicting column names
|
52
|
+
# @option options [Symbol, nil] :column (nil) the column name to use in database queries,
|
53
|
+
# defaults to filter_key if not specified
|
54
|
+
#
|
55
|
+
# @return [Filter] the new filter instance that was created and registered
|
56
|
+
#
|
57
|
+
# @example Defining exact match filters
|
58
|
+
# class ProductFilters < Kiroshi::Filters
|
59
|
+
# filter_by :category # Exact match on category
|
60
|
+
# filter_by :brand # Exact match on brand
|
61
|
+
# filter_by :active # Exact match on active status
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# @example Defining partial match filters
|
65
|
+
# class SearchFilters < Kiroshi::Filters
|
66
|
+
# filter_by :title, match: :like # Partial match on title
|
67
|
+
# filter_by :description, match: :like # Partial match on description
|
68
|
+
# filter_by :author_name, match: :like # Partial match on author name
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# @example Mixed filter types with different matching strategies
|
72
|
+
# class OrderFilters < Kiroshi::Filters
|
73
|
+
# filter_by :customer_name, match: :like # Partial match for customer search
|
74
|
+
# filter_by :status, match: :exact # Exact match for order status
|
75
|
+
# filter_by :payment_method # Exact match (default) for payment
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# @example Filters with table qualification for joined queries
|
79
|
+
# class DocumentTagFilters < Kiroshi::Filters
|
80
|
+
# filter_by :name, table: :documents # Filter by document name
|
81
|
+
# filter_by :tag_name, table: :tags # Filter by tag name
|
82
|
+
# filter_by :category, table: :categories # Filter by category name
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# @example Complex real-world filter class
|
86
|
+
# class ProductSearchFilters < Kiroshi::Filters
|
87
|
+
# filter_by :name, match: :like # Product name search
|
88
|
+
# filter_by :category_id # Exact category match
|
89
|
+
# filter_by :brand, match: :like # Brand name search
|
90
|
+
# filter_by :price_min # Minimum price
|
91
|
+
# filter_by :price_max # Maximum price
|
92
|
+
# filter_by :in_stock # Availability filter
|
93
|
+
# filter_by :category_name, table: :categories # Category name via join
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# @example Using custom column names
|
97
|
+
# class UserFilters < Kiroshi::Filters
|
98
|
+
# filter_by :full_name, column: :name, match: :like # Filter key 'full_name' maps to 'name' column
|
99
|
+
# filter_by :user_email, column: :email # Filter key 'user_email' maps to 'email' column
|
100
|
+
# filter_by :account_status, column: :status # Filter key 'account_status' maps to 'status' column
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# @note When using table qualification, ensure that the specified table
|
104
|
+
# is properly joined in the scope being filtered. The filter will not
|
105
|
+
# automatically add joins - it only qualifies the column name.
|
106
|
+
#
|
107
|
+
# @see Filter#initialize for detailed information about filter options
|
108
|
+
# @see Filters#apply for how these filters are used during query execution
|
109
|
+
#
|
110
|
+
# @since 0.3.0
|
106
111
|
|
107
112
|
# Creates a new Filters instance
|
108
113
|
#
|
109
114
|
# @param filters [Hash] a hash containing the filter values to be applied.
|
110
|
-
# Keys should correspond to
|
115
|
+
# Keys should correspond to filter keys defined with {.filter_by}.
|
111
116
|
# Values will be used for filtering. Nil or blank values are ignored.
|
112
117
|
#
|
113
118
|
# @example Creating filters with values
|
@@ -125,7 +130,7 @@ module Kiroshi
|
|
125
130
|
#
|
126
131
|
# @since 0.1.0
|
127
132
|
def initialize(filters = {})
|
128
|
-
@filters = filters
|
133
|
+
@filters = filters
|
129
134
|
end
|
130
135
|
|
131
136
|
# Applies all configured filters to the given scope
|
@@ -160,10 +165,13 @@ module Kiroshi
|
|
160
165
|
# filtered_articles = filters.apply(Article.all)
|
161
166
|
# # Generates: WHERE title LIKE '%Ruby%'
|
162
167
|
#
|
163
|
-
# @since 0.
|
168
|
+
# @since 0.2.0
|
164
169
|
def apply(scope)
|
165
|
-
|
166
|
-
|
170
|
+
filters.compact.each do |filter_key, value|
|
171
|
+
filter = self.class.filter_for(filter_key)
|
172
|
+
next unless filter
|
173
|
+
|
174
|
+
scope = filter.apply(scope: scope, value: value)
|
167
175
|
end
|
168
176
|
|
169
177
|
scope
|
@@ -171,14 +179,17 @@ module Kiroshi
|
|
171
179
|
|
172
180
|
private
|
173
181
|
|
174
|
-
|
175
|
-
|
176
|
-
# @!method filters
|
177
|
-
# @api private
|
178
|
-
# @private
|
182
|
+
# Returns the hash of filter values to be applied
|
179
183
|
#
|
180
|
-
#
|
184
|
+
# Uses lazy initialization to ensure @filters is never nil,
|
185
|
+
# defaulting to an empty hash when no filters were provided.
|
181
186
|
#
|
182
|
-
#
|
187
|
+
# @return [Hash] the hash of filter values to be applied
|
188
|
+
#
|
189
|
+
# @api private
|
190
|
+
# @since 0.3.0
|
191
|
+
def filters
|
192
|
+
@filters ||= {}
|
193
|
+
end
|
183
194
|
end
|
184
195
|
end
|
data/lib/kiroshi/version.rb
CHANGED
data/lib/kiroshi.rb
CHANGED
@@ -9,9 +9,10 @@
|
|
9
9
|
# using a declarative DSL. It supports multiple matching strategies and can
|
10
10
|
# be easily integrated into Rails controllers and other components.
|
11
11
|
#
|
12
|
-
# The gem is designed around
|
12
|
+
# The gem is designed around the main concept:
|
13
13
|
# - {Filters}: A base class for creating reusable filter sets
|
14
|
-
#
|
14
|
+
#
|
15
|
+
# Individual filters are handled internally and don't require direct interaction.
|
15
16
|
#
|
16
17
|
# @example Basic filter class definition
|
17
18
|
# class DocumentFilters < Kiroshi::Filters
|
@@ -68,25 +69,6 @@
|
|
68
69
|
# filtered_users = filters.apply(User.includes(:department))
|
69
70
|
# # Generates: WHERE email LIKE '%admin%' AND role = 'moderator' AND active = true
|
70
71
|
#
|
71
|
-
# @example Individual filter usage
|
72
|
-
# # Create standalone filters
|
73
|
-
# name_filter = Kiroshi::Filter.new(:name, match: :like)
|
74
|
-
# status_filter = Kiroshi::Filter.new(:status)
|
75
|
-
#
|
76
|
-
# # Apply filters step by step
|
77
|
-
# scope = Document.all
|
78
|
-
# scope = name_filter.apply(scope, { name: 'annual' })
|
79
|
-
# scope = status_filter.apply(scope, { status: 'published' })
|
80
|
-
#
|
81
|
-
# @example Filter matching types
|
82
|
-
# # Exact matching (default)
|
83
|
-
# Kiroshi::Filter.new(:status)
|
84
|
-
# # Generates: WHERE status = 'value'
|
85
|
-
#
|
86
|
-
# # Partial matching with LIKE
|
87
|
-
# Kiroshi::Filter.new(:title, match: :like)
|
88
|
-
# # Generates: WHERE title LIKE '%value%'
|
89
|
-
#
|
90
72
|
# @example Empty value handling
|
91
73
|
# filters = DocumentFilters.new(name: '', status: 'published')
|
92
74
|
# result = filters.apply(Document.all)
|
@@ -148,9 +130,7 @@
|
|
148
130
|
# end
|
149
131
|
#
|
150
132
|
# @see Filters Base class for creating filter sets
|
151
|
-
# @see Filter Individual filter implementation
|
152
133
|
# @see https://github.com/darthjee/kiroshi GitHub repository
|
153
|
-
# @see https://www.rubydoc.info/gems/kiroshi YARD documentation
|
154
134
|
#
|
155
135
|
# @since 0.1.0
|
156
136
|
module Kiroshi
|