fetcheable_on_api 0.4 → 0.5.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: d71da3322d2da67e94a8d5753ff04f081ac4e76c05d8afa198f1ebb8b1c2267c
4
- data.tar.gz: 0034ae8441754659242bc72fd900384a3517402baf9ce31de8d01ff348104218
3
+ metadata.gz: 204213e15d42807d8bb56fccb5fc87cdc5ece5915bb01ac9b117cfc111d51fc1
4
+ data.tar.gz: 8bb425ec48b7b84cd8f43444e47f88e7a459fa5f135a54aef36846d99557a0c7
5
5
  SHA512:
6
- metadata.gz: 28db2fe13b6a53cd50ed91bf7ac4c4ba73947676528299c95985cabfadecb174f884d61f15b8832779d65201ce2880149cbff0b7ae2ea8c06acad9f4930f5050
7
- data.tar.gz: ff5e7e8aa569c28e194eaed67f6baeba238e7aa06c69df01983003fa07bd94fd1082ef58d72b7584f5f49ea45ce42ea53af0ab6569a962d9023cd6e743f8d1ad
6
+ metadata.gz: 662357c5bec2bd2ba08717ba1d5eba8f7e3ab8d5e68bcea6a7019eee657e95ebb83845478e83ad8a369f741c7e61a05d5ff61d80f2c85bd5253313031992b66f
7
+ data.tar.gz: c9980e7009bcd25abbc620ac0c57ea716ec5887bb3f981df100e1f2a0ce270701a3d4b150ccb3c6e528a9f908b1f8b12b96119faf6bfa341373bf062da6c3198
@@ -0,0 +1,119 @@
1
+ # Association Sorting Solution
2
+
3
+ ## The Issue
4
+
5
+ You want to sort books by their author's name using:
6
+
7
+ ```ruby
8
+ sort_by :author_name,
9
+ as: :name,
10
+ class_name: User,
11
+ association: :author
12
+ ```
13
+
14
+ ## Current Status
15
+
16
+ The current implementation **should already work** with the following setup:
17
+
18
+ ```ruby
19
+ class BooksController < ApplicationController
20
+ # This configuration should work
21
+ sort_by :author_name, class_name: User, as: 'name'
22
+
23
+ def index
24
+ # THE KEY: Make sure to join the association
25
+ books = apply_fetcheable(Book.joins(:author))
26
+ render json: books
27
+ end
28
+ end
29
+ ```
30
+
31
+ ## Why It Should Work
32
+
33
+ 1. `class_name: User` tells Sortable to use the `users` table
34
+ 2. `as: 'name'` tells it to sort by the `name` column
35
+ 3. `Book.joins(:author)` ensures the `users` table is available in the query
36
+ 4. The result is `ORDER BY users.name ASC/DESC`
37
+
38
+ ## The Missing Piece: Association Option Support
39
+
40
+ To fully implement the `:association` option like in Filterable, we need to:
41
+
42
+ 1. ✅ Add `:association` to valid options in `sort_by` (done)
43
+ 2. ✅ Update documentation with examples (done)
44
+ 3. 🔧 **Optional**: Add association validation logic
45
+
46
+ ## Complete Implementation
47
+
48
+ Here's your controller setup:
49
+
50
+ ```ruby
51
+ class BooksController < ApplicationController
52
+ # Basic sorting
53
+ sort_by :title, :created_at
54
+
55
+ # Association sorting (current working version)
56
+ sort_by :author_name, class_name: User, as: 'name'
57
+
58
+ # Association sorting (with new association option)
59
+ sort_by :author_name, class_name: User, as: 'name', association: :author
60
+
61
+ def index
62
+ # IMPORTANT: Join the association
63
+ books = apply_fetcheable(Book.joins(:author))
64
+ render json: books
65
+ end
66
+ end
67
+ ```
68
+
69
+ ## API Usage
70
+
71
+ ```bash
72
+ # Sort by book title
73
+ GET /books?sort=title
74
+
75
+ # Sort by author name (ascending)
76
+ GET /books?sort=author_name
77
+
78
+ # Sort by author name (descending)
79
+ GET /books?sort=-author_name
80
+
81
+ # Multiple sorts: author name asc, then created_at desc
82
+ GET /books?sort=author_name,-created_at
83
+ ```
84
+
85
+ ## Testing the Implementation
86
+
87
+ The implementation should generate SQL like:
88
+
89
+ ```sql
90
+ SELECT books.*
91
+ FROM books
92
+ INNER JOIN users ON users.id = books.author_id
93
+ ORDER BY users.name ASC
94
+ ```
95
+
96
+ ## If It's Still Not Working
97
+
98
+ Check these common issues:
99
+
100
+ 1. **Missing Join**: Ensure `Book.joins(:author)` is called
101
+ 2. **Wrong Association Name**: Verify the association name matches your model
102
+ 3. **Field Name**: Ensure the `as: 'name'` field exists on the User model
103
+ 4. **Case Sensitivity**: Some databases are case-sensitive
104
+
105
+ ## Debug Steps
106
+
107
+ ```ruby
108
+ def index
109
+ puts "Sorts configuration: #{sorts_configuration}"
110
+
111
+ books = Book.joins(:author)
112
+ puts "Before apply_fetcheable SQL: #{books.to_sql}"
113
+
114
+ books = apply_fetcheable(books)
115
+ puts "After apply_fetcheable SQL: #{books.to_sql}"
116
+
117
+ render json: books
118
+ end
119
+ ```
data/CLAUDE.md ADDED
@@ -0,0 +1,97 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ FetcheableOnApi is a Ruby gem that provides filtering, sorting, and pagination functionality for Rails API controllers following JSONAPI specification. The gem automatically adds query parameter support for `filter`, `sort`, and `page` parameters to controllers.
8
+
9
+ ## Common Development Commands
10
+
11
+ ### Setup
12
+ ```bash
13
+ bin/setup # Install dependencies and setup project
14
+ ```
15
+
16
+ ### Testing
17
+ ```bash
18
+ rake spec # Run all tests (default rake task)
19
+ bundle exec rspec # Run tests with explicit bundler
20
+ ```
21
+
22
+ ### Console
23
+ ```bash
24
+ bin/console # Start IRB console with gem loaded
25
+ ```
26
+
27
+ ### Gem Management
28
+ ```bash
29
+ bundle exec rake install # Install gem locally
30
+ bundle exec rake release # Release new version (updates version, creates git tag, pushes to rubygems)
31
+ ```
32
+
33
+ ## Architecture
34
+
35
+ ### Core Module Structure
36
+
37
+ The gem follows a modular architecture with three main concern modules:
38
+
39
+ 1. **FetcheableOnApi::Filterable** (`lib/fetcheable_on_api/filterable.rb`)
40
+ - Handles `filter[attribute]=value` query parameters
41
+ - Supports 30+ Arel predicates (`:eq`, `:ilike`, `:between`, `:in`, etc.)
42
+ - Supports filtering through associations
43
+ - Supports custom lambda predicates
44
+
45
+ 2. **FetcheableOnApi::Sortable** (`lib/fetcheable_on_api/sortable.rb`)
46
+ - Handles `sort=attribute` query parameters
47
+ - Supports multiple sort fields (comma-separated)
48
+ - Supports ascending/descending with `+`/`-` prefixes
49
+ - Supports sorting through associations
50
+
51
+ 3. **FetcheableOnApi::Pageable** (`lib/fetcheable_on_api/pageable.rb`)
52
+ - Handles `page[number]` and `page[size]` query parameters
53
+ - Adds pagination headers to responses
54
+ - Configurable default page size
55
+
56
+ ### Main Entry Point
57
+
58
+ The main module (`lib/fetcheable_on_api.rb`) includes all three concerns and provides the `apply_fetcheable(collection)` method that controllers use to apply filtering, sorting, and pagination in sequence.
59
+
60
+ ### Controller Integration
61
+
62
+ Controllers gain access to the functionality by including the module (automatically done for ActionController::Base):
63
+
64
+ ```ruby
65
+ class QuestionsController < ActionController::Base
66
+ # Define allowed filters and sorts
67
+ filter_by :content, :category_id
68
+ sort_by :position, :created_at
69
+
70
+ def index
71
+ questions = apply_fetcheable(Question.all)
72
+ render json: questions
73
+ end
74
+ end
75
+ ```
76
+
77
+ ### Configuration
78
+
79
+ Global configuration is handled through `FetcheableOnApi::Configuration`:
80
+ - Default pagination size (default: 25)
81
+ - Configurable via `FetcheableOnApi.configure` block
82
+
83
+ ### Key Design Patterns
84
+
85
+ - **Class Attributes**: Each concern uses `class_attribute` to store configuration per controller
86
+ - **Parameter Validation**: Built-in parameter type validation with helpful error messages
87
+ - **Flexible Predicates**: Support for both built-in Arel predicates and custom lambda predicates
88
+ - **Association Support**: Can filter/sort through ActiveRecord associations
89
+ - **Header Integration**: Pagination info automatically added to response headers
90
+
91
+ ## Rails Integration
92
+
93
+ The gem integrates with Rails through `ActiveSupport.on_load :action_controller` hook, automatically including the module in all ActionController classes.
94
+
95
+ ## Testing Approach
96
+
97
+ Tests are located in `spec/` directory using RSpec. The gem supports testing against multiple Rails versions via gemfiles in `gemfiles/` directory (Rails 4.1 through 5.2 and head).
data/Gemfile.lock CHANGED
@@ -1,44 +1,60 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fetcheable_on_api (0.3.1)
4
+ fetcheable_on_api (0.5.0)
5
5
  activesupport (>= 4.1)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activesupport (6.0.2.1)
10
+ activesupport (7.1.5.1)
11
+ base64
12
+ benchmark (>= 0.3)
13
+ bigdecimal
11
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
- i18n (>= 0.7, < 2)
13
- minitest (~> 5.1)
14
- tzinfo (~> 1.1)
15
- zeitwerk (~> 2.2)
16
- ast (2.4.0)
17
- concurrent-ruby (1.1.5)
18
- diff-lcs (1.3)
19
- i18n (1.7.0)
15
+ connection_pool (>= 2.2.5)
16
+ drb
17
+ i18n (>= 1.6, < 2)
18
+ logger (>= 1.4.2)
19
+ minitest (>= 5.1)
20
+ mutex_m
21
+ securerandom (>= 0.3)
22
+ tzinfo (~> 2.0)
23
+ ast (2.4.3)
24
+ base64 (0.3.0)
25
+ benchmark (0.4.1)
26
+ bigdecimal (3.2.2)
27
+ concurrent-ruby (1.3.5)
28
+ connection_pool (2.5.3)
29
+ diff-lcs (1.6.2)
30
+ drb (2.2.3)
31
+ i18n (1.14.7)
20
32
  concurrent-ruby (~> 1.0)
21
- jaro_winkler (1.5.1)
22
- minitest (5.13.0)
23
- parallel (1.12.1)
24
- parser (2.5.1.2)
25
- ast (~> 2.4.0)
26
- powerpack (0.1.2)
27
- rainbow (3.0.0)
28
- rake (10.5.0)
29
- rspec (3.8.0)
30
- rspec-core (~> 3.8.0)
31
- rspec-expectations (~> 3.8.0)
32
- rspec-mocks (~> 3.8.0)
33
- rspec-core (3.8.0)
34
- rspec-support (~> 3.8.0)
35
- rspec-expectations (3.8.1)
33
+ jaro_winkler (1.5.6)
34
+ logger (1.7.0)
35
+ minitest (5.25.5)
36
+ mutex_m (0.3.0)
37
+ parallel (1.27.0)
38
+ parser (3.3.8.0)
39
+ ast (~> 2.4.1)
40
+ racc
41
+ powerpack (0.1.3)
42
+ racc (1.8.1)
43
+ rainbow (3.1.1)
44
+ rake (13.3.0)
45
+ rspec (3.13.1)
46
+ rspec-core (~> 3.13.0)
47
+ rspec-expectations (~> 3.13.0)
48
+ rspec-mocks (~> 3.13.0)
49
+ rspec-core (3.13.4)
50
+ rspec-support (~> 3.13.0)
51
+ rspec-expectations (3.13.5)
36
52
  diff-lcs (>= 1.2.0, < 2.0)
37
- rspec-support (~> 3.8.0)
38
- rspec-mocks (3.8.0)
53
+ rspec-support (~> 3.13.0)
54
+ rspec-mocks (3.13.5)
39
55
  diff-lcs (>= 1.2.0, < 2.0)
40
- rspec-support (~> 3.8.0)
41
- rspec-support (3.8.0)
56
+ rspec-support (~> 3.13.0)
57
+ rspec-support (3.13.4)
42
58
  rubocop (0.59.2)
43
59
  jaro_winkler (~> 1.5.1)
44
60
  parallel (~> 1.10)
@@ -47,22 +63,21 @@ GEM
47
63
  rainbow (>= 2.2.2, < 4.0)
48
64
  ruby-progressbar (~> 1.7)
49
65
  unicode-display_width (~> 1.0, >= 1.0.1)
50
- ruby-progressbar (1.10.0)
51
- thread_safe (0.3.6)
52
- tzinfo (1.2.6)
53
- thread_safe (~> 0.1)
54
- unicode-display_width (1.4.0)
55
- zeitwerk (2.2.2)
66
+ ruby-progressbar (1.13.0)
67
+ securerandom (0.3.2)
68
+ tzinfo (2.0.6)
69
+ concurrent-ruby (~> 1.0)
70
+ unicode-display_width (1.8.0)
56
71
 
57
72
  PLATFORMS
58
- ruby
73
+ x86_64-linux
59
74
 
60
75
  DEPENDENCIES
61
- bundler (~> 1.16)
76
+ bundler (~> 2.0)
62
77
  fetcheable_on_api!
63
- rake (~> 10.0)
78
+ rake (~> 13.0)
64
79
  rspec (~> 3.0)
65
80
  rubocop (~> 0.59.2)
66
81
 
67
82
  BUNDLED WITH
68
- 1.17.3
83
+ 2.4.22
data/README.md CHANGED
@@ -1,7 +1,71 @@
1
1
 
2
2
  # FetcheableOnApi
3
3
 
4
- FetcheableOnApi allows you to quickly and easily set up a filter system based on the JSONAPI specification for ActiveRecord objects.
4
+ [![Gem Version](https://badge.fury.io/rb/fetcheable_on_api.svg)](https://badge.fury.io/rb/fetcheable_on_api)
5
+ [![Documentation](https://img.shields.io/badge/docs-yard-blue.svg)](https://rubydoc.info/gems/fetcheable_on_api)
6
+
7
+ FetcheableOnApi is a Ruby gem that provides **filtering**, **sorting**, and **pagination** functionality for Rails API controllers following the [JSONAPI specification](https://jsonapi.org/). It allows you to quickly and easily transform query parameters into ActiveRecord scopes without writing repetitive controller code.
8
+
9
+ ## Features
10
+
11
+ - 🔍 **Comprehensive Filtering**: 30+ filter predicates (eq, ilike, between, in, gt, lt, etc.)
12
+ - 📊 **Flexible Sorting**: Multi-field sorting with ascending/descending control
13
+ - 📄 **Built-in Pagination**: Page-based pagination with automatic response headers
14
+ - 🔗 **Association Support**: Filter and sort through model associations
15
+ - 🛡️ **Security**: Built-in parameter validation and SQL injection protection
16
+ - ⚙️ **Configurable**: Customize defaults and behavior per application
17
+ - 🎯 **JSONAPI Compliant**: Follows official JSONAPI specification patterns
18
+
19
+ ## Quick Start
20
+
21
+ Add the gem to your Gemfile and configure your controllers:
22
+
23
+ ```ruby
24
+ class UsersController < ApplicationController
25
+ # Define allowed filters and sorts
26
+ filter_by :name, :email, :status
27
+ sort_by :name, :created_at, :updated_at
28
+
29
+ def index
30
+ users = apply_fetcheable(User.all)
31
+ render json: users
32
+ end
33
+ end
34
+ ```
35
+
36
+ Now your API supports rich query parameters:
37
+
38
+ ```bash
39
+ # Filter users by name and status
40
+ GET /users?filter[name]=john&filter[status]=active
41
+
42
+ # Sort by name ascending, then created_at descending
43
+ GET /users?sort=name,-created_at
44
+
45
+ # Paginate results
46
+ GET /users?page[number]=2&page[size]=25
47
+
48
+ # Combine all features
49
+ GET /users?filter[status]=active&sort=-created_at&page[number]=1&page[size]=10
50
+ ```
51
+
52
+ ## Table of Contents
53
+
54
+ - [Installation](#installation)
55
+ - [Configuration](#configuration)
56
+ - [Usage](#usage)
57
+ - [Basic Filtering](#basic-filtering)
58
+ - [Advanced Filtering](#advanced-filtering)
59
+ - [Sorting](#sorting)
60
+ - [Pagination](#pagination)
61
+ - [Association Filtering and Sorting](#association-filtering-and-sorting)
62
+ - [API Reference](#api-reference)
63
+ - [Filter Predicates](#filter-predicates)
64
+ - [Configuration Options](#configuration-options)
65
+ - [Examples](#examples)
66
+ - [Development](#development)
67
+ - [Contributing](#contributing)
68
+ - [License](#license)
5
69
 
6
70
  ## Installation
7
71
 
@@ -26,8 +90,254 @@ Finally, run the install generator:
26
90
  It will create the following initializer `config/initializers/fetcheable_on_api.rb`.
27
91
  This file contains all the informations about the existing configuration options.
28
92
 
93
+ ## Configuration
94
+
95
+ Configure FetcheableOnApi in `config/initializers/fetcheable_on_api.rb`:
96
+
97
+ ```ruby
98
+ FetcheableOnApi.configure do |config|
99
+ # Default number of records per page (default: 25)
100
+ config.pagination_default_size = 50
101
+ end
102
+ ```
103
+
104
+ ### Available Configuration Options
105
+
106
+ | Option | Description | Default | Example |
107
+ |--------|-------------|---------|---------|
108
+ | `pagination_default_size` | Default page size when not specified | `25` | `50` |
109
+
29
110
  ## Usage
30
111
 
112
+ ### Basic Filtering
113
+
114
+ Configure which attributes can be filtered in your controllers:
115
+
116
+ ```ruby
117
+ class UsersController < ApplicationController
118
+ # Allow filtering by these attributes
119
+ filter_by :name, :email, :status, :created_at
120
+
121
+ def index
122
+ users = apply_fetcheable(User.all)
123
+ render json: users
124
+ end
125
+ end
126
+ ```
127
+
128
+ Examples of filter queries:
129
+
130
+ ```bash
131
+ # Simple text filtering (uses ILIKE by default)
132
+ GET /users?filter[name]=john
133
+
134
+ # Multiple filters (AND logic between different fields)
135
+ GET /users?filter[name]=john&filter[status]=active
136
+
137
+ # Multiple values for same field (OR logic)
138
+ GET /users?filter[status]=active,pending
139
+ ```
140
+
141
+ ### Advanced Filtering
142
+
143
+ Use custom predicates for more specific filtering:
144
+
145
+ ```ruby
146
+ class UsersController < ApplicationController
147
+ filter_by :name # Default: ilike (partial match)
148
+ filter_by :email, with: :eq # Exact match
149
+ filter_by :age, with: :gteq # Greater than or equal
150
+ filter_by :created_at, with: :between, format: :datetime
151
+
152
+ def index
153
+ users = apply_fetcheable(User.all)
154
+ render json: users
155
+ end
156
+ end
157
+ ```
158
+
159
+ ### Sorting
160
+
161
+ Configure sortable attributes:
162
+
163
+ ```ruby
164
+ class UsersController < ApplicationController
165
+ # Allow sorting by these attributes
166
+ sort_by :name, :email, :created_at, :updated_at
167
+
168
+ # Case-insensitive sorting
169
+ sort_by :display_name, lower: true
170
+
171
+ def index
172
+ users = apply_fetcheable(User.all)
173
+ render json: users
174
+ end
175
+ end
176
+ ```
177
+
178
+ Sorting query examples:
179
+
180
+ ```bash
181
+ # Single field ascending
182
+ GET /users?sort=name
183
+
184
+ # Single field descending
185
+ GET /users?sort=-created_at
186
+
187
+ # Multiple fields (priority order)
188
+ GET /users?sort=status,-created_at,name
189
+ ```
190
+
191
+ ### Pagination
192
+
193
+ Pagination is automatically available and follows JSONAPI specification:
194
+
195
+ ```bash
196
+ # Get page 2 with 25 records per page (default size)
197
+ GET /users?page[number]=2
198
+
199
+ # Custom page size
200
+ GET /users?page[number]=1&page[size]=50
201
+
202
+ # Combine with filtering and sorting
203
+ GET /users?filter[status]=active&sort=-created_at&page[number]=2&page[size]=10
204
+ ```
205
+
206
+ Response headers automatically include pagination metadata:
207
+
208
+ ```
209
+ Pagination-Current-Page: 2
210
+ Pagination-Per: 10
211
+ Pagination-Total-Pages: 15
212
+ Pagination-Total-Count: 150
213
+ ```
214
+
215
+ ### Association Filtering and Sorting
216
+
217
+ Filter and sort through model associations:
218
+
219
+ ```ruby
220
+ class PostsController < ApplicationController
221
+ # Filter/sort by post attributes
222
+ filter_by :title, :content, :published
223
+ sort_by :title, :created_at
224
+
225
+ # Filter/sort by author name (User model)
226
+ filter_by :author, class_name: User, as: 'name'
227
+ sort_by :author, class_name: User, as: 'name'
228
+
229
+ # Sort by author name with explicit association (useful when field name differs from association)
230
+ sort_by :author_name, class_name: User, as: 'name', association: :author
231
+
232
+ # Filter by category name
233
+ filter_by :category, class_name: Category, as: 'name'
234
+
235
+ def index
236
+ # Make sure to join the associations
237
+ posts = apply_fetcheable(Post.joins(:author, :category))
238
+ render json: posts
239
+ end
240
+ end
241
+ ```
242
+
243
+ Query examples:
244
+
245
+ ```bash
246
+ # Filter by author name
247
+ GET /posts?filter[author]=john
248
+
249
+ # Sort by author name, then post creation date
250
+ GET /posts?sort=author,-created_at
251
+
252
+ # Sort by author name using explicit field name
253
+ GET /posts?sort=author_name
254
+
255
+ # Complex query with associations
256
+ GET /posts?filter[author]=john&filter[category]=tech&sort=author_name,-created_at
257
+ ```
258
+
259
+ ## API Reference
260
+
261
+ ### Filter Predicates
262
+
263
+ FetcheableOnApi supports 30+ filter predicates for different data types and use cases:
264
+
265
+ #### Text Predicates
266
+ - `:ilike` (default) - Case-insensitive partial match (`ILIKE '%value%'`)
267
+ - `:eq` - Exact match (`= 'value'`)
268
+ - `:matches` - Pattern match with SQL wildcards
269
+ - `:does_not_match` - Inverse pattern match
270
+
271
+ #### Numeric/Date Predicates
272
+ - `:gt` - Greater than
273
+ - `:gteq` - Greater than or equal
274
+ - `:lt` - Less than
275
+ - `:lteq` - Less than or equal
276
+ - `:between` - Between two values (requires array)
277
+
278
+ #### Array Predicates
279
+ - `:in` - Value in list
280
+ - `:not_in` - Value not in list
281
+ - `:in_any` - Any value in list
282
+ - `:in_all` - All values in list
283
+
284
+ #### Custom Predicates
285
+ You can also define custom lambda predicates:
286
+
287
+ ```ruby
288
+ filter_by :full_name, with: ->(collection, value) {
289
+ collection.arel_table[:first_name].matches("%#{value}%").or(
290
+ collection.arel_table[:last_name].matches("%#{value}%")
291
+ )
292
+ }
293
+ ```
294
+
295
+ ## Examples
296
+
297
+ ### Real-world API Controller
298
+
299
+ ```ruby
300
+ class API::V1::UsersController < ApplicationController
301
+ # Basic attribute filters
302
+ filter_by :email, with: :eq
303
+ filter_by :name, :username # Default :ilike predicate
304
+ filter_by :status, with: :in # Allow multiple values
305
+ filter_by :age, with: :gteq # Numeric comparison
306
+ filter_by :created_at, with: :between, format: :datetime
307
+
308
+ # Association filters
309
+ filter_by :company, class_name: Company, as: 'name'
310
+ filter_by :role, class_name: Role, as: 'name'
311
+
312
+ # Sorting configuration
313
+ sort_by :name, :username, :email, :created_at, :updated_at
314
+ sort_by :company, class_name: Company, as: 'name'
315
+ sort_by :display_name, lower: true # Case-insensitive sort
316
+
317
+ def index
318
+ users = apply_fetcheable(
319
+ User.joins(:company, :role)
320
+ .includes(:company, :role)
321
+ )
322
+
323
+ render json: users
324
+ end
325
+ end
326
+ ```
327
+
328
+ ### Example API Requests
329
+
330
+ ```bash
331
+ # Find active users named John from Acme company, sorted by creation date
332
+ GET /api/v1/users?filter[name]=john&filter[status]=active&filter[company]=acme&sort=-created_at
333
+
334
+ # Users created in the last month, paginated
335
+ GET /api/v1/users?filter[created_at]=1640995200,1643673600&page[number]=1&page[size]=20
336
+
337
+ # Search users by partial name, case-insensitive sort
338
+ GET /api/v1/users?filter[name]=john&sort=display_name
339
+ ```
340
+
31
341
  Imagine the following models called question and answer:
32
342
 
33
343
  ```ruby
@@ -505,6 +815,10 @@ $ curl -X GET \
505
815
  ]
506
816
  ```
507
817
 
818
+ By default fetcheable_on_api will join the associated model using the
819
+ `class_name` option you have provided. If another association should be used as
820
+ the target, use the `association:` option instead.
821
+
508
822
  Furthermore you can specify one of the supported `Arel` predicate.
509
823
 
510
824
  ```ruby