mixpanel 1.1.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,213 @@
1
+ [![Build Status](https://secure.travis-ci.org/zevarito/mixpanel.png)](http://travis-ci.org/zevarito/mixpanel)
2
+
3
+ ## Table of Contents
4
+
5
+ - [What is Mixpanel] (#what-is-mixpanel)
6
+ - [What does this Gem do?] (#what-does-this-gem-do)
7
+ - [Install] (#install)
8
+ - [Rack Middleware] (#rack-middleware)
9
+ - [Usage] (#usage)
10
+ - [Initialize Mixpanel class] (#initialize-mixpanel-class)
11
+ - [Examples] (#examples)
12
+ - [How to use it from Rails controllers] (#how-to-use-it-from-rails-controllers)
13
+ - [How to track events using Resque and Rails] (#how-to-track-events-using-resque-and-rails)
14
+ - [Supported Platforms] (#supported-platforms)
15
+ - [Deprecation Notes] (#deprecation-notes)
16
+ - [Collaborators and Maintainers] (#collaborators-and-maintainers)
17
+
18
+ ## What is Mixpanel
19
+
20
+ Mixpanel is a real-time analytics service that helps companies understand how users interact with web applications.
21
+ http://mixpanel.com
22
+
23
+ ## What does this Gem do?
24
+
25
+ - Track events with properties directly from your backend.
26
+ - Track events with properties through javascript using a rack middleware.
27
+
28
+ ## Install
29
+
30
+ ```ruby
31
+ gem install mixpanel
32
+ ```
33
+
34
+ ### Rack Middleware
35
+
36
+ *Only need if you want to track events from Javascript.*
37
+
38
+ If you are using Rails you can add this to your specific environment configuration file (located in config/environments/) or create a new
39
+ initializer for it:
40
+
41
+ ```ruby
42
+ config.middleware.use "Mixpanel::Tracker::Middleware", "YOUR_MIXPANEL_API_TOKEN", options
43
+ ```
44
+
45
+ Where **options** is a Hash that accepts the following keys:
46
+
47
+ * **insert_js_last** : true | false
48
+
49
+ *Default: false*.
50
+ By default the scripts are inserted into the head of the html response. If you'd prefer the scripts to run after
51
+ all rendering has completed you can set the insert_js_last flag and they'll be added at the end of the body tag.
52
+ This will work whether or not you opt for the aynchronous version of the API. However, when inserting js into an
53
+ ajax response it will have no effect.
54
+
55
+ * **persist** : true | false
56
+
57
+ *Default: false*.
58
+ If you would like, the Mixpanel gem may be configured to store its queue in a Rack session. This allows events
59
+ to be stored through redirects, helpful if you sign in and redirect but want to associate an event with that
60
+ action. The mixpanel gem will also remove duplicate events from your queue for information that should only be
61
+ trasmitted to the API once, such as `mixpanel.identify`, `mixpanel.name_tag`, `mixpanel.people.set`, and
62
+ `mixpanel.register`.
63
+ This allows you to use a before filter to set these variables, redirect, and still have them only transmitted
64
+ once.
65
+ *To enable persistence*, you must set it in both places, Middleware and when you initialize Mixpanel class.
66
+
67
+ * **config** : a Hash
68
+
69
+ *Default: {}*.
70
+ You can also pass Mixpanel configuration details as seen here
71
+ (https://mixpanel.com/docs/integration-libraries/javascript-full-api#set_config)
72
+
73
+ ## Usage
74
+
75
+ ### Initialize Mixpanel class
76
+
77
+ ```ruby
78
+ @mixpanel = Mixpanel::Tracker.new("YOUR_MIXPANEL_API_TOKEN", request.env, true, options)
79
+ ```
80
+ Where **options** is a Hash that accepts the following keys:
81
+
82
+ * **async** : true | false
83
+
84
+ *Default: false*.
85
+ Built in async feature. Events are sent to a subprocess via a pipe and the sub process which asynchronously send events to Mixpanel.
86
+ This process uses a single thread to upload events, and may start dropping events if your application generates
87
+ them at a very high rate.
88
+ If you like for a more robust async behavior take a look at Resque example.
89
+
90
+ * **url** : String
91
+
92
+ *Default: http://api.mixpanel.com/track/?data=*.
93
+ If you are proxying Mixpanel API requests then you can set a custom url and additionally stop the token from
94
+ being sent by marking it as false if you're going to let the proxy add it.
95
+ Example: { url: "http://localhost:8000/mixpanelproxy?data=" }.
96
+
97
+ * **persist** : true | false
98
+
99
+ *Default: false*.
100
+ If you would like, the Mixpanel gem may be configured to store its queue in a Rack session. This allows events
101
+ to be stored through redirects, helpful if you sign in and redirect but want to associate an event with that
102
+ action. The mixpanel gem will also remove duplicate events from your queue for information that should only be
103
+ trasmitted to the API once, such as `mixpanel.identify`, `mixpanel.name_tag`, `mixpanel.people.set`, and
104
+ `mixpanel.register`.
105
+ This allows you to use a before filter to set these variables, redirect, and still have them only transmitted
106
+ once.
107
+ *To enable persistence*, you must set it in both places, Middleware and when you initialize Mixpanel class.
108
+
109
+ ### Track events directly.
110
+
111
+ ```ruby
112
+ @mixpanel.track_event("Sign in", {:some => "property"})
113
+ ```
114
+
115
+ ### Append events to be tracked with Javascript.
116
+
117
+ *Note*: You should setup the [Rack Middleware](#rack-middleware).
118
+
119
+ ```ruby
120
+ @mixpanel.append_event("Sign in", {:some => "property"})
121
+ ```
122
+
123
+ ### Execute Javascript API call
124
+
125
+ *Note*: You should setup the [Rack Middleware](#rack-middleware).
126
+
127
+ ```ruby
128
+ @mixpanel.append_api("register", {:some => "property"})
129
+ @mixpanel.append_api("identify", "Unique Identifier")
130
+ ```
131
+
132
+ ## Examples
133
+
134
+ ### How to use it from Rails controllers?
135
+
136
+ In your ApplicationController class add a method to instantiate mixpanel.
137
+
138
+ ```ruby
139
+ before_filter :initialize_mixpanel
140
+
141
+ def initialize_mixpanel
142
+ @mixpanel = Mixpanel::Tracker.new("YOUR_MIXPANEL_API_TOKEN", request.env, options)
143
+ end
144
+ ```
145
+ ## How to track events using Resque and Rails
146
+
147
+ If you don't want to use the built in Mixpanel Gem async feature bellow there is an example about how to make
148
+ async calls using Resque.
149
+
150
+ [Resque is a Redis-backed Ruby library for creating background jobs](https://github.com/defunkt/resque)
151
+
152
+ ```ruby
153
+ class MixpanelTrackEventJob
154
+ @queue = :slow
155
+
156
+ def mixpanel(request_env)
157
+ Mixpanel::Tracker.new(MIXPANEL_TOKEN, request_env)
158
+ end
159
+
160
+ def perform(name, params, request_env)
161
+ mixpanel(request_env).track_event(name, params)
162
+ end
163
+ end
164
+ ```
165
+
166
+ ```ruby
167
+ class UsersController < ApplicationController
168
+ def create
169
+ @user = User.new(params[:user])
170
+
171
+ if @user.save
172
+ MixpanelTrackEventJob.enqueue("Sign up", {:invited => params[:invited]}, request.env)
173
+ redirect_to user_root_path
174
+ else
175
+ render :new
176
+ end
177
+ end
178
+ end
179
+ ```
180
+
181
+ ## Supported Platforms
182
+
183
+ - 1.8.7
184
+ - 1.9.2
185
+ - 1.9.3
186
+ - JRuby 1.8 Mode
187
+
188
+ ## Deprecation Notes
189
+
190
+ This way to initialize Mixpanel gem is not longer allowed.
191
+
192
+ ```ruby
193
+ Mixpanel.new
194
+ ```
195
+
196
+ Use this instead:
197
+
198
+ ```ruby
199
+ Mixpanel::Tracker.new
200
+ ```
201
+
202
+ ## Collaborators and Maintainers
203
+
204
+ * [Alvaro Gil](https://github.com/zevarito) (Author)
205
+ * [Nathan Baxter](https://github.com/LogicWolfe)
206
+ * [Jake Mallory](https://github.com/tinomen)
207
+ * [Logan Bowers](https://github.com/loganb)
208
+ * [jakemack](https://github.com/jakemack)
209
+ * [James Ferguson](https://github.com/JamesFerguson)
210
+ * [Brad Wilson](https://github.com/bradx3)
211
+ * [Mark Cheverton](https://github.com/ennui2342)
212
+ * [Jamie Quint](https://github.com/jamiequint)
213
+ * [Ryan Schmukler](https://github.com/rschmukler)
@@ -1,8 +1,4 @@
1
1
  require 'mixpanel/tracker'
2
2
 
3
3
  module Mixpanel
4
- def self.new(token, env, async = false)
5
- Kernel.warn("DEPRECATED: Use Mixpanel::Tracker.new instead")
6
- Mixpanel::Tracker.new(token, env, async)
7
- end
8
4
  end
@@ -6,18 +6,41 @@ require 'mixpanel/tracker/middleware'
6
6
 
7
7
  module Mixpanel
8
8
  class Tracker
9
- def initialize(token, env, async = false, url = 'http://api.mixpanel.com/track/?data=')
9
+ def initialize(token, env, options={})
10
10
  @token = token
11
11
  @env = env
12
- @async = async
13
- @url = url
14
- clear_queue
12
+ @async = options.fetch(:async, false)
13
+ @url = options.fetch(:url, 'http://api.mixpanel.com/track/?data=')
14
+ @persist = options.fetch(:persist, false)
15
+
16
+ if @persist
17
+ @env["rack.session"]["mixpanel_events"] ||= []
18
+ else
19
+ clear_queue
20
+ end
15
21
  end
16
22
 
17
23
  def append_event(event, properties = {})
18
24
  append_api('track', event, properties)
19
25
  end
20
26
 
27
+ def append_person_event(properties = {})
28
+ # evaluate symbols and rewrite
29
+ special_properties = %w{email created first_name last_name last_login username country_code}
30
+ special_properties.each do |key|
31
+ symbolized_key = key.to_sym
32
+ if properties.has_key?(symbolized_key)
33
+ properties["$#{key}"] = properties[symbolized_key]
34
+ properties.delete(symbolized_key)
35
+ end
36
+ end
37
+ append_api('people.set', properties)
38
+ end
39
+
40
+ def append_person_increment_event(property, increment=1)
41
+ append_api('people.increment', property, increment)
42
+ end
43
+
21
44
  def append_api(type, *args)
22
45
  queue << [type, args.map {|arg| arg.to_json}]
23
46
  end
@@ -41,11 +64,19 @@ module Mixpanel
41
64
  end
42
65
 
43
66
  def queue
44
- @env["mixpanel_events"]
67
+ if @persist
68
+ return @env["rack.session"]["mixpanel_events"]
69
+ else
70
+ return @env["mixpanel_events"]
71
+ end
45
72
  end
46
73
 
47
74
  def clear_queue
48
- @env["mixpanel_events"] = []
75
+ if @persist
76
+ @env["rack.session"]["mixpanel_events"] = []
77
+ else
78
+ @env["mixpanel_events"] = []
79
+ end
49
80
  end
50
81
 
51
82
  class <<self
@@ -1,4 +1,5 @@
1
1
  require 'rack'
2
+ require 'json'
2
3
 
3
4
  module Mixpanel
4
5
  class Tracker
@@ -7,8 +8,9 @@ module Mixpanel
7
8
  @app = app
8
9
  @token = mixpanel_token
9
10
  @options = {
10
- :async => false,
11
- :insert_js_last => false
11
+ :insert_js_last => false,
12
+ :persist => false,
13
+ :config => {}
12
14
  }.merge(options)
13
15
  end
14
16
 
@@ -18,6 +20,7 @@ module Mixpanel
18
20
  @status, @headers, @response = @app.call(env)
19
21
 
20
22
  if is_trackable_response?
23
+ merge_queue! if @options[:persist]
21
24
  update_response!
22
25
  update_content_length!
23
26
  delete_event_queue!
@@ -67,56 +70,69 @@ module Mixpanel
67
70
  end
68
71
 
69
72
  def is_trackable_response?
73
+ return false if @status == 302
70
74
  is_html_response? || is_javascript_response?
71
75
  end
72
76
 
73
77
  def render_mixpanel_scripts
74
- if @options[:async]
75
- <<-EOT
76
- <script type='text/javascript'>
77
- var mpq = [];
78
- mpq.push(["init", "#{@token}"]);
79
- (function(){var b,a,e,d,c;b=document.createElement("script");b.type="text/javascript";b.async=true;b.src=(document.location.protocol==="https:"?"https:":"http:")+"//api.mixpanel.com/site_media/js/api/mixpanel.js";a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(b,a);e=function(f){return function(){mpq.push([f].concat(Array.prototype.slice.call(arguments,0)))}};d=["track","track_links","track_forms","register","register_once","identify","name_tag","set_config"];for(c=0;c<d.length;c++){mpq[d[c]]=e(d[c])}})();
78
+ <<-EOT
79
+ <!-- start Mixpanel -->
80
+ <script type="text/javascript">
81
+ (function(c,a){var b,d,h,e;b=c.createElement("script");b.type="text/javascript";
82
+ b.async=!0;b.src=("https:"===c.location.protocol?"https:":"http:")+
83
+ '//api.mixpanel.com/site_media/js/api/mixpanel.2.js';d=c.getElementsByTagName("script")[0];
84
+ d.parentNode.insertBefore(b,d);a._i=[];a.init=function(b,c,f){function d(a,b){
85
+ var c=b.split(".");2==c.length&&(a=a[c[0]],b=c[1]);a[b]=function(){a.push([b].concat(
86
+ Array.prototype.slice.call(arguments,0)))}}var g=a;"undefined"!==typeof f?g=a[f]=[]:
87
+ f="mixpanel";g.people=g.people||[];h=['disable','track','track_pageview','track_links',
88
+ 'track_forms','register','register_once','unregister','identify','name_tag',
89
+ 'set_config','people.set','people.increment'];for(e=0;e<h.length;e++)d(g,h[e]);
90
+ a._i.push([b,c,f])};a.__SV=1.1;window.mixpanel=a})(document,window.mixpanel||[]);
91
+
92
+ mixpanel.init("#{@token}");
93
+ mixpanel.set_config(#{@options[:config].to_json});
80
94
  </script>
81
- EOT
82
- else
83
- <<-EOT
84
- <script type='text/javascript'>
85
- var mp_protocol = (('https:' == document.location.protocol) ? 'https://' : 'http://');
86
- document.write(unescape('%3Cscript src="' + mp_protocol + 'api.mixpanel.com/site_media/js/api/mixpanel.js" type="text/javascript"%3E%3C/script%3E'));
87
- </script>
88
- <script type='text/javascript'>
89
- try {
90
- var mpmetrics = new MixpanelLib('#{@token}');
91
- } catch(err) {
92
- null_fn = function () {};
93
- var mpmetrics = {
94
- track: null_fn, track_funnel: null_fn, register: null_fn, register_once: null_fn, register_funnel: null_fn
95
- };
96
- }
97
- </script>
98
- EOT
99
- end
95
+ <!-- end Mixpanel -->
96
+ EOT
100
97
  end
101
98
 
102
99
  def delete_event_queue!
103
- @env.delete('mixpanel_events')
100
+ if @options[:persist]
101
+ (@env['rack.session']).delete('mixpanel_events')
102
+ else
103
+ @env.delete('mixpanel_events')
104
+ end
104
105
  end
105
106
 
106
107
  def queue
107
- return [] if !@env.has_key?('mixpanel_events') || @env['mixpanel_events'].empty?
108
- @env['mixpanel_events']
108
+ if @options[:persist]
109
+ return [] if !(@env['rack.session']).has_key?('mixpanel_events') || @env['rack.session']['mixpanel_events'].empty?
110
+ @env['rack.session']['mixpanel_events']
111
+ else
112
+ return [] if !@env.has_key?('mixpanel_events') || @env['mixpanel_events'].empty?
113
+ @env['mixpanel_events']
114
+ end
115
+ end
116
+
117
+ def merge_queue!
118
+ present_hash = {}
119
+ special_events = ['identify', 'name_tag', 'people.set', 'register']
120
+ queue.uniq!
121
+
122
+ queue.reverse_each do |item|
123
+ is_special = special_events.include?(item[0])
124
+ if present_hash[item[0]] and is_special
125
+ queue.delete(item)
126
+ else
127
+ present_hash[item[0]] = true if is_special
128
+ end
129
+ end
109
130
  end
110
131
 
111
132
  def render_event_tracking_scripts(include_script_tag=true)
112
133
  return "" if queue.empty?
113
134
 
114
- if @options[:async]
115
- output = queue.map {|type, arguments| %(mpq.push(["#{type}", #{arguments.join(', ')}]);) }.join("\n")
116
- else
117
- output = queue.map {|type, arguments| %(mpmetrics.#{type}(#{arguments.join(', ')});) }.join("\n")
118
- end
119
-
135
+ output = queue.map {|type, arguments| %(mixpanel.#{type}(#{arguments.join(', ')});) }.join("\n")
120
136
  output = "try {#{output}} catch(err) {}"
121
137
 
122
138
  include_script_tag ? "<script type='text/javascript'>#{output}</script>" : output
@@ -1,8 +1,8 @@
1
- files = ['README.rdoc', 'LICENSE', 'Rakefile', 'mixpanel.gemspec', '{spec,lib}/**/*'].map {|f| Dir[f]}.flatten
1
+ files = ['README.md', 'LICENSE', 'Rakefile', 'mixpanel.gemspec', '{spec,lib}/**/*'].map {|f| Dir[f]}.flatten
2
2
 
3
3
  spec = Gem::Specification.new do |s|
4
4
  s.name = "mixpanel"
5
- s.version = "1.1.3"
5
+ s.version = "2.0.0"
6
6
  s.rubyforge_project = "mixpanel"
7
7
  s.description = "Simple lib to track events in Mixpanel service. It can be used in any rack based framework."
8
8
  s.author = "Alvaro Gil"
@@ -13,7 +13,7 @@ spec = Gem::Specification.new do |s|
13
13
  s.files = files
14
14
  s.require_path = "lib"
15
15
  s.has_rdoc = false
16
- s.extra_rdoc_files = ["README.rdoc"]
16
+ s.extra_rdoc_files = ["README.md"]
17
17
  s.add_dependency 'json'
18
18
  s.add_dependency 'rack'
19
19
  s.add_dependency 'escape'
@@ -22,6 +22,7 @@ spec = Gem::Specification.new do |s|
22
22
  s.add_development_dependency 'fakeweb'
23
23
  s.add_development_dependency 'nokogiri'
24
24
  s.add_development_dependency 'rake'
25
- s.add_development_dependency 'ruby-debug19' if RUBY_VERSION =~ /^1\.9/
25
+ s.add_development_dependency 'debugger' if RUBY_VERSION =~ /^1\.9\.3/
26
+ s.add_development_dependency 'ruby-debug19' if RUBY_VERSION =~ /^1\.9\.2/
26
27
  s.add_development_dependency 'ruby-debug' if RUBY_VERSION =~ /^1\.8/
27
28
  end
@@ -2,9 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  describe Mixpanel do
4
4
  context "Deprecated initialization mode" do
5
- it "should instantiate the object as it was doing before but drop a deprecation warning" do
6
- mixpanel = Mixpanel.new(MIX_PANEL_TOKEN, @env = {"REMOTE_ADDR" => "127.0.0.1"})
7
- mixpanel.should be_kind_of(Mixpanel::Tracker)
5
+ it "should not allow to initialize the class as the old way" do
6
+ lambda do
7
+ mixpanel = Mixpanel.new(MIX_PANEL_TOKEN, @env = {"REMOTE_ADDR" => "127.0.0.1"})
8
+ end.should raise_error(NoMethodError)
8
9
  end
9
10
  end
10
11
  end
@@ -1,5 +1,23 @@
1
1
  require 'spec_helper'
2
2
 
3
+ def exec_default_appends_on(mixpanel)
4
+ mixpanel.append_event("Visit", {:article => 1})
5
+ mixpanel.append_event("Sign in")
6
+ mixpanel.append_person_event(:first_name => "foo", :last_name => "bar", :username => "foobar")
7
+ mixpanel.append_person_increment_event(:sign_in_rate)
8
+ end
9
+
10
+ def check_for_default_appends_on(txt)
11
+ txt.should =~ /mixpanel\.track\("Visit",\s?\{"article":1\}\)/
12
+ txt.should =~ /mixpanel\.track\("Sign in",\s?\{\}\)/
13
+ txt.should =~ /mixpanel\.people\.set\(.*\);\nmixpanel.people.increment\(\"sign_in_rate\",\s?1\);/
14
+ match = txt.match(/mixpanel\.people\.set\((.*\));/)
15
+ match[1].should =~ /\"\$first_name\":\"foo\"/
16
+ match[1].should =~ /\"\$username\":\"foobar\"/
17
+ match[1].should =~ /\"\$last_name\":\"bar\"/
18
+ txt.should =~ /mixpanel\.people\.increment\(\"sign_in_rate\"\s?,\s?1\)/
19
+ end
20
+
3
21
  describe Mixpanel::Tracker::Middleware do
4
22
  include Rack::Test::Methods
5
23
 
@@ -17,7 +35,7 @@ describe Mixpanel::Tracker::Middleware do
17
35
  describe "Appending async mixpanel scripts" do
18
36
  describe "With ajax requests" do
19
37
  before do
20
- setup_rack_application(DummyApp, {:body => html_document, :headers => {"Content-Type" => "text/html"}}, {:async => true})
38
+ setup_rack_application(DummyApp, {:body => html_document, :headers => {"Content-Type" => "text/html"}})
21
39
  get "/", {}, {"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"}
22
40
  end
23
41
 
@@ -32,7 +50,7 @@ describe Mixpanel::Tracker::Middleware do
32
50
 
33
51
  describe "With large ajax response" do
34
52
  before do
35
- setup_rack_application(DummyApp, {:body => large_script, :headers => {"Content-Type" => "text/html"}}, {:async => true})
53
+ setup_rack_application(DummyApp, {:body => large_script, :headers => {"Content-Type" => "text/html"}})
36
54
  get "/", {}, {"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"}
37
55
  end
38
56
 
@@ -48,7 +66,7 @@ describe Mixpanel::Tracker::Middleware do
48
66
  describe "With regular requests" do
49
67
  describe "With js in head" do
50
68
  before do
51
- setup_rack_application(DummyApp, {:body => html_document, :headers => {"Content-Type" => "text/html"}}, {:async => true, :insert_js_last => false})
69
+ setup_rack_application(DummyApp, {:body => html_document, :headers => {"Content-Type" => "text/html"}}, {:insert_js_last => false})
52
70
  get "/"
53
71
  end
54
72
 
@@ -62,7 +80,7 @@ describe Mixpanel::Tracker::Middleware do
62
80
  end
63
81
 
64
82
  it "should use the specified token instantiating mixpanel lib" do
65
- last_response.should =~ /mpq.push\(\["init", "#{MIX_PANEL_TOKEN}"\]\)/
83
+ last_response.body.should =~ /mixpanel\.init\("#{MIX_PANEL_TOKEN}"\)/
66
84
  end
67
85
 
68
86
  it "should define Content-Length if not exist" do
@@ -76,7 +94,7 @@ describe Mixpanel::Tracker::Middleware do
76
94
 
77
95
  describe "With js last" do
78
96
  before do
79
- setup_rack_application(DummyApp, {:body => html_document, :headers => {"Content-Type" => "text/html"}}, {:async => true, :insert_js_last => true})
97
+ setup_rack_application(DummyApp, {:body => html_document, :headers => {"Content-Type" => "text/html"}}, {:insert_js_last => true})
80
98
  get "/"
81
99
  end
82
100
 
@@ -131,12 +149,12 @@ describe Mixpanel::Tracker::Middleware do
131
149
  Nokogiri::HTML(last_response.body).search('body script').should be_empty
132
150
  end
133
151
 
134
- it "should have 2 included scripts" do
135
- Nokogiri::HTML(last_response.body).search('script').size.should == 2
152
+ it "should have 1 included script" do
153
+ Nokogiri::HTML(last_response.body).search('script').size.should == 1
136
154
  end
137
155
 
138
156
  it "should use the specified token instantiating mixpanel lib" do
139
- last_response.should =~ /new MixpanelLib\('#{MIX_PANEL_TOKEN}'\)/
157
+ last_response.body.should =~ /mixpanel\.init\("#{MIX_PANEL_TOKEN}"\)/
140
158
  end
141
159
 
142
160
  it "should define Content-Length if not exist" do
@@ -165,13 +183,12 @@ describe Mixpanel::Tracker::Middleware do
165
183
  describe "Tracking async appended events" do
166
184
  before do
167
185
  @mixpanel = Mixpanel::Tracker.new(MIX_PANEL_TOKEN, {})
168
- @mixpanel.append_event("Visit", {:article => 1})
169
- @mixpanel.append_event("Sign in")
186
+ exec_default_appends_on @mixpanel
170
187
  end
171
188
 
172
189
  describe "With ajax requests and text/html response" do
173
190
  before do
174
- setup_rack_application(DummyApp, {:body => "<p>response</p>", :headers => {"Content-Type" => "text/html"}}, {:async => true})
191
+ setup_rack_application(DummyApp, {:body => "<p>response</p>", :headers => {"Content-Type" => "text/html"}})
175
192
 
176
193
  get "/", {}, {"mixpanel_events" => @mixpanel.queue, "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"}
177
194
  end
@@ -182,9 +199,7 @@ describe Mixpanel::Tracker::Middleware do
182
199
 
183
200
  it "should be tracking the correct events inside a script tag" do
184
201
  script = Nokogiri::HTML(last_response.body).search('script')
185
- script.inner_html.should =~ /try\s?\{(.*)\}\s?catch/m
186
- script.inner_html.should =~ /mpq\.push\(\["track",\s?"Visit",\s?\{"article":1\}\]\)/
187
- script.inner_html.should =~ /mpq\.push\(\["track",\s?"Sign in",\s?\{\}\]\)/
202
+ check_for_default_appends_on script.inner_html
188
203
  end
189
204
 
190
205
  it "should delete events queue after use it" do
@@ -194,7 +209,7 @@ describe Mixpanel::Tracker::Middleware do
194
209
 
195
210
  describe "With ajax requests and text/javascript response" do
196
211
  before do
197
- setup_rack_application(DummyApp, {:body => "alert('response')", :headers => {"Content-Type" => "text/javascript"}}, {:async => true})
212
+ setup_rack_application(DummyApp, {:body => "alert('response')", :headers => {"Content-Type" => "text/javascript"}})
198
213
  get "/", {}, {"mixpanel_events" => @mixpanel.queue, "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"}
199
214
  end
200
215
 
@@ -204,8 +219,7 @@ describe Mixpanel::Tracker::Middleware do
204
219
 
205
220
  it "should be tracking the correct events inside a try/catch" do
206
221
  script = last_response.body.match(/try\s?\{(.*)\}\s?catch/m)[1]
207
- script.should =~ /mpq\.push\(\["track",\s?"Visit",\s?\{"article":1\}\]\)/
208
- script.should =~ /mpq\.push\(\["track",\s?"Sign in",\s?\{\}\]\)/
222
+ check_for_default_appends_on script
209
223
  end
210
224
 
211
225
  it "should delete events queue after use it" do
@@ -215,7 +229,7 @@ describe Mixpanel::Tracker::Middleware do
215
229
 
216
230
  describe "With regular requests" do
217
231
  before do
218
- setup_rack_application(DummyApp, {:body => html_document, :headers => {"Content-Type" => "text/html"}}, {:async => true})
232
+ setup_rack_application(DummyApp, {:body => html_document, :headers => {"Content-Type" => "text/html"}})
219
233
 
220
234
  get "/", {}, {"mixpanel_events" => @mixpanel.queue}
221
235
  end
@@ -225,8 +239,7 @@ describe Mixpanel::Tracker::Middleware do
225
239
  end
226
240
 
227
241
  it "should be tracking the correct events" do
228
- last_response.body.should =~ /mpq\.push\(\["track",\s?"Visit",\s?\{"article":1\}\]\)/
229
- last_response.body.should =~ /mpq\.push\(\["track",\s?"Sign in",\s?\{\}\]\)/
242
+ check_for_default_appends_on last_response.body
230
243
  end
231
244
 
232
245
  it "should delete events queue after use it" do
@@ -238,8 +251,7 @@ describe Mixpanel::Tracker::Middleware do
238
251
  describe "Tracking appended events" do
239
252
  before do
240
253
  @mixpanel = Mixpanel::Tracker.new(MIX_PANEL_TOKEN, {})
241
- @mixpanel.append_event("Visit", {:article => 1})
242
- @mixpanel.append_event("Sign in")
254
+ exec_default_appends_on @mixpanel
243
255
  end
244
256
 
245
257
  describe "With ajax requests and text/html response" do
@@ -255,9 +267,7 @@ describe Mixpanel::Tracker::Middleware do
255
267
 
256
268
  it "should be tracking the correct events inside a script tag" do
257
269
  script = Nokogiri::HTML(last_response.body).search('script')
258
- script.inner_html.should =~ /try\s?\{(.*)\}\s?catch/m
259
- script.inner_html.should =~ /mpmetrics\.track\("Visit",\s?\{"article":1\}\)/
260
- script.inner_html.should =~ /mpmetrics\.track\("Sign in",\s?\{\}\)/
270
+ check_for_default_appends_on script.inner_html
261
271
  end
262
272
 
263
273
  it "should delete events queue after use it" do
@@ -277,8 +287,7 @@ describe Mixpanel::Tracker::Middleware do
277
287
 
278
288
  it "should be tracking the correct events inside a try/catch" do
279
289
  script = last_response.body.match(/try\s?\{(.*)\}\s?catch/m)[1]
280
- script.should =~ /mpmetrics\.track\("Visit",\s?\{"article":1\}\)/
281
- script.should =~ /mpmetrics\.track\("Sign in",\s?\{\}\)/
290
+ check_for_default_appends_on script
282
291
  end
283
292
 
284
293
  it "should delete events queue after use it" do
@@ -293,13 +302,12 @@ describe Mixpanel::Tracker::Middleware do
293
302
  get "/", {}, {"mixpanel_events" => @mixpanel.queue}
294
303
  end
295
304
 
296
- it "should render 3 script tags" do
297
- Nokogiri::HTML(last_response.body).search('script').size.should == 3
305
+ it "should render 2 script tags" do
306
+ Nokogiri::HTML(last_response.body).search('script').size.should == 2
298
307
  end
299
308
 
300
309
  it "should be tracking the correct events" do
301
- last_response.body.should =~ /mpmetrics\.track\("Visit",\s?\{"article":1\}\)/
302
- last_response.body.should =~ /mpmetrics\.track\("Sign in",\s?\{\}\)/
310
+ check_for_default_appends_on last_response.body
303
311
  end
304
312
 
305
313
  it "should delete events queue after use it" do
metadata CHANGED
@@ -1,149 +1,169 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: mixpanel
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 1
7
- - 1
8
- - 3
9
- version: 1.1.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Alvaro Gil
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2012-01-11 00:00:00 -02:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-08-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: json
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
- version: "0"
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
31
22
  type: :runtime
32
- version_requirements: *id001
33
- - !ruby/object:Gem::Dependency
34
- name: rack
35
23
  prerelease: false
36
- requirement: &id002 !ruby/object:Gem::Requirement
37
- none: false
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- segments:
42
- - 0
43
- version: "0"
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
44
38
  type: :runtime
45
- version_requirements: *id002
46
- - !ruby/object:Gem::Dependency
47
- name: escape
48
39
  prerelease: false
49
- requirement: &id003 !ruby/object:Gem::Requirement
50
- none: false
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- segments:
55
- - 0
56
- version: "0"
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: escape
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
57
54
  type: :runtime
58
- version_requirements: *id003
59
- - !ruby/object:Gem::Dependency
60
- name: rspec
61
55
  prerelease: false
62
- requirement: &id004 !ruby/object:Gem::Requirement
63
- none: false
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- segments:
68
- - 0
69
- version: "0"
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
70
  type: :development
71
- version_requirements: *id004
72
- - !ruby/object:Gem::Dependency
73
- name: rack-test
74
71
  prerelease: false
75
- requirement: &id005 !ruby/object:Gem::Requirement
76
- none: false
77
- requirements:
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- segments:
81
- - 0
82
- version: "0"
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rack-test
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
83
86
  type: :development
84
- version_requirements: *id005
85
- - !ruby/object:Gem::Dependency
86
- name: fakeweb
87
87
  prerelease: false
88
- requirement: &id006 !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ">="
92
- - !ruby/object:Gem::Version
93
- segments:
94
- - 0
95
- version: "0"
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: fakeweb
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
96
102
  type: :development
97
- version_requirements: *id006
98
- - !ruby/object:Gem::Dependency
99
- name: nokogiri
100
103
  prerelease: false
101
- requirement: &id007 !ruby/object:Gem::Requirement
102
- none: false
103
- requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- segments:
107
- - 0
108
- version: "0"
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: nokogiri
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
109
118
  type: :development
110
- version_requirements: *id007
111
- - !ruby/object:Gem::Dependency
112
- name: rake
113
119
  prerelease: false
114
- requirement: &id008 !ruby/object:Gem::Requirement
115
- none: false
116
- requirements:
117
- - - ">="
118
- - !ruby/object:Gem::Version
119
- segments:
120
- - 0
121
- version: "0"
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rake
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
122
134
  type: :development
123
- version_requirements: *id008
124
- - !ruby/object:Gem::Dependency
125
- name: ruby-debug19
126
135
  prerelease: false
127
- requirement: &id009 !ruby/object:Gem::Requirement
128
- none: false
129
- requirements:
130
- - - ">="
131
- - !ruby/object:Gem::Version
132
- segments:
133
- - 0
134
- version: "0"
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: debugger
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
135
150
  type: :development
136
- version_requirements: *id009
137
- description: Simple lib to track events in Mixpanel service. It can be used in any rack based framework.
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ description: Simple lib to track events in Mixpanel service. It can be used in any
159
+ rack based framework.
138
160
  email: zevarito@gmail.com
139
161
  executables: []
140
-
141
162
  extensions: []
142
-
143
- extra_rdoc_files:
144
- - README.rdoc
145
- files:
146
- - README.rdoc
163
+ extra_rdoc_files:
164
+ - README.md
165
+ files:
166
+ - README.md
147
167
  - LICENSE
148
168
  - Rakefile
149
169
  - mixpanel.gemspec
@@ -156,37 +176,28 @@ files:
156
176
  - lib/mixpanel/tracker/subprocess.rb
157
177
  - lib/mixpanel/tracker.rb
158
178
  - lib/mixpanel.rb
159
- has_rdoc: true
160
179
  homepage: http://github.com/zevarito/mixpanel
161
180
  licenses: []
162
-
163
181
  post_install_message:
164
182
  rdoc_options: []
165
-
166
- require_paths:
183
+ require_paths:
167
184
  - lib
168
- required_ruby_version: !ruby/object:Gem::Requirement
185
+ required_ruby_version: !ruby/object:Gem::Requirement
169
186
  none: false
170
- requirements:
171
- - - ">="
172
- - !ruby/object:Gem::Version
173
- segments:
174
- - 0
175
- version: "0"
176
- required_rubygems_version: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - ! '>='
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
192
  none: false
178
- requirements:
179
- - - ">="
180
- - !ruby/object:Gem::Version
181
- segments:
182
- - 0
183
- version: "0"
193
+ requirements:
194
+ - - ! '>='
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
184
197
  requirements: []
185
-
186
198
  rubyforge_project: mixpanel
187
- rubygems_version: 1.3.7
199
+ rubygems_version: 1.8.21
188
200
  signing_key:
189
201
  specification_version: 3
190
202
  summary: Supports direct request api and javascript requests through a middleware.
191
203
  test_files: []
192
-
@@ -1,120 +0,0 @@
1
- == What is Mixpanel (the service) ?
2
-
3
- Mixpanel is a real-time analytics service that helps companies understand how users interact with web applications.
4
- http://mixpanel.com
5
-
6
- == What does this Gem do?
7
-
8
- * Track events with properties directly from your backend.
9
- * Track events with properties through javascript using a rack middleware.
10
-
11
-
12
- == How to install?
13
-
14
- gem install mixpanel
15
-
16
-
17
- == How to use it with a Rails application?
18
-
19
- In your environment config file add this.
20
-
21
- Rails::Initializer.run do |config|
22
-
23
- config.middleware.use "Mixpanel::Tracker::Middleware", "YOUR_MIXPANEL_API_TOKEN"
24
-
25
- If you want to use the asynchronous version of Mixpanel's javascript API
26
-
27
- Rails::Initializer.run do |config|
28
-
29
- config.middleware.use "Mixpanel::Tracker::Middleware", "YOUR_MIXPANEL_API_TOKEN", :async => true
30
-
31
- By default the scripts are inserted into the head of the html response. If you'd prefer the scripts to run after all rendering has completed you can set the insert_js_last flag and they'll be added at the end of the body tag. This will work whether or not you opt for the aynchronous version of the API. However, when inserting js into an ajax response it will have no effect:
32
-
33
- Rails::Initializer.run do |config|
34
-
35
- config.middleware.use "Mixpanel::Tracker::Middleware", "YOUR_MIXPANEL_API_TOKEN", :async => true, :insert_js_last => true
36
-
37
- In your application_controller class add a method to instance mixpanel.
38
-
39
- before_filter :initialize_mixpanel
40
-
41
- def initialize_mixpanel
42
- @mixpanel = Mixpanel::Tracker.new("YOUR_MIXPANEL_API_TOKEN", request.env, true)
43
- end
44
-
45
- Then in each request you want to track some event you can use:
46
-
47
- To track events directly from your backend...
48
-
49
- @mixpanel.track_event("Sign in", {:some => "property"})
50
-
51
- To track events after response with javascript...
52
-
53
- @mixpanel.append_event("Sign in", {:some => "property"})
54
-
55
- To execute any javascript API call
56
-
57
- @mixpanel.append_api("register", {:some => "property"})
58
- @mixpanel.append_api("identify", "Unique Identifier")
59
-
60
- If you are proxying Mixpanel API requests then you can set a custom url and additionally stop the token from being sent by marking it as false if you're going to let the proxy add it...
61
-
62
- @mixpanel = Mixpanel::Tracker.new(flase, request.env, true, 'http://localhost:8000/mixpanelproxy?data=')
63
-
64
- == Resque and Rails example
65
-
66
- If you don't want to use the built in Mixpanel Gem async feature bellow there is an example about how to make async calls using Resque.
67
-
68
- {Resque is a Redis-backed Ruby library for creating background jobs}[https://github.com/defunkt/resque]
69
-
70
- class MixpanelTrackEventJob
71
- @queue = :slow
72
-
73
- def mixpanel(request_env)
74
- Mixpanel.new(MIXPANEL_TOKEN, request_env)
75
- end
76
-
77
- def perform(name, params, request_env)
78
- mixpanel(request_env).track_event(name, params)
79
- end
80
- end
81
-
82
- class UsersController < ApplicationController
83
- def create
84
- @user = User.new(params[:user])
85
-
86
- if @user.save
87
- MixpanelTrackEventJob.enqueue("Sign up", {:invited => params[:invited]}, request.env)
88
- redirect_to user_root_path
89
- else
90
- render :new
91
- end
92
- end
93
- end
94
-
95
- == Notes
96
-
97
- There are two forms of async operation:
98
- * Using MixpanelMiddleware, events are queued via Mixpanel#append_event and inserted into a JavaScript block within the HTML response.
99
- * Using Mixpanel.new(…, …, true), events are sent to a subprocess via a pipe and the sub process which asynchronously send events to Mixpanel. This process uses a single thread to upload events, and may start dropping events if your application generates them at a very high rate.
100
-
101
- == Deprecation Notes
102
-
103
- For a short term this method will be accepted but it will be deprecated soon.
104
-
105
- Mixpanel.new
106
-
107
- == Collaborations
108
-
109
- All collaborations are welcome to this project, please fork and make a pull request.
110
-
111
- == Collaborators and Maintainers
112
-
113
- * {Alvaro Gil}[https://github.com/zevarito] (Author)
114
- * {Nathan Baxter}[https://github.com/LogicWolfe]
115
- * {Jake Mallory}[https://github.com/tinomen]
116
- * {Logan Bowers}[https://github.com/loganb]
117
- * {jakemack}[https://github.com/jakemack]
118
- * {James Ferguson}[https://github.com/JamesFerguson]
119
- * {Brad Wilson}[https://github.com/bradx3]
120
- * {Mark Cheverton}[https://github.com/ennui2342]