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,116 @@
|
|
1
|
+
require 'commute/core/context'
|
2
|
+
|
3
|
+
require 'commute/core/processors/hook'
|
4
|
+
require 'commute/core/processors/request_builder'
|
5
|
+
require 'commute/core/processors/sequencer'
|
6
|
+
require 'commute/core/processors/code_status_processor'
|
7
|
+
|
8
|
+
module Commute
|
9
|
+
|
10
|
+
# Public: An API is a context that already provides some standard
|
11
|
+
# context alternations. The collection of these alternations
|
12
|
+
# form a client driver for a remote API.
|
13
|
+
#
|
14
|
+
# The way that a context
|
15
|
+
# is built in an api is exactly the same as you would built it
|
16
|
+
# outside of an API class.
|
17
|
+
#
|
18
|
+
# An API also provides some basic primitives to execute the
|
19
|
+
# transformed request of a context. This can be done through
|
20
|
+
# the `run` method.
|
21
|
+
#
|
22
|
+
# To help you build default contexts (that is a context that
|
23
|
+
# every API instance starts building from), a API class itself
|
24
|
+
# is also a builder. When a API class is instantiated, the
|
25
|
+
# base context is built from the current base builder state, and
|
26
|
+
# is used as a base builder for the API instance (A builder itself).
|
27
|
+
#
|
28
|
+
# Examples
|
29
|
+
#
|
30
|
+
# class PastieApi < Commute::Api
|
31
|
+
#
|
32
|
+
# using(:response_body) do
|
33
|
+
# append Downcaser.new
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# def get
|
37
|
+
# transform(:id) { |request, id|
|
38
|
+
# request.url = "http://pastie.org/pastes/#{id}/text"
|
39
|
+
# }.get
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# gist = PastieApi.get(id: 5109586)
|
44
|
+
#
|
45
|
+
class Api < Builder
|
46
|
+
class << self
|
47
|
+
include Buildable
|
48
|
+
|
49
|
+
# The Builder methods should only be called in the Class itself.
|
50
|
+
# Otherwise, something like TestApi.with ... would modify
|
51
|
+
# the base builder, while we want it to create a new builder
|
52
|
+
# off the base one, with the provided parameters.
|
53
|
+
#
|
54
|
+
# Use TestApi.builder.with ... instead.
|
55
|
+
private *Builder::METHODS
|
56
|
+
|
57
|
+
# Internal: Memoized base builder.
|
58
|
+
def builder
|
59
|
+
@builder ||= Builder.new Context.new(Stack.new {})
|
60
|
+
end
|
61
|
+
|
62
|
+
# Internal: When an Api inherits another Api, it inherits its context.
|
63
|
+
def inherited klass
|
64
|
+
klass.instance_variable_set :@builder, Builder.new(builder.context)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Internal: Forwards all methods on the class to the
|
69
|
+
# base context for this api.
|
70
|
+
#
|
71
|
+
# This is equivalent to calling TestApi.new.[method]
|
72
|
+
def self.method_missing method, *args, &block
|
73
|
+
self.new.send method, *args, &block
|
74
|
+
end
|
75
|
+
|
76
|
+
# Internal: A base stack that is used as a starting
|
77
|
+
# point for all apis. Includes a RequestBuilder
|
78
|
+
# and some convenient sequences for easy processor injection.
|
79
|
+
#
|
80
|
+
# Also provides basic on_request, on_response and
|
81
|
+
# on_complete hooks.
|
82
|
+
using do |stack, main|
|
83
|
+
main.append RequestBuilder.new
|
84
|
+
main.append Sequencer.new(:request), as: :request
|
85
|
+
main.append Sequencer.new(:execution), as: :execution
|
86
|
+
main.append Sequencer.new(:response), as: :response
|
87
|
+
main.append CodeStatusProcessor.new, as: :status
|
88
|
+
main.append Hook.new, as: :on_complete
|
89
|
+
|
90
|
+
stack.sequence(:execution) do
|
91
|
+
append Proc.new { |c|
|
92
|
+
Configuration.adapter.call c
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
# sequence(:request) do
|
97
|
+
# append Scope.new(Sequencer.new(:request_body), :body), as: :body
|
98
|
+
# end
|
99
|
+
|
100
|
+
# sequence(:response) do
|
101
|
+
# append Hook.new, as: :on_response
|
102
|
+
# append Scope.new(Sequencer.new(:response_body), :body), as: :body
|
103
|
+
# end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Public: Creates a new Api Builder, starting to build from
|
107
|
+
# the base context by default. Only when an Api has been
|
108
|
+
# turned into a context and then into a builder again, an
|
109
|
+
# argument will be given here.
|
110
|
+
#
|
111
|
+
# Returns a new Api instance.
|
112
|
+
def initialize context = self.class.builder.context
|
113
|
+
super
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
module Commute
|
2
|
+
|
3
|
+
# TODO comments
|
4
|
+
module Buildable
|
5
|
+
|
6
|
+
def self.included klass
|
7
|
+
klass.extend Forwardable
|
8
|
+
klass.def_delegators :builder, *Builder::METHODS
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def builder
|
14
|
+
Builder.new self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# TODO comments
|
19
|
+
class Builder
|
20
|
+
|
21
|
+
METHODS = [:with, :without, :enable, :disable,
|
22
|
+
:hook, :transform, :using, :get, :put, :post, :patch, :delete].freeze
|
23
|
+
|
24
|
+
# Public: Initializes a new Builder from a context that
|
25
|
+
# starts to build a new context on top of it.
|
26
|
+
#
|
27
|
+
# Use 'context' to get the built context.
|
28
|
+
def initialize context
|
29
|
+
@stack = context.stack
|
30
|
+
@parameters = context.parameters
|
31
|
+
@transformations = context.transformations
|
32
|
+
@disables = context.disables
|
33
|
+
@cloned = []
|
34
|
+
end
|
35
|
+
|
36
|
+
# Internal: Get the context built with this builder.
|
37
|
+
#
|
38
|
+
# Returns a new Context.
|
39
|
+
def context
|
40
|
+
# Make new clones now.
|
41
|
+
@cloned = []
|
42
|
+
# Return the context.
|
43
|
+
Context.new @stack, @parameters, @transformations, @disables, self.class
|
44
|
+
end
|
45
|
+
|
46
|
+
# Internal: When a method is called on the builder that
|
47
|
+
# does not exist, we assume that the developer wanted to
|
48
|
+
# call a method on the already built context. So we return
|
49
|
+
# that built context and forward the method to it.
|
50
|
+
#
|
51
|
+
# Returns the return value from the method invoked on the built context.
|
52
|
+
def method_missing name, *args, &block
|
53
|
+
context.send name, *args, &block
|
54
|
+
end
|
55
|
+
|
56
|
+
# Public: Adds logical parameters to the context.
|
57
|
+
# Logocal parameters whose names match the id of a
|
58
|
+
# layer, get passed to that layer as options.
|
59
|
+
#
|
60
|
+
# When a parameter is set to nil, it is excluded
|
61
|
+
# from the update, use `without` instead.
|
62
|
+
#
|
63
|
+
# Examples
|
64
|
+
#
|
65
|
+
# people.all.with(max: 100)
|
66
|
+
# people.all.enable(:auth).with(auth: 'secret')
|
67
|
+
#
|
68
|
+
def with parameters
|
69
|
+
cloned(:@parameters)
|
70
|
+
parameters.delete_if { |k,v| v.nil? }
|
71
|
+
@parameters.merge!(parameters) do |key, o, n|
|
72
|
+
if o.kind_of? Array
|
73
|
+
o + n
|
74
|
+
elsif o.kind_of? Hash
|
75
|
+
o.merge! n
|
76
|
+
else
|
77
|
+
n
|
78
|
+
end
|
79
|
+
end
|
80
|
+
self
|
81
|
+
end
|
82
|
+
alias :for :with
|
83
|
+
alias :where :with
|
84
|
+
|
85
|
+
def with! parameters
|
86
|
+
cloned(:@parameters)
|
87
|
+
parameters.delete_if { |k,v| v.nil? }
|
88
|
+
# Do not check for collisions.
|
89
|
+
@parameters.merge!(parameters)
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
# Public: Removes logical parameters from the context.
|
94
|
+
#
|
95
|
+
# Examples
|
96
|
+
#
|
97
|
+
# people.all.without(:index, :max)
|
98
|
+
#
|
99
|
+
def without *parameters
|
100
|
+
cloned(:@parameters)
|
101
|
+
parameters.each { |parameter| @parameters.delete parameter }
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
# Public: Enables processors in the stack. Causes the request
|
106
|
+
# to be processed by these processors before executing it.
|
107
|
+
#
|
108
|
+
# Only needed if the processor was disabled before. Adding
|
109
|
+
# a processor to a sequence will auto-enable it.
|
110
|
+
#
|
111
|
+
# Examples
|
112
|
+
#
|
113
|
+
# api = people.all.disable(:format)
|
114
|
+
# # later
|
115
|
+
# api.enable(:format)
|
116
|
+
#
|
117
|
+
def enable *processors
|
118
|
+
cloned(:@disables)
|
119
|
+
processors.each { |processor| @disables.delete processor }
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
# Public: Disable layers in the stack. Causes the request
|
124
|
+
# not to be processed by these layers.
|
125
|
+
#
|
126
|
+
# Equivalent for context.without(:auth)
|
127
|
+
#
|
128
|
+
# Examples
|
129
|
+
#
|
130
|
+
# people.all.disable(:auth)
|
131
|
+
#
|
132
|
+
def disable *processors
|
133
|
+
cloned(:@disables)
|
134
|
+
@disables = @disables | processors
|
135
|
+
self
|
136
|
+
end
|
137
|
+
|
138
|
+
# Public: Adds a hook to the context, this hook will be
|
139
|
+
# triggered for all requests made based on this context.
|
140
|
+
#
|
141
|
+
# Block form equivalent for context.with(event: handler)
|
142
|
+
#
|
143
|
+
# The logic behind this is that the id of the hook processor
|
144
|
+
# is the name of the event. The parameter (handler) will
|
145
|
+
# be passed as options to the processor.
|
146
|
+
#
|
147
|
+
# event - The event to register the handler for.
|
148
|
+
# handler - The handler to be triggered when an event comes up.
|
149
|
+
#
|
150
|
+
def hook event, &handler
|
151
|
+
with(event => handler)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Public: Adds a transformation to the context that instructs
|
155
|
+
# the context transformer how a request must be built from this context.
|
156
|
+
#
|
157
|
+
# Mostly used in API classes.
|
158
|
+
#
|
159
|
+
# dependencies - Logical parameters this transformation is based on (optional).
|
160
|
+
# transformation - The transformation logic.
|
161
|
+
#
|
162
|
+
# Yields
|
163
|
+
# No dependencies: the request and the context (if the arity permits it).
|
164
|
+
# Dependencies: The request and the dependency values (unless all dependency values are nil).
|
165
|
+
#
|
166
|
+
# Examples
|
167
|
+
#
|
168
|
+
# context.transform(:since) { |request, since| request.query[:since] = since }
|
169
|
+
# context.transform(:id) { |request, id| request.url = "http://pastie.com/#{id}"}
|
170
|
+
#
|
171
|
+
# context.transform { |request| request.url = 'http://example.com' }
|
172
|
+
#
|
173
|
+
# context.transform { |request, context|
|
174
|
+
# context[:user] ? "/users/#{self[:user]}/gists" : '/gists'
|
175
|
+
# }
|
176
|
+
#
|
177
|
+
def transform *dependencies, &transformation
|
178
|
+
cloned(:@transformations)
|
179
|
+
if dependencies.empty?
|
180
|
+
@transformations << transformation
|
181
|
+
else
|
182
|
+
@transformations << Proc.new do |request, context|
|
183
|
+
dependency_values = dependencies.map { |dep| context[dep] }
|
184
|
+
transformation.call request, *dependency_values unless dependency_values.all?(&:nil?)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
# Same as `transform` but removes all existing transformations for these dependencies.
|
191
|
+
def transform! *dependencies, &transformation
|
192
|
+
end
|
193
|
+
|
194
|
+
# Public: Used to alter the stack or one of its sequences.
|
195
|
+
#
|
196
|
+
# When no name is given, the stack is altered.
|
197
|
+
#
|
198
|
+
# name - The name of the sequence that needs altering.
|
199
|
+
#
|
200
|
+
# Yields the stack or one of its sequences.
|
201
|
+
# When the block has no arguments, the block is evalled
|
202
|
+
# on the stack or its sequence.
|
203
|
+
#
|
204
|
+
# Examples
|
205
|
+
#
|
206
|
+
# using(:response_body) do
|
207
|
+
# append(Commute::Common::Json::Parse.new)
|
208
|
+
# end
|
209
|
+
#
|
210
|
+
# # Is actually the equivalent for
|
211
|
+
# using do
|
212
|
+
# sequence(:response_body) do
|
213
|
+
# append(Commute::Common::Json::Parse.new)
|
214
|
+
# end
|
215
|
+
# end
|
216
|
+
#
|
217
|
+
def using name = nil, &alter
|
218
|
+
cloned(:@stack)
|
219
|
+
if name
|
220
|
+
sequence = @stack.sequence(name)
|
221
|
+
sequence.alter &alter if alter
|
222
|
+
else
|
223
|
+
@stack.alter &alter
|
224
|
+
end
|
225
|
+
self
|
226
|
+
end
|
227
|
+
|
228
|
+
# Public: Configure the HTTP request method.
|
229
|
+
# Adds a request transformation configuring the request http method.
|
230
|
+
[:get, :put, :post, :patch, :delete].each do |http_method|
|
231
|
+
define_method http_method do
|
232
|
+
transform { |request| request.method = http_method }
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# TODO
|
237
|
+
def limit max
|
238
|
+
with limit: max
|
239
|
+
end
|
240
|
+
|
241
|
+
# TODO
|
242
|
+
def offset index
|
243
|
+
with offset: index
|
244
|
+
end
|
245
|
+
|
246
|
+
# TODO
|
247
|
+
def order field, direction = :asc
|
248
|
+
with order: { field: field, direction: direction }
|
249
|
+
end
|
250
|
+
|
251
|
+
private
|
252
|
+
|
253
|
+
# Makes sure the given instance variable is cloned.
|
254
|
+
def cloned variable
|
255
|
+
if !@cloned.include? variable
|
256
|
+
@cloned << variable
|
257
|
+
instance_variable_set variable, instance_variable_get(variable).dup
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'commute/core/context'
|
2
|
+
|
3
|
+
module Commute
|
4
|
+
|
5
|
+
# Public: A Commuter is a package that holds a value
|
6
|
+
# associated to a context.
|
7
|
+
#
|
8
|
+
# A commuter can be tagged to make conditional
|
9
|
+
# processing possible.
|
10
|
+
#
|
11
|
+
class Commuter
|
12
|
+
attr_reader :context
|
13
|
+
|
14
|
+
# Public: Creates a new commuter
|
15
|
+
def initialize context, value = nil
|
16
|
+
@context = context
|
17
|
+
@value = value
|
18
|
+
@tags = []
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Returns the inner value of the commuter.
|
22
|
+
def get
|
23
|
+
@value
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Sets the inner value of the commuter.
|
27
|
+
#
|
28
|
+
# value - The new value.
|
29
|
+
def set value
|
30
|
+
@value = value
|
31
|
+
end
|
32
|
+
|
33
|
+
# Public: Changes the value of a commuter using a block.
|
34
|
+
# ONLY calls the block if the commuter value is not nil.
|
35
|
+
#
|
36
|
+
# Yields the value of the commuter.
|
37
|
+
#
|
38
|
+
# Returns nothing
|
39
|
+
def change
|
40
|
+
@value = yield @value unless @value.nil?
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
|
44
|
+
# Public: Get some parameters on how the commuter
|
45
|
+
# should be processed within a certain processor.
|
46
|
+
#
|
47
|
+
# processor_id - The id of the processor
|
48
|
+
#
|
49
|
+
# Returns whatever set in the context for this processor.
|
50
|
+
def parameters processor_id
|
51
|
+
self.context[processor_id]
|
52
|
+
end
|
53
|
+
|
54
|
+
# TODO
|
55
|
+
def disabled? processor_id
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
# TODO
|
60
|
+
def enabled? processor_id
|
61
|
+
!disabled?(processor_id)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Public: Tags the commuter.
|
65
|
+
#
|
66
|
+
# tag - The tag to tag with (can be any object).
|
67
|
+
#
|
68
|
+
# Returns nothing.
|
69
|
+
def tag tag
|
70
|
+
@tags << tag
|
71
|
+
return nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Public: Checks if a commuter has a certain tag.
|
75
|
+
#
|
76
|
+
# tag - The tag to check presence of (can be any object).
|
77
|
+
#
|
78
|
+
# Returns true if the commuter is tagged with the given tag.
|
79
|
+
def tagged? tag
|
80
|
+
@tags.include? tag
|
81
|
+
end
|
82
|
+
|
83
|
+
# Public: Delays a commuter by giving it a block that
|
84
|
+
# must definitely be executed before the commute can continue.
|
85
|
+
#
|
86
|
+
# delay - The delay that must be executed.
|
87
|
+
#
|
88
|
+
# Returns nothing.
|
89
|
+
def delay &delay
|
90
|
+
@delay = delay
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def return
|
95
|
+
delay {}
|
96
|
+
end
|
97
|
+
|
98
|
+
# Public: check if a commuter is delayed.
|
99
|
+
def delayed?
|
100
|
+
!!@delay
|
101
|
+
end
|
102
|
+
|
103
|
+
# Public: Wait for a delay to be executed.
|
104
|
+
# And the do something when done.
|
105
|
+
#
|
106
|
+
# Yields when the delay has been executed.
|
107
|
+
#
|
108
|
+
# Returns nothing.
|
109
|
+
def wait &done
|
110
|
+
delay = @delay
|
111
|
+
@delay = nil
|
112
|
+
delay.call &done
|
113
|
+
return nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
require 'commute/core/builder'
|
4
|
+
require 'commute/core/stack'
|
5
|
+
require 'commute/core/builder'
|
6
|
+
|
7
|
+
module Commute
|
8
|
+
class Context
|
9
|
+
include Buildable
|
10
|
+
|
11
|
+
attr_reader :stack, :parameters, :transformations, :disables
|
12
|
+
|
13
|
+
def initialize stack = nil, parameters = {}, transformations = [], \
|
14
|
+
disables = [], builder_class = Builder
|
15
|
+
@stack = stack.freeze
|
16
|
+
@parameters = parameters.freeze || {}
|
17
|
+
@transformations = transformations.freeze || []
|
18
|
+
@disables = disables.freeze || {}
|
19
|
+
@builder_class = builder_class
|
20
|
+
end
|
21
|
+
|
22
|
+
def [] key
|
23
|
+
@parameters[key]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Creates a new context, resetting all transformations.
|
27
|
+
#
|
28
|
+
def reset
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the
|
32
|
+
def run &on_complete
|
33
|
+
context = on_complete ? with(on_complete: on_complete) : self
|
34
|
+
commuter = Commuter.new context
|
35
|
+
@stack.call commuter
|
36
|
+
commuter.get
|
37
|
+
end
|
38
|
+
|
39
|
+
def run! *args, &block
|
40
|
+
result = self.run(*args, &block)
|
41
|
+
if result.kind_of?(Array)
|
42
|
+
result.first
|
43
|
+
else
|
44
|
+
result
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def method_missing method, *args, &block
|
49
|
+
b = builder
|
50
|
+
if b.respond_to?(method)
|
51
|
+
b.send method, *args, &block
|
52
|
+
else
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def builder
|
60
|
+
@builder_class.new self
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'commute/core/commuter'
|
2
|
+
|
3
|
+
module Commute
|
4
|
+
|
5
|
+
# Internal: Looks at a response and decides if it was
|
6
|
+
# successful or not. This is the most basic one of
|
7
|
+
# those checkers. It simply checks if the response
|
8
|
+
# code is a 2xx code, when it is, the request was
|
9
|
+
# a success.
|
10
|
+
#
|
11
|
+
# This processor replaces the commuter's Response
|
12
|
+
#
|
13
|
+
class CodeStatusProcessor
|
14
|
+
|
15
|
+
class CodeStatus
|
16
|
+
@id = :status
|
17
|
+
|
18
|
+
attr_reader :code
|
19
|
+
|
20
|
+
def initialize code
|
21
|
+
@code = code
|
22
|
+
@success = (200..299).include? code
|
23
|
+
end
|
24
|
+
|
25
|
+
def success?
|
26
|
+
@success
|
27
|
+
end
|
28
|
+
|
29
|
+
def fail?
|
30
|
+
!@success
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def call commuter
|
35
|
+
commuter.change do |response|
|
36
|
+
[response.body, CodeStatus.new(response.code)]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'commute/core/commuter'
|
2
|
+
|
3
|
+
module Commute
|
4
|
+
|
5
|
+
# Public: A Hook is a processor that yields the commuter value to a handler.
|
6
|
+
# The handler is given via the options for the processor (via its name).
|
7
|
+
#
|
8
|
+
class Hook
|
9
|
+
|
10
|
+
def call commuter, handler = nil
|
11
|
+
handler.call *commuter.get if handler
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'commute/core/commuter'
|
2
|
+
require 'commute/core/request'
|
3
|
+
|
4
|
+
module Commute
|
5
|
+
|
6
|
+
# Internal: A RequestBuilder builds a request given a context.
|
7
|
+
#
|
8
|
+
# It basically call all transformations defined on the context,
|
9
|
+
# resulting in a request. The request is then passed as the new
|
10
|
+
# value of the commuter (still with a reference to the context).
|
11
|
+
#
|
12
|
+
class RequestBuilder
|
13
|
+
@id = :builder
|
14
|
+
|
15
|
+
def call commuter
|
16
|
+
# Create a new request.
|
17
|
+
request = Request.new
|
18
|
+
# Build the request using context transformations.
|
19
|
+
commuter.context.transformations.each do |t|
|
20
|
+
t.call request, commuter.context
|
21
|
+
end
|
22
|
+
# Set the request on the commuter.
|
23
|
+
commuter.set request
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'commute/core/commuter'
|
2
|
+
|
3
|
+
module Commute
|
4
|
+
|
5
|
+
# Public: A sequencer is a processor that lazily fetches a sequence
|
6
|
+
# from the stack and executes it with the given commuter.
|
7
|
+
#
|
8
|
+
# This has the advantage that an either not ye existent sequence or
|
9
|
+
# a sequence that could be modified later, can be referenced from
|
10
|
+
# another sequence.
|
11
|
+
#
|
12
|
+
# Examples:
|
13
|
+
#
|
14
|
+
# Stack.new do |stack, main|
|
15
|
+
# # Defining a new sequence.
|
16
|
+
# response = stack.sequence(:response) do
|
17
|
+
# ...
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # This would work but if we alter the response
|
21
|
+
# # sequence later on, it would still use this one.
|
22
|
+
# main.append response
|
23
|
+
#
|
24
|
+
# # Instead we do
|
25
|
+
# main.append Sequencer.new(:response)
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
class Sequencer
|
29
|
+
|
30
|
+
# Public: Creates a new sequencer.
|
31
|
+
#
|
32
|
+
# name - The name of the sequence that needs to be called (lazily).
|
33
|
+
def initialize name
|
34
|
+
@name = name
|
35
|
+
end
|
36
|
+
|
37
|
+
def call commuter
|
38
|
+
# Get the stack from the processing context.
|
39
|
+
stack = commuter.context.stack
|
40
|
+
# Get the sequence we need to call.
|
41
|
+
sequence = stack.get(@name)
|
42
|
+
# Call the sequence if one was found.
|
43
|
+
sequence.call commuter if sequence
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|