analytics-ruby 2.2.5 → 2.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f217e8929c1705f8602e90bdd0f23b2e77fdb97aa57c530e7ae325005a640258
4
- data.tar.gz: 2a5674bf8575c46c472622cd2c2974a0f110215edb76dbce90126f1e62b43a99
3
+ metadata.gz: 51f3f02afed05c9c8461b454e0a3067962a75b8efde0930db0098eebf4a62829
4
+ data.tar.gz: e7d486d6c6186535e9a005c4d950e6e29d2dd9937f9b6da625191d786f76846a
5
5
  SHA512:
6
- metadata.gz: 5a118f808ca6a2fb9235ac9464665b618213e0e8ff372e001d4243da059e8cd4b5c2f8fbbac0c1eb8590f637c2d6594d5a5624183f11ba54355dc52769e8177c
7
- data.tar.gz: 1ab60c691e8422bb33b7e14b1803cab36e457056871c366dabc1143289fede5e174c812dbca492176189f79a33419b1d2d3ea90ea61f827bd0e7248bef61cc95
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: option.name,
70
- traits: json_hash(options.traits),
71
- properties: json_hash(option.properties)
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, @options)
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
- # @option attrs [String] :anonymous_id ID for a user when you don't know
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
- # @option attrs [Time] :timestamp When the event occurred (optional)
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
- check_user_id! attrs
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
- # @option attrs [String] :anonymous_id ID for a user when you don't know
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
- # @option attrs [String] :user_id The ID for this user in your database
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
- check_user_id! attrs
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
- # @option attrs [Hash] :context ({})
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
- # @option attrs [Time] :timestamp When the alias occurred (optional)
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
- # @option attrs [String] :anonymous_id ID for a user when you don't know
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] :integrations What integrations this event
198
- # goes to (optional)
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
- check_user_id! attrs
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
- # @option attrs [String] :anonymous_id ID for a user when you don't know
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
- # @option attrs [Time] :timestamp When the pageview occurred (optional)
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
- check_user_id! attrs
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
- # @option attrs [String] :anonymous_id ID for a user when you don't know
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] :options Options such as user traits (optional)
298
- # @option attrs [Hash] :properties Page properties (optional)
299
- # @option attrs [Time] :timestamp When the pageview occurred (optional)
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
- check_user_id! attrs
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