octarine 0.0.1 → 0.0.2

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 CHANGED
@@ -7,7 +7,7 @@ transforming one external request in to multiple internal requests.
7
7
 
8
8
  == Installation
9
9
 
10
- gem install octarine
10
+ $ gem install octarine
11
11
 
12
12
  == Usage
13
13
 
data/lib/octarine.rb CHANGED
@@ -1,2 +1,4 @@
1
- libs = %W{app endpoint path request response}.map(&"../octarine/".method(:+))
2
- libs.map {|lib| File.expand_path(lib, __FILE__)}.each(&method(:require))
1
+ libs = %W{app path path_template request response}
2
+ libs.map {|lib| File.expand_path("../octarine/#{lib}", __FILE__)}.each do |lib|
3
+ require lib
4
+ end
data/lib/octarine/app.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require "forwardable"
2
2
  require "http_router"
3
3
  require_relative "request"
4
- require_relative "endpoint"
4
+ require_relative "path"
5
5
 
6
6
  module Octarine # :nodoc:
7
7
  module App
@@ -45,22 +45,47 @@ module Octarine # :nodoc:
45
45
 
46
46
  [:add, :get, :post, :delete, :put, :default].each do |method|
47
47
  define_method(method) do |*args, &block|
48
+ if Hash === args.last
49
+ restrictions = Array(args.last[:restrict])
50
+ else
51
+ restrictions = []
52
+ args << {}
53
+ end
54
+ restrictions << @current_restriction if @current_restriction
55
+ args.last[:restrict] = restrictions.flatten
48
56
  (@handlers ||= []) << [method, *args, block]
49
57
  end
50
58
  end
51
59
 
52
- def add_route(route) # :nodoc:
53
- (@handlers ||= []) << [__method__, route, nil]
60
+ # :call-seq:
61
+ # restriction(name, response=401) {|request| block }
62
+ # restriction(name, response=401, proc)
63
+ #
64
+ # Create a named restriction. response will be returned if the block
65
+ # returns true, otherwise the handler subject to the restriction will be
66
+ # executed as usual.
67
+ #
68
+ def restriction(name, response=401, proc=nil, &block)
69
+ (@restrictions ||= {})[name] = [response, proc || block]
54
70
  end
55
71
 
56
- # :call-seq: request_class(klass)
72
+ # :call-seq: restrict(name) { block }
57
73
  #
74
+ # All handlers defined within the block will be subject to the named
75
+ # restriction.
76
+ #
77
+ def restrict(restriction)
78
+ (@current_restriction ||= []) << restriction
79
+ yield
80
+ ensure
81
+ @current_restriction.pop
82
+ end
83
+
58
84
  # Set the class of the request object handed to the path handler blocks.
59
85
  # Defaults to Octarine::Request.
60
86
  #
61
- def request_class(klass=nil)
62
- klass ? @request_class = klass : @request_class || Octarine::Request
63
- end
87
+ attr_writer :request_class
88
+ alias request_class request_class=
64
89
 
65
90
  # :call-seq: environment -> string
66
91
  #
@@ -71,41 +96,76 @@ module Octarine # :nodoc:
71
96
  end
72
97
 
73
98
  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
99
+ request_class = @request_class || Octarine::Request
100
+ restrictions = @restrictions
101
+ handlers = @handlers
102
+ super.instance_eval do
103
+ @request_class ||= request_class
104
+ @router ||= HttpRouter.new
105
+ @restrictions = restrictions
106
+ handlers.each {|m,*args| register_handler(m, *args[0..-2], &args[-1])}
107
+ self
81
108
  end
82
- instance
83
109
  end
110
+
84
111
  end
85
112
 
86
- attr_accessor :router # :nodoc:
113
+ extend Forwardable
114
+
115
+ attr_reader :router, :request_class
87
116
 
88
117
  ##
89
118
  # :method: call
90
119
  # :call-seq: app.call(env) -> array
91
120
  #
92
121
  # Rack-compatible #call method.
93
-
94
- extend Forwardable
95
- def_delegators :router, :url, :call
122
+ #
123
+ def_delegator :router, :call
96
124
 
97
125
  def self.included(includer) # :nodoc:
98
126
  includer.extend(ClassMethods)
99
127
  end
100
128
 
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)
129
+ private
130
+
131
+ def to_rack_response(res)
132
+ if res.respond_to?(:status) && res.respond_to?(:headers) &&
133
+ res.respond_to?(:body)
134
+ status, headers, body = res.status, res.headers, res.body
135
+ [status, headers, body.respond_to?(:each) ? body : [body].compact]
136
+ elsif res.respond_to?(:to_ary)
137
+ res.to_ary
138
+ elsif res.respond_to?(:to_str)
139
+ [200, {}, [res.to_str]]
140
+ elsif res.respond_to?(:to_i)
141
+ [res.to_i, {}, []]
142
+ else
143
+ [200, {}, res]
144
+ end
145
+ end
146
+
147
+ def register_handler(method, *args, &block)
148
+ return register_default(&block) if method == :default
149
+ restrictions = Hash === args[-1] ? Array(args[-1].delete(:restrict)) : []
150
+ restrictions.map! {|name| @restrictions[name]}
151
+ route = router.send(method, *args)
152
+ route.to do |env|
153
+ env.merge!("router.route" => route.original_path)
154
+ request = request_class.new(env)
155
+ response, = restrictions.find {|_,restr| instance_exec(request, &restr)}
156
+ if response.respond_to?(:to_proc)
157
+ response = instance_exec(request, &response)
158
+ elsif response.nil?
159
+ response = instance_exec(request, &block)
160
+ end
161
+ to_rack_response(response)
162
+ end
163
+ end
164
+
165
+ def register_default(&block)
166
+ router.default(Proc.new do |env|
167
+ to_rack_response(instance_exec(request_class.new(env), &block))
168
+ end)
109
169
  end
110
170
 
111
171
  end
data/lib/octarine/path.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "forwardable"
2
+ require_relative "path_template"
2
3
 
3
4
  module Octarine # :nodoc:
4
5
 
@@ -19,75 +20,115 @@ module Octarine # :nodoc:
19
20
  # path["history"] #=> "true"
20
21
  # path.to_s #=> "/users/1234/messages/4567?history=true"
21
22
  # path.path #=> "/users/1234/messages/4567"
22
- # path.query_string #=> "history=true"
23
23
  # path.to_hash #=> {:user_id=>"1234", :message_id=>"5678", "history"=>"true"}
24
- # path.query #=> {"history"=>"true"}
25
24
  #
26
25
  # 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,
26
+ # assoc, [], each, each_key, each_pair, each_value, empty?, fetch, has_key?,
27
+ # has_value?, key, key?, keys, rassoc, to_a, to_hash, value?, values,
29
28
  # values_at and all Enumerable methods
30
29
  #
31
30
  # The following methods are available and behave as if path was a string:
32
- # +, =~, bytesize, gsub, length, size, sub, to_str, to_s
31
+ # =~, bytesize, length, size
33
32
  #
34
33
  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
34
+ attr_reader :params
35
+ attr_accessor :template
36
+ protected :params, :template, :template=
41
37
 
42
38
  extend Forwardable
43
- def_delegators :@params, :assoc, :[], :each_key, :each_pair,
39
+ def_delegators :@params, :assoc, :[], :each, :each_key, :each_pair,
44
40
  :each_value, :empty?, :fetch, :has_key?, :has_value?, :key, :key?, :keys,
45
- :merge, :rassoc, :to_a, :value?, :values, :values_at,
41
+ :rassoc, :to_a, :value?, :values, :values_at,
46
42
  *Enumerable.public_instance_methods
47
43
  def_delegator :@params, :dup, :to_hash
48
- def_delegators :@full_path, :+, :=~, :bytesize, :gsub, :length, :size, :sub,
49
- :to_str, :to_s
44
+ def_delegators :@full_path, :=~, :bytesize, :length, :size
50
45
 
51
- # :call-seq: Path.new(string, path_params, query_string) -> path
46
+ # :call-seq: Path.new(template, path_string) -> path
52
47
  #
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.
48
+ # Create a new Path instance from a path template and a string of the path.
55
49
  #
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
50
+ def initialize(template, path)
51
+ @template = Octarine::PathTemplate.new(template)
52
+ @params = @template.recognize(path)
53
+
54
+ @params.each do |key, value|
55
+ next unless Symbol === key
56
+ self.class.class_eval {define_method(key) {@params[key]}}
62
57
  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
58
  end
69
59
 
70
- # :call-seq: path.lchomp(separator=$/) -> string
60
+ # :call-seq: path.without(part) -> new_path
71
61
  #
72
- # Like String#chomp, but removes seperator from the left of the path.
62
+ # Return a new path without part.
73
63
  #
74
- def lchomp(separator=$/)
75
- string = to_s
76
- string.start_with?(separator) ? string[separator.length..-1] : string
64
+ # path = Path.new("/users/:id", "/users/1")
65
+ # path.without("users").to_s #=> "/1"
66
+ #
67
+ # path = Path.new("/users/:id", "/users/1")
68
+ # path.without(":id").to_s #=> "/users"
69
+ #
70
+ def without(part)
71
+ dup.tap do |cp|
72
+ cp.template = @template.without(part)
73
+ cp.params.delete(part)
74
+ end
77
75
  end
78
76
 
79
- private
77
+ # :call-seq: path.merge(hash) -> new_path
78
+ #
79
+ # Returns a new path with contents of hash merged in to the path parameters.
80
+ #
81
+ # path = Path.new("/users/:id/favourites", "/users/1/favourites?limit=10")
82
+ # new_path = path.merge(:id => 2, "offset" => 20)
83
+ # new_path.to_s #=> "users/2/favourites?limit=10&offset=20"
84
+ #
85
+ def merge(other)
86
+ dup.tap {|cp| cp.params.merge!(other)}
87
+ end
80
88
 
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
89
+ # :call-seq: path + string -> new_path
90
+ #
91
+ # Returns a new path with string appended.
92
+ #
93
+ # path = Path.new("/users/:id", "/users/1")
94
+ # new_path = (path + "favourites/:favourite_id").merge(:favourite_id => 2)
95
+ # new_path.to_s #=> "/users/1/favourites/2"
96
+ #
97
+ def +(part)
98
+ dup.tap {|cp| cp.template = @template + part}
99
+ end
100
+
101
+ # :call-seq: path.to_s -> string
102
+ #
103
+ # Returns the path as a string.
104
+ #
105
+ def to_s
106
+ @template.apply(@params)
107
+ end
108
+ alias to_str to_s
109
+
110
+ # :call-seq: path == other -> bool
111
+ #
112
+ # Returns true if other is equal to path, false otherwise.
113
+ #
114
+ def ==(other)
115
+ self.class === other && to_s == other.to_s
116
+ end
117
+
118
+ # :call-seq: path === other -> bool
119
+ #
120
+ # Returns true if other as a string is equal to path as a string, false
121
+ # otherwise.
122
+ #
123
+ def ===(other)
124
+ to_s === other.to_s
87
125
  end
88
126
 
89
- def url_decode(str)
90
- str.tr("+", " ").gsub(/(%[0-9a-f]{2})+/i) {|m| [m.delete("%")].pack("H*")}
127
+ def initialize_copy(source) # :nodoc:
128
+ super
129
+ @template = @template.dup
130
+ @params = @params.dup
91
131
  end
132
+
92
133
  end
93
134
  end
@@ -0,0 +1,163 @@
1
+ module Octarine
2
+ class PathTemplate
3
+ BadTemplateError = Class.new(ArgumentError)
4
+
5
+ module StringExtention
6
+ def /(arg)
7
+ PathTemplate.new(self).apply(*arg.respond_to?(:to_ary) ? arg : [arg])
8
+ end
9
+ end
10
+
11
+ attr_reader :parts
12
+ protected :parts
13
+
14
+ def initialize(string)
15
+ @parts = parse(string)
16
+ end
17
+
18
+ def apply(*args)
19
+ params = args.last.respond_to?(:each) ? args.pop.dup : {}
20
+
21
+ @parts.find {|type, val| params[val] ||= args.pop if type == :format}
22
+ @parts.select do |type, value|
23
+ params[value] ||= args.shift if type == :variable && !args.empty?
24
+ end
25
+ @parts.find {|type, val| params[val] ||= args if type == :glob}
26
+
27
+ format = nil
28
+ path = []
29
+ @parts.each do |type, value|
30
+ case type
31
+ when :variable
32
+ path << params.delete(value)
33
+ when :glob
34
+ path << params.delete(value).join("/")
35
+ when :format
36
+ format = params.delete(value)
37
+ else
38
+ path << value
39
+ end
40
+ end
41
+
42
+ out = path == [nil] ? "/" : path.join("/")
43
+ out << ".#{format}" if format
44
+ out << query(params) if !params.empty?
45
+ out
46
+ end
47
+
48
+ def recognize(string)
49
+ other_parts = parse(string).each
50
+ params = {}
51
+
52
+ @parts.each do |type, value|
53
+ other_type, other_value = (other_parts.next rescue nil)
54
+ case type
55
+ when :variable
56
+ return unless other_value
57
+ params[value] = other_value
58
+ when :glob
59
+ params[value] = other_value ? [other_value] : []
60
+ while (other_parts.peek.first == :string rescue nil)
61
+ other_type, other_value = other_parts.next
62
+ params[value] << other_value
63
+ end
64
+ when :format
65
+ return nil unless type == other_type
66
+ params[value] = other_value.to_s
67
+ when :string
68
+ return nil unless type == other_type && value == other_value
69
+ end
70
+ end
71
+ other_type, other_value = (other_parts.next rescue nil)
72
+ return nil unless other_type == :query_string || other_type.nil?
73
+ if other_value
74
+ query = parse_query(other_value)
75
+ params.merge!(query)
76
+ end
77
+ params
78
+ end
79
+ alias === recognize
80
+
81
+ def +(string)
82
+ parts = parse(string)
83
+ parts.shift if parts.first == [:leading_joiner, nil]
84
+ dup.tap {|cp| cp.parts.concat(parts)}
85
+ end
86
+
87
+ def without(string)
88
+ part = parse(string).first
89
+ dup.tap {|cp| cp.parts.reject! {|pt| pt == part}}
90
+ end
91
+ alias - without
92
+
93
+ def ==(other)
94
+ self.class === other && parts == other.parts
95
+ end
96
+
97
+ def initialize_copy(source)
98
+ super
99
+ @parts = @parts.dup
100
+ end
101
+
102
+ private
103
+
104
+ def query(params)
105
+ return nil if !params || params.empty?
106
+ "?" << params.map {|kv| kv.join("=")}.join("&")
107
+ end
108
+
109
+ def tokenize(string)
110
+ string.scan(%r{([/:*.?]|[^/:*.?]+)}).flatten
111
+ end
112
+
113
+ def lex(tokens)
114
+ enum = tokens.each
115
+ parts = []
116
+ seen_glob = false
117
+ seen_format = false
118
+
119
+ parts << [:leading_joiner, nil] if enum.peek == "/"
120
+ while part = enum.next
121
+ raise BadTemplateError.new(".format must be last") if seen_format
122
+
123
+ case part
124
+ when "/"
125
+ when ":"
126
+ raise BadTemplateError.new(":variable cannot follow *glob") if seen_glob
127
+ parts << [:variable, enum.next.to_sym]
128
+ when "*"
129
+ raise BadTemplateError.new("multiple *glob not allowed") if seen_glob
130
+ seen_glob = true
131
+ parts << [:glob, enum.next.to_sym]
132
+ when "."
133
+ seen_format = true
134
+ parts << [:format, enum.next.to_sym]
135
+ when "?"
136
+ parts << [:query_string, enum.next.freeze]
137
+ else
138
+ parts << [:string, part.freeze]
139
+ end
140
+ end
141
+ rescue StopIteration
142
+ parts
143
+ end
144
+
145
+ def parse(string)
146
+ string = Symbol === string ? ":#{string}" : string.to_s
147
+ lex(tokenize(string))
148
+ end
149
+
150
+ def parse_query(string)
151
+ string.split("&").each_with_object({}) do |key_value, out|
152
+ key, value = key_value.split("=")
153
+ key, value = url_decode(key), url_decode(value)
154
+ out[key] = out.key?(key) ? [*out[key]].push(value) : value
155
+ end
156
+ end
157
+
158
+ def url_decode(str)
159
+ str.tr("+", " ").gsub(/(%[0-9a-f]{2})+/i) {|m| [m.delete("%")].pack("H*")}
160
+ end
161
+
162
+ end
163
+ end
@@ -1,5 +1,6 @@
1
1
  require_relative "response"
2
- require_relative "endpoint"
2
+ require_relative "path"
3
+ require_relative "simple_http"
3
4
 
4
5
  module Octarine # :nodoc:
5
6
  class Request
@@ -23,18 +24,22 @@ module Octarine # :nodoc:
23
24
  def initialize(env)
24
25
  @env = env
25
26
  env.delete("router")
26
- path_params = env.delete("router.params")
27
+ env.delete("router.params")
28
+ template = env.delete("router.route")
27
29
  @method = env["REQUEST_METHOD"]
28
30
  @host = env["SERVER_NAME"]
29
31
  @port = env["SERVER_PORT"]
30
- @path = Path.new(env["SCRIPT_NAME"] || env["PATH_INFO"], path_params,
31
- env["QUERY_STRING"])
32
+ path = env["SCRIPT_NAME"] || ""
33
+ path << env["PATH_INFO"] unless env["PATH_INFO"].empty?
34
+ full_path = path.dup
35
+ full_path << "?" << env["QUERY_STRING"] unless env["QUERY_STRING"].empty?
36
+ @path = Path.new(template || path, full_path)
32
37
  @input = env["rack.input"]
33
38
  end
34
39
 
35
40
  # :call-seq: request[header_name] -> header_value
36
41
  #
37
- # Retrieve headers.
42
+ # Retrieve header.
38
43
  # request["Content-Length"] #=> "123"
39
44
  # request["Content-Type"] #=> "application/json"
40
45
  #
@@ -43,47 +48,59 @@ module Octarine # :nodoc:
43
48
  unless upper_key == "CONTENT_LENGTH" || upper_key == "CONTENT_TYPE"
44
49
  upper_key[0,0] = "HTTP_"
45
50
  end
46
- @env[key]
51
+ @env[upper_key]
47
52
  end
48
53
 
49
- # :call-seq: request.to(endpoint) -> response
50
- # request.to(endpoint, path) -> response
51
- # request.to(endpoint, path, input) -> response
54
+ # :call-seq: request.header -> hash
55
+ # request.headers -> hash
52
56
  #
53
- # Re-issue request to new host/path.
57
+ # Get header as a hash.
58
+ # request.header
59
+ # #=> {"content-length" => "123", "content-type" => "application/json"}
54
60
  #
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)
61
+ def header
62
+ Hash[@env.select do |k,v|
63
+ k =~ /^HTTP_[^(VERSION)]/ || %W{CONTENT_LENGTH CONTENT_TYPE}.include?(k)
64
+ end.map do |key, value|
65
+ [key.sub(/HTTP_/, "").tr("_", "-").downcase, value]
66
+ end]
66
67
  end
68
+ alias headers header
67
69
 
68
- # :call-seq: request.redirect(path) -> response
70
+ # :call-seq: request.to(host) -> response
71
+ # request.to(host, path) -> response
72
+ # request.to(host, path, input) -> response
69
73
  #
70
- # Issue redirect to path.
74
+ # Re-issue request to new host/path.
71
75
  #
72
- def redirect(path)
73
-
76
+ def to(client, to_path=path, to_input=input)
77
+ client = SimpleHTTP.new(client.to_str) if client.respond_to?(:to_str)
78
+ res = if %W{POST PUT}.include?(method)
79
+ client.__send__(method.downcase, to_path, to_input, header_for_rerequest)
80
+ else
81
+ client.__send__(method.downcase, to_path, header_for_rerequest)
82
+ end
83
+ response_headers = res.headers
84
+ response_headers.delete("transfer-encoding")
85
+ response_headers.delete("content-length")
86
+ Octarine::Response.new(res.body, response_headers, res.status)
74
87
  end
75
88
 
76
89
  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
90
  version = " " + @env["HTTP_VERSION"] if @env.key?("HTTP_VERSION")
83
91
  "#{method} #{path}#{version}\r\n" << header.map do |key, value|
92
+ key = key.split(/-/).map(&:capitalize).join("-")
84
93
  "#{key}: #{value}"
85
94
  end.join("\r\n") << "\r\n\r\n"
86
95
  end
87
96
 
97
+ private
98
+
99
+ def header_for_rerequest
100
+ head = header
101
+ head.delete("host")
102
+ head
103
+ end
104
+
88
105
  end
89
106
  end
@@ -73,7 +73,7 @@ module Octarine # :nodoc:
73
73
  when 2
74
74
  @body = value
75
75
  else
76
- raise ArgumentError("Unexpected key #{key}")
76
+ raise ArgumentError.new("Unexpected key #{key}")
77
77
  end
78
78
  end
79
79
 
@@ -91,6 +91,7 @@ module Octarine # :nodoc:
91
91
 
92
92
  def apply(object, path=nil, &block)
93
93
  if object.respond_to?(:to_ary)
94
+ path = nil if path == "."
94
95
  return object.to_ary.map {|obj| apply(obj, path, &block)}
95
96
  end
96
97
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: octarine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-02 00:00:00.000000000Z
12
+ date: 2012-06-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: http_router
16
- requirement: &2153077060 !ruby/object:Gem::Requirement
16
+ requirement: &2152395640 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2153077060
24
+ version_requirements: *2152395640
25
25
  description: Sinatra-like DSL for writing a HTTP routing proxy.
26
26
  email: mat@sourcetagsandcodes.com
27
27
  executables: []
@@ -30,8 +30,8 @@ extra_rdoc_files:
30
30
  - README.rdoc
31
31
  files:
32
32
  - lib/octarine/app.rb
33
- - lib/octarine/endpoint.rb
34
33
  - lib/octarine/path.rb
34
+ - lib/octarine/path_template.rb
35
35
  - lib/octarine/request.rb
36
36
  - lib/octarine/response.rb
37
37
  - lib/octarine/simple_http.rb
@@ -61,7 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
61
  version: '0'
62
62
  requirements: []
63
63
  rubyforge_project:
64
- rubygems_version: 1.8.7
64
+ rubygems_version: 1.8.11
65
65
  signing_key:
66
66
  specification_version: 3
67
67
  summary: HTTP routing proxy DSL
@@ -1,125 +0,0 @@
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