partigi-partigirb 0.2.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/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +9 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/lib/partigirb/client.rb +190 -0
- data/lib/partigirb/core_ext.rb +78 -0
- data/lib/partigirb/handlers/atom_handler.rb +24 -0
- data/lib/partigirb/handlers/json_handler.rb +10 -0
- data/lib/partigirb/handlers/string_handler.rb +9 -0
- data/lib/partigirb/handlers/xml_handler.rb +51 -0
- data/lib/partigirb/transport.rb +160 -0
- data/lib/partigirb.rb +25 -0
- data/partigirb.gemspec +69 -0
- data/test/atom_handler_test.rb +61 -0
- data/test/client_test.rb +198 -0
- data/test/fixtures/alvaro_friends.atom.xml +80 -0
- data/test/fixtures/pulp_fiction.atom.xml +99 -0
- data/test/json_handler_test.rb +7 -0
- data/test/mocks/net_http_mock.rb +12 -0
- data/test/mocks/response_mock.rb +12 -0
- data/test/mocks/transport_mock.rb +15 -0
- data/test/test_helper.rb +47 -0
- data/test/transport_test.rb +8 -0
- data/test/xml_handler_test.rb +65 -0
- metadata +89 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Alvaro Bautista & Fernando Blat
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "partigirb"
|
8
|
+
gem.summary = %Q{TODO}
|
9
|
+
gem.email = ["alvarobp@gmail.com", "ferblape@gmail.com"]
|
10
|
+
gem.homepage = "http://github.com/partigi/partigirb"
|
11
|
+
gem.authors = ["Alvaro Bautista", "Fernando Blat"]
|
12
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
13
|
+
end
|
14
|
+
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
require 'rake/rdoctask'
|
43
|
+
Rake::RDocTask.new do |rdoc|
|
44
|
+
if File.exist?('VERSION.yml')
|
45
|
+
config = YAML.load(File.read('VERSION.yml'))
|
46
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
47
|
+
else
|
48
|
+
version = ""
|
49
|
+
end
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "partigirb #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
56
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.1
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module Partigirb
|
2
|
+
|
3
|
+
class PartigiStruct < OpenStruct
|
4
|
+
attr_accessor :id
|
5
|
+
end
|
6
|
+
|
7
|
+
#Raised by methods which call the API if a non-200 response status is received
|
8
|
+
class PartigiError < StandardError
|
9
|
+
attr_accessor :response_object
|
10
|
+
end
|
11
|
+
|
12
|
+
class Client
|
13
|
+
class Request #:nodoc:
|
14
|
+
attr_accessor :client, :path, :method, :api_version
|
15
|
+
|
16
|
+
def initialize(client,api_version=Partigirb::CURRENT_API_VERSION)
|
17
|
+
self.client = client
|
18
|
+
self.api_version = api_version
|
19
|
+
self.method = :get
|
20
|
+
self.path = ''
|
21
|
+
end
|
22
|
+
|
23
|
+
def <<(path)
|
24
|
+
self.path << path
|
25
|
+
end
|
26
|
+
|
27
|
+
def path?
|
28
|
+
path.length > 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def url
|
32
|
+
"#{scheme}://#{host}/api/v#{self.api_version}#{path}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def host
|
36
|
+
client.api_host
|
37
|
+
end
|
38
|
+
|
39
|
+
def scheme
|
40
|
+
'http'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
VALID_METHODS = [:get,:post,:put,:delete]
|
45
|
+
VALID_FORMATS = [:atom,:xml,:json]
|
46
|
+
|
47
|
+
PARTIGI_API_HOST = "www.partigi.com"
|
48
|
+
TIMESTAMP_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
|
49
|
+
|
50
|
+
attr_accessor :default_format, :headers, :api_version, :transport, :request, :api_host, :auth, :handlers
|
51
|
+
|
52
|
+
def initialize(options={})
|
53
|
+
self.transport = Transport.new
|
54
|
+
self.api_host = PARTIGI_API_HOST.clone
|
55
|
+
self.api_version = options[:api_version] || Partigirb::CURRENT_API_VERSION
|
56
|
+
self.headers = {"User-Agent"=>"Partigirb/#{Partigirb::VERSION}"}.merge!(options[:headers]||{})
|
57
|
+
self.default_format = options[:default_format] || :atom
|
58
|
+
self.handlers = {
|
59
|
+
:json => Partigirb::Handlers::JSONHandler.new,
|
60
|
+
:xml => Partigirb::Handlers::XMLHandler.new,
|
61
|
+
:atom => Partigirb::Handlers::AtomHandler.new,
|
62
|
+
:unknown => Partigirb::Handlers::StringHandler.new
|
63
|
+
}
|
64
|
+
self.handlers.merge!(options[:handlers]||{})
|
65
|
+
|
66
|
+
# Authentication param should be a hash with keys:
|
67
|
+
# login (required)
|
68
|
+
# api_secret (required)
|
69
|
+
# nonce (optional, would be automatically generated if missing)
|
70
|
+
# timestamp (optional, current timestamp will be automatically used if missing)
|
71
|
+
self.auth = options[:auth]
|
72
|
+
end
|
73
|
+
|
74
|
+
def method_missing(name,*args)
|
75
|
+
# If method is a format name, execute using that format
|
76
|
+
if format_invocation?(name)
|
77
|
+
return call_with_format(name,*args)
|
78
|
+
end
|
79
|
+
# If method ends in ! or ? use that to determine post or get
|
80
|
+
if name.to_s =~ /^(.*)(!|\?)$/
|
81
|
+
name = $1.to_sym
|
82
|
+
# ! is a post, ? is a get
|
83
|
+
self.request.method = ($2 == '!' ? :post : :get)
|
84
|
+
if format_invocation?(name)
|
85
|
+
return call_with_format(name,*args)
|
86
|
+
else
|
87
|
+
self.request << "/#{$1}"
|
88
|
+
return call_with_format(self.default_format,*args)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
# Else add to the request path
|
92
|
+
self.request << "/#{name}"
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
# Clears any pending request built up by chained methods but not executed
|
97
|
+
def clear
|
98
|
+
self.request = nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def request
|
102
|
+
@request ||= Request.new(self,api_version)
|
103
|
+
end
|
104
|
+
|
105
|
+
protected
|
106
|
+
|
107
|
+
def call_with_format(format,params={})
|
108
|
+
request << ".#{format}"
|
109
|
+
res = send_request(params)
|
110
|
+
process_response(format,res)
|
111
|
+
ensure
|
112
|
+
clear
|
113
|
+
end
|
114
|
+
|
115
|
+
def send_request(params)
|
116
|
+
begin
|
117
|
+
set_authentication_headers
|
118
|
+
|
119
|
+
transport.request(
|
120
|
+
request.method, request.url, :headers=>headers, :params=>params
|
121
|
+
)
|
122
|
+
rescue => e
|
123
|
+
puts e
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def process_response(format, res)
|
128
|
+
fmt_handler = handler(format)
|
129
|
+
|
130
|
+
begin
|
131
|
+
if res.code.to_i != 200
|
132
|
+
handle_error_response(res, Partigirb::Handlers::XMLHandler)
|
133
|
+
else
|
134
|
+
fmt_handler.decode_response(res.body)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# TODO: Test for errors
|
140
|
+
def handle_error_response(res, handler)
|
141
|
+
err = Partigirb::PartigiError.new
|
142
|
+
err.response_object = handler.decode_response(res.body)
|
143
|
+
raise err
|
144
|
+
end
|
145
|
+
|
146
|
+
def format_invocation?(name)
|
147
|
+
self.request.path? && VALID_FORMATS.include?(name)
|
148
|
+
end
|
149
|
+
|
150
|
+
def handler(format)
|
151
|
+
handlers[format] || handlers[:unknown]
|
152
|
+
end
|
153
|
+
|
154
|
+
# Adds the proper WSSE headers if there are the right authentication parameters
|
155
|
+
def set_authentication_headers
|
156
|
+
unless self.auth.nil? || self.auth === Hash || self.auth.empty?
|
157
|
+
auths = self.auth.stringify_keys
|
158
|
+
|
159
|
+
if auths.has_key?('login') && auths.has_key?('api_secret')
|
160
|
+
if !auths['timestamp'].nil?
|
161
|
+
timestamp = case auths['timestamp']
|
162
|
+
when Time
|
163
|
+
auths['timestamp'].strftime(TIMESTAMP_FORMAT)
|
164
|
+
when String
|
165
|
+
auths['timestamp']
|
166
|
+
end
|
167
|
+
else
|
168
|
+
timestamp = Time.now.strftime(TIMESTAMP_FORMAT) if timestamp.nil?
|
169
|
+
end
|
170
|
+
|
171
|
+
nonce = auths['nonce'] || generate_nonce
|
172
|
+
password_digest = generate_password_digest(nonce, timestamp, auths['login'], auths['api_secret'])
|
173
|
+
headers.merge!({
|
174
|
+
'Authorization' => "WSSE realm=\"#{PARTIGI_API_HOST}\", profile=\"UsernameToken\"",
|
175
|
+
'X-WSSE' => "UsernameToken Username=\"#{auths['login']}\", PasswordDigest=\"#{password_digest}\", Nonce=\"#{nonce}\", Created=\"#{timestamp}\""
|
176
|
+
})
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def generate_nonce
|
182
|
+
o = [('a'..'z'),('A'..'Z')].map{|i| i.to_a}.flatten
|
183
|
+
Digest::MD5.hexdigest((0..10).map{o[rand(o.length)]}.join)
|
184
|
+
end
|
185
|
+
|
186
|
+
def generate_password_digest(nonce, timestamp, login, secret)
|
187
|
+
Base64.encode64(Digest::SHA1.hexdigest("#{nonce}#{timestamp}#{login}#{secret}")).chomp
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Ruby Hash extensions from ActiveSupport
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
# Return a new hash with all keys converted to strings.
|
5
|
+
def stringify_keys
|
6
|
+
inject({}) do |options, (key, value)|
|
7
|
+
options[key.to_s] = value
|
8
|
+
options
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Destructively convert all keys to strings.
|
13
|
+
def stringify_keys!
|
14
|
+
keys.each do |key|
|
15
|
+
self[key.to_s] = delete(key)
|
16
|
+
end
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Object
|
22
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
23
|
+
# For example, "", " ", +nil+, [], and {} are blank.
|
24
|
+
#
|
25
|
+
# This simplifies
|
26
|
+
#
|
27
|
+
# if !address.nil? && !address.empty?
|
28
|
+
#
|
29
|
+
# to
|
30
|
+
#
|
31
|
+
# if !address.blank?
|
32
|
+
def blank?
|
33
|
+
respond_to?(:empty?) ? empty? : !self
|
34
|
+
end
|
35
|
+
|
36
|
+
# An object is present if it's not blank.
|
37
|
+
def present?
|
38
|
+
!blank?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class NilClass #:nodoc:
|
43
|
+
def blank?
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class FalseClass #:nodoc:
|
49
|
+
def blank?
|
50
|
+
true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class TrueClass #:nodoc:
|
55
|
+
def blank?
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Array #:nodoc:
|
61
|
+
alias_method :blank?, :empty?
|
62
|
+
end
|
63
|
+
|
64
|
+
class Hash #:nodoc:
|
65
|
+
alias_method :blank?, :empty?
|
66
|
+
end
|
67
|
+
|
68
|
+
class String #:nodoc:
|
69
|
+
def blank?
|
70
|
+
self !~ /\S/
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Numeric #:nodoc:
|
75
|
+
def blank?
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Partigirb
|
2
|
+
module Handlers
|
3
|
+
class AtomHandler < XMLHandler
|
4
|
+
def decode_response(body)
|
5
|
+
return REXML::Document.new if body.blank?
|
6
|
+
xml = REXML::Document.new(body.gsub(/>\s+</,'><'))
|
7
|
+
|
8
|
+
if xml.root.name == 'feed'
|
9
|
+
entries = xml.root.get_elements('entry')
|
10
|
+
|
11
|
+
# Depending on whether we have one or more entries we return an PartigiStruct or an array of PartigiStruct
|
12
|
+
if entries.size == 1
|
13
|
+
load_recursive(entries.first)
|
14
|
+
else
|
15
|
+
entries.map{|e| load_recursive(e)}
|
16
|
+
end
|
17
|
+
else
|
18
|
+
# We just parse as a common XML
|
19
|
+
load_recursive(xml.root)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Partigirb
|
2
|
+
module Handlers
|
3
|
+
class XMLHandler
|
4
|
+
def decode_response(body)
|
5
|
+
return REXML::Document.new if body.blank?
|
6
|
+
xml = REXML::Document.new(body.gsub(/>\s+</,'><'))
|
7
|
+
load_recursive(xml.root)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def load_recursive(node)
|
12
|
+
if array_node?(node)
|
13
|
+
node.elements.map {|e| load_recursive(e)}
|
14
|
+
elsif node.elements.size > 0
|
15
|
+
build_struct(node)
|
16
|
+
elsif node.elements.size == 0
|
17
|
+
value = node.text
|
18
|
+
fixnum?(value) ? value.to_i : value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_struct(node)
|
23
|
+
ts = PartigiStruct.new
|
24
|
+
node.elements.each do |e|
|
25
|
+
property = ""
|
26
|
+
|
27
|
+
if !e.namespace.blank?
|
28
|
+
ns = e.namespaces.invert[e.namespace]
|
29
|
+
property << "#{ns}_" unless ns == 'xmlns'
|
30
|
+
end
|
31
|
+
|
32
|
+
property << e.name
|
33
|
+
ts.send("#{property}=", load_recursive(e))
|
34
|
+
end
|
35
|
+
ts
|
36
|
+
end
|
37
|
+
|
38
|
+
# Most of the time Twitter specifies nodes that contain an array of
|
39
|
+
# sub-nodes with a type="array" attribute. There are some nodes that
|
40
|
+
# they dont' do that for, though, including the <ids> node returned
|
41
|
+
# by the social graph methods. This method tries to work in both situations.
|
42
|
+
def array_node?(node)
|
43
|
+
node.attributes['type'] == 'collection'
|
44
|
+
end
|
45
|
+
|
46
|
+
def fixnum?(value)
|
47
|
+
value =~ /^\d+$/
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module Partigirb
|
2
|
+
|
3
|
+
class Response #:nodoc:
|
4
|
+
attr_accessor :method, :request_uri, :status, :body
|
5
|
+
|
6
|
+
def initialize(method,request_uri,status,body)
|
7
|
+
self.method = method
|
8
|
+
self.request_uri = request_uri
|
9
|
+
self.status = status
|
10
|
+
self.body = body
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Transport
|
15
|
+
|
16
|
+
attr_accessor :debug
|
17
|
+
|
18
|
+
CRLF = "\r\n"
|
19
|
+
|
20
|
+
def req_class(method)
|
21
|
+
case method
|
22
|
+
when :get then Net::HTTP::Get
|
23
|
+
when :post then Net::HTTP::Post
|
24
|
+
when :put then Net::HTTP::Put
|
25
|
+
when :delete then Net::HTTP::Delete
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Options are one of
|
30
|
+
# - :params - a hash of parameters to be sent with the request. If a File is a parameter value, \
|
31
|
+
# a multipart request will be sent. If a Time is included, .httpdate will be called on it.
|
32
|
+
# - :headers - a hash of headers to send with the request
|
33
|
+
def request(method, string_url, options={})
|
34
|
+
params = stringify_params(options[:params])
|
35
|
+
if method == :get && params
|
36
|
+
string_url << query_string(params)
|
37
|
+
end
|
38
|
+
url = URI.parse(string_url)
|
39
|
+
begin
|
40
|
+
execute_request(method,url,options)
|
41
|
+
rescue Timeout::Error
|
42
|
+
raise "Timeout while #{method}ing #{url.to_s}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def execute_request(method,url,options={})
|
47
|
+
conn = Net::HTTP.new(url.host, url.port)
|
48
|
+
#conn.use_ssl = (url.scheme == 'https')
|
49
|
+
conn.start do |http|
|
50
|
+
req = req_class(method).new(url.request_uri)
|
51
|
+
|
52
|
+
add_headers(req,options[:headers])
|
53
|
+
if file_param?(options[:params])
|
54
|
+
add_multipart_data(req,options[:params])
|
55
|
+
else
|
56
|
+
add_form_data(req,options[:params])
|
57
|
+
end
|
58
|
+
|
59
|
+
dump_request(req) if debug
|
60
|
+
res = http.request(req)
|
61
|
+
dump_response(res) if debug
|
62
|
+
res
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def query_string(params)
|
67
|
+
query = case params
|
68
|
+
when Hash then params.map{|key,value| url_encode_param(key,value) }.join("&")
|
69
|
+
else url_encode(params.to_s)
|
70
|
+
end
|
71
|
+
if !(query == nil || query.length == 0) && query[0,1] != '?'
|
72
|
+
query = "?#{query}"
|
73
|
+
end
|
74
|
+
query
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def stringify_params(params)
|
79
|
+
return nil unless params
|
80
|
+
params.inject({}) do |h, pair|
|
81
|
+
key, value = pair
|
82
|
+
if value.respond_to? :httpdate
|
83
|
+
value = value.httpdate
|
84
|
+
end
|
85
|
+
h[key] = value
|
86
|
+
h
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def file_param?(params)
|
91
|
+
return false unless params
|
92
|
+
params.any? {|key,value| value.respond_to? :read }
|
93
|
+
end
|
94
|
+
|
95
|
+
def url_encode(value)
|
96
|
+
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
97
|
+
CGI.escape(value.to_s)
|
98
|
+
end
|
99
|
+
|
100
|
+
def url_encode_param(key,value)
|
101
|
+
"#{url_encode(key)}=#{url_encode(value)}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_headers(req,headers)
|
105
|
+
if headers
|
106
|
+
headers.each do |header, value|
|
107
|
+
req[header] = value
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def add_form_data(req,params)
|
113
|
+
if req.request_body_permitted? && params
|
114
|
+
req.set_form_data(params)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def add_multipart_data(req,params)
|
119
|
+
boundary = Time.now.to_i.to_s(16)
|
120
|
+
req["Content-Type"] = "multipart/form-data; boundary=#{boundary}"
|
121
|
+
body = ""
|
122
|
+
params.each do |key,value|
|
123
|
+
esc_key = url_encode(key)
|
124
|
+
body << "--#{boundary}#{CRLF}"
|
125
|
+
if value.respond_to?(:read)
|
126
|
+
mime_type = MIME::Types.type_for(value.path)[0] || MIME::Types["application/octet-stream"][0]
|
127
|
+
body << "Content-Disposition: form-data; name=\"#{esc_key}\"; filename=\"#{File.basename(value.path)}\"#{CRLF}"
|
128
|
+
body << "Content-Type: #{mime_type.simplified}#{CRLF*2}"
|
129
|
+
body << value.read
|
130
|
+
else
|
131
|
+
body << "Content-Disposition: form-data; name=\"#{esc_key}\"#{CRLF*2}#{value}"
|
132
|
+
end
|
133
|
+
body << CRLF
|
134
|
+
end
|
135
|
+
body << "--#{boundary}--#{CRLF*2}"
|
136
|
+
req.body = body
|
137
|
+
req["Content-Length"] = req.body.size
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def dump_request(req)
|
143
|
+
puts "Sending Request"
|
144
|
+
puts"#{req.method} #{req.path}"
|
145
|
+
dump_headers(req)
|
146
|
+
end
|
147
|
+
|
148
|
+
def dump_response(res)
|
149
|
+
puts "Received Response"
|
150
|
+
dump_headers(res)
|
151
|
+
puts res.body
|
152
|
+
end
|
153
|
+
|
154
|
+
def dump_headers(msg)
|
155
|
+
msg.each_header do |key, value|
|
156
|
+
puts "\t#{key}=#{value}"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/partigirb.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Partigirb
|
2
|
+
VERSION='0.1.0'
|
3
|
+
CURRENT_API_VERSION=1
|
4
|
+
end
|
5
|
+
|
6
|
+
$:.unshift File.dirname(__FILE__)
|
7
|
+
|
8
|
+
require 'open-uri'
|
9
|
+
require 'net/http'
|
10
|
+
require 'base64'
|
11
|
+
require 'digest'
|
12
|
+
require 'rexml/document'
|
13
|
+
require 'mime/types'
|
14
|
+
require 'ostruct'
|
15
|
+
|
16
|
+
require 'partigirb/core_ext'
|
17
|
+
|
18
|
+
require 'partigirb/handlers/xml_handler'
|
19
|
+
require 'partigirb/handlers/atom_handler'
|
20
|
+
require 'partigirb/handlers/json_handler'
|
21
|
+
require 'partigirb/handlers/string_handler'
|
22
|
+
|
23
|
+
require 'partigirb/transport'
|
24
|
+
require 'partigirb/client'
|
25
|
+
|