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,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
@@ -0,0 +1,6 @@
1
+
2
+ Dir[File.join(File.dirname(__FILE__), '..', 'vendor', 'gems', 'projectlocker-pulse-*')].each do |vendored_notifier|
3
+ $: << File.join(vendored_notifier, 'lib')
4
+ end
5
+
6
+ require 'pulse/capistrano'