kiroshi 0.0.1 → 0.1.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: f215b5db38cfab5958a6d87cfbfffe94a6a4ca9f36b114d6539f064e918aaf3c
4
- data.tar.gz: 11fd8327a643b27eab6feaa92eda2dd962ccbb573501a0dd06f0811ac840dbdc
3
+ metadata.gz: 3df16fe842c4fcd160633af8a5e4d91fa19cbb857dc135a31160c1f389a5f6c1
4
+ data.tar.gz: b3f98740895ae5c611d616c36472d77ce412a1086d9d6a9ffe4532a8b3fa2235
5
5
  SHA512:
6
- metadata.gz: 60fbd8f3a711432be3ecfdd1c565d535657b6424ee46eae72b85d345fed4dd160606eeb7e4c13c83de291b72cce32d6c63bbbc8c31949c1e5c0a3155ddb02821
7
- data.tar.gz: e57185d0789ef027ac87be751cc5054cdf52169beb7a7f9836aaa7e750b9776b4ae86751560c91b20dc20b93775a9f63f58d91948d7d85ec23a5012b1a151fdd
6
+ metadata.gz: 328641f23218cf3a3a3841af7027f42981dced635c03e47642e5f7bd3a9194d94e13e59a87f51f71adc51344642a682148606daff113ebf80f638e0cb0f5be19
7
+ data.tar.gz: e3cb7920836e0ad2d86d05875c74a57759c5b28d9309124539c61c902adaaaa3a9bf1d0f094e9b19500d23f4034563ac9de234b9fd6f36a21084054bfed09c13
data/.rubocop_todo.yml CHANGED
@@ -1,21 +1,23 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2025-08-15 21:59:24 UTC using RuboCop version 1.79.2.
3
+ # on 2025-08-17 15:11:59 UTC using RuboCop version 1.79.2.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 1
10
- # Configuration parameters: AllowComments, AllowEmptyLambdas.
11
- Lint/EmptyBlock:
12
- Exclude:
13
- - 'spec/**/*_spec.rb'
14
- - 'spec/dummy/config/routes.rb'
15
-
16
9
  # Offense count: 1
17
10
  # Configuration parameters: AllowedPatterns.
18
11
  # AllowedPatterns: ^expect_, ^assert_
19
12
  RSpec/NoExpectationExample:
20
13
  Exclude:
21
14
  - 'spec/lib/kiroshi_spec.rb'
15
+
16
+ # Offense count: 2
17
+ # Configuration parameters: AllowedConstants.
18
+ Style/Documentation:
19
+ Exclude:
20
+ - 'spec/**/*'
21
+ - 'test/**/*'
22
+ - 'lib/kiroshi/filter.rb'
23
+ - 'lib/kiroshi/filters.rb'
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gemspec
6
6
 
7
7
  gem 'actionpack', '7.2.2.1'
8
8
  gem 'activerecord', '7.2.2.1'
9
+ gem 'activesupport', '7.2.2.1'
9
10
  gem 'bundler', '~> 2.3'
10
11
  gem 'factory_bot', '6.2.1'
11
12
  gem 'minitest', '5.25.4'
data/README.md CHANGED
@@ -1,16 +1,24 @@
1
- Kiroshi
2
- ====
1
+ # Kiroshi
3
2
  [![Build Status](https://circleci.com/gh/darthjee/kiroshi.svg?style=shield)](https://circleci.com/gh/darthjee/kiroshi)
4
3
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/35480a5e82e74ff7a0186697b3f61a4b)](https://app.codacy.com/gh/darthjee/kiroshi/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
5
4
 
6
5
  ![kiroshi](https://raw.githubusercontent.com/darthjee/kiroshi/master/kiroshi.jpg)
7
6
 
8
- Yard Documentation
9
- -------------------
10
- [https://www.rubydoc.info/gems/kiroshi/0.0.1](https://www.rubydoc.info/gems/kiroshi/0.0.1)
11
7
 
12
- Installation
13
- ---------------
8
+ ## Yard Documentation
9
+
10
+ [https://www.rubydoc.info/gems/kiroshi/0.1.1](https://www.rubydoc.info/gems/kiroshi/0.1.1)
11
+
12
+ Kiroshi has been designed to make filtering ActiveRecord queries easier
13
+ by providing a flexible and reusable filtering system. It allows you to
14
+ define filter sets that can be applied to any ActiveRecord scope,
15
+ supporting both exact matches and partial matching using SQL LIKE operations.
16
+
17
+ Current Release: [0.1.1](https://github.com/darthjee/kiroshi/tree/0.1.1)
18
+
19
+ [Next release](https://github.com/darthjee/kiroshi/compare/0.1.1...master)
20
+
21
+ ## Installation
14
22
 
15
23
  - Install it
16
24
 
@@ -27,3 +35,249 @@ Installation
27
35
  ```bash
28
36
  bundle install kiroshi
29
37
  ```
38
+
39
+ ## Usage
40
+
41
+ ### Kiroshi::Filters
42
+
43
+ [Filters](https://www.rubydoc.info/gems/kiroshi/Kiroshi/Filters)
44
+ is a base class for implementing filter sets on ActiveRecord scopes.
45
+ It uses a class-level DSL to define filters and an instance-level interface to apply them.
46
+
47
+ #### Basic Usage
48
+
49
+ ```ruby
50
+ # Define a filter class
51
+ class DocumentFilters < Kiroshi::Filters
52
+ filter_by :name, match: :like
53
+ filter_by :status
54
+ filter_by :category
55
+ end
56
+
57
+ # Apply filters to a scope
58
+ filters = DocumentFilters.new(name: 'report', status: 'published')
59
+ filtered_documents = filters.apply(Document.all)
60
+ # Generates: WHERE name LIKE '%report%' AND status = 'published'
61
+ ```
62
+
63
+ #### Filter Types
64
+
65
+ Kiroshi supports two types of matching:
66
+
67
+ - `:exact` - Exact match (default)
68
+ - `:like` - Partial match using SQL LIKE
69
+
70
+ ```ruby
71
+ class UserFilters < Kiroshi::Filters
72
+ filter_by :email, match: :like # Partial matching
73
+ filter_by :role # Exact matching (default)
74
+ filter_by :active, match: :exact # Explicit exact matching
75
+ end
76
+
77
+ filters = UserFilters.new(email: 'admin', role: 'moderator')
78
+ filtered_users = filters.apply(User.all)
79
+ # Generates: WHERE email LIKE '%admin%' AND role = 'moderator'
80
+ ```
81
+
82
+ #### Advanced Examples
83
+
84
+ ##### Multiple Filter Types
85
+
86
+ ```ruby
87
+ class ProductFilters < Kiroshi::Filters
88
+ filter_by :name, match: :like
89
+ filter_by :category
90
+ filter_by :price, match: :exact
91
+ filter_by :brand
92
+ end
93
+
94
+ # Apply only some filters
95
+ filters = ProductFilters.new(name: 'laptop', category: 'electronics')
96
+ products = filters.apply(Product.all)
97
+ # Only name and category filters are applied, price and brand are ignored
98
+ ```
99
+
100
+ ##### Controller Integration
101
+
102
+ ```ruby
103
+ # URL: /documents?filter[name]=report&filter[status]=published&filter[author]=john
104
+ class DocumentsController < ApplicationController
105
+ def index
106
+ @documents = document_filters.apply(Document.all)
107
+ render json: @documents
108
+ end
109
+
110
+ private
111
+
112
+ def document_filters
113
+ DocumentFilters.new(filter_params)
114
+ end
115
+
116
+ def filter_params
117
+ params[:filter]&.permit(:name, :status, :category, :author)
118
+ end
119
+ end
120
+
121
+ class DocumentFilters < Kiroshi::Filters
122
+ filter_by :name, match: :like
123
+ filter_by :status
124
+ filter_by :category
125
+ filter_by :author, match: :like
126
+ end
127
+ ```
128
+
129
+ ##### Nested Resource Filtering
130
+
131
+ ```ruby
132
+ # URL: /users/123/articles?filter[title]=ruby&filter[published]=true&filter[tag]=tutorial
133
+ class ArticleFilters < Kiroshi::Filters
134
+ filter_by :title, match: :like
135
+ filter_by :published
136
+ filter_by :tag, match: :like
137
+ end
138
+
139
+ # In your controller
140
+ def articles
141
+ base_scope = current_user.articles
142
+ article_filters.apply(base_scope)
143
+ end
144
+
145
+ def article_filters
146
+ ArticleFilters.new(params[:filter]&.permit(:title, :published, :tag))
147
+ end
148
+ ```
149
+
150
+ ##### Joined Tables and Table Qualification
151
+
152
+ When working with joined tables that have columns with the same name, you can specify which table to filter on using the `table` parameter:
153
+
154
+ ```ruby
155
+ class DocumentFilters < Kiroshi::Filters
156
+ filter_by :name, match: :like # Filters by documents.name (default table)
157
+ filter_by :tag_name, match: :like, table: :tags # Filters by tags.name
158
+ filter_by :status # Filters by documents.status
159
+ filter_by :category, table: :documents # Explicitly filter by documents.category
160
+ end
161
+
162
+ # Example with joined scope
163
+ scope = Document.joins(:tags)
164
+ filters = DocumentFilters.new(tag_name: 'ruby', status: 'published')
165
+ filtered_documents = filters.apply(scope)
166
+ # Generates: WHERE tags.name LIKE '%ruby%' AND documents.status = 'published'
167
+ ```
168
+
169
+ ###### Table Qualification Examples
170
+
171
+ ```ruby
172
+ # Filter documents by tag name and document status
173
+ class DocumentTagFilters < Kiroshi::Filters
174
+ filter_by :tag_name, match: :like, table: :tags # Search in tags.name
175
+ filter_by :status, table: :documents # Search in documents.status
176
+ filter_by :title, match: :like # Search in documents.title (default table)
177
+ end
178
+
179
+ scope = Document.joins(:tags)
180
+ filters = DocumentTagFilters.new(tag_name: 'programming', status: 'published', title: 'Ruby')
181
+ result = filters.apply(scope)
182
+ # Generates: WHERE tags.name LIKE '%programming%' AND documents.status = 'published' AND documents.title LIKE '%Ruby%'
183
+
184
+ # Filter by both document and tag attributes with different field names
185
+ class AdvancedDocumentFilters < Kiroshi::Filters
186
+ filter_by :title, match: :like, table: :documents
187
+ filter_by :tag_name, match: :like, table: :tags
188
+ filter_by :category, table: :documents
189
+ filter_by :tag_color, table: :tags
190
+ end
191
+
192
+ scope = Document.joins(:tags)
193
+ filters = AdvancedDocumentFilters.new(
194
+ title: 'Ruby',
195
+ tag_name: 'tutorial',
196
+ category: 'programming',
197
+ tag_color: 'blue'
198
+ )
199
+ result = filters.apply(scope)
200
+ # Generates: WHERE documents.title LIKE '%Ruby%' AND tags.name LIKE '%tutorial%' AND documents.category = 'programming' AND tags.color = 'blue'
201
+ ```
202
+
203
+ The `table` parameter accepts both symbols and strings, and helps resolve column name ambiguity in complex joined queries.
204
+
205
+ ### Kiroshi::Filter
206
+
207
+ [Filter](https://www.rubydoc.info/gems/kiroshi/Kiroshi/Filter)
208
+ is the individual filter class that applies filtering logic to ActiveRecord scopes.
209
+ It's automatically used by `Kiroshi::Filters`, but can also be used standalone.
210
+
211
+ #### Standalone Usage
212
+
213
+ ```ruby
214
+ # Create individual filters
215
+ name_filter = Kiroshi::Filter.new(:name, match: :like)
216
+ status_filter = Kiroshi::Filter.new(:status, match: :exact)
217
+
218
+ # Apply filters manually
219
+ scope = Document.all
220
+ scope = name_filter.apply(scope, { name: 'report' })
221
+ scope = status_filter.apply(scope, { status: 'published' })
222
+ ```
223
+
224
+ #### Filter Options
225
+
226
+ - `match: :exact` - Performs exact matching (default)
227
+ - `match: :like` - Performs partial matching using SQL LIKE
228
+ - `table: :table_name` - Specifies which table to filter on (useful for joined queries)
229
+
230
+ ```ruby
231
+ # Exact match filter
232
+ exact_filter = Kiroshi::Filter.new(:status)
233
+ exact_filter.apply(Document.all, { status: 'published' })
234
+ # Generates: WHERE status = 'published'
235
+
236
+ # LIKE match filter
237
+ like_filter = Kiroshi::Filter.new(:title, match: :like)
238
+ like_filter.apply(Document.all, { title: 'Ruby' })
239
+ # Generates: WHERE title LIKE '%Ruby%'
240
+
241
+ # Table-qualified filter for joined queries
242
+ tag_filter = Kiroshi::Filter.new(:name, match: :like, table: :tags)
243
+ tag_filter.apply(Document.joins(:tags), { name: 'programming' })
244
+ # Generates: WHERE tags.name LIKE '%programming%'
245
+
246
+ # Document-specific filter in joined query
247
+ doc_filter = Kiroshi::Filter.new(:title, match: :exact, table: :documents)
248
+ doc_filter.apply(Document.joins(:tags), { title: 'Ruby Guide' })
249
+ # Generates: WHERE documents.title = 'Ruby Guide'
250
+ ```
251
+
252
+ #### Empty Value Handling
253
+
254
+ Filters automatically ignore empty or nil values:
255
+
256
+ ```ruby
257
+ filter = Kiroshi::Filter.new(:name)
258
+ filter.apply(Document.all, { name: nil }) # Returns original scope
259
+ filter.apply(Document.all, { name: '' }) # Returns original scope
260
+ filter.apply(Document.all, {}) # Returns original scope
261
+ filter.apply(Document.all, { name: 'value' }) # Applies filter
262
+ ```
263
+
264
+ #### Handling Column Name Ambiguity
265
+
266
+ When working with joined tables that have columns with the same name, use the `table` parameter to specify which table's column to filter:
267
+
268
+ ```ruby
269
+ # Without table specification - may cause ambiguity
270
+ scope = Document.joins(:tags) # Both documents and tags have 'name' column
271
+
272
+ # Specify which table to filter on
273
+ name_filter = Kiroshi::Filter.new(:name, match: :like, table: :tags)
274
+ result = name_filter.apply(scope, { name: 'ruby' })
275
+ # Generates: WHERE tags.name LIKE '%ruby%'
276
+
277
+ # Or filter by document name specifically
278
+ doc_name_filter = Kiroshi::Filter.new(:name, match: :like, table: :documents)
279
+ result = doc_name_filter.apply(scope, { name: 'guide' })
280
+ # Generates: WHERE documents.name LIKE '%guide%'
281
+ ```
282
+
283
+ **Priority**: When using `Kiroshi::Filters`, if a filter specifies a `table`, it takes priority over the scope's default table name.
data/config/yardstick.yml CHANGED
@@ -1,4 +1,4 @@
1
- threshold: 100
1
+ threshold: 100.0
2
2
  require_exact_threshold: false
3
3
  rules:
4
4
  ApiTag::Presence:
data/kiroshi.jpg ADDED
Binary file
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kiroshi
4
+ # @author darthjee
5
+ #
6
+ # A filter class that applies filtering logic to ActiveRecord scopes
7
+ #
8
+ # This class provides a flexible way to apply filters to database queries,
9
+ # supporting both exact matches and partial matches using SQL LIKE operations.
10
+ #
11
+ # @example Creating and applying an exact filter
12
+ # filter = Kiroshi::Filter.new(:name)
13
+ # filtered_scope = filter.apply(Document.all, { name: 'John' })
14
+ #
15
+ # @example Creating and applying a LIKE filter
16
+ # filter = Kiroshi::Filter.new(:title, match: :like)
17
+ # filtered_scope = filter.apply(Article.all, { title: 'Ruby' })
18
+ #
19
+ # @since 0.1.0
20
+ class Filter
21
+ attr_reader :attribute, :match, :table_name
22
+
23
+ # @!method attribute
24
+ # @api private
25
+ #
26
+ # Returns the attribute name to filter by
27
+ #
28
+ # @return [Symbol] the attribute name to filter by
29
+
30
+ # @!method match
31
+ # @api private
32
+ #
33
+ # Returns the matching type (+:exact+ or +:like+)
34
+ #
35
+ # @return [Symbol] the matching type (+:exact+ or +:like+)
36
+
37
+ # @!method table_name
38
+ # @api private
39
+ #
40
+ # Returns the table name to qualify the attribute
41
+ #
42
+ # @return [String, String, nil] the table name or nil if not specified
43
+
44
+ # Creates a new Filter instance
45
+ #
46
+ # @param attribute [Symbol] the attribute name to filter by
47
+ # @param match [Symbol] the matching type, defaults to :exact
48
+ # @param table [String, Symbol, nil] the table name to qualify the attribute, defaults to nil
49
+ # @option match [Symbol] :exact performs exact matching (default)
50
+ # @option match [Symbol] :like performs partial matching using SQL LIKE
51
+ #
52
+ # @example Creating an exact match filter
53
+ # filter = Kiroshi::Filter.new(:status)
54
+ #
55
+ # @example Creating a partial match filter
56
+ # filter = Kiroshi::Filter.new(:name, match: :like)
57
+ #
58
+ # @example Creating a filter with table qualification
59
+ # filter = Kiroshi::Filter.new(:name, table: 'documents')
60
+ #
61
+ # @since 0.1.0
62
+ def initialize(attribute, match: :exact, table: nil)
63
+ @attribute = attribute
64
+ @match = match
65
+ @table_name = table
66
+ end
67
+
68
+ # Applies the filter to the given scope
69
+ #
70
+ # This method examines the filters hash for a value corresponding to the
71
+ # filter's attribute and applies the appropriate WHERE clause to the scope.
72
+ # If no value is present or the value is blank, the original scope is returned unchanged.
73
+ #
74
+ # @param scope [ActiveRecord::Relation] the ActiveRecord scope to filter
75
+ # @param filters [Hash] a hash containing filter values
76
+ #
77
+ # @return [ActiveRecord::Relation] the filtered scope
78
+ #
79
+ # @example Applying an exact filter
80
+ # filter = Kiroshi::Filter.new(:status)
81
+ # filter.apply(Document.all, { status: 'published' })
82
+ # # Generates: WHERE status = 'published'
83
+ #
84
+ # @example Applying a LIKE filter
85
+ # filter = Kiroshi::Filter.new(:title, match: :like)
86
+ # filter.apply(Article.all, { title: 'Ruby' })
87
+ # # Generates: WHERE title LIKE '%Ruby%'
88
+ #
89
+ # @example Applying a filter with table qualification
90
+ # filter = Kiroshi::Filter.new(:name, table: 'documents')
91
+ # filter.apply(Document.joins(:tags), { name: 'report' })
92
+ # # Generates: WHERE documents.name = 'report'
93
+ #
94
+ # @example Applying a filter with table qualification for tags
95
+ # filter = Kiroshi::Filter.new(:name, table: 'tags')
96
+ # filter.apply(Document.joins(:tags), { name: 'ruby' })
97
+ # # Generates: WHERE tags.name = 'ruby'
98
+ #
99
+ # @example With empty filter value
100
+ # filter = Kiroshi::Filter.new(:name)
101
+ # filter.apply(User.all, { name: nil })
102
+ # # Returns the original scope unchanged
103
+ #
104
+ # @since 0.1.0
105
+ def apply(scope, filters)
106
+ runner = FilterRunner.new(filter: self, scope: scope, filters: filters)
107
+ runner.apply
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kiroshi
4
+ class FilterQuery
5
+ # @api private
6
+ # @author darthjee
7
+ #
8
+ # Query strategy for exact matching
9
+ #
10
+ # This class implements the exact match query strategy, generating
11
+ # WHERE clauses with exact equality comparisons.
12
+ #
13
+ # @example Applying exact match query
14
+ # query = Kiroshi::FilterQuery::Exact.new(filter_runner)
15
+ # query.apply
16
+ # # Generates: WHERE attribute = 'value'
17
+ #
18
+ # @since 0.1.1
19
+ class Exact < FilterQuery
20
+ # Applies exact match filtering to the scope
21
+ #
22
+ # This method generates a WHERE clause with exact equality matching
23
+ # for the filter's attribute and value.
24
+ #
25
+ # @return [ActiveRecord::Relation] the filtered scope with exact match
26
+ #
27
+ # @example Applying exact match
28
+ # query = Exact.new(filter_runner)
29
+ # query.apply
30
+ # # Generates: WHERE status = 'published'
31
+ #
32
+ # @since 0.1.1
33
+ def apply
34
+ scope.where(table_name => { attribute => filter_value })
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kiroshi
4
+ class FilterQuery
5
+ # @api private
6
+ # @author darthjee
7
+ #
8
+ # Query strategy for LIKE matching
9
+ #
10
+ # This class implements the LIKE match query strategy, generating
11
+ # WHERE clauses with SQL LIKE operations for partial matching.
12
+ #
13
+ # @example Applying LIKE match query
14
+ # query = Kiroshi::FilterQuery::Like.new(filter_runner)
15
+ # query.apply
16
+ # # Generates: WHERE table_name.attribute LIKE '%value%'
17
+ #
18
+ # @since 0.1.1
19
+ class Like < FilterQuery
20
+ # Applies LIKE match filtering to the scope
21
+ #
22
+ # This method generates a WHERE clause with SQL LIKE operation
23
+ # for partial matching, including table name prefix to avoid
24
+ # column ambiguity in complex queries.
25
+ #
26
+ # @return [ActiveRecord::Relation] the filtered scope with LIKE match
27
+ #
28
+ # @example Applying LIKE match
29
+ # query = Like.new(filter_runner)
30
+ # query.apply
31
+ # # Generates: WHERE documents.name LIKE '%ruby%'
32
+ #
33
+ # @since 0.1.1
34
+ def apply
35
+ scope.where(
36
+ "#{table_name}.#{attribute} LIKE ?",
37
+ "%#{filter_value}%"
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kiroshi
4
+ # @api private
5
+ # @author darthjee
6
+ #
7
+ # Factory class for creating filter query strategies
8
+ #
9
+ # This class implements the Strategy pattern for handling different types of
10
+ # database queries based on the filter match type. It provides a factory method
11
+ # to create the appropriate query strategy class.
12
+ #
13
+ # @example Getting an exact match query strategy
14
+ # query = Kiroshi::FilterQuery.for(:exact).new(filter_runner)
15
+ # query.apply
16
+ #
17
+ # @example Getting a LIKE match query strategy
18
+ # query = Kiroshi::FilterQuery.for(:like).new(filter_runner)
19
+ # query.apply
20
+ #
21
+ # @since 0.1.1
22
+ class FilterQuery
23
+ autoload :Exact, 'kiroshi/filter_query/exact'
24
+ autoload :Like, 'kiroshi/filter_query/like'
25
+
26
+ class << self
27
+ # Factory method to create the appropriate query strategy
28
+ #
29
+ # This method returns the correct query strategy class based on the
30
+ # match type provided. It serves as the main entry point for creating
31
+ # query strategies.
32
+ #
33
+ # @param match [Symbol] the type of matching to perform
34
+ # - :exact for exact matching
35
+ # - :like for partial matching using SQL LIKE
36
+ #
37
+ # @return [Class] the appropriate FilterQuery subclass
38
+ #
39
+ # @example Creating an exact match query
40
+ # query_class = Kiroshi::FilterQuery.for(:exact)
41
+ # # Returns Kiroshi::FilterQuery::Exact
42
+ #
43
+ # @example Creating a LIKE match query
44
+ # query_class = Kiroshi::FilterQuery.for(:like)
45
+ # # Returns Kiroshi::FilterQuery::Like
46
+ #
47
+ # @raise [ArgumentError] when an unsupported match type is provided
48
+ #
49
+ # @since 0.1.1
50
+ def for(match)
51
+ case match
52
+ when :exact
53
+ Exact
54
+ when :like
55
+ Like
56
+ else
57
+ raise ArgumentError, "Unsupported match type: #{match}"
58
+ end
59
+ end
60
+ end
61
+
62
+ # Creates a new FilterQuery instance
63
+ #
64
+ # @param filter_runner [Kiroshi::FilterRunner] the filter runner instance
65
+ #
66
+ # @since 0.1.1
67
+ def initialize(filter_runner)
68
+ @filter_runner = filter_runner
69
+ end
70
+
71
+ # Base implementation for applying a filter query
72
+ #
73
+ # This method should be overridden by subclasses to provide specific
74
+ # query logic for each match type.
75
+ #
76
+ # @return [ActiveRecord::Relation] the filtered scope
77
+ #
78
+ # @raise [NotImplementedError] when called on the base class
79
+ #
80
+ # @since 0.1.1
81
+ def apply
82
+ raise NotImplementedError, 'Subclasses must implement #apply method'
83
+ end
84
+
85
+ private
86
+
87
+ attr_reader :filter_runner
88
+
89
+ # @!method filter_runner
90
+ # @api private
91
+ # @private
92
+ #
93
+ # Returns the filter runner instance
94
+ #
95
+ # @return [Kiroshi::FilterRunner] the filter runner instance
96
+
97
+ delegate :scope, :attribute, :table_name, :filter_value, to: :filter_runner
98
+
99
+ # @!method scope
100
+ # @api private
101
+ # @private
102
+ #
103
+ # Returns the ActiveRecord scope being filtered
104
+ #
105
+ # @return [ActiveRecord::Relation] the scope being filtered
106
+
107
+ # @!method attribute
108
+ # @api private
109
+ # @private
110
+ #
111
+ # Returns the attribute name to filter by
112
+ #
113
+ # @return [Symbol] the attribute name to filter by
114
+
115
+ # @!method table_name
116
+ # @api private
117
+ # @private
118
+ #
119
+ # Returns the table name from the scope
120
+ #
121
+ # @return [String] the table name
122
+
123
+ # @!method filter_value
124
+ # @api private
125
+ # @private
126
+ #
127
+ # Returns the filter value for the current filter's attribute
128
+ #
129
+ # @return [Object, nil] the filter value or nil if not present
130
+ end
131
+ end