base_rails_app 0.0.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 (194) hide show
  1. data/LICENSE +1 -0
  2. data/README +1 -0
  3. data/lib/app/controllers/application_controller.rb +55 -0
  4. data/lib/app/controllers/password_resets_controller.rb +45 -0
  5. data/lib/app/controllers/user_sessions_controller.rb +25 -0
  6. data/lib/app/controllers/users_controller.rb +37 -0
  7. data/lib/app/helpers/application_helper.rb +4 -0
  8. data/lib/app/helpers/user_sessions_helper.rb +2 -0
  9. data/lib/app/helpers/users_helper.rb +2 -0
  10. data/lib/app/models/postman.rb +20 -0
  11. data/lib/app/models/user.rb +12 -0
  12. data/lib/app/models/user_session.rb +2 -0
  13. data/lib/app/views/application/404_not_found.html.erb +8 -0
  14. data/lib/app/views/layouts/application.html.erb +57 -0
  15. data/lib/app/views/password_resets/_sidebar.html.erb +0 -0
  16. data/lib/app/views/password_resets/edit.html.erb +33 -0
  17. data/lib/app/views/password_resets/new.html.erb +26 -0
  18. data/lib/app/views/postman/password_reset_instructions.html.erb +3 -0
  19. data/lib/app/views/postman/welcome_email.html.erb +3 -0
  20. data/lib/app/views/user_sessions/_form.html.erb +19 -0
  21. data/lib/app/views/user_sessions/_sidebar.html.erb +13 -0
  22. data/lib/app/views/user_sessions/new.html.erb +15 -0
  23. data/lib/app/views/users/_form.html.erb +28 -0
  24. data/lib/app/views/users/_sidebar.html.erb +6 -0
  25. data/lib/app/views/users/edit.html.erb +19 -0
  26. data/lib/app/views/users/new.html.erb +15 -0
  27. data/lib/app/views/users/show.html.erb +46 -0
  28. data/lib/base_rails_app.rb +5 -0
  29. data/lib/base_rails_app/boot.rb +2 -0
  30. data/lib/base_rails_app/tasks.rb +12 -0
  31. data/lib/config/configatron/cucumber.rb +0 -0
  32. data/lib/config/configatron/defaults.rb +4 -0
  33. data/lib/config/configatron/development.rb +1 -0
  34. data/lib/config/configatron/production.rb +0 -0
  35. data/lib/config/configatron/test.rb +0 -0
  36. data/lib/config/database.yml +25 -0
  37. data/lib/config/deploy.rb +15 -0
  38. data/lib/config/environments/cucumber.rb +22 -0
  39. data/lib/config/environments/development.rb +17 -0
  40. data/lib/config/environments/production.rb +28 -0
  41. data/lib/config/environments/test.rb +28 -0
  42. data/lib/config/gemtronics.rb +36 -0
  43. data/lib/config/initializers/backtrace_silencers.rb +7 -0
  44. data/lib/config/initializers/configatron.rb +1 -0
  45. data/lib/config/initializers/hoptoad.rb +3 -0
  46. data/lib/config/initializers/inflections.rb +10 -0
  47. data/lib/config/initializers/mailers.rb +1 -0
  48. data/lib/config/initializers/mime_types.rb +5 -0
  49. data/lib/config/initializers/new_rails_defaults.rb +21 -0
  50. data/lib/config/initializers/session_store.rb +17 -0
  51. data/lib/config/locales/en.yml +5 -0
  52. data/lib/config/routes.rb +51 -0
  53. data/lib/config/schedule.rb +20 -0
  54. data/lib/db/migrate/20090809175903_create_users.rb +30 -0
  55. data/lib/db/migrate/20090809204840_acts_as_taggable_on_migration.rb +29 -0
  56. data/lib/db/migrate/20090912171353_create_delayed_jobs.rb +20 -0
  57. data/lib/db/migrate/20090912171829_add_delayed_job_extras.rb +11 -0
  58. data/lib/db/migrate/20090929201455_add_more_time_columns_to_dj.rb +11 -0
  59. data/lib/db/schema.rb +72 -0
  60. data/lib/db/seeds.rb +7 -0
  61. data/lib/lib/tasks/cucumber.rake +36 -0
  62. data/lib/lib/tasks/rcov.rake +27 -0
  63. data/lib/lib/tasks/rspec.rake +183 -0
  64. data/lib/vendor/plugins/delayed_job/generators/delayed_job/delayed_job_generator.rb +22 -0
  65. data/lib/vendor/plugins/delayed_job/generators/delayed_job/templates/migration.rb +20 -0
  66. data/lib/vendor/plugins/delayed_job/init.rb +2 -0
  67. data/lib/vendor/plugins/delayed_job/lib/delayed/command.rb +65 -0
  68. data/lib/vendor/plugins/delayed_job/lib/delayed/job.rb +271 -0
  69. data/lib/vendor/plugins/delayed_job/lib/delayed/message_sending.rb +18 -0
  70. data/lib/vendor/plugins/delayed_job/lib/delayed/performable_method.rb +55 -0
  71. data/lib/vendor/plugins/delayed_job/lib/delayed/tasks.rb +15 -0
  72. data/lib/vendor/plugins/delayed_job/lib/delayed/worker.rb +54 -0
  73. data/lib/vendor/plugins/delayed_job/lib/delayed_job.rb +13 -0
  74. data/lib/vendor/plugins/delayed_job/recipes/delayed_job.rb +26 -0
  75. data/lib/vendor/plugins/delayed_job/tasks/jobs.rake +1 -0
  76. data/lib/vendor/plugins/hoptoad_notifier/install.rb +1 -0
  77. data/lib/vendor/plugins/hoptoad_notifier/lib/hoptoad_notifier.rb +418 -0
  78. data/lib/vendor/plugins/hoptoad_notifier/lib/hoptoad_tasks.rb +26 -0
  79. data/lib/vendor/plugins/hoptoad_notifier/recipes/hoptoad.rb +22 -0
  80. data/lib/vendor/plugins/hoptoad_notifier/tasks/hoptoad_notifier_tasks.rake +66 -0
  81. data/lib/vendor/plugins/inherited_resources/README.rdoc +524 -0
  82. data/lib/vendor/plugins/inherited_resources/inherited_resources.gemspec +71 -0
  83. data/lib/vendor/plugins/inherited_resources/init.rb +1 -0
  84. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources.rb +23 -0
  85. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/actions.rb +79 -0
  86. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/base.rb +42 -0
  87. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/base_helpers.rb +363 -0
  88. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/belongs_to_helpers.rb +89 -0
  89. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/class_methods.rb +338 -0
  90. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/dsl.rb +26 -0
  91. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/dumb_responder.rb +20 -0
  92. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/has_scope_helpers.rb +83 -0
  93. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/legacy/respond_to.rb +156 -0
  94. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/legacy/responder.rb +200 -0
  95. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/polymorphic_helpers.rb +155 -0
  96. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/singleton_helpers.rb +95 -0
  97. data/lib/vendor/plugins/inherited_resources/lib/inherited_resources/url_helpers.rb +179 -0
  98. data/lib/vendor/plugins/inherited_resources/test/aliases_test.rb +139 -0
  99. data/lib/vendor/plugins/inherited_resources/test/association_chain_test.rb +125 -0
  100. data/lib/vendor/plugins/inherited_resources/test/base_test.rb +225 -0
  101. data/lib/vendor/plugins/inherited_resources/test/belongs_to_test.rb +87 -0
  102. data/lib/vendor/plugins/inherited_resources/test/class_methods_test.rb +138 -0
  103. data/lib/vendor/plugins/inherited_resources/test/customized_base_test.rb +162 -0
  104. data/lib/vendor/plugins/inherited_resources/test/customized_belongs_to_test.rb +76 -0
  105. data/lib/vendor/plugins/inherited_resources/test/defaults_test.rb +70 -0
  106. data/lib/vendor/plugins/inherited_resources/test/flash_test.rb +88 -0
  107. data/lib/vendor/plugins/inherited_resources/test/has_scope_test.rb +139 -0
  108. data/lib/vendor/plugins/inherited_resources/test/locales/en.yml +17 -0
  109. data/lib/vendor/plugins/inherited_resources/test/nested_belongs_to_test.rb +108 -0
  110. data/lib/vendor/plugins/inherited_resources/test/optional_belongs_to_test.rb +164 -0
  111. data/lib/vendor/plugins/inherited_resources/test/polymorphic_test.rb +186 -0
  112. data/lib/vendor/plugins/inherited_resources/test/redirect_to_test.rb +51 -0
  113. data/lib/vendor/plugins/inherited_resources/test/respond_to_test.rb +155 -0
  114. data/lib/vendor/plugins/inherited_resources/test/singleton_test.rb +83 -0
  115. data/lib/vendor/plugins/inherited_resources/test/test_helper.rb +38 -0
  116. data/lib/vendor/plugins/inherited_resources/test/url_helpers_test.rb +537 -0
  117. data/lib/vendor/plugins/inherited_resources/test/views/cars/edit.html.erb +1 -0
  118. data/lib/vendor/plugins/inherited_resources/test/views/cars/index.html.erb +1 -0
  119. data/lib/vendor/plugins/inherited_resources/test/views/cars/new.html.erb +1 -0
  120. data/lib/vendor/plugins/inherited_resources/test/views/cars/show.html.erb +1 -0
  121. data/lib/vendor/plugins/inherited_resources/test/views/cities/edit.html.erb +1 -0
  122. data/lib/vendor/plugins/inherited_resources/test/views/cities/index.html.erb +1 -0
  123. data/lib/vendor/plugins/inherited_resources/test/views/cities/new.html.erb +1 -0
  124. data/lib/vendor/plugins/inherited_resources/test/views/cities/show.html.erb +1 -0
  125. data/lib/vendor/plugins/inherited_resources/test/views/comments/edit.html.erb +1 -0
  126. data/lib/vendor/plugins/inherited_resources/test/views/comments/index.html.erb +1 -0
  127. data/lib/vendor/plugins/inherited_resources/test/views/comments/new.html.erb +1 -0
  128. data/lib/vendor/plugins/inherited_resources/test/views/comments/show.html.erb +1 -0
  129. data/lib/vendor/plugins/inherited_resources/test/views/employees/edit.html.erb +1 -0
  130. data/lib/vendor/plugins/inherited_resources/test/views/employees/index.html.erb +1 -0
  131. data/lib/vendor/plugins/inherited_resources/test/views/employees/new.html.erb +1 -0
  132. data/lib/vendor/plugins/inherited_resources/test/views/employees/show.html.erb +1 -0
  133. data/lib/vendor/plugins/inherited_resources/test/views/managers/edit.html.erb +1 -0
  134. data/lib/vendor/plugins/inherited_resources/test/views/managers/new.html.erb +1 -0
  135. data/lib/vendor/plugins/inherited_resources/test/views/managers/show.html.erb +1 -0
  136. data/lib/vendor/plugins/inherited_resources/test/views/painters/edit.html.erb +1 -0
  137. data/lib/vendor/plugins/inherited_resources/test/views/painters/index.html.erb +1 -0
  138. data/lib/vendor/plugins/inherited_resources/test/views/painters/new.html.erb +1 -0
  139. data/lib/vendor/plugins/inherited_resources/test/views/painters/show.html.erb +1 -0
  140. data/lib/vendor/plugins/inherited_resources/test/views/pets/edit.html.erb +1 -0
  141. data/lib/vendor/plugins/inherited_resources/test/views/pets/index.html.erb +1 -0
  142. data/lib/vendor/plugins/inherited_resources/test/views/pets/new.html.erb +1 -0
  143. data/lib/vendor/plugins/inherited_resources/test/views/pets/show.html.erb +1 -0
  144. data/lib/vendor/plugins/inherited_resources/test/views/products/edit.html.erb +1 -0
  145. data/lib/vendor/plugins/inherited_resources/test/views/products/index.html.erb +1 -0
  146. data/lib/vendor/plugins/inherited_resources/test/views/products/new.html.erb +1 -0
  147. data/lib/vendor/plugins/inherited_resources/test/views/products/show.html.erb +1 -0
  148. data/lib/vendor/plugins/inherited_resources/test/views/professors/edit.html.erb +1 -0
  149. data/lib/vendor/plugins/inherited_resources/test/views/professors/index.html.erb +1 -0
  150. data/lib/vendor/plugins/inherited_resources/test/views/professors/new.html.erb +1 -0
  151. data/lib/vendor/plugins/inherited_resources/test/views/professors/show.html.erb +1 -0
  152. data/lib/vendor/plugins/inherited_resources/test/views/projects/index.html.erb +1 -0
  153. data/lib/vendor/plugins/inherited_resources/test/views/projects/index.json.erb +1 -0
  154. data/lib/vendor/plugins/inherited_resources/test/views/projects/respond_to_skip_default_template.html.erb +1 -0
  155. data/lib/vendor/plugins/inherited_resources/test/views/projects/respond_with_resource.html.erb +1 -0
  156. data/lib/vendor/plugins/inherited_resources/test/views/students/edit.html.erb +1 -0
  157. data/lib/vendor/plugins/inherited_resources/test/views/students/new.html.erb +1 -0
  158. data/lib/vendor/plugins/inherited_resources/test/views/trees/edit.html.erb +1 -0
  159. data/lib/vendor/plugins/inherited_resources/test/views/trees/index.html.erb +1 -0
  160. data/lib/vendor/plugins/inherited_resources/test/views/trees/new.html.erb +1 -0
  161. data/lib/vendor/plugins/inherited_resources/test/views/trees/show.html.erb +1 -0
  162. data/lib/vendor/plugins/inherited_resources/test/views/users/edit.html.erb +1 -0
  163. data/lib/vendor/plugins/inherited_resources/test/views/users/index.html.erb +1 -0
  164. data/lib/vendor/plugins/inherited_resources/test/views/users/new.html.erb +1 -0
  165. data/lib/vendor/plugins/inherited_resources/test/views/users/show.html.erb +1 -0
  166. data/lib/vendor/plugins/web-app-theme/README.md +22 -0
  167. data/lib/vendor/plugins/web-app-theme/images/avatar.png +0 -0
  168. data/lib/vendor/plugins/web-app-theme/index.html +473 -0
  169. data/lib/vendor/plugins/web-app-theme/javascripts/jquery-1.3.min.js +19 -0
  170. data/lib/vendor/plugins/web-app-theme/javascripts/jquery.localscroll.js +104 -0
  171. data/lib/vendor/plugins/web-app-theme/javascripts/jquery.scrollTo.js +150 -0
  172. data/lib/vendor/plugins/web-app-theme/rails_generators/theme/templates/view_layout_administration.html.erb +48 -0
  173. data/lib/vendor/plugins/web-app-theme/rails_generators/theme/templates/view_layout_sign.html.erb +15 -0
  174. data/lib/vendor/plugins/web-app-theme/rails_generators/theme/theme_generator.rb +38 -0
  175. data/lib/vendor/plugins/web-app-theme/rails_generators/themed/templates/view_edit.html.erb +20 -0
  176. data/lib/vendor/plugins/web-app-theme/rails_generators/themed/templates/view_form.html.erb +10 -0
  177. data/lib/vendor/plugins/web-app-theme/rails_generators/themed/templates/view_new.html.erb +19 -0
  178. data/lib/vendor/plugins/web-app-theme/rails_generators/themed/templates/view_show.html.erb +23 -0
  179. data/lib/vendor/plugins/web-app-theme/rails_generators/themed/templates/view_sidebar.html.erb +13 -0
  180. data/lib/vendor/plugins/web-app-theme/rails_generators/themed/templates/view_signin.html.erb +39 -0
  181. data/lib/vendor/plugins/web-app-theme/rails_generators/themed/templates/view_signup.html.erb +56 -0
  182. data/lib/vendor/plugins/web-app-theme/rails_generators/themed/templates/view_tables.html.erb +50 -0
  183. data/lib/vendor/plugins/web-app-theme/rails_generators/themed/themed_generator.rb +89 -0
  184. data/lib/vendor/plugins/web-app-theme/stylesheets/base.css +336 -0
  185. data/lib/vendor/plugins/web-app-theme/stylesheets/themes/bec-green/style.css +290 -0
  186. data/lib/vendor/plugins/web-app-theme/stylesheets/themes/bec/style.css +301 -0
  187. data/lib/vendor/plugins/web-app-theme/stylesheets/themes/blue/style.css +280 -0
  188. data/lib/vendor/plugins/web-app-theme/stylesheets/themes/default/style.css +267 -0
  189. data/lib/vendor/plugins/web-app-theme/stylesheets/themes/djime-cerulean/style.css +298 -0
  190. data/lib/vendor/plugins/web-app-theme/stylesheets/themes/drastic-dark/style.css +373 -0
  191. data/lib/vendor/plugins/web-app-theme/stylesheets/themes/kathleene/style.css +272 -0
  192. data/lib/vendor/plugins/web-app-theme/stylesheets/themes/orange/style.css +263 -0
  193. data/lib/vendor/plugins/web-app-theme/stylesheets/themes/reidb-greenish/style.css +301 -0
  194. metadata +257 -0
@@ -0,0 +1,15 @@
1
+ # Re-definitions are appended to existing tasks
2
+ task :environment
3
+ task :merb_env
4
+
5
+ namespace :jobs do
6
+ desc "Clear the delayed_job queue."
7
+ task :clear => [:merb_env, :environment] do
8
+ Delayed::Job.delete_all
9
+ end
10
+
11
+ desc "Start a delayed_job worker."
12
+ task :work => [:merb_env, :environment] do
13
+ Delayed::Worker.new(:min_priority => ENV['MIN_PRIORITY'], :max_priority => ENV['MAX_PRIORITY']).start
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ module Delayed
2
+ class Worker
3
+ SLEEP = 5
4
+
5
+ cattr_accessor :logger
6
+ self.logger = if defined?(Merb::Logger)
7
+ Merb.logger
8
+ elsif defined?(RAILS_DEFAULT_LOGGER)
9
+ RAILS_DEFAULT_LOGGER
10
+ end
11
+
12
+ def initialize(options={})
13
+ @quiet = options[:quiet]
14
+ Delayed::Job.min_priority = options[:min_priority] if options.has_key?(:min_priority)
15
+ Delayed::Job.max_priority = options[:max_priority] if options.has_key?(:max_priority)
16
+ end
17
+
18
+ def start
19
+ say "*** Starting job worker #{Delayed::Job.worker_name}"
20
+
21
+ trap('TERM') { say 'Exiting...'; $exit = true }
22
+ trap('INT') { say 'Exiting...'; $exit = true }
23
+
24
+ loop do
25
+ result = nil
26
+
27
+ realtime = Benchmark.realtime do
28
+ result = Delayed::Job.work_off
29
+ end
30
+
31
+ count = result.sum
32
+
33
+ break if $exit
34
+
35
+ if count.zero?
36
+ sleep(SLEEP)
37
+ else
38
+ say "#{count} jobs processed at %.4f j/s, %d failed ..." % [count / realtime, result.last]
39
+ end
40
+
41
+ break if $exit
42
+ end
43
+
44
+ ensure
45
+ Delayed::Job.clear_locks!
46
+ end
47
+
48
+ def say(text)
49
+ puts text unless @quiet
50
+ logger.info text if logger
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,13 @@
1
+ autoload :ActiveRecord, 'activerecord'
2
+
3
+ require File.dirname(__FILE__) + '/delayed/message_sending'
4
+ require File.dirname(__FILE__) + '/delayed/performable_method'
5
+ require File.dirname(__FILE__) + '/delayed/job'
6
+ require File.dirname(__FILE__) + '/delayed/worker'
7
+
8
+ Object.send(:include, Delayed::MessageSending)
9
+ Module.send(:include, Delayed::MessageSending::ClassMethods)
10
+
11
+ if defined?(Merb::Plugins)
12
+ Merb::Plugins.add_rakefiles File.dirname(__FILE__) / 'delayed' / 'tasks'
13
+ end
@@ -0,0 +1,26 @@
1
+ # Capistrano Recipes for managing delayed_job
2
+ #
3
+ # Add these callbacks to have the delayed_job process restart when the server
4
+ # is restarted:
5
+ #
6
+ # after "deploy:stop", "delayed_job:stop"
7
+ # after "deploy:start", "delayed_job:start"
8
+ # after "deploy:restart", "delayed_job:restart"
9
+
10
+
11
+ namespace :delayed_job do
12
+ desc "Stop the delayed_job process"
13
+ task :stop, :roles => :app do
14
+ run "cd #{current_path}; script/delayed_job -e #{rails_env} stop"
15
+ end
16
+
17
+ desc "Start the delayed_job process"
18
+ task :start, :roles => :app do
19
+ run "cd #{current_path}; script/delayed_job -e #{rails_env} start"
20
+ end
21
+
22
+ desc "Restart the delayed_job process"
23
+ task :restart, :roles => :app do
24
+ run "cd #{current_path}; script/delayed_job -e #{rails_env} restart"
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'delayed', 'tasks'))
@@ -0,0 +1 @@
1
+ puts IO.read(File.join(File.dirname(__FILE__), 'INSTALL'))
@@ -0,0 +1,418 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'rubygems'
4
+ require 'active_support'
5
+
6
+ # Plugin for applications to automatically post errors to the Hoptoad of their choice.
7
+ module HoptoadNotifier
8
+
9
+ IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
10
+ 'ActionController::RoutingError',
11
+ 'ActionController::InvalidAuthenticityToken',
12
+ 'CGI::Session::CookieStore::TamperedWithCookie',
13
+ 'ActionController::UnknownAction']
14
+
15
+ # Some of these don't exist for Rails 1.2.*, so we have to consider that.
16
+ IGNORE_DEFAULT.map!{|e| eval(e) rescue nil }.compact!
17
+ IGNORE_DEFAULT.freeze
18
+
19
+ IGNORE_USER_AGENT_DEFAULT = []
20
+
21
+ VERSION = "1.2.4"
22
+ LOG_PREFIX = "** [Hoptoad] "
23
+
24
+ HEADERS = {
25
+ 'Content-type' => 'application/x-yaml',
26
+ 'Accept' => 'text/xml, application/xml',
27
+ 'X-Hoptoad-Client-Name' => 'Hoptoad Notifier',
28
+ 'X-Hoptoad-Client-Version' => VERSION
29
+ }
30
+
31
+ class << self
32
+ attr_accessor :host, :port, :secure, :api_key, :http_open_timeout, :http_read_timeout,
33
+ :proxy_host, :proxy_port, :proxy_user, :proxy_pass, :output
34
+
35
+ def backtrace_filters
36
+ @backtrace_filters ||= []
37
+ end
38
+
39
+ def ignore_by_filters
40
+ @ignore_by_filters ||= []
41
+ end
42
+
43
+ # Takes a block and adds it to the list of ignore filters. When the filters
44
+ # run, the block will be handed the exception. If the block yields a value
45
+ # equivalent to "true," the exception will be ignored, otherwise it will be
46
+ # processed by hoptoad.
47
+ def ignore_by_filter &block
48
+ self.ignore_by_filters << block
49
+ end
50
+
51
+ # Takes a block and adds it to the list of backtrace filters. When the filters
52
+ # run, the block will be handed each line of the backtrace and can modify
53
+ # it as necessary. For example, by default a path matching the RAILS_ROOT
54
+ # constant will be transformed into "[RAILS_ROOT]"
55
+ def filter_backtrace &block
56
+ self.backtrace_filters << block
57
+ end
58
+
59
+ # The port on which your Hoptoad server runs.
60
+ def port
61
+ @port || (secure ? 443 : 80)
62
+ end
63
+
64
+ # The host to connect to.
65
+ def host
66
+ @host ||= 'hoptoadapp.com'
67
+ end
68
+
69
+ # The HTTP open timeout (defaults to 2 seconds).
70
+ def http_open_timeout
71
+ @http_open_timeout ||= 2
72
+ end
73
+
74
+ # The HTTP read timeout (defaults to 5 seconds).
75
+ def http_read_timeout
76
+ @http_read_timeout ||= 5
77
+ end
78
+
79
+ # Returns the list of errors that are being ignored. The array can be appended to.
80
+ def ignore
81
+ @ignore ||= (HoptoadNotifier::IGNORE_DEFAULT.dup)
82
+ @ignore.flatten!
83
+ @ignore
84
+ end
85
+
86
+ # Sets the list of ignored errors to only what is passed in here. This method
87
+ # can be passed a single error or a list of errors.
88
+ def ignore_only=(names)
89
+ @ignore = [names].flatten
90
+ end
91
+
92
+ # Returns the list of user agents that are being ignored. The array can be appended to.
93
+ def ignore_user_agent
94
+ @ignore_user_agent ||= (HoptoadNotifier::IGNORE_USER_AGENT_DEFAULT.dup)
95
+ @ignore_user_agent.flatten!
96
+ @ignore_user_agent
97
+ end
98
+
99
+ # Sets the list of ignored user agents to only what is passed in here. This method
100
+ # can be passed a single user agent or a list of user agents.
101
+ def ignore_user_agent_only=(names)
102
+ @ignore_user_agent = [names].flatten
103
+ end
104
+
105
+ # Returns a list of parameters that should be filtered out of what is sent to Hoptoad.
106
+ # By default, all "password" attributes will have their contents replaced.
107
+ def params_filters
108
+ @params_filters ||= %w(password password_confirmation)
109
+ end
110
+
111
+ def environment_filters
112
+ @environment_filters ||= %w()
113
+ end
114
+
115
+ def report_ready
116
+ write_verbose_log("Notifier #{VERSION} ready to catch errors")
117
+ end
118
+
119
+ def report_environment_info
120
+ write_verbose_log("Environment Info: #{environment_info}")
121
+ end
122
+
123
+ def report_response_body(response)
124
+ write_verbose_log("Response from Hoptoad: \n#{response}")
125
+ end
126
+
127
+ def environment_info
128
+ info = "[Ruby: #{RUBY_VERSION}]"
129
+ info << " [Rails: #{::Rails::VERSION::STRING}] [RailsEnv: #{RAILS_ENV}]" if defined?(Rails)
130
+ end
131
+
132
+ def write_verbose_log(message)
133
+ logger.info LOG_PREFIX + message if logger
134
+ end
135
+
136
+ # Checking for the logger in hopes we can get rid of the ugly syntax someday
137
+ def logger
138
+ if defined?(Rails.logger)
139
+ Rails.logger
140
+ elsif defined?(RAILS_DEFAULT_LOGGER)
141
+ RAILS_DEFAULT_LOGGER
142
+ end
143
+ end
144
+
145
+ # Call this method to modify defaults in your initializers.
146
+ #
147
+ # HoptoadNotifier.configure do |config|
148
+ # config.api_key = '1234567890abcdef'
149
+ # config.secure = false
150
+ # end
151
+ #
152
+ # NOTE: secure connections are not yet supported.
153
+ def configure
154
+ add_default_filters
155
+ yield self
156
+ if defined?(ActionController::Base) && !ActionController::Base.include?(HoptoadNotifier::Catcher)
157
+ ActionController::Base.send(:include, HoptoadNotifier::Catcher)
158
+ end
159
+ report_ready
160
+ end
161
+
162
+ def protocol #:nodoc:
163
+ secure ? "https" : "http"
164
+ end
165
+
166
+ def url #:nodoc:
167
+ URI.parse("#{protocol}://#{host}:#{port}/notices/")
168
+ end
169
+
170
+ def default_notice_options #:nodoc:
171
+ {
172
+ :api_key => HoptoadNotifier.api_key,
173
+ :error_message => 'Notification',
174
+ :backtrace => caller,
175
+ :request => {},
176
+ :session => {},
177
+ :environment => ENV.to_hash
178
+ }
179
+ end
180
+
181
+ # You can send an exception manually using this method, even when you are not in a
182
+ # controller. You can pass an exception or a hash that contains the attributes that
183
+ # would be sent to Hoptoad:
184
+ # * api_key: The API key for this project. The API key is a unique identifier that Hoptoad
185
+ # uses for identification.
186
+ # * error_message: The error returned by the exception (or the message you want to log).
187
+ # * backtrace: A backtrace, usually obtained with +caller+.
188
+ # * request: The controller's request object.
189
+ # * session: The contents of the user's session.
190
+ # * environment: ENV merged with the contents of the request's environment.
191
+ def notify notice = {}
192
+ Sender.new.notify_hoptoad( notice )
193
+ end
194
+
195
+ def add_default_filters
196
+ self.backtrace_filters.clear
197
+
198
+ filter_backtrace do |line|
199
+ line.gsub(/#{RAILS_ROOT}/, "[RAILS_ROOT]")
200
+ end
201
+
202
+ filter_backtrace do |line|
203
+ line.gsub(/^\.\//, "")
204
+ end
205
+
206
+ filter_backtrace do |line|
207
+ if defined?(Gem)
208
+ Gem.path.inject(line) do |line, path|
209
+ line.gsub(/#{path}/, "[GEM_ROOT]")
210
+ end
211
+ end
212
+ end
213
+
214
+ filter_backtrace do |line|
215
+ line if line !~ /lib\/#{File.basename(__FILE__)}/
216
+ end
217
+ end
218
+ end
219
+
220
+ # Include this module in Controllers in which you want to be notified of errors.
221
+ module Catcher
222
+
223
+ def self.included(base) #:nodoc:
224
+ if base.instance_methods.map(&:to_s).include? 'rescue_action_in_public' and !base.instance_methods.map(&:to_s).include? 'rescue_action_in_public_without_hoptoad'
225
+ base.send(:alias_method, :rescue_action_in_public_without_hoptoad, :rescue_action_in_public)
226
+ base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_hoptoad)
227
+ base.hide_action(:notify_hoptoad, :inform_hoptoad) if base.respond_to?(:hide_action)
228
+ end
229
+ end
230
+
231
+ # Overrides the rescue_action method in ActionController::Base, but does not inhibit
232
+ # any custom processing that is defined with Rails 2's exception helpers.
233
+ def rescue_action_in_public_with_hoptoad exception
234
+ notify_hoptoad(exception) unless ignore?(exception) || ignore_user_agent?
235
+ rescue_action_in_public_without_hoptoad(exception)
236
+ end
237
+
238
+ # This method should be used for sending manual notifications while you are still
239
+ # inside the controller. Otherwise it works like HoptoadNotifier.notify.
240
+ def notify_hoptoad hash_or_exception
241
+ if public_environment?
242
+ notice = normalize_notice(hash_or_exception)
243
+ notice = clean_notice(notice)
244
+ send_to_hoptoad(:notice => notice)
245
+ end
246
+ end
247
+
248
+ # Returns the default logger or a logger that prints to STDOUT. Necessary for manual
249
+ # notifications outside of controllers.
250
+ def logger
251
+ ActiveRecord::Base.logger
252
+ rescue
253
+ @logger ||= Logger.new(STDERR)
254
+ end
255
+
256
+ private
257
+
258
+ def public_environment? #nodoc:
259
+ defined?(RAILS_ENV) and !['development', 'test'].include?(RAILS_ENV)
260
+ end
261
+
262
+ def ignore?(exception) #:nodoc:
263
+ ignore_these = HoptoadNotifier.ignore.flatten
264
+ ignore_these.include?(exception.class) || ignore_these.include?(exception.class.name) || HoptoadNotifier.ignore_by_filters.find {|filter| filter.call(exception_to_data(exception))}
265
+ end
266
+
267
+ def ignore_user_agent? #:nodoc:
268
+ # Rails 1.2.6 doesn't have request.user_agent, so check for it here
269
+ user_agent = request.respond_to?(:user_agent) ? request.user_agent : request.env["HTTP_USER_AGENT"]
270
+ HoptoadNotifier.ignore_user_agent.flatten.any? { |ua| ua === user_agent }
271
+ end
272
+
273
+ def exception_to_data exception #:nodoc:
274
+ data = {
275
+ :api_key => HoptoadNotifier.api_key,
276
+ :error_class => exception.class.name,
277
+ :error_message => "#{exception.class.name}: #{exception.message}",
278
+ :backtrace => exception.backtrace,
279
+ :environment => ENV.to_hash
280
+ }
281
+
282
+ if self.respond_to? :request
283
+ data[:request] = {
284
+ :params => request.parameters.to_hash,
285
+ :rails_root => File.expand_path(RAILS_ROOT),
286
+ :url => "#{request.protocol}#{request.host}#{request.request_uri}"
287
+ }
288
+ data[:environment].merge!(request.env.to_hash)
289
+ end
290
+
291
+ if self.respond_to? :session
292
+ data[:session] = {
293
+ :key => session.instance_variable_get("@session_id"),
294
+ :data => session.respond_to?(:to_hash) ?
295
+ session.to_hash :
296
+ session.instance_variable_get("@data")
297
+ }
298
+ end
299
+
300
+ data
301
+ end
302
+
303
+ def normalize_notice(notice) #:nodoc:
304
+ case notice
305
+ when Hash
306
+ HoptoadNotifier.default_notice_options.merge(notice)
307
+ when Exception
308
+ HoptoadNotifier.default_notice_options.merge(exception_to_data(notice))
309
+ end
310
+ end
311
+
312
+ def clean_notice(notice) #:nodoc:
313
+ notice[:backtrace] = clean_hoptoad_backtrace(notice[:backtrace])
314
+ if notice[:request].is_a?(Hash) && notice[:request][:params].is_a?(Hash)
315
+ notice[:request][:params] = filter_parameters(notice[:request][:params]) if respond_to?(:filter_parameters)
316
+ notice[:request][:params] = clean_hoptoad_params(notice[:request][:params])
317
+ end
318
+ if notice[:environment].is_a?(Hash)
319
+ notice[:environment] = filter_parameters(notice[:environment]) if respond_to?(:filter_parameters)
320
+ notice[:environment] = clean_hoptoad_environment(notice[:environment])
321
+ end
322
+ clean_non_serializable_data(notice)
323
+ end
324
+
325
+ def log(level, message, response = nil)
326
+ logger.send level, LOG_PREFIX + message if logger
327
+ HoptoadNotifier.report_environment_info
328
+ HoptoadNotifier.report_response_body(response.body) if response && response.respond_to?(:body)
329
+ end
330
+
331
+ def send_to_hoptoad data #:nodoc:
332
+ url = HoptoadNotifier.url
333
+ http = Net::HTTP::Proxy(HoptoadNotifier.proxy_host,
334
+ HoptoadNotifier.proxy_port,
335
+ HoptoadNotifier.proxy_user,
336
+ HoptoadNotifier.proxy_pass).new(url.host, url.port)
337
+
338
+ http.use_ssl = true
339
+ http.read_timeout = HoptoadNotifier.http_read_timeout
340
+ http.open_timeout = HoptoadNotifier.http_open_timeout
341
+ http.use_ssl = !!HoptoadNotifier.secure
342
+
343
+ response = begin
344
+ http.post(url.path, stringify_keys(data).to_yaml, HEADERS)
345
+ rescue TimeoutError => e
346
+ log :error, "Timeout while contacting the Hoptoad server."
347
+ nil
348
+ end
349
+
350
+ case response
351
+ when Net::HTTPSuccess then
352
+ log :info, "Success: #{response.class}", response
353
+ else
354
+ log :error, "Failure: #{response.class}", response
355
+ end
356
+ end
357
+
358
+ def clean_hoptoad_backtrace backtrace #:nodoc:
359
+ if backtrace.to_a.size == 1
360
+ backtrace = backtrace.to_a.first.split(/\n\s*/)
361
+ end
362
+
363
+ filtered = backtrace.to_a.map do |line|
364
+ HoptoadNotifier.backtrace_filters.inject(line) do |line, proc|
365
+ proc.call(line)
366
+ end
367
+ end
368
+
369
+ filtered.compact
370
+ end
371
+
372
+ def clean_hoptoad_params params #:nodoc:
373
+ params.each do |k, v|
374
+ params[k] = "[FILTERED]" if HoptoadNotifier.params_filters.any? do |filter|
375
+ k.to_s.match(/#{filter}/)
376
+ end
377
+ end
378
+ end
379
+
380
+ def clean_hoptoad_environment env #:nodoc:
381
+ env.each do |k, v|
382
+ env[k] = "[FILTERED]" if HoptoadNotifier.environment_filters.any? do |filter|
383
+ k.to_s.match(/#{filter}/)
384
+ end
385
+ end
386
+ end
387
+
388
+ def clean_non_serializable_data(data) #:nodoc:
389
+ case data
390
+ when Hash
391
+ data.inject({}) do |result, (key, value)|
392
+ result.update(key => clean_non_serializable_data(value))
393
+ end
394
+ when Fixnum, Array, String, Bignum
395
+ data
396
+ else
397
+ data.to_s
398
+ end
399
+ end
400
+
401
+ def stringify_keys(hash) #:nodoc:
402
+ hash.inject({}) do |h, pair|
403
+ h[pair.first.to_s] = pair.last.is_a?(Hash) ? stringify_keys(pair.last) : pair.last
404
+ h
405
+ end
406
+ end
407
+
408
+ end
409
+
410
+ # A dummy class for sending notifications manually outside of a controller.
411
+ class Sender
412
+ def rescue_action_in_public(exception)
413
+ end
414
+
415
+ include HoptoadNotifier::Catcher
416
+ end
417
+ end
418
+