nice_cache 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZjFlNDA4NmZhMmFmMDc0ZDZjNGQyNjJkMDVjY2M3ZjhkMjQ4NWFmOA==
4
+ NWNmMTQxOTZhNjE1YzQ2YWI5NjIyZGZlNjBkYTU0NjA3YjkwYWYxMQ==
5
5
  data.tar.gz: !binary |-
6
- NGNkZDZhZmE5NmVhMGY4NTVkODQzMGIwYmVlN2E2ZjViNjY4NWY2Ng==
6
+ MTNkYzMwMGQ1MGVmZDU3YmZhYzc3MzNjYzY2MDE4ZmRjYjRlOGYyMg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NTY0ZTg0ZDAwN2YyNDcwY2Y1NWNhZjZkMTBjMzNiZmNmNmUyZWYyMDg3ODA4
10
- Y2ExZWY1N2FlMGY3ZjE2ZjNhOGM0YTI2ZDhlMzkwYjdiYjVkMzdiMjFhZTYw
11
- MWExMDBjMmJhYzQ4ZjBiYWNjYzJlZDNmOTU3NmEyNzgxMzZkM2U=
9
+ MGQwMTI2MWFlODFiZjllMDIzNzQwOTU4ZWRmN2VmYjFkMTQwMDIxYzc1ZGZl
10
+ MzM4ZTU3MjAyOGU5OTEwOWVkYWE3ZTcxOWVhYjJhNzI2MWM2YTRhMzk1ZDFk
11
+ MzEwNTk1NGM5ZmE3NGY4MTI2MDQ4NDBjYzc4MDhjN2ZkZmExY2E=
12
12
  data.tar.gz: !binary |-
13
- OWE0ZGQ4MTJjYWE2Y2ZhN2QzYjVjYzE5MTE1MDk5OTY5OWVjNGUxODgzOGI2
14
- ZWI5NDY1Zjk4ZmRlMDgyMjI4OTk5OTA1NzkwZmQ1YWE1ZWVlMDBiNmJhMmRj
15
- ZmFiNmZiNzY3NWFmMTc0NzQ2YjQ1YjlhYzA5MjgzZjQ4MzJhM2Y=
13
+ YmU1MGIxODNiODAzNjFlMTFlY2NiY2ZjNzkyMmY3MjY5OWRmZTMwMjIwOTU2
14
+ N2ZmNzA2YjRjNjY5NzIxMTNmNGJiMTAzNTQwNDE4ZGFhMWZmNmJjYmNmYzlh
15
+ MTdlZWE1NzE0N2QxNmRmZjk5YmFjZmZmZmMzYjZkOGJiNjA2NDQ=
@@ -2,54 +2,87 @@ require 'redis-namespace'
2
2
  require_relative "nice_cache/fragment"
3
3
  require_relative "nice_cache/tag"
4
4
 
5
- module NiceCache
6
5
 
7
- def self.config(&block)
8
- block.call(self)
6
+ class NiceCache
7
+ extend Forwardable
8
+ attr_accessor :redis, :tag_store
9
+
10
+ COMMANDS_WITH_TAGS = [
11
+ :set,
12
+ :setnx,
13
+ :hmset,
14
+ :hsetnx,
15
+ :lset,
16
+ :sadd,
17
+ :zadd,
18
+ # :mset,
19
+ # :msetnx,
20
+ # :zinterstore,
21
+ # :zunionstore
22
+ ]
23
+
24
+ def convert_tags(arg)
25
+ arg.respond_to?(:key) ? arg[:tags] : arg
9
26
  end
10
27
 
11
- def self.redis
12
- @redis ||= namespaced_redis
28
+ COMMANDS_WITH_TAGS.each do |method|
29
+ define_method method, ->(*args) do
30
+ if args.count > Redis.instance_method(method).arity
31
+ tags = convert_tags(args.pop)
32
+ key = args[0]
33
+ Fragment.new(self, key, tags)
34
+ end
35
+
36
+ redis.send(method, *args)
37
+ end
13
38
  end
14
39
 
15
- def self.redis=(custom_redis)
16
- @redis = namespaced_redis(custom_redis)
40
+ def del(fragment_name)
41
+ Fragment.find(self, fragment_name).delete
17
42
  end
18
43
 
19
- def self.cache(data, tags)
20
- tag_names = tags.is_a?(Hash) ? tags[:tags] : Array(tags)
21
- Fragment.new(data, tag_names)
22
- self
44
+ def destroy(key)
45
+ redis.del key
23
46
  end
24
47
 
25
- def self.sweep_tags(*tag_names)
26
- Tag.find(tag_names).map do |tag|
27
- tag.sweep
28
- end
48
+ def initialize(options = {})
49
+ @redis = options[:redis] || Redis.current
50
+ @tag_store = Redis::Namespace.new(:NiceCache, redis: redis)
29
51
  end
30
52
 
31
- def self.sweep_fragments(*fragment_names)
32
- fragment_names.map do |fragment_name|
33
- Fragment.find(fragment_name).destroy
34
- end
53
+ def method_missing(method, *args, &block)
54
+ return super unless redis.respond_to?(method)
55
+
56
+ self.class.send(:define_method, method, ->(*arguments) {
57
+ redis.send(method, *arguments, &block)
58
+ })
59
+
60
+ self.send(method, *args, &block)
35
61
  end
36
62
 
37
- def self.all_fragments_key
38
- "AllFragments"
63
+ def respond_to_missing?(method_name, include_private = false)
64
+ redis.respond_to?(method_name, include_private) || super
39
65
  end
40
66
 
41
- def self.cleanup
42
- fragments.map(&:destroy)
67
+ def sweep_tags(*tag_names)
68
+ Tag.find(self, tag_names).map do |tag|
69
+ tag.sweep
70
+ end
43
71
  end
44
72
 
45
- def self.fragments
46
- # O(N), N is number of fragment keys in NiceCache
47
- redis.smembers(all_fragments_key).map { |name| Fragment.find(name) }
73
+ def flush_index!
74
+ #TODO: Benchmark
75
+ # all_fragments.map { |fragment| fragment.delete }
76
+ tag_store.keys.map { |key| tag_store.del key }
48
77
  end
49
78
 
50
- private
51
- def self.namespaced_redis(redis = nil)
52
- redis ||= Redis.current
53
- Redis::Namespace.new(:NiceCache, redis: redis)
79
+ def all_fragments
80
+ tag_store.smembers Fragment.all
54
81
  end
82
+
83
+ private
84
+
85
+ # def timestamp_key(time)
86
+ # "Timestamp:#{time}"
87
+ # end
55
88
  end
@@ -1,95 +1,81 @@
1
- require_relative 'tag'
2
-
3
- module NiceCache
1
+ class NiceCache
4
2
  class Fragment
5
- attr_reader :name, :data
6
-
7
- def self.find(name)
8
- new.find(name)
9
- end
10
-
11
- def self.key(name)
12
- "Fragment:#{name}"
13
- end
3
+ attr_reader :name
14
4
 
15
- def self.tags_for(name)
16
- "TagsFor:#{name}"
5
+ def self.all
6
+ "AllFragments"
17
7
  end
18
8
 
19
- def initialize(data = nil, tag_names = nil)
20
- create(data, tag_names) if data && tag_names
9
+ def self.find(nice_cache, name)
10
+ new(nice_cache, name)
21
11
  end
22
12
 
23
- def create(data, tag_names)
24
- @name = stringify(tag_names)
25
- @data = data
26
- attach_tags(tag_names)
27
- save_to_redis
28
- end
29
-
30
- def find(name)
13
+ def initialize(nice_cache, name, tag_names = [])
14
+ @nice_cache = nice_cache
31
15
  @name = name
32
- @data = data_from_redis
33
- self
16
+ unless tag_names.empty?
17
+ attach_tags(tag_names)
18
+ persist
19
+ end
34
20
  end
35
21
 
36
22
  def tags
37
- Tag.find(redis_tag_names)
23
+ Tag.find(@nice_cache, tags_list)
38
24
  end
39
25
 
40
26
  def attach_with(tag)
41
- redis.sadd(attached_tags, tag.name)
27
+ store.sadd(tags_for_me, tag.name)
42
28
  end
43
29
 
44
- def destroy
30
+ def delete
45
31
  tags.each do |tag|
46
32
  tag.remove_from(self)
47
33
  end
34
+ # TODO: Timestamps
48
35
 
49
- redis.srem(NiceCache.all_fragments_key, @name)
50
- redis.del attached_tags
51
- redis.del fragment_key
36
+ destroy
52
37
  end
53
38
 
54
39
  def ==(other)
55
- [name, data] == [other.name, other.data]
40
+ name == other.name
56
41
  rescue
57
42
  false
58
43
  end
59
44
 
60
45
  private
61
46
 
62
- def attach_tags(tag_names)
63
- Tag.find(tag_names).map{ |tag| tag.attach_on(self) }
64
- end
65
-
66
- def stringify(tag_names)
67
- tag_names.map{ |tag| tag.to_s.capitalize }.join("-")
47
+ def self.tags_for(name)
48
+ "TagsFor:#{name}"
68
49
  end
69
50
 
70
- def attached_tags
51
+ def tags_for_me
71
52
  self.class.tags_for(@name)
72
53
  end
73
54
 
74
- def fragment_key
75
- self.class.key(@name)
55
+ def persist
56
+ store.sadd(self.class.all, name)
76
57
  end
77
58
 
78
- def redis_tag_names
79
- redis.smembers(attached_tags)
59
+ def attach_tags(tag_names)
60
+ Tag.find(@nice_cache, tag_names).map{ |tag| tag.attach_on(self) }
80
61
  end
81
62
 
82
- def save_to_redis
83
- redis.sadd(NiceCache.all_fragments_key, @name)
84
- redis.set(fragment_key, @data)
63
+ def tags_list
64
+ store.smembers(tags_for_me)
85
65
  end
86
66
 
87
- def data_from_redis
88
- redis.get(fragment_key)
67
+ def destroy
68
+ store.srem(self.class.all, name)
69
+ store.del tags_for_me
70
+ @nice_cache.destroy(name)
89
71
  end
90
72
 
91
- def redis
92
- NiceCache.redis
73
+ def store
74
+ @nice_cache.tag_store
93
75
  end
76
+
77
+ # def add_timestamp
78
+ # redis.sadd(NiceCache.all_fragments_key, @name)
79
+ # end
94
80
  end
95
81
  end
@@ -1,41 +1,32 @@
1
- require_relative 'fragment'
2
-
3
- module NiceCache
1
+ class NiceCache
4
2
  class Tag
5
3
  attr_reader :name
6
4
 
7
- def self.find(*tag_names)
8
- Array(tag_names).flatten.map{ |tag_symbol| new(tag_symbol) }
9
- end
10
-
11
- def self.key(name)
12
- "Tag:#{name}"
5
+ def self.find(nice_cache, *tag_names)
6
+ tag_names.flatten.map{ |tag_symbol| new(nice_cache, tag_symbol) }
13
7
  end
14
8
 
15
- def initialize(name)
9
+ def initialize(nice_cache, name)
10
+ @nice_cache = nice_cache
16
11
  @name = name.to_s
17
12
  end
18
13
 
19
14
  def attach_on(fragment)
20
- redis.sadd(tag_key, fragment.name)
15
+ save_fragment(fragment.name)
21
16
  fragment.attach_with(self)
22
17
  self
23
18
  end
24
19
 
25
20
  def fragments
26
- fragment_keys.map{ |fragment_key| Fragment.find(fragment_key) }
27
- end
28
-
29
- def tag_key
30
- self.class.key(@name)
21
+ fragments_list.map{ |fragment_name| Fragment.find(@nice_cache, fragment_name) }
31
22
  end
32
23
 
33
24
  def remove_from(fragment)
34
- redis.srem(tag_key, fragment.name)
25
+ store.srem(tag_key, fragment.name)
35
26
  end
36
27
 
37
28
  def sweep
38
- fragments.map(&:destroy)
29
+ fragments.map(&:delete)
39
30
  destroy
40
31
  end
41
32
 
@@ -47,17 +38,28 @@ module NiceCache
47
38
 
48
39
  private
49
40
 
50
- def redis
51
- NiceCache.redis
41
+ def store
42
+ @nice_cache.tag_store
52
43
  end
53
44
 
54
- def destroy
55
- redis.del tag_key
45
+ def self.key(name)
46
+ "Tag:#{name}"
47
+ end
48
+
49
+ def save_fragment(fragment_name)
50
+ store.sadd(tag_key, fragment_name)
51
+ end
52
+
53
+ def tag_key
54
+ self.class.key(@name)
56
55
  end
57
56
 
58
- def fragment_keys
59
- redis.smembers(tag_key)
57
+ def destroy
58
+ store.del tag_key
60
59
  end
61
60
 
61
+ def fragments_list
62
+ store.smembers(tag_key)
63
+ end
62
64
  end
63
65
  end
@@ -1,3 +1,3 @@
1
- module NiceCache
2
- VERSION = "0.1.0"
1
+ class NiceCache
2
+ VERSION = "0.2.0"
3
3
  end
@@ -3,45 +3,37 @@ require 'mock_redis'
3
3
 
4
4
  describe NiceCache::Fragment do
5
5
 
6
- subject(:fragment) { NiceCache::Fragment.new("Some Data", [:Year2014, :Month01, :Date01]) }
6
+ let(:nice_cache) do
7
+ NiceCache.new(redis: REDIS) # => from spec/spec_helper.rb
8
+ end
9
+ let(:store) { nice_cache.tag_store}
10
+
7
11
 
8
12
  after :all do
9
13
  puts "=== Clean up redis ==="
10
- puts Benchmark.measure {
11
- NiceCache.cleanup
14
+ puts Benchmark.measure{
15
+ nice_cache.flush_index!
12
16
  }
13
17
  end
14
18
 
15
- before :all do
16
- NiceCache.config do |options|
17
- options.redis = REDIS # => from spec/spec_helper.rb
18
- end
19
- end
20
-
21
- describe "#new" do
22
- it "has correct name" do
23
- expect(fragment.name).to eq "Year2014-Month01-Date01"
24
- end
25
-
26
- it "has correct data" do
27
- expect(fragment.data).to eq "Some Data"
28
- end
29
- end
30
-
31
- describe "#find" do
32
- let(:fetch_fragment) { NiceCache::Fragment.find("Year2014-Month01-Date01") }
19
+ subject(:fragment) { NiceCache::Fragment.new(nice_cache, "Foo", [:Year2014, :Month01, :Date01]) }
33
20
 
34
- it "find correct data with fragment name" do
35
- expect(fetch_fragment.data).to eq "Some Data"
36
- end
21
+ it "has correct name" do
22
+ expect(fragment.name).to eq "Foo"
37
23
  end
38
24
 
39
- it "get correct #tags" do
40
- expect(fragment.tags.length).to eq 3
25
+ it "has correct tags number" do
26
+ expect(fragment.tags.count).to eq 3
41
27
  end
42
28
 
43
29
  it "can compare with any classes" do
44
30
  expect(fragment == []).to be_false
45
31
  end
32
+
33
+ it "remove fragment after #delete" do
34
+ expect(store.exists('TagsFor:Foo')).to be_true
35
+ fragment.delete
36
+ expect(store.exists('TagsFor:Foo')).to be_false
37
+ end
46
38
  end
47
39
 
@@ -3,31 +3,51 @@ require 'mock_redis'
3
3
 
4
4
  describe NiceCache::Tag do
5
5
 
6
- subject(:tag) { NiceCache::Tag.new(:"Year2014") }
6
+ let(:nice_cache) do
7
+ NiceCache.new(redis: REDIS) # => from spec/spec_helper.rb
8
+ end
9
+ let(:store) { nice_cache.tag_store }
10
+ subject(:tag) { NiceCache::Tag.new(nice_cache, :"Year2014") }
7
11
 
8
12
  after :all do
9
13
  puts "=== Clean up redis ==="
10
- puts Benchmark.measure {
11
- NiceCache.cleanup
14
+ puts Benchmark.measure{
15
+ nice_cache.flush_index!
12
16
  }
13
17
  end
14
18
 
15
- before :all do
16
- NiceCache.config do |options|
17
- options.redis = REDIS # => from spec/spec_helper.rb
18
- end
19
- end
20
-
21
19
  it "have correct name" do
22
20
  expect(tag.name).to eq "Year2014"
23
21
  end
24
22
 
25
- describe "#attach_on and #fragments" do
26
- it "get correct #fragments after #attach_on" do
27
- fragment = NiceCache::Fragment.new("Some Data", [:"Year2014", :"Month01", :"Date01"])
23
+ it "find tag" do
24
+ found_tag = NiceCache::Tag.find(nice_cache, :"Year2014")
25
+ expect(found_tag).to eq [tag]
26
+ end
27
+
28
+ describe "interaction with fragment" do
29
+
30
+ let(:fragment) { NiceCache::Fragment.new(nice_cache, "fragment name") }
31
+
32
+ before do
28
33
  tag.attach_on(fragment)
34
+ end
35
+
36
+ it "get correct #fragments after #attach_on" do
29
37
  expect(tag.fragments).to eq [fragment]
30
38
  end
39
+
40
+ it "clear fragments after #remove_from" do
41
+ tag.remove_from(fragment)
42
+ expect(tag.fragments).to eq []
43
+ end
44
+
45
+ it "remove after #sweep" do
46
+ expect(store.exists("Tag:Year2014")).to be_true
47
+ tag.sweep
48
+ expect(store.exists("Tag:Year2014")).to eq false
49
+ end
50
+
31
51
  end
32
52
 
33
53
  it "can compare with any classes" do
@@ -3,66 +3,86 @@ require 'benchmark'
3
3
 
4
4
  describe NiceCache do
5
5
 
6
- it "change redis from #config" do
7
- NiceCache.config do |options|
8
- options.redis = REDIS # => from spec/spec_helper.rb
9
- end
10
-
11
- expect(NiceCache.redis.redis.class).to eq REDIS.class
6
+ subject(:nice_cache) do
7
+ NiceCache.new(redis: REDIS) # => from spec/spec_helper.rb
12
8
  end
13
9
 
14
10
  after :all do
15
11
  puts "=== Clean up redis ==="
16
- puts Benchmark.measure {
17
- NiceCache.cleanup
12
+ puts Benchmark.measure{
13
+ nice_cache.flush_index!
18
14
  }
19
15
  end
20
16
 
21
- before :each do
22
- NiceCache.cache("Some Data", tags: [:Year2014, :Month01, :Date01])
23
- end
17
+ it "respond_to all redis public methods" do
18
+ redis_methods = Redis.instance_methods(false)
24
19
 
25
- it "cleanup all the fragments and tags" do
26
- NiceCache::Fragment.find("Year2014-Month01-Date01")
27
- expect(NiceCache.redis.keys("*")).to_not be_empty
28
- NiceCache.cleanup
29
- expect(NiceCache.redis.keys("*")).to be_empty
20
+ redis_methods.each do |method|
21
+ expect(nice_cache.respond_to? method).to be_true
22
+ end
30
23
  end
31
24
 
32
- let(:found_fragment) { NiceCache::Fragment.find("Year2014-Month01-Date01") }
25
+ it "pass message to redis by #method_missing" do
26
+ expect(nice_cache.dbsize.is_a? Fixnum).to be_true
27
+ end
33
28
 
34
- it "cache fragment to #fragments" do
35
- expect(NiceCache.fragments).to eq [found_fragment]
29
+ it "not #respond_to wrong methods" do
30
+ expect(nice_cache.respond_to? :foo).to be_false
36
31
  end
37
32
 
38
- it "remove fragment" do
39
- NiceCache.sweep_fragments("Year2014-Month01-Date01")
40
- expect(NiceCache.fragments).to eq []
33
+ context "add a string" do
34
+
35
+ before :each do
36
+ @result = nice_cache.set("foo", "Some Data", tags: [:bar, :baz])
37
+ @tags = NiceCache::Tag.find(nice_cache, :bar)
38
+ end
39
+
40
+ it "set a string in redis with tags" do
41
+ expect(@result).to eq "OK"
42
+ end
43
+
44
+ it "set up the tags" do
45
+ expect(@tags.first.fragments).to_not be_empty
46
+ end
47
+
48
+ it "delete item in redis" do
49
+ expect(nice_cache.del "foo").to eq 1
50
+ end
51
+
52
+ it "delete tags and fragment" do
53
+ expect(nice_cache.tag_store.exists('TagsFor:foo')).to be_true
54
+ nice_cache.del "foo"
55
+ expect(nice_cache.tag_store.exists('TagsFor:foo')).to be_false
56
+ end
41
57
  end
42
58
 
43
- context "multiple fragments" do
59
+ context "sorted set" do
44
60
  before :each do
45
- NiceCache.cache("Other Data", tags: [:Year2014, :Month02, :Date01])
61
+ @result = nice_cache.zadd("TheSortedSet", 1, "one", tags: [:bar, :baz])
46
62
  end
47
63
 
48
- it "get correct #fragments length" do
49
- expect(NiceCache.fragments.length).to eq 2
64
+ it "fetch the correct item with #zrange" do
65
+ expect(nice_cache.zrange("TheSortedSet", 0, -1)).to eq ["one"]
50
66
  end
51
67
 
52
- it "sweep only one matched fragment" do
53
- p Benchmark.measure {
54
- NiceCache.sweep_tags(:Month02)
55
- expect(NiceCache.fragments.length).to eq 1
56
- }
68
+ it "sweep item with #sweep_tags" do
69
+ nice_cache.sweep_tags("bar")
70
+ expect(nice_cache.redis.exists("TheSortedSet")).to be_false
57
71
  end
58
72
 
59
- it "sweep both of matching fragments" do
60
- p Benchmark.measure {
61
- NiceCache.sweep_tags(:Year2014)
62
- expect(NiceCache.fragments.length).to eq 0
63
- }
73
+ it "sweep all item of same tag with #sweep_tags" do
74
+ nice_cache.set("TheString", "SomeData", tags: [:bar, :foo])
75
+ expect(nice_cache.redis.exists("TheSortedSet")).to be_true
76
+ nice_cache.sweep_tags("bar")
77
+ expect(nice_cache.redis.exists("TheSortedSet")).to be_false
64
78
  end
65
79
 
80
+ it "sweep items form #all_fragments" do
81
+ nice_cache.sadd("TheSet", "SomeData", tags: [:bar, :zoo])
82
+ expect(nice_cache.all_fragments).to match_array ["TheSortedSet", "TheSet"]
83
+ nice_cache.sweep_tags("zoo")
84
+ expect(nice_cache.all_fragments).to eq ["TheSortedSet"]
85
+ end
66
86
  end
67
87
  end
68
88
 
@@ -9,7 +9,8 @@ Dir[File.join(GEM_ROOT, 'spec', 'support', '**/*.rb')].each { |f| require f }
9
9
 
10
10
  require 'mock_redis'
11
11
  require 'redis-namespace'
12
- REDIS = MockRedis.new
13
- # REDIS = Redis.new
14
12
 
15
13
  require 'pry'
14
+ require 'benchmark'
15
+
16
+ REDIS = Redis::Namespace.new(:Spec, MockRedis.new)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nice_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taian Su
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-13 00:00:00.000000000 Z
11
+ date: 2014-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis