sequel 3.29.0 → 3.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/CHANGELOG +35 -3
  2. data/Rakefile +2 -1
  3. data/doc/association_basics.rdoc +11 -0
  4. data/doc/opening_databases.rdoc +2 -0
  5. data/doc/release_notes/3.30.0.txt +135 -0
  6. data/doc/testing.rdoc +17 -3
  7. data/lib/sequel/adapters/amalgalite.rb +2 -2
  8. data/lib/sequel/adapters/do/mysql.rb +5 -2
  9. data/lib/sequel/adapters/ibmdb.rb +2 -2
  10. data/lib/sequel/adapters/jdbc.rb +126 -43
  11. data/lib/sequel/adapters/jdbc/as400.rb +11 -3
  12. data/lib/sequel/adapters/jdbc/db2.rb +2 -1
  13. data/lib/sequel/adapters/jdbc/derby.rb +44 -19
  14. data/lib/sequel/adapters/jdbc/h2.rb +32 -19
  15. data/lib/sequel/adapters/jdbc/hsqldb.rb +21 -17
  16. data/lib/sequel/adapters/jdbc/jtds.rb +9 -4
  17. data/lib/sequel/adapters/jdbc/mssql.rb +3 -1
  18. data/lib/sequel/adapters/jdbc/mysql.rb +2 -1
  19. data/lib/sequel/adapters/jdbc/oracle.rb +21 -7
  20. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -2
  21. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -1
  22. data/lib/sequel/adapters/jdbc/sqlserver.rb +48 -18
  23. data/lib/sequel/adapters/mock.rb +2 -1
  24. data/lib/sequel/adapters/mysql.rb +4 -2
  25. data/lib/sequel/adapters/mysql2.rb +2 -2
  26. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  27. data/lib/sequel/adapters/openbase.rb +1 -1
  28. data/lib/sequel/adapters/oracle.rb +6 -6
  29. data/lib/sequel/adapters/postgres.rb +25 -12
  30. data/lib/sequel/adapters/shared/access.rb +14 -6
  31. data/lib/sequel/adapters/shared/db2.rb +36 -13
  32. data/lib/sequel/adapters/shared/firebird.rb +12 -5
  33. data/lib/sequel/adapters/shared/informix.rb +11 -3
  34. data/lib/sequel/adapters/shared/mssql.rb +94 -47
  35. data/lib/sequel/adapters/shared/mysql.rb +107 -49
  36. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
  37. data/lib/sequel/adapters/shared/oracle.rb +54 -27
  38. data/lib/sequel/adapters/shared/postgres.rb +65 -26
  39. data/lib/sequel/adapters/shared/progress.rb +4 -1
  40. data/lib/sequel/adapters/shared/sqlite.rb +36 -20
  41. data/lib/sequel/adapters/sqlite.rb +2 -3
  42. data/lib/sequel/adapters/swift/mysql.rb +3 -2
  43. data/lib/sequel/adapters/swift/sqlite.rb +2 -2
  44. data/lib/sequel/adapters/tinytds.rb +14 -8
  45. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -4
  46. data/lib/sequel/database/misc.rb +6 -2
  47. data/lib/sequel/dataset/graph.rb +33 -7
  48. data/lib/sequel/dataset/prepared_statements.rb +19 -5
  49. data/lib/sequel/dataset/sql.rb +611 -201
  50. data/lib/sequel/model/associations.rb +12 -5
  51. data/lib/sequel/model/base.rb +20 -5
  52. data/lib/sequel/plugins/sharding.rb +9 -29
  53. data/lib/sequel/sql.rb +2 -1
  54. data/lib/sequel/timezones.rb +14 -4
  55. data/lib/sequel/version.rb +1 -1
  56. data/spec/adapters/mysql_spec.rb +10 -0
  57. data/spec/adapters/oracle_spec.rb +1 -1
  58. data/spec/core/core_sql_spec.rb +3 -1
  59. data/spec/core/database_spec.rb +42 -0
  60. data/spec/core/dataset_spec.rb +10 -3
  61. data/spec/core/mock_adapter_spec.rb +4 -0
  62. data/spec/core/object_graph_spec.rb +38 -0
  63. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  64. data/spec/extensions/association_dependencies_spec.rb +2 -12
  65. data/spec/extensions/association_pks_spec.rb +35 -39
  66. data/spec/extensions/caching_spec.rb +23 -50
  67. data/spec/extensions/class_table_inheritance_spec.rb +30 -82
  68. data/spec/extensions/composition_spec.rb +18 -13
  69. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  70. data/spec/extensions/identity_map_spec.rb +33 -103
  71. data/spec/extensions/instance_filters_spec.rb +10 -21
  72. data/spec/extensions/instance_hooks_spec.rb +6 -24
  73. data/spec/extensions/json_serializer_spec.rb +4 -5
  74. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  75. data/spec/extensions/list_spec.rb +17 -39
  76. data/spec/extensions/many_through_many_spec.rb +135 -277
  77. data/spec/extensions/migration_spec.rb +18 -15
  78. data/spec/extensions/named_timezones_spec.rb +1 -1
  79. data/spec/extensions/nested_attributes_spec.rb +97 -92
  80. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  81. data/spec/extensions/prepared_statements_associations_spec.rb +22 -37
  82. data/spec/extensions/prepared_statements_safe_spec.rb +9 -27
  83. data/spec/extensions/prepared_statements_spec.rb +11 -30
  84. data/spec/extensions/prepared_statements_with_pk_spec.rb +6 -13
  85. data/spec/extensions/pretty_table_spec.rb +1 -6
  86. data/spec/extensions/rcte_tree_spec.rb +41 -43
  87. data/spec/extensions/schema_dumper_spec.rb +3 -6
  88. data/spec/extensions/serialization_spec.rb +20 -32
  89. data/spec/extensions/sharding_spec.rb +66 -140
  90. data/spec/extensions/single_table_inheritance_spec.rb +14 -36
  91. data/spec/extensions/spec_helper.rb +10 -64
  92. data/spec/extensions/sql_expr_spec.rb +20 -60
  93. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  94. data/spec/extensions/timestamps_spec.rb +6 -6
  95. data/spec/extensions/to_dot_spec.rb +1 -2
  96. data/spec/extensions/touch_spec.rb +13 -14
  97. data/spec/extensions/tree_spec.rb +11 -26
  98. data/spec/extensions/update_primary_key_spec.rb +30 -24
  99. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  100. data/spec/extensions/validation_helpers_spec.rb +16 -35
  101. data/spec/integration/dataset_test.rb +16 -4
  102. data/spec/integration/prepared_statement_test.rb +4 -2
  103. data/spec/model/eager_loading_spec.rb +16 -0
  104. data/spec/model/model_spec.rb +15 -1
  105. data/spec/model/record_spec.rb +60 -0
  106. metadata +23 -40
@@ -195,7 +195,7 @@ end
195
195
 
196
196
  describe "Sequel::IntegerMigrator" do
197
197
  before do
198
- dbc = Class.new(MockDatabase) do
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.sqls << insert_sql(h) end
224
- def update(h); db.versions.merge!(h); db.sqls << update_sql(h) end
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
- $sequel_migration_version = 0
319
- $sequel_migration_files = []
320
- @dsc = dsc = Class.new(MockDataset) do
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=>$sequel_migration_version})
338
+ yield({:version=>sequel_migration_version})
336
339
  when :schema_migrations, 'schema_migrations'
337
- $sequel_migration_files.sort.each{|f| yield(:filename=>f)}
340
+ self.class::FILES.sort.each{|f| yield(:filename=>f)}
338
341
  when :sm, 'sm'
339
- $sequel_migration_files.sort.each{|f| yield(:fn=>f)}
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
- $sequel_migration_version = h.values.first
349
+ self.sequel_migration_version = h.values.first
347
350
  when :schema_migrations, :sm, 'schema_migrations', 'sm'
348
- $sequel_migration_files << h.values.first
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
- $sequel_migration_version = h.values.first
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
- $sequel_migration_files.delete(opts[:where].args.last)
365
+ self.class::FILES.delete(opts[:where].args.last)
363
366
  end
364
367
  end
365
368
  end
366
- dbc = Class.new(MockDatabase) do
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 = MockDatabase.new
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
- before do
5
- mods = @mods = []
6
- i = 0
7
- gi = lambda{i}
8
- ii = lambda{i+=1}
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
- @c = Class.new(Sequel::Model(db))
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
- [@Artist, @Album, @Tag].each do |m|
46
- m.dataset.extend(ds_mod)
47
- end
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
- @mods.should == []
42
+ @db.sqls.should == []
62
43
  a.save
63
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
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
- @mods.should == []
52
+ @db.sqls.should == []
71
53
  a.save
72
- @mods.should == [[:is, :artists, {:name=>"Ar", :id=>1}, 1], [:is, :albums, {:name=>"Al"}, 2], [:u, :albums, {:artist_id=>nil}, "((artist_id = 1) AND (id != 2))"], [:u, :albums, {:name=>"Al", :artist_id=>1}, "(id = 2)"]]
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
- @mods.should == []
62
+ @db.sqls.should == []
78
63
  a.save
79
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
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
- @mods.should == []
70
+ @db.sqls.should == []
85
71
  a.save
86
- @mods.should == [[:is, :albums, {:name=>"Al"}, 1], [:is, :tags, {:name=>"T"}, 2], [:i, :at, {:album_id=>1, :tag_id=>2}, 3]]
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
- @mods.should == []
88
+ @db.sqls.should == []
101
89
  al.save
102
- @mods.should == [[:u, :albums, {:name=>"Al"}, '(id = 10)'], [:u, :artists, {:name=>"Ar2"}, '(id = 20)']]
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
- @mods.should == []
98
+ @db.sqls.should == []
111
99
  ar.save
112
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:u, :albums, {:name=>"Al2"}, '(id = 10)']]
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
- @mods.should == []
108
+ @db.sqls.should == []
121
109
  ar.save
122
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:u, :albums, {:name=>"Al2"}, '(id = 10)']]
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
- @mods.should == []
118
+ @db.sqls.should == []
131
119
  a.save
132
- @mods.should == [[:u, :albums, {:name=>"Al"}, '(id = 10)'], [:u, :tags, {:name=>"T2"}, '(id = 20)']]
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
- @mods.should == []
128
+ @db.sqls.should == []
141
129
  al.save
142
- @mods.should == [[:u, :albums, {:artist_id=>nil, :name=>'Al'}, '(id = 10)']]
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
- @mods.should == []
138
+ @db.sqls.should == []
151
139
  ar.save
152
- @mods.should == [[:u, :albums, {:artist_id=>nil}, "(artist_id = 20)"], [:u, :artists, {:name=>"Ar"}, "(id = 20)"]]
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
- @mods.should == []
148
+ @db.sqls.should == []
149
+ @Album.dataset._fetch = {:id=>1}
162
150
  ar.save
163
- @mods.should == [[:u, :albums, {:name=>"Al", :artist_id=>nil}, '(id = 10)'], [:u, :artists, {:name=>"Ar"}, '(id = 20)']]
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
- @mods.should == []
161
+ @db.sqls.should == []
172
162
  a.save
173
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)']]
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
- @mods.should == []
171
+ @db.sqls.should == []
182
172
  al.save
183
- @mods.should == [[:u, :albums, {:artist_id=>nil, :name=>'Al'}, '(id = 10)'], [:d, :artists, '(id = 20)']]
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
- @mods.should == []
182
+ @db.sqls.should == []
192
183
  ar.save
193
- @mods.should == [[:u, :artists, {:name=>"Ar"}, "(id = 20)"], [:d, :albums, "(id = 10)"]]
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
- @mods.should == []
192
+ @db.sqls.should == []
202
193
  ar.save
203
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:d, :albums, '(id = 10)']]
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
- @mods.should == []
202
+ @db.sqls.should == []
212
203
  a.save
213
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
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
- @mods.should == []
212
+ @db.sqls.should == []
222
213
  a.save
223
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
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
- @mods.should == []
222
+ @db.sqls.should == []
232
223
  a.save
233
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
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
- @mods.should == []
261
+ @db.sqls.should == []
271
262
  ar.save
272
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)']]
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
- @mods.should == []
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
- @mods.should == []
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
- @mods.should == []
292
+ @db.sqls.should == []
302
293
  a.save
303
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
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
- @mods.should == []
306
+ @db.sqls.should == []
315
307
  a.save(:validate=>false)
316
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
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
- @mods.should == []
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
- @mods.should == [[:u, :artists, {:name=>"Ar2"}, '(id = 20)']]
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
- @mods.should == []
332
+ @db.sqls.should == []
339
333
  a.save
340
- @mods.should == [[:is, :albums, {:name=>"Al"}, 1], [:is, :tags, {:name=>"T"}, 2], [:i, :at, {:album_id=>1, :tag_id=>2}, 3], [:is, :tags, {:name=>"T"}, 4], [:i, :at, {:album_id=>1, :tag_id=>4}, 5]]
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
- @mods.should == []
344
+ @db.sqls.should == []
347
345
  a.save
348
- @mods.should == [[:is, :albums, {:name=>"Al"}, 1], [:is, :tags, {:name=>"T"}, 2], [:i, :at, {:album_id=>1, :tag_id=>2}, 3], [:is, :tags, {:name=>"T2"}, 4], [:i, :at, {:album_id=>1, :tag_id=>4}, 5]]
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
- @mods.should == []
396
+ @db.sqls.should == []
395
397
  al.save
396
- @mods.should == [[:u, :albums, {:name=>'Al'}, '(id = 10)'], [:u, :tags, {:name=>'T2'}, '(id = 30)'], [:is, :tags, {:name=>"T3"}, 1], [:i, :at, {:album_id=>10, :tag_id=>1}, 2]]
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