sequel 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +32 -0
  3. data/doc/active_record.rdoc +2 -2
  4. data/doc/cheat_sheet.rdoc +0 -5
  5. data/doc/opening_databases.rdoc +3 -2
  6. data/doc/prepared_statements.rdoc +6 -0
  7. data/doc/release_notes/4.1.0.txt +85 -0
  8. data/doc/schema_modification.rdoc +9 -2
  9. data/lib/sequel/adapters/jdbc.rb +5 -0
  10. data/lib/sequel/adapters/mysql2.rb +24 -3
  11. data/lib/sequel/adapters/odbc.rb +6 -4
  12. data/lib/sequel/adapters/postgres.rb +25 -0
  13. data/lib/sequel/adapters/shared/mysql.rb +4 -29
  14. data/lib/sequel/adapters/shared/postgres.rb +14 -3
  15. data/lib/sequel/adapters/shared/sqlite.rb +4 -0
  16. data/lib/sequel/adapters/utils/replace.rb +36 -0
  17. data/lib/sequel/database/query.rb +1 -0
  18. data/lib/sequel/database/schema_generator.rb +12 -5
  19. data/lib/sequel/database/schema_methods.rb +2 -0
  20. data/lib/sequel/dataset/features.rb +5 -0
  21. data/lib/sequel/extensions/pg_json_ops.rb +0 -6
  22. data/lib/sequel/model/associations.rb +1 -1
  23. data/lib/sequel/plugins/instance_filters.rb +11 -1
  24. data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -2
  25. data/lib/sequel/plugins/prepared_statements.rb +38 -9
  26. data/lib/sequel/plugins/update_primary_key.rb +10 -0
  27. data/lib/sequel/sql.rb +1 -1
  28. data/lib/sequel/version.rb +1 -1
  29. data/spec/adapters/mysql_spec.rb +1 -22
  30. data/spec/adapters/postgres_spec.rb +79 -2
  31. data/spec/core/database_spec.rb +10 -0
  32. data/spec/core/dataset_spec.rb +8 -3
  33. data/spec/core/expression_filters_spec.rb +1 -1
  34. data/spec/core/schema_spec.rb +17 -2
  35. data/spec/extensions/caching_spec.rb +2 -2
  36. data/spec/extensions/hook_class_methods_spec.rb +0 -4
  37. data/spec/extensions/instance_filters_spec.rb +22 -0
  38. data/spec/extensions/migration_spec.rb +5 -5
  39. data/spec/extensions/nested_attributes_spec.rb +4 -4
  40. data/spec/extensions/prepared_statements_spec.rb +37 -26
  41. data/spec/extensions/update_primary_key_spec.rb +13 -0
  42. data/spec/integration/dataset_test.rb +36 -0
  43. data/spec/model/associations_spec.rb +20 -2
  44. data/spec/model/hooks_spec.rb +1 -7
  45. metadata +5 -2
@@ -508,6 +508,11 @@ describe "Database#run" do
508
508
  @db.sqls.should == ["DELETE FROM items"]
509
509
  end
510
510
 
511
+ specify "should handle placeholder literal strings" do
512
+ @db.run(Sequel.lit("DELETE FROM ?", :items))
513
+ @db.sqls.should == ["DELETE FROM items"]
514
+ end
515
+
511
516
  specify "should return nil" do
512
517
  @db.run("DELETE FROM items").should be_nil
513
518
  end
@@ -528,6 +533,11 @@ describe "Database#<<" do
528
533
  @db.sqls.should == ["DELETE FROM items"]
529
534
  end
530
535
 
536
+ specify "should handle placeholder literal strings" do
537
+ @db << Sequel.lit("DELETE FROM ?", :items)
538
+ @db.sqls.should == ["DELETE FROM items"]
539
+ end
540
+
531
541
  specify "should be chainable" do
532
542
  @db << "DELETE FROM items" << "DELETE FROM items2"
533
543
  @db.sqls.should == ["DELETE FROM items", "DELETE FROM items2"]
@@ -1609,10 +1609,10 @@ describe "Dataset#limit" do
1609
1609
  specify "should raise an error if an invalid limit or offset is used" do
1610
1610
  proc{@dataset.limit(-1)}.should raise_error(Sequel::Error)
1611
1611
  proc{@dataset.limit(0)}.should raise_error(Sequel::Error)
1612
- proc{@dataset.limit(1)}.should_not raise_error(Sequel::Error)
1612
+ proc{@dataset.limit(1)}.should_not raise_error
1613
1613
  proc{@dataset.limit(1, -1)}.should raise_error(Sequel::Error)
1614
- proc{@dataset.limit(1, 0)}.should_not raise_error(Sequel::Error)
1615
- proc{@dataset.limit(1, 1)}.should_not raise_error(Sequel::Error)
1614
+ proc{@dataset.limit(1, 0)}.should_not raise_error
1615
+ proc{@dataset.limit(1, 1)}.should_not raise_error
1616
1616
  end
1617
1617
  end
1618
1618
 
@@ -4508,3 +4508,8 @@ describe "Dataset#escape_like" do
4508
4508
  end
4509
4509
  end
4510
4510
 
4511
+ describe "Dataset#supports_replace?" do
4512
+ it "should be false by default" do
4513
+ Sequel::Dataset.new(nil).supports_replace?.should be_false
4514
+ end
4515
+ end
@@ -429,7 +429,7 @@ describe Sequel::SQL::VirtualRow do
429
429
  end
430
430
 
431
431
  it "should raise an error if an unsupported argument is used with a block" do
432
- proc{@d.l{count(:blah){}}}.should raise_error(Sequel::Error)
432
+ proc{@d.where{count(:blah){}}}.should raise_error(Sequel::Error)
433
433
  end
434
434
 
435
435
  it "should treat methods with a block and a leading argument :over as a window function call" do
@@ -446,7 +446,7 @@ describe "DB#create_table" do
446
446
  meta_def(@db, :execute_ddl){|*a| raise Sequel::DatabaseError if /blah/.match(a.first); super(*a)}
447
447
  lambda{@db.create_table(:cats){Integer :id; index :blah; index :id}}.should raise_error(Sequel::DatabaseError)
448
448
  @db.sqls.should == ['CREATE TABLE cats (id integer)']
449
- lambda{@db.create_table(:cats, :ignore_index_errors=>true){Integer :id; index :blah; index :id}}.should_not raise_error(Sequel::DatabaseError)
449
+ lambda{@db.create_table(:cats, :ignore_index_errors=>true){Integer :id; index :blah; index :id}}.should_not raise_error
450
450
  @db.sqls.should == ['CREATE TABLE cats (id integer)', 'CREATE INDEX cats_id_index ON cats (id)']
451
451
  end
452
452
 
@@ -551,6 +551,14 @@ describe "DB#create_table" do
551
551
  @db.sqls.should == ["CREATE TABLE cats (score integer, CONSTRAINT valid_score CHECK (score <= 100))"]
552
552
  end
553
553
 
554
+ specify "should accept named constraint definitions with options" do
555
+ @db.create_table(:cats) do
556
+ integer :score
557
+ constraint({:name=>:valid_score, :deferrable=>true}, 'score <= 100')
558
+ end
559
+ @db.sqls.should == ["CREATE TABLE cats (score integer, CONSTRAINT valid_score CHECK (score <= 100) DEFERRABLE INITIALLY DEFERRED)"]
560
+ end
561
+
554
562
  specify "should accept named constraint definitions with block" do
555
563
  @db.create_table(:cats) do
556
564
  constraint(:blah_blah){(x.sql_number > 0) & (y.sql_number < 1)}
@@ -892,6 +900,13 @@ describe "DB#alter_table" do
892
900
  @db.sqls.should == ["ALTER TABLE cats ADD CONSTRAINT valid_score CHECK (score <= 100)"]
893
901
  end
894
902
 
903
+ specify "should support add_constraint with options" do
904
+ @db.alter_table(:cats) do
905
+ add_constraint({:name=>:valid_score, :deferrable=>true}, 'score <= 100')
906
+ end
907
+ @db.sqls.should == ["ALTER TABLE cats ADD CONSTRAINT valid_score CHECK (score <= 100) DEFERRABLE INITIALLY DEFERRED"]
908
+ end
909
+
895
910
  specify "should support add_constraint with block" do
896
911
  @db.alter_table(:cats) do
897
912
  add_constraint(:blah_blah){(x.sql_number > 0) & (y.sql_number < 1)}
@@ -950,7 +965,7 @@ describe "DB#alter_table" do
950
965
  specify "should ignore errors if the database raises an error on an add_index call and the :ignore_errors option is used" do
951
966
  meta_def(@db, :execute_ddl){|*a| raise Sequel::DatabaseError}
952
967
  lambda{@db.add_index(:cats, :id)}.should raise_error(Sequel::DatabaseError)
953
- lambda{@db.add_index(:cats, :id, :ignore_errors=>true)}.should_not raise_error(Sequel::DatabaseError)
968
+ lambda{@db.add_index(:cats, :id, :ignore_errors=>true)}.should_not raise_error
954
969
  @db.sqls.should == []
955
970
  end
956
971
 
@@ -125,12 +125,12 @@ describe Sequel::Model, "caching" do
125
125
  m = @c.new
126
126
  proc {m.cache_key}.should raise_error(Sequel::Error)
127
127
  m.values[:id] = 1
128
- proc {m.cache_key}.should_not raise_error(Sequel::Error)
128
+ proc {m.cache_key}.should_not raise_error
129
129
 
130
130
  m = @c2.new
131
131
  proc {m.cache_key}.should raise_error(Sequel::Error)
132
132
  m.values[:id] = 1
133
- proc {m.cache_key}.should_not raise_error(Sequel::Error)
133
+ proc {m.cache_key}.should_not raise_error
134
134
  end
135
135
 
136
136
  it "should not raise error if trying to save a new record" do
@@ -184,7 +184,6 @@ describe "Model#before_create && Model#after_create" do
184
184
 
185
185
  specify ".create should cancel the save and raise an error if before_create returns false and raise_on_save_failure is true" do
186
186
  @c.before_create{false}
187
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
188
187
  proc{@c.create(:x => 2)}.should raise_error(Sequel::BeforeHookFailed)
189
188
  DB.sqls.should == []
190
189
  end
@@ -215,7 +214,6 @@ describe "Model#before_update && Model#after_update" do
215
214
 
216
215
  specify "#save should cancel the save and raise an error if before_update returns false and raise_on_save_failure is true" do
217
216
  @c.before_update{false}
218
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
219
217
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
220
218
  DB.sqls.should == []
221
219
  end
@@ -254,7 +252,6 @@ describe "Model#before_save && Model#after_save" do
254
252
 
255
253
  specify "#save should cancel the save and raise an error if before_save returns false and raise_on_save_failure is true" do
256
254
  @c.before_save{false}
257
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
258
255
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
259
256
  DB.sqls.should == []
260
257
  end
@@ -339,7 +336,6 @@ describe "Model#before_validation && Model#after_validation" do
339
336
 
340
337
  specify "#save should cancel the save and raise an error if before_validation returns false and raise_on_save_failure is true" do
341
338
  @c.before_validation{false}
342
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
343
339
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
344
340
  DB.sqls.should == []
345
341
  end
@@ -28,6 +28,28 @@ describe "instance_filters plugin" do
28
28
  DB.sqls.should == ["DELETE FROM people WHERE ((id = 1) AND (name = 'Jim'))"]
29
29
  end
30
30
 
31
+ specify "should work when using the prepared_statements plugin" do
32
+ @c.plugin :prepared_statements
33
+
34
+ @p.update(:name=>'Bob')
35
+ DB.sqls.should == ["UPDATE people SET name = 'Bob' WHERE (id = 1)"]
36
+ @p.instance_filter(:name=>'Jim')
37
+ @p.this.numrows = 0
38
+ proc{@p.update(:name=>'Joe')}.should raise_error(Sequel::Plugins::InstanceFilters::Error)
39
+ DB.sqls.should == ["UPDATE people SET name = 'Joe' WHERE ((id = 1) AND (name = 'Jim'))"]
40
+
41
+ @p = @c.load(:id=>1, :name=>'John', :num=>1)
42
+ @p.this.numrows = 1
43
+ @p.destroy
44
+ DB.sqls.should == ["DELETE FROM people WHERE (id = 1)"]
45
+ @p.instance_filter(:name=>'Jim')
46
+ @p.this.numrows = 0
47
+ proc{@p.destroy}.should raise_error(Sequel::Plugins::InstanceFilters::Error)
48
+ DB.sqls.should == ["DELETE FROM people WHERE ((id = 1) AND (name = 'Jim'))"]
49
+
50
+ @c.create.should be_a_kind_of(@c)
51
+ end
52
+
31
53
  specify "should apply all instance filters" do
32
54
  @p.instance_filter(:name=>'Jim')
33
55
  @p.instance_filter{num > 2}
@@ -194,19 +194,19 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
194
194
 
195
195
  specify "should raise in the down direction if migration uses unsupported method" do
196
196
  m = Sequel.migration{change{run 'SQL'}}
197
- proc{m.apply(@db, :up)}.should_not raise_error(Sequel::Error)
197
+ proc{m.apply(@db, :up)}.should_not raise_error
198
198
  proc{m.apply(@db, :down)}.should raise_error(Sequel::Error)
199
199
  end
200
200
 
201
201
  specify "should raise in the down direction if migration uses add_primary_key with an array" do
202
202
  m = Sequel.migration{change{alter_table(:a){add_primary_key [:b]}}}
203
- proc{m.apply(@db, :up)}.should_not raise_error(Sequel::Error)
203
+ proc{m.apply(@db, :up)}.should_not raise_error
204
204
  proc{m.apply(@db, :down)}.should raise_error(Sequel::Error)
205
205
  end
206
206
 
207
207
  specify "should raise in the down direction if migration uses add_foreign_key with an array" do
208
208
  m = Sequel.migration{change{alter_table(:a){add_foreign_key [:b]}}}
209
- proc{m.apply(@db, :up)}.should_not raise_error(Sequel::Error)
209
+ proc{m.apply(@db, :up)}.should_not raise_error
210
210
  proc{m.apply(@db, :down)}.should raise_error(Sequel::Error)
211
211
  end
212
212
  end
@@ -263,7 +263,7 @@ describe "Sequel::IntegerMigrator" do
263
263
  end
264
264
 
265
265
  specify "should not raise and error if there is a missing integer migration version and allow_missing_migration_files is true" do
266
- Sequel::Migrator.run(@db, "spec/files/missing_integer_migrations", :allow_missing_migration_files => true).should_not raise_error(Sequel::Migrator::Error)
266
+ proc{Sequel::Migrator.run(@db, "spec/files/missing_integer_migrations", :allow_missing_migration_files => true)}.should_not raise_error
267
267
  end
268
268
 
269
269
  specify "should raise and error if there is a duplicate integer migration version" do
@@ -622,7 +622,7 @@ describe "Sequel::TimestampMigrator" do
622
622
  @db[:schema_migrations].select_order_map(:filename).should == %w'1273253849_create_sessions.rb 1273253851_create_nodes.rb 1273253853_3_create_users.rb'
623
623
 
624
624
  @dir = 'spec/files/missing_timestamped_migrations'
625
- proc{@m.run(@db, @dir, :allow_missing_migration_files => true)}.should_not raise_error(Sequel::Migrator::Error)
625
+ proc{@m.run(@db, @dir, :allow_missing_migration_files => true)}.should_not raise_error
626
626
  [:schema_migrations, :sm1111, :sm2222, :sm3333].each{|n| @db.table_exists?(n).should be_true}
627
627
  @db[:schema_migrations].select_order_map(:filename).should == %w'1273253849_create_sessions.rb 1273253851_create_nodes.rb 1273253853_3_create_users.rb'
628
628
  end
@@ -340,7 +340,7 @@ describe "NestedAttributes plugin" do
340
340
  @Album.nested_attributes :artist
341
341
  proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should raise_error(Sequel::Error)
342
342
  @Album.nested_attributes :artist, :destroy=>true
343
- proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should_not raise_error(Sequel::Error)
343
+ proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should_not raise_error
344
344
  end
345
345
 
346
346
  it "should only allow removing associated objects if :remove option is used in the nested_attributes call" do
@@ -350,7 +350,7 @@ describe "NestedAttributes plugin" do
350
350
  @Album.nested_attributes :artist
351
351
  proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should raise_error(Sequel::Error)
352
352
  @Album.nested_attributes :artist, :remove=>true
353
- proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should_not raise_error(Sequel::Error)
353
+ proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should_not raise_error
354
354
  end
355
355
 
356
356
  it "should raise an Error if a primary key is given in a nested attribute hash, but no matching associated object exists" do
@@ -358,7 +358,7 @@ describe "NestedAttributes plugin" do
358
358
  ar = @Artist.load(:id=>20, :name=>'Ar')
359
359
  ar.associations[:albums] = [al]
360
360
  proc{ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])}.should raise_error(Sequel::Error)
361
- proc{ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])}.should_not raise_error(Sequel::Error)
361
+ proc{ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])}.should_not raise_error
362
362
  end
363
363
 
364
364
  it "should not raise an Error if an unmatched primary key is given, if the :strict=>false option is used" do
@@ -388,7 +388,7 @@ describe "NestedAttributes plugin" do
388
388
  co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
389
389
  ar.associations[:concerts] = [co]
390
390
  proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-04', :_delete=>'t'}])}.should raise_error(Sequel::Error)
391
- proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_delete=>'t'}])}.should_not raise_error(Sequel::Error)
391
+ proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_delete=>'t'}])}.should_not raise_error
392
392
  end
393
393
 
394
394
  it "should not raise an Error if an unmatched composite primary key is given, if the :strict=>false option is used" do
@@ -16,37 +16,48 @@ describe "prepared_statements plugin" do
16
16
  @db.sqls.should == ["SELECT * FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
17
17
  end
18
18
 
19
- specify "should correctly delete instance" do
20
- @p.destroy.should == @p
21
- @db.sqls.should == ["DELETE FROM people WHERE (id = 1)"]
22
- end
23
-
24
- specify "should correctly update instance" do
25
- @p.update(:name=>'bar').should == @c.load(:id=>1, :name=>'bar', :i => 2)
26
- @db.sqls.should == ["UPDATE people SET name = 'bar' WHERE (id = 1)"]
27
- end
28
-
29
- specify "should correctly create instance" do
30
- @c.create(:name=>'foo').should == @c.load(:id=>1, :name=>'foo', :i => 2)
31
- @db.sqls.should == ["INSERT INTO people (name) VALUES ('foo')", "SELECT * FROM people WHERE (id = 1) LIMIT 1"]
32
- end
19
+ shared_examples_for "prepared_statements plugin" do
20
+ specify "should correctly delete instance" do
21
+ @p.destroy.should == @p
22
+ @db.sqls.should == ["DELETE FROM people WHERE (id = 1)"]
23
+ end
33
24
 
34
- specify "should correctly create instance if dataset supports insert_select" do
35
- ds = @c.instance_dataset
36
- def ds.supports_insert_select?
37
- true
25
+ specify "should correctly update instance" do
26
+ @p.update(:name=>'bar').should == @c.load(:id=>1, :name=>'bar', :i => 2)
27
+ @db.sqls.should == ["UPDATE people SET name = 'bar' WHERE (id = 1)"]
38
28
  end
39
- def @ds.supports_insert_select?
40
- true
29
+
30
+ specify "should correctly create instance" do
31
+ @c.create(:name=>'foo').should == @c.load(:id=>1, :name=>'foo', :i => 2)
32
+ @db.sqls.should == ["INSERT INTO people (name) VALUES ('foo')", "SELECT * FROM people WHERE (id = 1) LIMIT 1"]
41
33
  end
42
- def @ds.insert_select(h)
43
- {:id=>1, :name=>'foo', :i => 2}
34
+
35
+ specify "should correctly create instance if dataset supports insert_select" do
36
+ @c.dataset_module do
37
+ def supports_insert_select?
38
+ true
39
+ end
40
+ def insert_select(h)
41
+ self._fetch = {:id=>1, :name=>'foo', :i => 2}
42
+ returning.server(:default).with_sql(:insert_sql, h).first
43
+ end
44
+ def insert_sql(*)
45
+ "#{super}#{' RETURNING *' if opts.has_key?(:returning)}"
46
+ end
47
+ end
48
+ @c.create(:name=>'foo').should == @c.load(:id=>1, :name=>'foo', :i => 2)
49
+ @db.sqls.should == ["INSERT INTO people (name) VALUES ('foo') RETURNING *"]
44
50
  end
45
- def @ds.insert_sql(*)
46
- "#{super}#{' RETURNING *' if opts.has_key?(:returning)}"
51
+ end
52
+
53
+ it_should_behave_like "prepared_statements plugin"
54
+
55
+ describe "when #use_prepared_statements_for? returns false" do
56
+ before do
57
+ @c.class_eval{def use_prepared_statements_for?(type) false end}
47
58
  end
48
- @c.create(:name=>'foo').should == @c.load(:id=>1, :name=>'foo', :i => 2)
49
- @db.sqls.should == ["INSERT INTO people (name) VALUES ('foo') RETURNING *"]
59
+
60
+ it_should_behave_like "prepared_statements plugin"
50
61
  end
51
62
 
52
63
  specify "should work correctly when subclassing" do
@@ -68,6 +68,19 @@ describe "Sequel::Plugins::UpdatePrimaryKey" do
68
68
  DB.sqls.should == ["SELECT * FROM a LIMIT 1", "UPDATE a SET a = 2 WHERE (a = 1)", "UPDATE a SET b = 4 WHERE (a = 2)", "UPDATE a SET b = 5 WHERE (a = 2)", "SELECT * FROM a"]
69
69
  end
70
70
 
71
+ specify "should work correctly when using the prepared_statements plugin" do
72
+ @c.plugin :prepared_statements
73
+ @ds._fetch = [[{:a=>1, :b=>3}], [{:a=>2, :b=>4}]]
74
+ o = @c.first
75
+ o.update(:a=>2, :b=>4)
76
+ @c.all.should == [@c.load(:a=>2, :b=>4)]
77
+ sqls = DB.sqls
78
+ ["UPDATE a SET a = 2, b = 4 WHERE (a = 1)", "UPDATE a SET b = 4, a = 2 WHERE (a = 1)"].should include(sqls.slice!(1))
79
+ sqls.should == ["SELECT * FROM a LIMIT 1", "SELECT * FROM a"]
80
+
81
+ o.delete
82
+ end
83
+
71
84
  specify "should clear the associations cache of non-many_to_one associations when changing the primary key" do
72
85
  @c.one_to_many :cs, :class=>@c
73
86
  @c.many_to_one :c, :class=>@c
@@ -1671,3 +1671,39 @@ describe "Emulated functions" do
1671
1671
  @ds.get(Sequel.trim(:a)).should == 'foo22'
1672
1672
  end
1673
1673
  end
1674
+
1675
+ describe "Dataset replace" do
1676
+ before do
1677
+ DB.create_table(:items){Integer :id, :unique=>true; Integer :value}
1678
+ sqls = []
1679
+ DB.loggers << Class.new{%w'info error'.each{|m| define_method(m){|sql| sqls << sql}}}.new
1680
+
1681
+ @d = DB[:items]
1682
+ sqls.clear
1683
+ end
1684
+
1685
+ after do
1686
+ DB.drop_table?(:items)
1687
+ end
1688
+
1689
+ specify "should use support arrays, datasets, and multiple values" do
1690
+ @d.replace([1, 2])
1691
+ @d.all.should == [{:id=>1, :value=>2}]
1692
+ @d.replace(1, 2)
1693
+ @d.all.should == [{:id=>1, :value=>2}]
1694
+ @d.replace(@d)
1695
+ @d.all.should == [{:id=>1, :value=>2}]
1696
+ end
1697
+
1698
+ specify "should create a record if the condition is not met" do
1699
+ @d.replace(:id => 111, :value => 333)
1700
+ @d.all.should == [{:id => 111, :value => 333}]
1701
+ end
1702
+
1703
+ specify "should update a record if the condition is met" do
1704
+ @d << {:id => 111}
1705
+ @d.all.should == [{:id => 111, :value => nil}]
1706
+ @d.replace(:id => 111, :value => 333)
1707
+ @d.all.should == [{:id => 111, :value => 333}]
1708
+ end
1709
+ end if DB.dataset.supports_replace?
@@ -134,8 +134,8 @@ describe Sequel::Model, "associate" do
134
134
  it "should allow cloning of one_to_many to one_to_one associations and vice-versa" do
135
135
  c = Class.new(Sequel::Model(:c))
136
136
  c.one_to_one :c
137
- proc{c.one_to_many :cs, :clone=>:c}.should_not raise_error(Sequel::Error)
138
- proc{c.one_to_one :c2, :clone=>:cs}.should_not raise_error(Sequel::Error)
137
+ proc{c.one_to_many :cs, :clone=>:c}.should_not raise_error
138
+ proc{c.one_to_one :c2, :clone=>:cs}.should_not raise_error
139
139
  end
140
140
 
141
141
  it "should clear associations cache when refreshing object manually" do
@@ -1239,6 +1239,14 @@ describe Sequel::Model, "one_to_many" do
1239
1239
  DB.sqls.should == ["SELECT * FROM attributes WHERE id = 234", "UPDATE attributes SET node_id = 1234 WHERE (id = 234)"]
1240
1240
  end
1241
1241
 
1242
+ it "should raise an error if the primary key passed to the add_ method does not match an existing record" do
1243
+ @c2.one_to_many :attributes, :class => @c1
1244
+ n = @c2.new(:id => 1234)
1245
+ @c1.dataset._fetch = []
1246
+ proc{n.add_attribute(234)}.should raise_error(Sequel::NoMatchingRow)
1247
+ DB.sqls.should == ["SELECT * FROM attributes WHERE id = 234"]
1248
+ end
1249
+
1242
1250
  it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
1243
1251
  @c2.one_to_many :attributes, :class => @c1
1244
1252
  proc{@c2.new(:id => 1234).add_attribute(@c2.new)}.should raise_error(Sequel::Error)
@@ -2013,6 +2021,16 @@ describe Sequel::Model, "many_to_many" do
2013
2021
  sqls.should == ["SELECT * FROM attributes WHERE id = 2345"]
2014
2022
  end
2015
2023
 
2024
+ it "should raise an error if the primary key passed to the add_ method does not match an existing record" do
2025
+ @c2.many_to_many :attributes, :class => @c1
2026
+
2027
+ n = @c2.load(:id => 1234)
2028
+ a = @c1.load(:id => 2345)
2029
+ @c1.dataset._fetch = []
2030
+ proc{n.add_attribute(2345)}.should raise_error(Sequel::NoMatchingRow)
2031
+ DB.sqls.should == ["SELECT * FROM attributes WHERE id = 2345"]
2032
+ end
2033
+
2016
2034
  it "should allow passing a hash to the add_ method which creates a new record" do
2017
2035
  @c2.many_to_many :attributes, :class => @c1
2018
2036
 
@@ -24,7 +24,7 @@ describe "Model#before_create && Model#after_create" do
24
24
  @c.send(:define_method, :before_create){false}
25
25
  proc{@c.create(:x => 2)}.should raise_error(Sequel::BeforeHookFailed)
26
26
  DB.sqls.should == []
27
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
27
+ proc{@c.load(:id => 2233).save}.should_not raise_error
28
28
  end
29
29
 
30
30
  specify ".create should cancel the save and return nil if before_create returns false and raise_on_save_failure is false" do
@@ -55,7 +55,6 @@ describe "Model#before_update && Model#after_update" do
55
55
 
56
56
  specify "#save should cancel the save and raise an error if before_update returns false and raise_on_save_failure is true" do
57
57
  @c.send(:define_method, :before_update){false}
58
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
59
58
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
60
59
  DB.sqls.should == []
61
60
  end
@@ -63,7 +62,6 @@ describe "Model#before_update && Model#after_update" do
63
62
  specify "#save should cancel the save and raise an error if before_update returns false and raise_on_failure option is true" do
64
63
  @c.send(:define_method, :before_update){false}
65
64
  @c.raise_on_save_failure = false
66
- proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should_not raise_error(Sequel::ValidationFailed)
67
65
  proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should raise_error(Sequel::BeforeHookFailed)
68
66
  DB.sqls.should == []
69
67
  end
@@ -104,7 +102,6 @@ describe "Model#before_save && Model#after_save" do
104
102
 
105
103
  specify "#save should cancel the save and raise an error if before_save returns false and raise_on_save_failure is true" do
106
104
  @c.send(:define_method, :before_save){false}
107
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
108
105
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
109
106
  DB.sqls.should == []
110
107
  end
@@ -112,7 +109,6 @@ describe "Model#before_save && Model#after_save" do
112
109
  specify "#save should cancel the save and raise an error if before_save returns false and raise_on_failure option is true" do
113
110
  @c.send(:define_method, :before_save){false}
114
111
  @c.raise_on_save_failure = false
115
- proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should_not raise_error(Sequel::ValidationFailed)
116
112
  proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should raise_error(Sequel::BeforeHookFailed)
117
113
  DB.sqls.should == []
118
114
  end
@@ -208,7 +204,6 @@ describe "Model#before_validation && Model#after_validation" do
208
204
 
209
205
  specify "#save should cancel the save and raise an error if before_validation returns false and raise_on_save_failure is true" do
210
206
  @c.send(:define_method, :before_validation){false}
211
- proc{@c.load(:id => 2233).save}.should_not raise_error(Sequel::ValidationFailed)
212
207
  proc{@c.load(:id => 2233).save}.should raise_error(Sequel::BeforeHookFailed)
213
208
  DB.sqls.should == []
214
209
  end
@@ -216,7 +211,6 @@ describe "Model#before_validation && Model#after_validation" do
216
211
  specify "#save should cancel the save and raise an error if before_validation returns false and raise_on_failure option is true" do
217
212
  @c.send(:define_method, :before_validation){false}
218
213
  @c.raise_on_save_failure = false
219
- proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should_not raise_error(Sequel::ValidationFailed)
220
214
  proc{@c.load(:id => 2233).save(:raise_on_failure => true)}.should raise_error(Sequel::BeforeHookFailed)
221
215
  DB.sqls.should == []
222
216
  end