octarine 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []