sequel 3.5.0 → 3.6.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 +108 -0
- data/README.rdoc +25 -14
- data/Rakefile +20 -1
- data/doc/advanced_associations.rdoc +61 -64
- data/doc/cheat_sheet.rdoc +16 -7
- data/doc/opening_databases.rdoc +3 -3
- data/doc/prepared_statements.rdoc +1 -1
- data/doc/reflection.rdoc +2 -1
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/schema.rdoc +19 -14
- data/lib/sequel/adapters/amalgalite.rb +5 -27
- data/lib/sequel/adapters/jdbc.rb +13 -3
- data/lib/sequel/adapters/jdbc/h2.rb +17 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +20 -7
- data/lib/sequel/adapters/mysql.rb +4 -3
- data/lib/sequel/adapters/oracle.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +87 -28
- data/lib/sequel/adapters/shared/mssql.rb +47 -6
- data/lib/sequel/adapters/shared/mysql.rb +12 -31
- data/lib/sequel/adapters/shared/postgres.rb +15 -12
- data/lib/sequel/adapters/shared/sqlite.rb +18 -0
- data/lib/sequel/adapters/sqlite.rb +1 -16
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +2 -0
- data/lib/sequel/database/schema_sql.rb +1 -1
- data/lib/sequel/dataset.rb +5 -179
- data/lib/sequel/dataset/actions.rb +123 -0
- data/lib/sequel/dataset/convenience.rb +18 -10
- data/lib/sequel/dataset/features.rb +65 -0
- data/lib/sequel/dataset/prepared_statements.rb +29 -23
- data/lib/sequel/dataset/query.rb +429 -0
- data/lib/sequel/dataset/sql.rb +67 -435
- data/lib/sequel/model/associations.rb +77 -13
- data/lib/sequel/model/base.rb +30 -8
- data/lib/sequel/model/errors.rb +4 -4
- data/lib/sequel/plugins/caching.rb +38 -15
- data/lib/sequel/plugins/force_encoding.rb +18 -7
- data/lib/sequel/plugins/hook_class_methods.rb +4 -0
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +40 -11
- data/lib/sequel/plugins/serialization.rb +17 -3
- data/lib/sequel/plugins/validation_helpers.rb +65 -18
- data/lib/sequel/sql.rb +23 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +96 -10
- data/spec/adapters/mysql_spec.rb +19 -0
- data/spec/adapters/postgres_spec.rb +65 -2
- data/spec/adapters/sqlite_spec.rb +10 -0
- data/spec/core/core_sql_spec.rb +9 -0
- data/spec/core/database_spec.rb +8 -4
- data/spec/core/dataset_spec.rb +122 -29
- data/spec/core/expression_filters_spec.rb +17 -0
- data/spec/extensions/caching_spec.rb +43 -3
- data/spec/extensions/force_encoding_spec.rb +43 -1
- data/spec/extensions/nested_attributes_spec.rb +55 -2
- data/spec/extensions/validation_helpers_spec.rb +71 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/dataset_test.rb +383 -9
- data/spec/integration/eager_loader_test.rb +0 -65
- data/spec/integration/model_test.rb +110 -0
- data/spec/integration/plugin_test.rb +306 -0
- data/spec/integration/prepared_statement_test.rb +32 -0
- data/spec/integration/schema_test.rb +8 -3
- data/spec/integration/spec_helper.rb +1 -25
- data/spec/model/association_reflection_spec.rb +38 -0
- data/spec/model/associations_spec.rb +184 -8
- data/spec/model/eager_loading_spec.rb +23 -0
- data/spec/model/model_spec.rb +8 -0
- data/spec/model/record_spec.rb +84 -1
- metadata +9 -2
@@ -23,11 +23,43 @@ describe "Prepared Statements and Bound Arguments" do
|
|
23
23
|
@ds.filter(:number=>@ds.ba(:$n)).call(:all, :n=>10).should == [{:id=>1, :number=>10}]
|
24
24
|
@ds.filter(:number=>@ds.ba(:$n)).call(:first, :n=>10).should == {:id=>1, :number=>10}
|
25
25
|
end
|
26
|
+
|
27
|
+
specify "should support blocks for select and all" do
|
28
|
+
@ds.filter(:number=>@ds.ba(:$n)).call(:select, :n=>10){|r| r[:number] *= 2}.should == [{:id=>1, :number=>20}]
|
29
|
+
@ds.filter(:number=>@ds.ba(:$n)).call(:all, :n=>10){|r| r[:number] *= 2}.should == [{:id=>1, :number=>20}]
|
30
|
+
end
|
31
|
+
|
32
|
+
specify "should support binding variables before the call with #bind" do
|
33
|
+
@ds.filter(:number=>@ds.ba(:$n)).bind(:n=>10).call(:select).should == [{:id=>1, :number=>10}]
|
34
|
+
@ds.filter(:number=>@ds.ba(:$n)).bind(:n=>10).call(:all).should == [{:id=>1, :number=>10}]
|
35
|
+
@ds.filter(:number=>@ds.ba(:$n)).bind(:n=>10).call(:first).should == {:id=>1, :number=>10}
|
36
|
+
|
37
|
+
@ds.bind(:n=>10).filter(:number=>@ds.ba(:$n)).call(:select).should == [{:id=>1, :number=>10}]
|
38
|
+
@ds.bind(:n=>10).filter(:number=>@ds.ba(:$n)).call(:all).should == [{:id=>1, :number=>10}]
|
39
|
+
@ds.bind(:n=>10).filter(:number=>@ds.ba(:$n)).call(:first).should == {:id=>1, :number=>10}
|
40
|
+
end
|
41
|
+
|
42
|
+
specify "should allow overriding variables specified with #bind" do
|
43
|
+
@ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
|
44
|
+
@ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).call(:all, :n=>10).should == [{:id=>1, :number=>10}]
|
45
|
+
@ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).call(:first, :n=>10).should == {:id=>1, :number=>10}
|
46
|
+
|
47
|
+
@ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).bind(:n=>10).call(:select).should == [{:id=>1, :number=>10}]
|
48
|
+
@ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).bind(:n=>10).call(:all).should == [{:id=>1, :number=>10}]
|
49
|
+
@ds.filter(:number=>@ds.ba(:$n)).bind(:n=>1).bind(:n=>10).call(:first).should == {:id=>1, :number=>10}
|
50
|
+
end
|
26
51
|
|
27
52
|
specify "should support placeholder literal strings" do
|
28
53
|
@ds.filter("number = ?", @ds.ba(:$n)).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
|
29
54
|
end
|
30
55
|
|
56
|
+
specify "should support named placeholder literal strings and handle multiple named placeholders correctly" do
|
57
|
+
@ds.filter("number = :n", :n=>@ds.ba(:$n)).call(:select, :n=>10).should == [{:id=>1, :number=>10}]
|
58
|
+
@ds.insert(:number=>20)
|
59
|
+
@ds.insert(:number=>30)
|
60
|
+
@ds.filter("number > :n1 AND number < :n2 AND number = :n3", :n3=>@ds.ba(:$n3), :n2=>@ds.ba(:$n2), :n1=>@ds.ba(:$n1)).call(:select, :n3=>20, :n2=>30, :n1=>10).should == [{:id=>2, :number=>20}]
|
61
|
+
end
|
62
|
+
|
31
63
|
specify "should support datasets with static sql and placeholders" do
|
32
64
|
INTEGRATION_DB["SELECT * FROM items WHERE number = ?", @ds.ba(:$n)].call(:select, :n=>10).should == [{:id=>1, :number=>10}]
|
33
65
|
end
|
@@ -29,10 +29,8 @@ describe "Database schema parser" do
|
|
29
29
|
INTEGRATION_DB.schema(:items, :reload=>true)
|
30
30
|
clear_sqls
|
31
31
|
INTEGRATION_DB.schema(:items)
|
32
|
-
sqls_should_be
|
33
32
|
clear_sqls
|
34
33
|
INTEGRATION_DB.schema(:items, :reload=>true)
|
35
|
-
sqls_should_be "PRAGMA table_info('items')"
|
36
34
|
end
|
37
35
|
|
38
36
|
specify "should raise an error when the table doesn't exist" do
|
@@ -53,7 +51,6 @@ describe "Database schema parser" do
|
|
53
51
|
col_info[:type].should == :integer
|
54
52
|
clear_sqls
|
55
53
|
INTEGRATION_DB.schema(:items)
|
56
|
-
sqls_should_be
|
57
54
|
end
|
58
55
|
|
59
56
|
cspecify "should parse primary keys from the schema properly", [proc{|db| db.class.adapter_scheme != :jdbc}, :mssql] do
|
@@ -161,6 +158,14 @@ describe "Database schema modifiers" do
|
|
161
158
|
@ds.insert([10])
|
162
159
|
@ds.columns!.should == [:number]
|
163
160
|
end
|
161
|
+
|
162
|
+
specify "should allow creating indexes with tables" do
|
163
|
+
@db.create_table!(:items){Integer :number; index :number}
|
164
|
+
@db.table_exists?(:items).should == true
|
165
|
+
@db.schema(:items, :reload=>true).map{|x| x.first}.should == [:number]
|
166
|
+
@ds.insert([10])
|
167
|
+
@ds.columns!.should == [:number]
|
168
|
+
end
|
164
169
|
|
165
170
|
specify "should handle foreign keys correctly when creating tables" do
|
166
171
|
@db.create_table!(:items) do
|
@@ -64,30 +64,6 @@ if defined?(INTEGRATION_DB) || defined?(INTEGRATION_URL) || ENV['SEQUEL_INTEGRAT
|
|
64
64
|
INTEGRATION_DB = Sequel.connect(url)
|
65
65
|
#INTEGRATION_DB.instance_variable_set(:@server_version, 80100)
|
66
66
|
end
|
67
|
-
class Spec::Example::ExampleGroup
|
68
|
-
def sqls_should_be(*args)
|
69
|
-
end
|
70
|
-
end
|
71
67
|
else
|
72
|
-
|
73
|
-
def sql_logger.info(str)
|
74
|
-
$sqls << str
|
75
|
-
end
|
76
|
-
INTEGRATION_DB = Sequel.sqlite('', :loggers=>[sql_logger], :quote_identifiers=>false)
|
77
|
-
class Spec::Example::ExampleGroup
|
78
|
-
def sqls_should_be(*sqls)
|
79
|
-
sqls.zip($sqls).each do |should_be, is|
|
80
|
-
case should_be
|
81
|
-
when String
|
82
|
-
is.should == should_be
|
83
|
-
when Regexp
|
84
|
-
is.should =~ should_be
|
85
|
-
else
|
86
|
-
raise ArgumentError, "need String or RegExp"
|
87
|
-
end
|
88
|
-
end
|
89
|
-
$sqls.length.should == sqls.length
|
90
|
-
clear_sqls
|
91
|
-
end
|
92
|
-
end
|
68
|
+
INTEGRATION_DB = Sequel.sqlite('', :quote_identifiers=>false)
|
93
69
|
end
|
@@ -135,3 +135,41 @@ describe Sequel::Model::Associations::AssociationReflection, "#select" do
|
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
138
|
+
describe Sequel::Model::Associations::AssociationReflection, "#can_have_associated_objects?" do
|
139
|
+
it "should be true for any given object (for backward compatibility)" do
|
140
|
+
Sequel::Model::Associations::AssociationReflection.new.can_have_associated_objects?(Object.new).should == true
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe Sequel::Model::Associations::AssociationReflection, "#associated_object_keys" do
|
145
|
+
before do
|
146
|
+
@c = Class.new(Sequel::Model)
|
147
|
+
class ::ParParent < Sequel::Model; end
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should use the primary keys for a many_to_one association" do
|
151
|
+
@c.many_to_one :c, :class=>ParParent
|
152
|
+
@c.association_reflection(:c).associated_object_keys.should == [:id]
|
153
|
+
@c.many_to_one :c, :class=>ParParent, :primary_key=>:d_id
|
154
|
+
@c.association_reflection(:c).associated_object_keys.should == [:d_id]
|
155
|
+
@c.many_to_one :c, :class=>ParParent, :key=>[:c_id1, :c_id2], :primary_key=>[:id1, :id2]
|
156
|
+
@c.association_reflection(:c).associated_object_keys.should == [:id1, :id2]
|
157
|
+
end
|
158
|
+
it "should use the keys for a one_to_many association" do
|
159
|
+
ParParent.one_to_many :cs, :class=>ParParent
|
160
|
+
ParParent.association_reflection(:cs).associated_object_keys.should == [:par_parent_id]
|
161
|
+
@c.one_to_many :cs, :class=>ParParent, :key=>:d_id
|
162
|
+
@c.association_reflection(:cs).associated_object_keys.should == [:d_id]
|
163
|
+
@c.one_to_many :cs, :class=>ParParent, :key=>[:c_id1, :c_id2], :primary_key=>[:id1, :id2]
|
164
|
+
@c.association_reflection(:cs).associated_object_keys.should == [:c_id1, :c_id2]
|
165
|
+
end
|
166
|
+
it "should use the right primary keys for a many_to_many association" do
|
167
|
+
@c.many_to_many :cs, :class=>ParParent
|
168
|
+
@c.association_reflection(:cs).associated_object_keys.should == [:id]
|
169
|
+
@c.many_to_many :cs, :class=>ParParent, :right_primary_key=>:d_id
|
170
|
+
@c.association_reflection(:cs).associated_object_keys.should == [:d_id]
|
171
|
+
@c.many_to_many :cs, :class=>ParParent, :right_key=>[:c_id1, :c_id2], :right_primary_key=>[:id1, :id2]
|
172
|
+
@c.association_reflection(:cs).associated_object_keys.should == [:id1, :id2]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
@@ -193,6 +193,12 @@ describe Sequel::Model, "many_to_one" do
|
|
193
193
|
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.parent_id = 1) AND (nodes.id = 234)) LIMIT 1"]
|
194
194
|
end
|
195
195
|
|
196
|
+
it "should not issue query if not all keys have values" do
|
197
|
+
@c2.many_to_one :parent, :class => @c2, :key=>[:id, :parent_id], :primary_key=>[:parent_id, :id]
|
198
|
+
@c2.new(:id => 1, :parent_id => nil).parent.should == nil
|
199
|
+
MODEL_DB.sqls.should == []
|
200
|
+
end
|
201
|
+
|
196
202
|
it "should raise an Error unless same number of composite keys used" do
|
197
203
|
proc{@c2.many_to_one :parent, :class => @c2, :primary_key=>[:parent_id, :id]}.should raise_error(Sequel::Error)
|
198
204
|
proc{@c2.many_to_one :parent, :class => @c2, :key=>[:id, :parent_id], :primary_key=>:id}.should raise_error(Sequel::Error)
|
@@ -552,11 +558,13 @@ describe Sequel::Model, "one_to_many" do
|
|
552
558
|
MODEL_DB.reset
|
553
559
|
|
554
560
|
@c1 = Class.new(Sequel::Model(:attributes)) do
|
561
|
+
def _refresh(ds); end
|
555
562
|
unrestrict_primary_key
|
556
563
|
columns :id, :node_id, :y
|
557
564
|
end
|
558
565
|
|
559
566
|
@c2 = Class.new(Sequel::Model(:nodes)) do
|
567
|
+
def _refresh(ds); end
|
560
568
|
unrestrict_primary_key
|
561
569
|
attr_accessor :xxx
|
562
570
|
|
@@ -567,6 +575,7 @@ describe Sequel::Model, "one_to_many" do
|
|
567
575
|
@dataset = @c2.dataset
|
568
576
|
|
569
577
|
@c2.dataset.extend(Module.new {
|
578
|
+
def empty?; false; end
|
570
579
|
def fetch_rows(sql)
|
571
580
|
@db << sql
|
572
581
|
yield Hash.new
|
@@ -574,6 +583,7 @@ describe Sequel::Model, "one_to_many" do
|
|
574
583
|
})
|
575
584
|
|
576
585
|
@c1.dataset.extend(Module.new {
|
586
|
+
def empty?; opts.has_key?(:empty) ? (super; true) : false; end
|
577
587
|
def fetch_rows(sql)
|
578
588
|
@db << sql
|
579
589
|
yield Hash.new
|
@@ -632,6 +642,12 @@ describe Sequel::Model, "one_to_many" do
|
|
632
642
|
@c2.load(:id => 1234, :x=>234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.id = 234))'
|
633
643
|
end
|
634
644
|
|
645
|
+
it "should not issue query if not all keys have values" do
|
646
|
+
@c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :id], :primary_key=>[:id, :x]
|
647
|
+
@c2.load(:id => 1234, :x=>nil).attributes.should == []
|
648
|
+
MODEL_DB.sqls.should == []
|
649
|
+
end
|
650
|
+
|
635
651
|
it "should raise an Error unless same number of composite keys used" do
|
636
652
|
proc{@c2.one_to_many :attributes, :class => @c1, :key=>[:node_id, :id]}.should raise_error(Sequel::Error)
|
637
653
|
proc{@c2.one_to_many :attributes, :class => @c1, :primary_key=>[:node_id, :id]}.should raise_error(Sequel::Error)
|
@@ -640,7 +656,7 @@ describe Sequel::Model, "one_to_many" do
|
|
640
656
|
proc{@c2.one_to_many :attributes, :class => @c1, :key=>[:node_id, :id, :x], :primary_key=>[:parent_id, :id]}.should raise_error(Sequel::Error)
|
641
657
|
end
|
642
658
|
|
643
|
-
it "should define an add_ method" do
|
659
|
+
it "should define an add_ method that works on existing records" do
|
644
660
|
@c2.one_to_many :attributes, :class => @c1
|
645
661
|
|
646
662
|
n = @c2.new(:id => 1234)
|
@@ -648,20 +664,77 @@ describe Sequel::Model, "one_to_many" do
|
|
648
664
|
a.save
|
649
665
|
MODEL_DB.reset
|
650
666
|
a.should == n.add_attribute(a)
|
667
|
+
a.values.should == {:node_id => 1234, :id => 2345}
|
651
668
|
MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)']
|
652
669
|
end
|
653
670
|
|
654
|
-
it "should define
|
671
|
+
it "should define an add_ method that works on new records" do
|
655
672
|
@c2.one_to_many :attributes, :class => @c1
|
656
673
|
|
657
674
|
n = @c2.new(:id => 1234)
|
658
|
-
a = @c1.new(:id =>
|
675
|
+
a = @c1.new(:id => 234)
|
676
|
+
# do not save
|
677
|
+
MODEL_DB.reset
|
678
|
+
a.should == n.add_attribute(a)
|
679
|
+
MODEL_DB.sqls.first.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
|
680
|
+
a.values.should == {:node_id => 1234, :id => 234}
|
681
|
+
end
|
682
|
+
|
683
|
+
it "should define a remove_ method that works on existing records" do
|
684
|
+
@c2.one_to_many :attributes, :class => @c1
|
685
|
+
|
686
|
+
n = @c2.new(:id => 1234)
|
687
|
+
a = @c1.new(:id => 2345, :node_id => 1234)
|
659
688
|
a.save
|
660
689
|
MODEL_DB.reset
|
661
690
|
a.should == n.remove_attribute(a)
|
691
|
+
a.values.should == {:node_id => nil, :id => 2345}
|
662
692
|
MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
|
663
693
|
end
|
694
|
+
|
695
|
+
it "should have the remove_ method raise an error if the passed object is not already associated" do
|
696
|
+
@c2.one_to_many :attributes, :class => @c1
|
697
|
+
@c1.dataset.opts[:empty] = true
|
698
|
+
|
699
|
+
n = @c2.new(:id => 1234)
|
700
|
+
a = @c1.load(:id => 2345, :node_id => 1234)
|
701
|
+
MODEL_DB.reset
|
702
|
+
proc{n.remove_attribute(a)}.should raise_error(Sequel::Error)
|
703
|
+
MODEL_DB.sqls.should == ["SELECT 1 FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1"]
|
704
|
+
end
|
705
|
+
|
706
|
+
it "should accept a hash for the add_ method and create a new record" do
|
707
|
+
@c2.one_to_many :attributes, :class => @c1
|
708
|
+
n = @c2.new(:id => 1234)
|
709
|
+
MODEL_DB.reset
|
710
|
+
@c1.load(:node_id => 1234, :id => 234).should == n.add_attribute(:id => 234)
|
711
|
+
MODEL_DB.sqls.first.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
|
712
|
+
end
|
713
|
+
|
714
|
+
it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
|
715
|
+
@c2.one_to_many :attributes, :class => @c1
|
716
|
+
proc{@c2.new(:id => 1234).add_attribute(@c2.new)}.should raise_error(Sequel::Error)
|
717
|
+
end
|
718
|
+
|
719
|
+
it "should accept a primary key for the remove_ method and remove an existing record" do
|
720
|
+
@c2.one_to_many :attributes, :class => @c1
|
721
|
+
n = @c2.new(:id => 1234)
|
722
|
+
ds = @c1.dataset
|
723
|
+
def ds.fetch_rows(sql)
|
724
|
+
db << sql
|
725
|
+
yield({:id=>234, :node_id=>1234})
|
726
|
+
end
|
727
|
+
MODEL_DB.reset
|
728
|
+
@c1.load(:node_id => nil, :id => 234).should == n.remove_attribute(234)
|
729
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 234)) LIMIT 1',
|
730
|
+
'UPDATE attributes SET node_id = NULL WHERE (id = 234)']
|
731
|
+
end
|
664
732
|
|
733
|
+
it "should raise an error in the remove_ method if the passed associated object is not of the correct type" do
|
734
|
+
@c2.one_to_many :attributes, :class => @c1
|
735
|
+
proc{@c2.new(:id => 1234).remove_attribute(@c2.new)}.should raise_error(Sequel::Error)
|
736
|
+
end
|
737
|
+
|
665
738
|
it "should have add_ method respect the :primary_key option" do
|
666
739
|
@c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
|
667
740
|
|
@@ -672,6 +745,21 @@ describe Sequel::Model, "one_to_many" do
|
|
672
745
|
a.should == n.add_attribute(a)
|
673
746
|
MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 5 WHERE (id = 2345)']
|
674
747
|
end
|
748
|
+
|
749
|
+
it "should have add_ method not add the same object to the cached association array if the object is already in the array" do
|
750
|
+
@c2.one_to_many :attributes, :class => @c1
|
751
|
+
|
752
|
+
n = @c2.new(:id => 1234)
|
753
|
+
a = @c1.new(:id => 2345)
|
754
|
+
a.save
|
755
|
+
MODEL_DB.reset
|
756
|
+
n.associations[:attributes] = []
|
757
|
+
a.should == n.add_attribute(a)
|
758
|
+
a.should == n.add_attribute(a)
|
759
|
+
a.values.should == {:node_id => 1234, :id => 2345}
|
760
|
+
MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)'] * 2
|
761
|
+
n.attributes.should == [a]
|
762
|
+
end
|
675
763
|
|
676
764
|
it "should have add_ method respect composite keys" do
|
677
765
|
@c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :y], :primary_key=>[:id, :x]
|
@@ -691,6 +779,22 @@ describe Sequel::Model, "one_to_many" do
|
|
691
779
|
MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(id = 2345\)/
|
692
780
|
end
|
693
781
|
|
782
|
+
it "should accept a array of composite primary key values for the remove_ method and remove an existing record" do
|
783
|
+
@c1.set_primary_key :id, :y
|
784
|
+
@c2.one_to_many :attributes, :class => @c1, :key=>:node_id, :primary_key=>:id
|
785
|
+
n = @c2.new(:id => 123)
|
786
|
+
ds = @c1.dataset
|
787
|
+
def ds.fetch_rows(sql)
|
788
|
+
db << sql
|
789
|
+
yield({:id=>234, :node_id=>123, :y=>5})
|
790
|
+
end
|
791
|
+
MODEL_DB.reset
|
792
|
+
@c1.load(:node_id => nil, :y => 5, :id => 234).should == n.remove_attribute([234, 5])
|
793
|
+
MODEL_DB.sqls.length.should == 2
|
794
|
+
MODEL_DB.sqls.first.should =~ /SELECT \* FROM attributes WHERE \(\(attributes.node_id = 123\) AND \(\((id|y) = (234|5)\) AND \((id|y) = (234|5)\)\)\) LIMIT 1/
|
795
|
+
MODEL_DB.sqls.last.should =~ /UPDATE attributes SET node_id = NULL WHERE \(\((id|y) = (234|5)\) AND \((id|y) = (234|5)\)\)/
|
796
|
+
end
|
797
|
+
|
694
798
|
it "should raise an error in add_ and remove_ if the passed object returns false to save (is not valid)" do
|
695
799
|
@c2.one_to_many :attributes, :class => @c1
|
696
800
|
n = @c2.new(:id => 1234)
|
@@ -714,7 +818,6 @@ describe Sequel::Model, "one_to_many" do
|
|
714
818
|
a = @c2.new
|
715
819
|
n = @c1.load(:id=>123)
|
716
820
|
proc{a.attributes_dataset}.should raise_error(Sequel::Error)
|
717
|
-
proc{a.attributes}.should raise_error(Sequel::Error)
|
718
821
|
proc{a.add_attribute(n)}.should raise_error(Sequel::Error)
|
719
822
|
proc{a.remove_attribute(n)}.should raise_error(Sequel::Error)
|
720
823
|
proc{a.remove_all_attributes}.should raise_error(Sequel::Error)
|
@@ -1056,6 +1159,7 @@ describe Sequel::Model, "one_to_many" do
|
|
1056
1159
|
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
|
1057
1160
|
attrib = @c1.new(:id=>3)
|
1058
1161
|
d = @c1.dataset
|
1162
|
+
@c1.class_eval{remove_method :_refresh}
|
1059
1163
|
def d.fetch_rows(s); yield({:id=>3}) end
|
1060
1164
|
@c2.new(:id => 1234).attribute = attrib
|
1061
1165
|
['INSERT INTO attributes (node_id, id) VALUES (1234, 3)',
|
@@ -1086,6 +1190,7 @@ describe Sequel::Model, "one_to_many" do
|
|
1086
1190
|
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true, :primary_key=>:xxx
|
1087
1191
|
attrib = @c1.new(:id=>3)
|
1088
1192
|
d = @c1.dataset
|
1193
|
+
@c1.class_eval{remove_method :_refresh}
|
1089
1194
|
def d.fetch_rows(s); yield({:id=>3}) end
|
1090
1195
|
@c2.new(:id => 1234, :xxx=>5).attribute = attrib
|
1091
1196
|
['INSERT INTO attributes (node_id, id) VALUES (5, 3)',
|
@@ -1410,6 +1515,12 @@ describe Sequel::Model, "many_to_many" do
|
|
1410
1515
|
@c2.load(:id => 1234, :x=>5).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.r1 = attributes.id) AND (attributes_nodes.r2 = attributes.y) AND (attributes_nodes.l1 = 1234) AND (attributes_nodes.l2 = 5))'
|
1411
1516
|
end
|
1412
1517
|
|
1518
|
+
it "should not issue query if not all keys have values" do
|
1519
|
+
@c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
|
1520
|
+
@c2.load(:id => 1234, :x=>nil).attributes.should == []
|
1521
|
+
MODEL_DB.sqls.should == []
|
1522
|
+
end
|
1523
|
+
|
1413
1524
|
it "should raise an Error unless same number of composite keys used" do
|
1414
1525
|
proc{@c2.many_to_many :attributes, :class => @c1, :left_key=>[:node_id, :id]}.should raise_error(Sequel::Error)
|
1415
1526
|
proc{@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>[:node_id, :id]}.should raise_error(Sequel::Error)
|
@@ -1504,7 +1615,7 @@ describe Sequel::Model, "many_to_many" do
|
|
1504
1615
|
@c2.new(:id => 1234).attributes_dataset.opts[:eager].should == {:attributes=>nil}
|
1505
1616
|
end
|
1506
1617
|
|
1507
|
-
it "should define an add_ method" do
|
1618
|
+
it "should define an add_ method that works on existing records" do
|
1508
1619
|
@c2.many_to_many :attributes, :class => @c1
|
1509
1620
|
|
1510
1621
|
n = @c2.load(:id => 1234)
|
@@ -1515,7 +1626,18 @@ describe Sequel::Model, "many_to_many" do
|
|
1515
1626
|
].should(include(MODEL_DB.sqls.first))
|
1516
1627
|
end
|
1517
1628
|
|
1518
|
-
it "should
|
1629
|
+
it "should allow passing a hash to the add_ method which creates a new record" do
|
1630
|
+
@c2.many_to_many :attributes, :class => @c1
|
1631
|
+
|
1632
|
+
n = @c2.load(:id => 1234)
|
1633
|
+
@c1.load(:id => 1).should == n.add_attribute(:id => 1)
|
1634
|
+
MODEL_DB.sqls.first.should == 'INSERT INTO attributes (id) VALUES (1)'
|
1635
|
+
['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 1)',
|
1636
|
+
'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (1, 1234)'
|
1637
|
+
].should(include(MODEL_DB.sqls.last))
|
1638
|
+
end
|
1639
|
+
|
1640
|
+
it "should define a remove_ method that works on existing records" do
|
1519
1641
|
@c2.many_to_many :attributes, :class => @c1
|
1520
1642
|
|
1521
1643
|
n = @c2.new(:id => 1234)
|
@@ -1524,6 +1646,30 @@ describe Sequel::Model, "many_to_many" do
|
|
1524
1646
|
MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))'
|
1525
1647
|
end
|
1526
1648
|
|
1649
|
+
it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
|
1650
|
+
@c2.many_to_many :attributes, :class => @c1
|
1651
|
+
proc{@c2.new(:id => 1234).add_attribute(@c2.new)}.should raise_error(Sequel::Error)
|
1652
|
+
end
|
1653
|
+
|
1654
|
+
it "should accept a primary key for the remove_ method and remove an existing record" do
|
1655
|
+
@c2.many_to_many :attributes, :class => @c1
|
1656
|
+
n = @c2.new(:id => 1234)
|
1657
|
+
ds = @c1.dataset
|
1658
|
+
def ds.fetch_rows(sql)
|
1659
|
+
db << sql
|
1660
|
+
yield({:id=>234})
|
1661
|
+
end
|
1662
|
+
MODEL_DB.reset
|
1663
|
+
@c1.load(:id => 234).should == n.remove_attribute(234)
|
1664
|
+
MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (id = 234) LIMIT 1',
|
1665
|
+
'DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))']
|
1666
|
+
end
|
1667
|
+
|
1668
|
+
it "should raise an error in the remove_ method if the passed associated object is not of the correct type" do
|
1669
|
+
@c2.many_to_many :attributes, :class => @c1
|
1670
|
+
proc{@c2.new(:id => 1234).remove_attribute(@c2.new)}.should raise_error(Sequel::Error)
|
1671
|
+
end
|
1672
|
+
|
1527
1673
|
it "should have the add_ method respect the :left_primary_key and :right_primary_key options" do
|
1528
1674
|
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
|
1529
1675
|
|
@@ -1535,6 +1681,17 @@ describe Sequel::Model, "many_to_many" do
|
|
1535
1681
|
].should(include(MODEL_DB.sqls.first))
|
1536
1682
|
end
|
1537
1683
|
|
1684
|
+
it "should have add_ method not add the same object to the cached association array if the object is already in the array" do
|
1685
|
+
@c2.many_to_many :attributes, :class => @c1
|
1686
|
+
|
1687
|
+
n = @c2.load(:id => 1234).set(:xxx=>5)
|
1688
|
+
a = @c1.load(:id => 2345).set(:yyy=>8)
|
1689
|
+
n.associations[:attributes] = []
|
1690
|
+
a.should == n.add_attribute(a)
|
1691
|
+
a.should == n.add_attribute(a)
|
1692
|
+
n.attributes.should == [a]
|
1693
|
+
end
|
1694
|
+
|
1538
1695
|
it "should have the add_ method respect composite keys" do
|
1539
1696
|
@c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
|
1540
1697
|
n = @c2.load(:id => 1234, :x=>5)
|
@@ -1572,12 +1729,27 @@ describe Sequel::Model, "many_to_many" do
|
|
1572
1729
|
MODEL_DB.sqls.should == ["DELETE FROM attributes_nodes WHERE ((l1 = 1234) AND (l2 = 5) AND (r1 = 2345) AND (r2 = 8))"]
|
1573
1730
|
end
|
1574
1731
|
|
1732
|
+
it "should accept a array of composite primary key values for the remove_ method and remove an existing record" do
|
1733
|
+
@c1.set_primary_key [:id, :y]
|
1734
|
+
@c2.many_to_many :attributes, :class => @c1
|
1735
|
+
n = @c2.new(:id => 1234)
|
1736
|
+
ds = @c1.dataset
|
1737
|
+
def ds.fetch_rows(sql)
|
1738
|
+
db << sql
|
1739
|
+
yield({:id=>234, :y=>8})
|
1740
|
+
end
|
1741
|
+
MODEL_DB.reset
|
1742
|
+
@c1.load(:id => 234, :y=>8).should == n.remove_attribute([234, 8])
|
1743
|
+
MODEL_DB.sqls.length.should == 2
|
1744
|
+
MODEL_DB.sqls.first.should =~ /SELECT \* FROM attributes WHERE \(\((id|y) = (234|8)\) AND \((id|y) = (234|8)\)\) LIMIT 1/
|
1745
|
+
MODEL_DB.sqls.last.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))'
|
1746
|
+
end
|
1747
|
+
|
1575
1748
|
it "should raise an error if the model object doesn't have a valid primary key" do
|
1576
1749
|
@c2.many_to_many :attributes, :class => @c1
|
1577
1750
|
a = @c2.new
|
1578
1751
|
n = @c1.load(:id=>123)
|
1579
1752
|
proc{a.attributes_dataset}.should raise_error(Sequel::Error)
|
1580
|
-
proc{a.attributes}.should raise_error(Sequel::Error)
|
1581
1753
|
proc{a.add_attribute(n)}.should raise_error(Sequel::Error)
|
1582
1754
|
proc{a.remove_attribute(n)}.should raise_error(Sequel::Error)
|
1583
1755
|
proc{a.remove_all_attributes}.should raise_error(Sequel::Error)
|
@@ -1957,7 +2129,6 @@ describe Sequel::Model, "many_to_many" do
|
|
1957
2129
|
end
|
1958
2130
|
|
1959
2131
|
it "should support a :uniq option that removes duplicates from the association" do
|
1960
|
-
h = []
|
1961
2132
|
@c2.many_to_many :attributes, :class => @c1, :uniq=>true
|
1962
2133
|
@c1.class_eval do
|
1963
2134
|
def @dataset.fetch_rows(sql)
|
@@ -1969,6 +2140,11 @@ describe Sequel::Model, "many_to_many" do
|
|
1969
2140
|
end
|
1970
2141
|
@c2.load(:id=>10, :parent_id=>20).attributes.should == [@c1.load(:id=>20), @c1.load(:id=>30)]
|
1971
2142
|
end
|
2143
|
+
|
2144
|
+
it "should support a :distinct option that uses the DISTINCT clause" do
|
2145
|
+
@c2.many_to_many :attributes, :class => @c1, :distinct=>true
|
2146
|
+
@c2.load(:id=>10).attributes_dataset.sql.should == "SELECT DISTINCT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 10))"
|
2147
|
+
end
|
1972
2148
|
end
|
1973
2149
|
|
1974
2150
|
describe Sequel::Model, " association reflection methods" do
|