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.
- data/CHANGELOG +3134 -0
- data/COPYING +19 -0
- data/README.rdoc +723 -0
- data/Rakefile +193 -0
- data/bin/sequel +196 -0
- data/doc/advanced_associations.rdoc +644 -0
- data/doc/cheat_sheet.rdoc +218 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/dataset_filtering.rdoc +158 -0
- data/doc/opening_databases.rdoc +296 -0
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/reflection.rdoc +84 -0
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/doc/release_notes/3.10.0.txt +286 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/doc/release_notes/3.4.0.txt +325 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/release_notes/3.8.0.txt +151 -0
- data/doc/release_notes/3.9.0.txt +233 -0
- data/doc/schema.rdoc +36 -0
- data/doc/sharding.rdoc +113 -0
- data/doc/virtual_rows.rdoc +205 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +90 -0
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/amalgalite.rb +176 -0
- data/lib/sequel/adapters/db2.rb +139 -0
- data/lib/sequel/adapters/dbi.rb +113 -0
- data/lib/sequel/adapters/do.rb +188 -0
- data/lib/sequel/adapters/do/mysql.rb +49 -0
- data/lib/sequel/adapters/do/postgres.rb +91 -0
- data/lib/sequel/adapters/do/sqlite.rb +40 -0
- data/lib/sequel/adapters/firebird.rb +283 -0
- data/lib/sequel/adapters/informix.rb +77 -0
- data/lib/sequel/adapters/jdbc.rb +587 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/h2.rb +133 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
- data/lib/sequel/adapters/mysql.rb +421 -0
- data/lib/sequel/adapters/odbc.rb +143 -0
- data/lib/sequel/adapters/odbc/mssql.rb +42 -0
- data/lib/sequel/adapters/openbase.rb +64 -0
- data/lib/sequel/adapters/oracle.rb +131 -0
- data/lib/sequel/adapters/postgres.rb +504 -0
- data/lib/sequel/adapters/shared/mssql.rb +490 -0
- data/lib/sequel/adapters/shared/mysql.rb +498 -0
- data/lib/sequel/adapters/shared/oracle.rb +195 -0
- data/lib/sequel/adapters/shared/postgres.rb +830 -0
- data/lib/sequel/adapters/shared/progress.rb +44 -0
- data/lib/sequel/adapters/shared/sqlite.rb +389 -0
- data/lib/sequel/adapters/sqlite.rb +224 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
- data/lib/sequel/connection_pool.rb +99 -0
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +293 -0
- data/lib/sequel/core_sql.rb +241 -0
- data/lib/sequel/database.rb +1079 -0
- data/lib/sequel/database/schema_generator.rb +327 -0
- data/lib/sequel/database/schema_methods.rb +203 -0
- data/lib/sequel/database/schema_sql.rb +320 -0
- data/lib/sequel/dataset.rb +32 -0
- data/lib/sequel/dataset/actions.rb +441 -0
- data/lib/sequel/dataset/features.rb +86 -0
- data/lib/sequel/dataset/graph.rb +254 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +227 -0
- data/lib/sequel/dataset/query.rb +709 -0
- data/lib/sequel/dataset/sql.rb +996 -0
- data/lib/sequel/exceptions.rb +51 -0
- data/lib/sequel/extensions/blank.rb +43 -0
- data/lib/sequel/extensions/inflector.rb +242 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/migration.rb +239 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/pagination.rb +100 -0
- data/lib/sequel/extensions/pretty_table.rb +82 -0
- data/lib/sequel/extensions/query.rb +52 -0
- data/lib/sequel/extensions/schema_dumper.rb +271 -0
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +46 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/metaprogramming.rb +9 -0
- data/lib/sequel/model.rb +120 -0
- data/lib/sequel/model/associations.rb +1514 -0
- data/lib/sequel/model/base.rb +1069 -0
- data/lib/sequel/model/default_inflections.rb +45 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/sequel/model/exceptions.rb +21 -0
- data/lib/sequel/model/inflections.rb +162 -0
- data/lib/sequel/model/plugins.rb +70 -0
- data/lib/sequel/plugins/active_model.rb +59 -0
- data/lib/sequel/plugins/association_dependencies.rb +103 -0
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/boolean_readers.rb +53 -0
- data/lib/sequel/plugins/caching.rb +141 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/force_encoding.rb +72 -0
- data/lib/sequel/plugins/hook_class_methods.rb +126 -0
- data/lib/sequel/plugins/identity_map.rb +116 -0
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/instance_hooks.rb +57 -0
- data/lib/sequel/plugins/lazy_attributes.rb +77 -0
- data/lib/sequel/plugins/many_through_many.rb +208 -0
- data/lib/sequel/plugins/nested_attributes.rb +206 -0
- data/lib/sequel/plugins/optimistic_locking.rb +81 -0
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/schema.rb +66 -0
- data/lib/sequel/plugins/serialization.rb +166 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/timestamps.rb +87 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +72 -0
- data/lib/sequel/plugins/validation_class_methods.rb +405 -0
- data/lib/sequel/plugins/validation_helpers.rb +223 -0
- data/lib/sequel/sql.rb +1020 -0
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +12 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/firebird_spec.rb +407 -0
- data/spec/adapters/informix_spec.rb +97 -0
- data/spec/adapters/mssql_spec.rb +403 -0
- data/spec/adapters/mysql_spec.rb +1019 -0
- data/spec/adapters/oracle_spec.rb +286 -0
- data/spec/adapters/postgres_spec.rb +969 -0
- data/spec/adapters/spec_helper.rb +51 -0
- data/spec/adapters/sqlite_spec.rb +432 -0
- data/spec/core/connection_pool_spec.rb +808 -0
- data/spec/core/core_sql_spec.rb +417 -0
- data/spec/core/database_spec.rb +1662 -0
- data/spec/core/dataset_spec.rb +3827 -0
- data/spec/core/expression_filters_spec.rb +595 -0
- data/spec/core/object_graph_spec.rb +296 -0
- data/spec/core/schema_generator_spec.rb +159 -0
- data/spec/core/schema_spec.rb +830 -0
- data/spec/core/spec_helper.rb +56 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/active_model_spec.rb +76 -0
- data/spec/extensions/association_dependencies_spec.rb +127 -0
- data/spec/extensions/association_proxies_spec.rb +50 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/boolean_readers_spec.rb +92 -0
- data/spec/extensions/caching_spec.rb +250 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/force_encoding_spec.rb +117 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/identity_map_spec.rb +202 -0
- data/spec/extensions/inflector_spec.rb +181 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/instance_hooks_spec.rb +133 -0
- data/spec/extensions/lazy_attributes_spec.rb +153 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +884 -0
- data/spec/extensions/migration_spec.rb +332 -0
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +396 -0
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +357 -0
- data/spec/extensions/schema_spec.rb +127 -0
- data/spec/extensions/serialization_spec.rb +209 -0
- data/spec/extensions/single_table_inheritance_spec.rb +96 -0
- data/spec/extensions/spec_helper.rb +91 -0
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/timestamps_spec.rb +150 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +69 -0
- data/spec/extensions/validation_class_methods_spec.rb +984 -0
- data/spec/extensions/validation_helpers_spec.rb +438 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/database_test.rb +26 -0
- data/spec/integration/dataset_test.rb +963 -0
- data/spec/integration/eager_loader_test.rb +734 -0
- data/spec/integration/model_test.rb +130 -0
- data/spec/integration/plugin_test.rb +814 -0
- data/spec/integration/prepared_statement_test.rb +213 -0
- data/spec/integration/schema_test.rb +361 -0
- data/spec/integration/spec_helper.rb +73 -0
- data/spec/integration/timezone_test.rb +55 -0
- data/spec/integration/transaction_test.rb +122 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +175 -0
- data/spec/model/associations_spec.rb +2633 -0
- data/spec/model/base_spec.rb +418 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1391 -0
- data/spec/model/hooks_spec.rb +240 -0
- data/spec/model/inflector_spec.rb +26 -0
- data/spec/model/model_spec.rb +593 -0
- data/spec/model/plugins_spec.rb +236 -0
- data/spec/model/record_spec.rb +1500 -0
- data/spec/model/spec_helper.rb +97 -0
- data/spec/model/validations_spec.rb +153 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_config.rb.example +10 -0
- 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
|