sequel 5.33.0 → 5.38.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +88 -0
- data/README.rdoc +1 -1
- data/doc/association_basics.rdoc +7 -2
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/model_plugins.rdoc +1 -1
- data/doc/opening_databases.rdoc +5 -1
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/release_notes/5.38.0.txt +28 -0
- data/doc/transactions.rdoc +0 -8
- data/doc/validations.rdoc +1 -1
- data/lib/sequel/adapters/jdbc.rb +13 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +4 -4
- data/lib/sequel/adapters/odbc.rb +4 -6
- data/lib/sequel/adapters/oracle.rb +2 -1
- data/lib/sequel/adapters/shared/mssql.rb +14 -4
- data/lib/sequel/adapters/shared/oracle.rb +13 -7
- data/lib/sequel/adapters/shared/postgres.rb +39 -1
- data/lib/sequel/adapters/shared/sqlite.rb +8 -2
- data/lib/sequel/adapters/tinytds.rb +1 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -0
- data/lib/sequel/connection_pool/sharded_single.rb +4 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
- data/lib/sequel/connection_pool/single.rb +1 -1
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +5 -6
- data/lib/sequel/database/connecting.rb +1 -2
- data/lib/sequel/database/misc.rb +30 -10
- data/lib/sequel/database/query.rb +2 -0
- data/lib/sequel/database/schema_generator.rb +0 -1
- data/lib/sequel/database/schema_methods.rb +16 -17
- data/lib/sequel/database/transactions.rb +8 -5
- data/lib/sequel/dataset/actions.rb +10 -6
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/query.rb +5 -4
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/core_refinements.rb +2 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
- data/lib/sequel/extensions/migration.rb +8 -2
- data/lib/sequel/extensions/pg_array_ops.rb +4 -0
- data/lib/sequel/extensions/pg_enum.rb +2 -0
- data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_inet.rb +2 -0
- data/lib/sequel/extensions/pg_json_ops.rb +46 -2
- data/lib/sequel/extensions/pg_range.rb +3 -7
- data/lib/sequel/extensions/pg_range_ops.rb +2 -0
- data/lib/sequel/extensions/pg_row.rb +0 -1
- data/lib/sequel/extensions/pg_row_ops.rb +24 -0
- data/lib/sequel/extensions/query.rb +1 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
- data/lib/sequel/extensions/s.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -3
- data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model.rb +3 -1
- data/lib/sequel/model/associations.rb +36 -20
- data/lib/sequel/model/base.rb +13 -5
- data/lib/sequel/model/plugins.rb +3 -3
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +1 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +3 -8
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dirty.rb +44 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/pg_array_associations.rb +2 -3
- data/lib/sequel/plugins/prepared_statements.rb +5 -11
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +10 -16
- data/lib/sequel/plugins/single_table_inheritance.rb +5 -0
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/version.rb +1 -1
- metadata +14 -3
@@ -158,6 +158,30 @@ module Sequel
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
end
|
161
|
+
|
162
|
+
# :nocov:
|
163
|
+
if defined?(PGRow::ArrayRow)
|
164
|
+
# :nocov:
|
165
|
+
class PGRow::ArrayRow
|
166
|
+
# Wrap the PGRow::ArrayRow instance in an PGRowOp, allowing you to easily use
|
167
|
+
# the PostgreSQL row functions and operators with literal rows.
|
168
|
+
def op
|
169
|
+
Sequel.pg_row_op(self)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# :nocov:
|
175
|
+
if defined?(PGRow::HashRow)
|
176
|
+
# :nocov:
|
177
|
+
class PGRow::HashRow
|
178
|
+
# Wrap the PGRow::ArrayRow instance in an PGRowOp, allowing you to easily use
|
179
|
+
# the PostgreSQL row functions and operators with literal rows.
|
180
|
+
def op
|
181
|
+
Sequel.pg_row_op(self)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
161
185
|
end
|
162
186
|
|
163
187
|
module SQL::Builders
|
@@ -48,7 +48,7 @@ class Sequel::Database
|
|
48
48
|
def _run_transaction_hooks(type, opts)
|
49
49
|
synchronize(opts[:server]) do |conn|
|
50
50
|
unless h = _trans(conn)
|
51
|
-
raise Error, "Cannot call run_#{type}_hooks outside of a transaction"
|
51
|
+
raise Sequel::Error, "Cannot call run_#{type}_hooks outside of a transaction"
|
52
52
|
end
|
53
53
|
|
54
54
|
if hooks = h[type]
|
data/lib/sequel/extensions/s.rb
CHANGED
@@ -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|double(?: precision)?|double\(\d+,\d+\))(?: unsigned)?\z/
|
41
41
|
{:type=>Float}
|
42
42
|
when 'boolean', 'bit', 'bool'
|
43
43
|
{:type=>TrueClass}
|
@@ -57,7 +57,7 @@ module Sequel
|
|
57
57
|
{:type=>String, :size=>($1.to_i if $1)}
|
58
58
|
when /\A(?:small)?money\z/
|
59
59
|
{:type=>BigDecimal, :size=>[19,2]}
|
60
|
-
when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?\z/
|
60
|
+
when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?(?: unsigned)?\z/
|
61
61
|
s = [($1.to_i if $1), ($2.to_i if $2)].compact
|
62
62
|
{:type=>BigDecimal, :size=>(s.empty? ? nil : s)}
|
63
63
|
when /\A(?:bytea|(?:tiny|medium|long)?blob|(?:var)?binary)(?:\((\d+)\))?\z/
|
@@ -218,7 +218,7 @@ END_MIG
|
|
218
218
|
gen.foreign_key(name, table, col_opts)
|
219
219
|
else
|
220
220
|
gen.column(name, type, col_opts)
|
221
|
-
if [Integer, :Bignum, Float].include?(type) && schema[:db_type] =~ / unsigned\z/io
|
221
|
+
if [Integer, :Bignum, Float, BigDecimal].include?(type) && schema[:db_type] =~ / unsigned\z/io
|
222
222
|
gen.check(Sequel::SQL::Identifier.new(name) >= 0)
|
223
223
|
end
|
224
224
|
end
|
@@ -53,7 +53,13 @@ module Sequel
|
|
53
53
|
# is given, it is used directly as the node or transition. Otherwise
|
54
54
|
# a node is created for the current object.
|
55
55
|
def dot(label, j=nil)
|
56
|
-
|
56
|
+
label = case label
|
57
|
+
when nil
|
58
|
+
"<nil>"
|
59
|
+
else
|
60
|
+
label.to_s
|
61
|
+
end
|
62
|
+
@dot << "#{j||@i} [label=#{label.inspect}];"
|
57
63
|
end
|
58
64
|
|
59
65
|
# Recursive method that parses all of Sequel's internal datastructures,
|
@@ -61,7 +67,7 @@ module Sequel
|
|
61
67
|
# structure.
|
62
68
|
def v(e, l)
|
63
69
|
@i += 1
|
64
|
-
dot(l, "#{@stack.last} -> #{@i}")
|
70
|
+
dot(l, "#{@stack.last} -> #{@i}")
|
65
71
|
@stack.push(@i)
|
66
72
|
case e
|
67
73
|
when LiteralString
|
@@ -144,7 +150,7 @@ module Sequel
|
|
144
150
|
dot "Dataset"
|
145
151
|
TO_DOT_OPTIONS.each do |k|
|
146
152
|
if val = e.opts[k]
|
147
|
-
v(val, k
|
153
|
+
v(val, k)
|
148
154
|
end
|
149
155
|
end
|
150
156
|
else
|
data/lib/sequel/model.rb
CHANGED
@@ -69,7 +69,9 @@ module Sequel
|
|
69
69
|
require_relative "model/base"
|
70
70
|
require_relative "model/exceptions"
|
71
71
|
require_relative "model/errors"
|
72
|
+
# :nocov:
|
72
73
|
if !defined?(::SEQUEL_NO_ASSOCIATIONS) && !ENV.has_key?('SEQUEL_NO_ASSOCIATIONS')
|
74
|
+
# :nocov:
|
73
75
|
require_relative 'model/associations'
|
74
76
|
plugin Model::Associations
|
75
77
|
end
|
@@ -77,7 +79,7 @@ module Sequel
|
|
77
79
|
def_Model(::Sequel)
|
78
80
|
|
79
81
|
# The setter methods (methods ending with =) that are never allowed
|
80
|
-
# to be called automatically via +set+/+update+/+new+/etc
|
82
|
+
# to be called automatically via +set+/+update+/+new+/etc.
|
81
83
|
RESTRICTED_SETTER_METHODS = instance_methods.map(&:to_s).select{|l| l.end_with?('=')}.freeze
|
82
84
|
end
|
83
85
|
end
|
@@ -164,11 +164,11 @@ module Sequel
|
|
164
164
|
# range to return the object(s) at the correct offset/limit.
|
165
165
|
def apply_ruby_eager_limit_strategy(rows, limit_and_offset = limit_and_offset())
|
166
166
|
name = self[:name]
|
167
|
+
return unless range = slice_range(limit_and_offset)
|
167
168
|
if returns_array?
|
168
|
-
range = slice_range(limit_and_offset)
|
169
169
|
rows.each{|o| o.associations[name] = o.associations[name][range] || []}
|
170
|
-
|
171
|
-
offset =
|
170
|
+
else
|
171
|
+
offset = range.begin
|
172
172
|
rows.each{|o| o.associations[name] = o.associations[name][offset]}
|
173
173
|
end
|
174
174
|
end
|
@@ -356,7 +356,7 @@ module Sequel
|
|
356
356
|
def finalize
|
357
357
|
return unless cache = self[:cache]
|
358
358
|
|
359
|
-
|
359
|
+
finalizer = proc do |meth, key|
|
360
360
|
next if has_key?(key)
|
361
361
|
|
362
362
|
# Allow calling private methods to make sure caching is done appropriately
|
@@ -364,6 +364,13 @@ module Sequel
|
|
364
364
|
self[key] = cache.delete(key) if cache.has_key?(key)
|
365
365
|
end
|
366
366
|
|
367
|
+
finalize_settings.each(&finalizer)
|
368
|
+
|
369
|
+
unless self[:instance_specific]
|
370
|
+
finalizer.call(:associated_eager_dataset, :associated_eager_dataset)
|
371
|
+
finalizer.call(:filter_by_associations_conditions_dataset, :filter_by_associations_conditions_dataset)
|
372
|
+
end
|
373
|
+
|
367
374
|
nil
|
368
375
|
end
|
369
376
|
|
@@ -371,9 +378,7 @@ module Sequel
|
|
371
378
|
FINALIZE_SETTINGS = {
|
372
379
|
:associated_class=>:class,
|
373
380
|
:associated_dataset=>:_dataset,
|
374
|
-
:associated_eager_dataset=>:associated_eager_dataset,
|
375
381
|
:eager_limit_strategy=>:_eager_limit_strategy,
|
376
|
-
:filter_by_associations_conditions_dataset=>:filter_by_associations_conditions_dataset,
|
377
382
|
:placeholder_loader=>:placeholder_loader,
|
378
383
|
:predicate_key=>:predicate_key,
|
379
384
|
:predicate_keys=>:predicate_keys,
|
@@ -432,7 +437,11 @@ module Sequel
|
|
432
437
|
if use_placeholder_loader?
|
433
438
|
cached_fetch(:placeholder_loader) do
|
434
439
|
Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
|
435
|
-
ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
|
440
|
+
ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
|
441
|
+
if self[:block]
|
442
|
+
ds = self[:block].call(ds)
|
443
|
+
end
|
444
|
+
ds
|
436
445
|
end
|
437
446
|
end
|
438
447
|
end
|
@@ -796,7 +805,7 @@ module Sequel
|
|
796
805
|
|
797
806
|
# Whether the placeholder loader can be used to load the association.
|
798
807
|
def use_placeholder_loader?
|
799
|
-
|
808
|
+
self[:use_placeholder_loader]
|
800
809
|
end
|
801
810
|
end
|
802
811
|
|
@@ -1244,7 +1253,9 @@ module Sequel
|
|
1244
1253
|
else
|
1245
1254
|
assoc_record.values.delete(left_key_alias)
|
1246
1255
|
end
|
1247
|
-
|
1256
|
+
|
1257
|
+
objects = h[hash_key]
|
1258
|
+
|
1248
1259
|
if assign_singular
|
1249
1260
|
objects.each do |object|
|
1250
1261
|
object.associations[name] ||= assoc_record
|
@@ -1791,11 +1802,12 @@ module Sequel
|
|
1791
1802
|
opts.merge!(:type => type, :name => name, :cache=>({} if cache_associations), :model => self)
|
1792
1803
|
|
1793
1804
|
opts[:block] = block if block
|
1794
|
-
|
1805
|
+
opts[:instance_specific] = true if orig_opts[:dataset]
|
1806
|
+
if !opts.has_key?(:instance_specific) && (block || orig_opts[:block])
|
1795
1807
|
# It's possible the association is instance specific, in that it depends on
|
1796
1808
|
# values other than the foreign key value. This needs to be checked for
|
1797
1809
|
# in certain places to disable optimizations.
|
1798
|
-
opts[:instance_specific] =
|
1810
|
+
opts[:instance_specific] = _association_instance_specific_default(name)
|
1799
1811
|
end
|
1800
1812
|
opts = assoc_class.new.merge!(opts)
|
1801
1813
|
|
@@ -1803,6 +1815,7 @@ module Sequel
|
|
1803
1815
|
raise(Error, "cannot clone an association to an association of different type (association #{name} with type #{type} cloning #{opts[:clone]} with type #{cloned_assoc[:type]})")
|
1804
1816
|
end
|
1805
1817
|
|
1818
|
+
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
|
1806
1819
|
opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
|
1807
1820
|
opts[:graph_join_type] ||= :left_outer
|
1808
1821
|
opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
|
@@ -1899,6 +1912,12 @@ module Sequel
|
|
1899
1912
|
Plugins.def_dataset_methods(self, [:eager, :eager_graph, :eager_graph_with_options, :association_join, :association_full_join, :association_inner_join, :association_left_join, :association_right_join])
|
1900
1913
|
|
1901
1914
|
private
|
1915
|
+
|
1916
|
+
# The default value for the instance_specific option, if the association
|
1917
|
+
# could be instance specific and the :instance_specific option is not specified.
|
1918
|
+
def _association_instance_specific_default(_)
|
1919
|
+
true
|
1920
|
+
end
|
1902
1921
|
|
1903
1922
|
# The module to use for the association's methods. Defaults to
|
1904
1923
|
# the overridable_methods_module.
|
@@ -1948,10 +1967,8 @@ module Sequel
|
|
1948
1967
|
if opts[:block]
|
1949
1968
|
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
1950
1969
|
end
|
1951
|
-
|
1952
|
-
|
1953
|
-
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
1954
|
-
end
|
1970
|
+
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
1971
|
+
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
1955
1972
|
def_association_method(opts)
|
1956
1973
|
|
1957
1974
|
return if opts[:read_only]
|
@@ -2122,9 +2139,7 @@ module Sequel
|
|
2122
2139
|
|
2123
2140
|
eager_load_results(opts, eo) do |assoc_record|
|
2124
2141
|
hash_key = uses_cks ? pk_meths.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(opts.primary_key_method)
|
2125
|
-
|
2126
|
-
objects.each{|object| object.associations[name] = assoc_record}
|
2127
|
-
end
|
2142
|
+
h[hash_key].each{|object| object.associations[name] = assoc_record}
|
2128
2143
|
end
|
2129
2144
|
end
|
2130
2145
|
|
@@ -2171,7 +2186,7 @@ module Sequel
|
|
2171
2186
|
eager_load_results(opts, eo) do |assoc_record|
|
2172
2187
|
assoc_record.values.delete(delete_rn) if delete_rn
|
2173
2188
|
hash_key = uses_cks ? km.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(km)
|
2174
|
-
|
2189
|
+
objects = h[hash_key]
|
2175
2190
|
if assign_singular
|
2176
2191
|
objects.each do |object|
|
2177
2192
|
unless object.associations[name]
|
@@ -3064,6 +3079,8 @@ module Sequel
|
|
3064
3079
|
# significantly slower in some cases (perhaps even the majority of cases), so you should
|
3065
3080
|
# only use this if you have benchmarked that it is faster for your use cases.
|
3066
3081
|
def eager_graph_with_options(associations, opts=OPTS)
|
3082
|
+
return self if associations.empty?
|
3083
|
+
|
3067
3084
|
opts = opts.dup unless opts.frozen?
|
3068
3085
|
associations = [associations] unless associations.is_a?(Array)
|
3069
3086
|
ds = if eg = @opts[:eager_graph]
|
@@ -3190,7 +3207,6 @@ module Sequel
|
|
3190
3207
|
# requirements :: an array, used as a stack for requirements
|
3191
3208
|
# *associations :: the associations to add to the graph
|
3192
3209
|
def eager_graph_associations(ds, model, ta, requirements, *associations)
|
3193
|
-
return ds if associations.empty?
|
3194
3210
|
associations.flatten.each do |association|
|
3195
3211
|
ds = case association
|
3196
3212
|
when Symbol, SQL::AliasedExpression
|
data/lib/sequel/model/base.rb
CHANGED
@@ -491,6 +491,11 @@ module Sequel
|
|
491
491
|
# the module using a the camelized plugin name under Sequel::Plugins.
|
492
492
|
def plugin(plugin, *args, &block)
|
493
493
|
m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
|
494
|
+
|
495
|
+
if !m.respond_to?(:apply) && !m.respond_to?(:configure) && (!args.empty? || block)
|
496
|
+
Deprecation.deprecate("Plugin #{plugin} accepts no arguments or block, and passing arguments/block to it", "Remove arguments and block when loading the plugin")
|
497
|
+
end
|
498
|
+
|
494
499
|
unless @plugins.include?(m)
|
495
500
|
@plugins << m
|
496
501
|
m.apply(self, *args, &block) if m.respond_to?(:apply)
|
@@ -500,8 +505,10 @@ module Sequel
|
|
500
505
|
dataset_extend(m::DatasetMethods, :create_class_methods=>false)
|
501
506
|
end
|
502
507
|
end
|
508
|
+
|
503
509
|
m.configure(self, *args, &block) if m.respond_to?(:configure)
|
504
510
|
end
|
511
|
+
ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
|
505
512
|
|
506
513
|
# Returns primary key attribute hash. If using a composite primary key
|
507
514
|
# value such be an array with values for each primary key in the correct
|
@@ -593,7 +600,7 @@ module Sequel
|
|
593
600
|
@columns = superclass.instance_variable_get(:@columns)
|
594
601
|
@db_schema = superclass.instance_variable_get(:@db_schema)
|
595
602
|
else
|
596
|
-
@dataset = @dataset.with_extend(*@dataset_method_modules.reverse)
|
603
|
+
@dataset = @dataset.with_extend(*@dataset_method_modules.reverse)
|
597
604
|
@db_schema = get_db_schema
|
598
605
|
end
|
599
606
|
|
@@ -632,8 +639,7 @@ module Sequel
|
|
632
639
|
|
633
640
|
# Cache of setter methods to allow by default, in order to speed up mass assignment.
|
634
641
|
def setter_methods
|
635
|
-
|
636
|
-
@setter_methods = get_setter_methods
|
642
|
+
@setter_methods || (@setter_methods = get_setter_methods)
|
637
643
|
end
|
638
644
|
|
639
645
|
# Returns name of primary table for the dataset. If the table for the dataset
|
@@ -751,6 +757,7 @@ module Sequel
|
|
751
757
|
else
|
752
758
|
define_singleton_method(meth){|*args, &block| dataset.public_send(meth, *args, &block)}
|
753
759
|
end
|
760
|
+
singleton_class.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
|
754
761
|
end
|
755
762
|
|
756
763
|
# Get the schema from the database, fall back on checking the columns
|
@@ -820,7 +827,6 @@ module Sequel
|
|
820
827
|
super
|
821
828
|
ivs = subclass.instance_variables
|
822
829
|
inherited_instance_variables.each do |iv, dup|
|
823
|
-
next if ivs.include?(iv)
|
824
830
|
if (sup_class_value = instance_variable_get(iv)) && dup
|
825
831
|
sup_class_value = case dup
|
826
832
|
when :dup
|
@@ -1116,7 +1122,7 @@ module Sequel
|
|
1116
1122
|
when nil
|
1117
1123
|
return false
|
1118
1124
|
when Array
|
1119
|
-
return false if
|
1125
|
+
return false if pkv.any?(&:nil?)
|
1120
1126
|
end
|
1121
1127
|
|
1122
1128
|
(obj.class == model) && (obj.pk == pkv)
|
@@ -2232,7 +2238,9 @@ module Sequel
|
|
2232
2238
|
plugin self
|
2233
2239
|
|
2234
2240
|
singleton_class.send(:undef_method, :dup, :clone, :initialize_copy)
|
2241
|
+
# :nocov:
|
2235
2242
|
if RUBY_VERSION >= '1.9.3'
|
2243
|
+
# :nocov:
|
2236
2244
|
singleton_class.send(:undef_method, :initialize_clone, :initialize_dup)
|
2237
2245
|
end
|
2238
2246
|
end
|
data/lib/sequel/model/plugins.rb
CHANGED
@@ -31,6 +31,7 @@ module Sequel
|
|
31
31
|
def self.def_dataset_methods(mod, meths)
|
32
32
|
Array(meths).each do |meth|
|
33
33
|
mod.class_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
|
34
|
+
mod.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
@@ -149,9 +150,8 @@ module Sequel
|
|
149
150
|
required_args = arity
|
150
151
|
arity -= 1 if keyword == :required
|
151
152
|
|
152
|
-
|
153
|
-
|
154
|
-
end
|
153
|
+
# callable currently is always a non-lambda Proc
|
154
|
+
optional_args -= arity
|
155
155
|
|
156
156
|
[required_args, optional_args, rest, keyword]
|
157
157
|
end
|
@@ -2,13 +2,17 @@
|
|
2
2
|
|
3
3
|
module Sequel
|
4
4
|
module Plugins
|
5
|
-
# The association_pks plugin adds association_pks and
|
6
|
-
# instance methods to the model class for each
|
7
|
-
#
|
8
|
-
#
|
5
|
+
# The association_pks plugin adds association_pks, association_pks=, and
|
6
|
+
# association_pks_dataset instance methods to the model class for each
|
7
|
+
# one_to_many and many_to_many association added. These methods allow for
|
8
|
+
# easily returning the primary keys of the associated objects, and easily
|
9
|
+
# modifying which objects are associated:
|
9
10
|
#
|
10
11
|
# Artist.one_to_many :albums
|
11
12
|
# artist = Artist[1]
|
13
|
+
# artist.album_pks_dataset
|
14
|
+
# # SELECT id FROM albums WHERE (albums.artist_id = 1)
|
15
|
+
#
|
12
16
|
# artist.album_pks # [1, 2, 3]
|
13
17
|
# artist.album_pks = [2, 4]
|
14
18
|
# artist.album_pks # [2, 4]
|
@@ -22,11 +26,18 @@ module Sequel
|
|
22
26
|
# This plugin makes modifications directly to the underlying tables,
|
23
27
|
# it does not create or return any model objects, and therefore does
|
24
28
|
# not call any callbacks. If you have any association callbacks,
|
25
|
-
# you probably should not use the setter methods.
|
29
|
+
# you probably should not use the setter methods this plugin adds.
|
26
30
|
#
|
27
31
|
# By default, changes to the association will not happen until the object
|
28
|
-
# is saved. However, using the delay_pks: false option, you can have
|
29
|
-
# changes made immediately when the association_pks setter method is called.
|
32
|
+
# is saved. However, using the delay_pks: false association option, you can have
|
33
|
+
# the changes made immediately when the association_pks setter method is called.
|
34
|
+
#
|
35
|
+
# By default, repeated calls to the association_pks getter method will not be
|
36
|
+
# cached, unless the setter method has been used and the delay_pks: false
|
37
|
+
# association option is not used. You can set caching of repeated calls to the
|
38
|
+
# association_pks getter method using the :cache_pks association option. You can
|
39
|
+
# pass the :refresh option when calling the getter method to ignore any existing
|
40
|
+
# cached values, similar to how the :refresh option works with associations.
|
30
41
|
#
|
31
42
|
# By default, if you pass a nil value to the setter, an exception will be raised.
|
32
43
|
# You can change this behavior by using the :association_pks_nil association option.
|
@@ -60,9 +71,11 @@ module Sequel
|
|
60
71
|
|
61
72
|
# Define a association_pks method using the block for the association reflection
|
62
73
|
def def_association_pks_methods(opts)
|
74
|
+
association_module_def(opts[:pks_dataset_method], &opts[:pks_dataset])
|
75
|
+
|
63
76
|
opts[:pks_getter_method] = :"#{singularize(opts[:name])}_pks_getter"
|
64
77
|
association_module_def(opts[:pks_getter_method], &opts[:pks_getter])
|
65
|
-
association_module_def(:"#{singularize(opts[:name])}_pks", opts){_association_pks_getter(opts)}
|
78
|
+
association_module_def(:"#{singularize(opts[:name])}_pks", opts){|dynamic_opts=OPTS| _association_pks_getter(opts, dynamic_opts)}
|
66
79
|
|
67
80
|
if opts[:pks_setter]
|
68
81
|
opts[:pks_setter_method] = :"#{singularize(opts[:name])}_pks_setter"
|
@@ -84,7 +97,9 @@ module Sequel
|
|
84
97
|
clpk = lpk.is_a?(Array)
|
85
98
|
crk = rk.is_a?(Array)
|
86
99
|
|
87
|
-
opts[:
|
100
|
+
dataset_method = opts[:pks_dataset_method] = :"#{singularize(opts[:name])}_pks_dataset"
|
101
|
+
|
102
|
+
opts[:pks_dataset] = if join_associated_table = opts[:association_pks_use_associated_table]
|
88
103
|
tname = opts[:join_table]
|
89
104
|
lambda do
|
90
105
|
cond = if clpk
|
@@ -95,16 +110,26 @@ module Sequel
|
|
95
110
|
rpk = opts.associated_class.primary_key
|
96
111
|
opts.associated_dataset.
|
97
112
|
naked.where(cond).
|
98
|
-
|
113
|
+
select(*Sequel.public_send(rpk.is_a?(Array) ? :deep_qualify : :qualify, opts.associated_class.table_name, rpk))
|
99
114
|
end
|
100
115
|
elsif clpk
|
101
116
|
lambda do
|
102
117
|
cond = lk.zip(lpk).map{|k, pk| [k, get_column_value(pk)]}
|
103
|
-
_join_table_dataset(opts).where(cond).
|
118
|
+
_join_table_dataset(opts).where(cond).select(*rk)
|
119
|
+
end
|
120
|
+
else
|
121
|
+
lambda do
|
122
|
+
_join_table_dataset(opts).where(lk=>get_column_value(lpk)).select(*rk)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
opts[:pks_getter] = if join_associated_table = opts[:association_pks_use_associated_table]
|
127
|
+
lambda do
|
128
|
+
public_send(dataset_method).map(opts.associated_class.primary_key)
|
104
129
|
end
|
105
130
|
else
|
106
131
|
lambda do
|
107
|
-
|
132
|
+
public_send(dataset_method).map(rk)
|
108
133
|
end
|
109
134
|
end
|
110
135
|
|
@@ -145,8 +170,14 @@ module Sequel
|
|
145
170
|
|
146
171
|
key = opts[:key]
|
147
172
|
|
173
|
+
dataset_method = opts[:pks_dataset_method] = :"#{singularize(opts[:name])}_pks_dataset"
|
174
|
+
|
175
|
+
opts[:pks_dataset] = lambda do
|
176
|
+
public_send(opts[:dataset_method]).select(*opts.associated_class.primary_key)
|
177
|
+
end
|
178
|
+
|
148
179
|
opts[:pks_getter] = lambda do
|
149
|
-
public_send(
|
180
|
+
public_send(dataset_method).map(opts.associated_class.primary_key)
|
150
181
|
end
|
151
182
|
|
152
183
|
unless opts[:read_only]
|
@@ -207,12 +238,22 @@ module Sequel
|
|
207
238
|
# Return the primary keys of the associated objects.
|
208
239
|
# If the receiver is a new object, return any saved
|
209
240
|
# pks, or an empty array if no pks have been saved.
|
210
|
-
def _association_pks_getter(opts)
|
241
|
+
def _association_pks_getter(opts, dynamic_opts=OPTS)
|
242
|
+
do_cache = opts[:cache_pks]
|
211
243
|
delay = opts.fetch(:delay_pks, true)
|
212
|
-
|
244
|
+
cache_or_delay = do_cache || delay
|
245
|
+
|
246
|
+
if dynamic_opts[:refresh] && @_association_pks
|
247
|
+
@_association_pks.delete(opts[:name])
|
248
|
+
end
|
249
|
+
|
250
|
+
if new? && cache_or_delay
|
213
251
|
(@_association_pks ||= {})[opts[:name]] ||= []
|
214
|
-
elsif
|
252
|
+
elsif cache_or_delay && @_association_pks && (objs = @_association_pks[opts[:name]])
|
215
253
|
objs
|
254
|
+
elsif do_cache
|
255
|
+
# pks_getter_method is private
|
256
|
+
(@_association_pks ||= {})[opts[:name]] = send(opts[:pks_getter_method])
|
216
257
|
else
|
217
258
|
# pks_getter_method is private
|
218
259
|
send(opts[:pks_getter_method])
|
@@ -254,9 +295,10 @@ module Sequel
|
|
254
295
|
|
255
296
|
if primary_key.is_a?(Array)
|
256
297
|
if (cols = sch.values_at(*klass.primary_key)).all? && (convs = cols.map{|c| c[:type] == :integer}).all?
|
298
|
+
db = model.db
|
257
299
|
pks.map do |cpk|
|
258
|
-
cpk.
|
259
|
-
|
300
|
+
cpk.map do |pk|
|
301
|
+
db.typecast_value(:integer, pk)
|
260
302
|
end
|
261
303
|
end
|
262
304
|
else
|