dyoder-waves 0.7.3
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/app/Rakefile +14 -0
- data/app/bin/waves-console +3 -0
- data/app/bin/waves-server +3 -0
- data/app/configurations/development.rb.erb +31 -0
- data/app/configurations/mapping.rb.erb +14 -0
- data/app/configurations/production.rb.erb +30 -0
- data/app/lib/application.rb.erb +3 -0
- data/app/startup.rb +5 -0
- data/app/templates/errors/not_found_404.mab +2 -0
- data/app/templates/errors/server_error_500.mab +2 -0
- data/app/templates/layouts/default.mab +14 -0
- data/bin/waves +66 -0
- data/bin/waves-console +4 -0
- data/bin/waves-server +4 -0
- data/lib/commands/waves-console.rb +24 -0
- data/lib/commands/waves-server.rb +55 -0
- data/lib/controllers/mixin.rb +158 -0
- data/lib/dispatchers/base.rb +52 -0
- data/lib/dispatchers/default.rb +67 -0
- data/lib/foundations/default.rb +28 -0
- data/lib/foundations/simple.rb +17 -0
- data/lib/helpers/common.rb +62 -0
- data/lib/helpers/form.rb +39 -0
- data/lib/helpers/formatting.rb +30 -0
- data/lib/helpers/model.rb +33 -0
- data/lib/helpers/view.rb +24 -0
- data/lib/layers/default_errors.rb +24 -0
- data/lib/layers/simple_errors.rb +17 -0
- data/lib/mapping/mapping.rb +252 -0
- data/lib/mapping/pretty_urls.rb +94 -0
- data/lib/renderers/erubis.rb +61 -0
- data/lib/renderers/markaby.rb +33 -0
- data/lib/renderers/mixin.rb +53 -0
- data/lib/runtime/application.rb +65 -0
- data/lib/runtime/configuration.rb +180 -0
- data/lib/runtime/console.rb +20 -0
- data/lib/runtime/debugger.rb +9 -0
- data/lib/runtime/logger.rb +52 -0
- data/lib/runtime/mime_types.rb +22 -0
- data/lib/runtime/request.rb +77 -0
- data/lib/runtime/response.rb +40 -0
- data/lib/runtime/response_mixin.rb +35 -0
- data/lib/runtime/response_proxy.rb +27 -0
- data/lib/runtime/server.rb +94 -0
- data/lib/runtime/session.rb +56 -0
- data/lib/tasks/cluster.rb +25 -0
- data/lib/tasks/gem.rb +31 -0
- data/lib/tasks/generate.rb +15 -0
- data/lib/utilities/inflect.rb +194 -0
- data/lib/utilities/integer.rb +17 -0
- data/lib/utilities/kernel.rb +34 -0
- data/lib/utilities/module.rb +17 -0
- data/lib/utilities/object.rb +17 -0
- data/lib/utilities/proc.rb +9 -0
- data/lib/utilities/string.rb +47 -0
- data/lib/utilities/symbol.rb +7 -0
- data/lib/verify/mapping.rb +29 -0
- data/lib/verify/request.rb +40 -0
- data/lib/views/mixin.rb +108 -0
- data/lib/waves.rb +80 -0
- metadata +260 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module Waves
|
2
|
+
|
3
|
+
class Console < Application
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
attr_reader :console
|
8
|
+
|
9
|
+
def load( options={} )
|
10
|
+
@console ||= Waves::Console.new( options )
|
11
|
+
end
|
12
|
+
|
13
|
+
# allow Waves::Console to act as The Console Instance
|
14
|
+
def method_missing(*args); @console.send(*args); end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Kernel
|
2
|
+
unless respond_to?(:debugger)
|
3
|
+
# Starts a debugging session if ruby-debug has been loaded (call waves-server --debugger to do load it).
|
4
|
+
def debugger
|
5
|
+
puts "debugger called"
|
6
|
+
Waves::Logger.info "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'logger'
|
2
|
+
module Waves
|
3
|
+
|
4
|
+
# Waves::Logger is based on Ruby's built-in Logger. It uses the same filtering approach
|
5
|
+
# (debug, info, warn, error, fatal), although the interface is slightly different.
|
6
|
+
# You won't typically instantiate this class directly; instead, you will specify the
|
7
|
+
# logging configuration you want in your configuration files. See Waves::Configurations
|
8
|
+
# for more information on this.
|
9
|
+
#
|
10
|
+
# To use the logger for output, you can usually just call +log+, since the Waves::ResponseHelper
|
11
|
+
# mixin defines it (meaning it is available in the mapping file, controllers, views, and
|
12
|
+
# templates). Or, you can access Waves::Logger directly. Either way, the logger provides five
|
13
|
+
# methods for output corresponding to the log levels.
|
14
|
+
#
|
15
|
+
# *Examples*
|
16
|
+
# # log the value of foo
|
17
|
+
# log.info "Value of foo: #{foo}"
|
18
|
+
#
|
19
|
+
# # fatal error!
|
20
|
+
# Waves::Logger.fatal "She can't hold up any longer, cap'n!"
|
21
|
+
#
|
22
|
+
module Logger
|
23
|
+
|
24
|
+
class << self
|
25
|
+
|
26
|
+
# Returns the object being used for output by the logger.
|
27
|
+
def output
|
28
|
+
@output ||= ( config[:output] ? File.expand_path( config[:output] ) : $stderr )
|
29
|
+
end
|
30
|
+
# Returns the active configuration for the logger.
|
31
|
+
def config ; @config ||= Waves::Server.config.log ; end
|
32
|
+
# Returns the logging level used to filter logging events.
|
33
|
+
def level ; @level ||= ::Logger.const_get( config[:level].to_s.upcase || 'INFO' ) ; end
|
34
|
+
# Starts the logger, using the active configuration to initialize it.
|
35
|
+
def start
|
36
|
+
@log = config[:rotation] ?
|
37
|
+
::Logger.new( output, config[:rotation].intern ) :
|
38
|
+
::Logger.new( output )
|
39
|
+
@log.level = level
|
40
|
+
self
|
41
|
+
end
|
42
|
+
# Forwards logging methods to the logger.
|
43
|
+
def method_missing(name,*args,&block)
|
44
|
+
@log.send name,*args, &block if @log
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Waves
|
2
|
+
|
3
|
+
# Waves::MimeTypes defines an interface for adding MIME types used in mapping requests
|
4
|
+
# to content types. Mongrel's MIME_TYPES hash is used as the baseline MIME map.
|
5
|
+
|
6
|
+
module MimeTypes
|
7
|
+
|
8
|
+
def self.[]( path )
|
9
|
+
mapping[ File.extname( path ) ]
|
10
|
+
end
|
11
|
+
|
12
|
+
# TODO: This does not seem to be working.
|
13
|
+
def self.<<( mapping )
|
14
|
+
mapping.merge!( mapping )
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.mapping
|
18
|
+
@mapping ||= Mongrel::DirHandler::MIME_TYPES
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Waves
|
2
|
+
# Waves::Request represents an HTTP request and has methods for accessing anything
|
3
|
+
# relating to the request. See Rack::Request for more information, since many methods
|
4
|
+
# are actually delegated to Rack::Request.
|
5
|
+
class Request
|
6
|
+
|
7
|
+
class ParseError < Exception ; end
|
8
|
+
|
9
|
+
attr_reader :response, :session
|
10
|
+
|
11
|
+
# Create a new request. Takes a env parameter representing the request passed in from Rack.
|
12
|
+
# You shouldn't need to call this directly.
|
13
|
+
def initialize( env )
|
14
|
+
@request = Rack::Request.new( env )
|
15
|
+
@response = Waves::Response.new( self )
|
16
|
+
@session = Waves::Session.new( self )
|
17
|
+
end
|
18
|
+
|
19
|
+
# Accessor not explicitly defined by Waves::Request are delegated to Rack::Request.
|
20
|
+
# Check the Rack documentation for more information.
|
21
|
+
def method_missing(name,*args)
|
22
|
+
@request.send(name,*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
# The request path (PATH_INFO). Ex: +/entry/2008-01-17+
|
26
|
+
def path
|
27
|
+
@request.path_info
|
28
|
+
end
|
29
|
+
|
30
|
+
# The request domain. Ex: +www.fubar.com+
|
31
|
+
def domain
|
32
|
+
@request.host
|
33
|
+
end
|
34
|
+
|
35
|
+
# The request content type.
|
36
|
+
def content_type
|
37
|
+
@request.env['CONTENT_TYPE']
|
38
|
+
end
|
39
|
+
|
40
|
+
# Supported request methods
|
41
|
+
METHODS = %w{get post put delete head options trace}
|
42
|
+
|
43
|
+
# Override the Rack methods for querying the request method.
|
44
|
+
METHODS.each do |method|
|
45
|
+
class_eval "def #{method}?; method == :#{method} end"
|
46
|
+
end
|
47
|
+
|
48
|
+
# The request method. Because browsers can't send PUT or DELETE
|
49
|
+
# requests this can be simulated by sending a POST with a hidden
|
50
|
+
# field named '_method' and a value with 'PUT' or 'DELETE'. Also
|
51
|
+
# accepted is when a query parameter named '_method' is provided.
|
52
|
+
def method
|
53
|
+
@method ||= begin
|
54
|
+
request_method = @request.request_method.downcase
|
55
|
+
if request_method == 'post'
|
56
|
+
_method = @request['_method']
|
57
|
+
_method.downcase! if _method
|
58
|
+
METHODS.include?(_method) ? _method.intern : :post
|
59
|
+
else
|
60
|
+
request_method.intern
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Raise a not found exception.
|
66
|
+
def not_found
|
67
|
+
raise Waves::Dispatchers::NotFoundError.new( @request.url + ' not found.' )
|
68
|
+
end
|
69
|
+
|
70
|
+
# Issue a redirect for the given path.
|
71
|
+
def redirect( path, status = '302' )
|
72
|
+
raise Waves::Dispatchers::Redirect.new( path, status )
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Waves
|
2
|
+
# Waves::Response represents an HTTP response and has methods for constructing a response.
|
3
|
+
# These include setters for +content_type+, +content_length+, +location+, and +expires+
|
4
|
+
# headers. You may also set the headers directly using the [] operator. If you don't find
|
5
|
+
# what you are looking for here, check the documentation for Rack::Response since many
|
6
|
+
# methods for this class are delegated to Rack::Response.
|
7
|
+
class Response
|
8
|
+
|
9
|
+
attr_reader :request
|
10
|
+
|
11
|
+
# Create a new response. Takes the request object. You shouldn't need to call this directly.
|
12
|
+
def initialize( request )
|
13
|
+
@request = request
|
14
|
+
@response = Rack::Response.new
|
15
|
+
end
|
16
|
+
|
17
|
+
%w( Content-Type Content-Length Location Expires ).each do |header|
|
18
|
+
define_method( header.downcase.gsub('-','_')+ '=' ) do | val |
|
19
|
+
@response[header] = val
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the sessions associated with the request, allowing you to set values within it.
|
24
|
+
# The session will be created if necessary and saved when the response is complete.
|
25
|
+
def session ; request.session ; end
|
26
|
+
|
27
|
+
# Finish the response. This will send the response back to the client, so you shouldn't
|
28
|
+
# attempt to further modify the response once this method is called. You don't usually
|
29
|
+
# need to call it yourself, since it is called by the dispatcher once request processing
|
30
|
+
# is finished.
|
31
|
+
def finish ; request.session.save ; @response.finish ; end
|
32
|
+
|
33
|
+
# Methods not explicitly defined by Waves::Response are delegated to Rack::Response.
|
34
|
+
# Check the Rack documentation for more informations
|
35
|
+
def method_missing(name,*args)
|
36
|
+
@response.send(name,*args)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Waves
|
2
|
+
|
3
|
+
# Defines a set of methods that simplify accessing common request and response methods.
|
4
|
+
# These include methods not necessarily associated with the Waves::Request and Waves::Response
|
5
|
+
# objects, but which may still be useful for constructing a response.
|
6
|
+
#
|
7
|
+
# This mixin assumes that a @request@ accessor already exists.
|
8
|
+
module ResponseMixin
|
9
|
+
# Access the response.
|
10
|
+
def response; request.response; end
|
11
|
+
# Access the request parameters.
|
12
|
+
def params; request.params; end
|
13
|
+
# Access the request session.
|
14
|
+
def session; request.session; end
|
15
|
+
# Access the request path.
|
16
|
+
def path; request.path; end
|
17
|
+
# Access the request url.
|
18
|
+
def url; request.url; end
|
19
|
+
# Access the request domain.
|
20
|
+
def domain; request.domain; end
|
21
|
+
# Issue a redirect for the given location.
|
22
|
+
def redirect(location, status = '302'); request.redirect(location, status); end
|
23
|
+
# Access the primary application's models
|
24
|
+
def models; Waves.application.models; end
|
25
|
+
# Access the primary application's views
|
26
|
+
def views; Waves.application.views; end
|
27
|
+
# Access the primary application's controllers
|
28
|
+
def controllers; Waves.application.controllers; end
|
29
|
+
# Raise a "not found" exception.
|
30
|
+
def not_found; request.not_found; end
|
31
|
+
# Access the Waves::Logger.
|
32
|
+
def log; Waves::Logger; end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Waves
|
2
|
+
|
3
|
+
class ResponseProxy
|
4
|
+
|
5
|
+
attr_reader :request
|
6
|
+
|
7
|
+
include ResponseMixin
|
8
|
+
|
9
|
+
def initialize(request); @request = request; end
|
10
|
+
|
11
|
+
def resource( resource, &block )
|
12
|
+
@resource = resource; yield.call
|
13
|
+
end
|
14
|
+
|
15
|
+
def controller( &block )
|
16
|
+
lambda { Waves.application.controllers[ @resource ].process( @request, &block ) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def view( &block )
|
20
|
+
lambda { |val| Waves.application.views[ @resource ].process( @request, val, &block ) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def redirect(path, status = '302'); @request.redirect(path, status); end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Waves
|
2
|
+
# You can run the Waves::Server via the +waves-server+ command or via <tt>rake cluster:start</tt>. Run <tt>waves-server --help</tt> for options on the <tt>waves-server</tt> command. The <tt>cluster.start</tt> task use the +mode+ environment parameter to determine which configuration to use. You can define +port+ to run on a single port, or +ports+ (taking an array) to run on multiple ports.
|
3
|
+
#
|
4
|
+
# *Example*
|
5
|
+
#
|
6
|
+
# Assume that +ports+ is set in the development configuration like this:
|
7
|
+
#
|
8
|
+
# ports [ 2020, 2021, 2022 ]
|
9
|
+
#
|
10
|
+
# Then you could start up instances on all three ports using:
|
11
|
+
#
|
12
|
+
# rake cluster:start mode=development
|
13
|
+
#
|
14
|
+
# This is the equivalent of running:
|
15
|
+
#
|
16
|
+
# waves-server -c development -p 2020 -d
|
17
|
+
# waves-server -c development -p 2021 -d
|
18
|
+
# waves-server -c development -p 2022 -d
|
19
|
+
#
|
20
|
+
class Server < Application
|
21
|
+
|
22
|
+
# Access the server thread.
|
23
|
+
attr_reader :thread
|
24
|
+
|
25
|
+
# Access the host we're binding to (set via the configuration).
|
26
|
+
def host ; options[:host] || config.host ; end
|
27
|
+
|
28
|
+
# Access the port we're listening on (set via the configuration).
|
29
|
+
def port ; options[:port] || config.port ; end
|
30
|
+
|
31
|
+
# Run the server as a daemon. Corresponds to the -d switch on +waves-server+.
|
32
|
+
def daemonize
|
33
|
+
pwd = Dir.pwd
|
34
|
+
Daemonize.daemonize( Waves::Logger.output )
|
35
|
+
Dir.chdir(pwd)
|
36
|
+
File.write( :log / "#{port}.pid", $$ )
|
37
|
+
end
|
38
|
+
|
39
|
+
# Start and / or access the Waves::Logger instance.
|
40
|
+
def log ; @log ||= Waves::Logger.start ; end
|
41
|
+
|
42
|
+
# Start the server.
|
43
|
+
def start
|
44
|
+
daemonize if options[:daemon]
|
45
|
+
start_debugger if options[:debugger]
|
46
|
+
log.info "** Waves Server starting on #{host}:#{port}"
|
47
|
+
handler, options = config.handler
|
48
|
+
handler.run( config.application.to_app, options ) do |server|
|
49
|
+
@server = server
|
50
|
+
trap('INT') { puts; stop } if @server.respond_to? :stop
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Stop the server.
|
55
|
+
def stop
|
56
|
+
log.info "** Waves Server Stopping ..."
|
57
|
+
if options[:daemon]
|
58
|
+
pid_file = :log / $$ + '.pid'; FileUtils.rm( pid_file ) if File.exist?( pid_file )
|
59
|
+
end
|
60
|
+
@server.stop
|
61
|
+
log.info "** Waves Server Stopped"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Provides access to the server mutex for thread-safe operation.
|
65
|
+
def synchronize( &block ) ; ( @mutex ||= Mutex.new ).synchronize( &block ) ; end
|
66
|
+
|
67
|
+
class << self
|
68
|
+
private :new, :dup, :clone
|
69
|
+
# Start or restart the server.
|
70
|
+
def run( options={} )
|
71
|
+
@server.stop if @server; @server = new( options ); @server.start
|
72
|
+
end
|
73
|
+
# Allows us to access the Waves::Server instance.
|
74
|
+
def method_missing(*args); @server.send(*args); end
|
75
|
+
# Probably wouldn't need this if I added a block parameter to method_missing.
|
76
|
+
def synchronize(&block) ; @server.synchronize(&block) ; end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def start_debugger
|
82
|
+
begin
|
83
|
+
require 'ruby-debug'
|
84
|
+
Debugger.start
|
85
|
+
Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
|
86
|
+
log.info "Debugger enabled"
|
87
|
+
rescue Exception
|
88
|
+
log.info "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
|
89
|
+
exit
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Waves
|
2
|
+
|
3
|
+
# Encapsulates the session associated with a given request. A session has an expiration
|
4
|
+
# and path. You must set these in your configuration file. See Waves::Configuration for
|
5
|
+
# more information.
|
6
|
+
#
|
7
|
+
class Session
|
8
|
+
|
9
|
+
# Create a new session object using the given request. This is not necessarily the
|
10
|
+
# same as constructing a new session. The session_id cookie for the request domain
|
11
|
+
# is used to store a session id. The actual session data will be stored in a directory
|
12
|
+
# specified by the application's configuration file.
|
13
|
+
def initialize( request )
|
14
|
+
@request = request
|
15
|
+
@data ||= ( File.exist?( session_path ) ? load_session : {} )
|
16
|
+
end
|
17
|
+
|
18
|
+
# Save the session data. You shouldn't typically have to call this directly, since it
|
19
|
+
# is called by Waves::Response#finish.
|
20
|
+
def save
|
21
|
+
if @data && @data.length > 0
|
22
|
+
File.write( session_path, @data.to_yaml )
|
23
|
+
@request.response.set_cookie( 'session_id',
|
24
|
+
:value => session_id, :path => '/',
|
25
|
+
:expires => Time.now + Waves::Server.config.session[:duration] )
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Access a given data element of the session using the given key.
|
30
|
+
def [](key) ; @data[key] ; end
|
31
|
+
# Set the given data element of the session using the given key and value.
|
32
|
+
def []=(key,val) ; @data[key] = val ; end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def session_id
|
37
|
+
@session_id ||= ( @request.cookies['session_id'] || generate_session_id )
|
38
|
+
end
|
39
|
+
|
40
|
+
def generate_session_id # from Camping ...
|
41
|
+
chars = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
|
42
|
+
(0..48).inject(''){|s,x| s+=chars[ rand(chars.length) ] }
|
43
|
+
end
|
44
|
+
|
45
|
+
def session_path
|
46
|
+
Waves::Application.instance.config.session[:path] / session_id
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_session
|
50
|
+
YAML.load( File.read( session_path ) )
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|