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