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.
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