parelation 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gemfiles/4.1.gemfile +3 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +288 -0
- data/Rakefile +4 -0
- data/lib/parelation/applier.rb +60 -0
- data/lib/parelation/criteria/limit.rb +24 -0
- data/lib/parelation/criteria/offset.rb +24 -0
- data/lib/parelation/criteria/order/object.rb +46 -0
- data/lib/parelation/criteria/order.rb +31 -0
- data/lib/parelation/criteria/query.rb +55 -0
- data/lib/parelation/criteria/select.rb +24 -0
- data/lib/parelation/criteria/where/caster.rb +82 -0
- data/lib/parelation/criteria/where/criteria_builder.rb +67 -0
- data/lib/parelation/criteria/where/directional_query_applier.rb +49 -0
- data/lib/parelation/criteria/where.rb +37 -0
- data/lib/parelation/criteria.rb +24 -0
- data/lib/parelation/errors/parameter.rb +2 -0
- data/lib/parelation/helpers.rb +12 -0
- data/lib/parelation/version.rb +3 -0
- data/lib/parelation.rb +19 -0
- data/parelation.gemspec +29 -0
- data/spec/lib/applier_spec.rb +51 -0
- data/spec/lib/criteria/limit_spec.rb +21 -0
- data/spec/lib/criteria/offset_spec.rb +21 -0
- data/spec/lib/criteria/order_spec.rb +36 -0
- data/spec/lib/criteria/query_spec.rb +31 -0
- data/spec/lib/criteria/select_spec.rb +21 -0
- data/spec/lib/criteria/where_spec.rb +157 -0
- data/spec/lib/helpers_spec.rb +21 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/db.rb +37 -0
- data/spec/support/env.rb +14 -0
- metadata +232 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 037f22447840bf0175afad3ffc9877399f52e45a
|
4
|
+
data.tar.gz: 245f4049e008a44245cc335f66efe0229e0702b3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4974134e76747012c27f44408d9804040d15c183a03bc3258000e8e04734d46288ba5f1e2e1213cd906678830042e9913ffd6c7b65f196b68f73954e58f82180
|
7
|
+
data.tar.gz: af3bcdb37c9ba6f84e27855816c6317ab809b800a5fe6519a5e9f76555da0169f9ebdd26cc1d2f318c54908ce4b3d87534fc55e341485c48950761e0395f2403
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
.DS_Store
|
7
|
+
Gemfile.lock
|
8
|
+
InstalledFiles
|
9
|
+
_yardoc
|
10
|
+
coverage
|
11
|
+
doc/
|
12
|
+
lib/bundler/man
|
13
|
+
pkg
|
14
|
+
rdoc
|
15
|
+
spec/reports
|
16
|
+
test/tmp
|
17
|
+
test/version_tmp
|
18
|
+
tmp
|
19
|
+
*.bundle
|
20
|
+
*.so
|
21
|
+
*.o
|
22
|
+
*.a
|
23
|
+
mkmf.log
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Michael van Rooijen
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
# Parelation
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/meskyanichi/parelation)
|
4
|
+
[](https://travis-ci.org/meskyanichi/parelation)
|
5
|
+
|
6
|
+
Parelation, for Rails/ActiveRecord 4.1.0+, allows you to query your ActiveRecord-mapped database easily, securely and quite flexibly using simple GET requests. It's used in your controller layer where it uses HTTP GET parameters to build on the ActiveRecord::Relation chain. This provides the client-side with the out-of-the-box flexibility to perform fairly dynamic queries without having to write boilerplate on the server.
|
7
|
+
|
8
|
+
This library was developed for- and extracted from [HireFire].
|
9
|
+
|
10
|
+
### Compatibility
|
11
|
+
|
12
|
+
- Rails/ActiveRecord 4.1.0+
|
13
|
+
- Ruby (MRI) 2.0+
|
14
|
+
- Ruby (RBX) 2.2+
|
15
|
+
|
16
|
+
### Installation
|
17
|
+
|
18
|
+
Compile and install the gem from source.
|
19
|
+
|
20
|
+
```rb
|
21
|
+
gem "parelation", github: "meskyanichi/parelation"
|
22
|
+
```
|
23
|
+
|
24
|
+
*This library won't be hosted on RubyGems.org until it's been tested more in development.*
|
25
|
+
|
26
|
+
### Example
|
27
|
+
|
28
|
+
Here's an example to get an idea of how it works. We'll fetch the `50` most recently created and `open` tickets, and we only want their `id`, `name` and `message` attributes.
|
29
|
+
|
30
|
+
```js
|
31
|
+
var params = {
|
32
|
+
"select[]": ["id", "name", "message"],
|
33
|
+
"where[state]": "open",
|
34
|
+
"order": "created_at:desc",
|
35
|
+
"limit": "50"
|
36
|
+
}
|
37
|
+
|
38
|
+
$.get("https://api.ticket.app/tickets", params, function(tickets){
|
39
|
+
console.log("Just fetched the 50 most recent and open tickets.")
|
40
|
+
$.each(tickets, function(ticket){
|
41
|
+
console.log("Ticket " + ticket.name + " loaded!")
|
42
|
+
})
|
43
|
+
})
|
44
|
+
```
|
45
|
+
|
46
|
+
Simply include `Parelation::Helpers` and use the `parelate` method. This will ensure that the provided parameters are converted and applied to the `Ticket.all` criteria chain.
|
47
|
+
|
48
|
+
```rb
|
49
|
+
class Api::V1::TicketsController < ApplicationController
|
50
|
+
include Parelation::Helpers
|
51
|
+
|
52
|
+
def index
|
53
|
+
render json: parelate(Ticket.all)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
You can also scope results to the `current_user`:
|
59
|
+
|
60
|
+
```rb
|
61
|
+
class Api::V1::TicketsController < ApplicationController
|
62
|
+
include Parelation::Helpers
|
63
|
+
|
64
|
+
def index
|
65
|
+
render json: parelate(current_user.tickets)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
Using the same JavaScript, this'll now fetch the 50 most recent open tickets scoped to the `current_user`.
|
71
|
+
|
72
|
+
|
73
|
+
### Parameter List (Reference)
|
74
|
+
|
75
|
+
Here follows a list of all possible query syntaxes. We'll assume we have a Ticket model to query on.
|
76
|
+
|
77
|
+
#### Select
|
78
|
+
|
79
|
+
```
|
80
|
+
/tickets?select[]=id&select[]=name&select[]=message
|
81
|
+
```
|
82
|
+
|
83
|
+
Translates to:
|
84
|
+
|
85
|
+
```rb
|
86
|
+
Ticket.select(:id, :name, :message)
|
87
|
+
```
|
88
|
+
|
89
|
+
#### Where
|
90
|
+
|
91
|
+
```
|
92
|
+
/tickets?where[state]=open
|
93
|
+
```
|
94
|
+
|
95
|
+
Translates to:
|
96
|
+
|
97
|
+
```rb
|
98
|
+
Ticket.where(state: "open")
|
99
|
+
```
|
100
|
+
|
101
|
+
You can also specify multiple multiple conditions:
|
102
|
+
|
103
|
+
```
|
104
|
+
/tickets?where[state][]=open&where[state][]=pending
|
105
|
+
```
|
106
|
+
|
107
|
+
Translates to:
|
108
|
+
|
109
|
+
```rb
|
110
|
+
Ticket.where(state: ["open", "pending"])
|
111
|
+
```
|
112
|
+
|
113
|
+
#### Where (directional)
|
114
|
+
|
115
|
+
* `where_gt` (greater than `>`)
|
116
|
+
* `where_gte` (greater than or equal to `>=`)
|
117
|
+
* `where_lt` (less than `<`)
|
118
|
+
* `where_lte` (less than or equal to `<=`)
|
119
|
+
|
120
|
+
```
|
121
|
+
/tickets?where_gt[created_at]=2014-01-01T00:00:00Z
|
122
|
+
```
|
123
|
+
|
124
|
+
Translates to:
|
125
|
+
|
126
|
+
```rb
|
127
|
+
Ticket.where("'tickets'.'created_at' > '2014-01-01 00:00:00.000000'")
|
128
|
+
```
|
129
|
+
|
130
|
+
You can also specify multiple conditions:
|
131
|
+
|
132
|
+
```
|
133
|
+
/tickets?where_gt[created_at]=2014-01-01T00:00:00Z&where_gt[updated_at]=2014-01-01T00:00:00Z
|
134
|
+
```
|
135
|
+
|
136
|
+
Translates to:
|
137
|
+
|
138
|
+
```rb
|
139
|
+
Ticket
|
140
|
+
.where("'tickets'.'created_at' > '2014-01-01 00:00:00.000000'")
|
141
|
+
.where("'tickets'.'updated_at' > '2014-01-01 00:00:00.000000'")
|
142
|
+
```
|
143
|
+
|
144
|
+
#### Query
|
145
|
+
|
146
|
+
```
|
147
|
+
/tickets?query[memory leak]=name
|
148
|
+
```
|
149
|
+
|
150
|
+
Translates to:
|
151
|
+
|
152
|
+
```rb
|
153
|
+
Ticket.where("'tickets'.'name' LIKE '%memory leak%'")
|
154
|
+
```
|
155
|
+
|
156
|
+
You can also specify multiple columns to scan:
|
157
|
+
|
158
|
+
```
|
159
|
+
/tickets?query[memory leak]=name&query[memory leak]=message
|
160
|
+
```
|
161
|
+
|
162
|
+
Translates to:
|
163
|
+
|
164
|
+
```rb
|
165
|
+
Ticket.where("(
|
166
|
+
'tickets'.'name' LIKE '%memory leak%' OR
|
167
|
+
'tickets'.'message' LIKE '%memory leak%'
|
168
|
+
)")
|
169
|
+
```
|
170
|
+
|
171
|
+
#### Order
|
172
|
+
|
173
|
+
```
|
174
|
+
/tickets?order=created_at:desc
|
175
|
+
```
|
176
|
+
|
177
|
+
Translates to:
|
178
|
+
|
179
|
+
```rb
|
180
|
+
Ticket.order(created_at: :desc)
|
181
|
+
```
|
182
|
+
|
183
|
+
You can also specify multiple order-operations:
|
184
|
+
|
185
|
+
```
|
186
|
+
/tickets?order[]=created_at:desc&order[]=name:asc
|
187
|
+
```
|
188
|
+
|
189
|
+
Translates to:
|
190
|
+
|
191
|
+
```rb
|
192
|
+
Ticket.order(created_at: :desc, name: :asc)
|
193
|
+
```
|
194
|
+
|
195
|
+
#### Limit
|
196
|
+
|
197
|
+
```
|
198
|
+
/tickets?limit=50
|
199
|
+
```
|
200
|
+
|
201
|
+
Translates to:
|
202
|
+
|
203
|
+
```rb
|
204
|
+
Ticket.limit(50)
|
205
|
+
```
|
206
|
+
|
207
|
+
#### Offset
|
208
|
+
|
209
|
+
```
|
210
|
+
/tickets?offset=25
|
211
|
+
```
|
212
|
+
|
213
|
+
Translates to:
|
214
|
+
|
215
|
+
```rb
|
216
|
+
Ticket.offset(25)
|
217
|
+
```
|
218
|
+
|
219
|
+
|
220
|
+
### Error Handling
|
221
|
+
|
222
|
+
When invalid parameters were sent, you can rescue the exception and return a message to the client.
|
223
|
+
|
224
|
+
```rb
|
225
|
+
class Api::V1::TicketsController < ApplicationController
|
226
|
+
include Parelation::Helpers
|
227
|
+
|
228
|
+
def index
|
229
|
+
render json: parelate(Ticket.all)
|
230
|
+
rescue Parelation::Errors::Parameter => error
|
231
|
+
render json: { error: error }, status: :bad_request
|
232
|
+
end
|
233
|
+
end
|
234
|
+
```
|
235
|
+
|
236
|
+
This will tell client developers what parameter failed in the HTTP response. This exception generally occurs when there is a typo in the URL's parameters. Knowing which parameter failed (described in `error`) helps narrowing down the issue.
|
237
|
+
|
238
|
+
|
239
|
+
### Contributing
|
240
|
+
|
241
|
+
Contributions are welcome, but please conform to these requirements:
|
242
|
+
|
243
|
+
- Ruby (MRI) 2.0+
|
244
|
+
- Ruby (RBX) 2.2+
|
245
|
+
- ActiveRecord 4.1.0+
|
246
|
+
- 100% Spec Coverage
|
247
|
+
- Generated by when running the test suite
|
248
|
+
- 100% [Passing Specs]
|
249
|
+
- Run test suite with `$ rspec spec`
|
250
|
+
- 4.0 [Code Climate Score]
|
251
|
+
- Run `$ rubycritic lib` to generate the score locally and receive tips
|
252
|
+
- No code smells
|
253
|
+
- No duplication
|
254
|
+
|
255
|
+
To start contributing, fork the project, clone it, and install the development dependencies:
|
256
|
+
|
257
|
+
```
|
258
|
+
git clone git@github.com:USERNAME/parelation.git
|
259
|
+
cd parelation
|
260
|
+
bundle
|
261
|
+
```
|
262
|
+
|
263
|
+
Ensure that everything works:
|
264
|
+
|
265
|
+
```
|
266
|
+
rspec spec
|
267
|
+
rubycritic lib
|
268
|
+
```
|
269
|
+
|
270
|
+
Create a new branch and start hacking:
|
271
|
+
|
272
|
+
```
|
273
|
+
git checkout -b my-contributions
|
274
|
+
```
|
275
|
+
|
276
|
+
Submit a pull request.
|
277
|
+
|
278
|
+
|
279
|
+
### Author / License
|
280
|
+
|
281
|
+
Copyright (c) 2014 Michael van Rooijen ( [@meskyanichi] )<br />
|
282
|
+
Released under the MIT [License].
|
283
|
+
|
284
|
+
[@meskyanichi]: https://twitter.com/meskyanichi
|
285
|
+
[HireFire]: http://hirefire.io
|
286
|
+
[Passing Specs]: https://travis-ci.org/meskyanichi/parelation
|
287
|
+
[Code Climate Score]: https://codeclimate.com/github/meskyanichi/parelation
|
288
|
+
[License]: https://github.com/meskyanichi/parelation/blob/master/LICENSE
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
class Parelation::Applier
|
2
|
+
|
3
|
+
# @return [Array] the list of active criteria classes
|
4
|
+
# that are used to narrow down database results.
|
5
|
+
#
|
6
|
+
CRITERIA = [
|
7
|
+
Parelation::Criteria::Select,
|
8
|
+
Parelation::Criteria::Limit,
|
9
|
+
Parelation::Criteria::Offset,
|
10
|
+
Parelation::Criteria::Order,
|
11
|
+
Parelation::Criteria::Query,
|
12
|
+
Parelation::Criteria::Where
|
13
|
+
]
|
14
|
+
|
15
|
+
# @return [ActiveRecord::Relation]
|
16
|
+
#
|
17
|
+
attr_reader :chain
|
18
|
+
|
19
|
+
# @return [ActionController::Parameters, Hash]
|
20
|
+
#
|
21
|
+
attr_reader :params
|
22
|
+
|
23
|
+
# @param chain [ActionController::Relation] the base chain to build on.
|
24
|
+
# @param params [ActionController::Parameters, Hash] user input via params.
|
25
|
+
#
|
26
|
+
def initialize(chain, params)
|
27
|
+
@chain = chain
|
28
|
+
@params = params
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [ActiveRecord::Relation] the criteria-applied {#chain}.
|
32
|
+
#
|
33
|
+
def apply
|
34
|
+
@apply ||= apply_to_chain
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Iterates over each user-provided parameter and incrementally
|
40
|
+
# updates the {#chain} to incorporate the user-requested criteria.
|
41
|
+
#
|
42
|
+
# @return [ActiveRecord::Relation]
|
43
|
+
#
|
44
|
+
def apply_to_chain
|
45
|
+
params.each do |param, value|
|
46
|
+
CRITERIA.each do |criteria|
|
47
|
+
if criteria.match?(param)
|
48
|
+
begin
|
49
|
+
@chain = criteria.new(chain, param, value).call
|
50
|
+
rescue
|
51
|
+
raise Parelation::Errors::Parameter,
|
52
|
+
"the #{param} parameter is invalid"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
chain
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Parelation::Criteria::Limit
|
2
|
+
include Parelation::Criteria
|
3
|
+
|
4
|
+
# @param param [String]
|
5
|
+
# @return [TrueClass, FalseClass]
|
6
|
+
#
|
7
|
+
def self.match?(param)
|
8
|
+
!!(param =~ /^limit$/)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [ActiveRecord::Relation]
|
12
|
+
#
|
13
|
+
def call
|
14
|
+
chain.limit(limit)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Alias for {#value}.
|
20
|
+
#
|
21
|
+
def limit
|
22
|
+
value
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Parelation::Criteria::Offset
|
2
|
+
include Parelation::Criteria
|
3
|
+
|
4
|
+
# @param param [String]
|
5
|
+
# @return [TrueClass, FalseClass]
|
6
|
+
#
|
7
|
+
def self.match?(param)
|
8
|
+
!!(param =~ /^offset$/)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [ActiveRecord::Relation]
|
12
|
+
#
|
13
|
+
def call
|
14
|
+
chain.offset(offset)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Alias for {#value}.
|
20
|
+
#
|
21
|
+
def offset
|
22
|
+
value
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Parelation::Criteria::Order::Object
|
2
|
+
|
3
|
+
# @return [String]
|
4
|
+
#
|
5
|
+
attr_reader :order
|
6
|
+
|
7
|
+
# @param order [String]
|
8
|
+
#
|
9
|
+
def initialize(order)
|
10
|
+
@order = order
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Hash] returns criteria for {ActiveRecord::Relation}.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# { created_at: :asc }
|
17
|
+
#
|
18
|
+
def criteria
|
19
|
+
{ field => direction }
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# @return [String] the name of the field to perform the ordering on.
|
25
|
+
#
|
26
|
+
def field
|
27
|
+
parts.first || ""
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Symbol, nil] the direction to order {#field},
|
31
|
+
# eiter :asc or :desc.
|
32
|
+
#
|
33
|
+
def direction
|
34
|
+
case parts.last
|
35
|
+
when "asc" then :asc
|
36
|
+
when "desc" then :desc
|
37
|
+
else nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Array<String, nil>] the criteria chunks (separated by +:+).
|
42
|
+
#
|
43
|
+
def parts
|
44
|
+
@parts ||= order.split(":")
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Parelation::Criteria::Order
|
2
|
+
include Parelation::Criteria
|
3
|
+
|
4
|
+
require_relative "order/object"
|
5
|
+
|
6
|
+
# @param param [String]
|
7
|
+
# @return [TrueClass, FalseClass]
|
8
|
+
#
|
9
|
+
def self.match?(param)
|
10
|
+
!!(param =~ /^order$/)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Applies the specified orderings to {#chain}.
|
14
|
+
#
|
15
|
+
# @return [ActiveRecord::Relation] the modified chain.
|
16
|
+
#
|
17
|
+
def call
|
18
|
+
orders.inject(chain) do |chain, order|
|
19
|
+
chain.order(order.criteria)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# @return [Array<Parelation::Criteria::Order::Object>] an
|
26
|
+
# array of attributes to order.
|
27
|
+
#
|
28
|
+
def orders
|
29
|
+
@orders ||= [value].flatten.map { |order| Object.new(order) }
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class Parelation::Criteria::Query
|
2
|
+
include Parelation::Criteria
|
3
|
+
|
4
|
+
# @param param [String]
|
5
|
+
# @return [TrueClass, FalseClass]
|
6
|
+
#
|
7
|
+
def self.match?(param)
|
8
|
+
!!(param =~ /^query$/)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [ActiveRecord::Relation]
|
12
|
+
#
|
13
|
+
def call
|
14
|
+
chain.where(*criteria)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# @return [Array] containing {#chain} criteria.
|
20
|
+
#
|
21
|
+
def criteria
|
22
|
+
[sql] + args
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String] an SQL statement based on the selected {#attributes}.
|
26
|
+
#
|
27
|
+
def sql
|
28
|
+
template = ->(field) { %Q{"#{chain.arel_table.name}"."#{field}" LIKE ?} }
|
29
|
+
attributes.map(&template).join(" OR ")
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Array] containing the query, one for each field in {#attributes}.
|
33
|
+
#
|
34
|
+
def args
|
35
|
+
attributes.count.times.map { "%#{query}%" }
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [String] the "search" query to perform.
|
39
|
+
#
|
40
|
+
def query
|
41
|
+
@query ||= parts.first
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Array<String>] an array of attributes to search in.
|
45
|
+
#
|
46
|
+
def attributes
|
47
|
+
@attributes ||= parts.last
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Array] containing the query and attributes.
|
51
|
+
#
|
52
|
+
def parts
|
53
|
+
@parts ||= [value.keys.first, [value.values.first].flatten]
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Parelation::Criteria::Select
|
2
|
+
include Parelation::Criteria
|
3
|
+
|
4
|
+
# @param param [String]
|
5
|
+
# @return [TrueClass, FalseClass]
|
6
|
+
#
|
7
|
+
def self.match?(param)
|
8
|
+
!!(param =~ /^select$/)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [ActiveRecord::Relation]
|
12
|
+
#
|
13
|
+
def call
|
14
|
+
chain.select(*attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Alias for {#value}.
|
20
|
+
#
|
21
|
+
def attributes
|
22
|
+
value
|
23
|
+
end
|
24
|
+
end
|