sequel 4.43.0 → 4.44.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +40 -0
  3. data/doc/active_record.rdoc +2 -2
  4. data/doc/code_order.rdoc +15 -0
  5. data/doc/dataset_filtering.rdoc +1 -1
  6. data/doc/model_dataset_method_design.rdoc +132 -0
  7. data/doc/opening_databases.rdoc +2 -2
  8. data/doc/release_notes/4.44.0.txt +125 -0
  9. data/lib/sequel/adapters/jdbc.rb +5 -1
  10. data/lib/sequel/adapters/jdbc/as400.rb +1 -1
  11. data/lib/sequel/adapters/postgres.rb +23 -14
  12. data/lib/sequel/core.rb +1 -1
  13. data/lib/sequel/database/schema_generator.rb +27 -0
  14. data/lib/sequel/dataset/actions.rb +1 -1
  15. data/lib/sequel/dataset/query.rb +5 -1
  16. data/lib/sequel/extensions/eval_inspect.rb +4 -4
  17. data/lib/sequel/extensions/implicit_subquery.rb +48 -0
  18. data/lib/sequel/extensions/to_dot.rb +1 -1
  19. data/lib/sequel/model.rb +3 -5
  20. data/lib/sequel/model/associations.rb +107 -4
  21. data/lib/sequel/model/base.rb +98 -12
  22. data/lib/sequel/model/dataset_module.rb +1 -1
  23. data/lib/sequel/plugins/active_model.rb +11 -3
  24. data/lib/sequel/plugins/association_dependencies.rb +7 -0
  25. data/lib/sequel/plugins/auto_validations.rb +10 -0
  26. data/lib/sequel/plugins/blacklist_security.rb +13 -4
  27. data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
  28. data/lib/sequel/plugins/column_conflicts.rb +8 -0
  29. data/lib/sequel/plugins/composition.rb +12 -2
  30. data/lib/sequel/plugins/constraint_validations.rb +12 -0
  31. data/lib/sequel/plugins/csv_serializer.rb +9 -0
  32. data/lib/sequel/plugins/defaults_setter.rb +6 -0
  33. data/lib/sequel/plugins/force_encoding.rb +4 -3
  34. data/lib/sequel/plugins/hook_class_methods.rb +6 -0
  35. data/lib/sequel/plugins/input_transformer.rb +9 -0
  36. data/lib/sequel/plugins/insert_returning_select.rb +8 -0
  37. data/lib/sequel/plugins/instance_hooks.rb +4 -3
  38. data/lib/sequel/plugins/json_serializer.rb +9 -0
  39. data/lib/sequel/plugins/lazy_attributes.rb +7 -0
  40. data/lib/sequel/plugins/many_through_many.rb +13 -2
  41. data/lib/sequel/plugins/nested_attributes.rb +7 -0
  42. data/lib/sequel/plugins/pg_array_associations.rb +18 -2
  43. data/lib/sequel/plugins/pg_row.rb +3 -3
  44. data/lib/sequel/plugins/pg_typecast_on_load.rb +7 -0
  45. data/lib/sequel/plugins/prepared_statements.rb +2 -2
  46. data/lib/sequel/plugins/prepared_statements_safe.rb +7 -0
  47. data/lib/sequel/plugins/serialization.rb +9 -0
  48. data/lib/sequel/plugins/single_table_inheritance.rb +13 -1
  49. data/lib/sequel/plugins/subclasses.rb +15 -1
  50. data/lib/sequel/plugins/touch.rb +7 -0
  51. data/lib/sequel/plugins/tree.rb +7 -0
  52. data/lib/sequel/plugins/typecast_on_load.rb +7 -0
  53. data/lib/sequel/plugins/update_refresh.rb +24 -13
  54. data/lib/sequel/plugins/validation_class_methods.rb +13 -0
  55. data/lib/sequel/sql.rb +28 -0
  56. data/lib/sequel/version.rb +1 -1
  57. data/spec/adapters/postgres_spec.rb +18 -15
  58. data/spec/core/dataset_spec.rb +5 -0
  59. data/spec/core/expression_filters_spec.rb +33 -0
  60. data/spec/extensions/active_model_spec.rb +15 -1
  61. data/spec/extensions/association_dependencies_spec.rb +8 -0
  62. data/spec/extensions/auto_validations_spec.rb +8 -0
  63. data/spec/extensions/blacklist_security_spec.rb +6 -0
  64. data/spec/extensions/class_table_inheritance_spec.rb +9 -0
  65. data/spec/extensions/column_conflicts_spec.rb +6 -0
  66. data/spec/extensions/composition_spec.rb +8 -0
  67. data/spec/extensions/constraint_validations_plugin_spec.rb +12 -0
  68. data/spec/extensions/csv_serializer_spec.rb +7 -0
  69. data/spec/extensions/defaults_setter_spec.rb +7 -0
  70. data/spec/extensions/force_encoding_spec.rb +14 -0
  71. data/spec/extensions/hook_class_methods_spec.rb +10 -0
  72. data/spec/extensions/implicit_subquery_spec.rb +60 -0
  73. data/spec/extensions/input_transformer_spec.rb +10 -0
  74. data/spec/extensions/insert_returning_select_spec.rb +6 -0
  75. data/spec/extensions/json_serializer_spec.rb +7 -0
  76. data/spec/extensions/lazy_attributes_spec.rb +6 -0
  77. data/spec/extensions/many_through_many_spec.rb +44 -0
  78. data/spec/extensions/nested_attributes_spec.rb +5 -0
  79. data/spec/extensions/pg_array_associations_spec.rb +46 -0
  80. data/spec/extensions/pg_typecast_on_load_spec.rb +5 -0
  81. data/spec/extensions/prepared_statements_safe_spec.rb +5 -0
  82. data/spec/extensions/serialization_spec.rb +7 -0
  83. data/spec/extensions/single_table_inheritance_spec.rb +19 -2
  84. data/spec/extensions/subclasses_spec.rb +13 -0
  85. data/spec/extensions/to_dot_spec.rb +1 -1
  86. data/spec/extensions/touch_spec.rb +6 -0
  87. data/spec/extensions/tree_spec.rb +6 -0
  88. data/spec/extensions/typecast_on_load_spec.rb +6 -0
  89. data/spec/extensions/update_refresh_spec.rb +7 -1
  90. data/spec/extensions/validation_class_methods_spec.rb +13 -0
  91. data/spec/model/association_reflection_spec.rb +177 -0
  92. data/spec/model/associations_spec.rb +16 -0
  93. data/spec/model/dataset_methods_spec.rb +59 -0
  94. data/spec/model/model_spec.rb +59 -0
  95. metadata +8 -2
@@ -694,4 +694,9 @@ describe "NestedAttributes plugin" do
694
694
  @Tag.columns :id, :name, :number
695
695
  proc{@Album.load(:id=>10, :name=>'Al').set_nested_attributes(:tags, [{:id=>30, :name=>'T2', :number=>3}], :fields=>[:name])}.must_raise(Sequel::Error)
696
696
  end
697
+
698
+ it "should freeze nested_attributes_module when freezing model class" do
699
+ @Artist.freeze
700
+ @Artist.nested_attributes_module.frozen?.must_equal true
701
+ end
697
702
  end
@@ -735,3 +735,49 @@ describe Sequel::Model, "pg_array_associations" do
735
735
  t.artists.must_equal [a]
736
736
  end
737
737
  end
738
+
739
+ describe "Sequel::Model.finalize_associations" do
740
+ before do
741
+ class ::Foo < Sequel::Model
742
+ plugin :pg_array_associations
743
+ many_to_pg_array :items
744
+ end
745
+ class ::Item < Sequel::Model
746
+ plugin :pg_array_associations
747
+ pg_array_to_many :foos
748
+ end
749
+ [Foo, Item].each(&:finalize_associations)
750
+ end
751
+ after do
752
+ Object.send(:remove_const, :Item)
753
+ Object.send(:remove_const, :Foo)
754
+ end
755
+
756
+ it "should finalize pg_array_to_many associations" do
757
+ r = Item.association_reflection(:foos)
758
+ r[:class].must_equal Foo
759
+ r[:_dataset].sql.must_equal "SELECT * FROM foos"
760
+ r[:associated_eager_dataset].sql.must_equal "SELECT * FROM foos"
761
+ r.fetch(:_eager_limit_strategy).must_be_nil
762
+ r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT array_agg(foos.id) FROM foos WHERE (foos.id IS NOT NULL)"
763
+ r[:predicate_key].must_equal Sequel.qualify(:foos, :id)
764
+ r[:predicate_keys].must_equal [Sequel.qualify(:foos, :id)]
765
+ r[:reciprocal].must_equal :items
766
+ r[:array_type].must_equal :integer
767
+ r[:primary_key].must_equal :id
768
+ r[:primary_key_method].must_equal :id
769
+ end
770
+
771
+ it "should finalize many_to_pg_array associations" do
772
+ r = Foo.association_reflection(:items)
773
+ r[:class].must_equal Item
774
+ r[:_dataset].sql.must_equal "SELECT * FROM items"
775
+ r[:associated_eager_dataset].sql.must_equal "SELECT * FROM items"
776
+ r.fetch(:_eager_limit_strategy).must_be_nil
777
+ r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT unnest(items.foo_ids) FROM items WHERE (items.foo_ids IS NOT NULL)"
778
+ r[:predicate_key].must_equal Sequel.qualify(:items, :foo_ids)
779
+ r[:predicate_keys].must_equal [Sequel.qualify(:items, :foo_ids)]
780
+ r[:reciprocal].must_equal :foos
781
+ r[:array_type].must_equal :integer
782
+ end
783
+ end
@@ -60,4 +60,9 @@ describe Sequel::Model, "PgTypecastOnLoad plugin" do
60
60
  it "should not mark the object as modified" do
61
61
  @c.first.modified?.must_equal false
62
62
  end
63
+
64
+ it "should freeze pg_typecast_on_load_columns" do
65
+ @c.freeze
66
+ @c.pg_typecast_on_load_columns.frozen?.must_equal true
67
+ end
63
68
  end
@@ -58,4 +58,9 @@ describe "prepared_statements_safe plugin" do
58
58
  c1.prepared_statements_column_defaults.must_equal(:name=>'foo')
59
59
  Class.new(c1).prepared_statements_column_defaults.must_equal(:name=>'foo')
60
60
  end
61
+
62
+ it "should freeze prepared statement column defaults when freezing model class" do
63
+ @c.freeze
64
+ @c.prepared_statements_column_defaults.frozen?.must_equal true
65
+ end
61
66
  end
@@ -361,4 +361,11 @@ describe "Serialization plugin" do
361
361
  o.column_changes.must_equal(:abc=>[1, 2], :def=>["hello", "hello2"])
362
362
  end
363
363
 
364
+ it "should freeze serialization metadata when freezing model class" do
365
+ @c.plugin :serialization, :yaml, :abc, :def
366
+ @c.freeze
367
+ @c.serialization_map.frozen?.must_equal true
368
+ @c.deserialization_map.frozen?.must_equal true
369
+ @c.serialization_module.frozen?.must_equal true
370
+ end
364
371
  end
@@ -19,6 +19,16 @@ describe Sequel::Model, "single table inheritance plugin" do
19
19
  Object.send(:remove_const, :StiTest)
20
20
  end
21
21
 
22
+ it "should freeze sti metadata when freezing model class" do
23
+ StiTest.freeze
24
+ StiTest.sti_dataset.frozen?.must_equal true
25
+
26
+ StiTestSub1.freeze
27
+ StiTestSub1.sti_key_array.frozen?.must_equal true
28
+
29
+ proc{class ::StiTestSub1Sub1 < StiTestSub1; end}.must_raise RuntimeError, TypeError
30
+ end
31
+
22
32
  it "should have simple_table = nil" do
23
33
  StiTest.simple_table.must_equal "sti_tests"
24
34
  StiTestSub1.simple_table.must_be_nil
@@ -180,8 +190,15 @@ describe Sequel::Model, "single table inheritance plugin" do
180
190
  end
181
191
  after do
182
192
  Object.send(:remove_const, :StiTest2)
183
- Object.send(:remove_const, :StiTest3)
184
- Object.send(:remove_const, :StiTest4)
193
+ Object.send(:remove_const, :StiTest3) if defined?(StiTest3)
194
+ Object.send(:remove_const, :StiTest4) if defined?(StiTest4)
195
+ end
196
+
197
+ it "should freeze sti key and model map if given as hashes when freezing model class" do
198
+ StiTest2.plugin :single_table_inheritance, :kind, :model_map=>{0=>StiTest2, 1=>:StiTest3, 2=>'StiTest4'}, :key_map=>{StiTest2=>4, 'StiTest3'=>5, 'StiTest4'=>6}
199
+ StiTest2.freeze
200
+ StiTest2.sti_key_map.frozen?.must_equal true
201
+ StiTest2.sti_model_map.frozen?.must_equal true
185
202
  end
186
203
 
187
204
  it "should have working row_proc if using set_dataset in subclass to remove columns" do
@@ -50,6 +50,19 @@ describe Sequel::Model, "Subclasses plugin" do
50
50
  sssc1.descendents.must_equal []
51
51
  end
52
52
 
53
+ it "#freeze_descendents should finalize the associations for all descendents" do
54
+ sc1 = Class.new(@c)
55
+ sc1.set_dataset :bars
56
+ sc1.set_primary_key :foo
57
+ sc2 = Class.new(@c)
58
+ sc2.set_dataset :bazs
59
+ sc2.many_to_one :bar, :class=>sc1
60
+ @c.freeze_descendents
61
+ sc1.frozen?.must_equal true
62
+ sc2.frozen?.must_equal true
63
+ sc2.association_reflection(:bar)[:primary_key].must_equal :foo
64
+ end
65
+
53
66
  it "plugin block should be called with each subclass created" do
54
67
  c = Class.new(Sequel::Model)
55
68
  a = []
@@ -80,7 +80,7 @@ END
80
80
  end
81
81
 
82
82
  it "should handle LiteralStrings" do
83
- dot(@ds.filter('a')).must_equal ["1 -> 2 [label=\"where\"];", "2 [label=\"\\\"(a)\\\".lit\"];"]
83
+ dot(@ds.filter('a')).must_equal ["1 -> 2 [label=\"where\"];", "2 [label=\"Sequel.lit(\\\"(a)\\\")\"];"]
84
84
  end
85
85
 
86
86
  it "should handle true, false, nil" do
@@ -208,4 +208,10 @@ describe "Touch plugin" do
208
208
  DB.sqls.must_equal ["UPDATE artists SET modified_on = CURRENT_TIMESTAMP WHERE (id = 4)",
209
209
  "UPDATE albums SET modified_on = CURRENT_TIMESTAMP WHERE (albums.artist_id = 4)"]
210
210
  end
211
+
212
+ it "should freeze touched associations when freezing model class" do
213
+ @Artist.plugin :touch, :associations=>:albums
214
+ @Artist.freeze
215
+ @Artist.touched_associations.frozen?.must_equal true
216
+ end
211
217
  end
@@ -274,5 +274,11 @@ describe Sequel::Model, "tree plugin with composite keys" do
274
274
  @c.dataset = @c.dataset.with_fetch(:id=>1, :id2=>6, :parent_id=>nil, :parent_id2=>2, :name=>'r')
275
275
  @c.root.update(:name => 'fdsa')
276
276
  end
277
+
278
+ it "freezes tree_order if it is an array" do
279
+ @c.tree_order = [:id]
280
+ @c.freeze
281
+ @c.tree_order.frozen?.must_equal true
282
+ end
277
283
  end
278
284
  end
@@ -77,4 +77,10 @@ describe Sequel::Model, "TypecastOnLoad plugin" do
77
77
  @c.plugin :typecast_on_load, :b
78
78
  @c.load(:id=>1, :b=>"1", :y=>"0").modified?.must_equal false
79
79
  end
80
+
81
+ it "should freeze typecast_on_load columns when freezing model class" do
82
+ @c.plugin :typecast_on_load, :b
83
+ @c.freeze
84
+ @c.typecast_on_load_columns.frozen?.must_equal true
85
+ end
80
86
  end
@@ -38,7 +38,6 @@ describe "Sequel::Plugins::UpdateRefresh" do
38
38
  o.name.must_equal 'b'
39
39
  end
40
40
 
41
-
42
41
  it "should refresh the instance after updating when returning specific columns" do
43
42
  @db.extend_datasets{def supports_returning?(x) true end; def update_sql(*); sql = super; update_returning_sql(sql); sql end}
44
43
  @c.plugin :insert_returning_select
@@ -50,4 +49,11 @@ describe "Sequel::Plugins::UpdateRefresh" do
50
49
  @db.sqls.must_equal ["UPDATE test SET name = 'a' WHERE (id = 1) RETURNING id, name"]
51
50
  o.name.must_equal 'b'
52
51
  end
52
+
53
+ it "should freeze update refresh columns when freezing model class" do
54
+ @db.extend_datasets{def supports_returning?(x) true end; def update_sql(*); sql = super; update_returning_sql(sql); sql end}
55
+ @c.plugin :update_refresh, :columns => [ :a ]
56
+ @c.freeze
57
+ @c.update_refresh_columns.frozen?.must_equal true
58
+ end
53
59
  end
@@ -16,6 +16,19 @@ describe Sequel::Model do
16
16
  end
17
17
  end
18
18
 
19
+ it "should freeze validation metadata when freezing model class" do
20
+ @c.validates_acceptance_of(:a)
21
+ @c.freeze
22
+ @c.validations.frozen?.must_equal true
23
+ @c.validations.values.all?(&:frozen?).must_equal true
24
+ @c.validation_reflections.frozen?.must_equal true
25
+ @c.validation_reflections.values.all? do |vs|
26
+ vs.frozen? && vs.all? do |v|
27
+ v.frozen? && v.last.frozen?
28
+ end
29
+ end.must_equal true
30
+ end
31
+
19
32
  it "should respond to validations, has_validations?, and validation_reflections" do
20
33
  @c.must_respond_to(:validations)
21
34
  @c.must_respond_to(:has_validations?)
@@ -15,6 +15,19 @@ describe Sequel::Model::Associations::AssociationReflection, "#associated_class"
15
15
  @c.association_reflection(:c).associated_class.must_equal ParParent
16
16
  end
17
17
 
18
+ it "should have inspect include association class and representation of association definition " do
19
+ ParParent.many_to_one :c
20
+ ParParent.association_reflection(:c).inspect.must_equal "#<Sequel::Model::Associations::ManyToOneAssociationReflection ParParent.many_to_one :c>"
21
+ ParParent.many_to_one :c, :class=>ParParent
22
+ ParParent.association_reflection(:c).inspect.must_equal "#<Sequel::Model::Associations::ManyToOneAssociationReflection ParParent.many_to_one :c, :class=>ParParent>"
23
+ ParParent.many_to_one :c, :class=>ParParent, :key=>:c_id
24
+ ["#<Sequel::Model::Associations::ManyToOneAssociationReflection ParParent.many_to_one :c, :key=>:c_id, :class=>ParParent>",
25
+ "#<Sequel::Model::Associations::ManyToOneAssociationReflection ParParent.many_to_one :c, :class=>ParParent, :key=>:c_id>"].must_include ParParent.association_reflection(:c).inspect
26
+
27
+ @c.one_to_many :foos do |ds| ds end
28
+ @c.association_reflection(:foos).inspect.must_equal "#<Sequel::Model::Associations::OneToManyAssociationReflection #{@c.to_s}.one_to_many :foos, :block=>#{@c.association_reflection(:foos)[:block].inspect}>"
29
+ end
30
+
18
31
  it "should figure out the class if the :class value is not present" do
19
32
  @c.many_to_one :c, :class=>'ParParent'
20
33
  @c.association_reflection(:c).keys.wont_include(:class)
@@ -30,6 +43,17 @@ describe Sequel::Model::Associations::AssociationReflection, "#associated_class"
30
43
  ParParent.many_to_one :par_parent, :class=>'ParParent', :class_namespace=>'ParParent'
31
44
  ParParent.association_reflection(:par_parent).associated_class.must_equal ParParent::ParParent
32
45
  end
46
+
47
+ it "should include association inspect output if an exception would be raised" do
48
+ r = @c.many_to_one(:c)
49
+
50
+ begin
51
+ r.associated_class
52
+ rescue NameError => e
53
+ end
54
+
55
+ e.message.must_include r.inspect
56
+ end
33
57
  end
34
58
 
35
59
  describe Sequel::Model::Associations::AssociationReflection, "#primary_key" do
@@ -563,3 +587,156 @@ describe Sequel::Model::Associations::AssociationReflection, "with default assoc
563
587
  r[:bar].must_equal 2
564
588
  end
565
589
  end
590
+
591
+ describe "Sequel::Model.freeze" do
592
+ it "should freeze the model class and not allow any changes to associations" do
593
+ model = Class.new(Sequel::Model(:items))
594
+ model.many_to_one :foo, :class=>model, :key=>:id
595
+ model.default_association_options = {:read_only=>true}
596
+ model.freeze
597
+
598
+ model.association_reflections.frozen?.must_equal true
599
+ model.association_reflection(:foo).frozen?.must_equal true
600
+ model.autoreloading_associations.frozen?.must_equal true
601
+ model.autoreloading_associations[:id].frozen?.must_equal true
602
+ model.default_association_options.frozen?.must_equal true
603
+ end
604
+
605
+ it "should allow subclasses of frozen model classes to modify associations" do
606
+ model = Class.new(Sequel::Model(:items))
607
+ model.many_to_one :foo, :class=>model, :key=>:id
608
+ model.freeze
609
+ model = Class.new(model)
610
+ model.dataset = :items2
611
+
612
+ model.association_reflection(:foo).frozen?.must_equal true
613
+ model.autoreloading_associations.frozen?.must_equal false
614
+ model.autoreloading_associations[:id].frozen?.must_equal false
615
+
616
+ model.many_to_one :bar, :class=>model, :key=>:id
617
+ model.many_to_one :foo, :class=>model, :key=>:id
618
+ model.association_reflections.frozen?.must_equal false
619
+ model.association_reflection(:foo).frozen?.must_equal false
620
+ model.association_reflection(:bar).frozen?.must_equal false
621
+
622
+ model.default_association_options.frozen?.wont_equal true
623
+ model.default_association_options = {:read_only=>true}
624
+ model.default_association_options.frozen?.wont_equal true
625
+ end
626
+ end
627
+
628
+ describe "Sequel::Model.finalize_associations" do
629
+ before do
630
+ class ::MtmItem < Sequel::Model
631
+ set_primary_key :mtm_id
632
+ many_to_many :items
633
+ many_to_one :item
634
+ end
635
+ class ::OtoItem < Sequel::Model
636
+ set_primary_key :oto_id
637
+ end
638
+ class ::Item < Sequel::Model
639
+ many_to_one :item
640
+ one_to_many :items, :limit=>10
641
+ one_to_one :mtm_item
642
+ many_to_many :mtm_items
643
+ one_through_one :oto_item
644
+ end
645
+ [MtmItem, OtoItem, Item].each(&:finalize_associations)
646
+ end
647
+ after do
648
+ Object.send(:remove_const, :Item)
649
+ Object.send(:remove_const, :MtmItem)
650
+ Object.send(:remove_const, :OtoItem)
651
+ end
652
+
653
+ it "should finalize many_to_one associations" do
654
+ r = Item.association_reflection(:item)
655
+ r[:class].must_equal Item
656
+ r[:_dataset].sql.must_equal "SELECT * FROM items LIMIT 1"
657
+ r[:associated_eager_dataset].sql.must_equal "SELECT * FROM items"
658
+ r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT items.id FROM items WHERE (items.id IS NOT NULL)"
659
+ r[:placeholder_loader].wont_be_nil
660
+ r[:predicate_key].must_equal Sequel.qualify(:items, :id)
661
+ r[:primary_key].must_equal :id
662
+ r[:primary_keys].must_equal [:id]
663
+ r[:primary_key_method].must_equal :id
664
+ r[:primary_key_methods].must_equal [:id]
665
+ r[:qualified_primary_key].must_equal Sequel.qualify(:items, :id)
666
+ r.fetch(:reciprocal_type).must_equal :one_to_many
667
+ r.fetch(:reciprocal).must_equal :items
668
+ end
669
+
670
+ it "should finalize one_to_many associations" do
671
+ r = Item.association_reflection(:items)
672
+ r[:class].must_equal Item
673
+ r[:_dataset].sql.must_equal "SELECT * FROM items LIMIT 10"
674
+ r[:associated_eager_dataset].sql.must_equal "SELECT * FROM items"
675
+ r[:_eager_limit_strategy].must_equal :union
676
+ r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT items.item_id FROM items WHERE ((items.item_id IS NOT NULL) AND (items.id IN (SELECT t1.id FROM items AS t1 WHERE (t1.item_id = items.item_id) LIMIT 10)))"
677
+ r[:placeholder_loader].wont_be_nil
678
+ r[:predicate_key].must_equal Sequel.qualify(:items, :item_id)
679
+ r[:predicate_keys].must_equal [Sequel.qualify(:items, :item_id)]
680
+ r[:qualified_primary_key].must_equal Sequel.qualify(:items, :id)
681
+ r.fetch(:reciprocal).must_equal :item
682
+ end
683
+
684
+ it "should finalize one_to_one associations" do
685
+ r = Item.association_reflection(:mtm_item)
686
+ r[:class].must_equal MtmItem
687
+ r[:_dataset].sql.must_equal "SELECT * FROM mtm_items LIMIT 1"
688
+ r[:associated_eager_dataset].sql.must_equal "SELECT * FROM mtm_items"
689
+ r[:_eager_limit_strategy].must_be_nil
690
+ r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT mtm_items.item_id FROM mtm_items WHERE (mtm_items.item_id IS NOT NULL)"
691
+ r[:placeholder_loader].wont_be_nil
692
+ r[:predicate_key].must_equal Sequel.qualify(:mtm_items, :item_id)
693
+ r[:predicate_keys].must_equal [Sequel.qualify(:mtm_items, :item_id)]
694
+ r[:qualified_primary_key].must_equal Sequel.qualify(:items, :id)
695
+ r.fetch(:reciprocal).must_equal :item
696
+ end
697
+
698
+ it "should finalize many_to_many associations" do
699
+ r = Item.association_reflection(:mtm_items)
700
+ r[:class].must_equal MtmItem
701
+ r[:_dataset].sql.must_equal "SELECT mtm_items.* FROM mtm_items INNER JOIN items_mtm_items ON (items_mtm_items.mtm_item_id = mtm_items.mtm_id)"
702
+ r[:associated_eager_dataset].sql.must_equal "SELECT mtm_items.* FROM mtm_items INNER JOIN items_mtm_items ON (items_mtm_items.mtm_item_id = mtm_items.mtm_id)"
703
+ r[:_eager_limit_strategy].must_be_nil
704
+ r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT items_mtm_items.item_id FROM mtm_items INNER JOIN items_mtm_items ON (items_mtm_items.mtm_item_id = mtm_items.mtm_id) WHERE (items_mtm_items.item_id IS NOT NULL)"
705
+ r[:placeholder_loader].wont_be_nil
706
+ r[:predicate_key].must_equal Sequel.qualify(:items_mtm_items, :item_id)
707
+ r[:predicate_keys].must_equal [Sequel.qualify(:items_mtm_items, :item_id)]
708
+ r.fetch(:reciprocal).must_equal :items
709
+ r[:associated_key_array].must_equal [Sequel.qualify(:items_mtm_items, :item_id).as(:x_foreign_key_x)]
710
+ r[:qualified_right_key].must_equal Sequel.qualify(:items_mtm_items, :mtm_item_id)
711
+ r[:join_table_source].must_equal :items_mtm_items
712
+ r[:join_table_alias].must_equal :items_mtm_items
713
+ r[:qualified_right_primary_key].must_equal Sequel.qualify(:mtm_items, :mtm_id)
714
+ r[:right_primary_key].must_equal :mtm_id
715
+ r[:right_primary_keys].must_equal [:mtm_id]
716
+ r[:right_primary_key_method].must_equal :mtm_id
717
+ r[:right_primary_key_methods].must_equal [:mtm_id]
718
+ r[:select].must_equal Sequel::SQL::ColumnAll.new(:mtm_items)
719
+ end
720
+
721
+ it "should finalize one_through_one associations" do
722
+ r = Item.association_reflection(:oto_item)
723
+ r[:class].must_equal OtoItem
724
+ r[:_dataset].sql.must_equal "SELECT oto_items.* FROM oto_items INNER JOIN items_oto_items ON (items_oto_items.oto_item_id = oto_items.oto_id) LIMIT 1"
725
+ r[:associated_eager_dataset].sql.must_equal "SELECT oto_items.* FROM oto_items INNER JOIN items_oto_items ON (items_oto_items.oto_item_id = oto_items.oto_id)"
726
+ r[:_eager_limit_strategy].must_be_nil
727
+ r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT items_oto_items.item_id FROM oto_items INNER JOIN items_oto_items ON (items_oto_items.oto_item_id = oto_items.oto_id) WHERE (items_oto_items.item_id IS NOT NULL)"
728
+ r[:placeholder_loader].wont_be_nil
729
+ r[:predicate_key].must_equal Sequel.qualify(:items_oto_items, :item_id)
730
+ r[:predicate_keys].must_equal [Sequel.qualify(:items_oto_items, :item_id)]
731
+ r[:associated_key_array].must_equal [Sequel.qualify(:items_oto_items, :item_id).as(:x_foreign_key_x)]
732
+ r[:qualified_right_key].must_equal Sequel.qualify(:items_oto_items, :oto_item_id)
733
+ r[:join_table_source].must_equal :items_oto_items
734
+ r[:join_table_alias].must_equal :items_oto_items
735
+ r[:qualified_right_primary_key].must_equal Sequel.qualify(:oto_items, :oto_id)
736
+ r[:right_primary_key].must_equal :oto_id
737
+ r[:right_primary_keys].must_equal [:oto_id]
738
+ r[:right_primary_key_method].must_equal :oto_id
739
+ r[:right_primary_key_methods].must_equal [:oto_id]
740
+ r[:select].must_equal Sequel::SQL::ColumnAll.new(:oto_items)
741
+ end
742
+ end
@@ -4624,3 +4624,19 @@ describe "association autoreloading" do
4624
4624
  DB.sqls.must_equal ['SELECT * FROM artists WHERE id = 1']
4625
4625
  end
4626
4626
  end
4627
+
4628
+ describe Sequel::Model, ".dataset_module" do
4629
+ before do
4630
+ @c = Class.new(Sequel::Model(:items))
4631
+ end
4632
+
4633
+ it "should have dataset_module support an eager method" do
4634
+ @c.many_to_one :foo, :class=>@c
4635
+ @c.many_to_one :bar, :class=>@c
4636
+ @c.many_to_one :baz, :class=>@c
4637
+ @c.many_to_one :quux, :class=>@c
4638
+ @c.dataset_module{eager(:foo, {:foo=>{:bar=>:baz}}, :quux)}
4639
+ @c.foo.opts[:eager].must_equal(:foo=>{:bar=>:baz}, :quux=>nil)
4640
+ @c.where(:bar).foo.opts[:eager].must_equal(:foo=>{:bar=>:baz}, :quux=>nil)
4641
+ end
4642
+ end