octarine 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc ADDED
@@ -0,0 +1,44 @@
1
+ = Octarine
2
+
3
+ Octarine is a Sinatra-like DSL for writing a HTTP routing proxy, i.e. a service
4
+ that accepts incoming requests, re-issues them to the appropriate endpoint, and
5
+ relays the output. It also includes support for modifying the output, or
6
+ transforming one external request in to multiple internal requests.
7
+
8
+ == Installation
9
+
10
+ gem install octarine
11
+
12
+ == Usage
13
+
14
+ class Router
15
+ include Octarine::App
16
+
17
+ get "/gp*rest" do |request|
18
+ request.to("globalpersonals.co.uk", request.path.lchomp("/gp"))
19
+ end
20
+ end
21
+
22
+ == Licence
23
+
24
+ (The MIT License)
25
+
26
+ Copyright (c) 2011 Global Personals, Ltd.
27
+
28
+ Permission is hereby granted, free of charge, to any person obtaining a copy
29
+ of this software and associated documentation files (the "Software"), to deal
30
+ in the Software without restriction, including without limitation the rights
31
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32
+ copies of the Software, and to permit persons to whom the Software is
33
+ furnished to do so, subject to the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be included in
36
+ all copies or substantial portions of the Software.
37
+
38
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
44
+ THE SOFTWARE.
data/lib/octarine.rb ADDED
@@ -0,0 +1,2 @@
1
+ libs = %W{app endpoint path request response}.map(&"../octarine/".method(:+))
2
+ libs.map {|lib| File.expand_path(lib, __FILE__)}.each(&method(:require))
@@ -0,0 +1,112 @@
1
+ require "forwardable"
2
+ require "http_router"
3
+ require_relative "request"
4
+ require_relative "endpoint"
5
+
6
+ module Octarine # :nodoc:
7
+ module App
8
+ module ClassMethods
9
+
10
+ ##
11
+ # :method: add
12
+ # :call-seq: add(path, opts={}) {|request| block }
13
+ #
14
+ # Adds block as a handler for path.
15
+
16
+ ##
17
+ # :method: get
18
+ # :call-seq: get(path, opts={}) {|request| block }
19
+ #
20
+ # Adds block as a handler for path when the request method is GET.
21
+
22
+ ##
23
+ # :method: post
24
+ # :call-seq: post(path, opts={}) {|request| block }
25
+ #
26
+ # Adds block as a handler for path when the request method is POST.
27
+
28
+ ##
29
+ # :method: delete
30
+ # :call-seq: delete(path, opts={}) {|request| block }
31
+ #
32
+ # Adds block as a handler for path when the request method is DELETE.
33
+
34
+ ##
35
+ # :method: put
36
+ # :call-seq: put(path, opts={}) {|request| block }
37
+ #
38
+ # Adds block as a handler for path when the request method is PUT.
39
+
40
+ ##
41
+ # :method: default
42
+ # :call-seq: default {|request| block }
43
+ #
44
+ # Adds block as a handler for when no path is matched.
45
+
46
+ [:add, :get, :post, :delete, :put, :default].each do |method|
47
+ define_method(method) do |*args, &block|
48
+ (@handlers ||= []) << [method, *args, block]
49
+ end
50
+ end
51
+
52
+ def add_route(route) # :nodoc:
53
+ (@handlers ||= []) << [__method__, route, nil]
54
+ end
55
+
56
+ # :call-seq: request_class(klass)
57
+ #
58
+ # Set the class of the request object handed to the path handler blocks.
59
+ # Defaults to Octarine::Request.
60
+ #
61
+ def request_class(klass=nil)
62
+ klass ? @request_class = klass : @request_class || Octarine::Request
63
+ end
64
+
65
+ # :call-seq: environment -> string
66
+ #
67
+ # Returns the current enviroment. Defaults to "development".
68
+ #
69
+ def environment
70
+ ENV["RACK_ENV"] || "development"
71
+ end
72
+
73
+ def new(*args) # :nodoc:
74
+ instance = super
75
+ instance.router = HttpRouter.new
76
+ @handlers.each do |method, *args|
77
+ block = args.pop
78
+ instance.router.send(method, *args) do |env|
79
+ instance.instance_exec(request_class.new(env), &block)
80
+ end
81
+ end
82
+ instance
83
+ end
84
+ end
85
+
86
+ attr_accessor :router # :nodoc:
87
+
88
+ ##
89
+ # :method: call
90
+ # :call-seq: app.call(env) -> array
91
+ #
92
+ # Rack-compatible #call method.
93
+
94
+ extend Forwardable
95
+ def_delegators :router, :url, :call
96
+
97
+ def self.included(includer) # :nodoc:
98
+ includer.extend(ClassMethods)
99
+ end
100
+
101
+ # :call-seq: app.endpoint(string) -> endpoint
102
+ # app.endpoint(client) -> endpoint
103
+ #
104
+ # Create an Octarine::Endpoint with either a string of the host:port or
105
+ # a client instance API compatible with Octarine::SimpleHTTP.
106
+ #
107
+ def endpoint(host_or_client)
108
+ Octarine::Endpoint.new(host_or_client)
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,125 @@
1
+ require "forwardable"
2
+ require_relative "simple_http"
3
+ require_relative "response"
4
+
5
+ module Octarine # :nodoc:
6
+
7
+ # Octarine::Endpoint is a wrapper round a http client that presents a DSL for
8
+ # generating paths and making requests.
9
+ #
10
+ # Examples:
11
+ #
12
+ # # GET users/123/messages/567
13
+ # endpoint.users(123).messages[567]
14
+ #
15
+ # # GET 123/details/address
16
+ # endpoint.(123).details["address"]
17
+ #
18
+ # # POST users/123/messages
19
+ # endpoint.users(123).post("messages", body)
20
+ #
21
+ class Endpoint
22
+ attr_accessor :path
23
+ protected :path, :path=
24
+ attr_reader :client
25
+ private :client
26
+
27
+ extend Forwardable
28
+ def_delegators :@client, :host, :port
29
+
30
+ # :call-seq: Endpoint.new(host) -> endpoint
31
+ # Endpoint.new(client) -> endpoint
32
+ #
33
+ # Create a new Endpoint instance, either with a string of host:port, or a
34
+ # http client instance API compatible with Octarine::SimpleHTTP
35
+ #
36
+ def initialize(host_or_client)
37
+ if host_or_client.respond_to?(:get)
38
+ @client = host_or_client
39
+ else
40
+ @client = Octarine::SimpleHTTP.new(host_or_client)
41
+ end
42
+ @path = ""
43
+ end
44
+
45
+ # :call-seq: endpoint.call(obj) -> new_endpoint
46
+ # endpoint.(obj) -> new_endpoint
47
+ #
48
+ # Returns a new endpoint with the string respresentation of obj added to the
49
+ # path. E.g. `endpoint.(123)` will return an endpoint with `/123` appended
50
+ # to the path.
51
+ #
52
+ def call(id)
53
+ method_missing(id)
54
+ end
55
+
56
+ # :call-seq: endpoint.method_missing(name, id) -> new_endpoint
57
+ # endpoint.method_missing(name) -> new_endpoint
58
+ # endpoint.anything(id) -> new_endpoint
59
+ # endpoint.anything -> new_endpoint
60
+ #
61
+ # Implements the path generation DSL.
62
+ # endpoint.foo(1).bar #=> new endpoint with path set to /foo/1/bar
63
+ #
64
+ def method_missing(name, *args)
65
+ super unless args.length <= 1 && !block_given?
66
+ copy = dup
67
+ copy.path = join(copy.path, name.to_s, *args)
68
+ copy
69
+ end
70
+
71
+ # :call-seq: endpoint.head(id, headers={}) -> response
72
+ #
73
+ # Make a HEAD request to endpoint's path plus `/id`.
74
+ #
75
+ def head(id, headers={})
76
+ response = client.head(join(path, id.to_s), headers)
77
+ Octarine::Response.new(response.body, response.headers, response.status)
78
+ end
79
+
80
+ # :call-seq: endpoint.head(id, headers={}) -> response
81
+ # endpoint[id] -> response
82
+ #
83
+ # Make a GET request to endpoint's path plus `/id`.
84
+ #
85
+ def get(id, headers={})
86
+ response = client.get(join(path, id.to_s), headers)
87
+ Octarine::Response.new(response.body, response.headers, response.status)
88
+ end
89
+ alias [] get
90
+
91
+ # :call-seq: endpoint.head(id, body=nil, headers={}) -> response
92
+ #
93
+ # Make a POST request to endpoint's path plus `/id`.
94
+ #
95
+ def post(id, body=nil, headers={})
96
+ response = client.post(join(path, id.to_s), body, headers)
97
+ Octarine::Response.new(response.body, response.headers, response.status)
98
+ end
99
+
100
+ # :call-seq: endpoint.head(id, body=nil, headers={}) -> response
101
+ #
102
+ # Make a PUT request to endpoint's path plus `/id`.
103
+ #
104
+ def put(id, body=nil, headers={})
105
+ response = client.put(join(path, id.to_s), body, headers)
106
+ Octarine::Response.new(response.body, response.headers, response.status)
107
+ end
108
+
109
+ # :call-seq: endpoint.head(id, headers={}) -> response
110
+ #
111
+ # Make a DELETE request to endpoint's path plus `/id`.
112
+ #
113
+ def delete(id, headers={})
114
+ response = client.delete(join(path, id.to_s), headers)
115
+ Octarine::Response.new(response.body, response.headers, response.status)
116
+ end
117
+
118
+ private
119
+
120
+ def join(*paths)
121
+ paths.map {|path| path.gsub(%r{(^/|/$)}, "")}.join("/")
122
+ end
123
+
124
+ end
125
+ end
@@ -0,0 +1,93 @@
1
+ require "forwardable"
2
+
3
+ module Octarine # :nodoc:
4
+
5
+ # Octarine::Path represents the path and query string portion of a url.
6
+ #
7
+ # You are unlikely to need to create a Path instance yourself, insted you
8
+ # will usually obtain one from Request#path.
9
+ #
10
+ # If you set a handler like so:
11
+ # get "/users/:user_id/messages/:message_id" {|request| ... }
12
+ # and a request is made like:
13
+ # GET /users/1234/messages/4567?history=true
14
+ # then `request.path` will behave as below:
15
+ # path.user_id #=> "1234"
16
+ # path[:user_id] #=> "1234"
17
+ # path.message_id #=> "5678"
18
+ # path[:message_id] #=> "5678"
19
+ # path["history"] #=> "true"
20
+ # path.to_s #=> "/users/1234/messages/4567?history=true"
21
+ # path.path #=> "/users/1234/messages/4567"
22
+ # path.query_string #=> "history=true"
23
+ # path.to_hash #=> {:user_id=>"1234", :message_id=>"5678", "history"=>"true"}
24
+ # path.query #=> {"history"=>"true"}
25
+ #
26
+ # The following methods are available and behave as if the path was a hash:
27
+ # assoc, [], each_key, each_pair, each_value, empty?, fetch, has_key?,
28
+ # has_value?, key, key?, keys, merge, rassoc, to_a, to_hash, value?, values,
29
+ # values_at and all Enumerable methods
30
+ #
31
+ # The following methods are available and behave as if path was a string:
32
+ # +, =~, bytesize, gsub, length, size, sub, to_str, to_s
33
+ #
34
+ class Path
35
+ # String of the path, without the query string.
36
+ attr_reader :path
37
+ # String of the query string.
38
+ attr_reader :query_string
39
+ # Hash of the query string.
40
+ attr_reader :query
41
+
42
+ extend Forwardable
43
+ def_delegators :@params, :assoc, :[], :each_key, :each_pair,
44
+ :each_value, :empty?, :fetch, :has_key?, :has_value?, :key, :key?, :keys,
45
+ :merge, :rassoc, :to_a, :value?, :values, :values_at,
46
+ *Enumerable.public_instance_methods
47
+ def_delegator :@params, :dup, :to_hash
48
+ def_delegators :@full_path, :+, :=~, :bytesize, :gsub, :length, :size, :sub,
49
+ :to_str, :to_s
50
+
51
+ # :call-seq: Path.new(string, path_params, query_string) -> path
52
+ #
53
+ # Create a new Path instance from the a string or the path, the path
54
+ # parameters as a hash, and a string of the query string.
55
+ #
56
+ def initialize(path, params, query_string)
57
+ @path = path
58
+ params.each do |key, value|
59
+ self.class.class_eval do
60
+ define_method(key) {@params[key]}
61
+ end
62
+ end
63
+ @query_string = query_string
64
+ @query = parse_query(@query_string)
65
+ @full_path = @path.dup
66
+ @full_path << "?#{@query_string}" if @query_string && !@query_string.empty?
67
+ @params = params.merge(@query)
68
+ end
69
+
70
+ # :call-seq: path.lchomp(separator=$/) -> string
71
+ #
72
+ # Like String#chomp, but removes seperator from the left of the path.
73
+ #
74
+ def lchomp(separator=$/)
75
+ string = to_s
76
+ string.start_with?(separator) ? string[separator.length..-1] : string
77
+ end
78
+
79
+ private
80
+
81
+ def parse_query(string)
82
+ string.split("&").each_with_object({}) do |key_value, out|
83
+ key, value = key_value.split("=")
84
+ key, value = url_decode(key), url_decode(value)
85
+ out[key] = out.key?(key) ? [*out[key]].push(value) : value
86
+ end
87
+ end
88
+
89
+ def url_decode(str)
90
+ str.tr("+", " ").gsub(/(%[0-9a-f]{2})+/i) {|m| [m.delete("%")].pack("H*")}
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,89 @@
1
+ require_relative "response"
2
+ require_relative "endpoint"
3
+
4
+ module Octarine # :nodoc:
5
+ class Request
6
+ # The Rack enviroment hash
7
+ attr_reader :env
8
+ # The request method, e.g. "GET", "POST"
9
+ attr_reader :method
10
+ # The host name the request was made to
11
+ attr_reader :host
12
+ # The port the request was made to
13
+ attr_reader :port
14
+ # An Octarine::Path representing the path request was made to
15
+ attr_reader :path
16
+ # The request POST/PUT body
17
+ attr_reader :input
18
+
19
+ # :call-seq: Request.new(env) -> request
20
+ #
21
+ # Create a Request instance with a Rack enviroment hash.
22
+ #
23
+ def initialize(env)
24
+ @env = env
25
+ env.delete("router")
26
+ path_params = env.delete("router.params")
27
+ @method = env["REQUEST_METHOD"]
28
+ @host = env["SERVER_NAME"]
29
+ @port = env["SERVER_PORT"]
30
+ @path = Path.new(env["SCRIPT_NAME"] || env["PATH_INFO"], path_params,
31
+ env["QUERY_STRING"])
32
+ @input = env["rack.input"]
33
+ end
34
+
35
+ # :call-seq: request[header_name] -> header_value
36
+ #
37
+ # Retrieve headers.
38
+ # request["Content-Length"] #=> "123"
39
+ # request["Content-Type"] #=> "application/json"
40
+ #
41
+ def [](key)
42
+ upper_key = key.to_s.tr("a-z-", "A-Z_")
43
+ unless upper_key == "CONTENT_LENGTH" || upper_key == "CONTENT_TYPE"
44
+ upper_key[0,0] = "HTTP_"
45
+ end
46
+ @env[key]
47
+ end
48
+
49
+ # :call-seq: request.to(endpoint) -> response
50
+ # request.to(endpoint, path) -> response
51
+ # request.to(endpoint, path, input) -> response
52
+ #
53
+ # Re-issue request to new host/path.
54
+ #
55
+ def to(endpoint=Octarine::Endpoint.new(host), to_path=path, to_input=input)
56
+ res = if %W{POST PUT}.include?(method)
57
+ header = {"content-type" => "application/json"}
58
+ endpoint.send(method.downcase, to_path, to_input, header)
59
+ else
60
+ endpoint.send(method.downcase, to_path)
61
+ end
62
+ headers = res.headers
63
+ headers.delete("transfer-encoding")
64
+ headers.delete("content-length")
65
+ Octarine::Response.new(res.body, headers, res.status)
66
+ end
67
+
68
+ # :call-seq: request.redirect(path) -> response
69
+ #
70
+ # Issue redirect to path.
71
+ #
72
+ def redirect(path)
73
+
74
+ end
75
+
76
+ def to_s # :nodoc:
77
+ header = Hash[@env.select do |k,v|
78
+ k =~ /^HTTP_[^(VERSION)]/ || %W{CONTENT_LENGTH CONTENT_TYPE}.include?(k)
79
+ end.map do |key, value|
80
+ [key.sub(/HTTP_/, "").split(/_/).map(&:capitalize).join("-"), value]
81
+ end]
82
+ version = " " + @env["HTTP_VERSION"] if @env.key?("HTTP_VERSION")
83
+ "#{method} #{path}#{version}\r\n" << header.map do |key, value|
84
+ "#{key}: #{value}"
85
+ end.join("\r\n") << "\r\n\r\n"
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,109 @@
1
+ require "forwardable"
2
+
3
+ module Octarine # :nodoc:
4
+ class Response
5
+ attr_accessor :body, :status, :header
6
+ alias headers header
7
+ alias headers= header=
8
+
9
+ extend Forwardable
10
+ def_delegators :to_ary, :first, :last
11
+
12
+ # :call-seq: Response.new(body) -> response
13
+ # Response.new(body, header) -> response
14
+ # Response.new(body, header, status) -> response
15
+ #
16
+ # Create a new Response instance.
17
+ #
18
+ def initialize(body=[], header={}, status=200)
19
+ status, header = header, status if header.respond_to?(:to_i)
20
+ @body = body
21
+ @header = header
22
+ @status = status.to_i
23
+ header["content-type"] ||= "text/html" unless [204, 304].include?(@status)
24
+ end
25
+
26
+ # :call-seq: response.update {|body| block } -> response
27
+ # response.update(path) {|value| block } -> response
28
+ #
29
+ # Called without an argument, the block will be supplied the response body,
30
+ # and the response body will be set to the result of the block. The response
31
+ # itself is returned.
32
+ #
33
+ # When called with an argument the body should be a hash, the body will be
34
+ # traversed accoring to the path supplied, the value of the body will be
35
+ # yielded to the block, and then replaced with the result of the block.
36
+ # Example:
37
+ # response.body
38
+ # #=> {"data" => [{"user" => "1234", "message" => "..."}]}
39
+ #
40
+ # response.update("data.user") {|id| User.find(id).to_hash}
41
+ #
42
+ # response.body
43
+ # #=> {"data" => [{"user" => {"id" => "1234", ...}, "message" => "..."}]}
44
+ #
45
+ def update(path=nil, &block)
46
+ @body = if body.respond_to?(:to_ary) && path.nil?
47
+ block.call(body)
48
+ else
49
+ apply(body, path, &block)
50
+ end
51
+ self
52
+ end
53
+
54
+ # :call-seq: response[key] -> value
55
+ #
56
+ # Get a header.
57
+ #
58
+ def [](key)
59
+ (key.is_a?(Numeric) ? to_ary : header)[key]
60
+ end
61
+
62
+ # :call-seq: response[key] = value -> value
63
+ #
64
+ # Set a header.
65
+ #
66
+ def []=(key, value)
67
+ return header[key] = value unless key.is_a?(Numeric)
68
+ case key
69
+ when 0
70
+ @status = value
71
+ when 1
72
+ @header = value
73
+ when 2
74
+ @body = value
75
+ else
76
+ raise ArgumentError("Unexpected key #{key}")
77
+ end
78
+ end
79
+
80
+ # :call-seq: response.to_ary -> array
81
+ # response.to_a -> array
82
+ #
83
+ # Convert to a Rack response array of [status, headers, body]
84
+ #
85
+ def to_ary
86
+ [status, header, body.respond_to?(:each) ? body : [body].compact]
87
+ end
88
+ alias to_a to_ary
89
+
90
+ private
91
+
92
+ def apply(object, path=nil, &block)
93
+ if object.respond_to?(:to_ary)
94
+ return object.to_ary.map {|obj| apply(obj, path, &block)}
95
+ end
96
+
97
+ key, rest = path.split(".", 2) if path
98
+ if rest
99
+ object[key] = apply(object[key], rest, &block)
100
+ elsif key
101
+ object[key] = block.call(object[key])
102
+ else
103
+ return block.call(object)
104
+ end
105
+ object
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,82 @@
1
+ require "net/http"
2
+
3
+ module Octarine # :nodoc:
4
+
5
+ # SimpleHTTP is a bare-bones implementation of a simple http client, designed
6
+ # to be easily replaceable with another implementation.
7
+ #
8
+ class SimpleHTTP
9
+ Response = Struct.new(:status, :headers, :body)
10
+
11
+ # :call-seq: SimpleHTTP.new(url, options={}) -> simple_http
12
+ # SimpleHTTP.new(options) -> simple_http
13
+ #
14
+ # Create a SimpleHTTP instace with either a url and options or options with
15
+ # a :url key.
16
+ #
17
+ def initialize(url, options={})
18
+ unless url.respond_to?(:to_str)
19
+ options = url
20
+ url = options[:url]
21
+ end
22
+ @host, @port = url.to_s.split(/:/)
23
+ end
24
+
25
+ # :call-seq: simple_http.head(path, headers={}) -> response
26
+ #
27
+ # Perform a HEAD request, returns a response that responds to #status,
28
+ # #headers, and #body
29
+ #
30
+ def head(path, headers={})
31
+ request(Net::HTTP::Head.new(path, headers))
32
+ end
33
+
34
+ # :call-seq: simple_http.get(path, headers={}) -> response
35
+ #
36
+ # Perform a GET request, returns a response that responds to #status,
37
+ # #headers, and #body
38
+ #
39
+ def get(path, headers={})
40
+ request(Net::HTTP::Get.new(path, headers))
41
+ end
42
+
43
+ # :call-seq: simple_http.post(path, body=nil, headers={}) -> response
44
+ #
45
+ # Perform a POST request, returns a response that responds to #status,
46
+ # #headers, and #body
47
+ #
48
+ def post(path, body=nil, headers={})
49
+ req = Net::HTTP::Post.new(path, headers)
50
+ req.body = body if body
51
+ request(req)
52
+ end
53
+
54
+ # :call-seq: simple_http.put(path, body=nil, headers={}) -> response
55
+ #
56
+ # Perform a PUT request, returns a response that responds to #status,
57
+ # #headers, and #body
58
+ #
59
+ def put(path, body=nil, headers={})
60
+ req = Net::HTTP::Put.new(path, headers)
61
+ req.body = body if body
62
+ request(req)
63
+ end
64
+
65
+ # :call-seq: simple_http.delete(path, headers={}) -> response
66
+ #
67
+ # Perform a DELETE request, returns a response that responds to #status,
68
+ # #headers, and #body
69
+ #
70
+ def delete(path, headers={})
71
+ request(Net::HTTP::Delete.new(path, headers))
72
+ end
73
+
74
+ private
75
+ def request(request)
76
+ response = Net::HTTP.start(@host, @port) {|http| http.request(request)}
77
+ headers = Hash[response.to_hash.map {|h,k| [h, k.join("\n")]}]
78
+ Response.new(response.code, headers, response.body)
79
+ end
80
+
81
+ end
82
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: octarine
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matthew Sadler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-02 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: http_router
16
+ requirement: &2153077060 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2153077060
25
+ description: Sinatra-like DSL for writing a HTTP routing proxy.
26
+ email: mat@sourcetagsandcodes.com
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files:
30
+ - README.rdoc
31
+ files:
32
+ - lib/octarine/app.rb
33
+ - lib/octarine/endpoint.rb
34
+ - lib/octarine/path.rb
35
+ - lib/octarine/request.rb
36
+ - lib/octarine/response.rb
37
+ - lib/octarine/simple_http.rb
38
+ - lib/octarine.rb
39
+ - README.rdoc
40
+ homepage: http://github.com/globaldev/octarine
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options:
44
+ - --main
45
+ - README.rdoc
46
+ - --charset
47
+ - utf-8
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 1.8.7
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: HTTP routing proxy DSL
68
+ test_files: []