webmachine 1.2.2 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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