bmarzolf-picnic 0.8.0.20090420
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +1 -0
- data/History.txt +78 -0
- data/LICENSE.txt +165 -0
- data/Manifest.txt +45 -0
- data/README.txt +31 -0
- data/Rakefile +64 -0
- data/lib/picnic/authentication.rb +254 -0
- data/lib/picnic/cli.rb +165 -0
- data/lib/picnic/conf.rb +135 -0
- data/lib/picnic/controllers.rb +4 -0
- data/lib/picnic/logger.rb +41 -0
- data/lib/picnic/server.rb +99 -0
- data/lib/picnic/service_control.rb +274 -0
- data/lib/picnic/version.rb +9 -0
- data/lib/picnic.rb +11 -0
- data/setup.rb +1585 -0
- data/test/picnic_test.rb +11 -0
- data/test/test_helper.rb +2 -0
- data/vendor/camping-2.0.20090420/CHANGELOG +118 -0
- data/vendor/camping-2.0.20090420/COPYING +18 -0
- data/vendor/camping-2.0.20090420/README +82 -0
- data/vendor/camping-2.0.20090420/Rakefile +180 -0
- data/vendor/camping-2.0.20090420/bin/camping +97 -0
- data/vendor/camping-2.0.20090420/doc/camping.1.gz +0 -0
- data/vendor/camping-2.0.20090420/examples/README +5 -0
- data/vendor/camping-2.0.20090420/examples/blog.rb +375 -0
- data/vendor/camping-2.0.20090420/examples/campsh.rb +629 -0
- data/vendor/camping-2.0.20090420/examples/tepee.rb +242 -0
- data/vendor/camping-2.0.20090420/extras/Camping.gif +0 -0
- data/vendor/camping-2.0.20090420/extras/permalink.gif +0 -0
- data/vendor/camping-2.0.20090420/lib/camping/ar/session.rb +132 -0
- data/vendor/camping-2.0.20090420/lib/camping/ar.rb +78 -0
- data/vendor/camping-2.0.20090420/lib/camping/mab.rb +26 -0
- data/vendor/camping-2.0.20090420/lib/camping/reloader.rb +184 -0
- data/vendor/camping-2.0.20090420/lib/camping/server.rb +159 -0
- data/vendor/camping-2.0.20090420/lib/camping/session.rb +75 -0
- data/vendor/camping-2.0.20090420/lib/camping-unabridged.rb +630 -0
- data/vendor/camping-2.0.20090420/lib/camping.rb +52 -0
- data/vendor/camping-2.0.20090420/setup.rb +1551 -0
- data/vendor/camping-2.0.20090420/test/apps/env_debug.rb +65 -0
- data/vendor/camping-2.0.20090420/test/apps/forms.rb +95 -0
- data/vendor/camping-2.0.20090420/test/apps/misc.rb +86 -0
- data/vendor/camping-2.0.20090420/test/apps/sessions.rb +38 -0
- data/vendor/camping-2.0.20090420/test/test_camping.rb +54 -0
- metadata +140 -0
data/lib/picnic/cli.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
require 'picnic'
|
4
|
+
require 'picnic/conf'
|
5
|
+
require 'picnic/server'
|
6
|
+
|
7
|
+
module Picnic
|
8
|
+
# Provides a command-line interface for your app.
|
9
|
+
# This is useful for creating a 'bin' file for launching your application.
|
10
|
+
#
|
11
|
+
# Usage example (put this in a file called 'foo'):
|
12
|
+
#
|
13
|
+
# #!/usr/bin/env ruby
|
14
|
+
#
|
15
|
+
# require 'rubygems'
|
16
|
+
# require 'picnic'
|
17
|
+
#
|
18
|
+
# require 'picnic/cli'
|
19
|
+
#
|
20
|
+
# cli = Picnic::Cli.new(
|
21
|
+
# 'foo',
|
22
|
+
# :app_file => "/path/to/foo.br"
|
23
|
+
# )
|
24
|
+
#
|
25
|
+
# cli.handle_cli_input
|
26
|
+
#
|
27
|
+
# Also see the ServiceControl class for info on how to use your cli script
|
28
|
+
# as a service.
|
29
|
+
class Cli
|
30
|
+
attr_accessor :app, :options
|
31
|
+
|
32
|
+
# Creates a new command-line interface handler.
|
33
|
+
#
|
34
|
+
# +app+:: The name of the application. This should match the name of the
|
35
|
+
# binary, which by default is expected to be in the same directory
|
36
|
+
# as the service control script.
|
37
|
+
# +options+:: A hash overriding default options. The options are:
|
38
|
+
# +app_file+:: The path to your application's main Ruby file.
|
39
|
+
# By default this is expected to be <tt>../lib/<app>.rb</tt>
|
40
|
+
# +pid_file+:: Where the app's PID file (containing the app's
|
41
|
+
# process ID) should be placed. By default this is
|
42
|
+
# <tt>/etc/<app>/<app>.pid</tt>
|
43
|
+
def initialize(app, options = {})
|
44
|
+
@app = app
|
45
|
+
|
46
|
+
@options = options || {}
|
47
|
+
@options[:app_file] ||= File.expand_path(File.dirname(File.expand_path(__FILE__))+"/../lib/#{app}.rb")
|
48
|
+
@options[:app_name] ||= app
|
49
|
+
@options[:app_module] ||= app.capitalize
|
50
|
+
@options[:pid_file] ||= "/etc/#{app}/#{app}.pid"
|
51
|
+
@options[:conf_file] ||= nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Parses command line options given to the script.
|
55
|
+
def handle_cli_input
|
56
|
+
# if File.exists? options[:app_file]
|
57
|
+
# try to use given app base path
|
58
|
+
$APP_ROOT = File.dirname(options[:app_file]).gsub(/\/(lib|bin)\/?$/, '')
|
59
|
+
# else
|
60
|
+
# require 'rubygems'
|
61
|
+
#
|
62
|
+
# # fall back to using gem installation
|
63
|
+
# matches = Gem::source_index.find_name(app)
|
64
|
+
# raise LoadError, "#{app} gem doesn't appear to be installed!" if matches.empty?
|
65
|
+
#
|
66
|
+
# gem_spec = matches.last
|
67
|
+
# $APP_ROOT = gem_spec.full_gem_path
|
68
|
+
#
|
69
|
+
# gem(app)
|
70
|
+
# end
|
71
|
+
|
72
|
+
unless File.file?(options[:app_file])
|
73
|
+
raise ArgumentError, "options[:app_file] points to #{options[:app_file].inspect} but this does not appear to be a valid Camping application!}"
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
puts "Loading #{app.inspect} code from #{$APP_ROOT.inspect}..."
|
78
|
+
|
79
|
+
$: << $APP_ROOT+"/lib"
|
80
|
+
|
81
|
+
$PID_FILE = @options[:pid_file]
|
82
|
+
$CONFIG_FILE = @options[:conf_file]
|
83
|
+
$VERBOSE = @options[:verbose]
|
84
|
+
|
85
|
+
opts = OptionParser.new do |opts|
|
86
|
+
#opts.banner = ""
|
87
|
+
#opts.define_head ""
|
88
|
+
#opts.separator ""
|
89
|
+
opts.on("-d", "--daemonize", "Run daemonized in the background") do
|
90
|
+
$DAEMONIZE = true
|
91
|
+
end
|
92
|
+
opts.on("-c", "--config FILE", "Use this config file (default is /etc/<app>/config.yml)") do |c|
|
93
|
+
puts "Using config file #{c.inspect}"
|
94
|
+
$CONFIG_FILE = c
|
95
|
+
end
|
96
|
+
opts.on("-P", "--pid_file FILE", "Path to pid file (used only when running daemonized; default is /etc/<app>/<app>.pid)") do |p|
|
97
|
+
if $DAEMONIZE && !File.exists?(p)
|
98
|
+
puts "Using pid file #{p.inspect}"
|
99
|
+
$PID_FILE = p
|
100
|
+
elsif File.exists?(p)
|
101
|
+
puts "The pid file already exists. Is #{app} running?\n" +
|
102
|
+
"You will have to first manually remove the pid file at '#{p}' to start the server as a daemon."
|
103
|
+
exit 1
|
104
|
+
else
|
105
|
+
puts "Not running as daemon. Ignoring pid option"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# optoinal block with additonal opts.on() calls specific to your application
|
110
|
+
if @options[:extra_cli_options]
|
111
|
+
@options[:extra_cli_options].call(opts)
|
112
|
+
end
|
113
|
+
|
114
|
+
# No argument, shows at tail. This will print an options summary.
|
115
|
+
# Try it and see!
|
116
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
117
|
+
puts opts
|
118
|
+
exit
|
119
|
+
end
|
120
|
+
|
121
|
+
opts.on_tail("-v", "--version", "Show the application's version number") do
|
122
|
+
require "#{$APP_ROOT}/lib/#{app}/version.rb"
|
123
|
+
app_mod = Object.const_get(@options[:app_module])
|
124
|
+
puts "#{app}-#{app_mod::VERSION::STRING}"
|
125
|
+
exit
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
begin
|
130
|
+
opts.parse! ARGV
|
131
|
+
rescue OptionParser::ParseError => ex
|
132
|
+
STDERR.puts "!! #{ex.message}"
|
133
|
+
puts "** use `#{File.basename($0)} --help` for more details..."
|
134
|
+
exit 1
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
$CONF = Picnic::Conf.new
|
139
|
+
$CONF.load_from_file(app, $APP_ROOT, $CONF_FILE)
|
140
|
+
|
141
|
+
if $DAEMONIZE
|
142
|
+
# TODO: use Process.daemon when RUBY_VERSION >= 1.9
|
143
|
+
|
144
|
+
exit if fork
|
145
|
+
Process.setsid
|
146
|
+
exit if fork
|
147
|
+
|
148
|
+
Dir.chdir $APP_ROOT
|
149
|
+
File.umask 0000
|
150
|
+
|
151
|
+
STDIN.reopen $CONF.log[:file], "a"
|
152
|
+
STDOUT.reopen $CONF.log[:file], "a"
|
153
|
+
STDERR.reopen $CONF.log[:file], "a"
|
154
|
+
|
155
|
+
File.open($PID_FILE, 'w'){ |f| f.write("#{Process.pid}") }
|
156
|
+
at_exit { File.delete($PID_FILE) if File.exist?($PID_FILE) }
|
157
|
+
end
|
158
|
+
|
159
|
+
server = Picnic::Server::Base.new($CONF, [options[:app_file]])
|
160
|
+
server.start
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
|
data/lib/picnic/conf.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
module Picnic
|
4
|
+
# Provides an interface for accessing your Picnic app's configuration file.
|
5
|
+
#
|
6
|
+
# Usage example:
|
7
|
+
#
|
8
|
+
# # Load the configuration from /etc/foo/config.yml
|
9
|
+
# Conf.load('foo')
|
10
|
+
#
|
11
|
+
# # The contents of config.yml is now available as follows:
|
12
|
+
# puts Conf[:server]
|
13
|
+
# puts Conf[:authentication][:username]
|
14
|
+
# # ... etc.
|
15
|
+
class Conf
|
16
|
+
def initialize(from_hash = {})
|
17
|
+
@conf = HashWithIndifferentAccess.new(from_hash)
|
18
|
+
|
19
|
+
@conf[:log] ||= HashWithIndifferentAccess.new
|
20
|
+
@conf[:log].merge!(:file => STDOUT, :level => 'DEBUG')
|
21
|
+
|
22
|
+
@conf[:uri_path] ||= "/"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Read a configuration option.
|
26
|
+
#
|
27
|
+
# For example:
|
28
|
+
# puts Conf[:server]
|
29
|
+
def [](key)
|
30
|
+
@conf[key]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Another way of reading a configuration option.
|
34
|
+
#
|
35
|
+
# The following statements are equivalent:
|
36
|
+
# puts Conf[:server]
|
37
|
+
# puts Conf.server
|
38
|
+
def method_missing(method, *args)
|
39
|
+
self[method]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Needs to be defined when we have a custom method_missing().
|
43
|
+
def respond_to?(method)
|
44
|
+
(@conf.stringify_keys.keys).include?(method.to_s) || super
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the path to your application's example config file.
|
48
|
+
#
|
49
|
+
# The example config file should be in the root directory of
|
50
|
+
# your application's distribution package and should be called
|
51
|
+
# <tt>config.example.yml</tt>. This file is used as a template
|
52
|
+
# for your app's configuration, to be customized by the end
|
53
|
+
# user.
|
54
|
+
def example_config_file_path(app_root)
|
55
|
+
"#{app_root}/config.example.yml"
|
56
|
+
end
|
57
|
+
|
58
|
+
# Copies the example config file into the appropriate
|
59
|
+
# configuration directory.
|
60
|
+
#
|
61
|
+
# +app_name+:: The name of your application. For example: <tt>foo</tt>
|
62
|
+
# +app_root+:: The path to your application's root directory. For example: <tt>/srv/www/camping/foo/</tt>
|
63
|
+
# +dest_conf_file:: The path where the example conf file should be copied to.
|
64
|
+
# For example: <tt>/etc/foo/config.yml</tt>
|
65
|
+
def copy_example_config_file(app_name, app_root, dest_conf_file)
|
66
|
+
require 'fileutils'
|
67
|
+
|
68
|
+
example_conf_file = example_config_file_path(app_root)
|
69
|
+
|
70
|
+
puts "\n#{app_name.to_s.upcase} SERVER HAS NOT YET BEEN CONFIGURED!!!\n"
|
71
|
+
puts "\nAttempting to copy sample configuration from '#{example_conf_file}' to '#{dest_conf_file}'...\n"
|
72
|
+
|
73
|
+
unless File.exists? example_conf_file
|
74
|
+
puts "\nThe example conf file does not exist! The author of #{app_name} may have forgotten to include it. You'll have to create the config file manually.\n"
|
75
|
+
exit 2
|
76
|
+
end
|
77
|
+
|
78
|
+
begin
|
79
|
+
dest_conf_file_dir = File.dirname(dest_conf_file)
|
80
|
+
FileUtils.mkpath(dest_conf_file_dir) unless File.exists? dest_conf_file_dir
|
81
|
+
FileUtils.cp(example_conf_file, dest_conf_file)
|
82
|
+
rescue Errno::EACCES
|
83
|
+
puts "\nIt appears that you do not have permissions to create the '#{dest_conf_file}' file. Try running this command using sudo (as root).\n"
|
84
|
+
exit 2
|
85
|
+
rescue => e
|
86
|
+
puts "\nFor some reason the '#{dest_conf_file}' file could not be created (#{e})."
|
87
|
+
puts "You'll have to copy the file manually. Use '#{example_conf_file}' as a template.\n"
|
88
|
+
exit 2
|
89
|
+
end
|
90
|
+
|
91
|
+
puts "\nA sample configuration has been created for you in '#{dest_conf_file}'. Please edit this file to" +
|
92
|
+
" suit your needs and then run #{app_name} again.\n"
|
93
|
+
exit 1
|
94
|
+
end
|
95
|
+
|
96
|
+
# Loads the configuration from the YAML file for the given app.
|
97
|
+
#
|
98
|
+
# <tt>app_name</tt> should be the name of your app; for example: <tt>foo</tt>
|
99
|
+
# <tt>app_root</tt> should be the path to your application's root directory; for example:: <tt>/srv/www/camping/foo/</tt>
|
100
|
+
# [<tt>config_file</tt>] can be the path to an alternate location for the config file to load
|
101
|
+
#
|
102
|
+
# By default, the configuration will be loaded from <tt>/etc/<app_name>/config.yml</tt>.
|
103
|
+
def load_from_file(app_name, app_root, config_file = nil)
|
104
|
+
conf_file = config_file || "/etc/#{app_name.to_s.downcase}/config.yml"
|
105
|
+
|
106
|
+
puts "Loading configuration for #{app_name.inspect} from #{conf_file.inspect}..."
|
107
|
+
|
108
|
+
begin
|
109
|
+
conf_file = etc_conf = conf_file
|
110
|
+
unless File.exists? conf_file
|
111
|
+
# can use local config.yml file in case we're running non-gem installation
|
112
|
+
conf_file = "#{app_root}/config.yml"
|
113
|
+
end
|
114
|
+
|
115
|
+
unless File.exists? conf_file
|
116
|
+
copy_example_config_file(app_name, app_root, etc_conf)
|
117
|
+
end
|
118
|
+
|
119
|
+
loaded_conf = HashWithIndifferentAccess.new(YAML.load_file(conf_file))
|
120
|
+
|
121
|
+
@conf.merge!(loaded_conf)
|
122
|
+
|
123
|
+
rescue => e
|
124
|
+
raise "Your #{app_name} configuration may be invalid."+
|
125
|
+
" Please double-check check your config.yml file."+
|
126
|
+
" Make sure that you are using spaces instead of tabs for your indentation!!" +
|
127
|
+
"\n\nTHE UNDERLYING ERROR WAS:\n#{e.inspect}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def merge_defaults(defaults)
|
132
|
+
@conf = HashWithIndifferentAccess.new(HashWithIndifferentAccess.new(defaults).merge(@conf))
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Picnic
|
4
|
+
module Logger
|
5
|
+
|
6
|
+
# Makes available a Logger instance under the global $LOG variable.
|
7
|
+
def init_global_logger!
|
8
|
+
logdev = ($CONF && $CONF.log[:file]) || STDOUT
|
9
|
+
$LOG = Picnic::Logger::Base.new(logdev)
|
10
|
+
$LOG.level = Picnic::Logger::Base.const_get(($CONF && $CONF.log[:level]) || 'DEBUG')
|
11
|
+
|
12
|
+
puts "Initialized global logger to #{logdev.inspect}."
|
13
|
+
end
|
14
|
+
module_function :init_global_logger!
|
15
|
+
|
16
|
+
class Base < ::Logger
|
17
|
+
def initialize(logdev, shift_age = 0, shift_size = 1048576)
|
18
|
+
begin
|
19
|
+
super
|
20
|
+
rescue Exception
|
21
|
+
puts "WARNING: Couldn't create Logger with output '#{logdev}'. Logger output will be redirected to STDOUT."
|
22
|
+
super(STDOUT, shift_age, shift_size)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def format_message(severity, datetime, progrname, msg)
|
27
|
+
(@formatter || @default_formatter).call(severity, datetime, progname, msg)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Custom log formatter used by the Picnic Logger.
|
32
|
+
class Formatter < ::Logger::Formatter
|
33
|
+
Format = "[%s#%d] %5s -- %s: %s\n"
|
34
|
+
|
35
|
+
def call(severity, time, progname, msg)
|
36
|
+
Format % [format_datetime(time), $$, severity, progname,
|
37
|
+
msg2str(msg)]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'camping/server'
|
2
|
+
|
3
|
+
module Picnic::Server
|
4
|
+
class Base < Camping::Server::Base
|
5
|
+
def start
|
6
|
+
handler, conf = case @conf.server
|
7
|
+
when "console"
|
8
|
+
ARGV.clear
|
9
|
+
IRB.start
|
10
|
+
exit
|
11
|
+
when "mongrel"
|
12
|
+
prep_mongrel
|
13
|
+
when "webrick"
|
14
|
+
prep_webrick
|
15
|
+
end
|
16
|
+
|
17
|
+
rapp = apps.first
|
18
|
+
|
19
|
+
if @conf.uri_path
|
20
|
+
rapp = Rack::URLMap.new(@conf.uri_path => rapp)
|
21
|
+
end
|
22
|
+
|
23
|
+
rapp = Rack::Static.new(rapp, @conf[:static]) if @conf[:static]
|
24
|
+
rapp = Rack::ContentLength.new(rapp)
|
25
|
+
rapp = FixContentLength.new(rapp)
|
26
|
+
rapp = Rack::Lint.new(rapp)
|
27
|
+
rapp = Camping::Server::XSendfile.new(rapp)
|
28
|
+
rapp = Rack::ShowExceptions.new(rapp)
|
29
|
+
|
30
|
+
handler.run(rapp, conf)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def prep_webrick
|
37
|
+
handler = Rack::Handler::WEBrick
|
38
|
+
options = {
|
39
|
+
:BindAddress => @conf.bind_address || "0.0.0.0",
|
40
|
+
:Port => @conf.port
|
41
|
+
}
|
42
|
+
|
43
|
+
cert_path = @conf.ssl_cert
|
44
|
+
key_path = @conf.ssl_key || @conf.ssl_cert
|
45
|
+
# look for the key in the ssl_cert if no ssl_key is specified
|
46
|
+
|
47
|
+
unless cert_path.nil? && key_path.nil?
|
48
|
+
raise "The specified certificate file #{cert_path.inspect} does not exist or is not readable. " +
|
49
|
+
" Your 'ssl_cert' configuration setting must be a path to a valid " +
|
50
|
+
" ssl certificate." unless
|
51
|
+
File.exists? cert_path
|
52
|
+
|
53
|
+
raise "The specified key file #{key_path.inspect} does not exist or is not readable. " +
|
54
|
+
" Your 'ssl_key' configuration setting must be a path to a valid " +
|
55
|
+
" ssl private key." unless
|
56
|
+
File.exists? key_path
|
57
|
+
|
58
|
+
require 'openssl'
|
59
|
+
require 'webrick/https'
|
60
|
+
|
61
|
+
cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
|
62
|
+
key = OpenSSL::PKey::RSA.new(File.read(key_path))
|
63
|
+
|
64
|
+
options[:SSLEnable] = true
|
65
|
+
options[:SSLVerifyClient] = ::OpenSSL::SSL::VERIFY_NONE
|
66
|
+
options[:SSLCertificate] = cert
|
67
|
+
options[:SSLPrivateKey] = key
|
68
|
+
end
|
69
|
+
|
70
|
+
return handler, options
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def prep_mongrel
|
75
|
+
handler = Rack::Handler::Mongrel
|
76
|
+
options = {
|
77
|
+
:Host => @conf.bind_address || "0.0.0.0",
|
78
|
+
:Port => @conf.port
|
79
|
+
}
|
80
|
+
|
81
|
+
return handler, options
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
class FixContentLength
|
87
|
+
def initialize(app)
|
88
|
+
@app = app
|
89
|
+
end
|
90
|
+
|
91
|
+
def call(env)
|
92
|
+
status, headers, body = @app.call(env)
|
93
|
+
if headers.has_key?('Content-Length') && headers['Content-Length'].to_i == 0
|
94
|
+
headers['Content-Length'] = body.body.length.to_s
|
95
|
+
end
|
96
|
+
[status, headers, body]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|