summaryrefslogtreecommitdiff
path: root/lib/metropolis/common.rb
blob: 383ebe9f283b1b40ab4f012ae1ea842d425ef20a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# -*- encoding: binary -*-
module Metropolis::Common
  include Rack::Utils # unescape
  include Metropolis::Constants
  HTTP_STATUS_BODIES = {}

  autoload :RO, 'metropolis/common/ro'

  def setup(opts)
    @headers = { Content_Type => 'application/octet-stream' }
    @headers.merge!(opts[:response_headers] || {})
    @nr_slots = opts[:nr_slots]

    if @path_pattern
      @nr_slots ||= 3
      @uri.path == '/' or
        raise ArgumentError, ":path_pattern may only be used if path is '/'"
      @path_pattern.scan(/%\d*x/).size == 1 or
        raise ArgumentError, "only one '/%\d*x/' may appear in #@path_pattern"
    else
      @nr_slots and
        raise ArgumentError, ":nr_slots may be used with :path_pattern"
    end

    @readonly = !!opts[:readonly]
    @exclusive = !!opts[:exclusive]
    if @readonly && @exclusive
      raise ArgumentError, ":readonly and :exclusive may not be used together"
    end
    case @encoding = opts[:encoding]
    when nil
    when :deflate
      extend(Metropolis::Deflate)
    when :gzip
      extend(Metropolis::Gzip)
    else
      raise ArgumentError, "unsupported encoding"
    end
    if filters = opts[:use]
      Array(filters).each { |filter| extend filter }
    end
  end

  def r(code, body = nil)
    body ||= HTTP_STATUS_BODIES[code] ||= "#{HTTP_STATUS_CODES[code]}\n"
    [ code,
      { Content_Length => body.size.to_s, Content_Type => Text_Plain },
      [ body ] ]
  end

  def call(env)
    if %r{\A/(.*)\z} =~ env[PATH_INFO]
      key = unescape($1)
      case env[REQUEST_METHOD]
      when "GET"
        get(key, env)
      when "HEAD"
        head(key, env)
      when "DELETE"
        delete(key)
      when "PUT"
        put(key, env)
      else
        r(405)
      end
    else # OPTIONS
      r(405)
    end
  end

  # generic HEAD implementation, some databases can optimize this by
  # not retrieving the value
  def head(key, env)
    r = get(key, env)
    r[2].clear
    r
  end
end