taggable_cache 0.2.1 → 0.3.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/.travis.yml +1 -2
- data/Gemfile +1 -1
- data/Gemfile.lock +6 -11
- data/gemfiles/Gemfile-rails.3.1.x +1 -1
- data/gemfiles/Gemfile-rails.3.2.x +1 -1
- data/lib/taggable_cache/dynamic_store.rb +5 -0
- data/lib/taggable_cache/railtie.rb +4 -1
- data/lib/taggable_cache/spectator.rb +2 -1
- data/lib/taggable_cache/store.rb +47 -0
- data/lib/taggable_cache/version.rb +1 -1
- data/spec/integration/rails_cache_spec.rb +2 -2
- data/spec/taggable-cache/store_spec.rb +80 -0
- data/taggable-cache.gemspec +1 -1
- metadata +10 -10
- data/gemfiles/Gemfile-rails.3.0.x +0 -13
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,16 +1,8 @@
|
|
1
|
-
GIT
|
2
|
-
remote: https://brain-geek@github.com/brain-geek/combustion.git
|
3
|
-
revision: 3574c523d1ce6d8805ab2218c8d52257b5848640
|
4
|
-
specs:
|
5
|
-
combustion (0.3.2)
|
6
|
-
rails (>= 3.0.0)
|
7
|
-
thor (>= 0.14.6)
|
8
|
-
|
9
1
|
PATH
|
10
2
|
remote: .
|
11
3
|
specs:
|
12
|
-
taggable_cache (0.
|
13
|
-
rails (>= 3.0
|
4
|
+
taggable_cache (0.3.0)
|
5
|
+
rails (>= 3.1.0)
|
14
6
|
redis (>= 2.2.2)
|
15
7
|
|
16
8
|
GEM
|
@@ -55,6 +47,9 @@ GEM
|
|
55
47
|
childprocess (0.3.1)
|
56
48
|
ffi (~> 1.0.6)
|
57
49
|
coderay (1.0.5)
|
50
|
+
combustion (0.3.2)
|
51
|
+
rails (>= 3.0.0)
|
52
|
+
thor (>= 0.14.6)
|
58
53
|
diff-lcs (1.1.3)
|
59
54
|
erubis (2.7.0)
|
60
55
|
ffi (1.0.11)
|
@@ -142,7 +137,7 @@ PLATFORMS
|
|
142
137
|
|
143
138
|
DEPENDENCIES
|
144
139
|
capybara
|
145
|
-
combustion
|
140
|
+
combustion
|
146
141
|
pry
|
147
142
|
pry-nav
|
148
143
|
rails (~> 3.2.0)
|
@@ -27,6 +27,10 @@ ActiveSupport::Cache::Store.class_eval do
|
|
27
27
|
taggable.get(*params).each do |m|
|
28
28
|
self.delete(m)
|
29
29
|
end
|
30
|
+
|
31
|
+
taggable.get_scope(*(params.delete_if{|a| not a.is_a? ActiveRecord::Base})).each do |m|
|
32
|
+
self.delete(m)
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
@@ -40,7 +44,6 @@ end
|
|
40
44
|
module TaggableCache
|
41
45
|
class Railtie < ::Rails::Railtie
|
42
46
|
initializer "taggable_cache" do |app|
|
43
|
-
# binding.pry
|
44
47
|
ActiveRecord::Base.observers << TaggableCache::Spectator
|
45
48
|
ActiveRecord::Base.add_observer TaggableCache::Spectator.instance
|
46
49
|
end
|
data/lib/taggable_cache/store.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "redis"
|
2
|
+
require "digest/md5"
|
2
3
|
|
3
4
|
class TaggableCache::Store
|
4
5
|
def initialize
|
@@ -14,6 +15,10 @@ class TaggableCache::Store
|
|
14
15
|
else
|
15
16
|
id_for(obj.class)
|
16
17
|
end
|
18
|
+
elsif obj.is_a? Arel::SelectManager
|
19
|
+
"query-keys-#{Digest::MD5.hexdigest(obj.to_sql)}"
|
20
|
+
elsif obj.is_a? ActiveRecord::Relation
|
21
|
+
id_for(obj.arel)
|
17
22
|
else
|
18
23
|
nil
|
19
24
|
end
|
@@ -21,6 +26,7 @@ class TaggableCache::Store
|
|
21
26
|
|
22
27
|
def add(tag, *members)
|
23
28
|
members.each do |element|
|
29
|
+
add_scope(tag, element) if is_scope? element
|
24
30
|
ident = id_for(element)
|
25
31
|
@redis.sadd ident, tag unless ident.nil?
|
26
32
|
end
|
@@ -34,4 +40,45 @@ class TaggableCache::Store
|
|
34
40
|
elements
|
35
41
|
end.flatten.compact
|
36
42
|
end
|
43
|
+
|
44
|
+
def is_scope?(scope)
|
45
|
+
(scope.is_a? ActiveRecord::Relation) || (scope.is_a? Arel::SelectManager)
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_scope(tag, scope)
|
49
|
+
scope = scope.arel if scope.is_a? ActiveRecord::Relation
|
50
|
+
table_name = scope.froms.first.name
|
51
|
+
query_fingerprint = Digest::MD5.hexdigest(scope.to_sql)
|
52
|
+
|
53
|
+
@redis.sadd "#{table_name}-scopes", "#{query_fingerprint}"
|
54
|
+
@redis.set "query-#{query_fingerprint}", Marshal.dump(scope)
|
55
|
+
end
|
56
|
+
|
57
|
+
def in_scope(scope, object)
|
58
|
+
return false unless object.persisted?
|
59
|
+
|
60
|
+
query = scope.where(scope.froms.first[:id].eq(object.id)).to_sql
|
61
|
+
|
62
|
+
object.class.connection.select_all(query).length > 0
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_scope(*members)
|
66
|
+
keys = members.delete_if{|a| not a.is_a? ActiveRecord::Base }.map do |object|
|
67
|
+
|
68
|
+
table_redis_key = "#{object.class.table_name}-scopes"
|
69
|
+
|
70
|
+
@redis.smembers(table_redis_key).map do |scope_key|
|
71
|
+
scope = Marshal.restore(@redis.get("query-#{scope_key}"))
|
72
|
+
|
73
|
+
if in_scope(scope, object)
|
74
|
+
ident = "query-keys-#{scope_key}"
|
75
|
+
elements = @redis.smembers(ident)
|
76
|
+
@redis.del(ident)
|
77
|
+
elements
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
keys.flatten.compact
|
83
|
+
end
|
37
84
|
end
|
@@ -46,7 +46,7 @@ describe 'TaggableCache::Rails::Cache' do
|
|
46
46
|
Rails.cache.read('lorem').should be_nil
|
47
47
|
end
|
48
48
|
|
49
|
-
|
49
|
+
describe "scope change" do
|
50
50
|
it "should not drop if changes are unrelated" do
|
51
51
|
Rails.cache.write('lorem', 'impsum', :depends_on => [Page.where(:name => 'bob')])
|
52
52
|
Rails.cache.read('lorem').should == 'impsum'
|
@@ -80,7 +80,7 @@ describe 'TaggableCache::Rails::Cache' do
|
|
80
80
|
page.name = 'jack'
|
81
81
|
page.save!
|
82
82
|
|
83
|
-
Rails.cache.read('lorem').should be_nil
|
83
|
+
Rails.cache.read('lorem').should be_nil
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
@@ -32,6 +32,86 @@ describe TaggableCache::Store do
|
|
32
32
|
it "should return nil if unknown is passed" do
|
33
33
|
@object.id_for(123).should be_nil
|
34
34
|
end
|
35
|
+
|
36
|
+
it "should make hash of arel/AR::relation object" do
|
37
|
+
@object.id_for(Page.order(:id)).should == @object.id_for(Page.order(:id).arel)
|
38
|
+
@object.id_for(Page.order(:id)).should_not be_nil
|
39
|
+
|
40
|
+
@object.id_for(Page.order(:id)).should_not == @object.id_for(Page.order(:name).arel)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "is_scope?" do
|
45
|
+
it "is not AR object or class" do
|
46
|
+
@object.is_scope?(Page).should be_false
|
47
|
+
@object.is_scope?(Page.create).should be_false
|
48
|
+
end
|
49
|
+
|
50
|
+
it "is arel object or AR relation" do
|
51
|
+
@object.is_scope?(Page.order(:id)).should be_true
|
52
|
+
@object.is_scope?(Page.order(:id).arel).should be_true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "scope flow" do
|
57
|
+
before :each do
|
58
|
+
@redis.flushall
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should add scope to model-specific set" do
|
62
|
+
@redis.smembers('pages-scopes').should == []
|
63
|
+
@object.add('tag_name', Page.where('name' => 'bob'))
|
64
|
+
@redis.smembers('pages-scopes').count.should == 1
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should be a marshalized arel object in model-specific set" do
|
68
|
+
Page.create(:name => 'bob')
|
69
|
+
@object.add('tag_scoped', Page.where('name' => 'bob'))
|
70
|
+
key = @redis.smembers('pages-scopes').first
|
71
|
+
query = @redis.get("query-#{key}")
|
72
|
+
Marshal.restore(query).should be_kind_of Arel::SelectManager
|
73
|
+
|
74
|
+
@redis.smembers("query-keys-#{key}").should == ['tag_scoped']
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should process 'in_scope' right way" do
|
78
|
+
bob = Page.create(:name => 'bob')
|
79
|
+
jack = Page.create(:name => 'jack')
|
80
|
+
|
81
|
+
@object.in_scope(Page.where(:name => 'bob'), bob).should be_true
|
82
|
+
@object.in_scope(Page.where(:name => 'bob'), jack).should be_false
|
83
|
+
|
84
|
+
@object.in_scope(Page.order(:id), Page.create).should be_true
|
85
|
+
|
86
|
+
#unsaved obj(without id)
|
87
|
+
@object.in_scope(Page.order(:id), Page.new).should be_false
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "get_scope" do
|
91
|
+
it "should return keys and leave nothing behind" do
|
92
|
+
#query to get all keys
|
93
|
+
p = Page.create
|
94
|
+
page = Page.order(:id)
|
95
|
+
|
96
|
+
@object.add('tag_name', page)
|
97
|
+
@object.get_scope(p).should == ['tag_name']
|
98
|
+
@object.get_scope(p).should == []
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should do multi-get" do
|
102
|
+
bob = Page.create(:name => 'bob')
|
103
|
+
jack = Page.create(:name => 'jack')
|
104
|
+
ian = Page.create(:name => 'ian')
|
105
|
+
|
106
|
+
@object.add('bob', Page.where(:name => 'bob'))
|
107
|
+
@object.add('jack', Page.where(:name => 'jack'))
|
108
|
+
@object.add('ian', Page.where(:name => 'ian'))
|
109
|
+
|
110
|
+
@object.get_scope(bob,jack).should == ['bob', 'jack']
|
111
|
+
@object.get_scope(bob,jack).should == []
|
112
|
+
@object.get_scope(ian).should == ['ian']
|
113
|
+
end
|
114
|
+
end
|
35
115
|
end
|
36
116
|
|
37
117
|
describe "Redis interaction" do
|
data/taggable-cache.gemspec
CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
|
|
28
28
|
s.rubygems_version = "1.8.15"
|
29
29
|
s.summary = "This gem simplifies cache expiration in rails by providing depends_on option to rails cache."
|
30
30
|
|
31
|
-
s.add_dependency(%q<rails>, [">= 3.0
|
31
|
+
s.add_dependency(%q<rails>, [">= 3.1.0"])
|
32
32
|
s.add_dependency(%q<redis>, [">= 2.2.2"])
|
33
33
|
end
|
34
34
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: taggable_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,18 +13,18 @@ date: 2012-02-09 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
|
-
requirement: &
|
16
|
+
requirement: &71322420 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 3.0
|
21
|
+
version: 3.1.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *71322420
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: redis
|
27
|
-
requirement: &
|
27
|
+
requirement: &71321470 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: 2.2.2
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *71321470
|
36
36
|
description: This gem simplifies cache expiration in rails
|
37
37
|
email: brain-geek@yandex.ua
|
38
38
|
executables: []
|
@@ -48,10 +48,10 @@ files:
|
|
48
48
|
- LICENSE.txt
|
49
49
|
- README.rdoc
|
50
50
|
- Rakefile
|
51
|
-
- gemfiles/Gemfile-rails.3.0.x
|
52
51
|
- gemfiles/Gemfile-rails.3.1.x
|
53
52
|
- gemfiles/Gemfile-rails.3.2.x
|
54
53
|
- lib/taggable_cache.rb
|
54
|
+
- lib/taggable_cache/dynamic_store.rb
|
55
55
|
- lib/taggable_cache/railtie.rb
|
56
56
|
- lib/taggable_cache/spectator.rb
|
57
57
|
- lib/taggable_cache/store.rb
|
@@ -85,7 +85,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
85
|
version: '0'
|
86
86
|
segments:
|
87
87
|
- 0
|
88
|
-
hash: -
|
88
|
+
hash: -516926623
|
89
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
90
|
none: false
|
91
91
|
requirements:
|
@@ -94,10 +94,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
94
|
version: '0'
|
95
95
|
segments:
|
96
96
|
- 0
|
97
|
-
hash: -
|
97
|
+
hash: -516926623
|
98
98
|
requirements: []
|
99
99
|
rubyforge_project:
|
100
|
-
rubygems_version: 1.8.
|
100
|
+
rubygems_version: 1.8.17
|
101
101
|
signing_key:
|
102
102
|
specification_version: 3
|
103
103
|
summary: This gem simplifies cache expiration in rails by providing depends_on option
|
@@ -1,13 +0,0 @@
|
|
1
|
-
source :rubygems
|
2
|
-
|
3
|
-
gem 'taggable_cache', :path => '..'
|
4
|
-
|
5
|
-
gem 'rake'
|
6
|
-
gem 'rspec-rails', :require => false
|
7
|
-
gem 'rspec'
|
8
|
-
gem 'capybara'
|
9
|
-
gem 'combustion', :git => 'https://brain-geek@github.com/brain-geek/combustion.git'
|
10
|
-
gem 'sqlite3'
|
11
|
-
|
12
|
-
|
13
|
-
gem 'rails', '~> 3.0.0'
|