whenauser 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in whenauser.gemspec
3
+ # dependencies are in whenauser.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,4 +1,15 @@
1
- whenauser
1
+ WhenAUser
2
2
  =========
3
3
 
4
- Connect your Rails applications to WhenAUser.
4
+ [WhenAUser.com](http://whenauser.com) is a rules engine as a service that uses events from your application to trigger calls to 3rd party SaaS APIs. This lets you eliminate business logic in your application, and use the WhenAUser web UI instead. This gem contains Rack middleware for connecting to WhenAUser. It generates two event streams, one for exceptions and the other for pageviews.
5
+
6
+ Usage
7
+ -----
8
+
9
+ config.middleware.use 'WhenAUser::Rack',
10
+ :token => 'miKTaOlW1xzvEDG-QF9ZrA'
11
+ config.middleware.use 'WhenAUser::Exceptions',
12
+ :token => 'T4Jk7141XQefcD35v1Wzwg'
13
+ config.middleware.use 'WhenAUser::Pageviews',
14
+ :ignore_if => lambda { |env| env['action_controller.instance'].is_a? SillyController }
15
+
@@ -0,0 +1,90 @@
1
+ # With inspiration from
2
+ # https://github.com/smartinez87/exception_notification
3
+ # http://sharagoz.com/posts/1-rolling-your-own-exception-handler-in-rails-3
4
+
5
+ require 'action_dispatch'
6
+
7
+ module WhenAUser
8
+ class Exceptions
9
+ include WhenAUser::Helpers
10
+
11
+ def self.default_ignored_exceptions
12
+ [].tap do |exceptions|
13
+ exceptions << 'ActiveRecord::RecordNotFound'
14
+ exceptions << 'AbstractController::ActionNotFound'
15
+ exceptions << 'ActionController::RoutingError'
16
+ end
17
+ end
18
+
19
+ def initialize(app, options={})
20
+ @app, @options = app, options
21
+ @options[:ignore_exceptions] ||= self.class.default_ignored_exceptions
22
+ @options[:ignore_crawlers] ||= WhenAUser.default_ignored_crawlers
23
+ @options[:ignore_if] ||= lambda { |env, e| false }
24
+ @options[:token] ||= WhenAUser.token
25
+ @options[:custom_data] ||= lambda { |env| {} }
26
+ end
27
+
28
+ def call(env)
29
+ begin
30
+ @app.call(env)
31
+ rescue Exception => exception
32
+ send_event_now event(env, exception), @options[:token] unless should_be_ignored(env, exception)
33
+ raise exception
34
+ end
35
+ end
36
+
37
+ private
38
+ def send_event_now(event, token)
39
+ event[:_timestamp] = Time.now.to_f unless event[:_timestamp] || event['_timestamp']
40
+ WhenAUser.endpoint.post token, event.to_json
41
+ end
42
+
43
+ def should_be_ignored(env, exception)
44
+ ignored_exception(@options[:ignore_exceptions], exception) ||
45
+ from_crawler(@options[:ignore_crawlers], env['HTTP_USER_AGENT']) ||
46
+ conditionally_ignored(@options[:ignore_if], env, exception)
47
+ end
48
+
49
+ def ignored_exception(ignore_array, exception)
50
+ Array.wrap(ignore_array).map(&:to_s).include?(exception.class.name)
51
+ end
52
+
53
+ def conditionally_ignored(ignore_proc, env, exception)
54
+ ignore_proc.call(env, exception)
55
+ rescue Exception => ex
56
+ false
57
+ end
58
+
59
+ def clean_backtrace(exception)
60
+ if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner)
61
+ Rails.backtrace_cleaner.send(:filter, exception.backtrace)
62
+ else
63
+ exception.backtrace
64
+ end
65
+ end
66
+
67
+ def event(env, exception)
68
+ request = ActionDispatch::Request.new(env)
69
+ backtrace = clean_backtrace(exception)
70
+ actor = backtrace.first.match(/^(.*:.*):/)[1] rescue @app.to_s
71
+ event = {
72
+ :_actor => actor,
73
+ :_domain => 'exception',
74
+ :_name => exception.class.to_s,
75
+ :message => exception.to_s,
76
+ :backtrace => backtrace.join("\n"),
77
+ :request_url => request.url,
78
+ :request_method => request.request_method,
79
+ :params => request.params.except(*WhenAUser.filter_parameters),
80
+ :user_agent => request.user_agent
81
+ }
82
+ user = current_user(env)
83
+ event.merge!(:current_user => user) if user
84
+ event.merge!(:referer_url => request.referer) if request.referer
85
+ event.merge!(@options[:custom_data].call(env))
86
+ event
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,22 @@
1
+ module WhenAUser
2
+ module Helpers
3
+ def from_crawler(ignore_array, agent)
4
+ ignore_array.each do |crawler|
5
+ return true if (agent =~ /\b(#{crawler})\b/i)
6
+ end unless ignore_array.blank?
7
+ false
8
+ end
9
+
10
+ def current_user(env)
11
+ controller = env['action_controller.instance']
12
+ if current_user = controller.instance_variable_get('@current_user') || controller.instance_eval('current_user')
13
+ [:login, :username, :email, :id].each do |field|
14
+ return current_user.send(field) if current_user.respond_to?(field)
15
+ end
16
+ end
17
+ nil
18
+ rescue
19
+ nil
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,67 @@
1
+ # With inspiration from
2
+ # https://github.com/smartinez87/exception_notification
3
+ # http://sharagoz.com/posts/1-rolling-your-own-exception-handler-in-rails-3
4
+
5
+ require 'action_dispatch'
6
+
7
+ module WhenAUser
8
+ class Pageviews
9
+ include WhenAUser::Helpers
10
+
11
+ def initialize(app, options={})
12
+ @app, @options = app, options
13
+ @options[:ignore_crawlers] ||= WhenAUser.default_ignored_crawlers
14
+ @options[:ignore_if] ||= lambda { |env| false }
15
+ @options[:custom_data] ||= lambda { |env| {} }
16
+ end
17
+
18
+ def call(env)
19
+ before = Time.now
20
+ status, headers, response = @app.call(env)
21
+ after = Time.now
22
+ request = ActionDispatch::Request.new(env)
23
+ WhenAUser.send_event event(env, request, status, after - before) unless should_be_ignored(env, request)
24
+ [status, headers, response]
25
+ end
26
+
27
+ private
28
+ def should_be_ignored(env, request)
29
+ (defined?(Rails) && env['action_controller.instance'].nil?) ||
30
+ from_crawler(@options[:ignore_crawlers], env['HTTP_USER_AGENT']) ||
31
+ conditionally_ignored(@options[:ignore_if], env)
32
+ end
33
+
34
+ def conditionally_ignored(ignore_proc, env)
35
+ ignore_proc.call(env)
36
+ rescue Exception => ex
37
+ false
38
+ end
39
+
40
+ def event_name(request)
41
+ if (params = request.params)['controller']
42
+ "#{params['controller']}##{params['action']}"
43
+ else
44
+ request.path.gsub('/', '-')
45
+ end
46
+ end
47
+
48
+ def event(env, request, status, duration)
49
+ actor = current_user(env) || 'anonymous'
50
+ event = {
51
+ :_actor => actor,
52
+ :_domain => 'pageview',
53
+ :_name => event_name(request),
54
+ :request_url => request.url,
55
+ :request_method => request.request_method,
56
+ :params => request.params.except(*WhenAUser.filter_parameters),
57
+ :user_agent => request.user_agent,
58
+ :status => status,
59
+ :duration => "%.2f" % (duration * 1000)
60
+ }
61
+ event.merge!(:referer_url => request.referer) if request.referer
62
+ event.merge!(@options[:custom_data].call(env))
63
+ event
64
+ end
65
+
66
+ end
67
+ end
@@ -1,3 +1,3 @@
1
1
  module WhenAUser
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/whenauser.rb CHANGED
@@ -1,67 +1,46 @@
1
1
  require 'whenauser/version'
2
- require 'whenauser/log_subscriber'
3
- require 'active_support/core_ext/module/attribute_accessors'
2
+ require 'whenauser/helpers'
3
+ require 'whenauser/exceptions'
4
+ require 'whenauser/pageviews'
4
5
  require 'faraday'
5
6
  require 'faraday_middleware'
6
-
7
- class ActionController::Base
8
- def self.inherited(subclass)
9
- super
10
- subclass.prepend_before_filter :whenauser_disable_pageview_events
11
- subclass.before_filter :whenauser_pageview_events
12
- end
13
-
14
- def whenauser_disable_pageview_events
15
- WhenAUser.disable_pageview_events!
16
- true
17
- end
18
-
19
- def whenauser_pageview_events
20
- WhenAUser.enable_pageview_events!
21
- true
22
- end
23
- end
7
+ require 'active_support/core_ext/module/attribute_accessors'
24
8
 
25
9
  module WhenAUser
26
- mattr_accessor :webhook_url
27
- mattr_accessor :token
28
- mattr_accessor :endpoint
29
- mattr_accessor :filter_parameters
30
- mattr_accessor :data
31
- mattr_accessor :state
10
+ mattr_accessor :endpoint, :filter_parameters, :queue, :token
32
11
 
33
- def self.setup(app)
34
- WhenAUser::RequestLogSubscriber.attach_to :action_controller
35
- self.webhook_url = app.config.whenauser.webhook_url
36
- self.token = app.config.whenauser.token
37
- self.filter_parameters = app.config.filter_parameters || []
38
- self.endpoint = Faraday::Connection.new webhook_url do |builder|
39
- builder.request :json
40
- builder.adapter Faraday.default_adapter
41
- end
42
- self.data = {}
43
- self.state = {}
12
+ def self.default_ignored_crawlers
13
+ %w(Baidu Gigabot Googlebot libwww-perl lwp-trivial msnbot SiteUptime Slurp WordPress ZIBB ZyBorg Yandex Jyxobot Huaweisymantecspider ApptusBot)
44
14
  end
45
-
46
- def self.custom_data=(hash)
47
- data[Thread.current] = hash
48
- end
49
-
15
+
50
16
  def self.send_event(event)
51
- endpoint.post token, event.to_json
52
- end
53
-
54
- def self.disable_pageview_events!
55
- state[Thread.current] = :disabled
17
+ event[:_timestamp] = Time.now.to_f unless event[:_timestamp] || event['_timestamp']
18
+ WhenAUser.queue << event
56
19
  end
57
20
 
58
- def self.enable_pageview_events!
59
- state[Thread.current] = :enabled
21
+ def self.flush
22
+ return if (events = WhenAUser.queue).empty?
23
+ WhenAUser.queue = []
24
+ endpoint.post WhenAUser.token, events.to_json
60
25
  end
61
-
62
- def self.pageview_events_enabled?
63
- state[Thread.current] == :enabled
26
+
27
+ class Rack
28
+ def initialize(app, options={})
29
+ options[:webhook_url] ||= 'http://whenauser.com/events'
30
+ @app = app
31
+ WhenAUser.filter_parameters = defined?(Rails) ? Rails.application.config.filter_parameters : []
32
+ WhenAUser.token = options[:token]
33
+ WhenAUser.endpoint = Faraday::Connection.new options[:webhook_url] do |builder|
34
+ builder.request :json
35
+ builder.adapter Faraday.default_adapter
36
+ end
37
+ end
38
+
39
+ def call(env)
40
+ WhenAUser.queue = []
41
+ status, headers, response = @app.call(env)
42
+ WhenAUser.flush
43
+ [status, headers, response]
44
+ end
64
45
  end
65
46
  end
66
-
67
- require 'whenauser/railtie' if defined?(Rails)
data/whenauser.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
8
8
  s.authors = ['David Anderson']
9
9
  s.email = ['david@alpinegizmo.com']
10
10
  s.homepage = 'https://github.com/tractionlabs/whenauser'
11
- s.summary = %q{Connect your Rails applications to WhenAUser}
12
- s.description = %q{Connect your Rails applications to WhenAUser}
11
+ s.summary = %q{Rack middleware for connecting to WhenAUser}
12
+ s.description = %q{Rack middleware for connecting to WhenAUser}
13
13
 
14
14
  s.files = `git ls-files`.split("\n")
15
15
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whenauser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-04 00:00:00.000000000 Z
12
+ date: 2012-07-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -75,7 +75,7 @@ dependencies:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
- description: Connect your Rails applications to WhenAUser
78
+ description: Rack middleware for connecting to WhenAUser
79
79
  email:
80
80
  - david@alpinegizmo.com
81
81
  executables: []
@@ -87,8 +87,9 @@ files:
87
87
  - README.md
88
88
  - Rakefile
89
89
  - lib/whenauser.rb
90
- - lib/whenauser/log_subscriber.rb
91
- - lib/whenauser/railtie.rb
90
+ - lib/whenauser/exceptions.rb
91
+ - lib/whenauser/helpers.rb
92
+ - lib/whenauser/pageviews.rb
92
93
  - lib/whenauser/version.rb
93
94
  - whenauser.gemspec
94
95
  homepage: https://github.com/tractionlabs/whenauser
@@ -114,5 +115,5 @@ rubyforge_project:
114
115
  rubygems_version: 1.8.24
115
116
  signing_key:
116
117
  specification_version: 3
117
- summary: Connect your Rails applications to WhenAUser
118
+ summary: Rack middleware for connecting to WhenAUser
118
119
  test_files: []
@@ -1,38 +0,0 @@
1
- require 'active_support/core_ext/class/attribute'
2
- require 'active_support/log_subscriber'
3
-
4
- module WhenAUser
5
- class RequestLogSubscriber < ActiveSupport::LogSubscriber
6
- def process_action(data)
7
- return unless WhenAUser.pageview_events_enabled?
8
- payload = data.payload
9
- return if (status = payload[:status] || 500).to_i == 302
10
- now = Time.now
11
- wau = {
12
- :_actor => 'anonymous',
13
- :_domain => 'pageview',
14
- :_name => "#{payload[:params]['controller']}##{payload[:params]['action']}",
15
- :_timestamp => now.to_i,
16
- :nsecs => now.nsec,
17
- :duration => "%.2f" % data.duration,
18
- :method => payload[:method],
19
- :path => payload[:path],
20
- :format => payload[:format],
21
- :status => status,
22
- :params => payload[:params].except(*WhenAUser.filter_parameters)
23
- }
24
- if payload[:exception]
25
- exception, message = payload[:exception]
26
- wau = wau.merge(:exception => exception, :error_message => message)
27
- else
28
- wau = wau.merge(:view => "%.2f" % payload[:view_runtime], :db => "%.2f" % payload[:db_runtime])
29
- end
30
- wau = wau.merge(custom_data)
31
- WhenAUser.send_event(wau)
32
- end
33
-
34
- def custom_data
35
- WhenAUser.data[Thread.current] || {}
36
- end
37
- end
38
- end
@@ -1,13 +0,0 @@
1
- require 'rails/railtie'
2
- require 'action_view/log_subscriber'
3
- require 'action_controller/log_subscriber'
4
-
5
- module Lograge
6
- class Railtie < Rails::Railtie
7
- config.whenauser = ActiveSupport::OrderedOptions.new
8
-
9
- initializer :whenauser do |app|
10
- WhenAUser.setup(app)
11
- end
12
- end
13
- end