rack-worker 0.0.1.rc2 → 0.0.1.rc3

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