waves 0.8.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/waves +4 -3
- data/doc/VERSION +1 -1
- data/lib/waves.rb +52 -40
- data/lib/{caches → waves/caches}/file.rb +3 -1
- data/lib/waves/caches/memcached.rb +56 -0
- data/lib/{caches → waves/caches}/simple.rb +6 -7
- data/lib/{caches → waves/caches}/synchronized.rb +15 -1
- data/lib/{commands → waves/commands}/console.rb +4 -4
- data/lib/{commands → waves/commands}/generate.rb +6 -5
- data/lib/{commands → waves/commands}/help.rb +0 -0
- data/lib/{commands → waves/commands}/server.rb +1 -1
- data/lib/{dispatchers → waves/dispatchers}/base.rb +17 -31
- data/lib/waves/dispatchers/default.rb +19 -0
- data/lib/{ext → waves/ext}/float.rb +0 -0
- data/lib/{ext → waves/ext}/hash.rb +0 -0
- data/lib/{ext → waves/ext}/integer.rb +16 -1
- data/lib/{ext → waves/ext}/kernel.rb +3 -7
- data/lib/{ext → waves/ext}/module.rb +3 -3
- data/lib/{ext → waves/ext}/object.rb +2 -0
- data/lib/waves/ext/string.rb +73 -0
- data/lib/{ext → waves/ext}/symbol.rb +0 -1
- data/lib/{ext → waves/ext}/tempfile.rb +0 -0
- data/lib/waves/ext/time.rb +5 -0
- data/lib/{foundations → waves/foundations}/classic.rb +9 -21
- data/lib/{foundations → waves/foundations}/compact.rb +15 -20
- data/lib/waves/foundations/rest.rb +311 -0
- data/lib/waves/helpers/basic.rb +13 -0
- data/lib/{helpers → waves/helpers}/doc_type.rb +3 -0
- data/lib/waves/helpers/form.rb +94 -0
- data/lib/waves/helpers/formatting.rb +14 -0
- data/lib/waves/layers/mvc.rb +65 -0
- data/lib/{layers → waves/layers}/mvc/controllers.rb +0 -0
- data/lib/{layers → waves/layers}/mvc/extensions.rb +23 -11
- data/lib/{layers → waves/layers}/orm/migration.rb +0 -0
- data/lib/{layers → waves/layers}/orm/providers/active_record.rb +2 -5
- data/lib/{layers → waves/layers}/orm/providers/active_record/migrations/empty.rb.erb +0 -0
- data/lib/{layers → waves/layers}/orm/providers/active_record/tasks/generate.rb +1 -1
- data/lib/{layers → waves/layers}/orm/providers/active_record/tasks/schema.rb +1 -1
- data/lib/{layers → waves/layers}/orm/providers/data_mapper.rb +0 -0
- data/lib/{layers → waves/layers}/orm/providers/filebase.rb +0 -0
- data/lib/{layers → waves/layers}/orm/providers/sequel.rb +28 -29
- data/lib/{layers → waves/layers}/orm/providers/sequel/migrations/empty.rb.erb +0 -0
- data/lib/{layers → waves/layers}/orm/providers/sequel/tasks/generate.rb +1 -1
- data/lib/{layers → waves/layers}/orm/providers/sequel/tasks/schema.rb +2 -0
- data/lib/waves/layers/rack/rack_cache.rb +32 -0
- data/lib/waves/layers/renderers/erubis.rb +52 -0
- data/lib/waves/layers/renderers/haml.rb +67 -0
- data/lib/waves/layers/renderers/markaby.rb +41 -0
- data/lib/waves/layers/text/inflect/english.rb +42 -0
- data/lib/waves/matchers/accept.rb +47 -0
- data/lib/waves/matchers/ext.rb +27 -0
- data/lib/waves/matchers/path.rb +72 -0
- data/lib/waves/matchers/query.rb +43 -0
- data/lib/waves/matchers/request.rb +86 -0
- data/lib/waves/matchers/requested.rb +31 -0
- data/lib/{matchers → waves/matchers}/resource.rb +8 -1
- data/lib/waves/matchers/traits.rb +30 -0
- data/lib/waves/matchers/uri.rb +69 -0
- data/lib/waves/media/mime_types.rb +542 -0
- data/lib/waves/renderers/mixin.rb +9 -0
- data/lib/waves/request/accept.rb +92 -0
- data/lib/{runtime → waves/request}/request.rb +77 -61
- data/lib/waves/resources/file_mixin.rb +11 -0
- data/lib/{resources → waves/resources}/mixin.rb +42 -44
- data/lib/waves/resources/paths.rb +132 -0
- data/lib/waves/response/client_errors.rb +10 -0
- data/lib/waves/response/packaged.rb +19 -0
- data/lib/waves/response/redirects.rb +35 -0
- data/lib/{runtime → waves/response}/response.rb +29 -11
- data/lib/{runtime → waves/response}/response_mixin.rb +30 -17
- data/lib/waves/runtime/applications.rb +18 -0
- data/lib/{runtime → waves/runtime}/configuration.rb +31 -25
- data/lib/waves/runtime/console.rb +24 -0
- data/lib/{runtime → waves/runtime}/logger.rb +3 -3
- data/lib/{runtime → waves/runtime}/mocks.rb +2 -2
- data/lib/waves/runtime/rackup.rb +37 -0
- data/lib/waves/runtime/runtime.rb +48 -0
- data/lib/waves/runtime/server.rb +33 -0
- data/lib/{servers → waves/servers}/base.rb +0 -0
- data/lib/{servers → waves/servers}/mongrel.rb +0 -0
- data/lib/{servers → waves/servers}/webrick.rb +0 -0
- data/lib/{tasks → waves/tasks}/gem.rb +0 -0
- data/lib/{tasks → waves/tasks}/generate.rb +0 -0
- data/lib/waves/views/cassy.rb +173 -0
- data/lib/{views → waves/views}/errors.rb +8 -7
- data/lib/waves/views/mixin.rb +23 -0
- data/lib/waves/views/templated.rb +40 -0
- data/samples/basic/basic_startup.rb +70 -0
- data/samples/basic/config.ru +9 -0
- data/samples/blog/configurations/development.rb +17 -16
- data/samples/blog/configurations/production.rb +0 -11
- data/samples/blog/resources/entry.rb +3 -3
- data/samples/blog/resources/map.rb +10 -3
- data/samples/blog/startup.rb +4 -3
- data/templates/classic/Rakefile +28 -29
- data/templates/classic/configurations/default.rb.erb +8 -3
- data/templates/classic/configurations/development.rb.erb +1 -20
- data/templates/classic/configurations/production.rb.erb +2 -16
- data/templates/classic/public/images/favicon.ico +0 -0
- data/templates/classic/resources/server.rb.erb +9 -0
- data/templates/classic/startup.rb.erb +3 -3
- data/templates/classic/views/css.rb.erb +14 -0
- data/templates/classic/views/default.rb.erb +17 -0
- data/templates/classic/views/errors.rb.erb +10 -0
- data/templates/classic/views/pages.rb.erb +14 -0
- data/templates/compact/startup.rb.erb +8 -3
- data/test/ext/object.rb +55 -0
- data/test/ext/shortcuts.rb +73 -0
- data/test/helpers.rb +17 -0
- data/test/match/accept.rb +78 -0
- data/test/match/ext.rb +156 -0
- data/test/match/methods.rb +22 -0
- data/test/match/params.rb +33 -0
- data/test/match/path.rb +106 -0
- data/test/match/query.rb +60 -0
- data/test/match/request.rb +91 -0
- data/test/match/requested.rb +149 -0
- data/test/match/uri.rb +136 -0
- data/test/process/request.rb +75 -0
- data/test/process/resource.rb +53 -0
- data/test/resources/path.rb +166 -0
- data/test/runtime/configurations.rb +19 -0
- data/test/runtime/request.rb +63 -0
- data/test/runtime/response.rb +85 -0
- data/test/views/views.rb +40 -0
- metadata +243 -157
- data/lib/caches/memcached.rb +0 -40
- data/lib/dispatchers/default.rb +0 -25
- data/lib/ext/string.rb +0 -20
- data/lib/helpers/basic.rb +0 -11
- data/lib/helpers/extended.rb +0 -21
- data/lib/helpers/form.rb +0 -42
- data/lib/helpers/formatting.rb +0 -30
- data/lib/helpers/layouts.rb +0 -37
- data/lib/helpers/model.rb +0 -37
- data/lib/helpers/view.rb +0 -22
- data/lib/layers/inflect/english.rb +0 -67
- data/lib/layers/mvc.rb +0 -54
- data/lib/layers/renderers/erubis.rb +0 -60
- data/lib/layers/renderers/haml.rb +0 -47
- data/lib/layers/renderers/markaby.rb +0 -29
- data/lib/matchers/accept.rb +0 -21
- data/lib/matchers/base.rb +0 -30
- data/lib/matchers/content_type.rb +0 -17
- data/lib/matchers/path.rb +0 -67
- data/lib/matchers/query.rb +0 -21
- data/lib/matchers/request.rb +0 -27
- data/lib/matchers/traits.rb +0 -19
- data/lib/matchers/uri.rb +0 -20
- data/lib/renderers/mixin.rb +0 -36
- data/lib/resources/paths.rb +0 -34
- data/lib/runtime/console.rb +0 -23
- data/lib/runtime/mime_types.rb +0 -536
- data/lib/runtime/monitor.rb +0 -32
- data/lib/runtime/runtime.rb +0 -67
- data/lib/runtime/server.rb +0 -20
- data/lib/runtime/session.rb +0 -27
- data/lib/runtime/worker.rb +0 -86
- data/lib/views/mixin.rb +0 -62
- data/samples/blog/blog.db +0 -0
- data/samples/blog/log/waves.production +0 -3
- data/templates/classic/resources/map.rb.erb +0 -8
- data/templates/classic/templates/errors/not_found_404.mab +0 -7
- data/templates/classic/templates/errors/server_error_500.mab +0 -7
- data/templates/classic/templates/layouts/default.mab +0 -14
- data/templates/classic/tmp/sessions/.gitignore +0 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
module Waves
|
2
|
+
class Accept < Array
|
3
|
+
|
4
|
+
#
|
5
|
+
# RFC 2616 section 14.1.
|
6
|
+
#
|
7
|
+
# Returns an array of elements of the form:
|
8
|
+
#
|
9
|
+
# [ term, params ]
|
10
|
+
#
|
11
|
+
# where is term is an array of MIME type components,
|
12
|
+
# with the true value as the wildcard (*)
|
13
|
+
#
|
14
|
+
# and where params is a hash of parameters (q, level, etc.),
|
15
|
+
# where the q param is auto-converted to a float and
|
16
|
+
# defaulted to 1.0.
|
17
|
+
#
|
18
|
+
# sorted by q value and then specificity, with ties going
|
19
|
+
# to HTML-related media-types
|
20
|
+
#
|
21
|
+
#
|
22
|
+
# TODO parsing must be optimized. This parses Accept,
|
23
|
+
# lang and charset
|
24
|
+
#
|
25
|
+
def Accept.parse(str)
|
26
|
+
return self.new if str.nil?
|
27
|
+
self.new( Accept.sort( str.split(',').map { |term| Accept.parse_media_type( term ) } ).uniq )
|
28
|
+
end
|
29
|
+
|
30
|
+
def Accept.parse_media_type( term )
|
31
|
+
t, *p = term.to_s.split(';').map(&:strip)
|
32
|
+
[ Accept.parse_media_range( t ), Accept.convert_params_to_hash( p ) ]
|
33
|
+
end
|
34
|
+
|
35
|
+
def Accept.parse_media_range( t )
|
36
|
+
t.split('/').map(&:strip).map { |x| x=='*' ? true : x }
|
37
|
+
end
|
38
|
+
|
39
|
+
def Accept.convert_params_to_hash( p )
|
40
|
+
rval = p.inject({}) { |h,p|
|
41
|
+
k,v = p.split('=').map(&:strip)
|
42
|
+
( v = v.to_f ) if k == 'q'
|
43
|
+
h[k] = v ; h
|
44
|
+
}
|
45
|
+
rval['q'] ||= 1.0
|
46
|
+
rval
|
47
|
+
end
|
48
|
+
|
49
|
+
def Accept.sort( terms )
|
50
|
+
terms.sort { |t1,t2|
|
51
|
+
# first compare on quality
|
52
|
+
c = t2[1]['q'] <=> t1[1]['q']
|
53
|
+
# next compare on specificity of the media type
|
54
|
+
c = t2[0].size <=> t1[0].size if ( c == 0 )
|
55
|
+
# finally, compare on specificity of parameters
|
56
|
+
c = t2[1].size <=> t1[1].size if ( c == 0 )
|
57
|
+
c
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def Accept.to_media_type( term )
|
62
|
+
term.first.map { |x| x==true ? '*' : x }.join("/")
|
63
|
+
end
|
64
|
+
|
65
|
+
def =~(arg) ; self.include? arg ; end
|
66
|
+
def ===(arg) ; self.include? arg ; end
|
67
|
+
|
68
|
+
# Check these Accepts against constraints.
|
69
|
+
#
|
70
|
+
def include?(arg)
|
71
|
+
# recursively test for any possibility if we get an array
|
72
|
+
# thus you can match against, say, %w( png jpg gif )
|
73
|
+
return arg.any? {|pat| self.include? pat } if arg.kind_of? Array
|
74
|
+
term = Accept.parse_media_type( arg ).first
|
75
|
+
self.map(&:first).any? { | type, subtype |
|
76
|
+
case term
|
77
|
+
when [ type ], [ subtype ], [ true ],
|
78
|
+
[ type, subtype ], [ type, true ],
|
79
|
+
[ true, true ] then true
|
80
|
+
else false
|
81
|
+
end
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
# Again, we play favorites here: in the absence of any accept header
|
86
|
+
# we go with 'text/html' as our favorite
|
87
|
+
def preferred_media_type
|
88
|
+
Accept.to_media_type( first ) || 'text/html'
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Waves
|
2
|
-
|
3
|
-
# Waves::Request represents an HTTP request and provides convenient methods for accessing request attributes.
|
2
|
+
|
3
|
+
# Waves::Request represents an HTTP request and provides convenient methods for accessing request attributes.
|
4
4
|
# See Rack::Request for documentation of any method not defined here.
|
5
5
|
|
6
6
|
class Request
|
@@ -13,26 +13,44 @@ module Waves
|
|
13
13
|
|
14
14
|
# Create a new request. Takes a env parameter representing the request passed in from Rack.
|
15
15
|
# You shouldn't need to call this directly.
|
16
|
-
|
16
|
+
#
|
17
|
+
def initialize(env)
|
17
18
|
@traits = Class.new { include Attributes }.new( :waves => {} )
|
18
|
-
@request = Rack::Request.new(
|
19
|
+
@request = Rack::Request.new(env).freeze
|
19
20
|
@response = Waves::Response.new( self )
|
20
|
-
@session = Waves::Session.new( self )
|
21
21
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
|
23
|
+
# Rack request object.
|
24
|
+
#
|
25
|
+
def rack_request()
|
26
|
+
@request
|
27
|
+
end
|
28
|
+
|
25
29
|
# Methods delegated directly to rack
|
26
30
|
%w( url scheme host port body query_string content_type media_type content_length referer ).each do |m|
|
27
31
|
define_method( m ) { @request.send( m ) }
|
28
32
|
end
|
29
33
|
|
34
|
+
# access common HTTP headers as methods
|
35
|
+
%w( user_agent cache_control ).each do |name|
|
36
|
+
key = "HTTP_#{name.to_s.upcase}"
|
37
|
+
define_method( name ) { @request.env[ key ] if @request.env.has_key?( key ) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def if_modified_since
|
41
|
+
@if_modified_since ||=
|
42
|
+
( Time.parse( @request.env[ 'HTTP_IF_MODIFIED_SINCE' ] ) if
|
43
|
+
@request.env.has_key?( 'HTTP_IF_MODIFIED_SINCE' ) )
|
44
|
+
end
|
45
|
+
|
46
|
+
def []( key ) ; @request.env[ key.to_s ] ; end
|
47
|
+
|
30
48
|
# The request path (PATH_INFO). Ex: +/entry/2008-01-17+
|
31
49
|
def path ; @request.path_info ; end
|
32
50
|
|
33
51
|
# Access to "params" - aka the query string - as a hash
|
34
52
|
def query ; @request.params ; end
|
35
|
-
|
53
|
+
|
36
54
|
alias_method :params, :query
|
37
55
|
alias_method :domain, :host
|
38
56
|
|
@@ -45,67 +63,65 @@ module Waves
|
|
45
63
|
# field named '_method' and a value with 'PUT' or 'DELETE'. Also
|
46
64
|
# accepted is when a query parameter named '_method' is provided.
|
47
65
|
def method
|
48
|
-
@method ||= ( ( ( m = @request.request_method.downcase ) == 'post' and
|
66
|
+
@method ||= ( ( ( m = @request.request_method.downcase ) == 'post' and
|
49
67
|
( n = @request['_method'] ) ) ? n.downcase : m ).intern
|
50
68
|
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
def method_missing( name, *args, &body )
|
56
|
-
return super unless args.empty? and body.nil?
|
57
|
-
key = "HTTP_#{name.to_s.upcase}"
|
58
|
-
@request.env[ key ] if @request.env.has_key?( key )
|
69
|
+
|
70
|
+
# Requested representation MIME type
|
71
|
+
def accept()
|
72
|
+
@accept ||= Accept.parse(@request.env['HTTP_ACCEPT'])
|
59
73
|
end
|
60
74
|
|
61
|
-
#
|
62
|
-
|
63
|
-
|
75
|
+
# Combination of Accept and file extension for matching.
|
76
|
+
#
|
77
|
+
# A file extension takes precedence over the Accept
|
78
|
+
# header, the Accept is ignored.
|
79
|
+
#
|
80
|
+
# The absence of a file extension is indicated using
|
81
|
+
# the special MIME type MimeTypes::Unspecified, which
|
82
|
+
# allows specialised handling thereof. The resource
|
83
|
+
# must specifically accept Unspecified for it to have
|
84
|
+
# an effect.
|
85
|
+
#
|
86
|
+
# @see matchers/requested.rb
|
87
|
+
# @see #accept
|
88
|
+
# @see #ext
|
89
|
+
# @see runtime/mime_types.rb for the actual definition
|
90
|
+
# of the Unspecified type.
|
91
|
+
#
|
92
|
+
def requested()
|
93
|
+
@requested ||= ( extension ? Accept.new( Accept.parse( MimeTypes[ extension ].join(",") ) + accept ).uniq : accept )
|
64
94
|
end
|
65
95
|
|
66
|
-
#
|
67
|
-
|
68
|
-
|
96
|
+
# Requested charset(s).
|
97
|
+
#
|
98
|
+
# @see matchers/accept.rb
|
99
|
+
#
|
100
|
+
def accept_charset()
|
101
|
+
@charset ||= Accept.parse(@request.env['HTTP_ACCEPT_CHARSET'])
|
69
102
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
if arg.size == 1 # implicit wildcard in arg
|
83
|
-
arg[0] == entry[0] or arg[0] == entry[1]
|
84
|
-
else
|
85
|
-
arg == entry
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def self.parse(string)
|
91
|
-
string.split(',').inject(self.new) { |a, entry| a << entry.split( ';' ).first.strip; a }
|
92
|
-
end
|
93
|
-
|
94
|
-
def default
|
95
|
-
return 'text/html' if self.include?('text/html')
|
96
|
-
find { |entry| ! entry.match(/\*/) } || 'text/html'
|
97
|
-
end
|
98
|
-
|
103
|
+
|
104
|
+
# Requested language(s).
|
105
|
+
#
|
106
|
+
# @see matchers/accept.rb
|
107
|
+
#
|
108
|
+
def accept_language()
|
109
|
+
@lang ||= Accept.parse(@request.env['HTTP_ACCEPT_LANGUAGE'])
|
110
|
+
end
|
111
|
+
|
112
|
+
# File extension of path, with leading dot
|
113
|
+
def extension
|
114
|
+
@ext ||= ( ( e = File.extname( path ) ).empty? ? nil : e )
|
99
115
|
end
|
100
116
|
|
101
|
-
|
102
|
-
|
103
|
-
def
|
104
|
-
|
105
|
-
|
117
|
+
alias :ext :extension
|
118
|
+
|
119
|
+
def basename
|
120
|
+
@basename ||= File.basename( path )
|
121
|
+
end
|
106
122
|
|
107
123
|
module Utilities
|
108
|
-
|
124
|
+
|
109
125
|
def self.destructure( hash )
|
110
126
|
destructured = {}
|
111
127
|
hash.keys.map { |key| key.split('.') }.each do |keys|
|
@@ -136,9 +152,9 @@ module Waves
|
|
136
152
|
destructure_with_array_keys( hash, new_prefix, keys, destructured )
|
137
153
|
end
|
138
154
|
end
|
139
|
-
|
155
|
+
|
140
156
|
end
|
141
|
-
|
157
|
+
|
142
158
|
end
|
143
159
|
|
144
160
|
end
|
@@ -1,18 +1,13 @@
|
|
1
1
|
module Waves
|
2
|
-
|
2
|
+
|
3
3
|
module Resources
|
4
|
-
|
5
|
-
StatusCodes = {
|
6
|
-
Waves::Dispatchers::NotFoundError => 404
|
7
|
-
}
|
8
|
-
|
9
|
-
|
4
|
+
|
10
5
|
module Mixin
|
11
|
-
|
6
|
+
|
12
7
|
attr_reader :request
|
13
|
-
|
8
|
+
|
14
9
|
module ClassMethods
|
15
|
-
|
10
|
+
|
16
11
|
def paths
|
17
12
|
unless @paths
|
18
13
|
resource = self
|
@@ -46,7 +41,7 @@ module Waves
|
|
46
41
|
methods.each do | method |
|
47
42
|
functor_with_self( method, matcher, &block )
|
48
43
|
end
|
49
|
-
paths.module_eval {
|
44
|
+
paths.module_eval {
|
50
45
|
define_method( generator ) { | *args | generate( path, args ) }
|
51
46
|
} if generator
|
52
47
|
end
|
@@ -62,71 +57,74 @@ module Waves
|
|
62
57
|
end
|
63
58
|
def handler( exception, &block ) ; functor( :handler, exception, &block ) ; end
|
64
59
|
def always( &block ) ; define_method( :always, &block ) ; end
|
65
|
-
|
60
|
+
|
66
61
|
end
|
67
62
|
|
68
63
|
# this is necessary because you can't define functors within a module because the functor attempts
|
69
64
|
# to incorporate the superclass functor table into it's own
|
70
65
|
def self.included( resource )
|
71
|
-
|
72
|
-
resource.module_eval do
|
73
|
-
|
66
|
+
|
67
|
+
resource.module_eval do
|
68
|
+
|
74
69
|
include ResponseMixin, Functor::Method ; extend ClassMethods
|
75
70
|
|
76
71
|
def initialize( request ); @request = request ; end
|
77
|
-
|
72
|
+
|
73
|
+
# define defaults for all the functors, providing the analog
|
74
|
+
# of "not implemented" behaviors. this avoids complicating
|
75
|
+
# the error handling with having to distinguish between
|
76
|
+
# functor match-related errors and actual application errors
|
77
|
+
|
78
|
+
# by default, don't do anything in the wrapper methods
|
79
|
+
before {} ; after {} ; always {}
|
80
|
+
|
81
|
+
# if we get here, this is a 404
|
82
|
+
%w( post get put delete head ).each do | method |
|
83
|
+
on( method ) { not_found }
|
84
|
+
end
|
85
|
+
|
86
|
+
# default handler is just to propagate the exception
|
87
|
+
handler( Exception ) { |e| raise( e ) }
|
88
|
+
|
78
89
|
def process
|
79
90
|
begin
|
80
|
-
before ;
|
81
|
-
rescue Waves::Dispatchers::Redirect => e
|
82
|
-
raise e
|
91
|
+
before ; rval = send( request.method ) ; after
|
83
92
|
rescue Exception => e
|
84
|
-
|
85
|
-
|
86
|
-
Waves::Logger.warn e.to_s
|
87
|
-
e.backtrace.each { |t| Waves::Logger.debug " #{t}" }
|
93
|
+
e.call( response ) if e.respond_to?( :call )
|
94
|
+
rval = handler( e )
|
88
95
|
ensure
|
89
96
|
always
|
90
97
|
end
|
91
|
-
|
98
|
+
# note: the dispatcher decides what to do with the
|
99
|
+
# return value; all we care about is returning the
|
100
|
+
# value from the appropriate application block
|
101
|
+
return rval
|
92
102
|
end
|
93
|
-
|
103
|
+
|
94
104
|
def to( resource )
|
95
105
|
resource = case resource
|
96
106
|
when Base
|
97
107
|
resource
|
98
108
|
when Symbol, String
|
99
|
-
begin
|
100
|
-
Waves.main::Resources[ resource ]
|
101
|
-
rescue NameError
|
102
|
-
raise Waves::Dispatchers::NotFoundError
|
103
|
-
end
|
104
109
|
Waves.main::Resources[ resource ]
|
105
110
|
end
|
106
111
|
r = traits.waves.resource = resource.new( request )
|
107
112
|
r.process
|
108
113
|
end
|
109
|
-
|
114
|
+
|
110
115
|
def redirect( path ) ; request.redirect( path ) ; end
|
111
|
-
|
112
|
-
# override for resources that may have long-running requests. this helps servers
|
116
|
+
|
117
|
+
# override for resources that may have long-running requests. this helps servers
|
113
118
|
# determine how to handle the request
|
114
119
|
def deferred? ; false ; end
|
115
|
-
|
116
|
-
before {} ; after {} ; always {}
|
117
|
-
# handler( Waves::Dispatchers::Redirect ) { |e| raise e }
|
118
|
-
|
119
|
-
%w( post get put delete head ).each do | method |
|
120
|
-
on( method ) { not_found }
|
121
|
-
end
|
122
|
-
|
120
|
+
|
123
121
|
end
|
124
|
-
|
122
|
+
|
125
123
|
end
|
126
124
|
|
127
125
|
end
|
128
|
-
|
129
|
-
class Base ; include Mixin ; end
|
126
|
+
|
127
|
+
class Base ; include Mixin ; end
|
130
128
|
|
131
129
|
end
|
132
130
|
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module Waves
|
2
|
+
|
3
|
+
module Resources
|
4
|
+
|
5
|
+
class Paths
|
6
|
+
|
7
|
+
def self.compiled; @compiled ||= {} ; end
|
8
|
+
|
9
|
+
def compiled_paths; self.class.compiled ; end
|
10
|
+
|
11
|
+
def generate( template, args )
|
12
|
+
return "/" if template.empty?
|
13
|
+
if template.is_a? Array
|
14
|
+
if args.size == 1 and args.first.is_a? Hash
|
15
|
+
process_hash( template, args.first )
|
16
|
+
else
|
17
|
+
process_array( template, args)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
"/#{ args * '/' }"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_array( template, args )
|
25
|
+
template_key = template
|
26
|
+
compiled = compiled_paths[template_key]
|
27
|
+
if compiled
|
28
|
+
return ( compiled % args ) rescue raise [template, args].inspect
|
29
|
+
end
|
30
|
+
compilable = true
|
31
|
+
cpath, interpolations = "", []
|
32
|
+
result = ( cpath % interpolations ) if template.all? do | want |
|
33
|
+
case want
|
34
|
+
when Symbol
|
35
|
+
cpath << "/%s" ; interpolations << args.shift
|
36
|
+
when String
|
37
|
+
cpath << "/#{want}"
|
38
|
+
when true
|
39
|
+
compilable = false
|
40
|
+
cpath += "/#{args.join("/")}"; args = []
|
41
|
+
when Hash
|
42
|
+
compilable = false
|
43
|
+
key, value = want.to_a.first
|
44
|
+
case value
|
45
|
+
when true
|
46
|
+
cpath += "/#{args.join("/")}"; args = []
|
47
|
+
when String, Symbol
|
48
|
+
compilable = true
|
49
|
+
component = args.shift
|
50
|
+
cpath << "/%s"
|
51
|
+
component ? interpolations << component : interpolations << value
|
52
|
+
when Regexp
|
53
|
+
component = args.shift.to_s
|
54
|
+
raise ArgumentError, "#{component} does not match #{want.inspect}" unless component =~ value
|
55
|
+
cpath << "/%s"; interpolations << component
|
56
|
+
end
|
57
|
+
when Regexp
|
58
|
+
compilable = false
|
59
|
+
component = args.shift.to_s
|
60
|
+
raise ArgumentError, "#{component} does not match #{want.inspect}" unless component =~ want
|
61
|
+
cpath << "/%s"; interpolations << component
|
62
|
+
end
|
63
|
+
end
|
64
|
+
raise ArgumentError, "Too many args" unless args.empty?
|
65
|
+
compiled_paths[template_key] = cpath if compilable
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
def process_hash( template, hash )
|
70
|
+
path = []
|
71
|
+
( "/#{ path * '/' }" ) if template.all? do |want|
|
72
|
+
case want
|
73
|
+
when Symbol
|
74
|
+
raise ArgumentError, "Path generator needs a value for #{want.inspect}" unless component = hash[want]
|
75
|
+
path << component
|
76
|
+
when String then path << want
|
77
|
+
when Hash
|
78
|
+
key, value = want.to_a.first
|
79
|
+
case value
|
80
|
+
when Regexp
|
81
|
+
raise ArgumentError, "Path generator needs a value for #{want.inspect}" unless component = hash[key]
|
82
|
+
raise ArgumentError, "#{component} does not match #{want.inspect}" unless component =~ value
|
83
|
+
path << component
|
84
|
+
when String, Symbol
|
85
|
+
hash.has_key?(key) ? path << hash[key] : path << value
|
86
|
+
when true
|
87
|
+
raise ArgumentError, "Path generator needs a value for #{want.inspect}" unless component = hash[key]
|
88
|
+
path += [component].flatten
|
89
|
+
end
|
90
|
+
when Regexp, true
|
91
|
+
raise ArgumentError, "Path generator can't take an args hash, as it contains a Regexp or the value true"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def original_generate( template, args )
|
97
|
+
if template.is_a? Array and not template.empty?
|
98
|
+
path = []
|
99
|
+
( "/#{ path * '/' }" ) if template.all? do | want |
|
100
|
+
case want
|
101
|
+
when true then path += args
|
102
|
+
when String then path << want
|
103
|
+
when Symbol then path << args.shift
|
104
|
+
when Regexp
|
105
|
+
component = args.shift.to_s
|
106
|
+
raise ArgumentError, "#{component} does not match #{want.inspect}" unless component =~ want
|
107
|
+
path << component
|
108
|
+
when Hash
|
109
|
+
key, value = want.to_a.first
|
110
|
+
case value
|
111
|
+
when true then path += args
|
112
|
+
when String, Symbol
|
113
|
+
# if no args to interpolate, use hash element value as default
|
114
|
+
!args.empty? ? path << args.shift : path << value
|
115
|
+
when Regexp
|
116
|
+
component = args.shift.to_s
|
117
|
+
raise ArgumentError, "#{component} does not match #{want.inspect}" unless component =~ value
|
118
|
+
path << component
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
else
|
123
|
+
"/#{ args * '/' }"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|