hanami-rethinkdb 0.2.3
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/.gitignore +15 -0
- data/.travis.yml +20 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +73 -0
- data/Rakefile +17 -0
- data/hanami-rethinkdb.gemspec +29 -0
- data/lib/hanami-rethinkdb.rb +2 -0
- data/lib/hanami/model/adapters/rethinkdb/collection.rb +332 -0
- data/lib/hanami/model/adapters/rethinkdb/command.rb +66 -0
- data/lib/hanami/model/adapters/rethinkdb/query.rb +486 -0
- data/lib/hanami/model/adapters/rethinkdb_adapter.rb +275 -0
- data/lib/hanami/rethinkdb/version.rb +12 -0
- data/test/model/adapters/rethinkdb_adapter_test.rb +980 -0
- data/test/test_helper.rb +25 -0
- data/test/version_test.rb +7 -0
- metadata +161 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Hanami
|
|
2
|
+
module Model
|
|
3
|
+
module Adapters
|
|
4
|
+
module Rethinkdb
|
|
5
|
+
# Execute a command for the given query.
|
|
6
|
+
#
|
|
7
|
+
# @see Hanami::Model::Adapters::Rethinkdb::Query
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
# @since 0.1.0
|
|
11
|
+
class Command
|
|
12
|
+
# Initialize a command
|
|
13
|
+
#
|
|
14
|
+
# @param query [Hanami::Model::Adapters::Rethinkdb::Query]
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
# @since 0.1.0
|
|
18
|
+
def initialize(query)
|
|
19
|
+
@collection = query.scoped
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Creates a document for the given entity.
|
|
23
|
+
#
|
|
24
|
+
# @param entity [Object] the entity to persist
|
|
25
|
+
#
|
|
26
|
+
# @see Hanami::Model::Adapters::Rethinkdb::Collection#insert
|
|
27
|
+
#
|
|
28
|
+
# @return the primary key of the just created document.
|
|
29
|
+
#
|
|
30
|
+
# @api private
|
|
31
|
+
# @since 0.1.0
|
|
32
|
+
def create(entity)
|
|
33
|
+
@collection.insert(entity)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Updates the corresponding document for the given entity.
|
|
37
|
+
#
|
|
38
|
+
# @param entity [Object] the entity to persist
|
|
39
|
+
#
|
|
40
|
+
# @see Hanami::Model::Adapters::Rethinkdb::Collection#update
|
|
41
|
+
#
|
|
42
|
+
# @api private
|
|
43
|
+
# @since 0.1.0
|
|
44
|
+
def update(entity)
|
|
45
|
+
@collection.update(entity)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Deletes all the documents for the current query.
|
|
49
|
+
#
|
|
50
|
+
# It's used to delete a single document or an entire database table.
|
|
51
|
+
#
|
|
52
|
+
# @see Hanami::Model::Adapters::RethinkdbAdapter#delete
|
|
53
|
+
# @see Hanami::Model::Adapters::RethinkdbAdapter#clear
|
|
54
|
+
#
|
|
55
|
+
# @api private
|
|
56
|
+
# @since 0.1.0
|
|
57
|
+
def delete
|
|
58
|
+
@collection.delete
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
alias_method :clear, :delete
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
require 'hanami/utils/kernel'
|
|
3
|
+
require 'rethinkdb'
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
module Model
|
|
7
|
+
module Adapters
|
|
8
|
+
module Rethinkdb
|
|
9
|
+
# Query the database with a powerful API.
|
|
10
|
+
#
|
|
11
|
+
# All the methods are chainable, it allows advanced composition of
|
|
12
|
+
# ReQL conditions.
|
|
13
|
+
#
|
|
14
|
+
# This works as a lazy filtering mechanism: the documents are fetched
|
|
15
|
+
# from the database only when needed.
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
#
|
|
19
|
+
# query.where(language: 'ruby')
|
|
20
|
+
# .and(framework: 'hanami')
|
|
21
|
+
# .desc(:users_count).all
|
|
22
|
+
#
|
|
23
|
+
# # the documents are fetched only when we invoke #all
|
|
24
|
+
#
|
|
25
|
+
# It implements Ruby's `Enumerable` and borrows some methods from
|
|
26
|
+
# `Array`. Expect a query to act like them.
|
|
27
|
+
#
|
|
28
|
+
# @since 0.1.0
|
|
29
|
+
class Query
|
|
30
|
+
include RethinkDB::Shortcuts
|
|
31
|
+
include Enumerable
|
|
32
|
+
extend Forwardable
|
|
33
|
+
|
|
34
|
+
def_delegators :all, :each, :to_s, :empty?
|
|
35
|
+
|
|
36
|
+
# @attr_reader conditions [Array] an accumulator for the called
|
|
37
|
+
# methods
|
|
38
|
+
#
|
|
39
|
+
# @since 0.1.0
|
|
40
|
+
# @api private
|
|
41
|
+
attr_reader :conditions
|
|
42
|
+
|
|
43
|
+
# Initialize a query
|
|
44
|
+
#
|
|
45
|
+
# @param collection [Hanami::Model::Adapters::Rethinkdb::Collection]
|
|
46
|
+
# the collection to query
|
|
47
|
+
#
|
|
48
|
+
# @param blk [Proc] an optional block that gets yielded in the
|
|
49
|
+
# context of the current query
|
|
50
|
+
#
|
|
51
|
+
# @return [Hanami::Model::Adapters::Rethinkdb::Query]
|
|
52
|
+
def initialize(collection, context = nil, &blk)
|
|
53
|
+
@collection, @context = collection, context
|
|
54
|
+
@conditions = []
|
|
55
|
+
|
|
56
|
+
instance_eval(&blk) if block_given?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Resolves the query by fetching documents from the database and
|
|
60
|
+
# translating them into entities.
|
|
61
|
+
#
|
|
62
|
+
# @return [Array] a collection of entities
|
|
63
|
+
#
|
|
64
|
+
# @since 0.1.0
|
|
65
|
+
def all
|
|
66
|
+
scoped.execute
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Adds a condition like SQL `WHERE` using r.filter().
|
|
70
|
+
#
|
|
71
|
+
# It accepts a `Hash` with only one pair.
|
|
72
|
+
# The key must be the name of the field expressed as a `Symbol`.
|
|
73
|
+
# The value is the one used by the ReQL query
|
|
74
|
+
#
|
|
75
|
+
# @param condition [Hash]
|
|
76
|
+
#
|
|
77
|
+
# @return self
|
|
78
|
+
#
|
|
79
|
+
# @since 0.1.0
|
|
80
|
+
#
|
|
81
|
+
# @example Fixed value
|
|
82
|
+
#
|
|
83
|
+
# query.where(language: 'ruby')
|
|
84
|
+
#
|
|
85
|
+
# # => r.filter(language: 'ruby')
|
|
86
|
+
#
|
|
87
|
+
# @example Multiple conditions
|
|
88
|
+
#
|
|
89
|
+
# query.where(language: 'ruby')
|
|
90
|
+
# .where(framework: 'hanami')
|
|
91
|
+
#
|
|
92
|
+
# # => r.filter(language: 'ruby').filter('framework: 'hanami')
|
|
93
|
+
#
|
|
94
|
+
# @example Blocks
|
|
95
|
+
#
|
|
96
|
+
# query.where { |doc| doc['age'] > 10 }
|
|
97
|
+
#
|
|
98
|
+
# # => r.filter { |doc| doc.bracket('age').gt('10') }
|
|
99
|
+
def where(condition = nil, &blk)
|
|
100
|
+
condition = condition || blk ||
|
|
101
|
+
fail(ArgumentError, 'You need to specify a condition.')
|
|
102
|
+
conditions.push([:filter, condition])
|
|
103
|
+
self
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
alias_method :and, :where
|
|
107
|
+
|
|
108
|
+
# Pluck only the specified fields. Documents without the fields are
|
|
109
|
+
# omitted.
|
|
110
|
+
#
|
|
111
|
+
# By default a query includes all the fields of a table.
|
|
112
|
+
#
|
|
113
|
+
# @param fields [Array<Symbol>]
|
|
114
|
+
#
|
|
115
|
+
# @return self
|
|
116
|
+
#
|
|
117
|
+
# @since 0.1.0
|
|
118
|
+
#
|
|
119
|
+
# @example Single field
|
|
120
|
+
#
|
|
121
|
+
# query.pluck(:name)
|
|
122
|
+
#
|
|
123
|
+
# # => r.pluck(:name)
|
|
124
|
+
#
|
|
125
|
+
# @example Multiple fields
|
|
126
|
+
#
|
|
127
|
+
# query.pluck(:name, :year)
|
|
128
|
+
#
|
|
129
|
+
# # => r.pluck(:name, :year)
|
|
130
|
+
def pluck(*fields)
|
|
131
|
+
conditions.push([:pluck, *fields])
|
|
132
|
+
self
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Only include documents with the given fields.
|
|
136
|
+
#
|
|
137
|
+
# @param fields [Array<Symbol>]
|
|
138
|
+
#
|
|
139
|
+
# @return self
|
|
140
|
+
#
|
|
141
|
+
# @since 0.1.0
|
|
142
|
+
#
|
|
143
|
+
# @example Single column
|
|
144
|
+
#
|
|
145
|
+
# query.has_fields(:name)
|
|
146
|
+
#
|
|
147
|
+
# # => r.has_fields(:name)
|
|
148
|
+
#
|
|
149
|
+
# @example Multiple columns
|
|
150
|
+
#
|
|
151
|
+
# query.has_fields(:name, :year)
|
|
152
|
+
#
|
|
153
|
+
# # => r.has_fields(:name, :year)
|
|
154
|
+
def has_fields(*fields) # rubocop:disable Style/PredicateName
|
|
155
|
+
conditions.push([:has_fields, *fields])
|
|
156
|
+
self
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Limit the number of documents to return.
|
|
160
|
+
#
|
|
161
|
+
# This operation is performed at the database level with r.limit().
|
|
162
|
+
#
|
|
163
|
+
# @param number [Fixnum]
|
|
164
|
+
#
|
|
165
|
+
# @return self
|
|
166
|
+
#
|
|
167
|
+
# @since 0.1.0
|
|
168
|
+
#
|
|
169
|
+
# @example
|
|
170
|
+
#
|
|
171
|
+
# query.limit(1)
|
|
172
|
+
#
|
|
173
|
+
# # => r.limit(1)
|
|
174
|
+
def limit(number)
|
|
175
|
+
conditions.push([:limit, number])
|
|
176
|
+
self
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Specify the ascending order of the documents, sorted by the given
|
|
180
|
+
# fields or index. Identify an index using `{ index: :key }`.
|
|
181
|
+
#
|
|
182
|
+
# The last invokation of this method takes precidence. Previously
|
|
183
|
+
# called sorts will be overwritten by RethinkDB.
|
|
184
|
+
#
|
|
185
|
+
# @param fields [Array<Symbol, Hash>] the field names, optionally with
|
|
186
|
+
# an index identifier
|
|
187
|
+
#
|
|
188
|
+
# @return self
|
|
189
|
+
#
|
|
190
|
+
# @since 0.1.0
|
|
191
|
+
#
|
|
192
|
+
# @see Hanami::Model::Adapters::Rethinkdb::Query#desc
|
|
193
|
+
#
|
|
194
|
+
# @example Single field
|
|
195
|
+
#
|
|
196
|
+
# query.order(:name)
|
|
197
|
+
#
|
|
198
|
+
# # => r.order_by(:name)
|
|
199
|
+
#
|
|
200
|
+
# @example Multiple columns
|
|
201
|
+
#
|
|
202
|
+
# query.order(:name, :year)
|
|
203
|
+
#
|
|
204
|
+
# # => r.order_by(:name, :year)
|
|
205
|
+
#
|
|
206
|
+
# @example Single index
|
|
207
|
+
#
|
|
208
|
+
# query.order(index: :date)
|
|
209
|
+
#
|
|
210
|
+
# # => r.order_by(index: :date)
|
|
211
|
+
#
|
|
212
|
+
# @example Mixed fields and index
|
|
213
|
+
#
|
|
214
|
+
# query.order(:name, :year, index: :date)
|
|
215
|
+
#
|
|
216
|
+
# # => r.order_by(:name, :year, index: :date)
|
|
217
|
+
def order(*fields)
|
|
218
|
+
conditions.push([:order_by, *fields])
|
|
219
|
+
self
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
alias_method :asc, :order
|
|
223
|
+
|
|
224
|
+
# Specify the descending order of the documents, sorted by the given
|
|
225
|
+
# fields or index. Identify an index using `{ index: :key }`.
|
|
226
|
+
#
|
|
227
|
+
# The last invokation of this method takes precidence. Previously
|
|
228
|
+
# called sorts will be overwritten by RethinkDB.
|
|
229
|
+
#
|
|
230
|
+
# @return self
|
|
231
|
+
#
|
|
232
|
+
# @since 0.1.0
|
|
233
|
+
#
|
|
234
|
+
# @see Hanami::Model::Adapters::Rethinkdb::Query#desc
|
|
235
|
+
#
|
|
236
|
+
# @example Single field
|
|
237
|
+
#
|
|
238
|
+
# query.desc(:name)
|
|
239
|
+
#
|
|
240
|
+
# # => r.order_by(r.desc(:name))
|
|
241
|
+
#
|
|
242
|
+
# @example Multiple columns
|
|
243
|
+
#
|
|
244
|
+
# query.desc(:name, :year)
|
|
245
|
+
#
|
|
246
|
+
# # => r.order_by(r.desc(:name), r.desc(:year))
|
|
247
|
+
#
|
|
248
|
+
# @example Single index
|
|
249
|
+
#
|
|
250
|
+
# query.desc(index: :date)
|
|
251
|
+
#
|
|
252
|
+
# # => r.order_by(index: r.desc(:date))
|
|
253
|
+
#
|
|
254
|
+
# @example Mixed fields and index
|
|
255
|
+
#
|
|
256
|
+
# query.desc(:name, :year, { index: r.desc(:date) })
|
|
257
|
+
#
|
|
258
|
+
# # => r.order_by(r.desc(:name), r.desc(:year), { index:
|
|
259
|
+
# r.desc(:date) })
|
|
260
|
+
def desc(*fields)
|
|
261
|
+
conditions.push([:order_by, *_desc_wrapper(*fields)])
|
|
262
|
+
self
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Returns the sum of the values for the given field.
|
|
266
|
+
#
|
|
267
|
+
# @param field [Symbol] the field name
|
|
268
|
+
#
|
|
269
|
+
# @return [Numeric]
|
|
270
|
+
#
|
|
271
|
+
# @since 0.1.0
|
|
272
|
+
#
|
|
273
|
+
# @example
|
|
274
|
+
#
|
|
275
|
+
# query.sum(:comments_count)
|
|
276
|
+
#
|
|
277
|
+
# # => r.sum(:comments_count)
|
|
278
|
+
def sum(field)
|
|
279
|
+
scoped.sum(field)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Returns the average of the values for the given field.
|
|
283
|
+
#
|
|
284
|
+
# @param field [Symbol] the column name
|
|
285
|
+
#
|
|
286
|
+
# @return [Numeric]
|
|
287
|
+
#
|
|
288
|
+
# @since 0.1.0
|
|
289
|
+
#
|
|
290
|
+
# @example
|
|
291
|
+
#
|
|
292
|
+
# query.average(:comments_count)
|
|
293
|
+
#
|
|
294
|
+
# # => r.avg(:comments_count)
|
|
295
|
+
def average(field)
|
|
296
|
+
scoped.avg(field)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
alias_method :avg, :average
|
|
300
|
+
|
|
301
|
+
# Returns the maximum value for the given field.
|
|
302
|
+
#
|
|
303
|
+
# @param field [Symbol] the field name
|
|
304
|
+
#
|
|
305
|
+
# @return result
|
|
306
|
+
#
|
|
307
|
+
# @since 0.1.0
|
|
308
|
+
#
|
|
309
|
+
# @example With numeric type
|
|
310
|
+
#
|
|
311
|
+
# query.max(:comments_count)
|
|
312
|
+
#
|
|
313
|
+
# # r.max(:comments_count)
|
|
314
|
+
#
|
|
315
|
+
# @example With string type
|
|
316
|
+
#
|
|
317
|
+
# query.max(:title)
|
|
318
|
+
#
|
|
319
|
+
# # => r.max(:title)
|
|
320
|
+
def max(field)
|
|
321
|
+
has_fields(field)
|
|
322
|
+
scoped.max(field)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Returns the minimum value for the given field.
|
|
326
|
+
#
|
|
327
|
+
# @param field [Symbol] the field name
|
|
328
|
+
#
|
|
329
|
+
# @return result
|
|
330
|
+
#
|
|
331
|
+
# @since 0.1.0
|
|
332
|
+
#
|
|
333
|
+
# @example With numeric type
|
|
334
|
+
#
|
|
335
|
+
# query.min(:comments_count)
|
|
336
|
+
#
|
|
337
|
+
# # => r.min(:comments_count)
|
|
338
|
+
#
|
|
339
|
+
# @example With string type
|
|
340
|
+
#
|
|
341
|
+
# query.min(:title)
|
|
342
|
+
#
|
|
343
|
+
# # => r.min(:title)
|
|
344
|
+
def min(field)
|
|
345
|
+
has_fields(field)
|
|
346
|
+
scoped.min(field)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Returns a count of the records for the current conditions.
|
|
350
|
+
#
|
|
351
|
+
# @return [Fixnum]
|
|
352
|
+
#
|
|
353
|
+
# @since 0.1.0
|
|
354
|
+
#
|
|
355
|
+
# @example
|
|
356
|
+
#
|
|
357
|
+
# query.where(author_id: 23).count # => 5
|
|
358
|
+
def count
|
|
359
|
+
scoped.count
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Apply all the conditions and returns a filtered collection.
|
|
363
|
+
#
|
|
364
|
+
# This operation is idempotent, and the returned result didn't
|
|
365
|
+
# fetched the documents yet.
|
|
366
|
+
#
|
|
367
|
+
# @return [Hanami::Model::Adapters::Rethinkdb::Collection]
|
|
368
|
+
#
|
|
369
|
+
# @since 0.1.0
|
|
370
|
+
def scoped
|
|
371
|
+
scope = @collection
|
|
372
|
+
|
|
373
|
+
conditions.each do |(method, *args)|
|
|
374
|
+
scope = scope.public_send(method, *args)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
scope
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
protected
|
|
381
|
+
|
|
382
|
+
# Handles missing methods for query combinations
|
|
383
|
+
#
|
|
384
|
+
# @api private
|
|
385
|
+
# @since 0.1.0
|
|
386
|
+
#
|
|
387
|
+
# @see Hanami::Model::Adapters:Rethinkdb::Query#apply
|
|
388
|
+
def method_missing(m, *args, &blk)
|
|
389
|
+
if @context.respond_to?(m)
|
|
390
|
+
apply @context.public_send(m, *args, &blk)
|
|
391
|
+
else
|
|
392
|
+
super
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
private
|
|
397
|
+
|
|
398
|
+
# Returns a new query that is the result of the merge of the current
|
|
399
|
+
# conditions with the ones of the given query.
|
|
400
|
+
#
|
|
401
|
+
# This is used to combine queries together in a Repository.
|
|
402
|
+
#
|
|
403
|
+
# @param query [Hanami::Model::Adapters::Rethinkdb::Query] the query
|
|
404
|
+
# to apply
|
|
405
|
+
#
|
|
406
|
+
# @return [Hanami::Model::Adapters::Rethinkdb::Query] a new query with
|
|
407
|
+
# the merged conditions
|
|
408
|
+
#
|
|
409
|
+
# @api private
|
|
410
|
+
# @since 0.1.0
|
|
411
|
+
#
|
|
412
|
+
# @example
|
|
413
|
+
# require 'hanami/model'
|
|
414
|
+
#
|
|
415
|
+
# class ArticleRepository
|
|
416
|
+
# include Hanami::Repository
|
|
417
|
+
#
|
|
418
|
+
# def self.by_author(author)
|
|
419
|
+
# query do
|
|
420
|
+
# where(author_id: author.id)
|
|
421
|
+
# end
|
|
422
|
+
# end
|
|
423
|
+
#
|
|
424
|
+
# def self.rank
|
|
425
|
+
# query.desc(:comments_count)
|
|
426
|
+
# end
|
|
427
|
+
#
|
|
428
|
+
# def self.rank_by_author(author)
|
|
429
|
+
# rank.by_author(author)
|
|
430
|
+
# end
|
|
431
|
+
# end
|
|
432
|
+
#
|
|
433
|
+
# # The code above combines two queries: `rank` and `by_author`.
|
|
434
|
+
# #
|
|
435
|
+
# # The first class method `rank` returns a `Rethinkdb::Query`
|
|
436
|
+
# # instance which doesn't respond to `by_author`. How to solve
|
|
437
|
+
# # this problem?
|
|
438
|
+
# #
|
|
439
|
+
# # 1. When we use `query` to fabricate a `Rethinkdb::Query` we
|
|
440
|
+
# # pass the current context (the repository itself) to the query
|
|
441
|
+
# # initializer.
|
|
442
|
+
# #
|
|
443
|
+
# # 2. When that query receives the `by_author` message, it's
|
|
444
|
+
# # captured by `method_missing` and dispatched to the repository.
|
|
445
|
+
# #
|
|
446
|
+
# # 3. The class method `by_author` returns a query too.
|
|
447
|
+
# #
|
|
448
|
+
# # 4. We just return a new query that is the result of the current
|
|
449
|
+
# # query's conditions (`rank`) and of the conditions from
|
|
450
|
+
# # `by_author`.
|
|
451
|
+
# #
|
|
452
|
+
# # You're welcome ;)
|
|
453
|
+
def apply(query)
|
|
454
|
+
dup.tap do |result|
|
|
455
|
+
result.conditions.push(*query.conditions)
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# Wrap the given fields with a desc operator.
|
|
460
|
+
#
|
|
461
|
+
# @return [Array] the wrapped fields
|
|
462
|
+
#
|
|
463
|
+
# @api private
|
|
464
|
+
# @since 0.1.0
|
|
465
|
+
def _desc_wrapper(*fields)
|
|
466
|
+
Array(fields).map do |field|
|
|
467
|
+
if field.is_a?(Hash)
|
|
468
|
+
field.merge(field) { |_k, v| r.desc(v) }
|
|
469
|
+
else
|
|
470
|
+
r.desc(field)
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
# Run the enclosed block on the database.
|
|
476
|
+
#
|
|
477
|
+
# @api private
|
|
478
|
+
# @since 0.1.0
|
|
479
|
+
def _run
|
|
480
|
+
yield.run(@connection)
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
end
|