sequel 4.21.0 → 4.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +32 -0
  3. data/README.rdoc +3 -4
  4. data/doc/opening_databases.rdoc +10 -75
  5. data/doc/release_notes/4.22.0.txt +72 -0
  6. data/lib/sequel/adapters/ado/access.rb +1 -1
  7. data/lib/sequel/adapters/cubrid.rb +3 -3
  8. data/lib/sequel/adapters/db2.rb +1 -0
  9. data/lib/sequel/adapters/dbi.rb +1 -0
  10. data/lib/sequel/adapters/fdbsql.rb +3 -2
  11. data/lib/sequel/adapters/firebird.rb +1 -0
  12. data/lib/sequel/adapters/ibmdb.rb +1 -21
  13. data/lib/sequel/adapters/informix.rb +1 -0
  14. data/lib/sequel/adapters/jdbc.rb +37 -49
  15. data/lib/sequel/adapters/jdbc/fdbsql.rb +1 -0
  16. data/lib/sequel/adapters/mysql.rb +5 -3
  17. data/lib/sequel/adapters/mysql2.rb +5 -2
  18. data/lib/sequel/adapters/odbc.rb +8 -4
  19. data/lib/sequel/adapters/openbase.rb +1 -0
  20. data/lib/sequel/adapters/oracle.rb +3 -46
  21. data/lib/sequel/adapters/postgres.rb +3 -36
  22. data/lib/sequel/adapters/shared/access.rb +1 -1
  23. data/lib/sequel/adapters/shared/fdbsql.rb +3 -3
  24. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  25. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +12 -44
  26. data/lib/sequel/adapters/shared/oracle.rb +6 -2
  27. data/lib/sequel/adapters/shared/postgres.rb +6 -6
  28. data/lib/sequel/adapters/shared/sqlite.rb +1 -1
  29. data/lib/sequel/adapters/sqlite.rb +3 -46
  30. data/lib/sequel/adapters/tinytds.rb +12 -28
  31. data/lib/sequel/adapters/utils/pg_types.rb +1 -1
  32. data/lib/sequel/connection_pool/sharded_threaded.rb +63 -16
  33. data/lib/sequel/connection_pool/threaded.rb +72 -18
  34. data/lib/sequel/core.rb +1 -1
  35. data/lib/sequel/database/connecting.rb +2 -2
  36. data/lib/sequel/database/misc.rb +5 -5
  37. data/lib/sequel/database/query.rb +3 -2
  38. data/lib/sequel/database/schema_generator.rb +19 -19
  39. data/lib/sequel/database/schema_methods.rb +2 -2
  40. data/lib/sequel/database/transactions.rb +3 -3
  41. data/lib/sequel/dataset/actions.rb +18 -8
  42. data/lib/sequel/dataset/graph.rb +2 -2
  43. data/lib/sequel/dataset/prepared_statements.rb +28 -1
  44. data/lib/sequel/dataset/query.rb +7 -7
  45. data/lib/sequel/exceptions.rb +27 -24
  46. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  47. data/lib/sequel/extensions/constraint_validations.rb +2 -2
  48. data/lib/sequel/extensions/date_arithmetic.rb +2 -2
  49. data/lib/sequel/extensions/pg_array.rb +10 -1
  50. data/lib/sequel/extensions/pg_row.rb +1 -1
  51. data/lib/sequel/extensions/pg_static_cache_updater.rb +1 -1
  52. data/lib/sequel/extensions/schema_dumper.rb +8 -8
  53. data/lib/sequel/extensions/split_array_nil.rb +1 -1
  54. data/lib/sequel/model.rb +1 -1
  55. data/lib/sequel/model/associations.rb +18 -11
  56. data/lib/sequel/model/base.rb +15 -15
  57. data/lib/sequel/model/exceptions.rb +11 -2
  58. data/lib/sequel/plugins/accessed_columns.rb +1 -1
  59. data/lib/sequel/plugins/auto_validations.rb +1 -1
  60. data/lib/sequel/plugins/boolean_readers.rb +1 -1
  61. data/lib/sequel/plugins/class_table_inheritance.rb +4 -7
  62. data/lib/sequel/plugins/composition.rb +1 -1
  63. data/lib/sequel/plugins/constraint_validations.rb +2 -2
  64. data/lib/sequel/plugins/csv_serializer.rb +171 -0
  65. data/lib/sequel/plugins/dirty.rb +2 -2
  66. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  67. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  68. data/lib/sequel/plugins/many_through_many.rb +1 -1
  69. data/lib/sequel/plugins/nested_attributes.rb +5 -5
  70. data/lib/sequel/plugins/pg_array_associations.rb +4 -4
  71. data/lib/sequel/plugins/prepared_statements.rb +2 -2
  72. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -1
  73. data/lib/sequel/plugins/serialization.rb +6 -6
  74. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
  75. data/lib/sequel/plugins/sharding.rb +3 -1
  76. data/lib/sequel/plugins/single_table_inheritance.rb +5 -13
  77. data/lib/sequel/plugins/static_cache.rb +2 -2
  78. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  79. data/lib/sequel/plugins/tree.rb +1 -1
  80. data/lib/sequel/plugins/validation_class_methods.rb +2 -2
  81. data/lib/sequel/plugins/validation_helpers.rb +4 -4
  82. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  83. data/lib/sequel/sql.rb +1 -1
  84. data/lib/sequel/version.rb +1 -1
  85. data/spec/adapters/postgres_spec.rb +17 -0
  86. data/spec/core/connection_pool_spec.rb +1 -1
  87. data/spec/core/dataset_spec.rb +22 -0
  88. data/spec/extensions/auto_validations_spec.rb +1 -1
  89. data/spec/extensions/blacklist_security_spec.rb +2 -2
  90. data/spec/extensions/csv_serializer_spec.rb +173 -0
  91. data/spec/extensions/json_serializer_spec.rb +2 -2
  92. data/spec/extensions/nested_attributes_spec.rb +9 -9
  93. data/spec/extensions/pg_array_spec.rb +5 -0
  94. data/spec/extensions/single_table_inheritance_spec.rb +21 -0
  95. data/spec/extensions/touch_spec.rb +1 -1
  96. data/spec/extensions/tree_spec.rb +4 -0
  97. data/spec/extensions/xml_serializer_spec.rb +3 -3
  98. data/spec/integration/prepared_statement_test.rb +1 -1
  99. data/spec/integration/schema_test.rb +7 -0
  100. data/spec/integration/type_test.rb +2 -2
  101. data/spec/model/associations_spec.rb +108 -14
  102. data/spec/model/base_spec.rb +8 -8
  103. data/spec/model/record_spec.rb +7 -7
  104. metadata +6 -2
@@ -11,7 +11,7 @@ describe "Sequel::Plugins::AutoValidations" do
11
11
  [:name, {:primary_key=>false, :type=>:string, :allow_null=>false, :max_length=>50}],
12
12
  [:num, {:primary_key=>false, :type=>:integer, :allow_null=>true}],
13
13
  [:d, {:primary_key=>false, :type=>:date, :allow_null=>false}],
14
- [:nnd, {:primary_key=>false, :type=>:string, :allow_null=>false, :ruby_default=>'nnd'}]]
14
+ [:nnd, {:primary_key=>false, :type=>:string, :allow_null=>false, :default=>'nnd'}]]
15
15
  end
16
16
  def db.supports_index_parsing?() true end
17
17
  def db.indexes(t, *)
@@ -16,8 +16,8 @@ describe Sequel::Model, "#(set|update)_except" do
16
16
 
17
17
  it "should raise errors if not all hash fields can be set and strict_param_setting is true" do
18
18
  @c.strict_param_setting = true
19
- proc{@c.new.set_except({:x => 1, :y => 2, :z=>3, :id=>4}, :x, :y)}.should raise_error(Sequel::Error)
20
- proc{@c.new.set_except({:x => 1, :y => 2, :z=>3}, :x, :y)}.should raise_error(Sequel::Error)
19
+ proc{@c.new.set_except({:x => 1, :y => 2, :z=>3, :id=>4}, :x, :y)}.should raise_error(Sequel::MassAssignmentRestriction)
20
+ proc{@c.new.set_except({:x => 1, :y => 2, :z=>3}, :x, :y)}.should raise_error(Sequel::MassAssignmentRestriction)
21
21
  (o = @c.new).set_except({:z => 3}, :x, :y)
22
22
  o.values.should == {:z=>3}
23
23
  end
@@ -0,0 +1,173 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::CsvSerializer" do
4
+ before do
5
+ artist = @Artist = Class.new(Sequel::Model(:artists))
6
+ @Artist.class_eval do
7
+ def self.name; 'Artist' end
8
+ unrestrict_primary_key
9
+ plugin :csv_serializer
10
+ columns :id, :name
11
+ def_column_accessor :id, :name
12
+ @db_schema = {:id=>{:type=>:integer}}
13
+ end
14
+ @Album = Class.new(Sequel::Model(:albums))
15
+ @Album.class_eval do
16
+ def self.name; 'Album' end
17
+ unrestrict_primary_key
18
+ attr_accessor :blah
19
+ plugin :csv_serializer
20
+ columns :id, :name, :artist_id
21
+ def_column_accessor :id, :name, :artist_id
22
+ @db_schema = {:id=>{:type=>:integer}, :artist_id=>{:type=>:integer}}
23
+ many_to_one :artist, :class=>artist
24
+ end
25
+ @artist = @Artist.load(:id=>2, :name=>'YJM')
26
+ @artist.associations[:albums] = []
27
+ @album = @Album.load(:id=>1, :name=>'RF')
28
+ @album.artist = @artist
29
+ @album.blah = 'Blah'
30
+ end
31
+
32
+ it "should round trip successfully" do
33
+ @Artist.from_csv(@artist.to_csv).should == @artist
34
+ @Album.from_csv(@album.to_csv).should == @album
35
+ end
36
+
37
+ it "should handle ruby objects in values" do
38
+ @Artist.send(:define_method, :name=) do |v|
39
+ super(Date.parse(v))
40
+ end
41
+ a = @Artist.load(:name=>Date.today)
42
+ opts = {:columns=>[:name]}
43
+ @Artist.from_csv(a.to_csv(opts), opts).should == a
44
+ end
45
+
46
+ it "should handle the :only option" do
47
+ @Artist.from_csv(@artist.to_csv(:only=>:name), :only=>:name).should == @Artist.load(:name=>@artist.name)
48
+ @Album.from_csv(@album.to_csv(:only=>[:id, :name]), :only=>[:id, :name]).should == @Album.load(:id=>@album.id, :name=>@album.name)
49
+ end
50
+
51
+ it "should handle the :except option" do
52
+ @Artist.from_csv(@artist.to_csv(:except=>:id), :except=>:id).should == @Artist.load(:name=>@artist.name)
53
+ @Album.from_csv(@album.to_csv(:except=>[:id, :artist_id]), :except=>[:id, :artist_id]).should == @Album.load(:name=>@album.name)
54
+ end
55
+
56
+ it "should handle the :include option for arbitrary attributes" do
57
+ @Album.from_csv(@album.to_csv(:include=>:blah), :include=>:blah).blah.should == @album.blah
58
+ end
59
+
60
+ it "should handle multiple inclusions using an array for the :include option" do
61
+ a = @Album.from_csv(@album.to_csv(:include=>[:blah]), :include=>:blah)
62
+ a.blah.should == @album.blah
63
+ end
64
+
65
+ it "#from_csv should set column values" do
66
+ @artist.from_csv('AS', :only=>:name)
67
+ @artist.name.should == 'AS'
68
+ @artist.id.should == 2
69
+
70
+ @artist.from_csv('1', :only=>:id)
71
+ @artist.name.should == 'AS'
72
+ @artist.id.should == 1
73
+ end
74
+
75
+ it ".array_from_csv should support :headers to specify headers" do
76
+ @albums = @Album.array_from_csv("AS,2\nDF,3", :headers=>['name', 'artist_id'])
77
+ @albums.map(&:name).should == %w'AS DF'
78
+ @albums.map(&:artist_id).should == [2, 3]
79
+
80
+ @albums = @Album.array_from_csv("2,AS\n3,DF", :headers=>[nil, 'name'])
81
+ @albums.map(&:name).should == %w'AS DF'
82
+ @albums.map(&:artist_id).should == [nil, nil]
83
+ end
84
+
85
+ it ".from_csv should support :headers to specify headers" do
86
+ @album = @Album.from_csv('AS,2', :headers=>['name', 'artist_id'])
87
+ @album.name.should == 'AS'
88
+ @album.artist_id.should == 2
89
+
90
+ @album = @Album.from_csv('2,AS', :headers=>[nil, 'name'])
91
+ @album.name.should == 'AS'
92
+ @album.artist_id.should == nil
93
+ end
94
+
95
+ it "#from_csv should support :headers to specify headers" do
96
+ @album.from_csv('AS,2', :headers=>['name'])
97
+ @album.name.should == 'AS'
98
+ @album.artist_id.should == 2
99
+
100
+ @album.from_csv('2,AS', :headers=>[nil, 'name'])
101
+ @album.name.should == 'AS'
102
+ @album.artist_id.should == 2
103
+ end
104
+
105
+ it "should support a to_csv class and dataset method" do
106
+ @Album.dataset._fetch = {:id=>1, :name=>'RF', :artist_id=>2}
107
+ @Artist.dataset._fetch = {:id=>2, :name=>'YJM'}
108
+ @Album.array_from_csv(@Album.to_csv).should == [@album]
109
+ @Album.array_from_csv(@Album.dataset.to_csv(:only=>:name), :only=>:name).should == [@Album.load(:name=>@album.name)]
110
+ end
111
+
112
+ it "should have dataset to_csv method respect :array option" do
113
+ a = @Album.new(:id=>1, :name=>'RF', :artist_id=>3)
114
+ @Album.array_from_csv(@Album.to_csv(:array=>[a])).should == [a]
115
+ end
116
+
117
+ it "#to_csv should respect class options" do
118
+ @Album = Class.new(Sequel::Model(:albums))
119
+ artist = @Artist
120
+ @Album.class_eval do
121
+ attr_accessor :blah
122
+ plugin :csv_serializer, :except => :id, :write_headers=>true, :include=>:blah
123
+ columns :id, :name, :artist_id
124
+ many_to_one :artist, :class=>artist
125
+ end
126
+ @album = @Album.load(:id=>2, :name=>'JK')
127
+ @album.artist = @artist
128
+ @album.blah = 'Gak'
129
+
130
+ @album.to_csv.should == "name,artist_id,blah\nJK,2,Gak\n"
131
+ @album.to_csv(:write_headers=>false).should == "JK,2,Gak\n"
132
+ @album.to_csv(:headers=>[:name]).should == "name\nJK\n"
133
+ @album.to_csv(:headers=>[:name, :id]).should == "name,id\nJK,2\n"
134
+ @album.to_csv(:only=>[:name]).should == "name,blah\nJK,Gak\n"
135
+ @album.to_csv(:except=>nil).should == "id,name,artist_id,blah\n2,JK,2,Gak\n"
136
+ @album.to_csv(:except=>[:blah]).should == "id,name,artist_id\n2,JK,2\n"
137
+ end
138
+
139
+ it "should store the default options in csv_serializer_opts" do
140
+ @Album.csv_serializer_opts.should == {}
141
+ c = Class.new(@Album)
142
+ @Album.csv_serializer_opts[:include] = :blah
143
+ c.plugin :csv_serializer, :naked=>false
144
+ c.csv_serializer_opts.should == {:naked=>false}
145
+ @Album.csv_serializer_opts.should == {:include=>:blah}
146
+ end
147
+
148
+ it "should work correctly when subclassing" do
149
+ @Artist2 = Class.new(@Artist)
150
+ @Artist2.plugin :csv_serializer, :only=>:name
151
+ @Artist3 = Class.new(@Artist2)
152
+ @Artist3.from_csv(@Artist3.load(:id=>2, :name=>'YYY').to_csv).should == @Artist3.load(:name=>'YYY')
153
+ end
154
+
155
+ it "should raise an error if attempting to set a restricted column and :all_columns is not used" do
156
+ @Artist.restrict_primary_key
157
+ proc{@Artist.from_csv(@artist.to_csv)}.should raise_error(Sequel::MassAssignmentRestriction)
158
+ end
159
+
160
+ it "should use a dataset's selected columns" do
161
+ columns = [:id]
162
+ ds = @Artist.select(*columns).limit(1)
163
+ ds.instance_variable_set(:@columns, columns)
164
+ ds._fetch = [:id => 10]
165
+ ds.to_csv(:write_headers => true).should == "id\n10\n"
166
+ end
167
+
168
+ it "should pass all the examples from the documentation" do
169
+ @album.to_csv(:write_headers=>true).should == "id,name,artist_id\n1,RF,2\n"
170
+ @album.to_csv(:only=>:name).should == "RF\n"
171
+ @album.to_csv(:except=>[:id, :artist_id]).should == "RF\n"
172
+ end
173
+ end
@@ -157,7 +157,7 @@ describe "Sequel::Plugins::JsonSerializer" do
157
157
 
158
158
  it "should raise an exception for json keys that aren't associations, columns, or setter methods" do
159
159
  Album.send(:undef_method, :blah=)
160
- proc{Album.from_json(@album.to_json(:include=>:blah))}.should raise_error(Sequel::Error)
160
+ proc{Album.from_json(@album.to_json(:include=>:blah))}.should raise_error(Sequel::MassAssignmentRestriction)
161
161
  end
162
162
 
163
163
  it "should support a to_json class and dataset method" do
@@ -269,7 +269,7 @@ describe "Sequel::Plugins::JsonSerializer" do
269
269
 
270
270
  it "should raise an error if attempting to set a restricted column and :all_columns is not used" do
271
271
  Artist.restrict_primary_key
272
- proc{Artist.from_json(@artist.to_json)}.should raise_error(Sequel::Error)
272
+ proc{Artist.from_json(@artist.to_json)}.should raise_error(Sequel::MassAssignmentRestriction)
273
273
  end
274
274
 
275
275
  it "should raise an error if an unsupported association is passed in the :associations option" do
@@ -433,7 +433,7 @@ describe "NestedAttributes plugin" do
433
433
  ar = @Artist.load(:id=>20, :name=>'Ar')
434
434
  a.associations[:artist] = ar
435
435
  @Album.nested_attributes :artist
436
- proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should raise_error(Sequel::Error)
436
+ proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should raise_error(Sequel::MassAssignmentRestriction)
437
437
  @Album.nested_attributes :artist, :destroy=>true
438
438
  proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.should_not raise_error
439
439
  end
@@ -443,7 +443,7 @@ describe "NestedAttributes plugin" do
443
443
  ar = @Artist.load(:id=>20, :name=>'Ar')
444
444
  a.associations[:artist] = ar
445
445
  @Album.nested_attributes :artist
446
- proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should raise_error(Sequel::Error)
446
+ proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should raise_error(Sequel::MassAssignmentRestriction)
447
447
  @Album.nested_attributes :artist, :remove=>true
448
448
  proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.should_not raise_error
449
449
  end
@@ -545,7 +545,7 @@ describe "NestedAttributes plugin" do
545
545
 
546
546
  it "should not accept nested attributes unless explicitly specified" do
547
547
  @Artist.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
548
- proc{@Artist.create({:name=>'Ar', :tags_attributes=>[{:name=>'T'}]})}.should raise_error(Sequel::Error)
548
+ proc{@Artist.create({:name=>'Ar', :tags_attributes=>[{:name=>'T'}]})}.should raise_error(Sequel::MassAssignmentRestriction)
549
549
  @db.sqls.should == []
550
550
  end
551
551
 
@@ -646,8 +646,8 @@ describe "NestedAttributes plugin" do
646
646
  "UPDATE tags SET name = 'T2' WHERE (id = 30)",
647
647
  "INSERT INTO tags (name) VALUES ('T3')",
648
648
  ["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
649
- proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
650
- proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
649
+ proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::MassAssignmentRestriction)
650
+ proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::MassAssignmentRestriction)
651
651
  end
652
652
 
653
653
  it "should accept a proc for the :fields option that accepts the associated object and returns an array of fields" do
@@ -664,8 +664,8 @@ describe "NestedAttributes plugin" do
664
664
  "UPDATE tags SET name = 'T2' WHERE (id = 30)",
665
665
  "INSERT INTO tags (name) VALUES ('T3')",
666
666
  ["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
667
- proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
668
- proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
667
+ proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::MassAssignmentRestriction)
668
+ proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::MassAssignmentRestriction)
669
669
  end
670
670
 
671
671
  it "should allow per-call options via the set_nested_attributes method" do
@@ -682,8 +682,8 @@ describe "NestedAttributes plugin" do
682
682
  "UPDATE tags SET name = 'T2' WHERE (id = 30)",
683
683
  "INSERT INTO tags (name) VALUES ('T3')",
684
684
  ["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
685
- proc{al.set_nested_attributes(:tags, [{:id=>30, :name=>'T2', :number=>3}], :fields=>[:name])}.should raise_error(Sequel::Error)
686
- proc{al.set_nested_attributes(:tags, [{:name=>'T2', :number=>3}], :fields=>[:name])}.should raise_error(Sequel::Error)
685
+ proc{al.set_nested_attributes(:tags, [{:id=>30, :name=>'T2', :number=>3}], :fields=>[:name])}.should raise_error(Sequel::MassAssignmentRestriction)
686
+ proc{al.set_nested_attributes(:tags, [{:name=>'T2', :number=>3}], :fields=>[:name])}.should raise_error(Sequel::MassAssignmentRestriction)
687
687
  end
688
688
 
689
689
  it "should have set_nested_attributes method raise error if called with a bad association" do
@@ -387,4 +387,9 @@ describe "pg_array extension" do
387
387
  @db.schema_type_class(:banana_array).should == Sequel::Postgres::PGArray
388
388
  @db.schema_type_class(:integer).should == Integer
389
389
  end
390
+
391
+ it "should convert ruby arrays to pg arrays as :default option values" do
392
+ @db.create_table('a'){column :b, 'c[]', :default=>[]; Integer :d}
393
+ @db.sqls.should == ['CREATE TABLE a (b c[] DEFAULT (ARRAY[]::c[]), d integer)']
394
+ end
390
395
  end
@@ -77,6 +77,13 @@ describe Sequel::Model, "single table inheritance plugin" do
77
77
  DB.sqls.should == ["INSERT INTO sti_tests (kind) VALUES ('StiTest')", "SELECT * FROM sti_tests WHERE (id = 10) LIMIT 1", "INSERT INTO sti_tests (kind) VALUES ('StiTestSub1')", "SELECT * FROM sti_tests WHERE ((sti_tests.kind IN ('StiTestSub1')) AND (id = 10)) LIMIT 1", "INSERT INTO sti_tests (kind) VALUES ('StiTestSub2')", "SELECT * FROM sti_tests WHERE ((sti_tests.kind IN ('StiTestSub2')) AND (id = 10)) LIMIT 1"]
78
78
  end
79
79
 
80
+ it "should destroy the model correctly" do
81
+ StiTest.load(:id=>1).destroy
82
+ StiTestSub1.load(:id=>1).destroy
83
+ StiTestSub2.load(:id=>1).destroy
84
+ DB.sqls.should == ["DELETE FROM sti_tests WHERE id = 1", "DELETE FROM sti_tests WHERE ((sti_tests.kind IN ('StiTestSub1')) AND (id = 1))", "DELETE FROM sti_tests WHERE ((sti_tests.kind IN ('StiTestSub2')) AND (id = 1))"]
85
+ end
86
+
80
87
  it "should handle validations on the type column field" do
81
88
  o = StiTestSub1.new
82
89
  def o.validate
@@ -200,6 +207,7 @@ describe Sequel::Model, "single table inheritance plugin" do
200
207
  StiTest2.dataset.row_proc.call(:kind=>0).should be_a_instance_of(StiTest2)
201
208
  StiTest2.dataset.row_proc.call(:kind=>1).should be_a_instance_of(StiTest3)
202
209
  StiTest2.dataset.row_proc.call(:kind=>2).should be_a_instance_of(StiTest4)
210
+ StiTest3.sti_model_map.should == StiTest2.sti_model_map
203
211
 
204
212
  StiTest2.create.kind.should == 4
205
213
  StiTest3.create.kind.should == 5
@@ -264,6 +272,19 @@ describe Sequel::Model, "single table inheritance plugin" do
264
272
  "SELECT * FROM sti_test2s WHERE (sti_test2s.kind IN (1, 0))"].should include(StiTest3.dataset.sql)
265
273
  end
266
274
 
275
+ it "should destroy the model correctly" do
276
+ StiTest2.plugin :single_table_inheritance, :kind, :model_map=>{'sti3'=>'StiTest3', 'sti3b'=>'StiTest3', 'sti4'=>'StiTest4'}
277
+ class ::StiTest3 < ::StiTest2; end
278
+ class ::StiTest4 < ::StiTest2; end
279
+ StiTest2.load(:id=>1).destroy
280
+ StiTest3.load(:id=>1).destroy
281
+ sqls = DB.sqls
282
+ sqls.shift.should == "DELETE FROM sti_test2s WHERE id = 1"
283
+ ["DELETE FROM sti_test2s WHERE ((sti_test2s.kind IN ('sti3', 'sti3b')) AND (id = 1))",
284
+ "DELETE FROM sti_test2s WHERE ((sti_test2s.kind IN ('sti3b', 'sti3')) AND (id = 1))"].should include(sqls.pop)
285
+ sqls.should == []
286
+ end
287
+
267
288
  it "should honor a :key_chooser" do
268
289
  StiTest2.plugin :single_table_inheritance, :kind, :key_chooser => proc{|inst| inst.model.to_s.downcase }
269
290
  class ::StiTest3 < ::StiTest2; end
@@ -171,7 +171,7 @@ describe "Touch plugin" do
171
171
 
172
172
  specify "should raise an error if given a column argument in touch that doesn't exist" do
173
173
  @Artist.plugin :touch
174
- proc{@a.touch(:x)}.should raise_error(Sequel::Error)
174
+ proc{@a.touch(:x)}.should raise_error(Sequel::MassAssignmentRestriction)
175
175
  end
176
176
 
177
177
  specify "should raise an Error when a nonexistent association is given" do
@@ -23,6 +23,10 @@ describe Sequel::Model, "tree plugin" do
23
23
  @c.associations.sort_by{|x| x.to_s}.should == [:children, :parent]
24
24
  end
25
25
 
26
+ it "should define the correct reciprocals" do
27
+ @c.associations.sort_by{|x| x.to_s}.map{|x| @c.association_reflection(x).reciprocal}.should == [:parent, :children]
28
+ end
29
+
26
30
  it "should define the correct associations when giving options" do
27
31
  klass(:children=>{:name=>:cs}, :parent=>{:name=>:p}).associations.sort_by{|x| x.to_s}.should == [:cs, :p]
28
32
  end
@@ -149,7 +149,7 @@ describe "Sequel::Plugins::XmlSerializer" do
149
149
 
150
150
  it "should raise an exception for xml tags that aren't associations, columns, or setter methods" do
151
151
  Album.send(:undef_method, :blah=)
152
- proc{Album.from_xml(@album.to_xml(:include=>:blah))}.should raise_error(Sequel::Error)
152
+ proc{Album.from_xml(@album.to_xml(:include=>:blah))}.should raise_error(Sequel::MassAssignmentRestriction)
153
153
  end
154
154
 
155
155
  it "should support a to_xml class and dataset method" do
@@ -184,7 +184,7 @@ describe "Sequel::Plugins::XmlSerializer" do
184
184
 
185
185
  it "should raise an error if attempting to set a restricted column and :all_columns is not used" do
186
186
  Artist.restrict_primary_key
187
- proc{Artist.from_xml(@artist.to_xml)}.should raise_error(Sequel::Error)
187
+ proc{Artist.from_xml(@artist.to_xml)}.should raise_error(Sequel::MassAssignmentRestriction)
188
188
  end
189
189
 
190
190
  it "should raise an error if an unsupported association is passed in the :associations option" do
@@ -193,7 +193,7 @@ describe "Sequel::Plugins::XmlSerializer" do
193
193
  end
194
194
 
195
195
  it "should raise an error if using from_xml and XML represents an array" do
196
- proc{Artist.from_xml(Artist.to_xml(:array=>[@artist]))}.should raise_error(Sequel::Error)
196
+ proc{Artist.from_xml(Artist.to_xml(:array=>[@artist]))}.should raise_error(Sequel::MassAssignmentRestriction)
197
197
  end
198
198
 
199
199
  it "should raise an error if using array_from_xml and XML does not represent an array" do
@@ -355,7 +355,7 @@ describe "Bound Argument Types" do
355
355
  @ds.filter(:s=>:$x).prepare(:first, :ps_string).call(:x=>@vs[:s])[:s].should == @vs[:s]
356
356
  end
357
357
 
358
- cspecify "should handle boolean type", [:do, :sqlite], [:odbc, :mssql], [:jdbc, :sqlite], [:jdbc, :db2], :oracle do
358
+ cspecify "should handle boolean type", [:do, :sqlite], [:jdbc, :sqlite], [:jdbc, :db2], :oracle do
359
359
  @ds.filter(:b=>:$x).prepare(:first, :ps_string).call(:x=>@vs[:b])[:b].should == @vs[:b]
360
360
  end
361
361
  end
@@ -113,6 +113,13 @@ describe "Database schema parser" do
113
113
  DB.schema(:items).first.last[:ruby_default].should == 'blah'
114
114
  end
115
115
 
116
+ specify "should make :default nil for a NULL default" do
117
+ DB.create_table!(:items){Integer :number}
118
+ DB.schema(:items).first.last[:default].should == nil
119
+ DB.create_table!(:items){Integer :number, :default=>0}
120
+ DB.schema(:items).first.last[:default].should_not == nil
121
+ end
122
+
116
123
  specify "should parse current timestamp defaults from the schema properly" do
117
124
  DB.create_table!(:items){Time :a, :default=>Sequel::CURRENT_TIMESTAMP}
118
125
  DB.schema(:items).first.last[:ruby_default].should == Sequel::CURRENT_TIMESTAMP
@@ -113,7 +113,7 @@ describe "Supported types" do
113
113
  ds.first[:name].should be_a_kind_of(::Sequel::SQL::Blob)
114
114
  end
115
115
 
116
- cspecify "should support generic boolean type", [:do, :sqlite], [:jdbc, :sqlite], [:jdbc, :db2], [:odbc, :mssql], :oracle do
116
+ cspecify "should support generic boolean type", [:do, :sqlite], [:jdbc, :sqlite], [:jdbc, :db2], :oracle do
117
117
  ds = create_items_table_with_column(:number, TrueClass)
118
118
  ds.insert(:number => true)
119
119
  ds.all.should == [{:number=>true}]
@@ -122,7 +122,7 @@ describe "Supported types" do
122
122
  ds.all.should == [{:number=>true}]
123
123
  end
124
124
 
125
- cspecify "should support generic boolean type with defaults", [:do, :sqlite], [:jdbc, :sqlite], [:jdbc, :db2], [:odbc, :mssql], :oracle do
125
+ cspecify "should support generic boolean type with defaults", [:do, :sqlite], [:jdbc, :sqlite], [:jdbc, :db2], :oracle do
126
126
  ds = create_items_table_with_column(:number, TrueClass, :default=>true)
127
127
  ds.insert
128
128
  ds.all.should == [{:number=>true}]
@@ -672,20 +672,33 @@ describe Sequel::Model, "many_to_one" do
672
672
  end
673
673
 
674
674
  it "should raise error and not call internal add or remove method if before callback returns false, even if raise_on_save_failure is false" do
675
- # The reason for this is that assignment in ruby always returns the argument instead of the result
676
- # of the method, so we can't return nil to signal that the association callback prevented the modification
677
675
  p = @c2.new
678
676
  c = @c2.load(:id=>123)
679
677
  p.raise_on_save_failure = false
680
678
  @c2.many_to_one :parent, :class => @c2, :before_set=>:bs
681
679
  def p.bs(x) false end
682
680
  p.should_not_receive(:_parent=)
683
- proc{p.parent = c}.should raise_error(Sequel::Error)
681
+ proc{p.parent = c}.should raise_error(Sequel::HookFailed)
682
+
683
+ p.parent.should == nil
684
+ p.associations[:parent] = c
685
+ p.parent.should == c
686
+ proc{p.parent = nil}.should raise_error(Sequel::HookFailed)
687
+ end
688
+
689
+ it "should raise error and not call internal add or remove method if before callback calls cancel_action, even if raise_on_save_failure is false" do
690
+ p = @c2.new
691
+ c = @c2.load(:id=>123)
692
+ p.raise_on_save_failure = false
693
+ @c2.many_to_one :parent, :class => @c2, :before_set=>:bs
694
+ def p.bs(x) cancel_action end
695
+ p.should_not_receive(:_parent=)
696
+ proc{p.parent = c}.should raise_error(Sequel::HookFailed)
684
697
 
685
698
  p.parent.should == nil
686
699
  p.associations[:parent] = c
687
700
  p.parent.should == c
688
- proc{p.parent = nil}.should raise_error(Sequel::Error)
701
+ proc{p.parent = nil}.should raise_error(Sequel::HookFailed)
689
702
  end
690
703
 
691
704
  it "should raise an error if a callback is not a proc or symbol" do
@@ -1095,20 +1108,33 @@ describe Sequel::Model, "one_to_one" do
1095
1108
  end
1096
1109
 
1097
1110
  it "should raise error and not call internal add or remove method if before callback returns false, even if raise_on_save_failure is false" do
1098
- # The reason for this is that assignment in ruby always returns the argument instead of the result
1099
- # of the method, so we can't return nil to signal that the association callback prevented the modification
1100
- p = @c2.new
1111
+ p = @c2.load(:id=>321)
1101
1112
  c = @c2.load(:id=>123)
1102
1113
  p.raise_on_save_failure = false
1103
1114
  @c2.one_to_one :parent, :class => @c2, :before_set=>:bs
1104
1115
  def p.bs(x) false end
1105
1116
  p.should_not_receive(:_parent=)
1106
- proc{p.parent = c}.should raise_error(Sequel::Error)
1117
+ proc{p.parent = c}.should raise_error(Sequel::HookFailed)
1107
1118
 
1108
- p.parent.should == nil
1119
+ p.associations[:parent].should == nil
1109
1120
  p.associations[:parent] = c
1110
1121
  p.parent.should == c
1111
- proc{p.parent = nil}.should raise_error(Sequel::Error)
1122
+ proc{p.parent = nil}.should raise_error(Sequel::HookFailed)
1123
+ end
1124
+
1125
+ it "should raise error and not call internal add or remove method if before callback returns false, even if raise_on_save_failure is false" do
1126
+ p = @c2.load(:id=>321)
1127
+ c = @c2.load(:id=>123)
1128
+ p.raise_on_save_failure = false
1129
+ @c2.one_to_one :parent, :class => @c2, :before_set=>:bs
1130
+ def p.bs(x) cancel_action end
1131
+ p.should_not_receive(:_parent=)
1132
+ proc{p.parent = c}.should raise_error(Sequel::HookFailed)
1133
+
1134
+ p.associations[:parent].should == nil
1135
+ p.associations[:parent] = c
1136
+ p.parent.should == c
1137
+ proc{p.parent = nil}.should raise_error(Sequel::HookFailed)
1112
1138
  end
1113
1139
 
1114
1140
  it "should not validate the associated object in setter if the :validate=>false option is used" do
@@ -1837,11 +1863,11 @@ describe Sequel::Model, "one_to_many" do
1837
1863
  p.should_not_receive(:_add_attribute)
1838
1864
  p.should_not_receive(:_remove_attribute)
1839
1865
  p.associations[:attributes] = []
1840
- proc{p.add_attribute(c)}.should raise_error(Sequel::Error)
1866
+ proc{p.add_attribute(c)}.should raise_error(Sequel::HookFailed)
1841
1867
  p.attributes.should == []
1842
1868
  p.associations[:attributes] = [c]
1843
1869
  p.should_receive(:br).once.with(c).and_return(false)
1844
- proc{p.remove_attribute(c)}.should raise_error(Sequel::Error)
1870
+ proc{p.remove_attribute(c)}.should raise_error(Sequel::HookFailed)
1845
1871
  p.attributes.should == [c]
1846
1872
  end
1847
1873
 
@@ -1861,6 +1887,39 @@ describe Sequel::Model, "one_to_many" do
1861
1887
  p.remove_attribute(c).should == nil
1862
1888
  p.attributes.should == [c]
1863
1889
  end
1890
+
1891
+ it "should raise error and not call internal add or remove method if before callback calls cancel_action if raise_on_save_failure is true" do
1892
+ p = @c2.load(:id=>10)
1893
+ c = @c1.load(:id=>123)
1894
+ @c2.one_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
1895
+ def p.ba(o); cancel_action; end
1896
+ p.should_not_receive(:_add_attribute)
1897
+ p.should_not_receive(:_remove_attribute)
1898
+ p.associations[:attributes] = []
1899
+ proc{p.add_attribute(c)}.should raise_error(Sequel::HookFailed)
1900
+ p.attributes.should == []
1901
+ p.associations[:attributes] = [c]
1902
+ def p.br(o); cancel_action; end
1903
+ proc{p.remove_attribute(c)}.should raise_error(Sequel::HookFailed)
1904
+ p.attributes.should == [c]
1905
+ end
1906
+
1907
+ it "should return nil and not call internal add or remove method if before callback calls cancel_action if raise_on_save_failure is false" do
1908
+ p = @c2.load(:id=>10)
1909
+ c = @c1.load(:id=>123)
1910
+ p.raise_on_save_failure = false
1911
+ @c2.one_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
1912
+ def p.ba(o); cancel_action; end
1913
+ p.should_not_receive(:_add_attribute)
1914
+ p.should_not_receive(:_remove_attribute)
1915
+ p.associations[:attributes] = []
1916
+ p.add_attribute(c).should == nil
1917
+ p.attributes.should == []
1918
+ p.associations[:attributes] = [c]
1919
+ def p.br(o); cancel_action; end
1920
+ p.remove_attribute(c).should == nil
1921
+ p.attributes.should == [c]
1922
+ end
1864
1923
  end
1865
1924
 
1866
1925
  describe Sequel::Model, "many_to_many" do
@@ -2642,11 +2701,11 @@ describe Sequel::Model, "many_to_many" do
2642
2701
  p.should_not_receive(:_remove_attribute)
2643
2702
  p.associations[:attributes] = []
2644
2703
  p.raise_on_save_failure = true
2645
- proc{p.add_attribute(c)}.should raise_error(Sequel::Error)
2704
+ proc{p.add_attribute(c)}.should raise_error(Sequel::HookFailed)
2646
2705
  p.attributes.should == []
2647
2706
  p.associations[:attributes] = [c]
2648
2707
  p.should_receive(:br).once.with(c).and_return(false)
2649
- proc{p.remove_attribute(c)}.should raise_error(Sequel::Error)
2708
+ proc{p.remove_attribute(c)}.should raise_error(Sequel::HookFailed)
2650
2709
  p.attributes.should == [c]
2651
2710
  end
2652
2711
 
@@ -2667,6 +2726,41 @@ describe Sequel::Model, "many_to_many" do
2667
2726
  p.attributes.should == [c]
2668
2727
  end
2669
2728
 
2729
+ it "should raise error and not call internal add or remove method if before callback calls cancel_action if raise_on_save_failure is true" do
2730
+ p = @c2.load(:id=>10)
2731
+ c = @c1.load(:id=>123)
2732
+ @c2.many_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
2733
+ def p.ba(o) cancel_action end
2734
+ p.should_receive(:ba).once.with(c).and_return(false)
2735
+ p.should_not_receive(:_add_attribute)
2736
+ p.should_not_receive(:_remove_attribute)
2737
+ p.associations[:attributes] = []
2738
+ p.raise_on_save_failure = true
2739
+ proc{p.add_attribute(c)}.should raise_error(Sequel::HookFailed)
2740
+ p.attributes.should == []
2741
+ p.associations[:attributes] = [c]
2742
+ def p.br(o) cancel_action end
2743
+ proc{p.remove_attribute(c)}.should raise_error(Sequel::HookFailed)
2744
+ p.attributes.should == [c]
2745
+ end
2746
+
2747
+ it "should return nil and not call internal add or remove method if before callback calls cancel_action if raise_on_save_failure is false" do
2748
+ p = @c2.load(:id=>10)
2749
+ c = @c1.load(:id=>123)
2750
+ p.raise_on_save_failure = false
2751
+ @c2.many_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
2752
+ def p.ba(o) cancel_action end
2753
+ p.should_not_receive(:_add_attribute)
2754
+ p.should_not_receive(:_remove_attribute)
2755
+ p.associations[:attributes] = []
2756
+ p.add_attribute(c).should == nil
2757
+ p.attributes.should == []
2758
+ p.associations[:attributes] = [c]
2759
+ def p.br(o) cancel_action end
2760
+ p.remove_attribute(c).should == nil
2761
+ p.attributes.should == [c]
2762
+ end
2763
+
2670
2764
  it "should support a :uniq option that removes duplicates from the association" do
2671
2765
  @c2.many_to_many :attributes, :class => @c1, :uniq=>true
2672
2766
  @c1.dataset._fetch = [{:id=>20}, {:id=>30}, {:id=>20}, {:id=>30}]