paraphrase 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -10
- data/README.md +177 -117
- data/gemfiles/3.1.gemfile.lock +1 -1
- data/gemfiles/3.2.gemfile.lock +1 -1
- data/gemfiles/4.0.gemfile.lock +1 -1
- data/gemfiles/4.1.gemfile.lock +1 -1
- data/lib/paraphrase/mapping.rb +0 -1
- data/lib/paraphrase/params_filter.rb +19 -7
- data/lib/paraphrase/query.rb +12 -12
- data/lib/paraphrase/repository.rb +6 -0
- data/lib/paraphrase/version.rb +1 -1
- data/spec/paraphrase/query_spec.rb +28 -8
- data/spec/spec_helper.rb +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1edd35702f438a125a18ad4ad3dd8fc4933c10a
|
4
|
+
data.tar.gz: f31827e436da49e098499f602d0225428492f73c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3901ba0d17fe8e7ec907c92bc51028f35385c2cd8c9d024c952e1a9d644e7685ed0e8d4f0578b146e952292fbe07364bdf3c3a71e52ac920ed3271626c6c8988
|
7
|
+
data.tar.gz: 2114539257bc4b5f2bfb3f55d85625530ec8aeb6d71959ec1ad3fc6a43ffb56ad9905827bd3fe35f22519439914bb33c7d05233893ab3af6159719965795fbf4
|
data/CHANGELOG.md
CHANGED
@@ -1,14 +1,40 @@
|
|
1
|
-
##
|
2
|
-
|
3
|
-
*
|
4
|
-
|
5
|
-
|
1
|
+
## 0.11.0 / 9-17-2014
|
2
|
+
|
3
|
+
* Enable setting default values in param processors. The following will now
|
4
|
+
work:
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
class PostQuery < Paraphrase::Query
|
8
|
+
map :sort, to: :sorted_by
|
9
|
+
|
10
|
+
param :sort do
|
11
|
+
params[:sort].presence || "newest"
|
12
|
+
end
|
13
|
+
|
14
|
+
scope :sorted_by do |sort_direction|
|
15
|
+
sort_direction == "newest" ? relation.order(created_at: :desc) : relation.order(:created_at)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
```
|
19
|
+
|
20
|
+
## 0.10.0 / 7-3-2014
|
21
|
+
|
22
|
+
* Change `Paraphrase::Query.source` to be a regular class attribute, removing
|
23
|
+
the DSL method `source` for defining the source.
|
24
|
+
* Add convenience class-level API for pre-processing query params.
|
25
|
+
* Rename `Params` to `ParamsFilter`. Always define a `ParamsFilter` subclass
|
26
|
+
for each `Paraphrase::Query` subclass on inheritance.
|
27
|
+
* Make params filtering consistent. Run custom method defiend on `ParamsFilter`
|
28
|
+
and then call `scrub` on the return value. Previously, `scrub` would not be
|
29
|
+
called if a custom method was defined.
|
30
|
+
* Rename `Scope` to the more appropriate `Mapping`.
|
6
31
|
* Mark `Mapping` and `ActiveModel` classes as private API
|
7
|
-
* Add
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
32
|
+
* Add `Paraphrase::Repository` for defining model scopes in a
|
33
|
+
`Paraphrase::Query` subclass. Scopes can be defined by re-opening the
|
34
|
+
`Repository` class available in any `Paraphrase::Query` subclass or using
|
35
|
+
`Query.scope` DSL. See README for more.
|
36
|
+
* Require `Paraphrase::Query` be initialized with an instance of
|
37
|
+
`ActiveRecord::Relation`. Update `Paraphrase::Syntax`
|
12
38
|
|
13
39
|
## 0.9.0 / 5-2-2014
|
14
40
|
|
data/README.md
CHANGED
@@ -3,125 +3,102 @@
|
|
3
3
|
[](https://codeclimate.com/github/ecbypi/paraphrase)
|
4
4
|
[](https://travis-ci.org/ecbypi/paraphrase)
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
`paraphrase` provides a way to map query params to model scopes and only apply
|
7
|
+
scopes when the mapped query params are present, removing all the conditional
|
8
|
+
checks you might perform in your controller to determine if a scope needs to be
|
9
|
+
applied.
|
8
10
|
|
9
|
-
|
11
|
+
With `paraphrase`, you can also de-clutter your model by removing
|
12
|
+
context-specific scopes into the query builder.
|
10
13
|
|
11
|
-
|
14
|
+
Take the following example:
|
12
15
|
|
13
|
-
```
|
14
|
-
|
15
|
-
|
16
|
+
```ruby
|
17
|
+
class PostsController < ActiveRecord::Base
|
18
|
+
def index
|
19
|
+
@posts = Post.all
|
16
20
|
|
17
|
-
|
21
|
+
names = params[:names]
|
18
22
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
## Usage
|
23
|
+
if names && names.delete_if { |name| name.blank? }.present?
|
24
|
+
@posts = @posts.published_by(names)
|
25
|
+
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
+
start_date = Time.zone.parse(params[:start_date])
|
28
|
+
end_date = Time.zone.parse(params[:end_date])
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
map :start_date, :end_date, to: :published_within
|
30
|
+
if start_date && end_date
|
31
|
+
@posts = @posts.published_within(start_date, end_date)
|
32
|
+
end
|
33
|
+
end
|
33
34
|
end
|
34
|
-
```
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
class Post < ActiveRecord::Base
|
37
|
+
def self.published_by(names)
|
38
|
+
joins(:user).where(users: { name: names })
|
39
|
+
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
# This needs the source specific since it will look for an `AdminPost` model.
|
45
|
-
self.source = :Post
|
41
|
+
def self.published_within(start_date, end_date)
|
42
|
+
where(published_at: start_date..end_date)
|
43
|
+
end
|
46
44
|
end
|
47
45
|
```
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
```ruby
|
53
|
-
# Based on the example `PostQuery` above, this will only apply `Post.by_user`
|
54
|
-
# and skip `Post.published_within` since `:end_date` is missing.
|
55
|
-
Post.paraphrase(author: 'Jim')
|
56
|
-
```
|
47
|
+
As the number of options for the query grows, the `index` method will continue
|
48
|
+
to accrue with conditional checks and the model will become bloated with that
|
49
|
+
are might only used in the controller.
|
57
50
|
|
58
|
-
|
51
|
+
By using paraphrase, the controller and model can be simplified to:
|
59
52
|
|
60
53
|
```ruby
|
61
|
-
class PostsController <
|
62
|
-
respond_to :html, :json
|
63
|
-
|
54
|
+
class PostsController < ActiveRecord::Base
|
64
55
|
def index
|
65
|
-
# Will filter out keys such as `:action` and `:controller`
|
66
56
|
@posts = Post.paraphrase(params)
|
67
|
-
respond_with(@posts)
|
68
57
|
end
|
69
58
|
end
|
70
|
-
```
|
71
59
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
after removing empty strings, the scope will not be called since an empty array
|
76
|
-
is considered a blank value.
|
60
|
+
class PostQuery < Paraphrase::Query
|
61
|
+
map :names, to: :published_by
|
62
|
+
map :start_date, :end_date, to: :published_within
|
77
63
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
64
|
+
param :start_date do
|
65
|
+
Time.zone.parse(params[:start_date]) rescue nil
|
66
|
+
end
|
82
67
|
|
83
|
-
|
84
|
-
|
85
|
-
where(name: names)
|
68
|
+
param :end_date do
|
69
|
+
Time.zone.parse(params[:end_date]) rescue nil
|
86
70
|
end
|
87
|
-
end
|
88
71
|
|
89
|
-
|
90
|
-
|
72
|
+
scope :published_by do |user_names|
|
73
|
+
relation.joins(:user).where(users: { name: user_names })
|
74
|
+
end
|
75
|
+
end
|
91
76
|
|
92
|
-
|
93
|
-
|
77
|
+
class Post < ActiveRecord::Base
|
78
|
+
def self.published_within(start_date, end_date)
|
79
|
+
where(published_at: start_date..end_date)
|
80
|
+
end
|
81
|
+
end
|
94
82
|
```
|
95
83
|
|
96
|
-
|
97
|
-
that replicate the functionality of an association like
|
98
|
-
`Post.for_user(user_id)` or allow you to build a default scope.
|
99
|
-
|
100
|
-
```ruby
|
101
|
-
class PostsController < ApplicationController
|
102
|
-
respond_to :html, :json
|
84
|
+
## Installation
|
103
85
|
|
104
|
-
|
105
|
-
def index
|
106
|
-
@user = User.find(params[:user_id])
|
86
|
+
Via a `Gemfile`:
|
107
87
|
|
108
|
-
|
109
|
-
|
88
|
+
```
|
89
|
+
gem 'paraphrase'
|
90
|
+
```
|
110
91
|
|
111
|
-
|
112
|
-
# @posts = @user.posts.published.paraphrase(params[:q])
|
113
|
-
#
|
114
|
-
# Order is independent too
|
115
|
-
# @posts = @user.posts.paraphrase(params[:q]).published
|
92
|
+
Or manually:
|
116
93
|
|
117
|
-
|
118
|
-
|
119
|
-
end
|
94
|
+
```
|
95
|
+
$ gem install paraphrase
|
120
96
|
```
|
121
97
|
|
122
|
-
|
98
|
+
## Usage
|
123
99
|
|
124
100
|
Scopes are mapped to param keys using `map`. You can specify one or more keys.
|
101
|
+
The scope will only be called if all the keys are present.
|
125
102
|
|
126
103
|
```ruby
|
127
104
|
class PostQuery < Paraphrase::Query
|
@@ -138,35 +115,84 @@ class Post < ActiveRecord::Base
|
|
138
115
|
where(published_on: pub_date)
|
139
116
|
end
|
140
117
|
end
|
118
|
+
|
119
|
+
Post.paraphrase(first_name: 'Jon', last_name: 'Richards', pub_date: '2010-10-01')
|
120
|
+
# => SELECT "posts".* FROM "posts"i
|
121
|
+
# WHERE "posts"."first_name" = 'Jon'
|
122
|
+
# AND "posts.last_name" = 'Richards'
|
123
|
+
# AND "posts.published_on" = '2010-10-01'
|
124
|
+
|
125
|
+
Post.paraphrase(first_name: 'Jon', pub_date: '2010-10-01')
|
126
|
+
# => SELECT "posts".* FROM "posts" WHERE "posts.published_on" = '2010-10-01'
|
141
127
|
```
|
142
128
|
|
129
|
+
### Changing the Model Class Used
|
130
|
+
|
131
|
+
By default, the `ActiveRecord` class is determined from the `demodulize`'d name
|
132
|
+
of the `Paraphrase::Query` sublcass. For instance, `DeliveryQuery` will use the
|
133
|
+
`Delivery` model by default.
|
134
|
+
|
135
|
+
If the name of the query class does not match this convention, the source can be
|
136
|
+
specified by setting the `source` class atribute.
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
# app/queries/admin_post_query.rb
|
140
|
+
class AdminPostQuery < Paraphrase::Query
|
141
|
+
self.source = :Post
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
### Whitelisting Query Params
|
146
|
+
|
143
147
|
If multiple query params are mapped to a scope, but only a subset are required,
|
144
|
-
use the `:whitelist` option to allow them to be blank. The `:whitelist`
|
145
|
-
|
148
|
+
use the `:whitelist` option to allow them to be blank. The `:whitelist` option
|
149
|
+
can be set to `true` to whitelist all keys, an individual key or an array of
|
150
|
+
keys.
|
146
151
|
|
147
152
|
```ruby
|
148
153
|
class PostQuery < Paraphrase::Query
|
149
|
-
# requires only :last_name to be passed in, :first_name can be nil
|
150
154
|
map :first_name, :last_name, to: :by_author, whitelist: :last_name
|
155
|
+
map :pub_date, to: :pub_date
|
151
156
|
end
|
152
157
|
|
153
158
|
class Post < ActiveRecord::Base
|
159
|
+
# `last_name` will be `nil` if not supplied.
|
154
160
|
def self.by_author(first_name, last_name)
|
155
|
-
query = where(
|
161
|
+
query = where(users: { first_name: first_name })
|
156
162
|
|
163
|
+
# Only filter by `:last_name` if supplied
|
157
164
|
if last_name
|
158
|
-
query = query.where(
|
165
|
+
query = query.where(users: { last_name: last_name })
|
159
166
|
end
|
160
167
|
|
161
168
|
query
|
162
169
|
end
|
163
170
|
end
|
164
171
|
|
165
|
-
Post.paraphrase(first_name: '
|
166
|
-
# => SELECT "posts".* FROM "posts"
|
172
|
+
Post.paraphrase(first_name: 'Jon', pub_date: '2010-10-01')
|
173
|
+
# => SELECT "posts".* FROM "posts"i
|
174
|
+
# WHERE "posts"."first_name" = 'Jon'
|
175
|
+
# AND "posts.published_on" = '2010-10-01'
|
176
|
+
```
|
177
|
+
|
178
|
+
Whitelisting is also useful for query params that are optional and have a
|
179
|
+
default, implied value such as with sorting:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
class PostQuery < Paraphrase::Query
|
183
|
+
map :sort, to: :sorted_by, whitelist: true
|
184
|
+
end
|
167
185
|
|
168
|
-
Post
|
169
|
-
|
186
|
+
class Post < ActiveRecord::Base
|
187
|
+
def self.sorted_by(sort_direction)
|
188
|
+
case sort_direction
|
189
|
+
when nil, 'newest'
|
190
|
+
order(created_at: :desc)
|
191
|
+
else
|
192
|
+
order(:created_at)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
170
196
|
```
|
171
197
|
|
172
198
|
### Boolean Scopes
|
@@ -194,21 +220,50 @@ Post.paraphrase(published: '1').to_sql
|
|
194
220
|
# => SELECT "posts".* FROM "posts" WHERE "posts"."published" = 't'
|
195
221
|
```
|
196
222
|
|
197
|
-
###
|
223
|
+
### Filtering `blank` Values
|
224
|
+
|
225
|
+
By default, `paraphrase` will recursively determine if the value of a query
|
226
|
+
param is `blank?`. This is meant to deal with form submissions, since blank
|
227
|
+
values are submitted even if the input is not filled in.
|
228
|
+
|
229
|
+
For example, if the value is an array containing empty strings, the empty
|
230
|
+
strings will be removed before being passed to the scope. If the array is empty
|
231
|
+
after removing empty strings, the scope will not be called since an empty array
|
232
|
+
is considered a blank value.
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
class UserQuery < Paraphrase::Query
|
236
|
+
map :names, to: :with_name
|
237
|
+
end
|
238
|
+
|
239
|
+
class User < ActiveRecord::Base
|
240
|
+
def self.with_name(names)
|
241
|
+
where(name: names)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
User.paraphrase(names: ['', 'Jim']).to_sql
|
246
|
+
# => SELECT "users".* FROM "users" WHERE "users"."name" IN ['Jim']
|
247
|
+
|
248
|
+
User.paraphrase(names: ['', '']).to_sql
|
249
|
+
# => SELECT "users".* FROM "users"
|
250
|
+
```
|
251
|
+
|
252
|
+
### Pre-processing Values
|
198
253
|
|
199
|
-
To pre-process a query param, such as an ISO formatted date, you can
|
200
|
-
|
201
|
-
|
202
|
-
|
254
|
+
To pre-process a query param, such as an ISO formatted date, you can use the
|
255
|
+
`param` class method or re-open the `ParamsFilter` class that is defined when
|
256
|
+
inheriting from `Paraphrase::Query`. Using the `param` class method defines the
|
257
|
+
equivalent method on the `ParamsFilter` class.
|
203
258
|
|
204
|
-
In the method, you have access to
|
205
|
-
|
259
|
+
In the method, you have access to `params` that represents the original,
|
260
|
+
unprocessed params.
|
206
261
|
|
207
262
|
```ruby
|
208
263
|
class PostQuery < Paraphrase::Query
|
209
264
|
map :start_date, :end_date, to: :published_within
|
210
265
|
|
211
|
-
class ParamsFilter
|
266
|
+
class ParamsFilter < Paraphrase::ParamsFilter
|
212
267
|
def start_date
|
213
268
|
Time.zone.parse(params[:start_date]) rescue nil
|
214
269
|
end
|
@@ -225,24 +280,30 @@ class Post < ActiveRecord::Base
|
|
225
280
|
end
|
226
281
|
end
|
227
282
|
|
283
|
+
Post.parahrase(start_date: '2011-03-21', end_date: '2013-03-25').to_sql
|
284
|
+
# => SELECT "posts".* FROM "posts"
|
285
|
+
WHERE "posts"."published_at" BETWEEN '2011-03-21' AND '2013-03-25'
|
286
|
+
|
287
|
+
# The typo in the `start_date` query param causes `Time.zone.parse` to fail so
|
288
|
+
# the pre-procssed `start_date` is `nil`. Since not all params are present, the
|
289
|
+
# scope is not run.
|
228
290
|
Post.parahrase(start_date: '201-03-21', end_date: '2013-03-25').to_sql
|
229
291
|
# => SELECT "posts".* FROM "posts"
|
230
292
|
```
|
231
293
|
|
232
294
|
In the above example, if either `:start_date` or `:end_date` are incorrectly
|
233
|
-
formatted, the `pubished_within` scope will not be applied
|
234
|
-
|
295
|
+
formatted, the `pubished_within` scope will not be applied since
|
296
|
+
`Time.zone.parse` will fail and return `nil`.
|
235
297
|
|
236
|
-
### Define scopes
|
298
|
+
### Define scopes in the `Query` class
|
237
299
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
proxy for defining methods on the `Repository` class.
|
300
|
+
Scopes can be defined in the `Query` class using the `scope` keyword or
|
301
|
+
re-opening the `Repository` class defined in the `Query` subclass. This helps to
|
302
|
+
avoid cluttering the model class with scopes that are only used by the query
|
303
|
+
class.
|
243
304
|
|
244
|
-
|
245
|
-
`Repository` instance.
|
305
|
+
When defining scopes this way, any `ActiveRecord::Relation` methods should be
|
306
|
+
called on the `relation` property of the `Repository` instance.
|
246
307
|
|
247
308
|
```ruby
|
248
309
|
class PostQuery < Paraphrase::Query
|
@@ -254,18 +315,17 @@ class PostQuery < Paraphrase::Query
|
|
254
315
|
relation.joins(:user).where(users: { name: authors })
|
255
316
|
end
|
256
317
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
# end
|
318
|
+
class Repository < Paraphrase::Repository
|
319
|
+
def titled(post_title)
|
320
|
+
relation.where(title: post_title)
|
321
|
+
end
|
322
|
+
end
|
263
323
|
end
|
264
324
|
|
265
325
|
class Post < ActiveRecord::Base
|
266
326
|
end
|
267
327
|
|
268
|
-
Post.paraphrase(authors: ['Robert', 'Susie']).to_sql
|
328
|
+
Post.paraphrase(authors: ['Robert', 'Susie'], title: 'Sunshine').to_sql
|
269
329
|
# => SELECT "posts".* FROM "posts"
|
270
330
|
# INNER JOIN "users" ON "users"."id" = "posts"."user_id"
|
271
331
|
# WHERE "users"."name" IN ('Robert', 'Susie')
|
data/gemfiles/3.1.gemfile.lock
CHANGED
data/gemfiles/3.2.gemfile.lock
CHANGED
data/gemfiles/4.0.gemfile.lock
CHANGED
data/gemfiles/4.1.gemfile.lock
CHANGED
data/lib/paraphrase/mapping.rb
CHANGED
@@ -1,17 +1,29 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'active_support/hash_with_indifferent_access'
|
3
|
+
|
1
4
|
module Paraphrase
|
5
|
+
# {ParamsFilter} is responsible for processing the query params the {Query}
|
6
|
+
# object was initialized with.
|
7
|
+
#
|
8
|
+
# In the following order, it:
|
9
|
+
#
|
10
|
+
# 1. Removes all keys not mapped to a model scope
|
11
|
+
# 2. Pre-processes the query param if a pre-processor is defined
|
12
|
+
# 3. Recursively removes blank values from the value
|
13
|
+
# 4. Removes the param if the pre-processed, scrubbed value is `blank?`
|
14
|
+
#
|
15
|
+
# Each {Query} subclass has its own {ParamsFilter} subclass defined on
|
16
|
+
# inheritance that can be customized to pre-process query params. The class
|
17
|
+
# can be re-opened inside the {Query} class definition or by calling the
|
18
|
+
# {Query.param param} class method.
|
2
19
|
class ParamsFilter
|
3
20
|
attr_reader :params, :result
|
4
21
|
|
5
22
|
def initialize(unfiltered_params, keys)
|
6
23
|
@params = unfiltered_params.with_indifferent_access.slice(*keys)
|
7
24
|
|
8
|
-
@result =
|
9
|
-
value = @params[key]
|
10
|
-
|
11
|
-
if respond_to?(key)
|
12
|
-
value = send(key)
|
13
|
-
end
|
14
|
-
|
25
|
+
@result = keys.inject(HashWithIndifferentAccess.new) do |result, key|
|
26
|
+
value = respond_to?(key) ? send(key) : @params[key]
|
15
27
|
value = scrub(value)
|
16
28
|
|
17
29
|
if value.present?
|
data/lib/paraphrase/query.rb
CHANGED
@@ -15,19 +15,16 @@ module Paraphrase
|
|
15
15
|
include ActiveModel
|
16
16
|
# @!attribute [r] mappings
|
17
17
|
# @return [Array<Paraphrase::Mapping>] mappings for query
|
18
|
-
# @!attribute [r] source
|
19
|
-
# @return [Symbol, String] name of the class to use as the source for the
|
20
|
-
# query
|
21
18
|
class_attribute :mappings, instance_writer: false
|
22
19
|
class_attribute :source, instance_writer: false, instance_reader: false
|
23
20
|
class_attribute :params_filter, instance_writer: false
|
24
21
|
class_attribute :repository, instance_writer: false
|
25
22
|
|
26
23
|
# @!attribute [r] params
|
27
|
-
# @return [HashWithIndifferentAccess] filtered parameters based on keys
|
28
|
-
#
|
24
|
+
# @return [HashWithIndifferentAccess] filtered parameters based on keys
|
25
|
+
# defined in `mappings`
|
29
26
|
# @!attribute [r] result
|
30
|
-
# @return [ActiveRecord::Relation]
|
27
|
+
# @return [ActiveRecord::Relation] resulting {ActiveRecord::Relation} instance from queries
|
31
28
|
attr_reader :params, :result
|
32
29
|
|
33
30
|
# Set `mappings` on inheritance to ensure they're unique per subclass
|
@@ -42,15 +39,15 @@ module Paraphrase
|
|
42
39
|
klass.const_set(:Repository, klass.repository)
|
43
40
|
end
|
44
41
|
|
45
|
-
# Keys
|
42
|
+
# Keys mapped to scopes
|
46
43
|
#
|
47
44
|
# @return [Array<Symbol>]
|
48
45
|
def self.keys
|
49
46
|
mappings.flat_map(&:keys)
|
50
47
|
end
|
51
48
|
|
52
|
-
# Add a {Mapping} instance to {Query#mappings}. Defines a reader
|
53
|
-
# key to read from {Query#params}.
|
49
|
+
# Add a {Mapping} instance to {Query#mappings}. Defines a reader
|
50
|
+
# for each key to read from {Query#params}.
|
54
51
|
#
|
55
52
|
# @overload map(*keys, options)
|
56
53
|
# Maps a key to a scope
|
@@ -78,7 +75,7 @@ module Paraphrase
|
|
78
75
|
# param
|
79
76
|
#
|
80
77
|
# @param [Symbol] query_param query param to process
|
81
|
-
# @param [Proc] block block to process the
|
78
|
+
# @param [Proc] block block to process the specified `query_param`
|
82
79
|
def self.param(query_param, &block)
|
83
80
|
params_filter.class_eval do
|
84
81
|
define_method(query_param, &block)
|
@@ -86,6 +83,9 @@ module Paraphrase
|
|
86
83
|
end
|
87
84
|
|
88
85
|
# Define a scope on `Repository`
|
86
|
+
#
|
87
|
+
# @param [Symbol] scope_name name of the scope specified in {Query.map}
|
88
|
+
# @param [Proc] block body of the scope to be defined
|
89
89
|
def self.scope(scope_name, &block)
|
90
90
|
repository.class_eval do
|
91
91
|
define_method(scope_name, &block)
|
@@ -95,8 +95,8 @@ module Paraphrase
|
|
95
95
|
# Filters out parameters irrelevant to the query and sets the base scope
|
96
96
|
# for to begin the chain.
|
97
97
|
#
|
98
|
-
# @param [Hash]
|
99
|
-
# @param [ActiveRecord::Relation] relation object to apply methods to
|
98
|
+
# @param [Hash] query_params query parameters
|
99
|
+
# @param [ActiveRecord::Relation] relation relation object to apply methods to
|
100
100
|
def initialize(query_params, relation = nil)
|
101
101
|
@params = filter_params(query_params || {})
|
102
102
|
|
@@ -1,4 +1,10 @@
|
|
1
1
|
module Paraphrase
|
2
|
+
# {Repository} is were query-specific scopes are defined. They can be defined
|
3
|
+
# by re-opening the class inside the {Query} class definition or by using the
|
4
|
+
# {Query.scope scope} class method on {Query}. Both methods are equivalent.
|
5
|
+
#
|
6
|
+
# Inside scopes defined on a {Repository}, the method has access to
|
7
|
+
# {Query#params} as `params`.
|
2
8
|
class Repository
|
3
9
|
attr_reader :relation, :mapping, :params
|
4
10
|
|
data/lib/paraphrase/version.rb
CHANGED
@@ -8,6 +8,7 @@ module Paraphrase
|
|
8
8
|
map :is_published, to: :published
|
9
9
|
map :authors, to: :by_users
|
10
10
|
map :start_date, :end_date, to: :published_between
|
11
|
+
map :sort, to: :sort_by
|
11
12
|
|
12
13
|
class ParamsFilter
|
13
14
|
def start_date
|
@@ -17,7 +18,7 @@ module Paraphrase
|
|
17
18
|
|
18
19
|
class Repository
|
19
20
|
def published_between(start_date, end_date)
|
20
|
-
where(published_at: start_date..end_date)
|
21
|
+
relation.where(published_at: start_date..end_date)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
@@ -25,9 +26,17 @@ module Paraphrase
|
|
25
26
|
Time.parse(params[:end_date]) rescue nil
|
26
27
|
end
|
27
28
|
|
29
|
+
param :sort do
|
30
|
+
params[:sort].presence || "newest"
|
31
|
+
end
|
32
|
+
|
28
33
|
scope :by_users do |authors|
|
29
34
|
relation.joins(:user).where(users: { name: authors })
|
30
35
|
end
|
36
|
+
|
37
|
+
scope :sort_by do |sort_direction|
|
38
|
+
sort_direction == "newest" ? relation.order("created_at DESC") : relation
|
39
|
+
end
|
31
40
|
end
|
32
41
|
|
33
42
|
describe ".map" do
|
@@ -86,21 +95,32 @@ module Paraphrase
|
|
86
95
|
expect(query.params[:authors]).to eq ['kevin']
|
87
96
|
expect(query.params).not_to have_key :title
|
88
97
|
end
|
98
|
+
|
99
|
+
it "can have default values defined" do
|
100
|
+
query = PostQuery.new(Hash.new)
|
101
|
+
|
102
|
+
expect(query.sort).to eq "newest"
|
103
|
+
end
|
89
104
|
end
|
90
105
|
|
91
106
|
it 'skips scopes if query params are missing' do
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
107
|
+
post = Post.create!(
|
108
|
+
published_at: Time.local(2009, 10, 30),
|
109
|
+
title: 'bar',
|
110
|
+
user: User.create!,
|
111
|
+
published: true
|
112
|
+
)
|
113
|
+
params = {
|
98
114
|
start_date: Time.local(2010, 10, 30),
|
99
115
|
end_date: 'foo',
|
100
116
|
is_published: '1',
|
101
117
|
authors: [],
|
102
118
|
title: ['', {}]
|
103
|
-
|
119
|
+
}
|
120
|
+
|
121
|
+
query = PostQuery.new(params)
|
122
|
+
|
123
|
+
expect(query.result).to eq [post]
|
104
124
|
end
|
105
125
|
|
106
126
|
it 'supports defining scopes in the query class' do
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paraphrase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eduardo Gutierrez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -276,7 +276,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
276
276
|
version: '0'
|
277
277
|
requirements: []
|
278
278
|
rubyforge_project:
|
279
|
-
rubygems_version: 2.2.
|
279
|
+
rubygems_version: 2.2.2
|
280
280
|
signing_key:
|
281
281
|
specification_version: 4
|
282
282
|
summary: Map query params to model scopes
|