sequel 4.20.0 → 4.21.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6daa0c1ef4f76e1d9bfdeedbf9208bf32646ab20
4
- data.tar.gz: f2cf0e139a84f465fe1146de98b767fad029fca9
3
+ metadata.gz: bd1a779cad72f3c5831e46f1cb9ee5c5fa0cb75a
4
+ data.tar.gz: 561c34f1d23f0eb77d59e483b2beab3ebed21172
5
5
  SHA512:
6
- metadata.gz: 28f1f4b25bd2c5db83ce6630ebd9310524dafb59a60ce9e72e3c8e6d12f0a9ea9eff9e1e2ec492fa3f7f655b76ee51b6f5a8e233a36b80370d51922d3024f504
7
- data.tar.gz: 7309df3435d53878e8aa1945fc728fedad302727d503af24c399285fbf511b70571ca7cbef4f9c1b5c615aac689d9971f8072b0c9e2fb77f89491410384be665
6
+ metadata.gz: c3a11392dd6ab72a6af845fe59b85a1bbde7827d27aa255b434129b76034ad1be980a5e45a37a0ea82b0849c1ab4cd2013c108c22f13e898c3d9c6da345d32f6
7
+ data.tar.gz: 327de977bc6020cd9413bc50d0df32a8432784ee99e9fa277dd74a89f7b6f3e3ef7a89159534bafeeb5b42c99d38f3881d5aee67e5bcb832354f94e8ed7391ef
data/CHANGELOG CHANGED
@@ -1,3 +1,21 @@
1
+ === 4.21.0 (2015-04-01)
2
+
3
+ * Support :tsquery and :tsvector options in Dataset#full_text_search on PostgreSQL, for using existing tsquery/tsvector expressions (jeremyevans)
4
+
5
+ * Fix TinyTds::Error being raised when trying to cancel a query on a closed connection in the tinytds adapter (jeremyevans)
6
+
7
+ * Add GenericExpression#!~ for inverting =~ on ruby 1.9 (similar to inverting a hash) (jeremyevans) (#979)
8
+
9
+ * Add GenericExpression#=~ for equality, inclusion, and pattern matching (similar to using a hash) (jeremyevans) (#979)
10
+
11
+ * Add Database#add_named_conversion_proc on PostgreSQL to make it easier to add conversion procs for types by name (jeremyevans)
12
+
13
+ * Make Sequel.pg_jsonb return JSONBOp instances instead of JSONOp instances when passed other than Array or Hash (jeremyevans) (#977)
14
+
15
+ * Demodulize default root name in json_serializer plugin (janko-m) (#968)
16
+
17
+ * Make Database#transaction work in after_commit/after_rollback blocks (jeremyevans)
18
+
1
19
  === 4.20.0 (2015-03-03)
2
20
 
3
21
  * Restore the use of AUTOINCREMENT on SQLite (jeremyevans) (#965)
data/README.rdoc CHANGED
@@ -223,6 +223,8 @@ Sequel also accepts expressions:
223
223
 
224
224
  my_posts = posts.where{stamp > Date.today << 1}
225
225
  # WHERE stamp > '2010-06-14'
226
+ my_posts = posts.where{stamp =~ Date.today}
227
+ # WHERE stamp = '2010-07-14'
226
228
 
227
229
  Some adapters will also let you specify Regexps:
228
230
 
data/Rakefile CHANGED
@@ -84,17 +84,18 @@ end
84
84
 
85
85
  begin
86
86
  begin
87
- # RSpec 1
88
- require "spec/rake/spectask"
89
- spec_class = Spec::Rake::SpecTask
90
- spec_files_meth = :spec_files=
91
- spec_opts_meth = :spec_opts=
92
- rescue LoadError
87
+ raise LoadError if ENV['RSPEC1']
93
88
  # RSpec 2
94
89
  require "rspec/core/rake_task"
95
90
  spec_class = RSpec::Core::RakeTask
96
91
  spec_files_meth = :pattern=
97
92
  spec_opts_meth = :rspec_opts=
93
+ rescue LoadError
94
+ # RSpec 1
95
+ require "spec/rake/spectask"
96
+ spec_class = Spec::Rake::SpecTask
97
+ spec_files_meth = :spec_files=
98
+ spec_opts_meth = :spec_opts=
98
99
  end
99
100
 
100
101
  spec = lambda do |name, files, d|
@@ -105,17 +105,22 @@ To filter by equality, you use the standard hash, which can be combined with oth
105
105
  items.where{Sequel.&({:category => 'ruby'}, (price + 100 < 200))}.sql
106
106
  #=> "SELECT * FROM items WHERE ((category = 'ruby') AND ((price + 100) < 200))"
107
107
 
108
+ You can also use the =~ operator:
109
+
110
+ items.where{(category =~ 'ruby') & (price + 100 < 200)}.sql
111
+ #=> "SELECT * FROM items WHERE ((category = 'ruby') AND ((price + 100) < 200))"
112
+
108
113
  This works with other hash values, such as arrays and ranges:
109
114
 
110
- items.where{Sequel.|({:category => ['ruby', 'other']}, (:price - 100 > 200))}.sql
115
+ items.where{Sequel.|({:category => ['ruby', 'other']}, (price - 100 > 200))}.sql
111
116
  #=> "SELECT * FROM items WHERE ((category IN ('ruby', 'other')) OR ((price - 100) <= 200))"
112
117
 
113
- items.where{Sequel.&({:price => (100..200)}, :active)}.sql
118
+ items.where{(price =~ (100..200)) & :active}.sql
114
119
  #=> "SELECT * FROM items WHERE ((price >= 100 AND price <= 200) AND active)"
115
120
 
116
121
  === Negating conditions
117
122
 
118
- You can use the exclude method to exclude conditions:
123
+ You can use the exclude method to exclude whole conditions:
119
124
 
120
125
  items.exclude(:category => 'ruby').sql
121
126
  #=> "SELECT * FROM items WHERE (category != 'ruby')"
@@ -126,6 +131,19 @@ You can use the exclude method to exclude conditions:
126
131
  items.exclude{price / 100 >= 200}.sql
127
132
  #=> "SELECT * FROM items WHERE ((price / 100) < 200)
128
133
 
134
+ To exclude only parts of conditions, you can use when in combination with Sequel.~ or the ~ method on Sequel expressions:
135
+
136
+ items.where{Sequel.&(Sequel.~(:category => 'ruby'), (price + 100 < 200))}.sql
137
+ #=> "SELECT * FROM items WHERE ((category != 'ruby') AND ((price + 100) < 200))"
138
+
139
+ items.where{~(category =~ 'ruby') & (price + 100 < 200)}.sql
140
+ #=> "SELECT * FROM items WHERE ((category != 'ruby') AND ((price + 100) < 200))"
141
+
142
+ On Ruby 1.9+, you can also use the !~ method:
143
+
144
+ items.where{(category !~ 'ruby') & (price + 100 < 200)}.sql
145
+ #=> "SELECT * FROM items WHERE ((category != 'ruby') AND ((price + 100) < 200))"
146
+
129
147
  === Comparing against column references
130
148
 
131
149
  You can also compare against other columns:
data/doc/querying.rdoc CHANGED
@@ -388,6 +388,12 @@ will be ANDed together:
388
388
  Artist.where(:name=>'A'...'M'){id > 5}
389
389
  # SELECT * FROM artists WHERE name >= 'A' AND name < 'M' AND id > 5
390
390
 
391
+ Using virtual row blocks, what you can do with single entry hash or an array with
392
+ a single two element array can also be done using the =~ method:
393
+
394
+ Artist.where{id =~ 5}
395
+ # SELECT * FROM artists WHERE id = 5
396
+
391
397
  === Symbols
392
398
 
393
399
  If you have a boolean column in the database, and you want only true
@@ -494,7 +500,22 @@ Or to use the NOT LIKE operator:
494
500
 
495
501
  Artist.exclude(Sequel.like(:name, '%J%'))
496
502
  # SELECT * FROM artists WHERE name NOT LIKE '%J%' ESCAPE '\'
503
+
504
+ You can use Sequel.~ to negate expressions:
505
+
506
+ Artist.where(Sequel.~(:id=>5))
507
+ # SELECT * FROM artists WHERE id != 5
508
+
509
+ On Sequel expression objects, you can use ~ to negate them:
497
510
 
511
+ Artist.where(~Sequel.like(:name, '%J%'))
512
+ # SELECT * FROM artists WHERE name NOT LIKE '%J%' ESCAPE '\'
513
+
514
+ On Ruby 1.9+, you can use !~ on Sequel expressions to create negated expressions:
515
+
516
+ Artist.where{ id !~ 5}
517
+ # SELECT * FROM artists WHERE id != 5
518
+
498
519
  === Removing
499
520
 
500
521
  To remove all existing filters, use +unfiltered+:
@@ -0,0 +1,94 @@
1
+ = New Features
2
+
3
+ * SQL::GenericExpression#=~ has been added as an alternative method
4
+ of specifying equality/inclusion/identity. Previously, you had to
5
+ use a hash. This led to some slightly weird looking syntax when
6
+ used inside virtual rows:
7
+
8
+ DB[:items].where{{function(:column)=>0}}
9
+ # SELECT FROM items WHERE function(column) = 0
10
+
11
+ You can now use =~ as an equivalent:
12
+
13
+ DB[:items].where{function(:column) =~ 0}
14
+ # SELECT FROM items WHERE function(column) = 0
15
+
16
+ Like when using a hash, this works also for inclusion:
17
+
18
+ DB[:items].where{function(:column) =~ [1,2,3]}
19
+ # SELECT FROM items WHERE function(column) IN (1, 2, 3)
20
+
21
+ for identity:
22
+
23
+ DB[:items].where{function(:column) =~ nil}
24
+ # SELECT FROM items WHERE function(column) IS NULL
25
+
26
+ and for matching (on MySQL/PostgreSQL):
27
+
28
+ DB[:items].where{function(:column) =~ /foo/i}
29
+ # SELECT FROM items WHERE function(column) ~* 'foo'
30
+
31
+ This new syntax makes more complex conditions simpler to express:
32
+
33
+ DB[:items].where{(function(:column) =~ 0) | (column =~ 1)}
34
+ # SELECT FROM items WHERE function(column) = 0 OR column = 1
35
+
36
+ compared to previous versions of Sequel:
37
+
38
+ DB[:items].where{Sequel.|({function(:column) => 0}, {:column => 1})}
39
+
40
+ On ruby 1.9+, you can also use SQL::GenericExpression#!~ to invert
41
+ the condition:
42
+
43
+ DB[:items].where{function(:column) !~ 0}
44
+ # SELECT FROM items WHERE function(column) != 0
45
+
46
+ DB[:items].where{function(:column) !~ [1,2,3]}
47
+ # SELECT FROM items WHERE function(column) NOT IN (1, 2, 3)
48
+
49
+ DB[:items].where{function(:column) !~ nil}
50
+ # SELECT FROM items WHERE function(column) IS NOT NULL
51
+
52
+ DB[:items].where{function(:column) !~ /foo/i}
53
+ # SELECT FROM items WHERE function(column) !~* 'foo'
54
+
55
+ This makes it simpler to write inverted conditions. Ruby 1.8
56
+ doesn't support overriding the !~ method, but you can still use the
57
+ unary ~ method to invert:
58
+
59
+ DB[:items].where{~(function(:column) =~ 0)}
60
+
61
+ * Database#add_named_conversion_proc has been added on PostgreSQL to
62
+ make it easier to add conversion procs by name instead of by OID:
63
+
64
+ DB.add_named_conversion_proc(:citext){|s| s}
65
+
66
+ * Database#full_text_search on PostgreSQL now supports :tsquery and
67
+ :tsvector options for using existing tsquery and/or tsvector
68
+ arguments, instead of assuming the arguments are query terms or
69
+ the text to be search.
70
+
71
+ = Other Improvements
72
+
73
+ * Database#transaction now works inside after_commit and
74
+ after_rollback hooks. Previously, it didn't work correctly as it
75
+ thought it was already inside the previously committed/rolled back
76
+ transaction.
77
+
78
+ * Sequel.pg_jsonb now returns JSONBOp instances instead of JSONOp
79
+ instances when passed other than Array or Hash.
80
+
81
+ * The tinytds adapter no longer tries to cancel a query on a closed
82
+ connection, which was causing an exception to be raised.
83
+
84
+ = Backwards Compatibility
85
+
86
+ * The default root name used in the JSON serializer is now demodulized
87
+ before being underscored. This changes the behavior when the model
88
+ is namespaced. For example, if the model class name is Mod::Model,
89
+ the previous default root name would be "mod/model", the new default
90
+ root name is "model".
91
+
92
+ * If you were calling =~ or !~ on SQL::GenericExpression objects and
93
+ expecting the default ruby behavior of returning nil for =~ and
94
+ true for !~, you'll have to update your code.
data/doc/sql.rdoc CHANGED
@@ -247,6 +247,10 @@ You can also specify this as an array of two element arrays:
247
247
 
248
248
  [[:column, 1]] # ("column" = 1)
249
249
 
250
+ For expression objects, you can also use the =~ method:
251
+
252
+ where{column =~ 1} # ("column" = 1)
253
+
250
254
  === Not Equal Operator (!=)
251
255
 
252
256
  You can specify a not equals condition by inverting the hash or array of two element arrays using <tt>Sequel.negate</tt> or <tt>Sequel.~</tt>:
@@ -256,11 +260,19 @@ You can specify a not equals condition by inverting the hash or array of two ele
256
260
  Sequel.~(:column => 1) # ("column" != 1)
257
261
  Sequel.~([[:column, 1]]) # ("column" != 1)
258
262
 
259
- The difference between the two is that negate only works on hashes and arrays of element arrays, and it negates all entries in the, while ~ does a general inversion. This is best shown by an example with multiple entries:
263
+ The difference between the two is that negate only works on hashes and arrays of element arrays, and it negates all entries in the hash or array, while ~ does a general inversion. This is best shown by an example with multiple entries:
260
264
 
261
265
  Sequel.negate(:column => 1, :foo => 2) # (("column" != 1) AND (foo != 2))
262
266
  Sequel.~(:column => 1, :foo => 2) # (("column" != 1) OR (foo != 2))
263
267
 
268
+ You can also use the ~ method on an equality expression:
269
+
270
+ where{~(column =~ 1)} # ("column" != 1)
271
+
272
+ On Ruby 1.9+, you can use the !~ method:
273
+
274
+ where{column !~ 1} # ("column" != 1)
275
+
264
276
  The most common need for not equals is in filters, in which case you can use the +exclude+ method:
265
277
 
266
278
  DB[:albums].exclude(:column=>1) # SELECT * FROM "albums" WHERE ("column" != 1)
@@ -279,6 +291,14 @@ As you may have guessed, Sequel switches from an = to an IN when the hash value
279
291
  {:column=>DB[:albums].select(:id)} # ("column" IN (SELECT "id" FROM "albums"))
280
292
  Sequel.~(:column=>DB[:albums].select(:id)) # ("column" NOT IN (SELECT "id" FROM "albums"))
281
293
 
294
+ Similar to =, you can also use =~ with expressions for inclusion:
295
+
296
+ where{column =~ [1, 2, 3]} # ("column" IN (1, 2, 3))
297
+
298
+ and on Ruby 1.9, !~ for exclusion:
299
+
300
+ where{column !~ [1, 2, 3]} # ("column" NOT IN (1, 2, 3))
301
+
282
302
  Sequel also supports the SQL EXISTS operator using <tt>Dataset#exists</tt>:
283
303
 
284
304
  DB[:albums].exists # EXISTS (SELECT * FROM albums)
@@ -297,6 +317,11 @@ Negation works the same way as it does for equality and inclusion:
297
317
  Sequel.~(:column=>true) # ("column" IS NOT TRUE)
298
318
  Sequel.~(:column=>false) # ("column" IS NOT FALSE)
299
319
 
320
+ Likewise, =~ works for identity (and Ruby 1.9, !~ for negative identity):
321
+
322
+ where{column =~ nil} # ("column" IS NULL)
323
+ where{column !~ nil} # ("column" IS NOT NULL)
324
+
300
325
  === Inversion Operator (NOT)
301
326
 
302
327
  Sequel's general inversion operator is ~, which works on symbols and most Sequel-specific expression objects:
data/doc/testing.rdoc CHANGED
@@ -166,6 +166,7 @@ The SEQUEL_INTEGRATION_URL environment variable specifies the Database connectio
166
166
 
167
167
  === Other
168
168
 
169
+ RSPEC1 :: Use RSpec 1 to run the tests even if a newer version is installed
169
170
  SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
170
171
  SEQUEL_CONNECTION_VALIDATOR :: Use the connection validator extension when running the specs
171
172
  SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
data/doc/validations.rdoc CHANGED
@@ -226,6 +226,7 @@ These methods check that the specified attributes can be valid integers or valid
226
226
 
227
227
  class Album < Sequel::Model
228
228
  def validate
229
+ super
229
230
  validates_includes [1, 2, 3, 4, 5], :rating
230
231
  end
231
232
  end
@@ -185,9 +185,21 @@ object returned, with the options for the window:
185
185
 
186
186
  == Operators
187
187
 
188
- VirtualRows use method_missing to handle almost all method calls. However, they
189
- have special handling of some operator methods to make certain things easier. The
190
- operators all use a prefix form.
188
+ VirtualRows use method_missing to handle almost all method calls. Since the
189
+ objects given by method_missing are SQL::Identifiers, SQL::QualifiedIdentifiers
190
+ or SQL::Functions, you can use all operators that they provide (see
191
+ DatasetFiltering[http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html#label-Filtering+using+expressions]):
192
+
193
+ ds.select{|o| o.price - 100}
194
+ ds.select{o.price - 100}
195
+ # SELECT (price - 100)
196
+
197
+ ds.where{|o| (o.price < 200) & (o.tax * 100 >= 23)}
198
+ ds.where{(price < 200) & (tax * 100 >= 0.23)}
199
+ # WHERE ((price < 200) AND ((tax * 100) >= 0.23))
200
+
201
+ However, VirtualRows have special handling of some operator methods to make
202
+ certain things easier. The operators all use a prefix form.
191
203
 
192
204
  === Math Operators
193
205
 
@@ -154,6 +154,15 @@ module Sequel
154
154
  # having callable values for the conversion proc for that type.
155
155
  attr_reader :conversion_procs
156
156
 
157
+ # Add a conversion proc for a named type. This should be used
158
+ # for types without fixed OIDs, which includes all types that
159
+ # are not included in a default PostgreSQL installation. If
160
+ # a block is given, it is used as the conversion proc, otherwise
161
+ # the conversion proc is looked up in the PG_NAMED_TYPES hash.
162
+ def add_named_conversion_proc(name, &block)
163
+ add_named_conversion_procs(conversion_procs, name=>(block || PG_NAMED_TYPES[name]))
164
+ end
165
+
157
166
  # Commit an existing prepared transaction with the given transaction
158
167
  # identifier string.
159
168
  def commit_prepared_transaction(transaction_id, opts=OPTS)
@@ -1284,22 +1293,33 @@ module Sequel
1284
1293
  # :phrase :: Similar to :plain, but also adding an ILIKE filter to ensure that
1285
1294
  # returned rows also include the exact phrase used.
1286
1295
  # :rank :: Set to true to order by the rank, so that closer matches are returned first.
1296
+ # :tsquery :: Specifies the terms argument is already a valid SQL expression returning a
1297
+ # tsquery, and can be used directly in the query.
1298
+ # :tsvector :: Specifies the cols argument is already a valid SQL expression returning a
1299
+ # tsvector, and can be used directly in the query.
1287
1300
  def full_text_search(cols, terms, opts = OPTS)
1288
1301
  lang = Sequel.cast(opts[:language] || 'simple', :regconfig)
1289
- terms = terms.join(' | ') if terms.is_a?(Array)
1290
- columns = full_text_string_join(cols)
1291
- query_func = (opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
1292
- vector = Sequel.function(:to_tsvector, lang, columns)
1293
- query = Sequel.function(query_func, lang, terms)
1294
1302
 
1295
- ds = where(Sequel.lit(["(", " @@ ", ")"], vector, query))
1303
+ unless opts[:tsvector]
1304
+ phrase_cols = full_text_string_join(cols)
1305
+ cols = Sequel.function(:to_tsvector, lang, phrase_cols)
1306
+ end
1307
+
1308
+ unless opts[:tsquery]
1309
+ phrase_terms = terms.is_a?(Array) ? terms.join(' | ') : terms
1310
+ query_func = (opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
1311
+ terms = Sequel.function(query_func, lang, phrase_terms)
1312
+ end
1313
+
1314
+ ds = where(Sequel.lit(["(", " @@ ", ")"], cols, terms))
1296
1315
 
1297
1316
  if opts[:phrase]
1298
- ds = ds.grep(cols, "%#{escape_like(terms)}%", :case_insensitive=>true)
1317
+ raise Error, "can't use :phrase with either :tsvector or :tsquery arguments to full_text_search together" if opts[:tsvector] || opts[:tsquery]
1318
+ ds = ds.grep(phrase_cols, "%#{escape_like(phrase_terms)}%", :case_insensitive=>true)
1299
1319
  end
1300
1320
 
1301
1321
  if opts[:rank]
1302
- ds = ds.order{ts_rank_cd(vector, query)}
1322
+ ds = ds.order{ts_rank_cd(cols, terms)}
1303
1323
  end
1304
1324
 
1305
1325
  ds
@@ -64,7 +64,7 @@ module Sequel
64
64
  rescue TinyTds::Error => e
65
65
  raise_error(e, :disconnect=>!c.active?)
66
66
  ensure
67
- r.cancel if r && c.sqlsent?
67
+ r.cancel if r && c.sqlsent? && c.active?
68
68
  end
69
69
  end
70
70
  end
@@ -199,7 +199,7 @@ module Sequel
199
199
  if pr = Sequel.synchronize{EXTENSIONS[ext]}
200
200
  pr.call(self)
201
201
  else
202
- raise(Error, "Extension #{ext} does not have specific support handling individual databases")
202
+ raise(Error, "Extension #{ext} does not have specific support handling individual databases (try: Sequel.extension #{ext.inspect})")
203
203
  end
204
204
  end
205
205
  self
@@ -187,20 +187,6 @@ module Sequel
187
187
  end
188
188
  end
189
189
 
190
- # Call all stored after_commit blocks for the given transaction
191
- def after_transaction_commit(conn)
192
- if ary = _trans(conn)[:after_commit]
193
- ary.each{|b| b.call}
194
- end
195
- end
196
-
197
- # Call all stored after_rollback blocks for the given transaction
198
- def after_transaction_rollback(conn)
199
- if ary = _trans(conn)[:after_rollback]
200
- ary.each{|b| b.call}
201
- end
202
- end
203
-
204
190
  # Whether the current thread/connection is already inside a transaction
205
191
  def already_in_transaction?(conn, opts)
206
192
  _trans(conn) && (!supports_savepoints? || !opts[:savepoint])
@@ -288,14 +274,10 @@ module Sequel
288
274
  # Remove the current thread from the list of active transactions
289
275
  def remove_transaction(conn, committed)
290
276
  if transaction_finished?(conn)
291
- begin
292
- if committed
293
- after_transaction_commit(conn)
294
- else
295
- after_transaction_rollback(conn)
296
- end
297
- ensure
298
- Sequel.synchronize{@transactions.delete(conn)}
277
+ callbacks = _trans(conn)[committed ? :after_commit : :after_rollback]
278
+ Sequel.synchronize{@transactions.delete(conn)}
279
+ if callbacks
280
+ callbacks.each{|b| b.call}
299
281
  end
300
282
  end
301
283
  end
@@ -42,7 +42,7 @@ module Sequel
42
42
  if pr = Sequel.synchronize{EXTENSIONS[ext]}
43
43
  pr.call(self)
44
44
  else
45
- raise(Error, "Extension #{ext} does not have specific support handling individual datasets")
45
+ raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
46
46
  end
47
47
  end
48
48
  self
@@ -3,7 +3,7 @@ module Sequel
3
3
  # PlaceholderLiteralizer allows you to record the application of arbitrary changes
4
4
  # to a dataset with placeholder arguments, recording where those placeholder arguments
5
5
  # are used in the query. When running the query, the literalization process is much
6
- # faster as Sequel can skip most of the work it normal has to do when literalizing a
6
+ # faster as Sequel can skip most of the work it normally has to do when literalizing a
7
7
  # dataset.
8
8
  #
9
9
  # Basically, this enables optimizations that allow Sequel to cache the SQL produced
@@ -27,7 +27,7 @@ module Sequel
27
27
  # loader = Sequel::Dataset::PlaceholderLiteralizer.loader(DB[:items]) do |pl, ds|
28
28
  # ds.join(pl.arg, :item_id=>:id)
29
29
  # end
30
- # loader(:cart_items)
30
+ # loader.all(:cart_items)
31
31
  #
32
32
  # Will not qualify the item_id column with cart_items. In this type of situation it's
33
33
  # best to add a table alias when joining:
@@ -35,7 +35,7 @@ module Sequel
35
35
  # loader = Sequel::Dataset::PlaceholderLiteralizer.loader(DB[:items]) do |pl, ds|
36
36
  # ds.join(Sequel.as(pl.arg, :t), :item_id=>:id)
37
37
  # end
38
- # loader(:cart_items)
38
+ # loader.all(:cart_items)
39
39
  #
40
40
  # There are other similar cases that are not handled, mainly when Sequel changes the
41
41
  # SQL produced depending on the types of the arguments.
@@ -41,7 +41,7 @@ module Sequel
41
41
  class Dataset
42
42
  module DatasetSourceAlias
43
43
  # Preprocess the list of sources and attempt to alias any
44
- # datasets in the sources to the first source of the resepctive
44
+ # datasets in the sources to the first source of the respective
45
45
  # dataset.
46
46
  def from(*source, &block)
47
47
  virtual_row_columns(source, block)
@@ -141,7 +141,7 @@ module Sequel
141
141
  module DatabaseMethods
142
142
  def self.extended(db)
143
143
  db.instance_eval do
144
- add_named_conversion_procs(conversion_procs, :hstore=>PG_NAMED_TYPES[:hstore])
144
+ add_named_conversion_proc(:hstore)
145
145
  @schema_type_classes[:hstore] = HStore
146
146
  end
147
147
  end
@@ -52,6 +52,14 @@
52
52
  # h.to_matrix # hstore_to_matrix(hstore_column)
53
53
  # h.values # avals(hstore_column)
54
54
  #
55
+ # Here are a couple examples for updating an existing hstore column:
56
+ #
57
+ # # Add a key, or update an existing key with a new value
58
+ # DB[:tab].update(:h=>Sequel.hstore_op(:h).concat('c'=>3))
59
+ #
60
+ # # Delete a key
61
+ # DB[:tab].update(:h=>Sequel.hstore_op(:h).delete('k1'))
62
+ #
55
63
  # See the PostgreSQL hstore function and operator documentation for more
56
64
  # details on what these functions and operators do.
57
65
  #
@@ -282,7 +282,7 @@ module Sequel
282
282
  end
283
283
  end
284
284
 
285
- # Wrap the array or hash in a Postgres::JSONArray or Postgres::JSONHash.
285
+ # Wrap the array or hash in a Postgres::JSONBArray or Postgres::JSONBHash.
286
286
  def pg_jsonb(v)
287
287
  case v
288
288
  when Postgres::JSONBArray, Postgres::JSONBHash
@@ -296,7 +296,7 @@ module Sequel
296
296
  when Postgres::JSONHash
297
297
  Postgres::JSONBHash.new(v.to_hash)
298
298
  else
299
- Sequel.pg_json_op(v)
299
+ Sequel.pg_jsonb_op(v)
300
300
  end
301
301
  end
302
302
  end
@@ -290,7 +290,9 @@ module Sequel
290
290
  end
291
291
 
292
292
  if root = opts[:root]
293
- root = model.send(:underscore, model.to_s) unless root.is_a?(String)
293
+ unless root.is_a?(String)
294
+ root = model.send(:underscore, model.send(:demodulize, model.to_s))
295
+ end
294
296
  h = {root => h}
295
297
  end
296
298
 
@@ -330,7 +332,7 @@ module Sequel
330
332
  opts.delete(:root)
331
333
  end
332
334
  unless collection_root.is_a?(String)
333
- collection_root = model.send(:pluralize, model.send(:underscore, model.to_s))
335
+ collection_root = model.send(:pluralize, model.send(:underscore, model.send(:demodulize, model.to_s)))
334
336
  end
335
337
  end
336
338
 
data/lib/sequel/sql.rb CHANGED
@@ -791,6 +791,38 @@ module Sequel
791
791
  end
792
792
  end
793
793
 
794
+ # This module includes methods for overriding the =~ method for SQL equality,
795
+ # inclusion, and pattern matching. It returns the same result that Sequel would
796
+ # return when using a hash with a single entry, where the receiver was the key
797
+ # and the argument was the value. Example:
798
+ #
799
+ # Sequel.expr(:a) =~ 1 # (a = 1)
800
+ # Sequel.expr(:a) =~ [1, 2] # (a IN [1, 2])
801
+ # Sequel.expr(:a) =~ nil # (a IS NULL)
802
+ #
803
+ # On Ruby 1.9+, this also adds the !~ method, for easily setting up not equals,
804
+ # exclusion, and inverse pattern matching. This is the same as as inverting the
805
+ # result of the =~ method
806
+ #
807
+ # Sequel.expr(:a) !~ 1 # (a != 1)
808
+ # Sequel.expr(:a) !~ [1, 2] # (a NOT IN [1, 2])
809
+ # Sequel.expr(:a) !~ nil # (a IS NOT NULL)
810
+ module PatternMatchMethods
811
+ # Set up an equality, inclusion, or pattern match operation, based on the type
812
+ # of the argument.
813
+ def =~(other)
814
+ BooleanExpression.send(:from_value_pair, self, other)
815
+ end
816
+
817
+ if RUBY_VERSION >= '1.9'
818
+ module_eval(<<-END, __FILE__, __LINE__+1)
819
+ def !~(other)
820
+ ~(self =~ other)
821
+ end
822
+ END
823
+ end
824
+ end
825
+
794
826
  # These methods are designed as replacements for the core extension operator
795
827
  # methods, so that Sequel is still easy to use if the core extensions are not
796
828
  # enabled.
@@ -1358,6 +1390,7 @@ module Sequel
1358
1390
  include InequalityMethods
1359
1391
  include NumericMethods
1360
1392
  include OrderMethods
1393
+ include PatternMatchMethods
1361
1394
  include StringMethods
1362
1395
  include SubscriptMethods
1363
1396
  end
@@ -3,7 +3,7 @@ module Sequel
3
3
  MAJOR = 4
4
4
  # The minor version of Sequel. Bumped for every non-patch level
5
5
  # release, generally around once a month.
6
- MINOR = 20
6
+ MINOR = 21
7
7
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
8
  # releases that fix regressions from previous versions.
9
9
  TINY = 0
@@ -924,6 +924,11 @@ describe "A PostgreSQL database" do
924
924
  @db[:posts].full_text_search(:title, 'rubinius ruby', :plain=>true).select_order_map(:title).should == ['jruby rubinius ruby maglev mri iron', 'ruby jruby maglev mri rubinius iron']
925
925
  @db[:posts].full_text_search(:title, 'jruby maglev', :plain=>true).select_order_map(:title).should == ['jruby rubinius ruby maglev mri iron', 'ruby jruby maglev mri rubinius iron']
926
926
 
927
+ @db[:posts].full_text_search(Sequel.function(:to_tsvector, 'simple', :title), 'rails', :tsvector=>true).all.should == [{:title=>'ruby rails', :body=>'yowsa'}]
928
+ @db[:posts].full_text_search(:title, Sequel.function(:to_tsquery, 'simple', 'rails'), :tsquery=>true).all.should == [{:title=>'ruby rails', :body=>'yowsa'}]
929
+ proc{@db[:posts].full_text_search(Sequel.function(:to_tsvector, 'simple', :title), 'rubinius ruby', :tsvector=>true, :phrase=>true)}.should raise_error(Sequel::Error)
930
+ proc{@db[:posts].full_text_search(:title, Sequel.function(:to_tsquery, 'simple', 'rails'), :tsquery=>true, :phrase=>true)}.should raise_error(Sequel::Error)
931
+
927
932
  @db[:posts].delete
928
933
  t1 = "bork " * 1000 + "ruby sequel"
929
934
  t2 = "ruby sequel " * 1000
@@ -899,6 +899,16 @@ shared_examples_for "Database#transaction" do
899
899
  @db.sqls.should == ['BEGIN', 'COMMIT']
900
900
  end
901
901
 
902
+ specify "should have transaction inside after_commit work correctly" do
903
+ @db.transaction{@db.after_commit{@db.transaction{@db.execute('foo')}}}
904
+ @db.sqls.should == ['BEGIN', 'COMMIT', 'BEGIN', 'foo', 'COMMIT']
905
+ end
906
+
907
+ specify "should have transaction inside after_rollback work correctly" do
908
+ @db.transaction(:rollback=>:always){@db.after_rollback{@db.transaction{@db.execute('foo')}}}
909
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'BEGIN', 'foo', 'COMMIT']
910
+ end
911
+
902
912
  specify "should not call after_commit if the transaction rolls back" do
903
913
  @db.transaction{@db.after_commit{@db.execute('foo')}; raise Sequel::Rollback}
904
914
  @db.sqls.should == ['BEGIN', 'ROLLBACK']
@@ -59,6 +59,34 @@ describe "Blockless Ruby Filters" do
59
59
  @d.l(~Sequel.expr(:x => nil)).should == '(x IS NOT NULL)'
60
60
  end
61
61
 
62
+ it "should support = and similar operations via =~ method" do
63
+ @d.l{x =~ 100}.should == '(x = 100)'
64
+ @d.l{x =~ 'a'}.should == '(x = \'a\')'
65
+ @d.l{x =~ true}.should == '(x IS TRUE)'
66
+ @d.l{x =~ false}.should == '(x IS FALSE)'
67
+ @d.l{x =~ nil}.should == '(x IS NULL)'
68
+ @d.l{x =~ (1...5)}.should == '((x >= 1) AND (x < 5))'
69
+ @d.l{x =~ [1,2,3]}.should == '(x IN (1, 2, 3))'
70
+
71
+ def @d.supports_regexp?; true end
72
+ @d.l{x =~ /blah/}.should == '(x ~ \'blah\')'
73
+ end
74
+
75
+ if RUBY_VERSION >= '1.9'
76
+ it "should support != and similar inversions via !~ method" do
77
+ @d.l{x !~ 100}.should == '(x != 100)'
78
+ @d.l{x !~ 'a'}.should == '(x != \'a\')'
79
+ @d.l{x !~ true}.should == '(x IS NOT TRUE)'
80
+ @d.l{x !~ false}.should == '(x IS NOT FALSE)'
81
+ @d.l{x !~ nil}.should == '(x IS NOT NULL)'
82
+ @d.l{x !~ (1...5)}.should == '((x < 1) OR (x >= 5))'
83
+ @d.l{x !~ [1,2,3]}.should == '(x NOT IN (1, 2, 3))'
84
+
85
+ def @d.supports_regexp?; true end
86
+ @d.l{x !~ /blah/}.should == '(x !~ \'blah\')'
87
+ end
88
+ end
89
+
62
90
  it "should support ~ via Hash and Regexp (if supported by database)" do
63
91
  def @d.supports_regexp?; true end
64
92
  @d.l(:x => /blah/).should == '(x ~ \'blah\')'
@@ -206,6 +206,19 @@ describe "Sequel::Plugins::JsonSerializer" do
206
206
  @album.to_json(:root=>true, :except => [:name, :artist_id]).to_s.should == '{"album":{"id":1}}'
207
207
  @album.to_json(:root=>true, :only => :name).to_s.should == '{"album":{"name":"RF"}}'
208
208
  end
209
+
210
+ it "should handle the :root option to qualify single records of namespaced models" do
211
+ module ::Namespace
212
+ class Album < Sequel::Model
213
+ plugin :json_serializer, :naked=>true
214
+ end
215
+ end
216
+ Namespace::Album.new({}).to_json(:root=>true).to_s.should == '{"album":{}}'
217
+ Namespace::Album.dataset._fetch = [{}]
218
+ Namespace::Album.dataset.to_json(:root=>:collection).to_s.should == '{"albums":[{}]}'
219
+ Namespace::Album.dataset.to_json(:root=>:both).to_s.should == '{"albums":[{"album":{}}]}'
220
+ Object.send(:remove_const, :Namespace)
221
+ end
209
222
 
210
223
  it "should handle the :root option with a string to qualify single records using the string as the key" do
211
224
  @album.to_json(:root=>"foo", :except => [:name, :artist_id]).to_s.should == '{"foo":{"id":1}}'
@@ -206,6 +206,7 @@ describe "Sequel::Postgres::JSONOp" do
206
206
  it "should be able to turn symbols into json ops using Sequel.pg_json" do
207
207
  @db.literal(Sequel.pg_json(:a)[1]).should == "(a -> 1)"
208
208
  @db.literal(Sequel.pg_jsonb(:a)[1]).should == "(a -> 1)"
209
+ @db.literal(Sequel.pg_jsonb(:a).contains('a'=>1)).should == "(a @> '{\"a\":1}'::jsonb)"
209
210
  end
210
211
 
211
212
  it "should allow transforming JSONArray instances into ArrayOp instances" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.20.0
4
+ version: 4.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-03 00:00:00.000000000 Z
11
+ date: 2015-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -193,6 +193,7 @@ extra_rdoc_files:
193
193
  - doc/release_notes/4.18.0.txt
194
194
  - doc/release_notes/4.19.0.txt
195
195
  - doc/release_notes/4.20.0.txt
196
+ - doc/release_notes/4.21.0.txt
196
197
  files:
197
198
  - CHANGELOG
198
199
  - MIT-LICENSE
@@ -301,6 +302,7 @@ files:
301
302
  - doc/release_notes/4.19.0.txt
302
303
  - doc/release_notes/4.2.0.txt
303
304
  - doc/release_notes/4.20.0.txt
305
+ - doc/release_notes/4.21.0.txt
304
306
  - doc/release_notes/4.3.0.txt
305
307
  - doc/release_notes/4.4.0.txt
306
308
  - doc/release_notes/4.5.0.txt