sequel 5.30.0 → 5.35.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.
- checksums.yaml +4 -4
- data/CHANGELOG +86 -0
- data/README.rdoc +1 -1
- data/doc/advanced_associations.rdoc +4 -4
- data/doc/association_basics.rdoc +10 -5
- data/doc/code_order.rdoc +12 -2
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/model_dataset_method_design.rdoc +1 -1
- data/doc/postgresql.rdoc +71 -0
- data/doc/release_notes/5.31.0.txt +148 -0
- data/doc/release_notes/5.32.0.txt +46 -0
- data/doc/release_notes/5.33.0.txt +24 -0
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/testing.rdoc +1 -1
- data/lib/sequel/adapters/oracle.rb +2 -1
- data/lib/sequel/adapters/shared/access.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +5 -5
- data/lib/sequel/adapters/shared/mysql.rb +9 -9
- data/lib/sequel/adapters/shared/oracle.rb +16 -16
- data/lib/sequel/adapters/shared/postgres.rb +169 -14
- data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
- data/lib/sequel/adapters/shared/sqlite.rb +33 -6
- data/lib/sequel/adapters/tinytds.rb +1 -0
- data/lib/sequel/connection_pool/sharded_single.rb +4 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +12 -12
- data/lib/sequel/connection_pool/single.rb +1 -1
- data/lib/sequel/connection_pool/threaded.rb +2 -2
- data/lib/sequel/core.rb +318 -314
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/misc.rb +16 -10
- data/lib/sequel/database/query.rb +3 -1
- data/lib/sequel/database/schema_generator.rb +0 -1
- data/lib/sequel/database/schema_methods.rb +15 -16
- data/lib/sequel/database/transactions.rb +7 -4
- 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/connection_expiration.rb +2 -2
- data/lib/sequel/extensions/connection_validator.rb +2 -2
- data/lib/sequel/extensions/core_refinements.rb +2 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
- data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
- data/lib/sequel/extensions/index_caching.rb +9 -7
- data/lib/sequel/extensions/integer64.rb +2 -0
- data/lib/sequel/extensions/migration.rb +1 -2
- data/lib/sequel/extensions/pg_array_ops.rb +4 -0
- data/lib/sequel/extensions/pg_enum.rb +7 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
- data/lib/sequel/extensions/pg_hstore.rb +6 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_inet.rb +15 -5
- data/lib/sequel/extensions/pg_interval.rb +2 -0
- data/lib/sequel/extensions/pg_json_ops.rb +2 -0
- data/lib/sequel/extensions/pg_range.rb +5 -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_timestamptz.rb +2 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
- data/lib/sequel/extensions/s.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +10 -4
- data/lib/sequel/extensions/server_block.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 +2 -0
- data/lib/sequel/model/associations.rb +54 -25
- data/lib/sequel/model/base.rb +70 -57
- data/lib/sequel/model/plugins.rb +3 -3
- data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
- data/lib/sequel/plugins/association_multi_add_remove.rb +2 -0
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/boolean_subsets.rb +4 -1
- data/lib/sequel/plugins/class_table_inheritance.rb +28 -28
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dirty.rb +13 -13
- data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/json_serializer.rb +3 -7
- 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 +15 -15
- data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +2 -0
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/timezones.rb +6 -4
- data/lib/sequel/version.rb +1 -1
- metadata +18 -2
data/lib/sequel/model/base.rb
CHANGED
|
@@ -460,47 +460,6 @@ module Sequel
|
|
|
460
460
|
super
|
|
461
461
|
end
|
|
462
462
|
|
|
463
|
-
# If possible, set the dataset for the model subclass as soon as it
|
|
464
|
-
# is created. Also, make sure the inherited class instance variables
|
|
465
|
-
# are copied into the subclass.
|
|
466
|
-
#
|
|
467
|
-
# Sequel queries the database to get schema information as soon as
|
|
468
|
-
# a model class is created:
|
|
469
|
-
#
|
|
470
|
-
# class Artist < Sequel::Model # Causes schema query
|
|
471
|
-
# end
|
|
472
|
-
def inherited(subclass)
|
|
473
|
-
super
|
|
474
|
-
ivs = subclass.instance_variables
|
|
475
|
-
inherited_instance_variables.each do |iv, dup|
|
|
476
|
-
next if ivs.include?(iv)
|
|
477
|
-
if (sup_class_value = instance_variable_get(iv)) && dup
|
|
478
|
-
sup_class_value = case dup
|
|
479
|
-
when :dup
|
|
480
|
-
sup_class_value.dup
|
|
481
|
-
when :hash_dup
|
|
482
|
-
h = {}
|
|
483
|
-
sup_class_value.each{|k,v| h[k] = v.dup}
|
|
484
|
-
h
|
|
485
|
-
when Proc
|
|
486
|
-
dup.call(sup_class_value)
|
|
487
|
-
else
|
|
488
|
-
raise Error, "bad inherited instance variable type: #{dup.inspect}"
|
|
489
|
-
end
|
|
490
|
-
end
|
|
491
|
-
subclass.instance_variable_set(iv, sup_class_value)
|
|
492
|
-
end
|
|
493
|
-
|
|
494
|
-
unless ivs.include?(:@dataset)
|
|
495
|
-
if @dataset && self != Model
|
|
496
|
-
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
|
497
|
-
elsif (n = subclass.name) && !n.to_s.empty?
|
|
498
|
-
db
|
|
499
|
-
subclass.set_dataset(subclass.implicit_table_name)
|
|
500
|
-
end
|
|
501
|
-
end
|
|
502
|
-
end
|
|
503
|
-
|
|
504
463
|
# Returns the implicit table name for the model class, which is the demodulized,
|
|
505
464
|
# underscored, pluralized name of the class.
|
|
506
465
|
#
|
|
@@ -515,12 +474,6 @@ module Sequel
|
|
|
515
474
|
call(values)
|
|
516
475
|
end
|
|
517
476
|
|
|
518
|
-
# Clear the setter_methods cache when a setter method is added.
|
|
519
|
-
def method_added(meth)
|
|
520
|
-
clear_setter_methods_cache if meth.to_s.end_with?('=')
|
|
521
|
-
super
|
|
522
|
-
end
|
|
523
|
-
|
|
524
477
|
# Mark the model as not having a primary key. Not having a primary key
|
|
525
478
|
# can cause issues, among which is that you won't be able to update records.
|
|
526
479
|
#
|
|
@@ -538,6 +491,11 @@ module Sequel
|
|
|
538
491
|
# the module using a the camelized plugin name under Sequel::Plugins.
|
|
539
492
|
def plugin(plugin, *args, &block)
|
|
540
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
|
+
|
|
541
499
|
unless @plugins.include?(m)
|
|
542
500
|
@plugins << m
|
|
543
501
|
m.apply(self, *args, &block) if m.respond_to?(:apply)
|
|
@@ -547,6 +505,7 @@ module Sequel
|
|
|
547
505
|
dataset_extend(m::DatasetMethods, :create_class_methods=>false)
|
|
548
506
|
end
|
|
549
507
|
end
|
|
508
|
+
|
|
550
509
|
m.configure(self, *args, &block) if m.respond_to?(:configure)
|
|
551
510
|
end
|
|
552
511
|
|
|
@@ -640,7 +599,7 @@ module Sequel
|
|
|
640
599
|
@columns = superclass.instance_variable_get(:@columns)
|
|
641
600
|
@db_schema = superclass.instance_variable_get(:@db_schema)
|
|
642
601
|
else
|
|
643
|
-
@dataset = @dataset.with_extend(*@dataset_method_modules.reverse)
|
|
602
|
+
@dataset = @dataset.with_extend(*@dataset_method_modules.reverse)
|
|
644
603
|
@db_schema = get_db_schema
|
|
645
604
|
end
|
|
646
605
|
|
|
@@ -679,8 +638,7 @@ module Sequel
|
|
|
679
638
|
|
|
680
639
|
# Cache of setter methods to allow by default, in order to speed up mass assignment.
|
|
681
640
|
def setter_methods
|
|
682
|
-
|
|
683
|
-
@setter_methods = get_setter_methods
|
|
641
|
+
@setter_methods || (@setter_methods = get_setter_methods)
|
|
684
642
|
end
|
|
685
643
|
|
|
686
644
|
# Returns name of primary table for the dataset. If the table for the dataset
|
|
@@ -854,6 +812,46 @@ module Sequel
|
|
|
854
812
|
meths
|
|
855
813
|
end
|
|
856
814
|
|
|
815
|
+
# If possible, set the dataset for the model subclass as soon as it
|
|
816
|
+
# is created. Also, make sure the inherited class instance variables
|
|
817
|
+
# are copied into the subclass.
|
|
818
|
+
#
|
|
819
|
+
# Sequel queries the database to get schema information as soon as
|
|
820
|
+
# a model class is created:
|
|
821
|
+
#
|
|
822
|
+
# class Artist < Sequel::Model # Causes schema query
|
|
823
|
+
# end
|
|
824
|
+
def inherited(subclass)
|
|
825
|
+
super
|
|
826
|
+
ivs = subclass.instance_variables
|
|
827
|
+
inherited_instance_variables.each do |iv, dup|
|
|
828
|
+
if (sup_class_value = instance_variable_get(iv)) && dup
|
|
829
|
+
sup_class_value = case dup
|
|
830
|
+
when :dup
|
|
831
|
+
sup_class_value.dup
|
|
832
|
+
when :hash_dup
|
|
833
|
+
h = {}
|
|
834
|
+
sup_class_value.each{|k,v| h[k] = v.dup}
|
|
835
|
+
h
|
|
836
|
+
when Proc
|
|
837
|
+
dup.call(sup_class_value)
|
|
838
|
+
else
|
|
839
|
+
raise Error, "bad inherited instance variable type: #{dup.inspect}"
|
|
840
|
+
end
|
|
841
|
+
end
|
|
842
|
+
subclass.instance_variable_set(iv, sup_class_value)
|
|
843
|
+
end
|
|
844
|
+
|
|
845
|
+
unless ivs.include?(:@dataset)
|
|
846
|
+
if @dataset && self != Model
|
|
847
|
+
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
|
848
|
+
elsif (n = subclass.name) && !n.to_s.empty?
|
|
849
|
+
db
|
|
850
|
+
subclass.set_dataset(subclass.implicit_table_name)
|
|
851
|
+
end
|
|
852
|
+
end
|
|
853
|
+
end
|
|
854
|
+
|
|
857
855
|
# A hash of instance variables to automatically set up in subclasses.
|
|
858
856
|
# Keys are instance variable symbols, values should be:
|
|
859
857
|
# nil :: Assign directly from superclass to subclass (frozen objects)
|
|
@@ -908,6 +906,12 @@ module Sequel
|
|
|
908
906
|
opts[:class_name] ||= '::' + ((name || '').split("::")[0..-2] + [camelize(default)]).join('::')
|
|
909
907
|
end
|
|
910
908
|
|
|
909
|
+
# Clear the setter_methods cache when a setter method is added.
|
|
910
|
+
def method_added(meth)
|
|
911
|
+
clear_setter_methods_cache if meth.to_s.end_with?('=')
|
|
912
|
+
super
|
|
913
|
+
end
|
|
914
|
+
|
|
911
915
|
# Module that the class includes that holds methods the class adds for column accessors and
|
|
912
916
|
# associations so that the methods can be overridden with +super+.
|
|
913
917
|
def overridable_methods_module
|
|
@@ -1116,7 +1120,7 @@ module Sequel
|
|
|
1116
1120
|
when nil
|
|
1117
1121
|
return false
|
|
1118
1122
|
when Array
|
|
1119
|
-
return false if
|
|
1123
|
+
return false if pkv.any?(&:nil?)
|
|
1120
1124
|
end
|
|
1121
1125
|
|
|
1122
1126
|
(obj.class == model) && (obj.pk == pkv)
|
|
@@ -1718,6 +1722,7 @@ module Sequel
|
|
|
1718
1722
|
|
|
1719
1723
|
# The values hash to use when inserting a new record.
|
|
1720
1724
|
alias _insert_values values
|
|
1725
|
+
private :_insert_values
|
|
1721
1726
|
|
|
1722
1727
|
# Refresh using a particular dataset, used inside save to make sure the same server
|
|
1723
1728
|
# is used for reading newly inserted values from the database
|
|
@@ -1772,14 +1777,12 @@ module Sequel
|
|
|
1772
1777
|
before_update
|
|
1773
1778
|
columns = opts[:columns]
|
|
1774
1779
|
if columns.nil?
|
|
1775
|
-
if opts[:changed]
|
|
1776
|
-
|
|
1777
|
-
columns_updated = @values.reject{|k,v| !cc.include?(k)}
|
|
1778
|
-
cc.clear
|
|
1780
|
+
columns_updated = if opts[:changed]
|
|
1781
|
+
_save_update_changed_colums_hash
|
|
1779
1782
|
else
|
|
1780
|
-
|
|
1781
|
-
_clear_changed_columns(:update)
|
|
1783
|
+
_save_update_all_columns_hash
|
|
1782
1784
|
end
|
|
1785
|
+
_clear_changed_columns(:update)
|
|
1783
1786
|
else # update only the specified columns
|
|
1784
1787
|
columns = Array(columns)
|
|
1785
1788
|
columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
|
@@ -1827,6 +1830,14 @@ module Sequel
|
|
|
1827
1830
|
v
|
|
1828
1831
|
end
|
|
1829
1832
|
|
|
1833
|
+
# Return a hash of values used when saving changed columns of an
|
|
1834
|
+
# existing object. Defaults to all of the objects current values
|
|
1835
|
+
# that are recorded as modified.
|
|
1836
|
+
def _save_update_changed_colums_hash
|
|
1837
|
+
cc = changed_columns
|
|
1838
|
+
@values.reject{|k,v| !cc.include?(k)}
|
|
1839
|
+
end
|
|
1840
|
+
|
|
1830
1841
|
# Validate the object if validating on save. Skips validation
|
|
1831
1842
|
# completely (including validation hooks) if
|
|
1832
1843
|
# skip_validation_on_save! has been called on the object,
|
|
@@ -2225,7 +2236,9 @@ module Sequel
|
|
|
2225
2236
|
plugin self
|
|
2226
2237
|
|
|
2227
2238
|
singleton_class.send(:undef_method, :dup, :clone, :initialize_copy)
|
|
2239
|
+
# :nocov:
|
|
2228
2240
|
if RUBY_VERSION >= '1.9.3'
|
|
2241
|
+
# :nocov:
|
|
2229
2242
|
singleton_class.send(:undef_method, :initialize_clone, :initialize_dup)
|
|
2230
2243
|
end
|
|
2231
2244
|
end
|
data/lib/sequel/model/plugins.rb
CHANGED
|
@@ -40,6 +40,7 @@ module Sequel
|
|
|
40
40
|
mod.send(:define_method, :inherited_instance_variables) do ||
|
|
41
41
|
super().merge!(hash)
|
|
42
42
|
end
|
|
43
|
+
mod.send(:private, :inherited_instance_variables)
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
# Add method to +mod+ that overrides set_dataset to call the method afterward.
|
|
@@ -148,9 +149,8 @@ module Sequel
|
|
|
148
149
|
required_args = arity
|
|
149
150
|
arity -= 1 if keyword == :required
|
|
150
151
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
end
|
|
152
|
+
# callable currently is always a non-lambda Proc
|
|
153
|
+
optional_args -= arity
|
|
154
154
|
|
|
155
155
|
[required_args, optional_args, rest, keyword]
|
|
156
156
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module Plugins
|
|
5
|
+
# The association_lazy_eager_option plugin supports passing
|
|
6
|
+
# an +:eager+ option to an association method. If the related
|
|
7
|
+
# association is already cached, the cached version will be
|
|
8
|
+
# returned. If the association is not already cached, it will
|
|
9
|
+
# be loaded, and the value of the +:eager+ option will be used
|
|
10
|
+
# to perform an eager load of the given associations.
|
|
11
|
+
# the plural versions.
|
|
12
|
+
#
|
|
13
|
+
# With Sequel's default behavior, you can already perform an
|
|
14
|
+
# eager load when lazy loading using a block:
|
|
15
|
+
#
|
|
16
|
+
# obj.association{|ds| ds.eager(:nested_association)}
|
|
17
|
+
#
|
|
18
|
+
# However, this will ignore any cached version. In more
|
|
19
|
+
# complex software, the association may already be cached
|
|
20
|
+
# and have the nested association cached inside of it, and
|
|
21
|
+
# using this callback approach then requires 2 unnecessary
|
|
22
|
+
# queries. This plugin will not perform any queries if the
|
|
23
|
+
# association is already cached, preventing duplicate work.
|
|
24
|
+
# However, you should make sure that an already loaded
|
|
25
|
+
# association has the nested association already eagerly
|
|
26
|
+
# loaded.
|
|
27
|
+
#
|
|
28
|
+
# Usage:
|
|
29
|
+
#
|
|
30
|
+
# # Make all model subclasses support the :eager association
|
|
31
|
+
# # method option (called before loading subclasses)
|
|
32
|
+
# Sequel::Model.plugin :association_lazy_eager_option
|
|
33
|
+
#
|
|
34
|
+
# # Make the Album class support the :eager association
|
|
35
|
+
# # method option
|
|
36
|
+
# Album.plugin :association_lazy_eager_option
|
|
37
|
+
module AssociationLazyEagerOption
|
|
38
|
+
module InstanceMethods
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# Return a dataset for the association after applying any dynamic callback.
|
|
42
|
+
def _associated_dataset(opts, dynamic_opts)
|
|
43
|
+
ds = super
|
|
44
|
+
|
|
45
|
+
if eager = dynamic_opts[:eager]
|
|
46
|
+
ds = ds.eager(eager)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
ds
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# A placeholder literalizer that can be used to load the association, or nil to not use one.
|
|
53
|
+
def _associated_object_loader(opts, dynamic_opts)
|
|
54
|
+
return if dynamic_opts[:eager]
|
|
55
|
+
super
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Whether to use a simple primary key lookup on the associated class when loading.
|
|
59
|
+
def load_with_primary_key_lookup?(opts, dynamic_opts)
|
|
60
|
+
return false if dynamic_opts[:eager]
|
|
61
|
+
super
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -39,6 +39,8 @@ module Sequel
|
|
|
39
39
|
# Album.plugin :association_multi_add_remove
|
|
40
40
|
module AssociationMultiAddRemove
|
|
41
41
|
module ClassMethods
|
|
42
|
+
private
|
|
43
|
+
|
|
42
44
|
# Define the methods use to add/remove/set multiple associated objects
|
|
43
45
|
# in a single method call.
|
|
44
46
|
def def_association_instance_methods(opts)
|
|
@@ -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
|