elevate 0.5.0 → 0.6.0

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.
@@ -1,4 +1,10 @@
1
1
  module Elevate
2
+ # A blank slate for hosting task blocks.
3
+ #
4
+ # Because task blocks run in another thread, it is dangerous to expose them
5
+ # to the calling context. This class acts as a sandbox for task blocks.
6
+ #
7
+ # @api private
2
8
  class TaskContext
3
9
  def initialize(args, &block)
4
10
  metaclass = class << self; self; end
@@ -1,3 +1,3 @@
1
1
  module Elevate
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -0,0 +1,159 @@
1
+ module Bacon
2
+ class Context
3
+ include ::Elevate
4
+ end
5
+ end
6
+
7
+ describe Elevate do
8
+ extend WebStub::SpecHelpers
9
+
10
+ describe "#async" do
11
+ it "runs the specified task asynchronously" do
12
+ async do
13
+ task do
14
+ true
15
+ end
16
+
17
+ on_finish do |result, exception|
18
+ @called = result
19
+ resume
20
+ end
21
+ end
22
+
23
+ wait_max 1.0 do
24
+ @called.should.be.true
25
+ end
26
+ end
27
+
28
+ it "passes provided args to the task as instance variables" do
29
+ async name: "harry" do
30
+ task do
31
+ @name
32
+ end
33
+
34
+ on_finish do |name, exception|
35
+ @result = name
36
+ resume
37
+ end
38
+ end
39
+
40
+ wait_max 1.0 do
41
+ @result.should == "harry"
42
+ end
43
+ end
44
+
45
+ it "allows tasks to report progress" do
46
+ @updates = []
47
+
48
+ async do
49
+ task do
50
+ sleep 0.1
51
+ yield 1
52
+ sleep 0.2
53
+ yield 2
54
+ sleep 0.3
55
+ yield 3
56
+
57
+ true
58
+ end
59
+
60
+ on_update do |count|
61
+ @updates << count
62
+ end
63
+
64
+ on_finish do |result, exception|
65
+ resume
66
+ end
67
+ end
68
+
69
+ wait_max 1.0 do
70
+ @updates.should == [1,2,3]
71
+ end
72
+ end
73
+
74
+ describe "timeouts" do
75
+ before do
76
+ stub_request(:get, "http://example.com/").
77
+ to_return(body: "Hello!", content_type: "text/plain", delay: 1.0)
78
+ end
79
+
80
+ it "does not cancel the operation if it completes in time" do
81
+ @timed_out = false
82
+
83
+ async do
84
+ timeout 3.0
85
+
86
+ task do
87
+ Elevate::HTTP.get("http://example.com/")
88
+
89
+ "finished"
90
+ end
91
+
92
+ on_finish do |result, exception|
93
+ @result = result
94
+ resume
95
+ end
96
+ end
97
+
98
+ wait_max 5.0 do
99
+ @result.should == "finished"
100
+ @timed_out.should.be.false
101
+ end
102
+ end
103
+
104
+ it "stops the operation when timeout interval has elapsed" do
105
+ @result = nil
106
+
107
+ @task = async do
108
+ timeout 0.5
109
+
110
+ task do
111
+ Elevate::HTTP.get("http://example.com/")
112
+
113
+ "finished"
114
+ end
115
+
116
+ on_finish do |result, exception|
117
+ @result = result
118
+ resume
119
+ end
120
+ end
121
+
122
+ wait_max 5.0 do
123
+ @result.should.not == "finished"
124
+
125
+ @task.timed_out?.should.be.true
126
+ end
127
+ end
128
+
129
+ it "invokes on_timeout when a timeout occurs" do
130
+ @result = ""
131
+ @timed_out = false
132
+
133
+ async do
134
+ timeout 0.5
135
+
136
+ task do
137
+ Elevate::HTTP.get("http://example.com/")
138
+
139
+ "finished"
140
+ end
141
+
142
+ on_timeout do
143
+ @timed_out = true
144
+ end
145
+
146
+ on_finish do |result, exception|
147
+ @result = result
148
+ resume
149
+ end
150
+ end
151
+
152
+ wait_max 5.0 do
153
+ @result.should.not == "finished"
154
+ @timed_out.should.be.true
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -1,4 +1,4 @@
1
- describe Elevate::HTTP::HTTPRequest do
1
+ describe Elevate::HTTP::Request do
2
2
  extend WebStub::SpecHelpers
3
3
 
4
4
  before do
@@ -11,15 +11,15 @@ describe Elevate::HTTP::HTTPRequest do
11
11
  end
12
12
 
13
13
  it "requires a valid HTTP method" do
14
- lambda { Elevate::HTTP::HTTPRequest.new(:invalid, @url) }.should.raise(ArgumentError)
14
+ lambda { Elevate::HTTP::Request.new(:invalid, @url) }.should.raise(ArgumentError)
15
15
  end
16
16
 
17
17
  it "requires a URL starting with http" do
18
- lambda { Elevate::HTTP::HTTPRequest.new(:get, "asdf") }.should.raise(ArgumentError)
18
+ lambda { Elevate::HTTP::Request.new(:get, "asdf") }.should.raise(ArgumentError)
19
19
  end
20
20
 
21
21
  it "requires the body to be an instance of NSData" do
22
- lambda { Elevate::HTTP::HTTPRequest.new(:get, @url, body: @body) }.should.raise(ArgumentError)
22
+ lambda { Elevate::HTTP::Request.new(:get, @url, body: @body) }.should.raise(ArgumentError)
23
23
  end
24
24
 
25
25
  describe "fulfilling a GET request" do
@@ -27,7 +27,7 @@ describe Elevate::HTTP::HTTPRequest do
27
27
  stub_request(:get, @url).
28
28
  to_return(body: @body, headers: {"Content-Type" => "text/plain"}, status_code: 201)
29
29
 
30
- @request = Elevate::HTTP::HTTPRequest.new(:get, @url)
30
+ @request = Elevate::HTTP::Request.new(:get, @url)
31
31
  @response = @request.response
32
32
  end
33
33
 
@@ -52,7 +52,7 @@ describe Elevate::HTTP::HTTPRequest do
52
52
  before do
53
53
  stub_request(:get, @url).with(headers: { "API-Token" => "abc123" }).to_return(body: @body)
54
54
 
55
- @request = Elevate::HTTP::HTTPRequest.new(:get, @url, headers: {})
55
+ @request = Elevate::HTTP::Request.new(:get, @url, headers: { "API-Token" => "abc123" })
56
56
  @response = @request.response
57
57
  end
58
58
 
@@ -67,7 +67,7 @@ describe Elevate::HTTP::HTTPRequest do
67
67
  end
68
68
 
69
69
  it "sends the body as part of the request" do
70
- request = Elevate::HTTP::HTTPRequest.new(:post, @url, body: @body.dataUsingEncoding(NSUTF8StringEncoding))
70
+ request = Elevate::HTTP::Request.new(:post, @url, body: @body.dataUsingEncoding(NSUTF8StringEncoding))
71
71
  response = request.response
72
72
 
73
73
  NSString.alloc.initWithData(response.body, encoding:NSUTF8StringEncoding).should == @body
@@ -82,9 +82,9 @@ describe Elevate::HTTP::HTTPRequest do
82
82
  it "aborts the request" do
83
83
  start = Time.now
84
84
 
85
- request = Elevate::HTTP::HTTPRequest.new(:get, @url)
86
- request.start()
87
- request.cancel()
85
+ request = Elevate::HTTP::Request.new(:get, @url)
86
+ request.send
87
+ request.cancel
88
88
 
89
89
  response = request.response # simulate blocking
90
90
  finish = Time.now
@@ -93,9 +93,9 @@ describe Elevate::HTTP::HTTPRequest do
93
93
  end
94
94
 
95
95
  it "sets the response to nil" do
96
- request = Elevate::HTTP::HTTPRequest.new(:get, @url)
97
- request.start()
98
- request.cancel()
96
+ request = Elevate::HTTP::Request.new(:get, @url)
97
+ request.send
98
+ request.cancel
99
99
 
100
100
  request.response.should.be.nil
101
101
  end
@@ -0,0 +1,195 @@
1
+ describe Elevate::HTTP do
2
+ extend WebStub::SpecHelpers
3
+
4
+ before { disable_network_access! }
5
+ after { enable_network_access! }
6
+
7
+ before do
8
+ @url = "http://www.example.com/"
9
+ end
10
+
11
+ Elevate::HTTP::Request::METHODS.each do |m|
12
+ describe ".#{m}" do
13
+ it "synchronously issues a HTTP #{m} request" do
14
+ stub = stub_request(m, @url)
15
+ Elevate::HTTP.send(m, @url)
16
+
17
+ stub.should.be.requested
18
+ end
19
+ end
20
+ end
21
+
22
+ describe "Request options" do
23
+ it "encodes query string of :headers" do
24
+ stub = stub_request(:get, "#{@url}?a=1&b=hello&c=4.2")
25
+
26
+ Elevate::HTTP.get(@url, query: { a: 1, b: "hello", c: "4.2" })
27
+
28
+ stub.should.be.requested
29
+ end
30
+
31
+ it "sends headers specified by :headers" do
32
+ stub = stub_request(:get, @url).
33
+ with(headers: { "API-Key" => "secret" })
34
+
35
+ Elevate::HTTP.get(@url, headers: { "API-Key" => "secret" })
36
+
37
+ stub.should.be.requested
38
+ end
39
+
40
+ it "sends body content specified by :body" do
41
+ stub = stub_request(:post, @url).with(body: "hello")
42
+
43
+ Elevate::HTTP.post(@url, body: "hello".dataUsingEncoding(NSUTF8StringEncoding))
44
+
45
+ stub.should.be.requested
46
+ end
47
+
48
+ describe "with a JSON body" do
49
+ it "encodes JSON dictionary specified by :json" do
50
+ stub = stub_request(:post, @url).with(body: '{"test":"secret"}')
51
+
52
+ Elevate::HTTP.post(@url, json: { "test" => "secret" })
53
+
54
+ stub.should.be.requested
55
+ end
56
+
57
+ it "encodes JSON array specified by :json" do
58
+ stub = stub_request(:post, @url).with(body: '["1","2"]')
59
+
60
+ Elevate::HTTP.post(@url, json: ["1", "2"])
61
+
62
+ stub.should.be.requested
63
+ end
64
+
65
+ it "sets the correct Content-Type" do
66
+ stub = stub_request(:post, @url).
67
+ with(body: '{"test":"secret"}', headers: { "Content-Type" => "application/json" })
68
+
69
+ Elevate::HTTP.post(@url, json: { "test" => "secret" })
70
+
71
+ stub.should.be.requested
72
+ end
73
+ end
74
+
75
+ describe "with a form body" do
76
+ it "encodes form data specified by :form" do
77
+ stub = stub_request(:post, @url).with(body: { "test" => "secret", "user" => "matt" })
78
+
79
+ Elevate::HTTP.post(@url, form: { "test" => "secret", "user" => "matt" })
80
+
81
+ stub.should.be.requested
82
+ end
83
+
84
+ it "sets the correct Content-Type" do
85
+ stub = stub_request(:post, @url).
86
+ with(body: { "test" => "secret", "user" => "matt" }, headers: { "Content-Type" => "application/x-www-form-urlencoded" })
87
+
88
+ Elevate::HTTP.post(@url, form: { "test" => "secret", "user" => "matt" })
89
+
90
+ stub.should.be.requested
91
+ end
92
+ end
93
+ end
94
+
95
+ describe "Response" do
96
+ it "returns a Response" do
97
+ stub_request(:get, @url).
98
+ to_return(body: "hello",
99
+ headers: { "X-TestHeader" => "Value" },
100
+ status_code: 204)
101
+
102
+ response = Elevate::HTTP.get(@url)
103
+
104
+ NSString.alloc.initWithData(response.body, encoding: NSUTF8StringEncoding).should == "hello"
105
+ NSString.alloc.initWithData(response.raw_body, encoding: NSUTF8StringEncoding).should == "hello"
106
+ response.error.should.be.nil
107
+ response.headers.keys.should.include("X-TestHeader")
108
+ response.status_code.should == 204
109
+ response.url.should == @url
110
+ end
111
+
112
+ describe "when the response is encoded as JSON" do
113
+ it "should automatically decode it" do
114
+ stub_request(:get, @url).
115
+ to_return(json: { user_id: "3", token: "secret" })
116
+
117
+ response = Elevate::HTTP.get(@url)
118
+
119
+ response.body.should == { "user_id" => "3", "token" => "secret" }
120
+ end
121
+
122
+ describe "when a JSON dictionary is returned" do
123
+ it "the returned response should behave like a Hash" do
124
+ stub_request(:get, @url).
125
+ to_return(json: { user_id: "3", token: "secret" })
126
+
127
+ response = Elevate::HTTP.get(@url)
128
+
129
+ response["user_id"].should == "3"
130
+ end
131
+ end
132
+
133
+ describe "when a JSON array is returned" do
134
+ it "the returned response should behave like an Array" do
135
+ stub_request(:get, @url).
136
+ to_return(json: ["apple", "orange", "pear"])
137
+
138
+ response = Elevate::HTTP.get(@url)
139
+
140
+ response.length.should == 3
141
+ end
142
+ end
143
+ end
144
+
145
+ describe "when the response is redirected" do
146
+ it "redirects to the final URL" do
147
+ @redirect_url = @url + "redirected"
148
+
149
+ stub_request(:get, @redirect_url).to_return(body: "redirected")
150
+ stub_request(:get, @url).to_redirect(url: @redirect_url)
151
+
152
+ response = Elevate::HTTP.get(@url)
153
+ response.url.should == @redirect_url
154
+ end
155
+
156
+ describe "with an infinite redirect loop" do
157
+ before do
158
+ @url2 = @url + "redirect"
159
+
160
+ stub_request(:get, @url).to_redirect(url: @url2)
161
+ stub_request(:get, @url2).to_redirect(url: @url)
162
+ end
163
+
164
+ it "raises a RequestError" do
165
+ lambda { Elevate::HTTP.get(@url) }.
166
+ should.raise(Elevate::HTTP::RequestError)
167
+ end
168
+ end
169
+ end
170
+
171
+ #describe "when the response has a HTTP error status" do
172
+ #it "raises RequestError" do
173
+ #stub_request(m, @url).to_return(status_code: 422)
174
+
175
+ #lambda { Elevate::HTTP.send(m, @url) }.should.raise(Elevate::HTTP::RequestError)
176
+ #end
177
+ #end
178
+
179
+ describe "when the request cannot be fulfilled" do
180
+ it "raises a RequestError" do
181
+ lambda { Elevate::HTTP.get(@url) }.
182
+ should.raise(Elevate::HTTP::RequestError)
183
+ end
184
+ end
185
+
186
+ describe "when the Internet connection is offline" do
187
+ it "raises an OfflineError" do
188
+ stub_request(:get, @url).to_fail(code: NSURLErrorNotConnectedToInternet)
189
+
190
+ lambda { Elevate::HTTP.get(@url) }.
191
+ should.raise(Elevate::HTTP::OfflineError)
192
+ end
193
+ end
194
+ end
195
+ end
@@ -30,6 +30,14 @@ describe Elevate::IOCoordinator do
30
30
  lambda { @coordinator.send(method, "hello") }.should.raise(Elevate::CancelledError)
31
31
  end
32
32
  end
33
+
34
+ describe "when IO has timed out" do
35
+ it "raises TimeoutError" do
36
+ @coordinator.cancel(Elevate::TimeoutError)
37
+
38
+ lambda { @coordinator.send(method, "hello") }.should.raise(Elevate::TimeoutError)
39
+ end
40
+ end
33
41
  end
34
42
  end
35
43