analytics-ruby 2.2.5 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/analytics +22 -7
- data/lib/segment/analytics/client.rb +48 -272
- data/lib/segment/analytics/field_parser.rb +192 -0
- data/lib/segment/analytics/logging.rb +34 -7
- data/lib/segment/analytics/message_batch.rb +16 -2
- data/lib/segment/analytics/test_queue.rb +56 -0
- data/lib/segment/analytics/{request.rb → transport.rb} +17 -13
- data/lib/segment/analytics/utils.rb +2 -6
- data/lib/segment/analytics/version.rb +1 -1
- data/lib/segment/analytics/worker.rb +15 -7
- data/lib/segment/analytics.rb +4 -2
- metadata +11 -44
- data/Gemfile +0 -2
- data/History.md +0 -222
- data/Makefile +0 -17
- data/README.md +0 -84
- data/RELEASING.md +0 -9
- data/Rakefile +0 -23
- data/analytics-ruby.gemspec +0 -33
- data/codecov.yml +0 -2
- data/lib/segment/analytics/message.rb +0 -26
- data/spec/helpers/runscope_client.rb +0 -38
- data/spec/segment/analytics/backoff_policy_spec.rb +0 -92
- data/spec/segment/analytics/client_spec.rb +0 -328
- data/spec/segment/analytics/e2e_spec.rb +0 -48
- data/spec/segment/analytics/message_batch_spec.rb +0 -49
- data/spec/segment/analytics/message_spec.rb +0 -35
- data/spec/segment/analytics/request_spec.rb +0 -244
- data/spec/segment/analytics/response_spec.rb +0 -30
- data/spec/segment/analytics/worker_spec.rb +0 -110
- data/spec/segment/analytics_spec.rb +0 -120
- data/spec/spec_helper.rb +0 -128
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51f3f02afed05c9c8461b454e0a3067962a75b8efde0930db0098eebf4a62829
|
4
|
+
data.tar.gz: e7d486d6c6186535e9a005c4d950e6e29d2dd9937f9b6da625191d786f76846a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2ce09d450de65620eb99f9fae86aa5ce57709d53aa4a93c73c2d3db0b173afad532383a50fe4dcb953df72171b731a263ff7308aed79b646a25bc202c555e4d
|
7
|
+
data.tar.gz: b3625484ebca6117ae79fc42f03a595dff861b8c5ca02a6ef12ea9e933229c1479dad40b4db1d85e61ddf585c7c5a072d68e9cd21ced6b2482db338f45073707
|
data/bin/analytics
CHANGED
@@ -29,6 +29,7 @@ command :send do |c|
|
|
29
29
|
c.option '--userId=<userId>', String, 'the user id to send the event as'
|
30
30
|
c.option '--anonymousId=<anonymousId>', String, 'the anonymous user id to send the event as'
|
31
31
|
c.option '--context=<context>', 'additional context for the event (JSON-encoded)'
|
32
|
+
c.option '--integrations=<integrations>', 'additional integrations for the event (JSON-encoded)'
|
32
33
|
|
33
34
|
c.option '--event=<event>', String, 'the event name to send with the event'
|
34
35
|
c.option '--properties=<properties>', 'the event properties to send (JSON-encoded)'
|
@@ -38,6 +39,7 @@ command :send do |c|
|
|
38
39
|
c.option '--traits=<traits>', 'the identify/group traits to send (JSON-encoded)'
|
39
40
|
|
40
41
|
c.option '--groupId=<groupId>', String, 'the group id'
|
42
|
+
c.option '--previousId=<previousId>', String, 'the previous id'
|
41
43
|
|
42
44
|
c.action do |args, options|
|
43
45
|
Analytics = Segment::Analytics.new({
|
@@ -52,7 +54,8 @@ command :send do |c|
|
|
52
54
|
event: options.event,
|
53
55
|
anonymous_id: options.anonymousId,
|
54
56
|
properties: json_hash(options.properties),
|
55
|
-
context: json_hash(options.context)
|
57
|
+
context: json_hash(options.context),
|
58
|
+
integrations: json_hash(options.integrations)
|
56
59
|
})
|
57
60
|
when "page"
|
58
61
|
Analytics.page({
|
@@ -60,22 +63,25 @@ command :send do |c|
|
|
60
63
|
anonymous_id: options.anonymousId,
|
61
64
|
name: options.name,
|
62
65
|
properties: json_hash(options.properties),
|
63
|
-
context: json_hash(options.context)
|
66
|
+
context: json_hash(options.context),
|
67
|
+
integrations: json_hash(options.integrations)
|
64
68
|
})
|
65
69
|
when "screen"
|
66
70
|
Analytics.screen({
|
67
71
|
user_id: options.userId,
|
68
72
|
anonymous_id: options.anonymousId,
|
69
|
-
name:
|
70
|
-
|
71
|
-
|
73
|
+
name: options.name,
|
74
|
+
properties: json_hash(options.properties),
|
75
|
+
context: json_hash(options.context),
|
76
|
+
integrations: json_hash(options.integrations)
|
72
77
|
})
|
73
78
|
when "identify"
|
74
79
|
Analytics.identify({
|
75
80
|
user_id: options.userId,
|
76
81
|
anonymous_id: options.anonymousId,
|
77
82
|
traits: json_hash(options.traits),
|
78
|
-
context: json_hash(options.context)
|
83
|
+
context: json_hash(options.context),
|
84
|
+
integrations: json_hash(options.integrations)
|
79
85
|
})
|
80
86
|
when "group"
|
81
87
|
Analytics.group({
|
@@ -83,7 +89,16 @@ command :send do |c|
|
|
83
89
|
anonymous_id: options.anonymousId,
|
84
90
|
group_id: options.groupId,
|
85
91
|
traits: json_hash(options.traits),
|
86
|
-
context: json_hash(options.context)
|
92
|
+
context: json_hash(options.context),
|
93
|
+
integrations: json_hash(options.integrations)
|
94
|
+
})
|
95
|
+
when "alias"
|
96
|
+
Analytics.alias({
|
97
|
+
previous_id: options.previousId,
|
98
|
+
user_id: options.userId,
|
99
|
+
anonymous_id: options.anonymousId,
|
100
|
+
context: json_hash(options.context),
|
101
|
+
integrations: json_hash(options.integrations)
|
87
102
|
})
|
88
103
|
else
|
89
104
|
raise "Invalid Message Type #{options.type}"
|
@@ -21,11 +21,12 @@ module Segment
|
|
21
21
|
symbolize_keys!(opts)
|
22
22
|
|
23
23
|
@queue = Queue.new
|
24
|
+
@test = opts[:test]
|
24
25
|
@write_key = opts[:write_key]
|
25
26
|
@max_queue_size = opts[:max_queue_size] || Defaults::Queue::MAX_SIZE
|
26
|
-
@options = opts
|
27
27
|
@worker_mutex = Mutex.new
|
28
|
-
@worker = Worker.new(@queue, @write_key,
|
28
|
+
@worker = Worker.new(@queue, @write_key, opts)
|
29
|
+
@worker_thread = nil
|
29
30
|
|
30
31
|
check_write_key!
|
31
32
|
|
@@ -43,58 +44,32 @@ module Segment
|
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
47
|
+
# @!macro common_attrs
|
48
|
+
# @option attrs [String] :anonymous_id ID for a user when you don't know
|
49
|
+
# who they are yet. (optional but you must provide either an
|
50
|
+
# `anonymous_id` or `user_id`)
|
51
|
+
# @option attrs [Hash] :context ({})
|
52
|
+
# @option attrs [Hash] :integrations What integrations this event
|
53
|
+
# goes to (optional)
|
54
|
+
# @option attrs [String] :message_id ID that uniquely
|
55
|
+
# identifies a message across the API. (optional)
|
56
|
+
# @option attrs [Time] :timestamp When the event occurred (optional)
|
57
|
+
# @option attrs [String] :user_id The ID for this user in your database
|
58
|
+
# (optional but you must provide either an `anonymous_id` or `user_id`)
|
59
|
+
# @option attrs [Hash] :options Options such as user traits (optional)
|
60
|
+
|
46
61
|
# Tracks an event
|
47
62
|
#
|
48
63
|
# @see https://segment.com/docs/sources/server/ruby/#track
|
49
64
|
#
|
50
65
|
# @param [Hash] attrs
|
51
|
-
#
|
52
|
-
# who they are yet. (optional but you must provide either an
|
53
|
-
# `anonymous_id` or `user_id`)
|
54
|
-
# @option attrs [Hash] :context ({})
|
66
|
+
#
|
55
67
|
# @option attrs [String] :event Event name
|
56
|
-
# @option attrs [Hash] :integrations What integrations this event
|
57
|
-
# goes to (optional)
|
58
|
-
# @option attrs [Hash] :options Options such as user traits (optional)
|
59
68
|
# @option attrs [Hash] :properties Event properties (optional)
|
60
|
-
# @
|
61
|
-
# @option attrs [String] :user_id The ID for this user in your database
|
62
|
-
# (optional but you must provide either an `anonymous_id` or `user_id`)
|
63
|
-
# @option attrs [String] :message_id ID that uniquely
|
64
|
-
# identifies a message across the API. (optional)
|
69
|
+
# @macro common_attrs
|
65
70
|
def track(attrs)
|
66
71
|
symbolize_keys! attrs
|
67
|
-
|
68
|
-
|
69
|
-
event = attrs[:event]
|
70
|
-
properties = attrs[:properties] || {}
|
71
|
-
timestamp = attrs[:timestamp] || Time.new
|
72
|
-
context = attrs[:context] || {}
|
73
|
-
message_id = attrs[:message_id].to_s if attrs[:message_id]
|
74
|
-
|
75
|
-
check_timestamp! timestamp
|
76
|
-
|
77
|
-
if event.nil? || event.empty?
|
78
|
-
raise ArgumentError, 'Must supply event as a non-empty string'
|
79
|
-
end
|
80
|
-
|
81
|
-
raise ArgumentError, 'Properties must be a Hash' unless properties.is_a? Hash
|
82
|
-
isoify_dates! properties
|
83
|
-
|
84
|
-
add_context context
|
85
|
-
|
86
|
-
enqueue({
|
87
|
-
:event => event,
|
88
|
-
:userId => attrs[:user_id],
|
89
|
-
:anonymousId => attrs[:anonymous_id],
|
90
|
-
:context => context,
|
91
|
-
:options => attrs[:options],
|
92
|
-
:integrations => attrs[:integrations],
|
93
|
-
:properties => properties,
|
94
|
-
:messageId => message_id,
|
95
|
-
:timestamp => datetime_in_iso8601(timestamp),
|
96
|
-
:type => 'track'
|
97
|
-
})
|
72
|
+
enqueue(FieldParser.parse_for_track(attrs))
|
98
73
|
end
|
99
74
|
|
100
75
|
# Identifies a user
|
@@ -102,46 +77,12 @@ module Segment
|
|
102
77
|
# @see https://segment.com/docs/sources/server/ruby/#identify
|
103
78
|
#
|
104
79
|
# @param [Hash] attrs
|
105
|
-
#
|
106
|
-
# who they are yet. (optional but you must provide either an
|
107
|
-
# `anonymous_id` or `user_id`)
|
108
|
-
# @option attrs [Hash] :context ({})
|
109
|
-
# @option attrs [Hash] :integrations What integrations this event
|
110
|
-
# goes to (optional)
|
111
|
-
# @option attrs [Hash] :options Options such as user traits (optional)
|
112
|
-
# @option attrs [Time] :timestamp When the event occurred (optional)
|
80
|
+
#
|
113
81
|
# @option attrs [Hash] :traits User traits (optional)
|
114
|
-
# @
|
115
|
-
# (optional but you must provide either an `anonymous_id` or `user_id`)
|
116
|
-
# @option attrs [String] :message_id ID that uniquely identifies a
|
117
|
-
# message across the API. (optional)
|
82
|
+
# @macro common_attrs
|
118
83
|
def identify(attrs)
|
119
84
|
symbolize_keys! attrs
|
120
|
-
|
121
|
-
|
122
|
-
traits = attrs[:traits] || {}
|
123
|
-
timestamp = attrs[:timestamp] || Time.new
|
124
|
-
context = attrs[:context] || {}
|
125
|
-
message_id = attrs[:message_id].to_s if attrs[:message_id]
|
126
|
-
|
127
|
-
check_timestamp! timestamp
|
128
|
-
|
129
|
-
raise ArgumentError, 'Must supply traits as a hash' unless traits.is_a? Hash
|
130
|
-
isoify_dates! traits
|
131
|
-
|
132
|
-
add_context context
|
133
|
-
|
134
|
-
enqueue({
|
135
|
-
:userId => attrs[:user_id],
|
136
|
-
:anonymousId => attrs[:anonymous_id],
|
137
|
-
:integrations => attrs[:integrations],
|
138
|
-
:context => context,
|
139
|
-
:traits => traits,
|
140
|
-
:options => attrs[:options],
|
141
|
-
:messageId => message_id,
|
142
|
-
:timestamp => datetime_in_iso8601(timestamp),
|
143
|
-
:type => 'identify'
|
144
|
-
})
|
85
|
+
enqueue(FieldParser.parse_for_identify(attrs))
|
145
86
|
end
|
146
87
|
|
147
88
|
# Aliases a user from one id to another
|
@@ -149,39 +90,12 @@ module Segment
|
|
149
90
|
# @see https://segment.com/docs/sources/server/ruby/#alias
|
150
91
|
#
|
151
92
|
# @param [Hash] attrs
|
152
|
-
#
|
153
|
-
# @option attrs [Hash] :integrations What integrations this must be
|
154
|
-
# sent to (optional)
|
155
|
-
# @option attrs [Hash] :options Options such as user traits (optional)
|
93
|
+
#
|
156
94
|
# @option attrs [String] :previous_id The ID to alias from
|
157
|
-
# @
|
158
|
-
# @option attrs [String] :user_id The ID to alias to
|
159
|
-
# @option attrs [String] :message_id ID that uniquely identifies a
|
160
|
-
# message across the API. (optional)
|
95
|
+
# @macro common_attrs
|
161
96
|
def alias(attrs)
|
162
97
|
symbolize_keys! attrs
|
163
|
-
|
164
|
-
from = attrs[:previous_id]
|
165
|
-
to = attrs[:user_id]
|
166
|
-
timestamp = attrs[:timestamp] || Time.new
|
167
|
-
context = attrs[:context] || {}
|
168
|
-
message_id = attrs[:message_id].to_s if attrs[:message_id]
|
169
|
-
|
170
|
-
check_presence! from, 'previous_id'
|
171
|
-
check_presence! to, 'user_id'
|
172
|
-
check_timestamp! timestamp
|
173
|
-
add_context context
|
174
|
-
|
175
|
-
enqueue({
|
176
|
-
:previousId => from,
|
177
|
-
:userId => to,
|
178
|
-
:integrations => attrs[:integrations],
|
179
|
-
:context => context,
|
180
|
-
:options => attrs[:options],
|
181
|
-
:messageId => message_id,
|
182
|
-
:timestamp => datetime_in_iso8601(timestamp),
|
183
|
-
:type => 'alias'
|
184
|
-
})
|
98
|
+
enqueue(FieldParser.parse_for_alias(attrs))
|
185
99
|
end
|
186
100
|
|
187
101
|
# Associates a user identity with a group.
|
@@ -189,48 +103,13 @@ module Segment
|
|
189
103
|
# @see https://segment.com/docs/sources/server/ruby/#group
|
190
104
|
#
|
191
105
|
# @param [Hash] attrs
|
192
|
-
#
|
193
|
-
# who they are yet. (optional but you must provide either an
|
194
|
-
# `anonymous_id` or `user_id`)
|
195
|
-
# @option attrs [Hash] :context ({})
|
106
|
+
#
|
196
107
|
# @option attrs [String] :group_id The ID of the group
|
197
|
-
# @option attrs [Hash] :
|
198
|
-
#
|
199
|
-
# @option attrs [Hash] :options Options such as user traits (optional)
|
200
|
-
# @option attrs [Time] :timestamp When the event occurred (optional)
|
201
|
-
# @option attrs [String] :user_id The ID for the user that is part of
|
202
|
-
# the group
|
203
|
-
# @option attrs [String] :message_id ID that uniquely identifies a
|
204
|
-
# message across the API. (optional)
|
108
|
+
# @option attrs [Hash] :traits User traits (optional)
|
109
|
+
# @macro common_attrs
|
205
110
|
def group(attrs)
|
206
111
|
symbolize_keys! attrs
|
207
|
-
|
208
|
-
|
209
|
-
group_id = attrs[:group_id]
|
210
|
-
user_id = attrs[:user_id]
|
211
|
-
traits = attrs[:traits] || {}
|
212
|
-
timestamp = attrs[:timestamp] || Time.new
|
213
|
-
context = attrs[:context] || {}
|
214
|
-
message_id = attrs[:message_id].to_s if attrs[:message_id]
|
215
|
-
|
216
|
-
raise ArgumentError, '.traits must be a hash' unless traits.is_a? Hash
|
217
|
-
isoify_dates! traits
|
218
|
-
|
219
|
-
check_presence! group_id, 'group_id'
|
220
|
-
check_timestamp! timestamp
|
221
|
-
add_context context
|
222
|
-
|
223
|
-
enqueue({
|
224
|
-
:groupId => group_id,
|
225
|
-
:userId => user_id,
|
226
|
-
:traits => traits,
|
227
|
-
:integrations => attrs[:integrations],
|
228
|
-
:options => attrs[:options],
|
229
|
-
:context => context,
|
230
|
-
:messageId => message_id,
|
231
|
-
:timestamp => datetime_in_iso8601(timestamp),
|
232
|
-
:type => 'group'
|
233
|
-
})
|
112
|
+
enqueue(FieldParser.parse_for_group(attrs))
|
234
113
|
end
|
235
114
|
|
236
115
|
# Records a page view
|
@@ -238,97 +117,26 @@ module Segment
|
|
238
117
|
# @see https://segment.com/docs/sources/server/ruby/#page
|
239
118
|
#
|
240
119
|
# @param [Hash] attrs
|
241
|
-
#
|
242
|
-
# who they are yet. (optional but you must provide either an
|
243
|
-
# `anonymous_id` or `user_id`)
|
244
|
-
# @option attrs [String] :category The page category (optional)
|
245
|
-
# @option attrs [Hash] :context ({})
|
246
|
-
# @option attrs [Hash] :integrations What integrations this event
|
247
|
-
# goes to (optional)
|
120
|
+
#
|
248
121
|
# @option attrs [String] :name Name of the page
|
249
|
-
# @option attrs [Hash] :options Options such as user traits (optional)
|
250
122
|
# @option attrs [Hash] :properties Page properties (optional)
|
251
|
-
# @
|
252
|
-
# @option attrs [String] :user_id The ID of the user viewing the page
|
253
|
-
# @option attrs [String] :message_id ID that uniquely identifies a
|
254
|
-
# message across the API. (optional)
|
123
|
+
# @macro common_attrs
|
255
124
|
def page(attrs)
|
256
125
|
symbolize_keys! attrs
|
257
|
-
|
258
|
-
|
259
|
-
name = attrs[:name].to_s
|
260
|
-
properties = attrs[:properties] || {}
|
261
|
-
timestamp = attrs[:timestamp] || Time.new
|
262
|
-
context = attrs[:context] || {}
|
263
|
-
message_id = attrs[:message_id].to_s if attrs[:message_id]
|
264
|
-
|
265
|
-
raise ArgumentError, '.properties must be a hash' unless properties.is_a? Hash
|
266
|
-
isoify_dates! properties
|
267
|
-
|
268
|
-
check_timestamp! timestamp
|
269
|
-
add_context context
|
270
|
-
|
271
|
-
enqueue({
|
272
|
-
:userId => attrs[:user_id],
|
273
|
-
:anonymousId => attrs[:anonymous_id],
|
274
|
-
:name => name,
|
275
|
-
:category => attrs[:category],
|
276
|
-
:properties => properties,
|
277
|
-
:integrations => attrs[:integrations],
|
278
|
-
:options => attrs[:options],
|
279
|
-
:context => context,
|
280
|
-
:messageId => message_id,
|
281
|
-
:timestamp => datetime_in_iso8601(timestamp),
|
282
|
-
:type => 'page'
|
283
|
-
})
|
126
|
+
enqueue(FieldParser.parse_for_page(attrs))
|
284
127
|
end
|
285
128
|
|
286
129
|
# Records a screen view (for a mobile app)
|
287
130
|
#
|
288
131
|
# @param [Hash] attrs
|
289
|
-
#
|
290
|
-
# who they are yet. (optional but you must provide either an
|
291
|
-
# `anonymous_id` or `user_id`)
|
292
|
-
# @option attrs [String] :category The screen category (optional)
|
293
|
-
# @option attrs [Hash] :context ({})
|
294
|
-
# @option attrs [Hash] :integrations What integrations this event
|
295
|
-
# goes to (optional)
|
132
|
+
#
|
296
133
|
# @option attrs [String] :name Name of the screen
|
297
|
-
# @option attrs [Hash] :
|
298
|
-
# @option attrs [
|
299
|
-
# @
|
300
|
-
# @option attrs [String] :user_id The ID of the user viewing the screen
|
301
|
-
# @option attrs [String] :message_id ID that uniquely identifies a
|
302
|
-
# message across the API. (optional)
|
134
|
+
# @option attrs [Hash] :properties Screen properties (optional)
|
135
|
+
# @option attrs [String] :category The screen category (optional)
|
136
|
+
# @macro common_attrs
|
303
137
|
def screen(attrs)
|
304
138
|
symbolize_keys! attrs
|
305
|
-
|
306
|
-
|
307
|
-
name = attrs[:name].to_s
|
308
|
-
properties = attrs[:properties] || {}
|
309
|
-
timestamp = attrs[:timestamp] || Time.new
|
310
|
-
context = attrs[:context] || {}
|
311
|
-
message_id = attrs[:message_id].to_s if attrs[:message_id]
|
312
|
-
|
313
|
-
raise ArgumentError, '.properties must be a hash' unless properties.is_a? Hash
|
314
|
-
isoify_dates! properties
|
315
|
-
|
316
|
-
check_timestamp! timestamp
|
317
|
-
add_context context
|
318
|
-
|
319
|
-
enqueue({
|
320
|
-
:userId => attrs[:user_id],
|
321
|
-
:anonymousId => attrs[:anonymous_id],
|
322
|
-
:name => name,
|
323
|
-
:properties => properties,
|
324
|
-
:category => attrs[:category],
|
325
|
-
:options => attrs[:options],
|
326
|
-
:integrations => attrs[:integrations],
|
327
|
-
:context => context,
|
328
|
-
:messageId => message_id,
|
329
|
-
:timestamp => timestamp.iso8601,
|
330
|
-
:type => 'screen'
|
331
|
-
})
|
139
|
+
enqueue(FieldParser.parse_for_screen(attrs))
|
332
140
|
end
|
333
141
|
|
334
142
|
# @return [Fixnum] number of messages in the queue
|
@@ -336,6 +144,14 @@ module Segment
|
|
336
144
|
@queue.length
|
337
145
|
end
|
338
146
|
|
147
|
+
def test_queue
|
148
|
+
unless @test
|
149
|
+
raise 'Test queue only available when setting :test to true.'
|
150
|
+
end
|
151
|
+
|
152
|
+
@test_queue ||= TestQueue.new
|
153
|
+
end
|
154
|
+
|
339
155
|
private
|
340
156
|
|
341
157
|
# private: Enqueues the action.
|
@@ -345,6 +161,8 @@ module Segment
|
|
345
161
|
# add our request id for tracing purposes
|
346
162
|
action[:messageId] ||= uid
|
347
163
|
|
164
|
+
test_queue << action if @test
|
165
|
+
|
348
166
|
if @queue.length < @max_queue_size
|
349
167
|
@queue << action
|
350
168
|
ensure_worker_running
|
@@ -360,53 +178,11 @@ module Segment
|
|
360
178
|
end
|
361
179
|
end
|
362
180
|
|
363
|
-
# private: Ensures that a string is non-empty
|
364
|
-
#
|
365
|
-
# obj - String|Number that must be non-blank
|
366
|
-
# name - Name of the validated value
|
367
|
-
#
|
368
|
-
def check_presence!(obj, name)
|
369
|
-
if obj.nil? || (obj.is_a?(String) && obj.empty?)
|
370
|
-
raise ArgumentError, "#{name} must be given"
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
# private: Adds contextual information to the call
|
375
|
-
#
|
376
|
-
# context - Hash of call context
|
377
|
-
def add_context(context)
|
378
|
-
context[:library] = { :name => 'analytics-ruby', :version => Segment::Analytics::VERSION.to_s }
|
379
|
-
end
|
380
|
-
|
381
181
|
# private: Checks that the write_key is properly initialized
|
382
182
|
def check_write_key!
|
383
183
|
raise ArgumentError, 'Write key must be initialized' if @write_key.nil?
|
384
184
|
end
|
385
185
|
|
386
|
-
# private: Checks the timstamp option to make sure it is a Time.
|
387
|
-
def check_timestamp!(timestamp)
|
388
|
-
raise ArgumentError, 'Timestamp must be a Time' unless timestamp.is_a? Time
|
389
|
-
end
|
390
|
-
|
391
|
-
def event(attrs)
|
392
|
-
symbolize_keys! attrs
|
393
|
-
|
394
|
-
{
|
395
|
-
:userId => user_id,
|
396
|
-
:name => name,
|
397
|
-
:properties => properties,
|
398
|
-
:context => context,
|
399
|
-
:timestamp => datetime_in_iso8601(timestamp),
|
400
|
-
:type => 'screen'
|
401
|
-
}
|
402
|
-
end
|
403
|
-
|
404
|
-
def check_user_id!(attrs)
|
405
|
-
unless attrs[:user_id] || attrs[:anonymous_id]
|
406
|
-
raise ArgumentError, 'Must supply either user_id or anonymous_id'
|
407
|
-
end
|
408
|
-
end
|
409
|
-
|
410
186
|
def ensure_worker_running
|
411
187
|
return if worker_running?
|
412
188
|
@worker_mutex.synchronize do
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module Segment
|
2
|
+
class Analytics
|
3
|
+
# Handles parsing fields according to the Segment Spec
|
4
|
+
#
|
5
|
+
# @see https://segment.com/docs/spec/
|
6
|
+
class FieldParser
|
7
|
+
class << self
|
8
|
+
include Segment::Analytics::Utils
|
9
|
+
|
10
|
+
# In addition to the common fields, track accepts:
|
11
|
+
#
|
12
|
+
# - "event"
|
13
|
+
# - "properties"
|
14
|
+
def parse_for_track(fields)
|
15
|
+
common = parse_common_fields(fields)
|
16
|
+
|
17
|
+
event = fields[:event]
|
18
|
+
properties = fields[:properties] || {}
|
19
|
+
|
20
|
+
check_presence!(event, 'event')
|
21
|
+
check_is_hash!(properties, 'properties')
|
22
|
+
|
23
|
+
isoify_dates! properties
|
24
|
+
|
25
|
+
common.merge({
|
26
|
+
:type => 'track',
|
27
|
+
:event => event.to_s,
|
28
|
+
:properties => properties
|
29
|
+
})
|
30
|
+
end
|
31
|
+
|
32
|
+
# In addition to the common fields, identify accepts:
|
33
|
+
#
|
34
|
+
# - "traits"
|
35
|
+
def parse_for_identify(fields)
|
36
|
+
common = parse_common_fields(fields)
|
37
|
+
|
38
|
+
traits = fields[:traits] || {}
|
39
|
+
check_is_hash!(traits, 'traits')
|
40
|
+
isoify_dates! traits
|
41
|
+
|
42
|
+
common.merge({
|
43
|
+
:type => 'identify',
|
44
|
+
:traits => traits
|
45
|
+
})
|
46
|
+
end
|
47
|
+
|
48
|
+
# In addition to the common fields, alias accepts:
|
49
|
+
#
|
50
|
+
# - "previous_id"
|
51
|
+
def parse_for_alias(fields)
|
52
|
+
common = parse_common_fields(fields)
|
53
|
+
|
54
|
+
previous_id = fields[:previous_id]
|
55
|
+
check_presence!(previous_id, 'previous_id')
|
56
|
+
|
57
|
+
common.merge({
|
58
|
+
:type => 'alias',
|
59
|
+
:previousId => previous_id
|
60
|
+
})
|
61
|
+
end
|
62
|
+
|
63
|
+
# In addition to the common fields, group accepts:
|
64
|
+
#
|
65
|
+
# - "group_id"
|
66
|
+
# - "traits"
|
67
|
+
def parse_for_group(fields)
|
68
|
+
common = parse_common_fields(fields)
|
69
|
+
|
70
|
+
group_id = fields[:group_id]
|
71
|
+
traits = fields[:traits] || {}
|
72
|
+
|
73
|
+
check_presence!(group_id, 'group_id')
|
74
|
+
check_is_hash!(traits, 'traits')
|
75
|
+
|
76
|
+
isoify_dates! traits
|
77
|
+
|
78
|
+
common.merge({
|
79
|
+
:type => 'group',
|
80
|
+
:groupId => group_id,
|
81
|
+
:traits => traits
|
82
|
+
})
|
83
|
+
end
|
84
|
+
|
85
|
+
# In addition to the common fields, page accepts:
|
86
|
+
#
|
87
|
+
# - "name"
|
88
|
+
# - "properties"
|
89
|
+
def parse_for_page(fields)
|
90
|
+
common = parse_common_fields(fields)
|
91
|
+
|
92
|
+
name = fields[:name] || ''
|
93
|
+
properties = fields[:properties] || {}
|
94
|
+
|
95
|
+
check_is_hash!(properties, 'properties')
|
96
|
+
|
97
|
+
isoify_dates! properties
|
98
|
+
|
99
|
+
common.merge({
|
100
|
+
:type => 'page',
|
101
|
+
:name => name.to_s,
|
102
|
+
:properties => properties
|
103
|
+
})
|
104
|
+
end
|
105
|
+
|
106
|
+
# In addition to the common fields, screen accepts:
|
107
|
+
#
|
108
|
+
# - "name"
|
109
|
+
# - "properties"
|
110
|
+
# - "category" (Not in spec, retained for backward compatibility"
|
111
|
+
def parse_for_screen(fields)
|
112
|
+
common = parse_common_fields(fields)
|
113
|
+
|
114
|
+
name = fields[:name]
|
115
|
+
properties = fields[:properties] || {}
|
116
|
+
category = fields[:category]
|
117
|
+
|
118
|
+
check_presence!(name, 'name')
|
119
|
+
check_is_hash!(properties, 'properties')
|
120
|
+
|
121
|
+
isoify_dates! properties
|
122
|
+
|
123
|
+
parsed = common.merge({
|
124
|
+
:type => 'screen',
|
125
|
+
:name => name,
|
126
|
+
:properties => properties
|
127
|
+
})
|
128
|
+
|
129
|
+
parsed[:category] = category if category
|
130
|
+
|
131
|
+
parsed
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def parse_common_fields(fields)
|
137
|
+
timestamp = fields[:timestamp] || Time.new
|
138
|
+
message_id = fields[:message_id].to_s if fields[:message_id]
|
139
|
+
context = fields[:context] || {}
|
140
|
+
|
141
|
+
check_user_id! fields
|
142
|
+
check_timestamp! timestamp
|
143
|
+
|
144
|
+
add_context! context
|
145
|
+
|
146
|
+
parsed = {
|
147
|
+
:context => context,
|
148
|
+
:messageId => message_id,
|
149
|
+
:timestamp => datetime_in_iso8601(timestamp)
|
150
|
+
}
|
151
|
+
|
152
|
+
parsed[:userId] = fields[:user_id] if fields[:user_id]
|
153
|
+
parsed[:anonymousId] = fields[:anonymous_id] if fields[:anonymous_id]
|
154
|
+
parsed[:integrations] = fields[:integrations] if fields[:integrations]
|
155
|
+
|
156
|
+
# Not in spec, retained for backward compatibility
|
157
|
+
parsed[:options] = fields[:options] if fields[:options]
|
158
|
+
|
159
|
+
parsed
|
160
|
+
end
|
161
|
+
|
162
|
+
def check_user_id!(fields)
|
163
|
+
unless fields[:user_id] || fields[:anonymous_id]
|
164
|
+
raise ArgumentError, 'Must supply either user_id or anonymous_id'
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def check_timestamp!(timestamp)
|
169
|
+
raise ArgumentError, 'Timestamp must be a Time' unless timestamp.is_a? Time
|
170
|
+
end
|
171
|
+
|
172
|
+
def add_context!(context)
|
173
|
+
context[:library] = { :name => 'analytics-ruby', :version => Segment::Analytics::VERSION.to_s }
|
174
|
+
end
|
175
|
+
|
176
|
+
# private: Ensures that a string is non-empty
|
177
|
+
#
|
178
|
+
# obj - String|Number that must be non-blank
|
179
|
+
# name - Name of the validated value
|
180
|
+
def check_presence!(obj, name)
|
181
|
+
if obj.nil? || (obj.is_a?(String) && obj.empty?)
|
182
|
+
raise ArgumentError, "#{name} must be given"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def check_is_hash!(obj, name)
|
187
|
+
raise ArgumentError, "#{name} must be a Hash" unless obj.is_a? Hash
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|