rackamole 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/History.txt +4 -0
  2. data/README.txt +93 -0
  3. data/Rakefile +28 -0
  4. data/aaa.txt +36 -0
  5. data/bin/rackamole +8 -0
  6. data/images/mole_logo.png +0 -0
  7. data/images/mole_logo.psd +0 -0
  8. data/images/mole_logo_small.png +0 -0
  9. data/images/mole_logo_small.psd +0 -0
  10. data/lib/rackamole.rb +46 -0
  11. data/lib/rackamole/interceptor.rb +20 -0
  12. data/lib/rackamole/logger.rb +121 -0
  13. data/lib/rackamole/mole.rb +134 -0
  14. data/lib/rackamole/store/log.rb +55 -0
  15. data/lib/rackamole/store/mongo.rb +104 -0
  16. data/samples/rails/moled/README +243 -0
  17. data/samples/rails/moled/Rakefile +10 -0
  18. data/samples/rails/moled/app/controllers/application_controller.rb +13 -0
  19. data/samples/rails/moled/app/controllers/fred_controller.rb +4 -0
  20. data/samples/rails/moled/app/helpers/application_helper.rb +3 -0
  21. data/samples/rails/moled/app/views/fred/index.html.erb +1 -0
  22. data/samples/rails/moled/config/boot.rb +110 -0
  23. data/samples/rails/moled/config/database.yml +22 -0
  24. data/samples/rails/moled/config/environment.rb +45 -0
  25. data/samples/rails/moled/config/environments/development.rb +17 -0
  26. data/samples/rails/moled/config/environments/production.rb +28 -0
  27. data/samples/rails/moled/config/environments/test.rb +28 -0
  28. data/samples/rails/moled/config/initializers/backtrace_silencers.rb +7 -0
  29. data/samples/rails/moled/config/initializers/inflections.rb +10 -0
  30. data/samples/rails/moled/config/initializers/mime_types.rb +5 -0
  31. data/samples/rails/moled/config/initializers/new_rails_defaults.rb +19 -0
  32. data/samples/rails/moled/config/initializers/session_store.rb +15 -0
  33. data/samples/rails/moled/config/locales/en.yml +5 -0
  34. data/samples/rails/moled/config/routes.rb +43 -0
  35. data/samples/rails/moled/db/development.sqlite3 +0 -0
  36. data/samples/rails/moled/doc/README_FOR_APP +2 -0
  37. data/samples/rails/moled/log/development.log +30 -0
  38. data/samples/rails/moled/log/production.log +0 -0
  39. data/samples/rails/moled/log/server.log +0 -0
  40. data/samples/rails/moled/log/test.log +0 -0
  41. data/samples/rails/moled/public/404.html +30 -0
  42. data/samples/rails/moled/public/422.html +30 -0
  43. data/samples/rails/moled/public/500.html +30 -0
  44. data/samples/rails/moled/public/favicon.ico +0 -0
  45. data/samples/rails/moled/public/images/rails.png +0 -0
  46. data/samples/rails/moled/public/index.html +275 -0
  47. data/samples/rails/moled/public/javascripts/application.js +2 -0
  48. data/samples/rails/moled/public/javascripts/controls.js +963 -0
  49. data/samples/rails/moled/public/javascripts/dragdrop.js +973 -0
  50. data/samples/rails/moled/public/javascripts/effects.js +1128 -0
  51. data/samples/rails/moled/public/javascripts/prototype.js +4320 -0
  52. data/samples/rails/moled/public/robots.txt +5 -0
  53. data/samples/rails/moled/script/about +4 -0
  54. data/samples/rails/moled/script/console +3 -0
  55. data/samples/rails/moled/script/dbconsole +3 -0
  56. data/samples/rails/moled/script/destroy +3 -0
  57. data/samples/rails/moled/script/generate +3 -0
  58. data/samples/rails/moled/script/performance/benchmarker +3 -0
  59. data/samples/rails/moled/script/performance/profiler +3 -0
  60. data/samples/rails/moled/script/plugin +3 -0
  61. data/samples/rails/moled/script/runner +3 -0
  62. data/samples/rails/moled/script/server +3 -0
  63. data/samples/rails/moled/test/performance/browsing_test.rb +9 -0
  64. data/samples/rails/moled/test/test_helper.rb +38 -0
  65. data/samples/sinatra/moled.rb +20 -0
  66. data/spec/expected_results/mole_exception.log +17 -0
  67. data/spec/expected_results/mole_feature.log +15 -0
  68. data/spec/expected_results/mole_perf.log +16 -0
  69. data/spec/rackamole/interceptor_spec.rb +33 -0
  70. data/spec/rackamole/logger_spec.rb +55 -0
  71. data/spec/rackamole/mole_spec.rb +90 -0
  72. data/spec/rackamole/store/log_spec.rb +54 -0
  73. data/spec/rackamole/store/mongo_spec.rb +120 -0
  74. data/spec/rackamole_spec.rb +20 -0
  75. data/spec/spec_helper.rb +8 -0
  76. data/tasks/ann.rake +80 -0
  77. data/tasks/bones.rake +20 -0
  78. data/tasks/gem.rake +201 -0
  79. data/tasks/git.rake +40 -0
  80. data/tasks/notes.rake +27 -0
  81. data/tasks/post_load.rake +34 -0
  82. data/tasks/rdoc.rake +51 -0
  83. data/tasks/rubyforge.rake +55 -0
  84. data/tasks/setup.rb +292 -0
  85. data/tasks/spec.rake +54 -0
  86. data/tasks/svn.rake +47 -0
  87. data/tasks/test.rake +40 -0
  88. data/tasks/zentest.rake +36 -0
  89. metadata +198 -0
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2009-11-11
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
data/README.txt ADDED
@@ -0,0 +1,93 @@
1
+ == RackAMole
2
+ Your web application observer...
3
+
4
+ == DESCRIPTION:
5
+
6
+ The MOle is a rack application that monitors user interactions with your web site. We are not
7
+ talking about counting page hits here. The MOle tracks all the information available to capture
8
+ the essence of a user interaction with your application. Using the MOle, you are able to see
9
+ which feature is a hit or a bust. As an added bonus, the MOle also track performance and exceptions
10
+ that might have escaped your test suites or alpha env. To boot your managers will love you for it!
11
+
12
+ Whether you are releasing a new application or improving on an old one, it is always a good thing
13
+ to know if anyone is using your application and if they are, how they are using it.
14
+ What features are your users most fond of and which features find their way into the abyss?
15
+ You will be able to rapidly assess whether or not your application is a hit and if
16
+ your coolest features are thought as such by your users. You will be able to elegantly record user
17
+ interactions and leverage these findings for the next iteration of your application.
18
+
19
+ == PROJECT INFORMATION
20
+
21
+ * Developer: Fernand Galiana [liquidrail.com]
22
+ * Forum: http://groups.google.com/group/rackamole
23
+ * Git: git://github.com/derailed/rackamole.git
24
+
25
+ == FEATURES:
26
+
27
+ * Monitors Rails or Sinatra applications
28
+ * Captures the essence of the request as well as user information
29
+ * Tracks performance issues based on your latency threshold
30
+ * Tracks exceptions that might occurred during a request
31
+
32
+ == REQUIREMENTS:
33
+
34
+ * Logging
35
+ * Hitimes
36
+ * MongoDb
37
+
38
+ == INSTALL:
39
+
40
+ * sudo gem install rackamole
41
+
42
+ NOTE: The gem is hosted on gemcutter.com - please update your gem sources if not
43
+ already specified
44
+
45
+ == USAGE:
46
+
47
+ === Rails applications
48
+
49
+ Edit your environment.rb file and add the following lines:
50
+
51
+ require 'rackamole'
52
+ config.middleware.use Rack::Mole, { :app_name => "My Cool App", :user_key => :user_name }
53
+
54
+ This instructs the mole to start logging information to the console and look for the user name
55
+ in the session using the :user_name key. There are other options available, please take a look
56
+ at the docs for more information.
57
+
58
+ === Sinatra Applications
59
+
60
+ Add the following lines in the config section and smoke it...
61
+
62
+ require 'rackamole'
63
+ configure do
64
+ use Rack::Mole, { :app_name => "My Sinatra App", :user_key => :user_name }
65
+ end
66
+
67
+ This assumes that you have session enabled to identify a user if not the mole will log the user
68
+ as 'Unknown'
69
+
70
+ == LICENSE:
71
+
72
+ (The MIT License)
73
+
74
+ Copyright (c) 2008 FIXME (different license?)
75
+
76
+ Permission is hereby granted, free of charge, to any person obtaining
77
+ a copy of this software and associated documentation files (the
78
+ 'Software'), to deal in the Software without restriction, including
79
+ without limitation the rights to use, copy, modify, merge, publish,
80
+ distribute, sublicense, and/or sell copies of the Software, and to
81
+ permit persons to whom the Software is furnished to do so, subject to
82
+ the following conditions:
83
+
84
+ The above copyright notice and this permission notice shall be
85
+ included in all copies or substantial portions of the Software.
86
+
87
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
88
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
89
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
90
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
91
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
92
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
93
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ begin
2
+ require 'bones'
3
+ Bones.setup
4
+ rescue LoadError
5
+ begin
6
+ load 'tasks/setup.rb'
7
+ rescue LoadError
8
+ raise RuntimeError, '### please install the "bones" gem ###'
9
+ end
10
+ end
11
+
12
+ ensure_in_path 'lib'
13
+ require 'rackamole'
14
+
15
+ task :default => 'spec:run'
16
+
17
+ PROJ.name = 'rackamole'
18
+ PROJ.authors = 'Fernand Galiana'
19
+ PROJ.email = 'fernand.galiana@gmail.com'
20
+ PROJ.url = 'http://rackamole.liquidrail.com'
21
+ PROJ.version = Rackamole::VERSION
22
+ PROJ.spec.opts << '--color'
23
+ PROJ.ruby_opts = %w[-W0]
24
+
25
+ # Dependencies
26
+ depend_on "logging" , ">= 1.2.2"
27
+ depend_on "hitimes" , ">= 1.0.3"
28
+ depend_on "mongodb-mongo", ">= 0.14.1"
data/aaa.txt ADDED
@@ -0,0 +1,36 @@
1
+ == Problem
2
+
3
+ All tests green, CC happy, Customers are creaming their pants reading their Cucumber scenarios. App is in production on bitch'n servers.
4
+ Top of the world right?
5
+
6
+ == Questions
7
+
8
+ Is the app being used ? How many customers hit it in the past week ? What are the most/least used features ? Why?
9
+ Are my cool ajax callbacks silently crapping out? Are there any perf issues ? ... ?
10
+
11
+ == Got Mole ? (http://github.com/derailed/rackamole)
12
+
13
+ The MOle is a rack component that traps your application requests and captures the associated context information.
14
+ By intercepting the request, this rack app can gather calling context, alert you of latency issues on certain operation and
15
+ also trap any exceptions that may have emerged in the request cycle. The MOle is currently able to either store this information
16
+ in your log files or in a mongo instance for later processing.
17
+
18
+ == Options
19
+
20
+ <tt>:app_name</tt> - The name of the application [Default: Moled App]
21
+ <tt>:environment</tt> - The environment for the application ie :environment => RAILS_ENV
22
+ <tt>:moleable</tt> - Enable/Disable the MOle [Default:true]
23
+ <tt>:store </tt> - The storage instance ie log file or database [Default:stdout]
24
+ <tt>:user_key</tt> - If session is enable, the session key for the user name or user_id. ie :user_key => :user_name
25
+
26
+ == Rails
27
+
28
+ require 'rackamole'
29
+ config.middleware.use Rack::Mole, { :app_name => "My Cool App", :user_key => :user_name }
30
+
31
+ == Sinatra
32
+
33
+ require 'rackamole'
34
+ configure do
35
+ use Rack::Mole, { :app_name => "My Sinatra App", :user_key => :user_name }
36
+ end
data/bin/rackamole ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), %w[.. lib rackamole]))
5
+
6
+ # Put your code here
7
+
8
+ # EOF
Binary file
Binary file
Binary file
Binary file
data/lib/rackamole.rb ADDED
@@ -0,0 +1,46 @@
1
+ module Rackamole
2
+
3
+ # :stopdoc:
4
+ VERSION = '0.0.1'
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ #
11
+ def self.version
12
+ VERSION
13
+ end
14
+
15
+ # Returns the library path for the module. If any arguments are given,
16
+ # they will be joined to the end of the libray path using
17
+ # <tt>File.join</tt>.
18
+ #
19
+ def self.libpath( *args )
20
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
21
+ end
22
+
23
+ # Returns the lpath for the module. If any arguments are given,
24
+ # they will be joined to the end of the path using
25
+ # <tt>File.join</tt>.
26
+ #
27
+ def self.path( *args )
28
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
29
+ end
30
+
31
+ # Utility method used to require all files ending in .rb that lie in the
32
+ # directory below this file that has the same name as the filename passed
33
+ # in. Optionally, a specific _directory_ name can be passed in such that
34
+ # the _filename_ does not have to be equivalent to the directory.
35
+ #
36
+ def self.require_all_libs_relative_to( fname, dir = nil )
37
+ dir ||= ::File.basename(fname, '.*')
38
+ search_me = ::File.expand_path(
39
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
40
+
41
+ Dir.glob(search_me).sort.each {|rb| require rb}
42
+ end
43
+
44
+ end
45
+
46
+ Rackamole.require_all_libs_relative_to(__FILE__)
@@ -0,0 +1,20 @@
1
+ module Rackamole
2
+ module Interceptor
3
+
4
+ # In order for the mole to trap framework exception, you must include
5
+ # the interceptor in your application controller.
6
+ # ie include Wackamole::Interceptor
7
+ def self.included( base )
8
+ base.send( :alias_method_chain, :rescue_action_in_public, :mole )
9
+ end
10
+
11
+ private
12
+
13
+ # Instructs the mole to trap the framework exception
14
+ def rescue_action_in_public_with_mole( exception )
15
+ # Stuff the exception in the env for mole rack retrieval
16
+ request.env['mole.exception'] = exception
17
+ rescue_action_in_public_without_mole( exception )
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,121 @@
1
+ require 'logging'
2
+ require 'forwardable'
3
+
4
+ module Rackamole
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 = ::Rackamole::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
+ end
121
+ end
@@ -0,0 +1,134 @@
1
+ require 'hitimes'
2
+ require 'mongo/util/ordered_hash'
3
+ require 'json'
4
+
5
+ module Rack
6
+ class Mole
7
+
8
+ # Initialize The Mole with the possible options
9
+ # <tt>:app_name</tt> - The name of the application [Default: Moled App]
10
+ # <tt>:environment</tt> - The environment for the application ie :environment => RAILS_ENV
11
+ # <tt>:perf_threshold</tt> - Any request taking longer than this value will get moled [Default: 10]
12
+ # <tt>:moleable</tt> - Enable/Disable the MOle [Default:true]
13
+ # <tt>:store</tt> - The storage instance ie log file or mongodb [Default:stdout]
14
+ # <tt>:user_key</tt> - If session is enable, the session key for the user name or user_id. ie :user_key => :user_name
15
+ def initialize( app, opts={} )
16
+ @app = app
17
+ init_options( opts )
18
+ end
19
+
20
+ def call( env )
21
+ # Bail if application is not moleable
22
+ return @app.call( env ) unless moleable?
23
+
24
+ response = nil
25
+ elapsed = Hitimes::Interval.measure do
26
+ response = @app.call( env )
27
+ end
28
+ @store.mole( mole_info( env, elapsed ) )
29
+ response
30
+ end
31
+
32
+ # ===========================================================================
33
+ private
34
+
35
+ # Load up configuration options
36
+ def init_options( opts )
37
+ options = default_options.merge( opts )
38
+ @environment = options[:environment]
39
+ @perf_threshold = options[:perf_threshold]
40
+ @moleable = options[:moleable]
41
+ @app_name = options[:app_name]
42
+ @user_key = options[:user_key]
43
+ @store = options[:store]
44
+ end
45
+
46
+ # Mole default options
47
+ def default_options
48
+ {
49
+ :app_name => "Moled App",
50
+ :moleable => true,
51
+ :perf_threshold => 10,
52
+ :store => Rackamole::Store::Log.new
53
+ }
54
+ end
55
+
56
+ # Extract interesting information from the request
57
+ def mole_info( env, elapsed )
58
+ request = Rack::Request.new( env )
59
+ session = env['rack.session']
60
+ route = get_route( request )
61
+ info = OrderedHash.new
62
+ ip, browser = identify( env )
63
+ user_id = nil
64
+ user_name = nil
65
+
66
+ if session and @user_key
67
+ if @user_key.instance_of? Symbol
68
+ user_name = session[@user_key]
69
+ elsif @user_key.instance_of? Hash
70
+ user_id = session[ @user_key[:session_key] ]
71
+ if @user_key[:extractor]
72
+ user_name = @user_key[:extractor].call( user_id )
73
+ end
74
+ end
75
+ end
76
+
77
+ info[:app_name] = @app_name
78
+ info[:environment] = @environment if @environment
79
+ info[:user_id] = user_id if user_id
80
+ info[:user_name] = user_name || "Unknown"
81
+ info[:ip] = ip
82
+ info[:browser] = browser
83
+ info[:request_time] = elapsed if elapsed
84
+ info[:perf_issue] = (elapsed and elapsed > @perf_threshold)
85
+ info[:url] = request.url
86
+ info[:method] = env['REQUEST_METHOD']
87
+ info[:path] = request.path
88
+ info[:route_info] = route if route
89
+
90
+ # Dump request params
91
+ unless request.params.empty?
92
+ info[:params] = OrderedHash.new
93
+ request.params.keys.sort.each { |k| info[:params][k.to_sym] = request.params[k].to_json }
94
+ end
95
+
96
+ # Dump session var
97
+ if session and !session.empty?
98
+ info[:session] = OrderedHash.new
99
+ session.keys.sort{ |a,b| a.to_s <=> b.to_s }.each { |k| info[:session][k.to_sym] = session[k].to_json }
100
+ end
101
+
102
+ # Check if an exception was raised. If so consume it and clear state
103
+ exception = env['mole.exception']
104
+ if exception
105
+ info[:ruby_version] = %x[ruby -v]
106
+ info[:stack] = trim_stack( exception )
107
+ env['mole.exception'] = nil
108
+ end
109
+
110
+ info
111
+ end
112
+
113
+ # Trim stack trace
114
+ def trim_stack( boom )
115
+ boom.backtrace[0...4]
116
+ end
117
+
118
+ # Identify request ie ip and browser configuration
119
+ def identify( request_env )
120
+ return request_env['HTTP_X_FORWARDED_FOR'] || request_env['REMOTE_ADDR'], request_env['HTTP_USER_AGENT']
121
+ end
122
+
123
+ # Checks if this application is moleable
124
+ def moleable?
125
+ @moleable
126
+ end
127
+
128
+ # Fetch route info if any...
129
+ def get_route( request )
130
+ return nil unless defined?( RAILS_ENV )
131
+ ::ActionController::Routing::Routes.recognize_path( request.path, {:method => request.request_method } )
132
+ end
133
+ end
134
+ end