kiroshi 0.2.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff66735688ded970094d5a88a9bcd67ad4f9cac7679fc55ceb75534d9b16d9fc
4
- data.tar.gz: d6da2da4ec294c0d134ceff028e19b30fa5e13ddab2fae2ad0582a5fdfc559aa
3
+ metadata.gz: 5748e560809cbfa28ea2b9d6ad06578ad1f4d0c67038bed6aca1a1978f27aa7c
4
+ data.tar.gz: b15225c3e02cbb634ec4fec69c240cf7bb623e8d89d9fa216c0c5521e98f08a1
5
5
  SHA512:
6
- metadata.gz: 15f142b43b0fcd7fc12f8cd2c1246031aabec104deaf1270030eca5afa56d5776d5c3fec301422c12baec0c531d5366f39832b4afe6ec3d1c83f40ce3e94aea5
7
- data.tar.gz: af94b97459b7a157c6ea30f36c36b937e9a75c25b7081106cf6b6d104b3514687a7da4842e9ac847b7669954bab65f707e8a609d55e3666393f6da09cad6d9c4
6
+ metadata.gz: e7ad5fe225a10041b672270a6f4407985c4be7b9761f9ae2ad5bfcebd45318c3caa03633dc066faf845e5b818fc36e8408e5991abf125d394b29b8af96ade5a3
7
+ data.tar.gz: b7ba3b33bda40558ebc76e9a1cf4c9fd803fb4881e28eac16f38e64f5562ca6748bbdb206a12a416ab19376f619be26d52986339bedd9b929dc33b65fa68a36a
data/.codacy.yml ADDED
@@ -0,0 +1,13 @@
1
+ ---
2
+ engines:
3
+ markdownlint:
4
+ enabled: true
5
+ exclude_paths:
6
+ - 'spec/**/*'
7
+ - 'coverage/**/*'
8
+ configurations:
9
+ MD033:
10
+ allowed_elements:
11
+ - details
12
+ - summary
13
+ MD041: false # Allow HTML in headers
@@ -0,0 +1,6 @@
1
+ {
2
+ "MD033": {
3
+ "allowed_elements": ["details", "summary"]
4
+ },
5
+ "MD041": false
6
+ }
data/README.md CHANGED
@@ -7,16 +7,16 @@
7
7
 
8
8
  ## Yard Documentation
9
9
 
10
- [https://www.rubydoc.info/gems/kiroshi/0.2.0](https://www.rubydoc.info/gems/kiroshi/0.2.0)
10
+ [https://www.rubydoc.info/gems/kiroshi/0.3.0](https://www.rubydoc.info/gems/kiroshi/0.3.0)
11
11
 
12
12
  Kiroshi has been designed to make filtering ActiveRecord queries easier
13
13
  by providing a flexible and reusable filtering system. It allows you to
14
14
  define filter sets that can be applied to any ActiveRecord scope,
15
15
  supporting both exact matches and partial matching using SQL LIKE operations.
16
16
 
17
- Current Release: [0.2.0](https://github.com/darthjee/kiroshi/tree/0.2.0)
17
+ Current Release: [0.3.0](https://github.com/darthjee/kiroshi/tree/0.3.0)
18
18
 
19
- [Next release](https://github.com/darthjee/kiroshi/compare/0.2.0...master)
19
+ [Next release](https://github.com/darthjee/kiroshi/compare/0.3.0...master)
20
20
 
21
21
  ## Installation
22
22
 
@@ -67,6 +67,9 @@ Kiroshi supports two types of matching:
67
67
  - `:exact` - Exact match (default)
68
68
  - `:like` - Partial match using SQL LIKE
69
69
 
70
+ <details>
71
+ <summary>Specifying filter types</summary>
72
+
70
73
  ```ruby
71
74
  class UserFilters < Kiroshi::Filters
72
75
  filter_by :email, match: :like # Partial matching
@@ -78,11 +81,15 @@ filters = UserFilters.new(email: 'admin', role: 'moderator')
78
81
  filtered_users = filters.apply(User.all)
79
82
  # Generates: WHERE email LIKE '%admin%' AND role = 'moderator'
80
83
  ```
84
+ </details>
81
85
 
82
86
  #### Advanced Examples
83
87
 
84
88
  ##### Multiple Filter Types
85
89
 
90
+ <details>
91
+ <summary>Applying only some filters</summary>
92
+
86
93
  ```ruby
87
94
  class ProductFilters < Kiroshi::Filters
88
95
  filter_by :name, match: :like
@@ -96,9 +103,13 @@ filters = ProductFilters.new(name: 'laptop', category: 'electronics')
96
103
  products = filters.apply(Product.all)
97
104
  # Only name and category filters are applied, price and brand are ignored
98
105
  ```
106
+ </details>
99
107
 
100
108
  ##### Controller Integration
101
109
 
110
+ <details>
111
+ <summary>Using filters in Rails controllers</summary>
112
+
102
113
  ```ruby
103
114
  # URL: /documents?filter[name]=report&filter[status]=published&filter[author]=john
104
115
  class DocumentsController < ApplicationController
@@ -125,9 +136,13 @@ class DocumentFilters < Kiroshi::Filters
125
136
  filter_by :author, match: :like
126
137
  end
127
138
  ```
139
+ </details>
128
140
 
129
141
  ##### Nested Resource Filtering
130
142
 
143
+ <details>
144
+ <summary>Filtering nested resources</summary>
145
+
131
146
  ```ruby
132
147
  # URL: /users/123/articles?filter[title]=ruby&filter[published]=true&filter[tag]=tutorial
133
148
  class ArticleFilters < Kiroshi::Filters
@@ -146,9 +161,13 @@ def article_filters
146
161
  ArticleFilters.new(params[:filter]&.permit(:title, :published, :tag))
147
162
  end
148
163
  ```
164
+ </details>
149
165
 
150
166
  ##### Joined Tables and Table Qualification
151
167
 
168
+ <details>
169
+ <summary>Working with joined tables</summary>
170
+
152
171
  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
172
 
154
173
  ```ruby
@@ -165,9 +184,13 @@ filters = DocumentFilters.new(tag_name: 'ruby', status: 'published')
165
184
  filtered_documents = filters.apply(scope)
166
185
  # Generates: WHERE tags.name LIKE '%ruby%' AND documents.status = 'published'
167
186
  ```
187
+ </details>
168
188
 
169
189
  ###### Table Qualification Examples
170
190
 
191
+ <details>
192
+ <summary>Advanced table qualification scenarios</summary>
193
+
171
194
  ```ruby
172
195
  # Filter documents by tag name and document status
173
196
  class DocumentTagFilters < Kiroshi::Filters
@@ -201,9 +224,54 @@ result = filters.apply(scope)
201
224
  ```
202
225
 
203
226
  The `table` parameter accepts both symbols and strings, and helps resolve column name ambiguity in complex joined queries.
227
+ </details>
228
+
229
+ ##### Custom Column Mapping
230
+
231
+ <details>
232
+ <summary>Using different filter keys from database columns</summary>
233
+
234
+ Sometimes you may want to use a different filter key name from the database column name. The `column` parameter allows you to specify which database column to query while keeping a descriptive filter key:
235
+
236
+ ```ruby
237
+ class UserFilters < Kiroshi::Filters
238
+ filter_by :full_name, column: :name, match: :like # Filter key 'full_name' queries 'name' column
239
+ filter_by :user_email, column: :email, match: :like # Filter key 'user_email' queries 'email' column
240
+ filter_by :account_status, column: :status # Filter key 'account_status' queries 'status' column
241
+ end
242
+
243
+ filters = UserFilters.new(full_name: 'John', user_email: 'admin', account_status: 'active')
244
+ result = filters.apply(User.all)
245
+ # Generates: WHERE name LIKE '%John%' AND email LIKE '%admin%' AND status = 'active'
246
+ ```
247
+ </details>
248
+
249
+ ###### Column Mapping with Table Qualification
250
+
251
+ <details>
252
+ <summary>Combining column mapping with table qualification</summary>
253
+
254
+ You can combine `column` and `table` parameters for complex scenarios:
255
+
256
+ ```ruby
257
+ class DocumentFilters < Kiroshi::Filters
258
+ filter_by :author_name, column: :name, table: :users, match: :like # Filter key 'author_name' queries 'users.name'
259
+ filter_by :doc_title, column: :title, table: :documents, match: :like # Filter key 'doc_title' queries 'documents.title'
260
+ filter_by :tag_label, column: :name, table: :tags, match: :like # Filter key 'tag_label' queries 'tags.name'
261
+ end
262
+
263
+ scope = Document.joins(:user, :tags)
264
+ filters = DocumentFilters.new(author_name: 'John', doc_title: 'Ruby', tag_label: 'tutorial')
265
+ result = filters.apply(scope)
266
+ # Generates: WHERE users.name LIKE '%John%' AND documents.title LIKE '%Ruby%' AND tags.name LIKE '%tutorial%'
267
+ ```
268
+
269
+ This feature is particularly useful when:
270
+ - Creating more descriptive filter parameter names for APIs
271
+ - Avoiding naming conflicts between filter keys and existing method names
272
+ - Building user-friendly filter interfaces with intuitive parameter names
273
+ </details>
204
274
 
205
275
  ## API Reference
206
276
 
207
277
  Kiroshi provides a simple, clean API focused on the `Kiroshi::Filters` class. Individual filters are handled internally and don't require direct interaction in most use cases.
208
-
209
- For detailed API documentation, see the [YARD documentation](https://www.rubydoc.info/gems/kiroshi/0.2.0).
@@ -23,14 +23,14 @@ module Kiroshi
23
23
  #
24
24
  # @since 0.1.0
25
25
  class Filter
26
- attr_reader :attribute, :match, :table_name
26
+ attr_reader :filter_key, :match, :table_name
27
27
 
28
- # @!method attribute
28
+ # @!method filter_key
29
29
  # @api private
30
30
  #
31
- # Returns the attribute name to filter by
31
+ # Returns the filter key name to identify this filter
32
32
  #
33
- # @return [Symbol] the attribute name to filter by
33
+ # @return [Symbol] the filter key name to identify this filter
34
34
 
35
35
  # @!method match
36
36
  # @api private
@@ -42,15 +42,16 @@ module Kiroshi
42
42
  # @!method table_name
43
43
  # @api private
44
44
  #
45
- # Returns the table name to qualify the attribute
45
+ # Returns the table name to qualify the column
46
46
  #
47
47
  # @return [String, String, nil] the table name or nil if not specified
48
48
 
49
49
  # Creates a new Filter instance
50
50
  #
51
- # @param attribute [Symbol] the attribute name to filter by
51
+ # @param filter_key [Symbol] the filter key name to identify this filter
52
52
  # @param match [Symbol] the matching type, defaults to :exact
53
- # @param table [String, Symbol, nil] the table name to qualify the attribute, defaults to nil
53
+ # @param table [String, Symbol, nil] the table name to qualify the column, defaults to nil
54
+ # @param column [Symbol] the column name to use in database queries, defaults to filter_key
54
55
  # @option match [Symbol] :exact performs exact matching (default)
55
56
  # @option match [Symbol] :like performs partial matching using SQL LIKE
56
57
  #
@@ -63,11 +64,27 @@ module Kiroshi
63
64
  # @example Creating a filter with table qualification
64
65
  # filter = Kiroshi::Filter.new(:name, table: 'documents')
65
66
  #
66
- # @since 0.1.0
67
- def initialize(attribute, match: :exact, table: nil)
68
- @attribute = attribute
67
+ # @example Creating a filter with custom column name
68
+ # filter = Kiroshi::Filter.new(:user_name, column: :full_name)
69
+ #
70
+ # @since 0.3.0
71
+ def initialize(filter_key, match: :exact, table: nil, column: nil)
72
+ @filter_key = filter_key
69
73
  @match = match
70
74
  @table_name = table
75
+ @column = column
76
+ end
77
+
78
+ # Returns the column name to use in database queries
79
+ #
80
+ # Uses lazy initialization - defaults to filter_key if no column was specified.
81
+ # This allows for flexible column mapping while maintaining backward compatibility.
82
+ #
83
+ # @return [Symbol] the column name to use in database queries
84
+ #
85
+ # @since 0.3.0
86
+ def column
87
+ @column ||= filter_key
71
88
  end
72
89
 
73
90
  # Applies the filter to the given scope
@@ -13,14 +13,14 @@ module Kiroshi
13
13
  # @example Applying exact match query
14
14
  # query = Kiroshi::FilterQuery::Exact.new(filter_runner)
15
15
  # query.apply
16
- # # Generates: WHERE attribute = 'value'
16
+ # # Generates: WHERE table_name.column = value
17
17
  #
18
18
  # @since 0.1.1
19
19
  class Exact < FilterQuery
20
20
  # Applies exact match filtering to the scope
21
21
  #
22
22
  # This method generates a WHERE clause with exact equality matching
23
- # for the filter's attribute and value.
23
+ # for the filter's column and value.
24
24
  #
25
25
  # @return [ActiveRecord::Relation] the filtered scope with exact match
26
26
  #
@@ -31,7 +31,7 @@ module Kiroshi
31
31
  #
32
32
  # @since 0.1.1
33
33
  def apply
34
- scope.where(table_name => { attribute => value })
34
+ scope.where(table_name => { column => value })
35
35
  end
36
36
  end
37
37
  end
@@ -13,7 +13,7 @@ module Kiroshi
13
13
  # @example Applying LIKE match query
14
14
  # query = Kiroshi::FilterQuery::Like.new(filter_runner)
15
15
  # query.apply
16
- # # Generates: WHERE table_name.attribute LIKE '%value%'
16
+ # # Generates: WHERE "table_name"."column" LIKE '%value%'
17
17
  #
18
18
  # @since 0.1.1
19
19
  class Like < FilterQuery
@@ -28,14 +28,38 @@ module Kiroshi
28
28
  # @example Applying LIKE match
29
29
  # query = Like.new(filter_runner)
30
30
  # query.apply
31
- # # Generates: WHERE documents.name LIKE '%ruby%'
31
+ # # Generates: WHERE "documents"."name" LIKE '%ruby%'
32
32
  #
33
33
  # @since 0.1.1
34
34
  def apply
35
- scope.where(
36
- "#{table_name}.#{attribute} LIKE ?",
37
- "%#{value}%"
38
- )
35
+ scope.where(sql_query, "%#{value}%")
36
+ end
37
+
38
+ private
39
+
40
+ # @api private
41
+ # @private
42
+ #
43
+ # Builds the SQL query string for LIKE matching
44
+ #
45
+ # This method constructs the SQL fragment with proper table and column
46
+ # qualification using double quotes to avoid conflicts with reserved words.
47
+ #
48
+ # @return [String] the SQL query fragment for LIKE matching
49
+ #
50
+ # @example Generated SQL fragment
51
+ # sql_query # => ' # Constructs the parameterized SQL query string for column matching
52
+ #
53
+ # @return [String] The SQL query string with placeholders
54
+ # @example
55
+ # sql_query
56
+ # # Returns: '"table_name"."column" LIKE ?''
57
+ #
58
+ # @since 0.3.0
59
+ def sql_query
60
+ <<~SQL.squish
61
+ "#{table_name}"."#{column}" LIKE ?
62
+ SQL
39
63
  end
40
64
  end
41
65
  end
@@ -94,7 +94,7 @@ module Kiroshi
94
94
  #
95
95
  # @return [Kiroshi::FilterRunner] the filter runner instance
96
96
 
97
- delegate :scope, :attribute, :table_name, :value, to: :filter_runner
97
+ delegate :scope, :column, :table_name, :value, to: :filter_runner
98
98
 
99
99
  # @!method scope
100
100
  # @api private
@@ -103,12 +103,12 @@ module Kiroshi
103
103
  #
104
104
  # @return [ActiveRecord::Relation] the scope
105
105
 
106
- # @!method attribute
106
+ # @!method column
107
107
  # @api private
108
108
  #
109
- # Returns the attribute name to filter by
109
+ # Returns the column name to use in database queries
110
110
  #
111
- # @return [Symbol] the attribute name
111
+ # @return [Symbol] the column name
112
112
 
113
113
  # @!method table_name
114
114
  # @api private
@@ -136,16 +136,16 @@ module Kiroshi
136
136
  #
137
137
  # @return [Kiroshi::Filter] the filter configuration
138
138
 
139
- delegate :attribute, to: :filter
139
+ delegate :column, to: :filter
140
140
  delegate :table_name, to: :scope, prefix: true
141
141
  delegate :table_name, to: :filter, prefix: true
142
142
 
143
- # @!method attribute
143
+ # @!method column
144
144
  # @api private
145
145
  #
146
- # Returns the attribute name to filter by
146
+ # Returns the column name to use in database queries
147
147
  #
148
- # @return [Symbol] the attribute name to filter by
148
+ # @return [Symbol] the column name to use in database queries
149
149
 
150
150
  # @!method scope_table_name
151
151
  # @api private
@@ -21,8 +21,8 @@ module Kiroshi
21
21
  # end
22
22
  #
23
23
  # @example Accessing filter configurations
24
- # DocumentFilters.filter_configs.keys # => [:name, :status, :category]
25
- # DocumentFilters.filter_configs[:name].match # => :like
24
+ # DocumentFilters.filter_configs.keys # => ["name", "status", "category"]
25
+ # DocumentFilters.filter_configs["name"].match # => :like
26
26
  #
27
27
  # @since 0.2.0
28
28
  # @author darthjee
@@ -38,37 +38,39 @@ module Kiroshi
38
38
  # options to handle complex database queries with joins and ambiguous
39
39
  # column names.
40
40
  #
41
- # @overload filter_by(attribute, **options)
42
- # @param attribute [Symbol] the attribute name to filter by
41
+ # @overload filter_by(filter_key, **options)
42
+ # @param filter_key [Symbol] the filter key name to identify this filter
43
43
  # @param options [Hash] additional options passed to {Filter#initialize}
44
44
  # @option options [Symbol] :match (:exact) the matching type
45
45
  # - +:exact+ for exact matching (default)
46
46
  # - +:like+ for partial matching using SQL LIKE with wildcards
47
- # @option options [String, Symbol, nil] :table (nil) the table name to qualify the attribute
47
+ # @option options [String, Symbol, nil] :table (nil) the table name to qualify the column
48
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
49
51
  #
50
52
  # @return (see Filters.filter_by)
51
53
  # @example (see Filters.filter_by)
52
54
  # @note (see Filters.filter_by)
53
55
  # @see (see Filters.filter_by)
54
56
  # @since (see Filters.filter_by)
55
- def filter_by(attribute, **)
56
- Filter.new(attribute, **).tap do |filter|
57
- filter_configs[attribute] = filter
57
+ def filter_by(filter_key, **options)
58
+ Filter.new(filter_key, **options).tap do |filter|
59
+ filter_configs[filter_key.to_s] = filter
58
60
  end
59
61
  end
60
62
 
61
63
  # @api private
62
- # Returns the filter configuration for a specific attribute
64
+ # Returns the filter configuration for a specific filter key
63
65
  #
64
66
  # This method provides a convenient way to retrieve a specific filter
65
- # by its attribute name. It's a shorthand for accessing the filter_configs
67
+ # by its filter key name. It's a shorthand for accessing the filter_configs
66
68
  # hash directly and is used internally by the filtering system.
67
69
  #
68
- # @param attribute [Symbol] the attribute name to look up
70
+ # @param filter_key [Symbol, String] the filter key name to look up
69
71
  #
70
- # @return [Filter, nil] the filter instance for the given attribute,
71
- # or nil if no filter is configured for that attribute
72
+ # @return [Filter, nil] the filter instance for the given filter key,
73
+ # or nil if no filter is configured for that filter key
72
74
  #
73
75
  # @example Retrieving a specific filter
74
76
  # class MyFilters < Kiroshi::Filters
@@ -76,16 +78,17 @@ module Kiroshi
76
78
  # filter_by :status
77
79
  # end
78
80
  #
79
- # MyFilters.filter_for(:name) # => #<Kiroshi::Filter:0x... @attribute=:name @match=:like>
80
- # MyFilters.filter_for(:status) # => #<Kiroshi::Filter:0x... @attribute=:status @match=:exact>
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>
81
83
  # MyFilters.filter_for(:unknown) # => nil
82
84
  #
83
85
  # @see .filter_configs for accessing the complete filter registry
84
86
  # @see Filters#apply for how this method is used during filtering
85
87
  #
86
- # @since 0.2.0
87
- def filter_for(attribute)
88
- filter_configs[attribute] || inherited_filter_for(attribute)
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)
89
92
  end
90
93
 
91
94
  private
@@ -96,17 +99,17 @@ module Kiroshi
96
99
  # Searches for a filter in the inheritance chain
97
100
  #
98
101
  # This method looks up the inheritance chain to find a filter configuration
99
- # for the given attribute. It only searches in superclasses that inherit
102
+ # for the given filter key. It only searches in superclasses that inherit
100
103
  # from Kiroshi::Filters, stopping when it reaches a non-Filters class.
101
104
  #
102
- # @param attribute [Symbol] the attribute name to look up
105
+ # @param filter_key_string [String] the filter key name to look up
103
106
  # @return [Filter, nil] the filter instance from a parent class, or nil if not found
104
107
  #
105
- # @since 0.2.0
106
- def inherited_filter_for(attribute)
108
+ # @since 0.3.0
109
+ def inherited_filter_for(filter_key_string)
107
110
  return nil unless superclass < Kiroshi::Filters
108
111
 
109
- superclass.filter_for(attribute)
112
+ superclass.filter_for(filter_key_string)
110
113
  end
111
114
 
112
115
  # @api private
@@ -116,7 +119,7 @@ module Kiroshi
116
119
  #
117
120
  # This method provides access to the internal registry of filters
118
121
  # that have been configured using {.filter_by}. The returned hash
119
- # contains {Filter} instances keyed by their attribute names, allowing
122
+ # contains {Filter} instances keyed by their filter key names, allowing
120
123
  # for efficient O(1) lookup during filter application.
121
124
  #
122
125
  # This method is primarily used internally by {Filters#apply} to
@@ -124,8 +127,8 @@ module Kiroshi
124
127
  # While marked as private API, it may be useful for introspection
125
128
  # and testing purposes.
126
129
  #
127
- # @return [Hash<Symbol, Filter>] hash of {Filter} instances configured
128
- # for this filter class, keyed by attribute name for efficient access
130
+ # @return [Hash<String, Filter>] hash of {Filter} instances configured
131
+ # for this filter class, keyed by filter key name for efficient access
129
132
  #
130
133
  # @example Accessing configured filters for introspection
131
134
  # class MyFilters < Kiroshi::Filters
@@ -135,17 +138,17 @@ module Kiroshi
135
138
  # end
136
139
  #
137
140
  # MyFilters.filter_configs.length # => 3
138
- # MyFilters.filter_configs.keys # => [:name, :status, :category]
139
- # MyFilters.filter_configs[:name].attribute # => :name
140
- # MyFilters.filter_configs[:name].match # => :like
141
- # MyFilters.filter_configs[:status].match # => :exact
142
- # MyFilters.filter_configs[:category].table_name # => :categories
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
143
146
  #
144
147
  # @example Using in tests to verify filter configuration
145
148
  # RSpec.describe ProductFilters do
146
149
  # it 'configures the expected filters' do
147
- # expect(described_class.filter_configs).to have_key(:name)
148
- # expect(described_class.filter_configs[:name].match).to eq(:like)
150
+ # expect(described_class.filter_configs).to have_key("name")
151
+ # expect(described_class.filter_configs["name"].match).to eq(:like)
149
152
  # end
150
153
  # end
151
154
  #
@@ -40,15 +40,17 @@ module Kiroshi
40
40
 
41
41
  extend ClassMethods
42
42
 
43
- # @method self.filter_by(attribute, **options)
43
+ # @method self.filter_by(filter_key, **options)
44
44
  # @api public
45
- # @param attribute [Symbol] the attribute name to filter by
45
+ # @param filter_key [Symbol] the filter key name to identify this filter
46
46
  # @param options [Hash] additional options passed to {Filter#initialize}
47
47
  # @option options [Symbol] :match (:exact) the matching type
48
48
  # - +:exact+ for exact matching (default)
49
49
  # - +:like+ for partial matching using SQL LIKE with wildcards
50
- # @option options [String, Symbol, nil] :table (nil) the table name to qualify the attribute
50
+ # @option options [String, Symbol, nil] :table (nil) the table name to qualify the column
51
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
52
54
  #
53
55
  # @return [Filter] the new filter instance that was created and registered
54
56
  #
@@ -91,6 +93,13 @@ module Kiroshi
91
93
  # filter_by :category_name, table: :categories # Category name via join
92
94
  # end
93
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
+ #
94
103
  # @note When using table qualification, ensure that the specified table
95
104
  # is properly joined in the scope being filtered. The filter will not
96
105
  # automatically add joins - it only qualifies the column name.
@@ -98,12 +107,12 @@ module Kiroshi
98
107
  # @see Filter#initialize for detailed information about filter options
99
108
  # @see Filters#apply for how these filters are used during query execution
100
109
  #
101
- # @since 0.1.0
110
+ # @since 0.3.0
102
111
 
103
112
  # Creates a new Filters instance
104
113
  #
105
114
  # @param filters [Hash] a hash containing the filter values to be applied.
106
- # Keys should correspond to attributes defined with {.filter_by}.
115
+ # Keys should correspond to filter keys defined with {.filter_by}.
107
116
  # Values will be used for filtering. Nil or blank values are ignored.
108
117
  #
109
118
  # @example Creating filters with values
@@ -121,7 +130,7 @@ module Kiroshi
121
130
  #
122
131
  # @since 0.1.0
123
132
  def initialize(filters = {})
124
- @filters = filters || {}
133
+ @filters = filters
125
134
  end
126
135
 
127
136
  # Applies all configured filters to the given scope
@@ -158,8 +167,8 @@ module Kiroshi
158
167
  #
159
168
  # @since 0.2.0
160
169
  def apply(scope)
161
- filters.compact.each do |attribute, value|
162
- filter = self.class.filter_for(attribute)
170
+ filters.compact.each do |filter_key, value|
171
+ filter = self.class.filter_for(filter_key)
163
172
  next unless filter
164
173
 
165
174
  scope = filter.apply(scope: scope, value: value)
@@ -170,14 +179,17 @@ module Kiroshi
170
179
 
171
180
  private
172
181
 
173
- attr_reader :filters
174
-
175
- # @!method filters
176
- # @api private
177
- # @private
182
+ # Returns the hash of filter values to be applied
178
183
  #
179
- # Returns the hash of filter values to be applied
184
+ # Uses lazy initialization to ensure @filters is never nil,
185
+ # defaulting to an empty hash when no filters were provided.
180
186
  #
181
- # @return [Hash] the hash of filter values to be applied
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
182
194
  end
183
195
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kiroshi
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/kiroshi.rb CHANGED
@@ -131,7 +131,6 @@
131
131
  #
132
132
  # @see Filters Base class for creating filter sets
133
133
  # @see https://github.com/darthjee/kiroshi GitHub repository
134
- # @see https://www.rubydoc.info/gems/kiroshi YARD documentation
135
134
  #
136
135
  # @since 0.1.0
137
136
  module Kiroshi
@@ -270,5 +270,45 @@ RSpec.describe Kiroshi::FilterQuery::Exact, type: :model do
270
270
  end
271
271
  end
272
272
  end
273
+
274
+ context 'when Filter#column is different from filter_key' do
275
+ let(:filter) { Kiroshi::Filter.new(:user_name, match: :exact, column: :full_name) }
276
+ let(:filter_value) { 'John Doe' }
277
+
278
+ let!(:matching_document) { create(:document, full_name: 'John Doe') }
279
+ let!(:non_matching_document) { create(:document, full_name: 'Jane Smith') }
280
+
281
+ let(:expected_sql) do
282
+ <<~SQL.squish
283
+ SELECT "documents".* FROM "documents" WHERE "documents"."full_name" = 'John Doe'
284
+ SQL
285
+ end
286
+
287
+ it 'uses the column name instead of filter_key in SQL' do
288
+ expect(query.apply.to_sql).to eq(expected_sql)
289
+ end
290
+
291
+ it 'returns records that match the column value' do
292
+ expect(query.apply).to include(matching_document)
293
+ end
294
+
295
+ it 'does not return records that do not match the column value' do
296
+ expect(query.apply).not_to include(non_matching_document)
297
+ end
298
+
299
+ context 'with table qualification' do
300
+ let(:filter) { Kiroshi::Filter.new(:user_name, match: :exact, table: :documents, column: :full_name) }
301
+
302
+ let(:expected_sql) do
303
+ <<~SQL.squish
304
+ SELECT "documents".* FROM "documents" WHERE "documents"."full_name" = 'John Doe'
305
+ SQL
306
+ end
307
+
308
+ it 'generates SQL with proper table and column qualification' do
309
+ expect(query.apply.to_sql).to eq(expected_sql)
310
+ end
311
+ end
312
+ end
273
313
  end
274
314
  end
@@ -17,7 +17,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
17
17
 
18
18
  let(:expected_sql) do
19
19
  <<~SQL.squish
20
- SELECT "documents".* FROM "documents" WHERE (documents.name LIKE '%test%')
20
+ SELECT "documents".* FROM "documents" WHERE ("documents"."name" LIKE '%test%')
21
21
  SQL
22
22
  end
23
23
 
@@ -47,7 +47,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
47
47
 
48
48
  let(:expected_sql) do
49
49
  <<~SQL.squish
50
- SELECT "documents".* FROM "documents" WHERE (documents.status LIKE '%pub%')
50
+ SELECT "documents".* FROM "documents" WHERE ("documents"."status" LIKE '%pub%')
51
51
  SQL
52
52
  end
53
53
 
@@ -78,7 +78,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
78
78
 
79
79
  let(:expected_sql) do
80
80
  <<~SQL.squish
81
- SELECT "documents".* FROM "documents" WHERE (documents.version LIKE '%1.2%')
81
+ SELECT "documents".* FROM "documents" WHERE ("documents"."version" LIKE '%1.2%')
82
82
  SQL
83
83
  end
84
84
 
@@ -104,7 +104,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
104
104
 
105
105
  let(:expected_sql) do
106
106
  <<~SQL.squish
107
- SELECT "documents".* FROM "documents" WHERE (documents.name LIKE '%nonexistent%')
107
+ SELECT "documents".* FROM "documents" WHERE ("documents"."name" LIKE '%nonexistent%')
108
108
  SQL
109
109
  end
110
110
 
@@ -218,7 +218,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
218
218
 
219
219
  it 'generates SQL with tags table qualification' do
220
220
  result_sql = query.apply.to_sql
221
- expect(result_sql).to include('tags.name LIKE')
221
+ expect(result_sql).to include('"tags"."name" LIKE')
222
222
  end
223
223
 
224
224
  it 'generates SQL with correct LIKE pattern for tag name' do
@@ -245,7 +245,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
245
245
 
246
246
  it 'generates SQL with documents table qualification' do
247
247
  result_sql = query.apply.to_sql
248
- expect(result_sql).to include('documents.name LIKE')
248
+ expect(result_sql).to include('"documents"."name" LIKE')
249
249
  end
250
250
 
251
251
  it 'generates SQL with correct LIKE pattern for document name' do
@@ -263,7 +263,52 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
263
263
 
264
264
  it 'generates SQL with string table qualification' do
265
265
  result_sql = query.apply.to_sql
266
- expect(result_sql).to include('tags.name LIKE')
266
+ expect(result_sql).to include('"tags"."name" LIKE')
267
+ end
268
+ end
269
+ end
270
+
271
+ context 'when Filter#column is different from filter_key' do
272
+ let(:filter) { Kiroshi::Filter.new(:user_name, match: :like, column: :full_name) }
273
+ let(:filter_value) { 'John' }
274
+
275
+ let!(:matching_document) { create(:document, full_name: 'John Doe') }
276
+ let!(:another_match) { create(:document, full_name: 'Johnny Smith') }
277
+ let!(:non_matching_document) { create(:document, full_name: 'Jane Wilson') }
278
+
279
+ let(:expected_sql) do
280
+ <<~SQL.squish
281
+ SELECT "documents".* FROM "documents" WHERE ("documents"."full_name" LIKE '%John%')
282
+ SQL
283
+ end
284
+
285
+ it 'uses the column name instead of filter_key in SQL' do
286
+ expect(query.apply.to_sql).to eq(expected_sql)
287
+ end
288
+
289
+ it 'returns records that partially match the column value' do
290
+ expect(query.apply).to include(matching_document)
291
+ end
292
+
293
+ it 'returns multiple records that contain the column value' do
294
+ expect(query.apply).to include(another_match)
295
+ end
296
+
297
+ it 'does not return records that do not contain the column value' do
298
+ expect(query.apply).not_to include(non_matching_document)
299
+ end
300
+
301
+ context 'with table qualification' do
302
+ let(:filter) { Kiroshi::Filter.new(:user_name, match: :like, table: :documents, column: :full_name) }
303
+
304
+ let(:expected_sql) do
305
+ <<~SQL.squish
306
+ SELECT "documents".* FROM "documents" WHERE ("documents"."full_name" LIKE '%John%')
307
+ SQL
308
+ end
309
+
310
+ it 'generates SQL with proper table and column qualification' do
311
+ expect(query.apply.to_sql).to eq(expected_sql)
267
312
  end
268
313
  end
269
314
  end
@@ -39,7 +39,7 @@ RSpec.describe Kiroshi::FilterRunner, type: :model do
39
39
  end
40
40
 
41
41
  it 'generates correct SQL with table name prefix' do
42
- expected_sql = "SELECT \"documents\".* FROM \"documents\" WHERE (documents.name LIKE '%test%')"
42
+ expected_sql = "SELECT \"documents\".* FROM \"documents\" WHERE (\"documents\".\"name\" LIKE '%test%')"
43
43
  expect(runner.apply.to_sql).to eq(expected_sql)
44
44
  end
45
45
  end
@@ -90,5 +90,46 @@ RSpec.describe Kiroshi::FilterRunner, type: :model do
90
90
  expect(runner.apply).not_to include(non_matching_document)
91
91
  end
92
92
  end
93
+
94
+ context 'when Filter#column is different from filter_key' do
95
+ let(:filter) { Kiroshi::Filter.new(:user_name, match: :exact, column: :full_name) }
96
+ let(:filter_value) { 'John Doe' }
97
+
98
+ let!(:matching_document) { create(:document, full_name: 'John Doe') }
99
+ let!(:non_matching_document) { create(:document, full_name: 'Jane Smith') }
100
+
101
+ it 'filters using the column name instead of filter_key' do
102
+ expect(runner.apply).to include(matching_document)
103
+ end
104
+
105
+ it 'does not return non-matching records' do
106
+ expect(runner.apply).not_to include(non_matching_document)
107
+ end
108
+
109
+ it 'generates correct SQL using the column name' do
110
+ expected_sql = "SELECT \"documents\".* FROM \"documents\" WHERE \"documents\".\"full_name\" = 'John Doe'"
111
+ expect(runner.apply.to_sql).to eq(expected_sql)
112
+ end
113
+
114
+ context 'with LIKE match' do
115
+ let(:filter) { Kiroshi::Filter.new(:user_name, match: :like, column: :full_name) }
116
+ let(:filter_value) { 'John' }
117
+
118
+ let!(:partial_match) { create(:document, full_name: 'Johnny Smith') }
119
+
120
+ it 'performs LIKE filtering using the column name' do
121
+ expect(runner.apply).to include(matching_document)
122
+ end
123
+
124
+ it 'includes partial matches using the column name' do
125
+ expect(runner.apply).to include(partial_match)
126
+ end
127
+
128
+ it 'generates correct LIKE SQL using the column name' do
129
+ expected_sql = "SELECT \"documents\".* FROM \"documents\" WHERE (\"documents\".\"full_name\" LIKE '%John%')"
130
+ expect(runner.apply.to_sql).to eq(expected_sql)
131
+ end
132
+ end
133
+ end
93
134
  end
94
135
  end
@@ -52,7 +52,51 @@ RSpec.describe Kiroshi::Filters::ClassMethods, type: :model do
52
52
  it do
53
53
  expect { filters_class.filter_by :name, match: :like, table: :documents }
54
54
  .to change { filter_instance.apply(scope) }
55
- .from(scope).to(scope.where('documents.name LIKE ?', '%test%'))
55
+ .from(scope).to(scope.where('"documents"."name" LIKE ?', '%test%'))
56
+ end
57
+ end
58
+
59
+ context 'when column is different from filter_key' do
60
+ let(:filters) { { user_name: 'John Doe' } }
61
+
62
+ context 'with exact match' do
63
+ it do
64
+ expect { filters_class.filter_by :user_name, column: :full_name }
65
+ .to change { filter_instance.apply(scope) }
66
+ .from(scope).to(scope.where(full_name: 'John Doe'))
67
+ end
68
+ end
69
+
70
+ context 'with like match' do
71
+ let(:filters) { { user_name: 'John' } }
72
+
73
+ it do
74
+ expect { filters_class.filter_by :user_name, match: :like, column: :full_name }
75
+ .to change { filter_instance.apply(scope) }
76
+ .from(scope).to(scope.where('"documents"."full_name" LIKE ?', '%John%'))
77
+ end
78
+ end
79
+
80
+ context 'with table qualification and different column' do
81
+ let(:scope) { Document.joins(:tags) }
82
+ let(:filters) { { tag_identifier: 'ruby' } }
83
+
84
+ it do
85
+ expect { filters_class.filter_by :tag_identifier, table: :tags, column: :name }
86
+ .to change { filter_instance.apply(scope) }
87
+ .from(scope).to(scope.where(tags: { name: 'ruby' }))
88
+ end
89
+ end
90
+
91
+ context 'with like match, table qualification and different column' do
92
+ let(:scope) { Document.joins(:tags) }
93
+ let(:filters) { { tag_identifier: 'rub' } }
94
+
95
+ it do
96
+ expect { filters_class.filter_by :tag_identifier, match: :like, table: :tags, column: :name }
97
+ .to change { filter_instance.apply(scope) }
98
+ .from(scope).to(scope.where('"tags"."name" LIKE ?', '%rub%'))
99
+ end
56
100
  end
57
101
  end
58
102
  end
@@ -91,6 +91,142 @@ RSpec.describe Kiroshi::Filters, type: :model do
91
91
  end
92
92
  end
93
93
 
94
+ context 'when filters have string keys' do
95
+ before do
96
+ filters_class.filter_by :name, match: :like
97
+ filters_class.filter_by :status
98
+ end
99
+
100
+ context 'with single string key filter' do
101
+ let(:filters) { { 'name' => 'test' } }
102
+
103
+ it 'returns documents matching the string key filter' do
104
+ expect(filter_instance.apply(scope)).to include(document)
105
+ end
106
+
107
+ it 'does not return documents not matching the string key filter' do
108
+ expect(filter_instance.apply(scope)).not_to include(other_document)
109
+ end
110
+
111
+ it 'generates SQL with LIKE operation for string key' do
112
+ expect(filter_instance.apply(scope).to_sql).to include('LIKE')
113
+ end
114
+ end
115
+
116
+ context 'with multiple string key filters' do
117
+ let(:filters) { { 'name' => 'test', 'status' => 'finished' } }
118
+
119
+ it 'returns documents matching all string key filters' do
120
+ expect(filter_instance.apply(scope)).to include(document)
121
+ end
122
+
123
+ it 'does not return documents not matching all string key filters' do
124
+ expect(filter_instance.apply(scope)).not_to include(other_document)
125
+ end
126
+ end
127
+
128
+ context 'with mixed string and symbol keys' do
129
+ let(:filters) { { 'name' => 'test', status: 'finished' } }
130
+
131
+ it 'returns documents matching both string and symbol key filters' do
132
+ expect(filter_instance.apply(scope)).to include(document)
133
+ end
134
+
135
+ it 'does not return documents not matching all mixed key filters' do
136
+ expect(filter_instance.apply(scope)).not_to include(other_document)
137
+ end
138
+
139
+ it 'treats string and symbol keys equivalently' do
140
+ string_result = filters_class.new({ 'name' => 'test', 'status' => 'finished' }).apply(scope)
141
+ symbol_result = filters_class.new({ name: 'test', status: 'finished' }).apply(scope)
142
+
143
+ expect(string_result.to_sql).to eq(symbol_result.to_sql)
144
+ end
145
+ end
146
+ end
147
+
148
+ context 'when filters is an instance of ActionController::Parameters' do
149
+ before do
150
+ filters_class.filter_by :name, match: :like
151
+ filters_class.filter_by :status
152
+ end
153
+
154
+ context 'with permitted parameters' do
155
+ let(:filters) do
156
+ ActionController::Parameters.new(
157
+ name: 'test',
158
+ status: 'finished',
159
+ unauthorized_param: 'ignored'
160
+ )
161
+ end
162
+
163
+ it 'returns documents matching the permitted parameters' do
164
+ expect(filter_instance.apply(scope)).to include(document)
165
+ end
166
+
167
+ it 'does not return documents not matching the permitted parameters' do
168
+ expect(filter_instance.apply(scope)).not_to include(other_document)
169
+ end
170
+
171
+ it 'generates SQL with LIKE operation for ActionController::Parameters' do
172
+ expect(filter_instance.apply(scope).to_sql).to include('LIKE')
173
+ end
174
+
175
+ it 'generates SQL with exact match for status parameter' do
176
+ expect(filter_instance.apply(scope).to_sql).to include("'finished'")
177
+ end
178
+ end
179
+
180
+ context 'with unpermitted parameters' do
181
+ let(:filters) do
182
+ ActionController::Parameters.new(
183
+ name: 'test',
184
+ status: 'finished'
185
+ )
186
+ end
187
+
188
+ it 'works with unpermitted parameters' do
189
+ expect(filter_instance.apply(scope)).to include(document)
190
+ end
191
+
192
+ it 'does not return documents not matching the parameters' do
193
+ expect(filter_instance.apply(scope)).not_to include(other_document)
194
+ end
195
+ end
196
+
197
+ context 'with string keys in ActionController::Parameters' do
198
+ let(:filters) do
199
+ ActionController::Parameters.new(
200
+ 'name' => 'test',
201
+ 'status' => 'finished'
202
+ )
203
+ end
204
+
205
+ it 'returns documents matching the string key parameters' do
206
+ expect(filter_instance.apply(scope)).to include(document)
207
+ end
208
+
209
+ it 'does not return documents not matching the string key parameters' do
210
+ expect(filter_instance.apply(scope)).not_to include(other_document)
211
+ end
212
+
213
+ it 'treats ActionController::Parameters with string keys same as regular hash' do
214
+ ac_params_result = filter_instance.apply(scope)
215
+ hash_result = filters_class.new({ 'name' => 'test', 'status' => 'finished' }).apply(scope)
216
+
217
+ expect(ac_params_result.to_sql).to eq(hash_result.to_sql)
218
+ end
219
+ end
220
+
221
+ context 'with empty ActionController::Parameters' do
222
+ let(:filters) { ActionController::Parameters.new({}) }
223
+
224
+ it 'returns the original scope unchanged' do
225
+ expect(filter_instance.apply(scope)).to eq(scope)
226
+ end
227
+ end
228
+ end
229
+
94
230
  context 'when scope has joined tables with clashing fields' do
95
231
  let(:scope) { Document.joins(:tags) }
96
232
  let(:filters) { { name: 'test_name' } }
@@ -143,7 +279,7 @@ RSpec.describe Kiroshi::Filters, type: :model do
143
279
 
144
280
  it 'generates SQL with table-qualified LIKE operation' do
145
281
  result = filter_instance.apply(scope)
146
- expect(result.to_sql).to include('documents.name LIKE')
282
+ expect(result.to_sql).to include('"documents"."name" LIKE')
147
283
  end
148
284
 
149
285
  it 'generates SQL with correct LIKE pattern' do
@@ -153,6 +289,80 @@ RSpec.describe Kiroshi::Filters, type: :model do
153
289
  end
154
290
  end
155
291
 
292
+ context 'when specifying a different column' do
293
+ let(:scope) { Document.joins(:tags) }
294
+ let(:filters) { { tag_name: 'ruby' } }
295
+
296
+ let!(:ruby_tag) { Tag.find_or_create_by(name: 'ruby') }
297
+ let!(:js_tag) { Tag.find_or_create_by(name: 'javascript') }
298
+
299
+ before do
300
+ filters_class.filter_by :tag_name, table: :tags, column: :name
301
+
302
+ document.tags << [ruby_tag]
303
+ other_document.tags << [js_tag]
304
+ end
305
+
306
+ it 'filters by the specified column name instead of filter key' do
307
+ expect(filter_instance.apply(scope)).to include(document)
308
+ end
309
+
310
+ it 'does not return documents not matching the column filter' do
311
+ expect(filter_instance.apply(scope)).not_to include(other_document)
312
+ end
313
+
314
+ it 'generates SQL that filters by tags.name using the column parameter' do
315
+ expect(filter_instance.apply(scope).to_sql).to include('"tags"."name"')
316
+ end
317
+
318
+ it 'generates SQL that includes the filter value' do
319
+ expect(filter_instance.apply(scope).to_sql).to include("'ruby'")
320
+ end
321
+
322
+ it 'does not use the filter key name in the SQL' do
323
+ # The filter key is :tag_name but column is :name, so SQL should use 'name' not 'tag_name'
324
+ expect(filter_instance.apply(scope).to_sql).not_to include('tag_name')
325
+ end
326
+
327
+ context 'with LIKE matching' do
328
+ let(:filters) { { tag_name: 'rub' } }
329
+
330
+ before do
331
+ filters_class.filter_by :tag_name, table: :tags, column: :name, match: :like
332
+ end
333
+
334
+ it 'applies LIKE matching to the specified column' do
335
+ expect(filter_instance.apply(scope)).to include(document)
336
+ end
337
+
338
+ it 'generates SQL with LIKE operation on the specified column' do
339
+ expect(filter_instance.apply(scope).to_sql).to include('"tags"."name" LIKE')
340
+ end
341
+
342
+ it 'generates SQL with correct LIKE pattern' do
343
+ expect(filter_instance.apply(scope).to_sql).to include("'%rub%'")
344
+ end
345
+ end
346
+
347
+ context 'with different filter key and column names' do
348
+ let(:filters) { { user_full_name: 'test' } }
349
+
350
+ before do
351
+ filters_class.filter_by :user_full_name, column: :name, match: :like
352
+ end
353
+
354
+ it 'uses the column name in database queries' do
355
+ result = filter_instance.apply(Document.all)
356
+ expect(result.to_sql).to include('"documents"."name"')
357
+ end
358
+
359
+ it 'does not use the filter key in SQL' do
360
+ result = filter_instance.apply(Document.all)
361
+ expect(result.to_sql).not_to include('user_full_name')
362
+ end
363
+ end
364
+ end
365
+
156
366
  context 'when filter was defined in the superclass' do
157
367
  subject(:filters_class) { Class.new(parent_class) }
158
368
 
data/spec/spec_helper.rb CHANGED
@@ -11,6 +11,7 @@ SimpleCov.start 'gem'
11
11
  require 'kiroshi'
12
12
  require 'pry-nav'
13
13
  require 'active_support/all'
14
+ require 'action_controller/metal/strong_parameters'
14
15
  require 'factory_bot'
15
16
 
16
17
  require 'active_record'
@@ -5,6 +5,7 @@ ActiveRecord::Schema.define do
5
5
 
6
6
  create_table :documents, force: true do |t|
7
7
  t.string :name
8
+ t.string :full_name
8
9
  t.string :status
9
10
  t.boolean :active
10
11
  t.integer :priority
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kiroshi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Darthjee
@@ -46,7 +46,9 @@ extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
48
  - ".circleci/config.yml"
49
+ - ".codacy.yml"
49
50
  - ".gitignore"
51
+ - ".markdownlint.json"
50
52
  - ".rspec"
51
53
  - ".rubocop.yml"
52
54
  - ".rubocop_todo.yml"