mixpanel 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +5 -4
- data/lib/mixpanel/mixpanel.rb +44 -2
- data/lib/mixpanel/mixpanel_subprocess.rb +30 -0
- data/mixpanel.gemspec +2 -1
- data/spec/mixpanel/mixpanel_spec.rb +29 -1
- metadata +24 -9
data/README.rdoc
CHANGED
|
@@ -33,7 +33,7 @@ In your application_controller class add a method to instance mixpanel.
|
|
|
33
33
|
before_filter :initialize_mixpanel
|
|
34
34
|
|
|
35
35
|
def initialize_mixpanel
|
|
36
|
-
@mixpanel = Mixpanel.new("YOUR_MIXPANEL_API_TOKEN", request.env)
|
|
36
|
+
@mixpanel = Mixpanel.new("YOUR_MIXPANEL_API_TOKEN", request.env, true)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
Then in each request you want to track some event you can use:
|
|
@@ -54,12 +54,13 @@ To execute any javascript API call
|
|
|
54
54
|
|
|
55
55
|
== Notes
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
There are two forms of async operation:
|
|
58
|
+
* Using MixpanelMiddleware, events are queued via Mixpanel#append_event and inserted into a JavaScript block within the HTML response.
|
|
59
|
+
* 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.
|
|
60
60
|
|
|
61
61
|
== Collaborators and Maintainers
|
|
62
62
|
|
|
63
63
|
* {Alvaro Gil}[https://github.com/zevarito] (Author)
|
|
64
64
|
* {Nathan Baxter}[https://github.com/LogicWolfe]
|
|
65
65
|
* {Jake Mallory}[https://github.com/tinomen]
|
|
66
|
+
* {Logan Bowers}[https://github.com/loganb]
|
data/lib/mixpanel/mixpanel.rb
CHANGED
|
@@ -2,10 +2,13 @@ require "open-uri"
|
|
|
2
2
|
require 'base64'
|
|
3
3
|
require 'json'
|
|
4
4
|
|
|
5
|
+
require 'thread'
|
|
6
|
+
|
|
5
7
|
class Mixpanel
|
|
6
|
-
def initialize(token, env)
|
|
8
|
+
def initialize(token, env, async = false)
|
|
7
9
|
@token = token
|
|
8
10
|
@env = env
|
|
11
|
+
@async = async
|
|
9
12
|
clear_queue
|
|
10
13
|
end
|
|
11
14
|
|
|
@@ -33,6 +36,35 @@ class Mixpanel
|
|
|
33
36
|
def clear_queue
|
|
34
37
|
@env["mixpanel_events"] = []
|
|
35
38
|
end
|
|
39
|
+
|
|
40
|
+
class <<self
|
|
41
|
+
WORKER_MUTEX = Mutex.new
|
|
42
|
+
|
|
43
|
+
def worker
|
|
44
|
+
WORKER_MUTEX.synchronize do
|
|
45
|
+
@worker || (@worker = IO.popen(self.cmd, 'w'))
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def dispose_worker(w)
|
|
50
|
+
WORKER_MUTEX.synchronize do
|
|
51
|
+
if(@worker == w)
|
|
52
|
+
@worker = nil
|
|
53
|
+
w.close
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def cmd
|
|
59
|
+
@cmd || begin
|
|
60
|
+
require 'escape'
|
|
61
|
+
require 'rbconfig'
|
|
62
|
+
interpreter = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['RUBY_SO_NAME'])
|
|
63
|
+
subprocess = File.join(File.dirname(__FILE__), 'mixpanel_subprocess.rb')
|
|
64
|
+
@cmd = Escape.shell_command([interpreter, subprocess])
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
36
68
|
|
|
37
69
|
private
|
|
38
70
|
|
|
@@ -44,7 +76,17 @@ class Mixpanel
|
|
|
44
76
|
data = Base64.encode64(JSON.generate(params)).gsub(/\n/,'')
|
|
45
77
|
url = "http://api.mixpanel.com/track/?data=#{data}"
|
|
46
78
|
|
|
47
|
-
|
|
79
|
+
if(@async)
|
|
80
|
+
w = Mixpanel.worker
|
|
81
|
+
begin
|
|
82
|
+
url << "\n"
|
|
83
|
+
w.write(url)
|
|
84
|
+
rescue Errno::EPIPE => e
|
|
85
|
+
Mixpanel.dispose_worker(w)
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
open(url).read
|
|
89
|
+
end
|
|
48
90
|
end
|
|
49
91
|
|
|
50
92
|
def build_event(event, properties)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
require 'mixpanel'
|
|
4
|
+
require 'open-uri'
|
|
5
|
+
|
|
6
|
+
require 'thread'
|
|
7
|
+
|
|
8
|
+
class Mixpanel::Subprocess
|
|
9
|
+
Q = Queue.new
|
|
10
|
+
ENDMARKER = Object.new
|
|
11
|
+
|
|
12
|
+
Thread.abort_on_exception = true
|
|
13
|
+
producer = Thread.new do
|
|
14
|
+
STDIN.each_line() do |url|
|
|
15
|
+
STDERR.puts("Dropped: #{url}") && next if Q.length > 10000
|
|
16
|
+
Q << url
|
|
17
|
+
end
|
|
18
|
+
Q << ENDMARKER
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
loop do
|
|
22
|
+
url = Q.pop
|
|
23
|
+
break if(url == ENDMARKER)
|
|
24
|
+
url.chomp!
|
|
25
|
+
next if(url.empty?) #for testing
|
|
26
|
+
|
|
27
|
+
open(url).read
|
|
28
|
+
end
|
|
29
|
+
producer.join
|
|
30
|
+
end
|
data/mixpanel.gemspec
CHANGED
|
@@ -2,7 +2,7 @@ files = ['README.rdoc', 'LICENSE', 'Rakefile', 'mixpanel.gemspec', '{spec,lib}/*
|
|
|
2
2
|
|
|
3
3
|
spec = Gem::Specification.new do |s|
|
|
4
4
|
s.name = "mixpanel"
|
|
5
|
-
s.version = "0.
|
|
5
|
+
s.version = "0.8.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"
|
|
@@ -16,6 +16,7 @@ spec = Gem::Specification.new do |s|
|
|
|
16
16
|
s.extra_rdoc_files = ["README.rdoc"]
|
|
17
17
|
s.add_dependency 'json'
|
|
18
18
|
s.add_dependency 'rack'
|
|
19
|
+
s.add_dependency 'escape'
|
|
19
20
|
s.add_development_dependency 'rspec'
|
|
20
21
|
s.add_development_dependency 'rack-test'
|
|
21
22
|
s.add_development_dependency 'fakeweb'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe Mixpanel do
|
|
4
|
-
before do
|
|
4
|
+
before(:each) do
|
|
5
5
|
@mixpanel = Mixpanel.new(MIX_PANEL_TOKEN, @env = {"REMOTE_ADDR" => "127.0.0.1"})
|
|
6
6
|
end
|
|
7
7
|
|
|
@@ -82,4 +82,32 @@ describe Mixpanel do
|
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
|
+
|
|
86
|
+
context "Accessing Mixpanel asynchronously" do
|
|
87
|
+
it "should open a subprocess successfully" do
|
|
88
|
+
w = Mixpanel.worker
|
|
89
|
+
w.should == Mixpanel.worker
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should be able to write lines to the worker" do
|
|
93
|
+
w = Mixpanel.worker
|
|
94
|
+
|
|
95
|
+
#On most systems this will exceed the pipe buffer size
|
|
96
|
+
8.times do
|
|
97
|
+
9000.times do
|
|
98
|
+
w.write("\n")
|
|
99
|
+
end
|
|
100
|
+
sleep 0.1
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should dispose of a worker" do
|
|
105
|
+
w = Mixpanel.worker
|
|
106
|
+
Mixpanel.dispose_worker(w)
|
|
107
|
+
|
|
108
|
+
w.closed?.should == true
|
|
109
|
+
w2 = Mixpanel.worker
|
|
110
|
+
w2.should_not == w
|
|
111
|
+
end
|
|
112
|
+
end
|
|
85
113
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mixpanel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash:
|
|
4
|
+
hash: 63
|
|
5
5
|
prerelease: false
|
|
6
6
|
segments:
|
|
7
7
|
- 0
|
|
8
|
-
-
|
|
8
|
+
- 8
|
|
9
9
|
- 0
|
|
10
|
-
version: 0.
|
|
10
|
+
version: 0.8.0
|
|
11
11
|
platform: ruby
|
|
12
12
|
authors:
|
|
13
13
|
- Alvaro Gil
|
|
@@ -15,7 +15,7 @@ autorequire:
|
|
|
15
15
|
bindir: bin
|
|
16
16
|
cert_chain: []
|
|
17
17
|
|
|
18
|
-
date:
|
|
18
|
+
date: 2011-01-25 00:00:00 -02:00
|
|
19
19
|
default_executable:
|
|
20
20
|
dependencies:
|
|
21
21
|
- !ruby/object:Gem::Dependency
|
|
@@ -47,7 +47,7 @@ dependencies:
|
|
|
47
47
|
type: :runtime
|
|
48
48
|
version_requirements: *id002
|
|
49
49
|
- !ruby/object:Gem::Dependency
|
|
50
|
-
name:
|
|
50
|
+
name: escape
|
|
51
51
|
prerelease: false
|
|
52
52
|
requirement: &id003 !ruby/object:Gem::Requirement
|
|
53
53
|
none: false
|
|
@@ -58,10 +58,10 @@ dependencies:
|
|
|
58
58
|
segments:
|
|
59
59
|
- 0
|
|
60
60
|
version: "0"
|
|
61
|
-
type: :
|
|
61
|
+
type: :runtime
|
|
62
62
|
version_requirements: *id003
|
|
63
63
|
- !ruby/object:Gem::Dependency
|
|
64
|
-
name:
|
|
64
|
+
name: rspec
|
|
65
65
|
prerelease: false
|
|
66
66
|
requirement: &id004 !ruby/object:Gem::Requirement
|
|
67
67
|
none: false
|
|
@@ -75,7 +75,7 @@ dependencies:
|
|
|
75
75
|
type: :development
|
|
76
76
|
version_requirements: *id004
|
|
77
77
|
- !ruby/object:Gem::Dependency
|
|
78
|
-
name:
|
|
78
|
+
name: rack-test
|
|
79
79
|
prerelease: false
|
|
80
80
|
requirement: &id005 !ruby/object:Gem::Requirement
|
|
81
81
|
none: false
|
|
@@ -89,7 +89,7 @@ dependencies:
|
|
|
89
89
|
type: :development
|
|
90
90
|
version_requirements: *id005
|
|
91
91
|
- !ruby/object:Gem::Dependency
|
|
92
|
-
name:
|
|
92
|
+
name: fakeweb
|
|
93
93
|
prerelease: false
|
|
94
94
|
requirement: &id006 !ruby/object:Gem::Requirement
|
|
95
95
|
none: false
|
|
@@ -102,6 +102,20 @@ dependencies:
|
|
|
102
102
|
version: "0"
|
|
103
103
|
type: :development
|
|
104
104
|
version_requirements: *id006
|
|
105
|
+
- !ruby/object:Gem::Dependency
|
|
106
|
+
name: nokogiri
|
|
107
|
+
prerelease: false
|
|
108
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
|
109
|
+
none: false
|
|
110
|
+
requirements:
|
|
111
|
+
- - ">="
|
|
112
|
+
- !ruby/object:Gem::Version
|
|
113
|
+
hash: 3
|
|
114
|
+
segments:
|
|
115
|
+
- 0
|
|
116
|
+
version: "0"
|
|
117
|
+
type: :development
|
|
118
|
+
version_requirements: *id007
|
|
105
119
|
description: Simple lib to track events in Mixpanel service. It can be used in any rack based framework.
|
|
106
120
|
email: zevarito@gmail.com
|
|
107
121
|
executables: []
|
|
@@ -121,6 +135,7 @@ files:
|
|
|
121
135
|
- spec/support/rack_apps.rb
|
|
122
136
|
- lib/mixpanel/mixpanel.rb
|
|
123
137
|
- lib/mixpanel/mixpanel_middleware.rb
|
|
138
|
+
- lib/mixpanel/mixpanel_subprocess.rb
|
|
124
139
|
- lib/mixpanel.rb
|
|
125
140
|
has_rdoc: true
|
|
126
141
|
homepage: http://cuboxsa.com
|