lupa 1.0.0 → 1.0.2
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 +5 -5
- data/.github/workflows/ci.yml +56 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +25 -0
- data/Gemfile +2 -0
- data/README.md +176 -81
- data/Rakefile +1 -1
- data/lib/lupa/scope_methods.rb +80 -3
- data/lib/lupa/search.rb +600 -150
- data/lib/lupa/version.rb +1 -1
- data/lib/lupa.rb +128 -3
- data/lupa.gemspec +3 -3
- data/test/default_search_attributes_search_test.rb +22 -0
- data/test/test_helper.rb +7 -0
- metadata +19 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d6c3063e519871ee2848d6202a6388482747e7e2668f04122036e3a5ae8dd1fa
|
|
4
|
+
data.tar.gz: 7cb6850bb59992671ac66d19ef3179bb7c400dae6d39faaa7cbe5bfbdb837643
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4b41e376873605d430296381df17225bf5d168b885857be7a5b395d206b4a7ab4f388473928898003539df96bbb64e6d4fc02c130914c5245b6adf0d3c2034ca
|
|
7
|
+
data.tar.gz: 3003501628ef7c228bf4762bdbaf9f81cb00b2b6e436f75940adfa710e1a2ffd30224b6a3155a828c1fa942a1ca1589ae7e9fbf4056c83dede6b0622381a423e
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ master ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ master ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
ruby-version:
|
|
16
|
+
- '2.2'
|
|
17
|
+
- '2.3'
|
|
18
|
+
- '2.4'
|
|
19
|
+
- '2.5'
|
|
20
|
+
- '2.6'
|
|
21
|
+
- '2.7'
|
|
22
|
+
- '3.0'
|
|
23
|
+
- '3.1'
|
|
24
|
+
- '3.2'
|
|
25
|
+
- '3.3'
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
|
|
30
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
31
|
+
uses: ruby/setup-ruby@v1
|
|
32
|
+
with:
|
|
33
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
34
|
+
bundler-cache: true
|
|
35
|
+
|
|
36
|
+
- name: Run tests
|
|
37
|
+
run: bundle exec rake test
|
|
38
|
+
|
|
39
|
+
- name: Upload coverage to Coveralls
|
|
40
|
+
if: ${{ matrix.ruby-version >= '2.5' }}
|
|
41
|
+
uses: coverallsapp/github-action@v2
|
|
42
|
+
with:
|
|
43
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
44
|
+
flag-name: ruby-${{ matrix.ruby-version }}
|
|
45
|
+
parallel: true
|
|
46
|
+
|
|
47
|
+
coveralls-finish:
|
|
48
|
+
needs: test
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
steps:
|
|
51
|
+
- name: Coveralls Finished
|
|
52
|
+
uses: coverallsapp/github-action@v2
|
|
53
|
+
with:
|
|
54
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
55
|
+
parallel-finished: true
|
|
56
|
+
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## 1.0.2
|
|
2
|
+
|
|
3
|
+
* documentation
|
|
4
|
+
* Add comprehensive RDoc documentation for all classes and methods
|
|
5
|
+
* Include detailed examples and usage patterns in RDoc
|
|
6
|
+
* Document all error classes with practical examples
|
|
7
|
+
* Fix multiple typos in README.md
|
|
8
|
+
* Update Table of Contents links in README.md
|
|
9
|
+
* Add benchmarks section comparing Lupa with HasScope and Searchlight
|
|
10
|
+
* Improve code examples and usage patterns in README.md
|
|
11
|
+
|
|
12
|
+
* ci
|
|
13
|
+
* Migrate from Travis CI to GitHub Actions
|
|
14
|
+
* Add support for Ruby 2.2 through 3.3
|
|
15
|
+
* Configure Coveralls for Ruby 2.5+ (conditional loading for compatibility)
|
|
16
|
+
|
|
17
|
+
* dependencies
|
|
18
|
+
* Update minitest development dependency from ~> 5.5.1 to ~> 5.5
|
|
19
|
+
* Update bundler development dependency from ~> 1.6 to >= 1.6
|
|
20
|
+
* Add rake development dependency >= 10.0
|
|
21
|
+
|
|
22
|
+
## 1.0.1
|
|
23
|
+
|
|
24
|
+
* enhancements
|
|
25
|
+
* A **Lupa::DefaultSearchAttributesError** exception will be raised if `default_search_attributes` does not return a hash.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -2,53 +2,66 @@
|
|
|
2
2
|
|
|
3
3
|
Lupa means *Magnifier* in spanish.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://github.com/edelpero/lupa/actions/workflows/ci.yml) [](https://coveralls.io/r/edelpero/lupa?branch=master) [](https://codeclimate.com/github/edelpero/lupa) [](http://inch-ci.org/github/edelpero/lupa)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Lupa lets you create simple, robust and scaleable search filters with ease using regular Ruby classes and object oriented design patterns.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Lupa is Framework and ORM agnostic. It will work with any ORM or Object that can build a query using **chained method calls**, like ActiveRecord: `
|
|
10
|
+
Product.where(name: 'Digital').where(category: '23').limit(2)`.
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
**Table of Contents:**
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
* [Search Class](#search-class)
|
|
15
|
+
* [Overview](#overview)
|
|
16
|
+
* [Definition](#definition)
|
|
17
|
+
* [Public Methods](#public-methods)
|
|
18
|
+
* [Default Search Scope](#default-search-scope)
|
|
19
|
+
* [Default Search Attributes](#default-search-attributes)
|
|
20
|
+
* [Combining Search Classes](#combining-search-classes)
|
|
21
|
+
* [Usage with Rails](#usage-with-rails)
|
|
22
|
+
* [Testing](#testing)
|
|
23
|
+
* [Testing Default Scope](#testing-default-scope)
|
|
24
|
+
* [Testing Default Search Attributes](#testing-default-search-attributes)
|
|
25
|
+
* [Testing Each Scope Method Individually](#testing-each-scope-method-individually)
|
|
26
|
+
* [Benchmarks](#benchmarks)
|
|
27
|
+
* [Lupa vs HasScope](#lupa-vs-hasscope)
|
|
28
|
+
* [Lupa vs Searchlight](#lupa-vs-searchlight)
|
|
29
|
+
* [Installation](#installation)
|
|
14
30
|
|
|
15
|
-
```haml
|
|
16
|
-
# app/views/products/_search.html.haml
|
|
17
31
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
= date_field_tag 'created_between[start_date]'
|
|
22
|
-
= date_field_tag 'created_between[end_date]'
|
|
23
|
-
= submit_tag :search
|
|
24
|
-
```
|
|
25
|
-
- Create a new instance of your search class and pass a collection to which all search conditions will be applied and specify the search params you want to apply:
|
|
32
|
+
## Search Class
|
|
33
|
+
|
|
34
|
+
### Overview
|
|
26
35
|
|
|
27
36
|
```ruby
|
|
28
|
-
|
|
37
|
+
products = ProductSearch.new(current_user.products).search(name: 'digital', category: '23')
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
protected
|
|
36
|
-
def search_params
|
|
37
|
-
params.permit(:name, :category, created_between: [:start_date, :end_date])
|
|
38
|
-
end
|
|
39
|
+
# Iterate over the search results
|
|
40
|
+
products.each do |product|
|
|
41
|
+
# Your logic goes here
|
|
39
42
|
end
|
|
40
43
|
```
|
|
41
|
-
|
|
44
|
+
Calling **.each** on the instance will build a search by chaining calls to **name** and **category** methods defined in our **ProductSearch::Scope** class.
|
|
42
45
|
|
|
43
|
-
```
|
|
44
|
-
# app/
|
|
46
|
+
```ruby
|
|
47
|
+
# app/searches/product_search.rb
|
|
45
48
|
|
|
46
|
-
|
|
49
|
+
class ProductSearch < Lupa::Search
|
|
50
|
+
# Scope class holds all your search methods.
|
|
51
|
+
class Scope
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
# Search method
|
|
54
|
+
def name
|
|
55
|
+
scope.where('name iLIKE ?', "%#{search_attributes[:name]}%")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Search method
|
|
59
|
+
def category
|
|
60
|
+
scope.where(category_id: search_attributes[:category])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
end
|
|
52
65
|
```
|
|
53
66
|
|
|
54
67
|
### Definition
|
|
@@ -65,7 +78,7 @@ end
|
|
|
65
78
|
Inside your **Scope** class you must define your scope methods. You'll also be able to access to the following methods inside your scope class: **scope** and **search_attributes**.
|
|
66
79
|
|
|
67
80
|
* **`scope:`** returns the current scope when the scope method is called.
|
|
68
|
-
* **`search_attributes:`** returns a hash containing the all search attributes specified.
|
|
81
|
+
* **`search_attributes:`** returns a hash containing the all search attributes specified including the default ones.
|
|
69
82
|
|
|
70
83
|
<u>**Note:**</u> All keys of **`search_attributes`** are symbolized.
|
|
71
84
|
|
|
@@ -75,36 +88,17 @@ Inside your **Scope** class you must define your scope methods. You'll also be a
|
|
|
75
88
|
class ProductSearch < Lupa::Search
|
|
76
89
|
# Scope class holds all your search methods.
|
|
77
90
|
class Scope
|
|
78
|
-
|
|
91
|
+
|
|
79
92
|
# Search method
|
|
80
93
|
def name
|
|
81
|
-
|
|
82
|
-
scope.where('name LIKE ?', "%#{search_attributes[:name]}%")
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Search method
|
|
87
|
-
def created_between
|
|
88
|
-
if created_start_date && created_end_date
|
|
89
|
-
scope.where(created_at: created_start_date..created_end_date)
|
|
90
|
-
end
|
|
94
|
+
scope.where('name LIKE ?', "%#{search_attributes[:name]}%")
|
|
91
95
|
end
|
|
92
|
-
|
|
96
|
+
|
|
93
97
|
# Search method
|
|
94
98
|
def category
|
|
95
|
-
scope.where(category_id: search_attributes[:category])
|
|
99
|
+
scope.where(category_id: search_attributes[:category])
|
|
96
100
|
end
|
|
97
|
-
|
|
98
|
-
private
|
|
99
|
-
# Parses search_attributes[:created_between][:start_date]
|
|
100
|
-
def created_start_date
|
|
101
|
-
search_attributes[:created_between] && search_attributes[:created_between][:start_date].try(:to_date)
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Parses search_attributes[:created_between][:end_date]
|
|
105
|
-
def created_end_date
|
|
106
|
-
search_attributes[:created_between] && search_attributes[:created_between][:end_date].try(:to_date)
|
|
107
|
-
end
|
|
101
|
+
|
|
108
102
|
end
|
|
109
103
|
end
|
|
110
104
|
```
|
|
@@ -154,7 +148,7 @@ search.unexisting_method
|
|
|
154
148
|
# => Lupa::ResultMethodNotImplementedError: The resulting scope does not respond to unexisting_method method.
|
|
155
149
|
```
|
|
156
150
|
|
|
157
|
-
|
|
151
|
+
### Default Search Scope
|
|
158
152
|
|
|
159
153
|
You can define a default search scope if you want to use a search class with an specific resource by overriding the initialize method as follows:
|
|
160
154
|
|
|
@@ -165,9 +159,9 @@ class ProductSearch < Lupa::Search
|
|
|
165
159
|
class Scope
|
|
166
160
|
...
|
|
167
161
|
end
|
|
168
|
-
|
|
162
|
+
|
|
169
163
|
# Be careful not to change the scope variable name,
|
|
170
|
-
# otherwise you will
|
|
164
|
+
# otherwise you will experience issues.
|
|
171
165
|
def initialize(scope = Product.all)
|
|
172
166
|
@scope = scope
|
|
173
167
|
end
|
|
@@ -183,7 +177,7 @@ search.first
|
|
|
183
177
|
# => #<Product id: 1, name: 'Eames Chair', category_id: 23, created_at: "2015-04-06 18:54:13", updated_at: "2015-04-06 18:54:13" >
|
|
184
178
|
```
|
|
185
179
|
|
|
186
|
-
|
|
180
|
+
### Default Search Attributes
|
|
187
181
|
|
|
188
182
|
Defining default search attributes will cause the scope method to be invoked always.
|
|
189
183
|
|
|
@@ -194,23 +188,31 @@ class ProductSearch < Lupa::Search
|
|
|
194
188
|
class Scope
|
|
195
189
|
...
|
|
196
190
|
end
|
|
197
|
-
|
|
191
|
+
|
|
198
192
|
# This should always return a hash
|
|
199
193
|
def default_search_attributes
|
|
200
|
-
{ category: 23 }
|
|
194
|
+
{ category: '23' }
|
|
201
195
|
end
|
|
202
196
|
end
|
|
203
197
|
```
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
search = ProductSearch.new(current_user.products).search(name: 'chair')
|
|
201
|
+
|
|
202
|
+
search.search_attributes
|
|
203
|
+
# => { name: 'chair', category: '23' }
|
|
204
|
+
```
|
|
205
|
+
|
|
204
206
|
**<u>Note:</u>** You can override default search attributes by passing it to the search params.
|
|
205
207
|
|
|
206
208
|
``` ruby
|
|
207
209
|
search = ProductSearch.new(current_user.products).search(name: 'chair', category: '42')
|
|
208
210
|
|
|
209
211
|
search.search_attributes
|
|
210
|
-
# => { name: 'chair', category: 42 }
|
|
212
|
+
# => { name: 'chair', category: '42' }
|
|
211
213
|
```
|
|
212
214
|
|
|
213
|
-
|
|
215
|
+
### Combining Search Classes
|
|
214
216
|
|
|
215
217
|
You can reuse your search class in order to keep them DRY.
|
|
216
218
|
|
|
@@ -221,29 +223,33 @@ A common example is searching records created between two dates. So lets create
|
|
|
221
223
|
|
|
222
224
|
class CreatedAtSearch < Lupa::Search
|
|
223
225
|
class Scope
|
|
224
|
-
|
|
226
|
+
|
|
225
227
|
def created_before
|
|
226
228
|
...
|
|
227
229
|
end
|
|
228
|
-
|
|
230
|
+
|
|
229
231
|
def created_after
|
|
230
232
|
...
|
|
231
233
|
end
|
|
232
|
-
|
|
234
|
+
|
|
233
235
|
def created_between
|
|
234
236
|
if created_start_date && created_end_date
|
|
235
237
|
scope.where(created_at: created_start_date..created_end_date)
|
|
236
238
|
end
|
|
237
239
|
end
|
|
238
|
-
|
|
240
|
+
|
|
239
241
|
private
|
|
240
242
|
|
|
243
|
+
# Parses search_attributes[:created_between][:start_date]
|
|
241
244
|
def created_start_date
|
|
242
|
-
search_attributes[:created_between] &&
|
|
245
|
+
search_attributes[:created_between] &&
|
|
246
|
+
search_attributes[:created_between][:start_date].try(:to_date)
|
|
243
247
|
end
|
|
244
|
-
|
|
248
|
+
|
|
249
|
+
# Parses search_attributes[:created_between][:end_date]
|
|
245
250
|
def created_end_date
|
|
246
|
-
search_attributes[:created_between] &&
|
|
251
|
+
search_attributes[:created_between] &&
|
|
252
|
+
search_attributes[:created_between][:end_date].try(:to_date)
|
|
247
253
|
end
|
|
248
254
|
end
|
|
249
255
|
end
|
|
@@ -256,25 +262,78 @@ Now we can use it in our **ProductSearch** class:
|
|
|
256
262
|
|
|
257
263
|
class ProductSearch < Lupa::Search
|
|
258
264
|
class Scope
|
|
259
|
-
|
|
265
|
+
|
|
260
266
|
def name
|
|
261
267
|
...
|
|
262
268
|
end
|
|
263
|
-
|
|
264
|
-
# We use CreatedAtSearch class to perform the search
|
|
269
|
+
|
|
270
|
+
# We use CreatedAtSearch class to perform the search.
|
|
271
|
+
# Be sure to always call `results` method on your composed
|
|
272
|
+
# search class.
|
|
265
273
|
def created_between
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
274
|
+
CreatedAtSearch.new(scope).
|
|
275
|
+
search(created_between: search_attributes[:created_between]).
|
|
276
|
+
results
|
|
269
277
|
end
|
|
270
|
-
|
|
278
|
+
|
|
271
279
|
def category
|
|
272
280
|
...
|
|
273
281
|
end
|
|
274
|
-
|
|
282
|
+
|
|
275
283
|
end
|
|
276
284
|
end
|
|
277
285
|
```
|
|
286
|
+
**Note:** If you are combining search classes. Be sure to always call **results** method on the search classes composing your main search class.
|
|
287
|
+
|
|
288
|
+
## Usage with Rails
|
|
289
|
+
|
|
290
|
+
### Forms
|
|
291
|
+
|
|
292
|
+
Define a custom form:
|
|
293
|
+
|
|
294
|
+
```haml
|
|
295
|
+
# app/views/products/_search.html.haml
|
|
296
|
+
|
|
297
|
+
= form_tag products_path, method: :get do
|
|
298
|
+
= text_field_tag 'name'
|
|
299
|
+
= select_tag 'category', options_from_collection_for_select(@categories, 'id', 'name')
|
|
300
|
+
= date_field_tag 'created_between[start_date]'
|
|
301
|
+
= date_field_tag 'created_between[end_date]'
|
|
302
|
+
= submit_tag :search
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Controllers
|
|
306
|
+
|
|
307
|
+
Create a new instance of your search class and pass a collection to which all search conditions will be applied and specify the search params you want to apply:
|
|
308
|
+
|
|
309
|
+
```ruby
|
|
310
|
+
# app/controllers/products_controller.rb
|
|
311
|
+
|
|
312
|
+
class ProductsController < ApplicationController
|
|
313
|
+
def index
|
|
314
|
+
@products = ProductSearch.new(current_user.products).search(search_params)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
protected
|
|
318
|
+
def search_params
|
|
319
|
+
params.permit(:name, :category, created_between: [:start_date, :end_date])
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
```
|
|
323
|
+
### Views
|
|
324
|
+
|
|
325
|
+
Loop through the search results on your view.
|
|
326
|
+
|
|
327
|
+
```haml
|
|
328
|
+
# app/views/products/index.html.haml
|
|
329
|
+
|
|
330
|
+
%h1 Products
|
|
331
|
+
|
|
332
|
+
%ul
|
|
333
|
+
- @products.each do |product|
|
|
334
|
+
%li
|
|
335
|
+
= "#{product.name} - #{product.price} - #{product.category}"
|
|
336
|
+
```
|
|
278
337
|
|
|
279
338
|
## Testing
|
|
280
339
|
|
|
@@ -400,6 +459,42 @@ describe ProductSearch do
|
|
|
400
459
|
end
|
|
401
460
|
```
|
|
402
461
|
|
|
462
|
+
## Benchmarks
|
|
463
|
+
|
|
464
|
+
I used [benchmark-ips](https://github.com/evanphx/benchmark-ips).
|
|
465
|
+
|
|
466
|
+
### Lupa vs. [HasScope](https://github.com/plataformatec/has_scope)
|
|
467
|
+
|
|
468
|
+
```
|
|
469
|
+
Calculating -------------------------------------
|
|
470
|
+
lupa 265.000 i/100ms
|
|
471
|
+
has_scope 254.000 i/100ms
|
|
472
|
+
-------------------------------------------------
|
|
473
|
+
lupa 3.526k (±24.7%) i/s - 67.045k
|
|
474
|
+
has_scope 3.252k (±24.8%) i/s - 61.976k
|
|
475
|
+
|
|
476
|
+
Comparison:
|
|
477
|
+
lupa: 3525.8 i/s
|
|
478
|
+
has_scope: 3252.0 i/s - 1.08x slower
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Lupa vs. [Searchlight](https://github.com/nathanl/searchlight)
|
|
482
|
+
|
|
483
|
+
```
|
|
484
|
+
Calculating -------------------------------------
|
|
485
|
+
lupa 480.000 i/100ms
|
|
486
|
+
searchlight 232.000 i/100ms
|
|
487
|
+
-------------------------------------------------
|
|
488
|
+
lupa 7.273k (±25.1%) i/s - 689.280k
|
|
489
|
+
searchlight 2.665k (±14.1%) i/s - 260.072k
|
|
490
|
+
|
|
491
|
+
Comparison:
|
|
492
|
+
lupa: 7273.5 i/s
|
|
493
|
+
searchlight: 2665.4 i/s - 2.73x slower
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
*If you know about another gem that was not included on the benchmark, feel free to run the benchmarks and send a Pull Request.*
|
|
497
|
+
|
|
403
498
|
## Installation
|
|
404
499
|
|
|
405
500
|
Add this line to your application's Gemfile:
|
data/Rakefile
CHANGED
data/lib/lupa/scope_methods.rb
CHANGED
|
@@ -1,13 +1,90 @@
|
|
|
1
1
|
module Lupa
|
|
2
|
+
# Internal module that provides common functionality to Scope classes.
|
|
3
|
+
# This module is automatically included in the Scope class defined within
|
|
4
|
+
# your search class.
|
|
5
|
+
#
|
|
6
|
+
# It provides access to two key attributes:
|
|
7
|
+
# - `scope`: The current scope being searched
|
|
8
|
+
# - `search_attributes`: The hash of search parameters
|
|
9
|
+
#
|
|
10
|
+
# @example Accessing scope and search_attributes in a Scope class
|
|
11
|
+
# class ProductSearch < Lupa::Search
|
|
12
|
+
# class Scope
|
|
13
|
+
# # scope and search_attributes are available here
|
|
14
|
+
# def name
|
|
15
|
+
# # scope is the current ActiveRecord relation (or any chainable object)
|
|
16
|
+
# scope.where('name LIKE ?', "%#{search_attributes[:name]}%")
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# def price_range
|
|
20
|
+
# # search_attributes contains all search parameters
|
|
21
|
+
# if search_attributes[:price_range]
|
|
22
|
+
# scope.where(price: search_attributes[:price_range])
|
|
23
|
+
# else
|
|
24
|
+
# scope
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# @note This module is included automatically by Lupa and should not be
|
|
31
|
+
# included manually in your code.
|
|
32
|
+
#
|
|
33
|
+
# @api private
|
|
34
|
+
# @since 0.1.0
|
|
2
35
|
module ScopeMethods
|
|
3
|
-
|
|
36
|
+
# @!attribute [rw] scope
|
|
37
|
+
# The current scope object that search methods will operate on.
|
|
38
|
+
# This is typically an ActiveRecord::Relation or similar chainable object.
|
|
39
|
+
# The scope is updated after each search method is called.
|
|
40
|
+
#
|
|
41
|
+
# @return [Object] the current scope object
|
|
42
|
+
#
|
|
43
|
+
# @example Accessing the scope in a search method
|
|
44
|
+
# def category
|
|
45
|
+
# # scope is the current state of the query chain
|
|
46
|
+
# scope.where(category_id: search_attributes[:category])
|
|
47
|
+
# end
|
|
4
48
|
attr_accessor :scope
|
|
5
|
-
attr_reader :search_attributes
|
|
6
49
|
|
|
50
|
+
# @!attribute [r] search_attributes
|
|
51
|
+
# A hash containing all search attributes, including default search attributes.
|
|
52
|
+
# All keys are symbolized automatically by Lupa.
|
|
53
|
+
#
|
|
54
|
+
# @return [Hash] the search attributes hash with symbolized keys
|
|
55
|
+
#
|
|
56
|
+
# @example Accessing search attributes in a scope method
|
|
57
|
+
# def search_by_name
|
|
58
|
+
# name = search_attributes[:name]
|
|
59
|
+
# scope.where('name LIKE ?', "%#{name}%") if name.present?
|
|
60
|
+
# end
|
|
61
|
+
#
|
|
62
|
+
# @example With nested hash attributes
|
|
63
|
+
# def created_between
|
|
64
|
+
# start_date = search_attributes[:created_between][:start_date]
|
|
65
|
+
# end_date = search_attributes[:created_between][:end_date]
|
|
66
|
+
# scope.where(created_at: start_date..end_date)
|
|
67
|
+
# end
|
|
68
|
+
attr_reader :search_attributes
|
|
69
|
+
|
|
70
|
+
# Initializes a new Scope instance with the given scope and search attributes.
|
|
71
|
+
# This method is called automatically by Lupa::Search and should not be called directly.
|
|
72
|
+
#
|
|
73
|
+
# @param scope [Object] the initial scope object to search on (e.g., ActiveRecord::Relation)
|
|
74
|
+
# @param search_attributes [Hash] the hash of search parameters with symbolized keys
|
|
75
|
+
#
|
|
76
|
+
# @return [ScopeMethods] the initialized scope instance
|
|
77
|
+
#
|
|
78
|
+
# @example Internal usage (automatically called by Lupa)
|
|
79
|
+
# # This happens internally when you call:
|
|
80
|
+
# ProductSearch.new(Product.all).search(name: 'chair')
|
|
81
|
+
# # Lupa automatically calls:
|
|
82
|
+
# ProductSearch::Scope.new(Product.all, { name: 'chair' })
|
|
83
|
+
#
|
|
84
|
+
# @api private
|
|
7
85
|
def initialize(scope, search_attributes)
|
|
8
86
|
@scope = scope
|
|
9
87
|
@search_attributes = search_attributes
|
|
10
88
|
end
|
|
11
|
-
|
|
12
89
|
end
|
|
13
90
|
end
|