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 +1 -1
- data/README.md +13 -2
- data/lib/whenauser/exceptions.rb +90 -0
- data/lib/whenauser/helpers.rb +22 -0
- data/lib/whenauser/pageviews.rb +67 -0
- data/lib/whenauser/version.rb +1 -1
- data/lib/whenauser.rb +33 -54
- data/whenauser.gemspec +2 -2
- metadata +7 -6
- data/lib/whenauser/log_subscriber.rb +0 -38
- data/lib/whenauser/railtie.rb +0 -13
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,15 @@
|
|
1
|
-
|
1
|
+
WhenAUser
|
2
2
|
=========
|
3
3
|
|
4
|
-
|
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
|
data/lib/whenauser/version.rb
CHANGED
data/lib/whenauser.rb
CHANGED
@@ -1,67 +1,46 @@
|
|
1
1
|
require 'whenauser/version'
|
2
|
-
require 'whenauser/
|
3
|
-
require '
|
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 :
|
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.
|
34
|
-
|
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
|
-
|
52
|
-
|
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.
|
59
|
-
|
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
|
-
|
63
|
-
|
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{
|
12
|
-
s.description = %q{
|
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.
|
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-
|
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:
|
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/
|
91
|
-
- lib/whenauser/
|
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:
|
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
|
data/lib/whenauser/railtie.rb
DELETED
@@ -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
|