cache_shoe 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4eddbae252fc203acd85d51eb3c106cb34f0fcd5
4
+ data.tar.gz: 641516d5d6a8c54260cd4bcbd7373fa101702506
5
+ SHA512:
6
+ metadata.gz: 4c0770ef7fd891dcb80b98fb5ec85b11ebf0c659eab815d792a6cf06f5dd6b04109a601bc7092de533fe6e08657db52c48a7d4b58db415ae2246f3c87630b002
7
+ data.tar.gz: d3ad26f6b649979717c191ad968a5a0ae4dcb2c46a41bc0c8a2a37127ff713e8f53bf0b164a060db5fce348014d77995bd6928f52b5b04e72fa35a0691a82816
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1
5
+ - 2.2
6
+ - ruby-head
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cache_shoe.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Jeff Deville
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,32 @@
1
+ # CacheShoe
2
+ [![Build Status](https://travis-ci.org/promptworks/cache_shoe.png?branch=master)](https://travis-ci.org/promptworks/cache_shoe)
3
+
4
+ TODO: Write a gem description
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'cache_shoe'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install cache_shoe
21
+
22
+ ## Usage
23
+
24
+ TODO: Write usage instructions here
25
+
26
+ ## Contributing
27
+
28
+ 1. Fork it ( https://github.com/[my-github-username]/cache_shoe/fork )
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create a new Pull Request
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ task default: [:spec]
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "cache_shoe"
7
+ spec.version = "0.0.1"
8
+ spec.authors = ["Jeff Deville"]
9
+ spec.email = ["jeffdeville@gmail.com"]
10
+ spec.summary = %q{Easily add caching and invalidation to RESTful clients}
11
+ spec.description = spec.summary
12
+ spec.homepage = "https://github.com/promptworks/cache_shoe"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "rspec", "~> 2.14.1"
23
+ spec.add_development_dependency "rspec-given"
24
+ spec.add_development_dependency "activesupport"
25
+ end
@@ -0,0 +1,162 @@
1
+ require 'logger'
2
+
3
+ module CacheShoe
4
+ PASS_THROUGH = :_pass_through
5
+
6
+ Configuration = Struct.new(:cache, :on_cache, :on_cache_clear, :logger) do
7
+ def logger
8
+ @logger ||= Logger.new(StringIO.new)
9
+ end
10
+ end
11
+
12
+ def self.included(base)
13
+ class << base
14
+ prepend ClassMethods
15
+ end
16
+ end
17
+
18
+ def self.config
19
+ @config ||= Configuration.new
20
+ yield @config if block_given?
21
+ @config
22
+ end
23
+
24
+ private
25
+
26
+ def self.get_cache_args(key_extractor, *args)
27
+ case key_extractor
28
+ when PASS_THROUGH then args
29
+ when Proc then [key_extractor.call(*args)]
30
+ when Symbol then [args.first.send(key_extractor)]
31
+ else
32
+ fail "Can't create a cache key from #{key_extractor.inspect}"
33
+ end
34
+ end
35
+
36
+ # Any way to delete this? Does the rails cache give us
37
+ # any of this for free?
38
+ def self.cache_key(class_name, method_id, args)
39
+ [class_name,
40
+ method_id,
41
+ (args.empty? ? 'empty' : CacheShoe.digest(args))
42
+ ].join("::")
43
+ end
44
+
45
+ def self.digest(obj)
46
+ resolve_cache_key(obj).to_s.downcase
47
+ end
48
+
49
+ def self.resolve_cache_key(obj)
50
+ case obj
51
+ when ::Array then obj.map { |v| resolve_cache_key v }
52
+ when ::Hash
53
+ obj.each_with_object({}) do |(k, v), memo|
54
+ memo[resolve_cache_key(k)] = resolve_cache_key(v)
55
+ end
56
+ else
57
+ obj
58
+ end
59
+ end
60
+
61
+ def self.on_cache_hit(key_val)
62
+ if config.on_cache
63
+ config.on_cache.call(key_val, :hit)
64
+ end
65
+ logger.info "cache hit #{key_val}"
66
+ end
67
+
68
+ def self.on_cache_miss(key_val)
69
+ if config.on_cache
70
+ config.on_cache.call(key_val, :miss)
71
+ end
72
+ logger.info "cache miss #{key_val}"
73
+ end
74
+
75
+ def self.set_cache(key_val, cache_val)
76
+ cache.write(key_val, nil_wrapper(cache_val))
77
+ nil_wrapper(cache_val)
78
+ end
79
+
80
+ def self.cache_method_clear(key)
81
+ cache.delete(key)
82
+ end
83
+
84
+ def self.cache
85
+ config.cache
86
+ end
87
+
88
+ def self.logger
89
+ config.logger
90
+ end
91
+
92
+ # Wrap result in an Array, so that if the cached method returns nil,
93
+ # that nil will be cached
94
+ def self.nil_wrapper(value)
95
+ [value]
96
+ end
97
+
98
+ def self.nil_unwrapper(wrapped_result)
99
+ wrapped_result.first
100
+ end
101
+
102
+ def self.on_cache_clear(
103
+ class_name, cached_method, clearing_method, key_extractors, *args)
104
+ Array(key_extractors).each do |key_extractor|
105
+ begin
106
+ cache_args = get_cache_args(key_extractor, *args)
107
+ cached_key = cache_key(class_name, cached_method, cache_args)
108
+ logger.info "Clearing cache from #{clearing_method}: #{cached_key}"
109
+ if config.on_cache_clear
110
+ config.on_cache_clear.call(
111
+ cached_key, clearing_method)
112
+ end
113
+ cache_method_clear cached_key
114
+ rescue
115
+ logger.error "Failed to clear cache from #{self.class.name}." \
116
+ "#{clearing_method}, because the key extractor raised"
117
+ end
118
+ end
119
+ end
120
+
121
+ module ClassMethods
122
+ module Helpers
123
+ # Move the CacheShoe methods here
124
+ def wrap_the_method_to_cache(method_id)
125
+ define_method method_id do |*args, &blk|
126
+ class_name = self.class.name
127
+ key_val = CacheShoe.cache_key(class_name, method_id, args)
128
+
129
+ cache_hit = true
130
+ result = CacheShoe.cache.fetch(key_val) do
131
+ cache_hit = false
132
+ CacheShoe.on_cache_miss key_val
133
+ CacheShoe.nil_wrapper super(*args, &blk)
134
+ end
135
+ CacheShoe.on_cache_hit(key_val) if cache_hit
136
+ CacheShoe.nil_unwrapper result
137
+ end
138
+ end
139
+
140
+ def create_cache_clear_wrapper_methods(cached_method, clear_on)
141
+ clear_on.each do |clearing_method, key_extractors|
142
+ define_method clearing_method do |*args, &blk|
143
+ class_name = self.class.name
144
+ CacheShoe.on_cache_clear(
145
+ class_name, cached_method,
146
+ clearing_method, key_extractors, *args)
147
+ super(*args, &blk)
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ def cache_method(method_to_cache, clear_on: {})
154
+ dyn_module = Module.new do
155
+ extend Helpers
156
+ wrap_the_method_to_cache(method_to_cache)
157
+ create_cache_clear_wrapper_methods(method_to_cache, clear_on)
158
+ end
159
+ prepend(dyn_module)
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ describe "when caching a service-style object" do
4
+ Given { CacheShoe.config.cache.clear }
5
+ Given(:thing) { Thing.new(123, 'name') }
6
+
7
+ shared_examples_for "caching" do
8
+ context "read caching" do
9
+ context "when nil, it should cache the nil" do
10
+ Given { service_ex.read("INVALID_ID") } # cache the nil
11
+ When(:result) { service_ex.read("INVALID_ID") } # now hit the cache
12
+ Then { service_ex.read_calls == 1 }
13
+ end
14
+
15
+ context "when not nil, it should cache that" do
16
+ Given { service_ex.create thing }
17
+ When { 2.times { service_ex.read(thing.id) } }
18
+ Then { service_ex.read_calls == 1 }
19
+ end
20
+ end
21
+
22
+ context "when creating it wipes the cache" do
23
+ Given { service_ex.read(123) } # populate the cache with nil
24
+ When { service_ex.create thing }
25
+ Given(:result) { service_ex.read(123) }
26
+ Then { result == thing } # verify cache was updated
27
+ And { service_ex.read_calls == 2 }
28
+ end
29
+
30
+ context "when deleting it wipes the cache" do
31
+ Given do
32
+ service_ex.create thing
33
+ # Populate the cache
34
+ service_ex.read 123
35
+ end
36
+
37
+ When(:result) do
38
+ service_ex.delete 123
39
+ service_ex.read(123)
40
+ end
41
+
42
+ Then { service_ex.read_calls == 2 }
43
+ And { result.nil? }
44
+ end
45
+ end
46
+
47
+ context "when using procs to clearing the cache" do
48
+ Given(:service_ex) { ProcServiceExample.new }
49
+ it_should_behave_like "caching"
50
+ end
51
+
52
+ context "when using symbol keys to clear the cache" do
53
+ Given(:service_ex) { KeyServiceExample.new }
54
+ it_should_behave_like "caching"
55
+ end
56
+
57
+ context "when caching multiple methods" do
58
+ Given(:service_ex) { CacheTwoMethodsExample.new }
59
+ it_should_behave_like "caching"
60
+
61
+ shared_context "create thing and cache the reads" do
62
+ Given do
63
+ service_ex.create thing
64
+ service_ex.read 123
65
+ service_ex.read_hash(id: 123)
66
+ end
67
+ end
68
+
69
+ context "caching works" do
70
+ include_context "create thing and cache the reads"
71
+ When(:result) do
72
+ [service_ex.read(123), service_ex.read_hash(id: 123)]
73
+ end
74
+
75
+ Then { service_ex.read_hash_calls == 1 }
76
+ And { service_ex.read_calls == 1 }
77
+ end
78
+
79
+ context "cache clearing works" do
80
+ include_context "create thing and cache the reads"
81
+ When(:result) do
82
+ service_ex.delete 123
83
+ [service_ex.read(123), service_ex.read_hash(id: 123)]
84
+ end
85
+
86
+ Then { service_ex.read_hash_calls == 2 }
87
+ And { service_ex.read_calls == 2 }
88
+ And { result.all?(&:nil?) }
89
+ end
90
+ end
91
+
92
+ context "when clearing multiple keys" do
93
+ Given(:service_ex) { MultiKeyClearExample.new }
94
+ it_should_behave_like "caching"
95
+
96
+ context "ensure both keys are cleared" do
97
+ Given do
98
+ service_ex.read thing.id
99
+ service_ex.read thing.name
100
+ end
101
+
102
+ When { service_ex.create thing }
103
+ Given(:result) do
104
+ [service_ex.read(thing.id), service_ex.read(thing.name)]
105
+ end
106
+
107
+ Then { result.all? { |result| result == thing } }
108
+ And { service_ex.read_calls == 4 }
109
+ end
110
+ end
111
+
112
+ context "when the cache methods raise" do
113
+ Given(:service_ex) { BrokenExample.new }
114
+ When(:result) { service_ex.create thing }
115
+ Then { service_ex.create_calls == 1 }
116
+ end
117
+ end
@@ -0,0 +1,26 @@
1
+ require 'rspec/given'
2
+
3
+ require 'cache_shoe'
4
+ require 'active_support/cache'
5
+
6
+ # This file was generated by the `rspec --init` command. Conventionally, all
7
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
8
+ # Require this file using `require "spec_helper"` to ensure that it is only
9
+ # loaded once.
10
+ #
11
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
12
+ Dir[File.join(
13
+ File.expand_path("../../spec/support/**/*.rb", __FILE__)
14
+ )].sort.each { |f| require f }
15
+
16
+ RSpec.configure do |config|
17
+ config.treat_symbols_as_metadata_keys_with_true_values = true
18
+ config.run_all_when_everything_filtered = true
19
+ config.filter_run :focus
20
+
21
+ # Run specs in random order to surface order dependencies. If you find an
22
+ # order dependency and want to debug it, you can fix the order by providing
23
+ # the seed, which is printed after each run.
24
+ # --seed 1234
25
+ config.order = 'random'
26
+ end
@@ -0,0 +1,52 @@
1
+ RSpec.configure do |config|
2
+ CACHE_ACTIONS = {}
3
+
4
+ CacheShoe.config.on_cache = lambda do |cache_key, cache_hit|
5
+ CACHE_ACTIONS[cache_key] ||= []
6
+ CACHE_ACTIONS[cache_key] << { read: cache_hit }
7
+ end
8
+
9
+ CacheShoe.config.on_cache_clear = lambda do |cache_key, trigger_method|
10
+ CACHE_ACTIONS[cache_key] ||= []
11
+ CACHE_ACTIONS[cache_key] << { trigger_method => :clear }
12
+ end
13
+
14
+ config.before :all do
15
+ CacheShoe.config.cache = ActiveSupport::Cache::MemoryStore.new
16
+ end
17
+
18
+ config.before :each do
19
+ CacheShoe.config.cache.clear
20
+ CACHE_ACTIONS.clear
21
+ end
22
+
23
+ config.after :each do
24
+ if example.exception
25
+ p "-" * 30
26
+ p "Cache Actions"
27
+ pp CACHE_ACTIONS
28
+ p "-" * 30
29
+ end
30
+ end
31
+ end
32
+
33
+ RSpec::Matchers.define :have_cached do |*expected_cache_pattern|
34
+ class << self
35
+ attr_accessor :cache_key, :actual_cache_pattern
36
+ end
37
+
38
+ match do |service_instance, method, *args|
39
+ self.cache_key = CacheShoe.cache_key(
40
+ service_instance.class.name, method, args)
41
+ self.actual_cache_pattern =
42
+ (CACHE_ACTIONS[cache_key] || [{}]).flatten
43
+
44
+ actual_cache_pattern == expected_cache_pattern
45
+ end
46
+
47
+ failure_message_for_should do |_service_instance, _method, *_args|
48
+ "Expected cache pattern: #{expected_cache_pattern}, but got: " \
49
+ "#{actual_cache_pattern} for " \
50
+ "cache_key #{cache_key}"
51
+ end
52
+ end
@@ -0,0 +1,84 @@
1
+ class Thing < Struct.new(:id, :name)
2
+ end
3
+
4
+ class BaseServiceExample
5
+ attr_accessor :create_calls, :read_calls,
6
+ :delete_calls, :storage
7
+
8
+ def initialize
9
+ self.create_calls = self.read_calls = self.delete_calls = 0
10
+ self.storage = {}
11
+ end
12
+
13
+ def create(thing)
14
+ @create_calls += 1
15
+ @storage[thing.id] = thing
16
+ thing
17
+ end
18
+
19
+ def read(id)
20
+ @read_calls += 1
21
+ @storage[id]
22
+ end
23
+
24
+ def delete(id)
25
+ @delete_calls += 1
26
+ @storage.delete(id)
27
+ end
28
+ end
29
+
30
+ class ProcServiceExample < BaseServiceExample
31
+ include CacheShoe
32
+ cache_method :read, clear_on: {
33
+ create: -> (thing) { thing.id }, delete: -> (id) { id }
34
+ }
35
+ end
36
+
37
+ class CacheTwoMethodsExample < BaseServiceExample
38
+ attr_accessor :read_hash_calls
39
+ include CacheShoe
40
+ cache_method :read, clear_on: {
41
+ create: -> (thing) { thing.id }, delete: -> (id) { id }
42
+ }
43
+
44
+ cache_method :read_hash, clear_on: {
45
+ create: -> (thing) { { id: thing.id } }, delete: -> (id) { { id: id } }
46
+ }
47
+
48
+ def initialize
49
+ self.read_hash_calls = 0
50
+ super
51
+ end
52
+
53
+ def read_hash(inputs)
54
+ @read_hash_calls += 1
55
+ @storage[inputs[:id]]
56
+ end
57
+ end
58
+
59
+ class KeyServiceExample < BaseServiceExample
60
+ include CacheShoe
61
+ cache_method :read, clear_on: { create: :id, delete: PASS_THROUGH }
62
+ end
63
+
64
+ class MultiKeyClearExample < BaseServiceExample
65
+ include CacheShoe
66
+ cache_method :read, clear_on: {
67
+ create: [:id, :name],
68
+ delete: PASS_THROUGH
69
+ }
70
+
71
+ def create(thing)
72
+ @create_calls += 1
73
+ @storage[thing.id] = thing
74
+ @storage[thing.name] = thing
75
+ thing
76
+ end
77
+ end
78
+
79
+ class BrokenExample < BaseServiceExample
80
+ include CacheShoe
81
+ cache_method :read, clear_on: {
82
+ create: -> (_thing) { fail 'I am broken!' }
83
+ }
84
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cache_shoe
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jeff Deville
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.14.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.14.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-given
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Easily add caching and invalidation to RESTful clients
84
+ email:
85
+ - jeffdeville@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - cache_shoe.gemspec
98
+ - lib/cache_shoe.rb
99
+ - spec/cache_shoe_spec.rb
100
+ - spec/spec_helper.rb
101
+ - spec/support/cacheify.rb
102
+ - spec/support/demo_classes.rb
103
+ homepage: https://github.com/promptworks/cache_shoe
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.4.3
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Easily add caching and invalidation to RESTful clients
127
+ test_files:
128
+ - spec/cache_shoe_spec.rb
129
+ - spec/spec_helper.rb
130
+ - spec/support/cacheify.rb
131
+ - spec/support/demo_classes.rb