sequel 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/CHANGELOG +1551 -4
  2. data/README +306 -19
  3. data/Rakefile +84 -56
  4. data/bin/sequel +106 -0
  5. data/doc/cheat_sheet.rdoc +225 -0
  6. data/doc/dataset_filtering.rdoc +182 -0
  7. data/lib/sequel_core.rb +136 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +54 -0
  9. data/lib/sequel_core/adapters/ado.rb +80 -0
  10. data/lib/sequel_core/adapters/db2.rb +148 -0
  11. data/lib/sequel_core/adapters/dbi.rb +117 -0
  12. data/lib/sequel_core/adapters/informix.rb +78 -0
  13. data/lib/sequel_core/adapters/jdbc.rb +186 -0
  14. data/lib/sequel_core/adapters/jdbc/mysql.rb +55 -0
  15. data/lib/sequel_core/adapters/jdbc/postgresql.rb +66 -0
  16. data/lib/sequel_core/adapters/jdbc/sqlite.rb +47 -0
  17. data/lib/sequel_core/adapters/mysql.rb +231 -0
  18. data/lib/sequel_core/adapters/odbc.rb +155 -0
  19. data/lib/sequel_core/adapters/odbc_mssql.rb +106 -0
  20. data/lib/sequel_core/adapters/openbase.rb +64 -0
  21. data/lib/sequel_core/adapters/oracle.rb +170 -0
  22. data/lib/sequel_core/adapters/postgres.rb +199 -0
  23. data/lib/sequel_core/adapters/shared/mysql.rb +275 -0
  24. data/lib/sequel_core/adapters/shared/postgres.rb +351 -0
  25. data/lib/sequel_core/adapters/shared/sqlite.rb +146 -0
  26. data/lib/sequel_core/adapters/sqlite.rb +138 -0
  27. data/lib/sequel_core/connection_pool.rb +194 -0
  28. data/lib/sequel_core/core_ext.rb +203 -0
  29. data/lib/sequel_core/core_sql.rb +184 -0
  30. data/lib/sequel_core/database.rb +471 -0
  31. data/lib/sequel_core/database/schema.rb +156 -0
  32. data/lib/sequel_core/dataset.rb +457 -0
  33. data/lib/sequel_core/dataset/callback.rb +13 -0
  34. data/lib/sequel_core/dataset/convenience.rb +245 -0
  35. data/lib/sequel_core/dataset/pagination.rb +96 -0
  36. data/lib/sequel_core/dataset/query.rb +41 -0
  37. data/lib/sequel_core/dataset/schema.rb +15 -0
  38. data/lib/sequel_core/dataset/sql.rb +889 -0
  39. data/lib/sequel_core/deprecated.rb +26 -0
  40. data/lib/sequel_core/exceptions.rb +42 -0
  41. data/lib/sequel_core/migration.rb +187 -0
  42. data/lib/sequel_core/object_graph.rb +216 -0
  43. data/lib/sequel_core/pretty_table.rb +71 -0
  44. data/lib/sequel_core/schema.rb +2 -0
  45. data/lib/sequel_core/schema/generator.rb +239 -0
  46. data/lib/sequel_core/schema/sql.rb +325 -0
  47. data/lib/sequel_core/sql.rb +812 -0
  48. data/lib/sequel_model.rb +5 -1
  49. data/lib/sequel_model/association_reflection.rb +3 -8
  50. data/lib/sequel_model/base.rb +15 -10
  51. data/lib/sequel_model/inflector.rb +3 -5
  52. data/lib/sequel_model/plugins.rb +1 -1
  53. data/lib/sequel_model/record.rb +11 -3
  54. data/lib/sequel_model/schema.rb +4 -4
  55. data/lib/sequel_model/validations.rb +6 -1
  56. data/spec/adapters/ado_spec.rb +17 -0
  57. data/spec/adapters/informix_spec.rb +96 -0
  58. data/spec/adapters/mysql_spec.rb +764 -0
  59. data/spec/adapters/oracle_spec.rb +222 -0
  60. data/spec/adapters/postgres_spec.rb +441 -0
  61. data/spec/adapters/spec_helper.rb +7 -0
  62. data/spec/adapters/sqlite_spec.rb +400 -0
  63. data/spec/integration/dataset_test.rb +51 -0
  64. data/spec/integration/eager_loader_test.rb +702 -0
  65. data/spec/integration/schema_test.rb +102 -0
  66. data/spec/integration/spec_helper.rb +44 -0
  67. data/spec/integration/type_test.rb +43 -0
  68. data/spec/rcov.opts +2 -0
  69. data/spec/sequel_core/connection_pool_spec.rb +363 -0
  70. data/spec/sequel_core/core_ext_spec.rb +156 -0
  71. data/spec/sequel_core/core_sql_spec.rb +427 -0
  72. data/spec/sequel_core/database_spec.rb +964 -0
  73. data/spec/sequel_core/dataset_spec.rb +2977 -0
  74. data/spec/sequel_core/expression_filters_spec.rb +346 -0
  75. data/spec/sequel_core/migration_spec.rb +261 -0
  76. data/spec/sequel_core/object_graph_spec.rb +234 -0
  77. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  78. data/spec/sequel_core/schema_generator_spec.rb +122 -0
  79. data/spec/sequel_core/schema_spec.rb +497 -0
  80. data/spec/sequel_core/spec_helper.rb +51 -0
  81. data/spec/{association_reflection_spec.rb → sequel_model/association_reflection_spec.rb} +6 -6
  82. data/spec/{associations_spec.rb → sequel_model/associations_spec.rb} +47 -18
  83. data/spec/{base_spec.rb → sequel_model/base_spec.rb} +2 -1
  84. data/spec/{caching_spec.rb → sequel_model/caching_spec.rb} +0 -0
  85. data/spec/{dataset_methods_spec.rb → sequel_model/dataset_methods_spec.rb} +13 -1
  86. data/spec/{eager_loading_spec.rb → sequel_model/eager_loading_spec.rb} +75 -14
  87. data/spec/{hooks_spec.rb → sequel_model/hooks_spec.rb} +4 -4
  88. data/spec/sequel_model/inflector_spec.rb +119 -0
  89. data/spec/{model_spec.rb → sequel_model/model_spec.rb} +30 -11
  90. data/spec/{plugins_spec.rb → sequel_model/plugins_spec.rb} +0 -0
  91. data/spec/{record_spec.rb → sequel_model/record_spec.rb} +47 -6
  92. data/spec/{schema_spec.rb → sequel_model/schema_spec.rb} +18 -4
  93. data/spec/{spec_helper.rb → sequel_model/spec_helper.rb} +3 -2
  94. data/spec/{validations_spec.rb → sequel_model/validations_spec.rb} +37 -17
  95. data/spec/spec_config.rb +9 -0
  96. data/spec/spec_config.rb.example +10 -0
  97. metadata +110 -37
  98. data/spec/inflector_spec.rb +0 -34
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ unless Object.const_defined?('Sequel')
3
+ $:.unshift(File.join(File.dirname(__FILE__), "../../lib/"))
4
+ require 'sequel_core'
5
+ end
6
+
7
+ class MockDataset < Sequel::Dataset
8
+ def insert(*args)
9
+ @db.execute insert_sql(*args)
10
+ end
11
+
12
+ def update(*args)
13
+ @db.execute update_sql(*args)
14
+ end
15
+
16
+ def fetch_rows(sql)
17
+ @db.execute(sql)
18
+ yield({:id => 1, :x => 1})
19
+ end
20
+
21
+ def quoted_identifier(c)
22
+ "\"#{c}\""
23
+ end
24
+ end
25
+
26
+ class MockDatabase < Sequel::Database
27
+ @@quote_identifiers = false
28
+ attr_reader :sqls
29
+
30
+ def execute(sql)
31
+ @sqls ||= []
32
+ @sqls << sql
33
+ end
34
+
35
+ def reset
36
+ @sqls = []
37
+ end
38
+
39
+ def transaction; yield; end
40
+
41
+ def dataset; MockDataset.new(self); end
42
+ end
43
+
44
+ class SchemaDummyDatabase < Sequel::Database
45
+ attr_reader :sqls
46
+
47
+ def execute(sql)
48
+ @sqls ||= []
49
+ @sqls << sql
50
+ end
51
+ end
@@ -3,7 +3,7 @@ require File.join(File.dirname(__FILE__), "spec_helper")
3
3
  describe Sequel::Model::Associations::AssociationReflection, "#associated_class" do
4
4
  before do
5
5
  @c = Class.new(Sequel::Model)
6
- class ParParent < Sequel::Model; end
6
+ class ::ParParent < Sequel::Model; end
7
7
  end
8
8
 
9
9
  it "should use the :class value if present" do
@@ -21,7 +21,7 @@ end
21
21
  describe Sequel::Model::Associations::AssociationReflection, "#associated_primary_key" do
22
22
  before do
23
23
  @c = Class.new(Sequel::Model)
24
- class ParParent < Sequel::Model; end
24
+ class ::ParParent < Sequel::Model; end
25
25
  end
26
26
 
27
27
  it "should use the :right_primary_key value if present" do
@@ -46,9 +46,9 @@ describe Sequel::Model::Associations::AssociationReflection, "#reciprocal" do
46
46
  end
47
47
 
48
48
  it "should figure out the reciprocal if the :reciprocal value is not present" do
49
- class ParParent < Sequel::Model; end
50
- class ParParentTwo < Sequel::Model; end
51
- class ParParentThree < Sequel::Model; end
49
+ class ::ParParent < Sequel::Model; end
50
+ class ::ParParentTwo < Sequel::Model; end
51
+ class ::ParParentThree < Sequel::Model; end
52
52
  ParParent.many_to_one :par_parent_two
53
53
  ParParentTwo.one_to_many :par_parents
54
54
  ParParent.many_to_many :par_parent_threes
@@ -68,7 +68,7 @@ end
68
68
  describe Sequel::Model::Associations::AssociationReflection, "#select" do
69
69
  before do
70
70
  @c = Class.new(Sequel::Model)
71
- class ParParent < Sequel::Model; end
71
+ class ::ParParent < Sequel::Model; end
72
72
  end
73
73
 
74
74
  it "should use the :select value if present" do
@@ -4,7 +4,7 @@ describe Sequel::Model, "associate" do
4
4
  it "should use explicit class if given a class, symbol, or string" do
5
5
  MODEL_DB.reset
6
6
  klass = Class.new(Sequel::Model(:nodes))
7
- class ParParent < Sequel::Model
7
+ class ::ParParent < Sequel::Model
8
8
  end
9
9
 
10
10
  klass.associate :many_to_one, :par_parent0, :class=>ParParent
@@ -48,6 +48,7 @@ describe Sequel::Model, "many_to_one" do
48
48
  MODEL_DB.reset
49
49
 
50
50
  @c2 = Class.new(Sequel::Model(:nodes)) do
51
+ unrestrict_primary_key
51
52
  columns :id, :parent_id, :par_parent_id, :blah
52
53
  end
53
54
 
@@ -66,7 +67,7 @@ describe Sequel::Model, "many_to_one" do
66
67
  end
67
68
 
68
69
  it "should use implicit class if omitted" do
69
- class ParParent < Sequel::Model
70
+ class ::ParParent < Sequel::Model
70
71
  end
71
72
 
72
73
  @c2.many_to_one :par_parent
@@ -79,7 +80,7 @@ describe Sequel::Model, "many_to_one" do
79
80
  end
80
81
 
81
82
  it "should use class inside module if given as a string" do
82
- module Par
83
+ module ::Par
83
84
  class Parent < Sequel::Model
84
85
  end
85
86
  end
@@ -263,10 +264,29 @@ describe Sequel::Model, "many_to_one" do
263
264
  MODEL_DB.sqls.should == []
264
265
  end
265
266
 
267
+ it "should get all matching records and only return the first if :key option is set to nil" do
268
+ c2 = @c2
269
+ @c2.one_to_many :children, :class => @c2, :key=>:parent_id
270
+ @c2.many_to_one :first_grand_parent, :class => @c2, :key=>nil, :eager_graph=>:children, :dataset=>proc{c2.filter(:children_id=>parent_id)}
271
+ ds = @c2.dataset
272
+ def ds.columns
273
+ [:id, :parent_id, :par_parent_id, :blah]
274
+ end
275
+ def ds.fetch_rows(sql, &block)
276
+ MODEL_DB.sqls << sql
277
+ yield({:id=>1, :parent_id=>0, :par_parent_id=>3, :blah=>4, :children_id=>2, :children_parent_id=>1, :children_par_parent_id=>5, :children_blah=>6})
278
+ end
279
+ p = @c2.new(:parent_id=>2)
280
+ fgp = p.first_grand_parent
281
+ MODEL_DB.sqls.should == ["SELECT nodes.id, nodes.parent_id, nodes.par_parent_id, nodes.blah, children.id AS children_id, children.parent_id AS children_parent_id, children.par_parent_id AS children_par_parent_id, children.blah AS children_blah FROM nodes LEFT OUTER JOIN nodes AS children ON (children.parent_id = nodes.id) WHERE (children_id = 2)"]
282
+ fgp.values.should == {:id=>1, :parent_id=>0, :par_parent_id=>3, :blah=>4}
283
+ fgp.children.first.values.should == {:id=>2, :parent_id=>1, :par_parent_id=>5, :blah=>6}
284
+ end
285
+
266
286
  it "should not create the setter method if :read_only option is used" do
267
287
  @c2.many_to_one :parent, :class => @c2, :read_only=>true
268
- @c2.instance_methods.should(include('parent'))
269
- @c2.instance_methods.should_not(include('parent='))
288
+ @c2.instance_methods.collect{|x| x.to_s}.should(include('parent'))
289
+ @c2.instance_methods.collect{|x| x.to_s}.should_not(include('parent='))
270
290
  end
271
291
 
272
292
  it "should raise an error if trying to set a model object that doesn't have a valid primary key" do
@@ -292,7 +312,7 @@ describe Sequel::Model, "many_to_one" do
292
312
 
293
313
  it "should make the change to the foreign_key value inside a _association= method" do
294
314
  @c2.many_to_one :parent, :class => @c2
295
- @c2.private_instance_methods.sort.should(include("_parent="))
315
+ @c2.private_instance_methods.collect{|x| x.to_s}.sort.should(include("_parent="))
296
316
  p = @c2.new
297
317
  c = @c2.load(:id=>123)
298
318
  def p._parent=(x)
@@ -362,6 +382,11 @@ describe Sequel::Model, "many_to_one" do
362
382
  proc{p.parent = nil}.should raise_error(Sequel::Error)
363
383
  end
364
384
 
385
+ it "should raise an error if a callback is not a proc or symbol" do
386
+ @c2.many_to_one :parent, :class => @c2, :before_add=>Object.new
387
+ proc{@c2.new.parent = @c2.load(:id=>1)}.should raise_error(Sequel::Error)
388
+ end
389
+
365
390
  it "should call the remove callbacks for the previous object and the add callbacks for the new object" do
366
391
  c = @c2.load(:id=>123)
367
392
  d = @c2.load(:id=>321)
@@ -398,10 +423,12 @@ describe Sequel::Model, "one_to_many" do
398
423
  MODEL_DB.reset
399
424
 
400
425
  @c1 = Class.new(Sequel::Model(:attributes)) do
426
+ unrestrict_primary_key
401
427
  columns :id, :node_id
402
428
  end
403
429
 
404
430
  @c2 = Class.new(Sequel::Model(:nodes)) do
431
+ unrestrict_primary_key
405
432
  attr_accessor :xxx
406
433
 
407
434
  def self.name; 'Node'; end
@@ -435,7 +462,7 @@ describe Sequel::Model, "one_to_many" do
435
462
  end
436
463
 
437
464
  it "should use implicit class if omitted" do
438
- class HistoricalValue < Sequel::Model
465
+ class ::HistoricalValue < Sequel::Model
439
466
  end
440
467
 
441
468
  @c2.one_to_many :historical_values
@@ -448,7 +475,7 @@ describe Sequel::Model, "one_to_many" do
448
475
  end
449
476
 
450
477
  it "should use class inside a module if given as a string" do
451
- module Historical
478
+ module ::Historical
452
479
  class Value < Sequel::Model
453
480
  end
454
481
  end
@@ -692,7 +719,7 @@ describe Sequel::Model, "one_to_many" do
692
719
 
693
720
  it "should not create the add_, remove_, or remove_all_ methods if :read_only option is used" do
694
721
  @c2.one_to_many :attributes, :class => @c1, :read_only=>true
695
- im = @c2.instance_methods
722
+ im = @c2.instance_methods.collect{|x| x.to_s}
696
723
  im.should(include('attributes'))
697
724
  im.should(include('attributes_dataset'))
698
725
  im.should_not(include('add_attribute'))
@@ -803,7 +830,7 @@ describe Sequel::Model, "one_to_many" do
803
830
 
804
831
  it "should not add a getter method if the :one_to_one option is true and :read_only option is true" do
805
832
  @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true, :read_only=>true
806
- im = @c2.instance_methods
833
+ im = @c2.instance_methods.collect{|x| x.to_s}
807
834
  im.should(include('attribute'))
808
835
  im.should_not(include('attribute='))
809
836
  end
@@ -846,7 +873,7 @@ describe Sequel::Model, "one_to_many" do
846
873
 
847
874
  it "should make non getter and setter methods private if :one_to_one option is used" do
848
875
  @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true do |ds| end
849
- meths = @c2.private_instance_methods(false)
876
+ meths = @c2.private_instance_methods(false).collect{|x| x.to_s}
850
877
  meths.should(include("attributes"))
851
878
  meths.should(include("add_attribute"))
852
879
  meths.should(include("attributes_dataset"))
@@ -854,7 +881,7 @@ describe Sequel::Model, "one_to_many" do
854
881
 
855
882
  it "should call an _add_ method internally to add attributes" do
856
883
  @c2.one_to_many :attributes, :class => @c1
857
- @c2.private_instance_methods.sort.should(include("_add_attribute"))
884
+ @c2.private_instance_methods.collect{|x| x.to_s}.sort.should(include("_add_attribute"))
858
885
  p = @c2.load(:id=>10)
859
886
  c = @c1.load(:id=>123)
860
887
  def p._add_attribute(x)
@@ -867,7 +894,7 @@ describe Sequel::Model, "one_to_many" do
867
894
 
868
895
  it "should call a _remove_ method internally to remove attributes" do
869
896
  @c2.one_to_many :attributes, :class => @c1
870
- @c2.private_instance_methods.sort.should(include("_remove_attribute"))
897
+ @c2.private_instance_methods.collect{|x| x.to_s}.sort.should(include("_remove_attribute"))
871
898
  p = @c2.load(:id=>10)
872
899
  c = @c1.load(:id=>123)
873
900
  def p._remove_attribute(x)
@@ -966,12 +993,14 @@ describe Sequel::Model, "many_to_many" do
966
993
  MODEL_DB.reset
967
994
 
968
995
  @c1 = Class.new(Sequel::Model(:attributes)) do
996
+ unrestrict_primary_key
969
997
  def self.name; 'Attribute'; end
970
998
  def self.to_s; 'Attribute'; end
971
999
  columns :id
972
1000
  end
973
1001
 
974
1002
  @c2 = Class.new(Sequel::Model(:nodes)) do
1003
+ unrestrict_primary_key
975
1004
  attr_accessor :xxx
976
1005
 
977
1006
  def self.name; 'Node'; end
@@ -1000,7 +1029,7 @@ describe Sequel::Model, "many_to_many" do
1000
1029
  end
1001
1030
 
1002
1031
  it "should use implicit class if omitted" do
1003
- class Tag < Sequel::Model
1032
+ class ::Tag < Sequel::Model
1004
1033
  end
1005
1034
 
1006
1035
  @c2.many_to_many :tags
@@ -1012,7 +1041,7 @@ describe Sequel::Model, "many_to_many" do
1012
1041
  end
1013
1042
 
1014
1043
  it "should use class inside module if given as a string" do
1015
- module Historical
1044
+ module ::Historical
1016
1045
  class Tag < Sequel::Model
1017
1046
  end
1018
1047
  end
@@ -1263,7 +1292,7 @@ describe Sequel::Model, "many_to_many" do
1263
1292
 
1264
1293
  it "should not create the add_, remove_, or remove_all_ methods if :read_only option is used" do
1265
1294
  @c2.many_to_many :attributes, :class => @c1, :read_only=>true
1266
- im = @c2.instance_methods
1295
+ im = @c2.instance_methods.collect{|x| x.to_s}
1267
1296
  im.should(include('attributes'))
1268
1297
  im.should(include('attributes_dataset'))
1269
1298
  im.should_not(include('add_attribute'))
@@ -1329,7 +1358,7 @@ describe Sequel::Model, "many_to_many" do
1329
1358
 
1330
1359
  it "should call an _add_ method internally to add attributes" do
1331
1360
  @c2.many_to_many :attributes, :class => @c1
1332
- @c2.private_instance_methods.sort.should(include("_add_attribute"))
1361
+ @c2.private_instance_methods.collect{|x| x.to_s}.sort.should(include("_add_attribute"))
1333
1362
  p = @c2.load(:id=>10)
1334
1363
  c = @c1.load(:id=>123)
1335
1364
  def p._add_attribute(x)
@@ -1342,7 +1371,7 @@ describe Sequel::Model, "many_to_many" do
1342
1371
 
1343
1372
  it "should call a _remove_ method internally to remove attributes" do
1344
1373
  @c2.many_to_many :attributes, :class => @c1
1345
- @c2.private_instance_methods.sort.should(include("_remove_attribute"))
1374
+ @c2.private_instance_methods.collect{|x| x.to_s}.sort.should(include("_remove_attribute"))
1346
1375
  p = @c2.load(:id=>10)
1347
1376
  c = @c1.load(:id=>123)
1348
1377
  def p._remove_attribute(x)
@@ -79,10 +79,11 @@ describe "Model#serialize" do
79
79
 
80
80
  @c.create(:abc => 1)
81
81
  @c.create(:abc => "hello")
82
+ x = [Marshal.dump("hello")].pack('m')
82
83
 
83
84
  MODEL_DB.sqls.should == [ \
84
85
  "INSERT INTO items (abc) VALUES ('BAhpBg==\n')", \
85
- "INSERT INTO items (abc) VALUES ('BAgiCmhlbGxv\n')", \
86
+ "INSERT INTO items (abc) VALUES ('#{x}')", \
86
87
  ]
87
88
  end
88
89
 
@@ -56,7 +56,19 @@ describe Sequel::Model::DatasetMethods, "#to_hash" do
56
56
  h.should be_a_kind_of(Hash)
57
57
  a = h.to_a
58
58
  a.collect{|x| x[1].class}.should == [@c, @c]
59
- [[[1, {:name=>1}], [2, {:name=>2}]], [[2, {:name=>2}], [1, {:name=>1}]]].should(include(a.collect{|x| [x[0], x[1].values]}))
59
+ a.sort_by{|x| x[0]}.collect{|x| [x[0], x[1].values]}.should == [[1, {:name=>1}], [2, {:name=>2}]]
60
+ end
61
+
62
+ it "should result in a hash with given value keys and model object values" do
63
+ def @d.fetch_rows(sql)
64
+ yield({:name=>1, :number=>3})
65
+ yield({:name=>2, :number=>4})
66
+ end
67
+ h = @d.to_hash(:number)
68
+ h.should be_a_kind_of(Hash)
69
+ a = h.to_a
70
+ a.collect{|x| x[1].class}.should == [@c, @c]
71
+ a.sort_by{|x| x[0]}.collect{|x| [x[0], x[1].values]}.should == [[3, {:name=>1, :number=>3}], [4, {:name=>2, :number=>4}]]
60
72
  end
61
73
 
62
74
  it "should raise an error if the class doesn't have a primary key" do
@@ -4,7 +4,7 @@ describe Sequel::Model, "#eager" do
4
4
  before(:each) do
5
5
  MODEL_DB.reset
6
6
 
7
- class EagerAlbum < Sequel::Model(:albums)
7
+ class ::EagerAlbum < Sequel::Model(:albums)
8
8
  columns :id, :band_id
9
9
  many_to_one :band, :class=>'EagerBand', :key=>:band_id
10
10
  one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id
@@ -17,7 +17,7 @@ describe Sequel::Model, "#eager" do
17
17
  many_to_many :genre_names, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :select=>[:id]
18
18
  end
19
19
 
20
- class EagerBand < Sequel::Model(:bands)
20
+ class ::EagerBand < Sequel::Model(:bands)
21
21
  columns :id
22
22
  one_to_many :albums, :class=>'EagerAlbum', :key=>:band_id, :eager=>:tracks
23
23
  one_to_many :graph_albums, :class=>'EagerAlbum', :key=>:band_id, :eager_graph=>:tracks
@@ -32,17 +32,17 @@ describe Sequel::Model, "#eager" do
32
32
  one_to_many :top_10_albums, :class=>'EagerAlbum', :key=>:band_id, :limit=>10
33
33
  end
34
34
 
35
- class EagerTrack < Sequel::Model(:tracks)
35
+ class ::EagerTrack < Sequel::Model(:tracks)
36
36
  columns :id, :album_id
37
37
  many_to_one :album, :class=>'EagerAlbum', :key=>:album_id
38
38
  end
39
39
 
40
- class EagerGenre < Sequel::Model(:genres)
40
+ class ::EagerGenre < Sequel::Model(:genres)
41
41
  columns :id
42
42
  many_to_many :albums, :class=>'EagerAlbum', :left_key=>:genre_id, :right_key=>:album_id, :join_table=>:ag
43
43
  end
44
44
 
45
- class EagerBandMember < Sequel::Model(:members)
45
+ class ::EagerBandMember < Sequel::Model(:members)
46
46
  columns :id
47
47
  many_to_many :bands, :class=>'EagerBand', :left_key=>:member_id, :right_key=>:band_id, :join_table=>:bm, :order =>:id
48
48
  end
@@ -106,6 +106,10 @@ describe Sequel::Model, "#eager" do
106
106
  })
107
107
  end
108
108
 
109
+ it "should raise an error if called without a symbol or hash" do
110
+ proc{EagerAlbum.eager(Object.new)}.should raise_error(Sequel::Error)
111
+ end
112
+
109
113
  it "should eagerly load a single many_to_one association" do
110
114
  a = EagerAlbum.eager(:band).all
111
115
  a.should be_a_kind_of(Array)
@@ -149,7 +153,7 @@ describe Sequel::Model, "#eager" do
149
153
  MODEL_DB.sqls.length.should == 2
150
154
  end
151
155
 
152
- it "should eagerly load multiple associations" do
156
+ it "should eagerly load multiple associations in a single call" do
153
157
  a = EagerAlbum.eager(:genres, :tracks, :band).all
154
158
  a.should be_a_kind_of(Array)
155
159
  a.size.should == 1
@@ -174,6 +178,31 @@ describe Sequel::Model, "#eager" do
174
178
  MODEL_DB.sqls.length.should == 4
175
179
  end
176
180
 
181
+ it "should eagerly load multiple associations in separate calls" do
182
+ a = EagerAlbum.eager(:genres).eager(:tracks).eager(:band).all
183
+ a.should be_a_kind_of(Array)
184
+ a.size.should == 1
185
+ a.first.should be_a_kind_of(EagerAlbum)
186
+ a.first.values.should == {:id => 1, :band_id => 2}
187
+ MODEL_DB.sqls.length.should == 4
188
+ MODEL_DB.sqls[0].should == 'SELECT * FROM albums'
189
+ MODEL_DB.sqls[1..-1].should(include('SELECT * FROM bands WHERE (bands.id IN (2))'))
190
+ MODEL_DB.sqls[1..-1].should(include('SELECT * FROM tracks WHERE (tracks.album_id IN (1))'))
191
+ MODEL_DB.sqls[1..-1].should(include('SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))'))
192
+ a = a.first
193
+ a.band.should be_a_kind_of(EagerBand)
194
+ a.band.values.should == {:id => 2}
195
+ a.tracks.should be_a_kind_of(Array)
196
+ a.tracks.size.should == 1
197
+ a.tracks.first.should be_a_kind_of(EagerTrack)
198
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
199
+ a.genres.should be_a_kind_of(Array)
200
+ a.genres.size.should == 1
201
+ a.genres.first.should be_a_kind_of(EagerGenre)
202
+ a.genres.first.values.should == {:id => 4}
203
+ MODEL_DB.sqls.length.should == 4
204
+ end
205
+
177
206
  it "should allow cascading of eager loading for associations of associated models" do
178
207
  a = EagerTrack.eager(:album=>{:band=>:members}).all
179
208
  a.should be_a_kind_of(Array)
@@ -430,13 +459,13 @@ end
430
459
 
431
460
  describe Sequel::Model, "#eager_graph" do
432
461
  after(:all) do
433
- class MockDataset
462
+ class ::MockDataset
434
463
  alias clone orig_clone
435
464
  end
436
465
  end
437
466
 
438
467
  before(:all) do
439
- class MockDataset
468
+ class ::MockDataset
440
469
  alias orig_clone clone
441
470
  def clone(opts = {})
442
471
  c = super()
@@ -446,15 +475,16 @@ describe Sequel::Model, "#eager_graph" do
446
475
  end
447
476
  end
448
477
 
449
- class GraphAlbum < Sequel::Model(:albums)
478
+ class ::GraphAlbum < Sequel::Model(:albums)
450
479
  dataset.opts[:from] = [:albums]
451
480
  columns :id, :band_id
452
481
  many_to_one :band, :class=>'GraphBand', :key=>:band_id
453
482
  one_to_many :tracks, :class=>'GraphTrack', :key=>:album_id
454
483
  many_to_many :genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag
484
+ many_to_one :previous_album, :class=>'GraphAlbum'
455
485
  end
456
486
 
457
- class GraphBand < Sequel::Model(:bands)
487
+ class ::GraphBand < Sequel::Model(:bands)
458
488
  dataset.opts[:from] = [:bands]
459
489
  columns :id, :vocalist_id
460
490
  many_to_one :vocalist, :class=>'GraphBandMember', :key=>:vocalist_id
@@ -463,25 +493,29 @@ describe Sequel::Model, "#eager_graph" do
463
493
  many_to_many :genres, :class=>'GraphGenre', :left_key=>:band_id, :right_key=>:genre_id, :join_table=>:bg
464
494
  end
465
495
 
466
- class GraphTrack < Sequel::Model(:tracks)
496
+ class ::GraphTrack < Sequel::Model(:tracks)
467
497
  dataset.opts[:from] = [:tracks]
468
498
  columns :id, :album_id
469
499
  many_to_one :album, :class=>'GraphAlbum', :key=>:album_id
470
500
  end
471
501
 
472
- class GraphGenre < Sequel::Model(:genres)
502
+ class ::GraphGenre < Sequel::Model(:genres)
473
503
  dataset.opts[:from] = [:genres]
474
504
  columns :id
475
505
  many_to_many :albums, :class=>'GraphAlbum', :left_key=>:genre_id, :right_key=>:album_id, :join_table=>:ag
476
506
  end
477
507
 
478
- class GraphBandMember < Sequel::Model(:members)
508
+ class ::GraphBandMember < Sequel::Model(:members)
479
509
  dataset.opts[:from] = [:members]
480
510
  columns :id
481
511
  many_to_many :bands, :class=>'GraphBand', :left_key=>:member_id, :right_key=>:band_id, :join_table=>:bm
482
512
  end
483
513
  end
484
514
 
515
+ it "should raise an error if called without a symbol or hash" do
516
+ proc{GraphAlbum.eager_graph(Object.new)}.should raise_error(Sequel::Error)
517
+ end
518
+
485
519
  it "should eagerly load a single many_to_one association" do
486
520
  ds = GraphAlbum.eager_graph(:band)
487
521
  ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
@@ -534,7 +568,7 @@ describe Sequel::Model, "#eager_graph" do
534
568
  a.genres.first.values.should == {:id => 4}
535
569
  end
536
570
 
537
- it "should eagerly load multiple associations" do
571
+ it "should eagerly load multiple associations in a single call" do
538
572
  ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
539
573
  ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id, tracks.id AS tracks_id, tracks.album_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
540
574
  def ds.fetch_rows(sql, &block)
@@ -558,6 +592,30 @@ describe Sequel::Model, "#eager_graph" do
558
592
  a.genres.first.values.should == {:id => 4}
559
593
  end
560
594
 
595
+ it "should eagerly load multiple associations in separate calls" do
596
+ ds = GraphAlbum.eager_graph(:genres).eager_graph(:tracks).eager_graph(:band)
597
+ ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id, tracks.id AS tracks_id, tracks.album_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
598
+ def ds.fetch_rows(sql, &block)
599
+ yield({:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6})
600
+ end
601
+ a = ds.all
602
+ a.should be_a_kind_of(Array)
603
+ a.size.should == 1
604
+ a.first.should be_a_kind_of(GraphAlbum)
605
+ a.first.values.should == {:id => 1, :band_id => 2}
606
+ a = a.first
607
+ a.band.should be_a_kind_of(GraphBand)
608
+ a.band.values.should == {:id => 2, :vocalist_id=>6}
609
+ a.tracks.should be_a_kind_of(Array)
610
+ a.tracks.size.should == 1
611
+ a.tracks.first.should be_a_kind_of(GraphTrack)
612
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
613
+ a.genres.should be_a_kind_of(Array)
614
+ a.genres.size.should == 1
615
+ a.genres.first.should be_a_kind_of(GraphGenre)
616
+ a.genres.first.values.should == {:id => 4}
617
+ end
618
+
561
619
  it "should allow cascading of eager loading for associations of associated models" do
562
620
  ds = GraphTrack.eager_graph(:album=>{:band=>:members})
563
621
  ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, members.id AS members_id FROM tracks LEFT OUTER JOIN albums AS album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands AS band ON (band.id = album.band_id) LEFT OUTER JOIN bm ON (bm.band_id = band.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
@@ -900,4 +958,7 @@ describe Sequel::Model, "#eager_graph" do
900
958
  GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON (active) LEFT OUTER JOIN genres AS active_genres ON ((price + 2) > 100)"
901
959
  end
902
960
 
961
+ it "should create unique table aliases for all associations" do
962
+ GraphAlbum.eager_graph(:previous_album=>{:previous_album=>:previous_album}).sql.should == "SELECT albums.id, albums.band_id, previous_album.id AS previous_album_id, previous_album.band_id AS previous_album_band_id, previous_album_0.id AS previous_album_0_id, previous_album_0.band_id AS previous_album_0_band_id, previous_album_1.id AS previous_album_1_id, previous_album_1.band_id AS previous_album_1_band_id FROM albums LEFT OUTER JOIN albums AS previous_album ON (previous_album.id = albums.previous_album_id) LEFT OUTER JOIN albums AS previous_album_0 ON (previous_album_0.id = previous_album.previous_album_id) LEFT OUTER JOIN albums AS previous_album_1 ON (previous_album_1.id = previous_album_0.previous_album_id)"
963
+ end
903
964
  end