find_cache 0.1.6 → 0.1.7

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.
@@ -1,10 +1,10 @@
1
- # FindCache
1
+ == FindCache
2
2
 
3
3
  It is simply an ActiveRecord object caching gem using Rails.cache methods.
4
4
 
5
- It makes ActiveRecord 'find_by_id, find_by_(attr)' methods and 'has_one, belongs_to relations' cacheable by id or any referenced columns using Rails.cache methods. It also supports fetching multiple records using ids with find_all_cache method(if cache store supports multiple reads [hint: memcached_store, dalli_store supports.]).
5
+ It makes "ActiveRecord 'find_by_id, find_by_(attr)' methods and 'has_one, belongs_to' relations" cacheable by id or any referenced columns using Rails.cache methods. It also supports fetching multiple records using ids with find_all_cache method(if cache store supports multiple reads [hint: memcached_store, dalli_store supports.]).
6
6
 
7
- ## Installation
7
+ == Installation
8
8
 
9
9
  Add this line to your application's Gemfile:
10
10
 
@@ -18,50 +18,44 @@ Or install it yourself as:
18
18
 
19
19
  $ gem install find_cache
20
20
 
21
- ## For has_one and belongs_to relations
21
+ == For has_one and belongs_to relations
22
22
 
23
23
  ### Sample Models:
24
24
 
25
- User (`has_one :user_detail`) ->
26
-
27
- find_cache_has_one :user_detail, UserDetail, :user_id
28
-
29
- find_cache_has_one attr_name_or_any_other_name, ModelName, foreign_key
30
-
31
- - name
32
- - email
33
- - username
34
-
35
- UserDetail (`belongs_to :user`) ->
36
-
37
- find_cache_belongs_to :user, User, :user_id
38
-
39
- find_cache_belongs_to attr_name_or_any_other_name, ModelName, foreign_key
25
+ class User < ActiveRecord::Base
26
+ has_one :user_detail
27
+
28
+ # to enable has_one caching
29
+ find_cache_has_one :user_detail, UserDetail, :user_id
30
+ end
40
31
 
41
- - user_id
42
- - avatar
43
- - age
44
- - sex
45
- - location
32
+ class UserDetail < ActiveRecord::Base
33
+ has_one :user_detail
34
+
35
+ # to enable belongs_to caching
36
+ find_cache_belongs_to :user, User, :user_id
37
+ end
46
38
 
47
- ## For finding a record
39
+ == For finding a record
48
40
 
49
41
  user = User.find_cache(id) # fetches from cache
50
- user.user_detail # fetches from cache if 'find_cache_has_one :user_detail, UserDetail, :user_id' added to User model
42
+ user.user_detail
43
+ # fetches from cache if the User model has
44
+ # 'find_cache_has_one :user_detail, UserDetail, :user_id'
51
45
 
52
46
  user = User.find_by_id(id) # fetches from DB
53
47
  user.user_detail # fetches from DB
54
48
 
55
- ## For fetching multiple ids
49
+ == For fetching multiple ids
56
50
 
57
51
  users = User.find_all_cache([1,2,3,4,5])
58
52
 
59
- ## For fetching a record by attribute
53
+ == For fetching a record by attribute
60
54
 
61
55
  user_detail = UserDetail.find_by_user_id(1) # from db
62
56
  user_detail = UserDetail.find_cache_by_ref(:user_id, 1) # from cache store
63
57
 
64
- ## For counter_cache 'cache invalidation'
58
+ == For counter_cache 'cache invalidation'
65
59
 
66
60
  ### Create a ruby file under config/initializers and add the following codes:
67
61
 
@@ -85,9 +79,10 @@ UserDetail (`belongs_to :user`) ->
85
79
  end
86
80
  end
87
81
 
88
- ## Notes
82
+ == Notes
89
83
 
90
84
  If your worker thread does not kill itself after execution you should clean the find_cache_store manually. It can be done simple in ApplicationController of your rails app adding this method as after_filter
85
+
91
86
  after_filter :clear_find_cache_store
92
87
 
93
88
  def clear_find_cache_store
@@ -96,11 +91,11 @@ If your worker thread does not kill itself after execution you should clean the
96
91
 
97
92
  Tested with dalli (https://github.com/mperham/dalli).
98
93
 
99
- ## Credits
94
+ == Credits
100
95
 
101
96
  FindCache is part of http://videofork.com project.
102
97
 
103
- ## Contributing
98
+ == Contributing
104
99
 
105
100
  1. Fork it
106
101
  2. Create your feature branch (`git checkout -b my-new-feature`)
@@ -1,32 +1,59 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  module FindCache
3
+
4
+ # = FindCache Cachable
3
5
  module Cacheable
4
6
  extend ActiveSupport::Concern
5
7
 
6
- module ClassMethods
8
+ module ClassMethods
9
+ # has_one relational caching with find_cache
10
+ #
11
+ # ==== Parameters
12
+ #
13
+ # * +attribute+ - The attribute that you wish to name for cache
14
+ # * +model+ - The Model which data will be retreived for caching
15
+ # * +foreign_key+ - The FK attribute name in the given Model
16
+ #
17
+ # ==== Examples
18
+ #
19
+ # # For User model with user_id FroeignKey in UserDetail model
20
+ # find_cache_has_one :user_detail, UserDetail, :user_id
7
21
  def find_cache_has_one(attribute, model, foreign_key)
8
22
  send :define_method, attribute.to_sym do |*args|
9
23
  $find_cache_store[KeyGen.global_cache_key]["#{attribute}_#{model}_#{foreign_key}_#{self.id}"] ||= model.find_cache_by_ref(foreign_key, self.id)
10
24
  end
11
25
  end
12
26
 
27
+ # belongs_to relational caching with find_cache
28
+ #
29
+ # ==== Parameters
30
+ #
31
+ # * +attribute+ - The attribute that you wish to name for cache
32
+ # * +model+ - The Model which data will be retreived for caching
33
+ # * +foreign_key+ - The FK attribute name of given Model for the current Model
34
+ #
35
+ # ==== Examples
36
+ #
37
+ # # In UserDetail model with user_id FroeignKey of User model
38
+ # find_cache_belongs_to :user, User, :user_id
13
39
  def find_cache_belongs_to(attribute, model, foreign_key)
14
40
  send :define_method, attribute.to_sym do |*args|
15
41
  $find_cache_store[KeyGen.global_cache_key]["#{attribute}_#{model}_#{foreign_key}_#{self.send(foreign_key.to_sym)}"] ||= model.find_cache(self.send(foreign_key.to_sym))
16
42
  end
17
43
  end
18
- end
19
-
20
- included do
21
- after_commit :expire_find_cache
22
-
23
- def expire_find_cache
24
- key = KeyGen.cache_key(self.class.name, self.id)
25
- Rails.cache.delete(key)
26
- $find_cache_store[KeyGen.global_cache_key].delete(key)
27
- end
28
44
 
29
- def self.find_cache(id)
45
+ # To find an Active Record object using find_by_id method
46
+ # and caching it using Rails.cache methods
47
+ #
48
+ # ==== Parameters
49
+ #
50
+ # * +id+ - Id for the model
51
+ #
52
+ # ==== Examples
53
+ #
54
+ # # Find a User object with id 1 from User model
55
+ # User.find_cache(1)
56
+ def find_cache(id)
30
57
  key = KeyGen.cache_key(name, id)
31
58
  $find_cache_store[KeyGen.global_cache_key][key] ||= (
32
59
  Rails.cache.fetch(key) do
@@ -35,12 +62,24 @@ module FindCache
35
62
  )
36
63
  end
37
64
 
38
- def self.find_cache_by_ref(ref_attr, ref_id)
39
- key_ref = KeyGen.cache_key_ref(name, ref_attr, ref_id)
65
+ # To find an Active Record object using attributes other than
66
+ # primary key (:id) and caching it using Rails.cache methods
67
+ #
68
+ # ==== Parameters
69
+ #
70
+ # * +ref_attr+ - Reference attribute which will be looked in table
71
+ # * +ref_value+ - Value for the given referenced attribute
72
+ #
73
+ # ==== Examples
74
+ #
75
+ # # Find an UserDetail object using user_id attribute with value 1
76
+ # UserDetail.find_cache_by_ref(:user_id, 1)
77
+ def find_cache_by_ref(ref_attr, ref_val)
78
+ key_ref = KeyGen.cache_key_ref(name, ref_attr, ref_val)
40
79
  if (id = Rails.cache.read(key_ref))
41
80
  find_cache(id)
42
81
  else
43
- item = send("find_by_#{ref_attr}", "#{ref_id}")
82
+ item = send("find_by_#{ref_attr}", "#{ref_val}")
44
83
  if item
45
84
  Rails.cache.write(key_ref, item.id)
46
85
  Rails.cache.write(KeyGen.cache_key(name, item.id), item)
@@ -49,10 +88,27 @@ module FindCache
49
88
  end
50
89
  end
51
90
 
52
- def self.find_all_cache(ids, order_by = "id DESC")
91
+
92
+ # To find all Active Record objects using id list
93
+ #
94
+ # ==== Parameters
95
+ #
96
+ # * +ids+ - List of ids
97
+ # * +order_by+ - optional order direction for results
98
+ #
99
+ # ==== Notes
100
+ # # Only valid if the Rails.cache store supports read_multi
101
+ #
102
+ # ==== Examples
103
+ #
104
+ # # Find users with ids ([21, 1, 2, 19, 43]) from User model
105
+ # UserDetail.find_all_cache([21, 1, 2, 19, 43])
106
+ # # Find users with ids ([21, 1, 2, 19, 43]) and id ASC
107
+ # UserDetail.find_all_cache(([21, 1, 2, 19, 43]), "id ASC")
108
+ def find_all_cache(ids, order_by = "id DESC")
53
109
  all_new_cache = []
54
110
  not_in_cache = []
55
- cache_ids = generate_cache_keys(ids)
111
+ cache_ids = ids.map { |id| KeyGen.cache_key(name, id) }
56
112
  all_cache = Rails.cache.read_multi(cache_ids)
57
113
  cache_ids.each_with_index do |cache_id, cache_id_index|
58
114
  if all_cache[cache_id]
@@ -71,14 +127,16 @@ module FindCache
71
127
  all_new_cache.each do |cached_item|
72
128
  $find_cache_store[KeyGen.global_cache_key][KeyGen.cache_key(name, cached_item.id)] = cached_item
73
129
  end
74
- end
75
-
76
- def self.generate_cache_keys(ids)
77
- cache_ids = []
78
- ids.each do |id|
79
- cache_ids << KeyGen.cache_key(name, id)
80
- end
81
- cache_ids
130
+ end
131
+ end
132
+
133
+ included do
134
+ after_commit :expire_find_cache
135
+
136
+ def expire_find_cache
137
+ key = KeyGen.cache_key(self.class.name, self.id)
138
+ Rails.cache.delete(key)
139
+ $find_cache_store[KeyGen.global_cache_key].delete(key)
82
140
  end
83
141
  end
84
142
  end
@@ -1,2 +1,3 @@
1
1
  # -*- encoding: utf-8 -*-
2
+ # A local cache store for cached objects of Threads
2
3
  $find_cache_store = {}
@@ -3,15 +3,48 @@ module FindCache
3
3
  class KeyGen
4
4
  require "simple_uuid"
5
5
 
6
+ # cache_key generator for model items
7
+ #
8
+ # ==== Parameters
9
+ #
10
+ # * +model_name+ - The name of the model
11
+ # * +id+ - Id for the given model
12
+ #
13
+ # ==== Examples
14
+ #
15
+ # FindCache::KeyGen.cache_key("User", 1)
6
16
  def self.cache_key(model_name, id)
7
17
  "#{model_name}/#{id}"
8
18
  end
9
19
 
20
+ # cache_key generator for model items with referenced attribute
21
+ #
22
+ # ==== Parameters
23
+ #
24
+ # * +model_name+ - The name of the model
25
+ # * +foreign_key_name+ - Name of the foreign_key attribute
26
+ # * +foreign_key_id+ - Id for the given foreign_key name
27
+ #
28
+ # ==== Examples
29
+ #
30
+ # FindCache::KeyGen.cache_key_ref("UserDetail", "user_id", 1)
10
31
  def self.cache_key_ref(model_name, foreign_key_name, foreign_key_id)
11
32
  "#{model_name}/#{foreign_key_name}-#{foreign_key_id}"
12
33
  end
13
34
 
14
- def self.global_cache_key(clear=false)
35
+ # cache_key generator for the current thread
36
+ #
37
+ # ==== Parameters
38
+ #
39
+ # * +clear+ - Boolean value to clean up the current Thread's cache key
40
+ #
41
+ # ==== Examples
42
+ #
43
+ # # For generating cache key for the current Thread
44
+ # FindCache::KeyGen.global_cache_key
45
+ # # For cleaning generated cache key for the current Thread
46
+ # FindCache::KeyGen.global_cache_key(true)
47
+ def self.global_cache_key(clear = false)
15
48
  return (Thread.current[:global_cache_key] = nil) if clear
16
49
  Thread.current[:global_cache_key] ||= (
17
50
  t = SimpleUUID::UUID.new(Time.now).to_i.to_s(16).rjust(32, "0")
@@ -20,6 +53,8 @@ module FindCache
20
53
  )
21
54
  end
22
55
 
56
+ # Cleaning all caches of the current Thread not the Rails.cache
57
+ #
23
58
  def self.clear_find_cache_store
24
59
  $find_cache_store.delete(KeyGen.global_cache_key)
25
60
  KeyGen.global_cache_key(true)
@@ -1,4 +1,4 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  module FindCache
3
- VERSION = "0.1.6"
3
+ VERSION = "0.1.7"
4
4
  end
Binary file
@@ -375,3 +375,54 @@ Connecting to database specified by database.yml
375
375
  User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 9 LIMIT 1
376
376
  User Load (0.3ms) SELECT "users".* FROM "users" LIMIT 1
377
377
  Post Load (0.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1 LIMIT 1
378
+ Connecting to database specified by database.yml
379
+ Post Load (0.2ms) SELECT "posts".* FROM "posts" LIMIT 1
380
+  (0.1ms) begin transaction
381
+ SQL (6.2ms) INSERT INTO "comments" ("content", "created_at", "post_id", "updated_at") VALUES (?, ?, ?, ?) [["content", "test content for comment"], ["created_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00], ["post_id", 1], ["updated_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00]]
382
+ Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 LIMIT 1
383
+ SQL (0.2ms) UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1 WHERE "posts"."id" = 1
384
+  (1.5ms) commit transaction
385
+ Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 LIMIT 1
386
+ Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 LIMIT 1
387
+ Post Load (0.2ms) SELECT "posts".* FROM "posts" LIMIT 1
388
+  (0.0ms) begin transaction
389
+ SQL (0.4ms) INSERT INTO "comments" ("content", "created_at", "post_id", "updated_at") VALUES (?, ?, ?, ?) [["content", "test 1 content for comment"], ["created_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00], ["post_id", 1], ["updated_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00]]
390
+ Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 LIMIT 1
391
+ SQL (0.1ms) UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1 WHERE "posts"."id" = 1
392
+  (1.6ms) commit transaction
393
+ Post Load (0.1ms) SELECT "posts".* FROM "posts" LIMIT 1
394
+  (0.0ms) begin transaction
395
+ SQL (0.3ms) INSERT INTO "comments" ("content", "created_at", "post_id", "updated_at") VALUES (?, ?, ?, ?) [["content", "test 2 content for comment"], ["created_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00], ["post_id", 1], ["updated_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00]]
396
+ Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 LIMIT 1
397
+ SQL (0.1ms) UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1 WHERE "posts"."id" = 1
398
+  (1.4ms) commit transaction
399
+ Post Load (0.1ms) SELECT "posts".* FROM "posts" LIMIT 1
400
+  (0.0ms) begin transaction
401
+ SQL (0.4ms) INSERT INTO "comments" ("content", "created_at", "post_id", "updated_at") VALUES (?, ?, ?, ?) [["content", "test 3 content for comment"], ["created_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00], ["post_id", 1], ["updated_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00]]
402
+ Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 LIMIT 1
403
+ SQL (0.1ms) UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1 WHERE "posts"."id" = 1
404
+  (1.5ms) commit transaction
405
+ Post Load (0.1ms) SELECT "posts".* FROM "posts" LIMIT 1
406
+  (0.0ms) begin transaction
407
+ SQL (0.3ms) INSERT INTO "comments" ("content", "created_at", "post_id", "updated_at") VALUES (?, ?, ?, ?) [["content", "test 4 content for comment"], ["created_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00], ["post_id", 1], ["updated_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00]]
408
+ Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 LIMIT 1
409
+ SQL (0.1ms) UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1 WHERE "posts"."id" = 1
410
+  (1.8ms) commit transaction
411
+ Comment Load (0.3ms) SELECT "comments".* FROM "comments" WHERE "comments"."id" IN (32, 33, 34, 35) ORDER BY id ASC
412
+ Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE "comments"."id" IN (32, 33, 34, 35) ORDER BY id ASC
413
+ Post Load (0.2ms) SELECT "posts".* FROM "posts" LIMIT 1
414
+ Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 LIMIT 1
415
+  (0.1ms) begin transaction
416
+  (0.1ms) commit transaction
417
+ Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 LIMIT 1
418
+ Post Load (0.3ms) SELECT "posts".* FROM "posts" LIMIT 1
419
+ User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
420
+  (0.1ms) begin transaction
421
+ SQL (0.6ms) INSERT INTO "users" ("created_at", "email", "password", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00], ["email", "test@videofork.com"], ["password", "test1234"], ["updated_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00]]
422
+ SQL (0.4ms) INSERT INTO "posts" ("body", "comments_count", "created_at", "title", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?) [["body", nil], ["comments_count", nil], ["created_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00], ["title", nil], ["updated_at", Wed, 29 Aug 2012 18:30:29 UTC +00:00], ["user_id", 10]]
423
+ Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 10 LIMIT 1
424
+  (1.8ms) commit transaction
425
+ User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 10 LIMIT 1
426
+ User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 10 LIMIT 1
427
+ User Load (0.3ms) SELECT "users".* FROM "users" LIMIT 1
428
+ Post Load (0.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1 LIMIT 1
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: find_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-29 00:00:00.000000000 Z
12
+ date: 2012-08-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -186,6 +186,7 @@ files:
186
186
  - test/dummy/tmp/cache/276/5C0/User%2F9
187
187
  - test/dummy/tmp/cache/276/6F0/Post%2F2
188
188
  - test/dummy/tmp/cache/277/700/Post%2F3
189
+ - test/dummy/tmp/cache/29E/F20/User%2F10
189
190
  - test/dummy/tmp/cache/3A8/070/Comment%2F7
190
191
  - test/dummy/tmp/cache/3A9/080/Comment%2F8
191
192
  - test/dummy/tmp/cache/3AA/090/Comment%2F9
@@ -197,10 +198,14 @@ files:
197
198
  - test/dummy/tmp/cache/3D5/D70/Comment%2F22
198
199
  - test/dummy/tmp/cache/3D6/D70/Comment%2F14
199
200
  - test/dummy/tmp/cache/3D6/D80/Comment%2F23
201
+ - test/dummy/tmp/cache/3D6/D90/Comment%2F32
200
202
  - test/dummy/tmp/cache/3D7/D80/Comment%2F15
201
203
  - test/dummy/tmp/cache/3D7/D90/Comment%2F24
204
+ - test/dummy/tmp/cache/3D7/DA0/Comment%2F33
202
205
  - test/dummy/tmp/cache/3D8/DA0/Comment%2F25
206
+ - test/dummy/tmp/cache/3D8/DB0/Comment%2F34
203
207
  - test/dummy/tmp/cache/3D9/DA0/Comment%2F17
208
+ - test/dummy/tmp/cache/3D9/DC0/Comment%2F35
204
209
  - test/dummy/tmp/cache/3DA/DB0/Comment%2F18
205
210
  - test/dummy/tmp/cache/3DA/DC0/Comment%2F27
206
211
  - test/dummy/tmp/cache/3DB/DC0/Comment%2F19
@@ -292,6 +297,7 @@ test_files:
292
297
  - test/dummy/tmp/cache/276/5C0/User%2F9
293
298
  - test/dummy/tmp/cache/276/6F0/Post%2F2
294
299
  - test/dummy/tmp/cache/277/700/Post%2F3
300
+ - test/dummy/tmp/cache/29E/F20/User%2F10
295
301
  - test/dummy/tmp/cache/3A8/070/Comment%2F7
296
302
  - test/dummy/tmp/cache/3A9/080/Comment%2F8
297
303
  - test/dummy/tmp/cache/3AA/090/Comment%2F9
@@ -303,10 +309,14 @@ test_files:
303
309
  - test/dummy/tmp/cache/3D5/D70/Comment%2F22
304
310
  - test/dummy/tmp/cache/3D6/D70/Comment%2F14
305
311
  - test/dummy/tmp/cache/3D6/D80/Comment%2F23
312
+ - test/dummy/tmp/cache/3D6/D90/Comment%2F32
306
313
  - test/dummy/tmp/cache/3D7/D80/Comment%2F15
307
314
  - test/dummy/tmp/cache/3D7/D90/Comment%2F24
315
+ - test/dummy/tmp/cache/3D7/DA0/Comment%2F33
308
316
  - test/dummy/tmp/cache/3D8/DA0/Comment%2F25
317
+ - test/dummy/tmp/cache/3D8/DB0/Comment%2F34
309
318
  - test/dummy/tmp/cache/3D9/DA0/Comment%2F17
319
+ - test/dummy/tmp/cache/3D9/DC0/Comment%2F35
310
320
  - test/dummy/tmp/cache/3DA/DB0/Comment%2F18
311
321
  - test/dummy/tmp/cache/3DA/DC0/Comment%2F27
312
322
  - test/dummy/tmp/cache/3DB/DC0/Comment%2F19