joshsusser (owner)

Revisions

gist: 217721 Download_button fork
public
Public Clone URL: git://gist.github.com/217721.git
Embed All Files: show embed
refraction.rb #
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# full project and documentation for Refraction is available at
# http://github.com/pivotal/refraction
 
require 'rack'
 
class Refraction
  class RequestContext
    attr_reader :env
    attr_reader :status, :message, :action
 
    def initialize(env)
      @action = nil
      @env = env
 
      hostname = env['SERVER_NAME'] # because the rack mock doesn't set the HTTP_HOST
      hostname = env['HTTP_HOST'].split(':').first if env['HTTP_HOST']
      env_path = env['PATH_INFO'] || env['REQUEST_PATH']
 
      @uri = URI::Generic.build(
        :scheme => env['rack.url_scheme'],
        :host => hostname,
        :path => env_path.empty? ? '/' : env_path
      )
      unless [URI::HTTP::DEFAULT_PORT, URI::HTTPS::DEFAULT_PORT].include?(env['SERVER_PORT'].to_i)
        @uri.port = env['SERVER_PORT']
      end
      @uri.query = env['QUERY_STRING'] if env['QUERY_STRING'] && !env['QUERY_STRING'].empty?
    end
 
    def response
      headers = {
        'Location' => location,
        'Content-Type' => 'text/plain',
        'Content-Length' => message.length.to_s
      }
      [status, headers, message]
    end
 
    # URI part accessors
 
    def scheme
      @uri.scheme
    end
 
    def host
      @uri.host
    end
 
    def port
      @uri.port
    end
 
    def path
      @uri.path
    end
 
    def query
      @uri.query
    end
 
    def method
      @env['REQUEST_METHOD']
    end
 
    # actions
 
    def set(options)
      if options.is_a?(String)
        @uri = URI.parse(options)
      else
        @uri.port = nil
        options.each do |k,v|
          k = 'scheme' if k == :protocol
          @uri.send("#{k}=", v)
        end
      end
    end
 
    def rewrite!(options)
      @action = :rewrite
      set(options)
    end
 
    def permanent!(options)
      @action = :permanent
      @status = 301
      set(options)
      @message = "moved to #{@uri}"
    end
 
    def found!(options)
      @action = :found
      @status = 302
      set(options)
      @message = "moved to #{@uri}"
    end
 
    def location
      @uri.to_s
    end
 
  end # RequestContext
 
  def self.configure(&block)
    @rules = block
  end
 
  def self.rules
    @rules
  end
 
  def initialize(app)
    @app = app
  end
 
  def rules
    self.class.rules
  end
 
  def call(env)
    if self.rules
      context = RequestContext.new(env)
 
      self.rules.call(context)
 
      case context.action
      when :permanent, :found
        context.response
      when :rewrite
        env["rack.url_scheme"] = context.scheme
        env["HTTP_HOST"] = env["SERVER_NAME"] = context.host
        env["HTTP_PORT"] = context.port if context.port
        env["PATH_INFO"] = env["REQUEST_PATH"] = context.path
        env["QUERY_STRING"] = context.query
        env["REQUEST_URI"] = context.query ? "#{context.path}?#{context.query}" : context.path
        @app.call(env)
      else
        @app.call(env)
      end
    else
      @app.call(env)
    end
  end
end