grape-async 0.1.2 → 0.1.3

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.
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