sequel 4.21.0 → 4.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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}]