mole 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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