waves 0.7.3 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/app/Rakefile +11 -19
  2. data/app/bin/waves-console +3 -5
  3. data/app/bin/waves-server +3 -5
  4. data/app/configurations/development.rb.erb +19 -11
  5. data/app/configurations/mapping.rb.erb +4 -5
  6. data/app/configurations/production.rb.erb +18 -13
  7. data/app/{doc/EMTPY → controllers/.gitignore} +0 -0
  8. data/app/{public/css/EMPTY → doc/.gitignore} +0 -0
  9. data/app/{public/flash/EMPTY → helpers/.gitignore} +0 -0
  10. data/app/lib/application.rb.erb +4 -51
  11. data/app/{public/images/EMPTY → lib/tasks/.gitignore} +0 -0
  12. data/app/{public/javascript/EMPTY → log/.gitignore} +0 -0
  13. data/app/{tmp/sessions/EMPTY → models/.gitignore} +0 -0
  14. data/app/public/css/.gitignore +0 -0
  15. data/app/public/flash/.gitignore +0 -0
  16. data/app/public/images/.gitignore +0 -0
  17. data/app/public/javascript/.gitignore +0 -0
  18. data/app/schema/migrations/.gitignore +0 -0
  19. data/app/startup.rb +5 -0
  20. data/app/templates/layouts/default.mab +2 -2
  21. data/app/tmp/sessions/.gitignore +0 -0
  22. data/app/views/.gitignore +0 -0
  23. data/bin/waves +38 -27
  24. data/bin/waves-console +3 -25
  25. data/bin/waves-server +4 -45
  26. data/lib/commands/waves-console.rb +21 -0
  27. data/lib/commands/waves-server.rb +55 -0
  28. data/lib/controllers/base.rb +11 -0
  29. data/lib/controllers/mixin.rb +130 -102
  30. data/lib/dispatchers/base.rb +65 -50
  31. data/lib/dispatchers/default.rb +79 -52
  32. data/lib/foundations/default.rb +26 -0
  33. data/lib/foundations/simple.rb +30 -0
  34. data/lib/helpers/common.rb +60 -56
  35. data/lib/helpers/default.rb +13 -0
  36. data/lib/helpers/form.rb +39 -38
  37. data/lib/helpers/formatting.rb +11 -11
  38. data/lib/helpers/model.rb +12 -12
  39. data/lib/helpers/view.rb +13 -13
  40. data/lib/layers/default_errors.rb +29 -0
  41. data/lib/layers/mvc.rb +58 -0
  42. data/lib/layers/orm/active_record.rb +41 -0
  43. data/lib/layers/orm/active_record/migrations/empty.rb.erb +9 -0
  44. data/lib/layers/orm/active_record/tasks/schema.rb +30 -0
  45. data/lib/layers/orm/data_mapper.rb +42 -0
  46. data/lib/layers/orm/filebase.rb +22 -0
  47. data/lib/layers/orm/migration.rb +70 -0
  48. data/lib/layers/orm/sequel.rb +82 -0
  49. data/lib/layers/orm/sequel/migrations/empty.rb.erb +9 -0
  50. data/lib/layers/orm/sequel/tasks/schema.rb +24 -0
  51. data/lib/layers/simple.rb +39 -0
  52. data/lib/layers/simple_errors.rb +26 -0
  53. data/lib/mapping/mapping.rb +222 -120
  54. data/lib/mapping/pretty_urls.rb +42 -41
  55. data/lib/renderers/erubis.rb +54 -31
  56. data/lib/renderers/markaby.rb +28 -28
  57. data/lib/renderers/mixin.rb +49 -52
  58. data/lib/runtime/application.rb +66 -48
  59. data/lib/runtime/blackboard.rb +57 -0
  60. data/lib/runtime/configuration.rb +117 -101
  61. data/lib/runtime/console.rb +19 -20
  62. data/lib/runtime/debugger.rb +9 -0
  63. data/lib/runtime/logger.rb +43 -37
  64. data/lib/runtime/mime_types.rb +19 -19
  65. data/lib/runtime/request.rb +72 -46
  66. data/lib/runtime/response.rb +37 -37
  67. data/lib/runtime/response_mixin.rb +26 -23
  68. data/lib/runtime/response_proxy.rb +25 -24
  69. data/lib/runtime/server.rb +99 -80
  70. data/lib/runtime/session.rb +63 -53
  71. data/lib/tasks/cluster.rb +26 -0
  72. data/lib/tasks/gem.rb +31 -0
  73. data/lib/tasks/generate.rb +80 -0
  74. data/lib/utilities/hash.rb +22 -0
  75. data/lib/utilities/inflect.rb +194 -0
  76. data/lib/utilities/integer.rb +15 -12
  77. data/lib/utilities/kernel.rb +32 -32
  78. data/lib/utilities/module.rb +11 -4
  79. data/lib/utilities/object.rb +5 -5
  80. data/lib/utilities/proc.rb +10 -0
  81. data/lib/utilities/string.rb +44 -38
  82. data/lib/utilities/symbol.rb +4 -4
  83. data/lib/views/base.rb +9 -0
  84. data/lib/views/mixin.rb +91 -89
  85. data/lib/waves.rb +29 -9
  86. metadata +52 -26
  87. data/app/configurations/default.rb.erb +0 -8
  88. data/app/controllers/default.rb.erb +0 -29
  89. data/app/helpers/default.rb.erb +0 -13
  90. data/app/lib/startup.rb.erb +0 -3
  91. data/app/lib/tasks/cluster.rb +0 -24
  92. data/app/lib/tasks/generate.rb +0 -15
  93. data/app/lib/tasks/schema.rb +0 -29
  94. data/app/models/default.rb.erb +0 -13
  95. data/app/schema/migrations/templates/empty.rb.erb +0 -9
  96. data/app/views/default.rb.erb +0 -13
@@ -0,0 +1,55 @@
1
+ require 'choice'
2
+
3
+ Choice.options do
4
+ header 'Run a waves application server.'
5
+ header ''
6
+ option :port do
7
+ short '-p'
8
+ long '--port=PORT'
9
+ desc 'Port to listen on.'
10
+ desc 'Defaults to value given in configuration.'
11
+ cast Integer
12
+ end
13
+ separator ''
14
+ option :host do
15
+ short '-h'
16
+ long '--host=HOST'
17
+ desc 'Host or IP address of the host to bind.'
18
+ desc 'Defaults to value given in configuration.'
19
+ end
20
+ separator ''
21
+ option :mode do
22
+ short '-c'
23
+ long '--config=CONFIG'
24
+ desc 'Configuration to use.'
25
+ desc 'Defaults to development.'
26
+ cast Symbol
27
+ end
28
+ separator ''
29
+ option :directory do
30
+ short '-D'
31
+ long '--dir=DIR'
32
+ desc 'Directory containing the application.'
33
+ desc 'Defaults to the current directory.'
34
+ end
35
+ separator ''
36
+ option :daemon do
37
+ short '-d'
38
+ long '--daemon'
39
+ desc 'Run as a daemon.'
40
+ end
41
+ separator ''
42
+ option :debugger do
43
+ short '-u'
44
+ long '--debugger'
45
+ desc 'Enable ruby-debug.'
46
+ end
47
+ separator ''
48
+ option :startup do
49
+ short '-s'
50
+ long '--startup'
51
+ desc 'Startup file to load.'
52
+ desc 'Defaults to "lib/startup.rb"'
53
+ end
54
+ end
55
+ Waves::Server.run( Choice.choices )
@@ -0,0 +1,11 @@
1
+ module Waves
2
+ module Controllers
3
+ class Base
4
+
5
+ include Waves::Controllers::Mixin
6
+
7
+ def attributes; params[model_name.singular.intern]; end
8
+
9
+ end
10
+ end
11
+ end
@@ -1,131 +1,159 @@
1
1
  module Waves
2
-
2
+
3
3
  #
4
- # Controllers in Waves are simply classes that provide a request / response context for operating on a model. While models are essentially just a way to manage data in an application, controllers manage data in response to a request. For example, a controller updates a model instance using parameters from the request.
4
+ # Controllers in Waves are simply classes that provide a request / response
5
+ # context for operating on a model. While models are essentially just a way
6
+ # to manage data in an application, controllers manage data in response to
7
+ # a request. For example, a controller updates a model instance using
8
+ # parameters from the request.
5
9
  #
6
- # Controller methods simply return data (a resource), if necessary, that can be then used by views to determine how to render that data. Controllers do not determine which view will be invoked. They are independent of the view; one controller method might be suitable for several different views. In some cases, controllers can choose to directly modify the response and possibly even short-circuit the view entirely. A good example of this is a redirect.
10
+ # Public controller methods simply return data (a resource), if necessary, that
11
+ # can be then used by views to determine how to render that data.
12
+ # Controllers do not determine which view will be invoked. They are
13
+ # independent of the view; one controller method might be suitable for
14
+ # several different views. In some cases, controllers can choose to
15
+ # directly modify the response and possibly even short-circuit the view
16
+ # entirely. A good example of this is a redirect.
7
17
  #
8
- # Controllers, like Views and Mappings, use the Waves::ResponseMixin to provide a rich context for working with the request and response objects. They can even call other controllers or views using the controllers method. In addition, they provide some basic reflection (access to the model and model_name that corresponds to the class name for the given model) and automatic parameter destructuring. This allows controller methods to access the request parameters as a hash, so that a POST variable named <tt>entry.title</tt> is accessed as <tt>params[:entry][:title]</tt>.
18
+ # Controllers, like Views and Mappings, use the Waves::ResponseMixin to
19
+ # provide a rich context for working with the request and response objects.
20
+ # They can even call other controllers or views using the controllers method.
21
+ # In addition, they provide some basic reflection (access to the model and
22
+ # model_name that corresponds to the class name for the given model) and
23
+ # automatic parameter destructuring. This allows controller methods to access
24
+ # the request parameters as a hash, so that a POST variable named
25
+ # <tt>entry.title</tt> is accessed as <tt>params[:entry][:title]</tt>.
9
26
  #
10
- # Controllers often do not have to be explicitly defined. Instead, one or more default controllers can be defined that are used as exemplars for a given model. By default, the +waves+ command generates a single default, placed in the application's <tt>controllers/default.rb</tt> file. This can be modified to change the default behavior for all controllers. Alternatively, the <tt>rake generate:controller</tt> command can be used to explicitly define a controller.
27
+ # Controllers often do not have to be explicitly defined. Instead, one or more
28
+ # default controllers can be defined that are used as exemplars for a given
29
+ # model. By default, the +waves+ command generates a single default, placed in
30
+ # the application's <tt>controllers/default.rb</tt> file. This can be modified
31
+ # to change the default behavior for all controllers. Alternatively, the
32
+ # <tt>rake generate:controller</tt> command can be used to explicitly define a
33
+ # controller.
11
34
  #
12
35
  # As an example, the code for the default controller is below for the Blog application.
13
36
  #
14
37
  # module Blog
15
- # module Controllers
16
- # class Default
38
+ # module Controllers
39
+ # class Default
17
40
  #
18
41
  # # Pick up the default controller methods, like param, url, etc.
19
- # include Waves::Controllers::Mixin
42
+ # include Waves::Controllers::Mixin
20
43
  #
21
44
  # # This gets me the parameters associated with this model
22
- # def attributes; params[model_name.singular.intern]; end
45
+ # def attributes; params[model_name.singular.intern]; end
23
46
  #
24
47
  # # A simple generic delegator to the model
25
- # def all; model.all; end
48
+ # def all; model.all; end
26
49
  #
27
50
  # # Find a specific instance based on a name or raise a 404
28
- # def find( name ); model[ :name => name ] or not_found; end
51
+ # def find( name ); model[ :name => name ] or not_found; end
29
52
  #
30
53
  # # Create a new instance based on the request params
31
- # def create; model.create( attributes ); end
54
+ # def create; model.create( attributes ); end
32
55
  #
33
56
  # # Update an existing record. find will raise a 404 if not found.
34
- # def update( name )
35
- # instance = find( name )
36
- # instance.set( attributes )
37
- # instance.save_changes
38
- # end
57
+ # def update( name )
58
+ # instance = find( name )
59
+ # instance.set( attributes )
60
+ # instance.save_changes
61
+ # end
39
62
  #
40
63
  # # Find and delete - or will raise a 404 if it doesn't exist
41
- # def delete( name ); find( name ).destroy; end
64
+ # def delete( name ); find( name ).destroy; end
42
65
  #
43
- # end
44
- # end
66
+ # end
67
+ # end
45
68
  # end
46
69
  #
47
- # Since the mapping file handles "glueing" controllers to views, controllers don't have to be at all concerned with views. They don't have to set instance variables, layouts, or contain logic to select the appropriate view based on the request. All they do is worry about updating the model when necessary based on the request.
70
+ # Since the mapping file handles "glueing" controllers to views, controllers
71
+ # don't have to be at all concerned with views. They don't have to set
72
+ # instance variables, layouts, or contain logic to select the appropriate
73
+ # view based on the request. All they do is worry about updating the model
74
+ # when necessary based on the request.
48
75
 
49
- module Controllers
76
+ module Controllers
50
77
 
51
78
  #
52
- # This mixin provides some handy methods for Waves controllers. You will probably
53
- # want to include it in any controllers you define for your application. The default
54
- # controllers generated using the +wave+ command already do this.
55
- #
56
- # Basically, what the mixin does is adapt the class so that it can be used within
57
- # mappings (see Waves::Mapping); add some simple reflection to allow controller methods
58
- # to be written generically (i.e., without reference to a specific model); and provide
59
- # parameter destructuring for the request parameters.
60
- #
61
-
62
- module Mixin
63
-
64
- attr_reader :request
65
-
66
- include Waves::ResponseMixin
67
-
68
- # When you include this Mixin, a +process+ class method is added to your class,
69
- # which accepts a request object and a block. The request object is used to initialize
70
- # the controller and the block is evaluated using +instance_eval+. This allows the
71
- # controller to be used within a mapping file.
72
-
73
- def self.included( c )
74
- def c.process( request, &block )
75
- self.new( request ).instance_eval( &block )
76
- end
77
- end
78
-
79
- def initialize( request ); @request = request; end
80
-
81
- # The params variable is taken from the request object and "destructured", so that
82
- # a parameter named 'blog.title' becomes:
83
- #
84
- # params['blog']['title']
85
- #
86
- # If you want to access the original parameters object, you can still do so using
87
- # +request.parameters+ instead of simply +params+.
88
- def params; @params ||= destructure(request.params); end
89
-
90
- # You can access the name of the model related to this controller using this method.
91
- # It simply takes the basename of the module and converts it to snake case, so if the
92
- # model uses a different plurality, this won't, in fact, be the model name.
93
- def model_name; self.class.basename.snake_case; end
94
-
95
- # This uses the model_name method to attempt to identify the model corresponding to this
96
- # controller. This allows you to write generic controller methods such as:
97
- #
98
- # model.find( name )
99
- #
100
- # to find an instance of a given model. Again, the plurality of the controller and
101
- # model must be the same for this to work.
102
- def model; Waves.application.models[ model_name.intern ]; end
103
-
104
- private
105
-
106
- def destructure(hash)
107
- rval = {}
108
- hash.keys.map{ |key|key.split('.') }.each do |keys|
109
- destructure_with_array_keys(hash,'',keys,rval)
110
- end
111
- rval
112
- end
113
-
114
- def destructure_with_array_keys(hash,prefix,keys,rval)
115
- if keys.length == 1
116
- val = hash[prefix+keys.first]
117
- rval[keys.first.intern] = case val
118
- when String then val.strip
119
- when Hash then val
120
- end
121
- else
122
- rval = ( rval[keys.first.intern] ||= {} )
123
- destructure_with_array_keys(hash,(keys.shift<<'.'),keys,rval)
124
- end
125
- end
126
-
127
- end
128
-
129
- end
130
-
131
- end
79
+ # Waves::Controllers::Mixin adapts a controller class for use in mappings and provides utility methods.
80
+ # It is included in controllers autocreated by the Default foundation, so you do not need to include
81
+ # it in subclasses of the same.
82
+ #
83
+ # The utility methods include simple reflection to allow controller methods
84
+ # to be written generically (i.e., without reference to a specific model) and
85
+ # parameter destructuring for the request parameters.
86
+ #
87
+
88
+ module Mixin
89
+
90
+ attr_reader :request
91
+
92
+ include Waves::ResponseMixin
93
+
94
+ # When this mixin is included it adds a class method named +process+,
95
+ # which accepts a request object and a block. The +process+ method initializes the
96
+ # controller with the request, then evaluates the block using +instance_eval+. This allows the
97
+ # controller to be used from within a mapping lambda (i.e. a ResponseProxy).
98
+
99
+ def self.included( mod )
100
+ def mod.process( request, &block )
101
+ self.new( request ).instance_eval( &block )
102
+ end
103
+ end
104
+
105
+ def initialize( request )
106
+ @request = request
107
+ end
108
+
109
+ # The params variable is taken from the request object and "destructured", so that
110
+ # a parameter named 'blog.title' becomes:
111
+ #
112
+ # params['blog']['title']
113
+ #
114
+ # If you want to access the original parameters object, you can still do so using
115
+ # +request.params+ instead of simply +params+.
116
+ def params; @params ||= destructure(request.params); end
117
+
118
+ # Returns the name of the model corresponding to this controller by taking the basename
119
+ # of the module and converting it to snake case. If the model plurality is different than
120
+ # the controller, this will not, in fact, be the model name.
121
+ def model_name; self.class.basename.snake_case; end
122
+
123
+ # Returns the model corresponding to this controller by naively assuming that
124
+ # +model_name+ must be correct. This allows you to write generic controller methods such as:
125
+ #
126
+ # model.find( name )
127
+ #
128
+ # to find an instance of a given model. Again, the plurality of the controller and
129
+ # model must be the same for this to work.
130
+ def model; Waves.application.models[ model_name.intern ]; end
131
+
132
+ private
133
+
134
+ def destructure(hash)
135
+ rval = {}
136
+ hash.keys.map{ |key|key.split('.') }.each do |keys|
137
+ destructure_with_array_keys(hash,'',keys,rval)
138
+ end
139
+ rval
140
+ end
141
+
142
+ def destructure_with_array_keys(hash,prefix,keys,rval)
143
+ if keys.length == 1
144
+ val = hash[prefix+keys.first]
145
+ rval[keys.first.intern] = case val
146
+ when String then val.strip
147
+ when Hash then val
148
+ end
149
+ else
150
+ rval = ( rval[keys.first.intern] ||= {} )
151
+ destructure_with_array_keys(hash,(keys.shift<<'.'),keys,rval)
152
+ end
153
+ end
154
+
155
+ end
156
+
157
+ end
158
+
159
+ end
@@ -1,52 +1,67 @@
1
1
  module Waves
2
2
 
3
- module Dispatchers
4
-
5
- class NotFoundError < Exception ; end
6
-
7
- class Redirect < Exception
8
- attr_reader :path
9
- def initialize( path )
10
- @path = path
11
- end
12
- end
13
-
14
- # The Base dispatcher simply makes it easier to write dispatchers by inheriting
15
- # from it. It creates a Waves request, ensures the request processing is done
16
- # within a mutex, benchmarks the request processing, logs it, and handles common
17
- # exceptions and redirects. Derived classes need only process the request within
18
- # their +safe+ method, which takes a Waves::Request and returns a Waves::Response.
19
-
20
- class Base
21
-
22
- # Like any Rack application, Waves' dispatchers must provide a call method
23
- # taking an +env+ parameter.
24
- def call( env )
25
- Waves::Server.synchronize do
26
- request = Waves::Request.new( env )
27
- response = request.response
28
- t = Benchmark.realtime do
29
- begin
30
- safe( request )
31
- rescue Dispatchers::Redirect => redirect
32
- response.status = '302'
33
- response.location = redirect.path
34
- rescue Dispatchers::NotFoundError => e
35
- html = Waves.application.views[:errors].process( request ) do
36
- not_found_404( :error => e )
37
- end
38
- response.status = '404'
39
- response.content_type = 'text/html'
40
- response.write( html )
41
- end
42
- end
43
- Waves::Logger.info "#{request.method}: #{request.url} handled in #{(t*1000).round} ms."
44
- response.finish
45
- end
46
- end
47
-
48
- end
49
-
50
- end
51
-
52
- end
3
+ module Dispatchers
4
+
5
+ # A NotFoundError means what you think it means. The dispatchers included with Waves do not
6
+ # natively intercept this exception. Instead an exception handler must be registered in the application
7
+ # mappings. The Simple foundation registers a minimal handler, while the Default foundation registers
8
+ # a slightly fleshier one.
9
+ class NotFoundError < Exception ; end
10
+
11
+ # Redirect exceptions are rescued by the Waves dispatcher and used to set the
12
+ # response status and location.
13
+ class Redirect < Exception
14
+ attr_reader :path, :status
15
+ def initialize( path, status = '302' )
16
+ @path = path
17
+ @status = status
18
+ end
19
+ end
20
+
21
+ # Waves::Dispatchers::Base provides the basic request processing structure.
22
+ # All other Waves dispatchers should inherit from it. It creates a Waves request,
23
+ # determines whether to enclose the request processing in a mutex, benchmarks it,
24
+ # logs it, and handles common exceptions and redirects. Derived classes need only
25
+ # process the request within the +safe+ method, which must take a Waves::Request and return a Waves::Response.
26
+
27
+ class Base
28
+
29
+ # As with any Rack application, a Waves dispatcher must provide a call method
30
+ # that takes an +env+ parameter.
31
+ def call( env )
32
+ if Waves.config.synchronize?
33
+ Waves::Application.instance.synchronize { _call( env ) }
34
+ else
35
+ _call( env )
36
+ end
37
+ end
38
+
39
+ # Called by event driven servers like thin and ebb. Returns true if
40
+ # the server should run the request in a separate thread, as determined by
41
+ # Configurations::Mapping#threaded?
42
+ def deferred?( env )
43
+ Waves::Application.instance.mapping.threaded?( env )
44
+ end
45
+
46
+ private
47
+
48
+ def _call( env )
49
+ request = Waves::Request.new( env )
50
+ response = request.response
51
+ t = Benchmark.realtime do
52
+ begin
53
+ safe( request )
54
+ rescue Dispatchers::Redirect => redirect
55
+ response.status = redirect.status
56
+ response.location = redirect.path
57
+ end
58
+ end
59
+ Waves::Logger.info "#{request.method}: #{request.url} handled in #{(t*1000).round} ms."
60
+ response.finish
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -1,54 +1,81 @@
1
1
  module Waves
2
-
3
- module Dispatchers
4
-
5
- #
6
- # The default dispatcher essentially checks the application's mapping to see
7
- # what to do with the request URL. It checks before and after filters (wrap
8
- # filters are just a combination of both) as well as path and url mappings.
9
- #
10
- # The default dispatcher also attempts to set the content type based on the
11
- # MIME type implied by any file extension used in the request URL using Mongrel's
12
- # MIME types YAML file.
13
- #
14
- # You can write your own dispatcher and use it in your application's configuration
15
- # file. By default, they use the default dispatcher, like this:
16
- #
17
- # application do
18
- # use Rack::ShowExceptions
19
- # run Waves::Dispatchers::Default.new
20
- # end
21
- #
22
-
23
- class Default < Base
24
-
25
- # All dispatchers using the Dispatchers::Base to provide thread-safety, logging, etc.
26
- # must provide a +safe+ method to handle creating a response from a request.
27
- # Takes a Waves::Request and returns a Waves::Reponse
28
- def safe( request )
29
-
30
- response = request.response
31
-
32
- Waves::Server.reload if Waves::Server.debug?
33
- response.content_type = Waves::Server.config.mime_types[ request.path ] || 'text/html'
34
-
35
- mapping = Waves::Server.mapping[ request ]
36
-
37
- mapping[:before].each do | block, args |
38
- ResponseProxy.new(request).instance_exec(*args,&block)
39
- end
40
-
41
- block, args = mapping[:action]
42
- response.write( ResponseProxy.new(request).instance_exec(*args, &block) )
43
-
44
- mapping[:after].each do | block, args |
45
- ResponseProxy.new(request).instance_exec(*args,&block)
46
- end
47
-
48
- end
49
-
50
- end
51
-
52
- end
53
-
2
+
3
+ module Dispatchers
4
+
5
+ #
6
+ # Waves::Dispatchers::Default matches requests against an application's mappings to
7
+ # determine what main action to take, as well as what before, after, always, and exception-handling
8
+ # blocks must run.
9
+ #
10
+ # The default dispatcher also attempts to set the content type based on the
11
+ # file extension used in the request URL. It does this using the class defined in
12
+ # the current configuration's +mime_types+ attribute, which defaults to Mongrel's
13
+ # MIME type handler.
14
+ #
15
+ # You can write your own dispatcher and use it in your application's configuration
16
+ # file. By default, they use the default dispatcher, like this:
17
+ #
18
+ # application do
19
+ # use Rack::ShowExceptions
20
+ # run Waves::Dispatchers::Default.new
21
+ # end
22
+ #
23
+
24
+ class Default < Base
25
+
26
+ # All dispatchers using the Dispatchers::Base to provide thread-safety, logging, etc.
27
+ # must provide a +safe+ method to handle creating a response from a request.
28
+ # Takes a Waves::Request and returns a Waves::Reponse
29
+ def safe( request )
30
+
31
+ response = request.response
32
+
33
+ Waves::Application.instance.reload if Waves::Application.instance.debug?
34
+ response.content_type = Waves::Application.instance.config.mime_types[ request.path ] || 'text/html'
35
+
36
+ mapping = Waves::Application.instance.mapping[ request ]
37
+
38
+ begin
39
+
40
+ mapping[:before].each do | block, args |
41
+ ResponseProxy.new(request).instance_exec(*args,&block)
42
+ end
43
+
44
+ request.not_found unless mapping[:action]
45
+
46
+ block, args = mapping[:action]
47
+ response.write( ResponseProxy.new(request).instance_exec(*args, &block) )
48
+
49
+ mapping[:after].each do | block, args |
50
+ ResponseProxy.new(request).instance_exec(*args,&block)
51
+ end
52
+
53
+ rescue Exception => e
54
+
55
+ handler = mapping[:handlers].detect do | exception, block, args |
56
+ e.is_a? exception
57
+ end
58
+ if handler
59
+ ResponseProxy.new(request).instance_exec(*handler[2], &handler[1])
60
+ else
61
+ raise e
62
+ end
63
+
64
+ ensure
65
+ mapping[:always].each do | block, args |
66
+ begin
67
+ ResponseProxy.new(request).instance_exec(*args,&block)
68
+ rescue Exception => e
69
+ Waves::Logger.info e.to_s
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
54
81
  end