sequel_core 1.5.1 → 2.0.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.
- data/CHANGELOG +116 -0
- data/COPYING +19 -19
- data/README +83 -32
- data/Rakefile +9 -20
- data/bin/sequel +43 -112
- data/doc/cheat_sheet.rdoc +225 -0
- data/doc/dataset_filtering.rdoc +257 -0
- data/lib/sequel_core/adapters/adapter_skeleton.rb +4 -2
- data/lib/sequel_core/adapters/ado.rb +3 -1
- data/lib/sequel_core/adapters/db2.rb +4 -2
- data/lib/sequel_core/adapters/dbi.rb +127 -113
- data/lib/sequel_core/adapters/informix.rb +4 -2
- data/lib/sequel_core/adapters/jdbc.rb +5 -3
- data/lib/sequel_core/adapters/mysql.rb +112 -46
- data/lib/sequel_core/adapters/odbc.rb +5 -7
- data/lib/sequel_core/adapters/odbc_mssql.rb +12 -3
- data/lib/sequel_core/adapters/openbase.rb +3 -1
- data/lib/sequel_core/adapters/oracle.rb +11 -9
- data/lib/sequel_core/adapters/postgres.rb +261 -262
- data/lib/sequel_core/adapters/sqlite.rb +72 -22
- data/lib/sequel_core/connection_pool.rb +140 -73
- data/lib/sequel_core/core_ext.rb +201 -66
- data/lib/sequel_core/core_sql.rb +123 -153
- data/lib/sequel_core/database/schema.rb +156 -0
- data/lib/sequel_core/database.rb +321 -338
- data/lib/sequel_core/dataset/callback.rb +11 -12
- data/lib/sequel_core/dataset/convenience.rb +213 -240
- data/lib/sequel_core/dataset/pagination.rb +58 -43
- data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +331 -0
- data/lib/sequel_core/dataset/query.rb +41 -0
- data/lib/sequel_core/dataset/schema.rb +15 -0
- data/lib/sequel_core/dataset/sequelizer.rb +41 -373
- data/lib/sequel_core/dataset/sql.rb +741 -632
- data/lib/sequel_core/dataset.rb +183 -168
- data/lib/sequel_core/deprecated.rb +1 -169
- data/lib/sequel_core/exceptions.rb +24 -19
- data/lib/sequel_core/migration.rb +44 -52
- data/lib/sequel_core/object_graph.rb +43 -42
- data/lib/sequel_core/pretty_table.rb +71 -76
- data/lib/sequel_core/schema/generator.rb +163 -105
- data/lib/sequel_core/schema/sql.rb +250 -93
- data/lib/sequel_core/schema.rb +2 -8
- data/lib/sequel_core/sql.rb +394 -0
- data/lib/sequel_core/worker.rb +37 -27
- data/lib/sequel_core.rb +99 -45
- data/spec/adapters/informix_spec.rb +0 -1
- data/spec/adapters/mysql_spec.rb +177 -124
- data/spec/adapters/oracle_spec.rb +0 -1
- data/spec/adapters/postgres_spec.rb +98 -58
- data/spec/adapters/sqlite_spec.rb +45 -4
- data/spec/blockless_filters_spec.rb +269 -0
- data/spec/connection_pool_spec.rb +21 -18
- data/spec/core_ext_spec.rb +169 -19
- data/spec/core_sql_spec.rb +56 -49
- data/spec/database_spec.rb +78 -17
- data/spec/dataset_spec.rb +300 -428
- data/spec/migration_spec.rb +1 -1
- data/spec/object_graph_spec.rb +5 -11
- data/spec/rcov.opts +1 -1
- data/spec/schema_generator_spec.rb +16 -4
- data/spec/schema_spec.rb +89 -10
- data/spec/sequelizer_spec.rb +56 -56
- data/spec/spec.opts +0 -5
- data/spec/spec_config.rb +7 -0
- data/spec/spec_config.rb.example +5 -5
- data/spec/spec_helper.rb +6 -0
- data/spec/worker_spec.rb +1 -1
- metadata +78 -63
data/lib/sequel_core/dataset.rb
CHANGED
@@ -1,13 +1,6 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
|
4
|
-
require 'base64'
|
5
|
-
|
6
|
-
require File.join(File.dirname(__FILE__), 'dataset/sql')
|
7
|
-
require File.join(File.dirname(__FILE__), 'dataset/sequelizer')
|
8
|
-
require File.join(File.dirname(__FILE__), 'dataset/convenience')
|
9
|
-
require File.join(File.dirname(__FILE__), 'dataset/callback')
|
10
|
-
require File.join(File.dirname(__FILE__), 'dataset/pagination')
|
1
|
+
%w'callback convenience pagination query schema sequelizer sql'.each do |f|
|
2
|
+
require "sequel_core/dataset/#{f}"
|
3
|
+
end
|
11
4
|
|
12
5
|
module Sequel
|
13
6
|
# A Dataset represents a view of a the data in a database, constrained by
|
@@ -16,6 +9,7 @@ module Sequel
|
|
16
9
|
#
|
17
10
|
# Query results are always retrieved on demand, so a dataset can be kept
|
18
11
|
# around and reused indefinitely:
|
12
|
+
#
|
19
13
|
# my_posts = DB[:posts].filter(:author => 'david') # no records are retrieved
|
20
14
|
# p my_posts.all # records are now retrieved
|
21
15
|
# ...
|
@@ -24,6 +18,7 @@ module Sequel
|
|
24
18
|
# In order to provide this functionality, dataset methods such as where,
|
25
19
|
# select, order, etc. return modified copies of the dataset, so you can
|
26
20
|
# use different datasets to access data:
|
21
|
+
#
|
27
22
|
# posts = DB[:posts]
|
28
23
|
# davids_posts = posts.filter(:author => 'david')
|
29
24
|
# old_posts = posts.filter('stamp < ?', Date.today - 7)
|
@@ -34,7 +29,7 @@ module Sequel
|
|
34
29
|
# === The Dataset Adapter Interface
|
35
30
|
#
|
36
31
|
# Each adapter should define its own dataset class as a descendant of
|
37
|
-
# Sequel::Dataset. The following methods should be
|
32
|
+
# Sequel::Dataset. The following methods should be overridden by the adapter
|
38
33
|
# Dataset class (each method with the stock implementation):
|
39
34
|
#
|
40
35
|
# # Iterate over the results of the SQL query and call the supplied
|
@@ -66,113 +61,158 @@ module Sequel
|
|
66
61
|
# @db.execute(delete_sql(opts)).affected_rows
|
67
62
|
# end
|
68
63
|
# end
|
64
|
+
#
|
65
|
+
# === Methods added via metaprogramming
|
66
|
+
#
|
67
|
+
# Some methods are added via metaprogramming:
|
68
|
+
#
|
69
|
+
# * ! methods - These methods are the same as their non-! counterparts,
|
70
|
+
# but they modify the receiver instead of returning a modified copy
|
71
|
+
# of the dataset.
|
72
|
+
# * inner_join, full_outer_join, right_outer_join, left_outer_join -
|
73
|
+
# This methods are shortcuts to join_table with the join type
|
74
|
+
# already specified.
|
69
75
|
class Dataset
|
70
76
|
include Enumerable
|
71
|
-
include Sequelizer
|
72
|
-
include SQL
|
73
|
-
include Convenience
|
74
|
-
include Callback
|
75
77
|
|
78
|
+
# The dataset options that require the removal of cached columns
|
79
|
+
# if changed.
|
80
|
+
COLUMN_CHANGE_OPTS = [:select, :sql, :from, :join].freeze
|
81
|
+
|
82
|
+
# Array of all subclasses of Dataset
|
83
|
+
DATASET_CLASSES = []
|
84
|
+
|
85
|
+
# All methods that should have a ! method added that modifies
|
86
|
+
# the receiver.
|
87
|
+
MUTATION_METHODS = %w'and distinct exclude exists filter from from_self full_outer_join graph
|
88
|
+
group group_and_count group_by having inner_join intersect invert join
|
89
|
+
left_outer_join limit naked or order order_by order_more paginate query reject
|
90
|
+
reverse reverse_order right_outer_join select select_all select_more
|
91
|
+
set_graph_aliases set_model sort sort_by unfiltered union unordered where'.collect{|x| x.to_sym}
|
92
|
+
|
93
|
+
NOTIMPL_MSG = "This method must be overridden in Sequel adapters".freeze
|
94
|
+
STOCK_TRANSFORMS = {
|
95
|
+
:marshal => [
|
96
|
+
# for backwards-compatibility we support also non-base64-encoded values.
|
97
|
+
proc {|v| Marshal.load(v.unpack('m')[0]) rescue Marshal.load(v)},
|
98
|
+
proc {|v| [Marshal.dump(v)].pack('m')}
|
99
|
+
],
|
100
|
+
:yaml => [
|
101
|
+
proc {|v| YAML.load v if v},
|
102
|
+
proc {|v| v.to_yaml}
|
103
|
+
]
|
104
|
+
}
|
105
|
+
|
76
106
|
attr_accessor :db, :opts, :row_proc
|
107
|
+
|
108
|
+
# Whether to quote identifiers for this dataset
|
109
|
+
attr_writer :quote_identifiers
|
77
110
|
|
78
|
-
|
79
|
-
|
80
|
-
#
|
81
|
-
# the array is iterated over.
|
82
|
-
def all(opts = nil, &block)
|
83
|
-
a = []
|
84
|
-
each(opts) {|r| a << r}
|
85
|
-
post_load(a)
|
86
|
-
a.each(&block) if block
|
87
|
-
a
|
88
|
-
end
|
89
|
-
|
90
|
-
# Constructs a new instance of a dataset with a database instance, initial
|
91
|
-
# options and an optional record class. Datasets are usually constructed by
|
92
|
-
# invoking Database methods:
|
111
|
+
# Constructs a new instance of a dataset with an associated database and
|
112
|
+
# options. Datasets are usually constructed by invoking Database methods:
|
113
|
+
#
|
93
114
|
# DB[:posts]
|
115
|
+
#
|
94
116
|
# Or:
|
117
|
+
#
|
95
118
|
# DB.dataset # the returned dataset is blank
|
96
119
|
#
|
97
120
|
# Sequel::Dataset is an abstract class that is not useful by itself. Each
|
98
121
|
# database adaptor should provide a descendant class of Sequel::Dataset.
|
99
122
|
def initialize(db, opts = nil)
|
100
123
|
@db = db
|
124
|
+
@quote_identifiers = db.quote_identifiers? if db.respond_to?(:quote_identifiers?)
|
101
125
|
@opts = opts || {}
|
102
126
|
@row_proc = nil
|
103
127
|
@transform = nil
|
104
128
|
end
|
105
129
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
130
|
+
### Class Methods ###
|
131
|
+
|
132
|
+
# The array of dataset subclasses.
|
133
|
+
def self.dataset_classes
|
134
|
+
DATASET_CLASSES
|
135
|
+
end
|
136
|
+
|
137
|
+
# Setup mutation (e.g. filter!) methods. These operate the same as the
|
138
|
+
# non-! methods, but replace the options of the current dataset with the
|
139
|
+
# options of the resulting dataset.
|
140
|
+
def self.def_mutation_method(*meths)
|
141
|
+
meths.each do |meth|
|
142
|
+
class_eval("def #{meth}!(*args, &block); mutation_method(:#{meth}, *args, &block) end")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Add the subclass to the array of subclasses.
|
147
|
+
def self.inherited(c)
|
148
|
+
DATASET_CLASSES << c
|
112
149
|
end
|
113
150
|
|
114
|
-
|
115
|
-
|
116
|
-
#
|
117
|
-
#
|
118
|
-
def
|
119
|
-
|
120
|
-
# r = @db.execute(sql)
|
121
|
-
# r.each(&block)
|
122
|
-
# end
|
123
|
-
raise NotImplementedError, NOTIMPL_MSG
|
151
|
+
### Instance Methods ###
|
152
|
+
|
153
|
+
# Alias for insert, but not aliased directly so subclasses
|
154
|
+
# don't have to override both methods.
|
155
|
+
def <<(*args)
|
156
|
+
insert(*args)
|
124
157
|
end
|
125
|
-
|
126
|
-
#
|
127
|
-
#
|
128
|
-
def
|
129
|
-
|
130
|
-
# @db.execute(insert_sql(*values)).last_insert_id
|
131
|
-
# end
|
132
|
-
raise NotImplementedError, NOTIMPL_MSG
|
158
|
+
|
159
|
+
# Return the dataset as a column with the given alias, so it can be used in the
|
160
|
+
# SELECT clause. This dataset should result in a single row and a single column.
|
161
|
+
def as(a)
|
162
|
+
::Sequel::SQL::ColumnExpr.new(self, 'AS', a)
|
133
163
|
end
|
134
|
-
|
135
|
-
#
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
164
|
+
|
165
|
+
# Returns an array with all records in the dataset. If a block is given,
|
166
|
+
# the array is iterated over after all items have been loaded.
|
167
|
+
def all(opts = nil, &block)
|
168
|
+
a = []
|
169
|
+
each(opts) {|r| a << r}
|
170
|
+
post_load(a)
|
171
|
+
a.each(&block) if block
|
172
|
+
a
|
141
173
|
end
|
142
174
|
|
143
|
-
#
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
175
|
+
# Returns a new clone of the dataset with with the given options merged.
|
176
|
+
# If the options changed include options in COLUMN_CHANGE_OPTS, the cached
|
177
|
+
# columns are deleted.
|
178
|
+
def clone(opts = {})
|
179
|
+
c = super()
|
180
|
+
c.opts = @opts.merge(opts)
|
181
|
+
c.instance_variable_set(:@columns, nil) unless (opts.keys & COLUMN_CHANGE_OPTS).empty?
|
182
|
+
c
|
149
183
|
end
|
150
184
|
|
151
|
-
# Returns the columns in the result set in their true order.
|
152
|
-
#
|
153
|
-
# a query is performed
|
154
|
-
# column information when a query is performed.
|
185
|
+
# Returns the columns in the result set in their true order.
|
186
|
+
# If the columns are currently cached, returns the cached value. Otherwise,
|
187
|
+
# a SELECT query is performed to get a single row. Adapters are expected
|
188
|
+
# to fill the columns cache with the column information when a query is performed.
|
189
|
+
# If the dataset does not have any rows, this will be an empty array.
|
190
|
+
# If you are looking for all columns for a single table, see Schema::SQL#schema.
|
155
191
|
def columns
|
156
|
-
|
192
|
+
single_record unless @columns
|
157
193
|
@columns || []
|
158
194
|
end
|
159
195
|
|
196
|
+
# Remove the cached list of columns and do a SELECT query to find
|
197
|
+
# the columns.
|
160
198
|
def columns!
|
161
|
-
|
162
|
-
|
199
|
+
@columns = nil
|
200
|
+
columns
|
163
201
|
end
|
164
202
|
|
165
|
-
#
|
166
|
-
def
|
167
|
-
|
203
|
+
# Add a mutation method to this dataset instance.
|
204
|
+
def def_mutation_method(*meths)
|
205
|
+
meths.each do |meth|
|
206
|
+
instance_eval("def #{meth}!(*args, &block); mutation_method(:#{meth}, *args, &block) end")
|
207
|
+
end
|
168
208
|
end
|
169
|
-
|
170
|
-
#
|
171
|
-
def
|
172
|
-
|
209
|
+
|
210
|
+
# Deletes the records in the dataset. Adapters should override this method.
|
211
|
+
def delete(opts = nil)
|
212
|
+
raise NotImplementedError, NOTIMPL_MSG
|
173
213
|
end
|
174
214
|
|
175
|
-
# Iterates over the records in the dataset
|
215
|
+
# Iterates over the records in the dataset.
|
176
216
|
def each(opts = nil, &block)
|
177
217
|
if graph = @opts[:graph]
|
178
218
|
graph_each(opts, &block)
|
@@ -188,25 +228,56 @@ module Sequel
|
|
188
228
|
self
|
189
229
|
end
|
190
230
|
|
231
|
+
# Executes a select query and fetches records, passing each record to the
|
232
|
+
# supplied block. Adapters should override this method.
|
233
|
+
def fetch_rows(sql, &block)
|
234
|
+
raise NotImplementedError, NOTIMPL_MSG
|
235
|
+
end
|
236
|
+
|
237
|
+
# Inserts values into the associated table. Adapters should override this
|
238
|
+
# method.
|
239
|
+
def insert(*values)
|
240
|
+
raise NotImplementedError, NOTIMPL_MSG
|
241
|
+
end
|
242
|
+
|
243
|
+
# Returns a string representation of the dataset including the class name
|
244
|
+
# and the corresponding SQL select statement.
|
245
|
+
def inspect
|
246
|
+
"#<#{self.class}: #{sql.inspect}>"
|
247
|
+
end
|
248
|
+
|
191
249
|
# Returns the the model classes associated with the dataset as a hash.
|
250
|
+
# If the dataset is associated with a single model class, a key of nil
|
251
|
+
# is used. For datasets with polymorphic models, the keys are
|
252
|
+
# values of the polymorphic column and the values are the corresponding
|
253
|
+
# model classes to which they map.
|
192
254
|
def model_classes
|
193
255
|
@opts[:models]
|
194
256
|
end
|
195
257
|
|
258
|
+
# Returns a naked dataset clone - i.e. a dataset that returns records as
|
259
|
+
# hashes rather than model objects.
|
260
|
+
def naked
|
261
|
+
clone.set_model(nil)
|
262
|
+
end
|
263
|
+
|
196
264
|
# Returns the column name for the polymorphic key.
|
197
265
|
def polymorphic_key
|
198
266
|
@opts[:polymorphic_key]
|
199
267
|
end
|
200
268
|
|
201
|
-
#
|
202
|
-
|
203
|
-
|
204
|
-
d = clone(:naked => true, :models => nil, :polymorphic_key => nil)
|
205
|
-
d.set_model(nil)
|
206
|
-
d
|
269
|
+
# Whether this dataset quotes identifiers.
|
270
|
+
def quote_identifiers?
|
271
|
+
@quote_identifiers
|
207
272
|
end
|
208
|
-
|
209
|
-
#
|
273
|
+
|
274
|
+
# Alias for set, but not aliased directly so subclasses
|
275
|
+
# don't have to override both methods.
|
276
|
+
def set(*args, &block)
|
277
|
+
update(*args, &block)
|
278
|
+
end
|
279
|
+
|
280
|
+
# Associates or disassociates the dataset with a model(s). If
|
210
281
|
# nil is specified, the dataset is turned into a naked dataset and returns
|
211
282
|
# records as hashes. If a model class specified, the dataset is modified
|
212
283
|
# to return records as instances of the model class, e.g:
|
@@ -241,20 +312,18 @@ module Sequel
|
|
241
312
|
# class corresponding to nil:
|
242
313
|
#
|
243
314
|
# dataset.set_model(:kind, {nil => DefaultClass, 1 => Person, 2 => Business})
|
244
|
-
#
|
245
|
-
# To disassociate a model from the dataset, you can call the #set_model
|
246
|
-
# and specify nil as the class:
|
247
|
-
#
|
248
|
-
# dataset.set_model(nil)
|
249
315
|
#
|
316
|
+
# To make sure that there is always a default model class, the hash provided
|
317
|
+
# should have a default value. To make the dataset map string values to
|
318
|
+
# model classes, and keep a good default, try:
|
319
|
+
#
|
320
|
+
# dataset.set_model(:kind, Hash.new{|h,k| h[k] = (k.constantize rescue DefaultClass)})
|
250
321
|
def set_model(key, *args)
|
251
|
-
#
|
322
|
+
# This code is more verbose then necessary for performance reasons
|
252
323
|
case key
|
253
|
-
when nil # set_model(nil) => no
|
254
|
-
# no argument provided, so the dataset is denuded
|
324
|
+
when nil # set_model(nil) => no argument provided, so the dataset is denuded
|
255
325
|
@opts.merge!(:naked => true, :models => nil, :polymorphic_key => nil)
|
256
326
|
self.row_proc = nil
|
257
|
-
# extend_with_stock_each
|
258
327
|
when Class
|
259
328
|
# isomorphic model
|
260
329
|
@opts.merge!(:naked => nil, :models => {nil => key}, :polymorphic_key => nil)
|
@@ -265,12 +334,11 @@ module Sequel
|
|
265
334
|
# otherwise we just pass the hash to the constructor
|
266
335
|
self.row_proc = proc{|h| key.new(h, *args)}
|
267
336
|
end
|
268
|
-
extend_with_destroy
|
269
337
|
when Symbol
|
270
338
|
# polymorphic model
|
271
339
|
hash = args.shift || raise(ArgumentError, "No class hash supplied for polymorphic model")
|
272
340
|
@opts.merge!(:naked => true, :models => hash, :polymorphic_key => key)
|
273
|
-
if hash.values.first.respond_to?(:load)
|
341
|
+
if (hash.empty? ? (hash[nil] rescue nil) : hash.values.first).respond_to?(:load)
|
274
342
|
# the class has a values setter method, so we use it
|
275
343
|
self.row_proc = proc do |h|
|
276
344
|
c = hash[h[key]] || hash[nil] || \
|
@@ -285,25 +353,12 @@ module Sequel
|
|
285
353
|
c.new(h, *args)
|
286
354
|
end
|
287
355
|
end
|
288
|
-
extend_with_destroy
|
289
356
|
else
|
290
357
|
raise ArgumentError, "Invalid model specified"
|
291
358
|
end
|
292
359
|
self
|
293
360
|
end
|
294
361
|
|
295
|
-
STOCK_TRANSFORMS = {
|
296
|
-
:marshal => [
|
297
|
-
# for backwards-compatibility we support also non-base64-encoded values.
|
298
|
-
proc {|v| Marshal.load(Base64.decode64(v)) rescue Marshal.load(v)},
|
299
|
-
proc {|v| Base64.encode64(Marshal.dump(v))}
|
300
|
-
],
|
301
|
-
:yaml => [
|
302
|
-
proc {|v| YAML.load v if v},
|
303
|
-
proc {|v| v.to_yaml}
|
304
|
-
]
|
305
|
-
}
|
306
|
-
|
307
362
|
# Sets a value transform which is used to convert values loaded and saved
|
308
363
|
# to/from the database. The transform should be supplied as a hash. Each
|
309
364
|
# value in the hash should be an array containing two proc objects - one
|
@@ -361,63 +416,23 @@ module Sequel
|
|
361
416
|
end
|
362
417
|
end
|
363
418
|
|
364
|
-
#
|
365
|
-
|
366
|
-
|
367
|
-
unless respond_to?(:destroy)
|
368
|
-
meta_def(:destroy) do
|
369
|
-
unless @opts[:models]
|
370
|
-
raise Error, "No model associated with this dataset"
|
371
|
-
end
|
372
|
-
count = 0
|
373
|
-
@db.transaction {each {|r| count += 1; r.destroy}}
|
374
|
-
count
|
375
|
-
end
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
|
-
@@dataset_classes = []
|
380
|
-
|
381
|
-
def self.dataset_classes #:nodoc:
|
382
|
-
@@dataset_classes
|
383
|
-
end
|
384
|
-
|
385
|
-
def self.inherited(c) #:nodoc:
|
386
|
-
@@dataset_classes << c
|
387
|
-
end
|
388
|
-
|
389
|
-
# Returns a string representation of the dataset including the class name
|
390
|
-
# and the corresponding SQL select statement.
|
391
|
-
def inspect
|
392
|
-
'#<%s: %s>' % [self.class.to_s, sql.inspect]
|
393
|
-
end
|
394
|
-
|
395
|
-
# Setup mutation (e.g. filter!) methods
|
396
|
-
def self.def_mutation_method(*meths)
|
397
|
-
meths.each do |meth|
|
398
|
-
class_eval("def #{meth}!(*args, &block); mutation_method(:#{meth}, *args, &block) end")
|
399
|
-
end
|
400
|
-
end
|
401
|
-
def def_mutation_method(*meths)
|
402
|
-
meths.each do |meth|
|
403
|
-
instance_eval("def #{meth}!(*args, &block); mutation_method(:#{meth}, *args, &block) end")
|
404
|
-
end
|
419
|
+
# Updates values for the dataset. Adapters should override this method.
|
420
|
+
def update(values, opts = nil)
|
421
|
+
raise NotImplementedError, NOTIMPL_MSG
|
405
422
|
end
|
406
|
-
|
407
|
-
|
408
|
-
group group_and_count group_by having inner_join intersect invert_order join
|
409
|
-
left_outer_join limit naked or order order_by order_more paginate query reject
|
410
|
-
reverse reverse_order right_outer_join select select_all select_more
|
411
|
-
set_graph_aliases set_model sort sort_by union unordered where'.collect{|x| x.to_sym}
|
412
|
-
|
423
|
+
|
424
|
+
# Add the mutation methods via metaprogramming
|
413
425
|
def_mutation_method(*MUTATION_METHODS)
|
414
426
|
|
415
427
|
private
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
428
|
+
|
429
|
+
# Modify the receiver with the results of sending the meth, args, and block
|
430
|
+
# to the receiver and merging the options of the resulting dataset into
|
431
|
+
# the receiver's options.
|
432
|
+
def mutation_method(meth, *args, &block)
|
433
|
+
copy = send(meth, *args, &block)
|
434
|
+
@opts.merge!(copy.opts)
|
435
|
+
self
|
436
|
+
end
|
421
437
|
end
|
422
438
|
end
|
423
|
-
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Sequel
|
2
2
|
# This module makes it easy to add deprecation functionality to other classes.
|
3
|
-
module Deprecation
|
3
|
+
module Deprecation # :nodoc:
|
4
4
|
# This sets the output stream for the deprecation messages. Set it to an IO
|
5
5
|
# (or any object that responds to puts) and it will call puts on that
|
6
6
|
# object with the deprecation message. Set to nil to ignore deprecation messages.
|
@@ -29,172 +29,4 @@ module Sequel
|
|
29
29
|
::Sequel::Deprecation.deprecate("#{meth} is deprecated, and will be removed in Sequel 2.0.#{" #{message}." if message}")
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
33
|
-
class << self
|
34
|
-
include Sequel::Deprecation
|
35
|
-
def method_missing(m, *args) #:nodoc:
|
36
|
-
deprecate("Sequel.method_missing", "You should define Sequel.#{m} for the adapter.")
|
37
|
-
c = Database.adapter_class(m)
|
38
|
-
begin
|
39
|
-
# three ways to invoke this:
|
40
|
-
# 0 arguments: Sequel.dbi
|
41
|
-
# 1 argument: Sequel.dbi(db_name)
|
42
|
-
# more args: Sequel.dbi(db_name, opts)
|
43
|
-
case args.size
|
44
|
-
when 0
|
45
|
-
opts = {}
|
46
|
-
when 1
|
47
|
-
opts = args[0].is_a?(Hash) ? args[0] : {:database => args[0]}
|
48
|
-
else
|
49
|
-
opts = args[1].merge(:database => args[0])
|
50
|
-
end
|
51
|
-
rescue
|
52
|
-
raise Error::AdapterNotFound, "Unknown adapter (#{m})"
|
53
|
-
end
|
54
|
-
c.new(opts)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
class Dataset
|
59
|
-
include Deprecation
|
60
|
-
|
61
|
-
MUTATION_RE = /^(.+)!$/.freeze
|
62
|
-
|
63
|
-
def clone_merge(opts = {}) #:nodoc:
|
64
|
-
deprecate("Sequel::Dataset#clone", "Use clone")
|
65
|
-
clone(opts)
|
66
|
-
end
|
67
|
-
|
68
|
-
def set_options(opts) #:nodoc:
|
69
|
-
deprecate("Sequel::Dataset#set_options")
|
70
|
-
@opts = opts
|
71
|
-
@columns = nil
|
72
|
-
end
|
73
|
-
|
74
|
-
def set_row_proc(&filter) #:nodoc:
|
75
|
-
deprecate("Sequel::Dataset#set_row_proc", "Use row_proc=")
|
76
|
-
@row_proc = filter
|
77
|
-
end
|
78
|
-
|
79
|
-
def remove_row_proc #:nodoc:
|
80
|
-
deprecate("Sequel::Dataset#remove_row_proc", "Use row_proc=nil")
|
81
|
-
@row_proc = nil
|
82
|
-
end
|
83
|
-
|
84
|
-
# Provides support for mutation methods (filter!, order!, etc.) and magic
|
85
|
-
# methods.
|
86
|
-
def method_missing(m, *args, &block) #:nodoc:
|
87
|
-
if m.to_s =~ MUTATION_RE
|
88
|
-
meth = $1.to_sym
|
89
|
-
super unless respond_to?(meth)
|
90
|
-
copy = send(meth, *args, &block)
|
91
|
-
super if copy.class != self.class
|
92
|
-
deprecate("Sequel::Dataset#method_missing", "Define Sequel::Dataset##{m}, or use Sequel::Dataset.def_mutation_method(:#{meth})")
|
93
|
-
@opts.merge!(copy.opts)
|
94
|
-
self
|
95
|
-
elsif magic_method_missing(m)
|
96
|
-
send(m, *args)
|
97
|
-
else
|
98
|
-
super
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
MAGIC_METHODS = {
|
103
|
-
/^order_by_(.+)$/ => proc {|c| proc {deprecate("Sequel::Dataset#method_missing", "Use order(#{c.inspect}) or define order_by_#{c}"); order(c)}},
|
104
|
-
/^first_by_(.+)$/ => proc {|c| proc {deprecate("Sequel::Dataset#method_missing", "Use order(#{c.inspect}).first or define first_by_#{c}"); order(c).first}},
|
105
|
-
/^last_by_(.+)$/ => proc {|c| proc {deprecate("Sequel::Dataset#method_missing", "Use order(#{c.inspect}).last or define last_by_#{c}"); order(c).last}},
|
106
|
-
/^filter_by_(.+)$/ => proc {|c| proc {|v| deprecate("Sequel::Dataset#method_missing", "Use filter(#{c.inspect}=>#{v.inspect}) or define filter_by_#{c}"); filter(c => v)}},
|
107
|
-
/^all_by_(.+)$/ => proc {|c| proc {|v| deprecate("Sequel::Dataset#method_missing", "Use filter(#{c.inspect}=>#{v.inspect}).all or define all_by_#{c}"); filter(c => v).all}},
|
108
|
-
/^find_by_(.+)$/ => proc {|c| proc {|v| deprecate("Sequel::Dataset#method_missing", "Use filter(#{c.inspect}=>#{v.inspect}).first or define find_by_#{c}"); filter(c => v).first}},
|
109
|
-
/^group_by_(.+)$/ => proc {|c| proc {deprecate("Sequel::Dataset#method_missing", "Use group(#{c.inspect}) or define group_by_#{c}"); group(c)}},
|
110
|
-
/^count_by_(.+)$/ => proc {|c| proc {deprecate("Sequel::Dataset#method_missing", "Use group_and_count(#{c.inspect}) or define count_by_#{c})"); group_and_count(c)}}
|
111
|
-
}
|
112
|
-
|
113
|
-
# Checks if the given method name represents a magic method and
|
114
|
-
# defines it. Otherwise, nil is returned.
|
115
|
-
def magic_method_missing(m) #:nodoc:
|
116
|
-
method_name = m.to_s
|
117
|
-
MAGIC_METHODS.each_pair do |r, p|
|
118
|
-
if method_name =~ r
|
119
|
-
impl = p[$1.to_sym]
|
120
|
-
return Dataset.class_def(m, &impl)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
nil
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
module SQL
|
128
|
-
module DeprecatedColumnMethods #:nodoc:
|
129
|
-
AS = 'AS'.freeze
|
130
|
-
DESC = 'DESC'.freeze
|
131
|
-
ASC = 'ASC'.freeze
|
132
|
-
|
133
|
-
def as(a) #:nodoc:
|
134
|
-
Sequel::Deprecation.deprecate("Object#as is deprecated and will be removed in Sequel 2.0. Use Symbol#as or String#as.")
|
135
|
-
ColumnExpr.new(self, AS, a)
|
136
|
-
end
|
137
|
-
def AS(a) #:nodoc:
|
138
|
-
Sequel::Deprecation.deprecate("Object#AS is deprecated and will be removed in Sequel 2.0. Use Symbol#as or String#as.")
|
139
|
-
ColumnExpr.new(self, AS, a)
|
140
|
-
end
|
141
|
-
def desc #:nodoc:
|
142
|
-
Sequel::Deprecation.deprecate("Object#desc is deprecated and will be removed in Sequel 2.0. Use Symbol#desc or String#desc.")
|
143
|
-
ColumnExpr.new(self, DESC)
|
144
|
-
end
|
145
|
-
def DESC #:nodoc:
|
146
|
-
Sequel::Deprecation.deprecate("Object#DESC is deprecated and will be removed in Sequel 2.0. Use Symbol#desc or String#desc.")
|
147
|
-
ColumnExpr.new(self, DESC)
|
148
|
-
end
|
149
|
-
def asc #:nodoc:
|
150
|
-
Sequel::Deprecation.deprecate("Object#asc is deprecated and will be removed in Sequel 2.0. Use Symbol#asc or String#asc.")
|
151
|
-
ColumnExpr.new(self, ASC)
|
152
|
-
end
|
153
|
-
def ASC #:nodoc:
|
154
|
-
Sequel::Deprecation.deprecate("Object#ASC is deprecated and will be removed in Sequel 2.0. Use Symbol#asc or String#asc.")
|
155
|
-
ColumnExpr.new(self, ASC)
|
156
|
-
end
|
157
|
-
def all #:nodoc:
|
158
|
-
Sequel::Deprecation.deprecate("Object#all is deprecated and will be removed in Sequel 2.0. Use :#{self}.* or '#{self}.*'.lit.")
|
159
|
-
Sequel::SQL::ColumnAll.new(self)
|
160
|
-
end
|
161
|
-
def ALL #:nodoc:
|
162
|
-
Sequel::Deprecation.deprecate("Object#ALL is deprecated and will be removed in Sequel 2.0. Use :#{self}.* or '#{self}.*'.lit.")
|
163
|
-
Sequel::SQL::ColumnAll.new(self)
|
164
|
-
end
|
165
|
-
|
166
|
-
def cast_as(t) #:nodoc:
|
167
|
-
Sequel::Deprecation.deprecate("Object#cast_as is deprecated and will be removed in Sequel 2.0. Use Symbol#cast_as or String#cast_as.")
|
168
|
-
if t.is_a?(Symbol)
|
169
|
-
t = t.to_s.lit
|
170
|
-
end
|
171
|
-
Sequel::SQL::Function.new(:cast, self.as(t))
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
class Object
|
178
|
-
include Sequel::SQL::DeprecatedColumnMethods
|
179
|
-
def Sequel(*args) #:nodoc:
|
180
|
-
Sequel::Deprecation.deprecate("Object#Sequel is deprecated and will be removed in Sequel 2.0. Use Sequel.connect.")
|
181
|
-
Sequel.connect(*args)
|
182
|
-
end
|
183
|
-
def rollback! #:nodoc:
|
184
|
-
Sequel::Deprecation.deprecate("Object#rollback! is deprecated and will be removed in Sequel 2.0. Use raise Sequel::Error::Rollback.")
|
185
|
-
raise Sequel::Error::Rollback
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
class Symbol
|
190
|
-
# Converts missing method calls into functions on columns, if the
|
191
|
-
# method name is made of all upper case letters.
|
192
|
-
def method_missing(sym, *args) #:nodoc:
|
193
|
-
if ((s = sym.to_s) =~ /^([A-Z]+)$/)
|
194
|
-
Sequel::Deprecation.deprecate("Symbol#method_missing is deprecated and will be removed in Sequel 2.0. Use :#{sym}[:#{self}].")
|
195
|
-
Sequel::SQL::Function.new(s.downcase, self)
|
196
|
-
else
|
197
|
-
super
|
198
|
-
end
|
199
|
-
end
|
200
32
|
end
|