papermill-agent 0.0.1
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 +11 -0
- data/LICENSE +20 -0
- data/README.md +18 -0
- data/Rakefile +5 -0
- data/config/papermill.sample.yml +1 -0
- data/doc/ideas.md +5 -0
- data/lib/papermill-agent.rb +17 -0
- data/lib/papermill-agent/agent.rb +76 -0
- data/lib/papermill-agent/rack/collector.rb +20 -0
- data/lib/papermill-agent/response_adapters/base.rb +27 -0
- data/lib/papermill-agent/response_adapters/rails.rb +15 -0
- data/lib/papermill-agent/response_adapters/sinatra.rb +9 -0
- data/lib/papermill-agent/response_parser.rb +24 -0
- data/lib/papermill-agent/storage.rb +26 -0
- data/spec/agent_spec.rb +92 -0
- data/spec/integration/rails2_spec.rb +0 -0
- data/spec/integration/rails3_spec.rb +0 -0
- data/spec/rack/collector_spec.rb +109 -0
- data/spec/response_parser_spec.rb +27 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/storage_spec.rb +34 -0
- metadata +106 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Alex Sharp
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
WARNING: This is pre-alpha software. Use at your own risk until 1.0.
|
3
|
+
|
4
|
+
The papermill agent parses responses from your web application.
|
5
|
+
|
6
|
+
## Usage Instructions
|
7
|
+
|
8
|
+
Papermill works via a middleware called the Collector.
|
9
|
+
|
10
|
+
#### In Rails
|
11
|
+
# in config/environment.rb
|
12
|
+
config.middleware.use 'Papermill::Collector'
|
13
|
+
|
14
|
+
#### In Sinatra/rack
|
15
|
+
# in config.ru
|
16
|
+
use Papermill::Collector
|
17
|
+
|
18
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
token: api-key
|
data/doc/ideas.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.dirname(File.expand_path(__FILE__))
|
3
|
+
|
4
|
+
module Papermill
|
5
|
+
autoload :Agent, 'papermill-agent/agent'
|
6
|
+
autoload :Collector, 'papermill-agent/rack/collector'
|
7
|
+
autoload :ResponseParser, 'papermill-agent/response_parser'
|
8
|
+
autoload :Storage, 'papermill-agent/storage'
|
9
|
+
|
10
|
+
module ResponseAdapters
|
11
|
+
autoload :Base, 'papermill-agent/response_adapters/base'
|
12
|
+
autoload :Rails, 'papermill-agent/response_adapters/rails'
|
13
|
+
autoload :Sinatra, 'papermill-agent/response_adapters/sinatra'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Papermill::Agent.instance.start
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'timeout'
|
3
|
+
require 'json'
|
4
|
+
require 'restclient'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
module Papermill
|
8
|
+
|
9
|
+
class Agent
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
# papermill endpoint which will receive client requests
|
13
|
+
API_ENDPOINT = 'http://api.papermillapp.com'
|
14
|
+
|
15
|
+
# send new request data every 10 seconds
|
16
|
+
UPDATE_INTERVAL = 10
|
17
|
+
|
18
|
+
attr_reader :last_sent, :mutex, :config
|
19
|
+
|
20
|
+
def start
|
21
|
+
@last_sent = Time.now
|
22
|
+
@mutex = Mutex.new
|
23
|
+
@config = YAML.load_file('config/papermill.yml')
|
24
|
+
Thread.abort_on_exception = true
|
25
|
+
|
26
|
+
@worker_thread = Thread.new do
|
27
|
+
loop do
|
28
|
+
if time_since_last_sent > UPDATE_INTERVAL
|
29
|
+
p "sending #{Storage.store.count} requests to papermill..."
|
30
|
+
send_data_to_papermill
|
31
|
+
@last_sent = Time.now
|
32
|
+
end
|
33
|
+
sleep_time = seconds_until_next_run
|
34
|
+
sleep sleep_time if sleep_time > 0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def time_since_last_sent
|
40
|
+
Time.now - last_sent
|
41
|
+
end
|
42
|
+
|
43
|
+
def seconds_until_next_run
|
44
|
+
UPDATE_INTERVAL - time_since_last_sent
|
45
|
+
end
|
46
|
+
|
47
|
+
def send_data_to_papermill
|
48
|
+
begin
|
49
|
+
Timeout.timeout(9) { do_request unless Storage.store.empty? }
|
50
|
+
rescue Timeout::Error
|
51
|
+
p 'timeout error'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def do_request
|
56
|
+
begin
|
57
|
+
RestClient.post API_ENDPOINT, { :api_key => config['token'], :payload => jsonify_payload }
|
58
|
+
rescue RestClient::Exception, Errno::ECONNREFUSED
|
59
|
+
p 'transmission error ocurred...'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return a json version of the storage array.
|
64
|
+
# Also, we'll clear out the storage here
|
65
|
+
#
|
66
|
+
# TODO: move this to the Storage class.
|
67
|
+
def jsonify_payload
|
68
|
+
mutex.synchronize do
|
69
|
+
json_data = JSON.generate(Storage.store.flatten)
|
70
|
+
Storage.clear
|
71
|
+
return json_data
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
module Papermill
|
3
|
+
|
4
|
+
# The Collector class is the middleware component of papermill.
|
5
|
+
# To use it with a rails app, you must add the following to
|
6
|
+
# your environment.rb file:
|
7
|
+
# config.middleware.use Papermill::Collector
|
8
|
+
class Collector
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
status, headers, response = @app.call(env)
|
15
|
+
ResponseParser.parse(status, headers, response, env)
|
16
|
+
[status, headers, response]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
module Papermill
|
3
|
+
module ResponseAdapters
|
4
|
+
|
5
|
+
class Base
|
6
|
+
attr_reader :status, :headers, :response, :env
|
7
|
+
def initialize(*args)
|
8
|
+
@status, @headers, @response, @env = args.flatten
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse
|
12
|
+
parsed_response = { :headers => headers.merge(env), :status => status }
|
13
|
+
# if @status != 304 && @response #&& !@response.body.is_a?(Proc)
|
14
|
+
# parsed_response.merge!(prepare_extra_fields)
|
15
|
+
# end
|
16
|
+
parsed_response.merge!(additional_response_data)
|
17
|
+
Papermill::Storage.store << parsed_response
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def additional_response_data
|
22
|
+
{:request_time => Time.now}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module Papermill
|
3
|
+
|
4
|
+
class ResponseParser
|
5
|
+
# ENV_CAPTURE_FIELDS = [
|
6
|
+
# 'PATH_INFO', 'QUERY_STRING', 'REMOTE_ADDR', 'REMOTE_HOST', 'REQUEST_METHOD',
|
7
|
+
# 'REQUEST_URI', 'SCRIPT_NAME', 'SERVER_NAME', 'SERVER_SOFTWARE', 'HTTP_HOST',
|
8
|
+
# 'HTTP_ACCEPT', 'HTTP_USER_AGENT', 'REQUEST_PATH'
|
9
|
+
# ]
|
10
|
+
|
11
|
+
def self.parse(status, headers, response, env = {})
|
12
|
+
klass = if defined?(Rails)
|
13
|
+
ResponseAdapters::Rails
|
14
|
+
elsif defined?(Sinatra)
|
15
|
+
ResponseAdapters::Sinatra
|
16
|
+
else
|
17
|
+
ResponseAdapters::Base
|
18
|
+
end
|
19
|
+
|
20
|
+
klass.new(status, headers, response, env).parse
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Papermill
|
4
|
+
|
5
|
+
# The Storage class inherits from Array, and thus can be used in a very simple
|
6
|
+
# and predictable way. This is used by the Collector to save requests until
|
7
|
+
# they are ready to be sent to the remote server, which is handled by the
|
8
|
+
# Agent class.
|
9
|
+
class Storage < Array
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# TODO: We need a mutex sync around adding items to storage
|
14
|
+
|
15
|
+
def store
|
16
|
+
instance
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear
|
20
|
+
store.clear
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
data/spec/agent_spec.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Papermill
|
4
|
+
|
5
|
+
describe 'the agent' do
|
6
|
+
it 'has a mutex' do
|
7
|
+
Agent.instance.mutex.should be_instance_of Mutex
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'when a new process is started' do
|
11
|
+
context 'when an agent process does not already exist' do
|
12
|
+
it 'should create a new agent process' do
|
13
|
+
Papermill::Agent.instance.should be_instance_of Papermill::Agent
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when an agent process already exists' do
|
18
|
+
it 'should only allow one agent instance to exist' do
|
19
|
+
Papermill::Agent.instance.should eql Papermill::Agent.instance
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when attempting to initialize the agent directly' do
|
25
|
+
it 'raises a NoMethodError for attempting to initialize a singleton' do
|
26
|
+
lambda {
|
27
|
+
Papermill::Agent.initialize
|
28
|
+
}.should raise_error(NoMethodError, /private method `initialize' called/)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'the api endpoint' do
|
35
|
+
subject { Agent::API_ENDPOINT }
|
36
|
+
it { should == 'http://api.papermillapp.com' }
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'determining the time since the last time data was sent to papermill' do
|
40
|
+
before do
|
41
|
+
Agent.instance.stub!(:last_sent => Time.mktime(2010, 11, 11, 0, 0, 0))
|
42
|
+
Time.stub!(:now => Time.mktime(2010, 11, 11, 0, 0, 9))
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'time since the last run' do
|
46
|
+
it 'subtracts the current time from @last_sent' do
|
47
|
+
Agent.instance.time_since_last_sent.should == 9
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'the time until the next run' do
|
52
|
+
it 'time until next = interval - time elapsed since last' do
|
53
|
+
Agent.instance.seconds_until_next_run.should == 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'configuration' do
|
60
|
+
it 'provides access to the api token' do
|
61
|
+
Agent.instance.config['token'].should == 'api-key'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'sending data to papermill' do
|
66
|
+
before do
|
67
|
+
Storage.store << [{:headers => {'Content-Type' => 'text/html'}, :status => 200}]
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'jsonifies the payload data' do
|
71
|
+
Agent.instance.jsonify_payload.should == '[{"headers":{"Content-Type":"text/html"},"status":200}]'
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'empties the store' do
|
75
|
+
Agent.instance.jsonify_payload
|
76
|
+
Storage.store.should be_empty
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'sends a request to the papermill api endpoint' do
|
80
|
+
RestClient.should_receive(:post).with(Agent::API_ENDPOINT, :api_key => 'api-key', :payload => '[{"headers":{"Content-Type":"text/html"},"status":200}]')
|
81
|
+
Agent.instance.send_data_to_papermill
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should not send anything if no requests have been stored' do
|
85
|
+
Storage.clear
|
86
|
+
Agent.should_not_receive(:do_request)
|
87
|
+
Agent.instance.send_data_to_papermill
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
File without changes
|
File without changes
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sinatra'
|
3
|
+
|
4
|
+
|
5
|
+
module Papermill
|
6
|
+
|
7
|
+
describe 'collecting statistics via the middleware layer' do
|
8
|
+
before do
|
9
|
+
@app = Sinatra.new Sinatra::Application do
|
10
|
+
get '/' do
|
11
|
+
'<html><body>Hello, world!</body></html>'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
@app.use Papermill::Collector
|
15
|
+
|
16
|
+
request_headers = {'HTTP_USER_AGENT' => 'agent', 'REMOTE_ADDR' => '0.0.0.0',
|
17
|
+
'QUERY_STRING' => 'q=search',
|
18
|
+
'SCRIPT_NAME' => 'my-script',
|
19
|
+
'HTTP_ACCEPT' => 'text/html, application/json',
|
20
|
+
'HTTP_HOST' => 'localhost',
|
21
|
+
'REQUEST_URI' => 'i-requested-this',
|
22
|
+
'REMOTE_HOST' => '123.345.34.2',
|
23
|
+
'SERVER_SOFTWARE' => 'apache'
|
24
|
+
}
|
25
|
+
|
26
|
+
Time.stub(:now => Time.utc(2010, 01, 01))
|
27
|
+
@status, @headers, @body = @app.call(Rack::MockRequest.env_for('/', request_headers))
|
28
|
+
end
|
29
|
+
|
30
|
+
def last_store
|
31
|
+
Storage.store.last
|
32
|
+
end
|
33
|
+
|
34
|
+
def headers
|
35
|
+
last_store[:headers]
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'records the query string' do
|
39
|
+
last_store[:status].should == 200
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'records request duration'
|
43
|
+
|
44
|
+
it 'records the request time' do
|
45
|
+
last_store[:request_time].should == Time.utc(2010, 01, 01)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'records the path info' do
|
49
|
+
headers['PATH_INFO'].should == '/'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'records the request uri' do
|
53
|
+
headers['REQUEST_URI'].should == 'i-requested-this'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'records the query remote addr' do
|
57
|
+
headers['REMOTE_ADDR'].should == '0.0.0.0'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'records the server name' do
|
61
|
+
headers['SERVER_NAME'].should == 'example.org'
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'records the query string' do
|
65
|
+
headers['QUERY_STRING'].should == 'q=search'
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'records the request method' do
|
69
|
+
headers['REQUEST_METHOD'].should == 'GET'
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'records the script name' do
|
73
|
+
headers['SCRIPT_NAME'].should == 'my-script'
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'records the user agent' do
|
77
|
+
headers['HTTP_USER_AGENT'].should == 'agent'
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'records the http accept types' do
|
81
|
+
headers['HTTP_ACCEPT'].should == 'text/html, application/json'
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'records the http host' do
|
85
|
+
headers['HTTP_HOST'].should == 'localhost'
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'records the server software' do
|
89
|
+
headers['SERVER_SOFTWARE'].should == 'apache'
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'records the remote host' do
|
93
|
+
headers['REMOTE_HOST'].should == '123.345.34.2'
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'records the url scheme' do
|
97
|
+
headers['rack.url_scheme'].should == 'http'
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'for a 304 response' do
|
101
|
+
it 'only records certain things'
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'for a streamed file response' do
|
105
|
+
it 'only records certain things'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Papermill
|
4
|
+
|
5
|
+
describe '.parse' do
|
6
|
+
before do
|
7
|
+
ResponseAdapters::Base.stub(:new).and_return(mock('object', :parse => nil))
|
8
|
+
end
|
9
|
+
context 'in a normal rack app' do
|
10
|
+
it 'uses the base adapter' do
|
11
|
+
ResponseAdapters::Base.should_receive(:new).and_return(mock('object', :parse => nil))
|
12
|
+
ResponseParser.parse(200, {}, [], {})
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'in a rails app' do
|
17
|
+
class Rails
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'uses the rails adapter' do
|
21
|
+
ResponseAdapters::Rails.should_receive(:new).and_return(mock('object', :parse => nil))
|
22
|
+
ResponseParser.parse(200, {}, [], {})
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
ENV['RACK_ENV'] = 'test'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
|
6
|
+
Bundler.require :default, :test
|
7
|
+
|
8
|
+
$:.unshift File.dirname(File.expand_path(__FILE__))
|
9
|
+
$:.unshift File.dirname(File.expand_path(__FILE__) + '/../lib')
|
10
|
+
|
11
|
+
require 'papermill-agent'
|
12
|
+
require 'fakeweb'
|
13
|
+
|
14
|
+
FakeWeb.allow_net_connect = false
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.before(:each) do
|
18
|
+
# clear the storage before each example
|
19
|
+
Papermill::Storage.clear
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Papermill
|
4
|
+
|
5
|
+
describe 'adding an item to the local data storage' do
|
6
|
+
it 'acts like an array' do
|
7
|
+
Storage.store.should respond_to(:<<)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'stores data' do
|
11
|
+
Storage.store << 'some data'
|
12
|
+
Storage.store.should include 'some data'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'the store' do
|
17
|
+
it 'wraps the singleton instance' do
|
18
|
+
Storage.store == Storage.instance
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'emptying the cache' do
|
23
|
+
before { Storage.clear }
|
24
|
+
|
25
|
+
it 'clears out the storage' do
|
26
|
+
Storage.store << 'stuff'
|
27
|
+
Storage.store.should == ['stuff']
|
28
|
+
|
29
|
+
Storage.clear
|
30
|
+
Storage.store.should == []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: papermill-agent
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Alex Sharp
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-11-19 00:00:00 -08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rest-client
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 6
|
31
|
+
- 1
|
32
|
+
version: 1.6.1
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: The client agent for papermillapp.com
|
36
|
+
email:
|
37
|
+
- ajsharp@gmail.com
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- README.md
|
44
|
+
- doc/ideas.md
|
45
|
+
files:
|
46
|
+
- lib/papermill-agent/agent.rb
|
47
|
+
- lib/papermill-agent/rack/collector.rb
|
48
|
+
- lib/papermill-agent/response_adapters/base.rb
|
49
|
+
- lib/papermill-agent/response_adapters/rails.rb
|
50
|
+
- lib/papermill-agent/response_adapters/sinatra.rb
|
51
|
+
- lib/papermill-agent/response_parser.rb
|
52
|
+
- lib/papermill-agent/storage.rb
|
53
|
+
- lib/papermill-agent.rb
|
54
|
+
- LICENSE
|
55
|
+
- README.md
|
56
|
+
- config/papermill.sample.yml
|
57
|
+
- Gemfile
|
58
|
+
- Rakefile
|
59
|
+
- doc/ideas.md
|
60
|
+
- spec/agent_spec.rb
|
61
|
+
- spec/integration/rails2_spec.rb
|
62
|
+
- spec/integration/rails3_spec.rb
|
63
|
+
- spec/rack/collector_spec.rb
|
64
|
+
- spec/response_parser_spec.rb
|
65
|
+
- spec/spec_helper.rb
|
66
|
+
- spec/storage_spec.rb
|
67
|
+
has_rdoc: true
|
68
|
+
homepage: http://github.com/ajsharp/papermill-agent
|
69
|
+
licenses: []
|
70
|
+
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 1.3.7
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: The client agent for papermillapp.com
|
99
|
+
test_files:
|
100
|
+
- spec/agent_spec.rb
|
101
|
+
- spec/integration/rails2_spec.rb
|
102
|
+
- spec/integration/rails3_spec.rb
|
103
|
+
- spec/rack/collector_spec.rb
|
104
|
+
- spec/response_parser_spec.rb
|
105
|
+
- spec/spec_helper.rb
|
106
|
+
- spec/storage_spec.rb
|