commute 0.2.0.rc.2 → 0.3.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/.todo +28 -12
- data/README.md +0 -1
- data/commute.gemspec +4 -5
- data/lib/commute/common/basic_auth.rb +10 -9
- data/lib/commute/common/caching.rb +208 -0
- data/lib/commute/common/chemicals.rb +47 -24
- data/lib/commute/common/eventmachine.rb +68 -0
- data/lib/commute/common/synchrony.rb +42 -0
- data/lib/commute/common/typhoeus.rb +64 -0
- data/lib/commute/core/api.rb +42 -29
- data/lib/commute/core/builder.rb +4 -15
- data/lib/commute/core/context.rb +156 -15
- data/lib/commute/core/http.rb +124 -0
- data/lib/commute/core/layer.rb +187 -0
- data/lib/commute/core/sequence.rb +83 -132
- data/lib/commute/core/stack.rb +63 -72
- data/lib/commute/core/status.rb +45 -0
- data/lib/commute/core/util/event_emitter.rb +58 -0
- data/lib/commute/core/util/path.rb +37 -0
- data/lib/commute/core/util/stream.rb +141 -0
- data/lib/commute/extensions/crud.rb +88 -0
- data/lib/commute/extensions/param.rb +20 -0
- data/lib/commute/extensions/url.rb +53 -0
- data/lib/commute/version.rb +1 -1
- data/spec/commute/common/caching_spec.rb +158 -0
- data/spec/commute/common/eventmachine_spec.rb +74 -0
- data/spec/commute/common/typhoeus_spec.rb +67 -0
- data/spec/commute/core/api_spec.rb +3 -1
- data/spec/commute/core/builder_spec.rb +8 -8
- data/spec/commute/core/http_spec.rb +39 -0
- data/spec/commute/core/layer_spec.rb +81 -0
- data/spec/commute/core/sequence_spec.rb +36 -150
- data/spec/commute/core/stack_spec.rb +33 -83
- data/spec/commute/core/util/event_emitter_spec.rb +35 -0
- data/spec/commute/core/util/path_spec.rb +29 -0
- data/spec/commute/core/util/stream_spec.rb +90 -0
- data/spec/commute/extensions/url_spec.rb +76 -0
- data/spec/spec_helper.rb +3 -1
- metadata +61 -48
- data/examples/gist_api.rb +0 -71
- data/examples/highrise_task_api.rb +0 -59
- data/examples/pastie_api.rb +0 -18
- data/lib/commute/aspects/caching.rb +0 -37
- data/lib/commute/aspects/crud.rb +0 -41
- data/lib/commute/aspects/pagination.rb +0 -16
- data/lib/commute/aspects/url.rb +0 -57
- data/lib/commute/common/cache.rb +0 -43
- data/lib/commute/common/conditional.rb +0 -27
- data/lib/commute/common/em-synchrony_adapter.rb +0 -29
- data/lib/commute/common/em_http_request_adapter.rb +0 -57
- data/lib/commute/common/typhoeus_adapter.rb +0 -40
- data/lib/commute/common/xml.rb +0 -7
- data/lib/commute/core/commuter.rb +0 -116
- data/lib/commute/core/processors/code_status_processor.rb +0 -40
- data/lib/commute/core/processors/hook.rb +0 -14
- data/lib/commute/core/processors/request_builder.rb +0 -26
- data/lib/commute/core/processors/sequencer.rb +0 -46
- data/lib/commute/core/request.rb +0 -58
- data/lib/commute/core/response.rb +0 -18
- data/spec/commute/aspects/caching_spec.rb +0 -12
- data/spec/commute/aspects/url_spec.rb +0 -61
- data/spec/commute/core/commuter_spec.rb +0 -64
- data/spec/commute/core/processors/code_status_processor_spec.rb +0 -5
- data/spec/commute/core/processors/hook_spec.rb +0 -25
- data/spec/commute/core/processors/request_builder_spec.rb +0 -25
- data/spec/commute/core/processors/sequencer_spec.rb +0 -33
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'commute/core/util/event_emitter'
|
2
|
+
|
3
|
+
module Commute
|
4
|
+
|
5
|
+
# Internal: A Simple Evented stream modelled after node.js' stream.
|
6
|
+
# More info: http://nodejs.org/api/stream.html.
|
7
|
+
#
|
8
|
+
# When you create a stream, you can write anything to it. For every
|
9
|
+
# write, an event 'data' is emitted to the listeners (evented reading).
|
10
|
+
#
|
11
|
+
# When a stream is ended, an 'end' event is emitted and the stream is
|
12
|
+
# not writeable anymore.
|
13
|
+
#
|
14
|
+
module Stream
|
15
|
+
include EventEmitter
|
16
|
+
|
17
|
+
class Simple
|
18
|
+
include Stream
|
19
|
+
end
|
20
|
+
|
21
|
+
# Internal: Opens a new writeable stream.
|
22
|
+
def initialize *args
|
23
|
+
super *args
|
24
|
+
@writeable = true
|
25
|
+
end
|
26
|
+
|
27
|
+
# Internal: Writes data to the stream.
|
28
|
+
# Note: Silenty does not write if the stream is not writeable (see #writable?).
|
29
|
+
#
|
30
|
+
# data - A chunk of data to write to the stream.
|
31
|
+
#
|
32
|
+
# Returns the written data (nil if no data was written).
|
33
|
+
def write data
|
34
|
+
if writeable?
|
35
|
+
emit :data, data
|
36
|
+
data
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Internal: Checks if the stream is writeable.
|
41
|
+
#
|
42
|
+
# Returns true if the stream is writeable.
|
43
|
+
def writeable?
|
44
|
+
@writeable
|
45
|
+
end
|
46
|
+
|
47
|
+
# Internal: Ends the stream.
|
48
|
+
# Note: After this, the stream is not writeable anymore.
|
49
|
+
#
|
50
|
+
# data - Last chunk of data to send.
|
51
|
+
#
|
52
|
+
# Returns Nothing
|
53
|
+
def end data = nil
|
54
|
+
emit :data, data if data
|
55
|
+
@writeable = false
|
56
|
+
emit :end
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Internal: Pipes the stream to another stream.
|
61
|
+
#
|
62
|
+
# Whenever data is written to the source stream,
|
63
|
+
# it gets written to the destination stream.
|
64
|
+
# When the source stream end, the destinations stream
|
65
|
+
# is ended as well.
|
66
|
+
#
|
67
|
+
# destination - The stream to pipe to.
|
68
|
+
# options - For future use.
|
69
|
+
#
|
70
|
+
# Returns Nothing.
|
71
|
+
def pipe destination, options = {}
|
72
|
+
on(:data) { |chunk| destination.write chunk }
|
73
|
+
on(:end) { destination.end }
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
# Buffers data and emits the entire buffer.
|
78
|
+
# Calls << on the initial for every chunk in the stream.
|
79
|
+
#
|
80
|
+
# initial - The initial value of the buffer (default: []).
|
81
|
+
#
|
82
|
+
# Yields if done buffering.
|
83
|
+
# Returns a new buffered stream.
|
84
|
+
def buffer
|
85
|
+
inject [], &:<<
|
86
|
+
end
|
87
|
+
|
88
|
+
def >> sink
|
89
|
+
buffer.on(:data) { |buffer| sink.concat buffer }
|
90
|
+
end
|
91
|
+
|
92
|
+
def map &block
|
93
|
+
Simple.new.tap do |stream|
|
94
|
+
on(:data) { |chunk| stream.write block.call(chunk) }
|
95
|
+
on(:end) { stream.end }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def reject &block
|
100
|
+
Simple.new.tap do |stream|
|
101
|
+
on(:data) { |chunk| stream.write chunk unless block.call(chunk) }
|
102
|
+
on(:end) { stream.end }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def select &block
|
107
|
+
Simple.new.tap do |stream|
|
108
|
+
on(:data) { |chunk| stream.write chunk if block.call(chunk) }
|
109
|
+
on(:end) { stream.end }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def inject initial, &block
|
114
|
+
Simple.new.tap do |stream|
|
115
|
+
triggered = false
|
116
|
+
on(:data) do |chunk|
|
117
|
+
initial = block.call initial, chunk
|
118
|
+
triggered = true
|
119
|
+
end
|
120
|
+
on(:end) do
|
121
|
+
stream.write initial if triggered
|
122
|
+
stream.end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def take amount
|
128
|
+
Simple.new.tap do |stream|
|
129
|
+
count = 0
|
130
|
+
on(:data) do |chunk|
|
131
|
+
if count < amount
|
132
|
+
stream.write chunk
|
133
|
+
count += 1
|
134
|
+
else
|
135
|
+
stream.end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Commute
|
2
|
+
module Extension
|
3
|
+
|
4
|
+
# Public: Defines standard Restful CRUD actions.
|
5
|
+
#
|
6
|
+
# create -> POST
|
7
|
+
# read -> GET
|
8
|
+
# update -> PUT
|
9
|
+
# destroy -> DELETE
|
10
|
+
#
|
11
|
+
# Note: Implement "one" and "list" methods to
|
12
|
+
# hook on different CRUD selectors.
|
13
|
+
#
|
14
|
+
# Examples:
|
15
|
+
#
|
16
|
+
# gist = gists.find(1).run!
|
17
|
+
# gist[:description] = "Testing CRUD"
|
18
|
+
# gists.update(gist).where(id: 1).run
|
19
|
+
# gists.destroy(1).run
|
20
|
+
#
|
21
|
+
# # Making use of contexts.
|
22
|
+
# handle = gists.where(id: 1)
|
23
|
+
# gist = handle.find.run!
|
24
|
+
# gist[:description] = "Testing CRUD"
|
25
|
+
# handle.update(gist).run
|
26
|
+
# handle.destroy
|
27
|
+
#
|
28
|
+
module Crud
|
29
|
+
METHODS = [:all, :find, :update, :create, :destroy].freeze
|
30
|
+
|
31
|
+
# Public: An Extended CRUD extension that uses PATCH for updates.
|
32
|
+
#
|
33
|
+
# create -> POST
|
34
|
+
# read -> GET
|
35
|
+
# update -> PATCH
|
36
|
+
# replace -> PUT
|
37
|
+
# destroy -> DELETE
|
38
|
+
#
|
39
|
+
module Extended
|
40
|
+
include Crud
|
41
|
+
METHODS = [:all, :find, :update, :replace, :create, :destroy].freeze
|
42
|
+
|
43
|
+
# Public: Replaces a resource
|
44
|
+
def replace body = nil
|
45
|
+
put.body(body).try :one
|
46
|
+
end
|
47
|
+
|
48
|
+
# Public: Updates a resource.
|
49
|
+
def update body = nil
|
50
|
+
patch.body(body).try :one
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Public: Get all resources.
|
55
|
+
def all
|
56
|
+
get.try :list
|
57
|
+
end
|
58
|
+
|
59
|
+
# Public: Find a resource using an id.
|
60
|
+
#
|
61
|
+
# id - The id of the resource (optional).
|
62
|
+
#
|
63
|
+
def find id = nil
|
64
|
+
with id: id
|
65
|
+
get.try :one
|
66
|
+
end
|
67
|
+
|
68
|
+
# Public: Updates a resource.
|
69
|
+
def update body = nil
|
70
|
+
put.body(body).try :one
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: Creates a resource.
|
74
|
+
def create body = nil
|
75
|
+
post.body(body).try :one
|
76
|
+
end
|
77
|
+
|
78
|
+
# Public: Destroys a resource.
|
79
|
+
#
|
80
|
+
# id - The id of the resource to destroy (optional).
|
81
|
+
#
|
82
|
+
def destroy id = nil
|
83
|
+
with id: id
|
84
|
+
delete.try :one
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Commute
|
2
|
+
module Extension
|
3
|
+
|
4
|
+
module Param
|
5
|
+
METHOD = [:param, :params]
|
6
|
+
|
7
|
+
def param name, param_name = name
|
8
|
+
transform name do |request, value|
|
9
|
+
request.query[param_name] = value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def params names
|
14
|
+
names.each do |name|
|
15
|
+
self.param name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Commute
|
4
|
+
module Extension
|
5
|
+
|
6
|
+
# The url extensions provides an `url` method that pushes
|
7
|
+
# a transformation on the context that renders the required url
|
8
|
+
# based on a pattern.
|
9
|
+
#
|
10
|
+
# The pattern uses the standard ruby format specification. When
|
11
|
+
# not all variables are present, the url in sanitized in a
|
12
|
+
# way that it is valid again.
|
13
|
+
#
|
14
|
+
# Examples:
|
15
|
+
#
|
16
|
+
# class GistApi < Commute::Api
|
17
|
+
# include Commute::Extension::Url
|
18
|
+
#
|
19
|
+
# url 'https://api.github.com/gists/%{filter}'
|
20
|
+
#
|
21
|
+
# def user user = nil
|
22
|
+
# with(user: user)
|
23
|
+
# url('https://api.github.com/user/%{user}/gists').all
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
module Url
|
28
|
+
METHODS = [:url].freeze
|
29
|
+
|
30
|
+
# Public: Define the url pattern to use.
|
31
|
+
#
|
32
|
+
# pattern - The patter with variables between %{}.
|
33
|
+
#
|
34
|
+
# Returns self.
|
35
|
+
def url pattern
|
36
|
+
# Transform the context, filling in the url.
|
37
|
+
transform do |request, context|
|
38
|
+
# First, render the url pattern.
|
39
|
+
rendered = pattern.gsub(/%{([^}]*)}/) { |m| context[$1.to_sym] }
|
40
|
+
# Parse the rendered url using uri.
|
41
|
+
uri = URI rendered
|
42
|
+
# Substitute multiple / by one /.
|
43
|
+
uri.path.gsub! /\/+/, '/'
|
44
|
+
# Substiture /.format by .format
|
45
|
+
uri.path.sub! /\/\.([^\/]+)$/, '.\1'
|
46
|
+
|
47
|
+
# Assign the uri to the request.
|
48
|
+
request.uri = uri
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/commute/version.rb
CHANGED
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'commute/common/caching'
|
3
|
+
|
4
|
+
describe Commute::Common::Caching do
|
5
|
+
|
6
|
+
let(:cache) { stub }
|
7
|
+
|
8
|
+
let(:response) {
|
9
|
+
Commute::Http::Response.new(nil).tap { |response|
|
10
|
+
response.headers['ETag'] = 'A'
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
let(:body) {
|
15
|
+
{ some: ['complex', 'structure'] }
|
16
|
+
}
|
17
|
+
|
18
|
+
let(:responder) {
|
19
|
+
proc { |router, request|
|
20
|
+
request.respond(response, status).tap { |response|
|
21
|
+
response.write body
|
22
|
+
response.end
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
let(:stack) {
|
28
|
+
Commute::Stack.new do |stack|
|
29
|
+
stack.sequence do |s|
|
30
|
+
s.append Commute::Common::Caching.new
|
31
|
+
s.append responder
|
32
|
+
end
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
let(:base) {
|
37
|
+
Commute::Context.new(stack).with(caching: {
|
38
|
+
cache: cache
|
39
|
+
}).context
|
40
|
+
}
|
41
|
+
|
42
|
+
describe 'a get request' do
|
43
|
+
let(:api) do
|
44
|
+
base.transform do |request|
|
45
|
+
request.method = :get
|
46
|
+
request.uri = URI('http://www.example.com')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'nothing in the cache' do
|
51
|
+
before do
|
52
|
+
cache.stubs(:get).returns nil
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should fire the request' do
|
56
|
+
responder.expects(:call).once.with do |router, request|
|
57
|
+
request.http.method.must_equal :get
|
58
|
+
end
|
59
|
+
call
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'the response was succesful' do
|
63
|
+
let(:status) { Commute::Http::Status.new(200) }
|
64
|
+
|
65
|
+
it 'should store the response in the cache and return the response' do
|
66
|
+
cache.expects(:set).with('http://www.example.com', {
|
67
|
+
data: body,
|
68
|
+
etag: 'A'
|
69
|
+
})
|
70
|
+
call
|
71
|
+
@body.must_equal body
|
72
|
+
@status.success?.must_equal true
|
73
|
+
@status.cached.must_equal false
|
74
|
+
@status.http.wont_be_nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'the response was not successful' do
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'something in the cache' do
|
83
|
+
before do
|
84
|
+
cache.stubs(:get).with('http://www.example.com').returns \
|
85
|
+
data: body,
|
86
|
+
etag: 'A'
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'without validation' do
|
90
|
+
it 'should return a response with the cached data' do
|
91
|
+
responder.expects(:call).never
|
92
|
+
call
|
93
|
+
@body.must_equal body
|
94
|
+
@status.success?.must_equal true
|
95
|
+
@status.cached.must_equal true
|
96
|
+
@status.updated.must_equal false
|
97
|
+
@status.http.must_be_nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'with validation' do
|
102
|
+
let(:base) {
|
103
|
+
Commute::Context.new(stack).with(caching: {
|
104
|
+
cache: cache,
|
105
|
+
validate: true
|
106
|
+
}).context
|
107
|
+
}
|
108
|
+
|
109
|
+
it 'should verify the etag of the resource' do
|
110
|
+
responder.expects(:call).once.with do |router, request|
|
111
|
+
request.http.headers['If-None-Match'].must_equal 'A'
|
112
|
+
end
|
113
|
+
call
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'the resource was not modified' do
|
117
|
+
let(:status) { Commute::Http::Status.new(304) }
|
118
|
+
|
119
|
+
it 'it should respond without updating the cache' do
|
120
|
+
cache.expects(:set).never
|
121
|
+
call
|
122
|
+
@body.must_equal body
|
123
|
+
@status.success?.must_equal true
|
124
|
+
@status.cached.must_equal true
|
125
|
+
@status.updated.must_equal false
|
126
|
+
@status.http.wont_be_nil
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'the resource was modified' do
|
131
|
+
let(:status) { Commute::Http::Status.new(200) }
|
132
|
+
|
133
|
+
it 'it should respond and update the cache' do
|
134
|
+
cache.expects(:set).with('http://www.example.com', {
|
135
|
+
data: body,
|
136
|
+
etag: 'A'
|
137
|
+
})
|
138
|
+
call
|
139
|
+
@body.must_equal body
|
140
|
+
@status.success?.must_equal true
|
141
|
+
@status.cached.must_equal true
|
142
|
+
@status.updated.must_equal true
|
143
|
+
@status.http.wont_be_nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe 'the response was not succesful' do
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def call
|
156
|
+
@body, @status = api.run
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'commute/common/eventmachine'
|
3
|
+
|
4
|
+
describe Commute::Common::Eventmachine do
|
5
|
+
|
6
|
+
let(:adapter) { Commute::Common::Eventmachine.new }
|
7
|
+
|
8
|
+
let(:router) { Commute::Stack::Router.new [adapter].each }
|
9
|
+
|
10
|
+
before do
|
11
|
+
@stub = stub_request(:post, "http://www.example.com/").with \
|
12
|
+
headers: {
|
13
|
+
'Accept-Encoding' => 'gzip, deflate'
|
14
|
+
},
|
15
|
+
body: 'Hello World!'
|
16
|
+
|
17
|
+
@done = proc {}
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should be able to make a successful request' do
|
21
|
+
@done.expects(:call).twice
|
22
|
+
EM.run do
|
23
|
+
# Stub the request.
|
24
|
+
@stub.to_return(status: 200, body: 'Hello!', headers: {})
|
25
|
+
|
26
|
+
# Create a Http Request.
|
27
|
+
http_request = Commute::Http::Request.new(nil).tap do |r|
|
28
|
+
r.uri = URI('http://www.example.com')
|
29
|
+
r.method = :post
|
30
|
+
end
|
31
|
+
|
32
|
+
# Execute the request.
|
33
|
+
request = router.call http_request do |response, status|
|
34
|
+
@done.call
|
35
|
+
status.success?.must_equal true
|
36
|
+
response.buffer.on(:data) do |body|
|
37
|
+
@done.call
|
38
|
+
body.join.must_equal 'Hello!'
|
39
|
+
end
|
40
|
+
|
41
|
+
EM.stop
|
42
|
+
end
|
43
|
+
request.write 'Hello'
|
44
|
+
request.write ' World!'
|
45
|
+
request.end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should be able to handle a bad response' do
|
50
|
+
@done.expects(:call).once
|
51
|
+
EM.run do
|
52
|
+
# Stub the request.
|
53
|
+
@stub.to_return(status: 500, headers: {})
|
54
|
+
|
55
|
+
# Create a Http Request.
|
56
|
+
http_request = Commute::Http::Request.new(nil).tap do |r|
|
57
|
+
r.uri = URI('http://www.example.com')
|
58
|
+
r.method = :post
|
59
|
+
end
|
60
|
+
|
61
|
+
# Execute the request.
|
62
|
+
request = router.call http_request do |response, status|
|
63
|
+
@done.call
|
64
|
+
status.fail?.must_equal true
|
65
|
+
response.on(:data) { @done.call }
|
66
|
+
|
67
|
+
EM.stop
|
68
|
+
end
|
69
|
+
request.write 'Hello'
|
70
|
+
request.write ' World!'
|
71
|
+
request.end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'commute/common/typhoeus'
|
3
|
+
|
4
|
+
describe Commute::Common::Typhoeus do
|
5
|
+
|
6
|
+
let(:adapter) { Commute::Common::Typhoeus.new }
|
7
|
+
|
8
|
+
let(:router) { Commute::Stack::Router.new [adapter].each }
|
9
|
+
|
10
|
+
before do
|
11
|
+
@stub = stub_request(:post, "http://www.example.com/").with \
|
12
|
+
headers: {
|
13
|
+
'User-Agent' => 'Typhoeus - https://github.com/typhoeus/typhoeus'
|
14
|
+
},
|
15
|
+
body: 'Hello World!'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should be able to make a successful request' do
|
19
|
+
# Stub the request.
|
20
|
+
@stub.to_return(status: 200, body: 'Hello!', headers: {})
|
21
|
+
|
22
|
+
# Create a Http Request.
|
23
|
+
http_request = Commute::Http::Request.new(nil).tap do |r|
|
24
|
+
r.uri = URI('http://www.example.com')
|
25
|
+
r.method = :post
|
26
|
+
end
|
27
|
+
|
28
|
+
# Execute the request.
|
29
|
+
response, body, status = nil, [], nil
|
30
|
+
request = router.call http_request do |_response, _status|
|
31
|
+
_response >> body
|
32
|
+
response, status = _response, _status
|
33
|
+
end
|
34
|
+
request.write 'Hello'
|
35
|
+
request.write ' World!'
|
36
|
+
request.end
|
37
|
+
|
38
|
+
# Do some checks.
|
39
|
+
status.success?.must_equal true
|
40
|
+
body.join.must_equal 'Hello!'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should be able to handle a bad response' do
|
44
|
+
# Stub the request.
|
45
|
+
@stub.to_return(status: 500, headers: {})
|
46
|
+
|
47
|
+
# Create a Http Request.
|
48
|
+
http_request = Commute::Http::Request.new(nil).tap do |r|
|
49
|
+
r.uri = URI('http://www.example.com')
|
50
|
+
r.method = :post
|
51
|
+
end
|
52
|
+
|
53
|
+
# Execute the request.
|
54
|
+
response, body, status = nil, [], nil
|
55
|
+
request = router.call http_request do |_response, _status|
|
56
|
+
_response >> body
|
57
|
+
response, status = _response, _status
|
58
|
+
end
|
59
|
+
request.write 'Hello'
|
60
|
+
request.write ' World!'
|
61
|
+
request.end
|
62
|
+
|
63
|
+
# Do some checks.
|
64
|
+
status.fail?.must_equal true
|
65
|
+
body.must_be_empty
|
66
|
+
end
|
67
|
+
end
|
@@ -48,14 +48,16 @@ describe Commute::Api do
|
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'should create a new Api instance when a method is called on the class' do
|
51
|
+
api_klass.builder[:id].must_equal 1
|
51
52
|
context = api_klass.with(id: 2).all.context
|
52
53
|
context.parameters.must_equal id: 2, text: 'go', help: true
|
54
|
+
api_klass.builder[:id].must_equal 1
|
53
55
|
end
|
54
56
|
|
55
57
|
it 'should deal with inheritance' do
|
56
58
|
context = TestInheritance.new.context
|
57
59
|
context.parameters[:id].must_equal 2
|
58
|
-
context.stack.sequence.
|
60
|
+
context.stack.sequence.size.must_be :>=, 2
|
59
61
|
end
|
60
62
|
|
61
63
|
describe '#new' do
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'commute/core/context'
|
3
|
-
require 'commute/core/
|
3
|
+
require 'commute/core/http'
|
4
4
|
|
5
5
|
describe Commute::Builder do
|
6
6
|
|
7
7
|
let(:parameters) {{}}
|
8
|
-
let(:stack) { Commute::Stack.new {
|
9
|
-
|
8
|
+
let(:stack) { Commute::Stack.new {
|
9
|
+
sequence.append Proc.new {}
|
10
10
|
}}
|
11
11
|
let(:transformations) {[]}
|
12
12
|
let(:disables) {[]}
|
@@ -15,7 +15,7 @@ describe Commute::Builder do
|
|
15
15
|
|
16
16
|
let(:builder) { Commute::Builder.new context }
|
17
17
|
|
18
|
-
let(:request) { Commute::Request.new }
|
18
|
+
let(:request) { Commute::Http::Request.new context }
|
19
19
|
|
20
20
|
describe '#with' do
|
21
21
|
it 'should add parameters to the context' do
|
@@ -82,18 +82,18 @@ describe Commute::Builder do
|
|
82
82
|
|
83
83
|
it 'should be able to add a dynamic transform without dependencies' do
|
84
84
|
context = builder.with(id: 1).transform { |request, context|
|
85
|
-
request.path = "/#{context[:id]}"
|
85
|
+
request.uri.path = "/#{context[:id]}"
|
86
86
|
}.context
|
87
87
|
context.transformations.first.call(request, context)
|
88
|
-
request.path.must_equal '/1'
|
88
|
+
request.uri.path.must_equal '/1'
|
89
89
|
end
|
90
90
|
|
91
91
|
it 'should be able to add a dynamic transform with dependencies' do
|
92
92
|
context = builder.with(id: 1).transform(:id) { |request, id|
|
93
|
-
request.path = "/#{id}"
|
93
|
+
request.uri.path = "/#{id}"
|
94
94
|
}.context
|
95
95
|
context.transformations.first.call(request, context)
|
96
|
-
request.path.must_equal '/1'
|
96
|
+
request.uri.path.must_equal '/1'
|
97
97
|
end
|
98
98
|
|
99
99
|
it 'should never call the transformation when its dependencies are all nil' do
|