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
@@ -2,42 +2,47 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe Sequel::Model, "associate" do
4
4
  it "should use explicit class if given a class, symbol, or string" do
5
- MODEL_DB.reset
6
- klass = Class.new(Sequel::Model(:nodes))
7
- class ::ParParent < Sequel::Model
5
+ begin
6
+ klass = Class.new(Sequel::Model(:nodes))
7
+ class ::ParParent < Sequel::Model; end
8
+
9
+ klass.associate :many_to_one, :par_parent0, :class=>ParParent
10
+ klass.associate :one_to_many, :par_parent1s, :class=>'ParParent'
11
+ klass.associate :many_to_many, :par_parent2s, :class=>:ParParent
12
+
13
+ klass.association_reflection(:"par_parent0").associated_class.should == ParParent
14
+ klass.association_reflection(:"par_parent1s").associated_class.should == ParParent
15
+ klass.association_reflection(:"par_parent2s").associated_class.should == ParParent
16
+ ensure
17
+ Object.send(:remove_const, :ParParent)
8
18
  end
9
-
10
- klass.associate :many_to_one, :par_parent0, :class=>ParParent
11
- klass.associate :one_to_many, :par_parent1s, :class=>'ParParent'
12
- klass.associate :many_to_many, :par_parent2s, :class=>:ParParent
13
-
14
- klass.association_reflection(:"par_parent0").associated_class.should == ParParent
15
- klass.association_reflection(:"par_parent1s").associated_class.should == ParParent
16
- klass.association_reflection(:"par_parent2s").associated_class.should == ParParent
17
19
  end
18
20
 
19
21
  it "should default to associating to other models in the same scope" do
20
- class ::AssociationModuleTest
21
- class Album < Sequel::Model
22
- many_to_one :artist
23
- many_to_many :tags
24
- end
25
- class Artist< Sequel::Model
26
- one_to_many :albums
27
- end
28
- class Tag < Sequel::Model
29
- many_to_many :albums
22
+ begin
23
+ class ::AssociationModuleTest
24
+ class Album < Sequel::Model
25
+ many_to_one :artist
26
+ many_to_many :tags
27
+ end
28
+ class Artist< Sequel::Model
29
+ one_to_many :albums
30
+ end
31
+ class Tag < Sequel::Model
32
+ many_to_many :albums
33
+ end
30
34
  end
35
+
36
+ ::AssociationModuleTest::Album.association_reflection(:artist).associated_class.should == ::AssociationModuleTest::Artist
37
+ ::AssociationModuleTest::Album.association_reflection(:tags).associated_class.should == ::AssociationModuleTest::Tag
38
+ ::AssociationModuleTest::Artist.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
39
+ ::AssociationModuleTest::Tag.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
40
+ ensure
41
+ Object.send(:remove_const, :AssociationModuleTest)
31
42
  end
32
-
33
- ::AssociationModuleTest::Album.association_reflection(:artist).associated_class.should == ::AssociationModuleTest::Artist
34
- ::AssociationModuleTest::Album.association_reflection(:tags).associated_class.should == ::AssociationModuleTest::Tag
35
- ::AssociationModuleTest::Artist.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
36
- ::AssociationModuleTest::Tag.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
37
43
  end
38
44
 
39
45
  it "should add a model_object and association_reflection accessors to the dataset, and return it with the current model object" do
40
- MODEL_DB.reset
41
46
  klass = Class.new(Sequel::Model(:nodes)) do
42
47
  columns :id, :a_id
43
48
  end
@@ -64,7 +69,6 @@ describe Sequel::Model, "associate" do
64
69
  end
65
70
 
66
71
  it "should allow extending the dataset with :extend option" do
67
- MODEL_DB.reset
68
72
  klass = Class.new(Sequel::Model(:nodes)) do
69
73
  columns :id, :a_id
70
74
  end
@@ -91,44 +95,46 @@ describe Sequel::Model, "associate" do
91
95
  end
92
96
 
93
97
  it "should clone an existing association with the :clone option" do
94
- MODEL_DB.reset
95
- klass = Class.new(Sequel::Model(:nodes))
96
-
97
- klass.many_to_one(:par_parent, :order=>:a){1}
98
- klass.one_to_many(:par_parent1s, :class=>'ParParent', :limit=>12){4}
99
- klass.many_to_many(:par_parent2s, :class=>:ParParent, :uniq=>true){2}
98
+ begin
99
+ class ::ParParent < Sequel::Model; end
100
+ klass = Class.new(Sequel::Model(:nodes))
101
+
102
+ klass.many_to_one(:par_parent, :order=>:a){1}
103
+ klass.one_to_many(:par_parent1s, :class=>'ParParent', :limit=>12){4}
104
+ klass.many_to_many(:par_parent2s, :class=>:ParParent, :uniq=>true){2}
100
105
 
101
- klass.many_to_one :par, :clone=>:par_parent, :select=>:b
102
- klass.one_to_many :par1s, :clone=>:par_parent1s, :order=>:b, :limit=>10, :block=>nil
103
- klass.many_to_many(:par2s, :clone=>:par_parent2s, :order=>:c){3}
104
-
105
- klass.association_reflection(:par).associated_class.should == ParParent
106
- klass.association_reflection(:par1s).associated_class.should == ParParent
107
- klass.association_reflection(:par2s).associated_class.should == ParParent
108
-
109
- klass.association_reflection(:par)[:order].should == :a
110
- klass.association_reflection(:par).select.should == :b
111
- klass.association_reflection(:par)[:block].call.should == 1
112
- klass.association_reflection(:par1s)[:limit].should == 10
113
- klass.association_reflection(:par1s)[:order].should == :b
114
- klass.association_reflection(:par1s)[:block].should == nil
115
- klass.association_reflection(:par2s)[:after_load].length.should == 1
116
- klass.association_reflection(:par2s)[:order].should == :c
117
- klass.association_reflection(:par2s)[:block].call.should == 3
106
+ klass.many_to_one :par, :clone=>:par_parent, :select=>:b
107
+ klass.one_to_many :par1s, :clone=>:par_parent1s, :order=>:b, :limit=>10, :block=>nil
108
+ klass.many_to_many(:par2s, :clone=>:par_parent2s, :order=>:c){3}
109
+
110
+ klass.association_reflection(:par).associated_class.should == ParParent
111
+ klass.association_reflection(:par1s).associated_class.should == ParParent
112
+ klass.association_reflection(:par2s).associated_class.should == ParParent
113
+
114
+ klass.association_reflection(:par)[:order].should == :a
115
+ klass.association_reflection(:par).select.should == :b
116
+ klass.association_reflection(:par)[:block].call.should == 1
117
+ klass.association_reflection(:par1s)[:limit].should == 10
118
+ klass.association_reflection(:par1s)[:order].should == :b
119
+ klass.association_reflection(:par1s)[:block].should == nil
120
+ klass.association_reflection(:par2s)[:after_load].length.should == 1
121
+ klass.association_reflection(:par2s)[:order].should == :c
122
+ klass.association_reflection(:par2s)[:block].call.should == 3
123
+ ensure
124
+ Object.send(:remove_const, :ParParent)
125
+ end
118
126
  end
119
127
 
120
128
  end
121
129
 
122
130
  describe Sequel::Model, "many_to_one" do
123
131
  before do
124
- MODEL_DB.reset
125
-
126
132
  @c2 = Class.new(Sequel::Model(:nodes)) do
127
133
  unrestrict_primary_key
128
134
  columns :id, :parent_id, :par_parent_id, :blah
129
135
  end
130
-
131
136
  @dataset = @c2.dataset
137
+ MODEL_DB.reset
132
138
  end
133
139
 
134
140
  it "should use implicit key if omitted" do
@@ -142,32 +148,43 @@ describe Sequel::Model, "many_to_one" do
142
148
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
143
149
  end
144
150
 
151
+ it "should allow association with the same name as the key if :key_alias is given" do
152
+ @c2.def_column_alias(:parent_id_id, :parent_id)
153
+ @c2.many_to_one :parent_id, :key_column=>:parent_id, :class => @c2
154
+ d = @c2.load(:id => 1, :parent_id => 234)
155
+ d.parent_id_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"
156
+ d.parent_id.should == @c2.load(:x => 1, :id => 1)
157
+ d.parent_id_id.should == 234
158
+ d[:parent_id].should == 234
159
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
160
+
161
+ d.parent_id_id = 3
162
+ d.parent_id_id.should == 3
163
+ d[:parent_id].should == 3
164
+ end
165
+
145
166
  it "should use implicit class if omitted" do
146
- class ::ParParent < Sequel::Model
167
+ begin
168
+ class ::ParParent < Sequel::Model; end
169
+ @c2.many_to_one :par_parent
170
+ @c2.new(:id => 1, :par_parent_id => 234).par_parent.class.should == ParParent
171
+ MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.id = 234) LIMIT 1"]
172
+ ensure
173
+ Object.send(:remove_const, :ParParent)
147
174
  end
148
-
149
- @c2.many_to_one :par_parent
150
-
151
- d = @c2.new(:id => 1, :par_parent_id => 234)
152
- p = d.par_parent
153
- p.class.should == ParParent
154
-
155
- MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.id = 234) LIMIT 1"]
156
175
  end
157
176
 
158
177
  it "should use class inside module if given as a string" do
159
- module ::Par
160
- class Parent < Sequel::Model
178
+ begin
179
+ module ::Par
180
+ class Parent < Sequel::Model; end
161
181
  end
182
+ @c2.many_to_one :par_parent, :class=>"Par::Parent"
183
+ @c2.new(:id => 1, :par_parent_id => 234).par_parent.class.should == Par::Parent
184
+ MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.id = 234) LIMIT 1"]
185
+ ensure
186
+ Object.send(:remove_const, :Par)
162
187
  end
163
-
164
- @c2.many_to_one :par_parent, :class=>"Par::Parent"
165
-
166
- d = @c2.new(:id => 1, :par_parent_id => 234)
167
- p = d.par_parent
168
- p.class.should == Par::Parent
169
-
170
- MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.id = 234) LIMIT 1"]
171
188
  end
172
189
 
173
190
  it "should use explicit key if given" do
@@ -181,6 +198,12 @@ describe Sequel::Model, "many_to_one" do
181
198
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 567) LIMIT 1"]
182
199
  end
183
200
 
201
+ it "should respect :qualify => false option" do
202
+ @c2.many_to_one :parent, :class => @c2, :key => :blah, :qualify=>false
203
+ @c2.new(:id => 1, :blah => 567).parent
204
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 567) LIMIT 1"]
205
+ end
206
+
184
207
  it "should use :primary_key option if given" do
185
208
  @c2.many_to_one :parent, :class => @c2, :key => :blah, :primary_key => :pk
186
209
  @c2.new(:id => 1, :blah => 567).parent
@@ -218,14 +241,12 @@ describe Sequel::Model, "many_to_one" do
218
241
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 567) AND (a = 32)) LIMIT 1"]
219
242
 
220
243
  @c2.many_to_one :parent, :class => @c2, :key => :blah, :conditions=>:a
221
- MODEL_DB.sqls.clear
222
244
  @c2.new(:id => 1, :blah => 567).parent
223
245
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 567) AND a) LIMIT 1"]
224
246
  end
225
247
 
226
248
  it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
227
- c2 = @c2
228
- @c2.many_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{c2.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
249
+ @c2.many_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{model.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
229
250
  ds.filter(:x.sql_number > 1)
230
251
  end
231
252
  @c2.load(:id => 100).child_20
@@ -234,24 +255,19 @@ describe Sequel::Model, "many_to_one" do
234
255
 
235
256
  it "should return nil if key value is nil" do
236
257
  @c2.many_to_one :parent, :class => @c2
237
-
238
- d = @c2.new(:id => 1)
239
- d.parent.should == nil
258
+ @c2.new(:id => 1).parent.should == nil
259
+ MODEL_DB.sqls.should == []
240
260
  end
241
261
 
242
262
  it "should cache negative lookup" do
243
263
  @c2.many_to_one :parent, :class => @c2
244
- ds = @c2.dataset
245
- def ds.fetch_rows(sql, &block)
246
- MODEL_DB.sqls << sql
247
- end
248
-
264
+ @c2.dataset._fetch = []
249
265
  d = @c2.new(:id => 1, :parent_id=>555)
250
266
  MODEL_DB.sqls.should == []
251
267
  d.parent.should == nil
252
268
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.id = 555) LIMIT 1']
253
269
  d.parent.should == nil
254
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.id = 555) LIMIT 1']
270
+ MODEL_DB.sqls.should == []
255
271
  end
256
272
 
257
273
  it "should define a setter method" do
@@ -314,11 +330,9 @@ describe Sequel::Model, "many_to_one" do
314
330
  @c2.many_to_one :parent, :class => @c2
315
331
 
316
332
  d = @c2.load(:id => 1)
317
- MODEL_DB.reset
318
333
  d.parent_id = 234
319
334
  d.associations[:parent].should == nil
320
- ds = @c2.dataset
321
- def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
335
+ @c2.dataset._fetch = {:id=>234}
322
336
  e = d.parent
323
337
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
324
338
  d.associations[:parent].should == e
@@ -357,13 +371,32 @@ describe Sequel::Model, "many_to_one" do
357
371
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
358
372
  end
359
373
 
374
+ it "should use a callback if given one as the argument" do
375
+ @c2.many_to_one :parent, :class => @c2
376
+
377
+ d = @c2.create(:id => 1)
378
+ MODEL_DB.reset
379
+ d.parent_id = 234
380
+ d.associations[:parent] = 42
381
+ d.parent(proc{|ds| ds.filter{name > 'M'}}).should_not == 42
382
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 234) AND (name > 'M')) LIMIT 1"]
383
+ end
384
+
385
+ it "should use a block given to the association method as a callback" do
386
+ @c2.many_to_one :parent, :class => @c2
387
+
388
+ d = @c2.create(:id => 1)
389
+ MODEL_DB.reset
390
+ d.parent_id = 234
391
+ d.associations[:parent] = 42
392
+ d.parent{|ds| ds.filter{name > 'M'}}.should_not == 42
393
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 234) AND (name > 'M')) LIMIT 1"]
394
+ end
395
+
360
396
  it "should have the setter add to the reciprocal one_to_many cached association list if it exists" do
361
397
  @c2.many_to_one :parent, :class => @c2
362
398
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
363
- ds = @c2.dataset
364
- def ds.fetch_rows(sql, &block)
365
- MODEL_DB.sqls << sql
366
- end
399
+ @c2.dataset._fetch = []
367
400
 
368
401
  d = @c2.new(:id => 1)
369
402
  e = @c2.new(:id => 2)
@@ -372,14 +405,13 @@ describe Sequel::Model, "many_to_one" do
372
405
  e.children.should_not(include(d))
373
406
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
374
407
 
375
- MODEL_DB.reset
376
408
  d = @c2.new(:id => 1)
377
409
  e = @c2.new(:id => 2)
378
410
  e.children.should_not(include(d))
379
411
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
380
412
  d.parent = e
381
413
  e.children.should(include(d))
382
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
414
+ MODEL_DB.sqls.should == []
383
415
  end
384
416
 
385
417
  it "should have many_to_one setter deal with a one_to_one reciprocal" do
@@ -407,10 +439,7 @@ describe Sequel::Model, "many_to_one" do
407
439
  it "should have the setter remove the object from the previous associated object's reciprocal one_to_many cached association list if it exists" do
408
440
  @c2.many_to_one :parent, :class => @c2
409
441
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
410
- ds = @c2.dataset
411
- def ds.fetch_rows(sql, &block)
412
- MODEL_DB.sqls << sql
413
- end
442
+ @c2.dataset._fetch = []
414
443
 
415
444
  d = @c2.new(:id => 1)
416
445
  e = @c2.new(:id => 2)
@@ -428,18 +457,24 @@ describe Sequel::Model, "many_to_one" do
428
457
  MODEL_DB.sqls.should == []
429
458
  end
430
459
 
460
+ it "should have the setter not modify the reciprocal if set to same value as current" do
461
+ @c2.many_to_one :parent, :class => @c2
462
+ @c2.one_to_many :children, :class => @c2, :key=>:parent_id
463
+
464
+ c1 = @c2.load(:id => 1, :parent_id=>nil)
465
+ c2 = @c2.load(:id => 2, :parent_id=>1)
466
+ c3 = @c2.load(:id => 3, :parent_id=>1)
467
+ c1.associations[:children] = [c2, c3]
468
+ c2.associations[:parent] = c1
469
+ c2.parent = c1
470
+ c1.children.should == [c2, c3]
471
+ MODEL_DB.sqls.should == []
472
+ end
473
+
431
474
  it "should get all matching records and only return the first if :key option is set to nil" do
432
- c2 = @c2
433
475
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
434
- @c2.many_to_one :first_grand_parent, :class => @c2, :key=>nil, :eager_graph=>:children, :dataset=>proc{c2.filter(:children_id=>parent_id)}
435
- ds = @c2.dataset
436
- def ds.columns
437
- [:id, :parent_id, :par_parent_id, :blah]
438
- end
439
- def ds.fetch_rows(sql, &block)
440
- MODEL_DB.sqls << sql
441
- yield({:id=>1, :parent_id=>0, :par_parent_id=>3, :blah=>4, :children_id=>2, :children_parent_id=>1, :children_par_parent_id=>5, :children_blah=>6})
442
- end
476
+ @c2.many_to_one :first_grand_parent, :class => @c2, :key=>nil, :eager_graph=>:children, :dataset=>proc{model.filter(:children_id=>parent_id)}
477
+ @c2.dataset.columns(:id, :parent_id, :par_parent_id, :blah)._fetch = [{:id=>1, :parent_id=>0, :par_parent_id=>3, :blah=>4, :children_id=>2, :children_parent_id=>1, :children_par_parent_id=>5, :children_blah=>6}, {}]
443
478
  p = @c2.new(:parent_id=>2)
444
479
  fgp = p.first_grand_parent
445
480
  MODEL_DB.sqls.should == ["SELECT nodes.id, nodes.parent_id, nodes.par_parent_id, nodes.blah, children.id AS children_id, children.parent_id AS children_parent_id, children.par_parent_id AS children_par_parent_id, children.blah AS children_blah FROM nodes LEFT OUTER JOIN nodes AS children ON (children.parent_id = nodes.id) WHERE (children_id = 2)"]
@@ -500,15 +535,15 @@ describe Sequel::Model, "many_to_one" do
500
535
  h = []
501
536
  @c2.many_to_one :parent, :class => @c2, :before_set=>[proc{|x,y| h << x.pk; h << (y ? -y.pk : :y)}, :blah], :after_set=>proc{h << 3}
502
537
  @c2.class_eval do
503
- class_variable_set(:@@blah, h)
538
+ self::Foo = h
504
539
  def []=(a, v)
505
- a == :parent_id ? (self.class.send(:class_variable_get, :@@blah) << (v ? 4 : 5)) : super
540
+ a == :parent_id ? (model::Foo << (v ? 4 : 5)) : super
506
541
  end
507
542
  def blah(x)
508
- self.class.send(:class_variable_get, :@@blah) << (x ? x.pk : :x)
543
+ model::Foo << (x ? x.pk : :x)
509
544
  end
510
545
  def blahr(x)
511
- self.class.send(:class_variable_get, :@@blah) << 6
546
+ model::Foo << 6
512
547
  end
513
548
  end
514
549
  p = @c2.load(:id=>10)
@@ -524,13 +559,11 @@ describe Sequel::Model, "many_to_one" do
524
559
  h = []
525
560
  @c2.many_to_one :parent, :class => @c2, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
526
561
  @c2.class_eval do
527
- class_variable_set(:@@blah, h)
562
+ self::Foo = h
528
563
  def al(v)
529
- self.class.send(:class_variable_get, :@@blah) << v.pk
530
- end
531
- def @dataset.fetch_rows(sql)
532
- yield({:id=>20})
564
+ model::Foo << v.pk
533
565
  end
566
+ dataset._fetch = {:id=>20}
534
567
  end
535
568
  p = @c2.load(:id=>10, :parent_id=>20)
536
569
  parent = p.parent
@@ -538,6 +571,18 @@ describe Sequel::Model, "many_to_one" do
538
571
  parent.pk.should == 20
539
572
  end
540
573
 
574
+ it "should support after_load association callback that changes the cached object" do
575
+ @c2.many_to_one :parent, :class => @c2, :after_load=>:al
576
+ @c2.class_eval do
577
+ def al(v)
578
+ associations[:parent] = :foo
579
+ end
580
+ end
581
+ p = @c2.load(:id=>10, :parent_id=>20)
582
+ p.parent.should == :foo
583
+ p.associations[:parent].should == :foo
584
+ end
585
+
541
586
  it "should raise error and not call internal add or remove method if before callback returns false, even if raise_on_save_failure is false" do
542
587
  # The reason for this is that assignment in ruby always returns the argument instead of the result
543
588
  # of the method, so we can't return nil to signal that the association callback prevented the modification
@@ -565,35 +610,32 @@ describe Sequel::Model, "many_to_one" do
565
610
  d = @c2.load(:id=>321)
566
611
  p = @c2.new
567
612
  p.associations[:parent] = d
568
- h = []
569
613
  @c2.many_to_one :parent, :class => @c2, :before_set=>:bs, :after_set=>:as
570
614
  @c2.class_eval do
571
- class_variable_set(:@@blah, h)
615
+ self::Foo = []
572
616
  def []=(a, v)
573
- a == :parent_id ? (self.class.send(:class_variable_get, :@@blah) << 5) : super
617
+ a == :parent_id ? (model::Foo << 5) : super
574
618
  end
575
619
  def bs(x)
576
- self.class.send(:class_variable_get, :@@blah) << x.pk
620
+ model::Foo << x.pk
577
621
  end
578
622
  def as(x)
579
- self.class.send(:class_variable_get, :@@blah) << x.pk * 2
623
+ model::Foo << x.pk * 2
580
624
  end
581
625
  end
582
626
  p.parent = c
583
- h.should == [123, 5, 246]
627
+ @c2::Foo.should == [123, 5, 246]
584
628
  end
585
629
  end
586
630
 
587
631
  describe Sequel::Model, "one_to_one" do
588
632
  before do
589
633
  @c1 = Class.new(Sequel::Model(:attributes)) do
590
- def _refresh(ds); end
591
634
  unrestrict_primary_key
592
635
  columns :id, :node_id, :y
593
636
  end
594
637
 
595
638
  @c2 = Class.new(Sequel::Model(:nodes)) do
596
- def _refresh(ds); end
597
639
  unrestrict_primary_key
598
640
  attr_accessor :xxx
599
641
 
@@ -602,24 +644,8 @@ describe Sequel::Model, "one_to_one" do
602
644
  columns :id, :x, :parent_id, :par_parent_id, :blah, :node_id
603
645
  end
604
646
  @dataset = @c2.dataset
605
-
606
- @c2.dataset.extend(Module.new {
607
- def empty?; false; end
608
- def fetch_rows(sql)
609
- @db << sql
610
- yield Hash.new
611
- end
612
- })
613
-
614
- @c1.dataset.extend(Module.new {
615
- def empty?; opts.has_key?(:empty) ? (super; true) : false; end
616
- def fetch_rows(sql)
617
- @db << sql
618
- yield Hash.new
619
- end
620
- })
621
-
622
- @dataset = @c2.dataset
647
+ @dataset._fetch = {}
648
+ @c1.dataset._fetch = {}
623
649
  MODEL_DB.reset
624
650
  end
625
651
 
@@ -641,26 +667,24 @@ describe Sequel::Model, "one_to_one" do
641
667
  it "should add a setter method" do
642
668
  @c2.one_to_one :attribute, :class => @c1
643
669
  attrib = @c1.new(:id=>3)
644
- d = @c1.dataset
645
- @c1.class_eval{remove_method :_refresh}
646
- def d.fetch_rows(s); yield({:id=>3}) end
670
+ @c1.dataset._fetch = @c1.instance_dataset._fetch = {:id=>3}
647
671
  @c2.new(:id => 1234).attribute = attrib
672
+ sqls = MODEL_DB.sqls
648
673
  ['INSERT INTO attributes (node_id, id) VALUES (1234, 3)',
649
- 'INSERT INTO attributes (id, node_id) VALUES (3, 1234)'].should(include(MODEL_DB.sqls.last))
650
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))'
651
- MODEL_DB.sqls.length.should == 2
674
+ 'INSERT INTO attributes (id, node_id) VALUES (3, 1234)'].should(include(sqls.slice! 1))
675
+ sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))', "SELECT * FROM attributes WHERE (id = 3) LIMIT 1"]
676
+
652
677
  @c2.new(:id => 1234).attribute.should == attrib
653
- MODEL_DB.sqls.clear
654
678
  attrib = @c1.load(:id=>3)
655
679
  @c2.new(:id => 1234).attribute = attrib
656
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))',
680
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 1",
681
+ 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))',
657
682
  "UPDATE attributes SET node_id = 1234 WHERE (id = 3)"]
658
683
  end
659
684
 
660
685
  it "should use a transaction in the setter method" do
661
686
  @c2.one_to_one :attribute, :class => @c1
662
687
  @c2.use_transactions = true
663
- MODEL_DB.sqls.clear
664
688
  attrib = @c1.load(:id=>3)
665
689
  @c2.new(:id => 1234).attribute = attrib
666
690
  MODEL_DB.sqls.should == ['BEGIN',
@@ -673,7 +697,6 @@ describe Sequel::Model, "one_to_one" do
673
697
  @c2.one_to_one :attribute, :class => @c1, :conditions=>{:a=>1} do |ds|
674
698
  ds.filter(:b=>2)
675
699
  end
676
- MODEL_DB.sqls.clear
677
700
  attrib = @c1.load(:id=>3)
678
701
  @c2.new(:id => 1234).attribute = attrib
679
702
  MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (a = 1) AND (b = 2) AND (id != 3))',
@@ -683,30 +706,30 @@ describe Sequel::Model, "one_to_one" do
683
706
  it "should have the setter method respect the :primary_key option" do
684
707
  @c2.one_to_one :attribute, :class => @c1, :primary_key=>:xxx
685
708
  attrib = @c1.new(:id=>3)
686
- d = @c1.dataset
687
- @c1.class_eval{remove_method :_refresh}
688
- def d.fetch_rows(s); yield({:id=>3}) end
709
+ @c1.dataset._fetch = @c1.instance_dataset._fetch = {:id=>3}
689
710
  @c2.new(:id => 1234, :xxx=>5).attribute = attrib
711
+ sqls = MODEL_DB.sqls
690
712
  ['INSERT INTO attributes (node_id, id) VALUES (5, 3)',
691
- 'INSERT INTO attributes (id, node_id) VALUES (3, 5)'].should(include(MODEL_DB.sqls.last))
692
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))'
693
- MODEL_DB.sqls.length.should == 2
713
+ 'INSERT INTO attributes (id, node_id) VALUES (3, 5)'].should(include(sqls.slice! 1))
714
+ sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))', "SELECT * FROM attributes WHERE (id = 3) LIMIT 1"]
715
+
694
716
  @c2.new(:id => 321, :xxx=>5).attribute.should == attrib
695
- MODEL_DB.sqls.clear
696
717
  attrib = @c1.load(:id=>3)
697
718
  @c2.new(:id => 621, :xxx=>5).attribute = attrib
698
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))',
719
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE (attributes.node_id = 5) LIMIT 1",
720
+ 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))',
699
721
  'UPDATE attributes SET node_id = 5 WHERE (id = 3)']
700
722
  end
701
723
 
702
724
  it "should have the setter method respect composite keys" do
703
725
  @c2.one_to_one :attribute, :class => @c1, :key=>[:node_id, :y], :primary_key=>[:id, :x]
704
726
  attrib = @c1.load(:id=>3, :y=>6)
705
- d = @c1.dataset
706
- def d.fetch_rows(s); yield({:id=>3, :y=>6}) end
727
+ @c1.dataset._fetch = {:id=>3, :y=>6}
707
728
  @c2.load(:id => 1234, :x=>5).attribute = attrib
708
- MODEL_DB.sqls.last.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 3\)/
709
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\) AND \(id != 3\)\)/
729
+ sqls = MODEL_DB.sqls
730
+ sqls.last.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 3\)/
731
+ sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\) AND \(id != 3\)\)/
732
+ sqls.length.should == 2
710
733
  end
711
734
 
712
735
  it "should use implicit key if omitted" do
@@ -721,31 +744,27 @@ describe Sequel::Model, "one_to_one" do
721
744
  end
722
745
 
723
746
  it "should use implicit class if omitted" do
724
- class ::ParParent < Sequel::Model
747
+ begin
748
+ class ::ParParent < Sequel::Model; end
749
+ @c2.one_to_one :par_parent
750
+ @c2.new(:id => 234).par_parent.class.should == ParParent
751
+ MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.node_id = 234) LIMIT 1"]
752
+ ensure
753
+ Object.send(:remove_const, :ParParent)
725
754
  end
726
-
727
- @c2.one_to_one :par_parent
728
-
729
- d = @c2.new(:id => 234)
730
- p = d.par_parent
731
- p.class.should == ParParent
732
-
733
- MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.node_id = 234) LIMIT 1"]
734
755
  end
735
756
 
736
757
  it "should use class inside module if given as a string" do
737
- module ::Par
738
- class Parent < Sequel::Model
758
+ begin
759
+ module ::Par
760
+ class Parent < Sequel::Model; end
739
761
  end
762
+ @c2.one_to_one :par_parent, :class=>"Par::Parent"
763
+ @c2.new(:id => 234).par_parent.class.should == Par::Parent
764
+ MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.node_id = 234) LIMIT 1"]
765
+ ensure
766
+ Object.send(:remove_const, :Par)
740
767
  end
741
-
742
- @c2.one_to_one :par_parent, :class=>"Par::Parent"
743
-
744
- d = @c2.new(:id => 234)
745
- p = d.par_parent
746
- p.class.should == Par::Parent
747
-
748
- MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.node_id = 234) LIMIT 1"]
749
768
  end
750
769
 
751
770
  it "should use explicit key if given" do
@@ -796,14 +815,12 @@ describe Sequel::Model, "one_to_one" do
796
815
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.node_id = 567) AND (a = 32)) LIMIT 1"]
797
816
 
798
817
  @c2.one_to_one :parent, :class => @c2, :conditions=>:a
799
- MODEL_DB.sqls.clear
800
818
  @c2.new(:id => 567).parent
801
819
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.node_id = 567) AND a) LIMIT 1"]
802
820
  end
803
821
 
804
822
  it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
805
- c2 = @c2
806
- @c2.one_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{c2.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
823
+ @c2.one_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{model.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
807
824
  ds.filter(:x.sql_number > 1)
808
825
  end
809
826
  @c2.load(:id => 100).child_20
@@ -813,24 +830,19 @@ describe Sequel::Model, "one_to_one" do
813
830
  it "should return nil if primary_key value is nil" do
814
831
  @c2.one_to_one :parent, :class => @c2, :primary_key=>:node_id
815
832
 
816
- d = @c2.new(:id => 1)
817
- d.parent.should == nil
833
+ @c2.new(:id => 1).parent.should be_nil
818
834
  MODEL_DB.sqls.should == []
819
835
  end
820
836
 
821
837
  it "should cache negative lookup" do
822
838
  @c2.one_to_one :parent, :class => @c2
823
- ds = @c2.dataset
824
- def ds.fetch_rows(sql, &block)
825
- MODEL_DB.sqls << sql
826
- end
827
-
839
+ @c2.dataset._fetch = []
828
840
  d = @c2.new(:id => 555)
829
841
  MODEL_DB.sqls.should == []
830
842
  d.parent.should == nil
831
843
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.node_id = 555) LIMIT 1']
832
844
  d.parent.should == nil
833
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.node_id = 555) LIMIT 1']
845
+ MODEL_DB.sqls.should == []
834
846
  end
835
847
 
836
848
  it "should define a setter method" do
@@ -838,28 +850,44 @@ describe Sequel::Model, "one_to_one" do
838
850
 
839
851
  d = @c2.new(:id => 1)
840
852
  f = @c2.new(:id => 3, :node_id=> 4321)
853
+ @c2.dataset._fetch = @c2.instance_dataset._fetch = {:id => 3, :node_id=>1}
841
854
  d.parent = f
842
855
  f.values.should == {:id => 3, :node_id=>1}
843
856
  d.parent.should == f
857
+ sqls = MODEL_DB.sqls
858
+ ["INSERT INTO nodes (node_id, id) VALUES (1, 3)",
859
+ "INSERT INTO nodes (id, node_id) VALUES (3, 1)"].should include(sqls.slice! 1)
860
+ sqls.should == ["UPDATE nodes SET node_id = NULL WHERE ((node_id = 1) AND (id != 3))", "SELECT * FROM nodes WHERE (id = 3) LIMIT 1"]
844
861
 
845
862
  d.parent = nil
846
863
  d.parent.should == nil
864
+ MODEL_DB.sqls.should == ["UPDATE nodes SET node_id = NULL WHERE (node_id = 1)"]
847
865
  end
848
866
 
849
867
  it "should have the setter method respect the :primary_key option" do
850
868
  @c2.one_to_one :parent, :class => @c2, :primary_key=>:blah
851
869
  d = @c2.new(:id => 1, :blah => 3)
852
870
  e = @c2.new(:id => 4321, :node_id=>444)
871
+ @c2.dataset._fetch = @c2.instance_dataset._fetch = {:id => 4321, :node_id => 3}
853
872
  d.parent = e
854
873
  e.values.should == {:id => 4321, :node_id => 3}
874
+ sqls = MODEL_DB.sqls
875
+ ["INSERT INTO nodes (node_id, id) VALUES (3, 4321)",
876
+ "INSERT INTO nodes (id, node_id) VALUES (4321, 3)"].should include(sqls.slice! 1)
877
+ sqls.should == ["UPDATE nodes SET node_id = NULL WHERE ((node_id = 3) AND (id != 4321))", "SELECT * FROM nodes WHERE (id = 4321) LIMIT 1"]
855
878
  end
856
879
 
857
880
  it "should have the setter method respect the :key option" do
858
881
  @c2.one_to_one :parent, :class => @c2, :key=>:blah
859
882
  d = @c2.new(:id => 3)
860
883
  e = @c2.new(:id => 4321, :blah=>444)
884
+ @c2.dataset._fetch = @c2.instance_dataset._fetch = {:id => 4321, :blah => 3}
861
885
  d.parent = e
862
886
  e.values.should == {:id => 4321, :blah => 3}
887
+ sqls = MODEL_DB.sqls
888
+ ["INSERT INTO nodes (blah, id) VALUES (3, 4321)",
889
+ "INSERT INTO nodes (id, blah) VALUES (4321, 3)"].should include(sqls.slice! 1)
890
+ sqls.should == ["UPDATE nodes SET blah = NULL WHERE ((blah = 3) AND (id != 4321))", "SELECT * FROM nodes WHERE (id = 4321) LIMIT 1"]
863
891
  end
864
892
 
865
893
  it "should persist changes to associated object when the setter is called" do
@@ -875,12 +903,11 @@ describe Sequel::Model, "one_to_one" do
875
903
 
876
904
  d = @c2.load(:id => 1)
877
905
  d.associations[:parent].should == nil
878
- ds = @c2.dataset
879
- def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
906
+ @c2.dataset._fetch = {:id=>234}
880
907
  e = d.parent
881
908
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.node_id = 1) LIMIT 1"]
882
909
  d.parent
883
- MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.node_id = 1) LIMIT 1"]
910
+ MODEL_DB.sqls.should == []
884
911
  d.associations[:parent].should == e
885
912
  end
886
913
 
@@ -922,7 +949,6 @@ describe Sequel::Model, "one_to_one" do
922
949
  e.child.should == d
923
950
  MODEL_DB.sqls.should == ["UPDATE nodes SET parent_id = NULL WHERE ((parent_id = 1) AND (id != 2))",
924
951
  "UPDATE nodes SET parent_id = 1 WHERE (id = 2)"]
925
- MODEL_DB.reset
926
952
  d.parent = nil
927
953
  e.child.should == nil
928
954
  MODEL_DB.sqls.should == ["UPDATE nodes SET parent_id = NULL WHERE (parent_id = 1)"]
@@ -931,17 +957,13 @@ describe Sequel::Model, "one_to_one" do
931
957
  it "should have the setter remove the object from the previous associated object's reciprocal many_to_one cached association list if it exists" do
932
958
  @c2.one_to_one :parent, :class => @c2, :key=>:parent_id
933
959
  @c2.many_to_one :child, :class => @c2, :key=>:parent_id
934
- ds = @c2.dataset
935
- def ds.fetch_rows(sql, &block)
936
- MODEL_DB.sqls << sql
937
- end
960
+ @c2.dataset._fetch = []
938
961
 
939
962
  d = @c2.load(:id => 1)
940
963
  e = @c2.load(:id => 2)
941
964
  f = @c2.load(:id => 3)
942
965
  e.child.should == nil
943
966
  f.child.should == nil
944
- MODEL_DB.reset
945
967
  d.parent = e
946
968
  e.child.should == d
947
969
  d.parent = f
@@ -951,6 +973,19 @@ describe Sequel::Model, "one_to_one" do
951
973
  f.child.should == nil
952
974
  end
953
975
 
976
+ it "should have the setter not modify the reciprocal if set to same value as current" do
977
+ @c2.one_to_one :parent, :class => @c2, :key=>:parent_id
978
+ @c2.many_to_one :child, :class => @c2, :key=>:parent_id
979
+
980
+ c1 = @c2.load(:id => 1, :parent_id=>nil)
981
+ c2 = @c2.load(:id => 2, :parent_id=>1)
982
+ c1.associations[:child] = c2
983
+ c2.associations[:parent] = c1
984
+ c2.parent = c1
985
+ c1.child.should == c2
986
+ MODEL_DB.sqls.should == []
987
+ end
988
+
954
989
  it "should not add associations methods directly to class" do
955
990
  @c2.one_to_one :parent, :class => @c2
956
991
  @c2.instance_methods.collect{|x| x.to_s}.should(include('parent'))
@@ -983,12 +1018,12 @@ describe Sequel::Model, "one_to_one" do
983
1018
  h = []
984
1019
  @c2.one_to_one :parent, :class => @c2, :before_set=>[proc{|x,y| h << x.pk; h << (y ? -y.pk : :y)}, :blah], :after_set=>proc{h << 3}
985
1020
  @c2.class_eval do
986
- class_variable_set(:@@blah, h)
1021
+ self::Foo = h
987
1022
  def blah(x)
988
- self.class.send(:class_variable_get, :@@blah) << (x ? x.pk : :x)
1023
+ model::Foo << (x ? x.pk : :x)
989
1024
  end
990
1025
  def blahr(x)
991
- self.class.send(:class_variable_get, :@@blah) << 6
1026
+ model::Foo << 6
992
1027
  end
993
1028
  end
994
1029
  p = @c2.load(:id=>10)
@@ -1004,13 +1039,11 @@ describe Sequel::Model, "one_to_one" do
1004
1039
  h = []
1005
1040
  @c2.one_to_one :parent, :class => @c2, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
1006
1041
  @c2.class_eval do
1007
- class_variable_set(:@@blah, h)
1042
+ self::Foo = h
1008
1043
  def al(v)
1009
- self.class.send(:class_variable_get, :@@blah) << v.pk
1010
- end
1011
- def @dataset.fetch_rows(sql)
1012
- yield({:id=>20})
1044
+ model::Foo << v.pk
1013
1045
  end
1046
+ @dataset._fetch = {:id=>20}
1014
1047
  end
1015
1048
  p = @c2.load(:id=>10)
1016
1049
  parent = p.parent
@@ -1048,15 +1081,15 @@ describe Sequel::Model, "one_to_one" do
1048
1081
  h = []
1049
1082
  @c2.one_to_one :parent, :class => @c2, :before_set=>:bs, :after_set=>:as
1050
1083
  @c2.class_eval do
1051
- class_variable_set(:@@blah, h)
1084
+ self::Foo = h
1052
1085
  def []=(a, v)
1053
- a == :node_id ? (self.class.send(:class_variable_get, :@@blah) << 5) : super
1086
+ a == :node_id ? (model::Foo << 5) : super
1054
1087
  end
1055
1088
  def bs(x)
1056
- self.class.send(:class_variable_get, :@@blah) << x.pk
1089
+ model::Foo << x.pk
1057
1090
  end
1058
1091
  def as(x)
1059
- self.class.send(:class_variable_get, :@@blah) << x.pk * 2
1092
+ model::Foo << x.pk * 2
1060
1093
  end
1061
1094
  end
1062
1095
  p.parent = c
@@ -1072,16 +1105,9 @@ end
1072
1105
 
1073
1106
  describe Sequel::Model, "one_to_many" do
1074
1107
  before do
1075
- MODEL_DB.reset
1076
-
1077
1108
  @c1 = Class.new(Sequel::Model(:attributes)) do
1078
- def _refresh(ds); end
1079
1109
  unrestrict_primary_key
1080
1110
  columns :id, :node_id, :y, :z
1081
-
1082
- def self.[](id)
1083
- load(id.is_a?(Array) ? {:id => id[0], :z => id[1]} : {:id => id})
1084
- end
1085
1111
  end
1086
1112
 
1087
1113
  @c2 = Class.new(Sequel::Model(:nodes)) do
@@ -1094,68 +1120,58 @@ describe Sequel::Model, "one_to_many" do
1094
1120
  columns :id, :x
1095
1121
  end
1096
1122
  @dataset = @c2.dataset
1097
-
1098
- @c2.dataset.extend(Module.new {
1099
- def empty?; false; end
1100
- def fetch_rows(sql)
1101
- @db << sql
1102
- yield Hash.new
1103
- end
1104
- })
1105
-
1106
- @c1.dataset.extend(Module.new {
1107
- def empty?; opts.has_key?(:empty) ? (super; true) : false; end
1108
- def fetch_rows(sql)
1109
- @db << sql
1110
- yield Hash.new
1111
- end
1112
- })
1123
+ @dataset._fetch = {}
1124
+ @c1.dataset._fetch = proc{|sql| sql =~ /SELECT 1/ ? {:a=>1} : {}}
1125
+ MODEL_DB.reset
1113
1126
  end
1114
1127
 
1115
1128
  it "should use implicit key if omitted" do
1116
1129
  @c2.one_to_many :attributes, :class => @c1
1117
-
1118
- n = @c2.new(:id => 1234)
1119
- a = n.attributes_dataset
1120
- a.should be_a_kind_of(Sequel::Dataset)
1121
- a.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1130
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1122
1131
  end
1123
1132
 
1124
1133
  it "should use implicit class if omitted" do
1125
- class ::HistoricalValue < Sequel::Model
1134
+ begin
1135
+ class ::HistoricalValue < Sequel::Model; end
1136
+ @c2.one_to_many :historical_values
1137
+
1138
+ v = @c2.new(:id => 1234).historical_values_dataset
1139
+ v.should be_a_kind_of(Sequel::Dataset)
1140
+ v.sql.should == 'SELECT * FROM historical_values WHERE (historical_values.node_id = 1234)'
1141
+ v.model.should == HistoricalValue
1142
+ ensure
1143
+ Object.send(:remove_const, :HistoricalValue)
1126
1144
  end
1127
-
1128
- @c2.one_to_many :historical_values
1129
-
1130
- n = @c2.new(:id => 1234)
1131
- v = n.historical_values_dataset
1132
- v.should be_a_kind_of(Sequel::Dataset)
1133
- v.sql.should == 'SELECT * FROM historical_values WHERE (historical_values.node_id = 1234)'
1134
- v.model.should == HistoricalValue
1135
1145
  end
1136
1146
 
1137
1147
  it "should use class inside a module if given as a string" do
1138
- module ::Historical
1139
- class Value < Sequel::Model
1148
+ begin
1149
+ module ::Historical
1150
+ class Value < Sequel::Model; end
1140
1151
  end
1152
+ @c2.one_to_many :historical_values, :class=>'Historical::Value'
1153
+
1154
+ v = @c2.new(:id => 1234).historical_values_dataset
1155
+ v.should be_a_kind_of(Sequel::Dataset)
1156
+ v.sql.should == 'SELECT * FROM values WHERE (values.node_id = 1234)'
1157
+ v.model.should == Historical::Value
1158
+ ensure
1159
+ Object.send(:remove_const, :Historical)
1141
1160
  end
1142
-
1143
- @c2.one_to_many :historical_values, :class=>'Historical::Value'
1144
-
1145
- n = @c2.new(:id => 1234)
1146
- v = n.historical_values_dataset
1147
- v.should be_a_kind_of(Sequel::Dataset)
1148
- v.sql.should == 'SELECT * FROM values WHERE (values.node_id = 1234)'
1149
- v.model.should == Historical::Value
1150
1161
  end
1151
1162
 
1152
- it "should use explicit key if given" do
1163
+ it "should use a callback if given one as the argument" do
1153
1164
  @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
1154
1165
 
1155
- n = @c2.new(:id => 1234)
1156
- a = n.attributes_dataset
1157
- a.should be_a_kind_of(Sequel::Dataset)
1158
- a.sql.should == 'SELECT * FROM attributes WHERE (attributes.nodeid = 1234)'
1166
+ d = @c2.load(:id => 1234)
1167
+ d.associations[:attributes] = []
1168
+ d.attributes(proc{|ds| ds.filter{name > 'M'}}).should_not == []
1169
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE ((attributes.nodeid = 1234) AND (name > 'M'))"]
1170
+ end
1171
+
1172
+ it "should use explicit key if given" do
1173
+ @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
1174
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.nodeid = 1234)'
1159
1175
  end
1160
1176
 
1161
1177
  it "should support_composite keys" do
@@ -1180,10 +1196,8 @@ describe Sequel::Model, "one_to_many" do
1180
1196
  it "should define an add_ method that works on existing records" do
1181
1197
  @c2.one_to_many :attributes, :class => @c1
1182
1198
 
1183
- n = @c2.new(:id => 1234)
1184
- a = @c1.new(:id => 2345)
1185
- a.save
1186
- MODEL_DB.reset
1199
+ n = @c2.load(:id => 1234)
1200
+ a = @c1.load(:id => 2345)
1187
1201
  a.should == n.add_attribute(a)
1188
1202
  a.values.should == {:node_id => 1234, :id => 2345}
1189
1203
  MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)']
@@ -1192,52 +1206,53 @@ describe Sequel::Model, "one_to_many" do
1192
1206
  it "should define an add_ method that works on new records" do
1193
1207
  @c2.one_to_many :attributes, :class => @c1
1194
1208
 
1195
- n = @c2.new(:id => 1234)
1209
+ n = @c2.load(:id => 1234)
1196
1210
  a = @c1.new(:id => 234)
1197
- # do not save
1198
- MODEL_DB.reset
1211
+ @c1.dataset._fetch = @c1.instance_dataset._fetch = {:node_id => 1234, :id => 234}
1199
1212
  a.should == n.add_attribute(a)
1200
- MODEL_DB.sqls.first.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1213
+ sqls = MODEL_DB.sqls
1214
+ sqls.shift.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1215
+ sqls.should == ["SELECT * FROM attributes WHERE (id = 234) LIMIT 1"]
1201
1216
  a.values.should == {:node_id => 1234, :id => 234}
1202
1217
  end
1203
1218
 
1204
1219
  it "should define a remove_ method that works on existing records" do
1205
1220
  @c2.one_to_many :attributes, :class => @c1
1206
1221
 
1207
- n = @c2.new(:id => 1234)
1208
- a = @c1.new(:id => 2345, :node_id => 1234)
1209
- a.save
1210
- MODEL_DB.reset
1222
+ n = @c2.load(:id => 1234)
1223
+ a = @c1.load(:id => 2345, :node_id => 1234)
1211
1224
  a.should == n.remove_attribute(a)
1212
1225
  a.values.should == {:node_id => nil, :id => 2345}
1213
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
1226
+ MODEL_DB.sqls.should == ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1", 'UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
1214
1227
  end
1215
1228
 
1216
1229
  it "should have the remove_ method raise an error if the passed object is not already associated" do
1217
1230
  @c2.one_to_many :attributes, :class => @c1
1218
- @c1.dataset.opts[:empty] = true
1219
1231
 
1220
1232
  n = @c2.new(:id => 1234)
1221
1233
  a = @c1.load(:id => 2345, :node_id => 1234)
1222
- MODEL_DB.reset
1234
+ @c1.dataset._fetch = []
1223
1235
  proc{n.remove_attribute(a)}.should raise_error(Sequel::Error)
1224
- MODEL_DB.sqls.should == ["SELECT 1 FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1"]
1236
+ MODEL_DB.sqls.should == ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1"]
1225
1237
  end
1226
1238
 
1227
1239
  it "should accept a hash for the add_ method and create a new record" do
1228
1240
  @c2.one_to_many :attributes, :class => @c1
1229
1241
  n = @c2.new(:id => 1234)
1230
1242
  MODEL_DB.reset
1231
- @c1.load(:node_id => 1234, :id => 234).should == n.add_attribute(:id => 234)
1232
- MODEL_DB.sqls.first.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1243
+ @c1.dataset._fetch = @c1.instance_dataset._fetch = {:node_id => 1234, :id => 234}
1244
+ n.add_attribute(:id => 234).should == @c1.load(:node_id => 1234, :id => 234)
1245
+ sqls = MODEL_DB.sqls
1246
+ sqls.shift.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1247
+ sqls.should == ["SELECT * FROM attributes WHERE (id = 234) LIMIT 1"]
1233
1248
  end
1234
1249
 
1235
1250
  it "should accept a primary key for the add_ method" do
1236
1251
  @c2.one_to_many :attributes, :class => @c1
1237
1252
  n = @c2.new(:id => 1234)
1238
- MODEL_DB.reset
1239
- @c1.load(:node_id => 1234, :id => 234).should == n.add_attribute(234)
1240
- MODEL_DB.sqls.first.should == "UPDATE attributes SET node_id = 1234 WHERE (id = 234)"
1253
+ @c1.dataset._fetch = {:id=>234, :node_id=>nil}
1254
+ n.add_attribute(234).should == @c1.load(:node_id => 1234, :id => 234)
1255
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE id = 234", "UPDATE attributes SET node_id = 1234 WHERE (id = 234)"]
1241
1256
  end
1242
1257
 
1243
1258
  it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
@@ -1248,13 +1263,8 @@ describe Sequel::Model, "one_to_many" do
1248
1263
  it "should accept a primary key for the remove_ method and remove an existing record" do
1249
1264
  @c2.one_to_many :attributes, :class => @c1
1250
1265
  n = @c2.new(:id => 1234)
1251
- ds = @c1.dataset
1252
- def ds.fetch_rows(sql)
1253
- db << sql
1254
- yield({:id=>234, :node_id=>1234})
1255
- end
1256
- MODEL_DB.reset
1257
- @c1.load(:node_id => nil, :id => 234).should == n.remove_attribute(234)
1266
+ @c1.dataset._fetch = {:id=>234, :node_id=>1234}
1267
+ n.remove_attribute(234).should == @c1.load(:node_id => nil, :id => 234)
1258
1268
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.id = 234)) LIMIT 1',
1259
1269
  'UPDATE attributes SET node_id = NULL WHERE (id = 234)']
1260
1270
  end
@@ -1268,10 +1278,8 @@ describe Sequel::Model, "one_to_many" do
1268
1278
  @c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
1269
1279
 
1270
1280
  n = @c2.new(:id => 1234, :xxx=>5)
1271
- a = @c1.new(:id => 2345)
1272
- a.save
1273
- MODEL_DB.reset
1274
- a.should == n.add_attribute(a)
1281
+ a = @c1.load(:id => 2345)
1282
+ n.add_attribute(a).should == a
1275
1283
  MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 5 WHERE (id = 2345)']
1276
1284
  end
1277
1285
 
@@ -1279,15 +1287,13 @@ describe Sequel::Model, "one_to_many" do
1279
1287
  @c2.one_to_many :attributes, :class => @c1
1280
1288
 
1281
1289
  n = @c2.new(:id => 1234)
1282
- a = @c1.new(:id => 2345)
1283
- a.save
1284
- MODEL_DB.reset
1290
+ a = @c1.load(:id => 2345)
1285
1291
  n.associations[:attributes] = []
1286
1292
  a.should == n.add_attribute(a)
1287
1293
  a.should == n.add_attribute(a)
1288
1294
  a.values.should == {:node_id => 1234, :id => 2345}
1289
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)'] * 2
1290
1295
  n.attributes.should == [a]
1296
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)'] * 2
1291
1297
  end
1292
1298
 
1293
1299
  it "should have add_ method respect composite keys" do
@@ -1295,18 +1301,24 @@ describe Sequel::Model, "one_to_many" do
1295
1301
 
1296
1302
  n = @c2.load(:id => 1234, :x=>5)
1297
1303
  a = @c1.load(:id => 2345)
1298
- a.should == n.add_attribute(a)
1299
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 2345\)/
1304
+ n.add_attribute(a).should == a
1305
+ sqls = MODEL_DB.sqls
1306
+ sqls.shift.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 2345\)/
1307
+ sqls.should == []
1300
1308
  end
1301
1309
 
1302
1310
  it "should have add_ method accept a composite key" do
1303
1311
  @c1.set_primary_key :id, :z
1304
1312
  @c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :y], :primary_key=>[:id, :x]
1313
+ @c1.dataset._fetch = {:id => 2345, :z => 8, :node_id => 1234, :y=>5}
1305
1314
 
1306
1315
  n = @c2.load(:id => 1234, :x=>5)
1307
1316
  a = @c1.load(:id => 2345, :z => 8, :node_id => 1234, :y=>5)
1308
- a.should == n.add_attribute([2345, 8])
1309
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = (1234|5), (node_id|y) = (1234|5) WHERE \(\((id|z) = (2345|8)\) AND \((id|z) = (2345|8)\)\)/
1317
+ n.add_attribute([2345, 8]).should == a
1318
+ sqls = MODEL_DB.sqls
1319
+ sqls.shift.should =~ /SELECT \* FROM attributes WHERE \(\((id|z) = (2345|8)\) AND \((id|z) = (2345|8)\)\) LIMIT 1/
1320
+ sqls.shift.should =~ /UPDATE attributes SET (node_id|y) = (1234|5), (node_id|y) = (1234|5) WHERE \(\((id|z) = (2345|8)\) AND \((id|z) = (2345|8)\)\)/
1321
+ sqls.should == []
1310
1322
  end
1311
1323
 
1312
1324
  it "should have remove_ method respect composite keys" do
@@ -1314,31 +1326,29 @@ describe Sequel::Model, "one_to_many" do
1314
1326
 
1315
1327
  n = @c2.load(:id => 1234, :x=>5)
1316
1328
  a = @c1.load(:id => 2345, :node_id=>1234, :y=>5)
1317
- a.should == n.remove_attribute(a)
1318
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(id = 2345\)/
1329
+ n.remove_attribute(a).should == a
1330
+ sqls = MODEL_DB.sqls
1331
+ sqls.pop.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(id = 2345\)/
1332
+ sqls.should == ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.y = 5) AND (id = 2345)) LIMIT 1"]
1319
1333
  end
1320
1334
 
1321
1335
  it "should accept a array of composite primary key values for the remove_ method and remove an existing record" do
1322
1336
  @c1.set_primary_key :id, :y
1323
1337
  @c2.one_to_many :attributes, :class => @c1, :key=>:node_id, :primary_key=>:id
1324
1338
  n = @c2.new(:id => 123)
1325
- ds = @c1.dataset
1326
- def ds.fetch_rows(sql)
1327
- db << sql
1328
- yield({:id=>234, :node_id=>123, :y=>5})
1329
- end
1330
- MODEL_DB.reset
1331
- @c1.load(:node_id => nil, :y => 5, :id => 234).should == n.remove_attribute([234, 5])
1332
- MODEL_DB.sqls.length.should == 2
1333
- MODEL_DB.sqls.first.should =~ /SELECT \* FROM attributes WHERE \(\(attributes.node_id = 123\) AND \(attributes\.(id|y) = (234|5)\) AND \(attributes\.(id|y) = (234|5)\)\) LIMIT 1/
1334
- MODEL_DB.sqls.last.should =~ /UPDATE attributes SET node_id = NULL WHERE \(\((id|y) = (234|5)\) AND \((id|y) = (234|5)\)\)/
1339
+ @c1.dataset._fetch = {:id=>234, :node_id=>123, :y=>5}
1340
+ n.remove_attribute([234, 5]).should == @c1.load(:node_id => nil, :y => 5, :id => 234)
1341
+ sqls = MODEL_DB.sqls
1342
+ sqls.length.should == 2
1343
+ sqls.first.should =~ /SELECT \* FROM attributes WHERE \(\(attributes.node_id = 123\) AND \(attributes\.(id|y) = (234|5)\) AND \(attributes\.(id|y) = (234|5)\)\) LIMIT 1/
1344
+ sqls.last.should =~ /UPDATE attributes SET node_id = NULL WHERE \(\((id|y) = (234|5)\) AND \((id|y) = (234|5)\)\)/
1335
1345
  end
1336
1346
 
1337
1347
  it "should raise an error in add_ and remove_ if the passed object returns false to save (is not valid)" do
1338
1348
  @c2.one_to_many :attributes, :class => @c1
1339
1349
  n = @c2.new(:id => 1234)
1340
1350
  a = @c1.new(:id => 2345)
1341
- def a.valid?(opts); false; end
1351
+ def a.validate() errors.add(:id, 'foo') end
1342
1352
  proc{n.add_attribute(a)}.should raise_error(Sequel::Error)
1343
1353
  proc{n.remove_attribute(a)}.should raise_error(Sequel::Error)
1344
1354
  end
@@ -1347,7 +1357,7 @@ describe Sequel::Model, "one_to_many" do
1347
1357
  @c2.one_to_many :attributes, :class => @c1, :validate=>false
1348
1358
  n = @c2.new(:id => 1234)
1349
1359
  a = @c1.new(:id => 2345)
1350
- def a.valid?(opts); false; end
1360
+ def a.validate() errors.add(:id, 'foo') end
1351
1361
  n.add_attribute(a).should == a
1352
1362
  n.remove_attribute(a).should == a
1353
1363
  end
@@ -1364,81 +1374,48 @@ describe Sequel::Model, "one_to_many" do
1364
1374
 
1365
1375
  it "should use :primary_key option if given" do
1366
1376
  @c1.one_to_many :nodes, :class => @c2, :primary_key => :node_id, :key=>:id
1367
- n = @c1.load(:id => 1234, :node_id=>4321)
1368
- n.nodes_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 4321)"
1377
+ @c1.load(:id => 1234, :node_id=>4321).nodes_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 4321)"
1369
1378
  end
1370
1379
 
1371
1380
  it "should support a select option" do
1372
1381
  @c2.one_to_many :attributes, :class => @c1, :select => [:id, :name]
1373
-
1374
- n = @c2.new(:id => 1234)
1375
- n.attributes_dataset.sql.should == "SELECT id, name FROM attributes WHERE (attributes.node_id = 1234)"
1382
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT id, name FROM attributes WHERE (attributes.node_id = 1234)"
1376
1383
  end
1377
1384
 
1378
1385
  it "should support a conditions option" do
1379
1386
  @c2.one_to_many :attributes, :class => @c1, :conditions => {:a=>32}
1380
- n = @c2.new(:id => 1234)
1381
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (a = 32))"
1387
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (a = 32))"
1382
1388
  @c2.one_to_many :attributes, :class => @c1, :conditions => ~:a
1383
- n = @c2.new(:id => 1234)
1384
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND NOT a)"
1389
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND NOT a)"
1385
1390
  end
1386
1391
 
1387
1392
  it "should support an order option" do
1388
1393
  @c2.one_to_many :attributes, :class => @c1, :order => :kind
1389
-
1390
- n = @c2.new(:id => 1234)
1391
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind"
1394
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind"
1392
1395
  end
1393
1396
 
1394
1397
  it "should support an array for the order option" do
1395
1398
  @c2.one_to_many :attributes, :class => @c1, :order => [:kind1, :kind2]
1396
-
1397
- n = @c2.new(:id => 1234)
1398
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind1, kind2"
1399
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind1, kind2"
1399
1400
  end
1400
1401
 
1401
1402
  it "should return array with all members of the association" do
1402
1403
  @c2.one_to_many :attributes, :class => @c1
1403
-
1404
- n = @c2.new(:id => 1234)
1405
- atts = n.attributes
1406
- atts.should be_a_kind_of(Array)
1407
- atts.size.should == 1
1408
- atts.first.should be_a_kind_of(@c1)
1409
- atts.first.values.should == {}
1410
-
1411
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1404
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1412
1405
  end
1413
1406
 
1414
1407
  it "should accept a block" do
1415
1408
  @c2.one_to_many :attributes, :class => @c1 do |ds|
1416
1409
  ds.filter(:xxx => @xxx)
1417
1410
  end
1418
-
1419
- n = @c2.new(:id => 1234)
1420
- atts = n.attributes
1421
- atts.should be_a_kind_of(Array)
1422
- atts.size.should == 1
1423
- atts.first.should be_a_kind_of(@c1)
1424
- atts.first.values.should == {}
1425
-
1426
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL))']
1411
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL))'
1427
1412
  end
1428
1413
 
1429
1414
  it "should support :order option with block" do
1430
1415
  @c2.one_to_many :attributes, :class => @c1, :order => :kind do |ds|
1431
1416
  ds.filter(:xxx => @xxx)
1432
1417
  end
1433
-
1434
- n = @c2.new(:id => 1234)
1435
- atts = n.attributes
1436
- atts.should be_a_kind_of(Array)
1437
- atts.size.should == 1
1438
- atts.first.should be_a_kind_of(@c1)
1439
- atts.first.values.should == {}
1440
-
1441
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL)) ORDER BY kind']
1418
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL)) ORDER BY kind'
1442
1419
  end
1443
1420
 
1444
1421
  it "should have the block argument affect the _dataset method" do
@@ -1450,18 +1427,11 @@ describe Sequel::Model, "one_to_many" do
1450
1427
 
1451
1428
  it "should support a :dataset option that is used instead of the default" do
1452
1429
  c1 = @c1
1453
- @c2.one_to_many :all_other_attributes, :class => @c1, :dataset=>proc{c1.filter(:nodeid=>pk).invert}, :order=>:a, :limit=>10 do |ds|
1430
+ @c2.one_to_many :all_other_attributes, :class => @c1, :dataset=>proc{c1.exclude(:nodeid=>pk)}, :order=>:a, :limit=>10 do |ds|
1454
1431
  ds.filter(:xxx => 5)
1455
1432
  end
1456
-
1457
1433
  @c2.new(:id => 1234).all_other_attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10'
1458
- n = @c2.new(:id => 1234)
1459
- atts = n.all_other_attributes
1460
- atts.should be_a_kind_of(Array)
1461
- atts.size.should == 1
1462
- atts.first.should be_a_kind_of(@c1)
1463
- atts.first.values.should == {}
1464
-
1434
+ @c2.new(:id => 1234).all_other_attributes.should == [@c1.load({})]
1465
1435
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10']
1466
1436
  end
1467
1437
 
@@ -1479,9 +1449,7 @@ describe Sequel::Model, "one_to_many" do
1479
1449
 
1480
1450
  it "should set cached instance variable when accessed" do
1481
1451
  @c2.one_to_many :attributes, :class => @c1
1482
-
1483
1452
  n = @c2.new(:id => 1234)
1484
- MODEL_DB.reset
1485
1453
  n.associations.include?(:attributes).should == false
1486
1454
  atts = n.attributes
1487
1455
  atts.should == n.associations[:attributes]
@@ -1490,9 +1458,7 @@ describe Sequel::Model, "one_to_many" do
1490
1458
 
1491
1459
  it "should use cached instance variable if available" do
1492
1460
  @c2.one_to_many :attributes, :class => @c1
1493
-
1494
1461
  n = @c2.new(:id => 1234)
1495
- MODEL_DB.reset
1496
1462
  n.associations[:attributes] = 42
1497
1463
  n.attributes.should == 42
1498
1464
  MODEL_DB.sqls.should == []
@@ -1500,9 +1466,7 @@ describe Sequel::Model, "one_to_many" do
1500
1466
 
1501
1467
  it "should not use cached instance variable if asked to reload" do
1502
1468
  @c2.one_to_many :attributes, :class => @c1
1503
-
1504
1469
  n = @c2.new(:id => 1234)
1505
- MODEL_DB.reset
1506
1470
  n.associations[:attributes] = 42
1507
1471
  n.attributes(true).should_not == 42
1508
1472
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
@@ -1510,10 +1474,8 @@ describe Sequel::Model, "one_to_many" do
1510
1474
 
1511
1475
  it "should add item to cached instance variable if it exists when calling add_" do
1512
1476
  @c2.one_to_many :attributes, :class => @c1
1513
-
1514
1477
  n = @c2.new(:id => 1234)
1515
- att = @c1.new(:id => 345)
1516
- MODEL_DB.reset
1478
+ att = @c1.load(:id => 345)
1517
1479
  a = []
1518
1480
  n.associations[:attributes] = a
1519
1481
  n.add_attribute(att)
@@ -1535,7 +1497,6 @@ describe Sequel::Model, "one_to_many" do
1535
1497
 
1536
1498
  n = @c2.load(:id => 1234)
1537
1499
  att = @c1.load(:id => 345)
1538
- MODEL_DB.reset
1539
1500
  a = [att]
1540
1501
  n.associations[:attributes] = a
1541
1502
  n.remove_attribute(att)
@@ -1587,13 +1548,9 @@ describe Sequel::Model, "one_to_many" do
1587
1548
  n = @c2.new(:id => 1234)
1588
1549
  atts = n.attributes
1589
1550
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1590
- atts.should be_a_kind_of(Array)
1591
- atts.size.should == 1
1592
- atts.first.should be_a_kind_of(@c1)
1593
- atts.first.values.should == {}
1594
- atts.first.node.should == n
1595
-
1596
- MODEL_DB.sqls.length.should == 1
1551
+ atts.should == [@c1.load({})]
1552
+ atts.map{|a| a.node}.should == [n]
1553
+ MODEL_DB.sqls.should == []
1597
1554
  end
1598
1555
 
1599
1556
  it "should use an explicit reciprocal instance variable if given" do
@@ -1602,19 +1559,15 @@ describe Sequel::Model, "one_to_many" do
1602
1559
  n = @c2.new(:id => 1234)
1603
1560
  atts = n.attributes
1604
1561
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1605
- atts.should be_a_kind_of(Array)
1606
- atts.size.should == 1
1607
- atts.first.should be_a_kind_of(@c1)
1608
- atts.first.values.should == {}
1609
- atts.first.associations[:wxyz].should == n
1610
-
1611
- MODEL_DB.sqls.length.should == 1
1562
+ atts.should == [@c1.load({})]
1563
+ atts.map{|a| a.associations[:wxyz]}.should == [n]
1564
+ MODEL_DB.sqls.should == []
1612
1565
  end
1613
1566
 
1614
1567
  it "should have an remove_all_ method that removes all associations" do
1615
1568
  @c2.one_to_many :attributes, :class => @c1
1616
1569
  @c2.new(:id => 1234).remove_all_attributes
1617
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)'
1570
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)']
1618
1571
  end
1619
1572
 
1620
1573
  it "should have remove_all method respect association filters" do
@@ -1628,13 +1581,15 @@ describe Sequel::Model, "one_to_many" do
1628
1581
  it "should have the remove_all_ method respect the :primary_key option" do
1629
1582
  @c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
1630
1583
  @c2.new(:id => 1234, :xxx=>5).remove_all_attributes
1631
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 5)'
1584
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (node_id = 5)']
1632
1585
  end
1633
1586
 
1634
1587
  it "should have the remove_all_ method respect composite keys" do
1635
1588
  @c2.one_to_many :attributes, :class => @c1, :key=>[:node_id, :y], :primary_key=>[:id, :x]
1636
1589
  @c2.new(:id => 1234, :x=>5).remove_all_attributes
1637
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\)\)/
1590
+ sqls = MODEL_DB.sqls
1591
+ sqls.pop.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\)\)/
1592
+ sqls.should == []
1638
1593
  end
1639
1594
 
1640
1595
  it "remove_all should set the cached instance variable to []" do
@@ -1648,10 +1603,8 @@ describe Sequel::Model, "one_to_many" do
1648
1603
  @c2.one_to_many :attributes, :class => @c1
1649
1604
  attrib = @c1.new(:id=>3)
1650
1605
  node = @c2.new(:id => 1234)
1651
- d = @c1.dataset
1652
- def d.fetch_rows(s); end
1606
+ @c1.dataset._fetch = [[], [{:id=>3, :node_id=>1234}]]
1653
1607
  node.attributes.should == []
1654
- def attrib.save(*); self end
1655
1608
  node.add_attribute(attrib)
1656
1609
  node.associations[:attributes].should == [attrib]
1657
1610
  node.remove_all_attributes.should == [attrib]
@@ -1665,15 +1618,12 @@ describe Sequel::Model, "one_to_many" do
1665
1618
  it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
1666
1619
  @c2.one_to_many :attributes, :class => @c1
1667
1620
  @c1.many_to_one :node, :class => @c2
1668
- d = @c1.dataset
1669
- def d.fetch_rows(s); end
1670
- d = @c2.dataset
1671
- def d.fetch_rows(s); end
1621
+ @c2.dataset._fetch = []
1622
+ @c1.dataset._fetch = [[], [{:id=>3, :node_id=>1234}]]
1672
1623
  attrib = @c1.new(:id=>3)
1673
- node = @c2.new(:id => 1234)
1624
+ node = @c2.load(:id => 1234)
1674
1625
  node.attributes.should == []
1675
1626
  attrib.node.should == nil
1676
- def attrib.save(*); self end
1677
1627
  node.add_attribute(attrib)
1678
1628
  attrib.associations[:node].should == node
1679
1629
  node.remove_all_attributes
@@ -1761,18 +1711,18 @@ describe Sequel::Model, "one_to_many" do
1761
1711
  h = []
1762
1712
  @c2.one_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
1763
1713
  @c2.class_eval do
1764
- class_variable_set(:@@blah, h)
1714
+ self::Foo = h
1765
1715
  def _add_attribute(v)
1766
- self.class.send(:class_variable_get, :@@blah) << 4
1716
+ model::Foo << 4
1767
1717
  end
1768
1718
  def _remove_attribute(v)
1769
- self.class.send(:class_variable_get, :@@blah) << 5
1719
+ model::Foo << 5
1770
1720
  end
1771
1721
  def blah(x)
1772
- self.class.send(:class_variable_get, :@@blah) << x.pk
1722
+ model::Foo << x.pk
1773
1723
  end
1774
1724
  def blahr(x)
1775
- self.class.send(:class_variable_get, :@@blah) << 6
1725
+ model::Foo << 6
1776
1726
  end
1777
1727
  end
1778
1728
  p = @c2.load(:id=>10)
@@ -1788,17 +1738,12 @@ describe Sequel::Model, "one_to_many" do
1788
1738
  h = []
1789
1739
  @c2.one_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
1790
1740
  @c2.class_eval do
1791
- class_variable_set(:@@blah, h)
1741
+ self::Foo = h
1792
1742
  def al(v)
1793
- v.each{|x| self.class.send(:class_variable_get, :@@blah) << x.pk}
1794
- end
1795
- end
1796
- @c1.class_eval do
1797
- def @dataset.fetch_rows(sql)
1798
- yield({:id=>20})
1799
- yield({:id=>30})
1743
+ v.each{|x| model::Foo << x.pk}
1800
1744
  end
1801
1745
  end
1746
+ @c1.dataset._fetch = [{:id=>20}, {:id=>30}]
1802
1747
  p = @c2.load(:id=>10, :parent_id=>20)
1803
1748
  attributes = p.attributes
1804
1749
  h.should == [[10, [20, 30]], 20, 30]
@@ -1845,23 +1790,13 @@ describe Sequel::Model, "one_to_many" do
1845
1790
  end
1846
1791
 
1847
1792
  describe Sequel::Model, "many_to_many" do
1848
-
1849
1793
  before do
1850
- MODEL_DB.reset
1851
-
1852
1794
  @c1 = Class.new(Sequel::Model(:attributes)) do
1853
1795
  unrestrict_primary_key
1854
1796
  attr_accessor :yyy
1855
1797
  def self.name; 'Attribute'; end
1856
1798
  def self.to_s; 'Attribute'; end
1857
1799
  columns :id, :y
1858
- def _refresh(ds)
1859
- self
1860
- end
1861
-
1862
- def self.[](id)
1863
- load(id.is_a?(Array) ? {:id => id[0], :y => id[1]} : {:id => id})
1864
- end
1865
1800
  end
1866
1801
 
1867
1802
  @c2 = Class.new(Sequel::Model(:nodes)) do
@@ -1873,98 +1808,61 @@ describe Sequel::Model, "many_to_many" do
1873
1808
  columns :id, :x
1874
1809
  end
1875
1810
  @dataset = @c2.dataset
1811
+ @c1.dataset.autoid = 1
1876
1812
 
1877
- [@c1, @c2].each do |c|
1878
- c.dataset.extend(Module.new {
1879
- def fetch_rows(sql)
1880
- @db << sql
1881
- yield Hash.new
1882
- end
1883
- })
1884
- end
1813
+ [@c1, @c2].each{|c| c.dataset._fetch = {}}
1814
+ MODEL_DB.reset
1885
1815
  end
1886
1816
 
1887
1817
  it "should use implicit key values and join table if omitted" do
1888
1818
  @c2.many_to_many :attributes, :class => @c1
1889
-
1890
- n = @c2.new(:id => 1234)
1891
- a = n.attributes_dataset
1892
- a.should be_a_kind_of(Sequel::Dataset)
1893
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
1819
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
1894
1820
  end
1895
1821
 
1896
1822
  it "should use implicit class if omitted" do
1897
- class ::Tag < Sequel::Model
1823
+ begin
1824
+ class ::Tag < Sequel::Model; end
1825
+ @c2.many_to_many :tags
1826
+ @c2.new(:id => 1234).tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1827
+ ensure
1828
+ Object.send(:remove_const, :Tag)
1898
1829
  end
1899
-
1900
- @c2.many_to_many :tags
1901
-
1902
- n = @c2.new(:id => 1234)
1903
- a = n.tags_dataset
1904
- a.should be_a_kind_of(Sequel::Dataset)
1905
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1906
1830
  end
1907
1831
 
1908
1832
  it "should use class inside module if given as a string" do
1909
- module ::Historical
1910
- class Tag < Sequel::Model
1833
+ begin
1834
+ module ::Historical
1835
+ class Tag < Sequel::Model; end
1911
1836
  end
1837
+ @c2.many_to_many :tags, :class=>'::Historical::Tag'
1838
+ @c2.new(:id => 1234).tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1839
+ ensure
1840
+ Object.send(:remove_const, :Historical)
1912
1841
  end
1913
-
1914
- @c2.many_to_many :tags, :class=>'::Historical::Tag'
1915
-
1916
- n = @c2.new(:id => 1234)
1917
- a = n.tags_dataset
1918
- a.should be_a_kind_of(Sequel::Dataset)
1919
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1920
1842
  end
1921
1843
 
1922
1844
  it "should use explicit key values and join table if given" do
1923
1845
  @c2.many_to_many :attributes, :class => @c1, :left_key => :nodeid, :right_key => :attributeid, :join_table => :attribute2node
1924
-
1925
- n = @c2.new(:id => 1234)
1926
- a = n.attributes_dataset
1927
- a.should be_a_kind_of(Sequel::Dataset)
1928
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON ((attribute2node.attributeid = attributes.id) AND (attribute2node.nodeid = 1234))'
1929
- end
1930
-
1931
- it "should handle an aliased join table when eager loading" do
1932
- r = @c2.many_to_many(:attributes, :class => @c1, :join_table => :attribute2node___attributes_nodes)
1933
-
1934
- a = @c2.eager_loading_dataset(r, @c2.dataset, [:id], nil)
1935
- a.sql.should == 'SELECT id, attributes_nodes.node_id AS x_foreign_key_x FROM nodes'
1846
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON ((attribute2node.attributeid = attributes.id) AND (attribute2node.nodeid = 1234))'
1936
1847
  end
1937
1848
 
1938
1849
  it "should support a conditions option" do
1939
1850
  @c2.many_to_many :attributes, :class => @c1, :conditions => {:a=>32}
1940
- n = @c2.new(:id => 1234)
1941
- a = n.attributes_dataset
1942
- a.should be_a_kind_of(Sequel::Dataset)
1943
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (a = 32)'
1851
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (a = 32)'
1852
+
1944
1853
  @c2.many_to_many :attributes, :class => @c1, :conditions => ['a = ?', 32]
1945
- n = @c2.new(:id => 1234)
1946
- a = n.attributes_dataset
1947
- a.should be_a_kind_of(Sequel::Dataset)
1948
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (a = 32)'
1949
- n.attributes.should == [@c1.load({})]
1854
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (a = 32)'
1855
+ @c2.new(:id => 1234).attributes.should == [@c1.load({})]
1950
1856
  end
1951
1857
 
1952
1858
  it "should support an order option" do
1953
1859
  @c2.many_to_many :attributes, :class => @c1, :order => :blah
1954
-
1955
- n = @c2.new(:id => 1234)
1956
- a = n.attributes_dataset
1957
- a.should be_a_kind_of(Sequel::Dataset)
1958
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah'
1860
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah'
1959
1861
  end
1960
1862
 
1961
1863
  it "should support an array for the order option" do
1962
1864
  @c2.many_to_many :attributes, :class => @c1, :order => [:blah1, :blah2]
1963
-
1964
- n = @c2.new(:id => 1234)
1965
- a = n.attributes_dataset
1966
- a.should be_a_kind_of(Sequel::Dataset)
1967
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah1, blah2'
1865
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah1, blah2'
1968
1866
  end
1969
1867
 
1970
1868
  it "should support :left_primary_key and :right_primary_key options" do
@@ -1999,19 +1897,13 @@ describe Sequel::Model, "many_to_many" do
1999
1897
  it "should support a select option" do
2000
1898
  @c2.many_to_many :attributes, :class => @c1, :select => :blah
2001
1899
 
2002
- n = @c2.new(:id => 1234)
2003
- a = n.attributes_dataset
2004
- a.should be_a_kind_of(Sequel::Dataset)
2005
- a.sql.should == 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
1900
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
2006
1901
  end
2007
1902
 
2008
1903
  it "should support an array for the select option" do
2009
1904
  @c2.many_to_many :attributes, :class => @c1, :select => [:attributes.*, :attribute_nodes__blah2]
2010
1905
 
2011
- n = @c2.new(:id => 1234)
2012
- a = n.attributes_dataset
2013
- a.should be_a_kind_of(Sequel::Dataset)
2014
- a.sql.should == 'SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
1906
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
2015
1907
  end
2016
1908
 
2017
1909
  it "should accept a block" do
@@ -2021,11 +1913,7 @@ describe Sequel::Model, "many_to_many" do
2021
1913
 
2022
1914
  n = @c2.new(:id => 1234)
2023
1915
  n.xxx = 555
2024
- a = n.attributes
2025
- a.should be_a_kind_of(Array)
2026
- a.size.should == 1
2027
- a.first.should be_a_kind_of(@c1)
2028
- MODEL_DB.sqls.first.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555)'
1916
+ n.attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555)'
2029
1917
  end
2030
1918
 
2031
1919
  it "should allow the :order option while accepting a block" do
@@ -2035,20 +1923,9 @@ describe Sequel::Model, "many_to_many" do
2035
1923
 
2036
1924
  n = @c2.new(:id => 1234)
2037
1925
  n.xxx = 555
2038
- a = n.attributes
2039
- a.should be_a_kind_of(Array)
2040
- a.size.should == 1
2041
- a.first.should be_a_kind_of(@c1)
2042
- MODEL_DB.sqls.first.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555) ORDER BY blah1, blah2'
1926
+ n.attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555) ORDER BY blah1, blah2'
2043
1927
  end
2044
1928
 
2045
- it "should have the block argument affect the _dataset method" do
2046
- @c2.many_to_many :attributes, :class => @c1 do |ds|
2047
- ds.filter(:xxx => 456)
2048
- end
2049
- @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 456)'
2050
- end
2051
-
2052
1929
  it "should support a :dataset option that is used instead of the default" do
2053
1930
  c1 = @c1
2054
1931
  @c2.many_to_many :attributes, :class => @c1, :dataset=>proc{c1.join_table(:natural, :an).filter(:an__nodeid=>pk)}, :order=> :a, :limit=>10, :select=>nil do |ds|
@@ -2058,11 +1935,8 @@ describe Sequel::Model, "many_to_many" do
2058
1935
  n = @c2.new(:id => 1234)
2059
1936
  n.xxx = 555
2060
1937
  n.attributes_dataset.sql.should == 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
2061
- a = n.attributes
2062
- a.should be_a_kind_of(Array)
2063
- a.size.should == 1
2064
- a.first.should be_a_kind_of(@c1)
2065
- MODEL_DB.sqls.first.should == 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
1938
+ n.attributes.should == [@c1.load({})]
1939
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10']
2066
1940
  end
2067
1941
 
2068
1942
  it "should support a :limit option" do
@@ -2077,15 +1951,32 @@ describe Sequel::Model, "many_to_many" do
2077
1951
  @c2.new(:id => 1234).attributes_dataset.opts[:eager].should == {:attributes=>nil}
2078
1952
  end
2079
1953
 
1954
+ it "should handle an aliased join table" do
1955
+ @c2.many_to_many :attributes, :class => @c1, :join_table => :attribute2node___attributes_nodes
1956
+ n = @c2.load(:id => 1234)
1957
+ a = @c1.load(:id => 2345)
1958
+ n.attributes_dataset.sql.should == "SELECT attributes.* FROM attributes INNER JOIN attribute2node AS attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))"
1959
+ a.should == n.add_attribute(a)
1960
+ a.should == n.remove_attribute(a)
1961
+ n.remove_all_attributes
1962
+ sqls = MODEL_DB.sqls
1963
+ ['INSERT INTO attribute2node (node_id, attribute_id) VALUES (1234, 2345)',
1964
+ 'INSERT INTO attribute2node (attribute_id, node_id) VALUES (2345, 1234)'].should(include(sqls.shift))
1965
+ ["DELETE FROM attribute2node WHERE ((node_id = 1234) AND (attribute_id = 2345))",
1966
+ "DELETE FROM attribute2node WHERE ((attribute_id = 2345) AND (node_id = 1234))"].should(include(sqls.shift))
1967
+ sqls.should == ["DELETE FROM attribute2node WHERE (node_id = 1234)"]
1968
+ end
1969
+
2080
1970
  it "should define an add_ method that works on existing records" do
2081
1971
  @c2.many_to_many :attributes, :class => @c1
2082
1972
 
2083
1973
  n = @c2.load(:id => 1234)
2084
1974
  a = @c1.load(:id => 2345)
2085
- a.should == n.add_attribute(a)
1975
+ n.add_attribute(a).should == a
1976
+ sqls = MODEL_DB.sqls
2086
1977
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)',
2087
- 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'
2088
- ].should(include(MODEL_DB.sqls.first))
1978
+ 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'].should(include(sqls.shift))
1979
+ sqls.should == []
2089
1980
  end
2090
1981
 
2091
1982
  it "should define an add_ method that works with a primary key" do
@@ -2093,21 +1984,25 @@ describe Sequel::Model, "many_to_many" do
2093
1984
 
2094
1985
  n = @c2.load(:id => 1234)
2095
1986
  a = @c1.load(:id => 2345)
2096
- a.should == n.add_attribute(2345)
1987
+ @c1.dataset._fetch = {:id=>2345}
1988
+ n.add_attribute(2345).should == a
1989
+ sqls = MODEL_DB.sqls
2097
1990
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)',
2098
- 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'
2099
- ].should(include(MODEL_DB.sqls.first))
1991
+ 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'].should(include(sqls.pop))
1992
+ sqls.should == ["SELECT * FROM attributes WHERE id = 2345"]
2100
1993
  end
2101
1994
 
2102
1995
  it "should allow passing a hash to the add_ method which creates a new record" do
2103
1996
  @c2.many_to_many :attributes, :class => @c1
2104
1997
 
2105
1998
  n = @c2.load(:id => 1234)
2106
- @c1.load(:id => 1).should == n.add_attribute(:id => 1)
2107
- MODEL_DB.sqls.first.should == 'INSERT INTO attributes (id) VALUES (1)'
1999
+ @c1.dataset._fetch = @c1.instance_dataset._fetch = {:id=>1}
2000
+ n.add_attribute(:id => 1).should == @c1.load(:id => 1)
2001
+ sqls = MODEL_DB.sqls
2108
2002
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 1)',
2109
2003
  'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (1, 1234)'
2110
- ].should(include(MODEL_DB.sqls.last))
2004
+ ].should(include(sqls.pop))
2005
+ sqls.should == ['INSERT INTO attributes (id) VALUES (1)', "SELECT * FROM attributes WHERE (id = 1) LIMIT 1"]
2111
2006
  end
2112
2007
 
2113
2008
  it "should define a remove_ method that works on existing records" do
@@ -2115,8 +2010,8 @@ describe Sequel::Model, "many_to_many" do
2115
2010
 
2116
2011
  n = @c2.new(:id => 1234)
2117
2012
  a = @c1.new(:id => 2345)
2118
- a.should == n.remove_attribute(a)
2119
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))'
2013
+ n.remove_attribute(a).should == a
2014
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))']
2120
2015
  end
2121
2016
 
2122
2017
  it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
@@ -2127,13 +2022,8 @@ describe Sequel::Model, "many_to_many" do
2127
2022
  it "should accept a primary key for the remove_ method and remove an existing record" do
2128
2023
  @c2.many_to_many :attributes, :class => @c1
2129
2024
  n = @c2.new(:id => 1234)
2130
- ds = @c1.dataset
2131
- def ds.fetch_rows(sql)
2132
- db << sql
2133
- yield({:id=>234})
2134
- end
2135
- MODEL_DB.reset
2136
- @c1.load(:id => 234).should == n.remove_attribute(234)
2025
+ @c1.dataset._fetch = {:id=>234}
2026
+ n.remove_attribute(234).should == @c1.load(:id => 234)
2137
2027
  MODEL_DB.sqls.should == ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (attributes.id = 234) LIMIT 1",
2138
2028
  "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"]
2139
2029
  end
@@ -2148,10 +2038,12 @@ describe Sequel::Model, "many_to_many" do
2148
2038
 
2149
2039
  n = @c2.load(:id => 1234).set(:xxx=>5)
2150
2040
  a = @c1.load(:id => 2345).set(:yyy=>8)
2151
- a.should == n.add_attribute(a)
2041
+ n.add_attribute(a).should == a
2042
+ sqls = MODEL_DB.sqls
2152
2043
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (5, 8)',
2153
2044
  'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (8, 5)'
2154
- ].should(include(MODEL_DB.sqls.first))
2045
+ ].should(include(sqls.pop))
2046
+ sqls.should == []
2155
2047
  end
2156
2048
 
2157
2049
  it "should have add_ method not add the same object to the cached association array if the object is already in the array" do
@@ -2170,7 +2062,9 @@ describe Sequel::Model, "many_to_many" do
2170
2062
  n = @c2.load(:id => 1234, :x=>5)
2171
2063
  a = @c1.load(:id => 2345, :y=>8)
2172
2064
  a.should == n.add_attribute(a)
2173
- m = /INSERT INTO attributes_nodes \((\w+), (\w+), (\w+), (\w+)\) VALUES \((\d+), (\d+), (\d+), (\d+)\)/.match(MODEL_DB.sqls.first)
2065
+ sqls = MODEL_DB.sqls
2066
+ m = /INSERT INTO attributes_nodes \((\w+), (\w+), (\w+), (\w+)\) VALUES \((\d+), (\d+), (\d+), (\d+)\)/.match(sqls.pop)
2067
+ sqls.should == []
2174
2068
  m.should_not == nil
2175
2069
  map = {'l1'=>1234, 'l2'=>5, 'r1'=>2345, 'r2'=>8}
2176
2070
  %w[l1 l2 r1 r2].each do |x|
@@ -2187,10 +2081,15 @@ describe Sequel::Model, "many_to_many" do
2187
2081
 
2188
2082
  it "should have the add_ method respect composite keys" do
2189
2083
  @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
2084
+ @c1.set_primary_key [:id, :y]
2190
2085
  n = @c2.load(:id => 1234, :x=>5)
2191
2086
  a = @c1.load(:id => 2345, :y=>8)
2192
- a.should == n.add_attribute([2345, 8])
2193
- MODEL_DB.sqls.first.should =~ /INSERT INTO attributes_nodes \([lr][12], [lr][12], [lr][12], [lr][12]\) VALUES \((1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8)\)/
2087
+ @c1.dataset._fetch = {:id => 2345, :y=>8}
2088
+ n.add_attribute([2345, 8]).should == a
2089
+ sqls = MODEL_DB.sqls
2090
+ sqls.shift.should =~ /SELECT \* FROM attributes WHERE \(\((id|y) = (8|2345)\) AND \((id|y) = (8|2345)\)\) LIMIT 1/
2091
+ sqls.pop.should =~ /INSERT INTO attributes_nodes \([lr][12], [lr][12], [lr][12], [lr][12]\) VALUES \((1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8)\)/
2092
+ sqls.should == []
2194
2093
  end
2195
2094
 
2196
2095
  it "should have the remove_ method respect the :left_primary_key and :right_primary_key options" do
@@ -2198,8 +2097,8 @@ describe Sequel::Model, "many_to_many" do
2198
2097
 
2199
2098
  n = @c2.new(:id => 1234, :xxx=>5)
2200
2099
  a = @c1.new(:id => 2345, :yyy=>8)
2201
- a.should == n.remove_attribute(a)
2202
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 8))'
2100
+ n.remove_attribute(a).should == a
2101
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 8))']
2203
2102
  end
2204
2103
 
2205
2104
  it "should have the remove_ method respect composite keys" do
@@ -2214,17 +2113,12 @@ describe Sequel::Model, "many_to_many" do
2214
2113
  @c1.set_primary_key [:id, :y]
2215
2114
  @c2.many_to_many :attributes, :class => @c1
2216
2115
  n = @c2.new(:id => 1234)
2217
- ds = @c1.dataset
2218
- def ds.fetch_rows(sql)
2219
- db << sql
2220
- yield({:id=>234, :y=>8})
2221
- end
2222
- MODEL_DB.reset
2116
+ @c1.dataset._fetch = {:id=>234, :y=>8}
2223
2117
  @c1.load(:id => 234, :y=>8).should == n.remove_attribute([234, 8])
2118
+ sqls = MODEL_DB.sqls
2224
2119
  ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE ((attributes.id = 234) AND (attributes.y = 8)) LIMIT 1",
2225
- "SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE ((attributes.y = 8) AND (attributes.id = 234)) LIMIT 1"].should include(MODEL_DB.sqls.first)
2226
- MODEL_DB.sqls.last.should == "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"
2227
- MODEL_DB.sqls.length.should == 2
2120
+ "SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE ((attributes.y = 8) AND (attributes.id = 234)) LIMIT 1"].should include(sqls.shift)
2121
+ sqls.should == ["DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"]
2228
2122
  end
2229
2123
 
2230
2124
  it "should raise an error if the model object doesn't have a valid primary key" do
@@ -2242,6 +2136,7 @@ describe Sequel::Model, "many_to_many" do
2242
2136
  n = @c1.new
2243
2137
  a = @c2.load(:id=>123)
2244
2138
  n.new?.should == true
2139
+ @c1.dataset._fetch = {:id=>1}
2245
2140
  a.add_attribute(n)
2246
2141
  n.new?.should == false
2247
2142
  end
@@ -2250,7 +2145,7 @@ describe Sequel::Model, "many_to_many" do
2250
2145
  @c2.many_to_many :attributes, :class => @c1
2251
2146
  n = @c1.new
2252
2147
  a = @c2.load(:id=>123)
2253
- def n.valid?(opts); false; end
2148
+ def n.validate() errors.add(:id, 'foo') end
2254
2149
  proc{a.add_attribute(n)}.should raise_error(Sequel::ValidationFailed)
2255
2150
  end
2256
2151
 
@@ -2259,7 +2154,7 @@ describe Sequel::Model, "many_to_many" do
2259
2154
  n = @c1.new
2260
2155
  n.raise_on_save_failure = false
2261
2156
  a = @c2.load(:id=>123)
2262
- def n.valid?(opts); false; end
2157
+ def n.validate() errors.add(:id, 'foo') end
2263
2158
  proc{a.add_attribute(n)}.should raise_error(Sequel::Error)
2264
2159
  end
2265
2160
 
@@ -2267,7 +2162,8 @@ describe Sequel::Model, "many_to_many" do
2267
2162
  @c2.many_to_many :attributes, :class => @c1, :validate=>false
2268
2163
  n = @c1.new
2269
2164
  a = @c2.load(:id=>123)
2270
- def n.valid?(opts); false; end
2165
+ def n.validate() errors.add(:id, 'foo') end
2166
+ @c1.dataset._fetch = {:id=>1}
2271
2167
  a.add_attribute(n)
2272
2168
  n.new?.should == false
2273
2169
  end
@@ -2282,31 +2178,23 @@ describe Sequel::Model, "many_to_many" do
2282
2178
  it "should provide an array with all members of the association" do
2283
2179
  @c2.many_to_many :attributes, :class => @c1
2284
2180
 
2285
- n = @c2.new(:id => 1234)
2286
- atts = n.attributes
2287
- atts.should be_a_kind_of(Array)
2288
- atts.size.should == 1
2289
- atts.first.should be_a_kind_of(@c1)
2290
-
2291
- MODEL_DB.sqls.first.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
2181
+ @c2.new(:id => 1234).attributes.should == [@c1.load({})]
2182
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))']
2292
2183
  end
2293
2184
 
2294
2185
  it "should set cached instance variable when accessed" do
2295
2186
  @c2.many_to_many :attributes, :class => @c1
2296
2187
 
2297
2188
  n = @c2.new(:id => 1234)
2298
- MODEL_DB.reset
2299
2189
  n.associations.include?(:attributes).should == false
2300
2190
  atts = n.attributes
2301
2191
  atts.should == n.associations[:attributes]
2302
- MODEL_DB.sqls.length.should == 1
2303
2192
  end
2304
2193
 
2305
2194
  it "should use cached instance variable if available" do
2306
2195
  @c2.many_to_many :attributes, :class => @c1
2307
2196
 
2308
2197
  n = @c2.new(:id => 1234)
2309
- MODEL_DB.reset
2310
2198
  n.associations[:attributes] = 42
2311
2199
  n.attributes.should == 42
2312
2200
  MODEL_DB.sqls.should == []
@@ -2316,18 +2204,16 @@ describe Sequel::Model, "many_to_many" do
2316
2204
  @c2.many_to_many :attributes, :class => @c1
2317
2205
 
2318
2206
  n = @c2.new(:id => 1234)
2319
- MODEL_DB.reset
2320
2207
  n.associations[:attributes] = 42
2321
2208
  n.attributes(true).should_not == 42
2322
- MODEL_DB.sqls.length.should == 1
2209
+ MODEL_DB.sqls.should == ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))"]
2323
2210
  end
2324
2211
 
2325
2212
  it "should add item to cached instance variable if it exists when calling add_" do
2326
2213
  @c2.many_to_many :attributes, :class => @c1
2327
2214
 
2328
2215
  n = @c2.new(:id => 1234)
2329
- att = @c1.new(:id => 345)
2330
- MODEL_DB.reset
2216
+ att = @c1.load(:id => 345)
2331
2217
  a = []
2332
2218
  n.associations[:attributes] = a
2333
2219
  n.add_attribute(att)
@@ -2339,7 +2225,7 @@ describe Sequel::Model, "many_to_many" do
2339
2225
  @c1.many_to_many :nodes, :class => @c2
2340
2226
 
2341
2227
  n = @c2.new(:id => 1234)
2342
- att = @c1.new(:id => 345)
2228
+ att = @c1.load(:id => 345)
2343
2229
  att.associations[:nodes] = []
2344
2230
  n.add_attribute(att)
2345
2231
  att.nodes.should == [n]
@@ -2349,8 +2235,7 @@ describe Sequel::Model, "many_to_many" do
2349
2235
  @c2.many_to_many :attributes, :class => @c1
2350
2236
 
2351
2237
  n = @c2.new(:id => 1234)
2352
- att = @c1.new(:id => 345)
2353
- MODEL_DB.reset
2238
+ att = @c1.load(:id => 345)
2354
2239
  a = [att]
2355
2240
  n.associations[:attributes] = a
2356
2241
  n.remove_attribute(att)
@@ -2397,13 +2282,13 @@ describe Sequel::Model, "many_to_many" do
2397
2282
  it "should have an remove_all_ method that removes all associations" do
2398
2283
  @c2.many_to_many :attributes, :class => @c1
2399
2284
  @c2.new(:id => 1234).remove_all_attributes
2400
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 1234)'
2285
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE (node_id = 1234)']
2401
2286
  end
2402
2287
 
2403
2288
  it "should have the remove_all_ method respect the :left_primary_key option" do
2404
2289
  @c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx
2405
2290
  @c2.new(:id => 1234, :xxx=>5).remove_all_attributes
2406
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 5)'
2291
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE (node_id = 5)']
2407
2292
  end
2408
2293
 
2409
2294
  it "should have the remove_all_ method respect composite keys" do
@@ -2421,10 +2306,9 @@ describe Sequel::Model, "many_to_many" do
2421
2306
 
2422
2307
  it "remove_all should return the array of previously associated items if the cached instance variable exists" do
2423
2308
  @c2.many_to_many :attributes, :class => @c1
2424
- attrib = @c1.new(:id=>3)
2425
- node = @c2.new(:id => 1234)
2426
- d = @c1.dataset
2427
- def d.fetch_rows(s); end
2309
+ attrib = @c1.load(:id=>3)
2310
+ node = @c2.load(:id => 1234)
2311
+ @c1.dataset._fetch = []
2428
2312
  node.attributes.should == []
2429
2313
  node.add_attribute(attrib)
2430
2314
  node.associations[:attributes].should == [attrib]
@@ -2439,11 +2323,9 @@ describe Sequel::Model, "many_to_many" do
2439
2323
  it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
2440
2324
  @c2.many_to_many :attributes, :class => @c1
2441
2325
  @c1.many_to_many :nodes, :class => @c2
2442
- d = @c1.dataset
2443
- def d.fetch_rows(s); end
2444
- d = @c2.dataset
2445
- def d.fetch_rows(s); end
2446
- attrib = @c1.new(:id=>3)
2326
+ @c1.dataset._fetch = []
2327
+ @c2.dataset._fetch = []
2328
+ attrib = @c1.load(:id=>3)
2447
2329
  node = @c2.new(:id => 1234)
2448
2330
  node.attributes.should == []
2449
2331
  attrib.nodes.should == []
@@ -2459,8 +2341,9 @@ describe Sequel::Model, "many_to_many" do
2459
2341
  o.add_attribute(@c1.load(:id=>44))
2460
2342
  o.remove_attribute(@c1.load(:id=>45))
2461
2343
  o.remove_all_attributes
2462
- MODEL_DB.sqls.first =~ /INSERT INTO attributes_nodes \((node_id|attribute_id|x), (node_id|attribute_id|x), (node_id|attribute_id|x)\) VALUES \((1234|123|44), (1234|123|44), (1234|123|44)\)/
2463
- MODEL_DB.sqls[1..-1].should == ["DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234) AND (attribute_id = 45))",
2344
+ sqls = MODEL_DB.sqls
2345
+ sqls.shift =~ /INSERT INTO attributes_nodes \((node_id|attribute_id|x), (node_id|attribute_id|x), (node_id|attribute_id|x)\) VALUES \((1234|123|44), (1234|123|44), (1234|123|44)\)/
2346
+ sqls.should == ["DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234) AND (attribute_id = 45))",
2464
2347
  "DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234))"]
2465
2348
  end
2466
2349
 
@@ -2543,18 +2426,18 @@ describe Sequel::Model, "many_to_many" do
2543
2426
  h = []
2544
2427
  @c2.many_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
2545
2428
  @c2.class_eval do
2546
- class_variable_set(:@@blah, h)
2429
+ self::Foo = h
2547
2430
  def _add_attribute(v)
2548
- self.class.send(:class_variable_get, :@@blah) << 4
2431
+ model::Foo << 4
2549
2432
  end
2550
2433
  def _remove_attribute(v)
2551
- self.class.send(:class_variable_get, :@@blah) << 5
2434
+ model::Foo << 5
2552
2435
  end
2553
2436
  def blah(x)
2554
- self.class.send(:class_variable_get, :@@blah) << x.pk
2437
+ model::Foo << x.pk
2555
2438
  end
2556
2439
  def blahr(x)
2557
- self.class.send(:class_variable_get, :@@blah) << 6
2440
+ model::Foo << 6
2558
2441
  end
2559
2442
  end
2560
2443
  p = @c2.load(:id=>10)
@@ -2570,17 +2453,12 @@ describe Sequel::Model, "many_to_many" do
2570
2453
  h = []
2571
2454
  @c2.many_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
2572
2455
  @c2.class_eval do
2573
- class_variable_set(:@@blah, h)
2456
+ self::Foo = h
2574
2457
  def al(v)
2575
- v.each{|x| self.class.send(:class_variable_get, :@@blah) << x.pk}
2576
- end
2577
- end
2578
- @c1.class_eval do
2579
- def @dataset.fetch_rows(sql)
2580
- yield({:id=>20})
2581
- yield({:id=>30})
2458
+ v.each{|x| model::Foo << x.pk}
2582
2459
  end
2583
2460
  end
2461
+ @c1.dataset._fetch = [{:id=>20}, {:id=>30}]
2584
2462
  p = @c2.load(:id=>10, :parent_id=>20)
2585
2463
  attributes = p.attributes
2586
2464
  h.should == [[10, [20, 30]], 20, 30]
@@ -2623,14 +2501,7 @@ describe Sequel::Model, "many_to_many" do
2623
2501
 
2624
2502
  it "should support a :uniq option that removes duplicates from the association" do
2625
2503
  @c2.many_to_many :attributes, :class => @c1, :uniq=>true
2626
- @c1.class_eval do
2627
- def @dataset.fetch_rows(sql)
2628
- yield({:id=>20})
2629
- yield({:id=>30})
2630
- yield({:id=>20})
2631
- yield({:id=>30})
2632
- end
2633
- end
2504
+ @c1.dataset._fetch = [{:id=>20}, {:id=>30}, {:id=>20}, {:id=>30}]
2634
2505
  @c2.load(:id=>10, :parent_id=>20).attributes.should == [@c1.load(:id=>20), @c1.load(:id=>30)]
2635
2506
  end
2636
2507
 
@@ -2651,76 +2522,611 @@ describe Sequel::Model, "many_to_many" do
2651
2522
  @c2.many_to_many :attributes, :class => @c1 do |ds|
2652
2523
  ds.filter(:join_table_att=>3)
2653
2524
  end
2654
- @c1.dataset.instance_eval do
2655
- def fetch_rows(sql, &block)
2656
- db.sqls << sql
2657
- yield(:id=>2)
2658
- end
2659
- end
2525
+ @c1.dataset._fetch = {:id=>2}
2660
2526
  @c2.load(:id=>1).remove_attribute(2)
2661
2527
  MODEL_DB.sqls.should == ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1)) WHERE ((join_table_att = 3) AND (attributes.id = 2)) LIMIT 1",
2662
2528
  "DELETE FROM attributes_nodes WHERE ((node_id = 1) AND (attribute_id = 2))"]
2663
2529
  end
2664
2530
  end
2665
2531
 
2666
- describe Sequel::Model, " association reflection methods" do
2532
+ describe "Filtering by associations" do
2667
2533
  before do
2668
- MODEL_DB.reset
2669
- @c1 = Class.new(Sequel::Model(:nodes)) do
2670
- def self.name; 'Node'; end
2671
- def self.to_s; 'Node'; end
2534
+ @Album = Class.new(Sequel::Model(:albums))
2535
+ artist = @Artist = Class.new(Sequel::Model(:artists))
2536
+ tag = @Tag = Class.new(Sequel::Model(:tags))
2537
+ track = @Track = Class.new(Sequel::Model(:tracks))
2538
+ album_info = @AlbumInfo = Class.new(Sequel::Model(:album_infos))
2539
+ @Artist.columns :id, :id1, :id2
2540
+ @Tag.columns :id, :tid1, :tid2
2541
+ @Track.columns :id, :album_id, :album_id1, :album_id2
2542
+ @AlbumInfo.columns :id, :album_id, :album_id1, :album_id2
2543
+ @Album.class_eval do
2544
+ columns :id, :id1, :id2, :artist_id, :artist_id1, :artist_id2
2545
+ many_to_one :artist, :class=>artist
2546
+ one_to_many :tracks, :class=>track, :key=>:album_id
2547
+ one_to_one :album_info, :class=>album_info, :key=>:album_id
2548
+ many_to_many :tags, :class=>tag, :left_key=>:album_id, :join_table=>:albums_tags
2549
+
2550
+ many_to_one :cartist, :class=>artist, :key=>[:artist_id1, :artist_id2], :primary_key=>[:id1, :id2]
2551
+ one_to_many :ctracks, :class=>track, :key=>[:album_id1, :album_id2], :primary_key=>[:id1, :id2]
2552
+ one_to_one :calbum_info, :class=>album_info, :key=>[:album_id1, :album_id2], :primary_key=>[:id1, :id2]
2553
+ many_to_many :ctags, :class=>tag, :left_key=>[:album_id1, :album_id2], :left_primary_key=>[:id1, :id2], :right_key=>[:tag_id1, :tag_id2], :right_primary_key=>[:tid1, :tid2], :join_table=>:albums_tags
2672
2554
  end
2673
2555
  end
2556
+
2557
+ it "should be able to filter on many_to_one associations" do
2558
+ @Album.filter(:artist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (albums.artist_id = 3)'
2559
+ end
2560
+
2561
+ it "should be able to filter on one_to_many associations" do
2562
+ @Album.filter(:tracks=>@Track.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE (albums.id = 3)'
2563
+ end
2564
+
2565
+ it "should be able to filter on one_to_one associations" do
2566
+ @Album.filter(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE (albums.id = 3)'
2567
+ end
2568
+
2569
+ it "should be able to filter on many_to_many associations" do
2570
+ @Album.filter(:tags=>@Tag.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id = 3) AND (albums_tags.album_id IS NOT NULL))))'
2571
+ end
2572
+
2573
+ it "should be able to filter on many_to_one associations with composite keys" do
2574
+ @Album.filter(:cartist=>@Artist.load(:id1=>3, :id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1 = 3) AND (albums.artist_id2 = 4))'
2575
+ end
2576
+
2577
+ it "should be able to filter on one_to_many associations with composite keys" do
2578
+ @Album.filter(:ctracks=>@Track.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1 = 3) AND (albums.id2 = 4))'
2579
+ end
2580
+
2581
+ it "should be able to filter on one_to_one associations with composite keys" do
2582
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1 = 3) AND (albums.id2 = 4))'
2583
+ end
2584
+
2585
+ it "should be able to filter on many_to_many associations with composite keys" do
2586
+ @Album.filter(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE ((albums_tags.tag_id1 = 3) AND (albums_tags.tag_id2 = 4) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
2587
+ end
2588
+
2589
+ it "should work inside a complex filter" do
2590
+ artist = @Artist.load(:id=>3)
2591
+ @Album.filter{foo & {:artist=>artist}}.sql.should == 'SELECT * FROM albums WHERE (foo AND (albums.artist_id = 3))'
2592
+ track = @Track.load(:album_id=>4)
2593
+ @Album.filter{foo & [[:artist, artist], [:tracks, track]]}.sql.should == 'SELECT * FROM albums WHERE (foo AND (albums.artist_id = 3) AND (albums.id = 4))'
2594
+ end
2595
+
2596
+ it "should raise for an invalid association name" do
2597
+ proc{@Album.filter(:foo=>@Artist.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2598
+ end
2599
+
2600
+ it "should raise for an invalid association type" do
2601
+ @Album.many_to_many :iatags, :clone=>:tags
2602
+ @Album.association_reflection(:iatags)[:type] = :foo
2603
+ proc{@Album.filter(:iatags=>@Tag.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2604
+ end
2605
+
2606
+ it "should raise for an invalid associated object class " do
2607
+ proc{@Album.filter(:tags=>@Artist.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2608
+ end
2609
+
2610
+ it "should raise for an invalid associated object class when multiple objects are used" do
2611
+ proc{@Album.filter(:tags=>[@Tag.load(:id=>3), @Artist.load(:id=>3)]).sql}.should raise_error(Sequel::Error)
2612
+ end
2613
+
2614
+ it "should correctly handle case when a multiple value association is used" do
2615
+ proc{@Album.filter(:tags=>[@Tag.load(:id=>3), @Artist.load(:id=>3)]).sql}.should raise_error(Sequel::Error)
2616
+ end
2617
+
2618
+ it "should not affect non-association IN/NOT IN filtering with an empty array" do
2619
+ @Album.filter(:tag_id=>[]).sql.should == 'SELECT * FROM albums WHERE (tag_id != tag_id)'
2620
+ @Album.exclude(:tag_id=>[]).sql.should == 'SELECT * FROM albums WHERE (tag_id = tag_id)'
2621
+ end
2622
+
2623
+ it "should work correctly in subclasses" do
2624
+ c = Class.new(@Album)
2625
+ c.many_to_one :sartist, :class=>@Artist
2626
+ c.filter(:sartist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (albums.sartist_id = 3)'
2627
+ end
2628
+
2629
+ it "should be able to exclude on many_to_one associations" do
2630
+ @Album.exclude(:artist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id != 3) OR (albums.artist_id IS NULL))'
2631
+ end
2632
+
2633
+ it "should be able to exclude on one_to_many associations" do
2634
+ @Album.exclude(:tracks=>@Track.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE ((albums.id != 3) OR (albums.id IS NULL))'
2635
+ end
2636
+
2637
+ it "should be able to exclude on one_to_one associations" do
2638
+ @Album.exclude(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE ((albums.id != 3) OR (albums.id IS NULL))'
2639
+ end
2640
+
2641
+ it "should be able to exclude on many_to_many associations" do
2642
+ @Album.exclude(:tags=>@Tag.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id = 3) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2643
+ end
2644
+
2645
+ it "should be able to exclude on many_to_one associations with composite keys" do
2646
+ @Album.exclude(:cartist=>@Artist.load(:id1=>3, :id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1 != 3) OR (albums.artist_id2 != 4) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
2647
+ end
2648
+
2649
+ it "should be able to exclude on one_to_many associations with composite keys" do
2650
+ @Album.exclude(:ctracks=>@Track.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1 != 3) OR (albums.id2 != 4) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2651
+ end
2652
+
2653
+ it "should be able to exclude on one_to_one associations with composite keys" do
2654
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1 != 3) OR (albums.id2 != 4) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2655
+ end
2656
+
2657
+ it "should be able to exclude on many_to_many associations with composite keys" do
2658
+ @Album.exclude(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE ((albums_tags.tag_id1 = 3) AND (albums_tags.tag_id2 = 4) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2659
+ end
2660
+
2661
+ it "should be able to filter on multiple many_to_one associations" do
2662
+ @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE (albums.artist_id IN (3, 4))'
2663
+ end
2664
+
2665
+ it "should be able to filter on multiple one_to_many associations" do
2666
+ @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (3, 4))'
2667
+ end
2668
+
2669
+ it "should be able to filter on multiple one_to_one associations" do
2670
+ @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (3, 4))'
2671
+ end
2672
+
2673
+ it "should be able to filter on multiple many_to_many associations" do
2674
+ @Album.filter(:tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3, 4)) AND (albums_tags.album_id IS NOT NULL))))'
2675
+ end
2676
+
2677
+ it "should be able to filter on multiple many_to_one associations with composite keys" do
2678
+ @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>5, :id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN ((3, 4), (5, 6)))'
2679
+ end
2680
+
2681
+ it "should be able to filter on multiple one_to_many associations with composite keys" do
2682
+ @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4), (5, 6)))'
2683
+ end
2684
+
2685
+ it "should be able to filter on multiple one_to_one associations with composite keys" do
2686
+ @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4), (5, 6)))'
2687
+ end
2688
+
2689
+ it "should be able to filter on multiple many_to_many associations with composite keys" do
2690
+ @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5, :tid2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4), (5, 6))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
2691
+ end
2692
+
2693
+ it "should be able to exclude on multiple many_to_one associations" do
2694
+ @Album.exclude(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id NOT IN (3, 4)) OR (albums.artist_id IS NULL))'
2695
+ end
2696
+
2697
+ it "should be able to exclude on multiple one_to_many associations" do
2698
+ @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (3, 4)) OR (albums.id IS NULL))'
2699
+ end
2700
+
2701
+ it "should be able to exclude on multiple one_to_one associations" do
2702
+ @Album.exclude(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (3, 4)) OR (albums.id IS NULL))'
2703
+ end
2704
+
2705
+ it "should be able to exclude on multiple many_to_many associations" do
2706
+ @Album.exclude(:tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3, 4)) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2707
+ end
2708
+
2709
+ it "should be able to exclude on multiple many_to_one associations with composite keys" do
2710
+ @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>5, :id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN ((3, 4), (5, 6))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
2711
+ end
2712
+
2713
+ it "should be able to exclude on multiple one_to_many associations with composite keys" do
2714
+ @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4), (5, 6))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2715
+ end
2716
+
2717
+ it "should be able to exclude on multiple one_to_one associations with composite keys" do
2718
+ @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4), (5, 6))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2719
+ end
2720
+
2721
+ it "should be able to exclude on multiple many_to_many associations with composite keys" do
2722
+ @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5, :tid2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4), (5, 6))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2723
+ end
2724
+
2725
+ it "should be able to handle NULL values when filtering many_to_one associations" do
2726
+ @Album.filter(:artist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2727
+ end
2728
+
2729
+ it "should be able to handle NULL values when filtering one_to_many associations" do
2730
+ @Album.filter(:tracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2731
+ end
2732
+
2733
+ it "should be able to handle NULL values when filtering one_to_one associations" do
2734
+ @Album.filter(:album_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2735
+ end
2736
+
2737
+ it "should be able to handle NULL values when filtering many_to_many associations" do
2738
+ @Album.filter(:tags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2739
+ end
2740
+
2741
+ it "should be able to handle filtering with NULL values for many_to_one associations with composite keys" do
2742
+ @Album.filter(:cartist=>@Artist.load(:id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2743
+ @Album.filter(:cartist=>@Artist.load(:id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2744
+ @Album.filter(:cartist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2745
+ end
2746
+
2747
+ it "should be able to filter with NULL values for one_to_many associations with composite keys" do
2748
+ @Album.filter(:ctracks=>@Track.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2749
+ @Album.filter(:ctracks=>@Track.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2750
+ @Album.filter(:ctracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2751
+ end
2752
+
2753
+ it "should be able to filter with NULL values for one_to_one associations with composite keys" do
2754
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2755
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2756
+ @Album.filter(:calbum_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2757
+ end
2758
+
2759
+ it "should be able to filter with NULL values for many_to_many associations with composite keys" do
2760
+ @Album.filter(:ctags=>@Tag.load(:tid1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2761
+ @Album.filter(:ctags=>@Tag.load(:tid2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2762
+ @Album.filter(:ctags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2763
+ end
2764
+
2765
+ it "should be able to handle NULL values when excluding many_to_one associations" do
2766
+ @Album.exclude(:artist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2767
+ end
2768
+
2769
+ it "should be able to handle NULL values when excluding one_to_many associations" do
2770
+ @Album.exclude(:tracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2771
+ end
2772
+
2773
+ it "should be able to handle NULL values when excluding one_to_one associations" do
2774
+ @Album.exclude(:album_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2775
+ end
2776
+
2777
+ it "should be able to handle NULL values when excluding many_to_many associations" do
2778
+ @Album.exclude(:tags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2779
+ end
2780
+
2781
+ it "should be able to handle excluding with NULL values for many_to_one associations with composite keys" do
2782
+ @Album.exclude(:cartist=>@Artist.load(:id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2783
+ @Album.exclude(:cartist=>@Artist.load(:id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2784
+ @Album.exclude(:cartist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2785
+ end
2786
+
2787
+ it "should be able to excluding with NULL values for one_to_many associations with composite keys" do
2788
+ @Album.exclude(:ctracks=>@Track.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2789
+ @Album.exclude(:ctracks=>@Track.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2790
+ @Album.exclude(:ctracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2791
+ end
2792
+
2793
+ it "should be able to excluding with NULL values for one_to_one associations with composite keys" do
2794
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2795
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2796
+ @Album.exclude(:calbum_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2797
+ end
2798
+
2799
+ it "should be able to excluding with NULL values for many_to_many associations with composite keys" do
2800
+ @Album.exclude(:ctags=>@Tag.load(:tid1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2801
+ @Album.exclude(:ctags=>@Tag.load(:tid2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2802
+ @Album.exclude(:ctags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2803
+ end
2804
+
2805
+ it "should be able to handle NULL values when filtering multiple many_to_one associations" do
2806
+ @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE (albums.artist_id IN (3))'
2807
+ @Album.filter(:artist=>[@Artist.new, @Artist.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2808
+ end
2809
+
2810
+ it "should be able to handle NULL values when filtering multiple one_to_many associations" do
2811
+ @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (3))'
2812
+ @Album.filter(:tracks=>[@Track.new, @Track.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2813
+ end
2814
+
2815
+ it "should be able to handle NULL values when filtering multiple one_to_one associations" do
2816
+ @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (3))'
2817
+ @Album.filter(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2818
+ end
2819
+
2820
+ it "should be able to handle NULL values when filtering multiple many_to_many associations" do
2821
+ @Album.filter(:tags=>[@Tag.load(:id=>3), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3)) AND (albums_tags.album_id IS NOT NULL))))'
2822
+ @Album.filter(:tags=>[@Tag.new, @Tag.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2823
+ end
2824
+
2825
+ it "should be able to handle NULL values when filtering multiple many_to_one associations with composite keys" do
2826
+ @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>3)]).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN ((3, 4)))'
2827
+ @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN ((3, 4)))'
2828
+ end
2829
+
2830
+ it "should be able handle NULL values when filtering multiple one_to_many associations with composite keys" do
2831
+ @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>3)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
2832
+ @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
2833
+ end
2834
+
2835
+ it "should be able to handle NULL values when filtering multiple one_to_one associations with composite keys" do
2836
+ @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
2837
+ @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
2838
+ end
2839
+
2840
+ it "should be able to handle NULL values when filtering multiple many_to_many associations with composite keys" do
2841
+ @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
2842
+ @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
2843
+ end
2844
+
2845
+ it "should be able to handle NULL values when excluding multiple many_to_one associations" do
2846
+ @Album.exclude(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id NOT IN (3)) OR (albums.artist_id IS NULL))'
2847
+ @Album.exclude(:artist=>[@Artist.new, @Artist.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2848
+ end
2849
+
2850
+ it "should be able to handle NULL values when excluding multiple one_to_many associations" do
2851
+ @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (3)) OR (albums.id IS NULL))'
2852
+ @Album.exclude(:tracks=>[@Track.new, @Track.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2853
+ end
2854
+
2855
+ it "should be able to handle NULL values when excluding multiple one_to_one associations" do
2856
+ @Album.exclude(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (3)) OR (albums.id IS NULL))'
2857
+ @Album.exclude(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2858
+ end
2859
+
2860
+ it "should be able to handle NULL values when excluding multiple many_to_many associations" do
2861
+ @Album.exclude(:tags=>[@Tag.load(:id=>3), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3)) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2862
+ @Album.exclude(:tags=>[@Tag.new, @Tag.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2863
+ end
2864
+
2865
+ it "should be able to handle NULL values when excluding multiple many_to_one associations with composite keys" do
2866
+ @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>3)]).sql.should == 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN ((3, 4))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
2867
+ @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN ((3, 4))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
2868
+ end
2869
+
2870
+ it "should be able handle NULL values when excluding multiple one_to_many associations with composite keys" do
2871
+ @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>3)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2872
+ @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2873
+ end
2874
+
2875
+ it "should be able to handle NULL values when excluding multiple one_to_one associations with composite keys" do
2876
+ @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2877
+ @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2878
+ end
2879
+
2880
+ it "should be able to handle NULL values when excluding multiple many_to_many associations with composite keys" do
2881
+ @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2882
+ @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2883
+ end
2884
+
2885
+ it "should be able to filter on many_to_one association datasets" do
2886
+ @Album.filter(:artist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE ((x = 1) AND (artists.id IS NOT NULL))))'
2887
+ end
2888
+
2889
+ it "should be able to filter on one_to_many association datasets" do
2890
+ @Album.filter(:tracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((x = 1) AND (tracks.album_id IS NOT NULL))))'
2891
+ end
2892
+
2893
+ it "should be able to filter on one_to_one association datasets" do
2894
+ @Album.filter(:album_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT album_infos.album_id FROM album_infos WHERE ((x = 1) AND (album_infos.album_id IS NOT NULL))))'
2895
+ end
2896
+
2897
+ it "should be able to filter on many_to_many association datasets" do
2898
+ @Album.filter(:tags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_tags.album_id IS NOT NULL))))'
2899
+ end
2900
+
2901
+ it "should be able to filter on many_to_one association datasets with composite keys" do
2902
+ @Album.filter(:cartist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((x = 1) AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL))))'
2903
+ end
2904
+
2905
+ it "should be able to filter on one_to_many association datasets with composite keys" do
2906
+ @Album.filter(:ctracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((x = 1) AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL))))'
2907
+ end
2908
+
2909
+ it "should be able to filter on one_to_one association datasets with composite keys" do
2910
+ @Album.filter(:calbum_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((x = 1) AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL))))'
2911
+ end
2912
+
2913
+ it "should be able to filter on many_to_many association datasets with composite keys" do
2914
+ @Album.filter(:ctags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN (SELECT tags.tid1, tags.tid2 FROM tags WHERE ((x = 1) AND (tags.tid1 IS NOT NULL) AND (tags.tid2 IS NOT NULL)))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
2915
+ end
2916
+
2917
+ it "should be able to exclude on many_to_one association datasets" do
2918
+ @Album.exclude(:artist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id NOT IN (SELECT artists.id FROM artists WHERE ((x = 1) AND (artists.id IS NOT NULL)))) OR (albums.artist_id IS NULL))'
2919
+ end
2920
+
2921
+ it "should be able to exclude on one_to_many association datasets" do
2922
+ @Album.exclude(:tracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT tracks.album_id FROM tracks WHERE ((x = 1) AND (tracks.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2923
+ end
2924
+
2925
+ it "should be able to exclude on one_to_one association datasets" do
2926
+ @Album.exclude(:album_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT album_infos.album_id FROM album_infos WHERE ((x = 1) AND (album_infos.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2927
+ end
2928
+
2929
+ it "should be able to exclude on many_to_many association datasets" do
2930
+ @Album.exclude(:tags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2931
+ end
2932
+
2933
+ it "should be able to exclude on many_to_one association datasets with composite keys" do
2934
+ @Album.exclude(:cartist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((x = 1) AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL)))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
2935
+ end
2936
+
2937
+ it "should be able to exclude on one_to_many association datasets with composite keys" do
2938
+ @Album.exclude(:ctracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((x = 1) AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2939
+ end
2940
+
2941
+ it "should be able to exclude on one_to_one association datasets with composite keys" do
2942
+ @Album.exclude(:calbum_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((x = 1) AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2943
+ end
2944
+
2945
+ it "should be able to exclude on many_to_many association datasets with composite keys" do
2946
+ @Album.exclude(:ctags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN (SELECT tags.tid1, tags.tid2 FROM tags WHERE ((x = 1) AND (tags.tid1 IS NOT NULL) AND (tags.tid2 IS NOT NULL)))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2947
+ end
2948
+
2949
+ it "should do a regular IN query if the dataset for a different model is used" do
2950
+ @Album.filter(:artist=>@Album.select(:x)).sql.should == 'SELECT * FROM albums WHERE (artist IN (SELECT x FROM albums))'
2951
+ end
2952
+
2953
+ it "should do a regular IN query if a non-model dataset is used" do
2954
+ @Album.filter(:artist=>@Album.db.from(:albums).select(:x)).sql.should == 'SELECT * FROM albums WHERE (artist IN (SELECT x FROM albums))'
2955
+ end
2956
+ end
2957
+
2958
+ describe "Sequel::Model Associations with clashing column names" do
2959
+ before do
2960
+ @db = Sequel.mock(:fetch=>{:id=>1, :object_id=>2})
2961
+ @Foo = Class.new(Sequel::Model(@db[:foos]))
2962
+ @Bar = Class.new(Sequel::Model(@db[:bars]))
2963
+ @Foo.columns :id, :object_id
2964
+ @Bar.columns :id, :object_id
2965
+ @Foo.def_column_alias(:obj_id, :object_id)
2966
+ @Bar.def_column_alias(:obj_id, :object_id)
2967
+ @Foo.one_to_many :bars, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :eager_loader_key=>:object_id, :class=>@Bar
2968
+ @Foo.one_to_one :bar, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :eager_loader_key=>:object_id, :class=>@Bar
2969
+ @Bar.many_to_one :foo, :key=>:obj_id, :key_column=>:object_id, :primary_key=>:object_id, :primary_key_method=>:obj_id, :class=>@Foo
2970
+ @Foo.many_to_many :mtmbars, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:foo_id, :right_key=>:object_id, :eager_loader_key=>:object_id, :class=>@Bar
2971
+ @Bar.many_to_many :mtmfoos, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:object_id, :right_key=>:foo_id, :eager_loader_key=>:object_id, :class=>@Foo
2972
+ @foo = @Foo.load(:id=>1, :object_id=>2)
2973
+ @bar = @Bar.load(:id=>1, :object_id=>2)
2974
+ @db.sqls
2975
+ end
2976
+
2977
+ it "should have working regular association methods" do
2978
+ @Bar.first.foo.should == @foo
2979
+ @Foo.first.bars.should == [@bar]
2980
+ @Foo.first.bar.should == @bar
2981
+ @Foo.first.mtmbars.should == [@bar]
2982
+ @Bar.first.mtmfoos.should == [@foo]
2983
+ end
2984
+
2985
+ it "should have working eager loading methods" do
2986
+ @Bar.eager(:foo).all.map{|o| [o, o.foo]}.should == [[@bar, @foo]]
2987
+ @Foo.eager(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
2988
+ @Foo.eager(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
2989
+ @db.fetch = [[{:id=>1, :object_id=>2}], [{:id=>1, :object_id=>2, :x_foreign_key_x=>2}]]
2990
+ @Foo.eager(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
2991
+ @db.fetch = [[{:id=>1, :object_id=>2}], [{:id=>1, :object_id=>2, :x_foreign_key_x=>2}]]
2992
+ @Bar.eager(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
2993
+ end
2994
+
2995
+ it "should have working eager graphing methods" do
2996
+ @db.fetch = {:id=>1, :object_id=>2, :foo_id=>1, :foo_object_id=>2}
2997
+ @Bar.eager_graph(:foo).all.map{|o| [o, o.foo]}.should == [[@bar, @foo]]
2998
+ @db.fetch = {:id=>1, :object_id=>2, :bars_id=>1, :bars_object_id=>2}
2999
+ @Foo.eager_graph(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
3000
+ @db.fetch = {:id=>1, :object_id=>2, :bar_id=>1, :bar_object_id=>2}
3001
+ @Foo.eager_graph(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
3002
+ @db.fetch = {:id=>1, :object_id=>2, :mtmfoos_id=>1, :mtmfoos_object_id=>2}
3003
+ @Bar.eager_graph(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
3004
+ @db.fetch = {:id=>1, :object_id=>2, :mtmbars_id=>1, :mtmbars_object_id=>2}
3005
+ @Foo.eager_graph(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
3006
+ end
3007
+
3008
+ it "should have working modification methods" do
3009
+ b = @Bar.load(:id=>2, :object_id=>3)
3010
+ f = @Foo.load(:id=>2, :object_id=>3)
3011
+ @db.numrows = 1
3012
+
3013
+ @bar.foo = f
3014
+ @bar.obj_id.should == 3
3015
+ @foo.bar = @bar
3016
+ @bar.obj_id.should == 2
3017
+
3018
+ @foo.add_bar(b)
3019
+ @db.fetch = [[{:id=>1, :object_id=>2}, {:id=>2, :object_id=>2}], [{:id=>1, :object_id=>2}]]
3020
+ @foo.bars.should == [@bar, b]
3021
+ @foo.remove_bar(b)
3022
+ @foo.bars.should == [@bar]
3023
+ @foo.remove_all_bars
3024
+ @foo.bars.should == []
3025
+
3026
+ @db.fetch = [[{:id=>1, :object_id=>2}], [], [{:id=>2, :object_id=>2}]]
3027
+ @bar = @Bar.load(:id=>1, :object_id=>2)
3028
+ @foo.mtmbars.should == [@bar]
3029
+ @foo.remove_all_mtmbars
3030
+ @foo.mtmbars.should == []
3031
+ @foo.add_mtmbar(b)
3032
+ @foo.mtmbars.should == [b]
3033
+ @foo.remove_mtmbar(b)
3034
+ @foo.mtmbars.should == []
3035
+
3036
+ @db.fetch = [[{:id=>2, :object_id=>3}], [], [{:id=>2, :object_id=>3}]]
3037
+ @bar.add_mtmfoo(f)
3038
+ @bar.mtmfoos.should == [f]
3039
+ @bar.remove_all_mtmfoos
3040
+ @bar.mtmfoos.should == []
3041
+ @bar.add_mtmfoo(f)
3042
+ @bar.mtmfoos.should == [f]
3043
+ @bar.remove_mtmfoo(f)
3044
+ @bar.mtmfoos.should == []
3045
+ end
3046
+ end
3047
+
3048
+ describe "Model#pk_or_nil" do
3049
+ before do
3050
+ @m = Class.new(Sequel::Model)
3051
+ @m.columns :id, :x, :y
3052
+ end
2674
3053
 
2675
- it "#all_association_reflections should include all association reflection hashes" do
2676
- @c1.all_association_reflections.should == []
3054
+ it "should be default return the value of the :id column" do
3055
+ m = @m.load(:id => 111, :x => 2, :y => 3)
3056
+ m.pk_or_nil.should == 111
3057
+ end
3058
+
3059
+ it "should be return the primary key value for custom primary key" do
3060
+ @m.set_primary_key :x
3061
+ m = @m.load(:id => 111, :x => 2, :y => 3)
3062
+ m.pk_or_nil.should == 2
3063
+ end
2677
3064
 
2678
- @c1.associate :many_to_one, :parent, :class => @c1
2679
- @c1.all_association_reflections.collect{|v| v[:name]}.should == [:parent]
2680
- @c1.all_association_reflections.collect{|v| v[:type]}.should == [:many_to_one]
2681
- @c1.all_association_reflections.collect{|v| v[:class]}.should == [@c1]
3065
+ it "should be return the primary key value for composite primary key" do
3066
+ @m.set_primary_key [:y, :x]
3067
+ m = @m.load(:id => 111, :x => 2, :y => 3)
3068
+ m.pk_or_nil.should == [3, 2]
3069
+ end
3070
+
3071
+ it "should raise if no primary key" do
3072
+ @m.set_primary_key nil
3073
+ m = @m.new(:id => 111, :x => 2, :y => 3)
3074
+ m.pk_or_nil.should be_nil
3075
+
3076
+ @m.no_primary_key
3077
+ m = @m.new(:id => 111, :x => 2, :y => 3)
3078
+ m.pk_or_nil.should be_nil
3079
+ end
3080
+ end
2682
3081
 
2683
- @c1.associate :one_to_many, :children, :class => @c1
2684
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}
2685
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:name]}.should == [:children, :parent]
2686
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:type]}.should == [:one_to_many, :many_to_one]
2687
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:class]}.should == [@c1, @c1]
3082
+ describe Sequel::Model, "#refresh" do
3083
+ before do
3084
+ @c = Class.new(Sequel::Model(:items)) do
3085
+ unrestrict_primary_key
3086
+ columns :id, :x
3087
+ end
3088
+ MODEL_DB.reset
2688
3089
  end
2689
3090
 
2690
- it "#association_reflection should return nil for nonexistent association" do
2691
- @c1.association_reflection(:blah).should == nil
3091
+ specify "should remove cached associations" do
3092
+ @c.many_to_one :node, :class=>@c
3093
+ @m = @c.new(:id => 555)
3094
+ @m.associations[:node] = 15
3095
+ @m.reload
3096
+ @m.associations.should == {}
2692
3097
  end
3098
+ end
2693
3099
 
2694
- it "#association_reflection should return association reflection hash if association exists" do
2695
- @c1.associate :many_to_one, :parent, :class => @c1
2696
- @c1.association_reflection(:parent).should be_a_kind_of(Sequel::Model::Associations::AssociationReflection)
2697
- @c1.association_reflection(:parent)[:name].should == :parent
2698
- @c1.association_reflection(:parent)[:type].should == :many_to_one
2699
- @c1.association_reflection(:parent)[:class].should == @c1
3100
+ describe "Model#freeze" do
3101
+ before do
3102
+ class ::Album < Sequel::Model
3103
+ columns :id
3104
+ class B < Sequel::Model
3105
+ columns :id, :album_id
3106
+ many_to_one :album, :class=>Album
3107
+ end
3108
+ one_to_one :b, :key=>:album_id, :class=>B
3109
+ end
3110
+ @o = Album.load(:id=>1).freeze
3111
+ MODEL_DB.sqls
3112
+ end
3113
+ after do
3114
+ Object.send(:remove_const, :Album)
3115
+ end
2700
3116
 
2701
- @c1.associate :one_to_many, :children, :class => @c1
2702
- @c1.association_reflection(:children).should be_a_kind_of(Sequel::Model::Associations::AssociationReflection)
2703
- @c1.association_reflection(:children)[:name].should == :children
2704
- @c1.association_reflection(:children)[:type].should == :one_to_many
2705
- @c1.association_reflection(:children)[:class].should == @c1
3117
+ it "should freeze the object's associations" do
3118
+ @o.associations.frozen?.should be_true
2706
3119
  end
2707
3120
 
2708
- it "#associations should include all association names" do
2709
- @c1.associations.should == []
2710
- @c1.associate :many_to_one, :parent, :class => @c1
2711
- @c1.associations.should == [:parent]
2712
- @c1.associate :one_to_many, :children, :class => @c1
2713
- @c1.associations.sort_by{|x|x.to_s}.should == [:children, :parent]
3121
+ it "should not break associations getters" do
3122
+ Album::B.dataset._fetch = {:album_id=>1, :id=>2}
3123
+ @o.b.should == Album::B.load(:id=>2, :album_id=>1)
3124
+ @o.associations[:b].should be_nil
2714
3125
  end
2715
3126
 
2716
- it "association reflections should be copied upon subclasing" do
2717
- @c1.associate :many_to_one, :parent, :class => @c1
2718
- c = Class.new(@c1)
2719
- @c1.associations.should == [:parent]
2720
- c.associations.should == [:parent]
2721
- c.associate :many_to_one, :parent2, :class => @c1
2722
- @c1.associations.should == [:parent]
2723
- c.associations.sort_by{|x| x.to_s}.should == [:parent, :parent2]
2724
- c.instance_methods.map{|x| x.to_s}.should include('parent')
3127
+ it "should not break reciprocal associations" do
3128
+ b = Album::B.load(:id=>2, :album_id=>nil)
3129
+ b.album = @o
3130
+ @o.associations[:b].should be_nil
2725
3131
  end
2726
3132
  end