derailed-mole 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. data/History.txt +17 -0
  2. data/Manifest.txt +137 -0
  3. data/README.txt +216 -0
  4. data/Rakefile +46 -0
  5. data/bin/molify +64 -0
  6. data/config/database.yml +21 -0
  7. data/config/test_database.yml +69 -0
  8. data/lib/mole.rb +260 -0
  9. data/lib/mole/db/migrate.rb +90 -0
  10. data/lib/mole/e_mole.rb +75 -0
  11. data/lib/mole/e_mole_helper.rb +2 -0
  12. data/lib/mole/logger.rb +134 -0
  13. data/lib/mole/models/mole_feature.rb +58 -0
  14. data/lib/mole/models/mole_log.rb +31 -0
  15. data/lib/mole/module.rb +292 -0
  16. data/lib/mole/moler.rb +71 -0
  17. data/lib/mole/utils/frameworks.rb +53 -0
  18. data/lib/mole/version.rb +15 -0
  19. data/mole.gemspec +37 -0
  20. data/samples/merbapp/README +14 -0
  21. data/samples/merbapp/Rakefile +124 -0
  22. data/samples/merbapp/app/controllers/application.rb +3 -0
  23. data/samples/merbapp/app/controllers/exceptions.rb +13 -0
  24. data/samples/merbapp/app/controllers/moled.rb +25 -0
  25. data/samples/merbapp/app/helpers/global_helper.rb +5 -0
  26. data/samples/merbapp/app/mailers/views/layout/application.html.erb +1 -0
  27. data/samples/merbapp/app/mailers/views/layout/application.text.erb +1 -0
  28. data/samples/merbapp/app/parts/views/layout/application.html.erb +1 -0
  29. data/samples/merbapp/app/views/exceptions/internal_server_error.html.erb +216 -0
  30. data/samples/merbapp/app/views/exceptions/not_acceptable.html.erb +38 -0
  31. data/samples/merbapp/app/views/exceptions/not_found.html.erb +40 -0
  32. data/samples/merbapp/app/views/layout/application.html.erb +11 -0
  33. data/samples/merbapp/app/views/moled/index.html.erb +5 -0
  34. data/samples/merbapp/app/views/moled/result.html.erb +5 -0
  35. data/samples/merbapp/config/boot.rb +11 -0
  36. data/samples/merbapp/config/dependencies.rb +41 -0
  37. data/samples/merbapp/config/environments/development.rb +1 -0
  38. data/samples/merbapp/config/environments/production.rb +1 -0
  39. data/samples/merbapp/config/environments/test.rb +1 -0
  40. data/samples/merbapp/config/merb.yml +82 -0
  41. data/samples/merbapp/config/merb_init.rb +26 -0
  42. data/samples/merbapp/config/mole_config.rb +33 -0
  43. data/samples/merbapp/config/router.rb +38 -0
  44. data/samples/merbapp/config/upload.conf +0 -0
  45. data/samples/merbapp/public/images/merb.jpg +0 -0
  46. data/samples/merbapp/public/merb.fcgi +6 -0
  47. data/samples/merbapp/public/stylesheets/master.css +119 -0
  48. data/samples/merbapp/script/destroy +32 -0
  49. data/samples/merbapp/script/generate +32 -0
  50. data/samples/merbapp/script/stop_merb +13 -0
  51. data/samples/merbapp/spec/spec_helper.rb +15 -0
  52. data/samples/merbapp/test/test_helper.rb +14 -0
  53. data/samples/railsapp/README +14 -0
  54. data/samples/railsapp/Rakefile +10 -0
  55. data/samples/railsapp/app/controllers/application.rb +13 -0
  56. data/samples/railsapp/app/controllers/moled_controller.rb +24 -0
  57. data/samples/railsapp/app/helpers/application_helper.rb +3 -0
  58. data/samples/railsapp/app/views/moled/index.html.erb +5 -0
  59. data/samples/railsapp/app/views/moled/result.html.erb +5 -0
  60. data/samples/railsapp/config/boot.rb +109 -0
  61. data/samples/railsapp/config/database.yml +13 -0
  62. data/samples/railsapp/config/environment.rb +59 -0
  63. data/samples/railsapp/config/environments/development.rb +18 -0
  64. data/samples/railsapp/config/environments/production.rb +20 -0
  65. data/samples/railsapp/config/environments/test.rb +22 -0
  66. data/samples/railsapp/config/initializers/inflections.rb +10 -0
  67. data/samples/railsapp/config/initializers/mime_types.rb +5 -0
  68. data/samples/railsapp/config/initializers/mole.rb +10 -0
  69. data/samples/railsapp/config/moles/mole_config.rb +44 -0
  70. data/samples/railsapp/config/routes.rb +35 -0
  71. data/samples/railsapp/doc/README_FOR_APP +2 -0
  72. data/samples/railsapp/public/.htaccess +40 -0
  73. data/samples/railsapp/public/404.html +30 -0
  74. data/samples/railsapp/public/422.html +30 -0
  75. data/samples/railsapp/public/500.html +30 -0
  76. data/samples/railsapp/public/dispatch.cgi +10 -0
  77. data/samples/railsapp/public/dispatch.fcgi +24 -0
  78. data/samples/railsapp/public/dispatch.rb +10 -0
  79. data/samples/railsapp/public/favicon.ico +0 -0
  80. data/samples/railsapp/public/images/rails.png +0 -0
  81. data/samples/railsapp/public/javascripts/application.js +2 -0
  82. data/samples/railsapp/public/javascripts/controls.js +963 -0
  83. data/samples/railsapp/public/javascripts/dragdrop.js +972 -0
  84. data/samples/railsapp/public/javascripts/effects.js +1120 -0
  85. data/samples/railsapp/public/javascripts/prototype.js +4225 -0
  86. data/samples/railsapp/public/robots.txt +5 -0
  87. data/samples/railsapp/script/about +3 -0
  88. data/samples/railsapp/script/console +3 -0
  89. data/samples/railsapp/script/destroy +3 -0
  90. data/samples/railsapp/script/generate +3 -0
  91. data/samples/railsapp/script/performance/benchmarker +3 -0
  92. data/samples/railsapp/script/performance/profiler +3 -0
  93. data/samples/railsapp/script/performance/request +3 -0
  94. data/samples/railsapp/script/plugin +3 -0
  95. data/samples/railsapp/script/process/inspector +3 -0
  96. data/samples/railsapp/script/process/reaper +3 -0
  97. data/samples/railsapp/script/process/spawner +3 -0
  98. data/samples/railsapp/script/runner +3 -0
  99. data/samples/railsapp/script/server +3 -0
  100. data/samples/railsapp/test/test_helper.rb +38 -0
  101. data/samples/rubyapp/README +22 -0
  102. data/samples/rubyapp/bin/ruby_app +35 -0
  103. data/samples/rubyapp/config/mole_conf.rb +31 -0
  104. data/samples/rubyapp/lib/fred.rb +22 -0
  105. data/spec/config/auto_mole_config.rb +26 -0
  106. data/spec/config/mole_config.rb +0 -0
  107. data/spec/config/moles/fred_config.rb +0 -0
  108. data/spec/data/blee.rb +64 -0
  109. data/spec/db/migrate_spec.rb +19 -0
  110. data/spec/emole_spec.rb +43 -0
  111. data/spec/logger_spec.rb +56 -0
  112. data/spec/models/mole_feature_spec.rb +48 -0
  113. data/spec/models/mole_log_spec.rb +62 -0
  114. data/spec/module_spec.rb +229 -0
  115. data/spec/mole_spec.rb +135 -0
  116. data/spec/moler_spec.rb +77 -0
  117. data/spec/spec_helper.rb +76 -0
  118. data/spec/utils/framework_spec.rb +99 -0
  119. data/tasks/ann.rake +76 -0
  120. data/tasks/annotations.rake +22 -0
  121. data/tasks/doc.rake +48 -0
  122. data/tasks/gem.rake +110 -0
  123. data/tasks/manifest.rake +49 -0
  124. data/tasks/mole.rake +115 -0
  125. data/tasks/post_load.rake +26 -0
  126. data/tasks/rubyforge.rake +57 -0
  127. data/tasks/setup.rb +227 -0
  128. data/tasks/spec.rake +54 -0
  129. data/tasks/svn.rake +44 -0
  130. data/tasks/test.rake +38 -0
  131. data/templates/mole/e_mole/exception_alerts.rhtml +14 -0
  132. data/templates/mole/e_mole/feature_alerts.rhtml +11 -0
  133. data/templates/mole/e_mole/perf_alerts.rhtml +12 -0
  134. metadata +206 -0
@@ -0,0 +1,21 @@
1
+ # ------------------------ Databases ------------------------
2
+ #
3
+ # Local login
4
+ local: &local
5
+ adapter: mysql
6
+ host: localhost
7
+ username: root
8
+ password:
9
+
10
+ local_dev: &local_dev
11
+ <<: *local
12
+ database: mole_dev
13
+
14
+ local_test: &local_test
15
+ <<: *local
16
+ database: mole_test
17
+
18
+ # ------------------------ Environments ------------------------
19
+ #
20
+ development: *local_dev
21
+ test: *local_test
@@ -0,0 +1,69 @@
1
+ #
2
+ # ------------------------ Databases ------------------------
3
+ #
4
+
5
+ # Production read-only
6
+ read_only: &read_only
7
+ adapter: mysql
8
+ host: 10.10.12.100
9
+ database: ci_sentiment_db
10
+ username: ciro
11
+ password: ciro
12
+
13
+ # Production writable
14
+ writable: &writable
15
+ adapter: mysql
16
+ host: 10.10.12.101
17
+ database: ci_sentiment_db
18
+ username: ciprod
19
+ password: ciprod
20
+
21
+ # Remote development database
22
+ dev2: &dev2
23
+ adapter: mysql
24
+ host: dev2
25
+ encoding: utf8
26
+ database: ci_sentiment_db
27
+ username: root
28
+ password: alchem1st
29
+
30
+ # CC test database
31
+ cc: &cc
32
+ adapter: mysql
33
+ host: 10.10.14.105
34
+ encoding: utf8
35
+ database: sentiment_test
36
+ username: root
37
+ password: alchem1st
38
+
39
+ # Local login
40
+ local: &local
41
+ adapter: mysql
42
+ host: localhost
43
+ username: root
44
+ password:
45
+
46
+ local_dev: &local_dev
47
+ <<: *local
48
+ database: sentiment_dev
49
+
50
+ local_test: &local_test
51
+ <<: *local
52
+ database: sentiment_test
53
+
54
+ yahoo: &yahoo
55
+ adapter: mysql
56
+ database: ci_yahoo_db
57
+ host: 10.10.12.104
58
+ username: ciprod
59
+ password: ciprod
60
+ #
61
+ # ------------------------ Environments ------------------------
62
+ #
63
+ development: *local_dev
64
+ test: *local_test
65
+ beta: *dev2
66
+ production: *writable
67
+ cc: *cc
68
+ yahoo_production: *yahoo
69
+ production_ro: *read_only
data/lib/mole.rb ADDED
@@ -0,0 +1,260 @@
1
+ # $Id$
2
+
3
+ # Equivalent to a header guard in C/C++
4
+ # Used to prevent the class/module from being loaded more than once
5
+ unless defined? Mole
6
+ require 'activerecord'
7
+ module Mole
8
+ # :stopdoc:
9
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
10
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
11
+
12
+ # :startdoc:
13
+ # The MOle can be ran in a couple of modes: Transient and Persistent
14
+ # Transient mode will log the output to the specified log file
15
+ # Persistent mode will log the mole output to your db
16
+ # The default is :transient
17
+ def self.run_modes #:nodoc:
18
+ [:transient, :persistent]
19
+ end
20
+
21
+ # MOle Default settings
22
+ def self.defaults #:nodoc:
23
+ @defaults ||= {
24
+ :moleable => false,
25
+ :application => "Default",
26
+ :perf_threshold => 5,
27
+ :mode => :transient,
28
+ :emole_from => "MOleBeatch",
29
+ :emole_recipients => [],
30
+ :mole_config => nil,
31
+ # logging options
32
+ :log_file => $stdout,
33
+ :log_level => :info,
34
+ :email_alerts_to => "MOleBeatch",
35
+ :email_alert_level => :error }
36
+ end
37
+
38
+ # Reset the configuration to what it would be when the class is parsed
39
+ # this is needed mainly for running specs. This resets the class to the
40
+ # state it was before initialize is called. initialize MUST be called
41
+ # after reset_configuration! is invoked
42
+ def self.reset_configuration! #:nodoc:
43
+ @logger.clear_appenders if @logger
44
+ @logger = nil
45
+ @config = nil
46
+ end
47
+
48
+ # Initialize the MOle
49
+ # Valid options are
50
+ # <tt>moleable</tt>:: specify if this application is moleable.
51
+ # Defaults to false.
52
+ # <tt>application</tt>:: the name of the application to be moled.
53
+ # <tt>perf_threshold</tt>:: the performance threshold over which a Mole condition will be issued.
54
+ # Defaults to 5 seconds
55
+ # <tt>mode</tt>:: the MOle logging mole. The mole can either log information to a db via
56
+ # the :persistent option or to a log file via the :transient flag.
57
+ # Defaults to transient
58
+ # <tt>emole_from</tt>:: the EMole originator when sending eMOle alerts.
59
+ # <tt>emole_recipients</tt>:: a collection of EMOle recipients
60
+ # <tt>mole_config</tt>:: the location of the MOle configuration file where the interceptors will
61
+ # be defined.
62
+ # <tt>log_file</tt>:: The log file to be used to log MOle interceptions
63
+ # <tt>log_level</tt>:: logging level ie :info, :debug, :error, :warn...
64
+ # <tt>email_alerts_to</tt>:: log level email alert recipients.
65
+ # <tt>email_alert_level</tt>:: specifies which log level will trigger email alerts to be sent
66
+ def self.initialize( opts={} )
67
+ @config = defaults.merge( opts )
68
+ @config[:email_alerts_to] = @config[:emole_recipients] if @config[:emole_recipients] and !@config[:emole_recipients].empty?
69
+ # Add the mole/lib to the ruby path...
70
+ $: << libpath
71
+ Mole.require_all_libs_relative_to __FILE__
72
+ end
73
+
74
+ # Loads the mole configuration file
75
+ # You can either specify a directory containing mole config files or
76
+ # a single mole config file via the mole_config option.
77
+ def self.load_mole_configuration
78
+ return unless moleable?
79
+ raise "Unable to find the MOle configuration from `#{conf_file}" if conf_file and !File.exists? conf_file
80
+ unless @config_loaded
81
+ @config_loader = true
82
+ if File.directory? conf_file
83
+ logger.debug "--- Loading MOle configs files from directory `#{conf_file}"
84
+ load_all_moles_relative_to( conf_file )
85
+ else
86
+ logger.debug "--- Loading single MOle config #{conf_file}"
87
+ load conf_file
88
+ end
89
+ end
90
+ @config_loaded
91
+ end
92
+
93
+ # Fetch the MOle configuration file
94
+ def self.conf_file #:nodoc:
95
+ config[:mole_config]
96
+ end
97
+
98
+ # EMole alert sender
99
+ def self.emole_from #:nodoc:
100
+ config[:emole_from]
101
+ end
102
+
103
+ # EMole alert recipients
104
+ def self.emole_recipients #:nodoc:
105
+ config[:emole_recipients]
106
+ end
107
+
108
+ # Fetch the MOle configuration
109
+ def self.config #:nodoc:
110
+ @config
111
+ end
112
+
113
+ # Debug
114
+ def self.dump #:nodoc:
115
+ puts ""
116
+ puts "Mole Configuration Landscape"
117
+ config.keys.sort{ |a,b| a.to_s <=> b.to_s }.each do |k|
118
+ key = k.to_s.rjust(20)
119
+ value = config[k].to_s.rjust(97,".")
120
+ puts "#{key} : #{value}"
121
+ end
122
+ end
123
+
124
+ # get a hold of a logger. This is the global logger for sentiment.
125
+ def self.logger #:nodoc:
126
+ @logger ||= ::Mole::Logger.new( { :log_file => config[:log_file],
127
+ :logger_name => "MOle",
128
+ :log_level => config[:log_level],
129
+ :email_alerts_to => config[:email_alerts_to],
130
+ :email_alert_level => config[:email_alert_level],
131
+ :additive => false } )
132
+ end
133
+
134
+ # The name of the MOled application
135
+ def self.application #:nodoc:
136
+ config[:application]
137
+ end
138
+
139
+ # Is this application is MOleable
140
+ def self.moleable? #:nodoc:
141
+ config[:moleable]
142
+ end
143
+
144
+ # Returns the MOle perf threshold. If any MOled features takes longer
145
+ # than this time to complete, then an alarm will be triggered...
146
+ def self.perf_threshold #:nodoc:
147
+ config[:perf_threshold]
148
+ end
149
+
150
+ # Enable to toggle between different log modes ie :persistent/:transient
151
+ def self.switch_mode( mode )
152
+ config[:mode] = mode
153
+ end
154
+
155
+ # Check if the MOle is running in persistent mode
156
+ def self.persistent?
157
+ config[:mode] == :persistent
158
+ end
159
+
160
+ # Returns the library path for the module. If any arguments are given,
161
+ # they will be joined to the end of the libray path using
162
+ # <tt>File.join</tt>.
163
+ #
164
+ def self.libpath( *args ) #:nodoc:
165
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, *args)
166
+ end
167
+
168
+ # Returns the lpath for the module. If any arguments are given,
169
+ # they will be joined to the end of the path using
170
+ # <tt>File.join</tt>.
171
+ #
172
+ def self.path( *args ) #:nodoc:
173
+ args.empty? ? PATH : ::File.join(PATH, *args)
174
+ end
175
+
176
+ # Utility method used to require all files ending in .rb that lie in the
177
+ # directory below this file that has the same name as the filename passed
178
+ # in. Optionally, a specific _directory_ name can be passed in such that
179
+ # the _filename_ does not have to be equivalent to the directory.
180
+ #
181
+ def self.require_all_libs_relative_to( fname, dir = nil ) #:nodoc:
182
+ dir ||= ::File.basename(fname, '.*')
183
+ search_me = ::File.expand_path( ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
184
+ Dir.glob(search_me).sort.each {|rb| require rb}
185
+ end
186
+
187
+ # Utility method used to load all MOle config files ending in .rb that lie in the
188
+ # directory below this file that has the same name as the filename passed
189
+ # in. Optionally, a specific _directory_ name can be passed in such that
190
+ # the _filename_ does not have to be equivalent to the directory.
191
+ #
192
+ def self.load_all_moles_relative_to( mole_dir ) #:nodoc:
193
+ search_me = ::File.join( mole_dir, '**', '*.rb')
194
+ Dir.glob(search_me).sort.each {|rb| load rb}
195
+ end
196
+
197
+ # Stolen from inflector
198
+ def self.camelize(lower_case_and_underscored_word)
199
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
200
+ end
201
+
202
+ # Fetch all ruby files in the given directory and return a cltn of class names
203
+ def self.find_controller_classes( dir )
204
+ classes = []
205
+ search_me = ::File.expand_path( ::File.join(dir, '*.rb'))
206
+ # BOZO !! This kind of sucks - need to exclude application controller for rails otherwise class loading error ??
207
+ Dir.glob(search_me).sort.each {|rb| classes << camelize( File.basename( rb, ".rb") ) unless File.basename( rb, ".rb") == "application" }
208
+ classes
209
+ end
210
+
211
+ # Automatically setup on perf MOle on any classes within a given directory
212
+ # NOTE: this call assumes the controller classes are in all in the path
213
+ def self.auto_perf( dir, &block )
214
+ controller_classes = find_controller_classes( dir )
215
+ controller_classes.each do |class_name|
216
+ clazz = Kernel.const_get( class_name )
217
+ features = ::Mole::Utils::Frameworks.features_for( clazz )
218
+ clazz.mole_perf( :features => features, &block )
219
+ end
220
+ end
221
+
222
+ # Automatically setup MOle untrapped exception on any classes within a given directory
223
+ # NOTE: this call assumes the controller classes are in all in the path
224
+ def self.auto_unchecked( dir, &block )
225
+ controller_classes = find_controller_classes( dir )
226
+ controller_classes.each do |class_name|
227
+ clazz = Kernel.const_get( class_name )
228
+ features = ::Mole::Utils::Frameworks.features_for( clazz )
229
+ clazz.mole_unchecked( :features => features, &block )
230
+ end
231
+ end
232
+
233
+ # Automatically setup MOle after filter on any classes within a given directory
234
+ # NOTE: this call assumes the controller classes are in all in the path
235
+ def self.auto_after( dir, &block )
236
+ controller_classes = find_controller_classes( dir )
237
+ controller_classes.each do |class_name|
238
+ clazz = Kernel.const_get( class_name )
239
+ features = ::Mole::Utils::Frameworks.features_for( clazz )
240
+ features.each do |feature|
241
+ clazz.mole_after( :feature => feature, &block )
242
+ end
243
+ end
244
+ end
245
+
246
+ # Automatically setup MOle after filter on any classes within a given directory
247
+ # NOTE: this call assumes the controller classes are in all in the path
248
+ def self.auto_before( dir, &block )
249
+ controller_classes = find_controller_classes( dir )
250
+ controller_classes.each do |class_name|
251
+ clazz = Kernel.const_get( class_name )
252
+ features = ::Mole::Utils::Frameworks.features_for( clazz )
253
+ features.each do |feature|
254
+ clazz.mole_before( :feature => feature, &block )
255
+ end
256
+ end
257
+ end
258
+
259
+ end
260
+ end
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # -----------------------------------------------------------------------------
4
+ # Sets up the MOle persistent layer
5
+ # Two tables are involved: mole_features and mole_logs
6
+ # -----------------------------------------------------------------------------
7
+ require 'rake'
8
+ require 'rake/tasklib'
9
+
10
+ module Mole
11
+ module Db
12
+ class Migrate
13
+ def initialize( opts )
14
+ @direction = opts.direction
15
+ @config = opts.configuration
16
+ @env = opts.environment
17
+ end
18
+
19
+ # Creates a MOle migration by creating or dropping the MOle related tables
20
+ def apply #:nodoc:
21
+ setup
22
+ @direction == :up ? migrate_up : migrate_down
23
+ end
24
+
25
+ # Setup database connection prior to applying migrations
26
+ def setup #:nodoc:
27
+ require 'rubygems'
28
+ gem "activerecord"
29
+ require 'active_record'
30
+ db_config = YAML.load_file( File.expand_path( @config ) )[@env]
31
+ ::ActiveRecord::Base.establish_connection(db_config)
32
+ end
33
+
34
+ # ---------------------------------------------------------------------
35
+ # Create mole persistence tables ( 2 tables mole_features/mole_logs )
36
+ def migrate_up
37
+ # Create the mole_features table if it doesn't exist
38
+ unless ActiveRecord::Schema.tables.include?('mole_features')
39
+ ActiveRecord::Schema.create_table('mole_features') do |t|
40
+ t.column :name, :string
41
+ t.column :context, :string
42
+ t.column :app_name, :string
43
+ t.column :created_at, :datetime
44
+ t.column :updated_at, :datetime
45
+ end
46
+ ActiveRecord::Schema.add_index( 'mole_features',
47
+ ['name', 'context', 'app_name'],
48
+ :name => 'feature_idx')
49
+ end
50
+ # Create the mole_logs table if it doesn't exist
51
+ unless ActiveRecord::Schema.tables.include?('mole_logs')
52
+ ActiveRecord::Schema.create_table('mole_logs') do |t|
53
+ t.column :mole_feature_id, :integer
54
+ t.column :user_id, :integer
55
+ t.column :params, :string, :limit => 1024
56
+ t.column :ip_address, :string
57
+ t.column :browser_type, :string
58
+ t.column :host_name, :string
59
+ t.column :created_at, :datetime
60
+ t.column :updated_at, :datetime
61
+ end
62
+ ActiveRecord::Schema.add_index( 'mole_logs',
63
+ ['mole_feature_id','user_id'],
64
+ :name => "log_feature_idx" )
65
+ ActiveRecord::Schema.add_index( 'mole_logs',
66
+ ['mole_feature_id','created_at'],
67
+ :name => "log_date_idx",
68
+ :unique => true )
69
+ end
70
+ end
71
+
72
+ # -------------------------------------------------------------------------
73
+ # Destroys mole persistence tables
74
+ def migrate_down
75
+ # Delete the mole_feature table
76
+ if ActiveRecord::Schema.tables.include?( 'mole_features' )
77
+ ActiveRecord::Schema.remove_index( 'mole_features', :name => 'feature_idx' )
78
+ ActiveRecord::Schema.drop_table( 'mole_features' )
79
+ end
80
+
81
+ # Delete the mole_logs table
82
+ if ActiveRecord::Schema.tables.include?( 'mole_logs' )
83
+ ActiveRecord::Schema.remove_index( 'mole_logs', :name => 'log_feature_idx' )
84
+ ActiveRecord::Schema.remove_index( 'mole_logs', :name =>'log_date_idx' )
85
+ ActiveRecord::Schema.drop_table( 'mole_logs' )
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,75 @@
1
+ require 'action_mailer'
2
+
3
+ module Mole
4
+ class EMole < ActionMailer::Base
5
+ self.template_root = File.join( File.dirname(__FILE__), %w[.. .. templates] )
6
+
7
+ def setup #:nodoc:
8
+ recipients ::Mole.emole_recipients
9
+ from ::Mole.emole_from
10
+ @host = `hostname`
11
+ end
12
+
13
+ # Setup aspect
14
+ %w[perf exception feature].each do |asp|
15
+ module_eval "def #{asp}_alerts_with_setup( context, user_name, opts={} ) setup; #{asp}_alerts_without_setup( context, user_name, opts) end #:nodoc:"
16
+ end
17
+
18
+ # send out feature alerts
19
+ def feature_alerts( context, user_id, options={} )
20
+ Mole.logger.debug "Sending feature email from #{::Mole.emole_from} -- to #{::Mole.emole_recipients}"
21
+ subject "[FEATURE] #{options[:feature]} -- #{@host} -- #{Mole.application} -- #{user_id}"
22
+ body :application => Mole.application,
23
+ :host_name => @host,
24
+ :context => context.class.name,
25
+ :feature => options[:feature],
26
+ :args => dump_args( options )
27
+ end
28
+ alias_method_chain :feature_alerts, :setup
29
+
30
+ # send out mole performance alert
31
+ def perf_alerts( context, user_id, options={} )
32
+ Mole.logger.debug "Sending perf email from #{::Mole.emole_from} -- to #{::Mole.emole_recipients}"
33
+ subject "[PERF] #{@host} -- #{Mole.application} -- #{user_id}"
34
+ body :application => ::Mole.application,
35
+ :host_name => @host,
36
+ :context => context.class.name,
37
+ :feature => options[:feature],
38
+ :elapsed_time => options[:elapsed_time] ,
39
+ :args => dump_args( options )
40
+ end
41
+ alias_method_chain :perf_alerts, :setup
42
+
43
+ # send out mole exception alerts
44
+ def exception_alerts( context, user_id, options={} )
45
+ Mole.logger.debug "Sending perf email from #{::Mole.emole_from} -- to #{::Mole.emole_recipients}"
46
+ subject "[EXCEPTION] #{@host} -- #{Mole.application} -- #{user_id}"
47
+ body :application => Mole.application,
48
+ :host_name => @host,
49
+ :context => context.class.name,
50
+ :feature => options[:feature],
51
+ :boom => options[:boom],
52
+ :trace => dump_stack( options[:boom] ),
53
+ :args => dump_args( options )
54
+ end
55
+ alias_method_chain :exception_alerts, :setup
56
+
57
+ # dumps partial stack
58
+ def dump_stack( boom )
59
+ return boom if boom.is_a? String
60
+ buff = boom.backtrace[0...3].join( "\r" )
61
+ end
62
+
63
+ # dumps arguments
64
+ def dump_args( args )
65
+ return "N/A" unless args
66
+ buff = []
67
+ args.keys.sort { |a,b| a.to_s <=> b.to_s}.each do |k|
68
+ key = k.to_s.rjust(20)
69
+ value = args[k].to_s.rjust(97,".")
70
+ buff << "#{key} : #{value}"
71
+ end
72
+ buff.join( "\r" )
73
+ end
74
+ end
75
+ end