Module: Webmachine::Decision::Flow

Includes:
Base64, Conneg, Translation
Included in:
FSM
Defined in:
lib/webmachine/decision/flow.rb

Overview

This module encapsulates all of the decisions in Webmachine's flow-chart. These invoke Resource::Callbacks methods to determine the appropriate response code, headers, and body for the response.

This module is included into FSM, which drives the processing of the chart.

See Also:

Constant Summary

VERSION =

Version of the flow diagram

3
START =

The first state in flow diagram

:b13

Constants included from Conneg

Conneg::HAS_ENCODING

Instance Method Summary (collapse)

Methods included from Translation

#t

Methods included from Conneg

#choose_charset, #choose_encoding, #choose_language, #choose_media_type, #do_choose, #language_match

Instance Method Details

- (Object) b10

Method allowed?



62
63
64
65
66
67
68
69
# File 'lib/webmachine/decision/flow.rb', line 62

def b10
  if resource.allowed_methods.include?(request.method)
    :b9
  else
    response.headers["Allow"] = resource.allowed_methods.join(", ")
    405
  end
end

- (Object) b11

URI too long?



57
58
59
# File 'lib/webmachine/decision/flow.rb', line 57

def b11
  decision_test(resource.uri_too_long?(request.uri), 414, :b10)
end

- (Object) b12

Known method?



52
53
54
# File 'lib/webmachine/decision/flow.rb', line 52

def b12
  decision_test(resource.known_methods.include?(request.method), :b11, 501)
end

- (Object) b13

Service available?



47
48
49
# File 'lib/webmachine/decision/flow.rb', line 47

def b13
  decision_test(resource.service_available?, :b12, 503)
end

- (Object) b3

OPTIONS?



138
139
140
141
142
143
144
145
# File 'lib/webmachine/decision/flow.rb', line 138

def b3
  if request.options?
    response.headers.merge!(resource.options)
    200
  else
    :c3
  end
end

- (Object) b4

Req Entity Too Large?



133
134
135
# File 'lib/webmachine/decision/flow.rb', line 133

def b4
  decision_test(resource.valid_entity_length?(request.content_length), :b3, 413)
end

- (Object) b5

Known Content-Type?



128
129
130
# File 'lib/webmachine/decision/flow.rb', line 128

def b5
  decision_test(resource.known_content_type?(request.content_type), :b4, 415)
end

- (Object) b6

Okay Content-* Headers?



123
124
125
# File 'lib/webmachine/decision/flow.rb', line 123

def b6
  decision_test(resource.valid_content_headers?(request.headers.grep(/content-/)), :b5,  501)
end

- (Object) b7

Forbidden?



118
119
120
# File 'lib/webmachine/decision/flow.rb', line 118

def b7
  decision_test(resource.forbidden?, 403, :b6)
end

- (Object) b8

Authorized?



102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/webmachine/decision/flow.rb', line 102

def b8
  result = resource.is_authorized?(request.authorization)
  case result
  when true
    :b7
  when Fixnum
    result
  when String
    response.headers['WWW-Authenticate'] = result
    401
  else
    401
  end
end

- (Object) b9

Content-MD5 present?



72
73
74
# File 'lib/webmachine/decision/flow.rb', line 72

def b9
  request.content_md5 ? :b9a : :b9b
end

- (Object) b9a

Content-MD5 valid?



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/webmachine/decision/flow.rb', line 77

def b9a
  case valid = resource.validate_content_checksum
  when Fixnum
    valid
  when true
    :b9b
  when false
    response.body = "Content-MD5 header does not match request body."
    400
  else # not_validated
    if decode64(request.content_md5) == Digest::MD5.hexdigest(request.body)
      :b9b
    else
      response.body = "Content-MD5 header does not match request body."
      400
    end
  end
end

- (Object) b9b

Malformed?



97
98
99
# File 'lib/webmachine/decision/flow.rb', line 97

def b9b
  decision_test(resource.malformed_request?, 400, :b8)
end

- (Object) c3

Accept exists?



148
149
150
151
152
153
154
155
# File 'lib/webmachine/decision/flow.rb', line 148

def c3
  if !request.accept
    ['Content-Type'] = MediaType.parse(resource.content_types_provided.first.first)
    :d4
  else
    :c4
  end
end

- (Object) c4

Acceptable media type available?



158
159
160
161
162
163
164
165
166
167
# File 'lib/webmachine/decision/flow.rb', line 158

def c4
  types = resource.content_types_provided.map {|pair| pair.first }
  chosen_type = choose_media_type(types, request.accept)
  if !chosen_type
    406
  else
    ['Content-Type'] = chosen_type
    :d4
  end
end

- (Object) d4

Accept-Language exists?



170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/webmachine/decision/flow.rb', line 170

def d4
  if !request.accept_language
    if language = choose_language(resource.languages_provided, "*")
      resource.language_chosen(language)
      :e5
    else
      406
    end
  else
    :d5
  end
end

- (Object) d5

Acceptable language available?



184
185
186
187
188
189
190
191
# File 'lib/webmachine/decision/flow.rb', line 184

def d5
  if language = choose_language(resource.languages_provided, request.accept_language)
    resource.language_chosen(language)
    :e5
  else
    406
  end
end

- (Object) decision_test(test, iftrue, iffalse)

Handles standard decisions where halting is allowed



35
36
37
38
39
40
41
42
43
44
# File 'lib/webmachine/decision/flow.rb', line 35

def decision_test(test, iftrue, iffalse)
  case test
  when Fixnum # Allows callbacks to "halt" with a given response code
    test
  when Falsey
    iffalse
  else
    iftrue
  end
end

- (Object) e5

Accept-Charset exists?



194
195
196
197
198
199
200
# File 'lib/webmachine/decision/flow.rb', line 194

def e5
  if !request.accept_charset
    choose_charset(resource.charsets_provided, "*") ? :f6 : 406
  else
    :e6
  end
end

- (Object) e6

Acceptable Charset available?



203
204
205
# File 'lib/webmachine/decision/flow.rb', line 203

def e6
  choose_charset(resource.charsets_provided, request.accept_charset) ? :f6 : 406
end

- (Object) f6

Accept-Encoding exists? (also, set content-type header here, now that charset is chosen)



209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/webmachine/decision/flow.rb', line 209

def f6
  chosen_type = ['Content-Type']
  if chosen_charset = ['Charset']
    chosen_type.params['charset'] = chosen_charset
  end
  response.headers['Content-Type'] = chosen_type.to_s
  if !request.accept_encoding
    choose_encoding(resource.encodings_provided, "identity;q=1.0,*;q=0.5") ? :g7 : 406
  else
    :f7
  end
end

- (Object) f7

Acceptable encoding available?



223
224
225
# File 'lib/webmachine/decision/flow.rb', line 223

def f7
  choose_encoding(resource.encodings_provided, request.accept_encoding) ? :g7 : 406
end

- (Object) g11

ETag in If-Match



245
246
247
248
# File 'lib/webmachine/decision/flow.rb', line 245

def g11
  request_etags = request.if_match.split(/\s*,\s*/).map {|etag| ETag.new(etag) }
  request_etags.include?(ETag.new(resource.generate_etag)) ? :h10 : 412
end

- (Object) g7

Resource exists?



228
229
230
231
232
# File 'lib/webmachine/decision/flow.rb', line 228

def g7
  # This is the first place after all conneg, so set Vary here
  response.headers['Vary'] =  variances.join(", ") if variances.any?
  decision_test(resource.resource_exists?, :g8, :h7)
end

- (Object) g8

If-Match exists?



235
236
237
# File 'lib/webmachine/decision/flow.rb', line 235

def g8
  request.if_match ? :g9 : :h10
end

- (Object) g9

If-Match: * exists?



240
241
242
# File 'lib/webmachine/decision/flow.rb', line 240

def g9
  quote(request.if_match) == '"*"' ? :h10 : :g11
end

- (Object) h10

If-Unmodified-Since exists?



256
257
258
# File 'lib/webmachine/decision/flow.rb', line 256

def h10
  request.if_unmodified_since ? :h11 : :i12
end

- (Object) h11

If-Unmodified-Since is valid date?



261
262
263
264
265
266
267
268
# File 'lib/webmachine/decision/flow.rb', line 261

def h11
  date = Time.httpdate(request.if_unmodified_since)
  ['If-Unmodified-Since'] = date
rescue ArgumentError
  :i12
else
  :h12
end

- (Object) h12

Last-Modified > I-UM-S?



271
272
273
# File 'lib/webmachine/decision/flow.rb', line 271

def h12
  resource.last_modified > ['If-Unmodified-Since'] ? 412 : :i12
end

- (Object) h7

If-Match exists?



251
252
253
# File 'lib/webmachine/decision/flow.rb', line 251

def h7
  (request.if_match && unquote(request.if_match) == '*') ? 412 : :i7
end

- (Object) i12

If-none-match exists?



294
295
296
# File 'lib/webmachine/decision/flow.rb', line 294

def i12
  request.if_none_match ? :i13 : :l13
end

- (Object) i13

If-none-match: * exists?



299
300
301
# File 'lib/webmachine/decision/flow.rb', line 299

def i13
  quote(request.if_none_match) == '"*"' ? :j18 : :k13
end

- (Object) i4

Moved permanently? (apply PUT to different URI)



276
277
278
279
280
281
282
283
284
285
286
# File 'lib/webmachine/decision/flow.rb', line 276

def i4
  case uri = resource.moved_permanently?
  when String, URI
    response.headers["Location"] = uri.to_s
    301
  when Fixnum
    uri
  else
    :p3
  end
end

- (Object) i7

PUT?



289
290
291
# File 'lib/webmachine/decision/flow.rb', line 289

def i7
  request.put? ? :i4 : :k7
end

- (Object) j18

GET or HEAD?



304
305
306
# File 'lib/webmachine/decision/flow.rb', line 304

def j18
  (request.get? || request.head?) ? 304 : 412
end

- (Object) k13

Etag in if-none-match?



327
328
329
330
# File 'lib/webmachine/decision/flow.rb', line 327

def k13
  request_etags = request.if_none_match.split(/\s*,\s*/).map {|etag| ETag.new(etag) }
  request_etags.include?(ETag.new(resource.generate_etag)) ? :j18 : :l13
end

- (Object) k5

Moved permanently?



309
310
311
312
313
314
315
316
317
318
319
# File 'lib/webmachine/decision/flow.rb', line 309

def k5
  case uri = resource.moved_permanently?
  when String, URI
    response.headers["Location"] = uri.to_s
    301
  when Fixnum
    uri
  else
    :l5
  end
end

- (Object) k7

Previously existed?



322
323
324
# File 'lib/webmachine/decision/flow.rb', line 322

def k7
  decision_test(resource.previously_existed?, :k5, :l7)
end

- (Object) l13

If-Modified-Since exists?



351
352
353
# File 'lib/webmachine/decision/flow.rb', line 351

def l13
  request.if_modified_since ? :l14 : :m16
end

- (Object) l14

IMS is valid date?



356
357
358
359
360
361
362
363
# File 'lib/webmachine/decision/flow.rb', line 356

def l14
  date = Time.httpdate(request.if_modified_since)
  ['If-Modified-Since'] = date
rescue ArgumentError
  :m16
else
  :l15
end

- (Object) l15

IMS > Now?



366
367
368
# File 'lib/webmachine/decision/flow.rb', line 366

def l15
  ['If-Modified-Since'] > Time.now ? :m16 : :l17
end

- (Object) l17

Last-Modified > IMS?



371
372
373
# File 'lib/webmachine/decision/flow.rb', line 371

def l17
  resource.last_modified.nil? || resource.last_modified > ['If-Modified-Since'] ? :m16 : 304
end

- (Object) l5

Moved temporarily?



333
334
335
336
337
338
339
340
341
342
343
# File 'lib/webmachine/decision/flow.rb', line 333

def l5
  case uri = resource.moved_temporarily?
  when String, URI
    response.headers["Location"] = uri.to_s
    307
  when Fixnum
    uri
  else
    :m5
  end
end

- (Object) l7

POST?



346
347
348
# File 'lib/webmachine/decision/flow.rb', line 346

def l7
  request.post? ? :m7 : 404
end

- (Object) m16

DELETE?



386
387
388
# File 'lib/webmachine/decision/flow.rb', line 386

def m16
  request.delete? ? :m20 : :n16
end

- (Object) m20

DELETE enacted immediately? (Also where DELETE is forced.)



391
392
393
# File 'lib/webmachine/decision/flow.rb', line 391

def m20
  decision_test(resource.delete_resource, :m20b, 500)
end

- (Object) m20b

Did the DELETE complete?



396
397
398
# File 'lib/webmachine/decision/flow.rb', line 396

def m20b
  decision_test(resource.delete_completed?, :o20, 202)
end

- (Object) m5

POST?



376
377
378
# File 'lib/webmachine/decision/flow.rb', line 376

def m5
  request.post? ? :n5 : 410
end

- (Object) m7

Server allows POST to missing resource?



381
382
383
# File 'lib/webmachine/decision/flow.rb', line 381

def m7
  decision_test(resource.allow_missing_post?, :n11, 404)
end

- (Object) n11

Redirect?



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# File 'lib/webmachine/decision/flow.rb', line 406

def n11
  # Stage1
  if resource.post_is_create?
    case uri = resource.create_path
    when nil
      raise InvalidResource, t('create_path_nil', :class => resource.class)
    when URI, String
      base_uri = resource.base_uri || request.base_uri
      new_uri = URI.join(base_uri.to_s, uri)
      request.disp_path = new_uri.path
      response.headers['Location'] = new_uri.to_s
      result = accept_helper
      return result if Fixnum === result
    end
  else
    case result = resource.process_post
    when true
      encode_body_if_set
    when Fixnum
      return result
    else
      raise InvalidResource, t('process_post_invalid', :result => result.inspect)
    end
  end
  if response.is_redirect?
    if response.headers['Location']
      303
    else
      raise InvalidResource, t('do_redirect')
    end
  else
    :p11
  end
end

- (Object) n16

POST?



442
443
444
# File 'lib/webmachine/decision/flow.rb', line 442

def n16
  request.post? ? :n11 : :o16
end

- (Object) n5

Server allows POST to missing resource?



401
402
403
# File 'lib/webmachine/decision/flow.rb', line 401

def n5
  decision_test(resource.allow_missing_post?, :n11, 410)
end

- (Object) o14

Conflict?



447
448
449
450
451
452
453
454
# File 'lib/webmachine/decision/flow.rb', line 447

def o14
  if resource.is_conflict?
    409
  else
    res = accept_helper
    (Fixnum === res) ? res : :p11
  end
end

- (Object) o16

PUT?



457
458
459
# File 'lib/webmachine/decision/flow.rb', line 457

def o16
  request.put? ? :o14 : :o18
end

- (Object) o18

Multiple representations? Also where body generation for GET and HEAD is done.



463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/webmachine/decision/flow.rb', line 463

def o18
  if request.get? || request.head?
    add_caching_headers
    content_type = ['Content-Type']
    handler = resource.content_types_provided.find {|ct, _| content_type.type_matches?(MediaType.parse(ct)) }.last
    result = resource.send(handler)
    if Fixnum === result
      result
    else
      response.body = result
      encode_body
      :o18b
    end
  else
    :o18b
  end
end

- (Object) o18b

Multiple choices?



482
483
484
# File 'lib/webmachine/decision/flow.rb', line 482

def o18b
  decision_test(resource.multiple_choices?, 300, 200)
end

- (Object) o20

Response includes an entity?



487
488
489
# File 'lib/webmachine/decision/flow.rb', line 487

def o20
  has_response_body? ? :o18 : 204
end

- (Object) p11

New resource?



502
503
504
# File 'lib/webmachine/decision/flow.rb', line 502

def p11
  !response.headers["Location"] ? :o20 : 201
end

- (Object) p3

Conflict?



492
493
494
495
496
497
498
499
# File 'lib/webmachine/decision/flow.rb', line 492

def p3
  if resource.is_conflict?
    409
  else
    res = accept_helper
    (Fixnum === res) ? res : :p11
  end
end