sequel 3.21.0 → 3.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -1,85 +1,84 @@
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(:insert_select) do |h|
17
- x = ii.call
18
- mods << [:is, first_source, h, x]
19
- h.merge(:id=>x)
20
- end
21
- define_method(:update) do |h|
22
- mods << [:u, first_source, h, literal(opts[:where])]
23
- 1
24
- end
25
- define_method(:delete) do
26
- mods << [:d, first_source, literal(opts[:where])]
27
- 1
28
- end
4
+ def check_sqls(should, is)
5
+ if should.is_a?(Array)
6
+ should.should include(is)
7
+ else
8
+ should.should == is
29
9
  end
30
- db = Sequel::Database.new({})
31
- db.meta_def(:dataset) do |*a|
32
- x = super(*a)
33
- x.extend(ds_mod)
34
- x
35
- end
36
- @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))
37
21
  @c.plugin :nested_attributes
38
22
  @Artist = Class.new(@c).set_dataset(:artists)
39
23
  @Album = Class.new(@c).set_dataset(:albums)
40
24
  @Tag = Class.new(@c).set_dataset(:tags)
41
- [@Artist, @Album, @Tag].each do |m|
42
- m.dataset.extend(ds_mod)
43
- end
25
+ @Concert = Class.new(@c).set_dataset(:concerts)
26
+ @Artist.plugin :skip_create_refresh
27
+ @Album.plugin :skip_create_refresh
28
+ @Tag.plugin :skip_create_refresh
29
+ @Concert.plugin :skip_create_refresh
44
30
  @Artist.columns :id, :name
45
31
  @Album.columns :id, :name, :artist_id
46
32
  @Tag.columns :id, :name
33
+ @Concert.columns :tour, :date, :artist_id, :playlist
34
+ @Concert.set_primary_key([:tour, :date])
35
+ @Concert.unrestrict_primary_key
47
36
  @Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
37
+ @Artist.one_to_many :concerts, :class=>@Concert, :key=>:artist_id
48
38
  @Artist.one_to_one :first_album, :class=>@Album, :key=>:artist_id
49
39
  @Album.many_to_one :artist, :class=>@Artist
50
40
  @Album.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
51
41
  @Artist.nested_attributes :albums, :first_album, :destroy=>true, :remove=>true
42
+ @Artist.nested_attributes :concerts, :destroy=>true, :remove=>true
52
43
  @Album.nested_attributes :artist, :tags, :destroy=>true, :remove=>true
44
+ @db.sqls
53
45
  end
54
46
 
55
47
  it "should support creating new many_to_one objects" do
56
48
  a = @Album.new({:name=>'Al', :artist_attributes=>{:name=>'Ar'}})
57
- @mods.should == []
49
+ @db.sqls.should == []
58
50
  a.save
59
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
51
+ check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
52
+ ["INSERT INTO albums (name, artist_id) VALUES ('Al', 1)", "INSERT INTO albums (artist_id, name) VALUES (1, 'Al')"])
60
53
  end
61
54
 
62
55
  it "should support creating new one_to_one objects" do
63
56
  a = @Artist.new(:name=>'Ar')
64
57
  a.id = 1
65
58
  a.first_album_attributes = {:name=>'Al'}
66
- @mods.should == []
59
+ @db.sqls.should == []
67
60
  a.save
68
- @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)"]]
61
+ check_sql_array(["INSERT INTO artists (name, id) VALUES ('Ar', 1)", "INSERT INTO artists (id, name) VALUES (1, 'Ar')"],
62
+ "INSERT INTO albums (name) VALUES ('Al')",
63
+ "UPDATE albums SET artist_id = NULL WHERE ((artist_id = 1) AND (id != 2))",
64
+ ["UPDATE albums SET artist_id = 1, name = 'Al' WHERE (id = 2)", "UPDATE albums SET name = 'Al', artist_id = 1 WHERE (id = 2)"])
69
65
  end
70
66
 
71
67
  it "should support creating new one_to_many objects" do
72
68
  a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:name=>'Al'}]})
73
- @mods.should == []
69
+ @db.sqls.should == []
74
70
  a.save
75
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
71
+ check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
72
+ ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
76
73
  end
77
74
 
78
75
  it "should support creating new many_to_many objects" do
79
76
  a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}]})
80
- @mods.should == []
77
+ @db.sqls.should == []
81
78
  a.save
82
- @mods.should == [[:is, :albums, {:name=>"Al"}, 1], [:is, :tags, {:name=>"T"}, 2], [:i, :at, {:album_id=>1, :tag_id=>2}, 3]]
79
+ check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
80
+ "INSERT INTO tags (name) VALUES ('T')",
81
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"])
83
82
  end
84
83
 
85
84
  it "should add new objects to the cached association array as soon as the *_attributes= method is called" do
@@ -88,14 +87,64 @@ describe "NestedAttributes plugin" do
88
87
  a.albums.first.tags.should == [@Tag.new(:name=>'T')]
89
88
  end
90
89
 
90
+ it "should support creating new objects with composite primary keys" do
91
+ insert = nil
92
+ @Concert.class_eval do
93
+ define_method :_insert do
94
+ insert = values
95
+ end
96
+ def before_create # Have to define the CPK somehow.
97
+ self.tour = 'To'
98
+ self.date = '2004-04-05'
99
+ super
100
+ end
101
+ end
102
+ a = @Artist.new({:name=>'Ar', :concerts_attributes=>[{:playlist=>'Pl'}]})
103
+ @db.sqls.should == []
104
+ a.save
105
+ @db.sqls.should == ["INSERT INTO artists (name) VALUES ('Ar')"]
106
+ insert.should == {:tour=>'To', :date=>'2004-04-05', :artist_id=>1, :playlist=>'Pl'}
107
+ end
108
+
109
+ it "should support creating new objects with specific primary keys if :unmatched_pk => :create is set" do
110
+ @Artist.nested_attributes :albums, :unmatched_pk=>:create
111
+ insert = nil
112
+ @Album.class_eval do
113
+ unrestrict_primary_key
114
+ define_method :_insert do
115
+ insert = values
116
+ end
117
+ end
118
+ a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:id=>7, :name=>'Al'}]})
119
+ @db.sqls.should == []
120
+ a.save
121
+ @db.sqls.should == ["INSERT INTO artists (name) VALUES ('Ar')"]
122
+ insert.should == {:artist_id=>1, :name=>'Al', :id=>7}
123
+ end
124
+
125
+ it "should support creating new objects with specific composite primary keys if :unmatched_pk => :create is set" do
126
+ insert = nil
127
+ @Artist.nested_attributes :concerts, :unmatched_pk=>:create
128
+ @Concert.class_eval do
129
+ define_method :_insert do
130
+ insert = values
131
+ end
132
+ end
133
+ a = @Artist.new({:name=>'Ar', :concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl'}]})
134
+ @db.sqls.should == []
135
+ a.save
136
+ @db.sqls.should == ["INSERT INTO artists (name) VALUES ('Ar')"]
137
+ insert.should == {:tour=>'To', :date=>'2004-04-05', :artist_id=>1, :playlist=>'Pl'}
138
+ end
139
+
91
140
  it "should support updating many_to_one objects" do
92
141
  al = @Album.load(:id=>10, :name=>'Al')
93
142
  ar = @Artist.load(:id=>20, :name=>'Ar')
94
143
  al.associations[:artist] = ar
95
144
  al.set(:artist_attributes=>{:id=>'20', :name=>'Ar2'})
96
- @mods.should == []
145
+ @db.sqls.should == []
97
146
  al.save
98
- @mods.should == [[:u, :albums, {:name=>"Al"}, '(id = 10)'], [:u, :artists, {:name=>"Ar2"}, '(id = 20)']]
147
+ @db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE artists SET name = 'Ar2' WHERE (id = 20)"]
99
148
  end
100
149
 
101
150
  it "should support updating one_to_one objects" do
@@ -103,9 +152,9 @@ describe "NestedAttributes plugin" do
103
152
  ar = @Artist.load(:id=>20, :name=>'Ar')
104
153
  ar.associations[:first_album] = al
105
154
  ar.set(:first_album_attributes=>{:id=>10, :name=>'Al2'})
106
- @mods.should == []
155
+ @db.sqls.should == []
107
156
  ar.save
108
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:u, :albums, {:name=>"Al2"}, '(id = 10)']]
157
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
109
158
  end
110
159
 
111
160
  it "should support updating one_to_many objects" do
@@ -113,9 +162,19 @@ describe "NestedAttributes plugin" do
113
162
  ar = @Artist.load(:id=>20, :name=>'Ar')
114
163
  ar.associations[:albums] = [al]
115
164
  ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2'}])
116
- @mods.should == []
165
+ @db.sqls.should == []
166
+ ar.save
167
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
168
+ end
169
+
170
+ it "should support updating one_to_many objects with _delete/_remove flags set to false" do
171
+ al = @Album.load(:id=>10, :name=>'Al')
172
+ ar = @Artist.load(:id=>20, :name=>'Ar')
173
+ ar.associations[:albums] = [al]
174
+ ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2', :_delete => 'f', :_remove => '0'}])
175
+ @db.sqls.should == []
117
176
  ar.save
118
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:u, :albums, {:name=>"Al2"}, '(id = 10)']]
177
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
119
178
  end
120
179
 
121
180
  it "should support updating many_to_many objects" do
@@ -123,19 +182,39 @@ describe "NestedAttributes plugin" do
123
182
  t = @Tag.load(:id=>20, :name=>'T')
124
183
  a.associations[:tags] = [t]
125
184
  a.set(:tags_attributes=>[{:id=>20, :name=>'T2'}])
126
- @mods.should == []
185
+ @db.sqls.should == []
186
+ a.save
187
+ @db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
188
+ end
189
+
190
+ it "should support updating many_to_many objects with _delete/_remove flags set to false" do
191
+ a = @Album.load(:id=>10, :name=>'Al')
192
+ t = @Tag.load(:id=>20, :name=>'T')
193
+ a.associations[:tags] = [t]
194
+ a.set(:tags_attributes=>[{:id=>20, :name=>'T2', '_delete' => false, '_remove' => 'F'}])
195
+ @db.sqls.should == []
127
196
  a.save
128
- @mods.should == [[:u, :albums, {:name=>"Al"}, '(id = 10)'], [:u, :tags, {:name=>"T2"}, '(id = 20)']]
197
+ @db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
129
198
  end
130
199
 
200
+ it "should support updating objects with composite primary keys" do
201
+ ar = @Artist.load(:id=>10, :name=>'Ar')
202
+ co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
203
+ ar.associations[:concerts] = [co]
204
+ ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl2'}])
205
+ @db.sqls.should == []
206
+ ar.save
207
+ check_sql_array("UPDATE artists SET name = 'Ar' WHERE (id = 10)", ["UPDATE concerts SET playlist = 'Pl2' WHERE ((tour = 'To') AND (date = '2004-04-05'))", "UPDATE concerts SET playlist = 'Pl2' WHERE ((date = '2004-04-05') AND (tour = 'To'))"])
208
+ end
209
+
131
210
  it "should support removing many_to_one objects" do
132
211
  al = @Album.load(:id=>10, :name=>'Al')
133
212
  ar = @Artist.load(:id=>20, :name=>'Ar')
134
213
  al.associations[:artist] = ar
135
214
  al.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})
136
- @mods.should == []
215
+ @db.sqls.should == []
137
216
  al.save
138
- @mods.should == [[:u, :albums, {:artist_id=>nil, :name=>'Al'}, '(id = 10)']]
217
+ 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)"])
139
218
  end
140
219
 
141
220
  it "should support removing one_to_one objects" do
@@ -143,10 +222,9 @@ describe "NestedAttributes plugin" do
143
222
  ar = @Artist.load(:id=>20, :name=>'Ar')
144
223
  ar.associations[:first_album] = al
145
224
  ar.set(:first_album_attributes=>{:id=>10, :_remove=>'t'})
146
- @mods.should == []
225
+ @db.sqls.should == []
147
226
  ar.save
148
- @mods.should == [[:u, :albums, {:artist_id=>nil}, "(artist_id = 20)"], [:u, :artists, {:name=>"Ar"}, "(id = 20)"]]
149
-
227
+ @db.sqls.should == ["UPDATE albums SET artist_id = NULL WHERE (artist_id = 20)", "UPDATE artists SET name = 'Ar' WHERE (id = 20)"]
150
228
  end
151
229
 
152
230
  it "should support removing one_to_many objects" do
@@ -154,9 +232,12 @@ describe "NestedAttributes plugin" do
154
232
  ar = @Artist.load(:id=>20, :name=>'Ar')
155
233
  ar.associations[:albums] = [al]
156
234
  ar.set(:albums_attributes=>[{:id=>10, :_remove=>'t'}])
157
- @mods.should == []
235
+ @db.sqls.should == []
236
+ @Album.dataset._fetch = {:id=>1}
158
237
  ar.save
159
- @mods.should == [[:u, :albums, {:name=>"Al", :artist_id=>nil}, '(id = 10)'], [:u, :artists, {:name=>"Ar"}, '(id = 20)']]
238
+ check_sql_array("SELECT 1 AS one FROM albums WHERE ((albums.artist_id = 20) AND (id = 10)) LIMIT 1",
239
+ ["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"],
240
+ "UPDATE artists SET name = 'Ar' WHERE (id = 20)")
160
241
  end
161
242
 
162
243
  it "should support removing many_to_many objects" do
@@ -164,19 +245,33 @@ describe "NestedAttributes plugin" do
164
245
  t = @Tag.load(:id=>20, :name=>'T')
165
246
  a.associations[:tags] = [t]
166
247
  a.set(:tags_attributes=>[{:id=>20, :_remove=>true}])
167
- @mods.should == []
248
+ @db.sqls.should == []
168
249
  a.save
169
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)']]
250
+ @db.sqls.should == ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)"]
170
251
  end
171
252
 
253
+ it "should support removing objects with composite primary keys" do
254
+ ar = @Artist.load(:id=>10, :name=>'Ar')
255
+ co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
256
+ ar.associations[:concerts] = [co]
257
+ ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_remove=>'t'}])
258
+ @db.sqls.should == []
259
+ @Concert.dataset._fetch = {:id=>1}
260
+ ar.save
261
+ check_sql_array(["SELECT 1 AS one FROM concerts WHERE ((concerts.artist_id = 10) AND (tour = 'To') AND (date = '2004-04-05')) LIMIT 1", "SELECT 1 AS one FROM concerts WHERE ((concerts.artist_id = 10) AND (date = '2004-04-05') AND (tour = 'To')) LIMIT 1"],
262
+ ["UPDATE concerts SET artist_id = NULL, playlist = 'Pl' WHERE ((tour = 'To') AND (date = '2004-04-05'))", "UPDATE concerts SET playlist = 'Pl', artist_id = NULL WHERE ((tour = 'To') AND (date = '2004-04-05'))", "UPDATE concerts SET artist_id = NULL, playlist = 'Pl' WHERE ((date = '2004-04-05') AND (tour = 'To'))", "UPDATE concerts SET playlist = 'Pl', artist_id = NULL WHERE ((date = '2004-04-05') AND (tour = 'To'))"],
263
+ "UPDATE artists SET name = 'Ar' WHERE (id = 10)")
264
+ end
265
+
172
266
  it "should support destroying many_to_one objects" do
173
267
  al = @Album.load(:id=>10, :name=>'Al')
174
268
  ar = @Artist.load(:id=>20, :name=>'Ar')
175
269
  al.associations[:artist] = ar
176
270
  al.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})
177
- @mods.should == []
271
+ @db.sqls.should == []
178
272
  al.save
179
- @mods.should == [[:u, :albums, {:artist_id=>nil, :name=>'Al'}, '(id = 10)'], [:d, :artists, '(id = 20)']]
273
+ 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)"],
274
+ "DELETE FROM artists WHERE (id = 20)")
180
275
  end
181
276
 
182
277
  it "should support destroying one_to_one objects" do
@@ -184,9 +279,9 @@ describe "NestedAttributes plugin" do
184
279
  ar = @Artist.load(:id=>20, :name=>'Ar')
185
280
  ar.associations[:first_album] = al
186
281
  ar.set(:first_album_attributes=>{:id=>10, :_delete=>'t'})
187
- @mods.should == []
282
+ @db.sqls.should == []
188
283
  ar.save
189
- @mods.should == [[:u, :artists, {:name=>"Ar"}, "(id = 20)"], [:d, :albums, "(id = 10)"]]
284
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
190
285
  end
191
286
 
192
287
  it "should support destroying one_to_many objects" do
@@ -194,9 +289,9 @@ describe "NestedAttributes plugin" do
194
289
  ar = @Artist.load(:id=>20, :name=>'Ar')
195
290
  ar.associations[:albums] = [al]
196
291
  ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
197
- @mods.should == []
292
+ @db.sqls.should == []
198
293
  ar.save
199
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:d, :albums, '(id = 10)']]
294
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
200
295
  end
201
296
 
202
297
  it "should support destroying many_to_many objects" do
@@ -204,19 +299,29 @@ describe "NestedAttributes plugin" do
204
299
  t = @Tag.load(:id=>20, :name=>'T')
205
300
  a.associations[:tags] = [t]
206
301
  a.set(:tags_attributes=>[{:id=>20, :_delete=>true}])
207
- @mods.should == []
302
+ @db.sqls.should == []
208
303
  a.save
209
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
304
+ @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)"]
210
305
  end
211
306
 
307
+ it "should support destroying objects with composite primary keys" do
308
+ ar = @Artist.load(:id=>10, :name=>'Ar')
309
+ co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
310
+ ar.associations[:concerts] = [co]
311
+ ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_delete=>'t'}])
312
+ @db.sqls.should == []
313
+ ar.save
314
+ check_sql_array("UPDATE artists SET name = 'Ar' WHERE (id = 10)", ["DELETE FROM concerts WHERE ((tour = 'To') AND (date = '2004-04-05'))", "DELETE FROM concerts WHERE ((date = '2004-04-05') AND (tour = 'To'))"])
315
+ end
316
+
212
317
  it "should support both string and symbol keys in nested attribute hashes" do
213
318
  a = @Album.load(:id=>10, :name=>'Al')
214
319
  t = @Tag.load(:id=>20, :name=>'T')
215
320
  a.associations[:tags] = [t]
216
321
  a.set('tags_attributes'=>[{'id'=>20, '_delete'=>true}])
217
- @mods.should == []
322
+ @db.sqls.should == []
218
323
  a.save
219
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
324
+ @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)"]
220
325
  end
221
326
 
222
327
  it "should support using a hash instead of an array for to_many nested attributes" do
@@ -224,9 +329,9 @@ describe "NestedAttributes plugin" do
224
329
  t = @Tag.load(:id=>20, :name=>'T')
225
330
  a.associations[:tags] = [t]
226
331
  a.set('tags_attributes'=>{'1'=>{'id'=>20, '_delete'=>true}})
227
- @mods.should == []
332
+ @db.sqls.should == []
228
333
  a.save
229
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
334
+ @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)"]
230
335
  end
231
336
 
232
337
  it "should only allow destroying associated objects if :destroy option is used in the nested_attributes call" do
@@ -263,11 +368,41 @@ describe "NestedAttributes plugin" do
263
368
  ar = @Artist.load(:id=>20, :name=>'Ar')
264
369
  ar.associations[:albums] = [al]
265
370
  ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])
266
- @mods.should == []
371
+ @db.sqls.should == []
267
372
  ar.save
268
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)']]
373
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)"]
269
374
  end
270
375
 
376
+ it "should not raise an Error if an unmatched primary key is given, if the :unmatched_pk=>:ignore option is used" do
377
+ @Artist.nested_attributes :albums, :unmatched_pk=>:ignore
378
+ al = @Album.load(:id=>10, :name=>'Al')
379
+ ar = @Artist.load(:id=>20, :name=>'Ar')
380
+ ar.associations[:albums] = [al]
381
+ ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])
382
+ @db.sqls.should == []
383
+ ar.save
384
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)"]
385
+ end
386
+
387
+ it "should raise an Error if a composite primary key is given in a nested attribute hash, but no matching associated object exists" do
388
+ ar = @Artist.load(:id=>10, :name=>'Ar')
389
+ co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
390
+ ar.associations[:concerts] = [co]
391
+ proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-04', :_delete=>'t'}])}.should raise_error(Sequel::Error)
392
+ proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_delete=>'t'}])}.should_not raise_error(Sequel::Error)
393
+ end
394
+
395
+ it "should not raise an Error if an unmatched composite primary key is given, if the :strict=>false option is used" do
396
+ @Artist.nested_attributes :concerts, :strict=>false
397
+ ar = @Artist.load(:id=>10, :name=>'Ar')
398
+ co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
399
+ ar.associations[:concerts] = [co]
400
+ ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-06', :_delete=>'t'}])
401
+ @db.sqls.should == []
402
+ ar.save
403
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 10)"]
404
+ end
405
+
271
406
  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
272
407
  @Artist.class_eval do
273
408
  def validate
@@ -276,10 +411,10 @@ describe "NestedAttributes plugin" do
276
411
  end
277
412
  end
278
413
  a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
279
- @mods.should == []
414
+ @db.sqls.should == []
280
415
  proc{a.save}.should raise_error(Sequel::ValidationFailed)
281
416
  a.errors.full_messages.should == ['artist name cannot be Ar']
282
- @mods.should == []
417
+ @db.sqls.should == []
283
418
  # Should preserve attributes
284
419
  a.artist.name.should == 'Ar'
285
420
  end
@@ -294,9 +429,10 @@ describe "NestedAttributes plugin" do
294
429
  end
295
430
  end
296
431
  a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
297
- @mods.should == []
432
+ @db.sqls.should == []
298
433
  a.save
299
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
434
+ check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
435
+ ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
300
436
  end
301
437
 
302
438
  it "should not attempt to validate nested attributes if the :validate=>false option is passed to save" do
@@ -307,23 +443,25 @@ describe "NestedAttributes plugin" do
307
443
  end
308
444
  end
309
445
  a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
310
- @mods.should == []
446
+ @db.sqls.should == []
311
447
  a.save(:validate=>false)
312
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
448
+ check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
449
+ ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
313
450
  end
314
451
 
315
452
  it "should not accept nested attributes unless explicitly specified" do
316
453
  @Artist.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
317
454
  proc{@Artist.create({:name=>'Ar', :tags_attributes=>[{:name=>'T'}]})}.should raise_error(Sequel::Error)
318
- @mods.should == []
455
+ @db.sqls.should == []
319
456
  end
320
457
 
321
458
  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
322
459
  al = @Album.load(:id=>10, :name=>'Al')
323
460
  ar = @Artist.load(:id=>20, :name=>'Ar')
324
461
  al.associations[:artist] = ar
462
+ @db.sqls.should == []
325
463
  al.update(:artist_attributes=>{:id=>'20', :name=>'Ar2'})
326
- @mods.should == [[:u, :artists, {:name=>"Ar2"}, '(id = 20)']]
464
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar2' WHERE (id = 20)"]
327
465
  end
328
466
 
329
467
  it "should have a :limit option limiting the amount of entries" do
@@ -331,19 +469,40 @@ describe "NestedAttributes plugin" do
331
469
  arr = [{:name=>'T'}]
332
470
  proc{@Album.new({:name=>'Al', :tags_attributes=>arr*3})}.should raise_error(Sequel::Error)
333
471
  a = @Album.new({:name=>'Al', :tags_attributes=>arr*2})
334
- @mods.should == []
472
+ @db.sqls.should == []
335
473
  a.save
336
- @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]]
474
+ check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
475
+ "INSERT INTO tags (name) VALUES ('T')",
476
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
477
+ "INSERT INTO tags (name) VALUES ('T')",
478
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
337
479
  end
338
480
 
339
481
  it "should accept a block that each hash gets passed to determine if it should be processed" do
340
482
  @Album.nested_attributes(:tags){|h| h[:name].empty?}
341
483
  a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}, {:name=>''}, {:name=>'T2'}]})
342
- @mods.should == []
484
+ @db.sqls.should == []
343
485
  a.save
344
- @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]]
486
+ check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
487
+ "INSERT INTO tags (name) VALUES ('T')",
488
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
489
+ "INSERT INTO tags (name) VALUES ('T2')",
490
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
345
491
  end
346
492
 
493
+ it "should accept a :transform block that returns a changed attributes hash" do
494
+ @Album.nested_attributes :tags, :transform=>proc{|parent, hash| hash[:name] << parent.name; hash }
495
+ a = @Album.new(:name => 'Al')
496
+ a.set(:tags_attributes=>[{:name=>'T'}, {:name=>'T2'}])
497
+ @db.sqls.should == []
498
+ a.save
499
+ check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
500
+ "INSERT INTO tags (name) VALUES ('TAl')",
501
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
502
+ "INSERT INTO tags (name) VALUES ('T2Al')",
503
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
504
+ end
505
+
347
506
  it "should return objects created/modified in the internal methods" do
348
507
  @Album.nested_attributes :tags, :remove=>true, :strict=>false
349
508
  objs = []
@@ -355,7 +514,7 @@ describe "NestedAttributes plugin" do
355
514
  a = @Album.new(:name=>'Al')
356
515
  a.associations[:tags] = [@Tag.load(:id=>6, :name=>'A'), @Tag.load(:id=>7, :name=>'A2')]
357
516
  a.tags_attributes = [{:id=>6, :name=>'T'}, {:id=>7, :name=>'T2', :_remove=>true}, {:name=>'T3'}, {:id=>8, :name=>'T4'}, {:id=>9, :name=>'T5', :_remove=>true}]
358
- objs.should == [[@Tag.load(:id=>6, :name=>'T'), :update], [@Tag.load(:id=>7, :name=>'A2'), :remove], [@Tag.new(:name=>'T3'), :create], [nil, :update], [nil, :remove]]
517
+ objs.should == [[@Tag.load(:id=>6, :name=>'T'), :update], [@Tag.load(:id=>7, :name=>'A2'), :remove], [@Tag.new(:name=>'T3'), :create]]
359
518
  end
360
519
 
361
520
  it "should raise an error if updating modifies the associated objects keys" do
@@ -387,9 +546,12 @@ describe "NestedAttributes plugin" do
387
546
  t = @Tag.load(:id=>30, :name=>'T', :number=>10)
388
547
  al.associations[:tags] = [t]
389
548
  al.set(:tags_attributes=>[{:id=>30, :name=>'T2'}, {:name=>'T3'}])
390
- @mods.should == []
549
+ @db.sqls.should == []
391
550
  al.save
392
- @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]]
551
+ check_sql_array("UPDATE albums SET name = 'Al' WHERE (id = 10)",
552
+ "UPDATE tags SET name = 'T2' WHERE (id = 30)",
553
+ "INSERT INTO tags (name) VALUES ('T3')",
554
+ ["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
393
555
  proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
394
556
  proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
395
557
  end