cachew 0.1.1 → 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.
@@ -14,23 +14,61 @@ ClassLength:
14
14
  CyclomaticComplexity:
15
15
  Max: 6
16
16
 
17
+ EmptyLineBetweenDefs:
18
+ AllowAdjacentOneLineDefs: true
19
+
17
20
  BlockNesting:
18
21
  Max: 3
19
22
 
20
23
  HashSyntax:
21
24
  EnforcedStyle: hash_rockets
22
25
 
23
- Encoding:
24
- Enabled: false
25
-
26
26
  StringLiterals:
27
27
  EnforcedStyle: double_quotes
28
28
 
29
+ AlignParameters:
30
+ EnforcedStyle: with_fixed_indentation
31
+
32
+ IndentHash:
33
+ EnforcedStyle: consistent
34
+
35
+ PercentLiteralDelimiters:
36
+ PreferredDelimiters:
37
+ '%': ()
38
+ '%i': ()
39
+ '%q': ()
40
+ '%Q': ()
41
+ '%r': '{}'
42
+ '%s': ()
43
+ '%w': '[]'
44
+ '%W': '[]'
45
+ '%x': ()
46
+
47
+ Encoding:
48
+ Enabled: false
49
+
29
50
  BracesAroundHashParameters:
30
51
  Enabled: false
31
52
 
32
53
  Documentation:
33
54
  Enabled: false
34
55
 
35
- EmptyLines:
56
+ # Not all trivial readers/writers can be defined with attr_* methods
57
+ TrivialAccessors:
58
+ Enabled: false
59
+
60
+ # New lambda syntax is UGLY, don't enforce it
61
+ Lambda:
36
62
  Enabled: false
63
+
64
+ # Ridiculous (and IMHO useless) restriction, that makes impossible aligning
65
+ # code like this:
66
+ #
67
+ # redis.hset :k1, now
68
+ # redis.hincrby :k2, 123
69
+ SingleSpaceBeforeFirstArg:
70
+ Enabled: false
71
+
72
+ AllCops:
73
+ Include:
74
+ - Gemfile
data/Gemfile CHANGED
@@ -1,17 +1,20 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
+
3
+ platforms :ruby_19, :ruby_20, :ruby_21 do
4
+ group :development do
5
+ gem "celluloid-io"
6
+ gem "guard-rspec"
7
+ end
8
+
9
+ gem "rubocop"
10
+ end
2
11
 
3
12
  gem "rake"
4
- gem "rspec"
5
- gem "guard-rspec"
6
- gem "rubocop"
13
+ gem "rspec", "~> 3.0"
14
+ gem "timecop"
15
+
7
16
  gem "coveralls", :require => false
8
17
  gem "simplecov", :require => false
9
18
 
10
- platforms :rbx do
11
- gem "racc"
12
- gem "rubinius-coverage", "~> 2.0"
13
- gem "rubysl", "~> 2.0"
14
- end
15
-
16
19
  # Specify your gem's dependencies in cachew.gemspec
17
20
  gemspec
data/README.md CHANGED
@@ -24,24 +24,57 @@ Or install it yourself as:
24
24
  ## Usage
25
25
 
26
26
  ``` ruby
27
- # With HashAdapter
28
- cache = Cachew.new({})
27
+ # In-memory Hash
28
+ cache = Cachew::Hash.new
29
29
 
30
- cache.has?(:foo) # => false
31
- cache.fetch(:foo) { :bar } # => :bar
30
+ cache.fetch(:foo) { Time.now.to_i } # => 1405724687
31
+ cache.fetch(:foo) { Time.now.to_i } # => 1405724687
32
32
 
33
- cache.has?(:foo) # => true
34
- cache.fetch(:foo) { :moo } # => :bar
33
+ # With ttl
34
+ cache.fetch(:foo, :ttl => 10) { Time.now.to_i } # => 1405724689
35
35
 
36
+ # Before 10 seconds will expire
37
+ cache.fetch(:foo, :ttl => 10) { Time.now.to_i } # => 1405724689
36
38
 
37
- # With (default) NullAdapter
38
- cache = Cachew.new
39
+ # Once 10+ seconds pass
40
+ cache.fetch(:foo, :ttl => 10) { Time.now.to_i } # => 1405724695
39
41
 
40
- cache.has?(:foo) # => false
41
- cache.fetch(:foo) { :bar } # => :bar
42
+ # Withot cache (think of it as NullLogger)
43
+ cache = Cachew::Null.new
42
44
 
43
- cache.has?(:foo) # => false
44
- cache.fetch(:foo) { :moo } # => :moo
45
+ cache.fetch(:foo) { Time.now.to_i } # => 1405724687
46
+ cache.fetch(:foo) { Time.now.to_i } # => 1405724688
47
+ ```
48
+
49
+ You can easily write your own adapters:
50
+
51
+ ```
52
+ class Cachew::Redis < Cache::Adapter
53
+ def initialize(client)
54
+ @client = client
55
+ end
56
+
57
+ private
58
+
59
+ def __set__(key, val, ttl)
60
+ val = Marshal.dump val
61
+
62
+ if 0 == ttl
63
+ @client.set key, val
64
+ else
65
+ @client.setex key, val, ttl
66
+ end
67
+ end
68
+
69
+ def __get__(key)
70
+ val = @client.get(key)
71
+ val ? Marshal.load(val) : UNDEFINED
72
+ end
73
+
74
+ def __key__(*)
75
+ "cachew:#{super}"
76
+ end
77
+ end
45
78
  ```
46
79
 
47
80
  ## Contributing
data/Rakefile CHANGED
@@ -1,9 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
- require 'rspec/core/rake_task'
3
+ require "rspec/core/rake_task"
4
4
  RSpec::Core::RakeTask.new
5
5
 
6
- require 'rubocop/rake_task'
7
- Rubocop::RakeTask.new
6
+ require "rubocop/rake_task"
7
+ RuboCop::RakeTask.new
8
8
 
9
9
  task :default => [:spec, :rubocop]
@@ -18,5 +18,5 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.5"
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
22
  end
@@ -1,19 +1,4 @@
1
+ require "cachew/adapter"
2
+ require "cachew/hash"
3
+ require "cachew/null"
1
4
  require "cachew/version"
2
- require "cachew/adapters"
3
-
4
- # Unified cache interface
5
- class Cachew
6
- extend Forwardable
7
-
8
- attr_reader :adapter
9
-
10
- def initialize(store = nil)
11
- @adapter = Adapters.build_adapter_for store
12
- end
13
-
14
- def fetch(key)
15
- has?(key) ? get(key) : set(key, yield)
16
- end
17
-
18
- def_delegators :adapter, :set, :get, :has?
19
- end
@@ -0,0 +1,78 @@
1
+ require "digest/md5"
2
+ require "json"
3
+
4
+ module Cachew
5
+ # Base class for Cachew adapters.
6
+ #
7
+ # @example Building custom adapters
8
+ #
9
+ # class Cachew::Redis < Cache::Adapter
10
+ # def initialize(client)
11
+ # @client = client
12
+ # end
13
+ #
14
+ # private
15
+ #
16
+ # def __set__(key, val, ttl)
17
+ # val = Marshal.dump val
18
+ #
19
+ # if 0 == ttl
20
+ # @client.set key, val
21
+ # else
22
+ # @client.setex key, val, ttl
23
+ # end
24
+ # end
25
+ #
26
+ # def __get__(key)
27
+ # val = @client.get(key)
28
+ # val ? Marshal.load(val) : UNDEFINED
29
+ # end
30
+ #
31
+ # def __key__(*)
32
+ # "cachew:#{super}"
33
+ # end
34
+ # end
35
+ #
36
+ class Adapter
37
+ # Internal constant used by `__get__` to notify that value is not in the
38
+ # cache or became stale
39
+ UNDEFINED = Object.new.freeze
40
+
41
+ # @example Usage
42
+ #
43
+ # cachew.fetch "some_key", :ttl => 60 do
44
+ # HTTP.get("http://example.com").to_s
45
+ # end
46
+ #
47
+ def fetch(key, opts = {})
48
+ key = __key__ key, opts
49
+ val = __get__ key
50
+
51
+ if UNDEFINED.equal? val
52
+ val = yield
53
+ ttl = opts.fetch(:ttl) { 0 }.to_i
54
+
55
+ __set__ key, val, ttl
56
+ end
57
+
58
+ val
59
+ end
60
+
61
+ private
62
+
63
+ # :nodoc:
64
+ def __set__(_key, _val, _ttl)
65
+ fail "Not implemented"
66
+ end
67
+
68
+ # :nodoc:
69
+ def __get__(_key)
70
+ fail "Not implemented"
71
+ end
72
+
73
+ # :nodoc:
74
+ def __key__(*args)
75
+ Digest::MD5.hexdigest JSON.dump args
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,25 @@
1
+ require "cachew/adapter"
2
+
3
+ module Cachew
4
+ # In-memory cache adapter.
5
+ class Hash < Adapter
6
+ # @param [#to_hash] store Underlying Hash used for storage
7
+ def initialize(store = {})
8
+ @store = store.to_hash
9
+ end
10
+
11
+ private
12
+
13
+ # :nodoc:
14
+ def __set__(key, val, ttl)
15
+ ttl = 0 >= ttl ? 0 : Time.now.to_i + ttl
16
+ @store[key] = [val, ttl]
17
+ end
18
+
19
+ # :nodoc:
20
+ def __get__(key)
21
+ val, ttl = @store[key]
22
+ ttl && (0 == ttl || ttl > Time.now.to_i) ? val : UNDEFINED
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ require "cachew/adapter"
2
+
3
+ module Cachew
4
+ # Null version of Cachew. Never caches anything, and returns value of given
5
+ # block directly.
6
+ class Null < Adapter
7
+ class << self
8
+ # Returns singleton instance of Null adapter.
9
+ # @return [Null]
10
+ def new
11
+ @instance ||= super
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ # :nodoc:
18
+ def __get__(*)
19
+ UNDEFINED
20
+ end
21
+
22
+ # :nodoc:
23
+ def __set__(*) end
24
+
25
+ # :nodoc:
26
+ def __key__(*) end
27
+ end
28
+ end
@@ -1,3 +1,4 @@
1
- class Cachew
2
- VERSION = "0.1.1"
1
+ module Cachew
2
+ # Cachew version
3
+ VERSION = "0.2.0"
3
4
  end
@@ -0,0 +1,75 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Cachew::Hash do
4
+ describe ".new" do
5
+ it "accepts any #to_hash object" do
6
+ storage = double(:to_hash => {})
7
+
8
+ described_class.new(storage).fetch(:foo) { :bar }
9
+
10
+ expect(storage.to_hash).not_to be_empty
11
+ end
12
+
13
+ it "tolerates argument-less call" do
14
+ expect { described_class.new }.not_to raise_error
15
+ end
16
+ end
17
+
18
+ describe "#[]" do
19
+ let(:cachew) { described_class.new }
20
+
21
+ context "when value is not cahed yet" do
22
+ it "evaluates given block" do
23
+ expect { |b| cachew.fetch :foo, &b }.to yield_control
24
+ end
25
+
26
+ it "returns evaluated value" do
27
+ expect(cachew.fetch(:foo) { :bar }).to be :bar
28
+ end
29
+ end
30
+
31
+ context "when value was already cached" do
32
+ before { cachew.fetch(:foo) { :cached } }
33
+
34
+ it "does not evaluates block" do
35
+ expect { |b| cachew.fetch(:foo, &b) }.not_to yield_control
36
+ end
37
+
38
+ it "returns previously cached value" do
39
+ expect(cachew.fetch(:foo) { :bar }).to be :cached
40
+ end
41
+ end
42
+
43
+ context "when value with expiration still fresh" do
44
+ before { cachew.fetch(:foo, :ttl => 84) { :cached } }
45
+
46
+ it "does not evaluates given block" do
47
+ Timecop.travel 42 do
48
+ expect { |b| cachew.fetch :foo, :ttl => 84, &b }.not_to yield_control
49
+ end
50
+ end
51
+
52
+ it "returns previously cached value" do
53
+ Timecop.travel 42 do
54
+ expect(cachew.fetch(:foo, :ttl => 84) { :bar }).to be :cached
55
+ end
56
+ end
57
+ end
58
+
59
+ context "when value with expiration became stale" do
60
+ before { cachew.fetch(:foo, :ttl => 21) { :cached } }
61
+
62
+ it "evaluates given block" do
63
+ Timecop.travel 42 do
64
+ expect { |b| cachew.fetch :foo, :ttl => 21, &b }.to yield_control
65
+ end
66
+ end
67
+
68
+ it "returns evaluated value" do
69
+ Timecop.travel 42 do
70
+ expect(cachew.fetch(:foo, :ttl => 21) { :bar }).to be :bar
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Cachew::Null do
4
+ let(:cachew) { described_class.new }
5
+
6
+ describe ".new" do
7
+ it "returns Singleton instance" do
8
+ expect(cachew).to be described_class.new
9
+ end
10
+ end
11
+
12
+ describe "#[]" do
13
+ it "always evaluates given block" do
14
+ expect { |b| cachew.fetch(:foo, &b) }.to yield_control
15
+ end
16
+
17
+ it "returns value of given block" do
18
+ [:foo, :bar, :baz].each do |val|
19
+ expect(cachew.fetch(:foo) { val }).to be val
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,23 +1,20 @@
1
1
  require "simplecov"
2
2
  require "coveralls"
3
-
3
+ require "timecop"
4
4
 
5
5
  SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
6
  SimpleCov::Formatter::HTMLFormatter,
7
7
  Coveralls::SimpleCov::Formatter
8
8
  ]
9
9
 
10
-
11
10
  SimpleCov.start do
12
11
  add_filter "/spec/"
13
12
  end
14
13
 
14
+ Timecop.safe_mode = true
15
15
 
16
16
  require "cachew"
17
17
 
18
-
19
18
  RSpec.configure do |config|
20
- config.expect_with :rspec do |c|
21
- c.syntax = :expect
22
- end
19
+ config.disable_monkey_patching!
23
20
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cachew
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
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: 2014-02-03 00:00:00.000000000 Z
12
+ date: 2014-07-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '1.5'
21
+ version: '1.6'
22
22
  type: :development
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: '1.5'
29
+ version: '1.6'
30
30
  description: Unified cache interface.
31
31
  email:
32
32
  - ixti@member.fsf.org
@@ -44,15 +44,12 @@ files:
44
44
  - Rakefile
45
45
  - cachew.gemspec
46
46
  - lib/cachew.rb
47
- - lib/cachew/adapters.rb
48
- - lib/cachew/adapters/base_adapter.rb
49
- - lib/cachew/adapters/hash_adapter.rb
50
- - lib/cachew/adapters/null_adapter.rb
47
+ - lib/cachew/adapter.rb
48
+ - lib/cachew/hash.rb
49
+ - lib/cachew/null.rb
51
50
  - lib/cachew/version.rb
52
- - spec/lib/cachew/adapters/hash_adapter_spec.rb
53
- - spec/lib/cachew/adapters/null_adapter_spec.rb
54
- - spec/lib/cachew/adapters_spec.rb
55
- - spec/lib/cachew_spec.rb
51
+ - spec/lib/cachew/hash_spec.rb
52
+ - spec/lib/cachew/null_spec.rb
56
53
  - spec/spec_helper.rb
57
54
  homepage: https://github.com/ixti/cachew
58
55
  licenses:
@@ -69,7 +66,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
69
66
  version: '0'
70
67
  segments:
71
68
  - 0
72
- hash: 797334216198397338
69
+ hash: 4155366000220806670
73
70
  required_rubygems_version: !ruby/object:Gem::Requirement
74
71
  none: false
75
72
  requirements:
@@ -78,16 +75,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
75
  version: '0'
79
76
  segments:
80
77
  - 0
81
- hash: 797334216198397338
78
+ hash: 4155366000220806670
82
79
  requirements: []
83
80
  rubyforge_project:
84
- rubygems_version: 1.8.23
81
+ rubygems_version: 1.8.23.2
85
82
  signing_key:
86
83
  specification_version: 3
87
- summary: cachew-0.1.1
84
+ summary: cachew-0.2.0
88
85
  test_files:
89
- - spec/lib/cachew/adapters/hash_adapter_spec.rb
90
- - spec/lib/cachew/adapters/null_adapter_spec.rb
91
- - spec/lib/cachew/adapters_spec.rb
92
- - spec/lib/cachew_spec.rb
86
+ - spec/lib/cachew/hash_spec.rb
87
+ - spec/lib/cachew/null_spec.rb
93
88
  - spec/spec_helper.rb
@@ -1,16 +0,0 @@
1
- require "cachew/adapters/base_adapter"
2
- require "cachew/adapters/hash_adapter"
3
- require "cachew/adapters/null_adapter"
4
-
5
- class Cachew
6
- module Adapters
7
- def self.build_adapter_for(store)
8
- case store
9
- when BaseAdapter then store
10
- when Cachew then store.adapter
11
- when Hash then HashAdapter.new(store)
12
- else NullAdapter.new
13
- end
14
- end
15
- end
16
- end
@@ -1,5 +0,0 @@
1
- class Cachew
2
- module Adapters
3
- BaseAdapter = Struct.new(:store)
4
- end
5
- end
@@ -1,17 +0,0 @@
1
- class Cachew
2
- module Adapters
3
- class HashAdapter < BaseAdapter
4
- def set(key, value)
5
- store[key] = value
6
- end
7
-
8
- def get(key)
9
- store[key]
10
- end
11
-
12
- def has?(key)
13
- store.key?(key)
14
- end
15
- end
16
- end
17
- end
@@ -1,17 +0,0 @@
1
- class Cachew
2
- module Adapters
3
- class NullAdapter < BaseAdapter
4
- def set(_, value)
5
- value
6
- end
7
-
8
- def get(_)
9
- nil
10
- end
11
-
12
- def has?(_)
13
- false
14
- end
15
- end
16
- end
17
- end
@@ -1,34 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Cachew::Adapters::HashAdapter do
4
- let(:adapter) { described_class.new({}) }
5
-
6
- describe "#set" do
7
- it "returns back given value" do
8
- expect(adapter.set(:foo, :bar)).to be :bar
9
- end
10
-
11
- it "sets value in the underlying store Hash" do
12
- adapter.set(:foo, :bar)
13
- expect(adapter.store).to eq :foo => :bar
14
- end
15
- end
16
-
17
- describe "#get" do
18
- it "returns nil if value does not exists" do
19
- expect(adapter.get(:foo)).to be_nil
20
- end
21
-
22
- it "returns saved value if exists" do
23
- adapter.store[:foo] = :bar
24
- expect(adapter.get(:foo)).to be :bar
25
- end
26
- end
27
-
28
- describe "#has?" do
29
- it "returns true even if stored value is nil" do
30
- adapter.store[:foo] = nil
31
- expect(adapter.has?(:foo)).to be_true
32
- end
33
- end
34
- end
@@ -1,25 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Cachew::Adapters::NullAdapter do
4
- let(:adapter) { described_class.new }
5
-
6
- describe "#set" do
7
- it "returns back given value" do
8
- expect(adapter.set(:foo, :bar)).to be :bar
9
- end
10
- end
11
-
12
- describe "#get" do
13
- it "always returns nil" do
14
- adapter.set(:foo, :bar)
15
- expect(adapter.get :foo).to be_nil
16
- end
17
- end
18
-
19
- describe "#has?" do
20
- it "always returns false" do
21
- adapter.set(:foo, :bar)
22
- expect(adapter.has? :foo).to be_false
23
- end
24
- end
25
- end
@@ -1,31 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Cachew::Adapters do
4
- describe ".build_adapter_for" do
5
- context "with Hash instance" do
6
- it "returns HashAdapter" do
7
- adapter = described_class.build_adapter_for :foo => :bar
8
- expect(adapter).to be_a Cachew::Adapters::HashAdapter
9
- end
10
- end
11
-
12
- context "with instance of BaseAdapter" do
13
- it "returns adapter as is" do
14
- adapter = Cachew::Adapters::HashAdapter.new :foo => :bar
15
- expect(described_class.build_adapter_for adapter).to be adapter
16
- end
17
- end
18
-
19
- context "with instance of Cachew" do
20
- it "returns original #adapter" do
21
- original = Cachew.new :foo => :bar
22
- expect(Cachew.new(original).adapter).to be original.adapter
23
- end
24
- end
25
-
26
- it "returns NullAdapter if can't find better candidate" do
27
- expect(described_class.build_adapter_for "test")
28
- .to be_a Cachew::Adapters::NullAdapter
29
- end
30
- end
31
- end
@@ -1,53 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Cachew do
4
- describe ".new" do
5
- it "initiates with NullAdapter by default" do
6
- expect(described_class.new.adapter).to be_a Cachew::Adapters::BaseAdapter
7
- end
8
-
9
- it "calls Adapters.build_adapter_for with given store" do
10
- store = { :foo => :bar }
11
- expect(Cachew::Adapters).to receive(:build_adapter_for).with store
12
- described_class.new store
13
- end
14
- end
15
-
16
- describe "#set" do
17
- subject(:cachew) { described_class.new.set }
18
- specify { expect { cachew.adapter.to receive :set } }
19
- end
20
-
21
- describe "#get" do
22
- subject(:cachew) { described_class.new.get }
23
- specify { expect { cachew.adapter.to receive :get } }
24
- end
25
-
26
- describe "#has?" do
27
- subject(:cachew) { described_class.new.has? }
28
- specify { expect { cachew.adapter.to receive :has? } }
29
- end
30
-
31
- describe "#fetch" do
32
- let(:cachew) { described_class.new :foo => :bar }
33
-
34
- it "calls has?" do
35
- expect(cachew).to receive(:has?)
36
- cachew.fetch(:foo) { :moo }
37
- end
38
-
39
- context "when adapter has? key" do
40
- it "calls get" do
41
- expect(cachew).to receive(:get)
42
- cachew.fetch(:foo) { :moo }
43
- end
44
- end
45
-
46
- context "when adapter does not has? key" do
47
- it "calls set" do
48
- expect(cachew).to receive(:set)
49
- cachew.fetch(:bar) { :moo }
50
- end
51
- end
52
- end
53
- end