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.
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
+