nice_cache 0.1.0 → 0.2.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 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