mixpanel-ruby 2.2.1 → 2.2.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51973ff0dbb444c35e3c971e1c2bea676b511795f50afa1972319ba2f35acda5
4
- data.tar.gz: 20b2c55a765a2bbfe0cdf03a94767d8ab1415abf4a7f1ac8d82b53f53c34dc38
3
+ metadata.gz: 528783e14b23218c322ec9513d2a7ba3112a41b54b53d9dd0cd2abeed7630b54
4
+ data.tar.gz: 5aded14afb1a1fca08fe5a02e386347edb665edc871677adf849a47af12658a2
5
5
  SHA512:
6
- metadata.gz: b7c80d420403a94fc63f5df839110bdb05033f208748cebb86aea6d92dc94d9edd8456978820214725040b0abe1eb58276185c4d15d352850d9bf07df1cd0254
7
- data.tar.gz: aad34a8e509085e21c89d274d0ac3a5caabaf89133891ed93c8a2b836f409e2797668a4f057d4f90a119ac792964b5fa62be0cb35bfa45db74bfe3fda46aaa47
6
+ metadata.gz: 58cdf71f0d7b8dfdcd85919fa6105694c24732a20b5f637b9e907d6fa5cb7d55c30e8f1ddd92d791708bf8425d0c886043bad13ccdf8dada91d8b2f6e328a235
7
+ data.tar.gz: 3aee5d979f1a9f27ef93a7acbf20acdf23341c653bf234b0a6fe55b619d73e57c5ef544b8e22e51b0ace0d958e48414c34c71274e18bf258b497416d48e69b0a
@@ -49,6 +49,9 @@ In particular, for Rails apps, the following projects are currently actively mai
49
49
 
50
50
  == Changes
51
51
 
52
+ == 2.2.2
53
+ * Add Group Analytics support with Mixpanel::Groups
54
+
52
55
  == 2.2.1
53
56
  * Fix buffer clearing on partially successful writes in BufferedConsumer.
54
57
 
@@ -58,9 +58,13 @@ module Mixpanel
58
58
  # they will be used instead of the default Mixpanel endpoints.
59
59
  # This can be useful for proxying, debugging, or if you prefer
60
60
  # not to use SSL for your events.
61
- def initialize(events_endpoint=nil, update_endpoint=nil, import_endpoint=nil)
61
+ def initialize(events_endpoint=nil,
62
+ update_endpoint=nil,
63
+ groups_endpoint=nil,
64
+ import_endpoint=nil)
62
65
  @events_endpoint = events_endpoint || 'https://api.mixpanel.com/track'
63
66
  @update_endpoint = update_endpoint || 'https://api.mixpanel.com/engage'
67
+ @groups_endpoint = groups_endpoint || 'https://api.mixpanel.com/groups'
64
68
  @import_endpoint = import_endpoint || 'https://api.mixpanel.com/import'
65
69
  end
66
70
 
@@ -75,6 +79,7 @@ module Mixpanel
75
79
  endpoint = {
76
80
  :event => @events_endpoint,
77
81
  :profile_update => @update_endpoint,
82
+ :group_update => @groups_endpoint,
78
83
  :import => @import_endpoint,
79
84
  }[type]
80
85
 
@@ -0,0 +1,201 @@
1
+ require 'date'
2
+ require 'json'
3
+ require 'time'
4
+
5
+ require 'mixpanel-ruby/consumer'
6
+ require 'mixpanel-ruby/error'
7
+
8
+ module Mixpanel
9
+
10
+ # Handles formatting Mixpanel group updates and
11
+ # sending them to the consumer. You will rarely need
12
+ # to instantiate this class directly- to send
13
+ # group updates, use Mixpanel::Tracker#groups
14
+ #
15
+ # tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
16
+ # tracker.groups.set(...) or .set_once(..), or .delete(...) etc.
17
+ class Groups
18
+
19
+ # You likely won't need to instantiate instances of Mixpanel::Groups
20
+ # directly. The best way to get an instance of Mixpanel::Groups is
21
+ #
22
+ # tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
23
+ # tracker.groups # An instance of Mixpanel::Groups
24
+ #
25
+ def initialize(token, error_handler=nil, &block)
26
+ @token = token
27
+ @error_handler = error_handler || ErrorHandler.new
28
+
29
+ if block
30
+ @sink = block
31
+ else
32
+ consumer = Consumer.new
33
+ @sink = consumer.method(:send!)
34
+ end
35
+ end
36
+
37
+ # Sets properties on a group record. Takes a Hash with string
38
+ # keys, and values that are strings, numbers, booleans, or
39
+ # DateTimes
40
+ #
41
+ # tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
42
+ # # Sets properties on group with id "1234"
43
+ # tracker.groups.set("GROUP KEY", "1234", {
44
+ # 'company' => 'Acme',
45
+ # 'plan' => 'Premium',
46
+ # 'Sign-Up Date' => DateTime.now
47
+ # });
48
+ #
49
+ # If you provide an ip argument, \Mixpanel will use that
50
+ # ip address for geolocation (rather than the ip of your server)
51
+ def set(group_key, group_id, properties, ip=nil, optional_params={})
52
+ properties = fix_property_dates(properties)
53
+ message = {
54
+ '$group_key' => group_key,
55
+ '$group_id' => group_id,
56
+ '$set' => properties,
57
+ }.merge(optional_params)
58
+ message['$ip'] = ip if ip
59
+
60
+ update(message)
61
+ end
62
+
63
+ # set_once works just like #set, but will only change the
64
+ # value of properties if they are not already present
65
+ # in the group. That means you can call set_once many times
66
+ # without changing an original value.
67
+ #
68
+ # tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
69
+ # tracker.groups.set_once("GROUP KEY", "1234", {
70
+ # 'First Login Date': DateTime.now
71
+ # });
72
+ #
73
+ def set_once(group_key, group_id, properties, ip=nil, optional_params={})
74
+ properties = fix_property_dates(properties)
75
+ message = {
76
+ '$group_key' => group_key,
77
+ '$group_id' => group_id,
78
+ '$set_once' => properties,
79
+ }.merge(optional_params)
80
+ message['$ip'] = ip if ip
81
+
82
+ update(message)
83
+ end
84
+
85
+ # Removes a specific value in a list property
86
+ #
87
+ # tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
88
+ #
89
+ # # removes "socks" from the "Items purchased" list property
90
+ # # for the specified group
91
+ # tracker.groups.remove("GROUP KEY", "1234", { 'Items purchased' => 'socks' })
92
+ #
93
+ def remove(group_key, group_id, properties, ip=nil, optional_params={})
94
+ properties = fix_property_dates(properties)
95
+ message = {
96
+ '$group_key' => group_key,
97
+ '$group_id' => group_id,
98
+ '$remove' => properties,
99
+ }.merge(optional_params)
100
+ message['$ip'] = ip if ip
101
+
102
+ update(message)
103
+ end
104
+
105
+ # Set union on list valued properties.
106
+ # Associates a list containing all elements of a given list,
107
+ # and all elements currently in a list associated with the given
108
+ # property. After a union, every element in the list associated
109
+ # with a property will be unique.
110
+ #
111
+ # tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
112
+ # tracker.groups.union("GROUP KEY", "1234", {
113
+ # 'Levels Completed' => ['Suffragette City']
114
+ # });
115
+ #
116
+ def union(group_key, group_id, properties, ip=nil, optional_params={})
117
+ properties = fix_property_dates(properties)
118
+ message = {
119
+ '$group_key' => group_key,
120
+ '$group_id' => group_id,
121
+ '$union' => properties,
122
+ }.merge(optional_params)
123
+ message['$ip'] = ip if ip
124
+
125
+ update(message)
126
+ end
127
+
128
+ # Removes properties and their values from a group.
129
+ #
130
+ # tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
131
+ #
132
+ # # removes a single property and its value from a group
133
+ # tracker.groups.unset("GROUP KEY", "1234", "Overdue Since")
134
+ #
135
+ # # removes multiple properties and their values from a group
136
+ # tracker.groups.unset("GROUP KEY",
137
+ # "1234",
138
+ # ["Overdue Since", "Paid Date"])
139
+ #
140
+ def unset(group_key, group_id, properties, ip=nil, optional_params={})
141
+ properties = [properties] unless properties.is_a?(Array)
142
+ message = {
143
+ '$group_key' => group_key,
144
+ '$group_id' => group_id,
145
+ '$unset' => properties,
146
+ }.merge(optional_params)
147
+ message['$ip'] = ip if ip
148
+
149
+ update(message)
150
+ end
151
+
152
+ # Permanently delete a group from \Mixpanel groups analytics (all group
153
+ # properties on events stay)
154
+ def delete_group(group_key, group_id, optional_params={})
155
+ update({
156
+ '$group_key' => group_key,
157
+ '$group_id' => group_id,
158
+ '$delete' => '',
159
+ }.merge(optional_params))
160
+ end
161
+
162
+ # Send a generic update to \Mixpanel groups analytics.
163
+ # Caller is responsible for formatting the update message, as
164
+ # documented in the \Mixpanel HTTP specification, and passing
165
+ # the message as a dict to #update. This
166
+ # method might be useful if you want to use very new
167
+ # or experimental features of groups analytics from Ruby
168
+ # The \Mixpanel HTTP tracking API is documented at
169
+ # https://mixpanel.com/help/reference/http
170
+ def update(message)
171
+ data = {
172
+ '$token' => @token,
173
+ '$time' => ((Time.now.to_f) * 1000.0).to_i,
174
+ }.merge(message)
175
+
176
+ message = {'data' => data}
177
+
178
+ ret = true
179
+ begin
180
+ @sink.call(:group_update, message.to_json)
181
+ rescue MixpanelError => e
182
+ @error_handler.handle(e)
183
+ ret = false
184
+ end
185
+
186
+ ret
187
+ end
188
+
189
+ private
190
+
191
+ def fix_property_dates(properties)
192
+ properties.inject({}) do |ret, (key, value)|
193
+ value = value.respond_to?(:new_offset) ? value.new_offset('0') : value
194
+ value = value.respond_to?(:utc) ? value.utc : value # Handle ActiveSupport::TimeWithZone
195
+
196
+ ret[key] = value.respond_to?(:strftime) ? value.strftime('%Y-%m-%dT%H:%M:%S') : value
197
+ ret
198
+ end
199
+ end
200
+ end
201
+ end
@@ -1,5 +1,6 @@
1
1
  require 'mixpanel-ruby/events.rb'
2
2
  require 'mixpanel-ruby/people.rb'
3
+ require 'mixpanel-ruby/groups.rb'
3
4
 
4
5
  module Mixpanel
5
6
  # Use Mixpanel::Tracker to track events and profile updates in your application.
@@ -13,16 +14,25 @@ module Mixpanel
13
14
  # tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
14
15
  # tracker.people.set(a_distinct_id, {properties})
15
16
  #
17
+ # To send groups updates, call
18
+ #
19
+ # tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
20
+ # tracker.groups.set(group_key, group_id, {properties})
21
+ #
16
22
  # You can find your project token in the settings dialog for your
17
23
  # project, inside of the Mixpanel web application.
18
24
  #
19
25
  # Mixpanel::Tracker is a subclass of Mixpanel::Events, and exposes
20
26
  # an instance of Mixpanel::People as Tracker#people
27
+ # and an instance of Mixpanel::Groups as Tracker#groups
21
28
  class Tracker < Events
22
29
  # An instance of Mixpanel::People. Use this to
23
30
  # send profile updates
24
31
  attr_reader :people
25
32
 
33
+ # An instance of Mixpanel::Groups. Use this to send groups updates
34
+ attr_reader :groups
35
+
26
36
  # Takes your Mixpanel project token, as a string.
27
37
  #
28
38
  # tracker = Mixpanel::Tracker.new(YOUR_MIXPANEL_TOKEN)
@@ -46,6 +56,7 @@ module Mixpanel
46
56
  super(token, error_handler, &block)
47
57
  @token = token
48
58
  @people = People.new(token, error_handler, &block)
59
+ @groups = Groups.new(token, error_handler, &block)
49
60
  end
50
61
 
51
62
  # A call to #track is a report that an event has occurred. #track
@@ -1,3 +1,3 @@
1
1
  module Mixpanel
2
- VERSION = '2.2.1'
2
+ VERSION = '2.2.2'
3
3
  end
@@ -22,6 +22,13 @@ describe Mixpanel::Consumer do
22
22
  with(:body => {'data' => 'IlRFU1QgRVZFTlQgTUVTU0FHRSI=', 'verbose' => '1' })
23
23
  end
24
24
 
25
+ it 'should send a request to api.mixpanel.com/groups on groups updates' do
26
+ stub_request(:any, 'https://api.mixpanel.com/groups').to_return({:body => '{"status": 1, "error": null}'})
27
+ subject.send!(:group_update, {'data' => 'TEST EVENT MESSAGE'}.to_json)
28
+ expect(WebMock).to have_requested(:post, 'https://api.mixpanel.com/groups').
29
+ with(:body => {'data' => 'IlRFU1QgRVZFTlQgTUVTU0FHRSI=', 'verbose' => '1' })
30
+ end
31
+
25
32
  it 'should send a request to api.mixpanel.com/import on event imports' do
26
33
  stub_request(:any, 'https://api.mixpanel.com/import').to_return({:body => '{"status": 1, "error": null}'})
27
34
  subject.send!(:import, {'data' => 'TEST EVENT MESSAGE', 'api_key' => 'API_KEY','verbose' => '1' }.to_json)
@@ -0,0 +1,159 @@
1
+ require 'spec_helper'
2
+ require 'active_support/time'
3
+
4
+ require 'mixpanel-ruby/groups'
5
+
6
+ describe Mixpanel::Groups do
7
+ before(:each) do
8
+ @time_now = Time.parse('Jun 6 1972, 16:23:04')
9
+ allow(Time).to receive(:now).and_return(@time_now)
10
+
11
+ @log = []
12
+ @groups = Mixpanel::Groups.new('TEST TOKEN') do |type, message|
13
+ @log << [type, JSON.load(message)]
14
+ end
15
+ end
16
+
17
+ it 'should send a well formed groups/set message' do
18
+ @groups.set("TEST GROUP KEY", "TEST GROUP ID", {
19
+ '$groupname' => 'Mixpanel',
20
+ '$grouprevenue' => 200
21
+ })
22
+ expect(@log).to eq([[:group_update, 'data' => {
23
+ '$token' => 'TEST TOKEN',
24
+ '$group_key' => 'TEST GROUP KEY',
25
+ '$group_id' => 'TEST GROUP ID',
26
+ '$time' => @time_now.to_i * 1000,
27
+ '$set' => {
28
+ '$groupname' => 'Mixpanel',
29
+ '$grouprevenue' => 200
30
+ }
31
+ }]])
32
+ end
33
+
34
+ it 'should properly cast dates' do
35
+ @groups.set("TEST GROUP KEY", "TEST GROUP ID", {
36
+ 'created_at' => DateTime.new(2013, 1, 2, 3, 4, 5)
37
+ })
38
+ expect(@log).to eq([[:group_update, 'data' => {
39
+ '$token' => 'TEST TOKEN',
40
+ '$group_key' => 'TEST GROUP KEY',
41
+ '$group_id' => 'TEST GROUP ID',
42
+ '$time' => @time_now.to_i * 1000,
43
+ '$set' => {
44
+ 'created_at' => '2013-01-02T03:04:05'
45
+ }
46
+ }]])
47
+ end
48
+
49
+ it 'should convert offset datetimes to UTC' do
50
+ @groups.set("TEST GROUP KEY", "TEST GROUP ID", {
51
+ 'created_at' => DateTime.new(2013, 1, 1, 18, 4, 5, '-8')
52
+ })
53
+ expect(@log).to eq([[:group_update, 'data' => {
54
+ '$token' => 'TEST TOKEN',
55
+ '$group_key' => 'TEST GROUP KEY',
56
+ '$group_id' => 'TEST GROUP ID',
57
+ '$time' => @time_now.to_i * 1000,
58
+ '$set' => {
59
+ 'created_at' => '2013-01-02T02:04:05'
60
+ }
61
+ }]])
62
+ end
63
+
64
+ it 'should convert offset ActiveSupport::TimeWithZone objects to UTC' do
65
+ Time.zone = 'Pacific Time (US & Canada)'
66
+ @groups.set("TEST GROUP KEY", "TEST GROUP ID", {
67
+ 'created_at' => Time.zone.local(2013, 1, 1, 18, 4, 5)
68
+ })
69
+ expect(@log).to eq([[:group_update, 'data' => {
70
+ '$token' => 'TEST TOKEN',
71
+ '$group_key' => 'TEST GROUP KEY',
72
+ '$group_id' => 'TEST GROUP ID',
73
+ '$time' => @time_now.to_i * 1000,
74
+ '$set' => {
75
+ 'created_at' => '2013-01-02T02:04:05'
76
+ }
77
+ }]])
78
+ end
79
+
80
+ it 'should send a well formed groups/set_once message' do
81
+ @groups.set_once("TEST GROUP KEY", "TEST GROUP ID", {
82
+ '$groupname' => 'Mixpanel',
83
+ '$grouprevenue' => 200
84
+ })
85
+ expect(@log).to eq([[:group_update, 'data' => {
86
+ '$token' => 'TEST TOKEN',
87
+ '$group_key' => 'TEST GROUP KEY',
88
+ '$group_id' => 'TEST GROUP ID',
89
+ '$time' => @time_now.to_i * 1000,
90
+ '$set_once' => {
91
+ '$groupname' => 'Mixpanel',
92
+ '$grouprevenue' => 200
93
+ }
94
+ }]])
95
+ end
96
+
97
+ it 'should send a well formed groups/remove message' do
98
+ @groups.remove("TEST GROUP KEY", "TEST GROUP ID", {
99
+ 'Albums' => 'Diamond Dogs'
100
+ })
101
+ expect(@log).to eq([[:group_update, 'data' => {
102
+ '$token' => 'TEST TOKEN',
103
+ '$group_key' => 'TEST GROUP KEY',
104
+ '$group_id' => 'TEST GROUP ID',
105
+ '$time' => @time_now.to_i * 1000,
106
+ '$remove' => {
107
+ 'Albums' => 'Diamond Dogs'
108
+ }
109
+ }]])
110
+ end
111
+
112
+ it 'should send a well formed groups/union message' do
113
+ @groups.union("TEST GROUP KEY", "TEST GROUP ID", {
114
+ 'Albums' => ['Diamond Dogs']
115
+ })
116
+ expect(@log).to eq([[:group_update, 'data' => {
117
+ '$token' => 'TEST TOKEN',
118
+ '$group_key' => 'TEST GROUP KEY',
119
+ '$group_id' => 'TEST GROUP ID',
120
+ '$time' => @time_now.to_i * 1000,
121
+ '$union' => {
122
+ 'Albums' => ['Diamond Dogs']
123
+ }
124
+ }]])
125
+ end
126
+
127
+ it 'should send a well formed unset message' do
128
+ @groups.unset("TEST GROUP KEY", "TEST GROUP ID", 'Albums')
129
+ expect(@log).to eq([[:group_update, 'data' => {
130
+ '$token' => 'TEST TOKEN',
131
+ '$group_key' => 'TEST GROUP KEY',
132
+ '$group_id' => 'TEST GROUP ID',
133
+ '$time' => @time_now.to_i * 1000,
134
+ '$unset' => ['Albums']
135
+ }]])
136
+ end
137
+
138
+ it 'should send a well formed unset message with multiple properties' do
139
+ @groups.unset("TEST GROUP KEY", "TEST GROUP ID", ['Albums', 'Vinyls'])
140
+ expect(@log).to eq([[:group_update, 'data' => {
141
+ '$token' => 'TEST TOKEN',
142
+ '$group_key' => 'TEST GROUP KEY',
143
+ '$group_id' => 'TEST GROUP ID',
144
+ '$time' => @time_now.to_i * 1000,
145
+ '$unset' => ['Albums', 'Vinyls']
146
+ }]])
147
+ end
148
+
149
+ it 'should send a well formed groups/delete message' do
150
+ @groups.delete_group("TEST GROUP KEY", "TEST GROUP ID")
151
+ expect(@log).to eq([[:group_update, 'data' => {
152
+ '$token' => 'TEST TOKEN',
153
+ '$group_key' => 'TEST GROUP KEY',
154
+ '$group_id' => 'TEST GROUP ID',
155
+ '$time' => @time_now.to_i * 1000,
156
+ '$delete' => ''
157
+ }]])
158
+ end
159
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixpanel-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mixpanel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-04 00:00:00.000000000 Z
11
+ date: 2019-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -86,6 +86,7 @@ files:
86
86
  - lib/mixpanel-ruby/consumer.rb
87
87
  - lib/mixpanel-ruby/error.rb
88
88
  - lib/mixpanel-ruby/events.rb
89
+ - lib/mixpanel-ruby/groups.rb
89
90
  - lib/mixpanel-ruby/people.rb
90
91
  - lib/mixpanel-ruby/tracker.rb
91
92
  - lib/mixpanel-ruby/version.rb
@@ -93,6 +94,7 @@ files:
93
94
  - spec/mixpanel-ruby/consumer_spec.rb
94
95
  - spec/mixpanel-ruby/error_spec.rb
95
96
  - spec/mixpanel-ruby/events_spec.rb
97
+ - spec/mixpanel-ruby/groups_spec.rb
96
98
  - spec/mixpanel-ruby/people_spec.rb
97
99
  - spec/mixpanel-ruby/tracker_spec.rb
98
100
  - spec/spec_helper.rb