octarine 0.0.1 → 0.0.2

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