rack-worker 0.0.1.rc2 → 0.0.1.rc3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,3 +2,11 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in rack-worker.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'rr'
8
+ gem 'turn'
9
+ gem 'webmock'
10
+ gem 'rack-test', :require => 'rack/test'
11
+ gem 'sinatra', :require => 'sinatra/base'
12
+ end
data/README.md CHANGED
@@ -1,6 +1,25 @@
1
1
  # Rack::Worker
2
2
 
3
- TODO: Write a gem description
3
+ Rack middleware that implements the Worker Pattern.
4
+
5
+ It processes GET requests with a worker backend and only serves them straight from a cache.
6
+ While processing the request it serves empty HTTP 202 responses.
7
+ Your web frontend is never blocked processing the request.
8
+
9
+
10
+ ## How it works
11
+
12
+ When GET requests hit your app, the middleware tries to serve them from the cache.
13
+
14
+ If the request is not found, it stores the environment data in the cache. A worker
15
+ process will then use the `App.call(env)` convention from Rack to run the request through
16
+ your webapp in the background as if it were a normal Rack request. The status, headers,
17
+ and body are then stored in the cache so they can be served.
18
+
19
+ What makes this technique different from a standard HTTP caching approach is that your
20
+ web server never processes the long HTTP request. The middleware will return empty
21
+ HTTP 202 responses unless the response is found in the cache. Every request that generates
22
+ a 202 will only queue one background job per URL.
4
23
 
5
24
  ## Installation
6
25
 
@@ -18,7 +37,36 @@ Or install it yourself as:
18
37
 
19
38
  ## Usage
20
39
 
21
- TODO: Write usage instructions here
40
+ ```ruby
41
+ class App < Sinatra::Base
42
+ use Rack::Worker
43
+
44
+ get '/long_ass_request' do
45
+ long_ass_work
46
+ end
47
+ end
48
+ ```
49
+
50
+ That's it! Now GETs to `/long_ass_request` will be processed in the background and only
51
+ serve HTTP 202 responses until they are processed, after which they will return whatever your
52
+ app would have returned.
53
+
54
+ If you already have `queue_classic` and `dalli` installed, everything will *just work*.
55
+
56
+ See configuration for setting an expiry time on records.
57
+
58
+ ## Configuration
59
+
60
+ ```ruby
61
+ Rack::Worker.cache = Dalli::Client.new(nil, {:expires_in => 300})
62
+ ```
63
+ The `cache` can be anything that responds to `get(key)` and `set(key, string)`
64
+
65
+ ```ruby
66
+ Rack::Worker.queue = QC
67
+ ```
68
+ The `queue` can be anything that responds to `enqueue(method, *params)`
69
+
22
70
 
23
71
  ## Contributing
24
72
 
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Worker
3
- VERSION = '0.0.1.rc2'
3
+ VERSION = '0.0.1.rc3'
4
4
  end
5
5
  end
data/lib/rack/worker.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rack/worker/version'
2
+ require 'json'
2
3
 
3
4
  module Rack
4
5
  class Worker
@@ -40,8 +41,8 @@ module Rack
40
41
  else
41
42
  unless cache.get("env-#{key}")
42
43
  cache.set("env-#{key}", env.to_json)
43
- queue.enqueue("#{self.class.name}.process_request",
44
- @app.class.name, key)
44
+ name = @app.is_a?(Class) ? @app.name : @app.class.name
45
+ queue.enqueue("#{self.class.name}.process_request", name, key)
45
46
  end
46
47
  [202, {"Content-type" => "text/plain"}, []]
47
48
  end
@@ -51,7 +52,7 @@ module Rack
51
52
  def self.process_request(classname, id)
52
53
  env = cache.get("env-#{id}")
53
54
  return unless env
54
- env = Yajl::Parser.parse(env)
55
+ env = JSON.parse(env)
55
56
  app = classname_to_class(classname)
56
57
  status, headers, body = app.call(env.merge('rack.worker_qc' => true))
57
58
  set_response(id, status, headers, body)
@@ -68,11 +69,11 @@ module Rack
68
69
  def get_response(key)
69
70
  response = cache.get("response-#{key}")
70
71
  return unless response
71
- Yajl::Parser.parse(response)
72
+ JSON.parse(response)
72
73
  end
73
74
 
74
75
  def key(env)
75
- env['REQUEST_PATH'] + '?' + env['QUERY_STRING']
76
+ (env['REQUEST_PATH'] || env['PATH_INFO']) + '?' + env['QUERY_STRING']
76
77
  end
77
78
  end
78
79
  end
data/rack-worker.gemspec CHANGED
@@ -14,4 +14,6 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "rack-worker"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Rack::Worker::VERSION
17
+
18
+ gem.add_dependency 'json'
17
19
  end
@@ -0,0 +1,14 @@
1
+ require 'bundler'
2
+ Bundler.require :test
3
+ require 'webmock/test_unit'
4
+ require "#{File.dirname(__FILE__)}/../lib/rack-worker"
5
+
6
+ class Rack::Worker::TestCase < Test::Unit::TestCase
7
+ include Rack::Test::Methods
8
+ include RR::Adapters::TestUnit
9
+
10
+ def teardown
11
+ super
12
+ WebMock.reset!
13
+ end
14
+ end
@@ -0,0 +1,110 @@
1
+ require 'test_helper'
2
+
3
+ module QueueTest
4
+ def test_processes_request_in_queue
5
+ Rack::Worker.cache = Object.new
6
+ mock(Rack::Worker.cache).get('response-/foo?') { false }
7
+ mock(Rack::Worker.cache).get('env-/foo?') { false }
8
+ mock(Rack::Worker.cache).get('env-/foo?') { {'rack.input' => []}.to_json }
9
+ mock(Rack::Worker.cache).set('env-/foo?', is_a(String))
10
+ mock(Rack::Worker.cache).set('response-/foo?', is_a(String))
11
+
12
+ mock_queue = Object.new
13
+ def mock_queue.enqueue(function_call, *args)
14
+ eval("#{function_call} *args")
15
+ end
16
+ Rack::Worker.queue = mock_queue
17
+
18
+ get '/foo'
19
+ assert_equal 202, last_response.status
20
+ end
21
+ end
22
+
23
+ class WorkerTest < Rack::Worker::TestCase
24
+ include QueueTest
25
+ class TestClassApp
26
+ def self.call(env)
27
+ [200, {"Content-Type" => "text/test"}, ['Hello, world']]
28
+ end
29
+ end
30
+
31
+ def app
32
+ Rack::Builder.new do
33
+ use Rack::Worker
34
+ run TestClassApp
35
+ end
36
+ end
37
+
38
+ def test_returns_empty_202_and_queues_when_not_in_cache_or_queue
39
+ Rack::Worker.cache = Object.new
40
+ mock(Rack::Worker.cache).get('response-/foo?') { false }
41
+ mock(Rack::Worker.cache).get('env-/foo?') { false }
42
+ stub(Rack::Worker.cache).set
43
+
44
+ Rack::Worker.queue = Object.new
45
+ mock(Rack::Worker.queue).enqueue('Rack::Worker.process_request', is_a(String), '/foo?')
46
+
47
+ get '/foo'
48
+ assert_equal 202, last_response.status
49
+ assert_equal '', last_response.body
50
+ end
51
+
52
+ def test_returns_empty_202_and_does_not_queue_when_not_in_cache_and_in_queue
53
+ Rack::Worker.cache = Object.new
54
+ mock(Rack::Worker.cache).get('response-/foo?') { false }
55
+ mock(Rack::Worker.cache).get('env-/foo?') { true }
56
+
57
+ get '/foo'
58
+ assert_equal 202, last_response.status
59
+ assert_equal '', last_response.body
60
+ end
61
+
62
+ def test_returns_response_in_queue
63
+ json = "{\"Hello\":\"World\"}"
64
+ Rack::Worker.cache = Object.new
65
+ mock(Rack::Worker.cache).get('response-/foo?') do
66
+ [302, {"Content-Type" => "application/json"}, [json]].to_json
67
+ end
68
+
69
+ get '/foo'
70
+ assert_equal 302, last_response.status
71
+ assert_equal 'application/json', last_response.headers['Content-Type']
72
+ assert_equal json, last_response.body
73
+ end
74
+
75
+ end
76
+
77
+ class SinatraTest < Rack::Worker::TestCase
78
+ include QueueTest
79
+
80
+ class TestClassApp < Sinatra::Base
81
+ get '*' do
82
+ headers 'Content-Type' => 'text/test'
83
+ 'Hello, world'
84
+ end
85
+ end
86
+
87
+ def app
88
+ Rack::Builder.new do
89
+ use Rack::Worker
90
+ run TestClassApp
91
+ end
92
+ end
93
+ end
94
+
95
+ class SinatraUseTest < Rack::Worker::TestCase
96
+ include QueueTest
97
+
98
+ class TestClassApp < Sinatra::Base
99
+ use Rack::Worker
100
+
101
+ get '*' do
102
+ headers 'Content-Type' => 'text/test'
103
+ 'Hello, world'
104
+ end
105
+ end
106
+
107
+ def app
108
+ TestClassApp
109
+ end
110
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-worker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.rc2
4
+ version: 0.0.1.rc3
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,18 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
  date: 2012-04-12 00:00:00.000000000 Z
13
- dependencies: []
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: &70334749515180 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70334749515180
14
25
  description: Rack middleware that implements the Worker Pattern
15
26
  email:
16
27
  - christopher.continanza@gmail.com
@@ -27,6 +38,8 @@ files:
27
38
  - lib/rack/worker.rb
28
39
  - lib/rack/worker/version.rb
29
40
  - rack-worker.gemspec
41
+ - test/test_helper.rb
42
+ - test/worker_test.rb
30
43
  homepage: ''
31
44
  licenses: []
32
45
  post_install_message:
@@ -53,4 +66,6 @@ specification_version: 3
53
66
  summary: Processes GET requests with a worker backend and only serves them straight
54
67
  from a cache. Your web frontend is never blocked processing the request. Implementation
55
68
  of the Worker Pattern
56
- test_files: []
69
+ test_files:
70
+ - test/test_helper.rb
71
+ - test/worker_test.rb