greenfinch-ruby 0.1.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.
@@ -0,0 +1,140 @@
1
+ require 'time'
2
+
3
+ require 'greenfinch-ruby/consumer'
4
+ require 'greenfinch-ruby/error'
5
+
6
+ module Greenfinch
7
+
8
+ # Handles formatting Greenfinch event tracking messages
9
+ # and sending them to the consumer. Greenfinch::Tracker
10
+ # is a subclass of this class, and the best way to
11
+ # track events is to instantiate a Greenfinch::Tracker
12
+ #
13
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN) # Has all of the methods of Greenfinch::Event
14
+ # tracker.track(...)
15
+ #
16
+ class Events
17
+
18
+ # You likely won't need to instantiate an instance of
19
+ # Greenfinch::Events directly. The best way to get an instance
20
+ # is to use Greenfinch::Tracker
21
+ #
22
+ # # tracker has all of the methods of Greenfinch::Events
23
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
24
+ #
25
+ def initialize(token, service_name, debug, error_handler=nil, &block)
26
+ @token = token
27
+ @service_name = service_name
28
+ @debug = debug
29
+ @error_handler = error_handler || ErrorHandler.new
30
+
31
+ if block
32
+ @sink = block
33
+ else
34
+ consumer = Consumer.new
35
+ @sink = consumer.method(:send!)
36
+ end
37
+ end
38
+
39
+ # Notes that an event has occurred, along with a distinct_id
40
+ # representing the source of that event (for example, a user id),
41
+ # an event name describing the event and a set of properties
42
+ # describing that event. Properties are provided as a Hash with
43
+ # string keys and strings, numbers or booleans as values.
44
+ #
45
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
46
+ #
47
+ # # Track that user "12345"'s credit card was declined
48
+ # tracker.track("12345", "Credit Card Declined")
49
+ #
50
+ # # Properties describe the circumstances of the event,
51
+ # # or aspects of the source or user associated with the event
52
+ # tracker.track("12345", "Welcome Email Sent", {
53
+ # 'Email Template' => 'Pretty Pink Welcome',
54
+ # 'User Sign-up Cohort' => 'July 2013'
55
+ # })
56
+ def track(distinct_id, event, properties={}, ip=nil)
57
+ properties = {
58
+ 'user_id' => distinct_id,
59
+ 'time' => Time.now.to_i,
60
+ 'mp_lib' => 'ruby',
61
+ '$lib_version' => Greenfinch::VERSION,
62
+ }.merge(properties)
63
+ properties['ip'] = ip if ip
64
+
65
+ data = {
66
+ 'event' => event,
67
+ 'prop' => properties,
68
+ }
69
+
70
+ message = {
71
+ 'data' => data,
72
+ 'jwt_token' => @token,
73
+ 'service_name' => @service_name,
74
+ 'debug' => @debug
75
+ }
76
+
77
+ ret = true
78
+
79
+ begin
80
+ @sink.call(:event, message.to_json)
81
+ rescue GreenfinchError => e
82
+ @error_handler.handle(e)
83
+ ret = false
84
+ end
85
+
86
+ ret
87
+ end
88
+
89
+ # Imports an event that has occurred in the past, along with a distinct_id
90
+ # representing the source of that event (for example, a user id),
91
+ # an event name describing the event and a set of properties
92
+ # describing that event. Properties are provided as a Hash with
93
+ # string keys and strings, numbers or booleans as values. By default,
94
+ # we pass the time of the method call as the time the event occured, if you
95
+ # wish to override this pass a timestamp in the properties hash.
96
+ #
97
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
98
+ #
99
+ # # Track that user "12345"'s credit card was declined
100
+ # tracker.import("API_KEY", "12345", "Credit Card Declined")
101
+ #
102
+ # # Properties describe the circumstances of the event,
103
+ # # or aspects of the source or user associated with the event
104
+ # tracker.import("API_KEY", "12345", "Welcome Email Sent", {
105
+ # 'Email Template' => 'Pretty Pink Welcome',
106
+ # 'User Sign-up Cohort' => 'July 2013',
107
+ # 'time' => 1369353600,
108
+ # })
109
+ def import(api_key, distinct_id, event, properties={}, ip=nil)
110
+ properties = {
111
+ 'distinct_id' => distinct_id,
112
+ 'token' => @token,
113
+ 'time' => Time.now.to_i,
114
+ 'mp_lib' => 'ruby',
115
+ '$lib_version' => Greenfinch::VERSION,
116
+ }.merge(properties)
117
+ properties['ip'] = ip if ip
118
+
119
+ data = {
120
+ 'event' => event,
121
+ 'properties' => properties,
122
+ }
123
+
124
+ message = {
125
+ 'data' => data,
126
+ 'api_key' => api_key,
127
+ }
128
+
129
+ ret = true
130
+ begin
131
+ @sink.call(:import, message.to_json)
132
+ rescue GreenfinchError => e
133
+ @error_handler.handle(e)
134
+ ret = false
135
+ end
136
+
137
+ ret
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,201 @@
1
+ require 'date'
2
+ require 'json'
3
+ require 'time'
4
+
5
+ require 'greenfinch-ruby/consumer'
6
+ require 'greenfinch-ruby/error'
7
+
8
+ module Greenfinch
9
+
10
+ # Handles formatting Greenfinch 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 Greenfinch::Tracker#groups
14
+ #
15
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_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 Greenfinch::Groups
20
+ # directly. The best way to get an instance of Greenfinch::Groups is
21
+ #
22
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
23
+ # tracker.groups # An instance of Greenfinch::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 = Greenfinch::Tracker.new(YOUR_GREENFINCH_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, \Greenfinch 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 = Greenfinch::Tracker.new(YOUR_GREENFINCH_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 = Greenfinch::Tracker.new(YOUR_GREENFINCH_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 = Greenfinch::Tracker.new(YOUR_GREENFINCH_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 = Greenfinch::Tracker.new(YOUR_GREENFINCH_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 \Greenfinch 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 \Greenfinch groups analytics.
163
+ # Caller is responsible for formatting the update message, as
164
+ # documented in the \Greenfinch 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 \Greenfinch HTTP tracking API is documented at
169
+ # https://greenfinch.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 GreenfinchError => 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
@@ -0,0 +1,254 @@
1
+ require 'date'
2
+ require 'json'
3
+ require 'time'
4
+
5
+ require 'greenfinch-ruby/consumer'
6
+ require 'greenfinch-ruby/error'
7
+
8
+ module Greenfinch
9
+
10
+ # Handles formatting Greenfinch profile updates and
11
+ # sending them to the consumer. You will rarely need
12
+ # to instantiate this class directly- to send
13
+ # profile updates, use Greenfinch::Tracker#people
14
+ #
15
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
16
+ # tracker.people.set(...) # Or .append(..), or track_charge(...) etc.
17
+ class People
18
+
19
+ # You likely won't need to instantiate instances of Greenfinch::People
20
+ # directly. The best way to get an instance of Greenfinch::People is
21
+ #
22
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
23
+ # tracker.people # An instance of Greenfinch::People
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 user record. Takes a Hash with string
38
+ # keys, and values that are strings, numbers, booleans, or
39
+ # DateTimes
40
+ #
41
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
42
+ # # Sets properties on profile with id "1234"
43
+ # tracker.people.set("1234", {
44
+ # 'company' => 'Acme',
45
+ # 'plan' => 'Premium',
46
+ # 'Sign-Up Date' => DateTime.now
47
+ # });
48
+ #
49
+ # If you provide an ip argument, \Greenfinch will use that
50
+ # ip address for geolocation (rather than the ip of your server)
51
+ def set(distinct_id, properties, ip=nil, optional_params={})
52
+ properties = fix_property_dates(properties)
53
+ message = {
54
+ '$distinct_id' => distinct_id,
55
+ '$set' => properties,
56
+ }.merge(optional_params)
57
+ message['$ip'] = ip if ip
58
+
59
+ update(message)
60
+ end
61
+
62
+ # set_once works just like #set, but will only change the
63
+ # value of properties if they are not already present
64
+ # in the profile. That means you can call set_once many times
65
+ # without changing an original value.
66
+ #
67
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
68
+ # tracker.people.set_once("12345", {
69
+ # 'First Login Date': DateTime.now
70
+ # });
71
+ #
72
+ def set_once(distinct_id, properties, ip=nil, optional_params={})
73
+ properties = fix_property_dates(properties)
74
+ message = {
75
+ '$distinct_id' => distinct_id,
76
+ '$set_once' => properties,
77
+ }.merge(optional_params)
78
+ message['$ip'] = ip if ip
79
+
80
+ update(message)
81
+ end
82
+
83
+ # Changes the value of properties by a numeric amount. Takes a
84
+ # hash with string keys and numeric properties. \Greenfinch will add
85
+ # the given amount to whatever value is currently assigned to the
86
+ # property. If no property exists with a given name, the value
87
+ # will be added to zero.
88
+ #
89
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
90
+ # tracker.people.increment("12345", {
91
+ # 'Coins Spent' => 7,
92
+ # 'Coins Earned' => -7, # Use a negative number to subtract
93
+ # });
94
+ #
95
+ def increment(distinct_id, properties, ip=nil, optional_params={})
96
+ properties = fix_property_dates(properties)
97
+ message = {
98
+ '$distinct_id' => distinct_id,
99
+ '$add' => properties,
100
+ }.merge(optional_params)
101
+ message['$ip'] = ip if ip
102
+
103
+ update(message)
104
+ end
105
+
106
+ # Convenience method- increases the value of a numeric property
107
+ # by one. Calling #plus_one(distinct_id, property_name) is the same as calling
108
+ # #increment(distinct_id, {property_name => 1})
109
+ #
110
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
111
+ # tracker.people.plus_one("12345", "Albums Released")
112
+ #
113
+ def plus_one(distinct_id, property_name, ip=nil, optional_params={})
114
+ increment(distinct_id, {property_name => 1}, ip, optional_params)
115
+ end
116
+
117
+ # Appends a values to the end of list-valued properties.
118
+ # If the given properties don't exist, a new list-valued
119
+ # property will be created.
120
+ #
121
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
122
+ # tracker.people.append("12345", {
123
+ # 'Login Dates' => DateTime.now,
124
+ # 'Alter Ego Names' => 'Ziggy Stardust'
125
+ # });
126
+ #
127
+ def append(distinct_id, properties, ip=nil, optional_params={})
128
+ properties = fix_property_dates(properties)
129
+ message = {
130
+ '$distinct_id' => distinct_id,
131
+ '$append' => properties,
132
+ }.merge(optional_params)
133
+ message['$ip'] = ip if ip
134
+
135
+ update(message)
136
+ end
137
+
138
+ # Set union on list valued properties.
139
+ # Associates a list containing all elements of a given list,
140
+ # and all elements currently in a list associated with the given
141
+ # property. After a union, every element in the list associated
142
+ # with a property will be unique.
143
+ #
144
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
145
+ # tracker.people.union("12345", {
146
+ # 'Levels Completed' => ['Suffragette City']
147
+ # });
148
+ #
149
+ def union(distinct_id, properties, ip=nil, optional_params={})
150
+ properties = fix_property_dates(properties)
151
+ message = {
152
+ '$distinct_id' => distinct_id,
153
+ '$union' => properties,
154
+ }.merge(optional_params)
155
+ message['$ip'] = ip if ip
156
+
157
+ update(message)
158
+ end
159
+
160
+ # Removes properties and their values from a profile.
161
+ #
162
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
163
+ #
164
+ # # removes a single property and its value from a profile
165
+ # tracker.people.unset("12345", "Overdue Since")
166
+ #
167
+ # # removes multiple properties and their values from a profile
168
+ # tracker.people.unset("12345", ["Overdue Since", "Paid Date"])
169
+ #
170
+ def unset(distinct_id, properties, ip=nil, optional_params={})
171
+ properties = [properties] unless properties.is_a?(Array)
172
+ message = {
173
+ '$distinct_id' => distinct_id,
174
+ '$unset' => properties,
175
+ }.merge(optional_params)
176
+ message['$ip'] = ip if ip
177
+
178
+ update(message)
179
+ end
180
+
181
+ # Records a payment to you to a profile. Charges recorded with
182
+ # #track_charge will appear in the \Greenfinch revenue report.
183
+ #
184
+ # tracker = Greenfinch::Tracker.new(YOUR_GREENFINCH_TOKEN)
185
+ #
186
+ # # records a charge of $25.32 from user 12345
187
+ # tracker.people.track_charge("12345", 25.32)
188
+ #
189
+ # # records a charge of $30.50 on the 2nd of January,
190
+ # greenfinch.people.track_charge("12345", 30.50, {
191
+ # '$time' => DateTime.parse("Jan 2 2013")
192
+ # })
193
+ #
194
+ def track_charge(distinct_id, amount, properties={}, ip=nil, optional_params={})
195
+ properties = fix_property_dates(properties)
196
+ charge_properties = properties.merge({'$amount' => amount})
197
+ append(distinct_id, {'$transactions' => charge_properties}, ip, optional_params)
198
+ end
199
+
200
+ # Clear all charges from a \Greenfinch people profile
201
+ def clear_charges(distinct_id, ip=nil, optional_params={})
202
+ unset(distinct_id, '$transactions', ip, optional_params)
203
+ end
204
+
205
+ # Permanently delete a profile from \Greenfinch people analytics
206
+ # To delete a user and ignore alias pass into optional params
207
+ # {"$ignore_alias"=>true}
208
+ def delete_user(distinct_id, optional_params={})
209
+ update({
210
+ '$distinct_id' => distinct_id,
211
+ '$delete' => '',
212
+ }.merge(optional_params))
213
+ end
214
+
215
+ # Send a generic update to \Greenfinch people analytics.
216
+ # Caller is responsible for formatting the update message, as
217
+ # documented in the \Greenfinch HTTP specification, and passing
218
+ # the message as a dict to #update. This
219
+ # method might be useful if you want to use very new
220
+ # or experimental features of people analytics from Ruby
221
+ # The \Greenfinch HTTP tracking API is documented at
222
+ # https://greenfinch.com/help/reference/http
223
+ def update(message)
224
+ data = {
225
+ '$token' => @token,
226
+ '$time' => ((Time.now.to_f) * 1000.0).to_i,
227
+ }.merge(message)
228
+
229
+ message = {'data' => data}
230
+
231
+ ret = true
232
+ begin
233
+ @sink.call(:profile_update, message.to_json)
234
+ rescue GreenfinchError => e
235
+ @error_handler.handle(e)
236
+ ret = false
237
+ end
238
+
239
+ ret
240
+ end
241
+
242
+ private
243
+
244
+ def fix_property_dates(properties)
245
+ properties.inject({}) do |ret, (key, value)|
246
+ value = value.respond_to?(:new_offset) ? value.new_offset('0') : value
247
+ value = value.respond_to?(:utc) ? value.utc : value # Handle ActiveSupport::TimeWithZone
248
+
249
+ ret[key] = value.respond_to?(:strftime) ? value.strftime('%Y-%m-%dT%H:%M:%S') : value
250
+ ret
251
+ end
252
+ end
253
+ end
254
+ end