sequel 4.14.0 → 4.15.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 +32 -0
- data/README.rdoc +3 -3
- data/Rakefile +1 -1
- data/doc/opening_databases.rdoc +20 -2
- data/doc/release_notes/4.15.0.txt +56 -0
- data/doc/testing.rdoc +10 -4
- data/lib/sequel/adapters/fdbsql.rb +285 -0
- data/lib/sequel/adapters/informix.rb +15 -0
- data/lib/sequel/adapters/jdbc/fdbsql.rb +65 -0
- data/lib/sequel/adapters/mock.rb +1 -0
- data/lib/sequel/adapters/shared/fdbsql.rb +550 -0
- data/lib/sequel/adapters/shared/postgres.rb +23 -10
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +10 -3
- data/lib/sequel/dataset/placeholder_literalizer.rb +7 -0
- data/lib/sequel/extensions/date_arithmetic.rb +5 -0
- data/lib/sequel/extensions/migration.rb +2 -2
- data/lib/sequel/extensions/pg_array.rb +15 -1
- data/lib/sequel/extensions/pg_json.rb +3 -0
- data/lib/sequel/extensions/pg_json_ops.rb +4 -4
- data/lib/sequel/extensions/schema_dumper.rb +9 -1
- data/lib/sequel/model/associations.rb +70 -21
- data/lib/sequel/plugins/active_model.rb +7 -2
- data/lib/sequel/plugins/many_through_many.rb +1 -0
- data/lib/sequel/plugins/pg_array_associations.rb +2 -1
- data/lib/sequel/plugins/split_values.rb +64 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/fdbsql_spec.rb +429 -0
- data/spec/adapters/informix_spec.rb +6 -0
- data/spec/adapters/postgres_spec.rb +49 -1
- data/spec/adapters/spec_helper.rb +6 -1
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/placeholder_literalizer_spec.rb +10 -0
- data/spec/extensions/date_arithmetic_spec.rb +7 -0
- data/spec/extensions/many_through_many_spec.rb +14 -0
- data/spec/extensions/migration_spec.rb +3 -3
- data/spec/extensions/pg_array_associations_spec.rb +9 -0
- data/spec/extensions/pg_json_ops_spec.rb +4 -8
- data/spec/extensions/schema_dumper_spec.rb +9 -0
- data/spec/extensions/spec_helper.rb +3 -0
- data/spec/extensions/split_values_spec.rb +22 -0
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +1 -1
- data/spec/integration/eager_loader_test.rb +1 -1
- data/spec/integration/plugin_test.rb +3 -2
- data/spec/integration/prepared_statement_test.rb +3 -3
- data/spec/integration/schema_test.rb +3 -3
- data/spec/integration/spec_helper.rb +6 -1
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/model/association_reflection_spec.rb +29 -0
- data/spec/model/associations_spec.rb +36 -0
- data/spec/model/eager_loading_spec.rb +14 -0
- data/spec/model/spec_helper.rb +3 -0
- data/spec/rspec_helper.rb +4 -0
- metadata +10 -2
@@ -809,27 +809,40 @@ module Sequel
|
|
809
809
|
|
810
810
|
# DDL statement for creating a table with the given name, columns, and options
|
811
811
|
def create_table_prefix_sql(name, options)
|
812
|
-
|
813
|
-
raise(Error, "can't provide :
|
814
|
-
raise(Error, "
|
812
|
+
prefix_sql = if options[:temp]
|
813
|
+
raise(Error, "can't provide both :temp and :unlogged to create_table") if options[:unlogged]
|
814
|
+
raise(Error, "can't provide both :temp and :foreign to create_table") if options[:foreign]
|
815
|
+
temporary_table_sql
|
816
|
+
elsif options[:foreign]
|
817
|
+
raise(Error, "can't provide both :foreign and :unlogged to create_table") if options[:unlogged]
|
818
|
+
'FOREIGN '
|
819
|
+
elsif options[:unlogged]
|
820
|
+
UNLOGGED
|
815
821
|
end
|
816
|
-
|
817
|
-
|
818
|
-
temporary_table_sql
|
819
|
-
elsif options[:unlogged]
|
820
|
-
UNLOGGED
|
821
|
-
end
|
822
|
-
"CREATE #{temp_or_unlogged_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}"
|
822
|
+
|
823
|
+
"CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}"
|
823
824
|
end
|
824
825
|
|
825
826
|
def create_table_sql(name, generator, options)
|
826
827
|
sql = super
|
828
|
+
|
827
829
|
if inherits = options[:inherits]
|
828
830
|
sql << " INHERITS (#{Array(inherits).map{|t| quote_schema_table(t)}.join(', ')})"
|
829
831
|
end
|
832
|
+
|
830
833
|
if on_commit = options[:on_commit]
|
834
|
+
raise(Error, "can't provide :on_commit without :temp to create_table") unless options[:temp]
|
835
|
+
raise(Error, "unsupported on_commit option: #{on_commit.inspect}") unless ON_COMMIT.has_key?(on_commit)
|
831
836
|
sql << " ON COMMIT #{ON_COMMIT[on_commit]}"
|
832
837
|
end
|
838
|
+
|
839
|
+
if server = options[:foreign]
|
840
|
+
sql << " SERVER #{quote_identifier(server)}"
|
841
|
+
if foreign_opts = options[:options]
|
842
|
+
sql << " OPTIONS (#{foreign_opts.map{|k, v| "#{k} #{literal(v.to_s)}"}.join(', ')})"
|
843
|
+
end
|
844
|
+
end
|
845
|
+
|
833
846
|
sql
|
834
847
|
end
|
835
848
|
|
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
# ---------------------
|
7
7
|
|
8
8
|
# Array of supported database adapters
|
9
|
-
ADAPTERS = %w'ado amalgalite cubrid db2 dbi do firebird ibmdb informix jdbc mock mysql mysql2 odbc openbase oracle postgres sqlanywhere sqlite swift tinytds'.collect{|x| x.to_sym}
|
9
|
+
ADAPTERS = %w'ado amalgalite cubrid db2 dbi do fdbsql firebird ibmdb informix jdbc mock mysql mysql2 odbc openbase oracle postgres sqlanywhere sqlite swift tinytds'.collect{|x| x.to_sym}
|
10
10
|
|
11
11
|
@single_threaded = false
|
12
12
|
|
@@ -171,10 +171,17 @@ module Sequel
|
|
171
171
|
# :engine :: The table engine to use for the table.
|
172
172
|
#
|
173
173
|
# PostgreSQL specific options:
|
174
|
-
# :on_commit :: Either :preserve_rows (default), :drop or :delete_rows.
|
175
|
-
#
|
176
|
-
# :
|
174
|
+
# :on_commit :: Either :preserve_rows (default), :drop or :delete_rows. Should
|
175
|
+
# only be specified when creating a temporary table.
|
176
|
+
# :foreign :: Create a foreign table. The value should be the name of the
|
177
|
+
# foreign server that was specified in CREATE SERVER.
|
178
|
+
# :inherits :: Inherit from a different table. An array can be
|
177
179
|
# specified to inherit from multiple tables.
|
180
|
+
# :unlogged :: Create the table as an unlogged table.
|
181
|
+
# :options :: The OPTIONS clause to use for foreign tables. Should be a hash
|
182
|
+
# where keys are option names and values are option values. Note
|
183
|
+
# that option names are unquoted, so you should not use untrusted
|
184
|
+
# keys.
|
178
185
|
#
|
179
186
|
# See <tt>Schema::Generator</tt> and the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
|
180
187
|
def create_table(name, options=OPTS, &block)
|
@@ -125,6 +125,13 @@ module Sequel
|
|
125
125
|
@arity = arity
|
126
126
|
end
|
127
127
|
|
128
|
+
# Return a new PlaceholderLiteralizer with a modified dataset. This yields the
|
129
|
+
# receiver's dataset to the block, and the block should return the new dataset
|
130
|
+
# to use.
|
131
|
+
def with_dataset
|
132
|
+
dup.instance_exec{@dataset = yield @dataset; self}
|
133
|
+
end
|
134
|
+
|
128
135
|
# Return an array of all objects by running the SQL query for the given arguments.
|
129
136
|
# If a block is given, yields all objects to the block after loading them.
|
130
137
|
def all(*args, &block)
|
@@ -62,6 +62,7 @@ module Sequel
|
|
62
62
|
DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}").freeze}).freeze
|
63
63
|
ACCESS_DURATION_UNITS = DURATION_UNITS.zip(%w'yyyy m d h n s'.map{|s| s.freeze}).freeze
|
64
64
|
DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s).freeze}).freeze
|
65
|
+
FDBSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.chop).freeze}).freeze
|
65
66
|
|
66
67
|
# Append the SQL fragment for the DateAdd expression to the SQL query.
|
67
68
|
def date_add_sql_append(sql, da)
|
@@ -122,6 +123,10 @@ module Sequel
|
|
122
123
|
expr = Sequel.+(expr, Sequel.lit(["", " "], value, sql_unit))
|
123
124
|
end
|
124
125
|
false
|
126
|
+
when :fdbsql
|
127
|
+
each_valid_interval_unit(h, FDBSQL_DURATION_UNITS) do |value, sql_unit|
|
128
|
+
expr = Sequel.+(expr, Sequel.lit(["INTERVAL ", " "], value, sql_unit))
|
129
|
+
end
|
125
130
|
else
|
126
131
|
raise Error, "date arithmetic is not implemented on #{db.database_type}"
|
127
132
|
end
|
@@ -207,8 +207,8 @@ module Sequel
|
|
207
207
|
@actions << [:drop_table, args.first]
|
208
208
|
end
|
209
209
|
|
210
|
-
def create_view(
|
211
|
-
@actions << [:drop_view,
|
210
|
+
def create_view(name, _, options={})
|
211
|
+
@actions << [:drop_view, name, options]
|
212
212
|
end
|
213
213
|
|
214
214
|
def rename_column(table, name, new_name)
|
@@ -203,7 +203,7 @@ module Sequel
|
|
203
203
|
procs = conversion_procs
|
204
204
|
procs[1115] = Creator.new("timestamp without time zone", procs[1114])
|
205
205
|
procs[1185] = Creator.new("timestamp with time zone", procs[1184])
|
206
|
-
copy_conversion_procs([
|
206
|
+
copy_conversion_procs([143, 791, 1000, 1001, 1003, 1005, 1006, 1007, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1021, 1022, 1028, 1182, 1183, 1231, 1270, 1561, 1563, 2951])
|
207
207
|
[:string_array, :integer_array, :decimal_array, :float_array, :boolean_array, :blob_array, :date_array, :time_array, :datetime_array].each do |v|
|
208
208
|
@schema_type_classes[v] = PGArray
|
209
209
|
end
|
@@ -542,6 +542,20 @@ module Sequel
|
|
542
542
|
register('real', :oid=>1021, :scalar_oid=>700, :scalar_typecast=>:float)
|
543
543
|
register('character', :oid=>1014, :array_type=>:text, :scalar_typecast=>:string)
|
544
544
|
register('character varying', :oid=>1015, :scalar_typecast=>:string, :type_symbol=>:varchar)
|
545
|
+
|
546
|
+
register('xml', :oid=>143, :scalar_oid=>142)
|
547
|
+
register('money', :oid=>791, :scalar_oid=>790)
|
548
|
+
register('bit', :oid=>1561, :scalar_oid=>1560)
|
549
|
+
register('bit varying', :oid=>1563, :scalar_oid=>1562, :type_symbol=>:varbit)
|
550
|
+
register('uuid', :oid=>2951, :scalar_oid=>2950)
|
551
|
+
|
552
|
+
register('xid', :oid=>1011, :scalar_oid=>28)
|
553
|
+
register('cid', :oid=>1012, :scalar_oid=>29)
|
554
|
+
|
555
|
+
register('name', :oid=>1003, :scalar_oid=>19)
|
556
|
+
register('tid', :oid=>1010, :scalar_oid=>27)
|
557
|
+
register('int2vector', :oid=>1006, :scalar_oid=>22)
|
558
|
+
register('oidvector', :oid=>1013, :scalar_oid=>30)
|
545
559
|
end
|
546
560
|
end
|
547
561
|
|
@@ -56,6 +56,9 @@
|
|
56
56
|
#
|
57
57
|
# DB.extension :pg_array, :pg_json
|
58
58
|
#
|
59
|
+
# Note that when accessing json hashes, you should always use strings for keys.
|
60
|
+
# Attempting to use other values (such as symbols) will not work correctly.
|
61
|
+
#
|
59
62
|
# This extension requires both the json and delegate libraries.
|
60
63
|
|
61
64
|
require 'delegate'
|
@@ -188,16 +188,16 @@ module Sequel
|
|
188
188
|
# structure of the record using #as on the resulting object:
|
189
189
|
#
|
190
190
|
# json_op.to_record.as(:x, [Sequel.lit('a integer'), Sequel.lit('b text')]) # json_to_record(json) AS x(a integer, b text)
|
191
|
-
def to_record
|
192
|
-
function(:to_record
|
191
|
+
def to_record
|
192
|
+
function(:to_record)
|
193
193
|
end
|
194
194
|
|
195
195
|
# Builds arbitrary set of records from json array of objects. You need to define the
|
196
196
|
# structure of the records using #as on the resulting object:
|
197
197
|
#
|
198
198
|
# json_op.to_recordset.as(:x, [Sequel.lit('a integer'), Sequel.lit('b text')]) # json_to_recordset(json) AS x(a integer, b text)
|
199
|
-
def to_recordset
|
200
|
-
function(:to_recordset
|
199
|
+
def to_recordset
|
200
|
+
function(:to_recordset)
|
201
201
|
end
|
202
202
|
|
203
203
|
# Returns the type of the outermost json value as text.
|
@@ -171,7 +171,15 @@ END_MIG
|
|
171
171
|
gen.primary_key(name, type_hash)
|
172
172
|
end
|
173
173
|
else
|
174
|
-
col_opts = options[:same_db]
|
174
|
+
col_opts = if options[:same_db]
|
175
|
+
h = {:type=>schema[:db_type]}
|
176
|
+
if database_type == :mysql && h[:type] =~ /\Atimestamp/
|
177
|
+
h[:null] = true
|
178
|
+
end
|
179
|
+
h
|
180
|
+
else
|
181
|
+
column_schema_to_ruby_type(schema)
|
182
|
+
end
|
175
183
|
type = col_opts.delete(:type)
|
176
184
|
col_opts.delete(:size) if col_opts[:size].nil?
|
177
185
|
col_opts[:default] = if schema[:ruby_default].nil?
|
@@ -8,8 +8,11 @@ module Sequel
|
|
8
8
|
|
9
9
|
# Set an empty association reflection hash in the model
|
10
10
|
def self.apply(model)
|
11
|
-
model.
|
12
|
-
|
11
|
+
model.instance_eval do
|
12
|
+
@association_reflections = {}
|
13
|
+
@autoreloading_associations = {}
|
14
|
+
@cache_associations = true
|
15
|
+
end
|
13
16
|
end
|
14
17
|
|
15
18
|
# AssociationReflection is a Hash subclass that keeps information on Sequel::Model associations. It
|
@@ -244,7 +247,6 @@ module Sequel
|
|
244
247
|
if eo[:eager_block] || eo[:loader] == false
|
245
248
|
strategy = true_eager_graph_limit_strategy if strategy == :union
|
246
249
|
objects = apply_eager_limit_strategy(eager_loading_dataset(eo), strategy).all
|
247
|
-
cascade = nil
|
248
250
|
elsif strategy == :union
|
249
251
|
objects = []
|
250
252
|
ds = associated_dataset
|
@@ -253,12 +255,12 @@ module Sequel
|
|
253
255
|
eo[:id_map].keys.each_slice(subqueries_per_union).each do |slice|
|
254
256
|
objects.concat(ds.with_sql(slice.map{|k| loader.sql(*k)}.join(joiner)).to_a)
|
255
257
|
end
|
258
|
+
ds = ds.eager(cascade) if cascade
|
259
|
+
ds.send(:post_load, objects)
|
256
260
|
else
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
if cascade && !(cascade = associated_dataset.send(:eager_options_for_associations, [cascade])).empty?
|
261
|
-
associated_eager_dataset.send(:eager_load, objects, cascade)
|
261
|
+
loader = placeholder_eager_loader
|
262
|
+
loader = loader.with_dataset{|ds| ds.eager(cascade)} if cascade
|
263
|
+
objects = loader.all(eo[:id_map].keys)
|
262
264
|
end
|
263
265
|
|
264
266
|
objects.each(&block)
|
@@ -397,7 +399,7 @@ module Sequel
|
|
397
399
|
end
|
398
400
|
|
399
401
|
if possible_recips.length == 1
|
400
|
-
cached_set(:reciprocal_type, possible_recips.first[:type]) if
|
402
|
+
cached_set(:reciprocal_type, possible_recips.first[:type]) if ambiguous_reciprocal_type?
|
401
403
|
possible_recips.first[:name]
|
402
404
|
end
|
403
405
|
end
|
@@ -468,7 +470,7 @@ module Sequel
|
|
468
470
|
# in a special sub-hash that always uses this method to synchronize access.
|
469
471
|
def cached_fetch(key)
|
470
472
|
fetch(key) do
|
471
|
-
h = self[:cache]
|
473
|
+
return yield unless h = self[:cache]
|
472
474
|
Sequel.synchronize{return h[key] if h.has_key?(key)}
|
473
475
|
value = yield
|
474
476
|
Sequel.synchronize{h[key] = value}
|
@@ -477,7 +479,7 @@ module Sequel
|
|
477
479
|
|
478
480
|
# Cache the value at the given key, synchronizing access.
|
479
481
|
def cached_set(key, value)
|
480
|
-
h = self[:cache]
|
482
|
+
return unless h = self[:cache]
|
481
483
|
Sequel.synchronize{h[key] = value}
|
482
484
|
end
|
483
485
|
# :nocov:
|
@@ -485,7 +487,7 @@ module Sequel
|
|
485
487
|
# On MRI, use a plain fetch, since the GVL will synchronize access.
|
486
488
|
def cached_fetch(key)
|
487
489
|
fetch(key) do
|
488
|
-
h = self[:cache]
|
490
|
+
return yield unless h = self[:cache]
|
489
491
|
h.fetch(key){h[key] = yield}
|
490
492
|
end
|
491
493
|
end
|
@@ -493,7 +495,8 @@ module Sequel
|
|
493
495
|
# On MRI, just set the value at the key in the cache, since the GVL
|
494
496
|
# will synchronize access.
|
495
497
|
def cached_set(key, value)
|
496
|
-
self[:cache]
|
498
|
+
return unless h = self[:cache]
|
499
|
+
h[key] = value
|
497
500
|
end
|
498
501
|
end
|
499
502
|
|
@@ -503,6 +506,12 @@ module Sequel
|
|
503
506
|
associated_class.dataset.clone
|
504
507
|
end
|
505
508
|
|
509
|
+
# Whether for the reciprocal type for the given association can not be
|
510
|
+
# known in advantage, false by default.
|
511
|
+
def ambiguous_reciprocal_type?
|
512
|
+
false
|
513
|
+
end
|
514
|
+
|
506
515
|
# Apply a limit strategy to the given dataset so that filter by
|
507
516
|
# associations works with a limited dataset.
|
508
517
|
def apply_filter_by_associations_limit_strategy(ds)
|
@@ -645,11 +654,17 @@ module Sequel
|
|
645
654
|
end
|
646
655
|
end
|
647
656
|
|
657
|
+
# The reciprocal type as an array, should be overridden in reflection subclasses that
|
658
|
+
# have ambiguous reciprocal types.
|
659
|
+
def possible_reciprocal_types
|
660
|
+
[reciprocal_type]
|
661
|
+
end
|
662
|
+
|
648
663
|
# Whether the given association reflection is possible reciprocal
|
649
664
|
# association for the current association reflection.
|
650
665
|
def reciprocal_association?(assoc_reflect)
|
651
|
-
|
652
|
-
assoc_reflect.associated_class == self[:model] &&
|
666
|
+
possible_reciprocal_types.include?(assoc_reflect[:type]) &&
|
667
|
+
(begin; assoc_reflect.associated_class; rescue NameError; end) == self[:model] &&
|
653
668
|
assoc_reflect[:conditions].nil? &&
|
654
669
|
assoc_reflect[:block].nil?
|
655
670
|
end
|
@@ -759,7 +774,7 @@ module Sequel
|
|
759
774
|
|
760
775
|
# The column(s) in the associated table that the key in the current table references (either a symbol or an array).
|
761
776
|
def primary_key
|
762
|
-
cached_fetch(:primary_key){associated_class.primary_key}
|
777
|
+
cached_fetch(:primary_key){associated_class.primary_key || raise(Error, "no primary key specified for #{associated_class.inspect}")}
|
763
778
|
end
|
764
779
|
|
765
780
|
# The columns in the associated table that the key in the current table references (always an array).
|
@@ -804,6 +819,12 @@ module Sequel
|
|
804
819
|
|
805
820
|
private
|
806
821
|
|
822
|
+
# Reciprocals of many_to_one associations could be either one_to_many or one_to_one,
|
823
|
+
# and which is not known in advance.
|
824
|
+
def ambiguous_reciprocal_type?
|
825
|
+
true
|
826
|
+
end
|
827
|
+
|
807
828
|
def filter_by_associations_conditions_associated_keys
|
808
829
|
qualify(associated_class.table_name, primary_keys)
|
809
830
|
end
|
@@ -822,14 +843,36 @@ module Sequel
|
|
822
843
|
self[:keys]
|
823
844
|
end
|
824
845
|
|
846
|
+
# The reciprocal type of a many_to_one association is either
|
847
|
+
# a one_to_many or a one_to_one association.
|
848
|
+
def possible_reciprocal_types
|
849
|
+
[:one_to_many, :one_to_one]
|
850
|
+
end
|
851
|
+
|
852
|
+
# Whether the given association reflection is possible reciprocal
|
825
853
|
def reciprocal_association?(assoc_reflect)
|
826
854
|
super && self[:keys] == assoc_reflect[:keys] && primary_key == assoc_reflect.primary_key
|
827
855
|
end
|
828
856
|
|
829
857
|
# The reciprocal type of a many_to_one association is either
|
830
|
-
# a one_to_many or a one_to_one association
|
858
|
+
# a one_to_many or a one_to_one association, look in the associated class
|
859
|
+
# to try to figure out which.
|
831
860
|
def reciprocal_type
|
832
|
-
cached_fetch(:reciprocal_type)
|
861
|
+
cached_fetch(:reciprocal_type) do
|
862
|
+
possible_recips = []
|
863
|
+
|
864
|
+
associated_class.all_association_reflections.each do |assoc_reflect|
|
865
|
+
if reciprocal_association?(assoc_reflect)
|
866
|
+
possible_recips << assoc_reflect
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
if possible_recips.length == 1
|
871
|
+
possible_recips.first[:type]
|
872
|
+
else
|
873
|
+
possible_reciprocal_types
|
874
|
+
end
|
875
|
+
end
|
833
876
|
end
|
834
877
|
end
|
835
878
|
|
@@ -1184,7 +1227,7 @@ module Sequel
|
|
1184
1227
|
|
1185
1228
|
# The primary key column(s) to use in the associated table (can be symbol or array).
|
1186
1229
|
def right_primary_key
|
1187
|
-
cached_fetch(:right_primary_key){associated_class.primary_key}
|
1230
|
+
cached_fetch(:right_primary_key){associated_class.primary_key || raise(Error, "no primary key specified for #{associated_class.inspect}")}
|
1188
1231
|
end
|
1189
1232
|
|
1190
1233
|
# The primary key columns to use in the associated table (always array).
|
@@ -1367,6 +1410,12 @@ module Sequel
|
|
1367
1410
|
# value changes.
|
1368
1411
|
attr_reader :autoreloading_associations
|
1369
1412
|
|
1413
|
+
# Whether association metadata should be cached in the association reflection. If not cached, it will be computed
|
1414
|
+
# on demand. In general you only want to set this to default when using code reloading. When using code reloading,
|
1415
|
+
# setting this will make sure that if an associated class is removed or modified, this class will not hang on to
|
1416
|
+
# the previous class.
|
1417
|
+
attr_accessor :cache_associations
|
1418
|
+
|
1370
1419
|
# The default :eager_limit_strategy option to use for limited or offset associations (default: true, causing Sequel
|
1371
1420
|
# to use what it considers the most appropriate strategy).
|
1372
1421
|
attr_accessor :default_eager_limit_strategy
|
@@ -1600,7 +1649,7 @@ module Sequel
|
|
1600
1649
|
orig_opts = cloned_assoc[:orig_opts].merge(orig_opts)
|
1601
1650
|
end
|
1602
1651
|
|
1603
|
-
opts = orig_opts.merge(:type => type, :name => name, :cache=>{}, :model => self)
|
1652
|
+
opts = orig_opts.merge(:type => type, :name => name, :cache=>({} if cache_associations), :model => self)
|
1604
1653
|
opts[:block] = block if block
|
1605
1654
|
if block || orig_opts[:block] || orig_opts[:dataset]
|
1606
1655
|
# It's possible the association is instance specific, in that it depends on
|
@@ -1683,7 +1732,7 @@ module Sequel
|
|
1683
1732
|
associate(:one_to_one, name, opts, &block)
|
1684
1733
|
end
|
1685
1734
|
|
1686
|
-
Plugins.inherited_instance_variables(self, :@association_reflections=>:dup, :@autoreloading_associations=>:hash_dup, :@default_eager_limit_strategy=>nil)
|
1735
|
+
Plugins.inherited_instance_variables(self, :@association_reflections=>:dup, :@autoreloading_associations=>:hash_dup, :@cache_associations=>nil, :@default_eager_limit_strategy=>nil)
|
1687
1736
|
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])
|
1688
1737
|
|
1689
1738
|
private
|
@@ -4,7 +4,7 @@ module Sequel
|
|
4
4
|
# The ActiveModel plugin makes Sequel::Model objects
|
5
5
|
# pass the ActiveModel::Lint tests, which should
|
6
6
|
# hopefully mean full ActiveModel compliance. This should
|
7
|
-
# allow the full support of Sequel::Model objects in Rails 3
|
7
|
+
# allow the full support of Sequel::Model objects in Rails 3+.
|
8
8
|
# This plugin requires active_model in order to use
|
9
9
|
# ActiveModel::Naming.
|
10
10
|
#
|
@@ -43,7 +43,12 @@ module Sequel
|
|
43
43
|
super
|
44
44
|
@destroyed = true
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
|
+
# Return ::ActiveModel::Name instance for the class.
|
48
|
+
def model_name
|
49
|
+
model.model_name
|
50
|
+
end
|
51
|
+
|
47
52
|
# False if the object is new? or has been destroyed, true otherwise.
|
48
53
|
def persisted?
|
49
54
|
!new? && @destroyed != true
|
@@ -229,6 +229,7 @@ module Sequel
|
|
229
229
|
opts[:left_keys] = Array(left_key)
|
230
230
|
opts[:uses_left_composite_keys] = left_key.is_a?(Array)
|
231
231
|
left_pk = (opts[:left_primary_key] ||= self.primary_key)
|
232
|
+
raise(Error, "no primary key specified for #{inspect}") unless left_pk
|
232
233
|
opts[:eager_loader_key] = left_pk unless opts.has_key?(:eager_loader_key)
|
233
234
|
opts[:left_primary_keys] = Array(left_pk)
|
234
235
|
lpkc = opts[:left_primary_key_column] ||= left_pk
|