papermill-agent 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
- gem 'rest-client', '~>1.6.1'
3
+ gemspec
4
4
 
5
5
  group :test do
6
6
  gem 'fakeweb', '>=1.3.0'
data/README.md CHANGED
@@ -3,16 +3,40 @@ WARNING: This is pre-alpha software. Use at your own risk until 1.0.
3
3
 
4
4
  The papermill agent parses responses from your web application.
5
5
 
6
- ## Usage Instructions
6
+ # Installation #
7
7
 
8
- Papermill works via a middleware called the Collector.
8
+ ## Step 1: Create config/papermill.yml ##
9
9
 
10
- #### In Rails
10
+ Papermill's configuration information is stored in a yml file located at
11
+ `[PROJECT_ROOT]/config/papermill.yml`. The only required configuration
12
+ option is your api token:
13
+
14
+ # config/papermill.yml
15
+ token: you-api-token
16
+
17
+ ## Step 2: include the middleware layer for request capturing and logging ##
18
+
19
+ Papermill works by capturing every request submitted to your application and
20
+ periodically sending them off to the PapermillApp servers. To enable this,
21
+ you must add the Papermill middleware class to your application's middleware
22
+ stack.
23
+
24
+ In rails, this is done in either `config/environment.rb` or `config/application.rb`,
25
+ depending on whether you're using rails 2 or rails 3. In a sinatra or rack app,
26
+ you simply add the middleware via rack's `use` method.
27
+
28
+ See the examples below.
29
+
30
+ ### rails 2.x ###
11
31
  # in config/environment.rb
12
32
  config.middleware.use 'Papermill::Collector'
13
33
 
14
- #### In Sinatra/rack
15
- # in config.ru
34
+ ### rails 3 ###
35
+ # in config/application.rb
36
+ config.middleware.use 'Papermill::Collector'
37
+
38
+ ### sinatra or a rack application ###
39
+ # in config.ru or in a Rack::Builder stack
16
40
  use Papermill::Collector
17
41
 
18
42
 
@@ -3,7 +3,7 @@ $:.unshift File.dirname(File.expand_path(__FILE__))
3
3
 
4
4
  module Papermill
5
5
  # papermill endpoint which will receive client requests
6
- API_ENDPOINT = 'http://api.papermillapp.com'
6
+ API_ENDPOINT = 'http://papermillapp.com/api'
7
7
 
8
8
  autoload :Agent, 'papermill-agent/agent'
9
9
  autoload :Collector, 'papermill-agent/rack/collector'
@@ -12,6 +12,7 @@ module Papermill
12
12
  UPDATE_INTERVAL = 10
13
13
 
14
14
  attr_reader :last_sent, :mutex, :config, :logger
15
+ attr_accessor :disabled
15
16
 
16
17
  # request timeout threshold for sending data to papermill
17
18
  @@request_timeout = 5
@@ -19,22 +20,28 @@ module Papermill
19
20
  @@request_timeout
20
21
  end
21
22
 
23
+ def disabled?
24
+ !!disabled
25
+ end
26
+
22
27
  def start
23
28
  @last_sent = Time.now
24
29
  @mutex = Mutex.new
25
- @config = Configurator.new
26
30
  @logger = Logger.new
31
+ @config = Configurator.new
27
32
  logger.info "\n\n=============== Starting papermill-agent at #{Time.now} in process #{Process.pid}"
28
33
  Thread.abort_on_exception = true
29
34
 
30
- @worker_thread = Thread.new do
31
- loop do
32
- if time_since_last_sent > UPDATE_INTERVAL
33
- send_data_to_papermill
34
- @last_sent = Time.now
35
+ unless disabled?
36
+ @worker_thread = Thread.new do
37
+ loop do
38
+ if time_since_last_sent > UPDATE_INTERVAL
39
+ send_data_to_papermill
40
+ @last_sent = Time.now
41
+ end
42
+ sleep_time = seconds_until_next_run
43
+ sleep sleep_time if sleep_time > 0
35
44
  end
36
- sleep_time = seconds_until_next_run
37
- sleep sleep_time if sleep_time > 0
38
45
  end
39
46
  end
40
47
  end
@@ -7,19 +7,33 @@ module Papermill
7
7
 
8
8
  def initialize(config_source = 'config/papermill.yml')
9
9
  load_config(config_source)
10
+ after_init
10
11
  end
11
12
 
13
+ # Loads the papermill configuration.
14
+ #
15
+ # By default, the papermill configuration is read from a yaml file located
16
+ # at ./config/papermill.yml. Still, the configuration source can either be
17
+ # a yaml file of any name or a ruby hash.
18
+ #
19
+ # If the configuration file cannot be found, the agent will be disabled.
12
20
  def load_config(config_source)
13
21
  @config = case
14
22
  when config_source.class == String
15
- YAML.load_file(config_source)
23
+ begin
24
+ YAML.load_file(config_source)
25
+ rescue Errno::ENOENT => e
26
+ disable_agent
27
+ Agent.instance.logger.info "The papermill configuration file does not exist. The papermill agent is disabled."
28
+ return nil
29
+ end
16
30
  when config_source.class == Hash
17
31
  config_source
18
32
  end
19
33
  end
20
34
 
21
35
  def token
22
- config['token']
36
+ (config && config.has_key?('token')) ? config['token'] : nil
23
37
  end
24
38
 
25
39
  def endpoint
@@ -50,6 +64,16 @@ module Papermill
50
64
  end
51
65
 
52
66
  private
67
+ # If there is no token, disable the agent.
68
+ def after_init
69
+ if token.nil?
70
+ disable_agent
71
+ end
72
+ end
73
+
74
+ def disable_agent
75
+ Agent.instance.disabled = true
76
+ end
53
77
 
54
78
  # holds the internal config hash
55
79
  def config
@@ -3,7 +3,7 @@ require 'logger'
3
3
  module Papermill
4
4
  class Logger < ::Logger
5
5
  def initialize(io_object = 'log/papermill.log')
6
- FileUtils.mkdir('log') unless Dir.exists?('log')
6
+ FileUtils.mkdir('log') unless File.exists?('log')
7
7
  super(io_object)
8
8
  end
9
9
 
@@ -4,6 +4,7 @@ module Papermill
4
4
  # The Collector class is the middleware component of papermill.
5
5
  # To use it with a rails app, you must add the following to
6
6
  # your environment.rb file:
7
+ #
7
8
  # config.middleware.use Papermill::Collector
8
9
  class Collector
9
10
  def initialize(app)
@@ -0,0 +1,14 @@
1
+ require 'papermill-agent'
2
+ require 'rails'
3
+
4
+ module Papermill
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ require 'papermill-agent/tasks'
8
+ end
9
+
10
+ # initializer "..." do |app|
11
+ # app.config.middleware.use "Papermill::Collector"
12
+ # end
13
+ end
14
+ end
@@ -30,11 +30,15 @@ module Papermill
30
30
  end
31
31
 
32
32
  def extract_env_info
33
- result_hash = env.select { |key, value| ENV_CAPTURE_FIELDS.include?(key) }
33
+ result_hash = {}
34
+
35
+ # Hash#select returns an array in 1.8, so we have to do some manual
36
+ # iteration to extract the key/value pairs from the environment hash.
37
+ ENV_CAPTURE_FIELDS.each { |key| result_hash[key] = env[key] }
34
38
 
35
39
  # rename rack.* keys b/c mongo does not allow key names containing a .
36
- rack_keys = ENV_CAPTURE_FIELDS.select { |key| key =~ /^rack\./ }
37
- rack_keys.each do |key|
40
+ naughty_rack_keys = ENV_CAPTURE_FIELDS.select { |key| key =~ /^rack\./ }
41
+ naughty_rack_keys.each do |key|
38
42
  new_key = key.gsub(/^rack\./, '').upcase
39
43
  result_hash[new_key] = result_hash.delete(key)
40
44
  end
@@ -0,0 +1,21 @@
1
+ require 'papermill-agent'
2
+
3
+ namespace :papermill do
4
+ desc "Generate a papermill configuration file"
5
+ task :create_config do
6
+ require 'yaml'
7
+
8
+ # don't overwrite existing configuration files
9
+ if File.exists?('./config/papermil.yml')
10
+ fail("A papermill configuration file already exists. Please remove it and try again.")
11
+ end
12
+
13
+ token = ENV['TOKEN'] || 'replace-with-your-token'
14
+
15
+ File.open('./config/papermill.yml', 'w') do |file|
16
+ config = {'token' => token}
17
+ YAML.dump(config, file)
18
+ end
19
+ STDOUT.puts "Your papermill configuration file has been created at config/papermill.yml."
20
+ end
21
+ end
data/spec/agent_spec.rb CHANGED
@@ -147,7 +147,7 @@ module Papermill
147
147
 
148
148
  describe 'the api endpoint' do
149
149
  subject { Papermill::API_ENDPOINT }
150
- it { should == 'http://api.papermillapp.com' }
150
+ it { should == 'http://papermillapp.com/api' }
151
151
  end
152
152
 
153
153
  describe 'request timeout interval' do
@@ -188,7 +188,7 @@ module Papermill
188
188
 
189
189
  describe 'serializing the payload' do
190
190
  it 'jsonifies the payload data' do
191
- agent.jsonify_payload.should == '[{"headers":{"Content-Type":"text/html"},"status":200}]'
191
+ agent.jsonify_payload.should == Storage.store.flatten.to_json
192
192
  end
193
193
  end
194
194
 
@@ -201,7 +201,7 @@ module Papermill
201
201
  end
202
202
 
203
203
  it 'sends a request to the papermill api endpoint' do
204
- RestClient.should_receive(:post).with(Papermill::API_ENDPOINT, :token => 'api-key', :payload => '[{"headers":{"Content-Type":"text/html"},"status":200}]')
204
+ RestClient.should_receive(:post).with(Papermill::API_ENDPOINT, :token => 'api-key', :payload => Storage.store.flatten.to_json)
205
205
  agent.send_data_to_papermill
206
206
  end
207
207
 
@@ -234,5 +234,30 @@ module Papermill
234
234
  end
235
235
  end
236
236
 
237
+ describe 'disabling the papermill agent' do
238
+ context 'when disabled is set to true' do
239
+ before { agent.disabled = true }
240
+ after { agent.disabled = false }
241
+
242
+ it 'is disabled' do
243
+ agent.should be_disabled
244
+ end
245
+ end
246
+
247
+ context 'wen disabled is set to false' do
248
+ before { agent.disabled = false }
249
+
250
+ it 'is disabled' do
251
+ agent.should_not be_disabled
252
+ end
253
+ end
254
+
255
+ context 'by default' do
256
+ it 'is not disabled' do
257
+ agent.should_not be_disabled
258
+ end
259
+ end
260
+ end
261
+
237
262
  end
238
263
 
@@ -8,6 +8,35 @@ module Papermill
8
8
  YAML.should_receive(:load_file).with('filename')
9
9
  Configurator.new('filename')
10
10
  end
11
+
12
+ context 'the config file does not exist' do
13
+ after { agent.disabled = false }
14
+
15
+ it 'disables the agent' do
16
+ lambda {
17
+ Configurator.new('does not exist')
18
+ }.should change(agent, :disabled?).to(true)
19
+ end
20
+
21
+ it 'does not raise a file load error' do
22
+ lambda {
23
+ Configurator.new('does not exist')
24
+ }.should_not raise_error(Errno::ENOENT)
25
+ end
26
+
27
+ it 'sets config to nil' do
28
+ config = Configurator.new('does not exist')
29
+ config.send(:config).should be_nil
30
+ end
31
+ end
32
+
33
+ context 'when no token key is found' do
34
+ it 'disables the agent' do
35
+ lambda {
36
+ Configurator.new({})
37
+ }.should change(agent, :disabled?).to(true)
38
+ end
39
+ end
11
40
  end
12
41
 
13
42
  context 'when a hash is passed in' do
@@ -22,14 +51,19 @@ module Papermill
22
51
  end
23
52
 
24
53
  describe 'given a configurator instance' do
25
- let(:configurator) do
26
- YAML.stub!(:load_file).and_return({'token' => 'token'})
27
- Configurator.new
28
- end
29
-
30
54
  describe 'the token' do
31
- it 'should return the token from the config' do
32
- configurator.token.should == 'token'
55
+ subject { config.token }
56
+
57
+ context 'when the token exists' do
58
+ let(:config) { Configurator.new({'token' => 'token'}) }
59
+
60
+ it { should == 'token' }
61
+ end
62
+
63
+ context 'when the token does not exist' do
64
+ let(:config) { Configurator.new({}) }
65
+
66
+ it { should be_nil }
33
67
  end
34
68
  end
35
69
 
@@ -159,8 +193,9 @@ module Papermill
159
193
  end
160
194
 
161
195
  describe 'retrieving a setting for the current environment' do
196
+ let(:config) { Configurator.new({'production' => {'my key' => 'my val'}}) }
197
+
162
198
  context 'for a given environment' do
163
- let(:config) { Configurator.new({'production' => {'my key' => 'my val'}}) }
164
199
  before { config.environment = 'production' }
165
200
 
166
201
  context 'for a setting that has a value' do
@@ -172,9 +207,31 @@ module Papermill
172
207
  context 'for a setting that does not have a value' do
173
208
  it 'returns nil' do
174
209
  config.setting('does not exist').should == nil
210
+ config.setting('does not exist').should === nil
211
+ config.setting('does not exist').should be_instance_of NilClass
212
+ end
213
+
214
+ it 'does not return false' do
215
+ config.setting('does not exist').should_not === false
216
+ config.setting('does not exist').should_not == false
217
+ config.setting('does not exist').should_not === FalseClass
218
+ config.setting('does not exist').should_not be_instance_of FalseClass
175
219
  end
176
220
  end
177
221
  end
178
222
 
223
+ context 'when trying to access a property with no environment' do
224
+ before { config.environment = 'not defined' }
225
+ subject { config.setting('does not exist') }
226
+
227
+ it 'should not return false' do
228
+ subject.should_not == false
229
+ end
230
+
231
+ it 'should return nil' do
232
+ subject.should == nil
233
+ end
234
+ end
235
+
179
236
  end
180
237
  end
@@ -14,14 +14,12 @@ module Papermill
14
14
  end
15
15
 
16
16
  context 'in a rails app' do
17
- before do
18
- # self.class.module_eval { remove_const(:Sinatra) if defined?(Sinatra) }
17
+ class Rails
19
18
  end
20
19
 
21
20
  it 'uses the rails adapter' do
22
- class Rails
23
- end
24
- ResponseParser.parse_klass.should == ResponseAdapters::Rails
21
+ ResponseAdapters::Rails.should_receive(:new).and_return(mock('object', :parse => nil))
22
+ ResponseParser.parse(200, {}, [], {})
25
23
  end
26
24
  end
27
25
  end
data/spec/spec_helper.rb CHANGED
@@ -28,4 +28,8 @@ RSpec.configure do |config|
28
28
  # clear the storage before each example
29
29
  Papermill::Storage.clear
30
30
  end
31
+
32
+ config.after(:each) do
33
+ agent.disabled = false
34
+ end
31
35
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 1
7
8
  - 0
8
- - 2
9
- version: 0.0.2
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Alex Sharp
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-01-02 00:00:00 -06:00
17
+ date: 2011-01-07 00:00:00 -08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -32,6 +32,19 @@ dependencies:
32
32
  version: 1.6.1
33
33
  type: :runtime
34
34
  version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: json_pure
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
35
48
  description: The client agent for papermillapp.com
36
49
  email:
37
50
  - ajsharp@gmail.com
@@ -47,11 +60,13 @@ files:
47
60
  - lib/papermill-agent/configurator.rb
48
61
  - lib/papermill-agent/logger.rb
49
62
  - lib/papermill-agent/rack/collector.rb
63
+ - lib/papermill-agent/railtie.rb
50
64
  - lib/papermill-agent/response_adapters/base.rb
51
65
  - lib/papermill-agent/response_adapters/rails.rb
52
66
  - lib/papermill-agent/response_adapters/sinatra.rb
53
67
  - lib/papermill-agent/response_parser.rb
54
68
  - lib/papermill-agent/storage.rb
69
+ - lib/papermill-agent/tasks.rb
55
70
  - lib/papermill-agent.rb
56
71
  - LICENSE
57
72
  - README.md