webmachine 1.2.2 → 1.6.0

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 (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/CHANGELOG.md +57 -0
  4. data/Gemfile +20 -15
  5. data/README.md +89 -91
  6. data/RELEASING.md +21 -0
  7. data/Rakefile +5 -21
  8. data/documentation/adapters.md +41 -0
  9. data/documentation/authentication-and-authorization.md +37 -0
  10. data/documentation/configurator.md +19 -0
  11. data/documentation/error-handling.md +86 -0
  12. data/documentation/examples.md +224 -0
  13. data/documentation/how-it-works.md +76 -0
  14. data/documentation/routes.md +112 -0
  15. data/documentation/validation.md +159 -0
  16. data/documentation/versioning-apis.md +74 -0
  17. data/documentation/visual-debugger.md +38 -0
  18. data/examples/application.rb +2 -2
  19. data/examples/debugger.rb +1 -1
  20. data/lib/webmachine.rb +3 -1
  21. data/lib/webmachine/adapter.rb +7 -13
  22. data/lib/webmachine/adapters.rb +1 -2
  23. data/lib/webmachine/adapters/httpkit.rb +74 -0
  24. data/lib/webmachine/adapters/lazy_request_body.rb +1 -2
  25. data/lib/webmachine/adapters/rack.rb +70 -25
  26. data/lib/webmachine/adapters/rack_mapped.rb +42 -0
  27. data/lib/webmachine/adapters/reel.rb +22 -23
  28. data/lib/webmachine/adapters/webrick.rb +16 -16
  29. data/lib/webmachine/application.rb +2 -2
  30. data/lib/webmachine/chunked_body.rb +3 -4
  31. data/lib/webmachine/configuration.rb +1 -1
  32. data/lib/webmachine/constants.rb +75 -0
  33. data/lib/webmachine/decision/conneg.rb +12 -10
  34. data/lib/webmachine/decision/flow.rb +42 -32
  35. data/lib/webmachine/decision/fsm.rb +14 -21
  36. data/lib/webmachine/decision/helpers.rb +10 -38
  37. data/lib/webmachine/dispatcher.rb +13 -10
  38. data/lib/webmachine/dispatcher/route.rb +45 -9
  39. data/lib/webmachine/errors.rb +9 -3
  40. data/lib/webmachine/events.rb +2 -2
  41. data/lib/webmachine/header_negotiation.rb +25 -0
  42. data/lib/webmachine/headers.rb +8 -3
  43. data/lib/webmachine/locale/en.yml +7 -5
  44. data/lib/webmachine/media_type.rb +10 -8
  45. data/lib/webmachine/request.rb +67 -26
  46. data/lib/webmachine/rescueable_exception.rb +62 -0
  47. data/lib/webmachine/resource.rb +1 -1
  48. data/lib/webmachine/resource/callbacks.rb +11 -9
  49. data/lib/webmachine/response.rb +3 -5
  50. data/lib/webmachine/spec/IO_response.body +1 -0
  51. data/lib/webmachine/spec/adapter_lint.rb +83 -37
  52. data/lib/webmachine/spec/test_resource.rb +15 -4
  53. data/lib/webmachine/streaming/fiber_encoder.rb +1 -5
  54. data/lib/webmachine/streaming/io_encoder.rb +7 -1
  55. data/lib/webmachine/trace.rb +1 -0
  56. data/lib/webmachine/trace/fsm.rb +20 -10
  57. data/lib/webmachine/trace/resource_proxy.rb +2 -0
  58. data/lib/webmachine/translation.rb +2 -1
  59. data/lib/webmachine/version.rb +3 -3
  60. data/memory_test.rb +37 -0
  61. data/spec/spec_helper.rb +17 -9
  62. data/spec/webmachine/adapter_spec.rb +14 -15
  63. data/spec/webmachine/adapters/httpkit_spec.rb +10 -0
  64. data/spec/webmachine/adapters/rack_mapped_spec.rb +71 -0
  65. data/spec/webmachine/adapters/rack_spec.rb +32 -6
  66. data/spec/webmachine/adapters/reel_spec.rb +16 -12
  67. data/spec/webmachine/adapters/webrick_spec.rb +2 -2
  68. data/spec/webmachine/application_spec.rb +18 -17
  69. data/spec/webmachine/chunked_body_spec.rb +3 -3
  70. data/spec/webmachine/configuration_spec.rb +5 -5
  71. data/spec/webmachine/cookie_spec.rb +13 -13
  72. data/spec/webmachine/decision/conneg_spec.rb +49 -43
  73. data/spec/webmachine/decision/falsey_spec.rb +4 -4
  74. data/spec/webmachine/decision/flow_spec.rb +195 -145
  75. data/spec/webmachine/decision/fsm_spec.rb +81 -19
  76. data/spec/webmachine/decision/helpers_spec.rb +20 -20
  77. data/spec/webmachine/dispatcher/rfc3986_percent_decode_spec.rb +22 -0
  78. data/spec/webmachine/dispatcher/route_spec.rb +114 -32
  79. data/spec/webmachine/dispatcher_spec.rb +49 -24
  80. data/spec/webmachine/errors_spec.rb +1 -1
  81. data/spec/webmachine/etags_spec.rb +19 -19
  82. data/spec/webmachine/events_spec.rb +6 -6
  83. data/spec/webmachine/headers_spec.rb +14 -14
  84. data/spec/webmachine/media_type_spec.rb +36 -36
  85. data/spec/webmachine/request_spec.rb +70 -39
  86. data/spec/webmachine/rescueable_exception_spec.rb +15 -0
  87. data/spec/webmachine/resource/authentication_spec.rb +6 -6
  88. data/spec/webmachine/response_spec.rb +18 -12
  89. data/spec/webmachine/trace/fsm_spec.rb +8 -8
  90. data/spec/webmachine/trace/resource_proxy_spec.rb +9 -9
  91. data/spec/webmachine/trace/trace_store_spec.rb +5 -5
  92. data/spec/webmachine/trace_spec.rb +3 -3
  93. data/webmachine.gemspec +2 -6
  94. metadata +78 -228
  95. data/lib/webmachine/adapters/hatetepe.rb +0 -108
  96. data/lib/webmachine/adapters/mongrel.rb +0 -127
  97. data/lib/webmachine/dispatcher/not_found_resource.rb +0 -5
  98. data/lib/webmachine/fiber18.rb +0 -88
  99. data/spec/webmachine/adapters/hatetepe_spec.rb +0 -60
  100. data/spec/webmachine/adapters/mongrel_spec.rb +0 -16
@@ -8,7 +8,7 @@ module Webmachine
8
8
  #
9
9
  # MyApp = Webmachine::Application.new do |app|
10
10
  # app.routes do
11
- # add ['*'], AssetResource
11
+ # add [:*], AssetResource
12
12
  # end
13
13
  #
14
14
  # app.configure do |config|
@@ -56,7 +56,7 @@ module Webmachine
56
56
  # @return an instance of the configured web-server adapter
57
57
  # @see Adapters
58
58
  def adapter
59
- @adapter ||= adapter_class.new(configuration, dispatcher)
59
+ @adapter ||= adapter_class.new(self)
60
60
  end
61
61
 
62
62
  # @return an instance of the configured web-server adapter
@@ -1,3 +1,5 @@
1
+ require 'webmachine/constants'
2
+
1
3
  module Webmachine
2
4
  # {ChunkedBody} is used to wrap an {Enumerable} object (like an enumerable
3
5
  # {Response#body}) so it yields proper chunks for chunked transfer encoding.
@@ -13,11 +15,8 @@ module Webmachine
13
15
  #
14
16
  # This is needed for Ruby webservers which don't do the chunking themselves.
15
17
  class ChunkedBody
16
- # Delimiter for chunked encoding
17
- CRLF = "\r\n"
18
-
19
18
  # Final chunk in any chunked-encoding response
20
- FINAL_CHUNK = "0#{CRLF}#{CRLF}"
19
+ FINAL_CHUNK = "0#{CRLF}#{CRLF}".freeze
21
20
 
22
21
  # Creates a new {ChunkedBody} from the given {Enumerable}.
23
22
  # @param [Enumerable] body the enumerable response body
@@ -5,7 +5,7 @@ module Webmachine
5
5
  # defaults will be filled in when {Webmachine::run} is called.
6
6
  # @attr [String] ip the interface to bind to, defaults to "0.0.0.0"
7
7
  # (all interfaces)
8
- # @attr [Fixnum] port the port to bind to, defaults to 8080
8
+ # @attr [Integer] port the port to bind to, defaults to 8080
9
9
  # @attr [Symbol] adapter the adapter to use, defaults to :WEBrick
10
10
  # @attr [Hash] adapter_options adapter-specific options, defaults to {}
11
11
  Configuration = Struct.new(:ip, :port, :adapter, :adapter_options)
@@ -0,0 +1,75 @@
1
+ module Webmachine
2
+ # Universal HTTP delimiter
3
+ CRLF = "\r\n".freeze
4
+
5
+ # HTTP Content-Type
6
+ CONTENT_TYPE = 'Content-Type'.freeze
7
+
8
+ # Default Content-Type
9
+ TEXT_HTML = 'text/html'.freeze
10
+
11
+ # HTTP Date
12
+ DATE = 'Date'.freeze
13
+
14
+ # HTTP Transfer-Encoding
15
+ TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
16
+
17
+ # HTTP Content-Length
18
+ CONTENT_LENGTH = 'Content-Length'.freeze
19
+
20
+ # A underscore
21
+ UNDERSCORE = '_'.freeze
22
+
23
+ # A dash
24
+ DASH = '-'.freeze
25
+
26
+ # A Slash
27
+ SLASH = '/'.freeze
28
+
29
+ MATCHES_ALL = '*/*'.freeze
30
+
31
+ GET_METHOD = "GET"
32
+ HEAD_METHOD = "HEAD"
33
+ POST_METHOD = "POST"
34
+ PUT_METHOD = "PUT"
35
+ DELETE_METHOD = "DELETE"
36
+ OPTIONS_METHOD = "OPTIONS"
37
+ TRACE_METHOD = "TRACE"
38
+ CONNECT_METHOD = "CONNECT"
39
+
40
+ STANDARD_HTTP_METHODS = [
41
+ GET_METHOD, HEAD_METHOD, POST_METHOD,
42
+ PUT_METHOD, DELETE_METHOD, TRACE_METHOD,
43
+ CONNECT_METHOD, OPTIONS_METHOD
44
+ ].map!(&:freeze)
45
+ STANDARD_HTTP_METHODS.freeze
46
+
47
+ # A colon
48
+ COLON = ':'.freeze
49
+
50
+ # http string
51
+ HTTP = 'http'.freeze
52
+
53
+ # Host string
54
+ HOST = 'Host'.freeze
55
+
56
+ # HTTP Content-Encoding
57
+ CONTENT_ENCODING = 'Content-Encoding'.freeze
58
+
59
+ # Charset string
60
+ CHARSET = 'Charset'.freeze
61
+
62
+ # Comma split match
63
+ SPLIT_COMMA = /\s*,\s*/.freeze
64
+
65
+ # Star Character
66
+ STAR = '*'.freeze
67
+
68
+ # HTTP Location
69
+ LOCATION = 'Location'.freeze
70
+
71
+ # identity Encoding
72
+ IDENTITY = 'identity'.freeze
73
+
74
+ SERVER = 'Server'.freeze
75
+ end
@@ -1,3 +1,4 @@
1
+ require 'webmachine/constants'
1
2
  require 'webmachine/translation'
2
3
  require 'webmachine/media_type'
3
4
 
@@ -13,7 +14,8 @@ module Webmachine
13
14
  # appropriate media type.
14
15
  # @api private
15
16
  def choose_media_type(provided, header)
16
- requested = MediaTypeList.build(header.split(/\s*,\s*/))
17
+ types = Array(header).map{|h| h.split(SPLIT_COMMA) }.flatten
18
+ requested = MediaTypeList.build(types)
17
19
  provided = provided.map do |p| # normalize_provided
18
20
  MediaType.parse(p)
19
21
  end
@@ -30,9 +32,9 @@ module Webmachine
30
32
  # @api private
31
33
  def choose_encoding(provided, header)
32
34
  encodings = provided.keys
33
- if encoding = do_choose(encodings, header, "identity")
34
- response.headers['Content-Encoding'] = encoding unless encoding == 'identity'
35
- metadata['Content-Encoding'] = encoding
35
+ if encoding = do_choose(encodings, header, IDENTITY)
36
+ response.headers[CONTENT_ENCODING] = encoding unless encoding == IDENTITY
37
+ metadata[CONTENT_ENCODING] = encoding
36
38
  end
37
39
  end
38
40
 
@@ -43,7 +45,7 @@ module Webmachine
43
45
  if provided && !provided.empty?
44
46
  charsets = provided.map {|c| c.first }
45
47
  if charset = do_choose(charsets, header, HAS_ENCODING ? Encoding.default_external.name : kcode_charset)
46
- metadata['Charset'] = charset
48
+ metadata[CHARSET] = charset
47
49
  end
48
50
  else
49
51
  true
@@ -55,8 +57,8 @@ module Webmachine
55
57
  # @api private
56
58
  def choose_language(provided, header)
57
59
  if provided && !provided.empty?
58
- requested = PriorityList.build(header.split(/\s*,\s*/))
59
- star_priority = requested.priority_of("*")
60
+ requested = PriorityList.build(header.split(SPLIT_COMMA))
61
+ star_priority = requested.priority_of(STAR)
60
62
  any_ok = star_priority && star_priority > 0.0
61
63
  accepted = requested.find do |priority, range|
62
64
  if priority == 0.0
@@ -97,9 +99,9 @@ module Webmachine
97
99
  # @api private
98
100
  def do_choose(choices, header, default)
99
101
  choices = choices.dup.map {|s| s.downcase }
100
- accepted = PriorityList.build(header.split(/\s*,\s*/))
102
+ accepted = PriorityList.build(header.split(SPLIT_COMMA))
101
103
  default_priority = accepted.priority_of(default)
102
- star_priority = accepted.priority_of("*")
104
+ star_priority = accepted.priority_of(STAR)
103
105
  default_ok = (default_priority.nil? && star_priority != 0.0) || default_priority
104
106
  any_ok = star_priority && star_priority > 0.0
105
107
  chosen = accepted.find do |priority, acceptable|
@@ -117,7 +119,7 @@ module Webmachine
117
119
 
118
120
  private
119
121
  # Matches acceptable items that include 'q' values
120
- CONNEG_REGEX = /^\s*(\S+);\s*q=(\S*)\s*$/
122
+ CONNEG_REGEX = /^\s*(\S+);\s*q=(\S*)\s*$/.freeze
121
123
 
122
124
  # Matches the requested media type (with potential modifiers)
123
125
  # against the provided types (with potential modifiers).
@@ -1,5 +1,7 @@
1
- require 'time'
1
+ require 'time'
2
2
  require 'digest/md5'
3
+ require 'base64'
4
+ require 'webmachine/constants'
3
5
  require 'webmachine/decision/conneg'
4
6
  require 'webmachine/decision/falsey'
5
7
  require 'webmachine/translation'
@@ -16,6 +18,8 @@ module Webmachine
16
18
  # of the chart.
17
19
  # @see https://raw.github.com/wiki/basho/webmachine/images/http-headers-status-v3.png
18
20
  module Flow
21
+ include Base64
22
+
19
23
  # Version of the flow diagram
20
24
  VERSION = 3
21
25
 
@@ -31,7 +35,7 @@ module Webmachine
31
35
  # Handles standard decisions where halting is allowed
32
36
  def decision_test(test, iftrue, iffalse)
33
37
  case test
34
- when Fixnum # Allows callbacks to "halt" with a given response code
38
+ when Integer # Allows callbacks to "halt" with a given response code
35
39
  test
36
40
  when Falsey
37
41
  iffalse
@@ -73,7 +77,7 @@ module Webmachine
73
77
  # Content-MD5 valid?
74
78
  def b9a
75
79
  case valid = resource.validate_content_checksum
76
- when Fixnum
80
+ when Integer
77
81
  valid
78
82
  when true
79
83
  :b9b
@@ -81,7 +85,7 @@ module Webmachine
81
85
  response.body = "Content-MD5 header does not match request body."
82
86
  400
83
87
  else # not_validated
84
- if request.content_md5 == Digest::MD5.hexdigest(request.body)
88
+ if decode64(request.content_md5) == Digest::MD5.hexdigest(request.body)
85
89
  :b9b
86
90
  else
87
91
  response.body = "Content-MD5 header does not match request body."
@@ -101,7 +105,7 @@ module Webmachine
101
105
  case result
102
106
  when true
103
107
  :b7
104
- when Fixnum
108
+ when Integer
105
109
  result
106
110
  when String
107
111
  response.headers['WWW-Authenticate'] = result
@@ -116,9 +120,10 @@ module Webmachine
116
120
  decision_test(resource.forbidden?, 403, :b6)
117
121
  end
118
122
 
123
+ CONTENT = /content-/.freeze
119
124
  # Okay Content-* Headers?
120
125
  def b6
121
- decision_test(resource.valid_content_headers?(request.headers.grep(/content-/)), :b5, 501)
126
+ decision_test(resource.valid_content_headers?(request.headers.grep(CONTENT)), :b5, 501)
122
127
  end
123
128
 
124
129
  # Known Content-Type?
@@ -144,7 +149,7 @@ module Webmachine
144
149
  # Accept exists?
145
150
  def c3
146
151
  if !request.accept
147
- metadata['Content-Type'] = MediaType.parse(resource.content_types_provided.first.first)
152
+ metadata[CONTENT_TYPE] = MediaType.parse(resource.content_types_provided.first.first)
148
153
  :d4
149
154
  else
150
155
  :c4
@@ -158,7 +163,7 @@ module Webmachine
158
163
  if !chosen_type
159
164
  406
160
165
  else
161
- metadata['Content-Type'] = chosen_type
166
+ metadata[CONTENT_TYPE] = chosen_type
162
167
  :d4
163
168
  end
164
169
  end
@@ -166,7 +171,7 @@ module Webmachine
166
171
  # Accept-Language exists?
167
172
  def d4
168
173
  if !request.accept_language
169
- if language = choose_language(resource.languages_provided, "*")
174
+ if language = choose_language(resource.languages_provided, STAR)
170
175
  resource.language_chosen(language)
171
176
  :e5
172
177
  else
@@ -190,7 +195,7 @@ module Webmachine
190
195
  # Accept-Charset exists?
191
196
  def e5
192
197
  if !request.accept_charset
193
- choose_charset(resource.charsets_provided, "*") ? :f6 : 406
198
+ choose_charset(resource.charsets_provided, STAR) ? :f6 : 406
194
199
  else
195
200
  :e6
196
201
  end
@@ -204,11 +209,11 @@ module Webmachine
204
209
  # Accept-Encoding exists?
205
210
  # (also, set content-type header here, now that charset is chosen)
206
211
  def f6
207
- chosen_type = metadata['Content-Type']
208
- if chosen_charset = metadata['Charset']
212
+ chosen_type = metadata[CONTENT_TYPE]
213
+ if chosen_charset = metadata[CHARSET]
209
214
  chosen_type.params['charset'] = chosen_charset
210
215
  end
211
- response.headers['Content-Type'] = chosen_type.to_s
216
+ response.headers[CONTENT_TYPE] = chosen_type.to_s
212
217
  if !request.accept_encoding
213
218
  choose_encoding(resource.encodings_provided, "identity;q=1.0,*;q=0.5") ? :g7 : 406
214
219
  else
@@ -240,13 +245,13 @@ module Webmachine
240
245
 
241
246
  # ETag in If-Match
242
247
  def g11
243
- request_etags = request.if_match.split(/\s*,\s*/).map {|etag| ETag.new(etag) }
248
+ request_etags = request.if_match.split(SPLIT_COMMA).map {|etag| ETag.new(etag) }
244
249
  request_etags.include?(ETag.new(resource.generate_etag)) ? :h10 : 412
245
250
  end
246
251
 
247
252
  # If-Match exists?
248
253
  def h7
249
- (request.if_match && unquote(request.if_match) == '*') ? 412 : :i7
254
+ (request.if_match && unquote(request.if_match) == STAR) ? 412 : :i7
250
255
  end
251
256
 
252
257
  # If-Unmodified-Since exists?
@@ -273,9 +278,9 @@ module Webmachine
273
278
  def i4
274
279
  case uri = resource.moved_permanently?
275
280
  when String, URI
276
- response.headers["Location"] = uri.to_s
281
+ response.headers[LOCATION] = uri.to_s
277
282
  301
278
- when Fixnum
283
+ when Integer
279
284
  uri
280
285
  else
281
286
  :p3
@@ -306,9 +311,9 @@ module Webmachine
306
311
  def k5
307
312
  case uri = resource.moved_permanently?
308
313
  when String, URI
309
- response.headers["Location"] = uri.to_s
314
+ response.headers[LOCATION] = uri.to_s
310
315
  301
311
- when Fixnum
316
+ when Integer
312
317
  uri
313
318
  else
314
319
  :l5
@@ -322,17 +327,22 @@ module Webmachine
322
327
 
323
328
  # Etag in if-none-match?
324
329
  def k13
325
- request_etags = request.if_none_match.split(/\s*,\s*/).map {|etag| ETag.new(etag) }
326
- request_etags.include?(ETag.new(resource.generate_etag)) ? :j18 : :l13
330
+ request_etags = request.if_none_match.split(SPLIT_COMMA).map {|etag| ETag.new(etag) }
331
+ resource_etag = resource.generate_etag
332
+ if resource_etag && request_etags.include?(ETag.new(resource_etag))
333
+ :j18
334
+ else
335
+ :l13
336
+ end
327
337
  end
328
338
 
329
339
  # Moved temporarily?
330
340
  def l5
331
341
  case uri = resource.moved_temporarily?
332
342
  when String, URI
333
- response.headers["Location"] = uri.to_s
343
+ response.headers[LOCATION] = uri.to_s
334
344
  307
335
- when Fixnum
345
+ when Integer
336
346
  uri
337
347
  else
338
348
  :m5
@@ -410,22 +420,22 @@ module Webmachine
410
420
  base_uri = resource.base_uri || request.base_uri
411
421
  new_uri = URI.join(base_uri.to_s, uri)
412
422
  request.disp_path = new_uri.path
413
- response.headers['Location'] = new_uri.to_s
423
+ response.headers[LOCATION] = new_uri.to_s
414
424
  result = accept_helper
415
- return result if Fixnum === result
425
+ return result if Integer === result
416
426
  end
417
427
  else
418
428
  case result = resource.process_post
419
429
  when true
420
430
  encode_body_if_set
421
- when Fixnum
431
+ when Integer
422
432
  return result
423
433
  else
424
434
  raise InvalidResource, t('process_post_invalid', :result => result.inspect)
425
435
  end
426
436
  end
427
437
  if response.is_redirect?
428
- if response.headers['Location']
438
+ if response.headers[LOCATION]
429
439
  303
430
440
  else
431
441
  raise InvalidResource, t('do_redirect')
@@ -446,7 +456,7 @@ module Webmachine
446
456
  409
447
457
  else
448
458
  res = accept_helper
449
- (Fixnum === res) ? res : :p11
459
+ (Integer === res) ? res : :p11
450
460
  end
451
461
  end
452
462
 
@@ -460,10 +470,10 @@ module Webmachine
460
470
  def o18
461
471
  if request.get? || request.head?
462
472
  add_caching_headers
463
- content_type = metadata['Content-Type']
473
+ content_type = metadata[CONTENT_TYPE]
464
474
  handler = resource.content_types_provided.find {|ct, _| content_type.type_matches?(MediaType.parse(ct)) }.last
465
475
  result = resource.send(handler)
466
- if Fixnum === result
476
+ if Integer === result
467
477
  result
468
478
  else
469
479
  response.body = result
@@ -491,13 +501,13 @@ module Webmachine
491
501
  409
492
502
  else
493
503
  res = accept_helper
494
- (Fixnum === res) ? res : :p11
504
+ (Integer === res) ? res : :p11
495
505
  end
496
506
  end
497
507
 
498
508
  # New resource?
499
509
  def p11
500
- !response.headers["Location"] ? :o20 : 201
510
+ !response.headers[LOCATION] ? :o20 : 201
501
511
  end
502
512
 
503
513
  end # module Flow
@@ -1,5 +1,8 @@
1
- require 'webmachine/decision/helpers'
1
+ require 'webmachine/decision/helpers'
2
+ require 'webmachine/trace'
2
3
  require 'webmachine/translation'
4
+ require 'webmachine/constants'
5
+ require 'webmachine/rescueable_exception'
3
6
 
4
7
  module Webmachine
5
8
  module Decision
@@ -8,6 +11,7 @@ module Webmachine
8
11
  class FSM
9
12
  include Flow
10
13
  include Helpers
14
+ include Trace::FSM
11
15
  include Translation
12
16
 
13
17
  attr_reader :resource, :request, :response, :metadata
@@ -15,7 +19,7 @@ module Webmachine
15
19
  def initialize(resource, request, response)
16
20
  @resource, @request, @response = resource, request, response
17
21
  @metadata = {}
18
- initialize_tracing
22
+ super
19
23
  end
20
24
 
21
25
  # Processes the request, iteratively invoking the decision methods in {Flow}.
@@ -26,7 +30,7 @@ module Webmachine
26
30
  trace_decision(state)
27
31
  result = handle_exceptions { send(state) }
28
32
  case result
29
- when Fixnum # Response code
33
+ when Integer # Response code
30
34
  respond(result)
31
35
  break
32
36
  when Symbol # Next state
@@ -35,7 +39,7 @@ module Webmachine
35
39
  raise InvalidResource, t('fsm_broke', :state => state, :result => result.inspect)
36
40
  end
37
41
  end
38
- rescue Exception => e
42
+ rescue => e
39
43
  Webmachine.render_error(500, request, response, :message => e.message)
40
44
  ensure
41
45
  trace_response(response)
@@ -45,12 +49,12 @@ module Webmachine
45
49
 
46
50
  def handle_exceptions
47
51
  yield
52
+ rescue Webmachine::RescuableException => e
53
+ resource.handle_exception(e)
54
+ 500
48
55
  rescue MalformedRequest => e
49
56
  Webmachine.render_error(400, request, response, :message => e.message)
50
57
  400
51
- rescue Exception => e
52
- resource.handle_exception(e)
53
- 500
54
58
  end
55
59
 
56
60
  def respond(code, headers={})
@@ -60,7 +64,7 @@ module Webmachine
60
64
  when 404
61
65
  Webmachine.render_error(code, request, response)
62
66
  when 304
63
- response.headers.delete('Content-Type')
67
+ response.headers.delete(CONTENT_TYPE)
64
68
  add_caching_headers
65
69
  end
66
70
 
@@ -69,19 +73,8 @@ module Webmachine
69
73
  response.code
70
74
  end
71
75
 
72
- ensure_content_length
73
- ensure_date_header
74
- end
75
-
76
- # When tracing is disabled, this does nothing.
77
- def trace_decision(state); end
78
- # When tracing is disabled, this does nothing.
79
- def trace_request(request); end
80
- # When tracing is disabled, this does nothing.
81
- def trace_response(response); end
82
-
83
- def initialize_tracing
84
- extend Trace::FSM if Trace.trace?(resource)
76
+ ensure_content_length(response)
77
+ ensure_date_header(response)
85
78
  end
86
79
  end # class FSM
87
80
  end # module Decision