viking-sequel 3.10.0

Sign up to get free protection for your applications and to get access to all the features.
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