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.
- checksums.yaml +4 -4
- data/CHANGELOG +32 -0
- data/README.rdoc +3 -4
- data/doc/opening_databases.rdoc +10 -75
- data/doc/release_notes/4.22.0.txt +72 -0
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/cubrid.rb +3 -3
- data/lib/sequel/adapters/db2.rb +1 -0
- data/lib/sequel/adapters/dbi.rb +1 -0
- data/lib/sequel/adapters/fdbsql.rb +3 -2
- data/lib/sequel/adapters/firebird.rb +1 -0
- data/lib/sequel/adapters/ibmdb.rb +1 -21
- data/lib/sequel/adapters/informix.rb +1 -0
- data/lib/sequel/adapters/jdbc.rb +37 -49
- data/lib/sequel/adapters/jdbc/fdbsql.rb +1 -0
- data/lib/sequel/adapters/mysql.rb +5 -3
- data/lib/sequel/adapters/mysql2.rb +5 -2
- data/lib/sequel/adapters/odbc.rb +8 -4
- data/lib/sequel/adapters/openbase.rb +1 -0
- data/lib/sequel/adapters/oracle.rb +3 -46
- data/lib/sequel/adapters/postgres.rb +3 -36
- data/lib/sequel/adapters/shared/access.rb +1 -1
- data/lib/sequel/adapters/shared/fdbsql.rb +3 -3
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +12 -44
- data/lib/sequel/adapters/shared/oracle.rb +6 -2
- data/lib/sequel/adapters/shared/postgres.rb +6 -6
- data/lib/sequel/adapters/shared/sqlite.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +3 -46
- data/lib/sequel/adapters/tinytds.rb +12 -28
- data/lib/sequel/adapters/utils/pg_types.rb +1 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +63 -16
- data/lib/sequel/connection_pool/threaded.rb +72 -18
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +2 -2
- data/lib/sequel/database/misc.rb +5 -5
- data/lib/sequel/database/query.rb +3 -2
- data/lib/sequel/database/schema_generator.rb +19 -19
- data/lib/sequel/database/schema_methods.rb +2 -2
- data/lib/sequel/database/transactions.rb +3 -3
- data/lib/sequel/dataset/actions.rb +18 -8
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/prepared_statements.rb +28 -1
- data/lib/sequel/dataset/query.rb +7 -7
- data/lib/sequel/exceptions.rb +27 -24
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/constraint_validations.rb +2 -2
- data/lib/sequel/extensions/date_arithmetic.rb +2 -2
- data/lib/sequel/extensions/pg_array.rb +10 -1
- data/lib/sequel/extensions/pg_row.rb +1 -1
- data/lib/sequel/extensions/pg_static_cache_updater.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +8 -8
- data/lib/sequel/extensions/split_array_nil.rb +1 -1
- data/lib/sequel/model.rb +1 -1
- data/lib/sequel/model/associations.rb +18 -11
- data/lib/sequel/model/base.rb +15 -15
- data/lib/sequel/model/exceptions.rb +11 -2
- data/lib/sequel/plugins/accessed_columns.rb +1 -1
- data/lib/sequel/plugins/auto_validations.rb +1 -1
- data/lib/sequel/plugins/boolean_readers.rb +1 -1
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -7
- data/lib/sequel/plugins/composition.rb +1 -1
- data/lib/sequel/plugins/constraint_validations.rb +2 -2
- data/lib/sequel/plugins/csv_serializer.rb +171 -0
- data/lib/sequel/plugins/dirty.rb +2 -2
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +5 -5
- data/lib/sequel/plugins/pg_array_associations.rb +4 -4
- data/lib/sequel/plugins/prepared_statements.rb +2 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +6 -6
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
- data/lib/sequel/plugins/sharding.rb +3 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +5 -13
- data/lib/sequel/plugins/static_cache.rb +2 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
- data/lib/sequel/plugins/tree.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +2 -2
- data/lib/sequel/plugins/validation_helpers.rb +4 -4
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +17 -0
- data/spec/core/connection_pool_spec.rb +1 -1
- data/spec/core/dataset_spec.rb +22 -0
- data/spec/extensions/auto_validations_spec.rb +1 -1
- data/spec/extensions/blacklist_security_spec.rb +2 -2
- data/spec/extensions/csv_serializer_spec.rb +173 -0
- data/spec/extensions/json_serializer_spec.rb +2 -2
- data/spec/extensions/nested_attributes_spec.rb +9 -9
- data/spec/extensions/pg_array_spec.rb +5 -0
- data/spec/extensions/single_table_inheritance_spec.rb +21 -0
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/tree_spec.rb +4 -0
- data/spec/extensions/xml_serializer_spec.rb +3 -3
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +7 -0
- data/spec/integration/type_test.rb +2 -2
- data/spec/model/associations_spec.rb +108 -14
- data/spec/model/base_spec.rb +8 -8
- data/spec/model/record_spec.rb +7 -7
- 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, :
|
|
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::
|
|
20
|
-
proc{@c.new.set_except({:x => 1, :y => 2, :z=>3}, :x, :y)}.should raise_error(Sequel::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
650
|
-
proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::
|
|
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::
|
|
668
|
-
proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::
|
|
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::
|
|
686
|
-
proc{al.set_nested_attributes(:tags, [{:name=>'T2', :number=>3}], :fields=>[:name])}.should raise_error(Sequel::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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], [:
|
|
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],
|
|
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],
|
|
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::
|
|
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::
|
|
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
|
-
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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}]
|