async_rack_test 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ html
3
+ pkg
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --tag ~slow
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,5 @@
1
+ === 0.0.1 / 2012-02-23
2
+
3
+ * Initial commit, copied from:
4
+ http://github.com/raggi/async_sinatra
5
+ http://libraggi.rubyforge.org/async_sinatra
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ group :test do
2
+ gem 'rack-test'
3
+ gem 'rspec'
4
+ gem 'em-spec', :require => 'em/spec'
5
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ GEM
2
+ specs:
3
+ bacon (1.1.0)
4
+ diff-lcs (1.1.3)
5
+ em-spec (0.2.6)
6
+ bacon
7
+ eventmachine
8
+ rspec (> 2.6.0)
9
+ test-unit
10
+ eventmachine (1.0.0.beta.4)
11
+ rack (1.3.6)
12
+ rack-test (0.6.1)
13
+ rack (>= 1.0)
14
+ rspec (2.8.0)
15
+ rspec-core (~> 2.8.0)
16
+ rspec-expectations (~> 2.8.0)
17
+ rspec-mocks (~> 2.8.0)
18
+ rspec-core (2.8.0)
19
+ rspec-expectations (2.8.0)
20
+ diff-lcs (~> 1.1.2)
21
+ rspec-mocks (2.8.0)
22
+ test-unit (2.4.7)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ em-spec
29
+ rack-test
30
+ rspec
data/README.rdoc ADDED
@@ -0,0 +1,81 @@
1
+ = async_rack_test
2
+
3
+ https://github.com/thoughtless/async_rack_test
4
+
5
+ Inspired by async_sinatra:
6
+ http://github.com/raggi/async_sinatra
7
+ http://libraggi.rubyforge.org/async_sinatra
8
+
9
+ == DESCRIPTION:
10
+
11
+ Extends rack-test to make working with EventMachine easier.
12
+
13
+ == SYNOPSIS:
14
+
15
+ A quick example:
16
+
17
+ require 'spec_helper'
18
+ require 'async_rack_test'
19
+
20
+ describe MyAsyncApp, :type => :request do
21
+ include AsyncRackTest::Methods
22
+ let(:app) { MyAsyncApp.new }
23
+
24
+ it 'should be successful' do
25
+ apost "/some_url", "some data"
26
+
27
+ last_response.should be_ok
28
+ end
29
+ end
30
+
31
+
32
+ == REQUIREMENTS:
33
+
34
+ TODO
35
+
36
+
37
+ == INSTALL:
38
+
39
+ TODO
40
+
41
+
42
+ == RUNNING THE SPECS:
43
+
44
+ If you want to run the tests for this gem, note that the included .rspec file
45
+ excludes the slow tests by default. To run the slow tests, use something like:
46
+
47
+ be rspec spec --tag slow
48
+
49
+
50
+ == TODO:
51
+
52
+ MAYBE: look into rack-test giving us a simple hook to add middleware
53
+ * ultimately AsyncRackTest just inserts ResyncApp as middleware
54
+ * maybe I can just do something like "use ResyncApp" rather than using my weird wrapper with aliases
55
+ Update gemspec file to include all the dependencies (development, test, and otherwise)
56
+
57
+
58
+ == LICENSE:
59
+
60
+ (The MIT License)
61
+
62
+ Copyright (c) 2012 Paul Cortens
63
+
64
+ Permission is hereby granted, free of charge, to any person obtaining
65
+ a copy of this software and associated documentation files (the
66
+ 'Software'), to deal in the Software without restriction, including
67
+ without limitation the rights to use, copy, modify, merge, publish,
68
+ distribute, sublicense, and/or sell copies of the Software, and to
69
+ permit persons to whom the Software is furnished to do so, subject to
70
+ the following conditions:
71
+
72
+ The above copyright notice and this permission notice shall be
73
+ included in all copies or substantial portions of the Software.
74
+
75
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
76
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
77
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
78
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
79
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
80
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
81
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
3
+ require "async_rack_test/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "async_rack_test"
7
+ s.version = AsyncRackTest::Version::STRING
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Paul Cortens"]
10
+ s.email = "paul@thoughtless.ca"
11
+ s.homepage = "http://github.com/thoughtless/async_rack_test"
12
+ s.summary = "async_rack_test-#{AsyncRackTest::Version::STRING}"
13
+ s.description = "Extends rack-test to make working with EventMachine easier."
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.extra_rdoc_files = [ 'README.rdoc', 'CHANGELOG.rdoc']
19
+ s.rdoc_options = ["--charset=UTF-8"]
20
+ s.require_path = "lib"
21
+
22
+ # TODO: Add rack-test as a dependency
23
+ end
@@ -0,0 +1,42 @@
1
+ require 'rack/test'
2
+ require 'async_rack_test/resync_app'
3
+
4
+
5
+ # Adds aget, apost, etc. which treat an asynchronous rack-app as synchronous.
6
+ module AsyncRackTest
7
+ class Timeout < StandardError; end
8
+
9
+ module Methods
10
+ include Rack::Test::Methods
11
+
12
+ # The original app
13
+ def async_app
14
+ @async_app ||= app
15
+ end
16
+ def sync_app
17
+ @sync_app ||= begin
18
+ ResyncApp.new(async_app)
19
+ end
20
+ end
21
+
22
+ def use_sync
23
+ async_app # Ensure we have cached the original app first.
24
+ self.instance_eval { class << self; self; end }.class_eval { alias :app :sync_app }
25
+ end
26
+ def use_async
27
+ async_app # Ensure we have cached the original app first.
28
+ self.instance_eval { class << self; self; end }.class_eval { alias :app :async_app }
29
+ end
30
+
31
+ %w(get put post delete head options).each do |m|
32
+ eval <<-RUBY, binding, __FILE__, __LINE__ + 1
33
+ def a#{m}(*args)
34
+ use_sync
35
+ #{m}(*args)
36
+ use_async
37
+ end
38
+ RUBY
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,44 @@
1
+ # Turns an async rack app into a sync one. This let's you test the app with
2
+ # rack-test as if it were synchronous.
3
+ module AsyncRackTest
4
+ class ResyncApp
5
+ attr_reader :app
6
+
7
+ # app => The async app that this is meant to wrap.
8
+ # opts
9
+ # :timeout => Number of seconds before giving up on the async app.
10
+ def initialize(app, opts={})
11
+ @app = app
12
+ @timeout = opts[:timeout] || 5
13
+ end
14
+
15
+ def call(env)
16
+ result = nil
17
+ env['async.callback'] = method(:write_async_response)
18
+ EM.run do
19
+ response = app.call(env)
20
+ if response[0] == -1
21
+ EM.add_periodic_timer(0.1) do
22
+ unless @async_response.nil?
23
+ result = @async_response
24
+ EM.stop
25
+ end
26
+ end
27
+ EM.add_timer(@timeout) do
28
+ raise Timeout, "Did not receive a response from the app within #{@timeout} seconds--app: #{app.inspect}"
29
+ end
30
+ else
31
+ result = response
32
+ EM.stop
33
+ end
34
+ end
35
+ result
36
+ end
37
+
38
+ private
39
+
40
+ def write_async_response(response)
41
+ @async_response = response
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ module AsyncRackTest # :nodoc:
2
+ module Version
3
+ STRING = '0.0.2'
4
+ end
5
+ end
@@ -0,0 +1,145 @@
1
+ require 'spec_helper'
2
+
3
+ describe AsyncRackTest do
4
+ class DummyTest
5
+ include AsyncRackTest::Methods
6
+ def app
7
+ @app ||= Proc.new do |env|
8
+ EM.next_tick do
9
+ env['async.callback'].call [200, {}, []]
10
+ end
11
+ [-1, {}, []]
12
+ end
13
+ end
14
+ end
15
+
16
+ describe AsyncRackTest::Methods do
17
+ let(:test_obj) { DummyTest.new }
18
+ describe '#sync_app' do
19
+ it 'should be a ResyncApp' do
20
+ test_obj.sync_app.should be_kind_of(AsyncRackTest::ResyncApp)
21
+ end
22
+ end
23
+
24
+ describe '#async_app' do
25
+ it 'should be the original app' do
26
+ test_obj.async_app.should be_kind_of(Proc)
27
+ end
28
+ end
29
+
30
+ describe '#app' do
31
+ context 'default' do
32
+ it 'should be the original app' do
33
+ test_obj.app.should == test_obj.async_app
34
+ end
35
+ end
36
+ context 'after calling #use_sync' do
37
+ it 'should be a ResyncApp' do
38
+ test_obj.use_sync
39
+ test_obj.app.should == test_obj.sync_app
40
+ end
41
+ context 'then after calling #use_async' do
42
+ it 'should be the original app' do
43
+ test_obj.use_sync
44
+ test_obj.use_async
45
+ test_obj.app.should == test_obj.async_app
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ describe '#aget' do
52
+ it 'should set the app be a ResyncApp' do
53
+ test_obj.should_receive(:use_sync)
54
+ test_obj.stub(:get)
55
+ test_obj.stub(:use_async)
56
+ test_obj.aget '/'
57
+ end
58
+ it 'should call #call on #sync_app' do
59
+ test_obj.sync_app.should_receive(:call).and_return([200, {}, []])
60
+ test_obj.aget '/'
61
+ end
62
+ it 'should call #get' do
63
+ test_obj.should_receive(:get)
64
+ test_obj.aget '/'
65
+ end
66
+ it 'should put the app back to the original' do
67
+ test_obj.aget '/'
68
+ test_obj.app.should == test_obj.async_app
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ describe AsyncRackTest::ResyncApp do
75
+ let(:resync_app) { AsyncRackTest::ResyncApp.new(DummyTest.new.app) }
76
+ describe '#call' do
77
+ it "should call #call on the passed in app" do
78
+ resync_app.app.should_receive(:call).and_return([200, {}, []])
79
+ resync_app.call({})
80
+ end
81
+
82
+ it "should return when the passed in app returns a status code other than -1", :slow => true do
83
+ @trigger_async = nil
84
+ async_app = Proc.new do |env|
85
+ EM.add_periodic_timer(0.01) do
86
+ if @trigger_async # This lets us control when the async behavior actually happens.
87
+ env['async.callback'].call [200, {}, []]
88
+ end
89
+ end
90
+ [-1, {}, []]
91
+ end
92
+ resync_app = AsyncRackTest::ResyncApp.new(async_app)
93
+
94
+ @result = nil
95
+ thread = Thread.new { @result = resync_app.call({}) }
96
+
97
+ sleep 1 # Give some time to guarantee the thread has had time to run.
98
+ @result.should be_nil
99
+ @trigger_async = true
100
+ thread.join
101
+ @result.should == [200, {}, []]
102
+ end
103
+
104
+ it "should return instantly when the passed in app is not async", :slow => true do
105
+ @trigger_async = nil
106
+ async_app = Proc.new do |env|
107
+ EM.add_periodic_timer(0.01) do
108
+ if @trigger_async # This lets us control when the async behavior actually happens.
109
+ raise 'This should never happen'
110
+ end
111
+ end
112
+ [200, {}, []]
113
+ end
114
+ resync_app = AsyncRackTest::ResyncApp.new(async_app)
115
+
116
+ @result = nil
117
+ thread = Thread.new { @result = resync_app.call({}) }
118
+
119
+ sleep 1 # Give some time to guarantee the thread has had time to run.
120
+ @result.should == [200, {}, []]
121
+ @trigger_async = true
122
+ sleep 1 # Give some time to guarantee the thread has had time to run.
123
+ thread.join
124
+ end
125
+
126
+ context "#sync_app doesn't respond within the set timeout", :slow => true do
127
+ let(:slow_app) do
128
+ Proc.new do |env|
129
+ EM.add_timer(1.1) do
130
+ env['async.callback'].call [200, {}, []]
131
+ end
132
+ [-1, {}, []]
133
+ end
134
+ end
135
+ let(:resync_app) { AsyncRackTest::ResyncApp.new(slow_app, :timeout => 1) }
136
+
137
+ it "should time out" do
138
+ lambda {
139
+ resync_app.call({})
140
+ }.should raise_error(AsyncRackTest::Timeout)
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,7 @@
1
+ require 'bundler/setup'
2
+ require 'async_rack_test'
3
+ require 'em-spec/rspec'
4
+
5
+ # Requires supporting ruby files with custom matchers and macros, etc,
6
+ # # in spec/support/ and its subdirectories.
7
+ Dir[File.join(File.expand_path("..", __FILE__), "support/**/*.rb")].each {|f| require f}
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: async_rack_test
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.2
6
+ platform: ruby
7
+ authors:
8
+ - Paul Cortens
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-06-12 00:00:00 Z
14
+ dependencies: []
15
+
16
+ description: Extends rack-test to make working with EventMachine easier.
17
+ email: paul@thoughtless.ca
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - CHANGELOG.rdoc
25
+ files:
26
+ - .gitignore
27
+ - .rspec
28
+ - CHANGELOG.rdoc
29
+ - Gemfile
30
+ - Gemfile.lock
31
+ - README.rdoc
32
+ - async_rack_test.gemspec
33
+ - lib/async_rack_test.rb
34
+ - lib/async_rack_test/resync_app.rb
35
+ - lib/async_rack_test/version.rb
36
+ - spec/async_rack_test_spec.rb
37
+ - spec/spec_helper.rb
38
+ homepage: http://github.com/thoughtless/async_rack_test
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --charset=UTF-8
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.8.11
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: async_rack_test-0.0.2
65
+ test_files: []
66
+