dyoder-waves 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/app/Rakefile +14 -0
  2. data/app/bin/waves-console +3 -0
  3. data/app/bin/waves-server +3 -0
  4. data/app/configurations/development.rb.erb +31 -0
  5. data/app/configurations/mapping.rb.erb +14 -0
  6. data/app/configurations/production.rb.erb +30 -0
  7. data/app/lib/application.rb.erb +3 -0
  8. data/app/startup.rb +5 -0
  9. data/app/templates/errors/not_found_404.mab +2 -0
  10. data/app/templates/errors/server_error_500.mab +2 -0
  11. data/app/templates/layouts/default.mab +14 -0
  12. data/bin/waves +66 -0
  13. data/bin/waves-console +4 -0
  14. data/bin/waves-server +4 -0
  15. data/lib/commands/waves-console.rb +24 -0
  16. data/lib/commands/waves-server.rb +55 -0
  17. data/lib/controllers/mixin.rb +158 -0
  18. data/lib/dispatchers/base.rb +52 -0
  19. data/lib/dispatchers/default.rb +67 -0
  20. data/lib/foundations/default.rb +28 -0
  21. data/lib/foundations/simple.rb +17 -0
  22. data/lib/helpers/common.rb +62 -0
  23. data/lib/helpers/form.rb +39 -0
  24. data/lib/helpers/formatting.rb +30 -0
  25. data/lib/helpers/model.rb +33 -0
  26. data/lib/helpers/view.rb +24 -0
  27. data/lib/layers/default_errors.rb +24 -0
  28. data/lib/layers/simple_errors.rb +17 -0
  29. data/lib/mapping/mapping.rb +252 -0
  30. data/lib/mapping/pretty_urls.rb +94 -0
  31. data/lib/renderers/erubis.rb +61 -0
  32. data/lib/renderers/markaby.rb +33 -0
  33. data/lib/renderers/mixin.rb +53 -0
  34. data/lib/runtime/application.rb +65 -0
  35. data/lib/runtime/configuration.rb +180 -0
  36. data/lib/runtime/console.rb +20 -0
  37. data/lib/runtime/debugger.rb +9 -0
  38. data/lib/runtime/logger.rb +52 -0
  39. data/lib/runtime/mime_types.rb +22 -0
  40. data/lib/runtime/request.rb +77 -0
  41. data/lib/runtime/response.rb +40 -0
  42. data/lib/runtime/response_mixin.rb +35 -0
  43. data/lib/runtime/response_proxy.rb +27 -0
  44. data/lib/runtime/server.rb +94 -0
  45. data/lib/runtime/session.rb +56 -0
  46. data/lib/tasks/cluster.rb +25 -0
  47. data/lib/tasks/gem.rb +31 -0
  48. data/lib/tasks/generate.rb +15 -0
  49. data/lib/utilities/inflect.rb +194 -0
  50. data/lib/utilities/integer.rb +17 -0
  51. data/lib/utilities/kernel.rb +34 -0
  52. data/lib/utilities/module.rb +17 -0
  53. data/lib/utilities/object.rb +17 -0
  54. data/lib/utilities/proc.rb +9 -0
  55. data/lib/utilities/string.rb +47 -0
  56. data/lib/utilities/symbol.rb +7 -0
  57. data/lib/verify/mapping.rb +29 -0
  58. data/lib/verify/request.rb +40 -0
  59. data/lib/views/mixin.rb +108 -0
  60. data/lib/waves.rb +80 -0
  61. 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
+