commute 0.2.0.rc.2 → 0.3.0.pre

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.
Files changed (66) hide show
  1. data/.todo +28 -12
  2. data/README.md +0 -1
  3. data/commute.gemspec +4 -5
  4. data/lib/commute/common/basic_auth.rb +10 -9
  5. data/lib/commute/common/caching.rb +208 -0
  6. data/lib/commute/common/chemicals.rb +47 -24
  7. data/lib/commute/common/eventmachine.rb +68 -0
  8. data/lib/commute/common/synchrony.rb +42 -0
  9. data/lib/commute/common/typhoeus.rb +64 -0
  10. data/lib/commute/core/api.rb +42 -29
  11. data/lib/commute/core/builder.rb +4 -15
  12. data/lib/commute/core/context.rb +156 -15
  13. data/lib/commute/core/http.rb +124 -0
  14. data/lib/commute/core/layer.rb +187 -0
  15. data/lib/commute/core/sequence.rb +83 -132
  16. data/lib/commute/core/stack.rb +63 -72
  17. data/lib/commute/core/status.rb +45 -0
  18. data/lib/commute/core/util/event_emitter.rb +58 -0
  19. data/lib/commute/core/util/path.rb +37 -0
  20. data/lib/commute/core/util/stream.rb +141 -0
  21. data/lib/commute/extensions/crud.rb +88 -0
  22. data/lib/commute/extensions/param.rb +20 -0
  23. data/lib/commute/extensions/url.rb +53 -0
  24. data/lib/commute/version.rb +1 -1
  25. data/spec/commute/common/caching_spec.rb +158 -0
  26. data/spec/commute/common/eventmachine_spec.rb +74 -0
  27. data/spec/commute/common/typhoeus_spec.rb +67 -0
  28. data/spec/commute/core/api_spec.rb +3 -1
  29. data/spec/commute/core/builder_spec.rb +8 -8
  30. data/spec/commute/core/http_spec.rb +39 -0
  31. data/spec/commute/core/layer_spec.rb +81 -0
  32. data/spec/commute/core/sequence_spec.rb +36 -150
  33. data/spec/commute/core/stack_spec.rb +33 -83
  34. data/spec/commute/core/util/event_emitter_spec.rb +35 -0
  35. data/spec/commute/core/util/path_spec.rb +29 -0
  36. data/spec/commute/core/util/stream_spec.rb +90 -0
  37. data/spec/commute/extensions/url_spec.rb +76 -0
  38. data/spec/spec_helper.rb +3 -1
  39. metadata +61 -48
  40. data/examples/gist_api.rb +0 -71
  41. data/examples/highrise_task_api.rb +0 -59
  42. data/examples/pastie_api.rb +0 -18
  43. data/lib/commute/aspects/caching.rb +0 -37
  44. data/lib/commute/aspects/crud.rb +0 -41
  45. data/lib/commute/aspects/pagination.rb +0 -16
  46. data/lib/commute/aspects/url.rb +0 -57
  47. data/lib/commute/common/cache.rb +0 -43
  48. data/lib/commute/common/conditional.rb +0 -27
  49. data/lib/commute/common/em-synchrony_adapter.rb +0 -29
  50. data/lib/commute/common/em_http_request_adapter.rb +0 -57
  51. data/lib/commute/common/typhoeus_adapter.rb +0 -40
  52. data/lib/commute/common/xml.rb +0 -7
  53. data/lib/commute/core/commuter.rb +0 -116
  54. data/lib/commute/core/processors/code_status_processor.rb +0 -40
  55. data/lib/commute/core/processors/hook.rb +0 -14
  56. data/lib/commute/core/processors/request_builder.rb +0 -26
  57. data/lib/commute/core/processors/sequencer.rb +0 -46
  58. data/lib/commute/core/request.rb +0 -58
  59. data/lib/commute/core/response.rb +0 -18
  60. data/spec/commute/aspects/caching_spec.rb +0 -12
  61. data/spec/commute/aspects/url_spec.rb +0 -61
  62. data/spec/commute/core/commuter_spec.rb +0 -64
  63. data/spec/commute/core/processors/code_status_processor_spec.rb +0 -5
  64. data/spec/commute/core/processors/hook_spec.rb +0 -25
  65. data/spec/commute/core/processors/request_builder_spec.rb +0 -25
  66. data/spec/commute/core/processors/sequencer_spec.rb +0 -33
@@ -1,29 +0,0 @@
1
- require "em-synchrony"
2
- require "em-synchrony/em-http"
3
-
4
- require 'commute/common/em_http_request_adapter'
5
-
6
- module Commute
7
- module Common
8
-
9
- # Public: Adapter that uses em-synchrony (with em-http-request).
10
- #
11
- # Requires use of em-synchrony and thus Eventmachine.
12
- #
13
- class EmSynchronyAdapter < EmHttpRequestAdapter
14
- @id = :adapter
15
-
16
- # Internal: Make a request through em-synchrony.
17
- def call commuter, options = {}
18
- # Create a native em-http request.
19
- em_request = to_request commuter.get
20
-
21
- # Create a Commute response.
22
- response = to_response em_request.response_header, em_request.response
23
- response.request = commuter.get
24
- # Set it on the commuter.
25
- commuter.set response
26
- end
27
- end
28
- end
29
- end
@@ -1,57 +0,0 @@
1
- require 'em-http-request'
2
-
3
- require 'commute/core/response'
4
-
5
- module Commute
6
- module Common
7
-
8
- # Public: Adapter that uses em-http-request, an Eventmachine based
9
- # HTTP client, as a underlaying commute engine.
10
- #
11
- # This requires all the commute requests to be made within the
12
- # Eventmachine loop.
13
- #
14
- class EmHttpRequestAdapter
15
- @id = :adapter
16
-
17
- # Internal: Make a request through Eventmachine.
18
- def call commuter, options = {}
19
- # Commuter delay for async processing.
20
- commuter.delay do |&done|
21
- # Create a native em-http request.
22
- em_request = to_request commuter.get
23
-
24
- # Set the callback.
25
- em_request.callback do
26
- # Create a Commute response.
27
- response = to_response em_request.response_header, em_request.response
28
- response.request = request
29
- # Set it on the commuter.
30
- commuter.set response
31
- # Async call finished.
32
- done.call
33
- end
34
- end
35
- end
36
-
37
- protected
38
-
39
- # Internal: Converts a Commute requests into an em-http request.
40
- def to_request request
41
- EventMachine::HttpRequest.new(request.url).send request.method, \
42
- query: request.query,
43
- head: request.headers,
44
- body: request.body
45
- end
46
-
47
- # Internal: Converts an em-http response into a Commute response.
48
- # Note: trims whitespace bodies.
49
- def to_response em_response_header, em_response
50
- Commute::Response.new.tap do |response|
51
- response.code = em_response_header.status
52
- response.body = em_response unless em_response.strip.empty?
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,40 +0,0 @@
1
- require 'typhoeus'
2
-
3
- require 'commute/core/response'
4
-
5
- module Commute
6
- module Common
7
-
8
- # Internal: Adapter that uses Typhoeus to make requests.
9
- #
10
- # Compatibility:
11
- #
12
- # TODO
13
- #
14
- class TyphoeusAdapter
15
- @id = :adapter
16
-
17
- def call commuter, options = {}
18
- commuter.change do |request|
19
- # puts request.inspect
20
- # Build a Typhoeus request from this request.
21
- typhoeus_request = Typhoeus::Request.new request.url, \
22
- method: request.method,
23
- body: request.body.to_s,
24
- params: request.query || {},
25
- headers: request.headers
26
- # Run the request.
27
- hydra = Typhoeus::Hydra.new
28
- hydra.queue typhoeus_request
29
- hydra.run
30
- typhoeus_response = typhoeus_request.response
31
- # Build a Commute response.
32
- Response.new(request).tap do |response|
33
- response.code = typhoeus_response.code
34
- response.body = typhoeus_response.body
35
- end
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,7 +0,0 @@
1
- module Commute
2
- module Common
3
-
4
- class Xml
5
- end
6
- end
7
- end
@@ -1,116 +0,0 @@
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
@@ -1,40 +0,0 @@
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
@@ -1,14 +0,0 @@
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
@@ -1,26 +0,0 @@
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
@@ -1,46 +0,0 @@
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
@@ -1,58 +0,0 @@
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
@@ -1,18 +0,0 @@
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
@@ -1,12 +0,0 @@
1
- require 'spec_helper'
2
- require 'commute/aspects/caching'
3
-
4
- describe Commute::Aspect::Caching do
5
-
6
- class TestApi < Commute::Api
7
- include Commute::Aspect::Caching
8
- end
9
-
10
- it '' do
11
- end
12
- end
@@ -1,61 +0,0 @@
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
@@ -1,64 +0,0 @@
1
- require 'spec_helper'
2
- require 'commute/core/commuter'
3
-
4
- describe Commute::Commuter do
5
-
6
- let(:context) { mock }
7
-
8
- let(:commuter) { Commute::Commuter.new(context, 1) }
9
-
10
- describe '#change' do
11
- it 'should take a block that processes the value' do
12
- commuter.change { |number| number + 1 }
13
- commuter.get.must_equal 2
14
- end
15
-
16
- describe 'when there is no value' do
17
- let(:commuter) { Commute::Commuter.new(context, nil) }
18
-
19
- it 'should not call the block' do
20
- block = Proc.new { |n| n+1 }
21
- block.expects(:call).never
22
- commuter.change &block
23
- end
24
- end
25
- end
26
-
27
- describe '#tag' do
28
- it 'should tag the commuter' do
29
- commuter.tag :cool
30
- commuter.tagged?(:cool).must_equal true
31
- end
32
- end
33
-
34
- describe '#context' do
35
- it 'should return the associated context' do
36
- commuter.context.must_equal context
37
- end
38
- end
39
-
40
- describe '#parameters' do
41
- it 'should return parameters needed for a processor to process the commuter' do
42
- context.expects(:[]).with(:processor).returns operation: :multiply
43
- commuter.parameters(:processor).must_equal operation: :multiply
44
- end
45
- end
46
-
47
- describe '#wait' do
48
- it 'waits for a delay and does something when done waiting' do
49
- done = lambda { @done = true }
50
- delay = Proc.new { |&done| done.call }
51
- commuter.delay &delay
52
- commuter.delayed?.must_equal true
53
- commuter.wait &done
54
- @done.must_equal true
55
-
56
- done = lambda { @done = false }
57
- delay = Proc.new { }
58
- commuter.delay &delay
59
- done.expects(:call).never
60
- commuter.wait &done
61
- @done.must_equal true
62
- end
63
- end
64
- end
@@ -1,5 +0,0 @@
1
- require 'spec_helper'
2
- require 'commute/core/processors/code_status_processor'
3
-
4
- describe Commute::CodeStatusProcessor do
5
- end
@@ -1,25 +0,0 @@
1
- require 'spec_helper'
2
- require 'commute/core/processors/hook'
3
-
4
- describe Commute::Hook do
5
-
6
- let(:hook) { Commute::Hook.new }
7
-
8
- let(:value) { mock }
9
-
10
- let(:commuter) { Commute::Commuter.new mock, value }
11
-
12
- describe 'when no handler is specified' do
13
- it 'should do nothing' do
14
- hook.call commuter
15
- end
16
- end
17
-
18
- describe 'when a handler is specified' do
19
- it 'should call the handler with the commuter value' do
20
- handler = mock
21
- handler.expects(:call).with(value)
22
- hook.call commuter, handler
23
- end
24
- end
25
- end
@@ -1,25 +0,0 @@
1
- require 'spec_helper'
2
- require 'commute/core/processors/request_builder'
3
-
4
- describe Commute::RequestBuilder do
5
-
6
- let(:context) { Commute::Context.new }
7
-
8
- let(:builder) { Commute::RequestBuilder.new }
9
-
10
- it 'should transform the context' do
11
- c = context.get.
12
- transform(:id) { |request, id| request.url = "http://pastie.org/pastes/#{id}/text" }.
13
- with(id: 1)
14
-
15
- commuter = Commute::Commuter.new(c, nil)
16
-
17
- builder.call commuter
18
-
19
- commuter.get.tap { |request|
20
- request.host.must_equal 'pastie.org'
21
- request.path.must_equal '/pastes/1/text'
22
- request.method.must_equal :get
23
- }
24
- end
25
- end
@@ -1,33 +0,0 @@
1
- require 'spec_helper'
2
- require 'commute/core/processors/sequencer'
3
-
4
- describe Commute::Sequencer do
5
-
6
- let(:stack) { Commute::Stack.new {} }
7
-
8
- let(:context) { Commute::Context.new stack }
9
-
10
- let(:commuter) { Commute::Commuter.new context, mock }
11
-
12
- let(:sequencer) { Commute::Sequencer.new(:response) }
13
-
14
- describe 'The sequence is not defined in the stack' do
15
- it 'should do nothing' do
16
- sequencer.call commuter
17
- end
18
- end
19
-
20
- describe 'The sequence is defined in the stack' do
21
- let(:sequence) { mock }
22
- let(:commuter) do
23
- Commute::Commuter.new context.using { |stack, main|
24
- stack.add sequence, :response
25
- }.context, mock
26
- end
27
-
28
- it 'should call the sequence' do
29
- sequence.expects(:call).once
30
- sequencer.call commuter
31
- end
32
- end
33
- end