sequel 3.29.0 → 3.30.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +35 -3
- data/Rakefile +2 -1
- data/doc/association_basics.rdoc +11 -0
- data/doc/opening_databases.rdoc +2 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/testing.rdoc +17 -3
- data/lib/sequel/adapters/amalgalite.rb +2 -2
- data/lib/sequel/adapters/do/mysql.rb +5 -2
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc.rb +126 -43
- data/lib/sequel/adapters/jdbc/as400.rb +11 -3
- data/lib/sequel/adapters/jdbc/db2.rb +2 -1
- data/lib/sequel/adapters/jdbc/derby.rb +44 -19
- data/lib/sequel/adapters/jdbc/h2.rb +32 -19
- data/lib/sequel/adapters/jdbc/hsqldb.rb +21 -17
- data/lib/sequel/adapters/jdbc/jtds.rb +9 -4
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +21 -7
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -2
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -1
- data/lib/sequel/adapters/jdbc/sqlserver.rb +48 -18
- data/lib/sequel/adapters/mock.rb +2 -1
- data/lib/sequel/adapters/mysql.rb +4 -2
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/openbase.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +6 -6
- data/lib/sequel/adapters/postgres.rb +25 -12
- data/lib/sequel/adapters/shared/access.rb +14 -6
- data/lib/sequel/adapters/shared/db2.rb +36 -13
- data/lib/sequel/adapters/shared/firebird.rb +12 -5
- data/lib/sequel/adapters/shared/informix.rb +11 -3
- data/lib/sequel/adapters/shared/mssql.rb +94 -47
- data/lib/sequel/adapters/shared/mysql.rb +107 -49
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
- data/lib/sequel/adapters/shared/oracle.rb +54 -27
- data/lib/sequel/adapters/shared/postgres.rb +65 -26
- data/lib/sequel/adapters/shared/progress.rb +4 -1
- data/lib/sequel/adapters/shared/sqlite.rb +36 -20
- data/lib/sequel/adapters/sqlite.rb +2 -3
- data/lib/sequel/adapters/swift/mysql.rb +3 -2
- data/lib/sequel/adapters/swift/sqlite.rb +2 -2
- data/lib/sequel/adapters/tinytds.rb +14 -8
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -4
- data/lib/sequel/database/misc.rb +6 -2
- data/lib/sequel/dataset/graph.rb +33 -7
- data/lib/sequel/dataset/prepared_statements.rb +19 -5
- data/lib/sequel/dataset/sql.rb +611 -201
- data/lib/sequel/model/associations.rb +12 -5
- data/lib/sequel/model/base.rb +20 -5
- data/lib/sequel/plugins/sharding.rb +9 -29
- data/lib/sequel/sql.rb +2 -1
- data/lib/sequel/timezones.rb +14 -4
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +10 -0
- data/spec/adapters/oracle_spec.rb +1 -1
- data/spec/core/core_sql_spec.rb +3 -1
- data/spec/core/database_spec.rb +42 -0
- data/spec/core/dataset_spec.rb +10 -3
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/core/object_graph_spec.rb +38 -0
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +35 -39
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +30 -82
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +33 -103
- data/spec/extensions/instance_filters_spec.rb +10 -21
- data/spec/extensions/instance_hooks_spec.rb +6 -24
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +135 -277
- data/spec/extensions/migration_spec.rb +18 -15
- data/spec/extensions/named_timezones_spec.rb +1 -1
- data/spec/extensions/nested_attributes_spec.rb +97 -92
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +22 -37
- data/spec/extensions/prepared_statements_safe_spec.rb +9 -27
- data/spec/extensions/prepared_statements_spec.rb +11 -30
- data/spec/extensions/prepared_statements_with_pk_spec.rb +6 -13
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +3 -6
- data/spec/extensions/serialization_spec.rb +20 -32
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +14 -36
- data/spec/extensions/spec_helper.rb +10 -64
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +1 -2
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/integration/dataset_test.rb +16 -4
- data/spec/integration/prepared_statement_test.rb +4 -2
- data/spec/model/eager_loading_spec.rb +16 -0
- data/spec/model/model_spec.rb +15 -1
- data/spec/model/record_spec.rb +60 -0
- metadata +23 -40
@@ -195,7 +195,7 @@ end
|
|
195
195
|
|
196
196
|
describe "Sequel::IntegerMigrator" do
|
197
197
|
before do
|
198
|
-
dbc = Class.new(
|
198
|
+
dbc = Class.new(Sequel::Mock::Database) do
|
199
199
|
attr_reader :drops, :tables_created, :columns_created, :versions
|
200
200
|
def initialize(*args)
|
201
201
|
super
|
@@ -211,7 +211,7 @@ describe "Sequel::IntegerMigrator" do
|
|
211
211
|
|
212
212
|
def create_table(name, opts={}, &block)
|
213
213
|
super
|
214
|
-
@columns_created << / \(?(\w+) integer.*\)?\z/.match(sqls.last)[1].to_sym
|
214
|
+
@columns_created << / \(?(\w+) integer.*\)?\z/.match(@sqls.last)[1].to_sym
|
215
215
|
@tables_created << name.to_sym
|
216
216
|
end
|
217
217
|
|
@@ -220,8 +220,8 @@ describe "Sequel::IntegerMigrator" do
|
|
220
220
|
ds.extend(Module.new do
|
221
221
|
def count; 1; end
|
222
222
|
def columns; db.columns_created end
|
223
|
-
def insert(h); db.versions.merge!(h); db.
|
224
|
-
def update(h); db.versions.merge!(h); db.
|
223
|
+
def insert(h); db.versions.merge!(h); db.run insert_sql(h) end
|
224
|
+
def update(h); db.versions.merge!(h); db.run update_sql(h) end
|
225
225
|
def fetch_rows(sql); db.execute(sql); yield(db.versions) unless db.versions.empty? end
|
226
226
|
end)
|
227
227
|
ds
|
@@ -315,9 +315,12 @@ end
|
|
315
315
|
|
316
316
|
describe "Sequel::TimestampMigrator" do
|
317
317
|
before do
|
318
|
-
|
319
|
-
|
320
|
-
|
318
|
+
sequel_migration_version = 0
|
319
|
+
@dsc = dsc = Class.new(Sequel::Mock::Dataset) do
|
320
|
+
self::FILES =[]
|
321
|
+
define_method(:sequel_migration_version){sequel_migration_version}
|
322
|
+
define_method(:sequel_migration_version=){|v| sequel_migration_version = v}
|
323
|
+
|
321
324
|
def columns
|
322
325
|
case opts[:from].first
|
323
326
|
when :schema_info, 'schema_info'
|
@@ -332,38 +335,38 @@ describe "Sequel::TimestampMigrator" do
|
|
332
335
|
def fetch_rows(sql)
|
333
336
|
case opts[:from].first
|
334
337
|
when :schema_info, 'schema_info'
|
335
|
-
yield({:version
|
338
|
+
yield({:version=>sequel_migration_version})
|
336
339
|
when :schema_migrations, 'schema_migrations'
|
337
|
-
|
340
|
+
self.class::FILES.sort.each{|f| yield(:filename=>f)}
|
338
341
|
when :sm, 'sm'
|
339
|
-
|
342
|
+
self.class::FILES.sort.each{|f| yield(:fn=>f)}
|
340
343
|
end
|
341
344
|
end
|
342
345
|
|
343
346
|
def insert(h={})
|
344
347
|
case opts[:from].first
|
345
348
|
when :schema_info, 'schema_info'
|
346
|
-
|
349
|
+
self.sequel_migration_version = h.values.first
|
347
350
|
when :schema_migrations, :sm, 'schema_migrations', 'sm'
|
348
|
-
|
351
|
+
self.class::FILES << h.values.first
|
349
352
|
end
|
350
353
|
end
|
351
354
|
|
352
355
|
def update(h={})
|
353
356
|
case opts[:from].first
|
354
357
|
when :schema_info, 'schema_info'
|
355
|
-
|
358
|
+
self.sequel_migration_version = h.values.first
|
356
359
|
end
|
357
360
|
end
|
358
361
|
|
359
362
|
def delete
|
360
363
|
case opts[:from].first
|
361
364
|
when :schema_migrations, :sm, 'schema_migrations', 'sm'
|
362
|
-
|
365
|
+
self.class::FILES.delete(opts[:where].args.last)
|
363
366
|
end
|
364
367
|
end
|
365
368
|
end
|
366
|
-
dbc = Class.new(
|
369
|
+
dbc = Class.new(Sequel::Mock::Database) do
|
367
370
|
tables = {}
|
368
371
|
define_method(:dataset){|*a| dsc.new(self, *a)}
|
369
372
|
define_method(:create_table){|name, *args| tables[name.to_sym] = true}
|
@@ -12,7 +12,7 @@ describe "Sequel named_timezones extension" do
|
|
12
12
|
before do
|
13
13
|
@tz_in = TZInfo::Timezone.get('America/Los_Angeles')
|
14
14
|
@tz_out = TZInfo::Timezone.get('America/New_York')
|
15
|
-
@db =
|
15
|
+
@db = Sequel.mock
|
16
16
|
@dt = DateTime.civil(2009,6,1,10,20,30,0)
|
17
17
|
Sequel.application_timezone = 'America/Los_Angeles'
|
18
18
|
Sequel.database_timezone = 'America/New_York'
|
@@ -1,50 +1,30 @@
|
|
1
1
|
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
2
|
|
3
3
|
describe "NestedAttributes plugin" do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
ds_mod = Module.new do
|
10
|
-
def empty?; false; end
|
11
|
-
define_method(:insert) do |h|
|
12
|
-
x = ii.call
|
13
|
-
mods << [:i, first_source, h, x]
|
14
|
-
x
|
15
|
-
end
|
16
|
-
define_method(:supports_insert_select?){true}
|
17
|
-
define_method(:insert_select) do |h|
|
18
|
-
x = ii.call
|
19
|
-
mods << [:is, first_source, h, x]
|
20
|
-
h.merge(:id=>x)
|
21
|
-
end
|
22
|
-
define_method(:update) do |h|
|
23
|
-
mods << [:u, first_source, h, literal(opts[:where])]
|
24
|
-
1
|
25
|
-
end
|
26
|
-
define_method(:delete) do
|
27
|
-
mods << [:d, first_source, literal(opts[:where])]
|
28
|
-
1
|
29
|
-
end
|
30
|
-
end
|
31
|
-
db = Sequel::Database.new({})
|
32
|
-
def db.connect(opts)
|
33
|
-
Object.new
|
34
|
-
end
|
35
|
-
db.meta_def(:dataset) do |*a|
|
36
|
-
x = super(*a)
|
37
|
-
x.extend(ds_mod)
|
38
|
-
x
|
4
|
+
def check_sqls(should, is)
|
5
|
+
if should.is_a?(Array)
|
6
|
+
should.should include(is)
|
7
|
+
else
|
8
|
+
should.should == is
|
39
9
|
end
|
40
|
-
|
10
|
+
end
|
11
|
+
|
12
|
+
def check_sql_array(*shoulds)
|
13
|
+
sqls = @db.sqls
|
14
|
+
shoulds.length.should == sqls.length
|
15
|
+
shoulds.zip(sqls){|s, i| check_sqls(s, i)}
|
16
|
+
end
|
17
|
+
|
18
|
+
before do
|
19
|
+
@db = Sequel.mock(:autoid=>1, :numrows=>1)
|
20
|
+
@c = Class.new(Sequel::Model(@db))
|
41
21
|
@c.plugin :nested_attributes
|
42
22
|
@Artist = Class.new(@c).set_dataset(:artists)
|
43
23
|
@Album = Class.new(@c).set_dataset(:albums)
|
44
24
|
@Tag = Class.new(@c).set_dataset(:tags)
|
45
|
-
|
46
|
-
|
47
|
-
|
25
|
+
@Artist.plugin :skip_create_refresh
|
26
|
+
@Album.plugin :skip_create_refresh
|
27
|
+
@Tag.plugin :skip_create_refresh
|
48
28
|
@Artist.columns :id, :name
|
49
29
|
@Album.columns :id, :name, :artist_id
|
50
30
|
@Tag.columns :id, :name
|
@@ -54,36 +34,44 @@ describe "NestedAttributes plugin" do
|
|
54
34
|
@Album.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
|
55
35
|
@Artist.nested_attributes :albums, :first_album, :destroy=>true, :remove=>true
|
56
36
|
@Album.nested_attributes :artist, :tags, :destroy=>true, :remove=>true
|
37
|
+
@db.sqls
|
57
38
|
end
|
58
39
|
|
59
40
|
it "should support creating new many_to_one objects" do
|
60
41
|
a = @Album.new({:name=>'Al', :artist_attributes=>{:name=>'Ar'}})
|
61
|
-
@
|
42
|
+
@db.sqls.should == []
|
62
43
|
a.save
|
63
|
-
|
44
|
+
check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
|
45
|
+
["INSERT INTO albums (name, artist_id) VALUES ('Al', 1)", "INSERT INTO albums (artist_id, name) VALUES (1, 'Al')"])
|
64
46
|
end
|
65
47
|
|
66
48
|
it "should support creating new one_to_one objects" do
|
67
49
|
a = @Artist.new(:name=>'Ar')
|
68
50
|
a.id = 1
|
69
51
|
a.first_album_attributes = {:name=>'Al'}
|
70
|
-
@
|
52
|
+
@db.sqls.should == []
|
71
53
|
a.save
|
72
|
-
|
54
|
+
check_sql_array(["INSERT INTO artists (name, id) VALUES ('Ar', 1)", "INSERT INTO artists (id, name) VALUES (1, 'Ar')"],
|
55
|
+
"INSERT INTO albums (name) VALUES ('Al')",
|
56
|
+
"UPDATE albums SET artist_id = NULL WHERE ((artist_id = 1) AND (id != 2))",
|
57
|
+
["UPDATE albums SET artist_id = 1, name = 'Al' WHERE (id = 2)", "UPDATE albums SET name = 'Al', artist_id = 1 WHERE (id = 2)"])
|
73
58
|
end
|
74
59
|
|
75
60
|
it "should support creating new one_to_many objects" do
|
76
61
|
a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:name=>'Al'}]})
|
77
|
-
@
|
62
|
+
@db.sqls.should == []
|
78
63
|
a.save
|
79
|
-
|
64
|
+
check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
|
65
|
+
["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
|
80
66
|
end
|
81
67
|
|
82
68
|
it "should support creating new many_to_many objects" do
|
83
69
|
a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}]})
|
84
|
-
@
|
70
|
+
@db.sqls.should == []
|
85
71
|
a.save
|
86
|
-
|
72
|
+
check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
|
73
|
+
"INSERT INTO tags (name) VALUES ('T')",
|
74
|
+
["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"])
|
87
75
|
end
|
88
76
|
|
89
77
|
it "should add new objects to the cached association array as soon as the *_attributes= method is called" do
|
@@ -97,9 +85,9 @@ describe "NestedAttributes plugin" do
|
|
97
85
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
98
86
|
al.associations[:artist] = ar
|
99
87
|
al.set(:artist_attributes=>{:id=>'20', :name=>'Ar2'})
|
100
|
-
@
|
88
|
+
@db.sqls.should == []
|
101
89
|
al.save
|
102
|
-
@
|
90
|
+
@db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE artists SET name = 'Ar2' WHERE (id = 20)"]
|
103
91
|
end
|
104
92
|
|
105
93
|
it "should support updating one_to_one objects" do
|
@@ -107,9 +95,9 @@ describe "NestedAttributes plugin" do
|
|
107
95
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
108
96
|
ar.associations[:first_album] = al
|
109
97
|
ar.set(:first_album_attributes=>{:id=>10, :name=>'Al2'})
|
110
|
-
@
|
98
|
+
@db.sqls.should == []
|
111
99
|
ar.save
|
112
|
-
@
|
100
|
+
@db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
|
113
101
|
end
|
114
102
|
|
115
103
|
it "should support updating one_to_many objects" do
|
@@ -117,9 +105,9 @@ describe "NestedAttributes plugin" do
|
|
117
105
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
118
106
|
ar.associations[:albums] = [al]
|
119
107
|
ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2'}])
|
120
|
-
@
|
108
|
+
@db.sqls.should == []
|
121
109
|
ar.save
|
122
|
-
@
|
110
|
+
@db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
|
123
111
|
end
|
124
112
|
|
125
113
|
it "should support updating many_to_many objects" do
|
@@ -127,9 +115,9 @@ describe "NestedAttributes plugin" do
|
|
127
115
|
t = @Tag.load(:id=>20, :name=>'T')
|
128
116
|
a.associations[:tags] = [t]
|
129
117
|
a.set(:tags_attributes=>[{:id=>20, :name=>'T2'}])
|
130
|
-
@
|
118
|
+
@db.sqls.should == []
|
131
119
|
a.save
|
132
|
-
@
|
120
|
+
@db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
|
133
121
|
end
|
134
122
|
|
135
123
|
it "should support removing many_to_one objects" do
|
@@ -137,9 +125,9 @@ describe "NestedAttributes plugin" do
|
|
137
125
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
138
126
|
al.associations[:artist] = ar
|
139
127
|
al.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})
|
140
|
-
@
|
128
|
+
@db.sqls.should == []
|
141
129
|
al.save
|
142
|
-
|
130
|
+
check_sql_array(["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"])
|
143
131
|
end
|
144
132
|
|
145
133
|
it "should support removing one_to_one objects" do
|
@@ -147,10 +135,9 @@ describe "NestedAttributes plugin" do
|
|
147
135
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
148
136
|
ar.associations[:first_album] = al
|
149
137
|
ar.set(:first_album_attributes=>{:id=>10, :_remove=>'t'})
|
150
|
-
@
|
138
|
+
@db.sqls.should == []
|
151
139
|
ar.save
|
152
|
-
@
|
153
|
-
|
140
|
+
@db.sqls.should == ["UPDATE albums SET artist_id = NULL WHERE (artist_id = 20)", "UPDATE artists SET name = 'Ar' WHERE (id = 20)"]
|
154
141
|
end
|
155
142
|
|
156
143
|
it "should support removing one_to_many objects" do
|
@@ -158,9 +145,12 @@ describe "NestedAttributes plugin" do
|
|
158
145
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
159
146
|
ar.associations[:albums] = [al]
|
160
147
|
ar.set(:albums_attributes=>[{:id=>10, :_remove=>'t'}])
|
161
|
-
@
|
148
|
+
@db.sqls.should == []
|
149
|
+
@Album.dataset._fetch = {:id=>1}
|
162
150
|
ar.save
|
163
|
-
|
151
|
+
check_sql_array("SELECT 1 FROM albums WHERE ((albums.artist_id = 20) AND (id = 10)) LIMIT 1",
|
152
|
+
["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"],
|
153
|
+
"UPDATE artists SET name = 'Ar' WHERE (id = 20)")
|
164
154
|
end
|
165
155
|
|
166
156
|
it "should support removing many_to_many objects" do
|
@@ -168,9 +158,9 @@ describe "NestedAttributes plugin" do
|
|
168
158
|
t = @Tag.load(:id=>20, :name=>'T')
|
169
159
|
a.associations[:tags] = [t]
|
170
160
|
a.set(:tags_attributes=>[{:id=>20, :_remove=>true}])
|
171
|
-
@
|
161
|
+
@db.sqls.should == []
|
172
162
|
a.save
|
173
|
-
@
|
163
|
+
@db.sqls.should == ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)"]
|
174
164
|
end
|
175
165
|
|
176
166
|
it "should support destroying many_to_one objects" do
|
@@ -178,9 +168,10 @@ describe "NestedAttributes plugin" do
|
|
178
168
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
179
169
|
al.associations[:artist] = ar
|
180
170
|
al.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})
|
181
|
-
@
|
171
|
+
@db.sqls.should == []
|
182
172
|
al.save
|
183
|
-
|
173
|
+
check_sql_array(["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"],
|
174
|
+
"DELETE FROM artists WHERE (id = 20)")
|
184
175
|
end
|
185
176
|
|
186
177
|
it "should support destroying one_to_one objects" do
|
@@ -188,9 +179,9 @@ describe "NestedAttributes plugin" do
|
|
188
179
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
189
180
|
ar.associations[:first_album] = al
|
190
181
|
ar.set(:first_album_attributes=>{:id=>10, :_delete=>'t'})
|
191
|
-
@
|
182
|
+
@db.sqls.should == []
|
192
183
|
ar.save
|
193
|
-
@
|
184
|
+
@db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
|
194
185
|
end
|
195
186
|
|
196
187
|
it "should support destroying one_to_many objects" do
|
@@ -198,9 +189,9 @@ describe "NestedAttributes plugin" do
|
|
198
189
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
199
190
|
ar.associations[:albums] = [al]
|
200
191
|
ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
|
201
|
-
@
|
192
|
+
@db.sqls.should == []
|
202
193
|
ar.save
|
203
|
-
@
|
194
|
+
@db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
|
204
195
|
end
|
205
196
|
|
206
197
|
it "should support destroying many_to_many objects" do
|
@@ -208,9 +199,9 @@ describe "NestedAttributes plugin" do
|
|
208
199
|
t = @Tag.load(:id=>20, :name=>'T')
|
209
200
|
a.associations[:tags] = [t]
|
210
201
|
a.set(:tags_attributes=>[{:id=>20, :_delete=>true}])
|
211
|
-
@
|
202
|
+
@db.sqls.should == []
|
212
203
|
a.save
|
213
|
-
@
|
204
|
+
@db.sqls.should == ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)", "DELETE FROM tags WHERE (id = 20)"]
|
214
205
|
end
|
215
206
|
|
216
207
|
it "should support both string and symbol keys in nested attribute hashes" do
|
@@ -218,9 +209,9 @@ describe "NestedAttributes plugin" do
|
|
218
209
|
t = @Tag.load(:id=>20, :name=>'T')
|
219
210
|
a.associations[:tags] = [t]
|
220
211
|
a.set('tags_attributes'=>[{'id'=>20, '_delete'=>true}])
|
221
|
-
@
|
212
|
+
@db.sqls.should == []
|
222
213
|
a.save
|
223
|
-
@
|
214
|
+
@db.sqls.should == ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)", "DELETE FROM tags WHERE (id = 20)"]
|
224
215
|
end
|
225
216
|
|
226
217
|
it "should support using a hash instead of an array for to_many nested attributes" do
|
@@ -228,9 +219,9 @@ describe "NestedAttributes plugin" do
|
|
228
219
|
t = @Tag.load(:id=>20, :name=>'T')
|
229
220
|
a.associations[:tags] = [t]
|
230
221
|
a.set('tags_attributes'=>{'1'=>{'id'=>20, '_delete'=>true}})
|
231
|
-
@
|
222
|
+
@db.sqls.should == []
|
232
223
|
a.save
|
233
|
-
@
|
224
|
+
@db.sqls.should == ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)", "DELETE FROM tags WHERE (id = 20)"]
|
234
225
|
end
|
235
226
|
|
236
227
|
it "should only allow destroying associated objects if :destroy option is used in the nested_attributes call" do
|
@@ -267,9 +258,9 @@ describe "NestedAttributes plugin" do
|
|
267
258
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
268
259
|
ar.associations[:albums] = [al]
|
269
260
|
ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])
|
270
|
-
@
|
261
|
+
@db.sqls.should == []
|
271
262
|
ar.save
|
272
|
-
@
|
263
|
+
@db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)"]
|
273
264
|
end
|
274
265
|
|
275
266
|
it "should not save if nested attribute is not valid and should include nested attribute validation errors in the main object's validation errors" do
|
@@ -280,10 +271,10 @@ describe "NestedAttributes plugin" do
|
|
280
271
|
end
|
281
272
|
end
|
282
273
|
a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
|
283
|
-
@
|
274
|
+
@db.sqls.should == []
|
284
275
|
proc{a.save}.should raise_error(Sequel::ValidationFailed)
|
285
276
|
a.errors.full_messages.should == ['artist name cannot be Ar']
|
286
|
-
@
|
277
|
+
@db.sqls.should == []
|
287
278
|
# Should preserve attributes
|
288
279
|
a.artist.name.should == 'Ar'
|
289
280
|
end
|
@@ -298,9 +289,10 @@ describe "NestedAttributes plugin" do
|
|
298
289
|
end
|
299
290
|
end
|
300
291
|
a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
|
301
|
-
@
|
292
|
+
@db.sqls.should == []
|
302
293
|
a.save
|
303
|
-
|
294
|
+
check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
|
295
|
+
["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
|
304
296
|
end
|
305
297
|
|
306
298
|
it "should not attempt to validate nested attributes if the :validate=>false option is passed to save" do
|
@@ -311,23 +303,25 @@ describe "NestedAttributes plugin" do
|
|
311
303
|
end
|
312
304
|
end
|
313
305
|
a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
|
314
|
-
@
|
306
|
+
@db.sqls.should == []
|
315
307
|
a.save(:validate=>false)
|
316
|
-
|
308
|
+
check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
|
309
|
+
["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
|
317
310
|
end
|
318
311
|
|
319
312
|
it "should not accept nested attributes unless explicitly specified" do
|
320
313
|
@Artist.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
|
321
314
|
proc{@Artist.create({:name=>'Ar', :tags_attributes=>[{:name=>'T'}]})}.should raise_error(Sequel::Error)
|
322
|
-
@
|
315
|
+
@db.sqls.should == []
|
323
316
|
end
|
324
317
|
|
325
318
|
it "should save when save_changes or update is called if nested attribute associated objects changed but there are no changes to the main object" do
|
326
319
|
al = @Album.load(:id=>10, :name=>'Al')
|
327
320
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
328
321
|
al.associations[:artist] = ar
|
322
|
+
@db.sqls.should == []
|
329
323
|
al.update(:artist_attributes=>{:id=>'20', :name=>'Ar2'})
|
330
|
-
@
|
324
|
+
@db.sqls.should == ["UPDATE artists SET name = 'Ar2' WHERE (id = 20)"]
|
331
325
|
end
|
332
326
|
|
333
327
|
it "should have a :limit option limiting the amount of entries" do
|
@@ -335,17 +329,25 @@ describe "NestedAttributes plugin" do
|
|
335
329
|
arr = [{:name=>'T'}]
|
336
330
|
proc{@Album.new({:name=>'Al', :tags_attributes=>arr*3})}.should raise_error(Sequel::Error)
|
337
331
|
a = @Album.new({:name=>'Al', :tags_attributes=>arr*2})
|
338
|
-
@
|
332
|
+
@db.sqls.should == []
|
339
333
|
a.save
|
340
|
-
|
334
|
+
check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
|
335
|
+
"INSERT INTO tags (name) VALUES ('T')",
|
336
|
+
["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
|
337
|
+
"INSERT INTO tags (name) VALUES ('T')",
|
338
|
+
["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
|
341
339
|
end
|
342
340
|
|
343
341
|
it "should accept a block that each hash gets passed to determine if it should be processed" do
|
344
342
|
@Album.nested_attributes(:tags){|h| h[:name].empty?}
|
345
343
|
a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}, {:name=>''}, {:name=>'T2'}]})
|
346
|
-
@
|
344
|
+
@db.sqls.should == []
|
347
345
|
a.save
|
348
|
-
|
346
|
+
check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
|
347
|
+
"INSERT INTO tags (name) VALUES ('T')",
|
348
|
+
["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
|
349
|
+
"INSERT INTO tags (name) VALUES ('T2')",
|
350
|
+
["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
|
349
351
|
end
|
350
352
|
|
351
353
|
it "should return objects created/modified in the internal methods" do
|
@@ -391,9 +393,12 @@ describe "NestedAttributes plugin" do
|
|
391
393
|
t = @Tag.load(:id=>30, :name=>'T', :number=>10)
|
392
394
|
al.associations[:tags] = [t]
|
393
395
|
al.set(:tags_attributes=>[{:id=>30, :name=>'T2'}, {:name=>'T3'}])
|
394
|
-
@
|
396
|
+
@db.sqls.should == []
|
395
397
|
al.save
|
396
|
-
|
398
|
+
check_sql_array("UPDATE albums SET name = 'Al' WHERE (id = 10)",
|
399
|
+
"UPDATE tags SET name = 'T2' WHERE (id = 30)",
|
400
|
+
"INSERT INTO tags (name) VALUES ('T3')",
|
401
|
+
["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
|
397
402
|
proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
|
398
403
|
proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
|
399
404
|
end
|