harbor 0.16.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +76 -0
- data/bin/harbor +7 -0
- data/lib/harbor.rb +17 -0
- data/lib/harbor/accessor_injector.rb +30 -0
- data/lib/harbor/application.rb +172 -0
- data/lib/harbor/auth/basic.rb +51 -0
- data/lib/harbor/block_io.rb +63 -0
- data/lib/harbor/cache.rb +90 -0
- data/lib/harbor/cache/disk.rb +99 -0
- data/lib/harbor/cache/item.rb +48 -0
- data/lib/harbor/cache/memory.rb +35 -0
- data/lib/harbor/cascade.rb +75 -0
- data/lib/harbor/console.rb +34 -0
- data/lib/harbor/container.rb +134 -0
- data/lib/harbor/contrib/debug.rb +236 -0
- data/lib/harbor/contrib/session/data_mapper.rb +74 -0
- data/lib/harbor/daemon.rb +105 -0
- data/lib/harbor/errors.rb +49 -0
- data/lib/harbor/events.rb +45 -0
- data/lib/harbor/exception_notifier.rb +59 -0
- data/lib/harbor/file.rb +66 -0
- data/lib/harbor/file_store.rb +69 -0
- data/lib/harbor/file_store/file.rb +100 -0
- data/lib/harbor/file_store/local.rb +71 -0
- data/lib/harbor/file_store/mosso.rb +154 -0
- data/lib/harbor/file_store/mosso/private.rb +8 -0
- data/lib/harbor/generator.rb +56 -0
- data/lib/harbor/generator/help.rb +34 -0
- data/lib/harbor/generator/setup.rb +82 -0
- data/lib/harbor/generator/skeletons/basic/config.ru.skel +21 -0
- data/lib/harbor/generator/skeletons/basic/lib/appname.rb.skel +49 -0
- data/lib/harbor/generator/skeletons/basic/lib/appname/controllers/home.rb +9 -0
- data/lib/harbor/generator/skeletons/basic/lib/appname/views/home/index.html.erb.skel +23 -0
- data/lib/harbor/generator/skeletons/basic/lib/appname/views/layouts/application.html.erb.skel +48 -0
- data/lib/harbor/generator/skeletons/basic/lib/appname/views/layouts/exception.html.erb.skel +13 -0
- data/lib/harbor/generator/skeletons/basic/lib/appname/views/layouts/login.html.erb.skel +11 -0
- data/lib/harbor/generator/skeletons/basic/log/development.log +0 -0
- data/lib/harbor/hooks.rb +105 -0
- data/lib/harbor/json_cookies.rb +37 -0
- data/lib/harbor/layouts.rb +61 -0
- data/lib/harbor/locale.rb +50 -0
- data/lib/harbor/locales.txt +22 -0
- data/lib/harbor/logging.rb +39 -0
- data/lib/harbor/logging/appenders/email.rb +84 -0
- data/lib/harbor/logging/request_logger.rb +34 -0
- data/lib/harbor/mail_servers/abstract.rb +12 -0
- data/lib/harbor/mail_servers/sendmail.rb +19 -0
- data/lib/harbor/mail_servers/smtp.rb +25 -0
- data/lib/harbor/mail_servers/test.rb +17 -0
- data/lib/harbor/mailer.rb +96 -0
- data/lib/harbor/messages.rb +17 -0
- data/lib/harbor/mime.rb +206 -0
- data/lib/harbor/plugin.rb +52 -0
- data/lib/harbor/plugin_list.rb +38 -0
- data/lib/harbor/request.rb +138 -0
- data/lib/harbor/response.rb +281 -0
- data/lib/harbor/router.rb +112 -0
- data/lib/harbor/script.rb +155 -0
- data/lib/harbor/session.rb +75 -0
- data/lib/harbor/session/abstract.rb +27 -0
- data/lib/harbor/session/cookie.rb +17 -0
- data/lib/harbor/shellwords.rb +26 -0
- data/lib/harbor/test/mailer.rb +10 -0
- data/lib/harbor/test/request.rb +28 -0
- data/lib/harbor/test/response.rb +17 -0
- data/lib/harbor/test/session.rb +11 -0
- data/lib/harbor/test/test.rb +22 -0
- data/lib/harbor/version.rb +3 -0
- data/lib/harbor/view.rb +89 -0
- data/lib/harbor/view_context.rb +134 -0
- data/lib/harbor/view_context/helpers.rb +7 -0
- data/lib/harbor/view_context/helpers/cache.rb +77 -0
- data/lib/harbor/view_context/helpers/form.rb +34 -0
- data/lib/harbor/view_context/helpers/html.rb +26 -0
- data/lib/harbor/view_context/helpers/text.rb +120 -0
- data/lib/harbor/view_context/helpers/url.rb +11 -0
- data/lib/harbor/xml_view.rb +57 -0
- data/lib/harbor/zipped_io.rb +203 -0
- data/lib/vendor/cloudfiles-1.3.0/cloudfiles.rb +77 -0
- data/lib/vendor/cloudfiles-1.3.0/cloudfiles/authentication.rb +46 -0
- data/lib/vendor/cloudfiles-1.3.0/cloudfiles/connection.rb +280 -0
- data/lib/vendor/cloudfiles-1.3.0/cloudfiles/container.rb +260 -0
- data/lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb +253 -0
- metadata +155 -0
data/Rakefile
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "pathname"
|
3
|
+
require "rake"
|
4
|
+
require "rake/rdoctask"
|
5
|
+
require "rake/testtask"
|
6
|
+
|
7
|
+
# Tests
|
8
|
+
task :default => [:test]
|
9
|
+
|
10
|
+
Rake::TestTask.new do |t|
|
11
|
+
t.libs << "test"
|
12
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
# rdoc
|
17
|
+
|
18
|
+
# desc "Generate RDoc documentation"
|
19
|
+
# Rake::RDocTask.new(:rdoc) do |rdoc|
|
20
|
+
# rdoc.options << '--line-numbers' << '--inline-source' <<
|
21
|
+
# '--main' << 'README' <<
|
22
|
+
# '--title' << 'Harbor Documentation' <<
|
23
|
+
# '--charset' << 'utf-8' << "--exclude" << "lib/harbor/generator/"
|
24
|
+
# rdoc.rdoc_dir = "doc"
|
25
|
+
# rdoc.rdoc_files.include 'README'
|
26
|
+
# rdoc.
|
27
|
+
# rdoc.rdoc_files.include('lib/rack.rb')
|
28
|
+
# rdoc.rdoc_files.include('lib/rack/*.rb')
|
29
|
+
# rdoc.rdoc_files.include('lib/rack/*/*.rb')
|
30
|
+
# end
|
31
|
+
|
32
|
+
task :rdoc do
|
33
|
+
sh <<-EOS.strip
|
34
|
+
rdoc -T harbor#{" --op " + ENV["OUTPUT_DIRECTORY"] if ENV["OUTPUT_DIRECTORY"]} --line-numbers --main README --title "Harbor Documentation" --exclude "lib/harbor/generator/*" lib/harbor.rb lib/harbor README
|
35
|
+
EOS
|
36
|
+
end
|
37
|
+
|
38
|
+
# Performance
|
39
|
+
task :perf => :performance
|
40
|
+
task :performance do
|
41
|
+
puts `ruby #{Pathname(__FILE__).dirname + "script/performance.rb"}`
|
42
|
+
end
|
43
|
+
|
44
|
+
# Gem
|
45
|
+
|
46
|
+
require "lib/harbor/version"
|
47
|
+
require "rake/gempackagetask"
|
48
|
+
|
49
|
+
NAME = "harbor"
|
50
|
+
SUMMARY = "Harbor Framework"
|
51
|
+
GEM_VERSION = Harbor::VERSION
|
52
|
+
|
53
|
+
spec = Gem::Specification.new do |s|
|
54
|
+
s.name = NAME
|
55
|
+
s.summary = s.description = SUMMARY
|
56
|
+
s.author = "Wieck Media"
|
57
|
+
s.homepage = "http://wiecklabs.com"
|
58
|
+
s.email = "dev@wieck.com"
|
59
|
+
s.version = GEM_VERSION
|
60
|
+
s.platform = Gem::Platform::RUBY
|
61
|
+
s.require_path = 'lib'
|
62
|
+
s.files = %w(Rakefile) + Dir.glob("lib/**/*")
|
63
|
+
s.executables = ['harbor']
|
64
|
+
|
65
|
+
s.add_dependency "rack", "~> 1.0.0"
|
66
|
+
s.add_dependency "erubis"
|
67
|
+
end
|
68
|
+
|
69
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
70
|
+
pkg.gem_spec = spec
|
71
|
+
end
|
72
|
+
|
73
|
+
desc "Install Harbor as a gem"
|
74
|
+
task :install => [:repackage] do
|
75
|
+
sh %{gem install pkg/#{NAME}-#{GEM_VERSION}}
|
76
|
+
end
|
data/bin/harbor
ADDED
data/lib/harbor.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
$:.unshift(Pathname(__FILE__).dirname.expand_path)
|
5
|
+
|
6
|
+
require "harbor/version"
|
7
|
+
require "harbor/hooks"
|
8
|
+
require "harbor/file_store"
|
9
|
+
require "harbor/shellwords"
|
10
|
+
require "harbor/file"
|
11
|
+
require "harbor/container"
|
12
|
+
require "harbor/router"
|
13
|
+
require "harbor/application"
|
14
|
+
require "harbor/cascade"
|
15
|
+
require "harbor/plugin"
|
16
|
+
require "harbor/mime"
|
17
|
+
require "harbor/errors"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Harbor
|
2
|
+
##
|
3
|
+
# A simple module to facilitate accessor based injection. This is used by Harbor::Plugin
|
4
|
+
# to allow you to easily construct plugins from hashes, i.e.:
|
5
|
+
#
|
6
|
+
# <%= plugin("user/tabs", :user => @user)
|
7
|
+
#
|
8
|
+
# It can of course be used for anything, however:
|
9
|
+
#
|
10
|
+
# class Dog
|
11
|
+
# include Harbor::AccessorInjector
|
12
|
+
# attr_accessor :owner
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# dog = Dog.new.inject(:owner => "Tom")
|
16
|
+
# dog.owner # => "Tom"
|
17
|
+
##
|
18
|
+
module AccessorInjector
|
19
|
+
|
20
|
+
def inject(options = {})
|
21
|
+
options.each do |key, value|
|
22
|
+
setter = "#{key}="
|
23
|
+
send(setter, value) if respond_to?(setter)
|
24
|
+
end
|
25
|
+
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
gem "rack", "~> 1.0.0"
|
2
|
+
require "rack"
|
3
|
+
|
4
|
+
require "yaml"
|
5
|
+
|
6
|
+
require Pathname(__FILE__).dirname + "request"
|
7
|
+
require Pathname(__FILE__).dirname + "response"
|
8
|
+
require Pathname(__FILE__).dirname + "block_io"
|
9
|
+
require Pathname(__FILE__).dirname + "zipped_io"
|
10
|
+
require Pathname(__FILE__).dirname + "events"
|
11
|
+
require Pathname(__FILE__).dirname + "messages"
|
12
|
+
|
13
|
+
module Harbor
|
14
|
+
class Application
|
15
|
+
|
16
|
+
include Harbor::Events
|
17
|
+
|
18
|
+
##
|
19
|
+
# Routes are defined in this method. Note that Harbor does not define any default routes,
|
20
|
+
# so you must reimplement this method in your application.
|
21
|
+
##
|
22
|
+
def self.routes(services)
|
23
|
+
raise NotImplementedError.new("Your application must redefine #{self}::routes.")
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :router, :environment, :services
|
27
|
+
|
28
|
+
def initialize(services, *args)
|
29
|
+
unless services.is_a?(Harbor::Container)
|
30
|
+
raise ArgumentError.new("Harbor::Application#services must be a Harbor::Container")
|
31
|
+
end
|
32
|
+
|
33
|
+
@services = services
|
34
|
+
|
35
|
+
@router = (!args.empty? && !args[0].is_a?(String) && args[0].respond_to?(:match)) ? args.shift : self.class.routes(@services)
|
36
|
+
@environment = args.last || "development"
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Request entry point called by Rack. It creates a request and response
|
41
|
+
# object based on the incoming request environment, checks for public
|
42
|
+
# files, and dispatches the request.
|
43
|
+
#
|
44
|
+
# It returns a rack response hash.
|
45
|
+
##
|
46
|
+
def call(env)
|
47
|
+
env["APP_ENVIRONMENT"] = environment
|
48
|
+
request = Request.new(self, env)
|
49
|
+
response = Response.new(request)
|
50
|
+
|
51
|
+
catch(:abort_request) do
|
52
|
+
if file = find_public_file(request.path_info[1..-1])
|
53
|
+
response.cache(nil, ::File.mtime(file), 86400) do
|
54
|
+
response.stream_file(file)
|
55
|
+
end
|
56
|
+
|
57
|
+
return response.to_a
|
58
|
+
end
|
59
|
+
|
60
|
+
handler = @router.match(request)
|
61
|
+
|
62
|
+
dispatch_request(handler, request, response)
|
63
|
+
end
|
64
|
+
|
65
|
+
response.to_a
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Request dispatch function, which handles 404's, exceptions,
|
70
|
+
# and logs requests.
|
71
|
+
##
|
72
|
+
def dispatch_request(handler, request, response)
|
73
|
+
start = Time.now
|
74
|
+
|
75
|
+
return handle_not_found(request, response) unless handler
|
76
|
+
|
77
|
+
handler.call(request, response)
|
78
|
+
rescue StandardError, LoadError, SyntaxError => e
|
79
|
+
handle_exception(e, request, response)
|
80
|
+
ensure
|
81
|
+
raise_event(:request_complete, request, response, start, Time.now)
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Method used to nicely handle cases where no routes or public files
|
86
|
+
# match the incoming request.
|
87
|
+
#
|
88
|
+
# By default, it will render "The page you requested could not be found".
|
89
|
+
#
|
90
|
+
# To use a custom 404 message, create a view "exceptions/404.html.erb", and
|
91
|
+
# optionally create a view "layouts/exception.html.erb" to style it.
|
92
|
+
##
|
93
|
+
def handle_not_found(request, response)
|
94
|
+
response.flush
|
95
|
+
response.status = 404
|
96
|
+
|
97
|
+
response.layout = "layouts/exception" if Harbor::View.exists?("layouts/exception")
|
98
|
+
|
99
|
+
if Harbor::View.exists?("exceptions/404.html.erb")
|
100
|
+
response.render "exceptions/404.html.erb"
|
101
|
+
else
|
102
|
+
response.puts "The page you requested could not be found"
|
103
|
+
end
|
104
|
+
|
105
|
+
raise_event(:not_found, request, response)
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Method used to nicely handle uncaught exceptions.
|
110
|
+
#
|
111
|
+
# Logs full error messages to the configured 'error' logger.
|
112
|
+
#
|
113
|
+
# By default, it will render "We're sorry, but something went wrong."
|
114
|
+
#
|
115
|
+
# To use a custom 500 message, create a view "exceptions/500.html.erb", and
|
116
|
+
# optionally create a view "layouts/exception.html.erb" to style it.
|
117
|
+
##
|
118
|
+
def handle_exception(exception, request, response)
|
119
|
+
response.flush
|
120
|
+
response.status = 500
|
121
|
+
|
122
|
+
trace = build_exception_trace(exception, request)
|
123
|
+
|
124
|
+
if environment == "development"
|
125
|
+
response.content_type = "text/html"
|
126
|
+
response.puts(Rack::ShowExceptions.new(nil).pretty(request.env, exception))
|
127
|
+
else
|
128
|
+
response.layout = "layouts/exception" if Harbor::View.exists?("layouts/exception")
|
129
|
+
|
130
|
+
if Harbor::View.exists?("exceptions/500.html.erb")
|
131
|
+
response.render "exceptions/500.html.erb", :exception => exception
|
132
|
+
else
|
133
|
+
response.puts "We're sorry, but something went wrong."
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
raise_event(:exception, exception, request, response, trace)
|
138
|
+
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
|
142
|
+
def find_public_file(file) #:nodoc:
|
143
|
+
public_path = Pathname(self.class.respond_to?(:public_path) ? self.class.public_path : "public")
|
144
|
+
path = public_path + file
|
145
|
+
|
146
|
+
path.file? ? path : nil
|
147
|
+
end
|
148
|
+
|
149
|
+
def default_layout
|
150
|
+
warn "Harbor::Application#default_layout has been deprecated. See Harbor::Layouts."
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def build_exception_trace(exception, request)
|
156
|
+
trace = ""
|
157
|
+
trace << "="*80
|
158
|
+
trace << "\n"
|
159
|
+
trace << "== [ #{self.class}: #{exception} @ #{Time.now} ] =="
|
160
|
+
trace << "\n"
|
161
|
+
trace << exception.backtrace.join("\n")
|
162
|
+
trace << "\n"
|
163
|
+
trace << "== [ Request ] =="
|
164
|
+
trace << "\n"
|
165
|
+
trace << request.env.to_yaml
|
166
|
+
trace << "\n"
|
167
|
+
trace << "="*80
|
168
|
+
trace << "\n"
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Harbor
|
2
|
+
module Auth
|
3
|
+
##
|
4
|
+
# Simple mechanism for implementing HTTP basic authentication.
|
5
|
+
#
|
6
|
+
# get("/secret-page") do |request, response|
|
7
|
+
# Harbor::Auth::Basic.authenticate(request, response) { |username, password| username == "wieck" }
|
8
|
+
#
|
9
|
+
# response.puts "Secret Stuff"
|
10
|
+
# end
|
11
|
+
##
|
12
|
+
class Basic
|
13
|
+
|
14
|
+
AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION']
|
15
|
+
|
16
|
+
attr_accessor :realm
|
17
|
+
|
18
|
+
##
|
19
|
+
# Checks the credentials provided in the request against the provided
|
20
|
+
# block. If the block returns false, the request aborted.
|
21
|
+
##
|
22
|
+
def self.authenticate(request, response) #:yields: username, password
|
23
|
+
auth = new(request)
|
24
|
+
|
25
|
+
unless auth.provided? && yield(auth.credentials)
|
26
|
+
response.headers["WWW-Authenticate"] = 'Basic realm="%s"' % auth.realm
|
27
|
+
response.unauthorized!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(request)
|
32
|
+
@request = request
|
33
|
+
end
|
34
|
+
|
35
|
+
def provided?
|
36
|
+
!!authorization_key
|
37
|
+
end
|
38
|
+
|
39
|
+
def credentials
|
40
|
+
@request.env[authorization_key].split(' ', 2).last.unpack("m*").first.split(/:/, 2)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def authorization_key
|
46
|
+
@authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @request.env.has_key?(key) }
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Harbor
|
2
|
+
##
|
3
|
+
# Used by Harbor::Response#send_file and Harbor::Response#stream_file to send
|
4
|
+
# large files or streams in chunks. This is a fallback measure for the cases
|
5
|
+
# where X-Sendfile cannot be used.
|
6
|
+
##
|
7
|
+
class BlockIO
|
8
|
+
|
9
|
+
def self.block_size
|
10
|
+
@@block_size ||= 500_000 # 500kb
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.block_size=(value)
|
14
|
+
raise ArgumentError.new("Harbor::BlockIO::block_size value must be a Fixnum") unless value.is_a?(Fixnum)
|
15
|
+
@@block_size = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(path_or_io)
|
19
|
+
case path_or_io
|
20
|
+
when ::IO
|
21
|
+
@io = path_or_io
|
22
|
+
@size = @io.stat.size
|
23
|
+
when StringIO
|
24
|
+
@io = path_or_io
|
25
|
+
@size = @io.size
|
26
|
+
when Harbor::FileStore::File
|
27
|
+
@io = path_or_io
|
28
|
+
@size = @io.size
|
29
|
+
@path = path_or_io.absolute_path
|
30
|
+
when Pathname
|
31
|
+
@path = path_or_io.expand_path.to_s
|
32
|
+
@io = path_or_io.open('r')
|
33
|
+
@size = path_or_io.size
|
34
|
+
else
|
35
|
+
@path = path_or_io.to_s
|
36
|
+
@io = ::File::open(@path, 'r')
|
37
|
+
@size = ::File.size(@path)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def path
|
42
|
+
@path
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
@io.read
|
47
|
+
end
|
48
|
+
|
49
|
+
def size
|
50
|
+
@size
|
51
|
+
end
|
52
|
+
|
53
|
+
def close
|
54
|
+
@io.close
|
55
|
+
end
|
56
|
+
|
57
|
+
def each
|
58
|
+
while data = @io.read(Harbor::BlockIO::block_size) do
|
59
|
+
yield data
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/harbor/cache.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require "thread"
|
2
|
+
require Pathname(__FILE__).dirname + "cache/item"
|
3
|
+
|
4
|
+
module Harbor
|
5
|
+
|
6
|
+
class Cache
|
7
|
+
|
8
|
+
class PutArgumentError < ArgumentError; end
|
9
|
+
|
10
|
+
attr_accessor :logger
|
11
|
+
|
12
|
+
def initialize(store)
|
13
|
+
raise ArgumentError.new("Harbor::Cache.new expects a non-null 'store' parameter") unless store
|
14
|
+
|
15
|
+
@store = store
|
16
|
+
@semaphore = Mutex.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def put(key, content, ttl, maximum_age = nil)
|
20
|
+
raise PutArgumentError.new("Harbor::Cache::Memory#put expects a String value for 'key', got #{key}") unless key.is_a?(String)
|
21
|
+
raise PutArgumentError.new("Harbor::Cache::Memory#put expects a Fixnum value greater than 0 for 'ttl', got #{ttl}") unless ttl.is_a?(Fixnum) && ttl > 0
|
22
|
+
raise PutArgumentError.new("Harbor::Cache::Memory#put expects nil, or a Fixnum value greater than 0 for 'maximum_age', got #{maximum_age}") unless maximum_age.nil? || (maximum_age.is_a?(Fixnum) && maximum_age > 0)
|
23
|
+
raise PutArgumentError.new("Harbor::Cache::Memory#put expects a maximum_age greater than the ttl, got ttl: #{ttl}, maximum_age: #{maximum_age}") if maximum_age && ttl && (maximum_age <= ttl)
|
24
|
+
|
25
|
+
@semaphore.synchronize do
|
26
|
+
# Prevent multiple writes of similar content to the cache
|
27
|
+
return true if (cached_item = @store.get(key)) && cached_item.fresh? && cached_item.content.hash == content.hash
|
28
|
+
@store.put(key, ttl, maximum_age, content, Time.now)
|
29
|
+
end
|
30
|
+
rescue
|
31
|
+
log("Harbor::Cache#put unable to store cached content.", $!)
|
32
|
+
|
33
|
+
raise if $!.is_a?(PutArgumentError)
|
34
|
+
ensure
|
35
|
+
content
|
36
|
+
end
|
37
|
+
|
38
|
+
def get(key)
|
39
|
+
if item = @store.get(key)
|
40
|
+
if item.fresh?
|
41
|
+
@semaphore.synchronize do
|
42
|
+
@store.bump(key)
|
43
|
+
end
|
44
|
+
|
45
|
+
item
|
46
|
+
else
|
47
|
+
delete(key)
|
48
|
+
|
49
|
+
item = nil
|
50
|
+
end
|
51
|
+
else
|
52
|
+
item = nil
|
53
|
+
end
|
54
|
+
rescue
|
55
|
+
log("Harbor::Cache#get unable to retrieve cached content.", $!)
|
56
|
+
ensure
|
57
|
+
defined?(item) ? item : nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete(key)
|
61
|
+
@semaphore.synchronize do
|
62
|
+
@store.delete(key)
|
63
|
+
end
|
64
|
+
rescue
|
65
|
+
log("Harbor::Cache#put unable to delete cached content.", $!)
|
66
|
+
ensure
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def delete_matching(key)
|
71
|
+
@semaphore.synchronize do
|
72
|
+
@store.delete_matching(key)
|
73
|
+
end
|
74
|
+
rescue
|
75
|
+
log("Harbor::Cache#put unable to delete cached content.", $!)
|
76
|
+
ensure
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def log(message, error)
|
83
|
+
if @logger
|
84
|
+
@logger.fatal("#{message} #{$!}\n#{$!.message}\nBacktrace:\n#{$!.backtrace.join("\n")}")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|