grape-async 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b8a6658bdec057ab8c3af4a2ad4bee0723e759a
4
- data.tar.gz: 8846be8cad70cd13854af9953867f756eeace202
3
+ metadata.gz: 7818cb5a44ad63386d861fd483b728ffd55b746c
4
+ data.tar.gz: f9f8b85b67ef81d34a61a4d3bdb69a8f808123f5
5
5
  SHA512:
6
- metadata.gz: 207a6b8ac1f3fbf66036668fb5f20882085a943a59e8a36faf51c2bb5d4aa7dd2c4d358c75cfdba8cac5729bc738bc2a90c04ced5e8a65d22492db49d1a055a9
7
- data.tar.gz: 3174176234b9fd20749c4dae03194875d51d6af7da28c3150739803a8de1369f0a66b7ef5e7d1dae738f1c75460e8b39aa7f0bd5a5b76dcd9002384a53ce5db3
6
+ metadata.gz: 4b40b12de2ddf6a8254b0d5a0982f02ba7be4e3e27e63910e7e7befc5902dbfbd8315ca0189986201d653fce8f818446597ea0de3597d18e6ae325a2823c1847
7
+ data.tar.gz: 2b5b48fb30d35bf643a100691eed443c67b98d18626f86dbd56e2601c51b0c20052b8719ab395a9094798bc0204eafb8b89ee732cb3259003c906c53f1c0ec2d
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- grape-async (0.1.0)
5
- activesupport
6
- eventmachine
7
- grape
4
+ grape-async (0.1.2)
5
+ activesupport (~> 4.2)
6
+ eventmachine (~> 1.0)
7
+ grape (~> 0.14)
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
@@ -20,12 +20,6 @@ GEM
20
20
  ice_nine (~> 0.11.0)
21
21
  thread_safe (~> 0.3, >= 0.3.1)
22
22
  builder (3.2.2)
23
- capybara (2.5.0)
24
- mime-types (>= 1.16)
25
- nokogiri (>= 1.3.3)
26
- rack (>= 1.0.0)
27
- rack-test (>= 0.5.4)
28
- xpath (~> 2.0)
29
23
  coderay (1.1.0)
30
24
  coercible (1.0.0)
31
25
  descendants_tracker (~> 0.0.1)
@@ -70,16 +64,10 @@ GEM
70
64
  rb-inotify (>= 0.9)
71
65
  lumberjack (1.0.9)
72
66
  method_source (0.8.2)
73
- mime-types (3.0)
74
- mime-types-data (~> 3.2015)
75
- mime-types-data (3.2015.1120)
76
- mini_portile2 (2.0.0)
77
67
  minitest (5.8.3)
78
68
  multi_json (1.11.2)
79
69
  multi_xml (0.5.5)
80
70
  nenv (0.2.0)
81
- nokogiri (1.6.7.1)
82
- mini_portile2 (~> 2.0.0.rc2)
83
71
  notiffany (0.0.8)
84
72
  nenv (~> 0.1)
85
73
  shellany (~> 0.0)
@@ -127,21 +115,18 @@ GEM
127
115
  coercible (~> 1.0)
128
116
  descendants_tracker (~> 0.0, >= 0.0.3)
129
117
  equalizer (~> 0.0, >= 0.0.9)
130
- xpath (2.0.0)
131
- nokogiri (~> 1.3)
132
118
 
133
119
  PLATFORMS
134
120
  ruby
135
121
 
136
122
  DEPENDENCIES
137
- bundler
138
- capybara
123
+ bundler (~> 1.0)
139
124
  grape-async!
140
125
  guard
141
126
  guard-rspec
142
127
  pry
143
128
  puma
144
- rack-test
129
+ rack-test (~> 0.5)
145
130
  rake
146
131
  rspec
147
132
  thin
data/Guardfile CHANGED
@@ -1,7 +1,7 @@
1
1
  # A sample Guardfile
2
2
  # More info at https://github.com/guard/guard#readme
3
3
 
4
- guard :rspec, cmd: 'bundle exec rspec', after_all_pass: false do
4
+ guard :rspec, cmd: 'bundle exec rspec', after_all_pass: false, failed_mode: :focus do
5
5
  watch(%r{^spec/.+_spec\.rb$})
6
6
  watch(%r{^spec/support/.+$}) { "spec" }
7
7
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
data/README.md CHANGED
@@ -30,6 +30,20 @@ end
30
30
  The `async` directive accepts either `:em` for EventMachine based async or `:threaded` for thread based async.
31
31
  The default is `:threaded`.
32
32
 
33
+ ## Examples
34
+
35
+ To run the provided example with Thin:
36
+
37
+ ```shell
38
+ bundle exec thin start -R examples/config.ru
39
+ ```
40
+
41
+ or with Puma
42
+
43
+ ```shell
44
+ bundle exec puma examples/config.ru -t 8:32 -w 3 -b tcp://0.0.0.0:3000
45
+ ```
46
+
33
47
  ## Contributing
34
48
 
35
49
  1. Fork it ( https://github.com/stuart/grape-async/fork )
@@ -1,7 +1,7 @@
1
- require '../lib/grape-async'
1
+ require './lib/grape-async'
2
2
 
3
3
  class API < Grape::API
4
- use Grape::Middleware::Async
4
+ use Grape::Async
5
5
 
6
6
  async :em
7
7
  desc "Get status using EM async timer"
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "grape-async"
7
- spec.version = '0.1.2'
7
+ spec.version = '0.1.3'
8
8
  spec.platform = Gem::Platform::RUBY
9
9
  spec.authors = ["Lachlan Laycock"]
10
10
  spec.email = ["l.laycock@stuart.com"]
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.add_dependency "eventmachine", '~> 1.0'
23
23
  spec.add_dependency "activesupport", '~> 4.2'
24
24
 
25
- spec.add_development_dependency 'bundler', '~> 3.0'
25
+ spec.add_development_dependency 'bundler', '~> 1.0'
26
26
  spec.add_development_dependency 'rake', '~> 10.0'
27
27
  spec.add_development_dependency 'rack-test', '~> 0.5'
28
28
  spec.add_development_dependency 'rspec', '~> 3.0'
@@ -1,4 +1,4 @@
1
1
  require "grape"
2
- require "grape-async/api"
2
+ require "grape-async/async"
3
3
  require "grape-async/endpoint"
4
- require "grape-async/middleware/async"
4
+ require "grape-async/extension"
@@ -0,0 +1,73 @@
1
+ module Grape
2
+ class Async < Grape::Middleware::Base
3
+
4
+ def call!(env)
5
+ @env = env
6
+ if endpoint.async_route? && !async_io.nil?
7
+ if endpoint.async_route?(:em)
8
+ proc = lambda {
9
+ EM.next_tick do
10
+ super
11
+ endpoint.deferred_resp.callback do
12
+ resp = endpoint.file || [endpoint.body]
13
+ async_call [endpoint.status, endpoint.header, resp]
14
+ end
15
+ end
16
+ }
17
+ if !EM.reactor_running?
18
+ EM.run do
19
+ proc.call
20
+ end
21
+ else
22
+ proc.call
23
+ end
24
+
25
+ else
26
+ Thread.new do
27
+ result = super
28
+ async_call result
29
+ yield
30
+ end
31
+ end
32
+
33
+ [-1, {}, []] # Return async response
34
+
35
+ else
36
+ super
37
+
38
+ end
39
+ end
40
+
41
+ def async_call(result)
42
+ if @env['async.callback']
43
+ async_io.call result
44
+ elsif @env['rack.hijack']
45
+ status, headers, body = result
46
+ begin
47
+ async_io.write("HTTP/1.1 #{status}\r\n")
48
+ headers.each do |key, value|
49
+ async_io.write("#{key}: #{value}\r\n")
50
+ end
51
+ async_io.write("Connection: close\r\n")
52
+ async_io.write("\r\n")
53
+ body.each do |data|
54
+ async_io.write(data)
55
+ end
56
+ ensure
57
+ async_io.close
58
+ end
59
+ end
60
+ end
61
+
62
+ def async_io
63
+ @async_io ||= @env['async.callback'] || begin
64
+ @env.key?('rack.hijack') ? @env['rack.hijack'].call : nil
65
+ end
66
+ end
67
+
68
+ def endpoint
69
+ @env[Grape::Env::API_ENDPOINT]
70
+ end
71
+
72
+ end
73
+ end
@@ -1,11 +1,13 @@
1
1
  module Grape
2
- class API
2
+ class Async
3
+ module Extension
3
4
 
4
- class << self
5
5
  def async(method = :threaded)
6
6
  route_setting :async, { async: true, async_method: method }
7
7
  end
8
+
9
+ Grape::API.extend self
10
+
8
11
  end
9
12
  end
10
-
11
13
  end
@@ -3,18 +3,26 @@ require 'eventmachine'
3
3
  require 'thin'
4
4
  require 'puma'
5
5
 
6
- describe Grape::Middleware::Async do
6
+ describe Grape::Async do
7
7
 
8
8
  def setup_async_obj!
9
9
  async_obj = double(:async_obj)
10
10
  allow(async_obj).to receive(:call)
11
- allow_any_instance_of(Grape::Middleware::Async).to receive(:async_io) {
11
+ allow_any_instance_of(Grape::Async).to receive(:async_io) {
12
12
  async_obj
13
13
  }
14
14
  end
15
15
 
16
- let(:host) { 'localhost' }
17
- let(:port) { 3333 }
16
+ def kill_server
17
+ @server.kill if @server.is_a?(Thread) and @server.alive?
18
+ end
19
+
20
+ def port
21
+ 3333
22
+ end
23
+
24
+ let(:host) { 'localhost' }
25
+ let(:delay) { 0.25 }
18
26
 
19
27
  before(:each) do
20
28
  Spec::Support::EndpointFaker::FakerAPI.clear_requests!
@@ -22,20 +30,25 @@ describe Grape::Middleware::Async do
22
30
 
23
31
  context "server async requests", type: :feature do
24
32
 
25
- let(:reqs_tracker) { Spec::Support::EndpointFaker::FakerAPI.requests }
26
- let(:async_responses) { %w(start start start done done done) }
27
- let(:sync_responses) { %w(start done start done start done) }
33
+ let(:reqs_tracker) { Spec::Support::EndpointFaker::FakerAPI.requests }
34
+ let(:async_responses) { %w(start:1 start:2 start:3 done:1 done:2 done:3) }
35
+ let(:sync_responses) { %w(start:1 done:1 start:2 done:2 start:3 done:3) }
28
36
 
29
37
  shared_examples "async requests" do
30
38
 
31
39
  let(:route) { '/async' }
32
-
40
+
33
41
  it "should make 3 thread based async requests" do
34
42
  threads = []
43
+ counter = 0
44
+ expect(@server).to be_alive
35
45
  3.times do
36
46
  threads << Thread.new {
37
- `curl -s http://#{host}:#{port}#{route}`
47
+ counter += 1
48
+ uri = URI.parse("http://#{host}:#{port}#{route}?counter=#{counter}&delay=#{delay}")
49
+ response = Net::HTTP.get_response(uri)
38
50
  }
51
+ sleep(delay / 3.0)
39
52
  end
40
53
  threads.each(&:join)
41
54
  expect(reqs_tracker).to eql(async_responses)
@@ -49,10 +62,14 @@ describe Grape::Middleware::Async do
49
62
 
50
63
  it "should make sync 3 requests" do
51
64
  threads = []
65
+ counter = 0
52
66
  3.times do
53
67
  threads << Thread.new {
54
- `curl -s http://#{host}:#{port}#{route}`
68
+ counter += 1
69
+ uri = URI.parse("http://#{host}:#{port}#{route}?counter=#{counter}")
70
+ response = Net::HTTP.get_response(uri)
55
71
  }
72
+ sleep(delay/3.0)
56
73
  end
57
74
  threads.each(&:join)
58
75
  expect(reqs_tracker).to eql(sync_responses)
@@ -64,13 +81,13 @@ describe Grape::Middleware::Async do
64
81
 
65
82
  before(:all) do
66
83
  @server = Thread.new {
67
- Thin::Server.start('localhost', 3333) { run Spec::Support::EndpointFaker::FakerAPI }
84
+ Thin::Server.start('localhost', port) { run Spec::Support::EndpointFaker::FakerAPI }
68
85
  }
69
86
  sleep(1)
70
87
  end
71
88
 
72
89
  after(:all) do
73
- @server.kill if @server.is_a?(Thread) and @server.alive?
90
+ kill_server
74
91
  end
75
92
 
76
93
  context "sync endpoints are run as sync" do
@@ -92,16 +109,17 @@ describe Grape::Middleware::Async do
92
109
  context "using the puma server" do
93
110
 
94
111
  before(:all) do
95
- @server = Thread.new {
96
- app = Spec::Support::EndpointFaker::FakerAPI.new
97
- Puma::Server.new(app).tap do |s|
98
- s.add_tcp_listener 'localhost', 3333
99
- end.run
100
- }
112
+ app = Spec::Support::EndpointFaker::FakerAPI.new
113
+ puma = Puma::Server.new(app).tap do |s|
114
+ s.add_tcp_listener 'localhost', port
115
+ end
116
+ puma.run
117
+ @server = puma.thread
118
+ sleep(1)
101
119
  end
102
120
 
103
121
  after(:all) do
104
- @server.kill if @server.is_a?(Thread) and @server.alive?
122
+ kill_server
105
123
  end
106
124
 
107
125
  context "sync endpoints are run as sync" do
@@ -131,13 +149,13 @@ describe Grape::Middleware::Async do
131
149
 
132
150
  before(:all) do
133
151
  @server = Thread.new {
134
- Rack::Handler::WEBrick.run(Spec::Support::EndpointFaker::FakerAPI.new, :Port => 3333)
152
+ Rack::Handler::WEBrick.run(Spec::Support::EndpointFaker::FakerAPI.new, :Port => port)
135
153
  }
136
154
  sleep(1)
137
155
  end
138
156
 
139
157
  after(:all) do
140
- @server.kill if @server.is_a?(Thread) and @server.alive?
158
+ kill_server
141
159
  end
142
160
 
143
161
  context "sync endpoints are run as sync" do
@@ -160,38 +178,39 @@ describe Grape::Middleware::Async do
160
178
  run Spec::Support::EndpointFaker::FakerAPI.new
161
179
  end
162
180
  }
181
+ let(:counter) { 1 }
163
182
 
164
183
  context "async endpoints" do
165
184
  it "should pass through the async middleware" do
166
- expect_any_instance_of(Grape::Middleware::Async).to receive(:call!).and_return([-1, {}, []])
185
+ expect_any_instance_of(Grape::Async).to receive(:call!).and_return([-1, {}, []])
167
186
  get '/async'
168
187
  end
169
188
 
170
189
  it "should receive async response" do
171
190
  setup_async_obj!
172
- get '/async'
191
+ get '/async', counter: counter, delay: delay
173
192
  expect(last_response.status).to eq(-1)
174
193
  end
175
194
 
176
195
  it "should NOT receive async response without server asysnc support" do
177
- get '/async'
196
+ get '/async', counter: counter, delay: delay
178
197
  expect(last_response.status).to eq(200)
179
198
  end
180
199
  end
181
200
 
182
201
  context "sync endpoints" do
183
202
  it "should pass through the async middleware" do
184
- expect_any_instance_of(Grape::Middleware::Async).to receive(:call!).and_return([200, {}, ['ok']])
185
- get '/sync'
203
+ expect_any_instance_of(Grape::Async).to receive(:call!).and_return([200, {}, ['ok']])
204
+ get '/sync', counter: counter
186
205
  end
187
206
 
188
207
  it "should NOT receive async response" do
189
- get '/sync'
208
+ get '/sync', counter: counter
190
209
  expect(last_response.status).to eq(200)
191
210
  end
192
211
 
193
212
  it "should NOT receive async response without server asysnc support" do
194
- get '/sync'
213
+ get '/sync', counter: counter
195
214
  expect(last_response.status).to eq(200)
196
215
  end
197
216
 
@@ -19,46 +19,51 @@ module Spec
19
19
 
20
20
  end
21
21
 
22
- logger = Logger.new('log/app.log')
23
- use Grape::Middleware::Async
24
- use Rack::CommonLogger
25
-
22
+ use Grape::Async
23
+
26
24
  helpers do
27
- def logger
28
- API.logger
29
- end
30
-
31
- def log_before!
32
- FakerAPI.requests << "start"
25
+ def log_start!(i)
26
+ FakerAPI.requests << "start:#{i}"
33
27
  end
34
28
 
35
- def log_done!
36
- FakerAPI.requests << "done"
29
+ def log_done!(i)
30
+ FakerAPI.requests << "done:#{i}"
37
31
  end
38
32
  end
39
33
 
40
34
  async
35
+ params do
36
+ requires :counter, type: Integer
37
+ requires :delay, type: Float
38
+ end
41
39
  get :async do
42
- log_before!
43
- sleep(1)
40
+ log_start!(params[:counter])
41
+ sleep(params[:delay])
44
42
  present({ status: 'ok'})
45
- log_done!
43
+ log_done!(params[:counter])
46
44
  end
47
45
 
48
46
  async :em
47
+ params do
48
+ requires :counter, type: Integer
49
+ requires :delay, type: Float
50
+ end
49
51
  get :async_em do
50
- log_before!
51
- EM.add_timer(1) do
52
+ log_start!(params[:counter])
53
+ EM.add_timer(params[:delay]) do
52
54
  present({ status: 'ok'})
53
55
  done
54
- log_done!
56
+ log_done!(params[:counter])
55
57
  end
56
58
  end
57
59
 
60
+ params do
61
+ requires :counter, type: Integer
62
+ end
58
63
  get :sync do
59
- log_before!
64
+ log_start!(params[:counter])
60
65
  present({ status: 'ok'})
61
- log_done!
66
+ log_done!(params[:counter])
62
67
  end
63
68
 
64
69
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-async
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lachlan Laycock
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-30 00:00:00.000000000 Z
11
+ date: 2016-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: grape
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.0'
61
+ version: '1.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.0'
68
+ version: '1.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -128,13 +128,13 @@ files:
128
128
  - grape-async.gemspec
129
129
  - lib/.DS_Store
130
130
  - lib/grape-async.rb
131
- - lib/grape-async/api.rb
131
+ - lib/grape-async/async.rb
132
132
  - lib/grape-async/endpoint.rb
133
- - lib/grape-async/middleware/async.rb
133
+ - lib/grape-async/extension.rb
134
134
  - spec/factories.rb
135
135
  - spec/lib/grape-async/api_spec.rb
136
+ - spec/lib/grape-async/async_spec.rb
136
137
  - spec/lib/grape-async/endpoint_spec.rb
137
- - spec/lib/grape-async/middleware/async_spec.rb
138
138
  - spec/spec_helper.rb
139
139
  - spec/support/endpoint_faker.rb
140
140
  homepage: https://github.com/stuartapp/grape-async
@@ -157,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
157
  version: '0'
158
158
  requirements: []
159
159
  rubyforge_project:
160
- rubygems_version: 2.4.5.1
160
+ rubygems_version: 2.5.1
161
161
  signing_key:
162
162
  specification_version: 4
163
163
  summary: Enable asyncronous endpoints to avoid blocking slow requests within EventMachine
@@ -165,7 +165,7 @@ summary: Enable asyncronous endpoints to avoid blocking slow requests within Eve
165
165
  test_files:
166
166
  - spec/factories.rb
167
167
  - spec/lib/grape-async/api_spec.rb
168
+ - spec/lib/grape-async/async_spec.rb
168
169
  - spec/lib/grape-async/endpoint_spec.rb
169
- - spec/lib/grape-async/middleware/async_spec.rb
170
170
  - spec/spec_helper.rb
171
171
  - spec/support/endpoint_faker.rb
@@ -1,71 +0,0 @@
1
- module Grape
2
- module Middleware
3
- class Async < Grape::Middleware::Base
4
-
5
- def call!(env)
6
- @env = env
7
- if endpoint.async_route? && !async_io.nil?
8
- if endpoint.async_route?(:em)
9
- proc = lambda {
10
- EM.next_tick do
11
- super
12
- endpoint.deferred_resp.callback do
13
- resp = endpoint.file || [endpoint.body]
14
- async_call [endpoint.status, endpoint.header, resp]
15
- end
16
- end
17
- }
18
- if !EM.reactor_running?
19
- EM.run do
20
- proc.call
21
- end
22
- else
23
- proc.call
24
- end
25
- else
26
- Thread.new do
27
- result = super
28
- async_call result
29
- yield
30
- end
31
- end
32
-
33
- [-1, {}, []] # Return async response
34
-
35
- else
36
- super
37
-
38
- end
39
- end
40
-
41
- def async_call(result)
42
- if @env['async.callback']
43
- async_io.call result
44
- elsif @env['rack.hijack']
45
- begin
46
- if result.last.is_a?(Rack::BodyProxy)
47
- async_io << result.last.body.first
48
- result.last.close unless result.last.closed?
49
- elsif result.last.is_a?(Array)
50
- async_io << result.last.first
51
- end
52
- ensure
53
- EM.stop if endpoint.async_route?(:em)
54
- @env['rack.hijack'].close
55
- end
56
- end
57
- end
58
-
59
- def async_io
60
- @env['async.callback'] || begin
61
- @env.key?('rack.hijack') ? @env['rack.hijack'].call : nil
62
- end
63
- end
64
-
65
- def endpoint
66
- @env[Grape::Env::API_ENDPOINT]
67
- end
68
-
69
- end
70
- end
71
- end