Class: Tom::Adapter

Inherits:
Object
  • Object
show all
Defined in:
lib/adapter.rb

Overview

Please see the README for examples on how to use this.

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeAdapter

Returns a new instance of Adapter.



37
38
39
# File 'lib/adapter.rb', line 37

def initialize
  @request = {}
end

Class Attribute Details

.hostObject

Returns the value of attribute host.



13
14
15
# File 'lib/adapter.rb', line 13

def host
  @host
end

Instance Attribute Details

#hostObject (readonly)

The hostname this adapter is connecting to (the “other” API endpoint).



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
# File 'lib/adapter.rb', line 11

class Adapter
  class << self
    attr_accessor :host
  end

  # Registers a route with the request dispatcher
  # so that this classes subclass gets called when
  # a request is made. One that matches the route.
  #
  # The route can be a string, but it becomes a 
  # regular expression in here, followed by methods.
  #
  # @param route [String] The route this Adapter should 
  #   respond to.
  #
  # @param methods [Array<Symbol>] Optional array of methods
  #   that this Adapter is listening to. It defaults to
  #   all (:head, :get, :put, :post, :delete)
  #
  # @return [Hash] See {Tom::Routes.register}
  def self.register_route(*args)
    route = args[0]
    methods = args[1..-1]
    Tom::Routes.register(route: /#{route}/, adapter: self, methods: methods)
  end

  def initialize
    @request = {}
  end

  # Takes a request from rack and issues the same
  # request again, just to a different host. This
  # method is to be used by subclasses.
  #
  # @param env [Array] The incoming (original request) 
  #   rack env object
  #
  # @return [Array] Your beloved triple of [status_code, headers,
  #   response_body]
  def forward_request(env)
    rewrite_request(env)
    options = http_request_options(env)
    url = @request[:host] + @request[:uri]

    result = Tom::Http.make_request(@request[:method], url, options)

    headers = {"Downstream-Url" => url}.merge result.response_header
    [result.response_header.status, headers, result.response]
  end

  # Takes a request and generates the options for calling
  # HttpRequest.put (or whatever the the requested
  # REQUEST_METHOD is).
  #
  # It's content depends on the request method, for PUTs
  # and POSTs this will add the request body
  #
  # @param env [Array] A rack env object
  #
  # @return [Hash] Has the value of @request[:body] inside its
  #   :body key, but only when the request method in the given
  #   env matches :put or :post
  def http_request_options(env)
    opts = {}
    if [:put, :post].include? @request[:method]
      opts[:body] = @request[:body] || extract_request_body(env)
    end
    opts
  end

  # Extracts the given POT/PUST (hehe) body. Overwrite this
  # if you want to do your own thing (e.g. if the params middleware
  # made the params an object, you should return a string here as
  # this is what we feed into the EM::HttpRequest
  #
  # @param env [Array] A rack env object
  #
  # @return [Hash] Returns whatever the client POSTed/PUT and defaults
  #   to an empty hash (while debugging, consider your middlewares,
  #   they might touch this depending on the Content-Type)
  def extract_request_body(env)
    Rack::Request.new(env).POST rescue {}
  end

  # Takes a request from rack and extracts the request
  # method, uri and returns the host this adapter talks
  # to. Can be overwritten if you want to change stuff
  # before forwarding it.
  #
  # @param env [Array] A rack env object
  #
  # @return [Hash] The @request variable
  def rewrite_request(env)
    rewritten = rewrite_host(env)
    @request = rewritten.merge(@request)
  end

  # Subclasses must implement this method to handle incoming
  # requests
  #
  # @param env [Array] The incoming (original request) 
  #   rack env object
  #
  # @return [void] This mofo raises and never returns anything.
  def handle(env)
    raise "Subclass, implement #handle(env)!"
  end

  private

  # Returns a hash that can be used as the @request variable,
  # which is exactly like the given env except for a changed
  # hostname.
  #
  # @param env [Array] A rack env object
  #
  # @return [Hash] With :host, :uri and :method
  def rewrite_host(env)
    { host:   self.class.host,
      uri:    env["REQUEST_URI"],
      method: env["REQUEST_METHOD"].downcase.to_sym
    }
  end
end

Class Method Details

.register_route(*args) ⇒ Hash

Registers a route with the request dispatcher so that this classes subclass gets called when a request is made. One that matches the route.

The route can be a string, but it becomes a regular expression in here, followed by methods.

Parameters:

  • route (String)

    The route this Adapter should respond to.

  • methods (Array<Symbol>)

    Optional array of methods that this Adapter is listening to. It defaults to all (:head, :get, :put, :post, :delete)

Returns:



31
32
33
34
35
# File 'lib/adapter.rb', line 31

def self.register_route(*args)
  route = args[0]
  methods = args[1..-1]
  Tom::Routes.register(route: /#{route}/, adapter: self, methods: methods)
end

Instance Method Details

#extract_request_body(env) ⇒ Hash

Extracts the given POT/PUST (hehe) body. Overwrite this if you want to do your own thing (e.g. if the params middleware made the params an object, you should return a string here as this is what we feed into the EM::HttpRequest

Parameters:

  • env (Array)

    A rack env object

Returns:

  • (Hash)

    Returns whatever the client POSTed/PUT and defaults to an empty hash (while debugging, consider your middlewares, they might touch this depending on the Content-Type)



91
92
93
# File 'lib/adapter.rb', line 91

def extract_request_body(env)
  Rack::Request.new(env).POST rescue {}
end

#forward_request(env) ⇒ Array

Takes a request from rack and issues the same request again, just to a different host. This method is to be used by subclasses.

Parameters:

  • env (Array)

    The incoming (original request) rack env object

Returns:

  • (Array)

    Your beloved triple of [status_code, headers, response_body]



50
51
52
53
54
55
56
57
58
59
# File 'lib/adapter.rb', line 50

def forward_request(env)
  rewrite_request(env)
  options = http_request_options(env)
  url = @request[:host] + @request[:uri]

  result = Tom::Http.make_request(@request[:method], url, options)

  headers = {"Downstream-Url" => url}.merge result.response_header
  [result.response_header.status, headers, result.response]
end

#handle(env) ⇒ void

This method returns an undefined value.

Subclasses must implement this method to handle incoming requests

Parameters:

  • env (Array)

    The incoming (original request) rack env object



115
116
117
# File 'lib/adapter.rb', line 115

def handle(env)
  raise "Subclass, implement #handle(env)!"
end

#http_request_options(env) ⇒ Hash

Takes a request and generates the options for calling HttpRequest.put (or whatever the the requested REQUEST_METHOD is).

It’s content depends on the request method, for PUTs and POSTs this will add the request body

Parameters:

  • env (Array)

    A rack env object

Returns:

  • (Hash)

    Has the value of @request inside its :body key, but only when the request method in the given env matches :put or :post



73
74
75
76
77
78
79
# File 'lib/adapter.rb', line 73

def http_request_options(env)
  opts = {}
  if [:put, :post].include? @request[:method]
    opts[:body] = @request[:body] || extract_request_body(env)
  end
  opts
end

#rewrite_request(env) ⇒ Hash

Takes a request from rack and extracts the request method, uri and returns the host this adapter talks to. Can be overwritten if you want to change stuff before forwarding it.

Parameters:

  • env (Array)

    A rack env object

Returns:

  • (Hash)

    The @request variable



103
104
105
106
# File 'lib/adapter.rb', line 103

def rewrite_request(env)
  rewritten = rewrite_host(env)
  @request = rewritten.merge(@request)
end