sequel_core 1.5.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/CHANGELOG +116 -0
  2. data/COPYING +19 -19
  3. data/README +83 -32
  4. data/Rakefile +9 -20
  5. data/bin/sequel +43 -112
  6. data/doc/cheat_sheet.rdoc +225 -0
  7. data/doc/dataset_filtering.rdoc +257 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +4 -2
  9. data/lib/sequel_core/adapters/ado.rb +3 -1
  10. data/lib/sequel_core/adapters/db2.rb +4 -2
  11. data/lib/sequel_core/adapters/dbi.rb +127 -113
  12. data/lib/sequel_core/adapters/informix.rb +4 -2
  13. data/lib/sequel_core/adapters/jdbc.rb +5 -3
  14. data/lib/sequel_core/adapters/mysql.rb +112 -46
  15. data/lib/sequel_core/adapters/odbc.rb +5 -7
  16. data/lib/sequel_core/adapters/odbc_mssql.rb +12 -3
  17. data/lib/sequel_core/adapters/openbase.rb +3 -1
  18. data/lib/sequel_core/adapters/oracle.rb +11 -9
  19. data/lib/sequel_core/adapters/postgres.rb +261 -262
  20. data/lib/sequel_core/adapters/sqlite.rb +72 -22
  21. data/lib/sequel_core/connection_pool.rb +140 -73
  22. data/lib/sequel_core/core_ext.rb +201 -66
  23. data/lib/sequel_core/core_sql.rb +123 -153
  24. data/lib/sequel_core/database/schema.rb +156 -0
  25. data/lib/sequel_core/database.rb +321 -338
  26. data/lib/sequel_core/dataset/callback.rb +11 -12
  27. data/lib/sequel_core/dataset/convenience.rb +213 -240
  28. data/lib/sequel_core/dataset/pagination.rb +58 -43
  29. data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +331 -0
  30. data/lib/sequel_core/dataset/query.rb +41 -0
  31. data/lib/sequel_core/dataset/schema.rb +15 -0
  32. data/lib/sequel_core/dataset/sequelizer.rb +41 -373
  33. data/lib/sequel_core/dataset/sql.rb +741 -632
  34. data/lib/sequel_core/dataset.rb +183 -168
  35. data/lib/sequel_core/deprecated.rb +1 -169
  36. data/lib/sequel_core/exceptions.rb +24 -19
  37. data/lib/sequel_core/migration.rb +44 -52
  38. data/lib/sequel_core/object_graph.rb +43 -42
  39. data/lib/sequel_core/pretty_table.rb +71 -76
  40. data/lib/sequel_core/schema/generator.rb +163 -105
  41. data/lib/sequel_core/schema/sql.rb +250 -93
  42. data/lib/sequel_core/schema.rb +2 -8
  43. data/lib/sequel_core/sql.rb +394 -0
  44. data/lib/sequel_core/worker.rb +37 -27
  45. data/lib/sequel_core.rb +99 -45
  46. data/spec/adapters/informix_spec.rb +0 -1
  47. data/spec/adapters/mysql_spec.rb +177 -124
  48. data/spec/adapters/oracle_spec.rb +0 -1
  49. data/spec/adapters/postgres_spec.rb +98 -58
  50. data/spec/adapters/sqlite_spec.rb +45 -4
  51. data/spec/blockless_filters_spec.rb +269 -0
  52. data/spec/connection_pool_spec.rb +21 -18
  53. data/spec/core_ext_spec.rb +169 -19
  54. data/spec/core_sql_spec.rb +56 -49
  55. data/spec/database_spec.rb +78 -17
  56. data/spec/dataset_spec.rb +300 -428
  57. data/spec/migration_spec.rb +1 -1
  58. data/spec/object_graph_spec.rb +5 -11
  59. data/spec/rcov.opts +1 -1
  60. data/spec/schema_generator_spec.rb +16 -4
  61. data/spec/schema_spec.rb +89 -10
  62. data/spec/sequelizer_spec.rb +56 -56
  63. data/spec/spec.opts +0 -5
  64. data/spec/spec_config.rb +7 -0
  65. data/spec/spec_config.rb.example +5 -5
  66. data/spec/spec_helper.rb +6 -0
  67. data/spec/worker_spec.rb +1 -1
  68. metadata +78 -63
@@ -1,13 +1,6 @@
1
- require 'time'
2
- require 'date'
3
- require 'yaml'
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 overriden by the adapter
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
- alias_method :size, :count
79
-
80
- # Returns an array with all records in the dataset. If a block is given,
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
- # Returns a new clone of the dataset with with the given options merged.
107
- def clone(opts = {})
108
- c = super()
109
- c.opts = @opts.merge(opts)
110
- c.instance_variable_set(:@columns, nil)
111
- c
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
- NOTIMPL_MSG = "This method must be overriden in Sequel adapters".freeze
115
-
116
- # Executes a select query and fetches records, passing each record to the
117
- # supplied block. Adapters should override this method.
118
- def fetch_rows(sql, &block)
119
- # @db.synchronize do
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
- # Inserts values into the associated table. Adapters should override this
127
- # method.
128
- def insert(*values)
129
- # @db.synchronize do
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
- # Updates values for the dataset. Adapters should override this method.
136
- def update(values, opts = nil)
137
- # @db.synchronize do
138
- # @db.execute(update_sql(values, opts)).affected_rows
139
- # end
140
- raise NotImplementedError, NOTIMPL_MSG
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
- # Deletes the records in the dataset. Adapters should override this method.
144
- def delete(opts = nil)
145
- # @db.synchronize do
146
- # @db.execute(delete_sql(opts)).affected_rows
147
- # end
148
- raise NotImplementedError, NOTIMPL_MSG
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. The stock
152
- # implementation returns the content of @columns. If @columns is nil,
153
- # a query is performed. Adapters are expected to fill @columns with the
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
- first unless @columns
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
- first
162
- @columns || []
199
+ @columns = nil
200
+ columns
163
201
  end
164
202
 
165
- # Inserts the supplied values into the associated table.
166
- def <<(*args)
167
- insert(*args)
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
- # Updates the dataset with the given values.
171
- def set(*args, &block)
172
- update(*args, &block)
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
- # Returns a naked dataset clone - i.e. a dataset that returns records as
202
- # hashes rather than model objects.
203
- def naked
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
- # Associates or disassociates the dataset with a model. If no argument or
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
- # pattern matching
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
- # Extends the dataset with a destroy method, that calls destroy for each
365
- # record in the dataset.
366
- def extend_with_destroy
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
- MUTATION_METHODS = %w'and distinct exclude exists filter from from_self full_outer_join graph
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
- def mutation_method(meth, *args, &block)
417
- copy = send(meth, *args, &block)
418
- @opts.merge!(copy.opts)
419
- self
420
- end
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