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