square-hoptoad_notifier 2.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGELOG +427 -0
  2. data/INSTALL +25 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.md +435 -0
  5. data/README_FOR_HEROKU_ADDON.md +93 -0
  6. data/Rakefile +227 -0
  7. data/SUPPORTED_RAILS_VERSIONS +10 -0
  8. data/TESTING.rdoc +8 -0
  9. data/generators/hoptoad/hoptoad_generator.rb +88 -0
  10. data/generators/hoptoad/lib/insert_commands.rb +34 -0
  11. data/generators/hoptoad/lib/rake_commands.rb +24 -0
  12. data/generators/hoptoad/templates/capistrano_hook.rb +6 -0
  13. data/generators/hoptoad/templates/hoptoad_notifier_tasks.rake +25 -0
  14. data/generators/hoptoad/templates/initializer.rb +6 -0
  15. data/lib/hoptoad_notifier.rb +153 -0
  16. data/lib/hoptoad_notifier/backtrace.rb +99 -0
  17. data/lib/hoptoad_notifier/capistrano.rb +20 -0
  18. data/lib/hoptoad_notifier/configuration.rb +242 -0
  19. data/lib/hoptoad_notifier/notice.rb +337 -0
  20. data/lib/hoptoad_notifier/rack.rb +42 -0
  21. data/lib/hoptoad_notifier/rails.rb +41 -0
  22. data/lib/hoptoad_notifier/rails/action_controller_catcher.rb +30 -0
  23. data/lib/hoptoad_notifier/rails/controller_methods.rb +68 -0
  24. data/lib/hoptoad_notifier/rails/error_lookup.rb +33 -0
  25. data/lib/hoptoad_notifier/rails/javascript_notifier.rb +42 -0
  26. data/lib/hoptoad_notifier/rails3_tasks.rb +82 -0
  27. data/lib/hoptoad_notifier/railtie.rb +32 -0
  28. data/lib/hoptoad_notifier/sender.rb +83 -0
  29. data/lib/hoptoad_notifier/shared_tasks.rb +29 -0
  30. data/lib/hoptoad_notifier/tasks.rb +83 -0
  31. data/lib/hoptoad_notifier/user_informer.rb +23 -0
  32. data/lib/hoptoad_notifier/version.rb +3 -0
  33. data/lib/hoptoad_tasks.rb +44 -0
  34. data/lib/rails/generators/hoptoad/hoptoad_generator.rb +94 -0
  35. data/lib/templates/javascript_notifier.erb +13 -0
  36. data/lib/templates/rescue.erb +91 -0
  37. data/rails/init.rb +1 -0
  38. data/script/integration_test.rb +38 -0
  39. data/test/backtrace_test.rb +118 -0
  40. data/test/catcher_test.rb +331 -0
  41. data/test/configuration_test.rb +216 -0
  42. data/test/helper.rb +248 -0
  43. data/test/hoptoad_tasks_test.rb +152 -0
  44. data/test/javascript_notifier_test.rb +52 -0
  45. data/test/logger_test.rb +85 -0
  46. data/test/notice_test.rb +448 -0
  47. data/test/notifier_test.rb +222 -0
  48. data/test/rack_test.rb +58 -0
  49. data/test/rails_initializer_test.rb +36 -0
  50. data/test/sender_test.rb +161 -0
  51. data/test/user_informer_test.rb +29 -0
  52. metadata +225 -0
@@ -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_expression %>
6
+ end
@@ -0,0 +1,153 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'rubygems'
4
+ begin
5
+ require 'active_support'
6
+ rescue LoadError
7
+ require 'activesupport'
8
+ end
9
+ require 'hoptoad_notifier/version'
10
+ require 'hoptoad_notifier/configuration'
11
+ require 'hoptoad_notifier/notice'
12
+ require 'hoptoad_notifier/sender'
13
+ require 'hoptoad_notifier/backtrace'
14
+ require 'hoptoad_notifier/rack'
15
+ require 'hoptoad_notifier/user_informer'
16
+
17
+ require 'hoptoad_notifier/railtie' if defined?(Rails::Railtie)
18
+
19
+ # Gem for applications to automatically post errors to the Hoptoad of their choice.
20
+ module HoptoadNotifier
21
+
22
+ API_VERSION = "2.0"
23
+ LOG_PREFIX = "** [Hoptoad] "
24
+
25
+ HEADERS = {
26
+ 'Content-type' => 'text/xml',
27
+ 'Accept' => 'text/xml, application/xml'
28
+ }
29
+
30
+ class << self
31
+ # The sender object is responsible for delivering formatted data to the Hoptoad server.
32
+ # Must respond to #send_to_hoptoad. See HoptoadNotifier::Sender.
33
+ attr_accessor :sender
34
+
35
+ # A Hoptoad configuration object. Must act like a hash and return sensible
36
+ # values for all Hoptoad configuration options. See HoptoadNotifier::Configuration.
37
+ attr_accessor :configuration
38
+
39
+ # Tell the log that the Notifier is good to go
40
+ def report_ready
41
+ write_verbose_log("Notifier #{VERSION} ready to catch errors")
42
+ end
43
+
44
+ # Prints out the environment info to the log for debugging help
45
+ def report_environment_info
46
+ write_verbose_log("Environment Info: #{environment_info}")
47
+ end
48
+
49
+ # Prints out the response body from Hoptoad for debugging help
50
+ def report_response_body(response)
51
+ write_verbose_log("Response from Hoptoad: \n#{response}")
52
+ end
53
+
54
+ # Returns the Ruby version, Rails version, and current Rails environment
55
+ def environment_info
56
+ info = "[Ruby: #{RUBY_VERSION}]"
57
+ info << " [#{configuration.framework}]"
58
+ info << " [Env: #{configuration.environment_name}]"
59
+ end
60
+
61
+ # Writes out the given message to the #logger
62
+ def write_verbose_log(message)
63
+ logger.info LOG_PREFIX + message if logger
64
+ end
65
+
66
+ # Look for the Rails logger currently defined
67
+ def logger
68
+ self.configuration.logger
69
+ end
70
+
71
+ # Call this method to modify defaults in your initializers.
72
+ #
73
+ # @example
74
+ # HoptoadNotifier.configure do |config|
75
+ # config.api_key = '1234567890abcdef'
76
+ # config.secure = false
77
+ # end
78
+ def configure(silent = false)
79
+ self.configuration ||= Configuration.new
80
+ yield(configuration)
81
+ self.sender = Sender.new(configuration)
82
+ report_ready unless silent
83
+ end
84
+
85
+ # Sends an exception manually using this method, even when you are not in a controller.
86
+ #
87
+ # @param [Exception] exception The exception you want to notify Hoptoad about.
88
+ # @param [Hash] opts Data that will be sent to Hoptoad.
89
+ #
90
+ # @option opts [String] :api_key The API key for this project. The API key is a unique identifier that Hoptoad uses for identification.
91
+ # @option opts [String] :error_message The error returned by the exception (or the message you want to log).
92
+ # @option opts [String] :backtrace A backtrace, usually obtained with +caller+.
93
+ # @option opts [String] :request The controller's request object.
94
+ # @option opts [String] :session The contents of the user's session.
95
+ # @option opts [String] :environment ENV merged with the contents of the request's environment.
96
+ def notify(exception, opts = {})
97
+ send_notice(build_notice_for(exception, opts))
98
+ end
99
+
100
+ # Sends the notice unless it is one of the default ignored exceptions
101
+ # @see HoptoadNotifier.notify
102
+ def notify_or_ignore(exception, opts = {})
103
+ notice = build_notice_for(exception, opts)
104
+ send_notice(notice) unless notice.ignore?
105
+ end
106
+
107
+ def build_lookup_hash_for(exception, options = {})
108
+ notice = build_notice_for(exception, options)
109
+
110
+ result = {}
111
+ result[:action] = notice.action rescue nil
112
+ result[:component] = notice.component rescue nil
113
+ result[:error_class] = notice.error_class if notice.error_class
114
+ result[:environment_name] = 'production'
115
+
116
+ unless notice.backtrace.lines.empty?
117
+ result[:file] = notice.backtrace.lines.first.file
118
+ result[:line_number] = notice.backtrace.lines.first.number
119
+ end
120
+
121
+ result
122
+ end
123
+
124
+ private
125
+
126
+ def send_notice(notice)
127
+ if configuration.public?
128
+ sender.send_to_hoptoad(notice.to_xml)
129
+ end
130
+ end
131
+
132
+ def build_notice_for(exception, opts = {})
133
+ exception = unwrap_exception(exception)
134
+ if exception.respond_to?(:to_hash)
135
+ opts = opts.merge(exception.to_hash)
136
+ else
137
+ opts = opts.merge(:exception => exception)
138
+ end
139
+ Notice.new(configuration.merge(opts))
140
+ end
141
+
142
+ def unwrap_exception(exception)
143
+ if exception.respond_to?(:original_exception)
144
+ exception.original_exception
145
+ elsif exception.respond_to?(:continued_exception)
146
+ exception.continued_exception
147
+ else
148
+ exception
149
+ end
150
+ end
151
+ end
152
+ end
153
+
@@ -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
@@ -0,0 +1,242 @@
1
+ module HoptoadNotifier
2
+ # Used to set up and modify settings for the notifier.
3
+ class Configuration
4
+
5
+ OPTIONS = [:api_key, :backtrace_filters, :development_environments,
6
+ :development_lookup, :environment_name, :host,
7
+ :http_open_timeout, :http_read_timeout, :ignore, :ignore_by_filters,
8
+ :ignore_user_agent, :notifier_name, :notifier_url, :notifier_version,
9
+ :params_filters, :project_root, :port, :protocol, :proxy_host,
10
+ :proxy_pass, :proxy_port, :proxy_user, :secure, :framework,
11
+ :user_information].freeze
12
+
13
+ # The API key for your project, found on the project edit form.
14
+ attr_accessor :api_key
15
+
16
+ # The host to connect to (defaults to hoptoadapp.com).
17
+ attr_accessor :host
18
+
19
+ # The port on which your Hoptoad server runs (defaults to 443 for secure
20
+ # connections, 80 for insecure connections).
21
+ attr_accessor :port
22
+
23
+ # +true+ for https connections, +false+ for http connections.
24
+ attr_accessor :secure
25
+
26
+ # The HTTP open timeout in seconds (defaults to 2).
27
+ attr_accessor :http_open_timeout
28
+
29
+ # The HTTP read timeout in seconds (defaults to 5).
30
+ attr_accessor :http_read_timeout
31
+
32
+ # The hostname of your proxy server (if using a proxy)
33
+ attr_accessor :proxy_host
34
+
35
+ # The port of your proxy server (if using a proxy)
36
+ attr_accessor :proxy_port
37
+
38
+ # The username to use when logging into your proxy server (if using a proxy)
39
+ attr_accessor :proxy_user
40
+
41
+ # The password to use when logging into your proxy server (if using a proxy)
42
+ attr_accessor :proxy_pass
43
+
44
+ # A list of parameters that should be filtered out of what is sent to Hoptoad.
45
+ # By default, all "password" attributes will have their contents replaced.
46
+ attr_reader :params_filters
47
+
48
+ # A list of filters for cleaning and pruning the backtrace. See #filter_backtrace.
49
+ attr_reader :backtrace_filters
50
+
51
+ # A list of filters for ignoring exceptions. See #ignore_by_filter.
52
+ attr_reader :ignore_by_filters
53
+
54
+ # A list of exception classes to ignore. The array can be appended to.
55
+ attr_reader :ignore
56
+
57
+ # A list of user agents that are being ignored. The array can be appended to.
58
+ attr_reader :ignore_user_agent
59
+
60
+ # A list of environments in which notifications should not be sent.
61
+ attr_accessor :development_environments
62
+
63
+ # +true+ if you want to check for production errors matching development errors, +false+ otherwise.
64
+ attr_accessor :development_lookup
65
+
66
+ # The name of the environment the application is running in
67
+ attr_accessor :environment_name
68
+
69
+ # The path to the project in which the error occurred, such as the RAILS_ROOT
70
+ attr_accessor :project_root
71
+
72
+ # The name of the notifier library being used to send notifications (such as "Hoptoad Notifier")
73
+ attr_accessor :notifier_name
74
+
75
+ # The version of the notifier library being used to send notifications (such as "1.0.2")
76
+ attr_accessor :notifier_version
77
+
78
+ # The url of the notifier library being used to send notifications
79
+ attr_accessor :notifier_url
80
+
81
+ # The logger used by HoptoadNotifier
82
+ attr_accessor :logger
83
+
84
+ # The text that the placeholder is replaced with. {{error_id}} is the actual error number.
85
+ attr_accessor :user_information
86
+
87
+ # The framework HoptoadNotifier is configured to use
88
+ attr_accessor :framework
89
+
90
+ DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
91
+
92
+ DEFAULT_BACKTRACE_FILTERS = [
93
+ lambda { |line|
94
+ if defined?(HoptoadNotifier.configuration.project_root) && HoptoadNotifier.configuration.project_root.to_s != ''
95
+ line.gsub(/#{HoptoadNotifier.configuration.project_root}/, "[PROJECT_ROOT]")
96
+ else
97
+ line
98
+ end
99
+ },
100
+ lambda { |line| line.gsub(/^\.\//, "") },
101
+ lambda { |line|
102
+ if defined?(Gem)
103
+ Gem.path.inject(line) do |line, path|
104
+ line.gsub(/#{path}/, "[GEM_ROOT]")
105
+ end
106
+ end
107
+ },
108
+ lambda { |line| line if line !~ %r{lib/hoptoad_notifier} }
109
+ ].freeze
110
+
111
+ IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
112
+ 'ActionController::RoutingError',
113
+ 'ActionController::InvalidAuthenticityToken',
114
+ 'CGI::Session::CookieStore::TamperedWithCookie',
115
+ 'ActionController::UnknownAction',
116
+ 'AbstractController::ActionNotFound']
117
+
118
+ alias_method :secure?, :secure
119
+
120
+ def initialize
121
+ @secure = false
122
+ @host = 'hoptoadapp.com'
123
+ @http_open_timeout = 2
124
+ @http_read_timeout = 5
125
+ @params_filters = DEFAULT_PARAMS_FILTERS.dup
126
+ @backtrace_filters = DEFAULT_BACKTRACE_FILTERS.dup
127
+ @ignore_by_filters = []
128
+ @ignore = IGNORE_DEFAULT.dup
129
+ @ignore_user_agent = []
130
+ @development_environments = %w(development test cucumber)
131
+ @development_lookup = true
132
+ @notifier_name = 'Hoptoad Notifier'
133
+ @notifier_version = VERSION
134
+ @notifier_url = 'http://hoptoadapp.com'
135
+ @framework = 'Standalone'
136
+ @user_information = 'Hoptoad Error {{error_id}}'
137
+ end
138
+
139
+ # Takes a block and adds it to the list of backtrace filters. When the filters
140
+ # run, the block will be handed each line of the backtrace and can modify
141
+ # it as necessary.
142
+ #
143
+ # @example
144
+ # config.filter_bracktrace do |line|
145
+ # line.gsub(/^#{Rails.root}/, "[RAILS_ROOT]")
146
+ # end
147
+ #
148
+ # @param [Proc] block The new backtrace filter.
149
+ # @yieldparam [String] line A line in the backtrace.
150
+ def filter_backtrace(&block)
151
+ self.backtrace_filters << block
152
+ end
153
+
154
+ # Takes a block and adds it to the list of ignore filters.
155
+ # When the filters run, the block will be handed the exception.
156
+ # @example
157
+ # config.ignore_by_filter do |exception_data|
158
+ # true if exception_data[:error_class] == "RuntimeError"
159
+ # end
160
+ #
161
+ # @param [Proc] block The new ignore filter
162
+ # @yieldparam [Hash] data The exception data given to +HoptoadNotifier.notify+
163
+ # @yieldreturn [Boolean] If the block returns true the exception will be ignored, otherwise it will be processed by hoptoad.
164
+ def ignore_by_filter(&block)
165
+ self.ignore_by_filters << block
166
+ end
167
+
168
+ # Overrides the list of default ignored errors.
169
+ #
170
+ # @param [Array<Exception>] names A list of exceptions to ignore.
171
+ def ignore_only=(names)
172
+ @ignore = [names].flatten
173
+ end
174
+
175
+ # Overrides the list of default ignored user agents
176
+ #
177
+ # @param [Array<String>] A list of user agents to ignore
178
+ def ignore_user_agent_only=(names)
179
+ @ignore_user_agent = [names].flatten
180
+ end
181
+
182
+ # Allows config options to be read like a hash
183
+ #
184
+ # @param [Symbol] option Key for a given attribute
185
+ def [](option)
186
+ send(option)
187
+ end
188
+
189
+ # Returns a hash of all configurable options
190
+ def to_hash
191
+ OPTIONS.inject({}) do |hash, option|
192
+ hash.merge(option.to_sym => send(option))
193
+ end
194
+ end
195
+
196
+ # Returns a hash of all configurable options merged with +hash+
197
+ #
198
+ # @param [Hash] hash A set of configuration options that will take precedence over the defaults
199
+ def merge(hash)
200
+ to_hash.merge(hash)
201
+ end
202
+
203
+ # Determines if the notifier will send notices.
204
+ # @return [Boolean] Returns +false+ if in a development environment, +true+ otherwise.
205
+ def public?
206
+ !development_environments.include?(environment_name)
207
+ end
208
+
209
+ def port
210
+ @port || default_port
211
+ end
212
+
213
+ def protocol
214
+ if secure?
215
+ 'https'
216
+ else
217
+ 'http'
218
+ end
219
+ end
220
+
221
+ def js_notifier=(*args)
222
+ warn '[HOPTOAD] config.js_notifier has been deprecated and has no effect. You should use <%= hoptoad_javascript_notifier %> directly at the top of your layouts. Be sure to place it before all other javascript.'
223
+ end
224
+
225
+ def environment_filters
226
+ warn 'config.environment_filters has been deprecated and has no effect.'
227
+ []
228
+ end
229
+
230
+ private
231
+
232
+ def default_port
233
+ if secure?
234
+ 443
235
+ else
236
+ 80
237
+ end
238
+ end
239
+
240
+ end
241
+
242
+ end