mixpanel-ruby-with-pixel-tracking 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/Gemfile +2 -0
- data/LICENSE +190 -0
- data/Rakefile +13 -0
- data/Readme.rdoc +76 -0
- data/demo/faraday_consumer.rb +40 -0
- data/demo/out_of_process_consumer.rb +71 -0
- data/demo/simple_messages.rb +8 -0
- data/lib/mixpanel-ruby.rb +3 -0
- data/lib/mixpanel-ruby/consumer.rb +240 -0
- data/lib/mixpanel-ruby/events.rb +116 -0
- data/lib/mixpanel-ruby/people.rb +258 -0
- data/lib/mixpanel-ruby/tracker.rb +137 -0
- data/lib/mixpanel-ruby/version.rb +3 -0
- data/mixpanel-ruby.gemspec +17 -0
- data/spec/mixpanel-ruby/consumer_spec.rb +135 -0
- data/spec/mixpanel-ruby/events_spec.rb +72 -0
- data/spec/mixpanel-ruby/people_spec.rb +167 -0
- data/spec/mixpanel-ruby/tracker_spec.rb +135 -0
- data/spec/spec_helper.rb +15 -0
- metadata +106 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'lib/mixpanel-ruby/version.rb')
|
2
|
+
|
3
|
+
spec = Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'mixpanel-ruby-with-pixel-tracking'
|
5
|
+
spec.version = Mixpanel::VERSION
|
6
|
+
spec.files = Dir.glob(`git ls-files`.split("\n"))
|
7
|
+
spec.require_paths = ['lib']
|
8
|
+
spec.summary = 'Temporary fork of the Official Mixpanel tracking library'
|
9
|
+
spec.description = 'Enables pixel-based event tracking'
|
10
|
+
spec.authors = [ 'Mixpanel', 'Pius Uzamere' ]
|
11
|
+
spec.email = 'support@mixpanel.com'
|
12
|
+
spec.homepage = 'https://mixpanel.com/help/reference/ruby'
|
13
|
+
|
14
|
+
spec.add_development_dependency('rake')
|
15
|
+
spec.add_development_dependency('rspec')
|
16
|
+
spec.add_development_dependency('webmock')
|
17
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'webmock'
|
3
|
+
require 'base64'
|
4
|
+
require 'mixpanel-ruby/consumer'
|
5
|
+
|
6
|
+
describe Mixpanel::Consumer do
|
7
|
+
before { WebMock.reset! }
|
8
|
+
|
9
|
+
shared_examples_for 'consumer' do
|
10
|
+
it 'should send a request to api.mixpanel.com/track on events' do
|
11
|
+
stub_request(:any, 'https://api.mixpanel.com/track').to_return({:body => '{"status": 1, "error": null}'})
|
12
|
+
subject.send(:event, {'data' => 'TEST EVENT MESSAGE'}.to_json)
|
13
|
+
WebMock.should have_requested(:post, 'https://api.mixpanel.com/track').
|
14
|
+
with(:body => {'data' => 'IlRFU1QgRVZFTlQgTUVTU0FHRSI=', 'verbose' => '1' })
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should send a request to api.mixpanel.com/people on profile updates' do
|
18
|
+
stub_request(:any, 'https://api.mixpanel.com/engage').to_return({:body => '{"status": 1, "error": null}'})
|
19
|
+
subject.send(:profile_update, {'data' => 'TEST EVENT MESSAGE'}.to_json)
|
20
|
+
WebMock.should have_requested(:post, 'https://api.mixpanel.com/engage').
|
21
|
+
with(:body => {'data' => 'IlRFU1QgRVZFTlQgTUVTU0FHRSI=', 'verbose' => '1' })
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should send a request to api.mixpanel.com/import on event imports' do
|
25
|
+
stub_request(:any, 'https://api.mixpanel.com/import').to_return({:body => '{"status": 1, "error": null}'})
|
26
|
+
subject.send(:import, {'data' => 'TEST EVENT MESSAGE', 'api_key' => 'API_KEY','verbose' => '1' }.to_json)
|
27
|
+
WebMock.should have_requested(:post, 'https://api.mixpanel.com/import').
|
28
|
+
with(:body => {'data' => 'IlRFU1QgRVZFTlQgTUVTU0FHRSI=', 'api_key' => 'API_KEY', 'verbose' => '1' })
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should encode long messages without newlines' do
|
32
|
+
stub_request(:any, 'https://api.mixpanel.com/track').to_return({:body => '{"status": 1, "error": null}'})
|
33
|
+
subject.send(:event, {'data' => 'BASE64-ENCODED VERSION OF BIN. THIS METHOD COMPLIES WITH RFC 2045. LINE FEEDS ARE ADDED TO EVERY 60 ENCODED CHARACTORS. IN RUBY 1.8 WE NEED TO JUST CALL ENCODE64 AND REMOVE THE LINE FEEDS, IN RUBY 1.9 WE CALL STRIC_ENCODED64 METHOD INSTEAD'}.to_json)
|
34
|
+
WebMock.should have_requested(:post, 'https://api.mixpanel.com/track').
|
35
|
+
with(:body => {'data' => 'IkJBU0U2NC1FTkNPREVEIFZFUlNJT04gT0YgQklOLiBUSElTIE1FVEhPRCBDT01QTElFUyBXSVRIIFJGQyAyMDQ1LiBMSU5FIEZFRURTIEFSRSBBRERFRCBUTyBFVkVSWSA2MCBFTkNPREVEIENIQVJBQ1RPUlMuIElOIFJVQlkgMS44IFdFIE5FRUQgVE8gSlVTVCBDQUxMIEVOQ09ERTY0IEFORCBSRU1PVkUgVEhFIExJTkUgRkVFRFMsIElOIFJVQlkgMS45IFdFIENBTEwgU1RSSUNfRU5DT0RFRDY0IE1FVEhPRCBJTlNURUFEIg==', 'verbose' => '1'})
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should provide thorough information in case mixpanel fails' do
|
39
|
+
stub_request(:any, 'https://api.mixpanel.com/track').to_return({:status => 401, :body => "nutcakes"})
|
40
|
+
expect { subject.send(:event, {'data' => 'TEST EVENT MESSAGE'}.to_json) }.to raise_exception('Could not write to Mixpanel, server responded with 401 returning: \'nutcakes\'')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'raw consumer' do
|
45
|
+
it_behaves_like 'consumer'
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'custom request consumer' do
|
49
|
+
subject do
|
50
|
+
ret = Mixpanel::Consumer.new
|
51
|
+
class << ret
|
52
|
+
attr_reader :called
|
53
|
+
def request(*args)
|
54
|
+
@called = true
|
55
|
+
super(*args)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
ret
|
60
|
+
end
|
61
|
+
|
62
|
+
after(:each) do
|
63
|
+
subject.called.should be_true
|
64
|
+
end
|
65
|
+
|
66
|
+
it_behaves_like 'consumer'
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
describe Mixpanel::BufferedConsumer do
|
72
|
+
let(:max_length) { 10 }
|
73
|
+
before { WebMock.reset! }
|
74
|
+
|
75
|
+
context 'Default BufferedConsumer' do
|
76
|
+
subject { Mixpanel::BufferedConsumer.new(nil, nil, nil, max_length) }
|
77
|
+
|
78
|
+
it 'should not send a request for a single message until flush is called' do
|
79
|
+
stub_request(:any, 'https://api.mixpanel.com/track').to_return({:body => '{"status": 1, "error": null}'})
|
80
|
+
subject.send(:event, {'data' => 'TEST EVENT 1'}.to_json)
|
81
|
+
WebMock.should have_not_requested(:post, 'https://api.mixpanel.com/track')
|
82
|
+
|
83
|
+
subject.flush()
|
84
|
+
WebMock.should have_requested(:post, 'https://api.mixpanel.com/track').
|
85
|
+
with(:body => {'data' => 'WyJURVNUIEVWRU5UIDEiXQ==', 'verbose' => '1' })
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should send one message when max_length events are tracked' do
|
89
|
+
stub_request(:any, 'https://api.mixpanel.com/track').to_return({:body => '{"status": 1, "error": null}'})
|
90
|
+
|
91
|
+
max_length.times do |i|
|
92
|
+
subject.send(:event, {'data' => "x #{i}"}.to_json)
|
93
|
+
end
|
94
|
+
|
95
|
+
WebMock.should have_requested(:post, 'https://api.mixpanel.com/track').
|
96
|
+
with(:body => {'data' => 'WyJ4IDAiLCJ4IDEiLCJ4IDIiLCJ4IDMiLCJ4IDQiLCJ4IDUiLCJ4IDYiLCJ4IDciLCJ4IDgiLCJ4IDkiXQ==', 'verbose' => '1' })
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should send one message per api key on import' do
|
100
|
+
stub_request(:any, 'https://api.mixpanel.com/import').to_return({:body => '{"status": 1, "error": null}'})
|
101
|
+
subject.send(:import, {'data' => 'TEST EVENT 1', 'api_key' => 'KEY 1'}.to_json)
|
102
|
+
subject.send(:import, {'data' => 'TEST EVENT 1', 'api_key' => 'KEY 2'}.to_json)
|
103
|
+
subject.send(:import, {'data' => 'TEST EVENT 2', 'api_key' => 'KEY 1'}.to_json)
|
104
|
+
subject.send(:import, {'data' => 'TEST EVENT 2', 'api_key' => 'KEY 2'}.to_json)
|
105
|
+
subject.flush
|
106
|
+
|
107
|
+
WebMock.should have_requested(:post, 'https://api.mixpanel.com/import').
|
108
|
+
with(:body => {'data' => 'IlRFU1QgRVZFTlQgMSI=', 'api_key' => 'KEY 1', 'verbose' => '1' })
|
109
|
+
|
110
|
+
WebMock.should have_requested(:post, 'https://api.mixpanel.com/import').
|
111
|
+
with(:body => {'data' => 'IlRFU1QgRVZFTlQgMSI=', 'api_key' => 'KEY 2', 'verbose' => '1' })
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'BufferedConsumer with block' do
|
116
|
+
let(:messages_seen) { [] }
|
117
|
+
subject do
|
118
|
+
Mixpanel::BufferedConsumer.new(nil, nil, nil, 3) do |type, message|
|
119
|
+
messages_seen << [type, message]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should call block instead of making default requests on flush' do
|
124
|
+
3.times do |i|
|
125
|
+
subject.send(:event, {'data' => "x #{i}"}.to_json)
|
126
|
+
end
|
127
|
+
|
128
|
+
expect(messages_seen).to match_array(
|
129
|
+
[[:event, "{\"data\":[\"x 0\",\"x 1\",\"x 2\"]}"]]
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mixpanel-ruby/events.rb'
|
3
|
+
require 'mixpanel-ruby/version.rb'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
describe Mixpanel::Events do
|
7
|
+
before(:each) do
|
8
|
+
@time_now = Time.parse('Jun 6 1972, 16:23:04')
|
9
|
+
Time.stub(:now).and_return(@time_now)
|
10
|
+
|
11
|
+
@log = []
|
12
|
+
@events = Mixpanel::Events.new('TEST TOKEN') do |type, message|
|
13
|
+
@log << [type, JSON.load(message)]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should send a well formed track/ message' do
|
18
|
+
@events.track('TEST ID', 'Test Event', {
|
19
|
+
'Circumstances' => 'During a test'
|
20
|
+
})
|
21
|
+
@log.should eq([[:event, 'data' => {
|
22
|
+
'event' => 'Test Event',
|
23
|
+
'properties' => {
|
24
|
+
'Circumstances' => 'During a test',
|
25
|
+
'distinct_id' => 'TEST ID',
|
26
|
+
'mp_lib' => 'ruby',
|
27
|
+
'$lib_version' => Mixpanel::VERSION,
|
28
|
+
'token' => 'TEST TOKEN',
|
29
|
+
'time' => @time_now.to_i
|
30
|
+
}
|
31
|
+
}]])
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
it 'should return a well formed pixel tracking uri if asked' do
|
36
|
+
@events.track('TEST ID', 'Test Event', {
|
37
|
+
'Circumstances' => 'During a test'
|
38
|
+
}, nil, true)
|
39
|
+
@log.should eq([[:event, 'data' => {
|
40
|
+
'event' => 'Test Event',
|
41
|
+
'properties' => {
|
42
|
+
'Circumstances' => 'During a test',
|
43
|
+
'distinct_id' => 'TEST ID',
|
44
|
+
'mp_lib' => 'ruby',
|
45
|
+
'$lib_version' => Mixpanel::VERSION,
|
46
|
+
'token' => 'TEST TOKEN',
|
47
|
+
'time' => @time_now.to_i
|
48
|
+
}
|
49
|
+
}]])
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
it 'should send a well formed import/ message' do
|
54
|
+
@events.import('API_KEY', 'TEST ID', 'Test Event', {
|
55
|
+
'Circumstances' => 'During a test'
|
56
|
+
})
|
57
|
+
@log.should eq([[:import, {
|
58
|
+
'api_key' => 'API_KEY',
|
59
|
+
'data' => {
|
60
|
+
'event' => 'Test Event',
|
61
|
+
'properties' => {
|
62
|
+
'Circumstances' => 'During a test',
|
63
|
+
'distinct_id' => 'TEST ID',
|
64
|
+
'mp_lib' => 'ruby',
|
65
|
+
'$lib_version' => Mixpanel::VERSION,
|
66
|
+
'token' => 'TEST TOKEN',
|
67
|
+
'time' => @time_now.to_i
|
68
|
+
}
|
69
|
+
}
|
70
|
+
} ]])
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mixpanel-ruby/people'
|
3
|
+
|
4
|
+
describe Mixpanel::People do
|
5
|
+
before(:each) do
|
6
|
+
@time_now = Time.parse('Jun 6 1972, 16:23:04')
|
7
|
+
Time.stub(:now).and_return(@time_now)
|
8
|
+
|
9
|
+
@log = []
|
10
|
+
@people = Mixpanel::People.new('TEST TOKEN') do |type, message|
|
11
|
+
@log << [type, JSON.load(message)]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should send a well formed engage/set message' do
|
16
|
+
@people.set("TEST ID", {
|
17
|
+
'$firstname' => 'David',
|
18
|
+
'$lastname' => 'Bowie',
|
19
|
+
})
|
20
|
+
@log.should eq([[:profile_update, 'data' => {
|
21
|
+
'$token' => 'TEST TOKEN',
|
22
|
+
'$distinct_id' => 'TEST ID',
|
23
|
+
'$time' => @time_now.to_i * 1000,
|
24
|
+
'$set' => {
|
25
|
+
'$firstname' => 'David',
|
26
|
+
'$lastname' => 'Bowie'
|
27
|
+
}
|
28
|
+
}]])
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should properly cast dates' do
|
32
|
+
@people.set("TEST ID", {
|
33
|
+
'created_at' => DateTime.new(2013, 1, 2, 3, 4, 5)
|
34
|
+
})
|
35
|
+
@log.should eq([[:profile_update, 'data' => {
|
36
|
+
'$token' => 'TEST TOKEN',
|
37
|
+
'$distinct_id' => 'TEST ID',
|
38
|
+
'$time' => @time_now.to_i * 1000,
|
39
|
+
'$set' => {
|
40
|
+
'created_at' => '2013-01-02T03:04:05'
|
41
|
+
}
|
42
|
+
}]])
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should send a well formed engage/set_once message' do
|
46
|
+
@people.set_once("TEST ID", {
|
47
|
+
'$firstname' => 'David',
|
48
|
+
'$lastname' => 'Bowie',
|
49
|
+
})
|
50
|
+
@log.should eq([[:profile_update, 'data' => {
|
51
|
+
'$token' => 'TEST TOKEN',
|
52
|
+
'$distinct_id' => 'TEST ID',
|
53
|
+
'$time' => @time_now.to_i * 1000,
|
54
|
+
'$set_once' => {
|
55
|
+
'$firstname' => 'David',
|
56
|
+
'$lastname' => 'Bowie'
|
57
|
+
}
|
58
|
+
}]])
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should send a well formed engage/add message' do
|
62
|
+
@people.increment("TEST ID", {'Albums Released' => 10})
|
63
|
+
@log.should eq([[:profile_update, 'data' => {
|
64
|
+
'$token' => 'TEST TOKEN',
|
65
|
+
'$distinct_id' => 'TEST ID',
|
66
|
+
'$time' => @time_now.to_i * 1000,
|
67
|
+
'$add' => {
|
68
|
+
'Albums Released' => 10
|
69
|
+
}
|
70
|
+
}]])
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should send an engage/add message with a value of 1' do
|
74
|
+
@people.plus_one("TEST ID", 'Albums Released')
|
75
|
+
@log.should eq([[:profile_update, 'data' => {
|
76
|
+
'$token' => 'TEST TOKEN',
|
77
|
+
'$distinct_id' => 'TEST ID',
|
78
|
+
'$time' => @time_now.to_i * 1000,
|
79
|
+
'$add' => {
|
80
|
+
'Albums Released' => 1
|
81
|
+
}
|
82
|
+
}]])
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should send a well formed engage/append message' do
|
86
|
+
@people.append("TEST ID", {'Albums' => 'Diamond Dogs'})
|
87
|
+
@log.should eq([[:profile_update, 'data' => {
|
88
|
+
'$token' => 'TEST TOKEN',
|
89
|
+
'$distinct_id' => 'TEST ID',
|
90
|
+
'$time' => @time_now.to_i * 1000,
|
91
|
+
'$append' => {
|
92
|
+
'Albums' => 'Diamond Dogs'
|
93
|
+
}
|
94
|
+
}]])
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should send a well formed engage/union message' do
|
98
|
+
@people.union("TEST ID", {'Albums' => ['Diamond Dogs']})
|
99
|
+
@log.should eq([[:profile_update, 'data' => {
|
100
|
+
'$token' => 'TEST TOKEN',
|
101
|
+
'$distinct_id' => 'TEST ID',
|
102
|
+
'$time' => @time_now.to_i * 1000,
|
103
|
+
'$union' => {
|
104
|
+
'Albums' => ['Diamond Dogs']
|
105
|
+
}
|
106
|
+
}]])
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should send a well formed unset message' do
|
110
|
+
@people.unset('TEST ID', 'Albums')
|
111
|
+
@log.should eq([[:profile_update, 'data' => {
|
112
|
+
'$token' => 'TEST TOKEN',
|
113
|
+
'$distinct_id' => 'TEST ID',
|
114
|
+
'$time' => @time_now.to_i * 1000,
|
115
|
+
'$unset' => ['Albums']
|
116
|
+
}]])
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should send a well formed unset message with multiple properties' do
|
120
|
+
@people.unset('TEST ID', ['Albums', 'Vinyls'])
|
121
|
+
@log.should eq([[:profile_update, 'data' => {
|
122
|
+
'$token' => 'TEST TOKEN',
|
123
|
+
'$distinct_id' => 'TEST ID',
|
124
|
+
'$time' => @time_now.to_i * 1000,
|
125
|
+
'$unset' => ['Albums', 'Vinyls']
|
126
|
+
}]])
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should send an engage/append with the right $transaction stuff' do
|
130
|
+
@people.track_charge("TEST ID", 25.42, {
|
131
|
+
'$time' => DateTime.new(1999,12,24,14, 02, 53),
|
132
|
+
'SKU' => '1234567'
|
133
|
+
})
|
134
|
+
@log.should eq([[:profile_update, 'data' => {
|
135
|
+
'$token' => 'TEST TOKEN',
|
136
|
+
'$distinct_id' => 'TEST ID',
|
137
|
+
'$time' => @time_now.to_i * 1000,
|
138
|
+
'$append' => {
|
139
|
+
'$transactions' => {
|
140
|
+
'$time' => '1999-12-24T14:02:53',
|
141
|
+
'SKU' => '1234567',
|
142
|
+
'$amount' => 25.42
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}]])
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should send a well formed engage/unset message for $transaction' do
|
149
|
+
@people.clear_charges("TEST ID")
|
150
|
+
@log.should eq([[:profile_update, 'data' => {
|
151
|
+
'$token' => 'TEST TOKEN',
|
152
|
+
'$distinct_id' => 'TEST ID',
|
153
|
+
'$time' => @time_now.to_i * 1000,
|
154
|
+
'$unset' => ['$transactions']
|
155
|
+
}]])
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should send a well formed engage/delete message' do
|
159
|
+
@people.delete_user("TEST ID")
|
160
|
+
@log.should eq([[:profile_update, 'data' => {
|
161
|
+
'$token' => 'TEST TOKEN',
|
162
|
+
'$distinct_id' => 'TEST ID',
|
163
|
+
'$time' => @time_now.to_i * 1000,
|
164
|
+
'$delete' => ''
|
165
|
+
}]])
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'mixpanel-ruby'
|
2
|
+
require 'base64'
|
3
|
+
require 'json'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
describe Mixpanel::Tracker do
|
7
|
+
before(:each) do
|
8
|
+
@time_now = Time.parse('Jun 6 1972, 16:23:04')
|
9
|
+
Time.stub(:now).and_return(@time_now)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should send an alias message to mixpanel no matter what the consumer is' do
|
13
|
+
WebMock.reset!
|
14
|
+
stub_request(:any, 'https://api.mixpanel.com/track').to_return({:body => '{"status": 1, "error": null}'})
|
15
|
+
mixpanel = Mixpanel::Tracker.new('TEST TOKEN') {|*args| }
|
16
|
+
mixpanel.alias('TEST ALIAS', 'TEST ID')
|
17
|
+
|
18
|
+
WebMock.should have_requested(:post, 'https://api.mixpanel.com/track').
|
19
|
+
with(:body => {:data => 'eyJldmVudCI6IiRjcmVhdGVfYWxpYXMiLCJwcm9wZXJ0aWVzIjp7ImRpc3RpbmN0X2lkIjoiVEVTVCBJRCIsImFsaWFzIjoiVEVTVCBBTElBUyIsInRva2VuIjoiVEVTVCBUT0tFTiJ9fQ==', 'verbose' => '1'})
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should send a request to the track api with the default consumer' do
|
23
|
+
WebMock.reset!
|
24
|
+
stub_request(:any, 'https://api.mixpanel.com/track').to_return({:body => '{"status": 1, "error": null}'})
|
25
|
+
stub_request(:any, 'https://api.mixpanel.com/engage').to_return({:body => '{"status": 1, "error": null}'})
|
26
|
+
mixpanel = Mixpanel::Tracker.new('TEST TOKEN')
|
27
|
+
|
28
|
+
mixpanel.track('TEST ID', 'TEST EVENT', {'Circumstances' => 'During test'})
|
29
|
+
|
30
|
+
body = nil
|
31
|
+
WebMock.should have_requested(:post, 'https://api.mixpanel.com/track').
|
32
|
+
with { |req| body = req.body }
|
33
|
+
|
34
|
+
message_urlencoded = body[/^data=(.*?)(?:&|$)/, 1]
|
35
|
+
message_json = Base64.strict_decode64(URI.unescape(message_urlencoded))
|
36
|
+
message = JSON.load(message_json)
|
37
|
+
message.should eq({
|
38
|
+
'event' => 'TEST EVENT',
|
39
|
+
'properties' => {
|
40
|
+
'Circumstances' => 'During test',
|
41
|
+
'distinct_id' => 'TEST ID',
|
42
|
+
'mp_lib' => 'ruby',
|
43
|
+
'$lib_version' => Mixpanel::VERSION,
|
44
|
+
'token' => 'TEST TOKEN',
|
45
|
+
'time' => @time_now.to_i
|
46
|
+
}
|
47
|
+
})
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should return a tracking uri if asked and not execute any requests' do
|
51
|
+
mixpanel = Mixpanel::Tracker.new('TEST TOKEN')
|
52
|
+
stub_request(:any, 'https://api.mixpanel.com/track').to_return({:body => '{"status": 1, "error": null}'})
|
53
|
+
|
54
|
+
|
55
|
+
a = mixpanel.track('TEST ID', 'TEST EVENT', {'Circumstances' => 'During test'}, nil, true)
|
56
|
+
a.should eq "https://api.mixpanel.com/track?data=eyJldmVudCI6IlRFU1QgRVZFTlQiLCJwcm9wZXJ0aWVzIjp7ImRpc3RpbmN0X2lkIjoiVEVTVCBJRCIsInRva2VuIjoiVEVTVCBUT0tFTiIsInRpbWUiOjc2NzIwOTg0LCJtcF9saWIiOiJydWJ5IiwiJGxpYl92ZXJzaW9uIjoiMS40LjAiLCJDaXJjdW1zdGFuY2VzIjoiRHVyaW5nIHRlc3QifX0%3D&verbose=1&img=1"
|
57
|
+
|
58
|
+
WebMock.should_not have_requested(:post, 'https://api.mixpanel.com/track')
|
59
|
+
WebMock.should_not have_requested(:get, 'https://api.mixpanel.com/track')
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
it 'should call a consumer block if one is given' do
|
81
|
+
messages = []
|
82
|
+
mixpanel = Mixpanel::Tracker.new('TEST TOKEN') do |type, message|
|
83
|
+
messages << [type, JSON.load(message)]
|
84
|
+
end
|
85
|
+
mixpanel.track('ID', 'Event')
|
86
|
+
mixpanel.import('API_KEY', 'ID', 'Import')
|
87
|
+
mixpanel.people.set('ID', {'k' => 'v'})
|
88
|
+
mixpanel.people.append('ID', {'k' => 'v'})
|
89
|
+
|
90
|
+
expect = [
|
91
|
+
[ :event, 'data' =>
|
92
|
+
{ 'event' => 'Event',
|
93
|
+
'properties' => {
|
94
|
+
'distinct_id' => 'ID',
|
95
|
+
'mp_lib' => 'ruby',
|
96
|
+
'$lib_version' => Mixpanel::VERSION,
|
97
|
+
'token' => 'TEST TOKEN',
|
98
|
+
'time' => @time_now.to_i
|
99
|
+
}
|
100
|
+
}
|
101
|
+
],
|
102
|
+
[ :import, {
|
103
|
+
'data' => {
|
104
|
+
'event' => 'Import',
|
105
|
+
'properties' => {
|
106
|
+
'distinct_id' => 'ID',
|
107
|
+
'mp_lib' => 'ruby',
|
108
|
+
'$lib_version' => Mixpanel::VERSION,
|
109
|
+
'token' => 'TEST TOKEN',
|
110
|
+
'time' => @time_now.to_i
|
111
|
+
}
|
112
|
+
},
|
113
|
+
'api_key' => 'API_KEY',
|
114
|
+
}
|
115
|
+
],
|
116
|
+
[ :profile_update, 'data' =>
|
117
|
+
{ '$token' => 'TEST TOKEN',
|
118
|
+
'$distinct_id' => 'ID',
|
119
|
+
'$time' => @time_now.to_i * 1000,
|
120
|
+
'$set' => {'k' => 'v'}
|
121
|
+
}
|
122
|
+
],
|
123
|
+
[ :profile_update, 'data' =>
|
124
|
+
{ '$token' => 'TEST TOKEN',
|
125
|
+
'$distinct_id' => 'ID',
|
126
|
+
'$time' => @time_now.to_i * 1000,
|
127
|
+
'$append' => {'k' => 'v'}
|
128
|
+
}
|
129
|
+
]
|
130
|
+
]
|
131
|
+
expect.zip(messages).each do |expect, found|
|
132
|
+
expect.should eq(found)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|