mole 1.0.12 → 1.0.15
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.
- metadata +24 -146
- data/History.txt +0 -17
- data/Manifest.txt +0 -132
- data/README.txt +0 -216
- data/Rakefile +0 -46
- data/config/database.yml +0 -21
- data/config/test_database.yml +0 -69
- data/lib/mole.rb +0 -260
- data/lib/mole/db/migrate.rb +0 -90
- data/lib/mole/e_mole.rb +0 -75
- data/lib/mole/logger.rb +0 -134
- data/lib/mole/models/mole_feature.rb +0 -58
- data/lib/mole/models/mole_log.rb +0 -31
- data/lib/mole/module.rb +0 -292
- data/lib/mole/moler.rb +0 -71
- data/lib/mole/utils/frameworks.rb +0 -53
- data/lib/mole/version.rb +0 -15
- data/samples/merbapp/README +0 -14
- data/samples/merbapp/Rakefile +0 -124
- data/samples/merbapp/app/controllers/application.rb +0 -3
- data/samples/merbapp/app/controllers/exceptions.rb +0 -13
- data/samples/merbapp/app/controllers/moled.rb +0 -25
- data/samples/merbapp/app/helpers/global_helper.rb +0 -5
- data/samples/merbapp/app/mailers/views/layout/application.html.erb +0 -1
- data/samples/merbapp/app/mailers/views/layout/application.text.erb +0 -1
- data/samples/merbapp/app/parts/views/layout/application.html.erb +0 -1
- data/samples/merbapp/app/views/exceptions/internal_server_error.html.erb +0 -216
- data/samples/merbapp/app/views/exceptions/not_acceptable.html.erb +0 -38
- data/samples/merbapp/app/views/exceptions/not_found.html.erb +0 -40
- data/samples/merbapp/app/views/layout/application.html.erb +0 -11
- data/samples/merbapp/app/views/moled/index.html.erb +0 -5
- data/samples/merbapp/app/views/moled/result.html.erb +0 -5
- data/samples/merbapp/config/boot.rb +0 -11
- data/samples/merbapp/config/dependencies.rb +0 -41
- data/samples/merbapp/config/environments/development.rb +0 -1
- data/samples/merbapp/config/environments/production.rb +0 -1
- data/samples/merbapp/config/environments/test.rb +0 -1
- data/samples/merbapp/config/merb.yml +0 -82
- data/samples/merbapp/config/merb_init.rb +0 -26
- data/samples/merbapp/config/mole_config.rb +0 -33
- data/samples/merbapp/config/router.rb +0 -38
- data/samples/merbapp/config/upload.conf +0 -0
- data/samples/merbapp/public/images/merb.jpg +0 -0
- data/samples/merbapp/public/merb.fcgi +0 -6
- data/samples/merbapp/public/stylesheets/master.css +0 -119
- data/samples/merbapp/script/destroy +0 -32
- data/samples/merbapp/script/generate +0 -32
- data/samples/merbapp/script/stop_merb +0 -13
- data/samples/merbapp/spec/spec_helper.rb +0 -15
- data/samples/merbapp/test/test_helper.rb +0 -14
- data/samples/railsapp/README +0 -14
- data/samples/railsapp/Rakefile +0 -10
- data/samples/railsapp/app/controllers/application.rb +0 -13
- data/samples/railsapp/app/controllers/moled_controller.rb +0 -23
- data/samples/railsapp/app/helpers/application_helper.rb +0 -3
- data/samples/railsapp/app/views/moled/index.html.erb +0 -5
- data/samples/railsapp/app/views/moled/result.html.erb +0 -5
- data/samples/railsapp/config/boot.rb +0 -109
- data/samples/railsapp/config/database.yml +0 -13
- data/samples/railsapp/config/environment.rb +0 -59
- data/samples/railsapp/config/environments/development.rb +0 -18
- data/samples/railsapp/config/environments/production.rb +0 -20
- data/samples/railsapp/config/environments/test.rb +0 -22
- data/samples/railsapp/config/initializers/inflections.rb +0 -10
- data/samples/railsapp/config/initializers/mime_types.rb +0 -5
- data/samples/railsapp/config/initializers/mole.rb +0 -10
- data/samples/railsapp/config/moles/mole_config.rb +0 -44
- data/samples/railsapp/config/routes.rb +0 -35
- data/samples/railsapp/doc/README_FOR_APP +0 -2
- data/samples/railsapp/public/.htaccess +0 -40
- data/samples/railsapp/public/404.html +0 -30
- data/samples/railsapp/public/422.html +0 -30
- data/samples/railsapp/public/500.html +0 -30
- data/samples/railsapp/public/dispatch.cgi +0 -10
- data/samples/railsapp/public/dispatch.fcgi +0 -24
- data/samples/railsapp/public/dispatch.rb +0 -10
- data/samples/railsapp/public/favicon.ico +0 -0
- data/samples/railsapp/public/images/rails.png +0 -0
- data/samples/railsapp/public/javascripts/application.js +0 -2
- data/samples/railsapp/public/javascripts/controls.js +0 -963
- data/samples/railsapp/public/javascripts/dragdrop.js +0 -972
- data/samples/railsapp/public/javascripts/effects.js +0 -1120
- data/samples/railsapp/public/javascripts/prototype.js +0 -4225
- data/samples/railsapp/public/robots.txt +0 -5
- data/samples/railsapp/script/about +0 -3
- data/samples/railsapp/script/console +0 -3
- data/samples/railsapp/script/destroy +0 -3
- data/samples/railsapp/script/generate +0 -3
- data/samples/railsapp/script/performance/benchmarker +0 -3
- data/samples/railsapp/script/performance/profiler +0 -3
- data/samples/railsapp/script/performance/request +0 -3
- data/samples/railsapp/script/plugin +0 -3
- data/samples/railsapp/script/process/inspector +0 -3
- data/samples/railsapp/script/process/reaper +0 -3
- data/samples/railsapp/script/process/spawner +0 -3
- data/samples/railsapp/script/runner +0 -3
- data/samples/railsapp/script/server +0 -3
- data/samples/railsapp/test/test_helper.rb +0 -38
- data/samples/rubyapp/README +0 -22
- data/samples/rubyapp/bin/ruby_app +0 -35
- data/samples/rubyapp/config/mole_conf.rb +0 -31
- data/samples/rubyapp/lib/fred.rb +0 -22
- data/spec/config/auto_mole_config.rb +0 -26
- data/spec/config/mole_config.rb +0 -0
- data/spec/config/moles/fred_config.rb +0 -0
- data/spec/data/blee.rb +0 -64
- data/spec/db/migrate_spec.rb +0 -19
- data/spec/emole_spec.rb +0 -43
- data/spec/logger_spec.rb +0 -56
- data/spec/models/mole_feature_spec.rb +0 -48
- data/spec/models/mole_log_spec.rb +0 -62
- data/spec/module_spec.rb +0 -229
- data/spec/mole_spec.rb +0 -135
- data/spec/moler_spec.rb +0 -77
- data/spec/spec_helper.rb +0 -76
- data/spec/utils/framework_spec.rb +0 -99
- data/tasks/ann.rake +0 -76
- data/tasks/annotations.rake +0 -22
- data/tasks/doc.rake +0 -48
- data/tasks/gem.rake +0 -110
- data/tasks/manifest.rake +0 -49
- data/tasks/mole.rake +0 -115
- data/tasks/post_load.rake +0 -26
- data/tasks/rubyforge.rake +0 -57
- data/tasks/setup.rb +0 -227
- data/tasks/spec.rake +0 -54
- data/tasks/svn.rake +0 -44
- data/tasks/test.rake +0 -38
- data/templates/mole/e_mole/exception_alerts.rhtml +0 -14
- data/templates/mole/e_mole/feature_alerts.rhtml +0 -11
- data/templates/mole/e_mole/perf_alerts.rhtml +0 -12
data/lib/mole/e_mole.rb
DELETED
@@ -1,75 +0,0 @@
|
|
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
|
data/lib/mole/logger.rb
DELETED
@@ -1,134 +0,0 @@
|
|
1
|
-
require 'logging'
|
2
|
-
require 'forwardable'
|
3
|
-
|
4
|
-
module Mole
|
5
|
-
class Logger
|
6
|
-
class ConfigurationError < StandardError ; end #:nodoc:
|
7
|
-
|
8
|
-
attr_reader :log # here for testing, don't really use it.
|
9
|
-
|
10
|
-
extend Forwardable
|
11
|
-
def_delegators :@log, :debug, :warn, :info, :error, :fatal
|
12
|
-
def_delegators :@log, :level=, :level
|
13
|
-
def_delegators :@log, :debug?, :warn?, :info?, :error?, :fatal?
|
14
|
-
def_delegators :@log, :add, :clear_appenders
|
15
|
-
|
16
|
-
# there are more options here than are typically utilized for the
|
17
|
-
# logger, they are made available if someone wants to utilize
|
18
|
-
# them directly.
|
19
|
-
#
|
20
|
-
def self.default_options #:nodoc:
|
21
|
-
@default_options ||= {
|
22
|
-
|
23
|
-
# log event layout pattern
|
24
|
-
# YYYY-MM-DDTHH:MM:SS 12345 LEVEL LoggerName : The Log message
|
25
|
-
#
|
26
|
-
:layout_pattern => '%d %5p %5l %c : %m\n'.freeze ,
|
27
|
-
:logger_name => Mole ,
|
28
|
-
:additive => true ,
|
29
|
-
|
30
|
-
# log file configuration options
|
31
|
-
# age -> how often to rotate the logs if a file is used
|
32
|
-
# keep_count -> how many of the log files to keep
|
33
|
-
:log_level => :info ,
|
34
|
-
:log_file => $stdout ,
|
35
|
-
:log_file_age => 'daily'.freeze ,
|
36
|
-
:log_file_keep_count => 7 ,
|
37
|
-
|
38
|
-
# email logging options
|
39
|
-
# buffsize -> number of log events used as a threshold to send an
|
40
|
-
# email. If that is not reached before the program
|
41
|
-
# exists then the at_exit handler for logging will flush
|
42
|
-
# the log to smtp.
|
43
|
-
:email_alerts_to => nil ,
|
44
|
-
:email_alert_level => :error ,
|
45
|
-
:email_alert_server => nil ,
|
46
|
-
:email_alert_buffsize => 200 ,
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
# create a new logger
|
51
|
-
#
|
52
|
-
def initialize( opts = {} )
|
53
|
-
@options = ::Mole::Logger.default_options.merge(opts)
|
54
|
-
@log = ::Logging::Logger[@options[:logger_name]]
|
55
|
-
@layout = ::Logging::Layouts::Pattern.new( { :pattern => @options[:layout_pattern] } )
|
56
|
-
|
57
|
-
# add appenders explicitly, since this logger may already be defined and
|
58
|
-
# already have loggers
|
59
|
-
@appenders = []
|
60
|
-
@appenders << log_file_appender if @options[:log_file]
|
61
|
-
@appenders << email_appender if @options[:email_alerts_to]
|
62
|
-
|
63
|
-
@log.appenders = @appenders
|
64
|
-
@log.level = @options[:log_level]
|
65
|
-
@log.additive = @options[:additive]
|
66
|
-
end
|
67
|
-
|
68
|
-
# File appender, this is either an IO appender or a RollingFile appender
|
69
|
-
# depending on if an IO object or a String is passed in.
|
70
|
-
#
|
71
|
-
# a configuration error is raised in any other circumstance
|
72
|
-
def log_file_appender #:nodoc:
|
73
|
-
appender_name = "#{@log.name}-log_file_appender"
|
74
|
-
if String === @options[:log_file] then
|
75
|
-
::Logging::Appenders::RollingFile.new( appender_name,
|
76
|
-
{ :layout => @layout,
|
77
|
-
:filename => @options[:log_file],
|
78
|
-
:age => @options[:log_file_age],
|
79
|
-
:keep => @options[:log_file_keep_count],
|
80
|
-
:safe => true # make sure log files are rolled using lockfile
|
81
|
-
})
|
82
|
-
elsif @options[:log_file].respond_to?(:print) then
|
83
|
-
::Logging::Appenders::IO.new( appender_name, @options[:log_file], :layout => @layout )
|
84
|
-
else
|
85
|
-
raise ConfigurationError, "Invalid :log_file option [#{@options[:log_file].inspect}]"
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# an email appender that uses :email_alerts_to option to send emails to.
|
90
|
-
# :email_alerts_to can either be a singe email address as a string or an
|
91
|
-
# Array of email addresses. Any other option for :email_alerts_to is
|
92
|
-
# invalid and raises an error.
|
93
|
-
#
|
94
|
-
def email_appender #:nodoc:
|
95
|
-
email_alerts_to = [ @options[:email_alerts_to] ].flatten.reject { |x| x == nil }
|
96
|
-
raise ConfigurationError, "Invalid :email_alerts_to option [#{@options[:email_alerts_to].inspect}]" unless email_alerts_to.size > 0
|
97
|
-
::Logging::Appenders::Email.new("#{@log.name}-email_appender",
|
98
|
-
{
|
99
|
-
:level => @options[:email_alert_level],
|
100
|
-
:layout => @layout,
|
101
|
-
:from => "#{@log.name}",
|
102
|
-
:to => "#{email_alerts_to.join(', ')}",
|
103
|
-
:subject => "Logging Alert from #{@log.name} on #{ENV['HOSTNAME']}",
|
104
|
-
:server => @options[:email_alert_server],
|
105
|
-
:buffsize => @options[:email_alert_buffsize], # lines
|
106
|
-
})
|
107
|
-
end
|
108
|
-
|
109
|
-
# create a new logger thi logger has no options when it is created although
|
110
|
-
# more can be added with the logger instance that is returned. The
|
111
|
-
# appenders of the current instance of Logger will be set on the new
|
112
|
-
# logger and the options of the current logger will be applied
|
113
|
-
def for( arg ) #:nodoc:
|
114
|
-
new_logger = ::Logging::Logger[arg]
|
115
|
-
new_logger.level = @options[:level]
|
116
|
-
new_logger.additive = @options[:additive]
|
117
|
-
new_logger.appenders = @appenders
|
118
|
-
return new_logger
|
119
|
-
end
|
120
|
-
|
121
|
-
# mole the bastard - create an entry into MOle log file
|
122
|
-
def log_it( context, feature, user_id, args )
|
123
|
-
args ||= "no args"
|
124
|
-
user_id ||= "N/A"
|
125
|
-
ip_addr, browser_type = MoleLog.log_details( context )
|
126
|
-
info = []
|
127
|
-
args.keys.sort { |a,b| a.to_s <=> b.to_s }.each { |k| info << "#{k}=>#{args[k]}" }
|
128
|
-
buff = ""
|
129
|
-
buff << "[#{ip_addr}/#{browser_type}]--" if ip_addr and browser_type
|
130
|
-
buff << "#{context.class.name}(#{feature}) --- #{user_id} -> #{info.join( ', ' ) }"
|
131
|
-
self.info( buff )
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# Feature model - Tracks the various application features in the db.
|
2
|
-
class MoleFeature < ActiveRecord::Base
|
3
|
-
has_many :mole_logs
|
4
|
-
|
5
|
-
class << self
|
6
|
-
# famous constants...
|
7
|
-
def all() "ALL" ; end
|
8
|
-
def exception() "Exception" ; end
|
9
|
-
def performance() "Performance"; end
|
10
|
-
|
11
|
-
# find performance feature
|
12
|
-
def find_performance_feature( app_name )
|
13
|
-
find_or_create_feature( performance, app_name )
|
14
|
-
end
|
15
|
-
|
16
|
-
# find exception feature
|
17
|
-
def find_exception_feature( app_name )
|
18
|
-
find_or_create_feature( exception, app_name )
|
19
|
-
end
|
20
|
-
|
21
|
-
# Find the all feature ( wildcard feature for given app )
|
22
|
-
def find_all_feature( app_name )
|
23
|
-
find_or_create_feature( all, app_name )
|
24
|
-
end
|
25
|
-
|
26
|
-
# Find all the MOled applications
|
27
|
-
def find_moled_application_names
|
28
|
-
res = find( :all,
|
29
|
-
:select => "distinct( app_name )",
|
30
|
-
:order => "name asc" )
|
31
|
-
res.map(&:app_name)
|
32
|
-
end
|
33
|
-
|
34
|
-
# Finds all the features available for a given application
|
35
|
-
def find_features( app_name )
|
36
|
-
# Creates the all feature if necessary
|
37
|
-
find_all_feature( app_name )
|
38
|
-
find( :all,
|
39
|
-
:conditions => ["app_name = ?", app_name],
|
40
|
-
:select => "id, name, context",
|
41
|
-
:order => "name asc" )
|
42
|
-
end
|
43
|
-
|
44
|
-
# locates an existing feature or create a new one if it does not exist.
|
45
|
-
def find_or_create_feature( name, app_name, ctx_name=nil )
|
46
|
-
if name.nil? or name.empty?
|
47
|
-
::Mole.logger.error( "--- MOLE ERROR - Invalid feature. Empty or nil" )
|
48
|
-
return nil
|
49
|
-
end
|
50
|
-
if ctx_name
|
51
|
-
res = find_by_name_and_context_and_app_name( name, ctx_name, app_name )
|
52
|
-
else
|
53
|
-
res = find_by_name_and_app_name( name, app_name )
|
54
|
-
end
|
55
|
-
res || create(:name => name,:context => ctx_name, :app_name => app_name )
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
data/lib/mole/models/mole_log.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# Log Model - Tracks and record user interactions with various features
|
2
|
-
# This will make it easy to write an app on top on the MOle to track a
|
3
|
-
# particular application utilization.
|
4
|
-
class MoleLog < ActiveRecord::Base
|
5
|
-
belongs_to :mole_feature
|
6
|
-
|
7
|
-
class << self
|
8
|
-
# mole the bastard - create db entry into mole logs
|
9
|
-
def log_it( context, feature, user_id, args )
|
10
|
-
args ||= "no args"
|
11
|
-
user_id ||= "N/A"
|
12
|
-
ip_addr, browser_type = log_details( context )
|
13
|
-
MoleLog.create( :mole_feature => feature,
|
14
|
-
:user_id => user_id,
|
15
|
-
:host_name => `hostname`,
|
16
|
-
:params => args.to_yaml,
|
17
|
-
:ip_address => ip_addr,
|
18
|
-
:browser_type => browser_type )
|
19
|
-
end
|
20
|
-
|
21
|
-
# extract orginating ip address and browser type
|
22
|
-
def log_details( context ) #:nodoc:
|
23
|
-
ip_addr, browser_type = nil
|
24
|
-
if context.respond_to? :request
|
25
|
-
ip_addr, browser_type = context.request.env['REMOTE_ADDR'], context.request.env['HTTP_USER_AGENT']
|
26
|
-
end
|
27
|
-
return ip_addr, browser_type
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
end
|
data/lib/mole/module.rb
DELETED
@@ -1,292 +0,0 @@
|
|
1
|
-
# =============================================================================
|
2
|
-
# Extends module to inject interceptors
|
3
|
-
# =============================================================================
|
4
|
-
require 'benchmark'
|
5
|
-
require 'active_support'
|
6
|
-
|
7
|
-
class Module
|
8
|
-
# intercepts a collection of features and wrap performance check based on the
|
9
|
-
# specified :perf_threshold. The trigger defaults to 5 secs if not explicitly set.
|
10
|
-
#
|
11
|
-
# Example:
|
12
|
-
#
|
13
|
-
# MyClass.mole_perf do |context, action, elapsed_time, ret, block, *args|
|
14
|
-
# Mole::DbMole.perf_it( context.session[:user_id],
|
15
|
-
# :controller => context.class.name,
|
16
|
-
# :action => action,
|
17
|
-
# :elapsed_time => "%3.3f" % elapsed_time )
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# This will trap all public methods on the MyClass that takes more than
|
21
|
-
# :perf_threshold to complete. You can override this default by using the option
|
22
|
-
# :features => [m1,m2,...]. This is handy for controller moling rails
|
23
|
-
# and merb context.
|
24
|
-
#
|
25
|
-
# If you elect not to use the block form of the call, you can pass in the
|
26
|
-
# following arguments to the option hash:
|
27
|
-
# <tt>:interceptor</tt>:: The class name of your interceptor class
|
28
|
-
# <tt>:method</tt>:: The name of the method to callback the interceptor on
|
29
|
-
# once a perf condition has been trapped.
|
30
|
-
def mole_perf( opts={}, &interceptor )
|
31
|
-
opts[:interceptor] ||= interceptor
|
32
|
-
opts[:method] ||= :call
|
33
|
-
opts[:features] ||= instance_methods( false )
|
34
|
-
opts[:features].each do |feature|
|
35
|
-
wrap feature
|
36
|
-
perf_mole_filters[feature.to_s] << [opts[:interceptor], opts[:method]]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# monitors a collections of features and wrap rescue logic to trap unchecked
|
41
|
-
# exceptions. You can handle to trap differently by either logging the event
|
42
|
-
# in the db or sending out email/IM notification.
|
43
|
-
#
|
44
|
-
# Example:
|
45
|
-
#
|
46
|
-
# MyClass.mole_unchecked do |context, action, boom, block, *args|
|
47
|
-
# Mole::Moler.check_it( context.session[:user_id],
|
48
|
-
# :controller => context.class.name,
|
49
|
-
# :action => action,
|
50
|
-
# :boom => boom )
|
51
|
-
# end
|
52
|
-
#
|
53
|
-
# This will wrap all public instance methods on MyClass. If any of these methods
|
54
|
-
# raises an unchecked exception, the MOle will surface the condition.
|
55
|
-
# This call also takes in a :features option to specify a list of methods if the
|
56
|
-
# default instance methods is not suitable, you can pass in a collection of methods
|
57
|
-
# that you wish to mole. This is handy in the case of Rails/Merb where conveniences
|
58
|
-
# are provided to gather controller actions
|
59
|
-
#
|
60
|
-
# If you elect not to use the block form of the call, you can pass in the
|
61
|
-
# following arguments to the option hash:
|
62
|
-
# <tt>:interceptor</tt>:: The class name of your interceptor class
|
63
|
-
# <tt>:method</tt>:: The name of the method to callback the interceptor on
|
64
|
-
# once an exception condition has been trapped.
|
65
|
-
def mole_unchecked( opts={}, &interceptor )
|
66
|
-
opts[:interceptor] ||= interceptor
|
67
|
-
opts[:method] ||= :call
|
68
|
-
opts[:features] ||= instance_methods( false )
|
69
|
-
opts[:features].each do |feature|
|
70
|
-
wrap feature
|
71
|
-
unchecked_mole_filters[feature.to_s] << [opts[:interceptor], opts[:method]]
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
# intercepts a feature before the feature is called. During the callback
|
76
|
-
# you will have access to the object ( context ) on which the call is intercepted,
|
77
|
-
# as well as the arguments/block, the feature was issued with.
|
78
|
-
#
|
79
|
-
# Example:
|
80
|
-
#
|
81
|
-
# MyClass.mole_before( :feature => :blee ) do |context, feature, block, *args|
|
82
|
-
# Mole::Moler.mole_it( context, feature, context.session[:user_id],
|
83
|
-
# :args => args )
|
84
|
-
# end
|
85
|
-
#
|
86
|
-
# This will wrap the method blee with a before interceptor. Before the blee call
|
87
|
-
# is issued on MyClass, control will be handed to the before interceptor.
|
88
|
-
#
|
89
|
-
# Options:
|
90
|
-
# <tt>:feature</tt>:: The name of the feature to be intercepted
|
91
|
-
# If you elect not to use the block form of the call, you can pass in the
|
92
|
-
# following arguments to the option hash:
|
93
|
-
# <tt>:interceptor</tt>:: The class name of your interceptor class. If no interceptor block is specified
|
94
|
-
# <tt>:method</tt>:: The name of the method to callback the interceptor on
|
95
|
-
# once an exception condition has been trapped.
|
96
|
-
def mole_before(opts={}, &interceptor)
|
97
|
-
raise "Missing :feature option" if opts[:feature].nil? or opts[:feature].to_s.empty?
|
98
|
-
opts[:interceptor] ||= interceptor
|
99
|
-
opts[:method] ||= :call
|
100
|
-
feature = opts[:feature].to_s
|
101
|
-
if before_mole_filters[feature].empty?
|
102
|
-
wrap feature
|
103
|
-
before_mole_filters[feature] << [opts[:interceptor], opts[:method]]
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
# intercepts a feature after the feature is called. During the callback
|
108
|
-
# you will have access to the object ( context ) on which the call is intercepted,
|
109
|
-
# as well as the arguments/block and return values the feature was issued with.
|
110
|
-
#
|
111
|
-
# Example:
|
112
|
-
#
|
113
|
-
# MyClass.mole_after( :feature => :blee ) do |context, feature, ret_val, block, *args|
|
114
|
-
# Mole::Moler.mole_it( context, feature, context.session[:user_id],
|
115
|
-
# :args => args )
|
116
|
-
# end
|
117
|
-
#
|
118
|
-
# This will wrap the method blee with an after interceptor. After the blee call
|
119
|
-
# is issued on MyClass, control will be handed to the after interceptor.
|
120
|
-
#
|
121
|
-
# Options:
|
122
|
-
# <tt>:feature</tt>:: The name of the feature to be intercepted
|
123
|
-
# <tt>:interceptor</tt>:: The class name of your interceptor class. If no interceptor block is specified
|
124
|
-
# <tt>:method</tt>:: The name of the method to callback the interceptor on
|
125
|
-
# once an exception condition has been trapped.
|
126
|
-
def mole_after(opts = {}, &interceptor)
|
127
|
-
raise "Missing :feature option" if opts[:feature].nil? or opts[:feature].to_s.empty?
|
128
|
-
opts[:interceptor] ||= interceptor
|
129
|
-
opts[:method] ||= :call
|
130
|
-
feature = opts[:feature].to_s
|
131
|
-
if after_mole_filters[feature].empty?
|
132
|
-
wrap feature
|
133
|
-
after_mole_filters[feature] << [opts[:interceptor], opts[:method]]
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
# ---------------------------------------------------------------------------
|
138
|
-
# Dumps moled feature info
|
139
|
-
def mole_dump( msg=nil )
|
140
|
-
puts "\n------------------------------------------------------------------"
|
141
|
-
puts "From #{msg}" if msg
|
142
|
-
puts "MOle Info for class <- #{self} ->"
|
143
|
-
puts "\nBefore filters"
|
144
|
-
before_mole_filters.keys.sort.each { |k| puts "\t#{k} --> #{before_mole_filters[k]}" }
|
145
|
-
puts "\nAfter filters"
|
146
|
-
after_mole_filters.keys.sort.each { |k| puts "\t#{k} --> #{after_mole_filters[k]}" }
|
147
|
-
puts "\nUnchecked filters"
|
148
|
-
unchecked_mole_filters.keys.sort.each { |k| puts "\t#{k} --> #{unchecked_mole_filters[k]}" }
|
149
|
-
puts "\nPerf filters"
|
150
|
-
perf_mole_filters.keys.sort.each { |k| puts "\t#{k} --> #{perf_mole_filters[k]}" }
|
151
|
-
puts "---------------------------------------------------------------------\n"
|
152
|
-
end
|
153
|
-
|
154
|
-
# ===========================================================================
|
155
|
-
private
|
156
|
-
|
157
|
-
# Strip out all invalid punctuation
|
158
|
-
def clean_method_name( method )
|
159
|
-
return method.to_s.sub(/([?!=])$/, ''), $1
|
160
|
-
end
|
161
|
-
|
162
|
-
# Clear MOle state for this class # Used for testing only
|
163
|
-
def mole_clear!
|
164
|
-
@before_mole_filters = nil
|
165
|
-
@after_mole_filters = nil
|
166
|
-
@perf_mole_filters = nil
|
167
|
-
@unchecked_mole_filters = nil
|
168
|
-
end
|
169
|
-
|
170
|
-
# ===========================================================================
|
171
|
-
public
|
172
|
-
|
173
|
-
# -------------------------------------------------------------------------
|
174
|
-
# Holds before filters
|
175
|
-
def before_mole_filters #:nodoc:
|
176
|
-
@before_mole_filters ||= Hash.new{ |h,k| h[k] = [] }
|
177
|
-
end
|
178
|
-
|
179
|
-
# -------------------------------------------------------------------------
|
180
|
-
# Holds after filters
|
181
|
-
def after_mole_filters #:nodoc:
|
182
|
-
@after_mole_filters ||= Hash.new{ |h,k| h[k] = [] }
|
183
|
-
end
|
184
|
-
|
185
|
-
# -------------------------------------------------------------------------
|
186
|
-
# Holds perf around filters
|
187
|
-
def perf_mole_filters #:nodoc:
|
188
|
-
@perf_mole_filters ||= Hash.new{ |h,k| h[k] = []}
|
189
|
-
end
|
190
|
-
|
191
|
-
# -------------------------------------------------------------------------
|
192
|
-
# Holds unchecked exception filters
|
193
|
-
def unchecked_mole_filters #:nodoc:
|
194
|
-
@unchecked_mole_filters ||= Hash.new{ |h,k| h[k] = []}
|
195
|
-
end
|
196
|
-
|
197
|
-
# -------------------------------------------------------------------------
|
198
|
-
# Attempt to find singleton class method with given name
|
199
|
-
# TODO Figure out how to get method for static signature...
|
200
|
-
# def find_public_class_method(method)
|
201
|
-
# singleton_methods.each { |name| puts "Looking for #{method}--#{method.class} -- #{name}#{name.class}";return name if name == method }
|
202
|
-
# nil
|
203
|
-
# end
|
204
|
-
|
205
|
-
# -------------------------------------------------------------------------
|
206
|
-
# Wrap method call
|
207
|
-
# TODO Add support for wrapping class methods ??
|
208
|
-
def wrap( method ) #:nodoc:
|
209
|
-
return if wrapped?( method )
|
210
|
-
begin
|
211
|
-
between = instance_method( method )
|
212
|
-
rescue
|
213
|
-
# between = find_public_class_method( method )
|
214
|
-
raise "Unable to find moled feature `#{method}" unless(between)
|
215
|
-
end
|
216
|
-
method_name, punctuation = clean_method_name( method )
|
217
|
-
code = <<-code
|
218
|
-
def #{method_name}_with_mole#{punctuation} (*a, &b)
|
219
|
-
key = '#{method}'
|
220
|
-
klass = self.class
|
221
|
-
between = klass.wrapped[key]
|
222
|
-
ret_val = nil
|
223
|
-
klass.apply_before_filters( klass.before_mole_filters[key], self, key, *a, &b ) if klass.before_mole_filters[key]
|
224
|
-
begin
|
225
|
-
elapsed = Benchmark::realtime do
|
226
|
-
ret_val = between.bind(self).call( *a, &b )
|
227
|
-
end
|
228
|
-
klass.apply_perf_filters( elapsed, klass.perf_mole_filters[key], self, key, ret_val, *a, &b ) if klass.perf_mole_filters[key]
|
229
|
-
rescue => boom
|
230
|
-
klass.apply_unchecked_filters( boom, klass.unchecked_mole_filters[key], self, key, *a, &b ) if klass.unchecked_mole_filters[key]
|
231
|
-
raise boom
|
232
|
-
end
|
233
|
-
klass.apply_after_filters( klass.after_mole_filters[key], self, key, ret_val, *a, &b ) if klass.after_mole_filters[key]
|
234
|
-
ret_val
|
235
|
-
end
|
236
|
-
code
|
237
|
-
|
238
|
-
module_eval code
|
239
|
-
alias_method_chain method, "mole"
|
240
|
-
wrapped[method.to_s] = between
|
241
|
-
end
|
242
|
-
|
243
|
-
def apply_before_filters( filters, clazz, key, *a, &b ) #:nodoc:
|
244
|
-
begin
|
245
|
-
filters.each { |r,m| r.send( m, clazz, key, b, *a ) }
|
246
|
-
rescue => ca_boom
|
247
|
-
::Mole.logger.error ">>> MOle Failure: Before-Filter -- " + ca_boom
|
248
|
-
ca_boom.backtrace.each { |l| ::Mole.logger.error l }
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
def apply_after_filters( filters, clazz, key, ret_val, *a, &b ) #:nodoc:
|
253
|
-
begin
|
254
|
-
filters.each { |r,m| r.send( m, clazz, key, ret_val, b, *a ) }
|
255
|
-
rescue => ca_boom
|
256
|
-
::Mole.logger.error ">>> MOle Failure: After-Filter -- " + ca_boom
|
257
|
-
ca_boom.backtrace.each { |l| ::Mole.logger.error l }
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
def apply_perf_filters( elapsed, filters, clazz, key, ret_val, *a, &b ) #:nodoc:
|
262
|
-
begin
|
263
|
-
if ( elapsed >= Mole.perf_threshold )
|
264
|
-
filters.each { |r,m| r.send( m, clazz, key, elapsed, ret_val, b, *a ) }
|
265
|
-
end
|
266
|
-
rescue => ca_boom
|
267
|
-
::Mole.logger.error ">>> MOle Failure: Perf-Filter -- " + ca_boom
|
268
|
-
ca_boom.backtrace.each { |l| ::Mole.logger.error l }
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
def apply_unchecked_filters( boom, filters, clazz, key, *a, &b ) #:nodoc:
|
273
|
-
begin
|
274
|
-
filters.each { |r,m| r.send( m, clazz, key, boom, b, *a ) }
|
275
|
-
rescue => ca_boom
|
276
|
-
::Mole.logger.error ">>> MOle Failure: Unchecked-Filter -- " + ca_boom
|
277
|
-
ca_boom.backtrace.each { |l| ::Mole.logger.error l }
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
# ---------------------------------------------------------------------------
|
282
|
-
# Log wrapped class
|
283
|
-
def wrapped #:nodoc:
|
284
|
-
@wrapped ||= {}
|
285
|
-
end
|
286
|
-
|
287
|
-
# ---------------------------------------------------------------------------
|
288
|
-
# Check if method has been wrapped
|
289
|
-
def wrapped?(which) #:nodoc:
|
290
|
-
wrapped.has_key?(which.to_s)
|
291
|
-
end
|
292
|
-
end
|