mole 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 (46) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +45 -0
  3. data/README.txt +140 -0
  4. data/Rakefile +47 -0
  5. data/bin/mole +8 -0
  6. data/bin/molify +64 -0
  7. data/config/database.yml +21 -0
  8. data/config/test_database.yml +69 -0
  9. data/lib/mole/db/migrate.rb +90 -0
  10. data/lib/mole/e_mole.rb +74 -0
  11. data/lib/mole/logger.rb +131 -0
  12. data/lib/mole/models/mole_feature.rb +45 -0
  13. data/lib/mole/models/mole_log.rb +56 -0
  14. data/lib/mole/module.rb +274 -0
  15. data/lib/mole/moler.rb +51 -0
  16. data/lib/mole/utils/frameworks.rb +22 -0
  17. data/lib/mole/version.rb +15 -0
  18. data/lib/mole.rb +175 -0
  19. data/spec/data/blee.rb +53 -0
  20. data/spec/db/migrate_spec.rb +19 -0
  21. data/spec/emole_spec.rb +43 -0
  22. data/spec/logger_spec.rb +56 -0
  23. data/spec/models/mole_feature_spec.rb +37 -0
  24. data/spec/models/mole_log_spec.rb +73 -0
  25. data/spec/module_spec.rb +171 -0
  26. data/spec/mole_spec.rb +38 -0
  27. data/spec/moler_spec.rb +65 -0
  28. data/spec/spec_helper.rb +59 -0
  29. data/spec/utils/framework_spec.rb +46 -0
  30. data/tasks/ann.rake +76 -0
  31. data/tasks/annotations.rake +22 -0
  32. data/tasks/doc.rake +48 -0
  33. data/tasks/gem.rake +110 -0
  34. data/tasks/manifest.rake +49 -0
  35. data/tasks/mole.rake +115 -0
  36. data/tasks/post_load.rake +26 -0
  37. data/tasks/rubyforge.rake +57 -0
  38. data/tasks/setup.rb +227 -0
  39. data/tasks/spec.rake +54 -0
  40. data/tasks/svn.rake +44 -0
  41. data/tasks/test.rake +38 -0
  42. data/templates/mole/e_mole/exception_alerts.rhtml +14 -0
  43. data/templates/mole/e_mole/feature_alerts.rhtml +11 -0
  44. data/templates/mole/e_mole/perf_alerts.rhtml +12 -0
  45. data/test/test_mole.rb +0 -0
  46. metadata +120 -0
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2008-02-13
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
data/Manifest.txt ADDED
@@ -0,0 +1,45 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/mole
6
+ bin/molify
7
+ config/database.yml
8
+ config/test_database.yml
9
+ lib/mole.rb
10
+ lib/mole/db/migrate.rb
11
+ lib/mole/e_mole.rb
12
+ lib/mole/logger.rb
13
+ lib/mole/models/mole_feature.rb
14
+ lib/mole/models/mole_log.rb
15
+ lib/mole/module.rb
16
+ lib/mole/moler.rb
17
+ lib/mole/utils/frameworks.rb
18
+ lib/mole/version.rb
19
+ spec/data/blee.rb
20
+ spec/db/migrate_spec.rb
21
+ spec/emole_spec.rb
22
+ spec/logger_spec.rb
23
+ spec/models/mole_feature_spec.rb
24
+ spec/models/mole_log_spec.rb
25
+ spec/module_spec.rb
26
+ spec/mole_spec.rb
27
+ spec/moler_spec.rb
28
+ spec/spec_helper.rb
29
+ spec/utils/framework_spec.rb
30
+ tasks/ann.rake
31
+ tasks/annotations.rake
32
+ tasks/doc.rake
33
+ tasks/gem.rake
34
+ tasks/manifest.rake
35
+ tasks/mole.rake
36
+ tasks/post_load.rake
37
+ tasks/rubyforge.rake
38
+ tasks/setup.rb
39
+ tasks/spec.rake
40
+ tasks/svn.rake
41
+ tasks/test.rake
42
+ templates/mole/e_mole/exception_alerts.rhtml
43
+ templates/mole/e_mole/feature_alerts.rhtml
44
+ templates/mole/e_mole/perf_alerts.rhtml
45
+ test/test_mole.rb
data/README.txt ADDED
@@ -0,0 +1,140 @@
1
+ The MOle
2
+ by Fernand Galiana
3
+ liquidrail.com
4
+
5
+ == DESCRIPTION:
6
+
7
+ The MOle allows you to track user's interactions with your ruby application and closely monitors
8
+ how your customers are using your application.
9
+
10
+ Whether you are releasing a new application or improving on an old one, it is always a good thing
11
+ to know if anyone is using your application and if they are, how they are using it.
12
+ What features are your users most fond of and which features find their way into the abyss?
13
+ Using the MOle you'll be able to rapidly assess whether or not your application is a hit and if
14
+ your coolest features are thought as such by your users. You will be able to elegantly record user
15
+ interactions and leverage these findings for the next iteration of your application.
16
+
17
+ == FEATURES:
18
+
19
+ The MOle allows you to easily
20
+
21
+ * Trap method calls within your ruby application. You can use the MOle with either a straight ruby
22
+ application, Rails or Merb. The MOle allows you to inject aspects across various method calls.
23
+ You are in full control on how and where to trap the calls and the arguments you want to record.
24
+ 'Moled' methods are not limited to controller's actions, you can also mole any third party or library methods.
25
+
26
+ * Single configuration file. You won't have to sprinkle MOle code all over your application.
27
+ The MOle instructions reside in a single file easy to manage and maintain.
28
+
29
+ * Trap and surface uncaught exceptions in one easy call. The MOle will watch your execution stack
30
+ and alert you when an unexpected exception is encountered
31
+
32
+ * Trap and surface performance bottle neck in your ruby application in one easy call. You can
33
+ specify a performance threshold within the MOle configuration file. Any methods taking longer
34
+ than the specified threshold will trip an alert.
35
+
36
+ * You can record users interaction by either using the MOle is a transient or persistent mode.
37
+ In the persistent mode, users interactions will be recorded in your database. In the transient
38
+ case, MOle events will be recorded in your application logs.
39
+
40
+ == INSTALL:
41
+
42
+ * sudo gem install mole
43
+
44
+ == SYNOPSIS:
45
+
46
+ * Mole initialization. This must be specified during your application initialization code.
47
+ In a rails app, this can be set in you application controller class or environment.rb. In
48
+ a Merb app, this can be set in your merb_init.rb file.
49
+
50
+ require 'mole'
51
+ Mole.initialize( :moleable => true,
52
+ :emole_from => "MoleBeatch@acme.com",
53
+ :emole_recipients => ['fernand@acme.com'],
54
+ :mode => :persistent,
55
+ :log_file => $stdout,
56
+ :log_level => :debug,
57
+ :perf_threshold => 2,
58
+ :application => "Smf",
59
+ :mole_config => File.join( File.dirname(__FILE__), %w[mole.rb] ) )
60
+
61
+ * Now you'll need to create a mole.rb specification file to specify the various aspects you'll want
62
+ to inject. This file is specified in the initialize call via the :mole_config tag.
63
+
64
+ To trap a feature before or after it occurs you can use the mole_before/mole_after
65
+ interceptors. The following will mole the "Posts" class after the "show" method is called.
66
+ The context argument hands you an execution context. In a rails/merb environment
67
+ this will be your controller object. You can handle this interaction any way
68
+ you want. The MOle provides a convenience class to record this interaction via
69
+ the database or the MOle logger in the guise of Mole::Moler. But you can also log it to
70
+ any media you'll find suitable. In a rails/merb context, you'll be able to extract session/params information
71
+ as in this case.
72
+
73
+ Posts.mole_after( :feature => :show ) { |context, feature, ret, args, block|
74
+ # Records the interaction to the database
75
+ Mole::Moler.mole_it( context, feature,
76
+ context.session[:user_id], # Retrieves which user performed this action
77
+ :post_id=> context.params[:post_id], # Records request parameter post id
78
+ :url => context.instance_variable_get( "@url" ) ) # Retrieves controller state
79
+ end
80
+ }
81
+
82
+ To record performance issues, you will need to provide a collection of
83
+ methods that must be watched on a given class. In the case of rails or merb you can easily fetch the list
84
+ of actions from the controller api and pass it to the MOle. The MOle provides convenience
85
+ classes to log the perf issues to your database and send out alerts when a perf condition is met.
86
+ Upon invocation, you will be handed a calling context, that comprises the feature (method) being
87
+ called, the actual time it took to complete the call and any args/block that was passed into
88
+ the feature that causes the performance threshold to be triggered. Setting the :email option
89
+ to true will also send out an email alert.
90
+
91
+ Posts.mole_perf( :features => merb_actions ) do |context, feature, elapsed_time, args, block|
92
+ user = context.instance_variable_get( "@user" )
93
+ key = context.params[:key], "N/A"
94
+ request = context.request.nil? ? "N/A" : context.request.remote_ip,
95
+ Mole::Moler.perf_it( context, user.id,
96
+ :controller => context.class.name,
97
+ :feature => feature,
98
+ :key => key,
99
+ :request => request,
100
+ :user => user.user_name,
101
+ :elapsed_time => "%3.3f" % elapsed_time,
102
+ :email => true )
103
+ end
104
+
105
+ The unchecked exception trap works very similarly to the mole_perf trap. It will pass in
106
+ the context of the method that triggered the exception.
107
+
108
+ Posts.mole_unchecked( :features => merb_actions ) do |context, action, boom, args, block|
109
+ sub = context.instance_variable_get("@sub")
110
+ user_id = "N/A"
111
+ user_id = sub.user.id if sub and sub.user
112
+ Mole::Moler.check_it( context, user_id,
113
+ :controller => context.class.name,
114
+ :action => action,
115
+ :boom => boom,
116
+ :email => true )
117
+ end
118
+
119
+ == LICENSE:
120
+
121
+ MIT Copyright (c) 2008
122
+
123
+ Permission is hereby granted, free of charge, to any person obtaining
124
+ a copy of this software and associated documentation files (the
125
+ 'Software'), to deal in the Software without restriction, including
126
+ without limitation the rights to use, copy, modify, merge, publish,
127
+ distribute, sublicense, and/or sell copies of the Software, and to
128
+ permit persons to whom the Software is furnished to do so, subject to
129
+ the following conditions:
130
+
131
+ The above copyright notice and this permission notice shall be
132
+ included in all copies or substantial portions of the Software.
133
+
134
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
135
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
136
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
137
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
138
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
139
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
140
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ load 'tasks/setup.rb'
6
+
7
+ ensure_in_path 'lib'
8
+ require 'mole'
9
+ require 'mole/version'
10
+
11
+ task :default => 'spec:run'
12
+
13
+ PROJ.name = 'mole'
14
+ PROJ.authors = 'Fernand Galiana'
15
+ PROJ.email = 'fernand@liquidrail.com'
16
+ PROJ.url = 'http://mole.rubyforge.org'
17
+ PROJ.rubyforge_name = 'mole'
18
+ PROJ.description = "A flexible way to track user's interactions within your ruby web applications"
19
+ PROJ.spec_opts << '--color'
20
+ PROJ.rcov_dir = ENV['CC_BUILD_ARTIFACTS'] ? "#{ENV['CC_BUILD_ARTIFACTS']}/test_coverage" : 'coverage'
21
+ PROJ.rdoc_dir = ENV['CC_BUILD_ARTIFACTS'] ? "#{ENV['CC_BUILD_ARTIFACTS']}/api_docs" : 'docs'
22
+ PROJ.ruby_opts = %w[-W0]
23
+ PROJ.version = ::Mole::Version.version
24
+ PROJ.svn = 'mole'
25
+ PROJ.rcov_threshold = 90.0
26
+ PROJ.executables = ['molify']
27
+
28
+ PROJ.exclude << %w[.DS_Store$ .swo$ .swp$]
29
+ PROJ.tests = FileList['test/**/test_*.rb']
30
+ PROJ.dependencies = [ ["logging"] ]
31
+ PROJ.annotation_tags << 'BOZO'
32
+
33
+ desc "Clean up artifact directories"
34
+ task :clean do
35
+ rcov_artifacts = File.join( File.dirname( __FILE__ ), "coverage" )
36
+ FileUtils.rm_rf rcov_artifacts if File.exists? rcov_artifacts
37
+ rdoc_artifacts = File.join( File.dirname( __FILE__ ), "docs" )
38
+ FileUtils.rm_rf rdoc_artifacts if File.exists? rdoc_artifacts
39
+ gem_artifacts = File.join( File.dirname( __FILE__ ), "pkg" )
40
+ FileUtils.rm_rf gem_artifacts if File.exists? gem_artifacts
41
+ end
42
+
43
+ task 'gem:package' => 'manifest:assert'
44
+
45
+
46
+ depend_on "logging" , "= 0.7.0"
47
+ depend_on "activerecord", "= 2.0.2"
data/bin/mole ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), '..', 'lib', 'mole'))
5
+
6
+ # Put your code here
7
+
8
+ # EOF
data/bin/molify ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
4
+ require File.expand_path(
5
+ File.join(File.dirname(__FILE__), '..', 'lib', 'mole') )
6
+
7
+ require 'mole/db/migrate'
8
+
9
+ module Mole
10
+ class Molify
11
+ # Performs db migration when the MOle is to be used in a persistent mode.
12
+ def initialize( argv=ARGV )
13
+ option_parser = default_option_parser
14
+ option_parser.parse!(argv)
15
+ puts options.inspect
16
+ ::Mole::Db::Migrate.new( options ).apply
17
+ end
18
+
19
+ # access the options
20
+ def options #:nodoc:
21
+ if not @options then
22
+ @options = OpenStruct.new
23
+ # Unless specified attempt to lookup config/database.yml
24
+ @options.configuration = File.join( Dir.pwd, %w[config database.yml] )
25
+ # Unless specified assumes test env
26
+ @options.environment = "test"
27
+ # Unless specficied migrates up
28
+ @options.direction = :up
29
+ end
30
+ return @options
31
+ end
32
+
33
+ def default_option_parser #:nodoc:
34
+ OptionParser.new do |op|
35
+ op.separator ""
36
+ op.separator "Molify options"
37
+
38
+ op.on( "-c", "--config FILE", "The location of the database configuration file." ) do |db|
39
+ options.configuration = db
40
+ end
41
+
42
+ op.on("-e", "--env ENV", "The environment to run in." ) do |env|
43
+ options.environment = env
44
+ end
45
+
46
+ op.on("-u", "--up", "Install MOle related tables") do |dir|
47
+ options.direction= :up
48
+ end
49
+
50
+ op.on("-d", "--down", "Uninstall MOle related tables") do |dir|
51
+ options.direction= :down
52
+ end
53
+
54
+ op.separator ""
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ Mole::Molify.new(ARGV)
61
+
62
+
63
+
64
+
@@ -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
@@ -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,74 @@
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
+ buff = boom.backtrace[0...3].join( "\r" )
60
+ end
61
+
62
+ # dumps arguments
63
+ def dump_args( args )
64
+ return "N/A" unless args
65
+ buff = []
66
+ args.keys.sort { |a,b| a.to_s <=> b.to_s}.each do |k|
67
+ key = k.to_s.rjust(20)
68
+ value = args[k].to_s.rjust(97,".")
69
+ buff << "#{key} : #{value}"
70
+ end
71
+ buff.join( "\r" )
72
+ end
73
+ end
74
+ end