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,100 @@
|
|
1
|
+
module Harbor
|
2
|
+
class FileStore
|
3
|
+
class File
|
4
|
+
|
5
|
+
attr_accessor :store, :path
|
6
|
+
|
7
|
+
def initialize(store, path)
|
8
|
+
@store = store
|
9
|
+
@path = path
|
10
|
+
|
11
|
+
@pending_writes = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def copy_on_write
|
15
|
+
return @copy_on_write if @copy_on_write
|
16
|
+
|
17
|
+
@copy_on_write = []
|
18
|
+
if store.options[:copy_on_write]
|
19
|
+
store.options[:copy_on_write].each do |name|
|
20
|
+
@copy_on_write << Harbor::FileStore[name].get(@path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@copy_on_write
|
25
|
+
end
|
26
|
+
|
27
|
+
def copy_on_read
|
28
|
+
return @copy_on_read if @copy_on_read
|
29
|
+
|
30
|
+
@copy_on_read = []
|
31
|
+
if store.options[:copy_on_read]
|
32
|
+
store.options[:copy_on_read].each do |name|
|
33
|
+
@copy_on_read << Harbor::FileStore[name].get(@path)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
@copy_on_read
|
38
|
+
end
|
39
|
+
|
40
|
+
def write(data)
|
41
|
+
open("wb")
|
42
|
+
|
43
|
+
copy_on_write.each do |file|
|
44
|
+
if store.options[:async_copy]
|
45
|
+
@pending_writes << lambda { file.write(data) }
|
46
|
+
else
|
47
|
+
file.write(data)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if data
|
52
|
+
@stream.write(data)
|
53
|
+
else
|
54
|
+
@stream.close
|
55
|
+
@stream = nil
|
56
|
+
|
57
|
+
Thread.new {
|
58
|
+
@pending_writes.each { |write| write.call }
|
59
|
+
@pending_writes = []
|
60
|
+
} if store.options[:async_copy]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def read(bytes = nil)
|
65
|
+
open("r")
|
66
|
+
|
67
|
+
data = @stream.read(bytes)
|
68
|
+
|
69
|
+
copy_on_read.each { |file| file.write(data) }
|
70
|
+
|
71
|
+
unless bytes && data
|
72
|
+
@stream.close
|
73
|
+
@stream = nil
|
74
|
+
end
|
75
|
+
|
76
|
+
data
|
77
|
+
end
|
78
|
+
|
79
|
+
def size
|
80
|
+
store.size(path)
|
81
|
+
end
|
82
|
+
|
83
|
+
def open(mode = "r")
|
84
|
+
@stream ||= store.open(path, mode)
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Returns the full path to this file
|
89
|
+
##
|
90
|
+
def absolute_path
|
91
|
+
(@store.root + @path).to_s
|
92
|
+
end
|
93
|
+
|
94
|
+
def exists?
|
95
|
+
store.exists?(path)
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Harbor
|
2
|
+
class FileStore
|
3
|
+
class Local < Harbor::FileStore
|
4
|
+
|
5
|
+
attr_accessor :root, :options
|
6
|
+
|
7
|
+
def initialize(path, options = {})
|
8
|
+
path = "#{path}/" unless path =~ /.*\/$/
|
9
|
+
@root = Pathname(path)
|
10
|
+
|
11
|
+
@options = options
|
12
|
+
|
13
|
+
::FileUtils.mkdir_p(path.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(path)
|
17
|
+
path = strip_leading_slash(path)
|
18
|
+
Harbor::FileStore::File.new(self, path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def put(path, absolute_path)
|
22
|
+
raise ArgumentError.new("Harbor::FileStore::Local#put[absolute_path] should be a path but was an IO") if absolute_path.is_a?(IO)
|
23
|
+
path = strip_leading_slash(path)
|
24
|
+
file = Harbor::FileStore::File.new(self, path)
|
25
|
+
|
26
|
+
::FileUtils.mkdir_p((@root + path).parent.to_s) unless (@root + path).parent.exist?
|
27
|
+
|
28
|
+
::FileUtils::cp(absolute_path, file.absolute_path)
|
29
|
+
file
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete(path)
|
33
|
+
path = strip_leading_slash(path)
|
34
|
+
::FileUtils.rm(@root + path)
|
35
|
+
Harbor::File.rmdir_p((@root + path).parent.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
def exists?(path)
|
39
|
+
path = strip_leading_slash(path)
|
40
|
+
(@root + path).exist?
|
41
|
+
end
|
42
|
+
|
43
|
+
def open(path, mode = "r", &block)
|
44
|
+
path = strip_leading_slash(path)
|
45
|
+
::FileUtils.mkdir_p((@root + path).parent.to_s) unless (@root + path).parent.exist?
|
46
|
+
::File.open(@root + path, mode, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def size(path)
|
50
|
+
path = strip_leading_slash(path)
|
51
|
+
::File.size(@root + path)
|
52
|
+
end
|
53
|
+
|
54
|
+
def local?
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def __size__
|
61
|
+
`du -sk #{Shellwords.escape(@root.to_s)} | awk '{ print $1; }'`.chomp.to_i * 1024
|
62
|
+
end
|
63
|
+
|
64
|
+
def strip_leading_slash(path)
|
65
|
+
path = path[1..path.size - 1] if path =~ /^\//
|
66
|
+
path
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require "vendor/cloudfiles-1.3.0/cloudfiles"
|
2
|
+
|
3
|
+
module Harbor
|
4
|
+
class FileStore
|
5
|
+
class Mosso < Harbor::FileStore
|
6
|
+
|
7
|
+
attr_accessor :container, :options
|
8
|
+
|
9
|
+
def initialize(username, api_key, container_name, options = {})
|
10
|
+
@username = username
|
11
|
+
@api_key = api_key
|
12
|
+
@container_name = container_name
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(path)
|
17
|
+
path = strip_leading_slash(path)
|
18
|
+
Harbor::FileStore::File.new(self, path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def put(filename, file)
|
22
|
+
filename = strip_leading_slash(filename)
|
23
|
+
url = container.connection.storagehost + container.connection.storagepath + "/#{container.name}/#{filename}"
|
24
|
+
token = container.connection.authtoken
|
25
|
+
|
26
|
+
path = nil
|
27
|
+
|
28
|
+
if file.is_a?(::File)
|
29
|
+
path = file.path
|
30
|
+
elsif file.is_a?(Harbor::FileStore::File) && file.store.local?
|
31
|
+
path = file.store.path + file.path
|
32
|
+
end
|
33
|
+
|
34
|
+
make_path(::File.dirname(path))
|
35
|
+
|
36
|
+
command = <<-CMD
|
37
|
+
curl -X "PUT" \\
|
38
|
+
-T #{path ? Shellwords.escape(path.to_s) : "-"} \\
|
39
|
+
-H "X-Auth-Token: #{token}" \\
|
40
|
+
-H "Content-Type: text/plain" \\
|
41
|
+
https://#{url}
|
42
|
+
CMD
|
43
|
+
|
44
|
+
if path
|
45
|
+
system(command)
|
46
|
+
else
|
47
|
+
IO::popen(command, "w") do |session|
|
48
|
+
case file
|
49
|
+
when ::File
|
50
|
+
while data = file.read(500_000)
|
51
|
+
session.write(data)
|
52
|
+
end
|
53
|
+
when Harbor::FileStore::File
|
54
|
+
file.read do |block|
|
55
|
+
session.write(block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete(filename)
|
63
|
+
filename = strip_leading_slash(filename)
|
64
|
+
container.delete_object(filename)
|
65
|
+
end
|
66
|
+
|
67
|
+
def exists?(filename)
|
68
|
+
filename = strip_leading_slash(filename)
|
69
|
+
container.object_exists?(filename)
|
70
|
+
end
|
71
|
+
|
72
|
+
def open(filename, mode = "r", &block)
|
73
|
+
filename = strip_leading_slash(filename)
|
74
|
+
url = container.connection.storagehost + container.connection.storagepath + "/#{container.name}/#{filename}"
|
75
|
+
token = container.connection.authtoken
|
76
|
+
|
77
|
+
if mode == "r"
|
78
|
+
command = <<-CMD
|
79
|
+
curl -s -X "GET" \\
|
80
|
+
-D - \\
|
81
|
+
-H "X-Auth-Token: #{token}" \\
|
82
|
+
https://#{url}
|
83
|
+
CMD
|
84
|
+
|
85
|
+
stream = IO::popen(command, "r")
|
86
|
+
|
87
|
+
headers = []
|
88
|
+
|
89
|
+
while line = stream.gets
|
90
|
+
break if line == "\r\n"
|
91
|
+
headers << line
|
92
|
+
end
|
93
|
+
elsif mode =~ /w/
|
94
|
+
make_path(::File.dirname(filename))
|
95
|
+
|
96
|
+
command = <<-CMD
|
97
|
+
curl -X "PUT" \\
|
98
|
+
-T #{"-"} \\
|
99
|
+
-H "X-Auth-Token: #{token}" \\
|
100
|
+
-H "Content-Type: text/plain" \\
|
101
|
+
https://#{url}
|
102
|
+
CMD
|
103
|
+
|
104
|
+
stream = IO::popen(command, "w")
|
105
|
+
end
|
106
|
+
|
107
|
+
if block_given?
|
108
|
+
yield stream
|
109
|
+
stream.close
|
110
|
+
else
|
111
|
+
stream
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def size(filename)
|
116
|
+
filename = strip_leading_slash(filename)
|
117
|
+
container.object(filename).bytes.to_i
|
118
|
+
end
|
119
|
+
|
120
|
+
def container
|
121
|
+
@container ||= connect!
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def connect!
|
127
|
+
@connection = CloudFiles::Connection.new(@username, @api_key, true)
|
128
|
+
@container = @connection.container(@container_name)
|
129
|
+
end
|
130
|
+
|
131
|
+
def connected?
|
132
|
+
@connection && @connection.authok?
|
133
|
+
end
|
134
|
+
|
135
|
+
def make_path(path)
|
136
|
+
if path == "." || path == "/"
|
137
|
+
return
|
138
|
+
else
|
139
|
+
unless @container.object_exists?(path)
|
140
|
+
object = @container.create_object(path)
|
141
|
+
object.write(nil, { 'Content-Type' => 'application/directory'} )
|
142
|
+
end
|
143
|
+
make_path(::File.dirname(path))
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def strip_leading_slash(path)
|
148
|
+
path = path[1..path.size - 1] if path =~ /^\//
|
149
|
+
path
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'erubis'
|
3
|
+
|
4
|
+
module Harbor
|
5
|
+
|
6
|
+
class Generator
|
7
|
+
|
8
|
+
class GeneratorError < StandardError; end
|
9
|
+
class UnknownCommandError < GeneratorError; end
|
10
|
+
class GeneratorArgumentError < GeneratorError; end
|
11
|
+
|
12
|
+
attr_accessor :app, :command, :klass, :description, :help
|
13
|
+
|
14
|
+
def initialize(app, command, klass, description, help)
|
15
|
+
@app, @command, @klass, @description, @help = app, command, klass, description, help
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.run(app, command, options = [])
|
19
|
+
executor = if generator = @@generators["#{app}:#{command}"]
|
20
|
+
generator.klass.new(options)
|
21
|
+
else
|
22
|
+
puts usage(app)
|
23
|
+
|
24
|
+
exit(1)
|
25
|
+
end
|
26
|
+
|
27
|
+
executor.run
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.usage(app)
|
31
|
+
usage = []
|
32
|
+
usage << "Usage #{app} command [options]"
|
33
|
+
usage << ""
|
34
|
+
usage << "Available commands:"
|
35
|
+
|
36
|
+
@@generators.select { |key,| key =~ /^#{app}/ }.sort.each do |key, generator|
|
37
|
+
usage << "%12s %s" % [generator.command, generator.description]
|
38
|
+
end
|
39
|
+
|
40
|
+
usage
|
41
|
+
end
|
42
|
+
|
43
|
+
@@generators = {}
|
44
|
+
def self.register(app, command, klass, description = "", help = "")
|
45
|
+
@@generators["#{app}:#{command}"] = new(app, command, klass, description, help)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.generators
|
49
|
+
@@generators
|
50
|
+
end
|
51
|
+
|
52
|
+
require Pathname(__FILE__).dirname + "generator/help"
|
53
|
+
require Pathname(__FILE__).dirname + "generator/setup"
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Harbor
|
2
|
+
class Generator
|
3
|
+
|
4
|
+
class HelpCommand
|
5
|
+
Harbor::Generator.register('harbor', 'help', self)
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@command = options.first
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
if @command
|
13
|
+
generator = Harbor::Generator.generators["harbor:#{@command}"]
|
14
|
+
|
15
|
+
if !generator
|
16
|
+
puts "Command '#{@command}' does not exist."
|
17
|
+
else
|
18
|
+
if generator.help.empty?
|
19
|
+
puts "No help available for '#{@command}'."
|
20
|
+
else
|
21
|
+
puts generator.help
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
exit(1)
|
26
|
+
else
|
27
|
+
puts Harbor::Generator.usage('harbor')
|
28
|
+
exit(1)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module Harbor
|
4
|
+
class Generator
|
5
|
+
class SetupCommand
|
6
|
+
HELP = <<-HELP
|
7
|
+
Usage: harbor setup app_name
|
8
|
+
|
9
|
+
Sets up a new Harbor port using the conventional directory
|
10
|
+
structure. The application can be booted using your server
|
11
|
+
of choice:
|
12
|
+
|
13
|
+
> rackup # http://localhost:9292/
|
14
|
+
> unicorn # http://localhost:8080/
|
15
|
+
> thin -R config.ru start # http://localhost:3000/
|
16
|
+
|
17
|
+
HELP
|
18
|
+
|
19
|
+
Harbor::Generator.register('harbor', 'setup', self, "Sets up a new harbor project.", HELP)
|
20
|
+
|
21
|
+
attr_reader :app_name
|
22
|
+
|
23
|
+
def initialize(options)
|
24
|
+
@app_name = options.first
|
25
|
+
|
26
|
+
unless @app_name
|
27
|
+
puts HELP
|
28
|
+
exit(1)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def log(action, message)
|
33
|
+
puts "%12s %s" % [action, message]
|
34
|
+
end
|
35
|
+
|
36
|
+
def run
|
37
|
+
unless File.exists?(@app_name)
|
38
|
+
log("create", @app_name)
|
39
|
+
`mkdir #{@app_name}`
|
40
|
+
end
|
41
|
+
|
42
|
+
skeleton_path = Pathname(__FILE__).dirname + 'skeletons/basic'
|
43
|
+
Dir[skeleton_path + "**/*"].each do |item|
|
44
|
+
item = Pathname(item)
|
45
|
+
relative_path = "#{@app_name}/#{item.relative_path_from(skeleton_path)}"
|
46
|
+
relative_path.sub!("appname", @app_name)
|
47
|
+
|
48
|
+
if File.exists?(relative_path.sub(/\.skel$/, ""))
|
49
|
+
log("exists", relative_path.sub(/\.skel$/, ""))
|
50
|
+
next
|
51
|
+
end
|
52
|
+
|
53
|
+
if item.directory?
|
54
|
+
log("create", relative_path)
|
55
|
+
|
56
|
+
`mkdir #{relative_path}`
|
57
|
+
elsif relative_path.sub!(/\.skel$/, "")
|
58
|
+
log("create", relative_path)
|
59
|
+
|
60
|
+
::File.open(relative_path, 'w') do |file|
|
61
|
+
file.puts Erubis::FastEruby.new(::File.read(item), :pattern => '##> <##').evaluate(self)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
log("create", relative_path)
|
65
|
+
|
66
|
+
FileUtils.cp(item, relative_path)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
`chmod +x #{@app_name}/config.ru`
|
72
|
+
end
|
73
|
+
|
74
|
+
def app_class
|
75
|
+
# 'sample' => 'Sample'
|
76
|
+
# 'cool_app' => 'CoolApp'
|
77
|
+
@app_name.gsub(/(^|-|_)[a-z0-9]{1}/) { |m| m.sub(/-|_/, '').upcase }
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|