identity_cache 0.5.1 → 1.0.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.
- checksums.yaml +5 -5
- data/.github/probots.yml +2 -0
- data/.github/workflows/ci.yml +26 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +24 -9
- data/CHANGELOG.md +21 -0
- data/Gemfile +5 -1
- data/README.md +28 -26
- data/Rakefile +14 -5
- data/dev.yml +9 -16
- data/gemfiles/Gemfile.latest-release +6 -0
- data/gemfiles/Gemfile.rails-edge +6 -0
- data/gemfiles/Gemfile.rails52 +6 -0
- data/identity_cache.gemspec +26 -10
- data/lib/identity_cache.rb +49 -46
- data/lib/identity_cache/belongs_to_caching.rb +12 -40
- data/lib/identity_cache/cache_fetcher.rb +6 -5
- data/lib/identity_cache/cache_hash.rb +2 -2
- data/lib/identity_cache/cache_invalidation.rb +4 -11
- data/lib/identity_cache/cache_key_generation.rb +17 -65
- data/lib/identity_cache/cache_key_loader.rb +128 -0
- data/lib/identity_cache/cached.rb +7 -0
- data/lib/identity_cache/cached/association.rb +87 -0
- data/lib/identity_cache/cached/attribute.rb +123 -0
- data/lib/identity_cache/cached/attribute_by_multi.rb +37 -0
- data/lib/identity_cache/cached/attribute_by_one.rb +88 -0
- data/lib/identity_cache/cached/belongs_to.rb +93 -0
- data/lib/identity_cache/cached/embedded_fetching.rb +41 -0
- data/lib/identity_cache/cached/prefetcher.rb +51 -0
- data/lib/identity_cache/cached/primary_index.rb +97 -0
- data/lib/identity_cache/cached/recursive/association.rb +68 -0
- data/lib/identity_cache/cached/recursive/has_many.rb +9 -0
- data/lib/identity_cache/cached/recursive/has_one.rb +9 -0
- data/lib/identity_cache/cached/reference/association.rb +16 -0
- data/lib/identity_cache/cached/reference/has_many.rb +105 -0
- data/lib/identity_cache/cached/reference/has_one.rb +100 -0
- data/lib/identity_cache/configuration_dsl.rb +53 -215
- data/lib/identity_cache/encoder.rb +95 -0
- data/lib/identity_cache/expiry_hook.rb +36 -0
- data/lib/identity_cache/fallback_fetcher.rb +2 -1
- data/lib/identity_cache/load_strategy/eager.rb +28 -0
- data/lib/identity_cache/load_strategy/lazy.rb +71 -0
- data/lib/identity_cache/load_strategy/load_request.rb +20 -0
- data/lib/identity_cache/load_strategy/multi_load_request.rb +27 -0
- data/lib/identity_cache/memoized_cache_proxy.rb +127 -58
- data/lib/identity_cache/parent_model_expiration.rb +45 -11
- data/lib/identity_cache/query_api.rb +128 -394
- data/lib/identity_cache/railtie.rb +8 -0
- data/lib/identity_cache/record_not_found.rb +6 -0
- data/lib/identity_cache/should_use_cache.rb +1 -0
- data/lib/identity_cache/version.rb +3 -2
- data/lib/identity_cache/with_primary_index.rb +136 -0
- data/lib/identity_cache/without_primary_index.rb +24 -3
- data/performance/cache_runner.rb +28 -34
- data/performance/cpu.rb +3 -2
- data/performance/externals.rb +4 -3
- data/performance/profile.rb +6 -5
- data/railgun.yml +16 -0
- metadata +44 -73
- data/Gemfile.rails42 +0 -6
- data/Gemfile.rails50 +0 -6
- data/test/attribute_cache_test.rb +0 -110
- data/test/cache_fetch_includes_test.rb +0 -46
- data/test/cache_hash_test.rb +0 -14
- data/test/cache_invalidation_test.rb +0 -139
- data/test/deeply_nested_associated_record_test.rb +0 -19
- data/test/denormalized_has_many_test.rb +0 -214
- data/test/denormalized_has_one_test.rb +0 -160
- data/test/fetch_multi_test.rb +0 -308
- data/test/fetch_test.rb +0 -258
- data/test/fixtures/serialized_record.mysql2 +0 -0
- data/test/fixtures/serialized_record.postgresql +0 -0
- data/test/helpers/active_record_objects.rb +0 -106
- data/test/helpers/database_connection.rb +0 -72
- data/test/helpers/serialization_format.rb +0 -51
- data/test/helpers/update_serialization_format.rb +0 -27
- data/test/identity_cache_test.rb +0 -29
- data/test/index_cache_test.rb +0 -161
- data/test/memoized_attributes_test.rb +0 -59
- data/test/memoized_cache_proxy_test.rb +0 -107
- data/test/normalized_belongs_to_test.rb +0 -107
- data/test/normalized_has_many_test.rb +0 -231
- data/test/normalized_has_one_test.rb +0 -9
- data/test/prefetch_associations_test.rb +0 -379
- data/test/readonly_test.rb +0 -109
- data/test/recursive_denormalized_has_many_test.rb +0 -131
- data/test/save_test.rb +0 -82
- data/test/schema_change_test.rb +0 -112
- data/test/serialization_format_change_test.rb +0 -16
- data/test/test_helper.rb +0 -140
data/test/fetch_test.rb
DELETED
@@ -1,258 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class FetchTest < IdentityCache::TestCase
|
4
|
-
NAMESPACE = IdentityCache::CacheKeyGeneration::DEFAULT_NAMESPACE
|
5
|
-
|
6
|
-
def setup
|
7
|
-
super
|
8
|
-
Item.cache_index :title, :unique => true
|
9
|
-
Item.cache_index :id, :title, :unique => true
|
10
|
-
|
11
|
-
@record = Item.new
|
12
|
-
@record.id = 1
|
13
|
-
@record.title = 'bob'
|
14
|
-
@cached_value = { class: @record.class.name, attributes: @record.attributes_before_type_cast }
|
15
|
-
@blob_key = "#{NAMESPACE}blob:Item:#{cache_hash("created_at:datetime,id:integer,item_id:integer,title:string,updated_at:datetime")}:1"
|
16
|
-
@index_key = "#{NAMESPACE}attr:Item:id:title:#{cache_hash('bob')}"
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_fetch_with_garbage_input
|
20
|
-
assert_nil Item.fetch_by_id('garbage')
|
21
|
-
end
|
22
|
-
|
23
|
-
def test_fetch_cache_hit
|
24
|
-
IdentityCache.cache.expects(:fetch).with(@blob_key).returns(@cached_value)
|
25
|
-
|
26
|
-
assert_equal @record, Item.fetch(1)
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_fetch_hit_cache_namespace
|
30
|
-
old_ns = IdentityCache.cache_namespace
|
31
|
-
IdentityCache.cache_namespace = proc { |model| "#{model.table_name}:#{old_ns}" }
|
32
|
-
|
33
|
-
new_blob_key = "items:#{@blob_key}"
|
34
|
-
IdentityCache.cache.expects(:fetch).with(new_blob_key).returns(@cached_value)
|
35
|
-
|
36
|
-
assert_equal @record, Item.fetch(1)
|
37
|
-
ensure
|
38
|
-
IdentityCache.cache_namespace = old_ns
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_exists_with_identity_cache_when_cache_hit
|
42
|
-
IdentityCache.cache.expects(:fetch).with(@blob_key).returns(@cached_value)
|
43
|
-
|
44
|
-
assert Item.exists_with_identity_cache?(1)
|
45
|
-
end
|
46
|
-
|
47
|
-
def test_exists_with_identity_cache_when_cache_miss_and_in_db
|
48
|
-
fetch = Spy.on(IdentityCache.cache, :fetch).and_call_through
|
49
|
-
Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
|
50
|
-
|
51
|
-
assert Item.exists_with_identity_cache?(1)
|
52
|
-
assert fetch.has_been_called_with?(@blob_key)
|
53
|
-
end
|
54
|
-
|
55
|
-
def test_exists_with_identity_cache_when_cache_miss_and_not_in_db
|
56
|
-
fetch = Spy.on(IdentityCache.cache, :fetch).and_call_through
|
57
|
-
Item.expects(:resolve_cache_miss).with(1).once.returns(nil)
|
58
|
-
|
59
|
-
assert !Item.exists_with_identity_cache?(1)
|
60
|
-
assert fetch.has_been_called_with?(@blob_key)
|
61
|
-
end
|
62
|
-
|
63
|
-
def test_fetch_miss
|
64
|
-
Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
|
65
|
-
|
66
|
-
results = []
|
67
|
-
fetch = Spy.on(IdentityCache.cache, :fetch).and_return {|_, &block| block.call.tap {|result| results << result}}
|
68
|
-
|
69
|
-
assert_equal @record, Item.fetch(1)
|
70
|
-
assert fetch.has_been_called_with?(@blob_key)
|
71
|
-
assert_equal [@cached_value], results
|
72
|
-
end
|
73
|
-
|
74
|
-
def test_fetch_miss_with_non_id_primary_key
|
75
|
-
hashed_key = Zlib::crc32("foo") % (2 ** 30 - 1)
|
76
|
-
fixture = KeyedRecord.create!(:value => "foo") { |r| r.hashed_key = hashed_key }
|
77
|
-
assert_equal fixture, KeyedRecord.fetch(hashed_key)
|
78
|
-
end
|
79
|
-
|
80
|
-
def test_fetch_conflict
|
81
|
-
resolve_cache_miss = Spy.on(Item, :resolve_cache_miss).and_return do
|
82
|
-
@record.send(:expire_cache)
|
83
|
-
@record
|
84
|
-
end
|
85
|
-
add = Spy.on(fetcher, :add).and_call_through
|
86
|
-
|
87
|
-
assert_equal @record, Item.fetch(1)
|
88
|
-
assert resolve_cache_miss.has_been_called_with?(1)
|
89
|
-
assert add.has_been_called_with?(@blob_key, @cached_value)
|
90
|
-
assert_equal IdentityCache::DELETED, backend.read(@record.primary_cache_index_key)
|
91
|
-
end
|
92
|
-
|
93
|
-
def test_fetch_conflict_after_delete
|
94
|
-
@record.send(:expire_cache)
|
95
|
-
assert_equal IdentityCache::DELETED, backend.read(@record.primary_cache_index_key)
|
96
|
-
|
97
|
-
resolve_cache_miss = Spy.on(Item, :resolve_cache_miss).and_return do
|
98
|
-
@record.send(:expire_cache)
|
99
|
-
@record
|
100
|
-
end
|
101
|
-
add = Spy.on(IdentityCache.cache.cache_fetcher, :add).and_call_through
|
102
|
-
|
103
|
-
assert_equal @record, Item.fetch(1)
|
104
|
-
assert resolve_cache_miss.has_been_called_with?(1)
|
105
|
-
refute add.has_been_called?
|
106
|
-
assert_equal IdentityCache::DELETED, backend.read(@record.primary_cache_index_key)
|
107
|
-
end
|
108
|
-
|
109
|
-
def test_fetch_by_id_not_found_should_return_nil
|
110
|
-
nonexistent_record_id = 10
|
111
|
-
fetcher.expects(:add).with(@blob_key + '0', IdentityCache::CACHED_NIL)
|
112
|
-
|
113
|
-
assert_nil Item.fetch_by_id(nonexistent_record_id)
|
114
|
-
end
|
115
|
-
|
116
|
-
def test_fetch_not_found_should_raise
|
117
|
-
nonexistent_record_id = 10
|
118
|
-
fetcher.expects(:add).with(@blob_key + '0', IdentityCache::CACHED_NIL)
|
119
|
-
|
120
|
-
assert_raises(ActiveRecord::RecordNotFound) { Item.fetch(nonexistent_record_id) }
|
121
|
-
end
|
122
|
-
|
123
|
-
def test_cached_nil_expiry_on_record_creation
|
124
|
-
key = @record.primary_cache_index_key
|
125
|
-
|
126
|
-
assert_nil Item.fetch_by_id(@record.id)
|
127
|
-
assert_equal IdentityCache::CACHED_NIL, backend.read(key)
|
128
|
-
|
129
|
-
@record.save!
|
130
|
-
assert_equal IdentityCache::DELETED, backend.read(key)
|
131
|
-
end
|
132
|
-
|
133
|
-
def test_fetch_by_title_hit
|
134
|
-
values = []
|
135
|
-
fetch = Spy.on(IdentityCache.cache, :fetch).and_return do |key, &block|
|
136
|
-
case key
|
137
|
-
# Read record with title bob
|
138
|
-
when @index_key then block.call.tap {|val| values << val}
|
139
|
-
# got id, do memcache lookup on that, hit -> done
|
140
|
-
when @blob_key then @cached_value
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# Id not found, use sql, SELECT id FROM records WHERE title = '...' LIMIT 1"
|
145
|
-
Item.connection.expects(:exec_query).returns(ActiveRecord::Result.new(['id'], [[1]]))
|
146
|
-
|
147
|
-
assert_equal @record, Item.fetch_by_title('bob')
|
148
|
-
assert_equal [1], values
|
149
|
-
assert fetch.has_been_called_with?(@index_key)
|
150
|
-
assert fetch.has_been_called_with?(@blob_key)
|
151
|
-
end
|
152
|
-
|
153
|
-
def test_fetch_by_title_cache_namespace
|
154
|
-
Item.send(:include, SwitchNamespace)
|
155
|
-
IdentityCache.cache.expects(:fetch).with("ns:#{@index_key}").returns(1)
|
156
|
-
IdentityCache.cache.expects(:fetch).with("ns:#{@blob_key}").returns(@cached_value)
|
157
|
-
|
158
|
-
assert_equal @record, Item.fetch_by_title('bob')
|
159
|
-
end
|
160
|
-
|
161
|
-
def test_fetch_by_title_stores_idcnil
|
162
|
-
Item.connection.expects(:exec_query).once.returns(ActiveRecord::Result.new([], []))
|
163
|
-
add = Spy.on(fetcher, :add).and_call_through
|
164
|
-
fetch = Spy.on(fetcher, :fetch).and_call_through
|
165
|
-
assert_nil Item.fetch_by_title('bob') # exec_query => nil
|
166
|
-
|
167
|
-
assert_nil Item.fetch_by_title('bob') # returns cached nil
|
168
|
-
assert_nil Item.fetch_by_title('bob') # returns cached nil
|
169
|
-
|
170
|
-
assert add.has_been_called_with?(@index_key, IdentityCache::CACHED_NIL)
|
171
|
-
assert_equal 3, fetch.calls.length
|
172
|
-
end
|
173
|
-
|
174
|
-
def test_fetch_by_bang_method
|
175
|
-
Item.connection.expects(:exec_query).returns(ActiveRecord::Result.new([], []))
|
176
|
-
assert_raises ActiveRecord::RecordNotFound do
|
177
|
-
Item.fetch_by_title!('bob')
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def test_fetch_does_not_communicate_to_cache_with_nil_id
|
182
|
-
fetcher.expects(:fetch).never
|
183
|
-
fetcher.expects(:add).never
|
184
|
-
assert_raises(ActiveRecord::RecordNotFound) { Item.fetch(nil) }
|
185
|
-
end
|
186
|
-
|
187
|
-
def test_fetch_cache_hit_does_not_checkout_database_connection
|
188
|
-
@record.save!
|
189
|
-
record = Item.fetch(@record.id)
|
190
|
-
|
191
|
-
ActiveRecord::Base.clear_active_connections!
|
192
|
-
|
193
|
-
assert_equal record, Item.fetch(@record.id)
|
194
|
-
|
195
|
-
assert_equal false, ActiveRecord::Base.connection_handler.active_connections?
|
196
|
-
end
|
197
|
-
|
198
|
-
def test_fetch_raises_when_called_on_a_scope
|
199
|
-
assert_raises(IdentityCache::UnsupportedScopeError) do
|
200
|
-
Item.where(updated_at: nil).fetch(1)
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
def test_fetch_by_raises_when_called_on_a_scope
|
205
|
-
assert_raises(IdentityCache::UnsupportedScopeError) do
|
206
|
-
Item.where(updated_at: nil).fetch_by_id(1)
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def test_fetch_on_derived_model_raises
|
211
|
-
assert_raises(IdentityCache::DerivedModelError) do
|
212
|
-
StiRecordTypeA.fetch(1)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
def test_returned_records_are_readonly_on_cache_hit
|
217
|
-
IdentityCache.with_fetch_read_only_records do
|
218
|
-
IdentityCache.cache.expects(:fetch).with(@blob_key).returns(@cached_value)
|
219
|
-
assert Item.fetch(1).readonly?
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
def test_returned_records_are_readonly_on_cache_miss
|
224
|
-
IdentityCache.with_fetch_read_only_records do
|
225
|
-
fetch = Spy.on(IdentityCache.cache, :fetch).and_call_through
|
226
|
-
Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
|
227
|
-
|
228
|
-
assert Item.exists_with_identity_cache?(1)
|
229
|
-
assert fetch.has_been_called_with?(@blob_key)
|
230
|
-
assert Item.fetch(1).readonly?
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
def test_returned_records_are_not_readonly_with_open_transactions
|
235
|
-
IdentityCache.with_fetch_read_only_records do
|
236
|
-
|
237
|
-
@record.transaction do
|
238
|
-
fetch = Spy.on(IdentityCache.cache, :fetch).and_call_through
|
239
|
-
Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
|
240
|
-
|
241
|
-
refute IdentityCache.should_use_cache?
|
242
|
-
refute fetch.has_been_called_with?(@blob_key)
|
243
|
-
refute Item.fetch(1).readonly?, "Fetched item was read-only"
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
def test_respects_should_use_cache_on_record
|
249
|
-
@record.save
|
250
|
-
Item.stubs(:should_use_cache?).returns(false)
|
251
|
-
|
252
|
-
assert_memcache_operations(0) do
|
253
|
-
assert_queries(1) do
|
254
|
-
Item.fetch_by_id(@record.id)
|
255
|
-
end
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
Binary file
|
Binary file
|
@@ -1,106 +0,0 @@
|
|
1
|
-
module SwitchNamespace
|
2
|
-
|
3
|
-
module ClassMethods
|
4
|
-
def rails_cache_key_namespace
|
5
|
-
"#{self.namespace}:#{super}"
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.included(base)
|
10
|
-
base.extend ClassMethods
|
11
|
-
base.class_eval do
|
12
|
-
class_attribute :namespace
|
13
|
-
self.namespace = 'ns'
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
module ActiveRecordObjects
|
19
|
-
|
20
|
-
def setup_models(base = ActiveRecord::Base)
|
21
|
-
Object.send :const_set, 'DeeplyAssociatedRecord', Class.new(base) {
|
22
|
-
include IdentityCache
|
23
|
-
belongs_to :item
|
24
|
-
belongs_to :associated_record
|
25
|
-
default_scope { order('name DESC') }
|
26
|
-
}
|
27
|
-
|
28
|
-
Object.send :const_set, 'AssociatedRecord', Class.new(base) {
|
29
|
-
include IdentityCache
|
30
|
-
belongs_to :item, inverse_of: :associated_records
|
31
|
-
belongs_to :item_two, inverse_of: :associated_records
|
32
|
-
has_many :deeply_associated_records
|
33
|
-
default_scope { order('id DESC') }
|
34
|
-
}
|
35
|
-
|
36
|
-
Object.send :const_set, 'NormalizedAssociatedRecord', Class.new(base) {
|
37
|
-
include IdentityCache
|
38
|
-
belongs_to :item
|
39
|
-
default_scope { order('id DESC') }
|
40
|
-
}
|
41
|
-
|
42
|
-
Object.send :const_set, 'NotCachedRecord', Class.new(base) {
|
43
|
-
belongs_to :item, :touch => true
|
44
|
-
default_scope { order('id DESC') }
|
45
|
-
}
|
46
|
-
|
47
|
-
Object.send :const_set, 'PolymorphicRecord', Class.new(base) {
|
48
|
-
belongs_to :owner, :polymorphic => true
|
49
|
-
}
|
50
|
-
|
51
|
-
Object.send :const_set, 'Deeply', Module.new
|
52
|
-
Deeply.send :const_set, 'Nested', Module.new
|
53
|
-
Deeply::Nested.send :const_set, 'AssociatedRecord', Class.new(base) {
|
54
|
-
include IdentityCache
|
55
|
-
}
|
56
|
-
|
57
|
-
Object.send :const_set, 'Item', Class.new(base) {
|
58
|
-
include IdentityCache
|
59
|
-
belongs_to :item
|
60
|
-
has_many :associated_records, inverse_of: :item
|
61
|
-
has_many :deeply_associated_records, inverse_of: :item
|
62
|
-
has_many :normalized_associated_records
|
63
|
-
has_many :not_cached_records
|
64
|
-
has_many :polymorphic_records, :as => 'owner'
|
65
|
-
has_one :polymorphic_record, :as => 'owner'
|
66
|
-
has_one :associated, :class_name => 'AssociatedRecord'
|
67
|
-
}
|
68
|
-
|
69
|
-
Object.send :const_set, 'ItemTwo', Class.new(base) {
|
70
|
-
include IdentityCache
|
71
|
-
has_many :associated_records, inverse_of: :item_two, foreign_key: :item_two_id
|
72
|
-
self.table_name = 'items2'
|
73
|
-
}
|
74
|
-
|
75
|
-
Object.send :const_set, 'KeyedRecord', Class.new(base) {
|
76
|
-
include IdentityCache
|
77
|
-
self.primary_key = "hashed_key"
|
78
|
-
}
|
79
|
-
|
80
|
-
Object.send :const_set, 'StiRecord', Class.new(base) {
|
81
|
-
include IdentityCache
|
82
|
-
has_many :polymorphic_records, :as => 'owner'
|
83
|
-
}
|
84
|
-
|
85
|
-
Object.send :const_set, 'StiRecordTypeA', Class.new(StiRecord) {
|
86
|
-
}
|
87
|
-
end
|
88
|
-
|
89
|
-
def teardown_models
|
90
|
-
ActiveSupport::DescendantsTracker.clear
|
91
|
-
ActiveSupport::Dependencies.clear
|
92
|
-
Object.send :remove_const, 'DeeplyAssociatedRecord'
|
93
|
-
Object.send :remove_const, 'PolymorphicRecord'
|
94
|
-
Object.send :remove_const, 'NormalizedAssociatedRecord'
|
95
|
-
Object.send :remove_const, 'AssociatedRecord'
|
96
|
-
Object.send :remove_const, 'NotCachedRecord'
|
97
|
-
Object.send :remove_const, 'Item'
|
98
|
-
Object.send :remove_const, 'ItemTwo'
|
99
|
-
Object.send :remove_const, 'KeyedRecord'
|
100
|
-
Object.send :remove_const, 'StiRecord'
|
101
|
-
Object.send :remove_const, 'StiRecordTypeA'
|
102
|
-
Deeply::Nested.send :remove_const, 'AssociatedRecord'
|
103
|
-
Deeply.send :remove_const, 'Nested'
|
104
|
-
Object.send :remove_const, 'Deeply'
|
105
|
-
end
|
106
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
module DatabaseConnection
|
2
|
-
def self.db_name
|
3
|
-
ENV.fetch('DB', 'mysql2')
|
4
|
-
end
|
5
|
-
|
6
|
-
def self.setup
|
7
|
-
db_config = ENV['DATABASE_URL'] || DEFAULT_CONFIG[db_name]
|
8
|
-
begin
|
9
|
-
ActiveRecord::Base.establish_connection(db_config)
|
10
|
-
ActiveRecord::Base.connection
|
11
|
-
rescue
|
12
|
-
raise unless db_config.is_a?(Hash)
|
13
|
-
ActiveRecord::Base.establish_connection(db_config.merge('database' => nil))
|
14
|
-
ActiveRecord::Base.connection.create_database(db_config['database'])
|
15
|
-
ActiveRecord::Base.establish_connection(db_config)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.drop_tables
|
20
|
-
TABLES.keys.each do |table|
|
21
|
-
ActiveRecord::Base.connection.drop_table(table) if table_exists?(table)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.table_exists?(table)
|
26
|
-
if ActiveRecord::Base.connection.respond_to?(:data_source_exists?)
|
27
|
-
ActiveRecord::Base.connection.data_source_exists?(table)
|
28
|
-
else
|
29
|
-
ActiveRecord::Base.connection.table_exists?(table)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.create_tables
|
34
|
-
TABLES.each do |table, fields|
|
35
|
-
fields = fields.dup
|
36
|
-
options = fields.last.is_a?(Hash) ? fields.pop : {}
|
37
|
-
ActiveRecord::Base.connection.create_table(table, options) do |t|
|
38
|
-
fields.each do |column_type, *args|
|
39
|
-
t.send(column_type, *args)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
TABLES = {
|
46
|
-
:polymorphic_records => [[:string, :owner_type], [:integer, :owner_id], [:timestamps, null: true]],
|
47
|
-
:deeply_associated_records => [[:string, :name], [:integer, :associated_record_id], [:integer, :item_id], [:timestamps, null: true]],
|
48
|
-
:associated_records => [[:string, :name], [:integer, :item_id], [:integer, :item_two_id]],
|
49
|
-
:normalized_associated_records => [[:string, :name], [:integer, :item_id], [:timestamps, null: true]],
|
50
|
-
:not_cached_records => [[:string, :name], [:integer, :item_id], [:timestamps, null: true]],
|
51
|
-
:items => [[:integer, :item_id], [:string, :title], [:timestamps, null: true]],
|
52
|
-
:items2 => [[:integer, :item_id], [:string, :title], [:timestamps, null: true]],
|
53
|
-
:keyed_records => [[:string, :value], :primary_key => "hashed_key"],
|
54
|
-
:sti_records => [[:string, :type], [:string, :name]],
|
55
|
-
}
|
56
|
-
|
57
|
-
DEFAULT_CONFIG = {
|
58
|
-
'mysql2' => {
|
59
|
-
'adapter' => 'mysql2',
|
60
|
-
'database' => 'identity_cache_test',
|
61
|
-
'host' => ENV['MYSQL_HOST'] || '127.0.0.1',
|
62
|
-
'username' => 'root'
|
63
|
-
},
|
64
|
-
'postgresql' => {
|
65
|
-
'adapter' => 'postgresql',
|
66
|
-
'database' => 'identity_cache_test',
|
67
|
-
'host' => ENV['POSTGRES_HOST'] || '127.0.0.1',
|
68
|
-
'username' => 'postgres',
|
69
|
-
'prepared_statements' => false,
|
70
|
-
}
|
71
|
-
}
|
72
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
module SerializationFormat
|
2
|
-
def serialized_record
|
3
|
-
AssociatedRecord.cache_has_many :deeply_associated_records, :embed => true
|
4
|
-
AssociatedRecord.cache_belongs_to :item, :embed => false
|
5
|
-
Item.cache_has_many :associated_records, :embed => true
|
6
|
-
Item.cache_has_one :associated
|
7
|
-
time = Time.parse('1970-01-01T00:00:00 UTC')
|
8
|
-
|
9
|
-
record = Item.new(:title => 'foo')
|
10
|
-
record.associated_records << AssociatedRecord.new(:name => 'bar')
|
11
|
-
record.associated_records << AssociatedRecord.new(:name => 'baz')
|
12
|
-
record.associated = AssociatedRecord.new(:name => 'bork')
|
13
|
-
record.not_cached_records << NotCachedRecord.new(:name => 'NoCache', created_at: time)
|
14
|
-
record.associated.deeply_associated_records << DeeplyAssociatedRecord.new(:name => "corge", created_at: time)
|
15
|
-
record.associated.deeply_associated_records << DeeplyAssociatedRecord.new(:name => "qux", created_at: time)
|
16
|
-
record.created_at = time
|
17
|
-
record.save
|
18
|
-
[Item, NotCachedRecord, DeeplyAssociatedRecord].each do |model|
|
19
|
-
model.update_all(updated_at: time)
|
20
|
-
end
|
21
|
-
record.reload
|
22
|
-
Item.fetch(record.id)
|
23
|
-
|
24
|
-
IdentityCache.fetch(record.primary_cache_index_key) do
|
25
|
-
STDERR.puts(
|
26
|
-
"\e[31m" \
|
27
|
-
"The record could not be retrieved from the cache." \
|
28
|
-
"Did you configure MEMCACHED_HOST?" \
|
29
|
-
"\e[0m",
|
30
|
-
)
|
31
|
-
exit(1)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def serialized_record_file
|
36
|
-
File.expand_path("../../fixtures/serialized_record.#{DatabaseConnection.db_name}", __FILE__)
|
37
|
-
end
|
38
|
-
|
39
|
-
def serialize(record, anIO = nil)
|
40
|
-
hash = {
|
41
|
-
:version => IdentityCache::CACHE_VERSION,
|
42
|
-
:record => record
|
43
|
-
}
|
44
|
-
|
45
|
-
if anIO
|
46
|
-
Marshal.dump(hash, anIO)
|
47
|
-
else
|
48
|
-
Marshal.dump(hash)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|