cashier 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +33 -0
- data/LICENSE.txt +20 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/cashier.gemspec +70 -0
- data/lib/cashier.rb +77 -0
- data/lib/cashier/controller_helper.rb +38 -0
- data/readme.md +105 -0
- data/spec/cashier_spec.rb +7 -0
- data/spec/spec_helper.rb +12 -0
- metadata +160 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2@cashier
|
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
gem 'redis'
|
7
|
+
gem 'redis-namespace'
|
8
|
+
|
9
|
+
# Add dependencies to develop your gem here.
|
10
|
+
# Include everything needed to run rake, tests, features, etc.
|
11
|
+
group :development do
|
12
|
+
gem "rspec", "~> 2.3.0"
|
13
|
+
gem "bundler", "~> 1.0.0"
|
14
|
+
gem "jeweler", "~> 1.5.2"
|
15
|
+
gem "rcov", ">= 0"
|
16
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.5.2)
|
7
|
+
bundler (~> 1.0.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
rake (0.8.7)
|
11
|
+
rcov (0.9.9)
|
12
|
+
redis (2.1.1)
|
13
|
+
redis-namespace (0.10.0)
|
14
|
+
redis (< 3.0.0)
|
15
|
+
rspec (2.3.0)
|
16
|
+
rspec-core (~> 2.3.0)
|
17
|
+
rspec-expectations (~> 2.3.0)
|
18
|
+
rspec-mocks (~> 2.3.0)
|
19
|
+
rspec-core (2.3.1)
|
20
|
+
rspec-expectations (2.3.0)
|
21
|
+
diff-lcs (~> 1.1.2)
|
22
|
+
rspec-mocks (2.3.0)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
ruby
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
bundler (~> 1.0.0)
|
29
|
+
jeweler (~> 1.5.2)
|
30
|
+
rcov
|
31
|
+
redis
|
32
|
+
redis-namespace
|
33
|
+
rspec (~> 2.3.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Adam Hawkins
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "cashier"
|
16
|
+
gem.homepage = "http://github.com/Adman65/cashier"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{Tag based caching for Rails}
|
19
|
+
gem.description = %Q{Associate different cached content with a tag, then expire by tag instead of key}
|
20
|
+
gem.email = "Adman1965@gmail.com"
|
21
|
+
gem.authors = ["Adam Hawkins"]
|
22
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
23
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
24
|
+
# gem.add_runtime_dependency 'redis'
|
25
|
+
# gem.add_runtime_dependency 'redis-namespace'
|
26
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
require 'rspec/core'
|
31
|
+
require 'rspec/core/rake_task'
|
32
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
33
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
34
|
+
end
|
35
|
+
|
36
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
37
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
38
|
+
spec.rcov = true
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :spec
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "cashier #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/cashier.gemspec
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{cashier}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Adam Hawkins"]
|
12
|
+
s.date = %q{2011-01-05}
|
13
|
+
s.description = %q{Associate different cached content with a tag, then expire by tag instead of key}
|
14
|
+
s.email = %q{Adman1965@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".rvmrc",
|
20
|
+
"Gemfile",
|
21
|
+
"Gemfile.lock",
|
22
|
+
"LICENSE.txt",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"cashier.gemspec",
|
26
|
+
"lib/cashier.rb",
|
27
|
+
"lib/cashier/controller_helper.rb",
|
28
|
+
"readme.md",
|
29
|
+
"spec/cashier_spec.rb",
|
30
|
+
"spec/spec_helper.rb"
|
31
|
+
]
|
32
|
+
s.homepage = %q{http://github.com/Adman65/cashier}
|
33
|
+
s.licenses = ["MIT"]
|
34
|
+
s.require_paths = ["lib"]
|
35
|
+
s.rubygems_version = %q{1.3.7}
|
36
|
+
s.summary = %q{Tag based caching for Rails}
|
37
|
+
s.test_files = [
|
38
|
+
"spec/cashier_spec.rb",
|
39
|
+
"spec/spec_helper.rb"
|
40
|
+
]
|
41
|
+
|
42
|
+
if s.respond_to? :specification_version then
|
43
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<redis>, [">= 0"])
|
48
|
+
s.add_runtime_dependency(%q<redis-namespace>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
50
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
51
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
52
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<redis>, [">= 0"])
|
55
|
+
s.add_dependency(%q<redis-namespace>, [">= 0"])
|
56
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
57
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
58
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
59
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
60
|
+
end
|
61
|
+
else
|
62
|
+
s.add_dependency(%q<redis>, [">= 0"])
|
63
|
+
s.add_dependency(%q<redis-namespace>, [">= 0"])
|
64
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
65
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
66
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
67
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
data/lib/cashier.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# Cashier
|
2
|
+
|
3
|
+
module Cashier
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# used to track all the tags Cashier is storing
|
7
|
+
STORAGE_KEY = 'cashier-tags'
|
8
|
+
|
9
|
+
def perform_caching?
|
10
|
+
::ApplicationController.perform_caching
|
11
|
+
end
|
12
|
+
|
13
|
+
# shamefully taken straight from Resque.
|
14
|
+
# Thanks Defunkt :D
|
15
|
+
|
16
|
+
# Accepts:
|
17
|
+
# 1. A 'hostname:port' string
|
18
|
+
# 2. A 'hostname:port:db' string (to select the Redis db)
|
19
|
+
# 3. A 'hostname:port/namespace' string (to set the Redis namespace)
|
20
|
+
# 4. A redis URL string 'redis://host:port'
|
21
|
+
# 5. An instance of `Redis`, `Redis::Client`, `Redis::DistRedis`,
|
22
|
+
# or `Redis::Namespace`.
|
23
|
+
def redis=(server)
|
24
|
+
if server.respond_to? :split
|
25
|
+
if server =~ /redis\:\/\//
|
26
|
+
redis = Redis.connect(:url => server)
|
27
|
+
else
|
28
|
+
server, namespace = server.split('/', 2)
|
29
|
+
host, port, db = server.split(':')
|
30
|
+
redis = Redis.new(:host => host, :port => port,
|
31
|
+
:thread_safe => true, :db => db)
|
32
|
+
end
|
33
|
+
namespace ||= :cashier
|
34
|
+
|
35
|
+
@redis = Redis::Namespace.new(namespace, :redis => redis)
|
36
|
+
elsif server.respond_to? :namespace=
|
37
|
+
@redis = server
|
38
|
+
else
|
39
|
+
@redis = Redis::Namespace.new(:cashier, :redis => server)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the current Redis connection. If none has been created, will
|
44
|
+
# create a new one.
|
45
|
+
def redis
|
46
|
+
return @redis if @redis
|
47
|
+
self.redis = 'localhost:6379'
|
48
|
+
self.redis
|
49
|
+
end
|
50
|
+
|
51
|
+
def expire(*tags)
|
52
|
+
return unless perform_caching?
|
53
|
+
|
54
|
+
tags.each do |tag|
|
55
|
+
# check to see if the tag exsists
|
56
|
+
# some redis versions return nil or []
|
57
|
+
members = redis.smembers(tags)
|
58
|
+
if members.is_a?(Array)
|
59
|
+
members.each do |cache_key|
|
60
|
+
Rails.cache.delete(cache_key)
|
61
|
+
end
|
62
|
+
redis.del(tag)
|
63
|
+
redis.srem(STORAGE_KEY, tag)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def tags
|
69
|
+
redis.smembers STORAGE_KEY
|
70
|
+
end
|
71
|
+
|
72
|
+
def wipe
|
73
|
+
expire(*tags)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
require 'cashier/controller_helper'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Hooks into ApplicationController's write_fragment method.
|
2
|
+
# write_fragment is used for action and fragment caching.
|
3
|
+
# Create an alias method chain to call our customer method
|
4
|
+
# which stores the associated key with the tag in a
|
5
|
+
# Redis Set. Then we can expire all those keys from anywhere
|
6
|
+
# in the code using Rails.cache.delete
|
7
|
+
#
|
8
|
+
# I use alias_method_chain instead of calling 'super'
|
9
|
+
# because there is a very rare case where someone
|
10
|
+
# may have redfined 'write_fragment' in their own
|
11
|
+
# controllers. Using an alias method chain
|
12
|
+
# keeps those methods intact.
|
13
|
+
|
14
|
+
module Cashier
|
15
|
+
module ControllerHelper
|
16
|
+
def self.included(klass)
|
17
|
+
klass.class_eval do
|
18
|
+
def write_fragment_with_tagged_key(key, content, options = nil)
|
19
|
+
if options && options[:tag] && Cashier.perform_caching?
|
20
|
+
passed_tags = case options[:tag].class.to_s
|
21
|
+
when 'Proc', 'Lambda'
|
22
|
+
options[:tag].call(self)
|
23
|
+
else
|
24
|
+
options[:tag]
|
25
|
+
end
|
26
|
+
tags = passed_tags.is_a?(Array) ? passed_tags : [passed_tags]
|
27
|
+
tags.each do |tag|
|
28
|
+
Cashier.redis.sadd tag, fragment_cache_key(key)
|
29
|
+
Cashier.redis.sadd Cashier::STORAGE_KEY, tag
|
30
|
+
end
|
31
|
+
end
|
32
|
+
write_fragment_without_tagged_key(key, content, options)
|
33
|
+
end
|
34
|
+
alias_method_chain :write_fragment, :tagged_key
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/readme.md
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# Cashier: Tag Based Caching for Rails
|
2
|
+
|
3
|
+
Manage your cache keys with tags, forget about keys!
|
4
|
+
|
5
|
+
## What Is It?
|
6
|
+
|
7
|
+
# in your view
|
8
|
+
cache @some_record, :tag => 'some-component'
|
9
|
+
|
10
|
+
# in another view
|
11
|
+
cache @some_releated_record, :tag => 'some-component'
|
12
|
+
|
13
|
+
# can have multiple tags
|
14
|
+
cache @something, :tag => ['dashboard', 'settings'] # can expire from either tag
|
15
|
+
|
16
|
+
# in your sweeper
|
17
|
+
Cashier.expire 'some-component' # don't worry about keys! Much easier to sweep with confidence
|
18
|
+
|
19
|
+
# in your controller
|
20
|
+
caches_action :tag => 'complicated-action', :cache_path => proc { |c|
|
21
|
+
# huge complicated mess of parameters
|
22
|
+
c.params
|
23
|
+
}
|
24
|
+
|
25
|
+
|
26
|
+
# need to access the controller?
|
27
|
+
caches_action :tag => proc {|c|
|
28
|
+
# c is the controller
|
29
|
+
"users/#{c.current_user.id}/dashboard"
|
30
|
+
}
|
31
|
+
|
32
|
+
# in your sweeper, in your observers, in your resque jobs...wherever
|
33
|
+
Cashier.expire 'complicated-action'
|
34
|
+
Cashier.expire 'tag1', 'tag2', 'tag3', 'tag4'
|
35
|
+
|
36
|
+
# what's cached
|
37
|
+
Cashier.tags
|
38
|
+
|
39
|
+
# sweep all stored keys
|
40
|
+
Cashier.wipe
|
41
|
+
|
42
|
+
## How it Came About
|
43
|
+
|
44
|
+
I work on an application that involves all sorts of caching. I try to use action caching whenever I possible.
|
45
|
+
I had an index action that had maybe ~20 different combination of filters and sorting. If you want to use
|
46
|
+
action caching you have to create a **unique** key for every combination. This created a nice 6 nested loop
|
47
|
+
to expire the cache. Once you had pagination, then you have even more combinations of possible cache keys.
|
48
|
+
I needed a better solution. I wanted to expire things logically as a viewed them on the page. IE, if
|
49
|
+
a record was added, I wanted to say "expire that page". Problem was that page contained ~1000 different keys.
|
50
|
+
So I needed something to store the keys for me and associate them with tags. That's exactly what cashier does.
|
51
|
+
Cache associate individual cache keys with a tag, then expire them all at once. This took my 7 layer loop
|
52
|
+
down two one line of code. It's also made managing the cache throught my application much easier.
|
53
|
+
|
54
|
+
## Why Tag Based Caching is Useful
|
55
|
+
|
56
|
+
1. You don't worry about keys. How many times have you created a complicated key for a fragment or action
|
57
|
+
then messed up when you tried to expire the cache
|
58
|
+
2. Associate your cached content into groups of related content. If you have records that are closely associated
|
59
|
+
or displayed together, then you can tag them and expire them at once.
|
60
|
+
3. **Expire cached content from anywhere.** If you've done any serious development, you know that Rails caching
|
61
|
+
does not work (easily) outside the scope of an HTTP request. If you have background jobs that manipulate data
|
62
|
+
or potentially invalidate cached data, you know how much of a pain it is to say `expire_fragment` in some random code.
|
63
|
+
4. Don't do anything differently! All you have to do is pass `:tag => 'something'` into `cache` (in the view) or `caches_action`
|
64
|
+
in the controller.
|
65
|
+
|
66
|
+
## How it Works
|
67
|
+
|
68
|
+
Cashier hooks into Rails' `expire_fragment` method using `alias_method_chain` to run some code that captures the key
|
69
|
+
and tag then stores that as a set in redis. Then uses the set members to loop over keys to deleting using `Rails.cache.delete`
|
70
|
+
|
71
|
+
## Configuration
|
72
|
+
|
73
|
+
Cashier needs Redis to function correctly. Create a yaml file. You may call it `config/cashier.yml`
|
74
|
+
|
75
|
+
development: localhost:6379
|
76
|
+
test: localhost:6379/test
|
77
|
+
|
78
|
+
Then write a simple initializer to configure Cahiser. Drop this file in in `config/initializers/cashier.rb`
|
79
|
+
|
80
|
+
resque_config = YAML.load_file(Raisl.root.join 'config', 'cashiser.yml')
|
81
|
+
Cashier.redis = resque_config[Rails.env]
|
82
|
+
|
83
|
+
Now in your `application_controller.rb` file just include the module
|
84
|
+
|
85
|
+
class ApplicationController < ActionController::Base
|
86
|
+
include Cashier::ControllerHelper
|
87
|
+
end
|
88
|
+
|
89
|
+
Now you're good to go!
|
90
|
+
|
91
|
+
## Contributing to Cashier
|
92
|
+
|
93
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
94
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
95
|
+
* Fork the project
|
96
|
+
* Start a feature/bugfix branch
|
97
|
+
* Commit and push until you are happy with your contribution
|
98
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
99
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
100
|
+
|
101
|
+
## Copyright
|
102
|
+
|
103
|
+
Copyright (c) 2010 Adam Hawkins. See LICENSE.txt for
|
104
|
+
further details.
|
105
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'cashier'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cashier
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Adam Hawkins
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-05 00:00:00 -08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: redis
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
prerelease: false
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: redis-namespace
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
42
|
+
version: "0"
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 2
|
55
|
+
- 3
|
56
|
+
- 0
|
57
|
+
version: 2.3.0
|
58
|
+
type: :development
|
59
|
+
prerelease: false
|
60
|
+
version_requirements: *id003
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: bundler
|
63
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 1
|
70
|
+
- 0
|
71
|
+
- 0
|
72
|
+
version: 1.0.0
|
73
|
+
type: :development
|
74
|
+
prerelease: false
|
75
|
+
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: jeweler
|
78
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
segments:
|
84
|
+
- 1
|
85
|
+
- 5
|
86
|
+
- 2
|
87
|
+
version: 1.5.2
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *id005
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: rcov
|
93
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
version: "0"
|
101
|
+
type: :development
|
102
|
+
prerelease: false
|
103
|
+
version_requirements: *id006
|
104
|
+
description: Associate different cached content with a tag, then expire by tag instead of key
|
105
|
+
email: Adman1965@gmail.com
|
106
|
+
executables: []
|
107
|
+
|
108
|
+
extensions: []
|
109
|
+
|
110
|
+
extra_rdoc_files:
|
111
|
+
- LICENSE.txt
|
112
|
+
files:
|
113
|
+
- .rvmrc
|
114
|
+
- Gemfile
|
115
|
+
- Gemfile.lock
|
116
|
+
- LICENSE.txt
|
117
|
+
- Rakefile
|
118
|
+
- VERSION
|
119
|
+
- cashier.gemspec
|
120
|
+
- lib/cashier.rb
|
121
|
+
- lib/cashier/controller_helper.rb
|
122
|
+
- readme.md
|
123
|
+
- spec/cashier_spec.rb
|
124
|
+
- spec/spec_helper.rb
|
125
|
+
has_rdoc: true
|
126
|
+
homepage: http://github.com/Adman65/cashier
|
127
|
+
licenses:
|
128
|
+
- MIT
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
hash: -1411570206960376896
|
140
|
+
segments:
|
141
|
+
- 0
|
142
|
+
version: "0"
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
segments:
|
149
|
+
- 0
|
150
|
+
version: "0"
|
151
|
+
requirements: []
|
152
|
+
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 1.3.7
|
155
|
+
signing_key:
|
156
|
+
specification_version: 3
|
157
|
+
summary: Tag based caching for Rails
|
158
|
+
test_files:
|
159
|
+
- spec/cashier_spec.rb
|
160
|
+
- spec/spec_helper.rb
|