zuk-picnic 0.7.999.20090212
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/CHANGELOG.txt +1 -0
- data/History.txt +68 -0
- data/LICENSE.txt +165 -0
- data/Manifest.txt +29 -0
- data/README.txt +31 -0
- data/Rakefile +62 -0
- data/lib/picnic/authentication.rb +218 -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 +98 -0
- data/lib/picnic/service_control.rb +274 -0
- data/lib/picnic/version.rb +9 -0
- data/lib/picnic.rb +48 -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.20090212/CHANGELOG +118 -0
- data/vendor/camping-2.0.20090212/COPYING +18 -0
- data/vendor/camping-2.0.20090212/README +119 -0
- data/vendor/camping-2.0.20090212/Rakefile +174 -0
- data/vendor/camping-2.0.20090212/bin/camping +99 -0
- data/vendor/camping-2.0.20090212/doc/camping.1.gz +0 -0
- data/vendor/camping-2.0.20090212/examples/README +5 -0
- data/vendor/camping-2.0.20090212/examples/blog.rb +375 -0
- data/vendor/camping-2.0.20090212/examples/campsh.rb +629 -0
- data/vendor/camping-2.0.20090212/examples/tepee.rb +242 -0
- data/vendor/camping-2.0.20090212/extras/Camping.gif +0 -0
- data/vendor/camping-2.0.20090212/extras/flipbook_rdoc.rb +491 -0
- data/vendor/camping-2.0.20090212/extras/permalink.gif +0 -0
- data/vendor/camping-2.0.20090212/lib/camping/ar/session.rb +132 -0
- data/vendor/camping-2.0.20090212/lib/camping/ar.rb +78 -0
- data/vendor/camping-2.0.20090212/lib/camping/mab.rb +26 -0
- data/vendor/camping-2.0.20090212/lib/camping/reloader.rb +163 -0
- data/vendor/camping-2.0.20090212/lib/camping/server.rb +158 -0
- data/vendor/camping-2.0.20090212/lib/camping/session.rb +74 -0
- data/vendor/camping-2.0.20090212/lib/camping-unabridged.rb +638 -0
- data/vendor/camping-2.0.20090212/lib/camping.rb +54 -0
- data/vendor/camping-2.0.20090212/setup.rb +1551 -0
- data/vendor/camping-2.0.20090212/test/apps/env_debug.rb +65 -0
- data/vendor/camping-2.0.20090212/test/apps/forms.rb +95 -0
- data/vendor/camping-2.0.20090212/test/apps/misc.rb +86 -0
- data/vendor/camping-2.0.20090212/test/apps/sessions.rb +38 -0
- data/vendor/camping-2.0.20090212/test/test_camping.rb +54 -0
- metadata +128 -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_path => "/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($0))+"/../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
|
+
$: << $APP_ROOT+"/lib"
|
73
|
+
|
74
|
+
$PID_FILE = @options[:pid_file]
|
75
|
+
$CONFIG_FILE = @options[:conf_file]
|
76
|
+
$VERBOSE = @options[:verbose]
|
77
|
+
|
78
|
+
opts = OptionParser.new do |opts|
|
79
|
+
opts.banner = "Usage: #{File.basename($0)} app.rb"
|
80
|
+
opts.define_head "#{File.basename($0)}, the microframework ON-button for ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
|
81
|
+
opts.separator ""
|
82
|
+
opts.separator "Application options:"
|
83
|
+
|
84
|
+
opts.on("-d", "--daemonize", "Run daemonized in the background") do
|
85
|
+
$DAEMONIZE = true
|
86
|
+
end
|
87
|
+
opts.on("-c", "--config FILE", "Use this config file (default is /etc/<app>/config.yml)") do |c|
|
88
|
+
puts "Using config file #{c.inspect}"
|
89
|
+
$CONFIG_FILE = c
|
90
|
+
end
|
91
|
+
opts.on("-P", "--pid_file FILE", "Path to pid file (used only when running daemonized; default is /etc/<app>/<app>.pid)") do |p|
|
92
|
+
if $DAEMONIZE && !File.exists?(p)
|
93
|
+
puts "Using pid file #{p.inspect}"
|
94
|
+
$PID_FILE = p
|
95
|
+
elsif File.exists?(p)
|
96
|
+
puts "The pid file already exists. Is #{app} running?\n" +
|
97
|
+
"You will have to first manually remove the pid file at '#{p}' to start the server as a daemon."
|
98
|
+
exit 1
|
99
|
+
else
|
100
|
+
puts "Not running as daemon. Ignoring pid option"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# optoinal block with additonal opts.on() calls specific to your application
|
105
|
+
if @options[:extra_cli_options]
|
106
|
+
@options[:extra_cli_options].call(opts)
|
107
|
+
end
|
108
|
+
|
109
|
+
opts.separator ""
|
110
|
+
opts.separator "Picnic options:"
|
111
|
+
|
112
|
+
# No argument, shows at tail. This will print an options summary.
|
113
|
+
# Try it and see!
|
114
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
115
|
+
puts opts
|
116
|
+
exit
|
117
|
+
end
|
118
|
+
|
119
|
+
opts.on_tail("-v", "--version", "Show the application's version number") do
|
120
|
+
require "#{$APP_ROOT}/lib/#{app}/version.rb"
|
121
|
+
app_mod = Object.const_get(@options[:app_module])
|
122
|
+
puts "#{app}-#{app_mod::VERSION::STRING}"
|
123
|
+
exit
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
begin
|
128
|
+
opts.parse! ARGV
|
129
|
+
rescue OptionParser::ParseError => ex
|
130
|
+
STDERR.puts "!! #{ex.message}"
|
131
|
+
puts "** use `#{File.basename($0)} --help` for more details..."
|
132
|
+
exit 1
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
$CONF = Picnic::Conf.new
|
137
|
+
$CONF.load_from_file(app, $APP_ROOT, $CONF_FILE)
|
138
|
+
|
139
|
+
if $DAEMONIZE
|
140
|
+
# TODO: use Process.daemon when RUBY_VERSION >= 1.9
|
141
|
+
|
142
|
+
exit if fork
|
143
|
+
Process.setsid
|
144
|
+
exit if fork
|
145
|
+
|
146
|
+
Dir.chdir $APP_ROOT
|
147
|
+
File.umask 0000
|
148
|
+
|
149
|
+
STDIN.reopen "/dev/null"
|
150
|
+
STDOUT.reopen "/dev/null", "a"
|
151
|
+
STDERR.reopen "/dev/null", "a"
|
152
|
+
|
153
|
+
if $PID_FILE
|
154
|
+
File.open($PID_FILE, 'w'){ |f| f.write("#{Process.pid}") }
|
155
|
+
at_exit { File.delete($PID_FILE) if File.exist?($PID_FILE) }
|
156
|
+
end
|
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,98 @@
|
|
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::ContentLength.new(rapp)
|
24
|
+
rapp = FixContentLength.new(rapp)
|
25
|
+
rapp = Rack::Lint.new(rapp)
|
26
|
+
rapp = Camping::Server::XSendfile.new(rapp)
|
27
|
+
rapp = Rack::ShowExceptions.new(rapp)
|
28
|
+
|
29
|
+
handler.run(rapp, conf)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def prep_webrick
|
36
|
+
handler = Rack::Handler::WEBrick
|
37
|
+
options = {
|
38
|
+
:BindAddress => @conf.bind_address || "0.0.0.0",
|
39
|
+
:Port => @conf.port
|
40
|
+
}
|
41
|
+
|
42
|
+
cert_path = @conf.ssl_cert
|
43
|
+
key_path = @conf.ssl_key || @conf.ssl_cert
|
44
|
+
# look for the key in the ssl_cert if no ssl_key is specified
|
45
|
+
|
46
|
+
unless cert_path.nil? && key_path.nil?
|
47
|
+
raise "The specified certificate file #{cert_path.inspect} does not exist or is not readable. " +
|
48
|
+
" Your 'ssl_cert' configuration setting must be a path to a valid " +
|
49
|
+
" ssl certificate." unless
|
50
|
+
File.exists? cert_path
|
51
|
+
|
52
|
+
raise "The specified key file #{key_path.inspect} does not exist or is not readable. " +
|
53
|
+
" Your 'ssl_key' configuration setting must be a path to a valid " +
|
54
|
+
" ssl private key." unless
|
55
|
+
File.exists? key_path
|
56
|
+
|
57
|
+
require 'openssl'
|
58
|
+
require 'webrick/https'
|
59
|
+
|
60
|
+
cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
|
61
|
+
key = OpenSSL::PKey::RSA.new(File.read(key_path))
|
62
|
+
|
63
|
+
options[:SSLEnable] = true
|
64
|
+
options[:SSLVerifyClient] = ::OpenSSL::SSL::VERIFY_NONE
|
65
|
+
options[:SSLCertificate] = cert
|
66
|
+
options[:SSLPrivateKey] = key
|
67
|
+
end
|
68
|
+
|
69
|
+
return handler, options
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def prep_mongrel
|
74
|
+
handler = Rack::Handler::Mongrel
|
75
|
+
options = {
|
76
|
+
:Host => @conf.bind_address || "0.0.0.0",
|
77
|
+
:Port => @conf.port
|
78
|
+
}
|
79
|
+
|
80
|
+
return handler, options
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
class FixContentLength
|
86
|
+
def initialize(app)
|
87
|
+
@app = app
|
88
|
+
end
|
89
|
+
|
90
|
+
def call(env)
|
91
|
+
status, headers, body = @app.call(env)
|
92
|
+
if headers.has_key?('Content-Length') && headers['Content-Length'].to_i == 0
|
93
|
+
headers['Content-Length'] = body.body.length.to_s
|
94
|
+
end
|
95
|
+
[status, headers, body]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|