hatless-hoptoad_notifier 2.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/CHANGELOG +149 -0
  2. data/INSTALL +25 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +382 -0
  5. data/Rakefile +217 -0
  6. data/SUPPORTED_RAILS_VERSIONS +9 -0
  7. data/TESTING.rdoc +8 -0
  8. data/generators/hoptoad/hoptoad_generator.rb +54 -0
  9. data/generators/hoptoad/lib/insert_commands.rb +34 -0
  10. data/generators/hoptoad/lib/rake_commands.rb +24 -0
  11. data/generators/hoptoad/templates/capistrano_hook.rb +6 -0
  12. data/generators/hoptoad/templates/hoptoad_notifier_tasks.rake +25 -0
  13. data/generators/hoptoad/templates/initializer.rb +6 -0
  14. data/lib/hoptoad_notifier/backtrace.rb +99 -0
  15. data/lib/hoptoad_notifier/capistrano.rb +20 -0
  16. data/lib/hoptoad_notifier/configuration.rb +232 -0
  17. data/lib/hoptoad_notifier/notice.rb +318 -0
  18. data/lib/hoptoad_notifier/rack.rb +40 -0
  19. data/lib/hoptoad_notifier/rails/action_controller_catcher.rb +29 -0
  20. data/lib/hoptoad_notifier/rails/controller_methods.rb +58 -0
  21. data/lib/hoptoad_notifier/rails/error_lookup.rb +33 -0
  22. data/lib/hoptoad_notifier/rails.rb +37 -0
  23. data/lib/hoptoad_notifier/rails3_tasks.rb +90 -0
  24. data/lib/hoptoad_notifier/railtie.rb +23 -0
  25. data/lib/hoptoad_notifier/sender.rb +63 -0
  26. data/lib/hoptoad_notifier/tasks.rb +97 -0
  27. data/lib/hoptoad_notifier/version.rb +3 -0
  28. data/lib/hoptoad_notifier.rb +148 -0
  29. data/lib/hoptoad_tasks.rb +40 -0
  30. data/lib/rails/generators/hoptoad/hoptoad_generator.rb +64 -0
  31. data/lib/templates/rescue.erb +91 -0
  32. data/rails/init.rb +1 -0
  33. data/script/integration_test.rb +38 -0
  34. data/test/backtrace_test.rb +118 -0
  35. data/test/catcher_test.rb +324 -0
  36. data/test/configuration_test.rb +208 -0
  37. data/test/helper.rb +239 -0
  38. data/test/hoptoad_tasks_test.rb +138 -0
  39. data/test/logger_test.rb +85 -0
  40. data/test/notice_test.rb +443 -0
  41. data/test/notifier_test.rb +222 -0
  42. data/test/rack_test.rb +58 -0
  43. data/test/rails_initializer_test.rb +36 -0
  44. data/test/sender_test.rb +123 -0
  45. metadata +204 -0
data/Rakefile ADDED
@@ -0,0 +1,217 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+ require 'cucumber/rake/task'
6
+
7
+ desc 'Default: run unit tests.'
8
+ task :default => [:test, :cucumber]
9
+
10
+ desc 'Test the hoptoad_notifier gem.'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib'
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = true
15
+ end
16
+
17
+ desc 'Run ginger tests'
18
+ task :ginger do
19
+ $LOAD_PATH << File.join(*%w[vendor ginger lib])
20
+ ARGV.clear
21
+ ARGV << 'test'
22
+ load File.join(*%w[vendor ginger bin ginger])
23
+ end
24
+
25
+ namespace :changeling do
26
+ desc "Bumps the version by a minor or patch version, depending on what was passed in."
27
+ task :bump, :part do |t, args|
28
+ # Thanks, Jeweler!
29
+ if HoptoadNotifier::VERSION =~ /^(\d+)\.(\d+)\.(\d+)(?:\.(.*?))?$/
30
+ major = $1.to_i
31
+ minor = $2.to_i
32
+ patch = $3.to_i
33
+ build = $4
34
+ else
35
+ abort
36
+ end
37
+
38
+ case args[:part]
39
+ when /minor/
40
+ minor += 1
41
+ patch = 0
42
+ when /patch/
43
+ patch += 1
44
+ else
45
+ abort
46
+ end
47
+
48
+ version = [major, minor, patch, build].compact.join('.')
49
+
50
+ File.open(File.join("lib", "hoptoad_notifier", "version.rb"), "w") do |f|
51
+ f.write <<EOF
52
+ module HoptoadNotifier
53
+ VERSION = "#{version}".freeze
54
+ end
55
+ EOF
56
+ end
57
+ end
58
+
59
+ desc "Writes out the new CHANGELOG and prepares the release"
60
+ task :change do
61
+ load 'lib/hoptoad_notifier/version.rb'
62
+ file = "CHANGELOG"
63
+ old = File.read(file)
64
+ version = HoptoadNotifier::VERSION
65
+ message = "Bumping to version #{version}"
66
+
67
+ File.open(file, "w") do |f|
68
+ f.write <<EOF
69
+ Version #{version} - #{Date.today}
70
+ ===============================================================================
71
+
72
+ #{`git log $(git tag | tail -1)..HEAD | git shortlog`}
73
+ #{old}
74
+ EOF
75
+ end
76
+
77
+ exec ["#{ENV["EDITOR"]} #{file}",
78
+ "git commit -aqm '#{message}'",
79
+ "git tag -a -m '#{message}' v#{version}",
80
+ "echo '\n\n\033[32mMarked v#{version} /' `git show-ref -s refs/heads/master` 'for release. Run: rake changeling:push\033[0m\n\n'"].join(' && ')
81
+ end
82
+
83
+ desc "Bump by a minor version (1.2.3 => 1.3.0)"
84
+ task :minor do |t|
85
+ Rake::Task['changeling:bump'].invoke(t.name)
86
+ Rake::Task['changeling:change'].invoke
87
+ end
88
+
89
+ desc "Bump by a patch version, (1.2.3 => 1.2.4)"
90
+ task :patch do |t|
91
+ Rake::Task['changeling:bump'].invoke(t.name)
92
+ Rake::Task['changeling:change'].invoke
93
+ end
94
+
95
+ desc "Push the latest version and tags"
96
+ task :push do |t|
97
+ system("git push origin master")
98
+ system("git push origin $(git tag | tail -1)")
99
+ end
100
+ end
101
+
102
+ begin
103
+ require 'yard'
104
+ YARD::Rake::YardocTask.new do |t|
105
+ t.files = ['lib/**/*.rb', 'TESTING.rdoc']
106
+ end
107
+ rescue LoadError
108
+ end
109
+
110
+ GEM_ROOT = File.dirname(__FILE__).freeze
111
+ VERSION_FILE = File.join(GEM_ROOT, 'lib', 'hoptoad_notifier', 'version')
112
+
113
+ require VERSION_FILE
114
+
115
+ gemspec = Gem::Specification.new do |s|
116
+ s.name = %q{hoptoad_notifier}
117
+ s.version = HoptoadNotifier::VERSION
118
+ s.summary = %q{Send your application errors to our hosted service and reclaim your inbox.}
119
+
120
+ s.files = FileList['[A-Z]*', 'generators/**/*.*', 'lib/**/*.rb',
121
+ 'test/**/*.rb', 'rails/**/*.rb', 'script/*',
122
+ 'lib/templates/*.erb']
123
+ s.require_path = 'lib'
124
+ s.test_files = Dir[*['test/**/*_test.rb']]
125
+
126
+ s.has_rdoc = true
127
+ s.extra_rdoc_files = ["README.rdoc"]
128
+ s.rdoc_options = ['--line-numbers', "--main", "README.rdoc"]
129
+
130
+ s.add_runtime_dependency("activesupport")
131
+ s.add_development_dependency("activerecord")
132
+ s.add_development_dependency("actionpack")
133
+ s.add_development_dependency("jferris-mocha")
134
+ s.add_development_dependency("nokogiri")
135
+ s.add_development_dependency("shoulda")
136
+
137
+ s.authors = ["thoughtbot, inc"]
138
+ s.email = %q{support@hoptoadapp.com}
139
+ s.homepage = "http://www.hoptoadapp.com"
140
+
141
+ s.platform = Gem::Platform::RUBY
142
+ end
143
+
144
+ Rake::GemPackageTask.new gemspec do |pkg|
145
+ pkg.need_tar = true
146
+ pkg.need_zip = true
147
+ end
148
+
149
+ desc "Clean files generated by rake tasks"
150
+ task :clobber => [:clobber_rdoc, :clobber_package]
151
+
152
+ desc "Generate a gemspec file"
153
+ task :gemspec do
154
+ File.open("#{gemspec.name}.gemspec", 'w') do |f|
155
+ f.write gemspec.to_ruby
156
+ end
157
+ end
158
+
159
+ LOCAL_GEM_ROOT = File.join(GEM_ROOT, 'tmp', 'local_gems').freeze
160
+ RAILS_VERSIONS = IO.read('SUPPORTED_RAILS_VERSIONS').strip.split("\n")
161
+ LOCAL_GEMS = [['sham_rack', nil], ['capistrano', nil], ['sqlite3-ruby', nil], ['sinatra', nil]] +
162
+ RAILS_VERSIONS.collect { |version| ['rails', version] }
163
+
164
+ task :vendor_test_gems do
165
+ old_gem_path = ENV['GEM_PATH']
166
+ old_gem_home = ENV['GEM_HOME']
167
+ ENV['GEM_PATH'] = LOCAL_GEM_ROOT
168
+ ENV['GEM_HOME'] = LOCAL_GEM_ROOT
169
+ LOCAL_GEMS.each do |gem_name, version|
170
+ gem_file_pattern = [gem_name, version || '*'].compact.join('-')
171
+ version_option = version ? "-v #{version}" : ''
172
+ pattern = File.join(LOCAL_GEM_ROOT, 'gems', "#{gem_file_pattern}")
173
+ existing = Dir.glob(pattern).first
174
+ unless existing
175
+ command = "gem install -i #{LOCAL_GEM_ROOT} --no-ri --no-rdoc --backtrace #{version_option} #{gem_name}"
176
+ puts "Vendoring #{gem_file_pattern}..."
177
+ unless system("#{command} 2>&1")
178
+ puts "Command failed: #{command}"
179
+ exit(1)
180
+ end
181
+ end
182
+ end
183
+ ENV['GEM_PATH'] = old_gem_path
184
+ ENV['GEM_HOME'] = old_gem_home
185
+ end
186
+
187
+ Cucumber::Rake::Task.new(:cucumber) do |t|
188
+ t.fork = true
189
+ t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'progress')]
190
+ end
191
+
192
+ task :cucumber => [:gemspec, :vendor_test_gems]
193
+
194
+ def define_rails_cucumber_tasks(additional_cucumber_args = '')
195
+ namespace :rails do
196
+ RAILS_VERSIONS.each do |version|
197
+ desc "Test integration of the gem with Rails #{version}"
198
+ task version => [:gemspec, :vendor_test_gems] do
199
+ puts "Testing Rails #{version}"
200
+ ENV['RAILS_VERSION'] = version
201
+ system("cucumber --format #{ENV['CUCUMBER_FORMAT'] || 'progress'} #{additional_cucumber_args} features/rails.feature")
202
+ end
203
+ end
204
+
205
+ desc "Test integration of the gem with all Rails versions"
206
+ task :all => RAILS_VERSIONS
207
+ end
208
+ end
209
+
210
+ namespace :cucumber do
211
+ namespace :wip do
212
+ define_rails_cucumber_tasks('--tags @wip')
213
+ end
214
+
215
+ define_rails_cucumber_tasks
216
+ end
217
+
@@ -0,0 +1,9 @@
1
+ 1.2.6
2
+ 2.0.2
3
+ 2.1.0
4
+ 2.1.2
5
+ 2.2.2
6
+ 2.3.2
7
+ 2.3.4
8
+ 2.3.5
9
+ 3.0.0.beta4
data/TESTING.rdoc ADDED
@@ -0,0 +1,8 @@
1
+ = For Maintainers:
2
+
3
+ When developing the Hoptoad Notifier, be sure to use the integration test
4
+ against an existing project on staging before pushing to master.
5
+
6
+ +./script/integration_test.rb <test project's api key> <staging server hostname>+
7
+
8
+ +./script/integration_test.rb <test project's api key> <staging server hostname> secure+
@@ -0,0 +1,54 @@
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 HoptoadGenerator < Rails::Generator::Base
5
+ def add_options!(opt)
6
+ opt.on('-k', '--api-key=key', String, "Your Hoptoad API key") {|v| options[:api_key] = v}
7
+ end
8
+
9
+ def manifest
10
+ if !api_key_configured? && !options[:api_key]
11
+ puts "Must pass --api-key or create config/initializers/hoptoad.rb"
12
+ exit
13
+ end
14
+ if plugin_is_present?
15
+ puts "You must first remove the hoptoad_notifier plugin. Please run: script/plugin remove hoptoad_notifier"
16
+ exit
17
+ end
18
+ record do |m|
19
+ m.directory 'lib/tasks'
20
+ m.file 'hoptoad_notifier_tasks.rake', 'lib/tasks/hoptoad_notifier_tasks.rake'
21
+ if ['config/deploy.rb', 'Capfile'].all? { |file| File.exists?(file) }
22
+ m.append_to 'config/deploy.rb', capistrano_hook
23
+ end
24
+ if options[:api_key]
25
+ if use_initializer?
26
+ m.template 'initializer.rb', 'config/initializers/hoptoad.rb',
27
+ :assigns => {:api_key => options[:api_key]}
28
+ else
29
+ m.template 'initializer.rb', 'config/hoptoad.rb',
30
+ :assigns => {:api_key => options[:api_key]}
31
+ m.append_to 'config/environment.rb', "require 'config/hoptoad'"
32
+ end
33
+ end
34
+ m.rake "hoptoad:test", :generate_only => true
35
+ end
36
+ end
37
+
38
+ def use_initializer?
39
+ Rails::VERSION::MAJOR > 1
40
+ end
41
+
42
+ def api_key_configured?
43
+ File.exists?('config/initializers/hoptoad.rb') ||
44
+ system("grep HoptoadNotifier config/environment.rb")
45
+ end
46
+
47
+ def capistrano_hook
48
+ IO.read(source_path('capistrano_hook.rb'))
49
+ end
50
+
51
+ def plugin_is_present?
52
+ File.exists?('vendor/plugins/hoptoad_notifier')
53
+ end
54
+ end
@@ -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,6 @@
1
+
2
+ Dir[File.join(File.dirname(__FILE__), '..', 'vendor', 'gems', 'hoptoad_notifier-*')].each do |vendored_notifier|
3
+ $: << File.join(vendored_notifier, 'lib')
4
+ end
5
+
6
+ require 'hoptoad_notifier/capistrano'
@@ -0,0 +1,25 @@
1
+ # Don't load anything when running the gems:* tasks.
2
+ # Otherwise, hoptoad_notifier will be considered a framework gem.
3
+ # https://thoughtbot.lighthouseapp.com/projects/14221/tickets/629
4
+ unless ARGV.any? {|a| a =~ /^gems/}
5
+
6
+ Dir[File.join(RAILS_ROOT, 'vendor', 'gems', 'hoptoad_notifier-*')].each do |vendored_notifier|
7
+ $: << File.join(vendored_notifier, 'lib')
8
+ end
9
+
10
+ begin
11
+ require 'hoptoad_notifier/tasks'
12
+ rescue LoadError => exception
13
+ namespace :hoptoad do
14
+ %w(deploy test log_stdout).each do |task_name|
15
+ desc "Missing dependency for hoptoad:#{task_name}"
16
+ task task_name do
17
+ $stderr.puts "Failed to run hoptoad:#{task_name} because of missing dependency."
18
+ $stderr.puts "You probably need to run `rake gems:install` to install the hoptoad_notifier gem"
19
+ abort exception.inspect
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,6 @@
1
+ <% if Rails::VERSION::MAJOR < 3 && Rails::VERSION::MINOR < 2 -%>
2
+ require 'hoptoad_notifier/rails'
3
+ <% end -%>
4
+ HoptoadNotifier.configure do |config|
5
+ config.api_key = '<%= api_key %>'
6
+ end
@@ -0,0 +1,99 @@
1
+ module HoptoadNotifier
2
+ # Front end to parsing the backtrace for each notice
3
+ class Backtrace
4
+
5
+ # Handles backtrace parsing line by line
6
+ class Line
7
+
8
+ INPUT_FORMAT = %r{^([^:]+):(\d+)(?::in `([^']+)')?$}.freeze
9
+
10
+ # The file portion of the line (such as app/models/user.rb)
11
+ attr_reader :file
12
+
13
+ # The line number portion of the line
14
+ attr_reader :number
15
+
16
+ # The method of the line (such as index)
17
+ attr_reader :method
18
+
19
+ # Parses a single line of a given backtrace
20
+ # @param [String] unparsed_line The raw line from +caller+ or some backtrace
21
+ # @return [Line] The parsed backtrace line
22
+ def self.parse(unparsed_line)
23
+ _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
24
+ new(file, number, method)
25
+ end
26
+
27
+ def initialize(file, number, method)
28
+ self.file = file
29
+ self.number = number
30
+ self.method = method
31
+ end
32
+
33
+ # Reconstructs the line in a readable fashion
34
+ def to_s
35
+ "#{file}:#{number}:in `#{method}'"
36
+ end
37
+
38
+ def ==(other)
39
+ to_s == other.to_s
40
+ end
41
+
42
+ def inspect
43
+ "<Line:#{to_s}>"
44
+ end
45
+
46
+ private
47
+
48
+ attr_writer :file, :number, :method
49
+ end
50
+
51
+ # holder for an Array of Backtrace::Line instances
52
+ attr_reader :lines
53
+
54
+ def self.parse(ruby_backtrace, opts = {})
55
+ ruby_lines = split_multiline_backtrace(ruby_backtrace)
56
+
57
+ filters = opts[:filters] || []
58
+ filtered_lines = ruby_lines.to_a.map do |line|
59
+ filters.inject(line) do |line, proc|
60
+ proc.call(line)
61
+ end
62
+ end.compact
63
+
64
+ lines = filtered_lines.collect do |unparsed_line|
65
+ Line.parse(unparsed_line)
66
+ end
67
+
68
+ instance = new(lines)
69
+ end
70
+
71
+ def initialize(lines)
72
+ self.lines = lines
73
+ end
74
+
75
+ def inspect
76
+ "<Backtrace: " + lines.collect { |line| line.inspect }.join(", ") + ">"
77
+ end
78
+
79
+ def ==(other)
80
+ if other.respond_to?(:lines)
81
+ lines == other.lines
82
+ else
83
+ false
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ attr_writer :lines
90
+
91
+ def self.split_multiline_backtrace(backtrace)
92
+ if backtrace.to_a.size == 1
93
+ backtrace.to_a.first.split(/\n\s*/)
94
+ else
95
+ backtrace
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,20 @@
1
+ # Defines deploy:notify_hoptoad which will send information about the deploy to Hoptoad.
2
+
3
+ Capistrano::Configuration.instance(:must_exist).load do
4
+ after "deploy", "deploy:notify_hoptoad"
5
+ after "deploy:migrations", "deploy:notify_hoptoad"
6
+
7
+ namespace :deploy do
8
+ desc "Notify Hoptoad of the deployment"
9
+ task :notify_hoptoad, :except => { :no_release => true } do
10
+ rails_env = fetch(:hoptoad_env, fetch(:rails_env, "production"))
11
+ local_user = ENV['USER'] || ENV['USERNAME']
12
+ executable = RUBY_PLATFORM.downcase.include?('mswin') ? 'rake.bat' : 'rake'
13
+ notify_command = "#{executable} hoptoad:deploy TO=#{rails_env} REVISION=#{current_revision} REPO=#{repository} USER=#{local_user}"
14
+ notify_command << " API_KEY=#{ENV['API_KEY']}" if ENV['API_KEY']
15
+ puts "Notifying Hoptoad of Deploy (#{notify_command})"
16
+ `#{notify_command}`
17
+ puts "Hoptoad Notification Complete."
18
+ end
19
+ end
20
+ end