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