sequel 3.21.0 → 3.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -1,36 +1,23 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "Model#save server use" do
4
-
5
- before(:each) do
6
- @c = Class.new(Sequel::Model(:items)) do
7
- columns :id, :x, :y
8
- end
9
- @c.db = MockDatabase.new
10
- db2 = @db2 = MockDatabase.new
11
- @c.class_eval do
12
- define_method(:after_save) do
13
- model.db = db2
14
- ds = model.dataset
15
- def ds.fetch_rows(sql)
16
- yield @db.execute(sql, @opts[:server])
17
- end
18
- @this = nil
19
- end
20
- end
4
+ before do
5
+ @db = Sequel.mock(:autoid=>proc{|sql| 10}, :fetch=>{:x=>1, :id=>10}, :servers=>{:blah=>{}, :read_only=>{}})
6
+ @c = Class.new(Sequel::Model(@db[:items]))
7
+ @c.columns :id, :x, :y
8
+ @c.dataset.columns(:id, :x, :y)
9
+ @db.sqls
21
10
  end
22
11
 
23
12
  it "should use the :default server if the model doesn't have one already specified" do
24
- @c.db.should_receive(:execute).with("INSERT INTO items (x) VALUES (1)").and_return(10)
25
- @db2.should_receive(:execute).with('SELECT * FROM items WHERE (id = 10) LIMIT 1', :default).and_return(:x=>1, :id=>10)
26
- @c.new(:x=>1).save
13
+ @c.new(:x=>1).save.should == @c.load(:x=>1, :id=>10)
14
+ @db.sqls.should == ["INSERT INTO items (x) VALUES (1)", 'SELECT * FROM items WHERE (id = 10) LIMIT 1']
27
15
  end
28
16
 
29
17
  it "should use the model's server if the model has one already specified" do
30
18
  @c.dataset = @c.dataset.server(:blah)
31
- @c.db.should_receive(:execute).with("INSERT INTO items (x) VALUES (1)").and_return(10)
32
- @db2.should_receive(:execute).with('SELECT * FROM items WHERE (id = 10) LIMIT 1', :blah).and_return(:x=>1, :id=>10)
33
- @c.new(:x=>1).save
19
+ @c.new(:x=>1).save.should == @c.load(:x=>1, :id=>10)
20
+ @db.sqls.should == ["INSERT INTO items (x) VALUES (1) -- blah", 'SELECT * FROM items WHERE (id = 10) LIMIT 1 -- blah']
34
21
  end
35
22
  end
36
23
 
@@ -39,32 +26,37 @@ describe "Model#save" do
39
26
  @c = Class.new(Sequel::Model(:items)) do
40
27
  columns :id, :x, :y
41
28
  end
42
- @c.dataset.meta_def(:insert){|h| super(h); 1}
29
+ @c.instance_dataset.autoid = @c.dataset.autoid = 13
43
30
  MODEL_DB.reset
44
31
  end
45
32
 
46
33
  it "should insert a record for a new model instance" do
47
34
  o = @c.new(:x => 1)
48
35
  o.save
49
- MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)",
50
- "SELECT * FROM items WHERE (id = 1) LIMIT 1"]
36
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)", "SELECT * FROM items WHERE (id = 13) LIMIT 1"]
51
37
  end
52
38
 
53
39
  it "should use dataset's insert_select method if present" do
54
- ds = @c.dataset = @c.dataset.clone
40
+ ds = @c.instance_dataset
41
+ ds._fetch = {:y=>2}
42
+ def ds.supports_insert_select?() true end
55
43
  def ds.insert_select(hash)
56
- execute("INSERT INTO items (y) VALUES (2)")
57
- {:y=>2}
44
+ execute("INSERT INTO items (y) VALUES (2) RETURNING *"){|r| return r}
58
45
  end
59
46
  o = @c.new(:x => 1)
60
47
  o.save
61
48
 
62
49
  o.values.should == {:y=>2}
63
- MODEL_DB.sqls.should == ["INSERT INTO items (y) VALUES (2)"]
50
+ MODEL_DB.sqls.should == ["INSERT INTO items (y) VALUES (2) RETURNING *"]
51
+ end
52
+
53
+ it "should not use dataset's insert_select method if specific columns are selected" do
54
+ ds = @c.dataset = @c.dataset.select(:y)
55
+ ds.should_not_receive(:insert_select)
56
+ @c.new(:x => 1).save
64
57
  end
65
58
 
66
59
  it "should use value returned by insert as the primary key and refresh the object" do
67
- @c.dataset.meta_def(:insert){|h| super(h); 13}
68
60
  o = @c.new(:x => 11)
69
61
  o.save
70
62
  MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (11)",
@@ -72,14 +64,12 @@ describe "Model#save" do
72
64
  end
73
65
 
74
66
  it "should allow you to skip refreshing by overridding _save_refresh" do
75
- @c.dataset.meta_def(:insert){|h| super(h); 13}
76
67
  @c.send(:define_method, :_save_refresh){}
77
68
  @c.create(:x => 11)
78
69
  MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (11)"]
79
70
  end
80
71
 
81
72
  it "should work correctly for inserting a record without a primary key" do
82
- @c.dataset.meta_def(:insert){|h| super(h); 13}
83
73
  @c.no_primary_key
84
74
  o = @c.new(:x => 11)
85
75
  o.save
@@ -87,15 +77,15 @@ describe "Model#save" do
87
77
  end
88
78
 
89
79
  it "should set the autoincrementing_primary_key value to the value returned by insert" do
90
- @c.dataset.meta_def(:insert){|h| super(h); 13}
91
80
  @c.unrestrict_primary_key
92
81
  @c.set_primary_key [:x, :y]
93
82
  o = @c.new(:x => 11)
94
83
  o.meta_def(:autoincrementing_primary_key){:y}
95
84
  o.save
96
- MODEL_DB.sqls.length.should == 2
97
- MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (11)"
98
- MODEL_DB.sqls.last.should =~ %r{SELECT \* FROM items WHERE \(\([xy] = 1[13]\) AND \([xy] = 1[13]\)\) LIMIT 1}
85
+ sqls = MODEL_DB.sqls
86
+ sqls.length.should == 2
87
+ sqls.first.should == "INSERT INTO items (x) VALUES (11)"
88
+ sqls.last.should =~ %r{SELECT \* FROM items WHERE \(\([xy] = 1[13]\) AND \([xy] = 1[13]\)\) LIMIT 1}
99
89
  end
100
90
 
101
91
  it "should update a record for an existing model instance" do
@@ -106,17 +96,18 @@ describe "Model#save" do
106
96
 
107
97
  it "should raise a NoExistingObject exception if the dataset update call doesn't return 1, unless require_modification is false" do
108
98
  o = @c.load(:id => 3, :x => 1)
109
- o.this.meta_def(:update){|*a| 0}
99
+ t = o.this
100
+ t.numrows = 0
110
101
  proc{o.save}.should raise_error(Sequel::NoExistingObject)
111
- o.this.meta_def(:update){|*a| 2}
102
+ t.numrows = 2
112
103
  proc{o.save}.should raise_error(Sequel::NoExistingObject)
113
- o.this.meta_def(:update){|*a| 1}
104
+ t.numrows = 1
114
105
  proc{o.save}.should_not raise_error
115
106
 
116
107
  o.require_modification = false
117
- o.this.meta_def(:update){|*a| 0}
108
+ t.numrows = 0
118
109
  proc{o.save}.should_not raise_error
119
- o.this.meta_def(:update){|*a| 2}
110
+ t.numrows = 2
120
111
  proc{o.save}.should_not raise_error
121
112
  end
122
113
 
@@ -185,22 +176,18 @@ describe "Model#save" do
185
176
  @c.use_transactions = true
186
177
  @c.load(:id => 3, :x => 1, :y => nil).save(:y)
187
178
  MODEL_DB.sqls.should == ["BEGIN", "UPDATE items SET y = NULL WHERE (id = 3)", "COMMIT"]
188
- MODEL_DB.reset
189
179
  @c.use_transactions = false
190
180
  @c.load(:id => 3, :x => 1, :y => nil).save(:y)
191
181
  MODEL_DB.sqls.should == ["UPDATE items SET y = NULL WHERE (id = 3)"]
192
- MODEL_DB.reset
193
182
  end
194
183
 
195
184
  it "should inherit Model's use_transactions setting" do
196
185
  @c.use_transactions = true
197
186
  Class.new(@c).load(:id => 3, :x => 1, :y => nil).save(:y)
198
187
  MODEL_DB.sqls.should == ["BEGIN", "UPDATE items SET y = NULL WHERE (id = 3)", "COMMIT"]
199
- MODEL_DB.reset
200
188
  @c.use_transactions = false
201
189
  Class.new(@c).load(:id => 3, :x => 1, :y => nil).save(:y)
202
190
  MODEL_DB.sqls.should == ["UPDATE items SET y = NULL WHERE (id = 3)"]
203
- MODEL_DB.reset
204
191
  end
205
192
 
206
193
  it "should use object's use_transactions setting" do
@@ -209,13 +196,11 @@ describe "Model#save" do
209
196
  @c.use_transactions = true
210
197
  o.save(:y)
211
198
  MODEL_DB.sqls.should == ["UPDATE items SET y = NULL WHERE (id = 3)"]
212
- MODEL_DB.reset
213
199
  o = @c.load(:id => 3, :x => 1, :y => nil)
214
200
  o.use_transactions = true
215
201
  @c.use_transactions = false
216
202
  o.save(:y)
217
203
  MODEL_DB.sqls.should == ["BEGIN", "UPDATE items SET y = NULL WHERE (id = 3)", "COMMIT"]
218
- MODEL_DB.reset
219
204
  end
220
205
 
221
206
  it "should use :transaction option if given" do
@@ -223,12 +208,10 @@ describe "Model#save" do
223
208
  o.use_transactions = true
224
209
  o.save(:y, :transaction=>false)
225
210
  MODEL_DB.sqls.should == ["UPDATE items SET y = NULL WHERE (id = 3)"]
226
- MODEL_DB.reset
227
211
  o = @c.load(:id => 3, :x => 1, :y => nil)
228
212
  o.use_transactions = false
229
213
  o.save(:y, :transaction=>true)
230
214
  MODEL_DB.sqls.should == ["BEGIN", "UPDATE items SET y = NULL WHERE (id = 3)", "COMMIT"]
231
- MODEL_DB.reset
232
215
  end
233
216
 
234
217
  it "should rollback if before_save returns false and raise_on_save_failure = true" do
@@ -240,7 +223,6 @@ describe "Model#save" do
240
223
  end
241
224
  proc { o.save(:y) }.should raise_error(Sequel::BeforeHookFailed)
242
225
  MODEL_DB.sqls.should == ["BEGIN", "ROLLBACK"]
243
- MODEL_DB.reset
244
226
  end
245
227
 
246
228
  it "should rollback if before_save returns false and :raise_on_failure option is true" do
@@ -252,7 +234,6 @@ describe "Model#save" do
252
234
  end
253
235
  proc { o.save(:y, :raise_on_failure => true) }.should raise_error(Sequel::BeforeHookFailed)
254
236
  MODEL_DB.sqls.should == ["BEGIN", "ROLLBACK"]
255
- MODEL_DB.reset
256
237
  end
257
238
 
258
239
  it "should not rollback outer transactions if before_save returns false and raise_on_save_failure = false" do
@@ -267,7 +248,6 @@ describe "Model#save" do
267
248
  MODEL_DB.run "BLAH"
268
249
  end
269
250
  MODEL_DB.sqls.should == ["BEGIN", "BLAH", "COMMIT"]
270
- MODEL_DB.reset
271
251
  end
272
252
 
273
253
  it "should rollback if before_save returns false and raise_on_save_failure = false" do
@@ -279,7 +259,6 @@ describe "Model#save" do
279
259
  end
280
260
  o.save(:y).should == nil
281
261
  MODEL_DB.sqls.should == ["BEGIN", "ROLLBACK"]
282
- MODEL_DB.reset
283
262
  end
284
263
 
285
264
  it "should not rollback if before_save throws Rollback and use_transactions = false" do
@@ -290,7 +269,118 @@ describe "Model#save" do
290
269
  end
291
270
  proc { o.save(:y) }.should raise_error(Sequel::Rollback)
292
271
  MODEL_DB.sqls.should == []
293
- MODEL_DB.reset
272
+ end
273
+
274
+ it "should support a :server option to set the server/shard to use" do
275
+ db = Sequel.mock(:fetch=>{:id=>13, :x=>1}, :autoid=>proc{13}, :numrows=>1, :servers=>{:s1=>{}})
276
+ c = Class.new(Sequel::Model(db[:items]))
277
+ c.columns :id, :x
278
+ db.sqls
279
+ o = c.new(:x => 1)
280
+ o.save(:server=>:s1)
281
+ db.sqls.should == ["INSERT INTO items (x) VALUES (1) -- s1", "SELECT * FROM items WHERE (id = 13) LIMIT 1 -- s1"]
282
+ o.save(:server=>:s1, :transaction=>true)
283
+ db.sqls.should == ["BEGIN -- s1", "UPDATE items SET x = 1 WHERE (id = 13) -- s1", 'COMMIT -- s1']
284
+ end
285
+ end
286
+
287
+ describe "Model#set_server" do
288
+ before do
289
+ @db = Sequel.mock(:fetch=>{:id=>13, :x=>1}, :autoid=>proc{13}, :numrows=>1, :servers=>{:s1=>{}})
290
+ @c = Class.new(Sequel::Model(@db[:items])) do
291
+ columns :id, :x
292
+ end
293
+ @db.sqls
294
+ end
295
+
296
+ it "should set the server to use when inserting" do
297
+ @c.new(:x => 1).set_server(:s1).save
298
+ @db.sqls.should == ["INSERT INTO items (x) VALUES (1) -- s1", "SELECT * FROM items WHERE (id = 13) LIMIT 1 -- s1"]
299
+ end
300
+
301
+ it "should set the server to use when updating" do
302
+ @c.load(:id=>13, :x => 1).set_server(:s1).save
303
+ @db.sqls.should == ["UPDATE items SET x = 1 WHERE (id = 13) -- s1"]
304
+ end
305
+
306
+ it "should set the server to use for transactions when saving" do
307
+ @c.load(:id=>13, :x => 1).set_server(:s1).save(:transaction=>true)
308
+ @db.sqls.should == ["BEGIN -- s1", "UPDATE items SET x = 1 WHERE (id = 13) -- s1", 'COMMIT -- s1']
309
+ end
310
+
311
+ it "should set the server to use when deleting" do
312
+ @c.load(:id=>13).set_server(:s1).delete
313
+ @db.sqls.should == ["DELETE FROM items WHERE (id = 13) -- s1"]
314
+ end
315
+
316
+ it "should set the server to use for transactions when destroying" do
317
+ o = @c.load(:id=>13).set_server(:s1)
318
+ o.use_transactions = true
319
+ o.destroy
320
+ @db.sqls.should == ["BEGIN -- s1", "DELETE FROM items WHERE (id = 13) -- s1", 'COMMIT -- s1']
321
+ end
322
+
323
+ it "should set the server on this if this is already loaded" do
324
+ o = @c.load(:id=>13, :x => 1)
325
+ o.this
326
+ o.set_server(:s1)
327
+ o.this.opts[:server].should == :s1
328
+ end
329
+
330
+ it "should set the server on this if this is not already loaded" do
331
+ @c.load(:id=>13, :x => 1).set_server(:s1).this.opts[:server].should == :s1
332
+ end
333
+ end
334
+
335
+ describe "Model#freeze" do
336
+ before do
337
+ class ::Album < Sequel::Model
338
+ columns :id
339
+ class B < Sequel::Model
340
+ columns :id, :album_id
341
+ end
342
+ end
343
+ @o = Album.load(:id=>1).freeze
344
+ MODEL_DB.sqls
345
+ end
346
+ after do
347
+ Object.send(:remove_const, :Album)
348
+ end
349
+
350
+ it "should freeze the object" do
351
+ @o.frozen?.should be_true
352
+ end
353
+
354
+ it "should freeze the object's values, associations, changed_columns, errors, and this" do
355
+ @o.values.frozen?.should be_true
356
+ @o.changed_columns.frozen?.should be_true
357
+ @o.errors.frozen?.should be_true
358
+ @o.this.frozen?.should be_true
359
+ end
360
+
361
+ it "should still have working class attr overriddable methods" do
362
+ Sequel::Model::BOOLEAN_SETTINGS.each{|m| @o.send(m) == Album.send(m)}
363
+ end
364
+
365
+ it "should have working new? method" do
366
+ @o.new?.should be_false
367
+ Album.new.freeze.new?.should be_true
368
+ end
369
+
370
+ it "should have working valid? method" do
371
+ @o.valid?.should be_true
372
+ o = Album.new
373
+ def o.validate() errors.add(:foo, '') end
374
+ o.freeze
375
+ o.valid?.should be_false
376
+ end
377
+
378
+ it "should raise an Error if trying to save/destroy/delete/refresh" do
379
+ proc{@o.save}.should raise_error(Sequel::Error)
380
+ proc{@o.destroy}.should raise_error(Sequel::Error)
381
+ proc{@o.delete}.should raise_error(Sequel::Error)
382
+ proc{@o.refresh}.should raise_error(Sequel::Error)
383
+ @o.db.sqls.should == []
294
384
  end
295
385
  end
296
386
 
@@ -299,7 +389,6 @@ describe "Model#marshallable" do
299
389
  class ::Album < Sequel::Model
300
390
  columns :id, :x
301
391
  end
302
- Album.dataset.meta_def(:insert){|h| super(h); 1}
303
392
  end
304
393
  after do
305
394
  Object.send(:remove_const, :Album)
@@ -381,7 +470,6 @@ describe "Model#modified[!?]" do
381
470
  end
382
471
 
383
472
  describe "Model#save_changes" do
384
-
385
473
  before do
386
474
  @c = Class.new(Sequel::Model(:items)) do
387
475
  unrestrict_primary_key
@@ -426,15 +514,14 @@ describe "Model#save_changes" do
426
514
  MODEL_DB.sqls.should == ["UPDATE items SET x = 2 WHERE (id = 3)"]
427
515
  o.save_changes
428
516
  o.save_changes
429
- MODEL_DB.sqls.should == ["UPDATE items SET x = 2 WHERE (id = 3)"]
430
- MODEL_DB.reset
517
+ MODEL_DB.sqls.should == []
431
518
 
432
519
  o.y = 4
433
520
  o.save_changes
434
521
  MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
435
522
  o.save_changes
436
523
  o.save_changes
437
- MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
524
+ MODEL_DB.sqls.should == []
438
525
  end
439
526
 
440
527
  it "should not consider columns changed if the values did not change" do
@@ -446,7 +533,6 @@ describe "Model#save_changes" do
446
533
  o.x = 3
447
534
  o.save_changes
448
535
  MODEL_DB.sqls.should == ["UPDATE items SET x = 3 WHERE (id = 3)"]
449
- MODEL_DB.reset
450
536
 
451
537
  o[:y] = nil
452
538
  o.save_changes
@@ -472,13 +558,11 @@ describe "Model#save_changes" do
472
558
  o.x = 2
473
559
  o.save_changes
474
560
  MODEL_DB.sqls.should == ["UPDATE items SET x = 3 WHERE (id = 3)"]
475
- MODEL_DB.reset
476
561
  o.save_changes
477
562
  MODEL_DB.sqls.should == []
478
563
  o.x = 4
479
564
  o.save_changes
480
565
  MODEL_DB.sqls.should == ["UPDATE items SET x = 5 WHERE (id = 3)"]
481
- MODEL_DB.reset
482
566
  end
483
567
 
484
568
  it "should update columns changed in a before_save hook" do
@@ -489,25 +573,21 @@ describe "Model#save_changes" do
489
573
  o.x = 2
490
574
  o.save_changes
491
575
  MODEL_DB.sqls.should == ["UPDATE items SET x = 3 WHERE (id = 3)"]
492
- MODEL_DB.reset
493
576
  o.save_changes
494
577
  MODEL_DB.sqls.should == []
495
578
  o.x = 4
496
579
  o.save_changes
497
580
  MODEL_DB.sqls.should == ["UPDATE items SET x = 5 WHERE (id = 3)"]
498
- MODEL_DB.reset
499
581
  end
500
582
  end
501
583
 
502
584
  describe "Model#new?" do
503
-
504
- before(:each) do
505
- MODEL_DB.reset
506
-
585
+ before do
507
586
  @c = Class.new(Sequel::Model(:items)) do
508
587
  unrestrict_primary_key
509
588
  columns :x
510
589
  end
590
+ MODEL_DB.reset
511
591
  end
512
592
 
513
593
  it "should be true for a new instance" do
@@ -523,74 +603,65 @@ describe "Model#new?" do
523
603
  end
524
604
 
525
605
  describe Sequel::Model, "w/ primary key" do
526
-
527
606
  it "should default to ':id'" do
528
607
  model_a = Class.new Sequel::Model
529
- model_a.primary_key.should be_equal(:id)
608
+ model_a.primary_key.should == :id
530
609
  end
531
610
 
532
611
  it "should be changed through 'set_primary_key'" do
533
- model_a = Class.new(Sequel::Model) { set_primary_key :a }
534
- model_a.primary_key.should be_equal(:a)
612
+ model_a = Class.new(Sequel::Model){ set_primary_key :a }
613
+ model_a.primary_key.should == :a
535
614
  end
536
615
 
537
616
  it "should support multi argument composite keys" do
538
- model_a = Class.new(Sequel::Model) { set_primary_key :a, :b }
539
- model_a.primary_key.should be_eql([:a, :b])
617
+ model_a = Class.new(Sequel::Model){ set_primary_key :a, :b }
618
+ model_a.primary_key.should == [:a, :b]
540
619
  end
541
620
 
542
621
  it "should accept single argument composite keys" do
543
- model_a = Class.new(Sequel::Model) { set_primary_key [:a, :b] }
544
- model_a.primary_key.should be_eql([:a, :b])
622
+ model_a = Class.new(Sequel::Model){ set_primary_key [:a, :b] }
623
+ model_a.primary_key.should == [:a, :b]
545
624
  end
546
-
547
625
  end
548
626
 
549
627
  describe Sequel::Model, "w/o primary key" do
550
628
  it "should return nil for primary key" do
551
- Class.new(Sequel::Model) { no_primary_key }.primary_key.should be_nil
629
+ Class.new(Sequel::Model){no_primary_key}.primary_key.should be_nil
552
630
  end
553
631
 
554
632
  it "should raise a Sequel::Error on 'this'" do
555
- instance = Class.new(Sequel::Model) { no_primary_key }.new
556
- proc { instance.this }.should raise_error(Sequel::Error)
633
+ instance = Class.new(Sequel::Model){no_primary_key}.new
634
+ proc{instance.this}.should raise_error(Sequel::Error)
557
635
  end
558
636
  end
559
637
 
560
638
  describe Sequel::Model, "with this" do
561
-
562
- before { @example = Class.new Sequel::Model(:examples); @example.columns :id, :a, :x, :y }
639
+ before do
640
+ @example = Class.new(Sequel::Model(:examples))
641
+ @example.columns :id, :a, :x, :y
642
+ end
563
643
 
564
644
  it "should return a dataset identifying the record" do
565
- instance = @example.load :id => 3
566
- instance.this.sql.should be_eql("SELECT * FROM examples WHERE (id = 3) LIMIT 1")
645
+ instance = @example.load(:id => 3)
646
+ instance.this.sql.should == "SELECT * FROM examples WHERE (id = 3) LIMIT 1"
567
647
  end
568
648
 
569
649
  it "should support arbitary primary keys" do
570
650
  @example.set_primary_key :a
571
651
 
572
- instance = @example.load :a => 3
573
- instance.this.sql.should be_eql("SELECT * FROM examples WHERE (a = 3) LIMIT 1")
652
+ instance = @example.load(:a => 3)
653
+ instance.this.sql.should == "SELECT * FROM examples WHERE (a = 3) LIMIT 1"
574
654
  end
575
655
 
576
656
  it "should support composite primary keys" do
577
657
  @example.set_primary_key :x, :y
578
- instance = @example.load :x => 4, :y => 5
579
-
580
- parts = [
581
- 'SELECT * FROM examples WHERE %s LIMIT 1',
582
- '((x = 4) AND (y = 5))',
583
- '((y = 5) AND (x = 4))'
584
- ].map { |expr| Regexp.escape expr }
585
- regexp = Regexp.new parts.first % "(?:#{parts[1]}|#{parts[2]})"
586
-
587
- instance.this.sql.should match(regexp)
658
+ instance = @example.load(:x => 4, :y => 5)
659
+ instance.this.sql.should =~ /SELECT \* FROM examples WHERE \(\([xy] = [45]\) AND \([xy] = [45]\)\) LIMIT 1/
588
660
  end
589
-
590
661
  end
591
662
 
592
663
  describe "Model#pk" do
593
- before(:each) do
664
+ before do
594
665
  @m = Class.new(Sequel::Model)
595
666
  @m.columns :id, :x, :y
596
667
  end
@@ -624,7 +695,7 @@ describe "Model#pk" do
624
695
  end
625
696
 
626
697
  describe "Model#pk_hash" do
627
- before(:each) do
698
+ before do
628
699
  @m = Class.new(Sequel::Model)
629
700
  @m.columns :id, :x, :y
630
701
  end
@@ -659,16 +730,14 @@ end
659
730
 
660
731
  describe Sequel::Model, "#set" do
661
732
  before do
662
- MODEL_DB.reset
663
-
664
733
  @c = Class.new(Sequel::Model(:items)) do
665
734
  set_primary_key :id
666
735
  columns :x, :y, :id
667
736
  end
668
737
  @c.strict_param_setting = false
669
- @c.instance_variable_set(:@columns, true)
670
738
  @o1 = @c.new
671
739
  @o2 = @c.load(:id => 5)
740
+ MODEL_DB.reset
672
741
  end
673
742
 
674
743
  it "should filter the given params using the model columns" do
@@ -716,6 +785,29 @@ describe Sequel::Model, "#set" do
716
785
  MODEL_DB.sqls.should == []
717
786
  end
718
787
 
788
+ it "should raise error if strict_param_setting is true and method does not exist" do
789
+ @o1.strict_param_setting = true
790
+ proc{@o1.set('foo' => 1)}.should raise_error(Sequel::Error)
791
+ end
792
+
793
+ it "should raise error if strict_param_setting is true and column is a primary key" do
794
+ @o1.strict_param_setting = true
795
+ proc{@o1.set('id' => 1)}.should raise_error(Sequel::Error)
796
+ end
797
+
798
+ it "should raise error if strict_param_setting is true and column is restricted" do
799
+ @o1.strict_param_setting = true
800
+ @c.set_restricted_columns :x
801
+ proc{@o1.set('x' => 1)}.should raise_error(Sequel::Error)
802
+ end
803
+
804
+ it "should not create a symbol if strict_param_setting is true and string is given" do
805
+ @o1.strict_param_setting = true
806
+ l = Symbol.all_symbols.length
807
+ proc{@o1.set('sadojafdso' => 1)}.should raise_error(Sequel::Error)
808
+ Symbol.all_symbols.length.should == l
809
+ end
810
+
719
811
  it "#set should correctly handle cases where an instance method is added to the class" do
720
812
  @o1.set(:x => 1)
721
813
  @o1.values.should == {:x => 1}
@@ -739,51 +831,74 @@ describe Sequel::Model, "#set" do
739
831
  @o1.set(:x => 2, :z => 3)
740
832
  @o1.values.should == {:x => 2, :z=>3}
741
833
  end
834
+
835
+ it "#set should correctly handle cases where a module with a setter method is included in the class" do
836
+ @o1.set(:x => 1)
837
+ @o1.values.should == {:x => 1}
838
+
839
+ @c.send(:include, Module.new do
840
+ def z=(v)
841
+ self[:z] = v
842
+ end
843
+ end)
844
+ @o1.set(:x => 2, :z => 3)
845
+ @o1.values.should == {:x => 2, :z=>3}
846
+ end
847
+
848
+ it "#set should correctly handle cases where the object extends a module with a setter method " do
849
+ @o1.set(:x => 1)
850
+ @o1.values.should == {:x => 1}
851
+
852
+ @o1.extend(Module.new do
853
+ def z=(v)
854
+ self[:z] = v
855
+ end
856
+ end)
857
+ @o1.set(:x => 2, :z => 3)
858
+ @o1.values.should == {:x => 2, :z=>3}
859
+ end
742
860
  end
743
861
 
744
862
  describe Sequel::Model, "#update" do
745
863
  before do
746
- MODEL_DB.reset
747
-
748
864
  @c = Class.new(Sequel::Model(:items)) do
749
865
  set_primary_key :id
750
866
  columns :x, :y, :id
751
867
  end
752
868
  @c.strict_param_setting = false
753
- @c.instance_variable_set(:@columns, true)
754
869
  @o1 = @c.new
755
870
  @o2 = @c.load(:id => 5)
871
+ MODEL_DB.reset
756
872
  end
757
873
 
758
874
  it "should filter the given params using the model columns" do
759
875
  @o1.update(:x => 1, :z => 2)
760
- MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
876
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
761
877
 
762
878
  MODEL_DB.reset
763
879
  @o2.update(:y => 1, :abc => 2)
764
- MODEL_DB.sqls.first.should == "UPDATE items SET y = 1 WHERE (id = 5)"
880
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 1 WHERE (id = 5)"]
765
881
  end
766
882
 
767
883
  it "should support virtual attributes" do
768
884
  @c.send(:define_method, :blah=){|v| self.x = v}
769
885
  @o1.update(:blah => 333)
770
- MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (333)"
886
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (333)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
771
887
  end
772
888
 
773
889
  it "should not modify the primary key" do
774
890
  @o1.update(:x => 1, :id => 2)
775
- MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
891
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
776
892
  MODEL_DB.reset
777
893
  @o2.update('y'=> 1, 'id'=> 2)
778
894
  @o2.values.should == {:y => 1, :id=> 5}
779
- MODEL_DB.sqls.first.should == "UPDATE items SET y = 1 WHERE (id = 5)"
895
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 1 WHERE (id = 5)"]
780
896
  end
781
897
  end
782
898
 
783
899
  describe Sequel::Model, "#set_fields" do
784
900
  before do
785
- @c = Class.new(Sequel::Model(:items))
786
- @c.class_eval do
901
+ @c = Class.new(Sequel::Model(:items)) do
787
902
  set_primary_key :id
788
903
  columns :x, :y, :z, :id
789
904
  end
@@ -799,15 +914,50 @@ describe Sequel::Model, "#set_fields" do
799
914
  @o1.values.should == {:x => 9, :y => 8, :id=>7}
800
915
  MODEL_DB.sqls.should == []
801
916
  end
917
+
918
+ it "should lookup into the hash without checking if the entry exists" do
919
+ @o1.set_fields({:x => 1}, [:x, :y])
920
+ @o1.values.should == {:x => 1, :y => nil}
921
+ @o1.set_fields(Hash.new(2), [:x, :y])
922
+ @o1.values.should == {:x => 2, :y => 2}
923
+ end
924
+
925
+ it "should skip missing fields if :missing=>:skip option is used" do
926
+ @o1.set_fields({:x => 3}, [:x, :y], :missing=>:skip)
927
+ @o1.values.should == {:x => 3}
928
+ @o1.set_fields({"x" => 4}, [:x, :y], :missing=>:skip)
929
+ @o1.values.should == {:x => 4}
930
+ @o1.set_fields(Hash.new(2).merge(:x=>2), [:x, :y], :missing=>:skip)
931
+ @o1.values.should == {:x => 2}
932
+ @o1.set_fields({:x => 1, :y => 2, :z=>3, :id=>4}, [:x, :y], :missing=>:skip)
933
+ @o1.values.should == {:x => 1, :y => 2}
934
+ end
935
+
936
+ it "should raise for missing fields if :missing=>:raise option is used" do
937
+ proc{@o1.set_fields({:x => 1}, [:x, :y], :missing=>:raise)}.should raise_error(Sequel::Error)
938
+ proc{@o1.set_fields(Hash.new(2).merge(:x=>2), [:x, :y], :missing=>:raise)}.should raise_error(Sequel::Error)
939
+ proc{@o1.set_fields({"x" => 1}, [:x, :y], :missing=>:raise)}.should raise_error(Sequel::Error)
940
+ @o1.set_fields({:x => 5, "y"=>2}, [:x, :y], :missing=>:raise)
941
+ @o1.values.should == {:x => 5, :y => 2}
942
+ @o1.set_fields({:x => 1, :y => 3, :z=>3, :id=>4}, [:x, :y], :missing=>:raise)
943
+ @o1.values.should == {:x => 1, :y => 3}
944
+ end
945
+
946
+ it "should use default behavior for an unrecognized :missing option" do
947
+ @o1.set_fields({:x => 1, :y => 2, :z=>3, :id=>4}, [:x, :y], :missing=>:foo)
948
+ @o1.values.should == {:x => 1, :y => 2}
949
+ @o1.set_fields({:x => 9, :y => 8, :z=>6, :id=>7}, [:x, :y, :id], :missing=>:foo)
950
+ @o1.values.should == {:x => 9, :y => 8, :id=>7}
951
+ MODEL_DB.sqls.should == []
952
+ end
953
+
802
954
  end
803
955
 
804
956
  describe Sequel::Model, "#update_fields" do
805
957
  before do
806
- @c = Class.new(Sequel::Model(:items))
807
- @c.class_eval do
958
+ @c = Class.new(Sequel::Model(:items)) do
808
959
  set_primary_key :id
809
960
  columns :x, :y, :z, :id
810
- def _refresh(ds); end
811
961
  end
812
962
  @c.strict_param_setting = true
813
963
  @o1 = @c.load(:id=>1)
@@ -817,21 +967,28 @@ describe Sequel::Model, "#update_fields" do
817
967
  it "should set only the given fields, and then save the changes to the record" do
818
968
  @o1.update_fields({:x => 1, :y => 2, :z=>3, :id=>4}, [:x, :y])
819
969
  @o1.values.should == {:x => 1, :y => 2, :id=>1}
820
- MODEL_DB.sqls.first.should =~ /UPDATE items SET [xy] = [12], [xy] = [12] WHERE \(id = 1\)/
821
- MODEL_DB.sqls.length.should == 1
822
- MODEL_DB.reset
970
+ sqls = MODEL_DB.sqls
971
+ sqls.pop.should =~ /UPDATE items SET [xy] = [12], [xy] = [12] WHERE \(id = 1\)/
972
+ sqls.should == []
823
973
 
824
974
  @o1.update_fields({:x => 1, :y => 5, :z=>6, :id=>7}, [:x, :y])
825
975
  @o1.values.should == {:x => 1, :y => 5, :id=>1}
826
976
  MODEL_DB.sqls.should == ["UPDATE items SET y = 5 WHERE (id = 1)"]
827
- MODEL_DB.reset
977
+ end
978
+
979
+ it "should support :missing=>:skip option" do
980
+ @o1.update_fields({:x => 1, :z=>3, :id=>4}, [:x, :y], :missing=>:skip)
981
+ @o1.values.should == {:x => 1, :id=>1}
982
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 1 WHERE (id = 1)"]
983
+ end
984
+
985
+ it "should support :missing=>:raise option" do
986
+ proc{@o1.update_fields({:x => 1}, [:x, :y], :missing=>:raise)}.should raise_error(Sequel::Error)
828
987
  end
829
988
  end
830
989
 
831
990
  describe Sequel::Model, "#(set|update)_(all|except|only)" do
832
991
  before do
833
- MODEL_DB.reset
834
-
835
992
  @c = Class.new(Sequel::Model(:items)) do
836
993
  set_primary_key :id
837
994
  columns :x, :y, :z, :id
@@ -839,8 +996,8 @@ describe Sequel::Model, "#(set|update)_(all|except|only)" do
839
996
  set_restricted_columns :y
840
997
  end
841
998
  @c.strict_param_setting = false
842
- @c.instance_variable_set(:@columns, true)
843
999
  @o1 = @c.new
1000
+ MODEL_DB.reset
844
1001
  end
845
1002
 
846
1003
  it "should raise errors if not all hash fields can be set and strict_param_setting is true" do
@@ -884,40 +1041,63 @@ describe Sequel::Model, "#(set|update)_(all|except|only)" do
884
1041
 
885
1042
  it "#update_all should update all attributes" do
886
1043
  @c.new.update_all(:x => 1, :id=>4)
887
- MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
888
- MODEL_DB.reset
1044
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
889
1045
  @c.new.update_all(:y => 1, :id=>4)
890
- MODEL_DB.sqls.first.should == "INSERT INTO items (y) VALUES (1)"
891
- MODEL_DB.reset
1046
+ MODEL_DB.sqls.should == ["INSERT INTO items (y) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
892
1047
  @c.new.update_all(:z => 1, :id=>4)
893
- MODEL_DB.sqls.first.should == "INSERT INTO items (z) VALUES (1)"
1048
+ MODEL_DB.sqls.should == ["INSERT INTO items (z) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
894
1049
  end
895
1050
 
896
1051
  it "#update_only should only update given attributes" do
897
1052
  @o1.update_only({:x => 1, :y => 2, :z=>3, :id=>4}, [:x])
898
- MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
899
- MODEL_DB.reset
1053
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
900
1054
  @c.new.update_only({:x => 1, :y => 2, :z=>3, :id=>4}, :x)
901
- MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
1055
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
902
1056
  end
903
1057
 
904
1058
  it "#update_except should not update given attributes" do
905
1059
  @o1.update_except({:x => 1, :y => 2, :z=>3, :id=>4}, [:y, :z])
906
- MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
907
- MODEL_DB.reset
1060
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
908
1061
  @c.new.update_except({:x => 1, :y => 2, :z=>3, :id=>4}, :y, :z)
909
- MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
1062
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
910
1063
  end
911
1064
  end
912
1065
 
913
- describe Sequel::Model, "#destroy" do
1066
+ describe Sequel::Model, "#destroy with filtered dataset" do
914
1067
  before do
1068
+ @model = Class.new(Sequel::Model(MODEL_DB[:items].where(:a=>1)))
1069
+ @model.columns :id, :a
1070
+ @instance = @model.load(:id => 1234)
915
1071
  MODEL_DB.reset
1072
+ end
1073
+
1074
+ it "should raise a NoExistingObject exception if the dataset delete call doesn't return 1" do
1075
+ @instance.this.meta_def(:execute_dui){|*a| 0}
1076
+ proc{@instance.delete}.should raise_error(Sequel::NoExistingObject)
1077
+ @instance.this.meta_def(:execute_dui){|*a| 2}
1078
+ proc{@instance.delete}.should raise_error(Sequel::NoExistingObject)
1079
+ @instance.this.meta_def(:execute_dui){|*a| 1}
1080
+ proc{@instance.delete}.should_not raise_error
1081
+
1082
+ @instance.require_modification = false
1083
+ @instance.this.meta_def(:execute_dui){|*a| 0}
1084
+ proc{@instance.delete}.should_not raise_error
1085
+ @instance.this.meta_def(:execute_dui){|*a| 2}
1086
+ proc{@instance.delete}.should_not raise_error
1087
+ end
1088
+
1089
+ it "should include WHERE clause when deleting" do
1090
+ @instance.destroy
1091
+ MODEL_DB.sqls.should == ["DELETE FROM items WHERE ((a = 1) AND (id = 1234))"]
1092
+ end
1093
+ end
1094
+
1095
+ describe Sequel::Model, "#destroy" do
1096
+ before do
916
1097
  @model = Class.new(Sequel::Model(:items))
917
1098
  @model.columns :id
918
- @model.dataset.meta_def(:delete){MODEL_DB.execute delete_sql;1}
919
-
920
1099
  @instance = @model.load(:id => 1234)
1100
+ MODEL_DB.reset
921
1101
  end
922
1102
 
923
1103
  it "should return self" do
@@ -926,65 +1106,56 @@ describe Sequel::Model, "#destroy" do
926
1106
  end
927
1107
 
928
1108
  it "should raise a NoExistingObject exception if the dataset delete call doesn't return 1" do
929
- @instance.this.meta_def(:delete){|*a| 0}
1109
+ @model.dataset.meta_def(:execute_dui){|*a| 0}
930
1110
  proc{@instance.delete}.should raise_error(Sequel::NoExistingObject)
931
- @instance.this.meta_def(:delete){|*a| 2}
1111
+ @model.dataset.meta_def(:execute_dui){|*a| 2}
932
1112
  proc{@instance.delete}.should raise_error(Sequel::NoExistingObject)
933
- @instance.this.meta_def(:delete){|*a| 1}
1113
+ @model.dataset.meta_def(:execute_dui){|*a| 1}
934
1114
  proc{@instance.delete}.should_not raise_error
935
1115
 
936
1116
  @instance.require_modification = false
937
- @instance.this.meta_def(:delete){|*a| 0}
1117
+ @model.dataset.meta_def(:execute_dui){|*a| 0}
938
1118
  proc{@instance.delete}.should_not raise_error
939
- @instance.this.meta_def(:delete){|*a| 2}
1119
+ @model.dataset.meta_def(:execute_dui){|*a| 2}
940
1120
  proc{@instance.delete}.should_not raise_error
941
1121
  end
942
1122
 
943
1123
  it "should run within a transaction if use_transactions is true" do
944
1124
  @instance.use_transactions = true
945
- @model.db.should_receive(:transaction)
946
1125
  @instance.destroy
1126
+ MODEL_DB.sqls.should == ["BEGIN", "DELETE FROM items WHERE id = 1234", "COMMIT"]
947
1127
  end
948
1128
 
949
1129
  it "should not run within a transaction if use_transactions is false" do
950
1130
  @instance.use_transactions = false
951
- @model.db.should_not_receive(:transaction)
952
1131
  @instance.destroy
1132
+ MODEL_DB.sqls.should == ["DELETE FROM items WHERE id = 1234"]
953
1133
  end
954
1134
 
955
1135
  it "should run within a transaction if :transaction option is true" do
956
1136
  @instance.use_transactions = false
957
- @model.db.should_receive(:transaction)
958
1137
  @instance.destroy(:transaction => true)
1138
+ MODEL_DB.sqls.should == ["BEGIN", "DELETE FROM items WHERE id = 1234", "COMMIT"]
959
1139
  end
960
1140
 
961
1141
  it "should not run within a transaction if :transaction option is false" do
962
1142
  @instance.use_transactions = true
963
- @model.db.should_not_receive(:transaction)
964
1143
  @instance.destroy(:transaction => false)
1144
+ MODEL_DB.sqls.should == ["DELETE FROM items WHERE id = 1234"]
965
1145
  end
966
1146
 
967
1147
  it "should run before_destroy and after_destroy hooks" do
968
1148
  @model.send(:define_method, :before_destroy){MODEL_DB.execute('before blah')}
969
1149
  @model.send(:define_method, :after_destroy){MODEL_DB.execute('after blah')}
970
1150
  @instance.destroy
971
-
972
- MODEL_DB.sqls.should == [
973
- "before blah",
974
- "DELETE FROM items WHERE (id = 1234)",
975
- "after blah"
976
- ]
1151
+ MODEL_DB.sqls.should == ["before blah", "DELETE FROM items WHERE id = 1234", "after blah"]
977
1152
  end
978
1153
  end
979
1154
 
980
1155
  describe Sequel::Model, "#exists?" do
981
- before(:each) do
1156
+ before do
982
1157
  @model = Class.new(Sequel::Model(:items))
983
- @ds = @model.dataset
984
- def @ds.fetch_rows(sql)
985
- db.execute(sql)
986
- yield(:x=>1) if sql =~ /id = 1/
987
- end
1158
+ @model.instance_dataset._fetch = @model.dataset._fetch = proc{|sql| {:x=>1} if sql =~ /id = 1/}
988
1159
  MODEL_DB.reset
989
1160
  end
990
1161
 
@@ -997,6 +1168,11 @@ describe Sequel::Model, "#exists?" do
997
1168
  @model.load(:id=>2).exists?.should be_false
998
1169
  MODEL_DB.sqls.should == ['SELECT 1 FROM items WHERE (id = 2) LIMIT 1']
999
1170
  end
1171
+
1172
+ it "should return false without issuing a query if the model object is new" do
1173
+ @model.new.exists?.should be_false
1174
+ MODEL_DB.sqls.should == []
1175
+ end
1000
1176
  end
1001
1177
 
1002
1178
  describe Sequel::Model, "#each" do
@@ -1008,7 +1184,7 @@ describe Sequel::Model, "#each" do
1008
1184
 
1009
1185
  specify "should iterate over the values" do
1010
1186
  h = {}
1011
- @m.each {|k, v| h[k] = v}
1187
+ @m.each{|k, v| h[k] = v}
1012
1188
  h.should == {:a => 1, :b => 2, :id => 4444}
1013
1189
  end
1014
1190
  end
@@ -1021,11 +1197,8 @@ describe Sequel::Model, "#keys" do
1021
1197
  end
1022
1198
 
1023
1199
  specify "should return the value keys" do
1024
- @m.keys.size.should == 3
1025
- @m.keys.should include(:a, :b, :id)
1026
-
1027
- @m = @model.new()
1028
- @m.keys.should == []
1200
+ @m.keys.sort_by{|k| k.to_s}.should == [:a, :b, :id]
1201
+ @model.new.keys.should == []
1029
1202
  end
1030
1203
  end
1031
1204
 
@@ -1094,13 +1267,10 @@ describe Sequel::Model, "#hash" do
1094
1267
  y = Class.new(Sequel::Model)
1095
1268
  y.columns :id, :x
1096
1269
  a = z.load(:id => 1, :x => 3)
1097
- b = z.load(:id => 1, :x => 4)
1098
- c = z.load(:id => 2, :x => 3)
1099
- d = y.load(:id => 1, :x => 3)
1100
1270
 
1101
- a.hash.should == b.hash
1102
- a.hash.should_not == c.hash
1103
- a.hash.should_not == d.hash
1271
+ a.hash.should == z.load(:id => 1, :x => 4).hash
1272
+ a.hash.should_not == z.load(:id => 2, :x => 3).hash
1273
+ a.hash.should_not == y.load(:id => 1, :x => 3).hash
1104
1274
  end
1105
1275
 
1106
1276
  specify "should be the same only for objects with the same class and values if the pk is nil" do
@@ -1109,14 +1279,71 @@ describe Sequel::Model, "#hash" do
1109
1279
  y = Class.new(Sequel::Model)
1110
1280
  y.columns :id, :x
1111
1281
  a = z.new(:x => 3)
1112
- b = z.new(:x => 4)
1113
- c = z.new(:x => 3)
1114
- d = y.new(:x => 3)
1115
1282
 
1116
- a.hash.should_not == b.hash
1117
- a.hash.should == c.hash
1118
- a.hash.should_not == d.hash
1283
+ a.hash.should_not == z.new(:x => 4).hash
1284
+ a.hash.should == z.new(:x => 3).hash
1285
+ a.hash.should_not == y.new(:x => 3).hash
1286
+ end
1287
+
1288
+ specify "should be the same only for objects with the same class and pk if pk is composite and all values are non-NULL" do
1289
+ z = Class.new(Sequel::Model)
1290
+ z.columns :id, :id2, :x
1291
+ z.set_primary_key(:id, :id2)
1292
+ y = Class.new(Sequel::Model)
1293
+ y.columns :id, :id2, :x
1294
+ y.set_primary_key(:id, :id2)
1295
+ a = z.load(:id => 1, :id2=>2, :x => 3)
1296
+
1297
+ a.hash.should == z.load(:id => 1, :id2=>2, :x => 4).hash
1298
+ a.hash.should_not == z.load(:id => 2, :id2=>1, :x => 3).hash
1299
+ a.hash.should_not == y.load(:id => 1, :id2=>1, :x => 3).hash
1300
+ end
1301
+
1302
+ specify "should be the same only for objects with the same class and value if pk is composite and one values is NULL" do
1303
+ z = Class.new(Sequel::Model)
1304
+ z.columns :id, :id2, :x
1305
+ z.set_primary_key(:id, :id2)
1306
+ y = Class.new(Sequel::Model)
1307
+ y.columns :id, :id2, :x
1308
+ y.set_primary_key(:id, :id2)
1309
+
1310
+ a = z.load(:id => 1, :id2 => nil, :x => 3)
1311
+ a.hash.should == z.load(:id => 1, :id2=>nil, :x => 3).hash
1312
+ a.hash.should_not == z.load(:id => 1, :id2=>nil, :x => 4).hash
1313
+ a.hash.should_not == y.load(:id => 1, :id2=>nil, :x => 3).hash
1314
+
1315
+ a = z.load(:id =>nil, :id2 => nil, :x => 3)
1316
+ a.hash.should == z.load(:id => nil, :id2=>nil, :x => 3).hash
1317
+ a.hash.should_not == z.load(:id => nil, :id2=>nil, :x => 4).hash
1318
+ a.hash.should_not == y.load(:id => nil, :id2=>nil, :x => 3).hash
1319
+
1320
+ a = z.load(:id => 1, :x => 3)
1321
+ a.hash.should == z.load(:id => 1, :x => 3).hash
1322
+ a.hash.should_not == z.load(:id => 1, :id2=>nil, :x => 3).hash
1323
+ a.hash.should_not == z.load(:id => 1, :x => 4).hash
1324
+ a.hash.should_not == y.load(:id => 1, :x => 3).hash
1325
+
1326
+ a = z.load(:x => 3)
1327
+ a.hash.should == z.load(:x => 3).hash
1328
+ a.hash.should_not == z.load(:id => nil, :id2=>nil, :x => 3).hash
1329
+ a.hash.should_not == z.load(:x => 4).hash
1330
+ a.hash.should_not == y.load(:x => 3).hash
1331
+ end
1332
+
1333
+ specify "should be the same only for objects with the same class and values if the no primary key" do
1334
+ z = Class.new(Sequel::Model)
1335
+ z.columns :id, :x
1336
+ z.no_primary_key
1337
+ y = Class.new(Sequel::Model)
1338
+ y.columns :id, :x
1339
+ y.no_primary_key
1340
+ a = z.new(:x => 3)
1341
+
1342
+ a.hash.should_not == z.new(:x => 4).hash
1343
+ a.hash.should == z.new(:x => 3).hash
1344
+ a.hash.should_not == y.new(:x => 3).hash
1119
1345
  end
1346
+
1120
1347
  end
1121
1348
 
1122
1349
  describe Sequel::Model, "#initialize" do
@@ -1161,10 +1388,32 @@ describe Sequel::Model, "#initialize" do
1161
1388
  m.values.should == {:x => 2}
1162
1389
  end
1163
1390
  end
1391
+
1392
+ describe Sequel::Model, "#initialize_set" do
1393
+ before do
1394
+ @c = Class.new(Sequel::Model){columns :id, :x, :y}
1395
+ end
1164
1396
 
1165
- describe Sequel::Model, ".create" do
1397
+ specify "should be called by initialize to set the column values" do
1398
+ @c.send(:define_method, :initialize_set){|h| set(:y => 3)}
1399
+ @c.new(:x => 2).values.should == {:y => 3}
1400
+ end
1401
+
1402
+ specify "should be called with the hash given to initialize " do
1403
+ x = nil
1404
+ @c.send(:define_method, :initialize_set){|y| x = y}
1405
+ @c.new(:x => 2)
1406
+ x.should == {:x => 2}
1407
+ end
1166
1408
 
1167
- before(:each) do
1409
+ specify "should not cause columns modified by the method to be considered as changed" do
1410
+ @c.send(:define_method, :initialize_set){|h| set(:y => 3)}
1411
+ @c.new(:x => 2).changed_columns.should == []
1412
+ end
1413
+ end
1414
+
1415
+ describe Sequel::Model, ".create" do
1416
+ before do
1168
1417
  MODEL_DB.reset
1169
1418
  @c = Class.new(Sequel::Model(:items)) do
1170
1419
  unrestrict_primary_key
@@ -1175,13 +1424,13 @@ describe Sequel::Model, ".create" do
1175
1424
  it "should be able to create rows in the associated table" do
1176
1425
  o = @c.create(:x => 1)
1177
1426
  o.class.should == @c
1178
- MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1)', "SELECT * FROM items WHERE (id IN ('INSERT INTO items (x) VALUES (1)')) LIMIT 1"]
1427
+ MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1)', "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
1179
1428
  end
1180
1429
 
1181
1430
  it "should be able to create rows without any values specified" do
1182
1431
  o = @c.create
1183
1432
  o.class.should == @c
1184
- MODEL_DB.sqls.should == ["INSERT INTO items DEFAULT VALUES", "SELECT * FROM items WHERE (id IN ('INSERT INTO items DEFAULT VALUES')) LIMIT 1"]
1433
+ MODEL_DB.sqls.should == ["INSERT INTO items DEFAULT VALUES", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
1185
1434
  end
1186
1435
 
1187
1436
  it "should accept a block and run it" do
@@ -1191,7 +1440,7 @@ describe Sequel::Model, ".create" do
1191
1440
  o1.should === o
1192
1441
  o3.should === o
1193
1442
  o2.should == :blah
1194
- MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (333)", "SELECT * FROM items WHERE (id IN ('INSERT INTO items (x) VALUES (333)')) LIMIT 1"]
1443
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (333)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
1195
1444
  end
1196
1445
 
1197
1446
  it "should create a row for a model with custom primary key" do
@@ -1204,49 +1453,45 @@ end
1204
1453
 
1205
1454
  describe Sequel::Model, "#refresh" do
1206
1455
  before do
1207
- MODEL_DB.reset
1208
1456
  @c = Class.new(Sequel::Model(:items)) do
1209
1457
  unrestrict_primary_key
1210
1458
  columns :id, :x
1211
1459
  end
1460
+ MODEL_DB.reset
1212
1461
  end
1213
1462
 
1214
1463
  specify "should reload the instance values from the database" do
1215
1464
  @m = @c.new(:id => 555)
1216
1465
  @m[:x] = 'blah'
1217
- @m.this.should_receive(:first).and_return({:x => 'kaboom', :id => 555})
1466
+ @c.instance_dataset._fetch = @c.dataset._fetch = {:x => 'kaboom', :id => 555}
1218
1467
  @m.refresh
1219
1468
  @m[:x].should == 'kaboom'
1469
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (id = 555) LIMIT 1"]
1220
1470
  end
1221
1471
 
1222
1472
  specify "should raise if the instance is not found" do
1223
1473
  @m = @c.new(:id => 555)
1224
- @m.this.should_receive(:first).and_return(nil)
1474
+ @c.instance_dataset._fetch =@c.dataset._fetch = []
1225
1475
  proc {@m.refresh}.should raise_error(Sequel::Error)
1476
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (id = 555) LIMIT 1"]
1226
1477
  end
1227
1478
 
1228
1479
  specify "should be aliased by #reload" do
1229
1480
  @m = @c.new(:id => 555)
1230
- @m.this.should_receive(:first).and_return({:x => 'kaboom', :id => 555})
1481
+ @c.instance_dataset._fetch =@c.dataset._fetch = {:x => 'kaboom', :id => 555}
1231
1482
  @m.reload
1232
1483
  @m[:x].should == 'kaboom'
1233
- end
1234
-
1235
- specify "should remove cached associations" do
1236
- @c.many_to_one :node, :class=>@c
1237
- @m = @c.new(:id => 555)
1238
- @m.associations[:node] = 15
1239
- @m.reload
1240
- @m.associations.should == {}
1484
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (id = 555) LIMIT 1"]
1241
1485
  end
1242
1486
  end
1243
1487
 
1244
1488
  describe Sequel::Model, "typecasting" do
1245
1489
  before do
1246
- MODEL_DB.reset
1247
1490
  @c = Class.new(Sequel::Model(:items)) do
1248
1491
  columns :x
1249
1492
  end
1493
+ @c.db_schema = {:x=>{:type=>:integer}}
1494
+ MODEL_DB.reset
1250
1495
  end
1251
1496
 
1252
1497
  after do
@@ -1255,14 +1500,13 @@ describe Sequel::Model, "typecasting" do
1255
1500
 
1256
1501
  specify "should not convert if typecasting is turned off" do
1257
1502
  @c.typecast_on_assignment = false
1258
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer}})
1259
1503
  m = @c.new
1260
1504
  m.x = '1'
1261
1505
  m.x.should == '1'
1262
1506
  end
1263
1507
 
1264
1508
  specify "should convert to integer for an integer field" do
1265
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer}})
1509
+ @c.db_schema = {:x=>{:type=>:integer}}
1266
1510
  m = @c.new
1267
1511
  m.x = '1'
1268
1512
  m.x.should == 1
@@ -1274,13 +1518,13 @@ describe Sequel::Model, "typecasting" do
1274
1518
 
1275
1519
  specify "should typecast '' to nil unless type is string or blob" do
1276
1520
  [:integer, :float, :decimal, :boolean, :date, :time, :datetime].each do |x|
1277
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>x}})
1521
+ @c.db_schema = {:x=>{:type=>x}}
1278
1522
  m = @c.new
1279
1523
  m.x = ''
1280
1524
  m.x.should == nil
1281
1525
  end
1282
1526
  [:string, :blob].each do |x|
1283
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>x}})
1527
+ @c.db_schema = {:x=>{:type=>x}}
1284
1528
  m = @c.new
1285
1529
  m.x = ''
1286
1530
  m.x.should == ''
@@ -1288,7 +1532,6 @@ describe Sequel::Model, "typecasting" do
1288
1532
  end
1289
1533
 
1290
1534
  specify "should not typecast '' to nil if typecast_empty_string_to_nil is false" do
1291
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer}})
1292
1535
  m = @c.new
1293
1536
  m.typecast_empty_string_to_nil = false
1294
1537
  proc{m.x = ''}.should raise_error
@@ -1296,22 +1539,31 @@ describe Sequel::Model, "typecasting" do
1296
1539
  proc{@c.new.x = ''}.should raise_error
1297
1540
  end
1298
1541
 
1542
+ specify "should handle typecasting where == raises an error on the object" do
1543
+ m = @c.new
1544
+ o = Object.new
1545
+ def o.==(v) raise ArgumentError end
1546
+ def o.to_i() 4 end
1547
+ m.x = o
1548
+ m.x.should == 4
1549
+ end
1550
+
1299
1551
  specify "should not typecast nil if NULLs are allowed" do
1300
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer,:allow_null=>true}})
1552
+ @c.db_schema[:x][:allow_null] = true
1301
1553
  m = @c.new
1302
1554
  m.x = nil
1303
1555
  m.x.should == nil
1304
1556
  end
1305
1557
 
1306
1558
  specify "should raise an error if attempting to typecast nil and NULLs are not allowed" do
1307
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer,:allow_null=>false}})
1559
+ @c.db_schema[:x][:allow_null] = false
1308
1560
  proc{@c.new.x = nil}.should raise_error(Sequel::Error)
1309
1561
  proc{@c.new.x = ''}.should raise_error(Sequel::Error)
1310
1562
  end
1311
1563
 
1312
1564
  specify "should not raise an error if NULLs are not allowed and typecasting is turned off" do
1313
1565
  @c.typecast_on_assignment = false
1314
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer,:allow_null=>false}})
1566
+ @c.db_schema[:x][:allow_null] = false
1315
1567
  m = @c.new
1316
1568
  m.x = nil
1317
1569
  m.x.should == nil
@@ -1320,7 +1572,6 @@ describe Sequel::Model, "typecasting" do
1320
1572
  specify "should not raise when typecasting nil to NOT NULL column but raise_on_typecast_failure is off" do
1321
1573
  @c.raise_on_typecast_failure = false
1322
1574
  @c.typecast_on_assignment = true
1323
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer,:allow_null=>false}})
1324
1575
  m = @c.new
1325
1576
  m.x = ''
1326
1577
  m.x.should == nil
@@ -1329,20 +1580,18 @@ describe Sequel::Model, "typecasting" do
1329
1580
  end
1330
1581
 
1331
1582
  specify "should raise an error if invalid data is used in an integer field" do
1332
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer}})
1333
1583
  proc{@c.new.x = 'a'}.should raise_error(Sequel::InvalidValue)
1334
1584
  end
1335
1585
 
1336
1586
  specify "should assign value if raise_on_typecast_failure is off and assigning invalid integer" do
1337
1587
  @c.raise_on_typecast_failure = false
1338
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer}})
1339
1588
  model = @c.new
1340
1589
  model.x = '1d'
1341
1590
  model.x.should == '1d'
1342
1591
  end
1343
1592
 
1344
1593
  specify "should convert to float for a float field" do
1345
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:float}})
1594
+ @c.db_schema = {:x=>{:type=>:float}}
1346
1595
  m = @c.new
1347
1596
  m.x = '1.3'
1348
1597
  m.x.should == 1.3
@@ -1353,20 +1602,20 @@ describe Sequel::Model, "typecasting" do
1353
1602
  end
1354
1603
 
1355
1604
  specify "should raise an error if invalid data is used in an float field" do
1356
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:float}})
1605
+ @c.db_schema = {:x=>{:type=>:float}}
1357
1606
  proc{@c.new.x = 'a'}.should raise_error(Sequel::InvalidValue)
1358
1607
  end
1359
1608
 
1360
1609
  specify "should assign value if raise_on_typecast_failure is off and assigning invalid float" do
1361
1610
  @c.raise_on_typecast_failure = false
1362
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:float}})
1611
+ @c.db_schema = {:x=>{:type=>:float}}
1363
1612
  model = @c.new
1364
1613
  model.x = '1d'
1365
1614
  model.x.should == '1d'
1366
1615
  end
1367
1616
 
1368
1617
  specify "should convert to BigDecimal for a decimal field" do
1369
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:decimal}})
1618
+ @c.db_schema = {:x=>{:type=>:decimal}}
1370
1619
  m = @c.new
1371
1620
  bd = BigDecimal.new('1.0')
1372
1621
  m.x = '1.0'
@@ -1380,13 +1629,13 @@ describe Sequel::Model, "typecasting" do
1380
1629
  end
1381
1630
 
1382
1631
  specify "should raise an error if invalid data is used in an decimal field" do
1383
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:decimal}})
1632
+ @c.db_schema = {:x=>{:type=>:decimal}}
1384
1633
  proc{@c.new.x = Date.today}.should raise_error(Sequel::InvalidValue)
1385
1634
  end
1386
1635
 
1387
1636
  specify "should assign value if raise_on_typecast_failure is off and assigning invalid decimal" do
1388
1637
  @c.raise_on_typecast_failure = false
1389
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:decimal}})
1638
+ @c.db_schema = {:x=>{:type=>:decimal}}
1390
1639
  model = @c.new
1391
1640
  time = Time.now
1392
1641
  model.x = time
@@ -1394,7 +1643,7 @@ describe Sequel::Model, "typecasting" do
1394
1643
  end
1395
1644
 
1396
1645
  specify "should convert to string for a string field" do
1397
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:string}})
1646
+ @c.db_schema = {:x=>{:type=>:string}}
1398
1647
  m = @c.new
1399
1648
  m.x = '1.3'
1400
1649
  m.x.should == '1.3'
@@ -1405,7 +1654,7 @@ describe Sequel::Model, "typecasting" do
1405
1654
  end
1406
1655
 
1407
1656
  specify "should convert to boolean for a boolean field" do
1408
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:boolean}})
1657
+ @c.db_schema = {:x=>{:type=>:boolean}}
1409
1658
  m = @c.new
1410
1659
  m.x = '1.3'
1411
1660
  m.x.should == true
@@ -1417,6 +1666,10 @@ describe Sequel::Model, "typecasting" do
1417
1666
  m.x.should == true
1418
1667
  m.x = 'T'
1419
1668
  m.x.should == true
1669
+ m.x = 'y'
1670
+ m.x.should == true
1671
+ m.x = 'Y'
1672
+ m.x.should == true
1420
1673
  m.x = true
1421
1674
  m.x.should == true
1422
1675
  m.x = nil
@@ -1433,6 +1686,14 @@ describe Sequel::Model, "typecasting" do
1433
1686
  m.x.should == false
1434
1687
  m.x = 'FALSE'
1435
1688
  m.x.should == false
1689
+ m.x = 'n'
1690
+ m.x.should == false
1691
+ m.x = 'N'
1692
+ m.x.should == false
1693
+ m.x = 'no'
1694
+ m.x.should == false
1695
+ m.x = 'NO'
1696
+ m.x.should == false
1436
1697
  m.x = '0'
1437
1698
  m.x.should == false
1438
1699
  m.x = 0
@@ -1442,7 +1703,7 @@ describe Sequel::Model, "typecasting" do
1442
1703
  end
1443
1704
 
1444
1705
  specify "should convert to date for a date field" do
1445
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:date}})
1706
+ @c.db_schema = {:x=>{:type=>:date}}
1446
1707
  m = @c.new
1447
1708
  y = Date.new(2007,10,21)
1448
1709
  m.x = '2007-10-21'
@@ -1456,7 +1717,7 @@ describe Sequel::Model, "typecasting" do
1456
1717
  end
1457
1718
 
1458
1719
  specify "should accept a hash with symbol or string keys for a date field" do
1459
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:date}})
1720
+ @c.db_schema = {:x=>{:type=>:date}}
1460
1721
  m = @c.new
1461
1722
  y = Date.new(2007,10,21)
1462
1723
  m.x = {:year=>2007, :month=>10, :day=>21}
@@ -1466,32 +1727,33 @@ describe Sequel::Model, "typecasting" do
1466
1727
  end
1467
1728
 
1468
1729
  specify "should raise an error if invalid data is used in a date field" do
1469
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:date}})
1730
+ @c.db_schema = {:x=>{:type=>:date}}
1470
1731
  proc{@c.new.x = 'a'}.should raise_error(Sequel::InvalidValue)
1471
1732
  proc{@c.new.x = 100}.should raise_error(Sequel::InvalidValue)
1472
1733
  end
1473
1734
 
1474
1735
  specify "should assign value if raise_on_typecast_failure is off and assigning invalid date" do
1475
1736
  @c.raise_on_typecast_failure = false
1476
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:date}})
1737
+ @c.db_schema = {:x=>{:type=>:date}}
1477
1738
  model = @c.new
1478
1739
  model.x = 4
1479
1740
  model.x.should == 4
1480
1741
  end
1481
1742
 
1482
- specify "should convert to time for a time field" do
1483
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:time}})
1743
+ specify "should convert to Sequel::SQLTime for a time field" do
1744
+ @c.db_schema = {:x=>{:type=>:time}}
1484
1745
  m = @c.new
1485
1746
  x = '10:20:30'
1486
- y = Time.parse(x)
1747
+ y = Sequel::SQLTime.parse(x)
1487
1748
  m.x = x
1488
1749
  m.x.should == y
1489
1750
  m.x = y
1490
1751
  m.x.should == y
1752
+ m.x.should be_a_kind_of(Sequel::SQLTime)
1491
1753
  end
1492
1754
 
1493
1755
  specify "should accept a hash with symbol or string keys for a time field" do
1494
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:time}})
1756
+ @c.db_schema = {:x=>{:type=>:time}}
1495
1757
  m = @c.new
1496
1758
  y = Time.parse('10:20:30')
1497
1759
  m.x = {:hour=>10, :minute=>20, :second=>30}
@@ -1501,7 +1763,7 @@ describe Sequel::Model, "typecasting" do
1501
1763
  end
1502
1764
 
1503
1765
  specify "should raise an error if invalid data is used in a time field" do
1504
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:time}})
1766
+ @c.db_schema = {:x=>{:type=>:time}}
1505
1767
  proc{@c.new.x = '0000'}.should raise_error
1506
1768
  proc{@c.new.x = Date.parse('2008-10-21')}.should raise_error(Sequel::InvalidValue)
1507
1769
  proc{@c.new.x = DateTime.parse('2008-10-21')}.should raise_error(Sequel::InvalidValue)
@@ -1509,14 +1771,14 @@ describe Sequel::Model, "typecasting" do
1509
1771
 
1510
1772
  specify "should assign value if raise_on_typecast_failure is off and assigning invalid time" do
1511
1773
  @c.raise_on_typecast_failure = false
1512
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:time}})
1774
+ @c.db_schema = {:x=>{:type=>:time}}
1513
1775
  model = @c.new
1514
1776
  model.x = '0000'
1515
1777
  model.x.should == '0000'
1516
1778
  end
1517
1779
 
1518
1780
  specify "should convert to the Sequel.datetime_class for a datetime field" do
1519
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:datetime}})
1781
+ @c.db_schema = {:x=>{:type=>:datetime}}
1520
1782
  m = @c.new
1521
1783
  x = '2007-10-21T10:20:30-07:00'
1522
1784
  y = Time.parse(x)
@@ -1541,7 +1803,7 @@ describe Sequel::Model, "typecasting" do
1541
1803
  end
1542
1804
 
1543
1805
  specify "should accept a hash with symbol or string keys for a datetime field" do
1544
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:datetime}})
1806
+ @c.db_schema = {:x=>{:type=>:datetime}}
1545
1807
  m = @c.new
1546
1808
  y = Time.parse('2007-10-21 10:20:30')
1547
1809
  m.x = {:year=>2007, :month=>10, :day=>21, :hour=>10, :minute=>20, :second=>30}
@@ -1557,7 +1819,7 @@ describe Sequel::Model, "typecasting" do
1557
1819
  end
1558
1820
 
1559
1821
  specify "should raise an error if invalid data is used in a datetime field" do
1560
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:datetime}})
1822
+ @c.db_schema = {:x=>{:type=>:datetime}}
1561
1823
  proc{@c.new.x = '0000'}.should raise_error(Sequel::InvalidValue)
1562
1824
  Sequel.datetime_class = DateTime
1563
1825
  proc{@c.new.x = '0000'}.should raise_error(Sequel::InvalidValue)
@@ -1566,7 +1828,7 @@ describe Sequel::Model, "typecasting" do
1566
1828
 
1567
1829
  specify "should assign value if raise_on_typecast_failure is off and assigning invalid datetime" do
1568
1830
  @c.raise_on_typecast_failure = false
1569
- @c.instance_variable_set(:@db_schema, {:x=>{:type=>:datetime}})
1831
+ @c.db_schema = {:x=>{:type=>:datetime}}
1570
1832
  model = @c.new
1571
1833
  model.x = '0000'
1572
1834
  model.x.should == '0000'
@@ -1584,11 +1846,7 @@ describe "Model#lock!" do
1584
1846
  @c = Class.new(Sequel::Model(:items)) do
1585
1847
  columns :id
1586
1848
  end
1587
- ds = @c.dataset
1588
- def ds.fetch_rows(sql)
1589
- db.execute(sql)
1590
- yield({:id=>1})
1591
- end
1849
+ @c.dataset._fetch = {:id=>1}
1592
1850
  MODEL_DB.reset
1593
1851
  end
1594
1852