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.
- data/CHANGELOG +26 -0
- data/Gemfile +3 -0
- data/Guardfile +6 -0
- data/INSTALL +20 -0
- data/MIT-LICENSE +23 -0
- data/README.md +439 -0
- data/README_FOR_HEROKU_ADDON.md +89 -0
- data/Rakefile +223 -0
- data/SUPPORTED_RAILS_VERSIONS +38 -0
- data/TESTING.md +41 -0
- data/features/metal.feature +18 -0
- data/features/rack.feature +60 -0
- data/features/rails.feature +272 -0
- data/features/rails_with_js_notifier.feature +97 -0
- data/features/rake.feature +27 -0
- data/features/sinatra.feature +29 -0
- data/features/step_definitions/file_steps.rb +10 -0
- data/features/step_definitions/metal_steps.rb +23 -0
- data/features/step_definitions/rack_steps.rb +23 -0
- data/features/step_definitions/rails_application_steps.rb +478 -0
- data/features/step_definitions/rake_steps.rb +17 -0
- data/features/support/env.rb +18 -0
- data/features/support/matchers.rb +35 -0
- data/features/support/projectlocker_pulse_shim.rb.template +16 -0
- data/features/support/rails.rb +201 -0
- data/features/support/rake/Rakefile +68 -0
- data/features/support/terminal.rb +107 -0
- data/features/user_informer.feature +63 -0
- data/generators/pulse/lib/insert_commands.rb +34 -0
- data/generators/pulse/lib/rake_commands.rb +24 -0
- data/generators/pulse/pulse_generator.rb +94 -0
- data/generators/pulse/templates/capistrano_hook.rb +6 -0
- data/generators/pulse/templates/initializer.rb +6 -0
- data/generators/pulse/templates/pulse_tasks.rake +25 -0
- data/install.rb +1 -0
- data/lib/projectlocker_pulse.rb +159 -0
- data/lib/pulse/backtrace.rb +108 -0
- data/lib/pulse/capistrano.rb +43 -0
- data/lib/pulse/configuration.rb +305 -0
- data/lib/pulse/notice.rb +390 -0
- data/lib/pulse/rack.rb +54 -0
- data/lib/pulse/rails/action_controller_catcher.rb +30 -0
- data/lib/pulse/rails/controller_methods.rb +85 -0
- data/lib/pulse/rails/error_lookup.rb +33 -0
- data/lib/pulse/rails/javascript_notifier.rb +47 -0
- data/lib/pulse/rails/middleware/exceptions_catcher.rb +33 -0
- data/lib/pulse/rails.rb +40 -0
- data/lib/pulse/rails3_tasks.rb +99 -0
- data/lib/pulse/railtie.rb +49 -0
- data/lib/pulse/rake_handler.rb +65 -0
- data/lib/pulse/sender.rb +128 -0
- data/lib/pulse/shared_tasks.rb +47 -0
- data/lib/pulse/tasks.rb +83 -0
- data/lib/pulse/user_informer.rb +27 -0
- data/lib/pulse/utils/blank.rb +53 -0
- data/lib/pulse/version.rb +3 -0
- data/lib/pulse_tasks.rb +64 -0
- data/lib/rails/generators/pulse/pulse_generator.rb +100 -0
- data/lib/templates/javascript_notifier.erb +15 -0
- data/lib/templates/rescue.erb +91 -0
- data/pulse.gemspec +39 -0
- data/rails/init.rb +1 -0
- data/resources/README.md +34 -0
- data/resources/ca-bundle.crt +3376 -0
- data/script/integration_test.rb +38 -0
- data/test/backtrace_test.rb +162 -0
- data/test/capistrano_test.rb +34 -0
- data/test/catcher_test.rb +333 -0
- data/test/configuration_test.rb +236 -0
- data/test/helper.rb +263 -0
- data/test/javascript_notifier_test.rb +51 -0
- data/test/logger_test.rb +79 -0
- data/test/notice_test.rb +490 -0
- data/test/notifier_test.rb +276 -0
- data/test/projectlocker_pulse_tasks_test.rb +170 -0
- data/test/pulse.xsd +88 -0
- data/test/rack_test.rb +58 -0
- data/test/rails_initializer_test.rb +36 -0
- data/test/recursion_test.rb +10 -0
- data/test/sender_test.rb +288 -0
- data/test/user_informer_test.rb +29 -0
- metadata +432 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
When /I run rake with (.+)/ do |command|
|
2
|
+
@rake_command = "rake #{command.gsub(' ','_')}"
|
3
|
+
@rake_result = `cd features/support/rake && GEM_HOME=#{BUILT_GEM_ROOT} #{@rake_command} 2>&1`
|
4
|
+
end
|
5
|
+
|
6
|
+
Then /Pulse should (|not) ?catch the exception/ do |condition|
|
7
|
+
if condition=='not'
|
8
|
+
@rake_result.should_not =~ /^pulse/
|
9
|
+
else
|
10
|
+
@rake_result.should =~ /^pulse/
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Then /Pulse should send the rake command line as the component name/ do
|
15
|
+
component = @rake_result.match(/^pulse (.*)$/)[1]
|
16
|
+
component.should == @rake_command
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'rspec'
|
4
|
+
|
5
|
+
PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
|
6
|
+
TEMP_DIR = File.join(PROJECT_ROOT, 'tmp').freeze
|
7
|
+
LOCAL_RAILS_ROOT = File.join(TEMP_DIR, 'rails_root').freeze
|
8
|
+
BUILT_GEM_ROOT = File.join(TEMP_DIR, 'built_gems').freeze
|
9
|
+
LOCAL_GEM_ROOT = File.join(TEMP_DIR, 'local_gems').freeze
|
10
|
+
RACK_FILE = File.join(TEMP_DIR, 'rack_app.rb').freeze
|
11
|
+
|
12
|
+
Before do
|
13
|
+
FileUtils.mkdir_p(TEMP_DIR)
|
14
|
+
FileUtils.rm_rf(BUILT_GEM_ROOT)
|
15
|
+
FileUtils.rm_rf(LOCAL_RAILS_ROOT)
|
16
|
+
FileUtils.rm_f(RACK_FILE)
|
17
|
+
FileUtils.mkdir_p(BUILT_GEM_ROOT)
|
18
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
RSpec::Matchers.define :have_content do |xpath, content|
|
2
|
+
match do |document|
|
3
|
+
@elements = document.search(xpath)
|
4
|
+
|
5
|
+
if @elements.empty?
|
6
|
+
false
|
7
|
+
else
|
8
|
+
element_with_content = document.at("#{xpath}[contains(.,'#{content}')]")
|
9
|
+
|
10
|
+
if element_with_content.nil?
|
11
|
+
@found = @elements.collect { |element| element.content }
|
12
|
+
|
13
|
+
false
|
14
|
+
else
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
failure_message_for_should do |document|
|
21
|
+
if @elements.empty?
|
22
|
+
"In XML:\n#{document}\nNo element at #{xpath}"
|
23
|
+
else
|
24
|
+
"In XML:\n#{document}\nGot content #{@found.inspect} at #{xpath} instead of #{content.inspect}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
failure_message_for_should_not do |document|
|
29
|
+
unless @elements.empty?
|
30
|
+
"In XML:\n#{document}\nExpcted no content #{content.inspect} at #{xpath}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
World(RSpec::Matchers)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'sham_rack'
|
2
|
+
|
3
|
+
Pulse.configuration.logger = Logger.new STDOUT if defined?(Pulse)
|
4
|
+
|
5
|
+
ShamRack.at("errors.projectlocker.com") do |env|
|
6
|
+
response = <<-end_xml
|
7
|
+
<notice>
|
8
|
+
<id>b6817316-9c45-ed26-45eb-780dbb86aadb</id>
|
9
|
+
<url>http://errors.projectlocker.com/locate/b6817316-9c45-ed26-45eb-780dbb86aadb</url>
|
10
|
+
</notice>
|
11
|
+
|
12
|
+
Request:
|
13
|
+
#{env["rack.input"].read}
|
14
|
+
end_xml
|
15
|
+
["200 OK", { "Content-type" => "text/xml" }, [response]]
|
16
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module RailsHelpers
|
2
|
+
def rails_root_exists?
|
3
|
+
File.exists?(environment_path)
|
4
|
+
end
|
5
|
+
|
6
|
+
def application_controller_filename
|
7
|
+
controller_filename = File.join(rails_root, 'app', 'controllers', "application_controller.rb")
|
8
|
+
end
|
9
|
+
|
10
|
+
def rails3?
|
11
|
+
rails_version =~ /^3/
|
12
|
+
end
|
13
|
+
|
14
|
+
def rails_root
|
15
|
+
LOCAL_RAILS_ROOT
|
16
|
+
end
|
17
|
+
|
18
|
+
def rails_uses_rack?
|
19
|
+
rails3? || rails_version =~ /^2\.3/
|
20
|
+
end
|
21
|
+
|
22
|
+
def rails_version
|
23
|
+
@rails_version ||= begin
|
24
|
+
if ENV["RAILS_VERSION"]
|
25
|
+
ENV["RAILS_VERSION"]
|
26
|
+
elsif bundler_manages_gems?
|
27
|
+
rails_version = open(gemfile_path).read.match(/gem.*rails["'].*["'](.+)["']/)[1]
|
28
|
+
else
|
29
|
+
environment_file = File.join(rails_root, 'config', 'environment.rb')
|
30
|
+
rails_version = `grep RAILS_GEM_VERSION #{environment_file}`.match(/[\d.]+/)[0]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def bundler_manages_gems?
|
36
|
+
File.exists?(gemfile_path)
|
37
|
+
end
|
38
|
+
|
39
|
+
def gemfile_path
|
40
|
+
gemfile = File.join(rails_root, 'Gemfile')
|
41
|
+
end
|
42
|
+
|
43
|
+
def rails_manages_gems?
|
44
|
+
rails_version =~ /^2\.[123]/
|
45
|
+
end
|
46
|
+
|
47
|
+
def rails_supports_initializers?
|
48
|
+
rails3? || rails_version =~ /^2\./
|
49
|
+
end
|
50
|
+
|
51
|
+
def rails_finds_generators_in_gems?
|
52
|
+
rails3? || rails_version =~ /^2\./
|
53
|
+
end
|
54
|
+
|
55
|
+
def version_string
|
56
|
+
ENV['RAILS_VERSION'] || `tail -n 1 SUPPORTED_RAILS_VERSIONS` # use latest version if ENV["RAILS_VERSION"] is undefined
|
57
|
+
end
|
58
|
+
|
59
|
+
def environment_path
|
60
|
+
File.join(rails_root, 'config', 'environment.rb')
|
61
|
+
end
|
62
|
+
|
63
|
+
def rakefile_path
|
64
|
+
File.join(rails_root, 'Rakefile')
|
65
|
+
end
|
66
|
+
|
67
|
+
def bundle_gem(gem_name, version = nil)
|
68
|
+
File.open(gemfile_path, 'a') do |file|
|
69
|
+
gem = "gem '#{gem_name}'"
|
70
|
+
gem += ", '#{version}'" if version
|
71
|
+
file.puts(gem)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def config_gem(gem_name, version = nil)
|
76
|
+
run = "Rails::Initializer.run do |config|"
|
77
|
+
insert = " config.gem '#{gem_name}'"
|
78
|
+
insert += ", :version => '#{version}'" if version
|
79
|
+
content = File.read(environment_path)
|
80
|
+
content = "require 'thread'\n#{content}"
|
81
|
+
if content.sub!(run, "#{run}\n#{insert}")
|
82
|
+
File.open(environment_path, 'wb') { |file| file.write(content) }
|
83
|
+
else
|
84
|
+
raise "Couldn't find #{run.inspect} in #{environment_path}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def config_gem_dependencies
|
89
|
+
insert = <<-END
|
90
|
+
if Gem::VERSION >= "1.3.6"
|
91
|
+
module Rails
|
92
|
+
class GemDependency
|
93
|
+
def requirement
|
94
|
+
r = super
|
95
|
+
(r == Gem::Requirement.default) ? nil : r
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
END
|
101
|
+
run = "Rails::Initializer.run do |config|"
|
102
|
+
content = File.read(environment_path)
|
103
|
+
if content.sub!(run, "#{insert}\n#{run}")
|
104
|
+
File.open(environment_path, 'wb') { |file| file.write(content) }
|
105
|
+
else
|
106
|
+
raise "Couldn't find #{run.inspect} in #{environment_path}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def require_thread
|
111
|
+
content = File.read(rakefile_path)
|
112
|
+
content = "require 'thread'\n#{content}"
|
113
|
+
File.open(rakefile_path, 'wb') { |file| file.write(content) }
|
114
|
+
end
|
115
|
+
|
116
|
+
def perform_request(uri, environment = 'production')
|
117
|
+
if rails3?
|
118
|
+
request_script = <<-SCRIPT
|
119
|
+
require File.expand_path('../config/environment', __FILE__)
|
120
|
+
|
121
|
+
|
122
|
+
env = Rack::MockRequest.env_for(#{uri.inspect})
|
123
|
+
response = RailsRoot::Application.call(env)
|
124
|
+
|
125
|
+
|
126
|
+
response = response.last if response.last.is_a?(ActionDispatch::Response)
|
127
|
+
|
128
|
+
if response.is_a?(Array)
|
129
|
+
puts response.join
|
130
|
+
else
|
131
|
+
puts response.body
|
132
|
+
end
|
133
|
+
SCRIPT
|
134
|
+
File.open(File.join(rails_root, 'request.rb'), 'w') { |file| file.write(request_script) }
|
135
|
+
@terminal.cd(rails_root)
|
136
|
+
@terminal.run("ruby -rthread ./script/rails runner -e #{environment} request.rb")
|
137
|
+
elsif rails_uses_rack?
|
138
|
+
request_script = <<-SCRIPT
|
139
|
+
require File.expand_path('../config/environment', __FILE__)
|
140
|
+
|
141
|
+
env = Rack::MockRequest.env_for(#{uri.inspect})
|
142
|
+
app = Rack::Lint.new(ActionController::Dispatcher.new)
|
143
|
+
|
144
|
+
status, headers, body = app.call(env)
|
145
|
+
|
146
|
+
response = ""
|
147
|
+
if body.respond_to?(:to_str)
|
148
|
+
response << body
|
149
|
+
else
|
150
|
+
body.each { |part| response << part }
|
151
|
+
end
|
152
|
+
|
153
|
+
puts response
|
154
|
+
SCRIPT
|
155
|
+
File.open(File.join(rails_root, 'request.rb'), 'w') { |file| file.write(request_script) }
|
156
|
+
@terminal.cd(rails_root)
|
157
|
+
@terminal.run("ruby -rthread ./script/runner -e #{environment} request.rb")
|
158
|
+
else
|
159
|
+
uri = URI.parse(uri)
|
160
|
+
request_script = <<-SCRIPT
|
161
|
+
require 'cgi'
|
162
|
+
class CGIWrapper < CGI
|
163
|
+
def initialize(*args)
|
164
|
+
@env_table = {}
|
165
|
+
@stdinput = $stdin
|
166
|
+
super(*args)
|
167
|
+
end
|
168
|
+
attr_reader :env_table
|
169
|
+
end
|
170
|
+
$stdin = StringIO.new("")
|
171
|
+
cgi = CGIWrapper.new
|
172
|
+
cgi.env_table.update({
|
173
|
+
'HTTPS' => 'off',
|
174
|
+
'REQUEST_METHOD' => "GET",
|
175
|
+
'HTTP_HOST' => #{[uri.host, uri.port].join(':').inspect},
|
176
|
+
'SERVER_PORT' => #{uri.port.inspect},
|
177
|
+
'REQUEST_URI' => #{uri.request_uri.inspect},
|
178
|
+
'PATH_INFO' => #{uri.path.inspect},
|
179
|
+
'QUERY_STRING' => #{uri.query.inspect}
|
180
|
+
})
|
181
|
+
require 'dispatcher' unless defined?(ActionController::Dispatcher)
|
182
|
+
Dispatcher.dispatch(cgi)
|
183
|
+
SCRIPT
|
184
|
+
File.open(File.join(rails_root, 'request.rb'), 'w') { |file| file.write(request_script) }
|
185
|
+
@terminal.cd(rails_root)
|
186
|
+
@terminal.run("ruby -rthread ./script/runner -e #{environment} request.rb")
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def monkeypatch_old_version
|
191
|
+
monkeypatchin= <<-MONKEYPATCHIN
|
192
|
+
|
193
|
+
MissingSourceFile::REGEXPS << [/^cannot load such file -- (.+)$/i, 1]
|
194
|
+
|
195
|
+
MONKEYPATCHIN
|
196
|
+
|
197
|
+
File.open(File.join(rails_root,"config","initializers", 'monkeypatchin.rb'), 'w') { |file| file.write(monkeypatchin) }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
World(RailsHelpers)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# A test harness for RakeHandler
|
2
|
+
#
|
3
|
+
require 'rake'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'projectlocker_pulse'
|
6
|
+
require 'pulse/rake_handler'
|
7
|
+
|
8
|
+
Pulse.configure do |c|
|
9
|
+
end
|
10
|
+
|
11
|
+
# Should catch exception
|
12
|
+
task :pulse do
|
13
|
+
Pulse.configuration.rescue_rake_exceptions = true
|
14
|
+
stub_tty_output(true)
|
15
|
+
raise_exception
|
16
|
+
end
|
17
|
+
|
18
|
+
# Should not catch exception
|
19
|
+
task :pulse_disabled do
|
20
|
+
Pulse.configuration.rescue_rake_exceptions = false
|
21
|
+
stub_tty_output(true)
|
22
|
+
raise_exception
|
23
|
+
end
|
24
|
+
|
25
|
+
# Should not catch exception as tty_output is true
|
26
|
+
task :pulse_autodetect_from_terminal do
|
27
|
+
Pulse.configuration.rescue_rake_exceptions = nil
|
28
|
+
stub_tty_output(true)
|
29
|
+
raise_exception
|
30
|
+
end
|
31
|
+
|
32
|
+
# Should catch exception as tty_output is false
|
33
|
+
task :pulse_autodetect_not_from_terminal do
|
34
|
+
Pulse.configuration.rescue_rake_exceptions = nil
|
35
|
+
stub_tty_output(false)
|
36
|
+
raise_exception
|
37
|
+
end
|
38
|
+
|
39
|
+
task :pulse_not_yet_configured do
|
40
|
+
Pulse.configuration.rescue_rake_exceptions = true
|
41
|
+
stub_tty_output(true)
|
42
|
+
stub_empty_sender
|
43
|
+
raise_exception
|
44
|
+
end
|
45
|
+
|
46
|
+
module Pulse
|
47
|
+
def self.notify_or_ignore(*args)
|
48
|
+
# TODO if you need to check more params, you'll have to use json.dump or something
|
49
|
+
$stderr.puts "pulse #{args[1][:component]}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def stub_empty_sender
|
54
|
+
Pulse.sender = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def stub_tty_output(value)
|
58
|
+
Rake.application.instance_eval do
|
59
|
+
@tty_output_stub = value
|
60
|
+
def tty_output?
|
61
|
+
@tty_output_stub
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def raise_exception
|
67
|
+
raise 'TEST'
|
68
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
Before do
|
4
|
+
@terminal = Terminal.new
|
5
|
+
end
|
6
|
+
|
7
|
+
After do |story|
|
8
|
+
if story.failed?
|
9
|
+
# puts @terminal.output
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Terminal
|
14
|
+
attr_reader :output, :status
|
15
|
+
attr_accessor :environment_variables, :invoke_heroku_rake_tasks_locally
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@cwd = FileUtils.pwd
|
19
|
+
@output = ""
|
20
|
+
@status = 0
|
21
|
+
@logger = Logger.new(File.join(TEMP_DIR, 'terminal.log'))
|
22
|
+
|
23
|
+
@invoke_heroku_rake_tasks_locally = false
|
24
|
+
|
25
|
+
@environment_variables = {
|
26
|
+
"GEM_HOME" => LOCAL_GEM_ROOT,
|
27
|
+
"GEM_PATH" => "#{LOCAL_GEM_ROOT}:#{BUILT_GEM_ROOT}",
|
28
|
+
"PATH" => "#{gem_bin_path}:#{ENV['PATH']}"
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def cd(directory)
|
33
|
+
@cwd = directory
|
34
|
+
end
|
35
|
+
|
36
|
+
def run(command)
|
37
|
+
command = optionally_invoke_heroku_rake_tasks_locally(command)
|
38
|
+
|
39
|
+
output << "#{command}\n"
|
40
|
+
FileUtils.cd(@cwd) do
|
41
|
+
# The ; forces ruby to shell out so the env settings work right
|
42
|
+
cmdline = "#{environment_settings} #{command} 2>&1 ; "
|
43
|
+
logger.debug(cmdline)
|
44
|
+
result = `#{cmdline}`
|
45
|
+
logger.debug(result)
|
46
|
+
output << result
|
47
|
+
end
|
48
|
+
@status = $?
|
49
|
+
end
|
50
|
+
|
51
|
+
def optionally_invoke_heroku_rake_tasks_locally(command)
|
52
|
+
if invoke_heroku_rake_tasks_locally
|
53
|
+
command.sub(/^heroku /, '')
|
54
|
+
else
|
55
|
+
command
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def echo(string)
|
60
|
+
logger.debug(string)
|
61
|
+
end
|
62
|
+
|
63
|
+
def flush!
|
64
|
+
@output = ""
|
65
|
+
end
|
66
|
+
|
67
|
+
def build_and_install_gem(gemspec)
|
68
|
+
pkg_dir = File.join(TEMP_DIR, 'pkg')
|
69
|
+
FileUtils.mkdir_p(pkg_dir)
|
70
|
+
output = `gem build #{gemspec} 2>&1`
|
71
|
+
gem_file = Dir.glob("*.gem").first
|
72
|
+
unless gem_file
|
73
|
+
raise "Gem didn't build:\n#{output}"
|
74
|
+
end
|
75
|
+
target = File.join(pkg_dir, gem_file)
|
76
|
+
FileUtils.mv(gem_file, target)
|
77
|
+
install_gem_to(LOCAL_GEM_ROOT, target)
|
78
|
+
end
|
79
|
+
|
80
|
+
def install_gem(gem)
|
81
|
+
install_gem_to(LOCAL_GEM_ROOT, gem)
|
82
|
+
end
|
83
|
+
|
84
|
+
def uninstall_gem(gem)
|
85
|
+
`gem uninstall -i #{LOCAL_GEM_ROOT} #{gem}`
|
86
|
+
end
|
87
|
+
|
88
|
+
def prepend_path(path)
|
89
|
+
@environment_variables['PATH'] = path + ":" + @environment_variables['PATH']
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def install_gem_to(root, gem)
|
95
|
+
`gem install -i #{root} --no-ri --no-rdoc #{gem}`
|
96
|
+
end
|
97
|
+
|
98
|
+
def environment_settings
|
99
|
+
@environment_variables.map { |key, value| "#{key}=#{value}" }.join(' ')
|
100
|
+
end
|
101
|
+
|
102
|
+
def gem_bin_path
|
103
|
+
File.join(LOCAL_GEM_ROOT, "bin")
|
104
|
+
end
|
105
|
+
|
106
|
+
attr_reader :logger
|
107
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
Feature: Inform the user of the Pulse notice that was just created
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given I have built and installed the "projectlocker-pulse" gem
|
5
|
+
|
6
|
+
Scenario: Rescue an exception in a controller
|
7
|
+
When I generate a new Rails application
|
8
|
+
And I configure the Pulse shim
|
9
|
+
And I configure my application to require the "projectlocker-pulse" gem
|
10
|
+
And I run the pulse generator with "-k myapikey"
|
11
|
+
And I define a response for "TestController#index":
|
12
|
+
"""
|
13
|
+
raise RuntimeError, "some message"
|
14
|
+
"""
|
15
|
+
And the response page for a "500" error is
|
16
|
+
"""
|
17
|
+
<!-- PROJECTLOCKER_ERRATA ERROR -->
|
18
|
+
"""
|
19
|
+
And I route "/test/index" to "test#index"
|
20
|
+
And I perform a request to "http://example.com:123/test/index?param=value"
|
21
|
+
Then I should see "Pulse Error b6817316-9c45-ed26-45eb-780dbb86aadb"
|
22
|
+
|
23
|
+
Scenario: Rescue an exception in a controller with a custom error string
|
24
|
+
When I generate a new Rails application
|
25
|
+
And I configure the Pulse shim
|
26
|
+
And I configure my application to require the "projectlocker-pulse" gem
|
27
|
+
And I configure the notifier to use the following configuration lines:
|
28
|
+
"""
|
29
|
+
config.user_information = 'Error #{{ error_id }}'
|
30
|
+
"""
|
31
|
+
And I run the pulse generator with "-k myapikey"
|
32
|
+
And I define a response for "TestController#index":
|
33
|
+
"""
|
34
|
+
raise RuntimeError, "some message"
|
35
|
+
"""
|
36
|
+
And the response page for a "500" error is
|
37
|
+
"""
|
38
|
+
<!-- PROJECTLOCKER_ERRATA ERROR -->
|
39
|
+
"""
|
40
|
+
And I route "/test/index" to "test#index"
|
41
|
+
And I perform a request to "http://example.com:123/test/index?param=value"
|
42
|
+
Then I should see "Error #b6817316-9c45-ed26-45eb-780dbb86aadb"
|
43
|
+
|
44
|
+
Scenario: Don't inform them user
|
45
|
+
When I generate a new Rails application
|
46
|
+
And I configure the Pulse shim
|
47
|
+
And I configure my application to require the "projectlocker-pulse" gem
|
48
|
+
And I configure the notifier to use the following configuration lines:
|
49
|
+
"""
|
50
|
+
config.user_information = false
|
51
|
+
"""
|
52
|
+
And I run the pulse generator with "-k myapikey"
|
53
|
+
And I define a response for "TestController#index":
|
54
|
+
"""
|
55
|
+
raise RuntimeError, "some message"
|
56
|
+
"""
|
57
|
+
And the response page for a "500" error is
|
58
|
+
"""
|
59
|
+
<!-- PROJECTLOCKER_ERRATA ERROR -->
|
60
|
+
"""
|
61
|
+
And I route "/test/index" to "test#index"
|
62
|
+
And I perform a request to "http://example.com:123/test/index?param=value"
|
63
|
+
Then I should not see "Pulse Error b6817316-9c45-ed26-45eb-780dbb86aadb"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Mostly pinched from http://github.com/ryanb/nifty-generators/tree/master
|
2
|
+
|
3
|
+
Rails::Generator::Commands::Base.class_eval do
|
4
|
+
def file_contains?(relative_destination, line)
|
5
|
+
File.read(destination_path(relative_destination)).include?(line)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Rails::Generator::Commands::Create.class_eval do
|
10
|
+
def append_to(file, line)
|
11
|
+
logger.insert "#{line} appended to #{file}"
|
12
|
+
unless options[:pretend] || file_contains?(file, line)
|
13
|
+
File.open(file, "a") do |file|
|
14
|
+
file.puts
|
15
|
+
file.puts line
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Rails::Generator::Commands::Destroy.class_eval do
|
22
|
+
def append_to(file, line)
|
23
|
+
logger.remove "#{line} removed from #{file}"
|
24
|
+
unless options[:pretend]
|
25
|
+
gsub_file file, "\n#{line}", ''
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Rails::Generator::Commands::List.class_eval do
|
31
|
+
def append_to(file, line)
|
32
|
+
logger.insert "#{line} appended to #{file}"
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Rails::Generator::Commands::Create.class_eval do
|
2
|
+
def rake(cmd, opts = {})
|
3
|
+
logger.rake "rake #{cmd}"
|
4
|
+
unless system("rake #{cmd}")
|
5
|
+
logger.rake "#{cmd} failed. Rolling back"
|
6
|
+
command(:destroy).invoke!
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
Rails::Generator::Commands::Destroy.class_eval do
|
12
|
+
def rake(cmd, opts = {})
|
13
|
+
unless opts[:generate_only]
|
14
|
+
logger.rake "rake #{cmd}"
|
15
|
+
system "rake #{cmd}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Rails::Generator::Commands::List.class_eval do
|
21
|
+
def rake(cmd, opts = {})
|
22
|
+
logger.rake "rake #{cmd}"
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/lib/insert_commands.rb")
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/lib/rake_commands.rb")
|
3
|
+
|
4
|
+
class PulseGenerator < Rails::Generator::Base
|
5
|
+
def add_options!(opt)
|
6
|
+
opt.on('-k', '--api-key=key', String, "Your Pulse API key") { |v| options[:api_key] = v}
|
7
|
+
opt.on('-h', '--heroku', "Use the Heroku addon to provide your Pulse API key") { |v| options[:heroku] = v}
|
8
|
+
opt.on('-a', '--app=myapp', String, "Your Heroku app name (only required if deploying to >1 Heroku app)") { |v| options[:app] = v}
|
9
|
+
end
|
10
|
+
|
11
|
+
def manifest
|
12
|
+
if !api_key_configured? && !options[:api_key] && !options[:heroku]
|
13
|
+
puts "Must pass --api-key or --heroku or create config/initializers/pulse.rb"
|
14
|
+
exit
|
15
|
+
end
|
16
|
+
if plugin_is_present?
|
17
|
+
puts "You must first remove the pulse plugin. Please run: script/plugin remove pulse"
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
record do |m|
|
21
|
+
m.directory 'lib/tasks'
|
22
|
+
m.file 'pulse_tasks.rake', 'lib/tasks/pulse_tasks.rake'
|
23
|
+
if ['config/deploy.rb', 'Capfile'].all? { |file| File.exists?(file) }
|
24
|
+
m.append_to 'config/deploy.rb', capistrano_hook
|
25
|
+
end
|
26
|
+
if api_key_expression
|
27
|
+
if use_initializer?
|
28
|
+
m.template 'initializer.rb', 'config/initializers/pulse.rb',
|
29
|
+
:assigns => {:api_key => api_key_expression}
|
30
|
+
else
|
31
|
+
m.template 'initializer.rb', 'config/pulse.rb',
|
32
|
+
:assigns => {:api_key => api_key_expression}
|
33
|
+
m.append_to 'config/environment.rb', "require 'config/pulse'"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
determine_api_key if heroku?
|
37
|
+
m.rake "pulse:test --trace", :generate_only => true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def api_key_expression
|
42
|
+
s = if options[:api_key]
|
43
|
+
"'#{options[:api_key]}'"
|
44
|
+
elsif options[:heroku]
|
45
|
+
"ENV['PULSE_API_KEY']"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def determine_api_key
|
50
|
+
puts "Attempting to determine your API Key from Heroku..."
|
51
|
+
ENV['PULSE_API_KEY'] = heroku_api_key
|
52
|
+
if ENV['PULSE_API_KEY'].blank?
|
53
|
+
puts "... Failed."
|
54
|
+
puts "WARNING: We were unable to detect the Pulse API Key from your Heroku environment."
|
55
|
+
puts "Your Heroku application environment may not be configured correctly."
|
56
|
+
exit 1
|
57
|
+
else
|
58
|
+
puts "... Done."
|
59
|
+
puts "Heroku's Pulse API Key is '#{ENV['PULSE_API_KEY']}'"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def heroku_var(var,app_name = nil)
|
64
|
+
app = app_name ? "--app #{app_name}" : ''
|
65
|
+
`heroku config #{app} | grep -E "#{var.upcase}" | awk '{ print $3; }'`.strip
|
66
|
+
end
|
67
|
+
|
68
|
+
def heroku_api_key
|
69
|
+
heroku_var("pulse_api_key",options[:app]).split.find {|x| x unless x.blank?}
|
70
|
+
end
|
71
|
+
|
72
|
+
def heroku?
|
73
|
+
options[:heroku] ||
|
74
|
+
system("grep PULSE_API_KEY config/initializers/pulse.rb") ||
|
75
|
+
system("grep PULSE_API_KEY config/environment.rb")
|
76
|
+
end
|
77
|
+
|
78
|
+
def use_initializer?
|
79
|
+
Rails::VERSION::MAJOR > 1
|
80
|
+
end
|
81
|
+
|
82
|
+
def api_key_configured?
|
83
|
+
File.exists?('config/initializers/pulse.rb') ||
|
84
|
+
system("grep Pulse config/environment.rb")
|
85
|
+
end
|
86
|
+
|
87
|
+
def capistrano_hook
|
88
|
+
IO.read(source_path('capistrano_hook.rb'))
|
89
|
+
end
|
90
|
+
|
91
|
+
def plugin_is_present?
|
92
|
+
File.exists?('vendor/plugins/pulse')
|
93
|
+
end
|
94
|
+
end
|