hakon-lacquer 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.bundle/config +2 -0
- data/.document +5 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +32 -0
- data/LICENSE +20 -0
- data/README.rdoc +83 -0
- data/Rakefile +36 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lacquer.gemspec +87 -0
- data/lib/generators/lacquer/USAGE +8 -0
- data/lib/generators/lacquer/install_generator.rb +15 -0
- data/lib/generators/lacquer/templates/initializer.rb +21 -0
- data/lib/generators/lacquer/templates/varnish.sample.vcl +102 -0
- data/lib/lacquer.rb +30 -0
- data/lib/lacquer/cache_utils.rb +52 -0
- data/lib/lacquer/configuration.rb +39 -0
- data/lib/lacquer/delayed_job_job.rb +7 -0
- data/lib/lacquer/resque_job.rb +9 -0
- data/lib/lacquer/varnish.rb +78 -0
- data/rails/init.rb +1 -0
- data/spec/lacquer/cache_utils_spec.rb +81 -0
- data/spec/lacquer/delayed_job_job_spec.rb +13 -0
- data/spec/lacquer/resque_job_spec.rb +13 -0
- data/spec/lacquer/varnish_spec.rb +176 -0
- data/spec/spec_helper.rb +33 -0
- metadata +186 -0
data/.bundle/config
ADDED
data/.document
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.0.3)
|
5
|
+
diff-lcs (1.1.2)
|
6
|
+
git (1.2.5)
|
7
|
+
i18n (0.5.0)
|
8
|
+
jeweler (1.5.2)
|
9
|
+
bundler (~> 1.0.0)
|
10
|
+
git (>= 1.2.5)
|
11
|
+
rake
|
12
|
+
rake (0.8.7)
|
13
|
+
rspec (2.3.0)
|
14
|
+
rspec-core (~> 2.3.0)
|
15
|
+
rspec-expectations (~> 2.3.0)
|
16
|
+
rspec-mocks (~> 2.3.0)
|
17
|
+
rspec-core (2.3.1)
|
18
|
+
rspec-expectations (2.3.0)
|
19
|
+
diff-lcs (~> 1.1.2)
|
20
|
+
rspec-mocks (2.3.0)
|
21
|
+
yard (0.6.4)
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
activesupport (~> 3.0)
|
28
|
+
i18n (~> 0.4)
|
29
|
+
jeweler
|
30
|
+
rake
|
31
|
+
rspec (~> 2.0)
|
32
|
+
yard
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Russ Smith
|
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/README.rdoc
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
= lacquer
|
2
|
+
|
3
|
+
Rails drop in for Varnish support.
|
4
|
+
|
5
|
+
== Install
|
6
|
+
Basic installation
|
7
|
+
|
8
|
+
sudo gem install lacquer
|
9
|
+
|
10
|
+
config/initializers/lacquer.rb
|
11
|
+
|
12
|
+
Lacquer.configure do |config|
|
13
|
+
# Globally enable/disable cache
|
14
|
+
config.enable_cache = true
|
15
|
+
|
16
|
+
# Unless overridden in a controller or action, the default will be used
|
17
|
+
config.default_ttl = 1.week
|
18
|
+
|
19
|
+
# Can be :none, :delayed_job, :resque
|
20
|
+
config.job_backend = :none
|
21
|
+
|
22
|
+
# Array of Varnish servers to manage
|
23
|
+
config.varnish_servers << {
|
24
|
+
:host => "0.0.0.0", :port => 6082 # if you have authentication enabled, add :secret => "your secret"
|
25
|
+
}
|
26
|
+
|
27
|
+
# Number of retries
|
28
|
+
config.retries = 5
|
29
|
+
|
30
|
+
# config handler (optional, if you use Hoptoad or another error tracking service)
|
31
|
+
config.command_error_handler = lambda { |s| HoptoadNotifier.notify(s) }
|
32
|
+
end
|
33
|
+
|
34
|
+
app/controllers/application_controller.rb
|
35
|
+
|
36
|
+
class ApplicationController < ActionController::Base
|
37
|
+
include Lacquer::CacheUtils
|
38
|
+
end
|
39
|
+
|
40
|
+
== Usage
|
41
|
+
|
42
|
+
To set a custom ttl for a controller:
|
43
|
+
|
44
|
+
before_filter { |controller| controller.set_cache_ttl(15.minutes) }
|
45
|
+
|
46
|
+
Clearing the cache:
|
47
|
+
|
48
|
+
class Posts < ApplicationController
|
49
|
+
after_filter :clear_cache, :only => [ :create, :update, :destroy ]
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def clear_cache
|
54
|
+
clear_cache_for(
|
55
|
+
root_path,
|
56
|
+
posts_path,
|
57
|
+
post_path(@post))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
== Gotchas
|
62
|
+
|
63
|
+
The default TTL for most actions is set to 0, since for most cases you'll probably want to be fairly explicit about what pages do get cached by varnish. The default cache header is typically:
|
64
|
+
|
65
|
+
Cache-Control: max-age=0, no-cache, private
|
66
|
+
|
67
|
+
This is good for normal controller actions, since you won't want to cache them. If TTL for an action is set to 0, it won't mess with the default header.
|
68
|
+
|
69
|
+
The key gotcha here is that cached pages strip cookies, so if your application relies on sessions and uses authenticity tokens, the user will need a session cookie set before form actions will work. Setting default TTL to 0 here will make sure these session cookies won't break.
|
70
|
+
|
71
|
+
As a result, all you have to do to set a cacheable action is the before filter above.
|
72
|
+
|
73
|
+
== Note on Patches/Pull Requests
|
74
|
+
|
75
|
+
* Fork the project.
|
76
|
+
* Make your feature addition or bug fix.
|
77
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
78
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
79
|
+
* Send me a pull request. Bonus points for topic branches.
|
80
|
+
|
81
|
+
== Copyright
|
82
|
+
|
83
|
+
Copyright (c) 2010 Russ Smith. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
|
4
|
+
require "rake"
|
5
|
+
require "yard"
|
6
|
+
require "rspec"
|
7
|
+
require "rspec/core/rake_task"
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'jeweler'
|
11
|
+
Jeweler::Tasks.new do |gem|
|
12
|
+
gem.name = "hakon-lacquer"
|
13
|
+
gem.summary = %Q{Rails drop in for Varnish support.}
|
14
|
+
gem.description = %Q{Rails drop in for Varnish support.}
|
15
|
+
gem.email = "russ@bashme.org"
|
16
|
+
gem.homepage = "http://github.com/russ/lacquer"
|
17
|
+
gem.authors = ["Russ Smith (russ@bashme.org)", "Ryan Johns", "Garry Tan (garry@posterous.com), Gabe da Silveira (gabe@websaviour.com)", "Håkon Lerring"]
|
18
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
19
|
+
end
|
20
|
+
Jeweler::GemcutterTasks.new
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Run all examples"
|
26
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
27
|
+
t.rspec_path = "rspec"
|
28
|
+
t.rspec_opts = %w[--color]
|
29
|
+
end
|
30
|
+
|
31
|
+
YARD::Rake::YardocTask.new do |t|
|
32
|
+
t.files = [ "lib/**/*.rb" ]
|
33
|
+
t.options = [ "--no-private" ]
|
34
|
+
end
|
35
|
+
|
36
|
+
task :default => [ :spec ]
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.4.1
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'lib', 'lacquer')
|
data/lacquer.gemspec
ADDED
@@ -0,0 +1,87 @@
|
|
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{lacquer}
|
8
|
+
s.version = "0.4.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Russ Smith (russ@bashme.org)", "Ryan Johns", "Garry Tan (garry@posterous.com), Gabe da Silveira (gabe@websaviour.com)", "H\303\245kon Lerring"]
|
12
|
+
s.date = %q{2010-12-28}
|
13
|
+
s.description = %q{Rails drop in for Varnish support.}
|
14
|
+
s.email = %q{russ@bashme.org}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".bundle/config",
|
21
|
+
".document",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"init.rb",
|
29
|
+
"lacquer.gemspec",
|
30
|
+
"lib/generators/lacquer/USAGE",
|
31
|
+
"lib/generators/lacquer/install_generator.rb",
|
32
|
+
"lib/generators/lacquer/templates/initializer.rb",
|
33
|
+
"lib/generators/lacquer/templates/varnish.sample.vcl",
|
34
|
+
"lib/lacquer.rb",
|
35
|
+
"lib/lacquer/cache_utils.rb",
|
36
|
+
"lib/lacquer/configuration.rb",
|
37
|
+
"lib/lacquer/delayed_job_job.rb",
|
38
|
+
"lib/lacquer/resque_job.rb",
|
39
|
+
"lib/lacquer/varnish.rb",
|
40
|
+
"rails/init.rb",
|
41
|
+
"spec/lacquer/cache_utils_spec.rb",
|
42
|
+
"spec/lacquer/delayed_job_job_spec.rb",
|
43
|
+
"spec/lacquer/resque_job_spec.rb",
|
44
|
+
"spec/lacquer/varnish_spec.rb",
|
45
|
+
"spec/spec_helper.rb"
|
46
|
+
]
|
47
|
+
s.homepage = %q{http://github.com/russ/lacquer}
|
48
|
+
s.require_paths = ["lib"]
|
49
|
+
s.rubygems_version = %q{1.3.7}
|
50
|
+
s.summary = %q{Rails drop in for Varnish support.}
|
51
|
+
s.test_files = [
|
52
|
+
"spec/lacquer/cache_utils_spec.rb",
|
53
|
+
"spec/lacquer/delayed_job_job_spec.rb",
|
54
|
+
"spec/lacquer/resque_job_spec.rb",
|
55
|
+
"spec/lacquer/varnish_spec.rb",
|
56
|
+
"spec/spec_helper.rb"
|
57
|
+
]
|
58
|
+
|
59
|
+
if s.respond_to? :specification_version then
|
60
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
61
|
+
s.specification_version = 3
|
62
|
+
|
63
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
64
|
+
s.add_runtime_dependency(%q<activesupport>, ["~> 3.0"])
|
65
|
+
s.add_runtime_dependency(%q<i18n>, ["~> 0.4"])
|
66
|
+
s.add_runtime_dependency(%q<jeweler>, [">= 0"])
|
67
|
+
s.add_runtime_dependency(%q<rake>, [">= 0"])
|
68
|
+
s.add_runtime_dependency(%q<yard>, [">= 0"])
|
69
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.0"])
|
70
|
+
else
|
71
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0"])
|
72
|
+
s.add_dependency(%q<i18n>, ["~> 0.4"])
|
73
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
74
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
75
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
76
|
+
s.add_dependency(%q<rspec>, ["~> 2.0"])
|
77
|
+
end
|
78
|
+
else
|
79
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0"])
|
80
|
+
s.add_dependency(%q<i18n>, ["~> 0.4"])
|
81
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
82
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
83
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
84
|
+
s.add_dependency(%q<rspec>, ["~> 2.0"])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Lacquer
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path(File.join(File.dirname(__FILE__), "templates"))
|
5
|
+
|
6
|
+
def copy_initializer
|
7
|
+
copy_file("initializer.rb", "config/initializers/lacquer.rb")
|
8
|
+
end
|
9
|
+
|
10
|
+
def copy_vcl
|
11
|
+
copy_file("varnish.sample.vcl", "config/varnish.sample.vcl")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Lacquer.configure do |config|
|
2
|
+
# Globally enable/disable cache
|
3
|
+
config.enable_cache = true
|
4
|
+
|
5
|
+
# Unless overridden in a controller or action, the default will be used
|
6
|
+
config.default_ttl = 1.week
|
7
|
+
|
8
|
+
# Can be :none, :delayed_job, :resque
|
9
|
+
config.job_backend = :none
|
10
|
+
|
11
|
+
# Array of Varnish servers to manage
|
12
|
+
config.varnish_servers << {
|
13
|
+
:host => "0.0.0.0", :port => 6082 # if you have authentication enabled, add :secret => "your secret"
|
14
|
+
}
|
15
|
+
|
16
|
+
# Number of retries
|
17
|
+
config.retries = 5
|
18
|
+
|
19
|
+
# Config handler (optional, if you use Hoptoad or another error tracking service)
|
20
|
+
config.command_error_handler = lambda { |s| HoptoadNotifier.notify(s) }
|
21
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
backend default {
|
2
|
+
.host = "0.0.0.0";
|
3
|
+
.port = "10000";
|
4
|
+
}
|
5
|
+
|
6
|
+
# Handling of requests that are received from clients.
|
7
|
+
# First decide whether or not to lookup data in the cache.
|
8
|
+
sub vcl_recv {
|
9
|
+
# Pipe requests that are non-RFC2616 or CONNECT which is weird.
|
10
|
+
if (req.request != "GET" &&
|
11
|
+
req.request != "HEAD" &&
|
12
|
+
req.request != "PUT" &&
|
13
|
+
req.request != "POST" &&
|
14
|
+
req.request != "TRACE" &&
|
15
|
+
req.request != "OPTIONS" &&
|
16
|
+
req.request != "DELETE") {
|
17
|
+
pipe;
|
18
|
+
}
|
19
|
+
|
20
|
+
# Pass requests that are not GET or HEAD
|
21
|
+
if (req.request != "GET" && req.request != "HEAD") {
|
22
|
+
pass;
|
23
|
+
}
|
24
|
+
|
25
|
+
# Pass requests that we know we aren't caching
|
26
|
+
if (req.url ~ "^/admin") {
|
27
|
+
pass;
|
28
|
+
}
|
29
|
+
|
30
|
+
# Handle compression correctly. Varnish treats headers literally, not
|
31
|
+
# semantically. So it is very well possible that there are cache misses
|
32
|
+
# because the headers sent by different browsers aren't the same.
|
33
|
+
# @see: http://varnish.projects.linpro.no/wiki/FAQ/Compression
|
34
|
+
if (req.http.Accept-Encoding) {
|
35
|
+
if (req.http.Accept-Encoding ~ "gzip") {
|
36
|
+
# if the browser supports it, we'll use gzip
|
37
|
+
set req.http.Accept-Encoding = "gzip";
|
38
|
+
} elsif (req.http.Accept-Encoding ~ "deflate") {
|
39
|
+
# next, try deflate if it is supported
|
40
|
+
set req.http.Accept-Encoding = "deflate";
|
41
|
+
} else {
|
42
|
+
# unknown algorithm. Probably junk, remove it
|
43
|
+
remove req.http.Accept-Encoding;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
# Clear cookie and authorization headers, set grace time, lookup in the cache
|
48
|
+
unset req.http.Cookie;
|
49
|
+
unset req.http.Authorization;
|
50
|
+
set req.grace = 30s;
|
51
|
+
lookup;
|
52
|
+
}
|
53
|
+
|
54
|
+
# Called when entering pipe mode
|
55
|
+
sub vcl_pipe {
|
56
|
+
# If we don't set the Connection: close header, any following
|
57
|
+
# requests from the client will also be piped through and
|
58
|
+
# left untouched by varnish. We don't want that.
|
59
|
+
set req.http.connection = "close";
|
60
|
+
pipe;
|
61
|
+
}
|
62
|
+
|
63
|
+
# Called when the requested object has been retrieved from the
|
64
|
+
# backend, or the request to the backend has failed
|
65
|
+
sub vcl_fetch {
|
66
|
+
# Do not cache the object if the backend application does not want us to.
|
67
|
+
if (obj.http.Cache-Control ~ "(no-cache|no-store|private|must-revalidate)") {
|
68
|
+
pass;
|
69
|
+
}
|
70
|
+
|
71
|
+
# Do not cache the object if the status is not in the 200s
|
72
|
+
if (obj.status >= 300) {
|
73
|
+
# Remove the Set-Cookie header
|
74
|
+
remove obj.http.Set-Cookie;
|
75
|
+
pass;
|
76
|
+
}
|
77
|
+
|
78
|
+
# Everything below here should be cached
|
79
|
+
|
80
|
+
# Remove the Set-Cookie header
|
81
|
+
remove obj.http.Set-Cookie;
|
82
|
+
|
83
|
+
# Set the grace time
|
84
|
+
set obj.grace = 30s;
|
85
|
+
|
86
|
+
# Deliver the object
|
87
|
+
deliver;
|
88
|
+
}
|
89
|
+
|
90
|
+
# Called before the response is sent back to the client
|
91
|
+
sub vcl_deliver {
|
92
|
+
# Force browsers and intermediary caches to always check back with us
|
93
|
+
set resp.http.Cache-Control = "private, max-age=0, must-revalidate";
|
94
|
+
set resp.http.Pragma = "no-cache";
|
95
|
+
|
96
|
+
# Add a header to indicate a cache HIT/MISS
|
97
|
+
if (obj.hits > 0) {
|
98
|
+
set resp.http.X-Cache = "HIT";
|
99
|
+
} else {
|
100
|
+
set resp.http.X-Cache = "MISS";
|
101
|
+
}
|
102
|
+
}
|
data/lib/lacquer.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "net/telnet"
|
3
|
+
|
4
|
+
require "openssl"
|
5
|
+
require "digest/sha2"
|
6
|
+
|
7
|
+
require "active_support/core_ext"
|
8
|
+
|
9
|
+
require "lacquer/configuration"
|
10
|
+
require "lacquer/cache_utils"
|
11
|
+
require "lacquer/varnish"
|
12
|
+
|
13
|
+
module Lacquer
|
14
|
+
class VarnishError < Exception; end # @private
|
15
|
+
class AuthenticationError < VarnishError; end # @private
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :configuration
|
19
|
+
|
20
|
+
# Call this method to modify defaults in your initailizers.
|
21
|
+
#
|
22
|
+
# Lacquer.configure do |config|
|
23
|
+
# config.varnish_servers << { :host => '0.0.0.0', :port => 6082, :timeout => 5 }
|
24
|
+
# end
|
25
|
+
def configure
|
26
|
+
self.configuration ||= Configuration.new
|
27
|
+
yield(configuration)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Lacquer
|
2
|
+
module CacheUtils
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
attr_reader :cache_ttl
|
6
|
+
|
7
|
+
if respond_to? :before_filter
|
8
|
+
before_filter :set_default_cache_ttl
|
9
|
+
after_filter :send_cache_control_headers
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Instance variable for the action ttl.
|
15
|
+
def set_cache_ttl(ttl)
|
16
|
+
@cache_ttl = ttl
|
17
|
+
end
|
18
|
+
|
19
|
+
# Called as a before filter to set default ttl
|
20
|
+
# for the entire application.
|
21
|
+
def set_default_cache_ttl
|
22
|
+
set_cache_ttl(Lacquer.configuration.default_ttl)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Sends url.purge command to varnish to clear cache.
|
26
|
+
#
|
27
|
+
# clear_cache_for(root_path, blog_posts_path, '/other/content/*')
|
28
|
+
def clear_cache_for(*paths)
|
29
|
+
paths.each do |path|
|
30
|
+
case Lacquer.configuration.job_backend
|
31
|
+
when :delayed_job
|
32
|
+
require 'lacquer/delayed_job_job'
|
33
|
+
Delayed::Job.enqueue(Lacquer::DelayedJobJob.new(path))
|
34
|
+
when :resque
|
35
|
+
require 'lacquer/resque_job'
|
36
|
+
Resque.enqueue(Lacquer::ResqueJob, path)
|
37
|
+
when :none
|
38
|
+
Varnish.new.purge(path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Sends cache control headers with page.
|
44
|
+
# These are the headers that varnish responds to
|
45
|
+
# to set cache properly.
|
46
|
+
def send_cache_control_headers
|
47
|
+
if Lacquer.configuration.enable_cache && @cache_ttl && @cache_ttl != 0
|
48
|
+
expires_in(@cache_ttl, :public => true)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Lacquer
|
2
|
+
class Configuration
|
3
|
+
OPTIONS = []
|
4
|
+
|
5
|
+
# Enable cache
|
6
|
+
attr_accessor :enable_cache
|
7
|
+
|
8
|
+
# Varnish servers
|
9
|
+
attr_accessor :varnish_servers
|
10
|
+
|
11
|
+
# Application default ttl
|
12
|
+
attr_accessor :default_ttl
|
13
|
+
|
14
|
+
# Number of retries before failing
|
15
|
+
attr_accessor :retries
|
16
|
+
|
17
|
+
# Job backend
|
18
|
+
attr_accessor :job_backend
|
19
|
+
|
20
|
+
# Error handler
|
21
|
+
attr_accessor :command_error_handler
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@enable_cache = true
|
25
|
+
@varnish_servers = []
|
26
|
+
@default_ttl = 0
|
27
|
+
@job_backend = :none
|
28
|
+
@retries = 5
|
29
|
+
@command_error_handler = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns a hash of all configurable options
|
33
|
+
def to_hash
|
34
|
+
OPTIONS.inject({}) do |hash, option|
|
35
|
+
hash.merge(option.to_sym => send(option))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Lacquer
|
2
|
+
class Varnish
|
3
|
+
def stats
|
4
|
+
send_command('stats').collect do |stats|
|
5
|
+
stats = stats.split("\n")
|
6
|
+
stats.shift
|
7
|
+
stats = stats.collect do |stat|
|
8
|
+
stat = stat.strip.match(/(\d+)\s+(.+)$/)
|
9
|
+
{ :key => stat[2], :value => stat[1] } if stat
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Sends the command 'url.purge *path*'
|
15
|
+
def purge(path)
|
16
|
+
send_command('url.purge ' << path).all? do |result|
|
17
|
+
result =~ /200/
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sends commands over telnet to varnish servers listed in the config.
|
22
|
+
def send_command(command)
|
23
|
+
Lacquer.configuration.varnish_servers.collect do |server|
|
24
|
+
retries = 0
|
25
|
+
response = nil
|
26
|
+
begin
|
27
|
+
retries += 1
|
28
|
+
connection = Net::Telnet.new(
|
29
|
+
'Host' => server[:host],
|
30
|
+
'Port' => server[:port],
|
31
|
+
'Timeout' => server[:timeout] || 5)
|
32
|
+
|
33
|
+
if(server[:secret])
|
34
|
+
connection.waitfor("Match" => /^107/) do |authentication_request|
|
35
|
+
matchdata = /^107 \d{2}\s*(.{32}).*$/m.match(authentication_request) # Might be a bit ugly regex, but it works great!
|
36
|
+
salt = matchdata[1]
|
37
|
+
if(salt.empty?)
|
38
|
+
raise VarnishError, "Bad authentication request"
|
39
|
+
end
|
40
|
+
|
41
|
+
digest = OpenSSL::Digest::Digest.new('sha256')
|
42
|
+
digest << salt
|
43
|
+
digest << "\n"
|
44
|
+
digest << server[:secret]
|
45
|
+
digest << "\n"
|
46
|
+
digest << salt
|
47
|
+
digest << "\n"
|
48
|
+
|
49
|
+
connection.cmd("String" => "auth #{digest.to_s}", "Match" => /\d{3}/) do |auth_response|
|
50
|
+
if(!(/^200/ =~ auth_response))
|
51
|
+
raise AuthenticationError, "Could not authenticate"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
connection.cmd('String' => command, 'Match' => /\n\n/) {|r| response = r.split("\n").first.strip}
|
58
|
+
connection.close
|
59
|
+
rescue Exception => e
|
60
|
+
if retries < Lacquer.configuration.retries
|
61
|
+
retry
|
62
|
+
else
|
63
|
+
if Lacquer.configuration.command_error_handler
|
64
|
+
Lacquer.configuration.command_error_handler.call({
|
65
|
+
:error_class => "Varnish Error, retried #{Lacquer.configuration.retries} times",
|
66
|
+
:error_message => "Error while trying to connect to #{server[:host]}:#{server[:port]}: #{e}",
|
67
|
+
:parameters => server,
|
68
|
+
:response => response })
|
69
|
+
else
|
70
|
+
raise VarnishError.new("Error while trying to connect to #{server[:host]}:#{server[:port]} #{e}")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
response
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'lacquer'
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
|
2
|
+
|
3
|
+
describe "Lacquer" do
|
4
|
+
before(:each) do
|
5
|
+
@controller = ControllerClass.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "talking to varnish" do
|
9
|
+
before(:each) do
|
10
|
+
@varnish_stub = mock('varnish')
|
11
|
+
Lacquer::Varnish.stub!(:new).and_return(@varnish_stub)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "when backend is :none" do
|
15
|
+
before(:each) do
|
16
|
+
Lacquer.configuration.job_backend = :none
|
17
|
+
end
|
18
|
+
|
19
|
+
it "sends commands to varnish instantly" do
|
20
|
+
@varnish_stub.should_receive(:purge).twice
|
21
|
+
@controller.clear_cache_for('/', '/blog/posts')
|
22
|
+
end
|
23
|
+
|
24
|
+
it "calls purge with the correct parameter" do
|
25
|
+
@varnish_stub.should_receive(:purge).with('/')
|
26
|
+
@controller.clear_cache_for('/')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "when backend is :delayed_job" do
|
31
|
+
it "sends commands to a delayed_job queue" do
|
32
|
+
Lacquer.configuration.job_backend = :delayed_job
|
33
|
+
|
34
|
+
Delayed::Job.should_receive(:enqueue).twice
|
35
|
+
@controller.clear_cache_for('/', '/blog/posts')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "when backend is :resque" do
|
40
|
+
it "sends commands to a resque queue" do
|
41
|
+
Lacquer.configuration.job_backend = :resque
|
42
|
+
|
43
|
+
Resque.should_receive(:enqueue).twice
|
44
|
+
@controller.clear_cache_for('/', '/blog/posts')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "when cache is enabled" do
|
50
|
+
describe "when no custom ttl is set" do
|
51
|
+
it "should send cache control headers based on default ttl" do
|
52
|
+
Lacquer.configuration.enable_cache = true
|
53
|
+
Lacquer.configuration.default_ttl = 1.week
|
54
|
+
|
55
|
+
@controller.set_default_cache_ttl
|
56
|
+
@controller.should_receive(:expires_in).with(1.week, :public => true)
|
57
|
+
@controller.send_cache_control_headers
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "when custom ttl is set" do
|
62
|
+
it "should send cache control headers based on custom set ttl" do
|
63
|
+
Lacquer.configuration.enable_cache = true
|
64
|
+
|
65
|
+
@controller.set_cache_ttl(10.week)
|
66
|
+
@controller.should_receive(:expires_in).with(10.week, :public => true)
|
67
|
+
@controller.send_cache_control_headers
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should allow purge by non-controller sweepers" do
|
73
|
+
@varnish_stub = mock('varnish')
|
74
|
+
Lacquer::Varnish.stub!(:new).and_return(@varnish_stub)
|
75
|
+
|
76
|
+
@sweeper = SweeperClass.new
|
77
|
+
|
78
|
+
@varnish_stub.should_receive(:purge)
|
79
|
+
@sweeper.clear_cache_for('/')
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
|
2
|
+
|
3
|
+
describe "DelayedJobJob" do
|
4
|
+
describe "perform" do
|
5
|
+
it "should purge the parameter" do
|
6
|
+
@varnish_mock = mock('varnish')
|
7
|
+
Lacquer::Varnish.stub!(:new).and_return(@varnish_mock)
|
8
|
+
|
9
|
+
@varnish_mock.should_receive(:purge).with('/').exactly(1).times
|
10
|
+
Lacquer::DelayedJobJob.new('/').perform
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
|
2
|
+
|
3
|
+
describe "ResqueJob" do
|
4
|
+
describe "perform" do
|
5
|
+
it "should purge the parameter" do
|
6
|
+
@varnish_mock = mock('varnish')
|
7
|
+
Lacquer::Varnish.stub!(:new).and_return(@varnish_mock)
|
8
|
+
|
9
|
+
@varnish_mock.should_receive(:purge).with('/').exactly(1).times
|
10
|
+
Lacquer::ResqueJob.perform('/')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
|
2
|
+
|
3
|
+
describe "Varnish" do
|
4
|
+
before(:each) do
|
5
|
+
@telnet_mock = mock('Net::Telnet')
|
6
|
+
Net::Telnet.stub!(:new).and_return(@telnet_mock)
|
7
|
+
@telnet_mock.stub!(:close)
|
8
|
+
@telnet_mock.stub!(:cmd)
|
9
|
+
@telnet_mock.stub!(:puts)
|
10
|
+
@telnet_mock.stub!(:waitfor)
|
11
|
+
Lacquer.configuration.retries.should == 5
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "with any command" do
|
15
|
+
describe "when connection is unsuccessful" do
|
16
|
+
it "should raise a Lacquer::VarnishError" do
|
17
|
+
@telnet_mock.stub!(:cmd).and_raise(Timeout::Error)
|
18
|
+
lambda {
|
19
|
+
Lacquer::Varnish.new.purge('/')
|
20
|
+
}.should raise_error(Lacquer::VarnishError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should retry on failure before erroring" do
|
24
|
+
@telnet_mock.stub!(:cmd).and_raise(Timeout::Error)
|
25
|
+
Net::Telnet.should_receive(:new).exactly(5).times
|
26
|
+
lambda {
|
27
|
+
Lacquer::Varnish.new.purge('/')
|
28
|
+
}.should raise_error(Lacquer::VarnishError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should close the connection afterwards" do
|
32
|
+
@telnet_mock.should_receive(:close).exactly(1).times
|
33
|
+
Lacquer::Varnish.new.purge('/')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "when using authentication" do
|
38
|
+
after(:each) do
|
39
|
+
Lacquer.configuration.varnish_servers.first[:secret] = nil
|
40
|
+
end
|
41
|
+
describe "with correct secret" do
|
42
|
+
before(:each) do
|
43
|
+
Lacquer.configuration.varnish_servers.first[:secret] = "the real secret"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return successfully when using correct secret" do
|
47
|
+
@telnet_mock.stub!(:waitfor).with("Match" => /^107/).and_yield('107 59 \nhaalpffwlcvblmdrinpnjwigwsbiiigq\n\nAuthentication required.\n\n')
|
48
|
+
@telnet_mock.stub!(:cmd).with("String" => "auth d218942acc92753db0c9fedddb32cde6158de28e903356caed1808cf0e23a15a", "Match" => /\d{3}/).and_yield('200')
|
49
|
+
@telnet_mock.stub!(:cmd).with("String" => "url.purge /", "Match" => /\n\n/).and_yield('200')
|
50
|
+
|
51
|
+
lambda {
|
52
|
+
Lacquer::Varnish.new.purge('/')
|
53
|
+
}.should_not raise_error
|
54
|
+
end
|
55
|
+
|
56
|
+
after(:each) do
|
57
|
+
Lacquer.configuration.varnish_servers.first[:secret] = nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "with wrong secret" do
|
62
|
+
before(:each) do
|
63
|
+
Lacquer.configuration.varnish_servers.first[:secret] = "the wrong secret"
|
64
|
+
end
|
65
|
+
it "should raise Lacquer::AuthenticationError when using wrong secret" do
|
66
|
+
@telnet_mock.stub!(:waitfor).with("Match" => /^107/).and_yield('107 59 \nhaalpffwlcvblmdrinpnjwigwsbiiigq\n\nAuthentication required.\n\n')
|
67
|
+
@telnet_mock.stub!(:cmd).with("String" => "auth 49725ec6723b64774a7ab918a24cba811130e99b7ac4b4c9d21ce9a8144762c8", "Match" => /\d{3}/).and_yield('107')
|
68
|
+
@telnet_mock.stub!(:cmd).with("url.purge /").and_yield('200')
|
69
|
+
|
70
|
+
lambda {
|
71
|
+
Lacquer::Varnish.new.purge('/')
|
72
|
+
}.should raise_error(Lacquer::VarnishError)
|
73
|
+
end
|
74
|
+
after(:each) do
|
75
|
+
Lacquer.configuration.varnish_servers.first[:secret] = nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "when connection is unsuccessful and an error handler is set" do
|
81
|
+
before(:each) do
|
82
|
+
Lacquer.configuration.command_error_handler = mock("command_error_handler")
|
83
|
+
end
|
84
|
+
it "should call handler on error" do
|
85
|
+
@telnet_mock.stub!(:cmd).and_raise(Timeout::Error)
|
86
|
+
Lacquer.configuration.command_error_handler.should_receive(:call).exactly(1).times
|
87
|
+
lambda {
|
88
|
+
Lacquer::Varnish.new.purge('/')
|
89
|
+
}.should_not raise_error(Lacquer::VarnishError)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "when sending a stats command" do
|
96
|
+
it "should return an array of stats" do
|
97
|
+
@telnet_mock.stub!(:cmd).and_yield(%Q[
|
98
|
+
200 2023
|
99
|
+
6263596 Client connections accepted
|
100
|
+
6260911 Client requests received
|
101
|
+
919605 Cache hits for pass
|
102
|
+
2123848 Cache misses
|
103
|
+
6723161 Backend conn. success
|
104
|
+
6641493 Fetch with Length
|
105
|
+
81512 Fetch wanted close
|
106
|
+
11 Fetch failed
|
107
|
+
1648 N struct sess_mem
|
108
|
+
81 N struct sess
|
109
|
+
22781 N struct object
|
110
|
+
23040 N struct objectcore
|
111
|
+
36047 N struct objecthead
|
112
|
+
56108 N struct smf
|
113
|
+
1646 N small free smf
|
114
|
+
263 N large free smf
|
115
|
+
55 N struct vbe_conn
|
116
|
+
804 N worker threads
|
117
|
+
1583 N worker threads created
|
118
|
+
2114 N worker threads limited
|
119
|
+
1609 N overflowed work requests
|
120
|
+
1 N backends
|
121
|
+
1693663 N expired objects
|
122
|
+
400637 N LRU nuked objects
|
123
|
+
10 N LRU moved objects
|
124
|
+
254 HTTP header overflows
|
125
|
+
2506470 Objects sent with write
|
126
|
+
6263541 Total Sessions
|
127
|
+
6260911 Total Requests
|
128
|
+
40 Total pipe
|
129
|
+
4599215 Total pass
|
130
|
+
6722994 Total fetch
|
131
|
+
2607029095 Total header bytes
|
132
|
+
55280196533 Total body bytes
|
133
|
+
6263536 Session Closed
|
134
|
+
5 Session herd
|
135
|
+
511352337 SHM records
|
136
|
+
33376035 SHM writes
|
137
|
+
33177 SHM flushes due to overflow
|
138
|
+
208858 SHM MTX contention
|
139
|
+
246 SHM cycles through buffer
|
140
|
+
6361382 allocator requests
|
141
|
+
54199 outstanding allocations
|
142
|
+
953389056 bytes allocated
|
143
|
+
120352768 bytes free
|
144
|
+
323 SMS allocator requests
|
145
|
+
151528 SMS bytes allocated
|
146
|
+
151528 SMS bytes freed
|
147
|
+
6723053 Backend requests made
|
148
|
+
1 N vcl total
|
149
|
+
1 N vcl available
|
150
|
+
49 N total active purges
|
151
|
+
197 N new purges added
|
152
|
+
148 N old purges deleted
|
153
|
+
6117 N objects tested
|
154
|
+
60375 N regexps tested against
|
155
|
+
140 N duplicate purges removed
|
156
|
+
944407 HCB Lookups without lock
|
157
|
+
3 HCB Lookups with lock
|
158
|
+
2099076 HCB Inserts
|
159
|
+
515351 Objects ESI parsed (unlock)
|
160
|
+
35371 Client uptime
|
161
|
+
|
162
|
+
500 22
|
163
|
+
Closing CLI connection
|
164
|
+
].strip)
|
165
|
+
stats = Lacquer::Varnish.new.stats
|
166
|
+
stats.size.should be(1)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe "when sending a purge command" do
|
171
|
+
it "should return successfully" do
|
172
|
+
@telnet_mock.stub!(:cmd).with("String" => "url.purge /", "Match" => /\n\n/).and_yield('200')
|
173
|
+
Lacquer::Varnish.new.purge('/').should be(true)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
3
|
+
|
4
|
+
require "lacquer"
|
5
|
+
require "rspec"
|
6
|
+
|
7
|
+
class ControllerClass
|
8
|
+
def self.before_filter(arg); end
|
9
|
+
def self.after_filter(arg); end
|
10
|
+
|
11
|
+
include Lacquer::CacheUtils
|
12
|
+
end
|
13
|
+
|
14
|
+
class SweeperClass
|
15
|
+
include Lacquer::CacheUtils
|
16
|
+
end
|
17
|
+
|
18
|
+
module Delayed;
|
19
|
+
module Job; end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Resque; end
|
23
|
+
|
24
|
+
Lacquer.configure do |config|
|
25
|
+
config.enable_cache = true
|
26
|
+
config.default_ttl = 1.week
|
27
|
+
config.job_backend = :none
|
28
|
+
config.varnish_servers << { :host => "0.0.0.0", :port => 6082 }
|
29
|
+
end
|
30
|
+
|
31
|
+
Rspec.configure do |c|
|
32
|
+
c.mock_with :rspec
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hakon-lacquer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 13
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 4
|
9
|
+
- 1
|
10
|
+
version: 0.4.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Russ Smith (russ@bashme.org)
|
14
|
+
- Ryan Johns
|
15
|
+
- Garry Tan (garry@posterous.com), Gabe da Silveira (gabe@websaviour.com)
|
16
|
+
- "H\xC3\xA5kon Lerring"
|
17
|
+
autorequire:
|
18
|
+
bindir: bin
|
19
|
+
cert_chain: []
|
20
|
+
|
21
|
+
date: 2010-12-28 00:00:00 +01:00
|
22
|
+
default_executable:
|
23
|
+
dependencies:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: activesupport
|
26
|
+
prerelease: false
|
27
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
hash: 7
|
33
|
+
segments:
|
34
|
+
- 3
|
35
|
+
- 0
|
36
|
+
version: "3.0"
|
37
|
+
type: :runtime
|
38
|
+
version_requirements: *id001
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: i18n
|
41
|
+
prerelease: false
|
42
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
hash: 3
|
48
|
+
segments:
|
49
|
+
- 0
|
50
|
+
- 4
|
51
|
+
version: "0.4"
|
52
|
+
type: :runtime
|
53
|
+
version_requirements: *id002
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: jeweler
|
56
|
+
prerelease: false
|
57
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
type: :runtime
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rake
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
type: :runtime
|
81
|
+
version_requirements: *id004
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: yard
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
type: :runtime
|
95
|
+
version_requirements: *id005
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rspec
|
98
|
+
prerelease: false
|
99
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ~>
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
hash: 3
|
105
|
+
segments:
|
106
|
+
- 2
|
107
|
+
- 0
|
108
|
+
version: "2.0"
|
109
|
+
type: :development
|
110
|
+
version_requirements: *id006
|
111
|
+
description: Rails drop in for Varnish support.
|
112
|
+
email: russ@bashme.org
|
113
|
+
executables: []
|
114
|
+
|
115
|
+
extensions: []
|
116
|
+
|
117
|
+
extra_rdoc_files:
|
118
|
+
- LICENSE
|
119
|
+
- README.rdoc
|
120
|
+
files:
|
121
|
+
- .bundle/config
|
122
|
+
- .document
|
123
|
+
- Gemfile
|
124
|
+
- Gemfile.lock
|
125
|
+
- LICENSE
|
126
|
+
- README.rdoc
|
127
|
+
- Rakefile
|
128
|
+
- VERSION
|
129
|
+
- init.rb
|
130
|
+
- lacquer.gemspec
|
131
|
+
- lib/generators/lacquer/USAGE
|
132
|
+
- lib/generators/lacquer/install_generator.rb
|
133
|
+
- lib/generators/lacquer/templates/initializer.rb
|
134
|
+
- lib/generators/lacquer/templates/varnish.sample.vcl
|
135
|
+
- lib/lacquer.rb
|
136
|
+
- lib/lacquer/cache_utils.rb
|
137
|
+
- lib/lacquer/configuration.rb
|
138
|
+
- lib/lacquer/delayed_job_job.rb
|
139
|
+
- lib/lacquer/resque_job.rb
|
140
|
+
- lib/lacquer/varnish.rb
|
141
|
+
- rails/init.rb
|
142
|
+
- spec/lacquer/cache_utils_spec.rb
|
143
|
+
- spec/lacquer/delayed_job_job_spec.rb
|
144
|
+
- spec/lacquer/resque_job_spec.rb
|
145
|
+
- spec/lacquer/varnish_spec.rb
|
146
|
+
- spec/spec_helper.rb
|
147
|
+
has_rdoc: true
|
148
|
+
homepage: http://github.com/russ/lacquer
|
149
|
+
licenses: []
|
150
|
+
|
151
|
+
post_install_message:
|
152
|
+
rdoc_options: []
|
153
|
+
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
none: false
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
hash: 3
|
162
|
+
segments:
|
163
|
+
- 0
|
164
|
+
version: "0"
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
166
|
+
none: false
|
167
|
+
requirements:
|
168
|
+
- - ">="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
hash: 3
|
171
|
+
segments:
|
172
|
+
- 0
|
173
|
+
version: "0"
|
174
|
+
requirements: []
|
175
|
+
|
176
|
+
rubyforge_project:
|
177
|
+
rubygems_version: 1.3.7
|
178
|
+
signing_key:
|
179
|
+
specification_version: 3
|
180
|
+
summary: Rails drop in for Varnish support.
|
181
|
+
test_files:
|
182
|
+
- spec/lacquer/cache_utils_spec.rb
|
183
|
+
- spec/lacquer/delayed_job_job_spec.rb
|
184
|
+
- spec/lacquer/resque_job_spec.rb
|
185
|
+
- spec/lacquer/varnish_spec.rb
|
186
|
+
- spec/spec_helper.rb
|