commute 0.1.2 → 0.2.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.todo +15 -0
- data/Gemfile +5 -2
- data/commute.gemspec +2 -1
- data/examples/gist_api.rb +71 -0
- data/examples/highrise_task_api.rb +59 -0
- data/examples/pastie_api.rb +18 -0
- data/lib/commute/aspects/caching.rb +37 -0
- data/lib/commute/aspects/crud.rb +41 -0
- data/lib/commute/aspects/pagination.rb +16 -0
- data/lib/commute/aspects/url.rb +57 -0
- data/lib/commute/common/basic_auth.rb +20 -0
- data/lib/commute/common/cache.rb +43 -0
- data/lib/commute/common/chemicals.rb +39 -0
- data/lib/commute/common/conditional.rb +27 -0
- data/lib/commute/common/em-synchrony_adapter.rb +29 -0
- data/lib/commute/common/em_http_request_adapter.rb +57 -0
- data/lib/commute/common/json.rb +28 -0
- data/lib/commute/common/typhoeus_adapter.rb +40 -0
- data/lib/commute/common/xml.rb +7 -0
- data/lib/commute/configuration.rb +8 -0
- data/lib/commute/core/api.rb +116 -0
- data/lib/commute/core/builder.rb +261 -0
- data/lib/commute/core/commuter.rb +116 -0
- data/lib/commute/core/context.rb +63 -0
- data/lib/commute/core/processors/code_status_processor.rb +40 -0
- data/lib/commute/core/processors/hook.rb +14 -0
- data/lib/commute/core/processors/request_builder.rb +26 -0
- data/lib/commute/core/processors/sequencer.rb +46 -0
- data/lib/commute/core/request.rb +58 -0
- data/lib/commute/core/response.rb +18 -0
- data/lib/commute/core/sequence.rb +180 -0
- data/lib/commute/core/stack.rb +145 -0
- data/lib/commute/version.rb +1 -1
- data/lib/commute.rb +4 -2
- data/spec/commute/aspects/caching_spec.rb +12 -0
- data/spec/commute/aspects/url_spec.rb +61 -0
- data/spec/commute/core/api_spec.rb +70 -0
- data/spec/commute/core/builder_spec.rb +123 -0
- data/spec/commute/core/commuter_spec.rb +64 -0
- data/spec/commute/core/processors/code_status_processor_spec.rb +5 -0
- data/spec/commute/core/processors/hook_spec.rb +25 -0
- data/spec/commute/core/processors/request_builder_spec.rb +25 -0
- data/spec/commute/core/processors/sequencer_spec.rb +33 -0
- data/spec/commute/core/sequence_spec.rb +190 -0
- data/spec/commute/core/stack_spec.rb +96 -0
- data/spec/spec_helper.rb +2 -3
- metadata +73 -18
- data/lib/commute/adapters/typhoeus.rb +0 -13
- data/lib/commute/api.rb +0 -86
- data/lib/commute/context.rb +0 -154
- data/lib/commute/layer.rb +0 -16
- data/lib/commute/stack.rb +0 -104
- data/spec/commute/api_spec.rb +0 -97
- data/spec/commute/context_spec.rb +0 -140
- data/spec/commute/layer_spec.rb +0 -22
- data/spec/commute/stack_spec.rb +0 -125
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Commute
|
4
|
+
|
5
|
+
class Request
|
6
|
+
|
7
|
+
# Scheme (http(s))
|
8
|
+
attr_accessor :scheme
|
9
|
+
|
10
|
+
# Host to connect to (String).
|
11
|
+
attr_accessor :host
|
12
|
+
|
13
|
+
# Port to connect to (Integer).
|
14
|
+
attr_accessor :port
|
15
|
+
|
16
|
+
# Path to request (String).
|
17
|
+
attr_accessor :path
|
18
|
+
|
19
|
+
# Url parameters (Hash).
|
20
|
+
attr_accessor :query
|
21
|
+
alias :params :query
|
22
|
+
alias :params= :query=
|
23
|
+
|
24
|
+
# The HTTP Method of the request (Symbol).
|
25
|
+
# Mostly :get, :post, :put, :patch, :delete
|
26
|
+
attr_accessor :method
|
27
|
+
|
28
|
+
# Request headers.
|
29
|
+
attr_accessor :headers
|
30
|
+
|
31
|
+
# The headers (Hash).
|
32
|
+
|
33
|
+
# The Body.
|
34
|
+
attr_accessor :body
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
@headers = {}
|
38
|
+
@query = {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def url= url
|
42
|
+
uri = URI url
|
43
|
+
@scheme = uri.scheme
|
44
|
+
@host = uri.host
|
45
|
+
@port = uri.port
|
46
|
+
@path = uri.path
|
47
|
+
@query = Hash[uri.query.split('&').map { |p| p.split('=') }] if uri.query
|
48
|
+
end
|
49
|
+
|
50
|
+
# SCHEME! TODO
|
51
|
+
def url
|
52
|
+
query = @query.map { |k,v| "#{k}=#{v}" }.join '&'
|
53
|
+
url = "#{@scheme}://#{@host}:#{@port}#{@path}"
|
54
|
+
url << "?#{query}" unless query.nil? || query.empty?
|
55
|
+
url
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Commute
|
2
|
+
|
3
|
+
class Response
|
4
|
+
|
5
|
+
# The request responsible for this response.
|
6
|
+
attr_accessor :request
|
7
|
+
|
8
|
+
# Response code.
|
9
|
+
attr_accessor :code
|
10
|
+
|
11
|
+
# The Body.
|
12
|
+
attr_accessor :body
|
13
|
+
|
14
|
+
def initialize request = nil
|
15
|
+
@request = request
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'commute/core/commuter'
|
2
|
+
|
3
|
+
module Commute
|
4
|
+
|
5
|
+
# Internal: A Sequence is an ordered list of processors, that all
|
6
|
+
# work together to incrementally process a Commuter.
|
7
|
+
#
|
8
|
+
class Sequence
|
9
|
+
|
10
|
+
# Public: Initializes a sequence without processors.
|
11
|
+
def initialize &alter
|
12
|
+
@processors = []
|
13
|
+
@index = {}
|
14
|
+
self.alter &alter if block_given?
|
15
|
+
end
|
16
|
+
|
17
|
+
# Public: Alters the sequence.
|
18
|
+
#
|
19
|
+
# Yields the sequence itself if the arity of the block is 1.
|
20
|
+
# Evaluates the block if the arity is 0.
|
21
|
+
#
|
22
|
+
# Returns the sequence itself.
|
23
|
+
def alter &alter
|
24
|
+
if alter.arity == 0
|
25
|
+
instance_eval &alter
|
26
|
+
else
|
27
|
+
yield self
|
28
|
+
end
|
29
|
+
# Return the Sequence.
|
30
|
+
return self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns an duplicate collection of processors.
|
34
|
+
def processors
|
35
|
+
@processors.clone
|
36
|
+
end
|
37
|
+
|
38
|
+
# Internal: Processes a commuter with al processors in the sequence.
|
39
|
+
#
|
40
|
+
# When a commuter provides parameters for a processor through the
|
41
|
+
# parameters method, given the processor id. Then those parameters
|
42
|
+
# are passed as an argument to the call method of the processor (if applicable).
|
43
|
+
#
|
44
|
+
# Returns Nothing.
|
45
|
+
def call commuter, options = {}
|
46
|
+
call_one 0, commuter
|
47
|
+
end
|
48
|
+
|
49
|
+
def call_one index, commuter
|
50
|
+
# Get the processor using the index.
|
51
|
+
processor = @processors[index]
|
52
|
+
# Immediately return if no processor is found.
|
53
|
+
return nil unless processor
|
54
|
+
# Identify the processor.
|
55
|
+
# TODO: not very performant
|
56
|
+
processor_id = @index.key processor
|
57
|
+
# Immediately jumpt to the next processor if this one is disabled.
|
58
|
+
if commuter.enabled? processor_id
|
59
|
+
# Get the parameters.
|
60
|
+
parameters = commuter.parameters(processor_id) if processor_id
|
61
|
+
# Calling the processor.
|
62
|
+
if parameters
|
63
|
+
processor.call commuter, parameters
|
64
|
+
else
|
65
|
+
processor.call commuter
|
66
|
+
end
|
67
|
+
end
|
68
|
+
# Call self for the next processor.
|
69
|
+
call_next = Proc.new { call_one index + 1, commuter }
|
70
|
+
if commuter.delayed?
|
71
|
+
commuter.wait &call_next
|
72
|
+
else
|
73
|
+
call_next.call
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Public: Appends a processor to a the list of processors.
|
78
|
+
#
|
79
|
+
# options - Optional parameters for processor naming and placement (default: {}):
|
80
|
+
# :as - The name of the processor in the sequence (optional).
|
81
|
+
# :after - A reference processor id after which the processor needs to be appended.
|
82
|
+
#
|
83
|
+
# Returns the appended processor.
|
84
|
+
def append processor, options = {}
|
85
|
+
# When a reference is given, append after it.
|
86
|
+
if options[:after]
|
87
|
+
@processors.insert id_index(options[:after]) + 1, processor
|
88
|
+
else
|
89
|
+
@processors << processor
|
90
|
+
end
|
91
|
+
# Identify the processor using different methods.
|
92
|
+
id = identify processor, options[:as]
|
93
|
+
# Add the processor to the index for direct access purposes.
|
94
|
+
@index[id] = processor if id
|
95
|
+
# Return the appended processor.
|
96
|
+
processor
|
97
|
+
end
|
98
|
+
|
99
|
+
# TODO
|
100
|
+
def insert processor, options = {}
|
101
|
+
# When a reference is given, insert before it.
|
102
|
+
if options[:before]
|
103
|
+
@processors.insert id_index(options[:before]) - 1, processor
|
104
|
+
else
|
105
|
+
@processors.unshift processor
|
106
|
+
end
|
107
|
+
# Identify the processor using different methods.
|
108
|
+
id = identify processor, options[:as]
|
109
|
+
# Add the processor to the index for direct access purposes.
|
110
|
+
@index[id] = processor if id
|
111
|
+
# Return the appended processor.
|
112
|
+
processor
|
113
|
+
end
|
114
|
+
|
115
|
+
# TODO TEST
|
116
|
+
def replace processor_id
|
117
|
+
processor = at processor_id
|
118
|
+
raise "processor does not exist #{processor_id}" unless processor
|
119
|
+
replacement = yield processor
|
120
|
+
@processors[@processors.index processor] = replacement
|
121
|
+
@index[processor_id] = replacement
|
122
|
+
end
|
123
|
+
|
124
|
+
# Public: Removes a processor from the sequence.
|
125
|
+
#
|
126
|
+
# When a processor object is given, the processor is removed. When its
|
127
|
+
# Symbol identifier is given, the associated processor is removed.
|
128
|
+
#
|
129
|
+
# Returns the removed processor.
|
130
|
+
def remove processor
|
131
|
+
if processor.is_a?(Symbol)
|
132
|
+
processor_id = processor
|
133
|
+
processor = at(processor)
|
134
|
+
@processors.delete processor
|
135
|
+
@index.delete processor_id
|
136
|
+
else
|
137
|
+
# Remove from list of processors.
|
138
|
+
@processors.delete processor
|
139
|
+
# Remove from index (if necessary).
|
140
|
+
@index.delete_if { |id, pr| pr == processor}
|
141
|
+
end
|
142
|
+
# Return the deleted processor.
|
143
|
+
processor
|
144
|
+
end
|
145
|
+
|
146
|
+
# Public
|
147
|
+
# Returns the processor with the given id.
|
148
|
+
#
|
149
|
+
# id - The Symbol identifier of a processor.
|
150
|
+
def at id
|
151
|
+
@index[id]
|
152
|
+
end
|
153
|
+
|
154
|
+
# Internal: Clones the sequence.
|
155
|
+
def dup
|
156
|
+
Sequence.new.tap do |sequence|
|
157
|
+
sequence.processors = @processors.clone
|
158
|
+
sequence.index = @index.clone
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def identify processor, override = nil
|
165
|
+
if override
|
166
|
+
override
|
167
|
+
else
|
168
|
+
processor.class.instance_variable_get :@id
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def id_index id
|
173
|
+
@processors.index at(id)
|
174
|
+
end
|
175
|
+
|
176
|
+
protected
|
177
|
+
|
178
|
+
attr_writer :processors, :index
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'commute/core/sequence'
|
2
|
+
|
3
|
+
module Commute
|
4
|
+
|
5
|
+
# Internal: A stack is a collection of sequences with
|
6
|
+
# A main sequence uses as a starting point for processing.
|
7
|
+
#
|
8
|
+
# When the stack is called with a processable, it gets
|
9
|
+
# processed by the main sequence. That main sequence
|
10
|
+
# can easily "embed" other sequences by adding them as
|
11
|
+
# a processer (a sequence is also a processor).
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# stack = Stack.new do |stack, sequence|
|
16
|
+
# stack.sequence(:calculation) do
|
17
|
+
# append Proc.new { |n| n.data += 1 }
|
18
|
+
# append Proc.new { |n| n.data *= 2 }
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# sequence.append stack.sequencer(:calculation)
|
22
|
+
# sequence.append Proc.new { |p| p.process { |n| "The result is #{n}" }}
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# stack.call Processable.new(context, 1)
|
26
|
+
# # => "The result is 4"
|
27
|
+
#
|
28
|
+
class Stack
|
29
|
+
|
30
|
+
# Public: Constructs a stack with a main sequence.
|
31
|
+
#
|
32
|
+
# sequence - Sequence used as main sequence.
|
33
|
+
#
|
34
|
+
# Yields
|
35
|
+
# 1. The Stack itself. (If the arity of the block is 2)
|
36
|
+
# 2. a new sequence to use a main sequence when no
|
37
|
+
# sequence is explicitely given.
|
38
|
+
#
|
39
|
+
# Raises an error if neither is given.
|
40
|
+
#
|
41
|
+
# Returns a Stack with a main sequence.
|
42
|
+
def initialize sequence = nil, &main
|
43
|
+
@sequences = {}
|
44
|
+
@main = if sequence
|
45
|
+
sequence
|
46
|
+
else
|
47
|
+
if main.arity == 1
|
48
|
+
Sequence.new &main
|
49
|
+
else
|
50
|
+
sequence = Sequence.new
|
51
|
+
yield self, sequence
|
52
|
+
sequence
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Public: Adds a sequence to the stack.
|
58
|
+
#
|
59
|
+
# sequence - The Sequence to be added.
|
60
|
+
# name - The name of the sequence.
|
61
|
+
#
|
62
|
+
# Returns the Sequence that was added.
|
63
|
+
def add sequence, name
|
64
|
+
@sequences[name] = sequence
|
65
|
+
end
|
66
|
+
|
67
|
+
# Public: Get a sequence from the stack.
|
68
|
+
#
|
69
|
+
# name - The name of the wanted Sequence.
|
70
|
+
#
|
71
|
+
# Returns the asked sequence.
|
72
|
+
def get name
|
73
|
+
@sequences[name]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Public: Manipulate an either existing or new sequence.
|
77
|
+
#
|
78
|
+
# name - The name of the sequence to alter or create.
|
79
|
+
#
|
80
|
+
# Yields
|
81
|
+
# 1. The Stack itself. (If the arity of the block is 2)
|
82
|
+
# 2. The existing sequence with given name or
|
83
|
+
# adds a new yielded sequence with the name to the stack.
|
84
|
+
#
|
85
|
+
# Returns the Sequence.
|
86
|
+
def sequence name = nil, &alter
|
87
|
+
if name
|
88
|
+
@sequences[name] || add(Sequence.new(&alter), name)
|
89
|
+
else
|
90
|
+
@main
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Public: Creates a processor that lazily fetches a named sequence
|
95
|
+
# on a call.
|
96
|
+
#
|
97
|
+
# name - The name of the sequence this sequencer stands in for.
|
98
|
+
#
|
99
|
+
# Returns a Sequencer
|
100
|
+
def sequencer name = nil
|
101
|
+
if name
|
102
|
+
Sequencer.new.here(name)
|
103
|
+
else
|
104
|
+
Sequencer.new
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Internal: Calls the stack aka call the main sequence.
|
109
|
+
#
|
110
|
+
# processable - The thing to process.
|
111
|
+
#
|
112
|
+
# Returns the processable
|
113
|
+
def call processable
|
114
|
+
@main.call processable
|
115
|
+
end
|
116
|
+
|
117
|
+
# Public: Alters the stack.
|
118
|
+
#
|
119
|
+
# Yields the stack itself if the arity of the block is 1.
|
120
|
+
# Evaluates the block if the arity is 0.
|
121
|
+
#
|
122
|
+
# Returns the stack itself.
|
123
|
+
def alter &alter
|
124
|
+
if alter.arity == 0
|
125
|
+
instance_eval &alter
|
126
|
+
else
|
127
|
+
yield self, @main
|
128
|
+
end
|
129
|
+
# Return the Stack.
|
130
|
+
return self
|
131
|
+
end
|
132
|
+
|
133
|
+
# Internal: Clones the sequence.
|
134
|
+
def dup
|
135
|
+
Stack.new(@main.dup).tap do |stack|
|
136
|
+
stack.sequences = Hash[@sequences.map { |k,v| [k, v.dup] }]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
protected
|
141
|
+
|
142
|
+
attr_writer :sequences
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
data/lib/commute/version.rb
CHANGED
data/lib/commute.rb
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'commute/core/context'
|
3
|
+
require 'commute/core/request'
|
4
|
+
require 'commute/aspects/url'
|
5
|
+
|
6
|
+
describe Commute::Aspect::Url do
|
7
|
+
|
8
|
+
Klass = Class.new(Commute::Builder) do |klass|
|
9
|
+
include Commute::Aspect::Url::ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:request) { Commute::Request.new }
|
13
|
+
|
14
|
+
it 'should create an url transformation without parameters' do
|
15
|
+
transformation = example('http://api.example.com')
|
16
|
+
# Hash that acts as a context.
|
17
|
+
context = {}
|
18
|
+
transformation.call(request, context)
|
19
|
+
# Checks.
|
20
|
+
request.scheme.must_equal 'http'
|
21
|
+
request.host.must_equal 'api.example.com'
|
22
|
+
request.path.must_be_empty
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should create an url transformation with parameters in the host and path' do
|
26
|
+
transformation = example('https://api.$root.com:3000/1/$scope/$id.xml')
|
27
|
+
# Hash that acts as a context.
|
28
|
+
context = {
|
29
|
+
root: 'example',
|
30
|
+
scope: 'people',
|
31
|
+
id: '3'
|
32
|
+
}
|
33
|
+
transformation.call(request, context)
|
34
|
+
# Checks.
|
35
|
+
request.scheme.must_equal 'https'
|
36
|
+
request.host.must_equal 'api.example.com'
|
37
|
+
request.path.must_equal '/1/people/3.xml'
|
38
|
+
request.port.must_equal '3000'
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should create an url transformation with parameters in the host and path when only some of the values are present' do
|
42
|
+
transformation = example('https://api.$root.com:3000/1/$scope/$id.xml')
|
43
|
+
# Hash that acts as a context.
|
44
|
+
context = {
|
45
|
+
root: 'example',
|
46
|
+
scope: 'people'
|
47
|
+
}
|
48
|
+
transformation.call(request, context)
|
49
|
+
# Checks.
|
50
|
+
request.scheme.must_equal 'https'
|
51
|
+
request.host.must_equal 'api.example.com'
|
52
|
+
request.path.must_equal '/1/people.xml'
|
53
|
+
request.port.must_equal '3000'
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def example pattern
|
59
|
+
Klass.new(Commute::Context.new).url(pattern).transformations.first
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'commute/core/api'
|
3
|
+
|
4
|
+
describe Commute::Api do
|
5
|
+
|
6
|
+
class TestApi < Commute::Api
|
7
|
+
with(text: 'hello world')
|
8
|
+
|
9
|
+
with(id: 1)
|
10
|
+
|
11
|
+
using do
|
12
|
+
sequence.append Proc.new {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def all
|
16
|
+
with(text: 'go', help: true).get
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class TestInheritance < TestApi
|
21
|
+
with(id: 2)
|
22
|
+
|
23
|
+
using do
|
24
|
+
sequence.append Proc.new {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:api_klass) { TestApi }
|
29
|
+
|
30
|
+
let(:api_klass2) do
|
31
|
+
Class.new(Commute::Api) do
|
32
|
+
with text: 'goodbye world'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should allow us to define a base context' do
|
37
|
+
api_klass.context.parameters.must_equal id: 1, text: 'hello world'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should isolate base contexts for each api' do
|
41
|
+
api_klass2.context.parameters.must_equal text: 'goodbye world'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should allow us to modify the base context externally' do
|
45
|
+
build = api_klass.builder.with id: 2
|
46
|
+
api_klass.context.parameters[:id].must_equal 2
|
47
|
+
api_klass.builder.with id: 1
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should create a new Api instance when a method is called on the class' do
|
51
|
+
context = api_klass.with(id: 2).all.context
|
52
|
+
context.parameters.must_equal id: 2, text: 'go', help: true
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should deal with inheritance' do
|
56
|
+
context = TestInheritance.new.context
|
57
|
+
context.parameters[:id].must_equal 2
|
58
|
+
context.stack.sequence.processors.size.must_be :>=, 2
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#new' do
|
62
|
+
it 'should build on the base context without modifying it' do
|
63
|
+
api = api_klass.new.context
|
64
|
+
api.with(id: 2).parameters[:id].must_equal 2
|
65
|
+
context = api.with(text: 'hi!').all.context
|
66
|
+
context.parameters[:id].must_equal 1
|
67
|
+
context.with(id: 2).parameters.must_equal text: 'go', help: true, id: 2
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'commute/core/context'
|
3
|
+
require 'commute/core/request'
|
4
|
+
|
5
|
+
describe Commute::Builder do
|
6
|
+
|
7
|
+
let(:parameters) {{}}
|
8
|
+
let(:stack) { Commute::Stack.new { |stack, main|
|
9
|
+
main.append Proc.new {}
|
10
|
+
}}
|
11
|
+
let(:transformations) {[]}
|
12
|
+
let(:disables) {[]}
|
13
|
+
|
14
|
+
let(:context) { Commute::Context.new stack, parameters, transformations, disables }
|
15
|
+
|
16
|
+
let(:builder) { Commute::Builder.new context }
|
17
|
+
|
18
|
+
let(:request) { Commute::Request.new }
|
19
|
+
|
20
|
+
describe '#with' do
|
21
|
+
it 'should add parameters to the context' do
|
22
|
+
builder.with(id: 1, text: 'hello').parameters.must_equal \
|
23
|
+
id: 1, text: 'hello'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should merge collisions by merging hashes and adding arrays' do
|
27
|
+
builder.
|
28
|
+
with(ids: [1,2], options: { limit: 1 }).
|
29
|
+
with(ids: [3,4], options: { index: 2 }).
|
30
|
+
parameters.must_equal \
|
31
|
+
ids: [1,2,3,4], options: { limit: 1, index: 2 }
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'with initial parameters' do
|
35
|
+
let(:parameters) {
|
36
|
+
{ text: 'hello', limit: 10 }
|
37
|
+
}
|
38
|
+
|
39
|
+
it 'should add parameters to the context' do
|
40
|
+
builder.with(id: 1, text: 'world').parameters.must_equal \
|
41
|
+
id: 1, text: 'world', limit: 10
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should not include parameters with nil value' do
|
46
|
+
builder.with(id: nil).parameters.must_equal({})
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#without' do
|
51
|
+
|
52
|
+
it 'should remove parameters' do
|
53
|
+
builder.with(id: 1, text: 'hello').without(:id).parameters.must_equal text: 'hello'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should work over contexts' do
|
57
|
+
context = builder.with(id: 1, text: 'hello').context
|
58
|
+
context2 = context.without(:id).context
|
59
|
+
context2.parameters.wont_include :id
|
60
|
+
context.parameters.must_include :id
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#enable' do
|
65
|
+
it 'should enable a processor' do
|
66
|
+
skip 'TODO'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#disable' do
|
71
|
+
it 'should disable a processor' do
|
72
|
+
skip 'TODO'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#transform' do
|
77
|
+
it 'should be able to add a static transform without dependencies' do
|
78
|
+
context = builder.transform { |request| request.method = :get }.context
|
79
|
+
context.transformations.first.call(request, context)
|
80
|
+
request.method.must_equal :get
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should be able to add a dynamic transform without dependencies' do
|
84
|
+
context = builder.with(id: 1).transform { |request, context|
|
85
|
+
request.path = "/#{context[:id]}"
|
86
|
+
}.context
|
87
|
+
context.transformations.first.call(request, context)
|
88
|
+
request.path.must_equal '/1'
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should be able to add a dynamic transform with dependencies' do
|
92
|
+
context = builder.with(id: 1).transform(:id) { |request, id|
|
93
|
+
request.path = "/#{id}"
|
94
|
+
}.context
|
95
|
+
context.transformations.first.call(request, context)
|
96
|
+
request.path.must_equal '/1'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should never call the transformation when its dependencies are all nil' do
|
100
|
+
proc = Proc.new {}
|
101
|
+
proc.expects(:call).never
|
102
|
+
context = builder.transform(:id, &proc)
|
103
|
+
context.transformations.first.call(request, context)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#using' do
|
108
|
+
it 'should work without a block' do
|
109
|
+
skip 'TODO'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#context' do
|
114
|
+
it 'should be a snapshot of the builder' do
|
115
|
+
builder.with id: 1
|
116
|
+
context = builder.context
|
117
|
+
context.parameters.must_equal id: 1
|
118
|
+
builder = context.with text: 'hello world'
|
119
|
+
context.parameters.must_equal id: 1
|
120
|
+
builder.context.parameters.must_equal id: 1, text: 'hello world'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|