whenauser 0.6.4 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -3,12 +3,12 @@ WhenAUser
3
3
 
4
4
  [WhenAUser.com](http://whenauser.com) is a rules engine that reacts to things users do or experience in your software, and makes things happen in 3rd party SaaS APIs -- without your having to write any code. Rather than implementing the most rapidly evolving parts of your application's business logic in code, your team can use the WhenAUser web app to specify "when", "how", and "who", with rules like these:
5
5
 
6
- * when a user gets a validation error twice for the same form, send an email to Frank
7
- * when a premium customer hasn't logged in for a month, flag them in Highrise
6
+ * when a user gets a form validation error three times in an hour, send an email to Frank
7
+ * when a premium customer hasn't logged in for a month, flag them in your CRM
8
8
  * when a user gets a 500 response, create a ticket in Zendesk
9
9
  * when a user invites ten friends, add them to the "well-connected" segment in MailChimp
10
10
 
11
- This gem contains Rack middleware that automatically generates two event streams, one for exceptions and the other for pageviews, that can be used to trigger rules in WhenAUser. You can (and probably should) also send more specific events manually.
11
+ This gem contains Rack middleware that automatically generates two event streams, one for exceptions and the other for pageviews, that can be used to trigger rules in WhenAUser. You can also send more specific events manually.
12
12
 
13
13
  Setup
14
14
  -----
@@ -30,9 +30,31 @@ You should create two incoming channels (event streams) in WhenAUser, and config
30
30
  ###As general-purpose Rack middleware, with or without Rails
31
31
 
32
32
  config.middleware.insert 0, 'WhenAUser::Rack', :token => 'CHANNEL_TOKEN'
33
- config.middleware.insert_after 'WhenAUser::Rack', 'WhenAUser::Pageviews'
33
+ config.middleware.use 'WhenAUser::Pageviews'
34
34
  config.middleware.use 'WhenAUser::Exceptions', :token => 'ERROR_CHANNEL_TOKEN'
35
35
 
36
+ The current user
37
+ ----------------
38
+
39
+ WhenAUser can take advantage of knowing about the user behind each event, which is supplied by the \_actor field. This gem employs a number of heuristics to determine the current user, but you can also help it out. If, for example, you want to use current_user.id as the \_actor for every event (and 0 for the logged out user), you could do this via:
40
+
41
+ controller_data '{:_actor => current_user.try(:id) || 0}'
42
+
43
+ The string will be evaluated in the context of your controller.
44
+
45
+ Sending other events
46
+ --------------------
47
+
48
+ To manually send an event when a user upgrades to a "premium" account:
49
+
50
+ WhenAUser.send_event(
51
+ :_actor => current_user.unique_id,
52
+ :_timestamp => Time.now.to_f,
53
+ :_domain => 'account',
54
+ :_name => 'upgrade',
55
+ :user_email => current_user.email,
56
+ :plan => 'premium' )
57
+
36
58
  Using girl_friday for asynchronous communication and persistence
37
59
  -----------------
38
60
 
@@ -66,7 +88,7 @@ WhenAUser::Rack accepts these options:
66
88
  * `webhook_url` -- defaults to 'http://whenauser.com/events'
67
89
  * `middleware` -- takes the symbol for a middleware and a block, configuring it
68
90
  * `queue` -- takes the class used for queuing (default: WhenAUser::MemoryQueue), and an optional hash
69
- * `custom_data` -- this proc is passed env, and should return a hash to be merged into every event
91
+ * `controller_data` -- a string evaluated in the context of the Rails controller (if any) handling the request; it should return a hash to be merged into every event
70
92
 
71
93
  The `exceptions` middleware accepts these options:
72
94
 
@@ -80,6 +102,7 @@ The `pageviews` middleware accepts these options:
80
102
 
81
103
  * `ignore_crawlers` -- an array of strings to match against the user agent, includes a number of webcrawlers by default
82
104
  * `ignore_if` -- this proc is passed env; if it returns true, the pageview is not reported to WhenAUser
105
+ * `ignore_if_controller` -- a string to be evaluated in the context of the Rails controller instance
83
106
  * `custom_data` -- this proc is passed env, and should return a hash to be merged into each automatically generated event
84
107
 
85
108
  The WhenAUser::Pageviews middleware uses the same token as WhenAUser::Rack.
@@ -90,18 +113,17 @@ Here's an example of how to skip sending any pageview events for all requests to
90
113
  ignore_if lambda { |env| env['action_controller.instance'].is_a? SillyController }
91
114
  end
92
115
 
93
- Sending other events
94
- --------------------
116
+ To make life easier in the case where you want a condition evaluated in the context of a Rails controller, you can do the same thing like this. (Only the pageviews middleware supports ignore_if_controller.)
95
117
 
96
- To manually send an event when a user upgrades to a "premium" account:
118
+ middleware :pageviews do
119
+ ignore_if_controller 'self.is_a?(EventsController)'
120
+ end
97
121
 
98
- WhenAUser.send_event(
99
- :_actor => current_user.unique_id,
100
- :_timestamp => Time.now.to_f,
101
- :_domain => 'account',
102
- :_name => 'upgrade',
103
- :user_email => current_user.email,
104
- :plan => 'premium' )
122
+ Or if you want to skip sending pageview events for requests from pingdom.com:
123
+
124
+ middleware :pageviews do
125
+ ignore_crawlers WhenAUser.default_ignored_crawlers + ['Pingdom.com_bot']
126
+ end
105
127
 
106
128
  Use Cases
107
129
  ---------
@@ -109,7 +131,7 @@ Use Cases
109
131
  ### Example rule triggers
110
132
 
111
133
  * whenever a `UserIsHavingAVeryBadDay` exception is raised
112
- * the first time any exception occurs
134
+ * the first time any particular exception occurs
113
135
  * whenever a request takes more than 20 seconds to process
114
136
  * whenever someone upgrades their account
115
137
  * whenever someone does comment#create more than 10 times in a day
@@ -120,7 +142,7 @@ Use Cases
120
142
  * send yourself an email or a mobile push message
121
143
  * send a user an email or a mobile push message
122
144
  * create a ticket in your ticketing system
123
- * add a data point to a Librato graph
145
+ * add a data point to a Librato or StatsMix graph
124
146
  * tag a user in WhenAUser, or in your CRM
125
147
  * segment a user in your email campaign tool
126
148
 
@@ -8,24 +8,23 @@ require 'net/http'
8
8
  require 'uri'
9
9
  require 'logger'
10
10
  require 'active_support/core_ext/module/attribute_accessors'
11
+ require 'active_support/core_ext/hash/indifferent_access'
11
12
 
12
13
  module WhenAUser
13
- mattr_accessor :filter_parameters, :buffer, :token, :webhook_url, :queue, :queue_options, :custom_data, :logger
14
+ mattr_accessor :filter_parameters, :buffer, :token, :webhook_url, :queue, :queue_options, :controller_data, :logger
14
15
 
15
16
  def self.default_ignored_crawlers
16
- %w(Baidu Gigabot Googlebot libwww-perl lwp-trivial msnbot SiteUptime Slurp WordPress ZIBB ZyBorg Yandex Jyxobot Huaweisymantecspider ApptusBot)
17
+ %w(Baidu Gigabot Googlebot libwww-perl lwp-trivial msnbot SiteUptime Slurp WordPress ZIBB ZyBorg Yandex Jyxobot Huaweisymantecspider ApptusBot NewRelicPinger)
17
18
  end
18
19
 
19
20
  def self.send_event(event)
20
- logger.debug "============= #{event.inspect} ============="
21
21
  buffer << event
22
22
  end
23
23
 
24
24
  def self.flush(env={})
25
25
  return if (events = WhenAUser.buffer).empty?
26
26
  WhenAUser.buffer = []
27
- events.each {|event| WhenAUser.prepare_event(event, env)}
28
- WhenAUser.queue.push(:payload => events)
27
+ WhenAUser.queue.push(:payload => events.map {|event| WhenAUser.prepare_event(event, env)})
29
28
  # WhenAUser.post_payload_to_token events.to_json, WhenAUser.token
30
29
  end
31
30
 
@@ -53,19 +52,39 @@ module WhenAUser
53
52
  end
54
53
 
55
54
  private
55
+ def self.page_event_name(request, params)
56
+ if params && params['controller']
57
+ "#{params['controller']}##{params['action']}"
58
+ else
59
+ request.path.gsub('/', '-')[1..-1]
60
+ end
61
+ end
62
+
56
63
  def self.prepare_event(event, env)
57
- event[:_actor] ||= current_user(env) || 'anonymous'
58
- event[:_timestamp] = Time.now.to_f unless (event[:_timestamp] || event['_timestamp'])
64
+ event = event.with_indifferent_access
65
+
66
+ if controller = env['action_controller.instance']
67
+ data = controller.instance_eval(WhenAUser.controller_data)
68
+ event.merge!(data)
69
+ end
70
+
71
+ event[:_actor] = current_user(env) || 'anonymous' unless event[:_actor].present?
72
+ event[:_timestamp] ||= Time.now.to_f
59
73
  event[:rails_env] = Rails.env if defined?(Rails)
74
+
60
75
  unless env.empty?
61
- event[:request_url] = env['whenauser.request_url']
62
- event[:request_method] = env['whenauser.request_method']
63
- event[:user_agent] = env['whenauser.user_agent']
64
- event[:referer_url] = env['whenauser.referer_url'] if env['whenauser.referer_url']
65
- request = ActionDispatch::Request.new(env)
66
- event[:params] = request.params.except(*WhenAUser.filter_parameters)
76
+ env['rack.input'].rewind
77
+ request = defined?(Rails) ? ActionDispatch::Request.new(env) : ::Rack::Request.new(env)
78
+ params = request.params
79
+
80
+ event[:_name] ||= page_event_name(request, params)
81
+ event[:request_url] = request.url
82
+ event[:request_method] = request.request_method
83
+ event[:user_agent] = request.user_agent
84
+ event[:referer_url] = request.referer if request.referer.present?
85
+ event[:params] = params
67
86
  end
68
- event.merge!(WhenAUser.custom_data.call(env))
87
+
69
88
  event
70
89
  end
71
90
 
@@ -79,16 +98,11 @@ module WhenAUser
79
98
  WhenAUser.token = options[:token]
80
99
  WhenAUser.queue = options[:queue] || WhenAUser::MemoryQueue
81
100
  WhenAUser.queue_options = options[:queue_options] || {}
82
- WhenAUser.custom_data = options[:custom_data] || lambda { |env| {} }
101
+ WhenAUser.controller_data = options[:controller_data] || '{}'
83
102
  end
84
103
 
85
104
  def call(env)
86
105
  WhenAUser.buffer = []
87
- request = ActionDispatch::Request.new(env)
88
- env['whenauser.request_url'] = request.url
89
- env['whenauser.request_method'] = request.request_method
90
- env['whenauser.user_agent'] = request.user_agent
91
- env['whenauser.referer_url'] = request.referer
92
106
  @app.call(env)
93
107
  ensure
94
108
  WhenAUser.flush(env)
@@ -10,13 +10,13 @@ module WhenAUser
10
10
 
11
11
  def initialize(app, options={})
12
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| {} }
13
+ @options[:ignore_crawlers] ||= WhenAUser.default_ignored_crawlers
14
+ @options[:ignore_if] ||= lambda { |env| false }
15
+ @options[:ignore_if_controller] ||= 'false'
16
+ @options[:custom_data] ||= lambda { |env| {} }
16
17
  end
17
18
 
18
19
  def call(env)
19
- request = ActionDispatch::Request.new(env)
20
20
  before = Time.now
21
21
  status, headers, response = @app.call(env)
22
22
  [status, headers, response]
@@ -25,38 +25,33 @@ module WhenAUser
25
25
  raise e
26
26
  ensure
27
27
  after = Time.now
28
- WhenAUser.send_event event(env, request, status, after - before) unless should_be_ignored(env, request)
28
+ WhenAUser.send_event event(env, status, after - before) unless should_be_ignored(env)
29
29
  end
30
30
 
31
31
  private
32
- def rails_asset_request?(env, request)
32
+ def rails_asset_request?(env)
33
33
  defined?(Rails) && env['action_controller.instance'].nil?
34
34
  end
35
35
 
36
- def should_be_ignored(env, request)
37
- rails_asset_request?(env, request) ||
36
+ def should_be_ignored(env)
37
+ rails_asset_request?(env) ||
38
38
  from_crawler(@options[:ignore_crawlers], env['HTTP_USER_AGENT']) ||
39
- conditionally_ignored(@options[:ignore_if], env)
39
+ conditionally_ignored(@options[:ignore_if], env) ||
40
+ conditionally_ignored_controller(@options[:ignore_if_controller], env)
40
41
  end
41
42
 
42
- def conditionally_ignored(ignore_proc, env)
43
- ignore_proc.call(env)
44
- rescue Exception => ex
45
- false
43
+ def conditionally_ignored_controller(condition, env)
44
+ controller = env['action_controller.instance']
45
+ controller.instance_eval condition
46
46
  end
47
47
 
48
- def page_event_name(request)
49
- if (params = request.params)['controller']
50
- "#{params['controller']}##{params['action']}"
51
- else
52
- request.path.gsub('/', '-')
53
- end
48
+ def conditionally_ignored(ignore_proc, env)
49
+ ignore_proc.call(env)
54
50
  end
55
51
 
56
- def event(env, request, status, duration)
52
+ def event(env, status, duration)
57
53
  event = {
58
54
  :_domain => (status.to_i >= 400) ? 'pageerror' : 'pageview',
59
- :_name => page_event_name(request),
60
55
  :status => status,
61
56
  :duration => "%.2f" % (duration * 1000)
62
57
  }
@@ -3,7 +3,7 @@ require 'active_record'
3
3
 
4
4
  module Whenauser
5
5
  class RailsConfigurator
6
- attr_accessor :token, :webhook_url, :middlewares, :queue, :queue_options
6
+ attr_accessor :token, :webhook_url, :middlewares, :queue, :controller_data, :queue_options
7
7
  def initialize
8
8
  @webhook_url = 'http://www.whenauser.com/events/'
9
9
  @middlewares = {}
@@ -26,6 +26,10 @@ module Whenauser
26
26
  @queue_options = options
27
27
  end
28
28
 
29
+ def controller_data(data)
30
+ @controller_data = data
31
+ end
32
+
29
33
  def girl_friday_options(options)
30
34
  @girl_friday_options = options
31
35
  end
@@ -73,10 +77,10 @@ module Whenauser
73
77
  :webhook_url => @webhook_url,
74
78
  :token => @token,
75
79
  :queue => @queue,
76
- :queue_options => @queue_options
80
+ :queue_options => @queue_options,
81
+ :controller_data => @controller_data
82
+ ::Rails.configuration.middleware.use('WhenAUser::Pageviews', @middlewares[:pageviews].configuration) if @middlewares.has_key?(:pageviews)
77
83
  ::Rails.configuration.middleware.use('WhenAUser::Exceptions', @middlewares[:exceptions].configuration) if @middlewares.has_key?(:exceptions)
78
- # puts "configuration with: #{@middlewares[:pageviews].configuration}"
79
- ::Rails.configuration.middleware.insert_after('WhenAUser::Rack', 'WhenAUser::Pageviews', @middlewares[:pageviews].configuration) if @middlewares.has_key?(:pageviews)
80
84
  end
81
85
  end
82
86
  end
@@ -1,3 +1,3 @@
1
1
  module WhenAUser
2
- VERSION = '0.6.4'
2
+ VERSION = '0.7.1'
3
3
  end
metadata CHANGED
@@ -1,72 +1,71 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: whenauser
3
- version: !ruby/object:Gem::Version
4
- version: 0.6.4
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 7
8
+ - 1
9
+ version: 0.7.1
6
10
  platform: ruby
7
- authors:
11
+ authors:
8
12
  - David Anderson
9
13
  - Chris Weis
10
14
  autorequire:
11
15
  bindir: bin
12
16
  cert_chain: []
13
- date: 2012-08-09 00:00:00.000000000 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
17
+
18
+ date: 2012-08-11 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
16
22
  name: activesupport
17
- requirement: !ruby/object:Gem::Requirement
18
- none: false
19
- requirements:
20
- - - ! '>='
21
- - !ruby/object:Gem::Version
22
- version: '0'
23
- type: :runtime
24
23
  prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
- requirements:
28
- - - ! '>='
29
- - !ruby/object:Gem::Version
30
- version: '0'
31
- - !ruby/object:Gem::Dependency
32
- name: actionpack
33
- requirement: !ruby/object:Gem::Requirement
34
- none: false
35
- requirements:
36
- - - ! '>='
37
- - !ruby/object:Gem::Version
38
- version: '0'
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
39
31
  type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: actionpack
40
35
  prerelease: false
41
- version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
- requirements:
44
- - - ! '>='
45
- - !ruby/object:Gem::Version
46
- version: '0'
47
- - !ruby/object:Gem::Dependency
48
- name: girl_friday
49
- requirement: !ruby/object:Gem::Requirement
50
- none: false
51
- requirements:
52
- - - ~>
53
- - !ruby/object:Gem::Version
54
- version: 0.10.0
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
55
43
  type: :runtime
44
+ version_requirements: *id002
45
+ - !ruby/object:Gem::Dependency
46
+ name: girl_friday
56
47
  prerelease: false
57
- version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
- requirements:
48
+ requirement: &id003 !ruby/object:Gem::Requirement
49
+ requirements:
60
50
  - - ~>
61
- - !ruby/object:Gem::Version
51
+ - !ruby/object:Gem::Version
52
+ segments:
53
+ - 0
54
+ - 10
55
+ - 0
62
56
  version: 0.10.0
57
+ type: :runtime
58
+ version_requirements: *id003
63
59
  description: Rack middleware for connecting to WhenAUser
64
- email:
60
+ email:
65
61
  - david@alpinegizmo.com
66
62
  executables: []
63
+
67
64
  extensions: []
65
+
68
66
  extra_rdoc_files: []
69
- files:
67
+
68
+ files:
70
69
  - .gitignore
71
70
  - Gemfile
72
71
  - README.md
@@ -81,28 +80,35 @@ files:
81
80
  - lib/whenauser/railtie.rb
82
81
  - lib/whenauser/version.rb
83
82
  - whenauser.gemspec
83
+ has_rdoc: true
84
84
  homepage: https://github.com/tractionlabs/whenauser
85
85
  licenses: []
86
+
86
87
  post_install_message:
87
88
  rdoc_options: []
88
- require_paths:
89
+
90
+ require_paths:
89
91
  - lib
90
- required_ruby_version: !ruby/object:Gem::Requirement
91
- none: false
92
- requirements:
93
- - - ! '>='
94
- - !ruby/object:Gem::Version
95
- version: '0'
96
- required_rubygems_version: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: '0'
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ segments:
104
+ - 0
105
+ version: "0"
102
106
  requirements: []
107
+
103
108
  rubyforge_project:
104
- rubygems_version: 1.8.24
109
+ rubygems_version: 1.3.6
105
110
  signing_key:
106
111
  specification_version: 3
107
112
  summary: Rack middleware for connecting to WhenAUser
108
113
  test_files: []
114
+