sequel 5.19.0 → 5.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +102 -0
- data/doc/dataset_filtering.rdoc +15 -0
- data/doc/opening_databases.rdoc +5 -1
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/release_notes/5.21.0.txt +87 -0
- data/doc/release_notes/5.22.0.txt +48 -0
- data/doc/release_notes/5.23.0.txt +56 -0
- data/doc/release_notes/5.24.0.txt +56 -0
- data/doc/sharding.rdoc +2 -0
- data/doc/testing.rdoc +1 -0
- data/doc/transactions.rdoc +38 -0
- data/lib/sequel/adapters/ado.rb +27 -19
- data/lib/sequel/adapters/jdbc.rb +7 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
- data/lib/sequel/adapters/mysql2.rb +2 -3
- data/lib/sequel/adapters/shared/mssql.rb +7 -7
- data/lib/sequel/adapters/shared/postgres.rb +37 -19
- data/lib/sequel/adapters/shared/sqlite.rb +27 -3
- data/lib/sequel/adapters/sqlite.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +12 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -0
- data/lib/sequel/database/logging.rb +7 -1
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +12 -3
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/database/transactions.rb +57 -5
- data/lib/sequel/dataset.rb +4 -2
- data/lib/sequel/dataset/actions.rb +3 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
- data/lib/sequel/dataset/query.rb +5 -1
- data/lib/sequel/dataset/sql.rb +11 -7
- data/lib/sequel/extensions/named_timezones.rb +52 -8
- data/lib/sequel/extensions/pg_array.rb +4 -0
- data/lib/sequel/extensions/pg_json.rb +387 -123
- data/lib/sequel/extensions/pg_range.rb +3 -2
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/extensions/server_block.rb +15 -4
- data/lib/sequel/model/associations.rb +35 -9
- data/lib/sequel/model/plugins.rb +104 -0
- data/lib/sequel/plugins/association_dependencies.rb +3 -3
- data/lib/sequel/plugins/association_pks.rb +14 -4
- data/lib/sequel/plugins/association_proxies.rb +3 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
- data/lib/sequel/plugins/composition.rb +13 -9
- data/lib/sequel/plugins/finder.rb +2 -2
- data/lib/sequel/plugins/hook_class_methods.rb +17 -5
- data/lib/sequel/plugins/insert_conflict.rb +72 -0
- data/lib/sequel/plugins/inverted_subsets.rb +2 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +147 -59
- data/lib/sequel/plugins/rcte_tree.rb +6 -0
- data/lib/sequel/plugins/static_cache.rb +8 -3
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/subset_conditions.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +5 -3
- data/lib/sequel/sql.rb +15 -3
- data/lib/sequel/timezones.rb +50 -11
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +24 -0
- data/spec/adapters/mysql_spec.rb +0 -5
- data/spec/adapters/postgres_spec.rb +319 -1
- data/spec/bin_spec.rb +1 -1
- data/spec/core/database_spec.rb +123 -2
- data/spec/core/dataset_spec.rb +33 -1
- data/spec/core/expression_filters_spec.rb +25 -1
- data/spec/core/schema_spec.rb +24 -0
- data/spec/extensions/class_table_inheritance_spec.rb +30 -8
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/hook_class_methods_spec.rb +22 -0
- data/spec/extensions/insert_conflict_spec.rb +103 -0
- data/spec/extensions/migration_spec.rb +13 -0
- data/spec/extensions/named_timezones_spec.rb +109 -2
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +45 -0
- data/spec/extensions/pg_json_spec.rb +218 -29
- data/spec/extensions/pg_range_spec.rb +76 -9
- data/spec/extensions/rcte_tree_spec.rb +6 -0
- data/spec/extensions/s_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +4 -2
- data/spec/extensions/server_block_spec.rb +38 -0
- data/spec/extensions/spec_helper.rb +8 -1
- data/spec/extensions/static_cache_cache_spec.rb +35 -0
- data/spec/integration/dataset_test.rb +25 -9
- data/spec/integration/plugin_test.rb +42 -0
- data/spec/integration/schema_test.rb +7 -2
- data/spec/integration/transaction_test.rb +50 -0
- data/spec/model/associations_spec.rb +84 -4
- data/spec/model/plugins_spec.rb +111 -0
- metadata +16 -2
@@ -453,13 +453,14 @@ module Sequel
|
|
453
453
|
end
|
454
454
|
|
455
455
|
ENDLESS_RANGE_NOT_SUPPORTED = RUBY_VERSION < '2.6'
|
456
|
+
STARTLESS_RANGE_NOT_SUPPORTED = RUBY_VERSION < '2.7'
|
456
457
|
|
457
458
|
# Return a ruby Range object for this instance, if one can be created.
|
458
459
|
def to_range
|
459
460
|
return @range if @range
|
460
461
|
raise(Error, "cannot create ruby range for an empty PostgreSQL range") if empty?
|
461
462
|
raise(Error, "cannot create ruby range when PostgreSQL range excludes beginning element") if exclude_begin?
|
462
|
-
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded beginning")
|
463
|
+
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded beginning") if STARTLESS_RANGE_NOT_SUPPORTED && !self.begin
|
463
464
|
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded ending") if ENDLESS_RANGE_NOT_SUPPORTED && !self.end
|
464
465
|
@range = Range.new(self.begin, self.end, exclude_end?)
|
465
466
|
end
|
@@ -468,7 +469,7 @@ module Sequel
|
|
468
469
|
# it must have a beginning and an ending (no unbounded ranges), and it cannot exclude
|
469
470
|
# the beginning element.
|
470
471
|
def valid_ruby_range?
|
471
|
-
!(empty? || exclude_begin? || !self.begin || (ENDLESS_RANGE_NOT_SUPPORTED && !self.end))
|
472
|
+
!(empty? || exclude_begin? || (STARTLESS_RANGE_NOT_SUPPORTED && !self.begin) || (ENDLESS_RANGE_NOT_SUPPORTED && !self.end))
|
472
473
|
end
|
473
474
|
|
474
475
|
# Whether the beginning of the range is unbounded.
|
@@ -7,7 +7,7 @@
|
|
7
7
|
# that when composite fields are retrieved, they are parsed and returned
|
8
8
|
# as instances of Sequel::Postgres::PGRow::(HashRow|ArrayRow), or
|
9
9
|
# optionally a custom type. HashRow and ArrayRow are DelegateClasses of
|
10
|
-
#
|
10
|
+
# Hash and Array, so they mostly act like a hash or array, but not
|
11
11
|
# completely (is_a?(Hash) and is_a?(Array) are false). If you want the
|
12
12
|
# actual hash for a HashRow, call HashRow#to_hash, and if you want the
|
13
13
|
# actual array for an ArrayRow, call ArrayRow#to_a. This is done so
|
@@ -228,7 +228,9 @@ module Sequel
|
|
228
228
|
if skip(/\)/)
|
229
229
|
values << nil
|
230
230
|
else
|
231
|
+
# :nocov:
|
231
232
|
until eos?
|
233
|
+
# :nocov:
|
232
234
|
if skip(/"/)
|
233
235
|
values << scan(/(\\.|""|[^"])*/).gsub(/\\(.)|"(")/, '\1\2')
|
234
236
|
skip(/"[,)]/)
|
@@ -37,7 +37,7 @@ module Sequel
|
|
37
37
|
{:type =>schema[:type] == :boolean ? TrueClass : Integer}
|
38
38
|
when /\Abigint(?:\((?:\d+)\))?(?: unsigned)?\z/
|
39
39
|
{:type=>:Bignum}
|
40
|
-
when /\A(?:real|float
|
40
|
+
when /\A(?:real|float(?: unsigned)?|double(?: precision)?|double\(\d+,\d+\)(?: unsigned)?)\z/
|
41
41
|
{:type=>Float}
|
42
42
|
when 'boolean', 'bit', 'bool'
|
43
43
|
{:type=>TrueClass}
|
@@ -52,6 +52,12 @@
|
|
52
52
|
# DB[:a].server(:read_only).delete # Uses shard2
|
53
53
|
# end
|
54
54
|
#
|
55
|
+
# If you use an invalid server when calling with_server, it will be
|
56
|
+
# treated the same way as if you called Dataset#server with an invalid
|
57
|
+
# server. By default, the default server will be used in such cases.
|
58
|
+
# If you would like a different server to be used, or an exception to
|
59
|
+
# be raised, then use the :servers_hash Database option.
|
60
|
+
#
|
55
61
|
# Related modules: Sequel::ServerBlock, Sequel::UnthreadedServerBlock,
|
56
62
|
# Sequel::ThreadedServerBlock
|
57
63
|
|
@@ -110,9 +116,9 @@ module Sequel
|
|
110
116
|
else
|
111
117
|
case server
|
112
118
|
when :default, nil
|
113
|
-
@default_servers[-1][0]
|
119
|
+
@servers[@default_servers[-1][0]]
|
114
120
|
when :read_only
|
115
|
-
@default_servers[-1][1]
|
121
|
+
@servers[@default_servers[-1][1]]
|
116
122
|
else
|
117
123
|
super
|
118
124
|
end
|
@@ -155,11 +161,16 @@ module Sequel
|
|
155
161
|
if !a || a.empty?
|
156
162
|
super
|
157
163
|
else
|
164
|
+
# Hash handling required to work when loaded after arbitrary servers plugin.
|
158
165
|
case server
|
159
166
|
when :default, nil
|
160
|
-
a[-1][0]
|
167
|
+
v = a[-1][0]
|
168
|
+
v = @servers[v] unless v.is_a?(Hash)
|
169
|
+
v
|
161
170
|
when :read_only
|
162
|
-
a[-1][1]
|
171
|
+
v = a[-1][1]
|
172
|
+
v = @servers[v] unless v.is_a?(Hash)
|
173
|
+
v
|
163
174
|
else
|
164
175
|
super
|
165
176
|
end
|
@@ -1617,9 +1617,10 @@ module Sequel
|
|
1617
1617
|
# is hash or array of two element arrays. Consider also specifying the :graph_block
|
1618
1618
|
# option if the value for this option is not a hash or array of two element arrays
|
1619
1619
|
# and you plan to use this association in eager_graph or association_join.
|
1620
|
-
# :dataset :: A proc that is
|
1620
|
+
# :dataset :: A proc that is used to define the method to get the base dataset to use (before the other
|
1621
1621
|
# options are applied). If the proc accepts an argument, it is passed the related
|
1622
|
-
# association reflection.
|
1622
|
+
# association reflection. It is a best practice to always have the dataset accept an argument
|
1623
|
+
# and use the argument to return the appropriate dataset.
|
1623
1624
|
# :distinct :: Use the DISTINCT clause when selecting associating object, both when
|
1624
1625
|
# lazy loading and eager loading via .eager (but not when using .eager_graph).
|
1625
1626
|
# :eager :: The associations to eagerly load via +eager+ when loading the associated object(s).
|
@@ -1909,7 +1910,7 @@ module Sequel
|
|
1909
1910
|
# can be easily overridden in the class itself while allowing for
|
1910
1911
|
# super to be called.
|
1911
1912
|
def association_module_def(name, opts=OPTS, &block)
|
1912
|
-
association_module(opts).
|
1913
|
+
association_module(opts).send(:define_method, name, &block)
|
1913
1914
|
end
|
1914
1915
|
|
1915
1916
|
# Add a private method to the module included in the class.
|
@@ -1944,6 +1945,13 @@ module Sequel
|
|
1944
1945
|
end
|
1945
1946
|
|
1946
1947
|
association_module_def(opts.dataset_method, opts){_dataset(opts)}
|
1948
|
+
if opts[:block]
|
1949
|
+
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
1950
|
+
end
|
1951
|
+
if opts[:dataset]
|
1952
|
+
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
1953
|
+
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
1954
|
+
end
|
1947
1955
|
def_association_method(opts)
|
1948
1956
|
|
1949
1957
|
return if opts[:read_only]
|
@@ -2204,12 +2212,28 @@ module Sequel
|
|
2204
2212
|
if one_to_one
|
2205
2213
|
opts[:setter] ||= proc do |o|
|
2206
2214
|
up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
|
2215
|
+
|
2216
|
+
if (froms = up_ds.opts[:from]) && (from = froms[0]) && (from.is_a?(Sequel::Dataset) || (from.is_a?(Sequel::SQL::AliasedExpression) && from.expression.is_a?(Sequel::Dataset)))
|
2217
|
+
if old = up_ds.first
|
2218
|
+
cks.each{|k| old.set_column_value(:"#{k}=", nil)}
|
2219
|
+
end
|
2220
|
+
save_old = true
|
2221
|
+
end
|
2222
|
+
|
2207
2223
|
if o
|
2208
|
-
|
2224
|
+
if !o.new? && !save_old
|
2225
|
+
up_ds = up_ds.exclude(o.pk_hash)
|
2226
|
+
end
|
2209
2227
|
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2210
2228
|
end
|
2229
|
+
|
2211
2230
|
checked_transaction do
|
2212
|
-
|
2231
|
+
if save_old
|
2232
|
+
old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
|
2233
|
+
else
|
2234
|
+
up_ds.skip_limit_check.update(ck_nil_hash)
|
2235
|
+
end
|
2236
|
+
|
2213
2237
|
o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
2214
2238
|
end
|
2215
2239
|
end
|
@@ -2287,7 +2311,8 @@ module Sequel
|
|
2287
2311
|
end
|
2288
2312
|
ds = ds.clone(:model_object => self)
|
2289
2313
|
ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
|
2290
|
-
|
2314
|
+
# block method is private
|
2315
|
+
ds = send(opts[:block_method], ds) if opts[:block_method]
|
2291
2316
|
ds
|
2292
2317
|
end
|
2293
2318
|
|
@@ -2310,10 +2335,11 @@ module Sequel
|
|
2310
2335
|
# Return an association dataset for the given association reflection
|
2311
2336
|
def _dataset(opts)
|
2312
2337
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
2313
|
-
ds = if opts[:
|
2314
|
-
|
2338
|
+
ds = if opts[:dataset_opt_arity] == 1
|
2339
|
+
# dataset_opt_method is private
|
2340
|
+
send(opts[:dataset_opt_method], opts)
|
2315
2341
|
else
|
2316
|
-
|
2342
|
+
send(opts[:dataset_opt_method])
|
2317
2343
|
end
|
2318
2344
|
_apply_association_options(opts, ds)
|
2319
2345
|
end
|
data/lib/sequel/model/plugins.rb
CHANGED
@@ -51,5 +51,109 @@ module Sequel
|
|
51
51
|
r
|
52
52
|
end
|
53
53
|
end
|
54
|
+
|
55
|
+
method_num = 0
|
56
|
+
method_num_mutex = Mutex.new
|
57
|
+
# Return a unique method name symbol for the given suffix.
|
58
|
+
SEQUEL_METHOD_NAME = lambda do |suffix|
|
59
|
+
:"_sequel_#{suffix}_#{method_num_mutex.synchronize{method_num += 1}}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# Define a private instance method using the block with the provided name and
|
63
|
+
# expected arity. If the name is given as a Symbol, it is used directly.
|
64
|
+
# If the name is given as a String, a unique name will be generated using
|
65
|
+
# that string. The expected_arity should be either 0 (no arguments) or
|
66
|
+
# 1 (single argument).
|
67
|
+
#
|
68
|
+
# If a block with an arity that does not match the expected arity is used,
|
69
|
+
# a deprecation warning will be issued. The method defined should still
|
70
|
+
# work, though it will be slower than a method with the expected arity.
|
71
|
+
#
|
72
|
+
# Sequel only checks arity for regular blocks, not lambdas. Lambdas were
|
73
|
+
# already strict in regards to arity, so there is no need to try to fix
|
74
|
+
# arity to keep backwards compatibility for lambdas.
|
75
|
+
#
|
76
|
+
# Blocks with required keyword arguments are not supported by this method.
|
77
|
+
def self.def_sequel_method(model, meth, expected_arity, &block)
|
78
|
+
if meth.is_a?(String)
|
79
|
+
meth = SEQUEL_METHOD_NAME.call(meth)
|
80
|
+
end
|
81
|
+
call_meth = meth
|
82
|
+
|
83
|
+
unless block.lambda?
|
84
|
+
required_args, optional_args, rest, keyword = _define_sequel_method_arg_numbers(block)
|
85
|
+
|
86
|
+
if keyword == :required
|
87
|
+
raise Error, "cannot use block with required keyword arguments when calling define_sequel_method with expected arity #{expected_arity}"
|
88
|
+
end
|
89
|
+
|
90
|
+
case expected_arity
|
91
|
+
when 0
|
92
|
+
unless required_args == 0
|
93
|
+
# SEQUEL6: remove
|
94
|
+
Sequel::Deprecation.deprecate("Arity mismatch in block passed to define_sequel_method. Expected Arity 0, but arguments required for #{block.inspect}. Support for this will be removed in Sequel 6.")
|
95
|
+
b = block
|
96
|
+
block = lambda{instance_exec(&b)} # Fallback
|
97
|
+
end
|
98
|
+
when 1
|
99
|
+
if required_args == 0 && optional_args == 0 && !rest
|
100
|
+
# SEQUEL6: remove
|
101
|
+
Sequel::Deprecation.deprecate("Arity mismatch in block passed to define_sequel_method. Expected Arity 1, but no arguments accepted for #{block.inspect}. Support for this will be removed in Sequel 6.")
|
102
|
+
temp_method = SEQUEL_METHOD_NAME.call("temp")
|
103
|
+
model.class_eval("def #{temp_method}(_) #{meth =~ /\A\w+\z/ ? "#{meth}_arity" : "send(:\"#{meth}_arity\")"} end", __FILE__, __LINE__)
|
104
|
+
model.send(:alias_method, meth, temp_method)
|
105
|
+
model.send(:undef_method, temp_method)
|
106
|
+
model.send(:private, meth)
|
107
|
+
meth = :"#{meth}_arity"
|
108
|
+
elsif required_args > 1
|
109
|
+
# SEQUEL6: remove
|
110
|
+
Sequel::Deprecation.deprecate("Arity mismatch in block passed to define_sequel_method. Expected Arity 1, but more arguments required for #{block.inspect}. Support for this will be removed in Sequel 6.")
|
111
|
+
b = block
|
112
|
+
block = lambda{|r| instance_exec(r, &b)} # Fallback
|
113
|
+
end
|
114
|
+
else
|
115
|
+
raise Error, "unexpected arity passed to define_sequel_method: #{expected_arity.inspect}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
model.send(:define_method, meth, &block)
|
120
|
+
model.send(:private, meth)
|
121
|
+
call_meth
|
122
|
+
end
|
123
|
+
|
124
|
+
# Return the number of required argument, optional arguments,
|
125
|
+
# whether the callable accepts any additional arguments,
|
126
|
+
# and whether the callable accepts keyword arguments (true, false
|
127
|
+
# or :required).
|
128
|
+
def self._define_sequel_method_arg_numbers(callable)
|
129
|
+
optional_args = 0
|
130
|
+
rest = false
|
131
|
+
keyword = false
|
132
|
+
callable.parameters.map(&:first).each do |arg_type, _|
|
133
|
+
case arg_type
|
134
|
+
when :opt
|
135
|
+
optional_args += 1
|
136
|
+
when :rest
|
137
|
+
rest = true
|
138
|
+
when :keyreq
|
139
|
+
keyword = :required
|
140
|
+
when :key, :keyrest
|
141
|
+
keyword ||= true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
arity = callable.arity
|
145
|
+
if arity < 0
|
146
|
+
arity = arity.abs - 1
|
147
|
+
end
|
148
|
+
required_args = arity
|
149
|
+
arity -= 1 if keyword == :required
|
150
|
+
|
151
|
+
if callable.is_a?(Proc) && !callable.lambda?
|
152
|
+
optional_args -= arity
|
153
|
+
end
|
154
|
+
|
155
|
+
[required_args, optional_args, rest, keyword]
|
156
|
+
end
|
157
|
+
private_class_method :_define_sequel_method_arg_numbers
|
54
158
|
end
|
55
159
|
end
|
@@ -60,9 +60,9 @@ module Sequel
|
|
60
60
|
association_dependencies[:"#{time}_#{action}"] << if action == :nullify
|
61
61
|
case type
|
62
62
|
when :one_to_many , :many_to_many
|
63
|
-
|
63
|
+
[r[:remove_all_method]]
|
64
64
|
when :one_to_one
|
65
|
-
|
65
|
+
[r[:setter_method], nil]
|
66
66
|
else
|
67
67
|
raise(Error, "Can't nullify many_to_one associated objects: association: #{association}")
|
68
68
|
end
|
@@ -97,7 +97,7 @@ module Sequel
|
|
97
97
|
def before_destroy
|
98
98
|
model.association_dependencies[:before_delete].each{|m| public_send(m).delete}
|
99
99
|
model.association_dependencies[:before_destroy].each{|m| public_send(m).destroy}
|
100
|
-
model.association_dependencies[:before_nullify].each{|
|
100
|
+
model.association_dependencies[:before_nullify].each{|args| public_send(*args)}
|
101
101
|
super
|
102
102
|
end
|
103
103
|
end
|
@@ -60,8 +60,15 @@ module Sequel
|
|
60
60
|
|
61
61
|
# Define a association_pks method using the block for the association reflection
|
62
62
|
def def_association_pks_methods(opts)
|
63
|
+
opts[:pks_getter_method] = :"#{singularize(opts[:name])}_pks_getter"
|
64
|
+
association_module_def(opts[:pks_getter_method], &opts[:pks_getter])
|
63
65
|
association_module_def(:"#{singularize(opts[:name])}_pks", opts){_association_pks_getter(opts)}
|
64
|
-
|
66
|
+
|
67
|
+
if opts[:pks_setter]
|
68
|
+
opts[:pks_setter_method] = :"#{singularize(opts[:name])}_pks_setter"
|
69
|
+
association_module_def(opts[:pks_setter_method], &opts[:pks_setter])
|
70
|
+
association_module_def(:"#{singularize(opts[:name])}_pks=", opts){|pks| _association_pks_setter(opts, pks)}
|
71
|
+
end
|
65
72
|
end
|
66
73
|
|
67
74
|
# Add a getter that checks the join table for matching records and
|
@@ -181,7 +188,8 @@ module Sequel
|
|
181
188
|
def after_save
|
182
189
|
if assoc_pks = @_association_pks
|
183
190
|
assoc_pks.each do |name, pks|
|
184
|
-
|
191
|
+
# pks_setter_method is private
|
192
|
+
send(model.association_reflection(name)[:pks_setter_method], pks)
|
185
193
|
end
|
186
194
|
@_association_pks = nil
|
187
195
|
end
|
@@ -206,7 +214,8 @@ module Sequel
|
|
206
214
|
elsif delay && @_association_pks && (objs = @_association_pks[opts[:name]])
|
207
215
|
objs
|
208
216
|
else
|
209
|
-
|
217
|
+
# pks_getter_method is private
|
218
|
+
send(opts[:pks_getter_method])
|
210
219
|
end
|
211
220
|
end
|
212
221
|
|
@@ -231,7 +240,8 @@ module Sequel
|
|
231
240
|
modified!
|
232
241
|
(@_association_pks ||= {})[opts[:name]] = pks
|
233
242
|
else
|
234
|
-
|
243
|
+
# pks_setter_method is private
|
244
|
+
send(opts[:pks_setter_method], pks)
|
235
245
|
end
|
236
246
|
end
|
237
247
|
|
@@ -64,6 +64,8 @@ module Sequel
|
|
64
64
|
array = [].freeze
|
65
65
|
|
66
66
|
if RUBY_VERSION < '2.6'
|
67
|
+
# :nocov:
|
68
|
+
|
67
69
|
# Default proc used to determine whether to send the method to the dataset.
|
68
70
|
# If the array would respond to it, sends it to the array instead of the dataset.
|
69
71
|
DEFAULT_PROXY_TO_DATASET = proc do |opts|
|
@@ -73,10 +75,9 @@ module Sequel
|
|
73
75
|
end
|
74
76
|
!array_method
|
75
77
|
end
|
76
|
-
else
|
77
78
|
# :nocov:
|
79
|
+
else
|
78
80
|
DEFAULT_PROXY_TO_DATASET = proc{|opts| !array.respond_to?(opts[:method])}
|
79
|
-
# :nocov:
|
80
81
|
end
|
81
82
|
|
82
83
|
# Set the association reflection to use, and whether the association should be
|
@@ -108,6 +108,16 @@ module Sequel
|
|
108
108
|
# a = Executive.where{{employees[:id]=>1}}.first # works
|
109
109
|
# a = Executive.where{{executives[:id]=>1}}.first # doesn't work
|
110
110
|
#
|
111
|
+
# Note that because subclass datasets select from a subquery, you cannot update,
|
112
|
+
# delete, or insert into them directly. To delete related rows, you need to go
|
113
|
+
# through the related tables and remove the related rows. Code that does this would
|
114
|
+
# be similar to:
|
115
|
+
#
|
116
|
+
# pks = Executive.where{num_staff < 10}.select_map(:id)
|
117
|
+
# Executive.cti_tables.reverse_each do |table|
|
118
|
+
# DB.from(table).where(:id=>pks).delete
|
119
|
+
# end
|
120
|
+
#
|
111
121
|
# = Usage
|
112
122
|
#
|
113
123
|
# # Use the default of storing the class name in the sti_key
|
@@ -326,6 +336,7 @@ module Sequel
|
|
326
336
|
cti_tables.reverse_each do |ct|
|
327
337
|
db.schema(ct).each{|sk,v| db_schema[sk] = v}
|
328
338
|
end
|
339
|
+
setup_auto_validations if respond_to?(:setup_auto_validations, true)
|
329
340
|
end
|
330
341
|
end
|
331
342
|
|
@@ -32,9 +32,10 @@ module Sequel
|
|
32
32
|
#
|
33
33
|
# The :mapping option is just a shortcut that works in particular
|
34
34
|
# cases. To handle any case, you can define a custom :composer
|
35
|
-
# and :decomposer procs. The :composer
|
36
|
-
#
|
37
|
-
#
|
35
|
+
# and :decomposer procs. The :composer and :decomposer procs will
|
36
|
+
# be used to define instance methods. The :composer will be called
|
37
|
+
# the first time the getter is called, and the :decomposer
|
38
|
+
# will be called before saving. The above example could
|
38
39
|
# also be implemented as:
|
39
40
|
#
|
40
41
|
# Album.composition :date,
|
@@ -74,9 +75,9 @@ module Sequel
|
|
74
75
|
#
|
75
76
|
# Options:
|
76
77
|
# :class :: if using the :mapping option, the class to use, as a Class, String or Symbol.
|
77
|
-
# :composer :: A proc
|
78
|
+
# :composer :: A proc used to define the method that the composition getter method will call
|
78
79
|
# to create the composition.
|
79
|
-
# :decomposer :: A proc
|
80
|
+
# :decomposer :: A proc used to define the method called before saving the model object,
|
80
81
|
# if the composition object exists, which sets the columns in the model object
|
81
82
|
# based on the value of the composition object.
|
82
83
|
# :mapping :: An array where each element is either a symbol or an array of two symbols.
|
@@ -129,15 +130,17 @@ module Sequel
|
|
129
130
|
|
130
131
|
# Define getter and setter methods for the composition object.
|
131
132
|
def define_composition_accessor(name, opts=OPTS)
|
132
|
-
|
133
|
+
composer_meth = opts[:composer_method] = Plugins.def_sequel_method(@composition_module, "#{name}_composer", 0, &opts[:composer])
|
134
|
+
opts[:decomposer_method] = Plugins.def_sequel_method(@composition_module, "#{name}_decomposer", 0, &opts[:decomposer])
|
133
135
|
@composition_module.class_eval do
|
134
136
|
define_method(name) do
|
135
137
|
if compositions.has_key?(name)
|
136
138
|
compositions[name]
|
137
139
|
elsif frozen?
|
138
|
-
|
140
|
+
# composer_meth is private
|
141
|
+
send(composer_meth)
|
139
142
|
else
|
140
|
-
compositions[name] =
|
143
|
+
compositions[name] = send(composer_meth)
|
141
144
|
end
|
142
145
|
end
|
143
146
|
define_method("#{name}=") do |v|
|
@@ -171,7 +174,8 @@ module Sequel
|
|
171
174
|
# For each composition, set the columns in the model class based
|
172
175
|
# on the composition object.
|
173
176
|
def before_validation
|
174
|
-
|
177
|
+
# decomposer_method is private
|
178
|
+
@compositions.keys.each{|n| send(model.compositions[n][:decomposer_method])} if @compositions
|
175
179
|
super
|
176
180
|
end
|
177
181
|
|