sequel 3.2.0 → 3.3.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 +40 -0
- data/Rakefile +1 -1
- data/doc/opening_databases.rdoc +7 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/lib/sequel/adapters/ado.rb +34 -39
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/jdbc.rb +27 -4
- data/lib/sequel/adapters/jdbc/h2.rb +14 -3
- data/lib/sequel/adapters/jdbc/mssql.rb +51 -0
- data/lib/sequel/adapters/mysql.rb +28 -12
- data/lib/sequel/adapters/odbc.rb +36 -30
- data/lib/sequel/adapters/odbc/mssql.rb +44 -0
- data/lib/sequel/adapters/shared/mssql.rb +185 -10
- data/lib/sequel/adapters/shared/mysql.rb +9 -9
- data/lib/sequel/adapters/shared/sqlite.rb +45 -47
- data/lib/sequel/connection_pool.rb +8 -5
- data/lib/sequel/core.rb +2 -8
- data/lib/sequel/database.rb +9 -10
- data/lib/sequel/database/schema_sql.rb +3 -2
- data/lib/sequel/dataset.rb +1 -0
- data/lib/sequel/dataset/sql.rb +15 -6
- data/lib/sequel/extensions/schema_dumper.rb +7 -7
- data/lib/sequel/model/associations.rb +16 -14
- data/lib/sequel/model/base.rb +25 -7
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/many_through_many.rb +0 -1
- data/lib/sequel/sql.rb +8 -11
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +42 -38
- data/spec/adapters/sqlite_spec.rb +0 -4
- data/spec/core/database_spec.rb +22 -1
- data/spec/core/dataset_spec.rb +37 -12
- data/spec/core/expression_filters_spec.rb +5 -0
- data/spec/core/schema_spec.rb +15 -8
- data/spec/extensions/association_proxies_spec.rb +47 -0
- data/spec/extensions/caching_spec.rb +2 -2
- data/spec/extensions/hook_class_methods_spec.rb +6 -6
- data/spec/extensions/many_through_many_spec.rb +13 -0
- data/spec/extensions/schema_dumper_spec.rb +12 -4
- data/spec/extensions/validation_class_methods_spec.rb +3 -3
- data/spec/integration/dataset_test.rb +47 -17
- data/spec/integration/prepared_statement_test.rb +5 -5
- data/spec/integration/schema_test.rb +111 -34
- data/spec/model/associations_spec.rb +128 -11
- data/spec/model/hooks_spec.rb +7 -6
- data/spec/model/model_spec.rb +54 -4
- data/spec/model/record_spec.rb +2 -3
- data/spec/model/validations_spec.rb +4 -4
- metadata +109 -101
- data/spec/adapters/ado_spec.rb +0 -93
@@ -91,12 +91,13 @@ module Sequel
|
|
91
91
|
when :rename_column, :set_column_type, :set_column_null, :set_column_default
|
92
92
|
o = op[:op]
|
93
93
|
opts = schema(table).find{|x| x.first == op[:name]}
|
94
|
-
|
95
|
-
name = o == :rename_column ? op[:new_name] : op[:name]
|
96
|
-
type = o == :set_column_type ? op[:type] :
|
97
|
-
null = o == :set_column_null ? op[:null] :
|
98
|
-
default = o == :set_column_default ? op[:default] :
|
99
|
-
|
94
|
+
opts = opts ? opts.last.dup : {}
|
95
|
+
opts[:name] = o == :rename_column ? op[:new_name] : op[:name]
|
96
|
+
opts[:type] = o == :set_column_type ? op[:type] : opts[:db_type]
|
97
|
+
opts[:null] = o == :set_column_null ? op[:null] : opts[:allow_null]
|
98
|
+
opts[:default] = o == :set_column_default ? op[:default] : opts[:ruby_default]
|
99
|
+
opts.delete(:default) if opts[:default] == nil
|
100
|
+
"ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(opts)}"
|
100
101
|
when :drop_index
|
101
102
|
"#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
|
102
103
|
else
|
@@ -187,10 +188,9 @@ module Sequel
|
|
187
188
|
column[:only_time] ? :time : :datetime
|
188
189
|
end
|
189
190
|
|
190
|
-
# MySQL doesn't have a true boolean class, so it uses tinyint
|
191
|
-
# MySQL doesn't have a true boolean class, so it uses tinyint
|
191
|
+
# MySQL doesn't have a true boolean class, so it uses tinyint(1)
|
192
192
|
def type_literal_generic_trueclass(column)
|
193
|
-
:tinyint
|
193
|
+
:'tinyint(1)'
|
194
194
|
end
|
195
195
|
end
|
196
196
|
|
@@ -107,48 +107,26 @@ module Sequel
|
|
107
107
|
# the column inside of a transaction.
|
108
108
|
def alter_table_sql(table, op)
|
109
109
|
case op[:op]
|
110
|
-
when :
|
110
|
+
when :add_index, :drop_index
|
111
111
|
super
|
112
|
+
when :add_column
|
113
|
+
if op[:unique] || op[:primary_key]
|
114
|
+
duplicate_table(table){|columns| columns.push(op)}
|
115
|
+
else
|
116
|
+
super
|
117
|
+
end
|
112
118
|
when :drop_column
|
113
|
-
|
114
|
-
|
115
|
-
columns_str = dataset.send(:identifier_list, columns_for(table, :except => op[:name]))
|
116
|
-
defined_columns_str = column_list_sql(defined_columns_for(table, :except => op[:name]))
|
117
|
-
["CREATE TEMPORARY TABLE #{bt}(#{defined_columns_str})",
|
118
|
-
"INSERT INTO #{bt} SELECT #{columns_str} FROM #{qt}",
|
119
|
-
"DROP TABLE #{qt}",
|
120
|
-
"CREATE TABLE #{qt}(#{defined_columns_str})",
|
121
|
-
"INSERT INTO #{qt} SELECT #{columns_str} FROM #{bt}",
|
122
|
-
"DROP TABLE #{bt}"]
|
119
|
+
ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
|
120
|
+
duplicate_table(table, :old_columns_proc=>ocp){|columns| columns.delete_if{|s| s[:name].to_s == op[:name].to_s}}
|
123
121
|
when :rename_column
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
new_columns = dataset.send(:identifier_list, new_columns_arr)
|
133
|
-
|
134
|
-
def_old_columns = column_list_sql(defined_columns_for(table))
|
135
|
-
|
136
|
-
def_new_columns_arr = defined_columns_for(table).map do |c|
|
137
|
-
c[:name] = op[:new_name].to_s if c[:name] == op[:name].to_s
|
138
|
-
c
|
139
|
-
end
|
140
|
-
|
141
|
-
def_new_columns = column_list_sql(def_new_columns_arr)
|
142
|
-
|
143
|
-
[
|
144
|
-
"CREATE TEMPORARY TABLE #{bt}(#{def_old_columns})",
|
145
|
-
"INSERT INTO #{bt}(#{old_columns}) SELECT #{old_columns} FROM #{qt}",
|
146
|
-
"DROP TABLE #{qt}",
|
147
|
-
"CREATE TABLE #{qt}(#{def_new_columns})",
|
148
|
-
"INSERT INTO #{qt}(#{new_columns}) SELECT #{old_columns} FROM #{bt}",
|
149
|
-
"DROP TABLE #{bt}"
|
150
|
-
]
|
151
|
-
|
122
|
+
ncp = lambda{|nc| nc.map!{|c| c.to_s == op[:name].to_s ? op[:new_name] : c}}
|
123
|
+
duplicate_table(table, :new_columns_proc=>ncp){|columns| columns.each{|s| s[:name] = op[:new_name] if s[:name].to_s == op[:name].to_s}}
|
124
|
+
when :set_column_default
|
125
|
+
duplicate_table(table){|columns| columns.each{|s| s[:default] = op[:default] if s[:name].to_s == op[:name].to_s}}
|
126
|
+
when :set_column_null
|
127
|
+
duplicate_table(table){|columns| columns.each{|s| s[:null] = op[:null] if s[:name].to_s == op[:name].to_s}}
|
128
|
+
when :set_column_type
|
129
|
+
duplicate_table(table){|columns| columns.each{|s| s[:type] = op[:type] if s[:name].to_s == op[:name].to_s}}
|
152
130
|
else
|
153
131
|
raise Error, "Unsupported ALTER TABLE operation"
|
154
132
|
end
|
@@ -156,19 +134,13 @@ module Sequel
|
|
156
134
|
|
157
135
|
# The array of column symbols in the table, except for ones given in opts[:except]
|
158
136
|
def backup_table_name(table, opts={})
|
137
|
+
table = table.gsub('`', '')
|
159
138
|
(opts[:times]||1000).times do |i|
|
160
139
|
table_name = "#{table}_backup#{i}"
|
161
140
|
return table_name unless table_exists?(table_name)
|
162
141
|
end
|
163
142
|
end
|
164
143
|
|
165
|
-
# The array of column symbols in the table, except for ones given in opts[:except]
|
166
|
-
def columns_for(table, opts={})
|
167
|
-
cols = schema_parse_table(table, {}).map{|c| c[0]}
|
168
|
-
cols = cols - Array(opts[:except])
|
169
|
-
cols
|
170
|
-
end
|
171
|
-
|
172
144
|
# Allow use without a generator, needed for the alter table hackery that Sequel allows.
|
173
145
|
def column_list_sql(generator)
|
174
146
|
generator.is_a?(Schema::Generator) ? super : generator.map{|c| column_definition_sql(c)}.join(', ')
|
@@ -177,7 +149,10 @@ module Sequel
|
|
177
149
|
# The array of column schema hashes, except for the ones given in opts[:except]
|
178
150
|
def defined_columns_for(table, opts={})
|
179
151
|
cols = parse_pragma(table, {})
|
180
|
-
cols.each
|
152
|
+
cols.each do |c|
|
153
|
+
c[:default] = LiteralString.new(c[:default]) if c[:default]
|
154
|
+
c[:type] = c[:db_type]
|
155
|
+
end
|
181
156
|
if opts[:except]
|
182
157
|
nono= Array(opts[:except]).compact.map{|n| n.to_s}
|
183
158
|
cols.reject!{|c| nono.include? c[:name] }
|
@@ -185,6 +160,29 @@ module Sequel
|
|
185
160
|
cols
|
186
161
|
end
|
187
162
|
|
163
|
+
# Duplicate an existing table by creating a new table, copying all records
|
164
|
+
# from the existing table into the new table, deleting the existing table
|
165
|
+
# and renaming the new table to the existing table's name.
|
166
|
+
def duplicate_table(table, opts={})
|
167
|
+
def_columns = defined_columns_for(table)
|
168
|
+
old_columns = def_columns.map{|c| c[:name]}
|
169
|
+
opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
|
170
|
+
|
171
|
+
yield def_columns if block_given?
|
172
|
+
def_columns_str = column_list_sql(def_columns)
|
173
|
+
new_columns = old_columns.dup
|
174
|
+
opts[:new_columns_proc].call(new_columns) if opts[:new_columns_proc]
|
175
|
+
|
176
|
+
qt = quote_schema_table(table)
|
177
|
+
bt = quote_identifier(backup_table_name(qt))
|
178
|
+
[
|
179
|
+
"CREATE TABLE #{bt}(#{def_columns_str})",
|
180
|
+
"INSERT INTO #{bt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{qt}",
|
181
|
+
"DROP TABLE #{qt}",
|
182
|
+
"ALTER TABLE #{bt} RENAME TO #{qt}"
|
183
|
+
]
|
184
|
+
end
|
185
|
+
|
188
186
|
# SQLite folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
189
187
|
def identifier_input_method_default
|
190
188
|
nil
|
@@ -95,16 +95,19 @@ class Sequel::ConnectionPool
|
|
95
95
|
def hold(server=:default)
|
96
96
|
begin
|
97
97
|
t = Thread.current
|
98
|
-
time = Time.new
|
99
|
-
timeout = time + @timeout
|
100
|
-
sleep_time = @sleep_time
|
101
98
|
if conn = owned_connection(t, server)
|
102
99
|
return yield(conn)
|
103
100
|
end
|
104
101
|
begin
|
105
|
-
|
106
|
-
|
102
|
+
unless conn = acquire(t, server)
|
103
|
+
time = Time.new
|
104
|
+
timeout = time + @timeout
|
105
|
+
sleep_time = @sleep_time
|
107
106
|
sleep sleep_time
|
107
|
+
until conn = acquire(t, server)
|
108
|
+
raise(::Sequel::PoolTimeout) if Time.new > timeout
|
109
|
+
sleep sleep_time
|
110
|
+
end
|
108
111
|
end
|
109
112
|
yield conn
|
110
113
|
rescue Sequel::DatabaseDisconnectError => dde
|
data/lib/sequel/core.rb
CHANGED
@@ -15,12 +15,7 @@
|
|
15
15
|
# object, which is closed (disconnected) when the block exits, just
|
16
16
|
# like a block passed to connect. For example:
|
17
17
|
#
|
18
|
-
# Sequel.sqlite('blog.db'){|db| puts db[:users].count}
|
19
|
-
#
|
20
|
-
# Sequel converts the column type tinyint to a boolean by default,
|
21
|
-
# you can override the conversion to use tinyint as an integer:
|
22
|
-
#
|
23
|
-
# Sequel.convert_tinyint_to_bool = false
|
18
|
+
# Sequel.sqlite('blog.db'){|db| puts db[:users].count}
|
24
19
|
#
|
25
20
|
# Sequel converts two digit years in Dates and DateTimes by default,
|
26
21
|
# so 01/02/03 is interpreted at January 2nd, 2003, and 12/13/99 is interpreted
|
@@ -37,13 +32,12 @@
|
|
37
32
|
# You can set the SEQUEL_NO_CORE_EXTENSIONS constant or environment variable to have
|
38
33
|
# Sequel not extend the core classes.
|
39
34
|
module Sequel
|
40
|
-
@convert_tinyint_to_bool = true
|
41
35
|
@convert_two_digit_years = true
|
42
36
|
@datetime_class = Time
|
43
37
|
@virtual_row_instance_eval = true
|
44
38
|
|
45
39
|
class << self
|
46
|
-
attr_accessor :
|
40
|
+
attr_accessor :convert_two_digit_years, :datetime_class, :virtual_row_instance_eval
|
47
41
|
end
|
48
42
|
|
49
43
|
# Returns true if the passed object could be a specifier of conditions, false otherwise.
|
data/lib/sequel/database.rb
CHANGED
@@ -137,9 +137,10 @@ module Sequel
|
|
137
137
|
scheme = uri.scheme
|
138
138
|
scheme = :dbi if scheme =~ /\Adbi-/
|
139
139
|
c = adapter_class(scheme)
|
140
|
-
uri_options =
|
140
|
+
uri_options = c.send(:uri_to_options, uri)
|
141
141
|
uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v} unless uri.query.to_s.strip.empty?
|
142
|
-
|
142
|
+
uri_options.entries.each{|k,v| uri_options[k] = URI.unescape(v) if v.is_a?(String)}
|
143
|
+
opts = uri_options.merge(opts)
|
143
144
|
end
|
144
145
|
when Hash
|
145
146
|
opts = conn_string.merge(opts)
|
@@ -846,27 +847,25 @@ module Sequel
|
|
846
847
|
# integer, string, date, datetime, boolean, and float.
|
847
848
|
def schema_column_type(db_type)
|
848
849
|
case db_type
|
849
|
-
when /\Atinyint/io
|
850
|
-
Sequel.convert_tinyint_to_bool ? :boolean : :integer
|
851
850
|
when /\Ainterval\z/io
|
852
851
|
:interval
|
853
|
-
when /\A(character( varying)?|(var)?char|text)/io
|
852
|
+
when /\A(character( varying)?|n?(var)?char|n?text)/io
|
854
853
|
:string
|
855
|
-
when /\A(int(eger)?|
|
854
|
+
when /\A(int(eger)?|(big|small|tiny)int)/io
|
856
855
|
:integer
|
857
856
|
when /\Adate\z/io
|
858
857
|
:date
|
859
|
-
when /\A(datetime|timestamp( with(out)? time zone)?)\z/io
|
858
|
+
when /\A((small)?datetime|timestamp( with(out)? time zone)?)\z/io
|
860
859
|
:datetime
|
861
860
|
when /\Atime( with(out)? time zone)?\z/io
|
862
861
|
:time
|
863
|
-
when /\
|
862
|
+
when /\A(boolean|bit)\z/io
|
864
863
|
:boolean
|
865
864
|
when /\A(real|float|double( precision)?)\z/io
|
866
865
|
:float
|
867
|
-
when /\A(((numeric|decimal)(\(\d+,\d+\))?)|money)\z/io
|
866
|
+
when /\A(((numeric|decimal)(\(\d+,\d+\))?)|(small)?money)\z/io
|
868
867
|
:decimal
|
869
|
-
when /bytea|blob/io
|
868
|
+
when /bytea|blob|image|(var)?binary/io
|
870
869
|
:blob
|
871
870
|
end
|
872
871
|
end
|
@@ -69,8 +69,9 @@ module Sequel
|
|
69
69
|
def column_definition_sql(column)
|
70
70
|
sql = "#{quote_identifier(column[:name])} #{type_literal(column)}"
|
71
71
|
sql << UNIQUE if column[:unique]
|
72
|
-
|
73
|
-
sql <<
|
72
|
+
null = column.include?(:null) ? column[:null] : column[:allow_null]
|
73
|
+
sql << NOT_NULL if null == false
|
74
|
+
sql << NULL if null == true
|
74
75
|
sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
|
75
76
|
sql << PRIMARY_KEY if column[:primary_key]
|
76
77
|
sql << " #{auto_increment_sql}" if column[:auto_increment]
|
data/lib/sequel/dataset.rb
CHANGED
@@ -342,6 +342,7 @@ module Sequel
|
|
342
342
|
# Modify the identifier returned from the database based on the
|
343
343
|
# identifier_output_method.
|
344
344
|
def output_identifier(v)
|
345
|
+
v = 'untitled' if v == ''
|
345
346
|
(i = identifier_output_method) ? v.to_s.send(i).to_sym : v.to_sym
|
346
347
|
end
|
347
348
|
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -272,14 +272,16 @@ module Sequel
|
|
272
272
|
end
|
273
273
|
|
274
274
|
# Returns a dataset selecting from the current dataset.
|
275
|
+
# Supplying the :alias option controls the name of the result.
|
275
276
|
#
|
276
|
-
# ds = DB[:items].order(:name)
|
277
|
-
# ds.sql
|
278
|
-
# ds.from_self.sql
|
279
|
-
|
277
|
+
# ds = DB[:items].order(:name).select(:id, :name)
|
278
|
+
# ds.sql #=> "SELECT id,name FROM items ORDER BY name"
|
279
|
+
# ds.from_self.sql #=> "SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS 't1'"
|
280
|
+
# ds.from_self(:alias=>:foo).sql #=> "SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS 'foo'"
|
281
|
+
def from_self(opts={})
|
280
282
|
fs = {}
|
281
283
|
@opts.keys.each{|k| fs[k] = nil}
|
282
|
-
clone(fs).from(self)
|
284
|
+
clone(fs).from(opts[:alias] ? as(opts[:alias]) : self)
|
283
285
|
end
|
284
286
|
|
285
287
|
# SQL fragment specifying an SQL function call
|
@@ -453,7 +455,7 @@ module Sequel
|
|
453
455
|
# * options - a hash of options, with any of the following keys:
|
454
456
|
# * :table_alias - the name of the table's alias when joining, necessary for joining
|
455
457
|
# to the same table more than once. No alias is used by default.
|
456
|
-
# * :
|
458
|
+
# * :implicit_qualifier - The name to use for qualifying implicit conditions. By default,
|
457
459
|
# the last joined or primary table is used.
|
458
460
|
# * block - The block argument should only be given if a JOIN with an ON clause is used,
|
459
461
|
# in which case it yields the table alias/name for the table currently being joined,
|
@@ -797,6 +799,13 @@ module Sequel
|
|
797
799
|
opts = {:all=>opts} unless opts.is_a?(Hash)
|
798
800
|
compound_clone(:union, dataset, opts)
|
799
801
|
end
|
802
|
+
|
803
|
+
# Returns a copy of the dataset with no limit or offset.
|
804
|
+
#
|
805
|
+
# dataset.limit(10, 20).unlimited # SELECT * FROM items
|
806
|
+
def unlimited
|
807
|
+
clone(:limit=>nil, :offset=>nil)
|
808
|
+
end
|
800
809
|
|
801
810
|
# Returns a copy of the dataset with no order.
|
802
811
|
#
|
@@ -109,30 +109,30 @@ END_MIG
|
|
109
109
|
case t = schema[:db_type].downcase
|
110
110
|
when /\A(?:medium|small)?int(?:eger)?(?:\((?:\d+)\))?\z/o
|
111
111
|
{:type=>Integer}
|
112
|
-
when /\Atinyint(?:\((
|
113
|
-
{:type=>(Sequel.convert_tinyint_to_bool ? TrueClass : Integer)}
|
112
|
+
when /\Atinyint(?:\((\d+)\))?\z/o
|
113
|
+
{:type=>(self.class.adapter_scheme == :mysql && $1 == '1' && Sequel::MySQL.convert_tinyint_to_bool ? TrueClass : Integer)}
|
114
114
|
when /\Abigint(?:\((?:\d+)\))?\z/o
|
115
115
|
{:type=>Bignum}
|
116
116
|
when /\A(?:real|float|double(?: precision)?)\z/o
|
117
117
|
{:type=>Float}
|
118
118
|
when 'boolean'
|
119
119
|
{:type=>TrueClass}
|
120
|
-
when /\A(?:(?:tiny|medium|long)?text|clob)\z/o
|
120
|
+
when /\A(?:(?:tiny|medium|long|n)?text|clob)\z/o
|
121
121
|
{:type=>String, :text=>true}
|
122
122
|
when 'date'
|
123
123
|
{:type=>Date}
|
124
|
-
when
|
124
|
+
when /\A(?:small)?datetime\z/o
|
125
125
|
{:type=>DateTime}
|
126
126
|
when /\Atimestamp(?: with(?:out)? time zone)?\z/o
|
127
127
|
{:type=>DateTime}
|
128
128
|
when /\Atime(?: with(?:out)? time zone)?\z/o
|
129
129
|
{:type=>Time, :only_time=>true}
|
130
|
-
when /\
|
130
|
+
when /\An?char(?:acter)?(?:\((\d+)\))?\z/o
|
131
131
|
{:type=>String, :size=>($1.to_i if $1), :fixed=>true}
|
132
|
-
when /\A(?:varchar|character varying|bpchar|string)(?:\((\d+)\))?\z/o
|
132
|
+
when /\A(?:n?varchar|character varying|bpchar|string)(?:\((\d+)\))?\z/o
|
133
133
|
s = ($1.to_i if $1)
|
134
134
|
{:type=>String, :size=>(s == 255 ? nil : s)}
|
135
|
-
when
|
135
|
+
when /\A(?:small)?money\z/o
|
136
136
|
{:type=>BigDecimal, :size=>[19,2]}
|
137
137
|
when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?\z/o
|
138
138
|
s = [($1.to_i if $1), ($2.to_i if $2)].compact
|
@@ -548,6 +548,7 @@ module Sequel
|
|
548
548
|
when Class
|
549
549
|
opts[:class_name] ||= opts[:class].name
|
550
550
|
end
|
551
|
+
opts[:class_name] ||= ((self.name || '').split("::")[0..-2] + [camelize(opts.returns_array? ? singularize(name) : name)]).join('::')
|
551
552
|
|
552
553
|
send(:"def_#{type}", opts)
|
553
554
|
|
@@ -625,17 +626,21 @@ module Sequel
|
|
625
626
|
|
626
627
|
# Add the add_ instance method
|
627
628
|
def def_add_method(opts)
|
628
|
-
association_module_def(opts.add_method){|o| add_associated_object(opts, o)}
|
629
|
+
association_module_def(opts.add_method){|o,*args| add_associated_object(opts, o, *args)}
|
629
630
|
end
|
630
631
|
|
631
|
-
# Adds methods to the module included in the class
|
632
|
-
# dataset and associated object(s).
|
632
|
+
# Adds methods related to the association's dataset to the module included in the class.
|
633
633
|
def def_association_dataset_methods(opts)
|
634
634
|
# If a block is given, define a helper method for it, because it takes
|
635
635
|
# an argument. This is unnecessary in Ruby 1.9, as that has instance_exec.
|
636
636
|
association_module_private_def(opts.dataset_helper_method, &opts[:block]) if opts[:block]
|
637
637
|
association_module_private_def(opts._dataset_method, &opts[:dataset])
|
638
638
|
association_module_def(opts.dataset_method){_dataset(opts)}
|
639
|
+
def_association_method(opts)
|
640
|
+
end
|
641
|
+
|
642
|
+
# Adds method for retrieving the associated objects to the module included in the class.
|
643
|
+
def def_association_method(opts)
|
639
644
|
association_module_def(opts.association_method){|*reload| load_associated_objects(opts, reload[0])}
|
640
645
|
end
|
641
646
|
|
@@ -646,7 +651,6 @@ module Sequel
|
|
646
651
|
left = (opts[:left_key] ||= opts.default_left_key)
|
647
652
|
right = (opts[:right_key] ||= opts.default_right_key)
|
648
653
|
left_pk = (opts[:left_primary_key] ||= self.primary_key)
|
649
|
-
opts[:class_name] ||= camelize(singularize(name))
|
650
654
|
opts[:cartesian_product_number] ||= 1
|
651
655
|
join_table = (opts[:join_table] ||= opts.default_join_table)
|
652
656
|
left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
|
@@ -705,7 +709,6 @@ module Sequel
|
|
705
709
|
opts[:key] = opts.default_key unless opts.include?(:key)
|
706
710
|
key = opts[:key]
|
707
711
|
opts[:cartesian_product_number] ||= 0
|
708
|
-
opts[:class_name] ||= camelize(name)
|
709
712
|
opts[:dataset] ||= proc do
|
710
713
|
klass = opts.associated_class
|
711
714
|
klass.filter(SQL::QualifiedIdentifier.new(klass.table_name, opts.primary_key)=>send(key))
|
@@ -750,7 +753,6 @@ module Sequel
|
|
750
753
|
model = self
|
751
754
|
key = (opts[:key] ||= opts.default_key)
|
752
755
|
primary_key = (opts[:primary_key] ||= self.primary_key)
|
753
|
-
opts[:class_name] ||= camelize(singularize(name))
|
754
756
|
opts[:dataset] ||= proc do
|
755
757
|
klass = opts.associated_class
|
756
758
|
klass.filter(SQL::QualifiedIdentifier.new(klass.table_name, key) => send(primary_key))
|
@@ -828,8 +830,8 @@ module Sequel
|
|
828
830
|
|
829
831
|
# Add the remove_ and remove_all instance methods
|
830
832
|
def def_remove_methods(opts)
|
831
|
-
association_module_def(opts.remove_method){|o| remove_associated_object(opts, o)}
|
832
|
-
association_module_def(opts.remove_all_method){remove_all_associated_objects(opts)}
|
833
|
+
association_module_def(opts.remove_method){|o,*args| remove_associated_object(opts, o, *args)}
|
834
|
+
association_module_def(opts.remove_all_method){|*args| remove_all_associated_objects(opts, *args)}
|
833
835
|
end
|
834
836
|
end
|
835
837
|
|
@@ -878,11 +880,11 @@ module Sequel
|
|
878
880
|
end
|
879
881
|
|
880
882
|
# Add the given associated object to the given association
|
881
|
-
def add_associated_object(opts, o)
|
883
|
+
def add_associated_object(opts, o, *args)
|
882
884
|
raise(Sequel::Error, "model object #{model} does not have a primary key") unless pk
|
883
885
|
raise(Sequel::Error, "associated object #{o.model} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
|
884
886
|
return if run_association_callbacks(opts, :before_add, o) == false
|
885
|
-
send(opts._add_method, o)
|
887
|
+
send(opts._add_method, o, *args)
|
886
888
|
associations[opts[:name]].push(o) if associations.include?(opts[:name])
|
887
889
|
add_reciprocal_object(opts, o)
|
888
890
|
run_association_callbacks(opts, :after_add, o)
|
@@ -921,20 +923,20 @@ module Sequel
|
|
921
923
|
end
|
922
924
|
|
923
925
|
# Remove all associated objects from the given association
|
924
|
-
def remove_all_associated_objects(opts)
|
926
|
+
def remove_all_associated_objects(opts, *args)
|
925
927
|
raise(Sequel::Error, "model object #{model} does not have a primary key") unless pk
|
926
|
-
send(opts._remove_all_method)
|
928
|
+
send(opts._remove_all_method, *args)
|
927
929
|
ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
|
928
930
|
associations[opts[:name]] = []
|
929
931
|
ret
|
930
932
|
end
|
931
933
|
|
932
934
|
# Remove the given associated object from the given association
|
933
|
-
def remove_associated_object(opts, o)
|
935
|
+
def remove_associated_object(opts, o, *args)
|
934
936
|
raise(Sequel::Error, "model object #{model} does not have a primary key") unless pk
|
935
937
|
raise(Sequel::Error, "associated object #{o.model} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
|
936
938
|
return if run_association_callbacks(opts, :before_remove, o) == false
|
937
|
-
send(opts._remove_method, o)
|
939
|
+
send(opts._remove_method, o, *args)
|
938
940
|
associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name])
|
939
941
|
remove_reciprocal_object(opts, o)
|
940
942
|
run_association_callbacks(opts, :after_remove, o)
|