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.

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

#b10Object

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

#b11Object

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

#b12Object

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

#b13Object

Service available?



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

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

#b3Object

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

#b4Object

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

#b5Object

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

#b6Object

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

#b7Object

Forbidden?



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

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

#b8Object

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

#b9Object

Content-MD5 present?



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

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

#b9aObject

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

#b9bObject

Malformed?



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

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

#c3Object

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

#c4Object

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

#d4Object

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

#d5Object

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

#decision_test(test, iftrue, iffalse) ⇒ Object

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

#e5Object

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

#e6Object

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

#f6Object

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

#f7Object

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

#g11Object

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

#g7Object

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

#g8Object

If-Match exists?



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

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

#g9Object

If-Match: * exists?



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

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

#h10Object

If-Unmodified-Since exists?



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

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

#h11Object

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

#h12Object

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

#h7Object

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

#i12Object

If-none-match exists?



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

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

#i13Object

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

#i4Object

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

#i7Object

PUT?



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

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

#j18Object

GET or HEAD?



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

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

#k13Object

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

#k5Object

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

#k7Object

Previously existed?



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

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

#l13Object

If-Modified-Since exists?



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

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

#l14Object

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

#l15Object

IMS > Now?



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

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

#l17Object

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

#l5Object

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

#l7Object

POST?



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

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

#m16Object

DELETE?



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

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

#m20Object

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

#m20bObject

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

#m5Object

POST?



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

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

#m7Object

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

#n11Object

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

#n16Object

POST?



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

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

#n5Object

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

#o14Object

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

#o16Object

PUT?



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

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

#o18Object

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

#o18bObject

Multiple choices?



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

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

#o20Object

Response includes an entity?



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

def o20
  has_response_body? ? :o18 : 204
end

#p11Object

New resource?



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

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

#p3Object

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