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,250 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sequel::Model, "caching" do
4
+ before do
5
+ MODEL_DB.reset
6
+
7
+ @cache_class = Class.new(Hash) do
8
+ attr_accessor :ttl
9
+ def set(k, v, ttl); self[k] = v; @ttl = ttl; end
10
+ def get(k); self[k]; end
11
+ end
12
+ cache = @cache_class.new
13
+ @cache = cache
14
+
15
+ @memcached_class = Class.new(Hash) do
16
+ attr_accessor :ttl
17
+ def set(k, v, ttl); self[k] = v; @ttl = ttl; end
18
+ def get(k); if self[k] then return self[k]; else raise ArgumentError; end end
19
+ end
20
+ cache2 = @memcached_class.new
21
+ @memcached = cache2
22
+
23
+ @c = Class.new(Sequel::Model(:items))
24
+ @c.class_eval do
25
+ plugin :caching, cache
26
+ def self.name; 'Item' end
27
+
28
+ columns :name, :id
29
+ end
30
+
31
+ @c3 = Class.new(Sequel::Model(:items))
32
+ @c3.class_eval do
33
+ plugin :caching, cache2
34
+ def self.name; 'Item' end
35
+
36
+ columns :name, :id
37
+ end
38
+
39
+ @c4 = Class.new(Sequel::Model(:items))
40
+ @c4.class_eval do
41
+ plugin :caching, cache2, :ignore_exceptions => true
42
+ def self.name; 'Item' end
43
+
44
+ columns :name, :id
45
+ end
46
+
47
+
48
+ $cache_dataset_row = {:name => 'sharon', :id => 1}
49
+ @dataset = @c.dataset = @c3.dataset = @c4.dataset
50
+ $sqls = []
51
+ @dataset.extend(Module.new {
52
+ def fetch_rows(sql)
53
+ $sqls << sql
54
+ yield $cache_dataset_row
55
+ end
56
+
57
+ def update(values)
58
+ $sqls << update_sql(values)
59
+ $cache_dataset_row.merge!(values)
60
+ 1
61
+ end
62
+
63
+ def delete
64
+ $sqls << delete_sql
65
+ 1
66
+ end
67
+ })
68
+
69
+ @c2 = Class.new(@c) do
70
+ def self.name; 'SubItem' end
71
+ end
72
+ end
73
+
74
+ it "should set the model's cache store" do
75
+ @c.cache_store.should be(@cache)
76
+ @c2.cache_store.should be(@cache)
77
+ end
78
+
79
+ it "should have a default ttl of 3600" do
80
+ @c.cache_ttl.should == 3600
81
+ @c2.cache_ttl.should == 3600
82
+ end
83
+
84
+ it "should take a ttl option" do
85
+ c = Class.new(Sequel::Model(:items))
86
+ c.plugin :caching, @cache, :ttl => 1234
87
+ c.cache_ttl.should == 1234
88
+ Class.new(c).cache_ttl.should == 1234
89
+ end
90
+
91
+ it "should allow overriding the ttl option via a plugin :caching call" do
92
+ @c.plugin :caching, @cache, :ttl => 1234
93
+ @c.cache_ttl.should == 1234
94
+ Class.new(@c).cache_ttl.should == 1234
95
+ end
96
+
97
+ it "should offer a set_cache_ttl method for setting the ttl" do
98
+ @c.cache_ttl.should == 3600
99
+ @c.set_cache_ttl 1234
100
+ @c.cache_ttl.should == 1234
101
+ Class.new(@c).cache_ttl.should == 1234
102
+ end
103
+
104
+ it "should generate a cache key appropriate to the class" do
105
+ m = @c.new
106
+ m.values[:id] = 1
107
+ m.cache_key.should == "#{m.class}:1"
108
+ m = @c2.new
109
+ m.values[:id] = 1
110
+ m.cache_key.should == "#{m.class}:1"
111
+
112
+ # custom primary key
113
+ @c.set_primary_key :ttt
114
+ m = @c.new
115
+ m.values[:ttt] = 333
116
+ m.cache_key.should == "#{m.class}:333"
117
+ c = Class.new(@c)
118
+ m = c.new
119
+ m.values[:ttt] = 333
120
+ m.cache_key.should == "#{m.class}:333"
121
+
122
+ # composite primary key
123
+ @c.set_primary_key [:a, :b, :c]
124
+ m = @c.new
125
+ m.values[:a] = 123
126
+ m.values[:c] = 456
127
+ m.values[:b] = 789
128
+ m.cache_key.should == "#{m.class}:123,789,456"
129
+ c = Class.new(@c)
130
+ m = c.new
131
+ m.values[:a] = 123
132
+ m.values[:c] = 456
133
+ m.values[:b] = 789
134
+ m.cache_key.should == "#{m.class}:123,789,456"
135
+ end
136
+
137
+ it "should raise error if attempting to generate cache_key and primary key value is null" do
138
+ m = @c.new
139
+ proc {m.cache_key}.should raise_error(Sequel::Error)
140
+ m.values[:id] = 1
141
+ proc {m.cache_key}.should_not raise_error(Sequel::Error)
142
+
143
+ m = @c2.new
144
+ proc {m.cache_key}.should raise_error(Sequel::Error)
145
+ m.values[:id] = 1
146
+ proc {m.cache_key}.should_not raise_error(Sequel::Error)
147
+ end
148
+
149
+ it "should not raise error if trying to save a new record" do
150
+ proc {@c.new(:name=>'blah').save}.should_not raise_error
151
+ proc {@c.create(:name=>'blah')}.should_not raise_error
152
+ proc {@c2.new(:name=>'blah').save}.should_not raise_error
153
+ proc {@c2.create(:name=>'blah')}.should_not raise_error
154
+ end
155
+
156
+ it "should set the cache when reading from the database" do
157
+ $sqls.should == []
158
+ @cache.should be_empty
159
+
160
+ m = @c[1]
161
+ $sqls.should == ['SELECT * FROM items WHERE (id = 1) LIMIT 1']
162
+ m.values.should == $cache_dataset_row
163
+ @cache[m.cache_key].should == m
164
+ m2 = @c[1]
165
+ $sqls.should == ['SELECT * FROM items WHERE (id = 1) LIMIT 1']
166
+ m2.should == m
167
+ m2.values.should == $cache_dataset_row
168
+
169
+ $sqls.clear
170
+ m = @c2[1]
171
+ $sqls.should == ['SELECT * FROM items WHERE (id = 1) LIMIT 1']
172
+ m.values.should == $cache_dataset_row
173
+ @cache[m.cache_key].should == m
174
+ m2 = @c2[1]
175
+ $sqls.should == ['SELECT * FROM items WHERE (id = 1) LIMIT 1']
176
+ m2.should == m
177
+ m2.values.should == $cache_dataset_row
178
+ end
179
+
180
+ it "should delete the cache when writing to the database" do
181
+ m = @c[1]
182
+ @cache[m.cache_key].should == m
183
+ m.name = 'hey'
184
+ m.save
185
+ @cache.has_key?(m.cache_key).should be_false
186
+ $sqls.last.should == "UPDATE items SET name = 'hey' WHERE (id = 1)"
187
+
188
+ m = @c2[1]
189
+ @cache[m.cache_key].should == m
190
+ m.name = 'hey'
191
+ m.save
192
+ @cache.has_key?(m.cache_key).should be_false
193
+ $sqls.last.should == "UPDATE items SET name = 'hey' WHERE (id = 1)"
194
+ end
195
+
196
+ it "should delete the cache when deleting the record" do
197
+ m = @c[1]
198
+ @cache[m.cache_key].should == m
199
+ m.delete
200
+ @cache.has_key?(m.cache_key).should be_false
201
+ $sqls.last.should == "DELETE FROM items WHERE (id = 1)"
202
+
203
+ m = @c2[1]
204
+ @cache[m.cache_key].should == m
205
+ m.delete
206
+ @cache.has_key?(m.cache_key).should be_false
207
+ $sqls.last.should == "DELETE FROM items WHERE (id = 1)"
208
+ end
209
+
210
+ it "should support #[] as a shortcut to #find with hash" do
211
+ m = @c[:id => 3]
212
+ @cache[m.cache_key].should be_nil
213
+ $sqls.last.should == "SELECT * FROM items WHERE (id = 3) LIMIT 1"
214
+ m = @c[1]
215
+ @cache[m.cache_key].should == m
216
+ $sqls.should == ["SELECT * FROM items WHERE (id = 3) LIMIT 1", \
217
+ "SELECT * FROM items WHERE (id = 1) LIMIT 1"]
218
+ @c[:id => 4]
219
+ $sqls.should == ["SELECT * FROM items WHERE (id = 3) LIMIT 1", \
220
+ "SELECT * FROM items WHERE (id = 1) LIMIT 1", \
221
+ "SELECT * FROM items WHERE (id = 4) LIMIT 1"]
222
+
223
+ $sqls.clear
224
+ m = @c2[:id => 3]
225
+ @cache[m.cache_key].should be_nil
226
+ $sqls.last.should == "SELECT * FROM items WHERE (id = 3) LIMIT 1"
227
+ m = @c2[1]
228
+ @cache[m.cache_key].should == m
229
+ $sqls.should == ["SELECT * FROM items WHERE (id = 3) LIMIT 1", \
230
+ "SELECT * FROM items WHERE (id = 1) LIMIT 1"]
231
+ @c2[:id => 4]
232
+ $sqls.should == ["SELECT * FROM items WHERE (id = 3) LIMIT 1", \
233
+ "SELECT * FROM items WHERE (id = 1) LIMIT 1", \
234
+ "SELECT * FROM items WHERE (id = 4) LIMIT 1"]
235
+ end
236
+
237
+ it "should support ignore_exception option" do
238
+ c = Class.new(Sequel::Model(:items))
239
+ c.plugin :caching, @cache, :ignore_exceptions => true
240
+ Class.new(c).cache_ignore_exceptions.should == true
241
+ end
242
+
243
+ it "should raise an exception if cache_store is memcached and ignore_exception is not enabled" do
244
+ proc{@c3[1]}.should raise_error
245
+ end
246
+
247
+ it "should rescue an exception if cache_store is memcached and ignore_exception is enabled" do
248
+ @c4[1].values.should == $cache_dataset_row
249
+ end
250
+ end
@@ -0,0 +1,252 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe "class_table_inheritance plugin" do
4
+ before do
5
+ @db = db = MODEL_DB.clone
6
+ def db.schema(table, opts={})
7
+ {:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]],
8
+ :managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}]],
9
+ :executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}]],
10
+ :staff=>[[:id, {:type=>:integer}], [:manager_id, {:type=>:integer}]],
11
+ }[table]
12
+ end
13
+ def db.dataset(*args)
14
+ ds = super(*args)
15
+ def ds.columns
16
+ {[:employees]=>[:id, :name, :kind],
17
+ [:managers]=>[:id, :num_staff],
18
+ [:executives]=>[:id, :num_managers],
19
+ [:staff]=>[:id, :manager_id],
20
+ [:employees, :managers]=>[:id, :name, :kind, :num_staff],
21
+ [:employees, :managers, :executives]=>[:id, :name, :kind, :num_staff, :num_managers],
22
+ [:employees, :staff]=>[:id, :name, :kind, :manager_id],
23
+ }[opts[:from] + (opts[:join] || []).map{|x| x.table}]
24
+ end
25
+ def ds.insert(*args)
26
+ db << insert_sql(*args)
27
+ 1
28
+ end
29
+ ds
30
+ end
31
+ class ::Employee < Sequel::Model(db)
32
+ def _refresh(x); @values[:id] = 1 end
33
+ def self.columns
34
+ dataset.columns
35
+ end
36
+ plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}
37
+ end
38
+ class ::Manager < Employee
39
+ one_to_many :staff_members, :class=>:Staff
40
+ end
41
+ class ::Executive < Manager
42
+ end
43
+ class ::Staff < Employee
44
+ many_to_one :manager
45
+ end
46
+ @ds = Employee.dataset
47
+ @db.reset
48
+ end
49
+ after do
50
+ Object.send(:remove_const, :Executive)
51
+ Object.send(:remove_const, :Manager)
52
+ Object.send(:remove_const, :Staff)
53
+ Object.send(:remove_const, :Employee)
54
+ end
55
+
56
+ specify "should have simple_table = nil" do
57
+ Employee.simple_table.should == nil
58
+ Manager.simple_table.should == nil
59
+ Executive.simple_table.should == nil
60
+ Staff.simple_table.should == nil
61
+ end
62
+
63
+ specify "should use a joined dataset in subclasses" do
64
+ Employee.dataset.sql.should == 'SELECT * FROM employees'
65
+ Manager.dataset.sql.should == 'SELECT * FROM employees INNER JOIN managers USING (id)'
66
+ Executive.dataset.sql.should == 'SELECT * FROM employees INNER JOIN managers USING (id) INNER JOIN executives USING (id)'
67
+ Staff.dataset.sql.should == 'SELECT * FROM employees INNER JOIN staff USING (id)'
68
+ end
69
+
70
+ it "should return rows with the correct class based on the polymorphic_key value" do
71
+ def @ds.fetch_rows(sql)
72
+ yield({:kind=>'Employee'})
73
+ yield({:kind=>'Manager'})
74
+ yield({:kind=>'Executive'})
75
+ yield({:kind=>'Staff'})
76
+ end
77
+ Employee.all.collect{|x| x.class}.should == [Employee, Manager, Executive, Staff]
78
+ end
79
+
80
+ it "should return rows with the correct class based on the polymorphic_key value for subclasses" do
81
+ ds = Manager.dataset
82
+ def ds.fetch_rows(sql)
83
+ yield({:kind=>'Manager'})
84
+ yield({:kind=>'Executive'})
85
+ end
86
+ Manager.all.collect{|x| x.class}.should == [Manager, Executive]
87
+ end
88
+
89
+ it "should return rows with the current class if cti_key is nil" do
90
+ Employee.plugin(:class_table_inheritance)
91
+ def @ds.fetch_rows(sql)
92
+ yield({:kind=>'Employee'})
93
+ yield({:kind=>'Manager'})
94
+ yield({:kind=>'Executive'})
95
+ yield({:kind=>'Staff'})
96
+ end
97
+ Employee.all.collect{|x| x.class}.should == [Employee, Employee, Employee, Employee]
98
+ end
99
+
100
+ it "should return rows with the current class if cti_key is nil in subclasses" do
101
+ Employee.plugin(:class_table_inheritance)
102
+ Object.send(:remove_const, :Executive)
103
+ Object.send(:remove_const, :Manager)
104
+ class ::Manager < Employee
105
+ end
106
+ class ::Executive < Manager
107
+ end
108
+ ds = Manager.dataset
109
+ def ds.fetch_rows(sql)
110
+ yield({:kind=>'Manager'})
111
+ yield({:kind=>'Executive'})
112
+ end
113
+ Manager.all.collect{|x| x.class}.should == [Manager, Manager]
114
+ end
115
+
116
+ it "should fallback to the main class if the given class does not exist" do
117
+ def @ds.fetch_rows(sql)
118
+ yield({:kind=>'Employee'})
119
+ yield({:kind=>'Manager'})
120
+ yield({:kind=>'Blah'})
121
+ yield({:kind=>'Staff'})
122
+ end
123
+ Employee.all.collect{|x| x.class}.should == [Employee, Manager, Employee, Staff]
124
+ end
125
+
126
+ it "should fallback to the main class if the given class does not exist in subclasses" do
127
+ ds = Manager.dataset
128
+ def ds.fetch_rows(sql)
129
+ yield({:kind=>'Manager'})
130
+ yield({:kind=>'Executive'})
131
+ yield({:kind=>'Blah'})
132
+ end
133
+ Manager.all.collect{|x| x.class}.should == [Manager, Executive, Manager]
134
+ end
135
+
136
+ it "should add a before_create hook that sets the model class name for the key" do
137
+ Employee.create
138
+ @db.sqls.should == ["INSERT INTO employees (kind) VALUES ('Employee')"]
139
+ end
140
+
141
+ it "should add a before_create hook that sets the model class name for the key in subclasses" do
142
+ Executive.create
143
+ @db.sqls.should == ["INSERT INTO employees (kind) VALUES ('Executive')",
144
+ "INSERT INTO managers (id) VALUES (1)",
145
+ "INSERT INTO executives (id) VALUES (1)"]
146
+ end
147
+
148
+ it "should ignore existing cti_key value" do
149
+ Employee.create(:kind=>'Manager')
150
+ @db.sqls.should == ["INSERT INTO employees (kind) VALUES ('Employee')"]
151
+ end
152
+
153
+ it "should ignore existing cti_key value in subclasses" do
154
+ Manager.create(:kind=>'Executive')
155
+ @db.sqls.should == ["INSERT INTO employees (kind) VALUES ('Manager')",
156
+ "INSERT INTO managers (id) VALUES (1)"]
157
+ end
158
+
159
+ it "should raise an error if attempting to create an anonymous subclass" do
160
+ proc{Class.new(Manager)}.should raise_error(Sequel::Error)
161
+ end
162
+
163
+ it "should allow specifying a map of names to tables to override implicit mapping" do
164
+ Manager.dataset.sql.should == 'SELECT * FROM employees INNER JOIN managers USING (id)'
165
+ Staff.dataset.sql.should == 'SELECT * FROM employees INNER JOIN staff USING (id)'
166
+ end
167
+
168
+ it "should lazily load attributes for columns in subclass tables" do
169
+ ds = Manager.dataset
170
+ def ds.fetch_rows(sql)
171
+ @db << sql
172
+ yield({:id=>1, :name=>'J', :kind=>'Executive', :num_staff=>2})
173
+ end
174
+ m = Manager[1]
175
+ @db.sqls.should == ['SELECT * FROM employees INNER JOIN managers USING (id) WHERE (id = 1) LIMIT 1']
176
+ @db.reset
177
+ ds = Executive.dataset
178
+ def ds.fetch_rows(sql)
179
+ @db << sql
180
+ yield({:num_managers=>3})
181
+ end
182
+ m.num_managers.should == 3
183
+ @db.sqls.should == ['SELECT num_managers FROM employees INNER JOIN managers USING (id) INNER JOIN executives USING (id) WHERE (id = 1) LIMIT 1']
184
+ m.values.should == {:id=>1, :name=>'J', :kind=>'Executive', :num_staff=>2, :num_managers=>3}
185
+ end
186
+
187
+ it "should include schema for columns for tables for ancestor classes" do
188
+ Employee.db_schema.should == {:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}}
189
+ Manager.db_schema.should == {:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer}}
190
+ Executive.db_schema.should == {:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer}, :num_managers=>{:type=>:integer}}
191
+ Staff.db_schema.should == {:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :manager_id=>{:type=>:integer}}
192
+ end
193
+
194
+ it "should use the correct primary key (which should have the same name in all subclasses)" do
195
+ [Employee, Manager, Executive, Staff].each{|c| c.primary_key.should == :id}
196
+ end
197
+
198
+ it "should have table_name return the table name of the most specific table" do
199
+ Employee.table_name.should == :employees
200
+ Manager.table_name.should == :managers
201
+ Executive.table_name.should == :executives
202
+ Staff.table_name.should == :staff
203
+ end
204
+
205
+ it "should delete the correct rows from all tables when deleting" do
206
+ Executive.load(:id=>1).delete
207
+ @db.sqls.should == ["DELETE FROM executives WHERE (id = 1)", "DELETE FROM managers WHERE (id = 1)", "DELETE FROM employees WHERE (id = 1)"]
208
+ end
209
+
210
+ it "should insert the correct rows into all tables when inserting" do
211
+ Executive.create(:num_managers=>3, :num_staff=>2, :name=>'E')
212
+ @db.sqls.length.should == 3
213
+ @db.sqls[0].should =~ /INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Executive)', '(E|Executive)'\)/
214
+ @db.sqls[1].should =~ /INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \([12], [12]\)/
215
+ @db.sqls[2].should =~ /INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([13], [13]\)/
216
+ end
217
+
218
+ it "should insert the correct rows into all tables with a given primary key" do
219
+ e = Executive.new(:num_managers=>3, :num_staff=>2, :name=>'E')
220
+ e.id = 2
221
+ e.save
222
+ @db.sqls.length.should == 3
223
+ @db.sqls[0].should =~ /INSERT INTO employees \((name|kind|id), (name|kind|id), (name|kind|id)\) VALUES \(('E'|'Executive'|2), ('E'|'Executive'|2), ('E'|'Executive'|2)\)/
224
+ @db.sqls[1].should =~ /INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \(2, 2\)/
225
+ @db.sqls[2].should =~ /INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([23], [23]\)/
226
+ end
227
+
228
+ it "should update the correct rows in all tables when updating" do
229
+ Executive.load(:id=>2).update(:num_managers=>3, :num_staff=>2, :name=>'E')
230
+ @db.sqls.should == ["UPDATE employees SET name = 'E' WHERE (id = 2)", "UPDATE managers SET num_staff = 2 WHERE (id = 2)", "UPDATE executives SET num_managers = 3 WHERE (id = 2)"]
231
+ end
232
+
233
+ it "should handle many_to_one relationships correctly" do
234
+ ds = Manager.dataset
235
+ def ds.fetch_rows(sql)
236
+ @db << sql
237
+ yield({:id=>3, :name=>'E', :kind=>'Executive', :num_managers=>3})
238
+ end
239
+ Staff.load(:manager_id=>3).manager.should == Executive.load(:id=>3, :name=>'E', :kind=>'Executive', :num_managers=>3)
240
+ @db.sqls.should == ['SELECT * FROM employees INNER JOIN managers USING (id) WHERE (managers.id = 3) LIMIT 1']
241
+ end
242
+
243
+ it "should handle one_to_many relationships correctly" do
244
+ ds = Staff.dataset
245
+ def ds.fetch_rows(sql)
246
+ @db << sql
247
+ yield({:id=>1, :name=>'S', :kind=>'Staff', :manager_id=>3})
248
+ end
249
+ Executive.load(:id=>3).staff_members.should == [Staff.load(:id=>1, :name=>'S', :kind=>'Staff', :manager_id=>3)]
250
+ @db.sqls.should == ['SELECT * FROM employees INNER JOIN staff USING (id) WHERE (staff.manager_id = 3)']
251
+ end
252
+ end