transport 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,61 @@
1
+ require 'cgi'
2
+
3
+ module Transport
4
+
5
+ # Common transport layer for http transfers.
6
+ class HTTP
7
+
8
+ # Builder for the transport layer requests.
9
+ class RequestBuilder
10
+
11
+ # Serializer for transport http parameters.
12
+ class ParameterSerializer
13
+
14
+ attr_reader :result
15
+
16
+ def initialize(parameters = nil)
17
+ @parameters = parameters || { }
18
+ end
19
+
20
+ def perform
21
+ quote_parameters
22
+ serialize_parameters
23
+ end
24
+
25
+ private
26
+
27
+ def quote_parameters
28
+ @quoted_parameters = { }
29
+ @parameters.each do |key, value|
30
+ encoded_key = CGI.escape(key.to_s)
31
+ @quoted_parameters[encoded_key] = self.class.escape value
32
+ end
33
+ end
34
+
35
+ def serialize_parameters
36
+ @result = if @parameters.nil? || @parameters.empty?
37
+ nil
38
+ else
39
+ @quoted_parameters.collect do |key, value|
40
+ self.class.pair key, value
41
+ end.join("&")
42
+ end
43
+ end
44
+
45
+ def self.escape(value)
46
+ value.is_a?(Array) ? value.map{ |element| CGI.escape element } : CGI.escape(value)
47
+ end
48
+
49
+ def self.pair(key, value)
50
+ value.is_a?(Array) ?
51
+ value.map{ |element| "#{key}=#{element}" }.join("&") :
52
+ "#{key}=#{value}"
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,37 @@
1
+ require File.join(File.dirname(__FILE__), "common")
2
+ require File.join(File.dirname(__FILE__), "http")
3
+
4
+ module Transport
5
+
6
+ # Transport layer for json transfers.
7
+ class JSON
8
+ include Common
9
+
10
+ autoload :RequestBuilder, File.join(File.dirname(__FILE__), "json", "request_builder")
11
+ autoload :ResponseParser, File.join(File.dirname(__FILE__), "json", "response_parser")
12
+
13
+ # ParserError will be raised if the response parser couldn't process the http response.
14
+ class ParserError < StandardError; end
15
+
16
+ def perform
17
+ perform_http_transport
18
+ parse_response
19
+ end
20
+
21
+ private
22
+
23
+ def perform_http_transport
24
+ http_transport = HTTP.new @uri, @request, @options
25
+ http_transport.perform
26
+ @http_response = http_transport.response
27
+ end
28
+
29
+ def parse_response
30
+ response_parser = ResponseParser.new @http_response
31
+ response_parser.perform
32
+ @response = response_parser.result
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,56 @@
1
+ require 'json'
2
+
3
+ module Transport
4
+
5
+ # Transport layer for json transfers.
6
+ class JSON
7
+
8
+ # Builder for the json transport layer requests.
9
+ class RequestBuilder
10
+ include Common::RequestBuilder
11
+
12
+ attr_reader :request
13
+
14
+ def perform
15
+ set_headers
16
+ convert_parameters_to_json
17
+ convert_body_to_json
18
+ build_http_request
19
+ end
20
+
21
+ private
22
+
23
+ def set_headers
24
+ headers = @options[:headers] || { }
25
+ headers.merge! "Accept" => "application/json"
26
+ headers.merge! "Content-Type" => "application/json" if @options[:body]
27
+ @options[:headers] = headers
28
+ end
29
+
30
+ def convert_parameters_to_json
31
+ return unless @options[:encode_parameters]
32
+ parameters = @options[:parameters]
33
+ if parameters
34
+ parameters.each do |key, value|
35
+ parameters[key] = value.to_json if value.respond_to?(:to_json)
36
+ end
37
+ @options[:parameters] = parameters
38
+ end
39
+ end
40
+
41
+ def convert_body_to_json
42
+ body = @options[:body]
43
+ @options[:body] = body.to_json if body
44
+ end
45
+
46
+ def build_http_request
47
+ http_request_builder = HTTP::RequestBuilder.new @http_method, @url, @options
48
+ http_request_builder.perform
49
+ @request = http_request_builder.request
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,33 @@
1
+ require 'json'
2
+
3
+ module Transport
4
+
5
+ # Transport layer for json transfers.
6
+ class JSON
7
+
8
+ # Parser for the json response.
9
+ class ResponseParser
10
+
11
+ attr_reader :result
12
+
13
+ def initialize(http_response)
14
+ @http_response = http_response
15
+ end
16
+
17
+ def perform
18
+ parse_response_body
19
+ end
20
+
21
+ private
22
+
23
+ def parse_response_body
24
+ @result = @http_response && @http_response.strip != "" ? ::JSON.parse(@http_response) : nil
25
+ rescue ::JSON::ParserError => error
26
+ raise ParserError, error.to_s
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,10 @@
1
+
2
+ module Transport
3
+
4
+ module Spec
5
+
6
+ autoload :Faker, File.join(File.dirname(__FILE__), "spec", "faker")
7
+
8
+ end
9
+
10
+ end
@@ -0,0 +1,75 @@
1
+ require 'yaml'
2
+ require 'rspec'
3
+
4
+ module Transport
5
+
6
+ module Spec
7
+
8
+ # Spec helper class to fake http and json request.
9
+ class Faker
10
+
11
+ TRANSPORT_CLASSES = [
12
+ Transport::HTTP,
13
+ Transport::JSON
14
+ ].freeze unless defined?(TRANSPORT_CLASSES)
15
+
16
+ # This error is raised if no fake request is found for the call.
17
+ class NoFakeRequestError < StandardError; end
18
+
19
+ def initialize(fakes)
20
+ @fakes = fakes
21
+ end
22
+
23
+ def stub_requests!
24
+ TRANSPORT_CLASSES.each do |transport_class|
25
+ stub_requests_for_transport! transport_class
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def stub_requests_for_transport!(transport_class)
32
+ transport_class.stub(:request).and_return do |*arguments|
33
+ request *arguments
34
+ end
35
+ end
36
+
37
+ def request(http_method, url, options = { })
38
+ parameters, headers, expected_status_code = options.values_at :parameters, :headers, :expected_status_code
39
+ fake = find_fake :http_method => http_method, :url => url, :parameters => parameters, :headers => headers
40
+ response = fake[:response]
41
+ check_status_code response, expected_status_code
42
+ response[:body].dup
43
+ end
44
+
45
+ def check_status_code(response, expected_status_code)
46
+ response_code, response_body = response.values_at :code, :body
47
+ raise Transport::UnexpectedStatusCodeError.new(
48
+ response_code.to_i,
49
+ response_body
50
+ ) if expected_status_code && expected_status_code.to_s != response_code
51
+ end
52
+
53
+ def find_fake(options)
54
+ fake = @fakes.detect{ |fake| self.class.fake_match? fake, options }
55
+ raise NoFakeRequestError, "no fake request found for [#{options[:http_method]} #{options[:url]} #{options[:parameters].inspect} #{options[:headers].inspect}]" unless fake
56
+ fake
57
+ end
58
+
59
+ def self.fake_match?(fake, options)
60
+ fake[:http_method].to_s == options[:http_method].to_s &&
61
+ fake[:url].to_s == options[:url].to_s &&
62
+ fake[:parameters] == options[:parameters] &&
63
+ fake[:headers] == options[:headers]
64
+ end
65
+
66
+ def self.fake!(filename)
67
+ faker = new YAML.load_file(filename)
68
+ faker.stub_requests!
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module Transport
3
+
4
+ # The UnexpectedStatusCodeError is raised if the :expected_status_code option is given to
5
+ # the :request method and the responded status code is different from the expected one.
6
+ class UnexpectedStatusCodeError < StandardError
7
+
8
+ attr_reader :status_code
9
+ attr_reader :message
10
+
11
+ def initialize(status_code, message = nil)
12
+ @status_code, @message = status_code, message
13
+ end
14
+
15
+ def to_s
16
+ "#{super} received status code #{self.status_code}" + (@message ? " [#{@message}]" : "")
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+ require 'logger'
3
+
4
+ describe "fetching the google start page" do
5
+
6
+ it "should return some html" do
7
+ response = Transport::HTTP.request :get, "http://www.google.de"
8
+ response.should_not be_nil
9
+ end
10
+
11
+ end
12
+
13
+ describe "fetch a search query result from google" do
14
+
15
+ it "should return some html" do
16
+ response = Transport::HTTP.request :get, "http://www.google.de/search", :parameters => { "q" => "transport" }
17
+ response.should_not be_nil
18
+ end
19
+
20
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+
3
+ describe "fetching the delicious bookmarks" do
4
+
5
+ it "should return some json" do
6
+ response = Transport::JSON.request :get, "http://feeds.delicious.com/v2/json", :logger => Logger.new(STDOUT)
7
+ response.should be_instance_of(Array)
8
+ end
9
+
10
+ end
@@ -0,0 +1,61 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper"))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "lib", "transport", "common", "request_builder"))
3
+
4
+ describe Transport::Common::RequestBuilder do
5
+
6
+ before :each do
7
+ @klass = Class.new
8
+ @klass.send :include, described_class
9
+ end
10
+
11
+ describe "initialize" do
12
+
13
+ before :each do
14
+ @object = @klass.new :test_http_method, "http://host:1234/test", :test_options
15
+ end
16
+
17
+ it "should set the http_method" do
18
+ @object.http_method.should == :test_http_method
19
+ end
20
+
21
+ it "should set the url" do
22
+ @object.url.should == "http://host:1234/test"
23
+ end
24
+
25
+ it "should set the options" do
26
+ @object.options.should == :test_options
27
+ end
28
+
29
+ it "should set the uri" do
30
+ @object.uri.host.should == "host"
31
+ @object.uri.port.should == 1234
32
+ end
33
+
34
+ end
35
+
36
+ describe "build" do
37
+
38
+ before :each do
39
+ @uri = Object.new
40
+ @request = Object.new
41
+ @object = mock described_class, :perform => nil, :uri => @uri, :request => @request
42
+ @klass.stub(:new).and_return(@object)
43
+ end
44
+
45
+ it "should initialize the request builder" do
46
+ @klass.should_receive(:new).with(:test_argument).and_return(@object)
47
+ @klass.build :test_argument
48
+ end
49
+
50
+ it "should perform the build" do
51
+ @object.should_receive(:perform)
52
+ @klass.build
53
+ end
54
+
55
+ it "should return the uri and the request" do
56
+ @klass.build.should == [ @uri, @request ]
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,68 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "lib", "transport", "common"))
3
+
4
+ describe Transport::Common do
5
+
6
+ before :each do
7
+ @klass = Class.new
8
+ @klass.send :include, described_class
9
+ end
10
+
11
+ describe "initialize" do
12
+
13
+ before :each do
14
+ @object = @klass.new :test_uri, :test_request, :test_options
15
+ end
16
+
17
+ it "should set the uri" do
18
+ @object.uri.should == :test_uri
19
+ end
20
+
21
+ it "should set the request" do
22
+ @object.request.should == :test_request
23
+ end
24
+
25
+ it "should set the options" do
26
+ @object.options.should == :test_options
27
+ end
28
+
29
+ end
30
+
31
+ describe "request" do
32
+
33
+ before :each do
34
+ @uri = Object.new
35
+ @request = Object.new
36
+
37
+ @klass::RequestBuilder.stub(:build).and_return([ @uri, @request ])
38
+
39
+ @object = mock described_class, :perform => nil, :response => :test_response
40
+ @klass.stub(:new).and_return(@object)
41
+ end
42
+
43
+ def do_request
44
+ @klass.request :test_http_method, :test_url, :test_options
45
+ end
46
+
47
+ it "should build the request" do
48
+ @klass::RequestBuilder.should_receive(:build).with(:test_http_method, :test_url, :test_options).and_return([ @uri, @request ])
49
+ do_request
50
+ end
51
+
52
+ it "should initialize the transport" do
53
+ @klass.should_receive(:new).with(@uri, @request, :test_options).and_return(@object)
54
+ do_request
55
+ end
56
+
57
+ it "should perform the transport" do
58
+ @object.should_receive(:perform)
59
+ do_request
60
+ end
61
+
62
+ it "should return the response" do
63
+ do_request.should == :test_response
64
+ end
65
+
66
+ end
67
+
68
+ end