sequel 3.41.0 → 3.42.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +28 -0
- data/README.rdoc +18 -6
- data/Rakefile +45 -40
- data/doc/release_notes/3.42.0.txt +74 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +55 -0
- data/lib/sequel/adapters/postgres.rb +4 -32
- data/lib/sequel/adapters/shared/mssql.rb +9 -3
- data/lib/sequel/adapters/shared/oracle.rb +5 -0
- data/lib/sequel/adapters/shared/postgres.rb +59 -2
- data/lib/sequel/adapters/shared/sqlite.rb +5 -0
- data/lib/sequel/database/misc.rb +21 -0
- data/lib/sequel/database/query.rb +10 -2
- data/lib/sequel/database/schema_generator.rb +9 -4
- data/lib/sequel/database/schema_methods.rb +18 -4
- data/lib/sequel/dataset/actions.rb +28 -12
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/dataset/sql.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +1 -0
- data/lib/sequel/model.rb +2 -3
- data/lib/sequel/model/base.rb +54 -33
- data/lib/sequel/model/dataset_module.rb +30 -0
- data/lib/sequel/plugins/force_encoding.rb +4 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +84 -73
- data/spec/core/database_spec.rb +11 -3
- data/spec/core/dataset_spec.rb +22 -0
- data/spec/core/schema_spec.rb +41 -0
- data/spec/extensions/force_encoding_spec.rb +9 -0
- data/spec/integration/schema_test.rb +32 -7
- data/spec/model/base_spec.rb +16 -0
- metadata +5 -2
@@ -140,6 +140,11 @@ module Sequel
|
|
140
140
|
sqlite_version >= 30300
|
141
141
|
end
|
142
142
|
|
143
|
+
# SQLite 3.6.19+ supports deferrable foreign key constraints.
|
144
|
+
def supports_deferrable_foreign_key_constraints?
|
145
|
+
sqlite_version >= 30619
|
146
|
+
end
|
147
|
+
|
143
148
|
# SQLite 3.6.8+ supports savepoints.
|
144
149
|
def supports_savepoints?
|
145
150
|
sqlite_version >= 30608
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -10,6 +10,10 @@ module Sequel
|
|
10
10
|
# in the extension).
|
11
11
|
EXTENSIONS = {}
|
12
12
|
|
13
|
+
# The general default size for string columns for all Sequel::Database
|
14
|
+
# instances.
|
15
|
+
DEFAULT_STRING_COLUMN_SIZE = 255
|
16
|
+
|
13
17
|
# Register an extension callback for Database objects. ext should be the
|
14
18
|
# extension name symbol, and mod should either be a Module that the
|
15
19
|
# database is extended with, or a callable object called with the database
|
@@ -44,11 +48,15 @@ module Sequel
|
|
44
48
|
# Set the timezone to use for this database, overridding <tt>Sequel.database_timezone</tt>.
|
45
49
|
attr_writer :timezone
|
46
50
|
|
51
|
+
# The specific default size of string columns for this Sequel::Database, usually 255 by default.
|
52
|
+
attr_accessor :default_string_column_size
|
53
|
+
|
47
54
|
# Constructs a new instance of a database connection with the specified
|
48
55
|
# options hash.
|
49
56
|
#
|
50
57
|
# Accepts the following options:
|
51
58
|
# :default_schema :: The default schema to use, see #default_schema.
|
59
|
+
# :default_string_column_size :: The default size of string columns, 255 by default.
|
52
60
|
# :identifier_input_method :: A string method symbol to call on identifiers going into the database
|
53
61
|
# :identifier_output_method :: A string method symbol to call on identifiers coming from the database
|
54
62
|
# :logger :: A specific logger to use
|
@@ -71,6 +79,7 @@ module Sequel
|
|
71
79
|
@opts[:single_threaded] = @single_threaded = typecast_value_boolean(@opts.fetch(:single_threaded, @@single_threaded))
|
72
80
|
@schemas = {}
|
73
81
|
@default_schema = @opts.fetch(:default_schema, default_schema_default)
|
82
|
+
@default_string_column_size = @opts[:default_string_column_size] || DEFAULT_STRING_COLUMN_SIZE
|
74
83
|
@prepared_statements = {}
|
75
84
|
@transactions = {}
|
76
85
|
@identifier_input_method = nil
|
@@ -205,6 +214,18 @@ module Sequel
|
|
205
214
|
false
|
206
215
|
end
|
207
216
|
|
217
|
+
# Whether the database supports deferrable constraints, false
|
218
|
+
# by default as few databases do.
|
219
|
+
def supports_deferrable_constraints?
|
220
|
+
false
|
221
|
+
end
|
222
|
+
|
223
|
+
# Whether the database supports deferrable foreign key constraints,
|
224
|
+
# false by default as few databases do.
|
225
|
+
def supports_deferrable_foreign_key_constraints?
|
226
|
+
supports_deferrable_constraints?
|
227
|
+
end
|
228
|
+
|
208
229
|
# Whether the database supports DROP TABLE IF EXISTS syntax,
|
209
230
|
# default is the same as #supports_create_table_if_not_exists?.
|
210
231
|
def supports_drop_table_if_exists?
|
@@ -232,7 +232,7 @@ module Sequel
|
|
232
232
|
|
233
233
|
# Starts a database transaction. When a database transaction is used,
|
234
234
|
# either all statements are successful or none of the statements are
|
235
|
-
# successful. Note that MySQL MyISAM
|
235
|
+
# successful. Note that MySQL MyISAM tables do not support transactions.
|
236
236
|
#
|
237
237
|
# The following general options are respected:
|
238
238
|
#
|
@@ -333,7 +333,15 @@ module Sequel
|
|
333
333
|
begin
|
334
334
|
committed = commit_or_rollback_transaction(e, conn, opts)
|
335
335
|
rescue Exception => e2
|
336
|
-
|
336
|
+
begin
|
337
|
+
raise_error(e2, :classes=>database_error_classes, :conn=>conn)
|
338
|
+
rescue Sequel::DatabaseError => e4
|
339
|
+
begin
|
340
|
+
rollback_transaction(conn, opts)
|
341
|
+
ensure
|
342
|
+
raise e4
|
343
|
+
end
|
344
|
+
end
|
337
345
|
ensure
|
338
346
|
remove_transaction(conn, committed)
|
339
347
|
end
|
@@ -79,10 +79,11 @@ module Sequel
|
|
79
79
|
# The following options are supported:
|
80
80
|
#
|
81
81
|
# :default :: The default value for the column.
|
82
|
-
# :deferrable ::
|
83
|
-
#
|
84
|
-
#
|
85
|
-
# DEFERRABLE INITIALLY DEFERRED on key creation.
|
82
|
+
# :deferrable :: For foreign key columns, this ensures referential integrity will work even if
|
83
|
+
# referencing table uses a foreign key value that does not
|
84
|
+
# yet exist on referenced table (but will exist before the transaction commits).
|
85
|
+
# Basically it adds DEFERRABLE INITIALLY DEFERRED on key creation.
|
86
|
+
# If you use :immediate as the value, uses DEFERRABLE INITIALLY IMMEDIATE.
|
86
87
|
# :index :: Create an index on this column. If given a hash, use the hash as the
|
87
88
|
# options for the index.
|
88
89
|
# :key :: For foreign key columns, the column in the associated table
|
@@ -239,6 +240,8 @@ module Sequel
|
|
239
240
|
# Add a unique constraint on the given columns to the DDL.
|
240
241
|
#
|
241
242
|
# unique(:name) # UNIQUE (name)
|
243
|
+
#
|
244
|
+
# Supports the same :deferrable option as #column.
|
242
245
|
def unique(columns, opts = {})
|
243
246
|
constraints << {:type => :unique, :columns => Array(columns)}.merge(opts)
|
244
247
|
end
|
@@ -304,6 +307,8 @@ module Sequel
|
|
304
307
|
#
|
305
308
|
# add_unique_constraint(:name) # ADD UNIQUE (name)
|
306
309
|
# add_unique_constraint(:name, :name=>:unique_name) # ADD CONSTRAINT unique_name UNIQUE (name)
|
310
|
+
#
|
311
|
+
# Supports the same :deferrable option as CreateTableGenerator#column.
|
307
312
|
def add_unique_constraint(columns, opts = {})
|
308
313
|
@operations << {:op => :add_constraint, :type => :unique, :columns => Array(columns)}.merge(opts)
|
309
314
|
end
|
@@ -493,7 +493,7 @@ module Sequel
|
|
493
493
|
sql << "(#{Array(column[:key]).map{|x| quote_identifier(x)}.join(COMMA_SEPARATOR)})" if column[:key]
|
494
494
|
sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
|
495
495
|
sql << " ON UPDATE #{on_update_clause(column[:on_update])}" if column[:on_update]
|
496
|
-
sql
|
496
|
+
constraint_deferrable_sql_append(sql, column[:deferrable])
|
497
497
|
sql
|
498
498
|
end
|
499
499
|
|
@@ -524,9 +524,23 @@ module Sequel
|
|
524
524
|
else
|
525
525
|
raise Error, "Invalid constriant type #{constraint[:type]}, should be :check, :primary_key, :foreign_key, or :unique"
|
526
526
|
end
|
527
|
+
constraint_deferrable_sql_append(sql, constraint[:deferrable])
|
527
528
|
sql
|
528
529
|
end
|
529
530
|
|
531
|
+
# SQL DDL fragment specifying the deferrable constraint attributes.
|
532
|
+
def constraint_deferrable_sql_append(sql, defer)
|
533
|
+
case defer
|
534
|
+
when nil
|
535
|
+
when false
|
536
|
+
sql << ' NOT DEFERRABLE'
|
537
|
+
when :immediate
|
538
|
+
sql << ' DEFERRABLE INITIALLY IMMEDIATE'
|
539
|
+
else
|
540
|
+
sql << ' DEFERRABLE INITIALLY DEFERRED'
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
530
544
|
# Execute the create table statements using the generator.
|
531
545
|
def create_table_from_generator(name, generator, options)
|
532
546
|
execute_ddl(create_table_sql(name, generator, options))
|
@@ -788,9 +802,9 @@ module Sequel
|
|
788
802
|
if column[:text]
|
789
803
|
uses_clob_for_text? ? :clob : :text
|
790
804
|
elsif column[:fixed]
|
791
|
-
"char(#{column[:size]||
|
805
|
+
"char(#{column[:size]||default_string_column_size})"
|
792
806
|
else
|
793
|
-
"varchar(#{column[:size]||
|
807
|
+
"varchar(#{column[:size]||default_string_column_size})"
|
794
808
|
end
|
795
809
|
end
|
796
810
|
|
@@ -810,7 +824,7 @@ module Sequel
|
|
810
824
|
def type_literal_specific(column)
|
811
825
|
type = column[:type]
|
812
826
|
type = "double precision" if type.to_s == 'double'
|
813
|
-
column[:size] ||=
|
827
|
+
column[:size] ||= default_string_column_size if type.to_s == 'varchar'
|
814
828
|
elements = column[:size] || column[:elements]
|
815
829
|
"#{type}#{literal(Array(elements)) if elements}#{UNSIGNED if column[:unsigned]}"
|
816
830
|
end
|
@@ -58,11 +58,14 @@ module Sequel
|
|
58
58
|
a
|
59
59
|
end
|
60
60
|
|
61
|
-
# Returns the average value for the given column.
|
61
|
+
# Returns the average value for the given column/expression.
|
62
|
+
# Uses a virtual row block if no argument is given.
|
62
63
|
#
|
63
64
|
# DB[:table].avg(:number) # SELECT avg(number) FROM table LIMIT 1
|
64
65
|
# # => 3
|
65
|
-
|
66
|
+
# DB[:table].avg{function(column)} # SELECT avg(function(column)) FROM table LIMIT 1
|
67
|
+
# # => 1
|
68
|
+
def avg(column=Sequel.virtual_row(&Proc.new))
|
66
69
|
aggregate_dataset.get{avg(column)}
|
67
70
|
end
|
68
71
|
|
@@ -342,11 +345,13 @@ module Sequel
|
|
342
345
|
end
|
343
346
|
|
344
347
|
# Returns the interval between minimum and maximum values for the given
|
345
|
-
# column.
|
348
|
+
# column/expression. Uses a virtual row block if no argument is given.
|
346
349
|
#
|
347
350
|
# DB[:table].interval(:id) # SELECT (max(id) - min(id)) FROM table LIMIT 1
|
348
351
|
# # => 6
|
349
|
-
|
352
|
+
# DB[:table].interval{function(column)} # SELECT (max(function(column)) - min(function(column))) FROM table LIMIT 1
|
353
|
+
# # => 7
|
354
|
+
def interval(column=Sequel.virtual_row(&Proc.new))
|
350
355
|
aggregate_dataset.get{max(column) - min(column)}
|
351
356
|
end
|
352
357
|
|
@@ -393,19 +398,25 @@ module Sequel
|
|
393
398
|
end
|
394
399
|
end
|
395
400
|
|
396
|
-
# Returns the maximum value for the given column.
|
401
|
+
# Returns the maximum value for the given column/expression.
|
402
|
+
# Uses a virtual row block if no argument is given.
|
397
403
|
#
|
398
404
|
# DB[:table].max(:id) # SELECT max(id) FROM table LIMIT 1
|
399
405
|
# # => 10
|
400
|
-
|
406
|
+
# DB[:table].max{function(column)} # SELECT max(function(column)) FROM table LIMIT 1
|
407
|
+
# # => 7
|
408
|
+
def max(column=Sequel.virtual_row(&Proc.new))
|
401
409
|
aggregate_dataset.get{max(column)}
|
402
410
|
end
|
403
411
|
|
404
|
-
# Returns the minimum value for the given column.
|
412
|
+
# Returns the minimum value for the given column/expression.
|
413
|
+
# Uses a virtual row block if no argument is given.
|
405
414
|
#
|
406
415
|
# DB[:table].min(:id) # SELECT min(id) FROM table LIMIT 1
|
407
416
|
# # => 1
|
408
|
-
|
417
|
+
# DB[:table].min{function(column)} # SELECT min(function(column)) FROM table LIMIT 1
|
418
|
+
# # => 0
|
419
|
+
def min(column=Sequel.virtual_row(&Proc.new))
|
409
420
|
aggregate_dataset.get{min(column)}
|
410
421
|
end
|
411
422
|
|
@@ -428,11 +439,13 @@ module Sequel
|
|
428
439
|
end
|
429
440
|
|
430
441
|
# Returns a +Range+ instance made from the minimum and maximum values for the
|
431
|
-
# given column.
|
442
|
+
# given column/expression. Uses a virtual row block if no argument is given.
|
432
443
|
#
|
433
444
|
# DB[:table].range(:id) # SELECT max(id) AS v1, min(id) AS v2 FROM table LIMIT 1
|
434
445
|
# # => 1..10
|
435
|
-
|
446
|
+
# DB[:table].interval{function(column)} # SELECT max(function(column)) AS v1, min(function(column)) AS v2 FROM table LIMIT 1
|
447
|
+
# # => 0..7
|
448
|
+
def range(column=Sequel.virtual_row(&Proc.new))
|
436
449
|
if r = aggregate_dataset.select{[min(column).as(v1), max(column).as(v2)]}.first
|
437
450
|
(r[:v1]..r[:v2])
|
438
451
|
end
|
@@ -543,11 +556,14 @@ module Sequel
|
|
543
556
|
end
|
544
557
|
end
|
545
558
|
|
546
|
-
# Returns the sum for the given column.
|
559
|
+
# Returns the sum for the given column/expression.
|
560
|
+
# Uses a virtual row block if no column is given.
|
547
561
|
#
|
548
562
|
# DB[:table].sum(:id) # SELECT sum(id) FROM table LIMIT 1
|
549
563
|
# # => 55
|
550
|
-
|
564
|
+
# DB[:table].sum{function(column)} # SELECT sum(function(column)) FROM table LIMIT 1
|
565
|
+
# # => 10
|
566
|
+
def sum(column=Sequel.virtual_row(&Proc.new))
|
551
567
|
aggregate_dataset.get{sum(column)}
|
552
568
|
end
|
553
569
|
|
data/lib/sequel/dataset/query.rb
CHANGED
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -206,7 +206,7 @@ module Sequel
|
|
206
206
|
COMMA_SEPARATOR = COMMA
|
207
207
|
CONDITION_FALSE = '(1 = 0)'.freeze
|
208
208
|
CONDITION_TRUE = '(1 = 1)'.freeze
|
209
|
-
COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
|
209
|
+
COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :offset, :compounds]
|
210
210
|
COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, WILDCARD).as(:count)
|
211
211
|
DATASET_ALIAS_BASE_NAME = 't'.freeze
|
212
212
|
DEFAULT = LiteralString.new('DEFAULT').freeze
|
data/lib/sequel/model.rb
CHANGED
@@ -106,7 +106,7 @@ module Sequel
|
|
106
106
|
# Class instance variables that are inherited in subclasses. If the value is <tt>:dup</tt>, dup is called
|
107
107
|
# on the superclass's instance variable when creating the instance variable in the subclass.
|
108
108
|
# If the value is +nil+, the superclass's instance variable is used directly in the subclass.
|
109
|
-
INHERITED_INSTANCE_VARIABLES = {:@allowed_columns=>:dup,
|
109
|
+
INHERITED_INSTANCE_VARIABLES = {:@allowed_columns=>:dup,
|
110
110
|
:@dataset_method_modules=>:dup, :@primary_key=>nil, :@use_transactions=>nil,
|
111
111
|
:@raise_on_save_failure=>nil, :@require_modification=>nil,
|
112
112
|
:@restricted_columns=>:dup, :@restrict_primary_key=>nil,
|
@@ -129,7 +129,6 @@ module Sequel
|
|
129
129
|
@db = nil
|
130
130
|
@db_schema = nil
|
131
131
|
@dataset_method_modules = []
|
132
|
-
@dataset_methods = {}
|
133
132
|
@overridable_methods_module = nil
|
134
133
|
@plugins = []
|
135
134
|
@primary_key = :id
|
@@ -147,7 +146,7 @@ module Sequel
|
|
147
146
|
@use_after_commit_rollback = true
|
148
147
|
@use_transactions = true
|
149
148
|
|
150
|
-
Sequel.require %w"default_inflections inflections plugins base exceptions errors", "model"
|
149
|
+
Sequel.require %w"default_inflections inflections plugins dataset_module base exceptions errors", "model"
|
151
150
|
if !defined?(::SEQUEL_NO_ASSOCIATIONS) && !ENV.has_key?('SEQUEL_NO_ASSOCIATIONS')
|
152
151
|
Sequel.require 'associations', 'model'
|
153
152
|
plugin Model::Associations
|
data/lib/sequel/model/base.rb
CHANGED
@@ -24,11 +24,6 @@ module Sequel
|
|
24
24
|
# with all of these modules.
|
25
25
|
attr_reader :dataset_method_modules
|
26
26
|
|
27
|
-
# Hash of dataset methods with method name keys and proc values that are
|
28
|
-
# stored so when the dataset changes, methods defined with def_dataset_method
|
29
|
-
# will be applied to the new dataset.
|
30
|
-
attr_reader :dataset_methods
|
31
|
-
|
32
27
|
# SQL string fragment used for faster DELETE statement creation when deleting/destroying
|
33
28
|
# model instances, or nil if the optimization should not be used. For internal use only.
|
34
29
|
attr_reader :fast_instance_delete_sql
|
@@ -179,13 +174,19 @@ module Sequel
|
|
179
174
|
end
|
180
175
|
|
181
176
|
# Extend the dataset with a module, similar to adding
|
182
|
-
# a plugin with the methods defined in DatasetMethods.
|
183
|
-
# is
|
184
|
-
#
|
185
|
-
# module
|
177
|
+
# a plugin with the methods defined in DatasetMethods.
|
178
|
+
# This is the recommended way to add methods to model datasets.
|
179
|
+
#
|
180
|
+
# If an argument, it should be a module, and is used to extend
|
181
|
+
# the underlying dataset. Otherwise an anonymous module is created, and
|
182
|
+
# if a block is given, it is module_evaled, allowing you do define
|
183
|
+
# dataset methods directly using the standard ruby def syntax.
|
184
|
+
# Returns the module given or the anonymous module created.
|
186
185
|
#
|
186
|
+
# # Usage with existing module
|
187
187
|
# Artist.dataset_module Sequel::ColumnsIntrospection
|
188
188
|
#
|
189
|
+
# # Usage with anonymous module
|
189
190
|
# Artist.dataset_module do
|
190
191
|
# def foo
|
191
192
|
# :bar
|
@@ -195,13 +196,24 @@ module Sequel
|
|
195
196
|
# # => :bar
|
196
197
|
# Artist.foo
|
197
198
|
# # => :bar
|
199
|
+
#
|
200
|
+
# Any anonymous modules created are actually instances of Sequel::Model::DatasetModule
|
201
|
+
# (a Module subclass), which allows you to call the subset method on them:
|
202
|
+
#
|
203
|
+
# Artist.dataset_module do
|
204
|
+
# subset :released, Sequel.identifier(release_date) > Sequel::CURRENT_DATE
|
205
|
+
# end
|
206
|
+
#
|
207
|
+
# Any public methods in the dataset module will have class methods created that
|
208
|
+
# call the method on the dataset, assuming that the class method is not already
|
209
|
+
# defined.
|
198
210
|
def dataset_module(mod = nil)
|
199
211
|
if mod
|
200
212
|
raise Error, "can't provide both argument and block to Model.dataset_module" if block_given?
|
201
213
|
dataset_extend(mod)
|
202
214
|
mod
|
203
215
|
else
|
204
|
-
@dataset_module ||=
|
216
|
+
@dataset_module ||= DatasetModule.new(self)
|
205
217
|
@dataset_module.module_eval(&Proc.new) if block_given?
|
206
218
|
dataset_extend(@dataset_module)
|
207
219
|
@dataset_module
|
@@ -270,6 +282,10 @@ module Sequel
|
|
270
282
|
# If a block is not given, just define a class method on the model for each argument
|
271
283
|
# that calls the dataset method of the same argument name.
|
272
284
|
#
|
285
|
+
# It is recommended that you define methods inside a block passed to #dataset_module
|
286
|
+
# instead of using this method, as #dataset_module allows you to use normal
|
287
|
+
# ruby def syntax.
|
288
|
+
#
|
273
289
|
# # Add new dataset method and class method that calls it
|
274
290
|
# Artist.def_dataset_method(:by_name){order(:name)}
|
275
291
|
# Artist.filter(:name.like('A%')).by_name
|
@@ -280,18 +296,12 @@ module Sequel
|
|
280
296
|
# Artist.server!(:server1)
|
281
297
|
def def_dataset_method(*args, &block)
|
282
298
|
raise(Error, "No arguments given") if args.empty?
|
299
|
+
|
283
300
|
if block
|
284
301
|
raise(Error, "Defining a dataset method using a block requires only one argument") if args.length > 1
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
end
|
289
|
-
args.each do |arg|
|
290
|
-
if arg.to_s =~ NORMAL_METHOD_NAME_REGEXP
|
291
|
-
instance_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__) unless respond_to?(arg, true)
|
292
|
-
else
|
293
|
-
def_model_dataset_method_block(arg)
|
294
|
-
end
|
302
|
+
dataset_module{define_method(args.first, &block)}
|
303
|
+
else
|
304
|
+
args.each{|arg| def_model_dataset_method(arg)}
|
295
305
|
end
|
296
306
|
end
|
297
307
|
|
@@ -483,8 +493,7 @@ module Sequel
|
|
483
493
|
# Returns self.
|
484
494
|
#
|
485
495
|
# This changes the row_proc of the dataset to return
|
486
|
-
# model objects
|
487
|
-
# and defines methods on the dataset using the dataset_methods.
|
496
|
+
# model objects and extends the dataset with the dataset_method_modules.
|
488
497
|
# It also attempts to determine the database schema for the model,
|
489
498
|
# based on the given dataset.
|
490
499
|
#
|
@@ -514,11 +523,10 @@ module Sequel
|
|
514
523
|
@columns = @dataset.columns rescue nil
|
515
524
|
else
|
516
525
|
@dataset_method_modules.each{|m| @dataset.extend(m)} if @dataset_method_modules
|
517
|
-
@dataset_methods.each{|meth, block| @dataset.meta_def(meth, &block)} if @dataset_methods
|
518
526
|
end
|
519
527
|
@dataset.model = self if @dataset.respond_to?(:model=)
|
520
528
|
check_non_connection_error{@db_schema = (inherited ? superclass.db_schema : get_db_schema)}
|
521
|
-
|
529
|
+
reset_instance_dataset
|
522
530
|
self
|
523
531
|
end
|
524
532
|
|
@@ -577,8 +585,8 @@ module Sequel
|
|
577
585
|
end
|
578
586
|
end
|
579
587
|
|
580
|
-
#
|
581
|
-
#
|
588
|
+
# Sets up a dataset method that returns a filtered dataset.
|
589
|
+
# Sometimes thought of as a scope, and like most dataset methods,
|
582
590
|
# they can be chained.
|
583
591
|
# For example:
|
584
592
|
#
|
@@ -597,9 +605,10 @@ module Sequel
|
|
597
605
|
# Both the args given and the block are passed to <tt>Dataset#filter</tt>.
|
598
606
|
#
|
599
607
|
# This method creates dataset methods that do not accept arguments. To create
|
600
|
-
# dataset methods that accept arguments, you
|
608
|
+
# dataset methods that accept arguments, you should use define a
|
609
|
+
# method directly inside a #dataset_module block.
|
601
610
|
def subset(name, *args, &block)
|
602
|
-
|
611
|
+
dataset_module.subset(name, *args, &block)
|
603
612
|
end
|
604
613
|
|
605
614
|
# Returns name of primary table for the dataset. If the table for the dataset
|
@@ -640,10 +649,10 @@ module Sequel
|
|
640
649
|
# module if the model has a dataset. Add dataset methods to the class for all
|
641
650
|
# public dataset methods.
|
642
651
|
def dataset_extend(mod)
|
643
|
-
dataset.extend(mod) if @dataset
|
652
|
+
@dataset.extend(mod) if @dataset
|
653
|
+
reset_instance_dataset
|
644
654
|
dataset_method_modules << mod
|
645
|
-
|
646
|
-
def_dataset_method(*meths) unless meths.empty?
|
655
|
+
mod.public_instance_methods.each{|meth| def_model_dataset_method(meth)}
|
647
656
|
end
|
648
657
|
|
649
658
|
# Create a column accessor for a column with a method name that is hard to use in ruby code.
|
@@ -671,8 +680,14 @@ module Sequel
|
|
671
680
|
# Define a model method that calls the dataset method with the same name,
|
672
681
|
# only used for methods with names that can't be presented directly in
|
673
682
|
# ruby code.
|
674
|
-
def
|
675
|
-
|
683
|
+
def def_model_dataset_method(meth)
|
684
|
+
return if respond_to?(meth, true)
|
685
|
+
|
686
|
+
if meth.to_s =~ NORMAL_METHOD_NAME_REGEXP
|
687
|
+
instance_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
|
688
|
+
else
|
689
|
+
meta_def(meth){|*args, &block| dataset.send(meth, *args, &block)}
|
690
|
+
end
|
676
691
|
end
|
677
692
|
|
678
693
|
# Get the schema from the database, fall back on checking the columns
|
@@ -799,6 +814,12 @@ module Sequel
|
|
799
814
|
"DELETE FROM #@simple_table WHERE #@simple_pk = ".freeze
|
800
815
|
end
|
801
816
|
end
|
817
|
+
|
818
|
+
# Reset the instance dataset to a modified copy of the current dataset,
|
819
|
+
# should be used whenever the model's dataset is modified.
|
820
|
+
def reset_instance_dataset
|
821
|
+
@instance_dataset = @dataset.limit(1).naked if @dataset
|
822
|
+
end
|
802
823
|
|
803
824
|
# Set the columns for this model and create accessor methods for each column.
|
804
825
|
def set_columns(new_columns)
|