commute 0.1.2 → 0.2.0.rc.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/.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
|