mole 1.0.12 → 1.0.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. metadata +24 -146
  2. data/History.txt +0 -17
  3. data/Manifest.txt +0 -132
  4. data/README.txt +0 -216
  5. data/Rakefile +0 -46
  6. data/config/database.yml +0 -21
  7. data/config/test_database.yml +0 -69
  8. data/lib/mole.rb +0 -260
  9. data/lib/mole/db/migrate.rb +0 -90
  10. data/lib/mole/e_mole.rb +0 -75
  11. data/lib/mole/logger.rb +0 -134
  12. data/lib/mole/models/mole_feature.rb +0 -58
  13. data/lib/mole/models/mole_log.rb +0 -31
  14. data/lib/mole/module.rb +0 -292
  15. data/lib/mole/moler.rb +0 -71
  16. data/lib/mole/utils/frameworks.rb +0 -53
  17. data/lib/mole/version.rb +0 -15
  18. data/samples/merbapp/README +0 -14
  19. data/samples/merbapp/Rakefile +0 -124
  20. data/samples/merbapp/app/controllers/application.rb +0 -3
  21. data/samples/merbapp/app/controllers/exceptions.rb +0 -13
  22. data/samples/merbapp/app/controllers/moled.rb +0 -25
  23. data/samples/merbapp/app/helpers/global_helper.rb +0 -5
  24. data/samples/merbapp/app/mailers/views/layout/application.html.erb +0 -1
  25. data/samples/merbapp/app/mailers/views/layout/application.text.erb +0 -1
  26. data/samples/merbapp/app/parts/views/layout/application.html.erb +0 -1
  27. data/samples/merbapp/app/views/exceptions/internal_server_error.html.erb +0 -216
  28. data/samples/merbapp/app/views/exceptions/not_acceptable.html.erb +0 -38
  29. data/samples/merbapp/app/views/exceptions/not_found.html.erb +0 -40
  30. data/samples/merbapp/app/views/layout/application.html.erb +0 -11
  31. data/samples/merbapp/app/views/moled/index.html.erb +0 -5
  32. data/samples/merbapp/app/views/moled/result.html.erb +0 -5
  33. data/samples/merbapp/config/boot.rb +0 -11
  34. data/samples/merbapp/config/dependencies.rb +0 -41
  35. data/samples/merbapp/config/environments/development.rb +0 -1
  36. data/samples/merbapp/config/environments/production.rb +0 -1
  37. data/samples/merbapp/config/environments/test.rb +0 -1
  38. data/samples/merbapp/config/merb.yml +0 -82
  39. data/samples/merbapp/config/merb_init.rb +0 -26
  40. data/samples/merbapp/config/mole_config.rb +0 -33
  41. data/samples/merbapp/config/router.rb +0 -38
  42. data/samples/merbapp/config/upload.conf +0 -0
  43. data/samples/merbapp/public/images/merb.jpg +0 -0
  44. data/samples/merbapp/public/merb.fcgi +0 -6
  45. data/samples/merbapp/public/stylesheets/master.css +0 -119
  46. data/samples/merbapp/script/destroy +0 -32
  47. data/samples/merbapp/script/generate +0 -32
  48. data/samples/merbapp/script/stop_merb +0 -13
  49. data/samples/merbapp/spec/spec_helper.rb +0 -15
  50. data/samples/merbapp/test/test_helper.rb +0 -14
  51. data/samples/railsapp/README +0 -14
  52. data/samples/railsapp/Rakefile +0 -10
  53. data/samples/railsapp/app/controllers/application.rb +0 -13
  54. data/samples/railsapp/app/controllers/moled_controller.rb +0 -23
  55. data/samples/railsapp/app/helpers/application_helper.rb +0 -3
  56. data/samples/railsapp/app/views/moled/index.html.erb +0 -5
  57. data/samples/railsapp/app/views/moled/result.html.erb +0 -5
  58. data/samples/railsapp/config/boot.rb +0 -109
  59. data/samples/railsapp/config/database.yml +0 -13
  60. data/samples/railsapp/config/environment.rb +0 -59
  61. data/samples/railsapp/config/environments/development.rb +0 -18
  62. data/samples/railsapp/config/environments/production.rb +0 -20
  63. data/samples/railsapp/config/environments/test.rb +0 -22
  64. data/samples/railsapp/config/initializers/inflections.rb +0 -10
  65. data/samples/railsapp/config/initializers/mime_types.rb +0 -5
  66. data/samples/railsapp/config/initializers/mole.rb +0 -10
  67. data/samples/railsapp/config/moles/mole_config.rb +0 -44
  68. data/samples/railsapp/config/routes.rb +0 -35
  69. data/samples/railsapp/doc/README_FOR_APP +0 -2
  70. data/samples/railsapp/public/.htaccess +0 -40
  71. data/samples/railsapp/public/404.html +0 -30
  72. data/samples/railsapp/public/422.html +0 -30
  73. data/samples/railsapp/public/500.html +0 -30
  74. data/samples/railsapp/public/dispatch.cgi +0 -10
  75. data/samples/railsapp/public/dispatch.fcgi +0 -24
  76. data/samples/railsapp/public/dispatch.rb +0 -10
  77. data/samples/railsapp/public/favicon.ico +0 -0
  78. data/samples/railsapp/public/images/rails.png +0 -0
  79. data/samples/railsapp/public/javascripts/application.js +0 -2
  80. data/samples/railsapp/public/javascripts/controls.js +0 -963
  81. data/samples/railsapp/public/javascripts/dragdrop.js +0 -972
  82. data/samples/railsapp/public/javascripts/effects.js +0 -1120
  83. data/samples/railsapp/public/javascripts/prototype.js +0 -4225
  84. data/samples/railsapp/public/robots.txt +0 -5
  85. data/samples/railsapp/script/about +0 -3
  86. data/samples/railsapp/script/console +0 -3
  87. data/samples/railsapp/script/destroy +0 -3
  88. data/samples/railsapp/script/generate +0 -3
  89. data/samples/railsapp/script/performance/benchmarker +0 -3
  90. data/samples/railsapp/script/performance/profiler +0 -3
  91. data/samples/railsapp/script/performance/request +0 -3
  92. data/samples/railsapp/script/plugin +0 -3
  93. data/samples/railsapp/script/process/inspector +0 -3
  94. data/samples/railsapp/script/process/reaper +0 -3
  95. data/samples/railsapp/script/process/spawner +0 -3
  96. data/samples/railsapp/script/runner +0 -3
  97. data/samples/railsapp/script/server +0 -3
  98. data/samples/railsapp/test/test_helper.rb +0 -38
  99. data/samples/rubyapp/README +0 -22
  100. data/samples/rubyapp/bin/ruby_app +0 -35
  101. data/samples/rubyapp/config/mole_conf.rb +0 -31
  102. data/samples/rubyapp/lib/fred.rb +0 -22
  103. data/spec/config/auto_mole_config.rb +0 -26
  104. data/spec/config/mole_config.rb +0 -0
  105. data/spec/config/moles/fred_config.rb +0 -0
  106. data/spec/data/blee.rb +0 -64
  107. data/spec/db/migrate_spec.rb +0 -19
  108. data/spec/emole_spec.rb +0 -43
  109. data/spec/logger_spec.rb +0 -56
  110. data/spec/models/mole_feature_spec.rb +0 -48
  111. data/spec/models/mole_log_spec.rb +0 -62
  112. data/spec/module_spec.rb +0 -229
  113. data/spec/mole_spec.rb +0 -135
  114. data/spec/moler_spec.rb +0 -77
  115. data/spec/spec_helper.rb +0 -76
  116. data/spec/utils/framework_spec.rb +0 -99
  117. data/tasks/ann.rake +0 -76
  118. data/tasks/annotations.rake +0 -22
  119. data/tasks/doc.rake +0 -48
  120. data/tasks/gem.rake +0 -110
  121. data/tasks/manifest.rake +0 -49
  122. data/tasks/mole.rake +0 -115
  123. data/tasks/post_load.rake +0 -26
  124. data/tasks/rubyforge.rake +0 -57
  125. data/tasks/setup.rb +0 -227
  126. data/tasks/spec.rake +0 -54
  127. data/tasks/svn.rake +0 -44
  128. data/tasks/test.rake +0 -38
  129. data/templates/mole/e_mole/exception_alerts.rhtml +0 -14
  130. data/templates/mole/e_mole/feature_alerts.rhtml +0 -11
  131. data/templates/mole/e_mole/perf_alerts.rhtml +0 -12
@@ -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
@@ -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
@@ -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
@@ -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