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
@@ -0,0 +1,112 @@
|
|
1
|
+
module Harbor
|
2
|
+
class Router
|
3
|
+
|
4
|
+
URI_CHAR = '[^/?:,&#\.\[\]]'.freeze unless defined?(URI_CHAR)
|
5
|
+
PARAM = /(:(#{URI_CHAR}+))/.freeze unless defined?(PARAM)
|
6
|
+
|
7
|
+
attr_accessor :routes
|
8
|
+
|
9
|
+
def initialize(&routes)
|
10
|
+
@routes = []
|
11
|
+
@route_match_cache = {}
|
12
|
+
instance_eval(&routes) if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def merge!(other)
|
16
|
+
self.routes |= other.routes
|
17
|
+
end
|
18
|
+
|
19
|
+
# Matches a GET request
|
20
|
+
def get(matcher, &handler)
|
21
|
+
register(:get, matcher, &handler)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Matches a POST (create) request
|
25
|
+
def post(matcher, &handler)
|
26
|
+
register(:post, matcher, &handler)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Matches a PUT (update) request
|
30
|
+
def put(matcher, &handler)
|
31
|
+
register(:put, matcher, &handler)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Matches a DELETE request
|
35
|
+
def delete(matcher, &handler)
|
36
|
+
register(:delete, matcher, &handler)
|
37
|
+
end
|
38
|
+
|
39
|
+
def using(container, klass, initializer = nil, &block)
|
40
|
+
self.class::Using.new(self, container, klass, initializer).instance_eval(&block)
|
41
|
+
end
|
42
|
+
|
43
|
+
class Using
|
44
|
+
def initialize(router, container, klass, initializer)
|
45
|
+
@router = router
|
46
|
+
@container = container
|
47
|
+
|
48
|
+
if klass.is_a?(String)
|
49
|
+
@service_name = klass
|
50
|
+
else
|
51
|
+
@service_name = klass.to_s
|
52
|
+
@container.register(@service_name, klass, &initializer)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
%w(get post put delete).each do |verb|
|
57
|
+
class_eval <<-EOS
|
58
|
+
def #{verb}(matcher, &handler)
|
59
|
+
@router.send(#{verb.inspect}, matcher) do |request, response|
|
60
|
+
service = @container.get(@service_name, :request => request, :response => response)
|
61
|
+
|
62
|
+
handler.arity == 2 ? handler[service, request] : handler[service]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
EOS
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def register(request_method, matcher, &handler)
|
70
|
+
matcher, param_keys = transform(matcher)
|
71
|
+
route = [request_method.to_s.upcase, matcher, param_keys, handler]
|
72
|
+
@routes << route
|
73
|
+
route
|
74
|
+
end
|
75
|
+
|
76
|
+
def clear
|
77
|
+
@routes = []
|
78
|
+
end
|
79
|
+
|
80
|
+
def match(request)
|
81
|
+
@routes.each do |request_method, matcher, param_keys, handler|
|
82
|
+
next unless request.request_method == request_method
|
83
|
+
|
84
|
+
# Strip trailing forward-slash on request path before matching
|
85
|
+
request_path = (request.path_info[-1] == ?/) ? request.path_info[0..-2] : request.path_info
|
86
|
+
|
87
|
+
next unless request_path =~ matcher
|
88
|
+
|
89
|
+
request.params.update(Hash[*param_keys.zip($~.captures).flatten])
|
90
|
+
return handler
|
91
|
+
end
|
92
|
+
|
93
|
+
# No routes matched, so return false
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def transform(matcher)
|
100
|
+
param_keys = []
|
101
|
+
|
102
|
+
if matcher.is_a?(String)
|
103
|
+
# Strip trailing forward-slash on routes
|
104
|
+
matcher = matcher[0..-2] if (matcher[-1] == ?/)
|
105
|
+
matcher = /^#{matcher.gsub('.', '[\.]').gsub(PARAM) { param_keys << $2; "(#{URI_CHAR}+)" }}$/
|
106
|
+
end
|
107
|
+
|
108
|
+
[matcher, param_keys]
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require "harbor/logging"
|
2
|
+
|
3
|
+
gem "thin"
|
4
|
+
require 'thin/version'
|
5
|
+
require 'thin/daemonizing'
|
6
|
+
|
7
|
+
module Logging #:nodoc:
|
8
|
+
def log(message)
|
9
|
+
Harbor::Script.logger << message + "\n"
|
10
|
+
end
|
11
|
+
module_function :log
|
12
|
+
end
|
13
|
+
|
14
|
+
module Harbor
|
15
|
+
##
|
16
|
+
# Class for defining and running daemonizable scripts.
|
17
|
+
#
|
18
|
+
# services = Harbor::Container.new
|
19
|
+
# services.register("mailer", Harbor::Mailer)
|
20
|
+
#
|
21
|
+
# class Processor < Harbor::Script
|
22
|
+
#
|
23
|
+
# attr_accessor :mailer
|
24
|
+
#
|
25
|
+
# def self.pid_file
|
26
|
+
# "tmp/processor.pid"
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# def self.log_file
|
30
|
+
# "log/processor.log"
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# def self.run!
|
34
|
+
# loop do
|
35
|
+
# # processing code
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Harbor::Script::Runner.new(ARGV, services, Processor).run!
|
41
|
+
##
|
42
|
+
class Script
|
43
|
+
include Thin::Daemonizable
|
44
|
+
|
45
|
+
attr_accessor :options, :services
|
46
|
+
|
47
|
+
def self.logger
|
48
|
+
@logger ||= begin
|
49
|
+
logger = Logging::Logger[self]
|
50
|
+
logger.additive = false
|
51
|
+
logger.level = :info
|
52
|
+
logger.add_appenders(Logging::Appenders::Stdout.new)
|
53
|
+
logger
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def logger
|
58
|
+
self.class.logger
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.pid_file
|
62
|
+
raise NotImplementedError.new("#{self}#pid_file must be defined.")
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.log_file
|
66
|
+
raise NotImplementedError.new("#{self}#log_file must be defined.")
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize
|
70
|
+
@on_restart = lambda do
|
71
|
+
exec("#{$0} #{'--daemonize' if @options[:daemonize]} start")
|
72
|
+
end
|
73
|
+
|
74
|
+
@pid_file = self.class.pid_file
|
75
|
+
@log_file = self.class.log_file
|
76
|
+
end
|
77
|
+
|
78
|
+
def log(message)
|
79
|
+
if @options[:daemonize]
|
80
|
+
self.class.logger << message + "\n"
|
81
|
+
else
|
82
|
+
puts message
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def name
|
87
|
+
$0
|
88
|
+
end
|
89
|
+
|
90
|
+
def stop
|
91
|
+
log ">> Stopping"
|
92
|
+
end
|
93
|
+
|
94
|
+
def run!
|
95
|
+
raise NotImplementedError.new("#{self}.run! must be defined.")
|
96
|
+
end
|
97
|
+
|
98
|
+
class Runner
|
99
|
+
|
100
|
+
COMMANDS = %w(start stop restart)
|
101
|
+
|
102
|
+
def initialize(argv, services, script)
|
103
|
+
raise ArgumentError.new("services must be a Harbor::Container but was #{services.inspect}") unless services.is_a?(Harbor::Container)
|
104
|
+
|
105
|
+
@argv = argv
|
106
|
+
@script = script
|
107
|
+
@script_name = @script.to_s
|
108
|
+
@services = services
|
109
|
+
|
110
|
+
@services.register(@script_name, @script) unless services.registered?(@script_name)
|
111
|
+
|
112
|
+
parse!
|
113
|
+
end
|
114
|
+
|
115
|
+
def parse!
|
116
|
+
@options = {
|
117
|
+
:daemonize => false
|
118
|
+
}
|
119
|
+
|
120
|
+
parser = OptionParser.new do |opts|
|
121
|
+
opts.on("-d", "--daemonize", "Daemonize process") { @options[:daemonize] = true }
|
122
|
+
end
|
123
|
+
|
124
|
+
parser.parse!(@argv)
|
125
|
+
@command = @argv.shift
|
126
|
+
end
|
127
|
+
|
128
|
+
def run!
|
129
|
+
unless COMMANDS.include?(@command)
|
130
|
+
puts "#{@command.inspect} is not a valid command. Commands are: #{COMMANDS.join(", ")}"
|
131
|
+
exit 1
|
132
|
+
end
|
133
|
+
|
134
|
+
send(@command)
|
135
|
+
end
|
136
|
+
|
137
|
+
def start
|
138
|
+
script = @services.get(@script_name, :options => @options, :services => @services)
|
139
|
+
|
140
|
+
script.daemonize if @options[:daemonize]
|
141
|
+
|
142
|
+
@argv.empty? ? script.run! : script.run!(*@argv)
|
143
|
+
end
|
144
|
+
|
145
|
+
def restart
|
146
|
+
Harbor::Script.restart(@script.pid_file)
|
147
|
+
end
|
148
|
+
|
149
|
+
def stop
|
150
|
+
Harbor::Script.kill(@script.pid_file)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require Pathname(__FILE__).dirname + 'session/abstract'
|
2
|
+
require Pathname(__FILE__).dirname + 'session/cookie'
|
3
|
+
|
4
|
+
module Harbor
|
5
|
+
|
6
|
+
class Session
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
:key => "harbor.session",
|
9
|
+
:domain => nil,
|
10
|
+
:path => "/",
|
11
|
+
:expire_after => nil,
|
12
|
+
:store => Cookie
|
13
|
+
}
|
14
|
+
|
15
|
+
##
|
16
|
+
# Configures non-default session settings.
|
17
|
+
#
|
18
|
+
# Harbor::Session.configure do |session|
|
19
|
+
# session[:domain] = "*.domain.com"
|
20
|
+
# session[:store] = Custom::Session::Store
|
21
|
+
# end
|
22
|
+
##
|
23
|
+
def self.configure #:yields: default_options
|
24
|
+
@options = DEFAULT_OPTIONS.dup
|
25
|
+
yield(@options)
|
26
|
+
@options
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.options
|
30
|
+
@options ||= DEFAULT_OPTIONS.dup
|
31
|
+
@options
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(request)
|
35
|
+
@options = self.class.options.dup
|
36
|
+
@cookie = request.cookies[@options[:key]]
|
37
|
+
@store = self.class.options[:store]
|
38
|
+
|
39
|
+
@data ||= @store.load_session(@cookie)
|
40
|
+
end
|
41
|
+
|
42
|
+
def key
|
43
|
+
@options[:key]
|
44
|
+
end
|
45
|
+
|
46
|
+
def []=(key, value)
|
47
|
+
@data[key] = value
|
48
|
+
end
|
49
|
+
|
50
|
+
def [](key)
|
51
|
+
@data[key]
|
52
|
+
end
|
53
|
+
|
54
|
+
def data
|
55
|
+
@data
|
56
|
+
end
|
57
|
+
|
58
|
+
def id
|
59
|
+
@data[:session_id]
|
60
|
+
end
|
61
|
+
|
62
|
+
def save
|
63
|
+
cookie = {}
|
64
|
+
cookie[:domain] = @options[:domain]
|
65
|
+
cookie[:path] = @options[:path]
|
66
|
+
cookie[:expires] = Time.now + @options[:expire_after] unless @options[:expire_after].nil?
|
67
|
+
cookie[:value] = @store.commit_session(@data)
|
68
|
+
cookie
|
69
|
+
end
|
70
|
+
|
71
|
+
def destroy
|
72
|
+
@data.clear
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Harbor
|
2
|
+
class Session
|
3
|
+
|
4
|
+
##
|
5
|
+
# Defines the API and default behavior for session stores. See Cookie, and
|
6
|
+
# contrib/session/data_mapper for examples of usage.
|
7
|
+
##
|
8
|
+
class Abstract
|
9
|
+
##
|
10
|
+
# Receives the raw cookie data, and should return a hash
|
11
|
+
# of the data for the session.
|
12
|
+
##
|
13
|
+
def self.load_session(cookie)
|
14
|
+
Marshal.load(cookie.unpack("m*")[0]) rescue {}
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Receives the session data hash, and should return the data
|
19
|
+
# to be set for the cookie's value
|
20
|
+
##
|
21
|
+
def self.commit_session(data)
|
22
|
+
[Marshal.dump(data)].pack("m*")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Harbor
|
2
|
+
class Session
|
3
|
+
|
4
|
+
##
|
5
|
+
# Basic enhancement to Abstract session to automatically generate
|
6
|
+
# session_id's.
|
7
|
+
##
|
8
|
+
class Cookie < Abstract
|
9
|
+
def self.load_session(cookie)
|
10
|
+
cookie = super
|
11
|
+
cookie[:session_id] ||= `uuidgen`.chomp
|
12
|
+
cookie
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
##
|
4
|
+
# This is a backport of ruby-1.8.7's Shellwords.escape function to 1.8.6
|
5
|
+
# for safely escaping filenames to be passed directly to the shell.
|
6
|
+
##
|
7
|
+
module Shellwords #:nodoc:
|
8
|
+
def escape(str)
|
9
|
+
# An empty argument will be skipped, so return empty quotes.
|
10
|
+
return "''" if str.empty?
|
11
|
+
|
12
|
+
str = str.dup
|
13
|
+
|
14
|
+
# Process as a single byte sequence because not all shell
|
15
|
+
# implementations are multibyte aware.
|
16
|
+
str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1")
|
17
|
+
|
18
|
+
# A LF cannot be escaped with a backslash because a backslash + LF
|
19
|
+
# combo is regarded as line continuation and simply ignored.
|
20
|
+
str.gsub!(/\n/, "'\n'")
|
21
|
+
|
22
|
+
return str
|
23
|
+
end
|
24
|
+
|
25
|
+
module_function :escape
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Harbor
|
2
|
+
module Test
|
3
|
+
class Request < Harbor::Request
|
4
|
+
|
5
|
+
attr_accessor :session, :env, :params
|
6
|
+
|
7
|
+
def params
|
8
|
+
@params ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Rack::Response defines self.new(env, *args), which means we can't initialize
|
13
|
+
# a new request via a container without replacing this method.
|
14
|
+
##
|
15
|
+
def self.new(*args)
|
16
|
+
super(nil, {})
|
17
|
+
end
|
18
|
+
|
19
|
+
def session
|
20
|
+
@session || Session.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def session=(session)
|
24
|
+
@session = Session.new(session)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|