Module: Webmachine::Decision::Flow

Includes:
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 collapse

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?



59
60
61
62
63
64
65
66
# File 'lib/webmachine/decision/flow.rb', line 59

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?



54
55
56
# File 'lib/webmachine/decision/flow.rb', line 54

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

#b12Object

Known method?



49
50
51
# File 'lib/webmachine/decision/flow.rb', line 49

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

#b13Object

Service available?



44
45
46
# File 'lib/webmachine/decision/flow.rb', line 44

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

#b3Object

OPTIONS?



135
136
137
138
139
140
141
142
# File 'lib/webmachine/decision/flow.rb', line 135

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

#b4Object

Req Entity Too Large?



130
131
132
# File 'lib/webmachine/decision/flow.rb', line 130

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

#b5Object

Known Content-Type?



125
126
127
# File 'lib/webmachine/decision/flow.rb', line 125

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

#b6Object

Okay Content-* Headers?



120
121
122
# File 'lib/webmachine/decision/flow.rb', line 120

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

#b7Object

Forbidden?



115
116
117
# File 'lib/webmachine/decision/flow.rb', line 115

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

#b8Object

Authorized?



99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/webmachine/decision/flow.rb', line 99

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?



69
70
71
# File 'lib/webmachine/decision/flow.rb', line 69

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

#b9aObject

Content-MD5 valid?



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

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 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?



94
95
96
# File 'lib/webmachine/decision/flow.rb', line 94

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

#c3Object

Accept exists?



145
146
147
148
149
150
151
152
# File 'lib/webmachine/decision/flow.rb', line 145

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?



155
156
157
158
159
160
161
162
163
164
# File 'lib/webmachine/decision/flow.rb', line 155

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?



167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/webmachine/decision/flow.rb', line 167

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?



181
182
183
184
185
186
187
188
# File 'lib/webmachine/decision/flow.rb', line 181

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



32
33
34
35
36
37
38
39
40
41
# File 'lib/webmachine/decision/flow.rb', line 32

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?



191
192
193
194
195
196
197
# File 'lib/webmachine/decision/flow.rb', line 191

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

#e6Object

Acceptable Charset available?



200
201
202
# File 'lib/webmachine/decision/flow.rb', line 200

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)



206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/webmachine/decision/flow.rb', line 206

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?



220
221
222
# File 'lib/webmachine/decision/flow.rb', line 220

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

#g11Object

ETag in If-Match



242
243
244
245
# File 'lib/webmachine/decision/flow.rb', line 242

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?



225
226
227
228
229
# File 'lib/webmachine/decision/flow.rb', line 225

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?



232
233
234
# File 'lib/webmachine/decision/flow.rb', line 232

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

#g9Object

If-Match: * exists?



237
238
239
# File 'lib/webmachine/decision/flow.rb', line 237

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

#h10Object

If-Unmodified-Since exists?



253
254
255
# File 'lib/webmachine/decision/flow.rb', line 253

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

#h11Object

If-Unmodified-Since is valid date?



258
259
260
261
262
263
264
265
# File 'lib/webmachine/decision/flow.rb', line 258

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?



268
269
270
# File 'lib/webmachine/decision/flow.rb', line 268

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

#h7Object

If-Match exists?



248
249
250
# File 'lib/webmachine/decision/flow.rb', line 248

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

#i12Object

If-none-match exists?



291
292
293
# File 'lib/webmachine/decision/flow.rb', line 291

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

#i13Object

If-none-match: * exists?



296
297
298
# File 'lib/webmachine/decision/flow.rb', line 296

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

#i4Object

Moved permanently? (apply PUT to different URI)



273
274
275
276
277
278
279
280
281
282
283
# File 'lib/webmachine/decision/flow.rb', line 273

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?



286
287
288
# File 'lib/webmachine/decision/flow.rb', line 286

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

#j18Object

GET or HEAD?



301
302
303
# File 'lib/webmachine/decision/flow.rb', line 301

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

#k13Object

Etag in if-none-match?



324
325
326
327
# File 'lib/webmachine/decision/flow.rb', line 324

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?



306
307
308
309
310
311
312
313
314
315
316
# File 'lib/webmachine/decision/flow.rb', line 306

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?



319
320
321
# File 'lib/webmachine/decision/flow.rb', line 319

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

#l13Object

If-Modified-Since exists?



348
349
350
# File 'lib/webmachine/decision/flow.rb', line 348

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

#l14Object

IMS is valid date?



353
354
355
356
357
358
359
360
# File 'lib/webmachine/decision/flow.rb', line 353

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

#l15Object

IMS > Now?



363
364
365
# File 'lib/webmachine/decision/flow.rb', line 363

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

#l17Object

Last-Modified > IMS?



368
369
370
# File 'lib/webmachine/decision/flow.rb', line 368

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

#l5Object

Moved temporarily?



330
331
332
333
334
335
336
337
338
339
340
# File 'lib/webmachine/decision/flow.rb', line 330

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?



343
344
345
# File 'lib/webmachine/decision/flow.rb', line 343

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

#m16Object

DELETE?



383
384
385
# File 'lib/webmachine/decision/flow.rb', line 383

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

#m20Object

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



388
389
390
# File 'lib/webmachine/decision/flow.rb', line 388

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

#m20bObject

Did the DELETE complete?



393
394
395
# File 'lib/webmachine/decision/flow.rb', line 393

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

#m5Object

POST?



373
374
375
# File 'lib/webmachine/decision/flow.rb', line 373

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

#m7Object

Server allows POST to missing resource?



378
379
380
# File 'lib/webmachine/decision/flow.rb', line 378

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

#n11Object

Redirect?



403
404
405
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
# File 'lib/webmachine/decision/flow.rb', line 403

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?



439
440
441
# File 'lib/webmachine/decision/flow.rb', line 439

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

#n5Object

Server allows POST to missing resource?



398
399
400
# File 'lib/webmachine/decision/flow.rb', line 398

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

#o14Object

Conflict?



444
445
446
447
448
449
450
451
# File 'lib/webmachine/decision/flow.rb', line 444

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

#o16Object

PUT?



454
455
456
# File 'lib/webmachine/decision/flow.rb', line 454

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

#o18Object

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



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

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?



479
480
481
# File 'lib/webmachine/decision/flow.rb', line 479

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

#o20Object

Response includes an entity?



484
485
486
# File 'lib/webmachine/decision/flow.rb', line 484

def o20
  has_response_body? ? :o18 : 204
end

#p11Object

New resource?



499
500
501
# File 'lib/webmachine/decision/flow.rb', line 499

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

#p3Object

Conflict?



489
490
491
492
493
494
495
496
# File 'lib/webmachine/decision/flow.rb', line 489

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