harbor 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|