projectlocker_pulse 0.2.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.
Files changed (82) hide show
  1. data/CHANGELOG +26 -0
  2. data/Gemfile +3 -0
  3. data/Guardfile +6 -0
  4. data/INSTALL +20 -0
  5. data/MIT-LICENSE +23 -0
  6. data/README.md +439 -0
  7. data/README_FOR_HEROKU_ADDON.md +89 -0
  8. data/Rakefile +223 -0
  9. data/SUPPORTED_RAILS_VERSIONS +38 -0
  10. data/TESTING.md +41 -0
  11. data/features/metal.feature +18 -0
  12. data/features/rack.feature +60 -0
  13. data/features/rails.feature +272 -0
  14. data/features/rails_with_js_notifier.feature +97 -0
  15. data/features/rake.feature +27 -0
  16. data/features/sinatra.feature +29 -0
  17. data/features/step_definitions/file_steps.rb +10 -0
  18. data/features/step_definitions/metal_steps.rb +23 -0
  19. data/features/step_definitions/rack_steps.rb +23 -0
  20. data/features/step_definitions/rails_application_steps.rb +478 -0
  21. data/features/step_definitions/rake_steps.rb +17 -0
  22. data/features/support/env.rb +18 -0
  23. data/features/support/matchers.rb +35 -0
  24. data/features/support/projectlocker_pulse_shim.rb.template +16 -0
  25. data/features/support/rails.rb +201 -0
  26. data/features/support/rake/Rakefile +68 -0
  27. data/features/support/terminal.rb +107 -0
  28. data/features/user_informer.feature +63 -0
  29. data/generators/pulse/lib/insert_commands.rb +34 -0
  30. data/generators/pulse/lib/rake_commands.rb +24 -0
  31. data/generators/pulse/pulse_generator.rb +94 -0
  32. data/generators/pulse/templates/capistrano_hook.rb +6 -0
  33. data/generators/pulse/templates/initializer.rb +6 -0
  34. data/generators/pulse/templates/pulse_tasks.rake +25 -0
  35. data/install.rb +1 -0
  36. data/lib/projectlocker_pulse.rb +159 -0
  37. data/lib/pulse/backtrace.rb +108 -0
  38. data/lib/pulse/capistrano.rb +43 -0
  39. data/lib/pulse/configuration.rb +305 -0
  40. data/lib/pulse/notice.rb +390 -0
  41. data/lib/pulse/rack.rb +54 -0
  42. data/lib/pulse/rails/action_controller_catcher.rb +30 -0
  43. data/lib/pulse/rails/controller_methods.rb +85 -0
  44. data/lib/pulse/rails/error_lookup.rb +33 -0
  45. data/lib/pulse/rails/javascript_notifier.rb +47 -0
  46. data/lib/pulse/rails/middleware/exceptions_catcher.rb +33 -0
  47. data/lib/pulse/rails.rb +40 -0
  48. data/lib/pulse/rails3_tasks.rb +99 -0
  49. data/lib/pulse/railtie.rb +49 -0
  50. data/lib/pulse/rake_handler.rb +65 -0
  51. data/lib/pulse/sender.rb +128 -0
  52. data/lib/pulse/shared_tasks.rb +47 -0
  53. data/lib/pulse/tasks.rb +83 -0
  54. data/lib/pulse/user_informer.rb +27 -0
  55. data/lib/pulse/utils/blank.rb +53 -0
  56. data/lib/pulse/version.rb +3 -0
  57. data/lib/pulse_tasks.rb +64 -0
  58. data/lib/rails/generators/pulse/pulse_generator.rb +100 -0
  59. data/lib/templates/javascript_notifier.erb +15 -0
  60. data/lib/templates/rescue.erb +91 -0
  61. data/pulse.gemspec +39 -0
  62. data/rails/init.rb +1 -0
  63. data/resources/README.md +34 -0
  64. data/resources/ca-bundle.crt +3376 -0
  65. data/script/integration_test.rb +38 -0
  66. data/test/backtrace_test.rb +162 -0
  67. data/test/capistrano_test.rb +34 -0
  68. data/test/catcher_test.rb +333 -0
  69. data/test/configuration_test.rb +236 -0
  70. data/test/helper.rb +263 -0
  71. data/test/javascript_notifier_test.rb +51 -0
  72. data/test/logger_test.rb +79 -0
  73. data/test/notice_test.rb +490 -0
  74. data/test/notifier_test.rb +276 -0
  75. data/test/projectlocker_pulse_tasks_test.rb +170 -0
  76. data/test/pulse.xsd +88 -0
  77. data/test/rack_test.rb +58 -0
  78. data/test/rails_initializer_test.rb +36 -0
  79. data/test/recursion_test.rb +10 -0
  80. data/test/sender_test.rb +288 -0
  81. data/test/user_informer_test.rb +29 -0
  82. metadata +432 -0
@@ -0,0 +1,40 @@
1
+ require 'projectlocker_pulse'
2
+ require 'pulse/rails/controller_methods'
3
+ require 'pulse/rails/action_controller_catcher'
4
+ require 'pulse/rails/error_lookup'
5
+ require 'pulse/rails/javascript_notifier'
6
+
7
+ module Pulse
8
+ module Rails
9
+ def self.initialize
10
+ if defined?(ActionController::Base)
11
+ ActionController::Base.send(:include, Pulse::Rails::ActionControllerCatcher)
12
+ ActionController::Base.send(:include, Pulse::Rails::ErrorLookup)
13
+ ActionController::Base.send(:include, Pulse::Rails::ControllerMethods)
14
+ ActionController::Base.send(:include, Pulse::Rails::JavascriptNotifier)
15
+ end
16
+
17
+ rails_logger = if defined?(::Rails.logger)
18
+ ::Rails.logger
19
+ elsif defined?(RAILS_DEFAULT_LOGGER)
20
+ RAILS_DEFAULT_LOGGER
21
+ end
22
+
23
+ if defined?(::Rails.configuration) && ::Rails.configuration.respond_to?(:middleware)
24
+ ::Rails.configuration.middleware.insert_after 'ActionController::Failsafe',
25
+ Pulse::Rack
26
+ ::Rails.configuration.middleware.insert_after 'Rack::Lock',
27
+ Pulse::UserInformer
28
+ end
29
+
30
+ Pulse.configure(true) do |config|
31
+ config.logger = rails_logger
32
+ config.environment_name = defined?(::Rails.env) && ::Rails.env || defined?(RAILS_ENV) && RAILS_ENV
33
+ config.project_root = defined?(::Rails.root) && ::Rails.root || defined?(RAILS_ROOT) && RAILS_ROOT
34
+ config.framework = defined?(::Rails.version) && "Rails: #{::Rails.version}" || defined?(::Rails::VERSION::STRING) && "Rails: #{::Rails::VERSION::STRING}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ Pulse::Rails.initialize
@@ -0,0 +1,99 @@
1
+ require 'projectlocker_pulse'
2
+ require File.join(File.dirname(__FILE__), 'shared_tasks')
3
+
4
+ puts "\n\nLOADED RAILS3 TASKS\n\n"
5
+ namespace :pulse do
6
+ desc "Verify your gem installation by sending a test exception to the pulse service"
7
+ task :test => [:environment] do
8
+ Rails.logger = defined?(ActiveSupport::TaggedLogging) ?
9
+ ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) :
10
+ Logger.new(STDOUT)
11
+
12
+ def wait_for_threads
13
+ # if using multiple threads, we have to wait for
14
+ # them to finish
15
+ if GirlFriday.status.empty?
16
+ Thread.list.each do |thread|
17
+ thread.join unless thread == Thread.current
18
+ end
19
+ else
20
+ GirlFriday.shutdown!
21
+ end
22
+ end
23
+
24
+ Rails.logger.level = Logger::DEBUG
25
+ Pulse.configure(true) do |config|
26
+ config.logger = Rails.logger
27
+ end
28
+
29
+ require './app/controllers/application_controller'
30
+
31
+ class PulseTestingException < RuntimeError; end
32
+
33
+ unless Pulse.configuration.api_key
34
+ puts "Pulse needs an API key configured! Check the README to see how to add it."
35
+ exit
36
+ end
37
+
38
+ Pulse.configuration.development_environments = []
39
+
40
+ puts "Configuration:"
41
+ Pulse.configuration.to_hash.each do |key, value|
42
+ puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
43
+ end
44
+
45
+ unless defined?(ApplicationController)
46
+ puts "No ApplicationController found"
47
+ exit
48
+ end
49
+
50
+ puts 'Setting up the Controller.'
51
+ class ApplicationController
52
+ # This is to bypass any filters that may prevent access to the action.
53
+ prepend_before_filter :test_pulse
54
+ def test_pulse
55
+ puts "Raising '#{exception_class.name}' to simulate application failure."
56
+ raise exception_class.new, 'Testing Pulse via "rake pulse:test". If you can see this, it works.'
57
+ end
58
+
59
+ # def rescue_action(exception)
60
+ # rescue_action_in_public exception
61
+ # end
62
+
63
+ # Ensure we actually have an action to go to.
64
+ def verify; end
65
+
66
+ # def consider_all_requests_local
67
+ # false
68
+ # end
69
+
70
+ # def local_request?
71
+ # false
72
+ # end
73
+
74
+ def exception_class
75
+ exception_name = ENV['EXCEPTION'] || "PulseTestingException"
76
+ Object.const_get(exception_name)
77
+ rescue
78
+ Object.const_set(exception_name, Class.new(Exception))
79
+ end
80
+
81
+ def logger
82
+ nil
83
+ end
84
+ end
85
+ class PulseVerificationController < ApplicationController; end
86
+
87
+ Rails.application.routes_reloader.execute_if_updated
88
+ Rails.application.routes.draw do
89
+ match 'verify' => 'application#verify', :as => 'verify'
90
+ end
91
+
92
+ puts 'Processing request.'
93
+ env = Rack::MockRequest.env_for("/verify")
94
+
95
+ Rails.application.call(env)
96
+
97
+ wait_for_threads
98
+ end
99
+ end
@@ -0,0 +1,49 @@
1
+ require 'projectlocker_pulse'
2
+ require 'rails'
3
+
4
+ module Pulse
5
+ class Railtie < ::Rails::Railtie
6
+ rake_tasks do
7
+ require 'pulse/rake_handler'
8
+ require 'pulse/rails3_tasks'
9
+ #load "#{File.dirname(__FILE__)}/rails3_tasks.rb"
10
+ end
11
+
12
+ initializer "pulse.use_rack_middleware" do |app|
13
+ app.config.middleware.insert 0, "Pulse::UserInformer"
14
+ app.config.middleware.insert_after "Pulse::UserInformer","Pulse::Rack"
15
+ end
16
+
17
+ config.after_initialize do
18
+ Pulse.configure(true) do |config|
19
+ config.logger ||= config.async? ? ::Logger.new(STDERR) : ::Rails.logger
20
+ config.environment_name ||= ::Rails.env
21
+ config.project_root ||= ::Rails.root
22
+ config.framework = "Rails: #{::Rails::VERSION::STRING}"
23
+ end
24
+
25
+ ActiveSupport.on_load(:action_controller) do
26
+ # Lazily load action_controller methods
27
+ #
28
+ require 'pulse/rails/javascript_notifier'
29
+ require 'pulse/rails/controller_methods'
30
+
31
+ include Pulse::Rails::JavascriptNotifier
32
+ include Pulse::Rails::ControllerMethods
33
+ end
34
+
35
+ if defined?(::ActionDispatch::DebugExceptions)
36
+ # We should catch the exceptions in ActionDispatch::DebugExceptions in Rails 3.2.x.
37
+ #
38
+ require 'pulse/rails/middleware/exceptions_catcher'
39
+ ::ActionDispatch::DebugExceptions.send(:include,Pulse::Rails::Middleware::ExceptionsCatcher)
40
+ elsif defined?(::ActionDispatch::ShowExceptions)
41
+ # ActionDispatch::DebugExceptions is not defined in Rails 3.0.x and 3.1.x so
42
+ # catch the exceptions in ShowExceptions.
43
+ #
44
+ require 'pulse/rails/middleware/exceptions_catcher'
45
+ ::ActionDispatch::ShowExceptions.send(:include,Pulse::Rails::Middleware::ExceptionsCatcher)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,65 @@
1
+ # Patch Rake::Application to handle errors with Pulse
2
+ module Pulse::RakeHandler
3
+ def self.included(klass)
4
+ klass.class_eval do
5
+ include Rake087Methods unless defined?(Rake::VERSION) && Rake::VERSION >= '0.9.0'
6
+ alias_method :display_error_message_without_pulse, :display_error_message
7
+ alias_method :display_error_message, :display_error_message_with_pulse
8
+ end
9
+ end
10
+
11
+ def display_error_message_with_pulse(ex)
12
+ if Pulse.sender && Pulse.configuration &&
13
+ (Pulse.configuration.rescue_rake_exceptions ||
14
+ (Pulse.configuration.rescue_rake_exceptions===nil && !self.tty_output?))
15
+
16
+ Pulse.notify_or_ignore(ex, :component => reconstruct_command_line, :cgi_data => ENV)
17
+ end
18
+
19
+ display_error_message_without_pulse(ex)
20
+ end
21
+
22
+ def reconstruct_command_line
23
+ "rake #{ARGV.join( ' ' )}"
24
+ end
25
+
26
+ # This module brings Rake 0.8.7 error handling to 0.9.0 standards
27
+ module Rake087Methods
28
+ # Method taken from Rake 0.9.0 source
29
+ #
30
+ # Provide standard exception handling for the given block.
31
+ def standard_exception_handling
32
+ begin
33
+ yield
34
+ rescue SystemExit => ex
35
+ # Exit silently with current status
36
+ raise
37
+ rescue OptionParser::InvalidOption => ex
38
+ $stderr.puts ex.message
39
+ exit(false)
40
+ rescue Exception => ex
41
+ # Exit with error message
42
+ display_error_message(ex)
43
+ exit(false)
44
+ end
45
+ end
46
+
47
+ # Method extracted from Rake 0.8.7 source
48
+ def display_error_message(ex)
49
+ $stderr.puts "#{name} aborted!"
50
+ $stderr.puts ex.message
51
+ if options.trace
52
+ $stderr.puts ex.backtrace.join("\n")
53
+ else
54
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
55
+ $stderr.puts "(See full trace by running task with --trace)"
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ Rake.application.instance_eval do
62
+ class << self
63
+ include Pulse::RakeHandler
64
+ end
65
+ end
@@ -0,0 +1,128 @@
1
+ module Pulse
2
+ # Sends out the notice to Pulse
3
+ class Sender
4
+
5
+ NOTICES_URI = '/notifier_api/v2/notices/'.freeze
6
+ HTTP_ERRORS = [Timeout::Error,
7
+ Errno::EINVAL,
8
+ Errno::ECONNRESET,
9
+ EOFError,
10
+ Net::HTTPBadResponse,
11
+ Net::HTTPHeaderSyntaxError,
12
+ Net::ProtocolError,
13
+ Errno::ECONNREFUSED].freeze
14
+
15
+ def initialize(options = {})
16
+ [ :proxy_host,
17
+ :proxy_port,
18
+ :proxy_user,
19
+ :proxy_pass,
20
+ :protocol,
21
+ :host,
22
+ :port,
23
+ :secure,
24
+ :use_system_ssl_cert_chain,
25
+ :http_open_timeout,
26
+ :http_read_timeout
27
+ ].each do |option|
28
+ instance_variable_set("@#{option}", options[option])
29
+ end
30
+ end
31
+
32
+ # Sends the notice data off to Pulse for processing.
33
+ #
34
+ # @param [Notice or String] notice The notice to be sent off
35
+ def send_to_pulse(notice)
36
+ data = notice.respond_to?(:to_xml) ? notice.to_xml : notice
37
+ http = setup_http_connection
38
+
39
+ response = begin
40
+ http.post(url.path, data, HEADERS)
41
+ rescue *HTTP_ERRORS => e
42
+ log :level => :error,
43
+ :message => "Unable to contact the Pulse server. HTTP Error=#{e}"
44
+ nil
45
+ end
46
+
47
+ case response
48
+ when Net::HTTPSuccess then
49
+ log :level => :info,
50
+ :message => "Success: #{response.class}",
51
+ :response => response
52
+ else
53
+ log :level => :error,
54
+ :message => "Failure: #{response.class}",
55
+ :response => response,
56
+ :notice => notice
57
+ end
58
+
59
+ if response && response.respond_to?(:body)
60
+ error_id = response.body.match(%r{<id[^>]*>(.*?)</id>})
61
+ error_id[1] if error_id
62
+ end
63
+ rescue => e
64
+ log :level => :error,
65
+ :message => "[Pulse::Sender#send_to_pulse] Cannot send notification. Error: #{e.class}" +
66
+ " - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
67
+
68
+ nil
69
+ end
70
+
71
+ attr_reader :proxy_host,
72
+ :proxy_port,
73
+ :proxy_user,
74
+ :proxy_pass,
75
+ :protocol,
76
+ :host,
77
+ :port,
78
+ :secure,
79
+ :use_system_ssl_cert_chain,
80
+ :http_open_timeout,
81
+ :http_read_timeout
82
+
83
+ alias_method :secure?, :secure
84
+ alias_method :use_system_ssl_cert_chain?, :use_system_ssl_cert_chain
85
+
86
+ private
87
+
88
+ def url
89
+ URI.parse("#{protocol}://#{host}:#{port}").merge(NOTICES_URI)
90
+ end
91
+
92
+ def log(opts = {})
93
+ opts[:logger].send opts[:level], LOG_PREFIX + opts[:message] if opts[:logger]
94
+ Pulse.report_environment_info
95
+ Pulse.report_response_body(opts[:response].body) if opts[:response] && opts[:response].respond_to?(:body)
96
+ Pulse.report_notice(opts[:notice]) if opts[:notice]
97
+ end
98
+
99
+ def logger
100
+ Pulse.logger
101
+ end
102
+
103
+ def setup_http_connection
104
+ http =
105
+ Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass).
106
+ new(url.host, url.port)
107
+
108
+ http.read_timeout = http_read_timeout
109
+ http.open_timeout = http_open_timeout
110
+
111
+ if secure?
112
+ http.use_ssl = true
113
+
114
+ http.ca_file = Pulse.configuration.ca_bundle_path
115
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
116
+ else
117
+ http.use_ssl = false
118
+ end
119
+
120
+ http
121
+ rescue => e
122
+ log :level => :error,
123
+ :message => "[Pulse::Sender#setup_http_connection] Failure initializing the HTTP connection.\n" +
124
+ "Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
125
+ raise e
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,47 @@
1
+ namespace :pulse do
2
+ desc "Notify Pulse of a new deploy."
3
+ task :deploy do
4
+ require 'pulse_tasks'
5
+
6
+ if defined?(Rails.root)
7
+ initializer_file = Rails.root.join('config', 'initializers','pulse.rb')
8
+
9
+ if initializer_file.exist?
10
+ load initializer_file
11
+ else
12
+ Rake::Task[:environment].invoke
13
+ end
14
+ end
15
+
16
+ PulseTasks.deploy(:rails_env => ENV['TO'],
17
+ :scm_revision => ENV['REVISION'],
18
+ :scm_repository => ENV['REPO'],
19
+ :local_username => ENV['USER'],
20
+ :api_key => ENV['API_KEY'],
21
+ :dry_run => ENV['DRY_RUN'])
22
+ end
23
+
24
+ task :log_stdout do
25
+ require 'logger'
26
+ RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
27
+ end
28
+
29
+ namespace :heroku do
30
+ desc "Install Heroku deploy notifications addon"
31
+ task :add_deploy_notification => [:environment] do
32
+
33
+ def heroku_var(var)
34
+ `heroku config | grep -E "#{var.upcase}" | awk '{ print $3; }'`.strip
35
+ end
36
+
37
+ heroku_rails_env = heroku_var("rails_env")
38
+ heroku_api_key = heroku_var("pulse_api_key").split.find {|x| x unless x.blank?} ||
39
+ Pulse.configuration.api_key
40
+
41
+ command = %Q(heroku addons:add deployhooks:http --url="http://errors.projectlocker.com/deploys.txt?deploy[rails_env]=#{heroku_rails_env}&api_key=#{heroku_api_key}")
42
+
43
+ puts "\nRunning:\n#{command}\n"
44
+ puts `#{command}`
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,83 @@
1
+ require 'projectlocker_pulse'
2
+ require File.join(File.dirname(__FILE__), 'shared_tasks')
3
+
4
+ namespace :pulse do
5
+ desc "Verify your gem installation by sending a test exception to the pulse service"
6
+ task :test => ['pulse:log_stdout', :environment] do
7
+ RAILS_DEFAULT_LOGGER.level = Logger::DEBUG
8
+
9
+ require 'action_controller/test_process'
10
+
11
+ Dir["app/controllers/application*.rb"].each { |file| require(File.expand_path(file)) }
12
+
13
+ class PulseTestingException < RuntimeError; end
14
+
15
+ unless Pulse.configuration.api_key
16
+ puts "Pulse needs an API key configured! Check the README to see how to add it."
17
+ exit
18
+ end
19
+
20
+ Pulse.configuration.development_environments = []
21
+
22
+ catcher = Pulse::Rails::ActionControllerCatcher
23
+ in_controller = ApplicationController.included_modules.include?(catcher)
24
+ in_base = ActionController::Base.included_modules.include?(catcher)
25
+ if !in_controller || !in_base
26
+ puts "Rails initialization did not occur"
27
+ exit
28
+ end
29
+
30
+ puts "Configuration:"
31
+ Pulse.configuration.to_hash.each do |key, value|
32
+ puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
33
+ end
34
+
35
+ unless defined?(ApplicationController)
36
+ puts "No ApplicationController found"
37
+ exit
38
+ end
39
+
40
+ puts 'Setting up the Controller.'
41
+ class ApplicationController
42
+ # This is to bypass any filters that may prevent access to the action.
43
+ prepend_before_filter :test_pulse
44
+ def test_pulse
45
+ puts "Raising '#{exception_class.name}' to simulate application failure."
46
+ raise exception_class.new, 'Testing Pulse via "rake pulse:test". If you can see this, it works.'
47
+ end
48
+
49
+ def rescue_action(exception)
50
+ rescue_action_in_public exception
51
+ end
52
+
53
+ # Ensure we actually have an action to go to.
54
+ def verify; end
55
+
56
+ def consider_all_requests_local
57
+ false
58
+ end
59
+
60
+ def local_request?
61
+ false
62
+ end
63
+
64
+ def exception_class
65
+ exception_name = ENV['EXCEPTION'] || "PulseTestingException"
66
+ exception_name.split("::").inject(Object){|klass, name| klass.const_get(name)}
67
+ rescue
68
+ Object.const_set(exception_name.gsub(/:+/, "_"), Class.new(Exception))
69
+ end
70
+
71
+ def logger
72
+ nil
73
+ end
74
+ end
75
+ class PulseVerificationController < ApplicationController; end
76
+
77
+ puts 'Processing request.'
78
+ request = ActionController::TestRequest.new("REQUEST_URI" => "/pulse_verification_controller")
79
+ response = ActionController::TestResponse.new
80
+ PulseVerificationController.new.process(request, response)
81
+ end
82
+ end
83
+
@@ -0,0 +1,27 @@
1
+ module Pulse
2
+ class UserInformer
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def replacement(with)
8
+ Pulse.configuration.user_information.gsub(/\{\{\s*error_id\s*\}\}/, with.to_s)
9
+ end
10
+
11
+ def call(env)
12
+ status, headers, body = @app.call(env)
13
+ if env['pulse.error_id'] && Pulse.configuration.user_information
14
+ new_body = []
15
+ replace = replacement(env['pulse.error_id'])
16
+ body.each do |chunk|
17
+ new_body << chunk.gsub("<!-- PULSE ERROR -->", replace)
18
+ end
19
+ body.close if body.respond_to?(:close)
20
+ headers['Content-Length'] = new_body.sum(&:bytesize).to_s
21
+ body = new_body
22
+ end
23
+ [status, headers, body]
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,53 @@
1
+ # stolen from ActiveSupport
2
+
3
+ class Object
4
+ def blank?
5
+ respond_to?(:empty?) ? empty? : !self
6
+ end
7
+
8
+ def present?
9
+ !blank?
10
+ end
11
+
12
+ def presence
13
+ self if present?
14
+ end
15
+ end
16
+
17
+ class NilClass
18
+ def blank?
19
+ true
20
+ end
21
+ end
22
+
23
+ class FalseClass
24
+ def blank?
25
+ true
26
+ end
27
+ end
28
+
29
+ class TrueClass
30
+ def blank?
31
+ false
32
+ end
33
+ end
34
+
35
+ class Array
36
+ alias_method :blank?, :empty?
37
+ end
38
+
39
+ class Hash
40
+ alias_method :blank?, :empty?
41
+ end
42
+
43
+ class String
44
+ def blank?
45
+ self !~ /[^[:space:]]/
46
+ end
47
+ end
48
+
49
+ class Numeric
50
+ def blank?
51
+ false
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Pulse
2
+ VERSION = "0.2.1".freeze
3
+ end
@@ -0,0 +1,64 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ # Capistrano tasks for notifying Pulse of deploys
5
+ module PulseTasks
6
+
7
+ # Alerts Pulse of a deploy.
8
+ #
9
+ # @param [Hash] opts Data about the deploy that is set to Pulse
10
+ #
11
+ # @option opts [String] :api_key Api key of you Pulse application
12
+ # @option opts [String] :rails_env Environment of the deploy (production, staging)
13
+ # @option opts [String] :scm_revision The given revision/sha that is being deployed
14
+ # @option opts [String] :scm_repository Address of your repository to help with code lookups
15
+ # @option opts [String] :local_username Who is deploying
16
+ def self.deploy(opts = {})
17
+ api_key = opts.delete(:api_key) || Pulse.configuration.api_key
18
+ if api_key.blank?
19
+ puts "I don't seem to be configured with an API key. Please check your configuration."
20
+ return false
21
+ end
22
+
23
+ if opts[:rails_env].blank?
24
+ puts "I don't know to which Rails environment you are deploying (use the TO=production option)."
25
+ return false
26
+ end
27
+
28
+ dry_run = opts.delete(:dry_run)
29
+ params = {'api_key' => api_key}
30
+ opts.each {|k,v| params["deploy[#{k}]"] = v }
31
+
32
+ host = Pulse.configuration.host || 'errors.projectlocker.com'
33
+ port = Pulse.configuration.port
34
+
35
+ proxy = Net::HTTP.Proxy(Pulse.configuration.proxy_host,
36
+ Pulse.configuration.proxy_port,
37
+ Pulse.configuration.proxy_user,
38
+ Pulse.configuration.proxy_pass)
39
+ http = proxy.new(host, port)
40
+
41
+
42
+
43
+ # Handle Security
44
+ if Pulse.configuration.secure?
45
+ http.use_ssl = true
46
+ http.ca_file = Pulse.configuration.ca_bundle_path
47
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
48
+ end
49
+
50
+ post = Net::HTTP::Post.new("/deploys.txt")
51
+ post.set_form_data(params)
52
+
53
+ if dry_run
54
+ puts http.inspect, params.inspect
55
+ return true
56
+ else
57
+ response = http.request(post)
58
+
59
+ puts response.body
60
+ return Net::HTTPSuccess === response
61
+ end
62
+ end
63
+ end
64
+