File: core_extensions.rdoc

package info (click to toggle)
ruby-sequel 5.63.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,408 kB
  • sloc: ruby: 113,747; makefile: 3
file content (405 lines) | stat: -rw-r--r-- 11,592 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
= Sequel's Core Extensions

== Background

Historically, Sequel added methods to many of the core classes, and usage of those methods was the primary and recommended way to use Sequel.  For example:

  DB[:table].select(:column.cast(Integer)). # Symbol#cast 
    where(:column.like('A%')).              # Symbol#like
    order({1=>2}.case(0, :a))               # Hash#case

While Sequel never overrode any methods defined by ruby, it is possible that other libraries could define the same methods that Sequel defines, which could cause problems.  Also, some rubyists do not like using libraries that add methods to the core classes.

Alternatives for the core extension methods were added to Sequel, so the query above could be written as:

  DB[:table].select(Sequel.cast(:column, Integer)).
    where(Sequel.like(:column, 'A%')).
    order(Sequel.case({1=>2}, 0, :a))

or with virtual rows:

  DB[:table].select{column.as(Integer)}.
    where{column.like('A%')}.
    order(Sequel.case({1=>2}, 0, :a))

Almost all of the core extension methods have a replacement on the Sequel module.  So it is now up to the user which style to use.  Using the methods on the Sequel module results in slightly more verbose code, but allows the code to work without modifications to the core classes.

== Issues

There is no recommendation on whether the core_extensions should be used or not.  It is very rare that any of the methods added by core_extensions actually causes a problem, but some of them can make it more difficult to find other problems.  For example, if you type:

  do_something if value | other_value

while meaning to type:

  do_something if value || other_value

and value is a Symbol, instead of a NoMethodError being raised because Symbol#| is not implemented by default, <tt>value | other_value</tt> will return a Sequel expression object, which if will evaluate as true, and do_something will be called.

== Usage

All of Sequel's extensions to the core classes are stored in Sequel's core_extensions extension, which you can load via:

  Sequel.extension :core_extensions

== No Internal Dependency

Sequel has no internal dependency on the core extensions.  This includes Sequel's core, Sequel::Model, and all plugins and extensions that ship with Sequel.  However, it is possible that external plugins and extensions will depend on the core extensions.  Such plugins and extensions should be updated so that they no longer depend on the core extensions.

== Refinements

Most of the these extensions can be added on a per-file basis using refinements (if you are using Ruby 2.0+).  To use refinements, first load them:

  Sequel.extension :core_refinements

Then for each file where you want to use the refinements:

  using Sequel::CoreRefinements

== Core Extension Methods

This section will briefly describe all of the methods added to the core classes, and what the alternative method is that doesn't require the core extensions.

=== Symbol & String

==== as

Symbol#as and String#as return Sequel aliased expressions using the provided alias:

  :a.as(:b)  # SQL: a AS b
  'a'.as(:b) # SQL: 'a' AS b

Alternatives:

  Sequel[:a].as(:b)
  Sequel.as(:a, :b)

==== cast

Symbol#cast and String#cast return Sequel cast expressions for typecasting in the database:

  :a.cast(Integer)  # SQL: CAST(a AS integer)
  'a'.cast(Integer) # SQL: CAST('a' AS integer)

Alternatives:

  Sequel[:a].cast(Integer)
  Sequel.cast(:a, Integer)

==== cast_numeric

Symbol#cast_numeric and String#cast_numeric return Sequel cast expressions for typecasting in the database, defaulting to integers, where the returned expression is treated as an numeric value:

  :a.cast_numeric         # SQL: CAST(a AS integer)
  'a'.cast_numeric(Float) # SQL: CAST('a' AS double precision)

Alternative:

  Sequel[:a].cast_numeric
  Sequel.cast_numeric(:a)

==== cast_string

Symbol#cast_string and String#cast_string return Sequel cast expressions for typecasting in the database, defaulting to strings, where the returned expression is treated as a string value:

  :a.cast_string         # SQL: CAST(a AS varchar(255))
  'a'.cast_string(:text) # SQL: CAST('a' AS text)

Alternatives:

  Sequel[:a].cast_string
  Sequel.cast_string(:a)

=== Symbol

==== identifier

Symbol#identifier wraps the symbol in an Sequel identifier object.  If symbol splitting is enabled (no longer the default), it also makes sure the symbol will not be split.  If symbol splitting is disabled (the default), there is little reason to use this).

  :column.identifier # SQL: column

Alternatives:

  Sequel[:column]
  Sequel.identifier(:column)

==== asc

Symbol#asc is used to define an ascending order on a column.  It exists mostly for consistency with #desc, since ascending is the default order:

  :a.asc # SQL: a ASC

Alternatives:

  Sequel[:a].asc
  Sequel.asc(:a)

==== desc

Symbol#desc is used to defined a descending order on a column.  The returned value is usually passed to one of the dataset order methods.

  :a.desc # SQL: a DESC

Alternatives:

  Sequel[:a].desc
  Sequel.desc(:a)

==== +, -, *, /

The standard mathematical operators are defined on Symbol, and return a Sequel numeric expression object representing the operation:

    :a + :b # SQL: a + b
    :a - :b # SQL: a - b
    :a * :b # SQL: a * b
    :a / :b # SQL: a / b 
    :a ** :b # SQL: power(a, b)

Sequel also supports ruby's coercion protocols on symbols (note that this does not work when using refinements):

    1 + :b # SQL: 1 + b

Alternatives:

    Sequel[:a] + :b
    Sequel[:a] - :b
    Sequel[:a] * :b
    Sequel[:a] / :b
    Sequel[:a] ** :b

    Sequel.+(:a, :b)
    Sequel.-(:a, :b)
    Sequel.*(:a, :b)
    Sequel./(:a, :b)
    Sequel.**(:a, :b)

==== *

The * operator is overloaded on Symbol such that if it is called with no arguments, it represents a selection of all columns in the table:

  :a.* # SQL: a.*

Alternative:

  Sequel[:a].*

==== qualify

Symbol#qualify qualifies the identifier (e.g. a column) with a another identifier (e.g. a table):

  :column.qualify(:table) # SQL: table.column

Alternative:

  Sequel[:table][:column]

Note the reversed order of the arguments.  For the Symbol#qualify method, the argument is the qualifier, while for Sequel[][], the first [] is the qualifier, and the second [] is the identifier.

==== like

Symbol#like returns a case sensitive LIKE expression between the identifier and the given argument:

  :a.like('b%') # SQL: a LIKE 'b%' ESCAPE '\'

Alternatives:

  Sequel[:a].like('b%')
  Sequel.like(:a, 'b%')

==== ilike

Symbol#ilike returns a case insensitive LIKE expression between the identifier and the given argument:

  :a.ilike('b%') # SQL: a ILIKE 'b%' ESCAPE '\'

Alternatives:

  Sequel[:a].ilike('b%')
  Sequel.ilike(:a, 'b%')

==== sql_subscript

Symbol#sql_subscript returns a Sequel expression representing an SQL array access:

  :a.sql_subscript(1) # SQL: a[1]

Alternatives:

  Sequel[:a].sql_subscript(1)
  Sequel.subscript(:a, 1)

==== extract

Symbol#extract does a datetime part extraction from the receiver:

  :a.extract(:year) # SQL: extract(year FROM a)

Alternatives:

  Sequel[:a].extract(:year)
  Sequel.extract(:year, :a)

==== sql_boolean, sql_number, sql_string

These Symbol methods are used to force the treating of the object as a specific SQL type, instead of as a general SQL type.  For example:

  :a.sql_boolean + 1  # NoMethodError
  :a.sql_number << 1  # SQL: a << 1
  :a.sql_string + 'a' # SQL: a || 'a'

Alternatives:

  Sequel[:a].sql_boolean
  Sequel[:a].sql_number
  Sequel[:a].sql_string

==== sql_function

Symbol#sql_function returns an SQL function call expression object:

  :now.sql_function # SQL: now()
  :sum.sql_function(:a) # SQL: sum(a)
  :concat.sql_function(:a, :b) # SQL: concat(a, b)

Alternatives:

  Sequel[:sum].function(:a)
  Sequel.function(:sum, :a)

=== String

==== lit

String#lit creates a literal string, using placeholders if any arguments are given.  Literal strings are not escaped, they are treated as SQL code, not as an SQL string:

  'a'.lit          # SQL: a
  '"a" = ?'.lit(1) # SQL: "a" = 1

Alternatives:

  Sequel.lit('a')
  Sequel.lit('a = ?', 1)

==== to_sequel_blob

String#to_sequel_blob returns the string wrapper in Sequel blob object.  Often blobs need to be handled differently than regular strings by the database adapters.

  "a\0".to_sequel_blob # SQL: X'6100'

Alternative:

  Sequel.blob("a\0")

=== Hash, Array, & Symbol

==== ~

Array#~, Hash#~, and Symbol#~ treat the receiver as a conditions specifier, not matching all of the conditions:

  ~{a: 1, b: [2, 3]}       # SQL: a != 1 OR b NOT IN (2, 3)
  ~[[:a, 1], [:b, [1, 2]]] # SQL: a != 1 OR b NOT IN (1, 2)

Alternatives:

   ~Sequel[a: 1, b: [2, 3]]
   Sequel.~(a: 1, b: [2, 3])

=== Hash & Array

==== case

Array#case and Hash#case return an SQL CASE expression, where the keys are conditions and the values are results:

  {{a: [2,3]} => 1}.case(0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
  [[{a: [2,3]}, 1]].case(0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END

Alternative:

  Sequel.case({{a: [2,3]}=>1}, 0)

==== sql_expr

Array#sql_expr and Hash#sql_expr treat the receiver as a conditions specifier, matching all of the conditions in the array.

  {a: 1, b: [2, 3]}.sql_expr     # SQL: a = 1 AND b IN (2, 3)
  [[:a, 1], [:b, [2, 3]]].sql_expr # SQL: a = 1 AND b IN (2, 3)

Alternative:

  Sequel[a: 1, b: [2, 3]]

==== sql_negate

Array#sql_negate and Hash#sql_negate treat the receiver as a conditions specifier, matching none of the conditions in the array:

  {a: 1, b: [2, 3]}.sql_negate       # SQL: a != 1 AND b NOT IN (2, 3)
  [[:a, 1], [:b, [2, 3]]].sql_negate # SQL: a != 1 AND b NOT IN (2, 3)

Alternative:

  Sequel.negate(a: 1, b: [2, 3])

==== sql_or

Array#sql_or nd Hash#sql_or treat the receiver as a conditions specifier, matching any of the conditions in the array:

  {a: 1, b: [2, 3]}.sql_or       # SQL: a = 1 OR b IN (2, 3)
  [[:a, 1], [:b, [2, 3]]].sql_or # SQL: a = 1 OR b IN (2, 3)

Alternative:

  Sequel.or(a: 1, b: [2, 3])

=== Array

==== sql_value_list

Array#sql_value_list wraps the array in an array subclass, which Sequel will always treat as a value list and not a conditions specifier.  By default, Sequel treats arrays of two element arrays as a conditions specifier.

  DB[:a].where('(a, b) IN ?', [[1, 2], [3, 4]])                # SQL: (a, b) IN ((1 = 2) AND (3 = 4))
  DB[:a].where('(a, b) IN ?', [[1, 2], [3, 4]].sql_value_list) # SQL: (a, b) IN ((1, 2), (3, 4))

Alternative:

  Sequel.value_list([[1, 2], [3, 4]])

==== sql_string_join

Array#sql_string_join joins all of the elements in the array in an SQL string concatentation expression:

  [:a].sql_string_join           # SQL: a
  [:a, :b].sql_string_join       # SQL: a || b
  [:a, 'b'].sql_string_join      # SQL: a || 'b'
  ['a', :b].sql_string_join(' ') # SQL: 'a' || ' ' || b

Alternative:

  Sequel.join(['a', :b], ' ')

=== Hash & Symbol

==== &

Hash#& and Symbol#& return a Sequel boolean expression, matching the condition specified by the receiver and the condition specified by the given argument:

  :a & :b        # SQL: a AND b
  {a: 1} & :b    # SQL: a = 1 AND b
  {a: true} & :b # SQL: a IS TRUE AND b

Alternatives:

  Sequel[a: 1] & :b
  Sequel.&({a: 1}, :b)

==== |

Hash#| returns a Sequel boolean expression, matching the condition specified by the receiver or the condition specified by the given argument:

  :a | :b        # SQL: a OR b
  {a: 1} | :b    # SQL: a = 1 OR b
  {a: true} | :b # SQL: a IS TRUE OR b

Alternative:

  Sequel[a: 1] | :b
  Sequel.|({a: 1}, :b)