sequel 2.7.1 → 2.8.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 +56 -0
- data/README +1 -0
- data/Rakefile +1 -1
- data/lib/sequel_core.rb +9 -16
- data/lib/sequel_core/adapters/ado.rb +6 -15
- data/lib/sequel_core/adapters/db2.rb +8 -10
- data/lib/sequel_core/adapters/dbi.rb +6 -4
- data/lib/sequel_core/adapters/informix.rb +21 -22
- data/lib/sequel_core/adapters/jdbc.rb +69 -10
- data/lib/sequel_core/adapters/jdbc/postgresql.rb +1 -0
- data/lib/sequel_core/adapters/mysql.rb +81 -13
- data/lib/sequel_core/adapters/odbc.rb +32 -4
- data/lib/sequel_core/adapters/openbase.rb +6 -5
- data/lib/sequel_core/adapters/oracle.rb +23 -7
- data/lib/sequel_core/adapters/postgres.rb +42 -32
- data/lib/sequel_core/adapters/shared/mssql.rb +37 -62
- data/lib/sequel_core/adapters/shared/mysql.rb +22 -7
- data/lib/sequel_core/adapters/shared/oracle.rb +27 -48
- data/lib/sequel_core/adapters/shared/postgres.rb +64 -43
- data/lib/sequel_core/adapters/shared/progress.rb +31 -0
- data/lib/sequel_core/adapters/shared/sqlite.rb +15 -4
- data/lib/sequel_core/adapters/sqlite.rb +6 -14
- data/lib/sequel_core/connection_pool.rb +47 -13
- data/lib/sequel_core/database.rb +60 -35
- data/lib/sequel_core/database/schema.rb +4 -4
- data/lib/sequel_core/dataset.rb +12 -3
- data/lib/sequel_core/dataset/convenience.rb +4 -13
- data/lib/sequel_core/dataset/prepared_statements.rb +30 -28
- data/lib/sequel_core/dataset/sql.rb +144 -85
- data/lib/sequel_core/dataset/stored_procedures.rb +75 -0
- data/lib/sequel_core/dataset/unsupported.rb +31 -0
- data/lib/sequel_core/exceptions.rb +6 -0
- data/lib/sequel_core/schema/generator.rb +4 -3
- data/lib/sequel_core/schema/sql.rb +41 -23
- data/lib/sequel_core/sql.rb +29 -1
- data/lib/sequel_model/associations.rb +1 -1
- data/lib/sequel_model/record.rb +31 -28
- data/spec/adapters/mysql_spec.rb +37 -4
- data/spec/adapters/oracle_spec.rb +26 -4
- data/spec/adapters/sqlite_spec.rb +7 -0
- data/spec/integration/prepared_statement_test.rb +24 -0
- data/spec/integration/schema_test.rb +1 -1
- data/spec/sequel_core/connection_pool_spec.rb +49 -2
- data/spec/sequel_core/core_sql_spec.rb +9 -2
- data/spec/sequel_core/database_spec.rb +64 -14
- data/spec/sequel_core/dataset_spec.rb +105 -7
- data/spec/sequel_core/schema_spec.rb +40 -12
- data/spec/sequel_core/spec_helper.rb +1 -0
- data/spec/sequel_model/spec_helper.rb +1 -0
- metadata +6 -3
@@ -0,0 +1,75 @@
|
|
1
|
+
module Sequel
|
2
|
+
class Dataset
|
3
|
+
module StoredProcedureMethods
|
4
|
+
SQL_QUERY_TYPE = Hash.new{|h,k| h[k] = k}
|
5
|
+
SQL_QUERY_TYPE[:first] = SQL_QUERY_TYPE[:all] = :select
|
6
|
+
|
7
|
+
# The name of the stored procedure to call
|
8
|
+
attr_accessor :sproc_name
|
9
|
+
|
10
|
+
# Call the prepared statement
|
11
|
+
def call(*args, &block)
|
12
|
+
@sproc_args = args
|
13
|
+
case @sproc_type
|
14
|
+
when :select, :all
|
15
|
+
all(&block)
|
16
|
+
when :first
|
17
|
+
first
|
18
|
+
when :insert
|
19
|
+
insert
|
20
|
+
when :update
|
21
|
+
update
|
22
|
+
when :delete
|
23
|
+
delete
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Programmer friendly string showing this is a stored procedure,
|
28
|
+
# showing the name of the procedure.
|
29
|
+
def inspect
|
30
|
+
"<#{self.class.name}/StoredProcedure name=#{@sproc_name}>"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Set the type of the sproc and override the corresponding _sql
|
34
|
+
# method to return the empty string (since the result will be
|
35
|
+
# ignored anyway).
|
36
|
+
def sproc_type=(type)
|
37
|
+
@sproc_type = type
|
38
|
+
meta_def("#{sql_query_type}_sql"){|*a| ''}
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# The type of query (:select, :insert, :delete, :update).
|
44
|
+
def sql_query_type
|
45
|
+
SQL_QUERY_TYPE[@sproc_type]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module StoredProcedures
|
50
|
+
# For the given type (:select, :first, :insert, :update, or :delete),
|
51
|
+
# run the database stored procedure with the given name with the given
|
52
|
+
# arguments.
|
53
|
+
def call_sproc(type, name, *args)
|
54
|
+
prepare_sproc(type, name).call(*args)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Transform this dataset into a stored procedure that you can call
|
58
|
+
# multiple times with new arguments.
|
59
|
+
def prepare_sproc(type, name)
|
60
|
+
sp = clone
|
61
|
+
prepare_extend_sproc(sp)
|
62
|
+
sp.sproc_type = type
|
63
|
+
sp.sproc_name = name
|
64
|
+
sp
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Extend the dataset with the stored procedure methods.
|
70
|
+
def prepare_extend_sproc(ds)
|
71
|
+
ds.extend(StoredProcedureMethods)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Sequel::Dataset
|
2
|
+
# This module should be included in the dataset class for all databases that
|
3
|
+
# don't support INTERSECT or EXCEPT.
|
4
|
+
module UnsupportedIntersectExcept
|
5
|
+
# Raise an Error if EXCEPT is used
|
6
|
+
def except(ds, all=false)
|
7
|
+
raise(Sequel::Error, "EXCEPT not supported")
|
8
|
+
end
|
9
|
+
|
10
|
+
# Raise an Error if INTERSECT is used
|
11
|
+
def intersect(ds, all=false)
|
12
|
+
raise(Sequel::Error, "INTERSECT not supported")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# This module should be included in the dataset class for all databases that
|
17
|
+
# don't support INTERSECT ALL or EXCEPT ALL.
|
18
|
+
module UnsupportedIntersectExceptAll
|
19
|
+
# Raise an Error if EXCEPT is used
|
20
|
+
def except(ds, all=false)
|
21
|
+
raise(Sequel::Error, "EXCEPT ALL not supported") if all
|
22
|
+
super(ds)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Raise an Error if INTERSECT is used
|
26
|
+
def intersect(ds, all=false)
|
27
|
+
raise(Sequel::Error, "INTERSECT ALL not supported") if all
|
28
|
+
super(ds)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -31,4 +31,10 @@ module Sequel
|
|
31
31
|
# Generic error raised by the database adapters, indicating a
|
32
32
|
# problem originating from the database server.
|
33
33
|
class DatabaseError < Error; end
|
34
|
+
|
35
|
+
# Error that should be raised by adapters when they determine that the connection
|
36
|
+
# to the database has been lost. Instructs the connection pool code to
|
37
|
+
# remove that connection from the pool so that other connections can be acquired
|
38
|
+
# automatically.
|
39
|
+
class DatabaseDisconnectError < DatabaseError; end
|
34
40
|
end
|
@@ -127,9 +127,10 @@ module Sequel
|
|
127
127
|
# optional middle argument denotes the type.
|
128
128
|
#
|
129
129
|
# Examples:
|
130
|
-
# primary_key(:id)
|
130
|
+
# primary_key(:id)
|
131
131
|
# primary_key(:zip_code, :null => false)
|
132
132
|
# primary_key([:street_number, :house_number])
|
133
|
+
# primary_key(:id, :string, :auto_increment => false)
|
133
134
|
def primary_key(name, *args)
|
134
135
|
return composite_primary_key(name, *args) if name.is_a?(Array)
|
135
136
|
@primary_key = @db.serial_primary_key_options.merge({:name => name})
|
@@ -276,8 +277,8 @@ module Sequel
|
|
276
277
|
end
|
277
278
|
|
278
279
|
# Modify a column's type in the DDL for the table.
|
279
|
-
def set_column_type(name, type)
|
280
|
-
@operations << {:op => :set_column_type, :name => name, :type => type}
|
280
|
+
def set_column_type(name, type, opts={})
|
281
|
+
@operations << {:op => :set_column_type, :name => name, :type => type}.merge(opts)
|
281
282
|
end
|
282
283
|
|
283
284
|
# Modify a column's NOT NULL constraint.
|
@@ -29,7 +29,7 @@ module Sequel
|
|
29
29
|
when :rename_column
|
30
30
|
"RENAME COLUMN #{quoted_name} TO #{quote_identifier(op[:new_name])}"
|
31
31
|
when :set_column_type
|
32
|
-
"ALTER COLUMN #{quoted_name} TYPE #{op
|
32
|
+
"ALTER COLUMN #{quoted_name} TYPE #{type_literal(op)}"
|
33
33
|
when :set_column_default
|
34
34
|
"ALTER COLUMN #{quoted_name} SET DEFAULT #{literal(op[:default])}"
|
35
35
|
when :set_column_null
|
@@ -187,9 +187,9 @@ module Sequel
|
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
190
|
+
# Proxy the quote_schema_table method to the dataset
|
190
191
|
def quote_schema_table(table)
|
191
|
-
|
192
|
-
"#{"#{quote_identifier(schema)}." if schema}#{quote_identifier(table)}"
|
192
|
+
schema_utility_dataset.quote_schema_table(table)
|
193
193
|
end
|
194
194
|
|
195
195
|
# Proxy the quote_identifier method to the dataset, used for quoting tables and columns.
|
@@ -202,7 +202,7 @@ module Sequel
|
|
202
202
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_schema_table(new_name)}"
|
203
203
|
end
|
204
204
|
|
205
|
-
# Parse the schema from the database
|
205
|
+
# Parse the schema from the database.
|
206
206
|
# If the table_name is not given, returns the schema for all tables as a hash.
|
207
207
|
# If the table_name is given, returns the schema for a single table as an
|
208
208
|
# array with all members being arrays of length 2. Available options are:
|
@@ -212,11 +212,17 @@ module Sequel
|
|
212
212
|
# unless you are sure that schema has not been called before with a
|
213
213
|
# table_name, otherwise you may only getting the schemas for tables
|
214
214
|
# that have been requested explicitly.
|
215
|
-
|
216
|
-
|
215
|
+
# * :schema - An explicit schema to use. It may also be implicitly provided
|
216
|
+
# via the table name.
|
217
|
+
def schema(table = nil, opts={})
|
218
|
+
if table
|
219
|
+
sch, table_name = schema_and_table(table)
|
220
|
+
quoted_name = quote_schema_table(table)
|
221
|
+
end
|
222
|
+
opts = opts.merge(:schema=>sch) if sch && !opts.include?(:schema)
|
217
223
|
if opts[:reload] && @schemas
|
218
224
|
if table_name
|
219
|
-
@schemas.delete(
|
225
|
+
@schemas.delete(quoted_name)
|
220
226
|
else
|
221
227
|
@schemas = nil
|
222
228
|
end
|
@@ -224,26 +230,31 @@ module Sequel
|
|
224
230
|
|
225
231
|
if @schemas
|
226
232
|
if table_name
|
227
|
-
return @schemas[
|
233
|
+
return @schemas[quoted_name] if @schemas[quoted_name]
|
228
234
|
else
|
229
235
|
return @schemas
|
230
236
|
end
|
231
237
|
end
|
238
|
+
|
239
|
+
@schemas ||= Hash.new do |h,k|
|
240
|
+
quote_name = quote_schema_table(k)
|
241
|
+
h[quote_name] if h.include?(quote_name)
|
242
|
+
end
|
232
243
|
|
233
244
|
if table_name
|
234
|
-
@schemas ||= {}
|
235
245
|
if respond_to?(:schema_parse_table, true)
|
236
|
-
@schemas[
|
246
|
+
@schemas[quoted_name] = schema_parse_table(table_name, opts)
|
237
247
|
else
|
238
248
|
raise Error, 'schema parsing is not implemented on this database'
|
239
249
|
end
|
240
250
|
else
|
241
251
|
if respond_to?(:schema_parse_tables, true)
|
242
|
-
@schemas
|
252
|
+
@schemas.merge!(schema_parse_tables(opts))
|
243
253
|
elsif respond_to?(:schema_parse_table, true) and respond_to?(:tables, true)
|
244
|
-
tables.each{|t|
|
254
|
+
tables.each{|t| @schemas[quote_identifier(t)] = schema_parse_table(t.to_s, opts)}
|
245
255
|
@schemas
|
246
256
|
else
|
257
|
+
@schemas = nil
|
247
258
|
raise Error, 'schema parsing is not implemented on this database'
|
248
259
|
end
|
249
260
|
end
|
@@ -256,30 +267,37 @@ module Sequel
|
|
256
267
|
|
257
268
|
private
|
258
269
|
|
270
|
+
# Remove the cached schema for the given schema name
|
271
|
+
def remove_cached_schema(table)
|
272
|
+
@schemas.delete(quote_schema_table(table)) if @schemas
|
273
|
+
end
|
274
|
+
|
259
275
|
# Match the database's column type to a ruby type via a
|
260
276
|
# regular expression. The following ruby types are supported:
|
261
277
|
# integer, string, date, datetime, boolean, and float.
|
262
278
|
def schema_column_type(db_type)
|
263
279
|
case db_type
|
264
|
-
when /\Atinyint/
|
280
|
+
when /\Atinyint/io
|
265
281
|
Sequel.convert_tinyint_to_bool ? :boolean : :integer
|
266
|
-
when /\
|
267
|
-
:
|
268
|
-
when /\A(character( varying)?|varchar|text)/
|
282
|
+
when /\Ainterval\z/io
|
283
|
+
:interval
|
284
|
+
when /\A(character( varying)?|varchar|text)/io
|
269
285
|
:string
|
270
|
-
when /\
|
286
|
+
when /\A(int(eger)?|bigint|smallint)/io
|
287
|
+
:integer
|
288
|
+
when /\Adate\z/io
|
271
289
|
:date
|
272
|
-
when /\A(datetime|timestamp( with(out)? time zone)?)\z/
|
290
|
+
when /\A(datetime|timestamp( with(out)? time zone)?)\z/io
|
273
291
|
:datetime
|
274
|
-
when /\Atime( with(out)? time zone)?\z/
|
292
|
+
when /\Atime( with(out)? time zone)?\z/io
|
275
293
|
:time
|
276
|
-
when
|
294
|
+
when /\Aboolean\z/io
|
277
295
|
:boolean
|
278
|
-
when /\A(real|float|double( precision)?)\z/
|
296
|
+
when /\A(real|float|double( precision)?)\z/io
|
279
297
|
:float
|
280
|
-
when /\A(numeric(\(\d+,\d+\))?|decimal|money)\z/
|
298
|
+
when /\A(numeric(\(\d+,\d+\))?|decimal|money)\z/io
|
281
299
|
:decimal
|
282
|
-
when
|
300
|
+
when /\Abytea\z/io
|
283
301
|
:blob
|
284
302
|
end
|
285
303
|
end
|
data/lib/sequel_core/sql.rb
CHANGED
@@ -685,6 +685,34 @@ module Sequel
|
|
685
685
|
end
|
686
686
|
end
|
687
687
|
|
688
|
+
# Represents a literal string with placeholders and arguments.
|
689
|
+
# This is necessary to ensure delayed literalization of the arguments
|
690
|
+
# required for the prepared statement support
|
691
|
+
class PlaceholderLiteralString < SpecificExpression
|
692
|
+
# The arguments that will be subsituted into the placeholders.
|
693
|
+
attr_reader :args
|
694
|
+
|
695
|
+
# The literal string containing placeholders
|
696
|
+
attr_reader :str
|
697
|
+
|
698
|
+
# Whether to surround the expression with parantheses
|
699
|
+
attr_reader :parens
|
700
|
+
|
701
|
+
# Create an object with the given conditions and
|
702
|
+
# default value.
|
703
|
+
def initialize(str, args, parens=false)
|
704
|
+
@str = str
|
705
|
+
@args = args
|
706
|
+
@parens = parens
|
707
|
+
end
|
708
|
+
|
709
|
+
# Delegate the creation of the resulting SQL to the given dataset,
|
710
|
+
# since it may be database dependent.
|
711
|
+
def to_s(ds)
|
712
|
+
ds.placeholder_literal_string_sql(self)
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
688
716
|
# Subclass of ComplexExpression where the expression results
|
689
717
|
# in a numeric value in SQL.
|
690
718
|
class NumericExpression < ComplexExpression
|
@@ -831,7 +859,7 @@ module Sequel
|
|
831
859
|
# LiteralString is used to represent literal SQL expressions. A
|
832
860
|
# LiteralString is copied verbatim into an SQL statement. Instances of
|
833
861
|
# LiteralString can be created by calling String#lit.
|
834
|
-
# LiteralStrings can use all of the SQL::
|
862
|
+
# LiteralStrings can also use all of the SQL::OrderMethods and the
|
835
863
|
# SQL::ComplexExpressionMethods.
|
836
864
|
class LiteralString < ::String
|
837
865
|
include SQL::OrderMethods
|
@@ -394,7 +394,7 @@ module Sequel::Model::Associations
|
|
394
394
|
return if old_val and run_association_callbacks(opts, :before_remove, old_val) == false
|
395
395
|
return if o and run_association_callbacks(opts, :before_add, o) == false
|
396
396
|
send(opts._setter_method, o)
|
397
|
-
|
397
|
+
associations[name] = o
|
398
398
|
remove_reciprocal_object(opts, old_val) if old_val
|
399
399
|
add_reciprocal_object(opts, o) if o
|
400
400
|
run_association_callbacks(opts, :after_add, o) if o
|
data/lib/sequel_model/record.rb
CHANGED
@@ -4,15 +4,6 @@ module Sequel
|
|
4
4
|
# to be called automatically via set.
|
5
5
|
RESTRICTED_SETTER_METHODS = %w"== === []= taguri= typecast_empty_string_to_nil= typecast_on_assignment= strict_param_setting= raise_on_save_failure= raise_on_typecast_failure="
|
6
6
|
|
7
|
-
# The current cached associations. A hash with the keys being the
|
8
|
-
# association name symbols and the values being the associated object
|
9
|
-
# or nil (many_to_one), or the array of associated objects (*_to_many).
|
10
|
-
attr_reader :associations
|
11
|
-
|
12
|
-
# The columns that have been updated. This isn't completely accurate,
|
13
|
-
# see Model#[]=.
|
14
|
-
attr_reader :changed_columns
|
15
|
-
|
16
7
|
# The hash of attribute values. Keys are symbols with the names of the
|
17
8
|
# underlying database columns.
|
18
9
|
attr_reader :values
|
@@ -32,9 +23,7 @@ module Sequel
|
|
32
23
|
# string keys will work if from_db is false.
|
33
24
|
# * from_db - should only be set by Model.load, forget it
|
34
25
|
# exists.
|
35
|
-
def initialize(values = {}, from_db = false
|
36
|
-
@associations = {}
|
37
|
-
@changed_columns = []
|
26
|
+
def initialize(values = {}, from_db = false)
|
38
27
|
if from_db
|
39
28
|
@new = false
|
40
29
|
@values = values
|
@@ -42,8 +31,8 @@ module Sequel
|
|
42
31
|
@values = {}
|
43
32
|
@new = true
|
44
33
|
set(values)
|
45
|
-
|
46
|
-
yield self if
|
34
|
+
changed_columns.clear
|
35
|
+
yield self if block_given?
|
47
36
|
end
|
48
37
|
after_initialize
|
49
38
|
end
|
@@ -61,7 +50,7 @@ module Sequel
|
|
61
50
|
# If the column isn't in @values, we can't assume it is
|
62
51
|
# NULL in the database, so assume it has changed.
|
63
52
|
if new? || !@values.include?(column) || value != @values[column]
|
64
|
-
|
53
|
+
changed_columns << column unless changed_columns.include?(column)
|
65
54
|
@values[column] = typecast_value(column, value)
|
66
55
|
end
|
67
56
|
end
|
@@ -84,6 +73,19 @@ module Sequel
|
|
84
73
|
# self.class.
|
85
74
|
alias_method :model, :class
|
86
75
|
|
76
|
+
# The current cached associations. A hash with the keys being the
|
77
|
+
# association name symbols and the values being the associated object
|
78
|
+
# or nil (many_to_one), or the array of associated objects (*_to_many).
|
79
|
+
def associations
|
80
|
+
@associations ||= {}
|
81
|
+
end
|
82
|
+
|
83
|
+
# The columns that have been updated. This isn't completely accurate,
|
84
|
+
# see Model#[]=.
|
85
|
+
def changed_columns
|
86
|
+
@changed_columns ||= []
|
87
|
+
end
|
88
|
+
|
87
89
|
# Deletes and returns self. Does not run destroy hooks.
|
88
90
|
# Look into using destroy instead.
|
89
91
|
def delete
|
@@ -171,7 +173,8 @@ module Sequel
|
|
171
173
|
# exists in the database.
|
172
174
|
def refresh
|
173
175
|
@values = this.first || raise(Error, "Record not found")
|
174
|
-
|
176
|
+
changed_columns.clear
|
177
|
+
associations.clear
|
175
178
|
self
|
176
179
|
end
|
177
180
|
alias_method :reload, :refresh
|
@@ -220,12 +223,12 @@ module Sequel
|
|
220
223
|
else
|
221
224
|
return save_failure(:update) if before_update == false
|
222
225
|
if columns.empty?
|
223
|
-
vals = opts[:changed] ? @values.reject{|k,v|
|
226
|
+
vals = opts[:changed] ? @values.reject{|k,v| !changed_columns.include?(k)} : @values
|
224
227
|
this.update(vals)
|
225
|
-
|
228
|
+
changed_columns.clear
|
226
229
|
else # update only the specified columns
|
227
|
-
this.update(@values.reject
|
228
|
-
|
230
|
+
this.update(@values.reject{|k, v| !columns.include?(k)})
|
231
|
+
changed_columns.reject!{|c| columns.include?(c)}
|
229
232
|
end
|
230
233
|
after_update
|
231
234
|
end
|
@@ -237,7 +240,7 @@ module Sequel
|
|
237
240
|
# chanaged. If no columns have been changed, returns nil. If unable to
|
238
241
|
# save, returns false unless raise_on_save_failure is true.
|
239
242
|
def save_changes
|
240
|
-
save(:changed=>true) || false unless
|
243
|
+
save(:changed=>true) || false unless changed_columns.empty?
|
241
244
|
end
|
242
245
|
|
243
246
|
# Updates the instance with the supplied values with support for virtual
|
@@ -357,7 +360,7 @@ module Sequel
|
|
357
360
|
raise(Sequel::Error, 'associated object does not have a primary key') if opts.need_associated_primary_key? && !o.pk
|
358
361
|
return if run_association_callbacks(opts, :before_add, o) == false
|
359
362
|
send(opts._add_method, o)
|
360
|
-
|
363
|
+
associations[opts[:name]].push(o) if associations.include?(opts[:name])
|
361
364
|
add_reciprocal_object(opts, o)
|
362
365
|
run_association_callbacks(opts, :after_add, o)
|
363
366
|
o
|
@@ -378,8 +381,8 @@ module Sequel
|
|
378
381
|
# Load the associated objects using the dataset
|
379
382
|
def load_associated_objects(opts, reload=false)
|
380
383
|
name = opts[:name]
|
381
|
-
if
|
382
|
-
|
384
|
+
if associations.include?(name) and !reload
|
385
|
+
associations[name]
|
383
386
|
else
|
384
387
|
objs = if opts.returns_array?
|
385
388
|
send(opts.dataset_method).all
|
@@ -393,7 +396,7 @@ module Sequel
|
|
393
396
|
run_association_callbacks(opts, :after_load, objs)
|
394
397
|
# Only one_to_many associations should set the reciprocal object
|
395
398
|
objs.each{|o| add_reciprocal_object(opts, o)} if opts.set_reciprocal_to_self?
|
396
|
-
|
399
|
+
associations[name] = objs
|
397
400
|
end
|
398
401
|
end
|
399
402
|
|
@@ -401,8 +404,8 @@ module Sequel
|
|
401
404
|
def remove_all_associated_objects(opts)
|
402
405
|
raise(Sequel::Error, 'model object does not have a primary key') unless pk
|
403
406
|
send(opts._remove_all_method)
|
404
|
-
ret =
|
405
|
-
|
407
|
+
ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
|
408
|
+
associations[opts[:name]] = []
|
406
409
|
ret
|
407
410
|
end
|
408
411
|
|
@@ -412,7 +415,7 @@ module Sequel
|
|
412
415
|
raise(Sequel::Error, 'associated object does not have a primary key') if opts.need_associated_primary_key? && !o.pk
|
413
416
|
return if run_association_callbacks(opts, :before_remove, o) == false
|
414
417
|
send(opts._remove_method, o)
|
415
|
-
|
418
|
+
associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name])
|
416
419
|
remove_reciprocal_object(opts, o)
|
417
420
|
run_association_callbacks(opts, :after_remove, o)
|
418
421
|
o
|