sequel 3.2.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|