curb-fu 0.4.3
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/lib/curb-fu.rb +64 -0
- data/lib/curb-fu/authentication.rb +5 -0
- data/lib/curb-fu/core_ext.rb +59 -0
- data/lib/curb-fu/entity.rb +54 -0
- data/lib/curb-fu/request.rb +12 -0
- data/lib/curb-fu/request/base.rb +118 -0
- data/lib/curb-fu/request/common.rb +34 -0
- data/lib/curb-fu/request/parameter.rb +33 -0
- data/lib/curb-fu/request/test.rb +153 -0
- data/lib/curb-fu/response.rb +179 -0
- data/lib/curb-fu/test.rb +6 -0
- data/lib/curb-fu/test/request_logger.rb +31 -0
- data/lib/curb-fu/test/server.rb +21 -0
- data/spec/fixtures/foo.txt +1 -0
- data/spec/lib/curb-fu/core_ext_spec.rb +100 -0
- data/spec/lib/curb-fu/entity_spec.rb +37 -0
- data/spec/lib/curb-fu/request/base_spec.rb +240 -0
- data/spec/lib/curb-fu/request/parameter_spec.rb +77 -0
- data/spec/lib/curb-fu/request/test_spec.rb +223 -0
- data/spec/lib/curb-fu/response_spec.rb +99 -0
- data/spec/lib/curb_fu_spec.rb +49 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +11 -0
- metadata +95 -0
data/lib/curb-fu.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
$:.unshift(dir) unless $:.include?(dir)
|
3
|
+
require 'curb-fu/response'
|
4
|
+
require 'curb-fu/request'
|
5
|
+
require 'curb-fu/entity'
|
6
|
+
require 'curb-fu/authentication'
|
7
|
+
require 'curb-fu/core_ext'
|
8
|
+
require 'curb-fu/test'
|
9
|
+
|
10
|
+
module CurbFu
|
11
|
+
class << self
|
12
|
+
def get(*args)
|
13
|
+
CurbFu::Request.get(*args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def post(*args)
|
17
|
+
CurbFu::Request.post(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def put(*args)
|
21
|
+
CurbFu::Request.put(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(*args)
|
25
|
+
CurbFu::Request.delete(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_accessor :stubs
|
29
|
+
|
30
|
+
def stubs=(val)
|
31
|
+
if val
|
32
|
+
@stubs = {}
|
33
|
+
val.each do |hostname, rack_app|
|
34
|
+
stub(hostname, rack_app)
|
35
|
+
end
|
36
|
+
|
37
|
+
unless CurbFu::Request.include?(CurbFu::Request::Test)
|
38
|
+
CurbFu::Request.send(:include, CurbFu::Request::Test)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
@stubs = nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def stub(hostname, rack_app)
|
46
|
+
raise "You must use CurbFu.stubs= to define initial stubs before using stub()" if @stubs.nil?
|
47
|
+
@stubs[hostname] = CurbFu::Request::Test::Interface.new(rack_app, hostname)
|
48
|
+
end
|
49
|
+
|
50
|
+
def stubs
|
51
|
+
@stubs
|
52
|
+
end
|
53
|
+
|
54
|
+
def debug=(val)
|
55
|
+
@debug = val ? true : false
|
56
|
+
end
|
57
|
+
|
58
|
+
def debug?
|
59
|
+
@debug
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
##
|
4
|
+
# ActiveSupport look alike for to_param. Very useful.
|
5
|
+
module CurbFu
|
6
|
+
module HashExtensions
|
7
|
+
def self.included(base)
|
8
|
+
base.send(:include, InstanceMethods) unless base.methods.include?(:to_param)
|
9
|
+
#base.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
def to_param(prefix)
|
14
|
+
collect do |k, v|
|
15
|
+
key_prefix = prefix ? "#{prefix}[#{k}]" : k
|
16
|
+
v.to_param(key_prefix)
|
17
|
+
end.join("&")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ObjectExtensions
|
23
|
+
def self.included(base)
|
24
|
+
base.send(:include, InstanceMethods) unless base.methods.include?(:to_param)
|
25
|
+
#base.extend(ClassMethods)
|
26
|
+
end
|
27
|
+
|
28
|
+
module InstanceMethods
|
29
|
+
def to_param(prefix)
|
30
|
+
value = CGI::escape(to_s)
|
31
|
+
"#{prefix}=#{value}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module ArrayExtensions
|
37
|
+
def self.included(base)
|
38
|
+
base.send(:include, InstanceMethods) unless base.methods.include?(:to_param)
|
39
|
+
#base.extend(ClassMethods)
|
40
|
+
end
|
41
|
+
|
42
|
+
module InstanceMethods
|
43
|
+
def to_param(prefix)
|
44
|
+
prefix = "#{prefix}[]"
|
45
|
+
collect { |item| "#{item.to_param(prefix)}" }.join('&')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Hash
|
52
|
+
include CurbFu::HashExtensions
|
53
|
+
end
|
54
|
+
class Array
|
55
|
+
include CurbFu::ArrayExtensions
|
56
|
+
end
|
57
|
+
class Object
|
58
|
+
include CurbFu::ObjectExtensions
|
59
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module CurbFu
|
5
|
+
class Entity
|
6
|
+
attr_accessor :content_type, :data
|
7
|
+
|
8
|
+
def initialze(data)
|
9
|
+
self.data = data
|
10
|
+
end
|
11
|
+
|
12
|
+
def data
|
13
|
+
@data ||= ''
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_request_data
|
17
|
+
return data if data.is_a?(String)
|
18
|
+
|
19
|
+
case content_type
|
20
|
+
when :uri then
|
21
|
+
to_uri
|
22
|
+
when 'application/json',:json then
|
23
|
+
to_json
|
24
|
+
# when 'application/xml',:xml then to_xml
|
25
|
+
when 'text/www-form-urlencoded',:form,:url then
|
26
|
+
to_www_form_url_encoded
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# def to_xml
|
31
|
+
# "XML"
|
32
|
+
# end
|
33
|
+
|
34
|
+
def to_uri
|
35
|
+
strings = data.inject([]) do |acc, (name, value)|
|
36
|
+
acc << "#{name.to_s}=#{value}"
|
37
|
+
acc
|
38
|
+
end
|
39
|
+
URI.escape(strings.join('&'))
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_json
|
43
|
+
data.to_json
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_www_form_url_encoded
|
47
|
+
strings = data.inject([]) do |acc, (name, value)|
|
48
|
+
acc << "#{CGI.escape(name.to_s)}=#{CGI.escape(value)}"
|
49
|
+
acc
|
50
|
+
end
|
51
|
+
strings.join('&')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module CurbFu
|
2
|
+
class Request
|
3
|
+
module Base
|
4
|
+
include Common
|
5
|
+
|
6
|
+
def build(url_params, query_params = {})
|
7
|
+
curb = Curl::Easy.new(build_url(url_params, query_params))
|
8
|
+
|
9
|
+
headers = global_headers
|
10
|
+
|
11
|
+
unless url_params.is_a?(String)
|
12
|
+
curb.userpwd = "#{url_params[:username]}:#{url_params[:password]}" if url_params[:username]
|
13
|
+
if url_params[:authtype]
|
14
|
+
curb.http_auth_types = url_params[:authtype]
|
15
|
+
elsif url_params[:username]
|
16
|
+
curb.http_auth_types = CurbFu::Authentication::BASIC
|
17
|
+
end
|
18
|
+
|
19
|
+
headers = headers.merge(url_params[:headers]) unless url_params[:headers].nil?
|
20
|
+
headers["Expect"] = '' unless url_params[:headers] && url_params[:headers]["Expect"]
|
21
|
+
end
|
22
|
+
|
23
|
+
curb.headers = headers
|
24
|
+
curb.timeout = @timeout
|
25
|
+
|
26
|
+
curb
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set headers to be used for every request
|
30
|
+
# * headers: hash of header names and values
|
31
|
+
def global_headers=(headers)
|
32
|
+
@global_headers = headers
|
33
|
+
end
|
34
|
+
|
35
|
+
# Headers to be used for every request
|
36
|
+
# Returns: hash of header names and values
|
37
|
+
def global_headers
|
38
|
+
@global_headers ||= {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def get(url, params = {})
|
42
|
+
curb = self.build(url, params)
|
43
|
+
curb.http_get
|
44
|
+
CurbFu::Response::Base.from_curb_response(curb)
|
45
|
+
end
|
46
|
+
|
47
|
+
def put(url, params = {})
|
48
|
+
curb = self.build(url, params)
|
49
|
+
curb.http_put("")
|
50
|
+
CurbFu::Response::Base.from_curb_response(curb)
|
51
|
+
end
|
52
|
+
|
53
|
+
def post(url, params = {})
|
54
|
+
fields = create_post_fields(params)
|
55
|
+
fields = [fields] if fields.is_a?(String)
|
56
|
+
|
57
|
+
curb = self.build(url)
|
58
|
+
curb.http_post(*fields)
|
59
|
+
CurbFu::Response::Base.from_curb_response(curb)
|
60
|
+
end
|
61
|
+
|
62
|
+
def post_file(url, params = {}, filez = {})
|
63
|
+
fields = create_post_fields(params)
|
64
|
+
fields += create_file_fields(filez)
|
65
|
+
|
66
|
+
curb = self.build(url)
|
67
|
+
curb.multipart_form_post = true
|
68
|
+
|
69
|
+
begin
|
70
|
+
curb.http_post(*fields)
|
71
|
+
rescue Curl::Err::InvalidPostFieldError => e
|
72
|
+
field_list = (params.merge(filez)).inject([]) { |list, (name, value)| list << "#{name} => #{value.to_s[0..49].inspect}"; list }
|
73
|
+
raise e, "There was an attempt to post invalid fields. The fields were:\n#{field_list.join("\n")}"
|
74
|
+
end
|
75
|
+
CurbFu::Response::Base.from_curb_response(curb)
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete(url)
|
79
|
+
curb = self.build(url)
|
80
|
+
curb.http_delete
|
81
|
+
CurbFu::Response::Base.from_curb_response(curb)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def create_post_fields(params)
|
86
|
+
return params if params.is_a? String
|
87
|
+
|
88
|
+
fields = []
|
89
|
+
params.each do |name, value|
|
90
|
+
value_string = value if value.is_a?(String)
|
91
|
+
value_string = value.join(',') if value.is_a?(Array)
|
92
|
+
value_string ||= value.to_s
|
93
|
+
|
94
|
+
fields << Curl::PostField.content(name.to_s,value_string)
|
95
|
+
end
|
96
|
+
return fields
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_put_fields(params)
|
100
|
+
return params if params.is_a? String
|
101
|
+
|
102
|
+
params.inject([]) do |list, (k,v)|
|
103
|
+
v = v.is_a?(Array) ? v.join(',') : v
|
104
|
+
list << "#{k}=#{v}"
|
105
|
+
list
|
106
|
+
end.join('&')
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_file_fields(filez)
|
110
|
+
fields = []
|
111
|
+
filez.each do |name, path|
|
112
|
+
fields << Curl::PostField.file(name, path)
|
113
|
+
end
|
114
|
+
fields
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module CurbFu
|
2
|
+
class Request
|
3
|
+
module Common
|
4
|
+
def timeout=(val)
|
5
|
+
@timeout = val
|
6
|
+
end
|
7
|
+
|
8
|
+
def timeout
|
9
|
+
@timeout.nil? ? 10 : @timeout
|
10
|
+
end
|
11
|
+
|
12
|
+
def build_url(url_params, query_params = {})
|
13
|
+
if url_params.is_a? String
|
14
|
+
built_url = url_params
|
15
|
+
else
|
16
|
+
built_url = "http://#{url_params[:host]}"
|
17
|
+
built_url += ":" + url_params[:port].to_s if url_params[:port]
|
18
|
+
built_url += url_params[:path] if url_params[:path]
|
19
|
+
end
|
20
|
+
|
21
|
+
# TODO: update for use with CurbFu::Entity
|
22
|
+
if query_params.is_a? String
|
23
|
+
built_url += query_params
|
24
|
+
elsif !query_params.empty?
|
25
|
+
built_url += "?"
|
26
|
+
built_url += query_params.collect do |name, value|
|
27
|
+
CurbFu::Request::Parameter.new(name, value).to_uri_param
|
28
|
+
end.join('&')
|
29
|
+
end
|
30
|
+
built_url
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module CurbFu
|
2
|
+
class Request
|
3
|
+
class Parameter
|
4
|
+
attr_accessor :name, :value
|
5
|
+
|
6
|
+
def initialize(name, value)
|
7
|
+
self.name = name
|
8
|
+
self.value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.build_uri_params(param_hash)
|
12
|
+
param_hash.to_param
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.build_post_fields(param_hash)
|
16
|
+
param_hash.to_post_fields
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_uri_param
|
20
|
+
value.to_param(name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_curl_post_field
|
24
|
+
field_string = value.to_param(name)
|
25
|
+
fields = field_string.split('&').collect do |field_value_pair|
|
26
|
+
field_name, field_value = field_value_pair.split('=')
|
27
|
+
Curl::PostField.content(field_name, CGI::unescape(field_value))
|
28
|
+
end
|
29
|
+
fields.length == 1 ? fields[0] : fields
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'rack/test'
|
2
|
+
|
3
|
+
module CurbFu
|
4
|
+
class Request
|
5
|
+
module Test
|
6
|
+
include Common
|
7
|
+
|
8
|
+
def self.included(target)
|
9
|
+
target.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def get(url, params = {})
|
14
|
+
request_options = build_request_options(url)
|
15
|
+
params = hashify_params(params) if params.is_a?(String)
|
16
|
+
respond(request_options[:interface], :get, request_options[:url],
|
17
|
+
params, request_options[:headers], request_options[:username], request_options[:password])
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(url, params = {})
|
21
|
+
request_options = build_request_options(url)
|
22
|
+
respond(request_options[:interface], :post, request_options[:url],
|
23
|
+
params, request_options[:headers], request_options[:username], request_options[:password])
|
24
|
+
end
|
25
|
+
|
26
|
+
def post_file(url, params = {}, filez = {})
|
27
|
+
request_options = build_request_options(url)
|
28
|
+
uploaded_files = filez.inject({}) do |hsh, (name, path)|
|
29
|
+
hsh[name] = Rack::Test::UploadedFile.new(path)
|
30
|
+
hsh
|
31
|
+
end
|
32
|
+
respond(request_options[:interface], :post, request_options[:url],
|
33
|
+
params.merge(uploaded_files), request_options[:headers], request_options[:username], request_options[:password])
|
34
|
+
end
|
35
|
+
|
36
|
+
def put(url, params = {})
|
37
|
+
request_options = build_request_options(url)
|
38
|
+
respond(request_options[:interface], :put, request_options[:url],
|
39
|
+
params, request_options[:headers], request_options[:username], request_options[:password])
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete(url, params = {})
|
43
|
+
request_options = build_request_options(url)
|
44
|
+
params = hashify_params(params) if params.is_a?(String)
|
45
|
+
respond(request_options[:interface], :delete, request_options[:url],
|
46
|
+
params, request_options[:headers], request_options[:username], request_options[:password])
|
47
|
+
end
|
48
|
+
|
49
|
+
def hashify_params(param_string)
|
50
|
+
param_string.sub!(/^\?/,'')
|
51
|
+
param_string.split('&').inject({}) do |hsh, pair|
|
52
|
+
key, value = pair.split('=')
|
53
|
+
|
54
|
+
if key.match(/(.+)\[\]$/)
|
55
|
+
key = $1
|
56
|
+
hsh[key] ||= []
|
57
|
+
hsh[key] << value
|
58
|
+
elsif key.match(/([^\[]+)\[(.+)\]$/)
|
59
|
+
key = $1
|
60
|
+
subkey = $2
|
61
|
+
hsh[key] ||= {}
|
62
|
+
hsh[key].update( subkey => value )
|
63
|
+
else
|
64
|
+
hsh[key] = value
|
65
|
+
end
|
66
|
+
|
67
|
+
hsh
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_request_options(url)
|
72
|
+
options = {}
|
73
|
+
options[:headers] = (url.is_a?(String)) ? nil : url.delete(:headers)
|
74
|
+
options[:url] = build_url(url)
|
75
|
+
options[:username], options[:password] = get_auth(url)
|
76
|
+
options[:interface] = get_interface(url)
|
77
|
+
options
|
78
|
+
end
|
79
|
+
|
80
|
+
def respond(interface, operation, url, params, headers, username = nil, password = nil)
|
81
|
+
if interface.nil?
|
82
|
+
raise Curl::Err::ConnectionFailedError
|
83
|
+
else
|
84
|
+
unless headers.nil?
|
85
|
+
process_headers(headers).each do |name, value|
|
86
|
+
interface.header(name, value)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
interface.authorize(username, password) unless username.nil?
|
90
|
+
puts "sending #{operation} to #{url} with params #{params.inspect} using interface #{interface.inspect}" if CurbFu.debug?
|
91
|
+
begin
|
92
|
+
response = interface.send(operation, url, params, 'SERVER_NAME' => interface.hostname)
|
93
|
+
rescue => e
|
94
|
+
puts "Caught error: #{e}, #{e.backtrace.join("\n")}" if CurbFu.debug?
|
95
|
+
raise e
|
96
|
+
end
|
97
|
+
CurbFu::Response::Base.from_rack_response(response)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def process_headers(headers)
|
102
|
+
headers.inject({}) do |accum, (header_name, value)|
|
103
|
+
key = header_name.gsub("-", "_").upcase
|
104
|
+
key = "HTTP_" + key unless key =~ /^HTTP_/
|
105
|
+
accum[key] = value
|
106
|
+
accum
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_interface(url)
|
111
|
+
if url.is_a?(Hash)
|
112
|
+
host = url[:host]
|
113
|
+
else
|
114
|
+
host = parse_hostname(url)
|
115
|
+
end
|
116
|
+
match_host(host)
|
117
|
+
end
|
118
|
+
|
119
|
+
def parse_hostname(uri)
|
120
|
+
parsed_hostname = URI.parse(uri)
|
121
|
+
parsed_hostname.host || parsed_hostname.path
|
122
|
+
end
|
123
|
+
|
124
|
+
def match_host(host)
|
125
|
+
match = CurbFu.stubs.find do |(hostname, interface)|
|
126
|
+
hostname == host
|
127
|
+
end
|
128
|
+
match.last unless match.nil?
|
129
|
+
end
|
130
|
+
|
131
|
+
def get_auth(url)
|
132
|
+
username = password = nil
|
133
|
+
if url.is_a?(Hash) && url[:username]
|
134
|
+
username = url[:username]
|
135
|
+
password = url[:password]
|
136
|
+
end
|
137
|
+
[username, password]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class Interface
|
142
|
+
include Rack::Test::Methods
|
143
|
+
|
144
|
+
attr_accessor :app, :hostname
|
145
|
+
|
146
|
+
def initialize(app, hostname = 'example.org')
|
147
|
+
self.app = app
|
148
|
+
self.hostname = hostname
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|