mole 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +45 -0
  3. data/README.txt +140 -0
  4. data/Rakefile +47 -0
  5. data/bin/mole +8 -0
  6. data/bin/molify +64 -0
  7. data/config/database.yml +21 -0
  8. data/config/test_database.yml +69 -0
  9. data/lib/mole/db/migrate.rb +90 -0
  10. data/lib/mole/e_mole.rb +74 -0
  11. data/lib/mole/logger.rb +131 -0
  12. data/lib/mole/models/mole_feature.rb +45 -0
  13. data/lib/mole/models/mole_log.rb +56 -0
  14. data/lib/mole/module.rb +274 -0
  15. data/lib/mole/moler.rb +51 -0
  16. data/lib/mole/utils/frameworks.rb +22 -0
  17. data/lib/mole/version.rb +15 -0
  18. data/lib/mole.rb +175 -0
  19. data/spec/data/blee.rb +53 -0
  20. data/spec/db/migrate_spec.rb +19 -0
  21. data/spec/emole_spec.rb +43 -0
  22. data/spec/logger_spec.rb +56 -0
  23. data/spec/models/mole_feature_spec.rb +37 -0
  24. data/spec/models/mole_log_spec.rb +73 -0
  25. data/spec/module_spec.rb +171 -0
  26. data/spec/mole_spec.rb +38 -0
  27. data/spec/moler_spec.rb +65 -0
  28. data/spec/spec_helper.rb +59 -0
  29. data/spec/utils/framework_spec.rb +46 -0
  30. data/tasks/ann.rake +76 -0
  31. data/tasks/annotations.rake +22 -0
  32. data/tasks/doc.rake +48 -0
  33. data/tasks/gem.rake +110 -0
  34. data/tasks/manifest.rake +49 -0
  35. data/tasks/mole.rake +115 -0
  36. data/tasks/post_load.rake +26 -0
  37. data/tasks/rubyforge.rake +57 -0
  38. data/tasks/setup.rb +227 -0
  39. data/tasks/spec.rake +54 -0
  40. data/tasks/svn.rake +44 -0
  41. data/tasks/test.rake +38 -0
  42. data/templates/mole/e_mole/exception_alerts.rhtml +14 -0
  43. data/templates/mole/e_mole/feature_alerts.rhtml +11 -0
  44. data/templates/mole/e_mole/perf_alerts.rhtml +12 -0
  45. data/test/test_mole.rb +0 -0
  46. metadata +120 -0
@@ -0,0 +1,131 @@
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 => "mail.collectiveintellect.com".freeze ,
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
+ self.info( "[#{ip_addr}/#{browser_type}]-- #{context}(#{feature}) --- #{user_id} -> #{info.join( ', ' ) }" )
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,45 @@
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_feature( performance, app_name )
14
+ end
15
+
16
+ # find exception feature
17
+ def find_exception_feature( app_name )
18
+ find_feature( exception, app_name )
19
+ end
20
+
21
+ def find_all_feature( app_name )
22
+ find_feature( all, app_name )
23
+ end
24
+
25
+ # Finds all the features available for a given application
26
+ def find_features( app_name )
27
+ # Creates the all feature if necessary
28
+ find_all_feature( app_name )
29
+ MoleFeature.find( :all,
30
+ :conditions => ["app_name = ?", app_name],
31
+ :select => "id, name, context",
32
+ :order => "name asc" )
33
+ end
34
+
35
+ # locates an existing feature or create a new one
36
+ def find_feature( name, app_name, ctx_name=nil )
37
+ if name.nil? or name.empty?
38
+ ::Mole.logger.error( "--- MOLE ERROR - Invalid feature. Empty or nil" )
39
+ return nil
40
+ end
41
+ find_by_name_and_context_and_app_name( name, ctx_name, app_name ) ||
42
+ create(:name => name,:context => ctx_name, :app_name => app_name )
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,56 @@
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
+ # logs unchecked exception
9
+ def check_it( context, user_id, args )
10
+ if args[:boom]
11
+ args[:trace] = dump_stack( args[:boom] )
12
+ args[:boom] = args[:boom].to_s
13
+ end
14
+ log_it( context, MoleFeature::find_exception_feature( ::Mole.application ), user_id, args ) if ::Mole::moleable?
15
+ end
16
+
17
+ # logs perf occurence
18
+ def perf_it( context, user_id, args )
19
+ log_it( context, MoleFeature::find_performance_feature( ::Mole.application ), user_id, args ) if ::Mole::moleable?
20
+ end
21
+
22
+ # persists mole information to the database.
23
+ # This call will store information on the mole dedicated tables namely
24
+ # mole_features and mole_logs.
25
+ def mole_it(context, feature, user_id, args)
26
+ log_it( context, MoleFeature::find_feature( feature, ::Mole.application, context.class.name ), user_id, args ) if ::Mole::moleable?
27
+ end
28
+
29
+ # mole the bastard - create db entry into mole logs
30
+ def log_it( context, feature, user_id, args )
31
+ args ||= "no args"
32
+ user_id ||= "N/A"
33
+ ip_addr, browser_type = log_details( context )
34
+ MoleLog.create( :mole_feature => feature,
35
+ :user_id => user_id,
36
+ :host_name => `hostname`,
37
+ :params => args.to_yaml,
38
+ :ip_address => ip_addr,
39
+ :browser_type => browser_type )
40
+ end
41
+
42
+ # dumps partial stack
43
+ def dump_stack( boom )
44
+ buff = boom.backtrace[0...3].join( "-" )
45
+ end
46
+
47
+ # extract orginating ip address and browser type
48
+ def log_details( context ) #:nodoc:
49
+ ip_addr, browser_type = nil
50
+ if context.respond_to? :request
51
+ ip_addr, browser_type = context.request.env['REMOTE_ADDR'], context.request.env['HTTP_USER_AGENT']
52
+ end
53
+ return ip_addr, browser_type
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,274 @@
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, 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={}, &filter )
31
+ opts[:interceptor] ||= filter
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, 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={}, &filter )
66
+ opts[:interceptor] ||= filter
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, *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 = {}, &filter)
97
+ raise "Missing :feature option" if opts[:feature].nil? or opts[:feature].to_s.empty?
98
+ opts[:interceptor] ||= filter
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, *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
+ # def dump
138
+ # puts "Filters for class <- #{self} ->"
139
+ # puts "Before filters"
140
+ # before_mole_filters.each_pair do |k,v|
141
+ # puts "#{k} --> #{v}"
142
+ # end
143
+ # puts "After filters"
144
+ # after_mole_filters.each_pair do |k,v|
145
+ # puts "#{k} --> #{v}"
146
+ # end
147
+ # puts "Unchecked filters"
148
+ # unchecked_mole_filters.each_pair do |k,v|
149
+ # puts "#{k} --> #{v}"
150
+ # end
151
+ # puts "Perf filters"
152
+ # perf_mole_filters.each_pair do |k,v|
153
+ # puts "#{k} --> #{v}"
154
+ # end
155
+ # end
156
+
157
+ # ===========================================================================
158
+ # protected
159
+
160
+ # ---------------------------------------------------------------------------
161
+ # Holds before filters
162
+ def before_mole_filters #:nodoc:
163
+ @before_mole_filters ||= Hash.new{ |h,k| h[k] = [] }
164
+ end
165
+
166
+ # ---------------------------------------------------------------------------
167
+ # Holds after filters
168
+ def after_mole_filters #:nodoc:
169
+ @after_mole_filters ||= Hash.new{ |h,k| h[k] = [] }
170
+ end
171
+
172
+ # Holds perf around filters
173
+ def perf_mole_filters #:nodoc:
174
+ @perf_mole_filters ||= Hash.new{ |h,k| h[k] = []}
175
+ end
176
+
177
+ # Holds unchecked exception filters
178
+ def unchecked_mole_filters #:nodoc:
179
+ @unchecked_mole_filters ||= Hash.new{ |h,k| h[k] = []}
180
+ end
181
+
182
+ # Attempt to find singleton class method with given name
183
+ # TODO Figure out how to get method for static signature...
184
+ # def find_public_class_method(method)
185
+ # singleton_methods.each { |name| puts "Looking for #{method}--#{method.class} -- #{name}#{name.class}";return name if name == method }
186
+ # nil
187
+ # end
188
+
189
+ # Wrap method call
190
+ # TODO Add support for wrapping class methods ??
191
+ def wrap( method ) #:nodoc:
192
+ return if wrapped?( method )
193
+ begin
194
+ between = instance_method( method )
195
+ rescue
196
+ # between = find_public_class_method( method )
197
+ raise NameError unless(between)
198
+ end
199
+ code = <<-code
200
+ def #{method}_with_mole (*a, &b)
201
+ key = '#{method}'
202
+ klass = self.class
203
+ between = klass.wrapped[key]
204
+ ret_val = nil
205
+ klass.apply_before_filters( klass.before_mole_filters[key], self, key, *a, &b )
206
+ begin
207
+ elapsed = Benchmark::realtime do
208
+ ret_val = between.bind(self).call(*a)
209
+ end
210
+ klass.apply_perf_filters( elapsed, klass.perf_mole_filters[key], self, key, *a, &b )
211
+ rescue => boom
212
+ klass.apply_unchecked_filters( boom, klass.unchecked_mole_filters[key], self, key, *a, &b )
213
+ raise boom
214
+ end
215
+ klass.apply_after_filters( klass.after_mole_filters[key], self, key, *a, &b )
216
+ ret_val
217
+ end
218
+ code
219
+
220
+ module_eval code
221
+ alias_method_chain method, "mole"
222
+ wrapped[method.to_s] = between
223
+ end
224
+
225
+ def apply_before_filters( filters, clazz, key, *a, &b ) #:nodoc:
226
+ begin
227
+ filters.each { |r,m| r.send(m, clazz, key, *a, &b) }
228
+ rescue => ca_boom
229
+ ::Mole.logger.error ">>> Mole Error: Before-Filter -- " + ca_boom
230
+ # ca_boom.backtrace.each { |l| ::Mole.logger.error l }
231
+ end
232
+ end
233
+
234
+ def apply_after_filters( filters, clazz, key, *a, &b ) #:nodoc:
235
+ begin
236
+ filters.each { |r,m| r.send(m, clazz, key, *a, &b) }
237
+ rescue => ca_boom
238
+ ::Mole.logger.error ">>> Mole Error: After-Filter -- " + ca_boom
239
+ # ca_boom.backtrace.each { |l| ::Mole.logger.error l }
240
+ end
241
+ end
242
+
243
+ def apply_perf_filters( elapsed, filters, clazz, key, *a ) #:nodoc:
244
+ begin
245
+ if ( elapsed >= Mole.perf_threshold )
246
+ filters.each { |r,m| r.send(m, clazz, key, elapsed, *a) }
247
+ end
248
+ rescue => ca_boom
249
+ ::Mole.logger.error ">>> Mole Error: Perf-Filter -- " + ca_boom
250
+ # ca_boom.backtrace.each { |l| ::Mole.logger.error l }
251
+ end
252
+ end
253
+
254
+ def apply_unchecked_filters( boom, filters, clazz, key, *a, &b ) #:nodoc:
255
+ begin
256
+ filters.each { |r,m| r.send(m, clazz, key, boom, *a, &b ) }
257
+ rescue => ca_boom
258
+ ::Mole.logger.error ">>> Mole Error: Unchecked-Filter -- " + ca_boom
259
+ # ca_boom.backtrace.each { |l| ::Mole.logger.error l }
260
+ end
261
+ end
262
+
263
+ # ---------------------------------------------------------------------------
264
+ # Log wrapped class
265
+ def wrapped #:nodoc:
266
+ @wrapped ||= {}
267
+ end
268
+
269
+ # ---------------------------------------------------------------------------
270
+ # Check if method has been wrapped
271
+ def wrapped?(which) #:nodoc:
272
+ wrapped.has_key?(which.to_s)
273
+ end
274
+ end
data/lib/mole/moler.rb ADDED
@@ -0,0 +1,51 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Manages different ways to log the Mole interceptors. Currently there are two
3
+ # allowed modes ie persistent and transient
4
+ # Persistent mode will log the interception to the database for easy viewing
5
+ # and reporting.
6
+ # Transient mode will log the interaction to the mole logger.
7
+ # -----------------------------------------------------------------------------
8
+ module Mole
9
+ class Moler
10
+ class << self
11
+ # log an unchecked exception. Moler will look at the MOle
12
+ # configuration to see if this event should be persisted to the db or
13
+ # sent out to the logger.
14
+ def check_it( context, user_id, args )
15
+ return unless ::Mole.moleable?
16
+ if ::Mole.persistent?
17
+ MoleLog.log_it( context, MoleFeature::find_exception_feature( ::Mole.application ), user_id, args )
18
+ else
19
+ ::Mole.logger.log_it( context, "Exception", user_id, args )
20
+ end
21
+ # Send out email notification if requested
22
+ Mole::EMole.deliver_exception_alerts( context, user_id, args ) if args[:email] and args[:email] == true
23
+ end
24
+
25
+ # log performance occurence.
26
+ def perf_it( context, user_id, args )
27
+ return unless ::Mole.moleable?
28
+ if ::Mole.persistent?
29
+ MoleLog.log_it( context, MoleFeature::find_performance_feature( ::Mole.application ), user_id, args )
30
+ else
31
+ ::Mole.logger.log_it( context, "Performance", user_id, args )
32
+ end
33
+ # Send out email notification if requested
34
+ Mole::EMole.deliver_perf_alerts( context, user_id, args ) if args[:email] and args[:email] == true
35
+ end
36
+
37
+ # log a mole feature occurence.
38
+ def mole_it(context, feature, user_id, args)
39
+ return unless ::Mole.moleable?
40
+ if ::Mole.persistent?
41
+ MoleLog.log_it( context, MoleFeature::find_feature( feature, ::Mole.application, context.class.name ), user_id, args )
42
+ else
43
+ ::Mole.logger.log_it( context, feature, user_id, args )
44
+ end
45
+ # Send out email notification if requested
46
+ args[:feature] = feature
47
+ Mole::EMole.deliver_feature_alerts( context, user_id, args ) if args[:email] and args[:email] == true
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,22 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Convenience for extracting actions out controller classes
3
+ # Currently supported frameworks : Rails and Merb, more in the future...
4
+ # -----------------------------------------------------------------------------
5
+ module Mole
6
+ module Utils
7
+ class Frameworks
8
+ class << self
9
+ # Retrieves the collection of callable actions from a Merb controller.
10
+ def merb_actions( controller_class )
11
+ actions = []
12
+ controller_class.send( :callable_actions ).keys.each { |action| actions << action }
13
+ end
14
+
15
+ # Retrieves the collection of callable actions from a Rails controller
16
+ def rails_actions( controller_class )
17
+ controller_class.send( :action_methods )
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ module Mole
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ # Returns the version string for the library.
8
+ #
9
+ def self.version
10
+ [ MAJOR, MINOR, TINY].join( "." )
11
+ end
12
+ end
13
+ end
14
+
15
+