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 collapse

VERSION =

Version of the flow diagram

3
START =

The first state in flow diagram

:b13
CONTENT =
/content-/.freeze

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?



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

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?



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

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

#b12Object

Known method?



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

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

#b13Object

Service available?



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

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

#b3Object

OPTIONS?



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

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

#b4Object

Req Entity Too Large?



135
136
137
# File 'lib/webmachine/decision/flow.rb', line 135

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

#b5Object

Known Content-Type?



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

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

#b6Object

Okay Content-* Headers?



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

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

#b7Object

Forbidden?



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

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

#b8Object

Authorized?



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

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?



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

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

#b9aObject

Content-MD5 valid?



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

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?



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

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

#c3Object

Accept exists?



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

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?



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

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?



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

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

#d5Object

Acceptable language available?



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

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



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

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?



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

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

#e6Object

Acceptable Charset available?



205
206
207
# File 'lib/webmachine/decision/flow.rb', line 205

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)



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

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?



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

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

#g11Object

ETag in If-Match



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

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

#g7Object

Resource exists?



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

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?



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

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

#g9Object

If-Match: * exists?



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

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

#h10Object

If-Unmodified-Since exists?



258
259
260
# File 'lib/webmachine/decision/flow.rb', line 258

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

#h11Object

If-Unmodified-Since is valid date?



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

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?



273
274
275
# File 'lib/webmachine/decision/flow.rb', line 273

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

#h7Object

If-Match exists?



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

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

#i12Object

If-none-match exists?



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

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

#i13Object

If-none-match: * exists?



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

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

#i4Object

Moved permanently? (apply PUT to different URI)



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

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?



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

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

#j18Object

GET or HEAD?



306
307
308
# File 'lib/webmachine/decision/flow.rb', line 306

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

#k13Object

Etag in if-none-match?



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

def k13
  request_etags = request.if_none_match.split(SPLIT_SEMI).map {|etag| ETag.new(etag) }
  resource_etag = resource.generate_etag
  if resource_etag && request_etags.include?(ETag.new(resource_etag))
     :j18
  else
    :l13
  end
end

#k5Object

Moved permanently?



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

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?



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

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

#l13Object

If-Modified-Since exists?



358
359
360
# File 'lib/webmachine/decision/flow.rb', line 358

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

#l14Object

IMS is valid date?



363
364
365
366
367
368
369
370
# File 'lib/webmachine/decision/flow.rb', line 363

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

#l15Object

IMS > Now?



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

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

#l17Object

Last-Modified > IMS?



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

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

#l5Object

Moved temporarily?



340
341
342
343
344
345
346
347
348
349
350
# File 'lib/webmachine/decision/flow.rb', line 340

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?



353
354
355
# File 'lib/webmachine/decision/flow.rb', line 353

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

#m16Object

DELETE?



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

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

#m20Object

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



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

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

#m20bObject

Did the DELETE complete?



403
404
405
# File 'lib/webmachine/decision/flow.rb', line 403

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

#m5Object

POST?



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

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

#m7Object

Server allows POST to missing resource?



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

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

#n11Object

Redirect?



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
440
441
442
443
444
445
446
# File 'lib/webmachine/decision/flow.rb', line 413

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?



449
450
451
# File 'lib/webmachine/decision/flow.rb', line 449

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

#n5Object

Server allows POST to missing resource?



408
409
410
# File 'lib/webmachine/decision/flow.rb', line 408

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

#o14Object

Conflict?



454
455
456
457
458
459
460
461
# File 'lib/webmachine/decision/flow.rb', line 454

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

#o16Object

PUT?



464
465
466
# File 'lib/webmachine/decision/flow.rb', line 464

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

#o18Object

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



470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/webmachine/decision/flow.rb', line 470

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?



489
490
491
# File 'lib/webmachine/decision/flow.rb', line 489

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

#o20Object

Response includes an entity?



494
495
496
# File 'lib/webmachine/decision/flow.rb', line 494

def o20
  has_response_body? ? :o18 : 204
end

#p11Object

New resource?



509
510
511
# File 'lib/webmachine/decision/flow.rb', line 509

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

#p3Object

Conflict?



499
500
501
502
503
504
505
506
# File 'lib/webmachine/decision/flow.rb', line 499

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