viking-sequel 3.10.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 (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,236 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sequel::Model, ".plugin" do
4
+ before do
5
+ module Sequel::Plugins
6
+ module Timestamped
7
+ module InstanceMethods
8
+ def get_stamp(*args); @values[:stamp] end
9
+ def abc; 123; end
10
+ end
11
+
12
+ module ClassMethods
13
+ def def; 234; end
14
+ end
15
+
16
+ module DatasetMethods
17
+ def ghi; 345; end
18
+ end
19
+ end
20
+ end
21
+ @c = Class.new(Sequel::Model(:items))
22
+ @t = Sequel::Plugins::Timestamped
23
+ end
24
+ after do
25
+ Sequel::Plugins.send(:remove_const, :Timestamped)
26
+ end
27
+
28
+ it "should raise LoadError if the plugin is not found" do
29
+ proc{@c.plugin :something_or_other}.should raise_error(LoadError)
30
+ end
31
+
32
+ it "should store the plugin in .plugins" do
33
+ @c.plugins.should_not include(@t)
34
+ @c.plugin @t
35
+ @c.plugins.should include(@t)
36
+ end
37
+
38
+ it "should be inherited in subclasses" do
39
+ @c.plugins.should_not include(@t)
40
+ c1 = Class.new(@c)
41
+ @c.plugin @t
42
+ c2 = Class.new(@c)
43
+ @c.plugins.should include(@t)
44
+ c1.plugins.should_not include(@t)
45
+ c2.plugins.should include(@t)
46
+ end
47
+
48
+ it "should accept a symbol and load the module from the Sequel::Plugins namespace" do
49
+ @c.plugin :timestamped
50
+ @c.plugins.should include(@t)
51
+ end
52
+
53
+ it "should accept a module" do
54
+ m = Module.new
55
+ @c.plugin m
56
+ @c.plugins.should include(m)
57
+ end
58
+
59
+ it "should not attempt to load a plugin twice" do
60
+ @c.plugins.should_not include(@t)
61
+ @c.plugin @t
62
+ @c.plugins.reject{|m| m != @t}.length.should == 1
63
+ @c.plugin @t
64
+ @c.plugins.reject{|m| m != @t}.length.should == 1
65
+ end
66
+
67
+ it "should call apply and configure if the plugin responds to it, with the args and block used" do
68
+ m = Module.new do
69
+ def self.args; @args; end
70
+ def self.block; @block; end
71
+ def self.block_call; @block.call; end
72
+ def self.args2; @args2; end
73
+ def self.block2; @block2; end
74
+ def self.block2_call; @block2.call; end
75
+ def self.apply(model, *args, &block)
76
+ @args = args
77
+ @block = block
78
+ model.send(:define_method, :blah){43}
79
+ end
80
+ def self.configure(model, *args, &block)
81
+ @args2 = args
82
+ @block2 = block
83
+ model.send(:define_method, :blag){44}
84
+ end
85
+ end
86
+ b = lambda{42}
87
+ @c.plugin(m, 123, 1=>2, &b)
88
+
89
+ m.args.should == [123, {1=>2}]
90
+ m.block.should == b
91
+ m.block_call.should == 42
92
+ @c.new.blah.should == 43
93
+
94
+ m.args2.should == [123, {1=>2}]
95
+ m.block2.should == b
96
+ m.block2_call.should == 42
97
+ @c.new.blag.should == 44
98
+ end
99
+
100
+ it "should call configure even if the plugin has already been loaded" do
101
+ m = Module.new do
102
+ @args = []
103
+ def self.args; @args; end
104
+ def self.configure(model, *args, &block)
105
+ @args << [block, *args]
106
+ end
107
+ end
108
+
109
+ b = lambda{42}
110
+ @c.plugin(m, 123, 1=>2, &b)
111
+ m.args.should == [[b, 123, {1=>2}]]
112
+
113
+ b2 = lambda{44}
114
+ @c.plugin(m, 234, 2=>3, &b2)
115
+ m.args.should == [[b, 123, {1=>2}], [b2, 234, {2=>3}]]
116
+ end
117
+
118
+ it "should call things in the following order: apply, InstanceMethods, ClassMethods, DatasetMethods, configure" do
119
+ m = Module.new do
120
+ @args = []
121
+ def self.args; @args; end
122
+ def self.apply(model, *args, &block)
123
+ @args << :apply
124
+ end
125
+ def self.configure(model, *args, &block)
126
+ @args << :configure
127
+ end
128
+ im = Module.new do
129
+ def self.included(model)
130
+ model.plugins.last.args << :im
131
+ end
132
+ end
133
+ cm = Module.new do
134
+ def self.extended(model)
135
+ model.plugins.last.args << :cm
136
+ end
137
+ end
138
+ dm = Module.new do
139
+ def self.extended(dataset)
140
+ dataset.model.plugins.last.args << :dm
141
+ end
142
+ end
143
+ const_set(:InstanceMethods, im)
144
+ const_set(:ClassMethods, cm)
145
+ const_set(:DatasetMethods, dm)
146
+ end
147
+
148
+ b = lambda{44}
149
+ @c.plugin(m, 123, 1=>2, &b)
150
+ m.args.should == [:apply, :im, :cm, :dm, :configure]
151
+ @c.plugin(m, 234, 2=>3, &b)
152
+ m.args.should == [:apply, :im, :cm, :dm, :configure, :configure]
153
+ end
154
+
155
+ it "should include an InstanceMethods module in the class if the plugin includes it" do
156
+ @c.plugin @t
157
+ m = @c.new
158
+ m.should respond_to(:get_stamp)
159
+ m.should respond_to(:abc)
160
+ m.abc.should == 123
161
+ t = Time.now
162
+ m[:stamp] = t
163
+ m.get_stamp.should == t
164
+ end
165
+
166
+ it "should extend the class with a ClassMethods module if the plugin includes it" do
167
+ @c.plugin @t
168
+ @c.def.should == 234
169
+ end
170
+
171
+ it "should extend the class's dataset with a DatasetMethods module if the plugin includes it" do
172
+ @c.plugin @t
173
+ @c.dataset.ghi.should == 345
174
+ @c.ghi.should == 345
175
+ end
176
+
177
+ it "should save the DatasetMethods module and apply it later if the class doesn't have a dataset" do
178
+ c = Class.new(Sequel::Model)
179
+ c.plugin @t
180
+ proc{c.ghi}.should raise_error(Sequel::Error)
181
+ c.dataset = MODEL_DB[:i]
182
+ c.dataset.ghi.should == 345
183
+ c.ghi.should == 345
184
+ end
185
+
186
+ it "should save the DatasetMethods module and apply it later if the class has a dataset" do
187
+ @c.plugin @t
188
+ @c.dataset = MODEL_DB[:i]
189
+ @c.dataset.ghi.should == 345
190
+ @c.ghi.should == 345
191
+ end
192
+
193
+ it "should define class methods for all public instance methods in DatasetMethod" do
194
+ m = Module.new do
195
+ dm = Module.new do
196
+ def a; 1; end
197
+ def b; 2; end
198
+ end
199
+ const_set(:DatasetMethods, dm)
200
+ end
201
+ @c.plugin m
202
+ @c.dataset.a.should == 1
203
+ @c.dataset.b.should == 2
204
+ @c.a.should == 1
205
+ @c.b.should == 2
206
+ end
207
+
208
+ it "should define class methods for all public instance methods in DatasetMethod" do
209
+ m = Module.new do
210
+ dm = Module.new do
211
+ def b; 2; end
212
+ private
213
+ def a; 1; end
214
+ end
215
+ const_set(:DatasetMethods, dm)
216
+ end
217
+ @c.plugin m
218
+ @c.dataset.b.should == 2
219
+ lambda{@c.dataset.a}.should raise_error(NoMethodError)
220
+ @c.dataset.send(:a).should == 1
221
+ @c.b.should == 2
222
+ lambda{@c.a}.should raise_error(NoMethodError)
223
+ lambda{@c.send(:a)}.should raise_error(NoMethodError)
224
+ end
225
+
226
+ it "should not raise an error if the DatasetMethod module has no public instance methods" do
227
+ m = Module.new do
228
+ dm = Module.new do
229
+ private
230
+ def a; 1; end
231
+ end
232
+ const_set(:DatasetMethods, dm)
233
+ end
234
+ lambda{@c.plugin m}.should_not raise_error
235
+ end
236
+ end
@@ -0,0 +1,1500 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
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
21
+ end
22
+
23
+ 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
27
+ end
28
+
29
+ it "should use the model's server if the model has one already specified" do
30
+ @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
34
+ end
35
+ end
36
+
37
+ describe "Model#save" do
38
+ before do
39
+ @c = Class.new(Sequel::Model(:items)) do
40
+ columns :id, :x, :y
41
+ end
42
+ @c.dataset.meta_def(:insert){|h| super(h); 1}
43
+ MODEL_DB.reset
44
+ end
45
+
46
+ it "should insert a record for a new model instance" do
47
+ o = @c.new(:x => 1)
48
+ o.save
49
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)",
50
+ "SELECT * FROM items WHERE (id = 1) LIMIT 1"]
51
+ end
52
+
53
+ it "should use dataset's insert_select method if present" do
54
+ ds = @c.dataset = @c.dataset.clone
55
+ def ds.insert_select(hash)
56
+ execute("INSERT INTO items (y) VALUES (2)")
57
+ {:y=>2}
58
+ end
59
+ o = @c.new(:x => 1)
60
+ o.save
61
+
62
+ o.values.should == {:y=>2}
63
+ MODEL_DB.sqls.should == ["INSERT INTO items (y) VALUES (2)"]
64
+ end
65
+
66
+ it "should use value returned by insert as the primary key" do
67
+ @c.dataset.meta_def(:insert){|h| super(h); 13}
68
+ o = @c.new(:x => 11)
69
+ o.save
70
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (11)",
71
+ "SELECT * FROM items WHERE (id = 13) LIMIT 1"]
72
+ end
73
+
74
+ it "should work correctly for inserting a record without a primary key" do
75
+ @c.dataset.meta_def(:insert){|h| super(h); 13}
76
+ @c.no_primary_key
77
+ o = @c.new(:x => 11)
78
+ o.save
79
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (11)"]
80
+ end
81
+
82
+ it "should set the autoincrementing_primary_key value to the value returned by insert" do
83
+ @c.dataset.meta_def(:insert){|h| super(h); 13}
84
+ @c.unrestrict_primary_key
85
+ @c.set_primary_key [:x, :y]
86
+ o = @c.new(:x => 11)
87
+ o.meta_def(:autoincrementing_primary_key){:y}
88
+ o.save
89
+ MODEL_DB.sqls.length.should == 2
90
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (11)"
91
+ MODEL_DB.sqls.last.should =~ %r{SELECT \* FROM items WHERE \(\([xy] = 1[13]\) AND \([xy] = 1[13]\)\) LIMIT 1}
92
+ end
93
+
94
+ it "should update a record for an existing model instance" do
95
+ o = @c.load(:id => 3, :x => 1)
96
+ o.save
97
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 1 WHERE (id = 3)"]
98
+ end
99
+
100
+ it "should raise a NoExistingObject exception if the dataset update call doesn't return 1, unless require_modification is false" do
101
+ o = @c.load(:id => 3, :x => 1)
102
+ o.this.meta_def(:update){|*a| 0}
103
+ proc{o.save}.should raise_error(Sequel::NoExistingObject)
104
+ o.this.meta_def(:update){|*a| 2}
105
+ proc{o.save}.should raise_error(Sequel::NoExistingObject)
106
+ o.this.meta_def(:update){|*a| 1}
107
+ proc{o.save}.should_not raise_error
108
+
109
+ o.require_modification = false
110
+ o.this.meta_def(:update){|*a| 0}
111
+ proc{o.save}.should_not raise_error
112
+ o.this.meta_def(:update){|*a| 2}
113
+ proc{o.save}.should_not raise_error
114
+ end
115
+
116
+ it "should update only the given columns if given" do
117
+ o = @c.load(:id => 3, :x => 1, :y => nil)
118
+ o.save(:y)
119
+ MODEL_DB.sqls.first.should == "UPDATE items SET y = NULL WHERE (id = 3)"
120
+ end
121
+
122
+ it "should mark saved columns as not changed" do
123
+ o = @c.load(:id => 3, :x => 1, :y => nil)
124
+ o[:y] = 4
125
+ o.changed_columns.should == [:y]
126
+ o.save(:x)
127
+ o.changed_columns.should == [:y]
128
+ o.save(:y)
129
+ o.changed_columns.should == []
130
+ end
131
+
132
+ it "should mark all columns as not changed if this is a new record" do
133
+ o = @c.new(:x => 1, :y => nil)
134
+ o.x = 4
135
+ o.changed_columns.should == [:x]
136
+ o.save
137
+ o.changed_columns.should == []
138
+ end
139
+
140
+ it "should mark all columns as not changed if this is a new record and insert_select was used" do
141
+ @c.dataset.meta_def(:insert_select){|h| h.merge(:id=>1)}
142
+ o = @c.new(:x => 1, :y => nil)
143
+ o.x = 4
144
+ o.changed_columns.should == [:x]
145
+ o.save
146
+ o.changed_columns.should == []
147
+ end
148
+
149
+ it "should store previous value of @new in @was_new and as well as the hash used for updating in @columns_updated until after hooks finish running" do
150
+ res = nil
151
+ @c.send(:define_method, :after_save){ res = [@columns_updated, @was_new]}
152
+ o = @c.new(:x => 1, :y => nil)
153
+ o[:x] = 2
154
+ o.save
155
+ res.should == [nil, true]
156
+ o.after_save
157
+ res.should == [nil, nil]
158
+
159
+ res = nil
160
+ o = @c.load(:id => 23,:x => 1, :y => nil)
161
+ o[:x] = 2
162
+ o.save
163
+ res.should == [{:x => 2, :y => nil}, nil]
164
+ o.after_save
165
+ res.should == [nil, nil]
166
+
167
+ res = nil
168
+ o = @c.load(:id => 23,:x => 2, :y => nil)
169
+ o[:x] = 2
170
+ o[:y] = 22
171
+ o.save(:x)
172
+ res.should == [{:x=>2},nil]
173
+ o.after_save
174
+ res.should == [nil, nil]
175
+ end
176
+
177
+ it "should use Model's use_transactions setting by default" do
178
+ @c.use_transactions = true
179
+ @c.load(:id => 3, :x => 1, :y => nil).save(:y)
180
+ MODEL_DB.sqls.should == ["BEGIN", "UPDATE items SET y = NULL WHERE (id = 3)", "COMMIT"]
181
+ MODEL_DB.reset
182
+ @c.use_transactions = false
183
+ @c.load(:id => 3, :x => 1, :y => nil).save(:y)
184
+ MODEL_DB.sqls.should == ["UPDATE items SET y = NULL WHERE (id = 3)"]
185
+ MODEL_DB.reset
186
+ end
187
+
188
+ it "should inherit Model's use_transactions setting" do
189
+ @c.use_transactions = true
190
+ Class.new(@c).load(:id => 3, :x => 1, :y => nil).save(:y)
191
+ MODEL_DB.sqls.should == ["BEGIN", "UPDATE items SET y = NULL WHERE (id = 3)", "COMMIT"]
192
+ MODEL_DB.reset
193
+ @c.use_transactions = false
194
+ Class.new(@c).load(:id => 3, :x => 1, :y => nil).save(:y)
195
+ MODEL_DB.sqls.should == ["UPDATE items SET y = NULL WHERE (id = 3)"]
196
+ MODEL_DB.reset
197
+ end
198
+
199
+ it "should use object's use_transactions setting" do
200
+ o = @c.load(:id => 3, :x => 1, :y => nil)
201
+ o.use_transactions = false
202
+ @c.use_transactions = true
203
+ o.save(:y)
204
+ MODEL_DB.sqls.should == ["UPDATE items SET y = NULL WHERE (id = 3)"]
205
+ MODEL_DB.reset
206
+ o = @c.load(:id => 3, :x => 1, :y => nil)
207
+ o.use_transactions = true
208
+ @c.use_transactions = false
209
+ o.save(:y)
210
+ MODEL_DB.sqls.should == ["BEGIN", "UPDATE items SET y = NULL WHERE (id = 3)", "COMMIT"]
211
+ MODEL_DB.reset
212
+ end
213
+
214
+ it "should use :transaction option if given" do
215
+ o = @c.load(:id => 3, :x => 1, :y => nil)
216
+ o.use_transactions = true
217
+ o.save(:y, :transaction=>false)
218
+ MODEL_DB.sqls.should == ["UPDATE items SET y = NULL WHERE (id = 3)"]
219
+ MODEL_DB.reset
220
+ o = @c.load(:id => 3, :x => 1, :y => nil)
221
+ o.use_transactions = false
222
+ o.save(:y, :transaction=>true)
223
+ MODEL_DB.sqls.should == ["BEGIN", "UPDATE items SET y = NULL WHERE (id = 3)", "COMMIT"]
224
+ MODEL_DB.reset
225
+ end
226
+
227
+ it "should rollback if before_save returns false and raise_on_save_failure = true" do
228
+ o = @c.load(:id => 3, :x => 1, :y => nil)
229
+ o.use_transactions = true
230
+ o.raise_on_save_failure = true
231
+ def o.before_save
232
+ false
233
+ end
234
+ proc { o.save(:y) }.should raise_error(Sequel::BeforeHookFailed)
235
+ MODEL_DB.sqls.should == ["BEGIN", "ROLLBACK"]
236
+ MODEL_DB.reset
237
+ end
238
+
239
+ it "should not rollback outer transactions if before_save returns false and raise_on_save_failure = false" do
240
+ o = @c.load(:id => 3, :x => 1, :y => nil)
241
+ o.use_transactions = true
242
+ o.raise_on_save_failure = false
243
+ def o.before_save
244
+ false
245
+ end
246
+ MODEL_DB.transaction do
247
+ o.save(:y).should == nil
248
+ MODEL_DB.run "BLAH"
249
+ end
250
+ MODEL_DB.sqls.should == ["BEGIN", "BLAH", "COMMIT"]
251
+ MODEL_DB.reset
252
+ end
253
+
254
+ it "should rollback if before_save returns false and raise_on_save_failure = false" do
255
+ o = @c.load(:id => 3, :x => 1, :y => nil)
256
+ o.use_transactions = true
257
+ o.raise_on_save_failure = false
258
+ def o.before_save
259
+ false
260
+ end
261
+ o.save(:y).should == nil
262
+ MODEL_DB.sqls.should == ["BEGIN", "ROLLBACK"]
263
+ MODEL_DB.reset
264
+ end
265
+
266
+ it "should not rollback if before_save throws Rollback and use_transactions = false" do
267
+ o = @c.load(:id => 3, :x => 1, :y => nil)
268
+ o.use_transactions = false
269
+ def o.before_save
270
+ raise Sequel::Rollback
271
+ end
272
+ proc { o.save(:y) }.should raise_error(Sequel::Rollback)
273
+ MODEL_DB.sqls.should == []
274
+ MODEL_DB.reset
275
+ end
276
+ end
277
+
278
+ describe "Model#marshallable" do
279
+ before do
280
+ class ::Album < Sequel::Model
281
+ columns :id, :x
282
+ end
283
+ Album.dataset.meta_def(:insert){|h| super(h); 1}
284
+ end
285
+ after do
286
+ Object.send(:remove_const, :Album)
287
+ end
288
+
289
+ it "should make an object marshallable" do
290
+ i = Album.new(:x=>2)
291
+ s = nil
292
+ i2 = nil
293
+ i.marshallable!
294
+ proc{s = Marshal.dump(i)}.should_not raise_error
295
+ proc{i2 = Marshal.load(s)}.should_not raise_error
296
+ i2.should == i
297
+
298
+ i.save
299
+ i.marshallable!
300
+ proc{s = Marshal.dump(i)}.should_not raise_error
301
+ proc{i2 = Marshal.load(s)}.should_not raise_error
302
+ i2.should == i
303
+
304
+ i.save
305
+ i.marshallable!
306
+ proc{s = Marshal.dump(i)}.should_not raise_error
307
+ proc{i2 = Marshal.load(s)}.should_not raise_error
308
+ i2.should == i
309
+ end
310
+ end
311
+
312
+ describe "Model#modified[!?]" do
313
+ before do
314
+ @c = Class.new(Sequel::Model(:items))
315
+ @c.class_eval do
316
+ columns :id, :x
317
+ @db_schema = {:x => {:type => :integer}}
318
+ end
319
+ MODEL_DB.reset
320
+ end
321
+
322
+ it "should be true if the object is new" do
323
+ @c.new.modified?.should == true
324
+ end
325
+
326
+ it "should be false if the object has not been modified" do
327
+ @c.load(:id=>1).modified?.should == false
328
+ end
329
+
330
+ it "should be true if the object has been modified" do
331
+ o = @c.load(:id=>1, :x=>2)
332
+ o.x = 3
333
+ o.modified?.should == true
334
+ end
335
+
336
+ it "should be true if the object is marked modified!" do
337
+ o = @c.load(:id=>1, :x=>2)
338
+ o.modified!
339
+ o.modified?.should == true
340
+ end
341
+
342
+ it "should be false if the object is marked modified! after saving until modified! again" do
343
+ o = @c.load(:id=>1, :x=>2)
344
+ o.modified!
345
+ o.save
346
+ o.modified?.should == false
347
+ o.modified!
348
+ o.modified?.should == true
349
+ end
350
+
351
+ it "should be false if a column value is set that is the same as the current value after typecasting" do
352
+ o = @c.load(:id=>1, :x=>2)
353
+ o.x = '2'
354
+ o.modified?.should == false
355
+ end
356
+
357
+ it "should be true if a column value is set that is the different as the current value after typecasting" do
358
+ o = @c.load(:id=>1, :x=>'2')
359
+ o.x = '2'
360
+ o.modified?.should == true
361
+ end
362
+ end
363
+
364
+ describe "Model#save_changes" do
365
+
366
+ before do
367
+ @c = Class.new(Sequel::Model(:items)) do
368
+ unrestrict_primary_key
369
+ columns :id, :x, :y
370
+ end
371
+ MODEL_DB.reset
372
+ end
373
+
374
+ it "should always save if the object is new" do
375
+ o = @c.new(:x => 1)
376
+ o.save_changes
377
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
378
+ end
379
+
380
+ it "should take options passed to save" do
381
+ o = @c.new(:x => 1)
382
+ def o.valid?; false; end
383
+ proc{o.save_changes}.should raise_error(Sequel::Error)
384
+ MODEL_DB.sqls.should == []
385
+ o.save_changes(:validate=>false)
386
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
387
+ end
388
+
389
+ it "should do nothing if no changed columns" do
390
+ o = @c.load(:id => 3, :x => 1, :y => nil)
391
+ o.save_changes
392
+ MODEL_DB.sqls.should == []
393
+ end
394
+
395
+ it "should do nothing if modified? is false" do
396
+ o = @c.load(:id => 3, :x => 1, :y => nil)
397
+ def o.modified?; false; end
398
+ o.save_changes
399
+ MODEL_DB.sqls.should == []
400
+ end
401
+
402
+ it "should update only changed columns" do
403
+ o = @c.load(:id => 3, :x => 1, :y => nil)
404
+ o.x = 2
405
+
406
+ o.save_changes
407
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 2 WHERE (id = 3)"]
408
+ o.save_changes
409
+ o.save_changes
410
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 2 WHERE (id = 3)"]
411
+ MODEL_DB.reset
412
+
413
+ o.y = 4
414
+ o.save_changes
415
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
416
+ o.save_changes
417
+ o.save_changes
418
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
419
+ end
420
+
421
+ it "should not consider columns changed if the values did not change" do
422
+ o = @c.load(:id => 3, :x => 1, :y => nil)
423
+ o.x = 1
424
+
425
+ o.save_changes
426
+ MODEL_DB.sqls.should == []
427
+ o.x = 3
428
+ o.save_changes
429
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 3 WHERE (id = 3)"]
430
+ MODEL_DB.reset
431
+
432
+ o[:y] = nil
433
+ o.save_changes
434
+ MODEL_DB.sqls.should == []
435
+ o[:y] = 4
436
+ o.save_changes
437
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
438
+ end
439
+
440
+ it "should clear changed_columns" do
441
+ o = @c.load(:id => 3, :x => 1, :y => nil)
442
+ o.x = 4
443
+ o.changed_columns.should == [:x]
444
+ o.save_changes
445
+ o.changed_columns.should == []
446
+ end
447
+
448
+ it "should update columns changed in a before_update hook" do
449
+ o = @c.load(:id => 3, :x => 1, :y => nil)
450
+ @c.send(:define_method, :before_update){self.x += 1}
451
+ o.save_changes
452
+ MODEL_DB.sqls.should == []
453
+ o.x = 2
454
+ o.save_changes
455
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 3 WHERE (id = 3)"]
456
+ MODEL_DB.reset
457
+ o.save_changes
458
+ MODEL_DB.sqls.should == []
459
+ o.x = 4
460
+ o.save_changes
461
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 5 WHERE (id = 3)"]
462
+ MODEL_DB.reset
463
+ end
464
+
465
+ it "should update columns changed in a before_save hook" do
466
+ o = @c.load(:id => 3, :x => 1, :y => nil)
467
+ @c.send(:define_method, :before_update){self.x += 1}
468
+ o.save_changes
469
+ MODEL_DB.sqls.should == []
470
+ o.x = 2
471
+ o.save_changes
472
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 3 WHERE (id = 3)"]
473
+ MODEL_DB.reset
474
+ o.save_changes
475
+ MODEL_DB.sqls.should == []
476
+ o.x = 4
477
+ o.save_changes
478
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 5 WHERE (id = 3)"]
479
+ MODEL_DB.reset
480
+ end
481
+ end
482
+
483
+ describe "Model#new?" do
484
+
485
+ before(:each) do
486
+ MODEL_DB.reset
487
+
488
+ @c = Class.new(Sequel::Model(:items)) do
489
+ unrestrict_primary_key
490
+ columns :x
491
+ end
492
+ end
493
+
494
+ it "should be true for a new instance" do
495
+ n = @c.new(:x => 1)
496
+ n.should be_new
497
+ end
498
+
499
+ it "should be false after saving" do
500
+ n = @c.new(:x => 1)
501
+ n.save
502
+ n.should_not be_new
503
+ end
504
+ end
505
+
506
+ describe Sequel::Model, "w/ primary key" do
507
+
508
+ it "should default to ':id'" do
509
+ model_a = Class.new Sequel::Model
510
+ model_a.primary_key.should be_equal(:id)
511
+ end
512
+
513
+ it "should be changed through 'set_primary_key'" do
514
+ model_a = Class.new(Sequel::Model) { set_primary_key :a }
515
+ model_a.primary_key.should be_equal(:a)
516
+ end
517
+
518
+ it "should support multi argument composite keys" do
519
+ model_a = Class.new(Sequel::Model) { set_primary_key :a, :b }
520
+ model_a.primary_key.should be_eql([:a, :b])
521
+ end
522
+
523
+ it "should accept single argument composite keys" do
524
+ model_a = Class.new(Sequel::Model) { set_primary_key [:a, :b] }
525
+ model_a.primary_key.should be_eql([:a, :b])
526
+ end
527
+
528
+ end
529
+
530
+ describe Sequel::Model, "w/o primary key" do
531
+ it "should return nil for primary key" do
532
+ Class.new(Sequel::Model) { no_primary_key }.primary_key.should be_nil
533
+ end
534
+
535
+ it "should raise a Sequel::Error on 'this'" do
536
+ instance = Class.new(Sequel::Model) { no_primary_key }.new
537
+ proc { instance.this }.should raise_error(Sequel::Error)
538
+ end
539
+ end
540
+
541
+ describe Sequel::Model, "with this" do
542
+
543
+ before { @example = Class.new Sequel::Model(:examples); @example.columns :id, :a, :x, :y }
544
+
545
+ it "should return a dataset identifying the record" do
546
+ instance = @example.load :id => 3
547
+ instance.this.sql.should be_eql("SELECT * FROM examples WHERE (id = 3) LIMIT 1")
548
+ end
549
+
550
+ it "should support arbitary primary keys" do
551
+ @example.set_primary_key :a
552
+
553
+ instance = @example.load :a => 3
554
+ instance.this.sql.should be_eql("SELECT * FROM examples WHERE (a = 3) LIMIT 1")
555
+ end
556
+
557
+ it "should support composite primary keys" do
558
+ @example.set_primary_key :x, :y
559
+ instance = @example.load :x => 4, :y => 5
560
+
561
+ parts = [
562
+ 'SELECT * FROM examples WHERE %s LIMIT 1',
563
+ '((x = 4) AND (y = 5))',
564
+ '((y = 5) AND (x = 4))'
565
+ ].map { |expr| Regexp.escape expr }
566
+ regexp = Regexp.new parts.first % "(?:#{parts[1]}|#{parts[2]})"
567
+
568
+ instance.this.sql.should match(regexp)
569
+ end
570
+
571
+ end
572
+
573
+ describe "Model#pk" do
574
+ before(:each) do
575
+ @m = Class.new(Sequel::Model)
576
+ @m.columns :id, :x, :y
577
+ end
578
+
579
+ it "should be default return the value of the :id column" do
580
+ m = @m.load(:id => 111, :x => 2, :y => 3)
581
+ m.pk.should == 111
582
+ end
583
+
584
+ it "should be return the primary key value for custom primary key" do
585
+ @m.set_primary_key :x
586
+ m = @m.load(:id => 111, :x => 2, :y => 3)
587
+ m.pk.should == 2
588
+ end
589
+
590
+ it "should be return the primary key value for composite primary key" do
591
+ @m.set_primary_key [:y, :x]
592
+ m = @m.load(:id => 111, :x => 2, :y => 3)
593
+ m.pk.should == [3, 2]
594
+ end
595
+
596
+ it "should raise if no primary key" do
597
+ @m.set_primary_key nil
598
+ m = @m.new(:id => 111, :x => 2, :y => 3)
599
+ proc {m.pk}.should raise_error(Sequel::Error)
600
+
601
+ @m.no_primary_key
602
+ m = @m.new(:id => 111, :x => 2, :y => 3)
603
+ proc {m.pk}.should raise_error(Sequel::Error)
604
+ end
605
+ end
606
+
607
+ describe "Model#pk_hash" do
608
+ before(:each) do
609
+ @m = Class.new(Sequel::Model)
610
+ @m.columns :id, :x, :y
611
+ end
612
+
613
+ it "should be default return the value of the :id column" do
614
+ m = @m.load(:id => 111, :x => 2, :y => 3)
615
+ m.pk_hash.should == {:id => 111}
616
+ end
617
+
618
+ it "should be return the primary key value for custom primary key" do
619
+ @m.set_primary_key :x
620
+ m = @m.load(:id => 111, :x => 2, :y => 3)
621
+ m.pk_hash.should == {:x => 2}
622
+ end
623
+
624
+ it "should be return the primary key value for composite primary key" do
625
+ @m.set_primary_key [:y, :x]
626
+ m = @m.load(:id => 111, :x => 2, :y => 3)
627
+ m.pk_hash.should == {:y => 3, :x => 2}
628
+ end
629
+
630
+ it "should raise if no primary key" do
631
+ @m.set_primary_key nil
632
+ m = @m.new(:id => 111, :x => 2, :y => 3)
633
+ proc {m.pk_hash}.should raise_error(Sequel::Error)
634
+
635
+ @m.no_primary_key
636
+ m = @m.new(:id => 111, :x => 2, :y => 3)
637
+ proc {m.pk_hash}.should raise_error(Sequel::Error)
638
+ end
639
+ end
640
+
641
+ describe Sequel::Model, "#set" do
642
+ before do
643
+ MODEL_DB.reset
644
+
645
+ @c = Class.new(Sequel::Model(:items)) do
646
+ set_primary_key :id
647
+ columns :x, :y, :id
648
+ end
649
+ @c.strict_param_setting = false
650
+ @c.instance_variable_set(:@columns, true)
651
+ @o1 = @c.new
652
+ @o2 = @c.load(:id => 5)
653
+ end
654
+
655
+ it "should filter the given params using the model columns" do
656
+ @o1.set(:x => 1, :z => 2)
657
+ @o1.values.should == {:x => 1}
658
+ MODEL_DB.sqls.should == []
659
+
660
+ @o2.set(:y => 1, :abc => 2)
661
+ @o2.values.should == {:y => 1, :id=> 5}
662
+ MODEL_DB.sqls.should == []
663
+ end
664
+
665
+ it "should work with both strings and symbols" do
666
+ @o1.set('x'=> 1, 'z'=> 2)
667
+ @o1.values.should == {:x => 1}
668
+ MODEL_DB.sqls.should == []
669
+
670
+ @o2.set('y'=> 1, 'abc'=> 2)
671
+ @o2.values.should == {:y => 1, :id=> 5}
672
+ MODEL_DB.sqls.should == []
673
+ end
674
+
675
+ it "should support virtual attributes" do
676
+ @c.send(:define_method, :blah=){|v| self.x = v}
677
+ @o1.set(:blah => 333)
678
+ @o1.values.should == {:x => 333}
679
+ MODEL_DB.sqls.should == []
680
+ @o1.set('blah'=> 334)
681
+ @o1.values.should == {:x => 334}
682
+ MODEL_DB.sqls.should == []
683
+ end
684
+
685
+ it "should not modify the primary key" do
686
+ @o1.set(:x => 1, :id => 2)
687
+ @o1.values.should == {:x => 1}
688
+ MODEL_DB.sqls.should == []
689
+ @o2.set('y'=> 1, 'id'=> 2)
690
+ @o2.values.should == {:y => 1, :id=> 5}
691
+ MODEL_DB.sqls.should == []
692
+ end
693
+
694
+ it "should return self" do
695
+ returned_value = @o1.set(:x => 1, :z => 2)
696
+ returned_value.should == @o1
697
+ MODEL_DB.sqls.should == []
698
+ end
699
+ end
700
+
701
+ describe Sequel::Model, "#update" do
702
+ before do
703
+ MODEL_DB.reset
704
+
705
+ @c = Class.new(Sequel::Model(:items)) do
706
+ set_primary_key :id
707
+ columns :x, :y, :id
708
+ end
709
+ @c.strict_param_setting = false
710
+ @c.instance_variable_set(:@columns, true)
711
+ @o1 = @c.new
712
+ @o2 = @c.load(:id => 5)
713
+ end
714
+
715
+ it "should filter the given params using the model columns" do
716
+ @o1.update(:x => 1, :z => 2)
717
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
718
+
719
+ MODEL_DB.reset
720
+ @o2.update(:y => 1, :abc => 2)
721
+ MODEL_DB.sqls.first.should == "UPDATE items SET y = 1 WHERE (id = 5)"
722
+ end
723
+
724
+ it "should support virtual attributes" do
725
+ @c.send(:define_method, :blah=){|v| self.x = v}
726
+ @o1.update(:blah => 333)
727
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (333)"
728
+ end
729
+
730
+ it "should not modify the primary key" do
731
+ @o1.update(:x => 1, :id => 2)
732
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
733
+ MODEL_DB.reset
734
+ @o2.update('y'=> 1, 'id'=> 2)
735
+ @o2.values.should == {:y => 1, :id=> 5}
736
+ MODEL_DB.sqls.first.should == "UPDATE items SET y = 1 WHERE (id = 5)"
737
+ end
738
+ end
739
+
740
+ describe Sequel::Model, "#(set|update)_(all|except|only)" do
741
+ before do
742
+ MODEL_DB.reset
743
+
744
+ @c = Class.new(Sequel::Model(:items)) do
745
+ set_primary_key :id
746
+ columns :x, :y, :z, :id
747
+ set_allowed_columns :x
748
+ set_restricted_columns :y
749
+ end
750
+ @c.strict_param_setting = false
751
+ @c.instance_variable_set(:@columns, true)
752
+ @o1 = @c.new
753
+ end
754
+
755
+ it "#set_all should set all attributes" do
756
+ @o1.set_all(:x => 1, :y => 2, :z=>3, :id=>4)
757
+ @o1.values.should == {:x => 1, :y => 2, :z=>3}
758
+ end
759
+
760
+ it "#set_only should only set given attributes" do
761
+ @o1.set_only({:x => 1, :y => 2, :z=>3, :id=>4}, [:x, :y])
762
+ @o1.values.should == {:x => 1, :y => 2}
763
+ @o1.set_only({:x => 4, :y => 5, :z=>6, :id=>7}, :x, :y)
764
+ @o1.values.should == {:x => 4, :y => 5}
765
+ end
766
+
767
+ it "#set_except should not set given attributes" do
768
+ @o1.set_except({:x => 1, :y => 2, :z=>3, :id=>4}, [:y, :z])
769
+ @o1.values.should == {:x => 1}
770
+ @o1.set_except({:x => 4, :y => 2, :z=>3, :id=>4}, :y, :z)
771
+ @o1.values.should == {:x => 4}
772
+ end
773
+
774
+ it "#update_all should update all attributes" do
775
+ @c.new.update_all(:x => 1, :id=>4)
776
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
777
+ MODEL_DB.reset
778
+ @c.new.update_all(:y => 1, :id=>4)
779
+ MODEL_DB.sqls.first.should == "INSERT INTO items (y) VALUES (1)"
780
+ MODEL_DB.reset
781
+ @c.new.update_all(:z => 1, :id=>4)
782
+ MODEL_DB.sqls.first.should == "INSERT INTO items (z) VALUES (1)"
783
+ end
784
+
785
+ it "#update_only should only update given attributes" do
786
+ @o1.update_only({:x => 1, :y => 2, :z=>3, :id=>4}, [:x])
787
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
788
+ MODEL_DB.reset
789
+ @c.new.update_only({:x => 1, :y => 2, :z=>3, :id=>4}, :x)
790
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
791
+ end
792
+
793
+ it "#update_except should not update given attributes" do
794
+ @o1.update_except({:x => 1, :y => 2, :z=>3, :id=>4}, [:y, :z])
795
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
796
+ MODEL_DB.reset
797
+ @c.new.update_except({:x => 1, :y => 2, :z=>3, :id=>4}, :y, :z)
798
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
799
+ end
800
+ end
801
+
802
+ describe Sequel::Model, "#destroy" do
803
+ before do
804
+ MODEL_DB.reset
805
+ @model = Class.new(Sequel::Model(:items))
806
+ @model.columns :id
807
+ @model.dataset.meta_def(:delete){MODEL_DB.execute delete_sql;1}
808
+
809
+ @instance = @model.load(:id => 1234)
810
+ end
811
+
812
+ it "should return self" do
813
+ @model.send(:define_method, :after_destroy){3}
814
+ @instance.destroy.should == @instance
815
+ end
816
+
817
+ it "should raise a NoExistingObject exception if the dataset delete call doesn't return 1" do
818
+ @instance.this.meta_def(:delete){|*a| 0}
819
+ proc{@instance.delete}.should raise_error(Sequel::NoExistingObject)
820
+ @instance.this.meta_def(:delete){|*a| 2}
821
+ proc{@instance.delete}.should raise_error(Sequel::NoExistingObject)
822
+ @instance.this.meta_def(:delete){|*a| 1}
823
+ proc{@instance.delete}.should_not raise_error
824
+
825
+ @instance.require_modification = false
826
+ @instance.this.meta_def(:delete){|*a| 0}
827
+ proc{@instance.delete}.should_not raise_error
828
+ @instance.this.meta_def(:delete){|*a| 2}
829
+ proc{@instance.delete}.should_not raise_error
830
+ end
831
+
832
+ it "should run within a transaction if use_transactions is true" do
833
+ @instance.use_transactions = true
834
+ @model.db.should_receive(:transaction)
835
+ @instance.destroy
836
+ end
837
+
838
+ it "should not run within a transaction if use_transactions is false" do
839
+ @instance.use_transactions = false
840
+ @model.db.should_not_receive(:transaction)
841
+ @instance.destroy
842
+ end
843
+
844
+ it "should run within a transaction if :transaction option is true" do
845
+ @instance.use_transactions = false
846
+ @model.db.should_receive(:transaction)
847
+ @instance.destroy(:transaction => true)
848
+ end
849
+
850
+ it "should not run within a transaction if :transaction option is false" do
851
+ @instance.use_transactions = true
852
+ @model.db.should_not_receive(:transaction)
853
+ @instance.destroy(:transaction => false)
854
+ end
855
+
856
+ it "should run before_destroy and after_destroy hooks" do
857
+ @model.send(:define_method, :before_destroy){MODEL_DB.execute('before blah')}
858
+ @model.send(:define_method, :after_destroy){MODEL_DB.execute('after blah')}
859
+ @instance.destroy
860
+
861
+ MODEL_DB.sqls.should == [
862
+ "before blah",
863
+ "DELETE FROM items WHERE (id = 1234)",
864
+ "after blah"
865
+ ]
866
+ end
867
+ end
868
+
869
+ describe Sequel::Model, "#exists?" do
870
+ before(:each) do
871
+ @model = Class.new(Sequel::Model(:items))
872
+ @m = @model.new
873
+ end
874
+
875
+ it "should returns true when #this.count > 0" do
876
+ @m.this.meta_def(:count) {1}
877
+ @m.exists?.should be_true
878
+ end
879
+
880
+ it "should return false when #this.count == 0" do
881
+ @m.this.meta_def(:count) {0}
882
+ @m.exists?.should be_false
883
+ end
884
+ end
885
+
886
+ describe Sequel::Model, "#each" do
887
+ before do
888
+ @model = Class.new(Sequel::Model(:items))
889
+ @model.columns :a, :b, :id
890
+ @m = @model.load(:a => 1, :b => 2, :id => 4444)
891
+ end
892
+
893
+ specify "should iterate over the values" do
894
+ h = {}
895
+ @m.each {|k, v| h[k] = v}
896
+ h.should == {:a => 1, :b => 2, :id => 4444}
897
+ end
898
+ end
899
+
900
+ describe Sequel::Model, "#keys" do
901
+ before do
902
+ @model = Class.new(Sequel::Model(:items))
903
+ @model.columns :a, :b, :id
904
+ @m = @model.load(:a => 1, :b => 2, :id => 4444)
905
+ end
906
+
907
+ specify "should return the value keys" do
908
+ @m.keys.size.should == 3
909
+ @m.keys.should include(:a, :b, :id)
910
+
911
+ @m = @model.new()
912
+ @m.keys.should == []
913
+ end
914
+ end
915
+
916
+ describe Sequel::Model, "#==" do
917
+ specify "should compare instances by values" do
918
+ z = Class.new(Sequel::Model)
919
+ z.columns :id, :x
920
+ a = z.load(:id => 1, :x => 3)
921
+ b = z.load(:id => 1, :x => 4)
922
+ c = z.load(:id => 1, :x => 3)
923
+
924
+ a.should_not == b
925
+ a.should == c
926
+ b.should_not == c
927
+ end
928
+
929
+ specify "should be aliased to #eql?" do
930
+ z = Class.new(Sequel::Model)
931
+ z.columns :id, :x
932
+ a = z.load(:id => 1, :x => 3)
933
+ b = z.load(:id => 1, :x => 4)
934
+ c = z.load(:id => 1, :x => 3)
935
+
936
+ a.eql?(b).should == false
937
+ a.eql?(c).should == true
938
+ b.eql?(c).should == false
939
+ end
940
+ end
941
+
942
+ describe Sequel::Model, "#===" do
943
+ specify "should compare instances by class and pk if pk is not nil" do
944
+ z = Class.new(Sequel::Model)
945
+ z.columns :id, :x
946
+ y = Class.new(Sequel::Model)
947
+ y.columns :id, :x
948
+ a = z.load(:id => 1, :x => 3)
949
+ b = z.load(:id => 1, :x => 4)
950
+ c = z.load(:id => 2, :x => 3)
951
+ d = y.load(:id => 1, :x => 3)
952
+
953
+ a.should === b
954
+ a.should_not === c
955
+ a.should_not === d
956
+ end
957
+
958
+ specify "should always be false if the primary key is nil" do
959
+ z = Class.new(Sequel::Model)
960
+ z.columns :id, :x
961
+ y = Class.new(Sequel::Model)
962
+ y.columns :id, :x
963
+ a = z.new(:x => 3)
964
+ b = z.new(:x => 4)
965
+ c = z.new(:x => 3)
966
+ d = y.new(:x => 3)
967
+
968
+ a.should_not === b
969
+ a.should_not === c
970
+ a.should_not === d
971
+ end
972
+ end
973
+
974
+ describe Sequel::Model, "#hash" do
975
+ specify "should be the same only for objects with the same class and pk if the pk is not nil" do
976
+ z = Class.new(Sequel::Model)
977
+ z.columns :id, :x
978
+ y = Class.new(Sequel::Model)
979
+ y.columns :id, :x
980
+ a = z.load(:id => 1, :x => 3)
981
+ b = z.load(:id => 1, :x => 4)
982
+ c = z.load(:id => 2, :x => 3)
983
+ d = y.load(:id => 1, :x => 3)
984
+
985
+ a.hash.should == b.hash
986
+ a.hash.should_not == c.hash
987
+ a.hash.should_not == d.hash
988
+ end
989
+
990
+ specify "should be the same only for objects with the same class and values if the pk is nil" do
991
+ z = Class.new(Sequel::Model)
992
+ z.columns :id, :x
993
+ y = Class.new(Sequel::Model)
994
+ y.columns :id, :x
995
+ a = z.new(:x => 3)
996
+ b = z.new(:x => 4)
997
+ c = z.new(:x => 3)
998
+ d = y.new(:x => 3)
999
+
1000
+ a.hash.should_not == b.hash
1001
+ a.hash.should == c.hash
1002
+ a.hash.should_not == d.hash
1003
+ end
1004
+ end
1005
+
1006
+ describe Sequel::Model, "#initialize" do
1007
+ before do
1008
+ @c = Class.new(Sequel::Model) do
1009
+ columns :id, :x
1010
+ end
1011
+ @c.strict_param_setting = false
1012
+ end
1013
+
1014
+ specify "should accept values" do
1015
+ m = @c.new(:x => 2)
1016
+ m.values.should == {:x => 2}
1017
+ end
1018
+
1019
+ specify "should not modify the primary key" do
1020
+ m = @c.new(:id => 1, :x => 2)
1021
+ m.values.should == {:x => 2}
1022
+ end
1023
+
1024
+ specify "should accept no values" do
1025
+ m = @c.new
1026
+ m.values.should == {}
1027
+ end
1028
+
1029
+ specify "should accept a block to execute" do
1030
+ m = @c.new {|o| o[:id] = 1234}
1031
+ m.id.should == 1234
1032
+ end
1033
+
1034
+ specify "should accept virtual attributes" do
1035
+ @c.send(:define_method, :blah=){|x| @blah = x}
1036
+ @c.send(:define_method, :blah){@blah}
1037
+
1038
+ m = @c.new(:x => 2, :blah => 3)
1039
+ m.values.should == {:x => 2}
1040
+ m.blah.should == 3
1041
+ end
1042
+
1043
+ specify "should convert string keys into symbol keys" do
1044
+ m = @c.new('x' => 2)
1045
+ m.values.should == {:x => 2}
1046
+ end
1047
+ end
1048
+
1049
+ describe Sequel::Model, ".create" do
1050
+
1051
+ before(:each) do
1052
+ MODEL_DB.reset
1053
+ @c = Class.new(Sequel::Model(:items)) do
1054
+ unrestrict_primary_key
1055
+ columns :x
1056
+ end
1057
+ end
1058
+
1059
+ it "should be able to create rows in the associated table" do
1060
+ o = @c.create(:x => 1)
1061
+ o.class.should == @c
1062
+ MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1)', "SELECT * FROM items WHERE (id IN ('INSERT INTO items (x) VALUES (1)')) LIMIT 1"]
1063
+ end
1064
+
1065
+ it "should be able to create rows without any values specified" do
1066
+ o = @c.create
1067
+ o.class.should == @c
1068
+ MODEL_DB.sqls.should == ["INSERT INTO items DEFAULT VALUES", "SELECT * FROM items WHERE (id IN ('INSERT INTO items DEFAULT VALUES')) LIMIT 1"]
1069
+ end
1070
+
1071
+ it "should accept a block and run it" do
1072
+ o1, o2, o3 = nil, nil, nil
1073
+ o = @c.create {|o4| o1 = o4; o3 = o4; o2 = :blah; o3.x = 333}
1074
+ o.class.should == @c
1075
+ o1.should === o
1076
+ o3.should === o
1077
+ o2.should == :blah
1078
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (333)", "SELECT * FROM items WHERE (id IN ('INSERT INTO items (x) VALUES (333)')) LIMIT 1"]
1079
+ end
1080
+
1081
+ it "should create a row for a model with custom primary key" do
1082
+ @c.set_primary_key :x
1083
+ o = @c.create(:x => 30)
1084
+ o.class.should == @c
1085
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (30)", "SELECT * FROM items WHERE (x = 30) LIMIT 1"]
1086
+ end
1087
+ end
1088
+
1089
+ describe Sequel::Model, "#refresh" do
1090
+ before do
1091
+ MODEL_DB.reset
1092
+ @c = Class.new(Sequel::Model(:items)) do
1093
+ unrestrict_primary_key
1094
+ columns :id, :x
1095
+ end
1096
+ end
1097
+
1098
+ specify "should reload the instance values from the database" do
1099
+ @m = @c.new(:id => 555)
1100
+ @m[:x] = 'blah'
1101
+ @m.this.should_receive(:first).and_return({:x => 'kaboom', :id => 555})
1102
+ @m.refresh
1103
+ @m[:x].should == 'kaboom'
1104
+ end
1105
+
1106
+ specify "should raise if the instance is not found" do
1107
+ @m = @c.new(:id => 555)
1108
+ @m.this.should_receive(:first).and_return(nil)
1109
+ proc {@m.refresh}.should raise_error(Sequel::Error)
1110
+ end
1111
+
1112
+ specify "should be aliased by #reload" do
1113
+ @m = @c.new(:id => 555)
1114
+ @m.this.should_receive(:first).and_return({:x => 'kaboom', :id => 555})
1115
+ @m.reload
1116
+ @m[:x].should == 'kaboom'
1117
+ end
1118
+
1119
+ specify "should remove cached associations" do
1120
+ @c.many_to_one :node, :class=>@c
1121
+ @m = @c.new(:id => 555)
1122
+ @m.associations[:node] = 15
1123
+ @m.reload
1124
+ @m.associations.should == {}
1125
+ end
1126
+ end
1127
+
1128
+ describe Sequel::Model, "typecasting" do
1129
+ before do
1130
+ MODEL_DB.reset
1131
+ @c = Class.new(Sequel::Model(:items)) do
1132
+ columns :x
1133
+ end
1134
+ end
1135
+
1136
+ after do
1137
+ Sequel.datetime_class = Time
1138
+ end
1139
+
1140
+ specify "should not convert if typecasting is turned off" do
1141
+ @c.typecast_on_assignment = false
1142
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer}})
1143
+ m = @c.new
1144
+ m.x = '1'
1145
+ m.x.should == '1'
1146
+ end
1147
+
1148
+ specify "should convert to integer for an integer field" do
1149
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer}})
1150
+ m = @c.new
1151
+ m.x = '1'
1152
+ m.x.should == 1
1153
+ m.x = 1
1154
+ m.x.should == 1
1155
+ m.x = 1.3
1156
+ m.x.should == 1
1157
+ end
1158
+
1159
+ specify "should typecast '' to nil unless type is string or blob" do
1160
+ [:integer, :float, :decimal, :boolean, :date, :time, :datetime].each do |x|
1161
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>x}})
1162
+ m = @c.new
1163
+ m.x = ''
1164
+ m.x.should == nil
1165
+ end
1166
+ [:string, :blob].each do |x|
1167
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>x}})
1168
+ m = @c.new
1169
+ m.x = ''
1170
+ m.x.should == ''
1171
+ end
1172
+ end
1173
+
1174
+ specify "should not typecast '' to nil if typecast_empty_string_to_nil is false" do
1175
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer}})
1176
+ m = @c.new
1177
+ m.typecast_empty_string_to_nil = false
1178
+ proc{m.x = ''}.should raise_error
1179
+ @c.typecast_empty_string_to_nil = false
1180
+ proc{@c.new.x = ''}.should raise_error
1181
+ end
1182
+
1183
+ specify "should not typecast nil if NULLs are allowed" do
1184
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer,:allow_null=>true}})
1185
+ m = @c.new
1186
+ m.x = nil
1187
+ m.x.should == nil
1188
+ end
1189
+
1190
+ specify "should raise an error if attempting to typecast nil and NULLs are not allowed" do
1191
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer,:allow_null=>false}})
1192
+ proc{@c.new.x = nil}.should raise_error(Sequel::Error)
1193
+ proc{@c.new.x = ''}.should raise_error(Sequel::Error)
1194
+ end
1195
+
1196
+ specify "should not raise an error if NULLs are not allowed and typecasting is turned off" do
1197
+ @c.typecast_on_assignment = false
1198
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer,:allow_null=>false}})
1199
+ m = @c.new
1200
+ m.x = nil
1201
+ m.x.should == nil
1202
+ end
1203
+
1204
+ specify "should not raise when typecasting nil to NOT NULL column but raise_on_typecast_failure is off" do
1205
+ @c.raise_on_typecast_failure = false
1206
+ @c.typecast_on_assignment = true
1207
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer,:allow_null=>false}})
1208
+ m = @c.new
1209
+ m.x = ''
1210
+ m.x.should == nil
1211
+ m.x = nil
1212
+ m.x.should == nil
1213
+ end
1214
+
1215
+ specify "should raise an error if invalid data is used in an integer field" do
1216
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer}})
1217
+ proc{@c.new.x = 'a'}.should raise_error(Sequel::InvalidValue)
1218
+ end
1219
+
1220
+ specify "should assign value if raise_on_typecast_failure is off and assigning invalid integer" do
1221
+ @c.raise_on_typecast_failure = false
1222
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:integer}})
1223
+ model = @c.new
1224
+ model.x = '1d'
1225
+ model.x.should == '1d'
1226
+ end
1227
+
1228
+ specify "should convert to float for a float field" do
1229
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:float}})
1230
+ m = @c.new
1231
+ m.x = '1.3'
1232
+ m.x.should == 1.3
1233
+ m.x = 1
1234
+ m.x.should == 1.0
1235
+ m.x = 1.3
1236
+ m.x.should == 1.3
1237
+ end
1238
+
1239
+ specify "should raise an error if invalid data is used in an float field" do
1240
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:float}})
1241
+ proc{@c.new.x = 'a'}.should raise_error(Sequel::InvalidValue)
1242
+ end
1243
+
1244
+ specify "should assign value if raise_on_typecast_failure is off and assigning invalid float" do
1245
+ @c.raise_on_typecast_failure = false
1246
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:float}})
1247
+ model = @c.new
1248
+ model.x = '1d'
1249
+ model.x.should == '1d'
1250
+ end
1251
+
1252
+ specify "should convert to BigDecimal for a decimal field" do
1253
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:decimal}})
1254
+ m = @c.new
1255
+ bd = BigDecimal.new('1.0')
1256
+ m.x = '1.0'
1257
+ m.x.should == bd
1258
+ m.x = 1.0
1259
+ m.x.should == bd
1260
+ m.x = 1
1261
+ m.x.should == bd
1262
+ m.x = bd
1263
+ m.x.should == bd
1264
+ end
1265
+
1266
+ specify "should raise an error if invalid data is used in an decimal field" do
1267
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:decimal}})
1268
+ proc{@c.new.x = Date.today}.should raise_error(Sequel::InvalidValue)
1269
+ end
1270
+
1271
+ specify "should assign value if raise_on_typecast_failure is off and assigning invalid decimal" do
1272
+ @c.raise_on_typecast_failure = false
1273
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:decimal}})
1274
+ model = @c.new
1275
+ time = Time.now
1276
+ model.x = time
1277
+ model.x.should == time
1278
+ end
1279
+
1280
+ specify "should convert to string for a string field" do
1281
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:string}})
1282
+ m = @c.new
1283
+ m.x = '1.3'
1284
+ m.x.should == '1.3'
1285
+ m.x = 1
1286
+ m.x.should == '1'
1287
+ m.x = 1.3
1288
+ m.x.should == '1.3'
1289
+ end
1290
+
1291
+ specify "should convert to boolean for a boolean field" do
1292
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:boolean}})
1293
+ m = @c.new
1294
+ m.x = '1.3'
1295
+ m.x.should == true
1296
+ m.x = 1
1297
+ m.x.should == true
1298
+ m.x = 1.3
1299
+ m.x.should == true
1300
+ m.x = 't'
1301
+ m.x.should == true
1302
+ m.x = 'T'
1303
+ m.x.should == true
1304
+ m.x = true
1305
+ m.x.should == true
1306
+ m.x = nil
1307
+ m.x.should == nil
1308
+ m.x = ''
1309
+ m.x.should == nil
1310
+ m.x = []
1311
+ m.x.should == nil
1312
+ m.x = 'f'
1313
+ m.x.should == false
1314
+ m.x = 'F'
1315
+ m.x.should == false
1316
+ m.x = 'false'
1317
+ m.x.should == false
1318
+ m.x = 'FALSE'
1319
+ m.x.should == false
1320
+ m.x = '0'
1321
+ m.x.should == false
1322
+ m.x = 0
1323
+ m.x.should == false
1324
+ m.x = false
1325
+ m.x.should == false
1326
+ end
1327
+
1328
+ specify "should convert to date for a date field" do
1329
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:date}})
1330
+ m = @c.new
1331
+ y = Date.new(2007,10,21)
1332
+ m.x = '2007-10-21'
1333
+ m.x.should == y
1334
+ m.x = Date.parse('2007-10-21')
1335
+ m.x.should == y
1336
+ m.x = Time.parse('2007-10-21')
1337
+ m.x.should == y
1338
+ m.x = DateTime.parse('2007-10-21')
1339
+ m.x.should == y
1340
+ end
1341
+
1342
+ specify "should accept a hash with symbol or string keys for a date field" do
1343
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:date}})
1344
+ m = @c.new
1345
+ y = Date.new(2007,10,21)
1346
+ m.x = {:year=>2007, :month=>10, :day=>21}
1347
+ m.x.should == y
1348
+ m.x = {'year'=>'2007', 'month'=>'10', 'day'=>'21'}
1349
+ m.x.should == y
1350
+ end
1351
+
1352
+ specify "should raise an error if invalid data is used in a date field" do
1353
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:date}})
1354
+ proc{@c.new.x = 'a'}.should raise_error(Sequel::InvalidValue)
1355
+ proc{@c.new.x = 100}.should raise_error(Sequel::InvalidValue)
1356
+ end
1357
+
1358
+ specify "should assign value if raise_on_typecast_failure is off and assigning invalid date" do
1359
+ @c.raise_on_typecast_failure = false
1360
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:date}})
1361
+ model = @c.new
1362
+ model.x = 4
1363
+ model.x.should == 4
1364
+ end
1365
+
1366
+ specify "should convert to time for a time field" do
1367
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:time}})
1368
+ m = @c.new
1369
+ x = '10:20:30'
1370
+ y = Time.parse(x)
1371
+ m.x = x
1372
+ m.x.should == y
1373
+ m.x = y
1374
+ m.x.should == y
1375
+ end
1376
+
1377
+ specify "should accept a hash with symbol or string keys for a time field" do
1378
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:time}})
1379
+ m = @c.new
1380
+ y = Time.parse('10:20:30')
1381
+ m.x = {:hour=>10, :minute=>20, :second=>30}
1382
+ m.x.should == y
1383
+ m.x = {'hour'=>'10', 'minute'=>'20', 'second'=>'30'}
1384
+ m.x.should == y
1385
+ end
1386
+
1387
+ specify "should raise an error if invalid data is used in a time field" do
1388
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:time}})
1389
+ proc{@c.new.x = '0000'}.should raise_error
1390
+ proc{@c.new.x = 'a'}.should_not raise_error # Valid Time
1391
+ proc{@c.new.x = Date.parse('2008-10-21')}.should raise_error(Sequel::InvalidValue)
1392
+ proc{@c.new.x = DateTime.parse('2008-10-21')}.should raise_error(Sequel::InvalidValue)
1393
+ end
1394
+
1395
+ specify "should assign value if raise_on_typecast_failure is off and assigning invalid time" do
1396
+ @c.raise_on_typecast_failure = false
1397
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:time}})
1398
+ model = @c.new
1399
+ model.x = '0000'
1400
+ model.x.should == '0000'
1401
+ end
1402
+
1403
+ specify "should convert to the Sequel.datetime_class for a datetime field" do
1404
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:datetime}})
1405
+ m = @c.new
1406
+ x = '2007-10-21T10:20:30-07:00'
1407
+ y = Time.parse(x)
1408
+ m.x = x
1409
+ m.x.should == y
1410
+ m.x = DateTime.parse(x)
1411
+ m.x.should == y
1412
+ m.x = Time.parse(x)
1413
+ m.x.should == y
1414
+ m.x = Date.parse('2007-10-21')
1415
+ m.x.should == Time.parse('2007-10-21')
1416
+ Sequel.datetime_class = DateTime
1417
+ y = DateTime.parse(x)
1418
+ m.x = x
1419
+ m.x.should == y
1420
+ m.x = DateTime.parse(x)
1421
+ m.x.should == y
1422
+ m.x = Time.parse(x)
1423
+ m.x.should == y
1424
+ m.x = Date.parse('2007-10-21')
1425
+ m.x.should == DateTime.parse('2007-10-21')
1426
+ end
1427
+
1428
+ specify "should accept a hash with symbol or string keys for a datetime field" do
1429
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:datetime}})
1430
+ m = @c.new
1431
+ y = Time.parse('2007-10-21 10:20:30')
1432
+ m.x = {:year=>2007, :month=>10, :day=>21, :hour=>10, :minute=>20, :second=>30}
1433
+ m.x.should == y
1434
+ m.x = {'year'=>'2007', 'month'=>'10', 'day'=>'21', 'hour'=>'10', 'minute'=>'20', 'second'=>'30'}
1435
+ m.x.should == y
1436
+ Sequel.datetime_class = DateTime
1437
+ y = DateTime.parse('2007-10-21 10:20:30')
1438
+ m.x = {:year=>2007, :month=>10, :day=>21, :hour=>10, :minute=>20, :second=>30}
1439
+ m.x.should == y
1440
+ m.x = {'year'=>'2007', 'month'=>'10', 'day'=>'21', 'hour'=>'10', 'minute'=>'20', 'second'=>'30'}
1441
+ m.x.should == y
1442
+ end
1443
+
1444
+ specify "should raise an error if invalid data is used in a datetime field" do
1445
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:datetime}})
1446
+ proc{@c.new.x = '0000'}.should raise_error(Sequel::InvalidValue)
1447
+ proc{@c.new.x = 'a'}.should_not raise_error # Valid Time
1448
+ Sequel.datetime_class = DateTime
1449
+ proc{@c.new.x = '0000'}.should raise_error(Sequel::InvalidValue)
1450
+ proc{@c.new.x = 'a'}.should raise_error(Sequel::InvalidValue)
1451
+ end
1452
+
1453
+ specify "should assign value if raise_on_typecast_failure is off and assigning invalid datetime" do
1454
+ @c.raise_on_typecast_failure = false
1455
+ @c.instance_variable_set(:@db_schema, {:x=>{:type=>:datetime}})
1456
+ model = @c.new
1457
+ model.x = '0000'
1458
+ model.x.should == '0000'
1459
+ Sequel.datetime_class = DateTime
1460
+ model = @c.new
1461
+ model.x = '0000'
1462
+ model.x.should == '0000'
1463
+ model.x = 'a'
1464
+ model.x.should == 'a'
1465
+ end
1466
+ end
1467
+
1468
+ describe "Model#lock!" do
1469
+ before do
1470
+ @c = Class.new(Sequel::Model(:items)) do
1471
+ columns :id
1472
+ end
1473
+ ds = @c.dataset
1474
+ def ds.fetch_rows(sql)
1475
+ db.execute(sql)
1476
+ yield({:id=>1})
1477
+ end
1478
+ MODEL_DB.reset
1479
+ end
1480
+
1481
+ it "should do nothing if the record is a new record" do
1482
+ o = @c.new
1483
+ called = false
1484
+ o.meta_def(:_refresh){|x| called = true; super(x)}
1485
+ x = o.lock!
1486
+ x.should == o
1487
+ called.should == false
1488
+ MODEL_DB.sqls.should == []
1489
+ end
1490
+
1491
+ it "should refresh the record using for_update if it is not a new record" do
1492
+ o = @c.load(:id => 1)
1493
+ called = false
1494
+ o.meta_def(:_refresh){|x| called = true; super(x)}
1495
+ x = o.lock!
1496
+ x.should == o
1497
+ called.should == true
1498
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (id = 1) LIMIT 1 FOR UPDATE"]
1499
+ end
1500
+ end