sequel 3.8.0 → 3.9.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.
- data/CHANGELOG +48 -0
- data/Rakefile +6 -28
- data/bin/sequel +7 -2
- data/doc/release_notes/3.9.0.txt +233 -0
- data/lib/sequel/adapters/ado.rb +4 -8
- data/lib/sequel/adapters/amalgalite.rb +1 -1
- data/lib/sequel/adapters/dbi.rb +3 -3
- data/lib/sequel/adapters/do.rb +7 -13
- data/lib/sequel/adapters/jdbc.rb +10 -16
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/mysql.rb +10 -23
- data/lib/sequel/adapters/odbc.rb +6 -10
- data/lib/sequel/adapters/postgres.rb +0 -5
- data/lib/sequel/adapters/shared/mssql.rb +17 -9
- data/lib/sequel/adapters/shared/mysql.rb +16 -7
- data/lib/sequel/adapters/shared/sqlite.rb +5 -0
- data/lib/sequel/adapters/sqlite.rb +2 -1
- data/lib/sequel/connection_pool.rb +67 -349
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +46 -15
- data/lib/sequel/database.rb +11 -9
- data/lib/sequel/dataset/convenience.rb +23 -0
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/query.rb +9 -5
- data/lib/sequel/dataset/sql.rb +87 -12
- data/lib/sequel/extensions/inflector.rb +8 -1
- data/lib/sequel/extensions/schema_dumper.rb +3 -4
- data/lib/sequel/model/associations.rb +5 -43
- data/lib/sequel/model/base.rb +9 -2
- data/lib/sequel/model/default_inflections.rb +1 -1
- data/lib/sequel/model/exceptions.rb +11 -1
- data/lib/sequel/model/inflections.rb +8 -1
- data/lib/sequel/model/plugins.rb +2 -12
- data/lib/sequel/plugins/active_model.rb +5 -0
- data/lib/sequel/plugins/association_dependencies.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/optimistic_locking.rb +65 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +14 -3
- data/lib/sequel/plugins/validation_helpers.rb +2 -2
- data/lib/sequel/sql.rb +2 -2
- data/lib/sequel/timezones.rb +2 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +19 -0
- data/spec/adapters/mysql_spec.rb +4 -0
- data/spec/adapters/postgres_spec.rb +180 -0
- data/spec/adapters/spec_helper.rb +15 -1
- data/spec/core/connection_pool_spec.rb +119 -78
- data/spec/core/database_spec.rb +41 -50
- data/spec/core/dataset_spec.rb +115 -4
- data/spec/extensions/active_model_spec.rb +40 -34
- data/spec/extensions/boolean_readers_spec.rb +1 -1
- data/spec/extensions/migration_spec.rb +43 -38
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/schema_dumper_spec.rb +4 -4
- data/spec/extensions/single_table_inheritance_spec.rb +19 -11
- data/spec/integration/dataset_test.rb +44 -1
- data/spec/integration/plugin_test.rb +39 -0
- data/spec/integration/prepared_statement_test.rb +58 -7
- data/spec/integration/spec_helper.rb +4 -0
- data/spec/model/eager_loading_spec.rb +24 -0
- data/spec/model/validations_spec.rb +5 -1
- metadata +114 -106
data/lib/sequel/model/base.rb
CHANGED
@@ -720,7 +720,7 @@ module Sequel
|
|
720
720
|
def save(*columns)
|
721
721
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
722
722
|
if opts[:validate] != false and !valid?
|
723
|
-
raise(ValidationFailed
|
723
|
+
raise(ValidationFailed.new(errors)) if raise_on_save_failure
|
724
724
|
return
|
725
725
|
end
|
726
726
|
checked_save_failure{checked_transaction(opts){_save(columns, opts)}}
|
@@ -811,11 +811,18 @@ module Sequel
|
|
811
811
|
# allow running inside a transaction
|
812
812
|
def _destroy(opts)
|
813
813
|
return save_failure(:destroy) if before_destroy == false
|
814
|
-
|
814
|
+
_destroy_delete
|
815
815
|
after_destroy
|
816
816
|
self
|
817
817
|
end
|
818
818
|
|
819
|
+
# Internal delete method to call when destroying an object,
|
820
|
+
# separated from delete to allow you to override destroy's version
|
821
|
+
# without affecting delete.
|
822
|
+
def _destroy_delete
|
823
|
+
delete
|
824
|
+
end
|
825
|
+
|
819
826
|
def _insert
|
820
827
|
ds = model.dataset
|
821
828
|
if ds.respond_to?(:insert_select) and h = ds.insert_select(@values)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Sequel
|
2
2
|
# Proc that is instance evaled to create the default inflections for both the
|
3
3
|
# model inflector and the inflector extension.
|
4
|
-
DEFAULT_INFLECTIONS_PROC =
|
4
|
+
DEFAULT_INFLECTIONS_PROC = proc do
|
5
5
|
plural(/$/, 's')
|
6
6
|
plural(/s$/i, 's')
|
7
7
|
plural(/(alias|(?:stat|octop|vir|b)us)$/i, '\1es')
|
@@ -1,6 +1,16 @@
|
|
1
1
|
module Sequel
|
2
2
|
# This exception will be raised when raise_on_save_failure is set and validation fails
|
3
|
-
class ValidationFailed < Error
|
3
|
+
class ValidationFailed < Error
|
4
|
+
def initialize(errors)
|
5
|
+
if errors.respond_to?(:full_messages)
|
6
|
+
@errors = errors
|
7
|
+
super(errors.full_messages.join(', '))
|
8
|
+
else
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
attr_reader :errors
|
13
|
+
end
|
4
14
|
|
5
15
|
# This exception will be raised when raise_on_save_failure is set and a before hook returns false
|
6
16
|
class BeforeHookFailed < Error; end
|
@@ -39,7 +39,14 @@ module Sequel
|
|
39
39
|
@plurals, @singulars, @uncountables = [], [], []
|
40
40
|
|
41
41
|
class << self
|
42
|
-
|
42
|
+
# Array of 2 element arrays, first containing a regex, and the second containing a substitution pattern, used for plurization.
|
43
|
+
attr_reader :plurals
|
44
|
+
|
45
|
+
# Array of 2 element arrays, first containing a regex, and the second containing a substitution pattern, used for singularization.
|
46
|
+
attr_reader :singulars
|
47
|
+
|
48
|
+
# Array of strings for words were the singular form is the same as the plural form
|
49
|
+
attr_reader :uncountables
|
43
50
|
end
|
44
51
|
|
45
52
|
# Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
|
data/lib/sequel/model/plugins.rb
CHANGED
@@ -60,25 +60,15 @@ module Sequel
|
|
60
60
|
|
61
61
|
private
|
62
62
|
|
63
|
-
# Returns the new style location for the plugin name.
|
64
|
-
def plugin_gem_location(plugin)
|
65
|
-
"sequel/plugins/#{plugin}"
|
66
|
-
end
|
67
|
-
|
68
|
-
# Returns the old style location for the plugin name.
|
69
|
-
def plugin_gem_location_old(plugin)
|
70
|
-
"sequel_#{plugin}"
|
71
|
-
end
|
72
|
-
|
73
63
|
# Returns the module for the specified plugin. If the module is not
|
74
64
|
# defined, the corresponding plugin gem is automatically loaded.
|
75
65
|
def plugin_module(plugin)
|
76
66
|
module_name = plugin.to_s.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
|
77
67
|
if not Sequel::Plugins.const_defined?(module_name)
|
78
68
|
begin
|
79
|
-
|
69
|
+
Sequel.tsk_require "sequel/plugins/#{plugin}"
|
80
70
|
rescue LoadError
|
81
|
-
|
71
|
+
Sequel.tsk_require "sequel_#{plugin}"
|
82
72
|
end
|
83
73
|
end
|
84
74
|
Sequel::Plugins.const_get(module_name)
|
@@ -1,10 +1,15 @@
|
|
1
|
+
require 'active_model'
|
1
2
|
module Sequel
|
2
3
|
module Plugins
|
3
4
|
# The ActiveModel plugin makes Sequel::Model objects the
|
4
5
|
# pass the ActiveModel::Lint tests, which should
|
5
6
|
# hopefully mean full ActiveModel compliance. This should
|
6
7
|
# allow the full support of Sequel::Model objects in Rails 3.
|
8
|
+
# This plugin requires active_model in order to use
|
9
|
+
# ActiveModel::Naming.
|
7
10
|
module ActiveModel
|
11
|
+
ClassMethods = ::ActiveModel::Naming
|
12
|
+
|
8
13
|
module InstanceMethods
|
9
14
|
# Record that an object was destroyed, for later use by
|
10
15
|
# destroyed?
|
@@ -16,7 +16,7 @@ module Sequel
|
|
16
16
|
# and dependency action values. You can provide the hash to the plugin call itself or
|
17
17
|
# to the add_association_dependencies method:
|
18
18
|
#
|
19
|
-
# Business.plugin :association_dependencies, :address
|
19
|
+
# Business.plugin :association_dependencies, :address=>:delete
|
20
20
|
# # or:
|
21
21
|
# Artist.plugin :association_dependencies
|
22
22
|
# Artist.add_association_dependencies :albums=>:destroy, :reviews=>:delete, :tags=>:nullify
|
@@ -193,7 +193,7 @@ module Sequel
|
|
193
193
|
opts[:eager_grapher] ||= proc do |ds, assoc_alias, table_alias|
|
194
194
|
iq = table_alias
|
195
195
|
opts.edges.each do |t|
|
196
|
-
ds = ds.graph(t[:table], t.include?(:only_conditions) ? t[:only_conditions] : (Array(t[:right]).zip(Array(t[:left])) + t[:conditions]), :select=>false, :table_alias=>ds.
|
196
|
+
ds = ds.graph(t[:table], t.include?(:only_conditions) ? t[:only_conditions] : (Array(t[:right]).zip(Array(t[:left])) + t[:conditions]), :select=>false, :table_alias=>ds.unused_table_alias(t[:table]), :join_type=>t[:join_type], :implicit_qualifier=>iq, &t[:block])
|
197
197
|
iq = nil
|
198
198
|
end
|
199
199
|
fe = opts[:final_edge]
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Plugins
|
3
|
+
# This plugin implements a simple database-independent locking mechanism
|
4
|
+
# to ensure that concurrent updates do not override changes. This is
|
5
|
+
# best implemented by a code example:
|
6
|
+
#
|
7
|
+
# class Person < Sequel::Model
|
8
|
+
# plugin :optimistic_locking
|
9
|
+
# end
|
10
|
+
# p1 = Person[1]
|
11
|
+
# p2 = Person[1]
|
12
|
+
# p1.update(:name=>'Jim') # works
|
13
|
+
# p2.update(:name=>'Bob') # raises Sequel::Plugins::OptimisticLocking::Error
|
14
|
+
#
|
15
|
+
# In order for this plugin to work, you need to make sure that the database
|
16
|
+
# table has a lock_version column (or other column you name via the lock_column
|
17
|
+
# class level accessor) that defaults to 0.
|
18
|
+
#
|
19
|
+
# This plugin does not work with the class_table_inheritance plugin.
|
20
|
+
module OptimisticLocking
|
21
|
+
# Exception class raised when trying to update or destroy a stale object.
|
22
|
+
class Error < Sequel::Error
|
23
|
+
end
|
24
|
+
|
25
|
+
# Set the lock_column to the :lock_column option, or :lock_version if
|
26
|
+
# that option is not given.
|
27
|
+
def self.configure(model, opts={})
|
28
|
+
model.lock_column = opts[:lock_column] || :lock_version
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
# The column holding the version of the lock
|
33
|
+
attr_accessor :lock_column
|
34
|
+
|
35
|
+
# Copy the lock_column value into the subclass
|
36
|
+
def inherited(subclass)
|
37
|
+
super
|
38
|
+
subclass.lock_column = lock_column
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module InstanceMethods
|
43
|
+
private
|
44
|
+
|
45
|
+
# Only delete the object when destroying if it has the same lock version. If the row
|
46
|
+
# doesn't have the same lock version, raise an error.
|
47
|
+
def _destroy_delete
|
48
|
+
lc = model.lock_column
|
49
|
+
raise(Error, "Attempt to destroy a stale object") if this.filter(lc=>send(lc)).delete != 1
|
50
|
+
end
|
51
|
+
|
52
|
+
# Only update the row if it has the same lock version, and increment the
|
53
|
+
# lock version. If the row doesn't have the same lock version, raise
|
54
|
+
# an Error.
|
55
|
+
def _update(columns)
|
56
|
+
lc = model.lock_column
|
57
|
+
lcv = send(lc)
|
58
|
+
columns[lc] = lcv + 1
|
59
|
+
raise(Error, "Attempt to update a stale object") if this.filter(lc=>lcv).update(columns) != 1
|
60
|
+
send("#{lc}=", lcv + 1)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -19,11 +19,10 @@ module Sequel
|
|
19
19
|
# dataset's row_proc so that the dataset yields objects of varying classes,
|
20
20
|
# where the class used has the same name as the key field.
|
21
21
|
def self.configure(model, key)
|
22
|
-
m = model.method(:constantize)
|
23
22
|
model.instance_eval do
|
24
23
|
@sti_key = key
|
25
24
|
@sti_dataset = dataset
|
26
|
-
dataset.row_proc = lambda{|r|
|
25
|
+
dataset.row_proc = lambda{|r| model.sti_load(r)}
|
27
26
|
end
|
28
27
|
end
|
29
28
|
|
@@ -42,13 +41,25 @@ module Sequel
|
|
42
41
|
super
|
43
42
|
sk = sti_key
|
44
43
|
sd = sti_dataset
|
45
|
-
subclass.set_dataset(sd.filter(sk=>subclass.name.to_s), :inherited=>true)
|
44
|
+
subclass.set_dataset(sd.filter(SQL::QualifiedIdentifier.new(table_name, sk)=>subclass.name.to_s), :inherited=>true)
|
46
45
|
subclass.instance_eval do
|
47
46
|
@sti_key = sk
|
48
47
|
@sti_dataset = sd
|
49
48
|
@simple_table = nil
|
50
49
|
end
|
51
50
|
end
|
51
|
+
|
52
|
+
# Return an instance of the class specified by sti_key,
|
53
|
+
# used by the row_proc.
|
54
|
+
def sti_load(r)
|
55
|
+
v = r[sti_key]
|
56
|
+
model = if (v && v != '')
|
57
|
+
constantize(v) rescue self
|
58
|
+
else
|
59
|
+
self
|
60
|
+
end
|
61
|
+
model.load(r)
|
62
|
+
end
|
52
63
|
end
|
53
64
|
|
54
65
|
module InstanceMethods
|
@@ -18,8 +18,8 @@ module Sequel
|
|
18
18
|
# attribute(s) to validate.
|
19
19
|
# Options:
|
20
20
|
# * :allow_blank - Whether to skip the validation if the value is blank. You should
|
21
|
-
# make sure all objects respond to blank if you use this option, which you can do by
|
22
|
-
#
|
21
|
+
# make sure all objects respond to blank if you use this option, which you can do by:
|
22
|
+
# Sequel.extension :blank
|
23
23
|
# * :allow_missing - Whether to skip the validation if the attribute isn't a key in the
|
24
24
|
# values hash. This is different from allow_nil, because Sequel only sends the attributes
|
25
25
|
# in the values when doing an insert or update. If the attribute is not present, Sequel
|
data/lib/sequel/sql.rb
CHANGED
@@ -360,7 +360,7 @@ module Sequel
|
|
360
360
|
# and SQL::StringExpression).
|
361
361
|
#
|
362
362
|
# This defines the like (LIKE) and ilike methods, used for pattern matching.
|
363
|
-
# like is case sensitive, ilike is case insensitive.
|
363
|
+
# like is case sensitive (if the database supports it), ilike is case insensitive.
|
364
364
|
module StringMethods
|
365
365
|
# Create a BooleanExpression case insensitive pattern match of self
|
366
366
|
# with the given patterns. See StringExpression.like.
|
@@ -368,7 +368,7 @@ module Sequel
|
|
368
368
|
StringExpression.like(self, *(ces << {:case_insensitive=>true}))
|
369
369
|
end
|
370
370
|
|
371
|
-
# Create a BooleanExpression case sensitive pattern match of self with
|
371
|
+
# Create a BooleanExpression case sensitive (if the database supports it) pattern match of self with
|
372
372
|
# the given patterns. See StringExpression.like.
|
373
373
|
def like(*ces)
|
374
374
|
StringExpression.like(self, *ces)
|
data/lib/sequel/timezones.rb
CHANGED
@@ -49,8 +49,8 @@ module Sequel
|
|
49
49
|
# same time and just modifying the timezone.
|
50
50
|
def convert_input_datetime_no_offset(v, input_timezone)
|
51
51
|
case input_timezone
|
52
|
-
when :utc
|
53
|
-
v# DateTime assumes UTC if no offset is given
|
52
|
+
when :utc, nil
|
53
|
+
v # DateTime assumes UTC if no offset is given
|
54
54
|
when :local
|
55
55
|
v.new_offset(LOCAL_DATETIME_OFFSET) - LOCAL_DATETIME_OFFSET
|
56
56
|
else
|
data/lib/sequel/version.rb
CHANGED
data/spec/adapters/mssql_spec.rb
CHANGED
@@ -352,3 +352,22 @@ context "MSSSQL::Dataset#disable_insert_output" do
|
|
352
352
|
MSSQL_DB[:test].disable_insert_output.send(:simple_select_all?).should == true
|
353
353
|
end
|
354
354
|
end
|
355
|
+
|
356
|
+
context "MSSSQL::Dataset#into" do
|
357
|
+
before do
|
358
|
+
@db = MSSQL_DB
|
359
|
+
end
|
360
|
+
|
361
|
+
specify "should format SELECT statement" do
|
362
|
+
@db[:t].into(:new).select_sql.should == "SELECT * INTO NEW FROM T"
|
363
|
+
end
|
364
|
+
|
365
|
+
specify "should select rows into a new table" do
|
366
|
+
@db.create_table!(:t) {Integer :id; String :value}
|
367
|
+
@db[:t].insert(:id => 1, :value => "test")
|
368
|
+
@db << @db[:t].into(:new).select_sql
|
369
|
+
@db[:new].all.should == [{:id => 1, :value => "test"}]
|
370
|
+
@db.drop_table(:t)
|
371
|
+
@db.drop_table(:new)
|
372
|
+
end
|
373
|
+
end
|
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -79,6 +79,10 @@ context "A MySQL database" do
|
|
79
79
|
specify "should provide the server version" do
|
80
80
|
MYSQL_DB.server_version.should >= 40000
|
81
81
|
end
|
82
|
+
|
83
|
+
specify "should handle the creation and dropping of an InnoDB table with foreign keys" do
|
84
|
+
proc{MYSQL_DB.create_table!(:test_innodb, :engine=>:InnoDB){primary_key :id; foreign_key :fk, :test_innodb, :key=>:id}}.should_not raise_error
|
85
|
+
end
|
82
86
|
end
|
83
87
|
|
84
88
|
if MYSQL_DB.class.adapter_scheme == :mysql
|
@@ -543,6 +543,186 @@ context "Postgres::Database schema qualified tables" do
|
|
543
543
|
end
|
544
544
|
end
|
545
545
|
|
546
|
+
context "Postgres::Database schema qualified tables and eager graphing" do
|
547
|
+
before(:all) do
|
548
|
+
@db = POSTGRES_DB
|
549
|
+
@db.run "DROP SCHEMA s CASCADE" rescue nil
|
550
|
+
@db.run "CREATE SCHEMA s"
|
551
|
+
@db.quote_identifiers = true
|
552
|
+
|
553
|
+
@db.create_table(:s__bands){primary_key :id; String :name}
|
554
|
+
@db.create_table(:s__albums){primary_key :id; String :name; foreign_key :band_id, :s__bands}
|
555
|
+
@db.create_table(:s__tracks){primary_key :id; String :name; foreign_key :album_id, :s__albums}
|
556
|
+
@db.create_table(:s__members){primary_key :id; String :name; foreign_key :band_id, :s__bands}
|
557
|
+
|
558
|
+
@Band = Class.new(Sequel::Model(:s__bands))
|
559
|
+
@Album = Class.new(Sequel::Model(:s__albums))
|
560
|
+
@Track = Class.new(Sequel::Model(:s__tracks))
|
561
|
+
@Member = Class.new(Sequel::Model(:s__members))
|
562
|
+
def @Band.name; :Band; end
|
563
|
+
def @Album.name; :Album; end
|
564
|
+
def @Track.name; :Track; end
|
565
|
+
def @Member.name; :Member; end
|
566
|
+
|
567
|
+
@Band.one_to_many :albums, :class=>@Album, :order=>:name
|
568
|
+
@Band.one_to_many :members, :class=>@Member, :order=>:name
|
569
|
+
@Album.many_to_one :band, :class=>@Band, :order=>:name
|
570
|
+
@Album.one_to_many :tracks, :class=>@Track, :order=>:name
|
571
|
+
@Track.many_to_one :album, :class=>@Album, :order=>:name
|
572
|
+
@Member.many_to_one :band, :class=>@Band, :order=>:name
|
573
|
+
|
574
|
+
@Member.many_to_many :members, :class=>@Member, :join_table=>:s__bands, :right_key=>:id, :left_key=>:id, :left_primary_key=>:band_id, :right_primary_key=>:band_id, :order=>:name
|
575
|
+
@Band.many_to_many :tracks, :class=>@Track, :join_table=>:s__albums, :right_key=>:id, :right_primary_key=>:album_id, :order=>:name
|
576
|
+
|
577
|
+
@b1 = @Band.create(:name=>"BM")
|
578
|
+
@b2 = @Band.create(:name=>"J")
|
579
|
+
@a1 = @Album.create(:name=>"BM1", :band=>@b1)
|
580
|
+
@a2 = @Album.create(:name=>"BM2", :band=>@b1)
|
581
|
+
@a3 = @Album.create(:name=>"GH", :band=>@b2)
|
582
|
+
@a4 = @Album.create(:name=>"GHL", :band=>@b2)
|
583
|
+
@t1 = @Track.create(:name=>"BM1-1", :album=>@a1)
|
584
|
+
@t2 = @Track.create(:name=>"BM1-2", :album=>@a1)
|
585
|
+
@t3 = @Track.create(:name=>"BM2-1", :album=>@a2)
|
586
|
+
@t4 = @Track.create(:name=>"BM2-2", :album=>@a2)
|
587
|
+
@m1 = @Member.create(:name=>"NU", :band=>@b1)
|
588
|
+
@m2 = @Member.create(:name=>"TS", :band=>@b1)
|
589
|
+
@m3 = @Member.create(:name=>"NS", :band=>@b2)
|
590
|
+
@m4 = @Member.create(:name=>"JC", :band=>@b2)
|
591
|
+
end
|
592
|
+
after(:all) do
|
593
|
+
@db.quote_identifiers = false
|
594
|
+
@db.run "DROP SCHEMA s CASCADE"
|
595
|
+
end
|
596
|
+
|
597
|
+
specify "should return all eager graphs correctly" do
|
598
|
+
bands = @Band.order(:bands__name).eager_graph(:albums).all
|
599
|
+
bands.should == [@b1, @b2]
|
600
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
601
|
+
|
602
|
+
bands = @Band.order(:bands__name).eager_graph(:albums=>:tracks).all
|
603
|
+
bands.should == [@b1, @b2]
|
604
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
605
|
+
bands.map{|x| x.albums.map{|y| y.tracks}}.should == [[[@t1, @t2], [@t3, @t4]], [[], []]]
|
606
|
+
|
607
|
+
bands = @Band.order(:bands__name).eager_graph({:albums=>:tracks}, :members).all
|
608
|
+
bands.should == [@b1, @b2]
|
609
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
610
|
+
bands.map{|x| x.albums.map{|y| y.tracks}}.should == [[[@t1, @t2], [@t3, @t4]], [[], []]]
|
611
|
+
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
612
|
+
end
|
613
|
+
|
614
|
+
specify "should have eager graphs work with previous joins" do
|
615
|
+
bands = @Band.order(:bands__name).select(:s__bands.*).join(:s__members, :band_id=>:id).from_self(:alias=>:bands0).eager_graph(:albums=>:tracks).all
|
616
|
+
bands.should == [@b1, @b2]
|
617
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
618
|
+
bands.map{|x| x.albums.map{|y| y.tracks}}.should == [[[@t1, @t2], [@t3, @t4]], [[], []]]
|
619
|
+
end
|
620
|
+
|
621
|
+
specify "should have eager graphs work with joins with the same tables" do
|
622
|
+
bands = @Band.order(:bands__name).select(:s__bands.*).join(:s__members, :band_id=>:id).eager_graph({:albums=>:tracks}, :members).all
|
623
|
+
bands.should == [@b1, @b2]
|
624
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
625
|
+
bands.map{|x| x.albums.map{|y| y.tracks}}.should == [[[@t1, @t2], [@t3, @t4]], [[], []]]
|
626
|
+
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
627
|
+
end
|
628
|
+
|
629
|
+
specify "should have eager graphs work with self referential associations" do
|
630
|
+
bands = @Band.order(:bands__name).eager_graph(:tracks=>{:album=>:band}).all
|
631
|
+
bands.should == [@b1, @b2]
|
632
|
+
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
633
|
+
bands.map{|x| x.tracks.map{|y| y.album}}.should == [[@a1, @a1, @a2, @a2], []]
|
634
|
+
bands.map{|x| x.tracks.map{|y| y.album.band}}.should == [[@b1, @b1, @b1, @b1], []]
|
635
|
+
|
636
|
+
members = @Member.order(:members__name).eager_graph(:members).all
|
637
|
+
members.should == [@m4, @m3, @m1, @m2]
|
638
|
+
members.map{|x| x.members}.should == [[@m4, @m3], [@m4, @m3], [@m1, @m2], [@m1, @m2]]
|
639
|
+
|
640
|
+
members = @Member.order(:members__name).eager_graph(:band, :members=>:band).all
|
641
|
+
members.should == [@m4, @m3, @m1, @m2]
|
642
|
+
members.map{|x| x.band}.should == [@b2, @b2, @b1, @b1]
|
643
|
+
members.map{|x| x.members}.should == [[@m4, @m3], [@m4, @m3], [@m1, @m2], [@m1, @m2]]
|
644
|
+
members.map{|x| x.members.map{|y| y.band}}.should == [[@b2, @b2], [@b2, @b2], [@b1, @b1], [@b1, @b1]]
|
645
|
+
end
|
646
|
+
|
647
|
+
specify "should have eager graphs work with a from_self dataset" do
|
648
|
+
bands = @Band.order(:bands__name).from_self.eager_graph(:tracks=>{:album=>:band}).all
|
649
|
+
bands.should == [@b1, @b2]
|
650
|
+
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
651
|
+
bands.map{|x| x.tracks.map{|y| y.album}}.should == [[@a1, @a1, @a2, @a2], []]
|
652
|
+
bands.map{|x| x.tracks.map{|y| y.album.band}}.should == [[@b1, @b1, @b1, @b1], []]
|
653
|
+
end
|
654
|
+
|
655
|
+
specify "should have eager graphs work with different types of aliased from tables" do
|
656
|
+
bands = @Band.order(:tracks__name).from(:s__bands___tracks).eager_graph(:tracks).all
|
657
|
+
bands.should == [@b1, @b2]
|
658
|
+
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
659
|
+
|
660
|
+
bands = @Band.order(:tracks__name).from(:s__bands.as(:tracks)).eager_graph(:tracks).all
|
661
|
+
bands.should == [@b1, @b2]
|
662
|
+
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
663
|
+
|
664
|
+
bands = @Band.order(:tracks__name).from(:s__bands.as(:tracks.identifier)).eager_graph(:tracks).all
|
665
|
+
bands.should == [@b1, @b2]
|
666
|
+
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
667
|
+
|
668
|
+
bands = @Band.order(:tracks__name).from(:s__bands.as('tracks')).eager_graph(:tracks).all
|
669
|
+
bands.should == [@b1, @b2]
|
670
|
+
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
671
|
+
end
|
672
|
+
|
673
|
+
specify "should have eager graphs work with join tables with aliases" do
|
674
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums___tracks, :band_id=>:id.qualify(:s__bands)).eager_graph(:albums=>:tracks).all
|
675
|
+
bands.should == [@b1, @b2]
|
676
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
677
|
+
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
678
|
+
|
679
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums.as(:tracks), :band_id=>:id.qualify(:s__bands)).eager_graph(:albums=>:tracks).all
|
680
|
+
bands.should == [@b1, @b2]
|
681
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
682
|
+
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
683
|
+
|
684
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums.as('tracks'), :band_id=>:id.qualify(:s__bands)).eager_graph(:albums=>:tracks).all
|
685
|
+
bands.should == [@b1, @b2]
|
686
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
687
|
+
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
688
|
+
|
689
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums.as(:tracks.identifier), :band_id=>:id.qualify(:s__bands)).eager_graph(:albums=>:tracks).all
|
690
|
+
bands.should == [@b1, @b2]
|
691
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
692
|
+
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
693
|
+
|
694
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id=>:id.qualify(:s__bands)}, :table_alias=>:tracks).eager_graph(:albums=>:tracks).all
|
695
|
+
bands.should == [@b1, @b2]
|
696
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
697
|
+
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
698
|
+
|
699
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id=>:id.qualify(:s__bands)}, :table_alias=>'tracks').eager_graph(:albums=>:tracks).all
|
700
|
+
bands.should == [@b1, @b2]
|
701
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
702
|
+
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
703
|
+
|
704
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id=>:id.qualify(:s__bands)}, :table_alias=>:tracks.identifier).eager_graph(:albums=>:tracks).all
|
705
|
+
bands.should == [@b1, @b2]
|
706
|
+
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
707
|
+
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
708
|
+
end
|
709
|
+
|
710
|
+
specify "should have eager graphs work with different types of qualified from tables" do
|
711
|
+
bands = @Band.order(:bands__name).from(:bands.qualify(:s)).eager_graph(:tracks).all
|
712
|
+
bands.should == [@b1, @b2]
|
713
|
+
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
714
|
+
|
715
|
+
bands = @Band.order(:bands__name).from(:bands.identifier.qualify(:s)).eager_graph(:tracks).all
|
716
|
+
bands.should == [@b1, @b2]
|
717
|
+
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
718
|
+
|
719
|
+
bands = @Band.order(:bands__name).from(Sequel::SQL::QualifiedIdentifier.new(:s, 'bands')).eager_graph(:tracks).all
|
720
|
+
bands.should == [@b1, @b2]
|
721
|
+
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
722
|
+
end
|
723
|
+
|
724
|
+
end
|
725
|
+
|
546
726
|
if POSTGRES_DB.server_version >= 80300
|
547
727
|
|
548
728
|
POSTGRES_DB.create_table! :test6 do
|