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,438 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::ValidationHelpers" do
4
+ before do
5
+ @c = Class.new(Sequel::Model) do
6
+ def self.set_validations(&block)
7
+ define_method(:validate, &block)
8
+ end
9
+ columns :value
10
+ end
11
+ @c.plugin :validation_helpers
12
+ @m = @c.new
13
+ end
14
+
15
+ specify "should take an :allow_blank option" do
16
+ @c.set_validations{validates_format(/.+_.+/, :value, :allow_blank=>true)}
17
+ @m.value = 'abc_'
18
+ @m.should_not be_valid
19
+ @m.value = '1_1'
20
+ @m.should be_valid
21
+ o = Object.new
22
+ @m.value = o
23
+ @m.should_not be_valid
24
+ def o.blank?
25
+ true
26
+ end
27
+ @m.should be_valid
28
+ end
29
+
30
+ specify "should take an :allow_missing option" do
31
+ @c.set_validations{validates_format(/.+_.+/, :value, :allow_missing=>true)}
32
+ @m.values.clear
33
+ @m.should be_valid
34
+ @m.value = nil
35
+ @m.should_not be_valid
36
+ @m.value = '1_1'
37
+ @m.should be_valid
38
+ end
39
+
40
+ specify "should take an :allow_nil option" do
41
+ @c.set_validations{validates_format(/.+_.+/, :value, :allow_nil=>true)}
42
+ @m.value = 'abc_'
43
+ @m.should_not be_valid
44
+ @m.value = '1_1'
45
+ @m.should be_valid
46
+ @m.value = nil
47
+ @m.should be_valid
48
+ end
49
+
50
+ specify "should take a :message option" do
51
+ @c.set_validations{validates_format(/.+_.+/, :value, :message=>"is so blah")}
52
+ @m.value = 'abc_'
53
+ @m.should_not be_valid
54
+ @m.errors.full_messages.should == ['value is so blah']
55
+ @m.value = '1_1'
56
+ @m.should be_valid
57
+ end
58
+
59
+ specify "should allow a proc for the :message option" do
60
+ @c.set_validations{validates_format(/.+_.+/, :value, :message=>proc{|f| "doesn't match #{f.inspect}"})}
61
+ @m.value = 'abc_'
62
+ @m.should_not be_valid
63
+ @m.errors.should == {:value=>["doesn't match /.+_.+/"]}
64
+ end
65
+
66
+ specify "should take multiple attributes in the same call" do
67
+ @c.columns :value, :value2
68
+ @c.set_validations{validates_presence([:value, :value2])}
69
+ @m.should_not be_valid
70
+ @m.value = 1
71
+ @m.should_not be_valid
72
+ @m.value2 = 1
73
+ @m.should be_valid
74
+ end
75
+
76
+ specify "should support modifying default options for all models" do
77
+ @c.set_validations{validates_presence(:value)}
78
+ @m.should_not be_valid
79
+ @m.errors.should == {:value=>['is not present']}
80
+ o = Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS[:presence].dup
81
+ Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS[:presence][:message] = lambda{"was not entered"}
82
+ @m.should_not be_valid
83
+ @m.errors.should == {:value=>["was not entered"]}
84
+ @m.value = 1
85
+ @m.should be_valid
86
+
87
+ @m.values.clear
88
+ Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS[:presence][:allow_missing] = true
89
+ @m.should be_valid
90
+ @m.value = nil
91
+ @m.should_not be_valid
92
+ @m.errors.should == {:value=>["was not entered"]}
93
+
94
+
95
+ c = Class.new(Sequel::Model)
96
+ c.class_eval do
97
+ plugin :validation_helpers
98
+ set_columns([:value])
99
+ def validate
100
+ validates_presence(:value)
101
+ end
102
+ end
103
+ m = c.new(:value=>nil)
104
+ m.should_not be_valid
105
+ m.errors.should == {:value=>["was not entered"]}
106
+ Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS[:presence] = o
107
+ end
108
+
109
+ specify "should support modifying default validation options for a particular model" do
110
+ @c.set_validations{validates_presence(:value)}
111
+ @m.should_not be_valid
112
+ @m.errors.should == {:value=>['is not present']}
113
+ @c.class_eval do
114
+ def default_validation_helpers_options(type)
115
+ {:allow_missing=>true, :message=>proc{'was not entered'}}
116
+ end
117
+ end
118
+ @m.value = nil
119
+ @m.should_not be_valid
120
+ @m.errors.should == {:value=>["was not entered"]}
121
+ @m.value = 1
122
+ @m.should be_valid
123
+
124
+ @m.values.clear
125
+ @m.should be_valid
126
+
127
+ c = Class.new(Sequel::Model)
128
+ c.class_eval do
129
+ plugin :validation_helpers
130
+ attr_accessor :value
131
+ def validate
132
+ validates_presence(:value)
133
+ end
134
+ end
135
+ m = c.new
136
+ m.should_not be_valid
137
+ m.errors.should == {:value=>['is not present']}
138
+ end
139
+
140
+ specify "should support validates_exact_length" do
141
+ @c.set_validations{validates_exact_length(3, :value)}
142
+ @m.should_not be_valid
143
+ @m.value = '123'
144
+ @m.should be_valid
145
+ @m.value = '12'
146
+ @m.should_not be_valid
147
+ @m.value = '1234'
148
+ @m.should_not be_valid
149
+ end
150
+
151
+ specify "should support validate_format" do
152
+ @c.set_validations{validates_format(/.+_.+/, :value)}
153
+ @m.value = 'abc_'
154
+ @m.should_not be_valid
155
+ @m.value = 'abc_def'
156
+ @m.should be_valid
157
+ end
158
+
159
+ specify "should support validates_includes with an array" do
160
+ @c.set_validations{validates_includes([1,2], :value)}
161
+ @m.should_not be_valid
162
+ @m.value = 1
163
+ @m.should be_valid
164
+ @m.value = 1.5
165
+ @m.should_not be_valid
166
+ @m.value = 2
167
+ @m.should be_valid
168
+ @m.value = 3
169
+ @m.should_not be_valid
170
+ end
171
+
172
+ specify "should support validates_includes with a range" do
173
+ @c.set_validations{validates_includes(1..4, :value)}
174
+ @m.should_not be_valid
175
+ @m.value = 1
176
+ @m.should be_valid
177
+ @m.value = 1.5
178
+ @m.should be_valid
179
+ @m.value = 0
180
+ @m.should_not be_valid
181
+ @m.value = 5
182
+ @m.should_not be_valid
183
+ end
184
+
185
+ specify "should supports validates_integer" do
186
+ @c.set_validations{validates_integer(:value)}
187
+ @m.value = 'blah'
188
+ @m.should_not be_valid
189
+ @m.value = '123'
190
+ @m.should be_valid
191
+ @m.value = '123.1231'
192
+ @m.should_not be_valid
193
+ end
194
+
195
+ specify "should support validates_length_range" do
196
+ @c.set_validations{validates_length_range(2..5, :value)}
197
+ @m.should_not be_valid
198
+ @m.value = '12345'
199
+ @m.should be_valid
200
+ @m.value = '1'
201
+ @m.should_not be_valid
202
+ @m.value = '123456'
203
+ @m.should_not be_valid
204
+ end
205
+
206
+ specify "should support validates_max_length" do
207
+ @c.set_validations{validates_max_length(5, :value)}
208
+ @m.should_not be_valid
209
+ @m.value = '12345'
210
+ @m.should be_valid
211
+ @m.value = '123456'
212
+ @m.should_not be_valid
213
+ end
214
+
215
+ specify "should support validates_min_length" do
216
+ @c.set_validations{validates_min_length(5, :value)}
217
+ @m.should_not be_valid
218
+ @m.value = '12345'
219
+ @m.should be_valid
220
+ @m.value = '1234'
221
+ @m.should_not be_valid
222
+ end
223
+
224
+ specify "should support validates_not_string" do
225
+ @c.set_validations{validates_not_string(:value)}
226
+ @m.value = 123
227
+ @m.should be_valid
228
+ @m.value = '123'
229
+ @m.should_not be_valid
230
+ @m.errors.full_messages.should == ['value is a string']
231
+ @m.meta_def(:db_schema){{:value=>{:type=>:integer}}}
232
+ @m.should_not be_valid
233
+ @m.errors.full_messages.should == ['value is not a valid integer']
234
+ end
235
+
236
+ specify "should support validates_numeric" do
237
+ @c.set_validations{validates_numeric(:value)}
238
+ @m.value = 'blah'
239
+ @m.should_not be_valid
240
+ @m.value = '123'
241
+ @m.should be_valid
242
+ @m.value = '123.1231'
243
+ @m.should be_valid
244
+ @m.value = '+1'
245
+ @m.should be_valid
246
+ @m.value = '-1'
247
+ @m.should be_valid
248
+ @m.value = '+1.123'
249
+ @m.should be_valid
250
+ @m.value = '-0.123'
251
+ @m.should be_valid
252
+ @m.value = '-0.123E10'
253
+ @m.should be_valid
254
+ @m.value = '32.123e10'
255
+ @m.should be_valid
256
+ @m.value = '+32.123E10'
257
+ @m.should be_valid
258
+ @m.should be_valid
259
+ @m.value = '.0123'
260
+ end
261
+
262
+ specify "should support validates_type" do
263
+ @c.set_validations{validates_type(Integer, :value)}
264
+ @m.value = 123
265
+ @m.should be_valid
266
+ @m.value = '123'
267
+ @m.should_not be_valid
268
+ @m.errors.full_messages.should == ['value is not a Integer']
269
+
270
+ @c.set_validations{validates_type(:String, :value)}
271
+ @m.value = '123'
272
+ @m.should be_valid
273
+ @m.value = 123
274
+ @m.should_not be_valid
275
+ @m.errors.full_messages.should == ['value is not a String']
276
+
277
+ @c.set_validations{validates_type('Integer', :value)}
278
+ @m.value = 123
279
+ @m.should be_valid
280
+ @m.value = 123.05
281
+ @m.should_not be_valid
282
+ @m.errors.full_messages.should == ['value is not a Integer']
283
+ end
284
+
285
+ specify "should support validates_presence" do
286
+ @c.set_validations{validates_presence(:value)}
287
+ @m.should_not be_valid
288
+ @m.value = ''
289
+ @m.should_not be_valid
290
+ @m.value = 1234
291
+ @m.should be_valid
292
+ @m.value = nil
293
+ @m.should_not be_valid
294
+ @m.value = true
295
+ @m.should be_valid
296
+ @m.value = false
297
+ @m.should be_valid
298
+ @m.value = Time.now
299
+ @m.should be_valid
300
+ end
301
+
302
+ it "should support validates_unique with a single attribute" do
303
+ @c.columns(:id, :username, :password)
304
+ @c.set_dataset MODEL_DB[:items]
305
+ @c.set_validations{validates_unique(:username)}
306
+ @c.dataset.extend(Module.new {
307
+ def fetch_rows(sql)
308
+ @db << sql
309
+
310
+ case sql
311
+ when /COUNT.*username = '0records'/
312
+ yield({:v => 0})
313
+ when /COUNT.*username = '1record'/
314
+ yield({:v => 1})
315
+ end
316
+ end
317
+ })
318
+
319
+ @user = @c.new(:username => "0records", :password => "anothertest")
320
+ @user.should be_valid
321
+ @user = @c.load(:id=>3, :username => "0records", :password => "anothertest")
322
+ @user.should be_valid
323
+
324
+ @user = @c.new(:username => "1record", :password => "anothertest")
325
+ @user.should_not be_valid
326
+ @user.errors.full_messages.should == ['username is already taken']
327
+ @user = @c.load(:id=>4, :username => "1record", :password => "anothertest")
328
+ @user.should_not be_valid
329
+ @user.errors.full_messages.should == ['username is already taken']
330
+
331
+ ds1 = @c.dataset.filter([[:username, '0records']])
332
+ ds2 = ds1.exclude(:id=>1)
333
+ @c.dataset.should_receive(:filter).with([[:username, '0records']]).twice.and_return(ds1)
334
+ ds1.should_receive(:exclude).with(:id=>1).once.and_return(ds2)
335
+
336
+ @user = @c.load(:id=>1, :username => "0records", :password => "anothertest")
337
+ @user.should be_valid
338
+ MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (id != 1)) LIMIT 1"
339
+ @user = @c.new(:username => "0records", :password => "anothertest")
340
+ @user.should be_valid
341
+ MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE (username = '0records') LIMIT 1"
342
+ end
343
+
344
+ it "should support validates_unique with multiple attributes" do
345
+ @c.columns(:id, :username, :password)
346
+ @c.set_dataset MODEL_DB[:items]
347
+ @c.set_validations{validates_unique([:username, :password])}
348
+ @c.dataset.extend(Module.new {
349
+ def fetch_rows(sql)
350
+ @db << sql
351
+
352
+ case sql
353
+ when /COUNT.*username = '0records'/
354
+ yield({:v => 0})
355
+ when /COUNT.*username = '1record'/
356
+ yield({:v => 1})
357
+ end
358
+ end
359
+ })
360
+
361
+ @user = @c.new(:username => "0records", :password => "anothertest")
362
+ @user.should be_valid
363
+ @user = @c.load(:id=>3, :username => "0records", :password => "anothertest")
364
+ @user.should be_valid
365
+
366
+ @user = @c.new(:username => "1record", :password => "anothertest")
367
+ @user.should_not be_valid
368
+ @user.errors.full_messages.should == ['username and password is already taken']
369
+ @user = @c.load(:id=>4, :username => "1record", :password => "anothertest")
370
+ @user.should_not be_valid
371
+ @user.errors.full_messages.should == ['username and password is already taken']
372
+
373
+ ds1 = @c.dataset.filter([[:username, '0records'], [:password, 'anothertest']])
374
+ ds2 = ds1.exclude(:id=>1)
375
+ @c.dataset.should_receive(:filter).with([[:username, '0records'], [:password, 'anothertest']]).twice.and_return(ds1)
376
+ ds1.should_receive(:exclude).with(:id=>1).once.and_return(ds2)
377
+
378
+ @user = @c.load(:id=>1, :username => "0records", :password => "anothertest")
379
+ @user.should be_valid
380
+ MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (password = 'anothertest') AND (id != 1)) LIMIT 1"
381
+ @user = @c.new(:username => "0records", :password => "anothertest")
382
+ @user.should be_valid
383
+ MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (password = 'anothertest')) LIMIT 1"
384
+ end
385
+
386
+ it "should support validates_unique with a block" do
387
+ @c.columns(:id, :username, :password)
388
+ @c.set_dataset MODEL_DB[:items]
389
+ @c.set_validations{validates_unique(:username){|ds| ds.filter(:active)}}
390
+ @c.dataset.extend(Module.new {
391
+ def fetch_rows (sql)
392
+ @db << sql
393
+ yield({:v => 0})
394
+ end
395
+ })
396
+
397
+ MODEL_DB.reset
398
+ @c.new(:username => "0records", :password => "anothertest").should be_valid
399
+ @c.load(:id=>3, :username => "0records", :password => "anothertest").should be_valid
400
+ MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND active) LIMIT 1",
401
+ "SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND active AND (id != 3)) LIMIT 1"]
402
+ end
403
+
404
+ it "should support :only_if_modified option for validates_unique, and not check uniqueness for existing records if values haven't changed" do
405
+ @c.columns(:id, :username, :password)
406
+ @c.set_dataset MODEL_DB[:items]
407
+ @c.set_validations{validates_unique([:username, :password], :only_if_modified=>true)}
408
+
409
+ @c.dataset.extend(Module.new {
410
+ def fetch_rows (sql)
411
+ @db << sql
412
+ yield({:v => 0})
413
+ end
414
+ })
415
+
416
+ MODEL_DB.reset
417
+ @c.new(:username => "0records", :password => "anothertest").should be_valid
418
+ MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (password = 'anothertest')) LIMIT 1"]
419
+ MODEL_DB.reset
420
+ m = @c.load(:id=>3, :username => "0records", :password => "anothertest")
421
+ m.should be_valid
422
+ MODEL_DB.sqls.should == []
423
+
424
+ m.username = '1'
425
+ m.should be_valid
426
+ MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '1') AND (password = 'anothertest') AND (id != 3)) LIMIT 1"]
427
+
428
+ m = @c.load(:id=>3, :username => "0records", :password => "anothertest")
429
+ MODEL_DB.reset
430
+ m.password = '1'
431
+ m.should be_valid
432
+ MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (password = '1') AND (id != 3)) LIMIT 1"]
433
+ MODEL_DB.reset
434
+ m.username = '2'
435
+ m.should be_valid
436
+ MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '2') AND (password = '1') AND (id != 3)) LIMIT 1"]
437
+ end
438
+ end
@@ -0,0 +1,281 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ shared_examples_for "regular and composite key associations" do
4
+ specify "should return no objects if none are associated" do
5
+ @album.artist.should == nil
6
+ @artist.albums.should == []
7
+ @album.tags.should == []
8
+ @tag.albums.should == []
9
+ end
10
+
11
+ specify "should have add and set methods work any associated objects" do
12
+ @album.update(:artist => @artist)
13
+ @album.add_tag(@tag)
14
+
15
+ @album.reload
16
+ @artist.reload
17
+ @tag.reload
18
+
19
+ @album.artist.should == @artist
20
+ @artist.albums.should == [@album]
21
+ @album.tags.should == [@tag]
22
+ @tag.albums.should == [@album]
23
+ end
24
+
25
+ specify "should have remove methods work" do
26
+ @album.update(:artist => @artist)
27
+ @album.add_tag(@tag)
28
+
29
+ @album.update(:artist => nil)
30
+ @album.remove_tag(@tag)
31
+
32
+ @album.reload
33
+ @artist.reload
34
+ @tag.reload
35
+
36
+ @album.artist.should == nil
37
+ @artist.albums.should == []
38
+ @album.tags.should == []
39
+ @tag.albums.should == []
40
+ end
41
+
42
+ specify "should have remove_all methods work" do
43
+ @artist.add_album(@album)
44
+ @album.add_tag(@tag)
45
+
46
+ @artist.remove_all_albums
47
+ @album.remove_all_tags
48
+
49
+ @album.reload
50
+ @artist.reload
51
+ @tag.reload
52
+
53
+ @album.artist.should == nil
54
+ @artist.albums.should == []
55
+ @album.tags.should == []
56
+ @tag.albums.should == []
57
+ end
58
+
59
+ specify "should eager load via eager correctly" do
60
+ @album.update(:artist => @artist)
61
+ @album.add_tag(@tag)
62
+
63
+ a = Artist.eager(:albums=>:tags).all
64
+ a.should == [@artist]
65
+ a.first.albums.should == [@album]
66
+ a.first.albums.first.tags.should == [@tag]
67
+
68
+ a = Tag.eager(:albums=>:artist).all
69
+ a.should == [@tag]
70
+ a.first.albums.should == [@album]
71
+ a.first.albums.first.artist.should == @artist
72
+ end
73
+
74
+ specify "should eager load via eager_graph correctly" do
75
+ @album.update(:artist => @artist)
76
+ @album.add_tag(@tag)
77
+
78
+ a = Artist.eager_graph(:albums=>:tags).all
79
+ a.should == [@artist]
80
+ a.first.albums.should == [@album]
81
+ a.first.albums.first.tags.should == [@tag]
82
+
83
+ a = Tag.eager_graph(:albums=>:artist).all
84
+ a.should == [@tag]
85
+ a.first.albums.should == [@album]
86
+ a.first.albums.first.artist.should == @artist
87
+ end
88
+
89
+ specify "should work with a many_through_many association" do
90
+ @album.update(:artist => @artist)
91
+ @album.add_tag(@tag)
92
+
93
+ @album.reload
94
+ @artist.reload
95
+ @tag.reload
96
+
97
+ @album.tags.should == [@tag]
98
+
99
+ a = Artist.eager(:tags).all
100
+ a.should == [@artist]
101
+ a.first.tags.should == [@tag]
102
+
103
+ a = Artist.eager_graph(:tags).all
104
+ a.should == [@artist]
105
+ a.first.tags.should == [@tag]
106
+
107
+ a = Album.eager(:artist=>:tags).all
108
+ a.should == [@album]
109
+ a.first.artist.should == @artist
110
+ a.first.artist.tags.should == [@tag]
111
+
112
+ a = Album.eager_graph(:artist=>:tags).all
113
+ a.should == [@album]
114
+ a.first.artist.should == @artist
115
+ a.first.artist.tags.should == [@tag]
116
+ end
117
+ end
118
+
119
+ describe "Sequel::Model Simple Associations" do
120
+ before do
121
+ @db = INTEGRATION_DB
122
+ @db.create_table!(:artists) do
123
+ primary_key :id
124
+ String :name
125
+ end
126
+ @db.create_table!(:albums) do
127
+ primary_key :id
128
+ String :name
129
+ foreign_key :artist_id, :artists
130
+ end
131
+ @db.create_table!(:tags) do
132
+ primary_key :id
133
+ String :name
134
+ end
135
+ @db.create_table!(:albums_tags) do
136
+ foreign_key :album_id, :albums
137
+ foreign_key :tag_id, :tags
138
+ end
139
+ class ::Artist < Sequel::Model(@db)
140
+ one_to_many :albums
141
+ plugin :many_through_many
142
+ Artist.many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]]
143
+ end
144
+ class ::Album < Sequel::Model(@db)
145
+ many_to_one :artist
146
+ many_to_many :tags
147
+ end
148
+ class ::Tag < Sequel::Model(@db)
149
+ many_to_many :albums
150
+ end
151
+ @album = Album.create(:name=>'Al')
152
+ @artist = Artist.create(:name=>'Ar')
153
+ @tag = Tag.create(:name=>'T')
154
+ end
155
+ after do
156
+ @db.drop_table(:albums_tags, :tags, :albums, :artists)
157
+ [:Tag, :Album, :Artist].each{|x| Object.send(:remove_const, x)}
158
+ end
159
+
160
+ it_should_behave_like "regular and composite key associations"
161
+
162
+ specify "should have add method accept hashes and create new records" do
163
+ @artist.remove_all_albums
164
+ Album.delete
165
+ @album = @artist.add_album(:name=>'Al2')
166
+ Album.first[:name].should == 'Al2'
167
+ @artist.albums_dataset.first[:name].should == 'Al2'
168
+
169
+ @album.remove_all_tags
170
+ Tag.delete
171
+ @album.add_tag(:name=>'T2')
172
+ Tag.first[:name].should == 'T2'
173
+ @album.tags_dataset.first[:name].should == 'T2'
174
+ end
175
+
176
+ specify "should have remove method accept primary key and remove related album" do
177
+ @artist.add_album(@album)
178
+ @artist.reload.remove_album(@album.id)
179
+ @artist.reload.albums.should == []
180
+
181
+ @album.add_tag(@tag)
182
+ @album.reload.remove_tag(@tag.id)
183
+ @tag.reload.albums.should == []
184
+ end
185
+
186
+ specify "should have remove method raise an error for one_to_many records if the object isn't already associated" do
187
+ proc{@artist.remove_album(@album.id)}.should raise_error(Sequel::Error)
188
+ proc{@artist.remove_album(@album)}.should raise_error(Sequel::Error)
189
+ end
190
+ end
191
+
192
+ describe "Sequel::Model Composite Key Associations" do
193
+ before do
194
+ @db = INTEGRATION_DB
195
+ @db.create_table!(:artists) do
196
+ Integer :id1
197
+ Integer :id2
198
+ String :name
199
+ primary_key [:id1, :id2]
200
+ end
201
+ @db.create_table!(:albums) do
202
+ Integer :id1
203
+ Integer :id2
204
+ String :name
205
+ Integer :artist_id1
206
+ Integer :artist_id2
207
+ foreign_key [:artist_id1, :artist_id2], :artists
208
+ primary_key [:id1, :id2]
209
+ end
210
+ @db.create_table!(:tags) do
211
+ Integer :id1
212
+ Integer :id2
213
+ String :name
214
+ primary_key [:id1, :id2]
215
+ end
216
+ @db.create_table!(:albums_tags) do
217
+ Integer :album_id1
218
+ Integer :album_id2
219
+ Integer :tag_id1
220
+ Integer :tag_id2
221
+ foreign_key [:album_id1, :album_id2], :albums
222
+ foreign_key [:tag_id1, :tag_id2], :tags
223
+ end
224
+ class ::Artist < Sequel::Model(@db)
225
+ set_primary_key :id1, :id2
226
+ unrestrict_primary_key
227
+ one_to_many :albums, :key=>[:artist_id1, :artist_id2]
228
+ plugin :many_through_many
229
+ Artist.many_through_many :tags, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]]
230
+ end
231
+ class ::Album < Sequel::Model(@db)
232
+ set_primary_key :id1, :id2
233
+ unrestrict_primary_key
234
+ many_to_one :artist, :key=>[:artist_id1, :artist_id2]
235
+ many_to_many :tags, :left_key=>[:album_id1, :album_id2], :right_key=>[:tag_id1, :tag_id2]
236
+ end
237
+ class ::Tag < Sequel::Model(@db)
238
+ set_primary_key :id1, :id2
239
+ unrestrict_primary_key
240
+ many_to_many :albums, :right_key=>[:album_id1, :album_id2], :left_key=>[:tag_id1, :tag_id2]
241
+ end
242
+ @album = Album.create(:name=>'Al', :id1=>1, :id2=>2)
243
+ @artist = Artist.create(:name=>'Ar', :id1=>3, :id2=>4)
244
+ @tag = Tag.create(:name=>'T', :id1=>5, :id2=>6)
245
+ end
246
+ after do
247
+ @db.drop_table(:albums_tags, :tags, :albums, :artists)
248
+ [:Tag, :Album, :Artist].each{|x| Object.send(:remove_const, x)}
249
+ end
250
+
251
+ it_should_behave_like "regular and composite key associations"
252
+
253
+ specify "should have add method accept hashes and create new records" do
254
+ @artist.remove_all_albums
255
+ Album.delete
256
+ @artist.add_album(:id1=>1, :id2=>2, :name=>'Al2')
257
+ Album.first[:name].should == 'Al2'
258
+ @artist.albums_dataset.first[:name].should == 'Al2'
259
+
260
+ @album.remove_all_tags
261
+ Tag.delete
262
+ @album.add_tag(:id1=>1, :id2=>2, :name=>'T2')
263
+ Tag.first[:name].should == 'T2'
264
+ @album.tags_dataset.first[:name].should == 'T2'
265
+ end
266
+
267
+ specify "should have remove method accept primary key and remove related album" do
268
+ @artist.add_album(@album)
269
+ @artist.reload.remove_album([@album.id1, @album.id2])
270
+ @artist.reload.albums.should == []
271
+
272
+ @album.add_tag(@tag)
273
+ @album.reload.remove_tag([@tag.id1, @tag.id2])
274
+ @tag.reload.albums.should == []
275
+ end
276
+
277
+ specify "should have remove method raise an error for one_to_many records if the object isn't already associated" do
278
+ proc{@artist.remove_album([@album.id1, @album.id2])}.should raise_error(Sequel::Error)
279
+ proc{@artist.remove_album(@album)}.should raise_error(Sequel::Error)
280
+ end
281
+ end