sequel_core 2.2.0 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. metadata +30 -101
  2. data/CHANGELOG +0 -1519
  3. data/COPYING +0 -19
  4. data/README +0 -313
  5. data/Rakefile +0 -158
  6. data/bin/sequel +0 -117
  7. data/doc/cheat_sheet.rdoc +0 -225
  8. data/doc/dataset_filtering.rdoc +0 -182
  9. data/lib/sequel_core.rb +0 -136
  10. data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -68
  11. data/lib/sequel_core/adapters/ado.rb +0 -90
  12. data/lib/sequel_core/adapters/db2.rb +0 -160
  13. data/lib/sequel_core/adapters/dbi.rb +0 -127
  14. data/lib/sequel_core/adapters/informix.rb +0 -89
  15. data/lib/sequel_core/adapters/jdbc.rb +0 -110
  16. data/lib/sequel_core/adapters/mysql.rb +0 -486
  17. data/lib/sequel_core/adapters/odbc.rb +0 -167
  18. data/lib/sequel_core/adapters/odbc_mssql.rb +0 -106
  19. data/lib/sequel_core/adapters/openbase.rb +0 -76
  20. data/lib/sequel_core/adapters/oracle.rb +0 -182
  21. data/lib/sequel_core/adapters/postgres.rb +0 -560
  22. data/lib/sequel_core/adapters/sqlite.rb +0 -270
  23. data/lib/sequel_core/connection_pool.rb +0 -194
  24. data/lib/sequel_core/core_ext.rb +0 -197
  25. data/lib/sequel_core/core_sql.rb +0 -184
  26. data/lib/sequel_core/database.rb +0 -462
  27. data/lib/sequel_core/database/schema.rb +0 -156
  28. data/lib/sequel_core/dataset.rb +0 -457
  29. data/lib/sequel_core/dataset/callback.rb +0 -13
  30. data/lib/sequel_core/dataset/convenience.rb +0 -245
  31. data/lib/sequel_core/dataset/pagination.rb +0 -96
  32. data/lib/sequel_core/dataset/query.rb +0 -41
  33. data/lib/sequel_core/dataset/schema.rb +0 -15
  34. data/lib/sequel_core/dataset/sql.rb +0 -889
  35. data/lib/sequel_core/deprecated.rb +0 -26
  36. data/lib/sequel_core/exceptions.rb +0 -42
  37. data/lib/sequel_core/migration.rb +0 -187
  38. data/lib/sequel_core/object_graph.rb +0 -216
  39. data/lib/sequel_core/pretty_table.rb +0 -71
  40. data/lib/sequel_core/schema.rb +0 -2
  41. data/lib/sequel_core/schema/generator.rb +0 -239
  42. data/lib/sequel_core/schema/sql.rb +0 -326
  43. data/lib/sequel_core/sql.rb +0 -812
  44. data/lib/sequel_core/worker.rb +0 -68
  45. data/spec/adapters/informix_spec.rb +0 -96
  46. data/spec/adapters/mysql_spec.rb +0 -765
  47. data/spec/adapters/oracle_spec.rb +0 -222
  48. data/spec/adapters/postgres_spec.rb +0 -441
  49. data/spec/adapters/sqlite_spec.rb +0 -413
  50. data/spec/connection_pool_spec.rb +0 -363
  51. data/spec/core_ext_spec.rb +0 -156
  52. data/spec/core_sql_spec.rb +0 -427
  53. data/spec/database_spec.rb +0 -963
  54. data/spec/dataset_spec.rb +0 -2933
  55. data/spec/expression_filters_spec.rb +0 -316
  56. data/spec/migration_spec.rb +0 -261
  57. data/spec/object_graph_spec.rb +0 -230
  58. data/spec/pretty_table_spec.rb +0 -58
  59. data/spec/rcov.opts +0 -6
  60. data/spec/schema_generator_spec.rb +0 -122
  61. data/spec/schema_spec.rb +0 -422
  62. data/spec/spec.opts +0 -0
  63. data/spec/spec_config.rb +0 -7
  64. data/spec/spec_config.rb.example +0 -8
  65. data/spec/spec_helper.rb +0 -55
  66. data/spec/worker_spec.rb +0 -96
@@ -1,156 +0,0 @@
1
- module Sequel
2
- class Database
3
- # Adds a column to the specified table. This method expects a column name,
4
- # a datatype and optionally a hash with additional constraints and options:
5
- #
6
- # DB.add_column :items, :name, :text, :unique => true, :null => false
7
- # DB.add_column :items, :category, :text, :default => 'ruby'
8
- #
9
- # See alter_table.
10
- def add_column(table, *args)
11
- alter_table(table) {add_column(*args)}
12
- end
13
-
14
- # Adds an index to a table for the given columns:
15
- #
16
- # DB.add_index :posts, :title
17
- # DB.add_index :posts, [:author, :title], :unique => true
18
- #
19
- # See alter_table.
20
- def add_index(table, *args)
21
- alter_table(table) {add_index(*args)}
22
- end
23
-
24
- # Alters the given table with the specified block. Here are the currently
25
- # available operations:
26
- #
27
- # DB.alter_table :items do
28
- # add_column :category, :text, :default => 'ruby'
29
- # drop_column :category
30
- # rename_column :cntr, :counter
31
- # set_column_type :value, :float
32
- # set_column_default :value, :float
33
- # add_index [:group, :category]
34
- # drop_index [:group, :category]
35
- # end
36
- #
37
- # Note that #add_column accepts all the options available for column
38
- # definitions using create_table, and #add_index accepts all the options
39
- # available for index definition.
40
- #
41
- # See Schema::AlterTableGenerator.
42
- def alter_table(name, &block)
43
- g = Schema::AlterTableGenerator.new(self, &block)
44
- alter_table_sql_list(name, g.operations).each {|sql| execute(sql)}
45
- end
46
-
47
- # Creates a table with the columns given in the provided block:
48
- #
49
- # DB.create_table :posts do
50
- # primary_key :id, :serial
51
- # column :title, :text
52
- # column :content, :text
53
- # index :title
54
- # end
55
- #
56
- # See Schema::Generator.
57
- def create_table(name, &block)
58
- g = Schema::Generator.new(self, &block)
59
- create_table_sql_list(name, *g.create_info).each {|sql| execute(sql)}
60
- end
61
-
62
- # Forcibly creates a table. If the table already exists it is dropped.
63
- def create_table!(name, &block)
64
- drop_table(name) rescue nil
65
- create_table(name, &block)
66
- end
67
-
68
- # Creates a view, replacing it if it already exists:
69
- #
70
- # DB.create_or_replace_view(:cheap_items, "SELECT * FROM items WHERE price < 100")
71
- # DB.create_or_replace_view(:ruby_items, DB[:items].filter(:category => 'ruby'))
72
- def create_or_replace_view(name, source)
73
- source = source.sql if source.is_a?(Dataset)
74
- execute("CREATE OR REPLACE VIEW #{name} AS #{source}")
75
- end
76
-
77
- # Creates a view based on a dataset or an SQL string:
78
- #
79
- # DB.create_view(:cheap_items, "SELECT * FROM items WHERE price < 100")
80
- # DB.create_view(:ruby_items, DB[:items].filter(:category => 'ruby'))
81
- def create_view(name, source)
82
- source = source.sql if source.is_a?(Dataset)
83
- execute("CREATE VIEW #{name} AS #{source}")
84
- end
85
-
86
- # Removes a column from the specified table:
87
- #
88
- # DB.drop_column :items, :category
89
- #
90
- # See alter_table.
91
- def drop_column(table, *args)
92
- alter_table(table) {drop_column(*args)}
93
- end
94
-
95
- # Removes an index for the given table and column/s:
96
- #
97
- # DB.drop_index :posts, :title
98
- # DB.drop_index :posts, [:author, :title]
99
- #
100
- # See alter_table.
101
- def drop_index(table, columns)
102
- alter_table(table) {drop_index(columns)}
103
- end
104
-
105
- # Drops one or more tables corresponding to the given table names:
106
- #
107
- # DB.drop_table(:posts, :comments)
108
- def drop_table(*names)
109
- names.each {|n| execute(drop_table_sql(n))}
110
- end
111
-
112
- # Drops a view:
113
- #
114
- # DB.drop_view(:cheap_items)
115
- def drop_view(name)
116
- execute("DROP VIEW #{name}")
117
- end
118
-
119
- # Renames a table:
120
- #
121
- # DB.tables #=> [:items]
122
- # DB.rename_table :items, :old_items
123
- # DB.tables #=> [:old_items]
124
- def rename_table(*args)
125
- execute(rename_table_sql(*args))
126
- end
127
-
128
- # Renames a column in the specified table. This method expects the current
129
- # column name and the new column name:
130
- #
131
- # DB.rename_column :items, :cntr, :counter
132
- #
133
- # See alter_table.
134
- def rename_column(table, *args)
135
- alter_table(table) {rename_column(*args)}
136
- end
137
-
138
- # Sets the default value for the given column in the given table:
139
- #
140
- # DB.set_column_default :items, :category, 'perl!'
141
- #
142
- # See alter_table.
143
- def set_column_default(table, *args)
144
- alter_table(table) {set_column_default(*args)}
145
- end
146
-
147
- # Set the data type for the given column in the given table:
148
- #
149
- # DB.set_column_type :items, :price, :float
150
- #
151
- # See alter_table.
152
- def set_column_type(table, *args)
153
- alter_table(table) {set_column_type(*args)}
154
- end
155
- end
156
- end
@@ -1,457 +0,0 @@
1
- %w'callback convenience pagination query schema sql'.each do |f|
2
- require "sequel_core/dataset/#{f}"
3
- end
4
-
5
- module Sequel
6
- # A Dataset represents a view of a the data in a database, constrained by
7
- # specific parameters such as filtering conditions, order, etc. Datasets
8
- # can be used to create, retrieve, update and delete records.
9
- #
10
- # Query results are always retrieved on demand, so a dataset can be kept
11
- # around and reused indefinitely:
12
- #
13
- # my_posts = DB[:posts].filter(:author => 'david') # no records are retrieved
14
- # p my_posts.all # records are now retrieved
15
- # ...
16
- # p my_posts.all # records are retrieved again
17
- #
18
- # In order to provide this functionality, dataset methods such as where,
19
- # select, order, etc. return modified copies of the dataset, so you can
20
- # use different datasets to access data:
21
- #
22
- # posts = DB[:posts]
23
- # davids_posts = posts.filter(:author => 'david')
24
- # old_posts = posts.filter('stamp < ?', Date.today - 7)
25
- #
26
- # Datasets are Enumerable objects, so they can be manipulated using any
27
- # of the Enumerable methods, such as map, inject, etc.
28
- #
29
- # === The Dataset Adapter Interface
30
- #
31
- # Each adapter should define its own dataset class as a descendant of
32
- # Sequel::Dataset. The following methods should be overridden by the adapter
33
- # Dataset class (each method with the stock implementation):
34
- #
35
- # # Iterate over the results of the SQL query and call the supplied
36
- # # block with each record (as a hash).
37
- # def fetch_rows(sql, &block)
38
- # @db.synchronize do
39
- # r = @db.execute(sql)
40
- # r.each(&block)
41
- # end
42
- # end
43
- #
44
- # # Insert records.
45
- # def insert(*values)
46
- # @db.synchronize do
47
- # @db.execute(insert_sql(*values)).last_insert_id
48
- # end
49
- # end
50
- #
51
- # # Update records.
52
- # def update(*args, &block)
53
- # @db.synchronize do
54
- # @db.execute(update_sql(*args, &block)).affected_rows
55
- # end
56
- # end
57
- #
58
- # # Delete records.
59
- # def delete(opts = nil)
60
- # @db.synchronize do
61
- # @db.execute(delete_sql(opts)).affected_rows
62
- # end
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.
75
- class Dataset
76
- include Enumerable
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
-
106
- # The database that corresponds to this dataset
107
- attr_accessor :db
108
-
109
- # The hash of options for this dataset, keys are symbols.
110
- attr_accessor :opts
111
-
112
- # The row_proc for this database, should be a Proc that takes
113
- # a single hash argument and returns the object you want to
114
- # fetch_rows to return.
115
- attr_accessor :row_proc
116
-
117
- # Whether to quote identifiers for this dataset
118
- attr_writer :quote_identifiers
119
-
120
- # Constructs a new instance of a dataset with an associated database and
121
- # options. Datasets are usually constructed by invoking Database methods:
122
- #
123
- # DB[:posts]
124
- #
125
- # Or:
126
- #
127
- # DB.dataset # the returned dataset is blank
128
- #
129
- # Sequel::Dataset is an abstract class that is not useful by itself. Each
130
- # database adaptor should provide a descendant class of Sequel::Dataset.
131
- def initialize(db, opts = nil)
132
- @db = db
133
- @quote_identifiers = db.quote_identifiers? if db.respond_to?(:quote_identifiers?)
134
- @opts = opts || {}
135
- @row_proc = nil
136
- @transform = nil
137
- end
138
-
139
- ### Class Methods ###
140
-
141
- # The array of dataset subclasses.
142
- def self.dataset_classes
143
- DATASET_CLASSES
144
- end
145
-
146
- # Setup mutation (e.g. filter!) methods. These operate the same as the
147
- # non-! methods, but replace the options of the current dataset with the
148
- # options of the resulting dataset.
149
- def self.def_mutation_method(*meths)
150
- meths.each do |meth|
151
- class_eval("def #{meth}!(*args, &block); mutation_method(:#{meth}, *args, &block) end")
152
- end
153
- end
154
-
155
- # Add the subclass to the array of subclasses.
156
- def self.inherited(c)
157
- DATASET_CLASSES << c
158
- end
159
-
160
- ### Instance Methods ###
161
-
162
- # Alias for insert, but not aliased directly so subclasses
163
- # don't have to override both methods.
164
- def <<(*args)
165
- insert(*args)
166
- end
167
-
168
- # Return the dataset as a column with the given alias, so it can be used in the
169
- # SELECT clause. This dataset should result in a single row and a single column.
170
- def as(aliaz)
171
- ::Sequel::SQL::AliasedExpression.new(self, aliaz)
172
- end
173
-
174
- # Returns an array with all records in the dataset. If a block is given,
175
- # the array is iterated over after all items have been loaded.
176
- def all(opts = nil, &block)
177
- a = []
178
- each(opts) {|r| a << r}
179
- post_load(a)
180
- a.each(&block) if block
181
- a
182
- end
183
-
184
- # Returns a new clone of the dataset with with the given options merged.
185
- # If the options changed include options in COLUMN_CHANGE_OPTS, the cached
186
- # columns are deleted.
187
- def clone(opts = {})
188
- c = super()
189
- c.opts = @opts.merge(opts)
190
- c.instance_variable_set(:@columns, nil) if opts.keys.any?{|o| COLUMN_CHANGE_OPTS.include?(o)}
191
- c
192
- end
193
-
194
- # Returns the columns in the result set in their true order.
195
- # If the columns are currently cached, returns the cached value. Otherwise,
196
- # a SELECT query is performed to get a single row. Adapters are expected
197
- # to fill the columns cache with the column information when a query is performed.
198
- # If the dataset does not have any rows, this will be an empty array.
199
- # If you are looking for all columns for a single table, see Schema::SQL#schema.
200
- def columns
201
- return @columns if @columns
202
- ds = unfiltered.unordered.clone(:distinct => nil)
203
- ds.single_record
204
- @columns = ds.instance_variable_get(:@columns)
205
- @columns || []
206
- end
207
-
208
- # Remove the cached list of columns and do a SELECT query to find
209
- # the columns.
210
- def columns!
211
- @columns = nil
212
- columns
213
- end
214
-
215
- # Add a mutation method to this dataset instance.
216
- def def_mutation_method(*meths)
217
- meths.each do |meth|
218
- instance_eval("def #{meth}!(*args, &block); mutation_method(:#{meth}, *args, &block) end")
219
- end
220
- end
221
-
222
- # Deletes the records in the dataset. Adapters should override this method.
223
- def delete(opts = nil)
224
- raise NotImplementedError, NOTIMPL_MSG
225
- end
226
-
227
- # Iterates over the records in the dataset.
228
- def each(opts = nil, &block)
229
- if graph = @opts[:graph]
230
- graph_each(opts, &block)
231
- else
232
- row_proc = @row_proc unless opts && opts[:naked]
233
- transform = @transform
234
- fetch_rows(select_sql(opts)) do |r|
235
- r = transform_load(r) if transform
236
- r = row_proc[r] if row_proc
237
- yield r
238
- end
239
- end
240
- self
241
- end
242
-
243
- # Executes a select query and fetches records, passing each record to the
244
- # supplied block. Adapters should override this method.
245
- def fetch_rows(sql, &block)
246
- raise NotImplementedError, NOTIMPL_MSG
247
- end
248
-
249
- # Inserts values into the associated table. Adapters should override this
250
- # method.
251
- def insert(*values)
252
- raise NotImplementedError, NOTIMPL_MSG
253
- end
254
-
255
- # Returns a string representation of the dataset including the class name
256
- # and the corresponding SQL select statement.
257
- def inspect
258
- "#<#{self.class}: #{sql.inspect}>"
259
- end
260
-
261
- # Returns the the model classes associated with the dataset as a hash.
262
- # If the dataset is associated with a single model class, a key of nil
263
- # is used. For datasets with polymorphic models, the keys are
264
- # values of the polymorphic column and the values are the corresponding
265
- # model classes to which they map.
266
- def model_classes
267
- @opts[:models]
268
- end
269
-
270
- # Returns a naked dataset clone - i.e. a dataset that returns records as
271
- # hashes rather than model objects.
272
- def naked
273
- clone.set_model(nil)
274
- end
275
-
276
- # Returns the column name for the polymorphic key.
277
- def polymorphic_key
278
- @opts[:polymorphic_key]
279
- end
280
-
281
- # Whether this dataset quotes identifiers.
282
- def quote_identifiers?
283
- @quote_identifiers
284
- end
285
-
286
- # Alias for set, but not aliased directly so subclasses
287
- # don't have to override both methods.
288
- def set(*args, &block)
289
- update(*args, &block)
290
- end
291
-
292
- # Associates or disassociates the dataset with a model(s). If
293
- # nil is specified, the dataset is turned into a naked dataset and returns
294
- # records as hashes. If a model class specified, the dataset is modified
295
- # to return records as instances of the model class, e.g:
296
- #
297
- # class MyModel
298
- # def initialize(values)
299
- # @values = values
300
- # ...
301
- # end
302
- # end
303
- #
304
- # dataset.set_model(MyModel)
305
- #
306
- # You can also provide additional arguments to be passed to the model's
307
- # initialize method:
308
- #
309
- # class MyModel
310
- # def initialize(values, options)
311
- # @values = values
312
- # ...
313
- # end
314
- # end
315
- #
316
- # dataset.set_model(MyModel, :allow_delete => false)
317
- #
318
- # The dataset can be made polymorphic by specifying a column name as the
319
- # polymorphic key and a hash mapping column values to model classes.
320
- #
321
- # dataset.set_model(:kind, {1 => Person, 2 => Business})
322
- #
323
- # You can also set a default model class to fall back on by specifying a
324
- # class corresponding to nil:
325
- #
326
- # dataset.set_model(:kind, {nil => DefaultClass, 1 => Person, 2 => Business})
327
- #
328
- # To make sure that there is always a default model class, the hash provided
329
- # should have a default value. To make the dataset map string values to
330
- # model classes, and keep a good default, try:
331
- #
332
- # dataset.set_model(:kind, Hash.new{|h,k| h[k] = (k.constantize rescue DefaultClass)})
333
- def set_model(key, *args)
334
- # This code is more verbose then necessary for performance reasons
335
- case key
336
- when nil # set_model(nil) => no argument provided, so the dataset is denuded
337
- @opts.merge!(:naked => true, :models => nil, :polymorphic_key => nil)
338
- self.row_proc = nil
339
- when Class
340
- # isomorphic model
341
- @opts.merge!(:naked => nil, :models => {nil => key}, :polymorphic_key => nil)
342
- if key.respond_to?(:load)
343
- # the class has a values setter method, so we use it
344
- self.row_proc = proc{|h| key.load(h, *args)}
345
- else
346
- # otherwise we just pass the hash to the constructor
347
- self.row_proc = proc{|h| key.new(h, *args)}
348
- end
349
- when Symbol
350
- # polymorphic model
351
- hash = args.shift || raise(ArgumentError, "No class hash supplied for polymorphic model")
352
- @opts.merge!(:naked => true, :models => hash, :polymorphic_key => key)
353
- if (hash.empty? ? (hash[nil] rescue nil) : hash.values.first).respond_to?(:load)
354
- # the class has a values setter method, so we use it
355
- self.row_proc = proc do |h|
356
- c = hash[h[key]] || hash[nil] || \
357
- raise(Error, "No matching model class for record (#{polymorphic_key} => #{h[polymorphic_key].inspect})")
358
- c.load(h, *args)
359
- end
360
- else
361
- # otherwise we just pass the hash to the constructor
362
- self.row_proc = proc do |h|
363
- c = hash[h[key]] || hash[nil] || \
364
- raise(Error, "No matching model class for record (#{polymorphic_key} => #{h[polymorphic_key].inspect})")
365
- c.new(h, *args)
366
- end
367
- end
368
- else
369
- raise ArgumentError, "Invalid model specified"
370
- end
371
- self
372
- end
373
-
374
- # Sets a value transform which is used to convert values loaded and saved
375
- # to/from the database. The transform should be supplied as a hash. Each
376
- # value in the hash should be an array containing two proc objects - one
377
- # for transforming loaded values, and one for transforming saved values.
378
- # The following example demonstrates how to store Ruby objects in a dataset
379
- # using Marshal serialization:
380
- #
381
- # dataset.transform(:obj => [
382
- # proc {|v| Marshal.load(v)},
383
- # proc {|v| Marshal.dump(v)}
384
- # ])
385
- #
386
- # dataset.insert_sql(:obj => 1234) #=>
387
- # "INSERT INTO items (obj) VALUES ('\004\bi\002\322\004')"
388
- #
389
- # Another form of using transform is by specifying stock transforms:
390
- #
391
- # dataset.transform(:obj => :marshal)
392
- #
393
- # The currently supported stock transforms are :marshal and :yaml.
394
- def transform(t)
395
- @transform = t
396
- t.each do |k, v|
397
- case v
398
- when Array
399
- if (v.size != 2) || !v.first.is_a?(Proc) && !v.last.is_a?(Proc)
400
- raise Error::InvalidTransform, "Invalid transform specified"
401
- end
402
- else
403
- unless v = STOCK_TRANSFORMS[v]
404
- raise Error::InvalidTransform, "Invalid transform specified"
405
- else
406
- t[k] = v
407
- end
408
- end
409
- end
410
- self
411
- end
412
-
413
- # Applies the value transform for data loaded from the database.
414
- def transform_load(r)
415
- r.inject({}) do |m, kv|
416
- k, v = *kv
417
- m[k] = (tt = @transform[k]) ? tt[0][v] : v
418
- m
419
- end
420
- end
421
-
422
- # Applies the value transform for data saved to the database.
423
- def transform_save(r)
424
- r.inject({}) do |m, kv|
425
- k, v = *kv
426
- m[k] = (tt = @transform[k]) ? tt[1][v] : v
427
- m
428
- end
429
- end
430
-
431
- # Updates values for the dataset. Adapters should override this method.
432
- def update(values, opts = nil)
433
- raise NotImplementedError, NOTIMPL_MSG
434
- end
435
-
436
- # Add the mutation methods via metaprogramming
437
- def_mutation_method(*MUTATION_METHODS)
438
-
439
- protected
440
-
441
- # Return true if the dataset has a non-nil value for any key in opts.
442
- def options_overlap(opts)
443
- !(@opts.collect{|k,v| k unless v.nil?}.compact & opts).empty?
444
- end
445
-
446
- private
447
-
448
- # Modify the receiver with the results of sending the meth, args, and block
449
- # to the receiver and merging the options of the resulting dataset into
450
- # the receiver's options.
451
- def mutation_method(meth, *args, &block)
452
- copy = send(meth, *args, &block)
453
- @opts.merge!(copy.opts)
454
- self
455
- end
456
- end
457
- end