thinkingdata-ruby 2.0.0 → 2.0.1

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.
@@ -1,495 +1,509 @@
1
- require 'securerandom'
2
- require 'thinkingdata-ruby/td_errors'
3
- require 'thinkingdata-ruby/td_version'
4
-
5
- ##
6
- # ThinkingData module
7
- module ThinkingData
8
- @is_enable_log = false
9
- @is_stringent = false
10
-
11
- ##
12
- # Enable SDK log or not
13
- # @param enable [Boolean] true or false
14
- def self.set_enable_log(enable)
15
- unless [true, false].include? enable
16
- enable = false
17
- end
18
- @is_enable_log = enable
19
- end
20
-
21
- ##
22
- # Get log status
23
- # @return [Boolean] enable or not
24
- def self.get_enable_log
25
- @is_enable_log
26
- end
27
-
28
- ##
29
- # Check or not parameter
30
- # @param enable [Boolean] check or not
31
- def self.set_stringent(enable)
32
- unless [true, false].include? enable
33
- enable = false
34
- end
35
- @is_stringent = enable
36
- end
37
-
38
- ##
39
- # Get parameter check status of SDK
40
- # @return [Boolean] check or not
41
- def self.get_stringent
42
- @is_stringent
43
- end
44
-
45
- ##
46
- # Analytics class。 Provides the function of tracking data
47
- class TDAnalytics
48
- LIB_PROPERTIES = {
49
- '#lib' => 'ruby',
50
- '#lib_version' => ThinkingData::VERSION,
51
- }
52
-
53
- @dynamic_block = nil
54
-
55
- ##
56
- # Init function
57
- # @param consumer [consumer] data consumer: TDLoggerConsumer | TDDebugConsumer | TDBatchConsumer
58
- # @param error_handler [TDErrorHandler] custom error handler, process SDK error. It could be nil
59
- # @param uuid [Boolean] Whether to automatically add uuid
60
- def initialize(consumer, error_handler = nil, uuid: false)
61
- @error_handler = error_handler || TDErrorHandler.new
62
- @consumer = consumer
63
- @super_properties = {}
64
- @uuid_enable = uuid
65
- TDLog.info("SDK init success.")
66
- end
67
-
68
- ##
69
- # Set common properties
70
- def set_super_properties(properties, skip_local_check = false)
71
- unless ThinkingData::get_stringent == false || skip_local_check || _check_properties(:track, properties)
72
- @error_handler.handle(IllegalParameterError.new("Invalid super properties"))
73
- return false
74
- end
75
- properties.each do |k, v|
76
- if v.is_a?(Time)
77
- @super_properties[k] = _format_time(v)
78
- else
79
- @super_properties[k] = v
80
- end
81
- end
82
- end
83
-
84
- ##
85
- # Clear super properties
86
- def clear_super_properties
87
- @super_properties = {}
88
- end
89
-
90
- ##
91
- # Set dynamic super properties
92
- def set_dynamic_super_properties(&block)
93
- @dynamic_block = block
94
- end
95
-
96
- ##
97
- # Clear dynamic super properties
98
- def clear_dynamic_super_properties
99
- @dynamic_block = nil
100
- end
101
-
102
- ##
103
- # Report ordinary event
104
- # event_name: (require) A string of 50 letters and digits that starts with '#' or a letter
105
- # distinct_id: (optional) distinct ID
106
- # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
107
- # properties: (optional) string、number、Time、boolean
108
- # time: (optional)Time
109
- # ip: (optional) ip
110
- # first_check_id: (optional) The value cannot be null for the first event
111
- # skip_local_check: (optional) check data or not
112
- def track(event_name: nil, distinct_id: nil, account_id: nil, properties: {}, time: nil, ip: nil,first_check_id:nil, skip_local_check: false)
113
- begin
114
- _check_name event_name
115
- _check_id(distinct_id, account_id)
116
- unless skip_local_check
117
- _check_properties(:track, properties)
118
- end
119
- rescue TDAnalyticsError => e
120
- @error_handler.handle(e)
121
- return false
122
- end
123
-
124
- _internal_track(:track, event_name: event_name, distinct_id: distinct_id, account_id: account_id, properties: properties, time: time, ip: ip, first_check_id: first_check_id)
125
- end
126
-
127
- ##
128
- # Report overridable event
129
- # event_name: (require) A string of 50 letters and digits that starts with '#' or a letter
130
- # event_id: (require) string
131
- # distinct_id: (optional) distinct ID
132
- # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
133
- # properties: (optional) string、number、Time、boolean
134
- # time: (optional)Time
135
- # ip: (optional) ip
136
- # skip_local_check: (optional) check data or not
137
- def track_overwrite(event_name: nil,event_id: nil, distinct_id: nil, account_id: nil, properties: {}, time: nil, ip: nil, skip_local_check: false)
138
- begin
139
- _check_name event_name
140
- _check_event_id event_id
141
- _check_id(distinct_id, account_id)
142
- unless skip_local_check
143
- _check_properties(:track_overwrite, properties)
144
- end
145
- rescue TDAnalyticsError => e
146
- @error_handler.handle(e)
147
- return false
148
- end
149
-
150
- _internal_track(:track_overwrite, event_name: event_name, event_id: event_id, distinct_id: distinct_id, account_id: account_id, properties: properties, time: time, ip: ip)
151
- end
152
-
153
- ##
154
- # Report updatable event
155
- # event_name: (require) A string of 50 letters and digits that starts with '#' or a letter
156
- # event_id: (require) string
157
- # distinct_id: (optional) distinct ID
158
- # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
159
- # properties: (optional) string、number、Time、boolean
160
- # time: (optional)Time
161
- # ip: (optional) ip
162
- # skip_local_check: (optional) check data or not
163
- def track_update(event_name: nil,event_id: nil, distinct_id: nil, account_id: nil, properties: {}, time: nil, ip: nil, skip_local_check: false)
164
- begin
165
- _check_name event_name
166
- _check_event_id event_id
167
- _check_id(distinct_id, account_id)
168
- unless skip_local_check
169
- _check_properties(:track_update, properties)
170
- end
171
- rescue TDAnalyticsError => e
172
- @error_handler.handle(e)
173
- return false
174
- end
175
-
176
- _internal_track(:track_update, event_name: event_name, event_id: event_id, distinct_id: distinct_id, account_id: account_id, properties: properties, time: time, ip: ip)
177
- end
178
-
179
- ##
180
- # Set user properties. would overwrite existing names
181
- # distinct_id: (optional) distinct ID
182
- # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
183
- # properties: (optional) string、number、Time、boolean
184
- # ip: (optional) ip
185
- def user_set(distinct_id: nil, account_id: nil, properties: {}, ip: nil)
186
- begin
187
- _check_id(distinct_id, account_id)
188
- _check_properties(:user_set, properties)
189
- rescue TDAnalyticsError => e
190
- @error_handler.handle(e)
191
- return false
192
- end
193
-
194
- _internal_track(:user_set, distinct_id: distinct_id, account_id: account_id, properties: properties, ip: ip)
195
- end
196
-
197
- ##
198
- # Set user properties, If such property had been set before, this message would be neglected
199
- # distinct_id: (optional) distinct ID
200
- # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
201
- # properties: (optional) string、number、Time、boolean
202
- # ip: (optional) ip
203
- def user_set_once(distinct_id: nil, account_id: nil, properties: {}, ip: nil)
204
- begin
205
- _check_id(distinct_id, account_id)
206
- _check_properties(:user_setOnce, properties)
207
- rescue TDAnalyticsError => e
208
- @error_handler.handle(e)
209
- return false
210
- end
211
-
212
- _internal_track(:user_setOnce,
213
- distinct_id: distinct_id,
214
- account_id: account_id,
215
- properties: properties,
216
- ip: ip,
217
- )
218
- end
219
-
220
- ##
221
- # To append user properties of array type
222
- # distinct_id: (optional) distinct ID
223
- # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
224
- # properties: (optional) string、number、Time、boolean
225
- def user_append(distinct_id: nil, account_id: nil, properties: {})
226
- begin
227
- _check_id(distinct_id, account_id)
228
- _check_properties(:user_append, properties)
229
- rescue TDAnalyticsError => e
230
- @error_handler.handle(e)
231
- return false
232
- end
233
-
234
- _internal_track(:user_append,
235
- distinct_id: distinct_id,
236
- account_id: account_id,
237
- properties: properties,
238
- )
239
- end
240
-
241
- ##
242
- # To append user properties of array type. It filters out duplicate values
243
- # distinct_id: (optional) distinct ID
244
- # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
245
- # properties: (optional) string、number、Time、boolean
246
- def user_uniq_append(distinct_id: nil, account_id: nil, properties: {})
247
- begin
248
- _check_id(distinct_id, account_id)
249
- _check_properties(:user_uniq_append, properties)
250
- rescue TDAnalyticsError => e
251
- @error_handler.handle(e)
252
- return false
253
- end
254
-
255
- _internal_track(:user_uniq_append,
256
- distinct_id: distinct_id,
257
- account_id: account_id,
258
- properties: properties,
259
- )
260
- end
261
-
262
- ##
263
- # Clear the user properties of users
264
- # distinct_id: (optional) distinct ID
265
- # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
266
- # properties: (optional) string、number、Time、boolean
267
- def user_unset(distinct_id: nil, account_id: nil, property: nil)
268
- properties = {}
269
- if property.is_a?(Array)
270
- property.each do |k|
271
- properties[k] = 0
272
- end
273
- else
274
- properties[property] = 0
275
- end
276
-
277
- begin
278
- _check_id(distinct_id, account_id)
279
- _check_properties(:user_unset, properties)
280
- rescue TDAnalyticsError => e
281
- @error_handler.handle(e)
282
- return false
283
- end
284
-
285
- _internal_track(:user_unset,
286
- distinct_id: distinct_id,
287
- account_id: account_id,
288
- properties: properties,
289
- )
290
- end
291
-
292
- ##
293
- # To accumulate operations against the property
294
- # distinct_id: (optional) distinct ID
295
- # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
296
- # properties: (optional) string、number、Time、boolean
297
- def user_add(distinct_id: nil, account_id: nil, properties: {})
298
- begin
299
- _check_id(distinct_id, account_id)
300
- _check_properties(:user_add, properties)
301
- rescue TDAnalyticsError => e
302
- @error_handler.handle(e)
303
- return false
304
- end
305
-
306
- _internal_track(:user_add,
307
- distinct_id: distinct_id,
308
- account_id: account_id,
309
- properties: properties,
310
- )
311
- end
312
-
313
- ##
314
- # Delete a user, This operation cannot be undone
315
- # distinct_id: (optional) distinct ID
316
- # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
317
- def user_del(distinct_id: nil, account_id: nil)
318
- begin
319
- _check_id(distinct_id, account_id)
320
- rescue TDAnalyticsError => e
321
- @error_handler.handle(e)
322
- return false
323
- end
324
-
325
- _internal_track(:user_del,
326
- distinct_id: distinct_id,
327
- account_id: account_id,
328
- )
329
- end
330
-
331
- ##
332
- # Report data immediately
333
- def flush
334
- TDLog.info("SDK flush data.")
335
- return true unless defined? @consumer.flush
336
- ret = true
337
- begin
338
- @consumer.flush
339
- rescue TDAnalyticsError => e
340
- @error_handler.handle(e)
341
- ret = false
342
- end
343
- ret
344
- end
345
-
346
- ##
347
- # Close and exit sdk
348
- def close
349
- return true unless defined? @consumer.close
350
- ret = true
351
- begin
352
- @consumer.close
353
- rescue TDAnalyticsError => e
354
- @error_handler.handle(e)
355
- ret = false
356
- end
357
-
358
- TDLog.info("SDK close.")
359
-
360
- ret
361
- end
362
-
363
- private
364
-
365
- def _internal_track(type, properties: {}, event_name: nil, event_id:nil, account_id: nil, distinct_id: nil, ip: nil,first_check_id: nil, time: nil)
366
- if type == :track || type == :track_update || type == :track_overwrite
367
- dynamic_properties = @dynamic_block.respond_to?(:call) ? @dynamic_block.call : {}
368
- properties = LIB_PROPERTIES.merge(@super_properties).merge(dynamic_properties).merge(properties)
369
- end
370
-
371
- data = {
372
- '#type' => type,
373
- }
374
-
375
- properties.each do |k, v|
376
- if v.is_a?(Time)
377
- properties[k] = _format_time(v)
378
- end
379
- end
380
-
381
- _move_preset_properties([:'#ip', :"#time", :"#app_id", :"#uuid"], data, properties: properties)
382
-
383
- if data[:'#time'] == nil
384
- if time == nil
385
- time = Time.now
386
- end
387
- data[:'#time'] = _format_time(time)
388
- end
389
-
390
- data['properties'] = properties
391
- data['#event_name'] = event_name if (type == :track || type == :track_update || type == :track_overwrite)
392
- data['#event_id'] = event_id if (type == :track_update || type == :track_overwrite)
393
- data['#account_id'] = account_id if account_id
394
- data['#distinct_id'] = distinct_id if distinct_id
395
- data['#ip'] = ip if ip
396
- data['#first_check_id'] = first_check_id if first_check_id
397
- data[:'#uuid'] = SecureRandom.uuid if @uuid_enable and data[:'#uuid'] == nil
398
-
399
- ret = true
400
- begin
401
- @consumer.add(data)
402
- rescue TDAnalyticsError => e
403
- @error_handler.handle(e)
404
- ret = false
405
- end
406
-
407
- ret
408
- end
409
-
410
- def _format_time(time)
411
- time.strftime("%Y-%m-%d %H:%M:%S.#{((time.to_f * 1000.0).to_i % 1000).to_s.rjust(3, "0")}")
412
- end
413
-
414
- def _check_event_id(event_id)
415
- if ThinkingData::get_stringent == false
416
- return true
417
- end
418
-
419
- raise IllegalParameterError.new("the event_id or property cannot be nil") if event_id.nil?
420
- true
421
- end
422
-
423
- def _check_name(name)
424
- if ThinkingData::get_stringent == false
425
- return true
426
- end
427
-
428
- raise IllegalParameterError.new("the name of event or property cannot be nil") if name.nil?
429
-
430
- unless name.instance_of?(String) || name.instance_of?(Symbol)
431
- raise IllegalParameterError.new("#{name} is invalid. It must be String or Symbol")
432
- end
433
- true
434
- end
435
-
436
- def _check_properties(type, properties)
437
- if ThinkingData::get_stringent == false
438
- return true
439
- end
440
-
441
- unless properties.instance_of? Hash
442
- return false
443
- end
444
-
445
- properties.each do |k, v|
446
- _check_name k
447
- next if v.nil?
448
- unless v.is_a?(Integer) || v.is_a?(Float) || v.is_a?(Symbol) || v.is_a?(String) || v.is_a?(Time) || !!v == v || v.is_a?(Array)
449
- raise IllegalParameterError.new("The value of properties must be type in Integer, Float, Symbol, String, Array,and Time")
450
- end
451
-
452
- if type == :user_add
453
- raise IllegalParameterError.new("Property value for user add must be numbers") unless v.is_a?(Integer) || v.is_a?(Float)
454
- end
455
- if v.is_a?(Array)
456
- v.each_index do |i|
457
- if v[i].is_a?(Time)
458
- v[i] = _format_time(v[i])
459
- end
460
- end
461
- end
462
- end
463
- true
464
- end
465
-
466
- def _check_id(distinct_id, account_id)
467
- if ThinkingData::get_stringent == false
468
- return true
469
- end
470
-
471
- raise IllegalParameterError.new("account id or distinct id must be provided.") if distinct_id.nil? && account_id.nil?
472
- end
473
-
474
- def _move_preset_properties(keys, data, properties: {})
475
- property_keys = properties.keys
476
- keys.each { |k|
477
- if property_keys.include? k
478
- data[k] = properties[k]
479
- properties.delete(k)
480
- end
481
- }
482
- end
483
- end
484
-
485
- ##
486
- # SDK log module
487
- class TDLog
488
- def self.info(*msg)
489
- if ThinkingData::get_enable_log
490
- print("[ThinkingData][#{Time.now}] ")
491
- puts(msg)
492
- end
493
- end
494
- end
495
- end
1
+ require 'securerandom'
2
+ require 'thinkingdata-ruby/td_errors'
3
+ require 'thinkingdata-ruby/td_version'
4
+
5
+ ##
6
+ # ThinkingData module
7
+ module ThinkingData
8
+ @is_enable_log = false
9
+ @is_stringent = false
10
+
11
+ ##
12
+ # Enable SDK log or not
13
+ # @param enable [Boolean] true or false
14
+ def self.set_enable_log(enable)
15
+ unless [true, false].include? enable
16
+ enable = false
17
+ end
18
+ @is_enable_log = enable
19
+ end
20
+
21
+ ##
22
+ # Get log status
23
+ # @return [Boolean] enable or not
24
+ def self.get_enable_log
25
+ @is_enable_log
26
+ end
27
+
28
+ ##
29
+ # Check or not parameter
30
+ # @param enable [Boolean] check or not
31
+ def self.set_stringent(enable)
32
+ unless [true, false].include? enable
33
+ enable = false
34
+ end
35
+ @is_stringent = enable
36
+ end
37
+
38
+ ##
39
+ # Get parameter check status of SDK
40
+ # @return [Boolean] check or not
41
+ def self.get_stringent
42
+ @is_stringent
43
+ end
44
+
45
+ ##
46
+ # Analytics class。 Provides the function of tracking data
47
+ class TDAnalytics
48
+ LIB_PROPERTIES = {
49
+ '#lib' => 'ruby',
50
+ '#lib_version' => ThinkingData::VERSION,
51
+ }
52
+
53
+ @dynamic_block = nil
54
+
55
+ ##
56
+ # Init function
57
+ # @param consumer [consumer] data consumer: TDLoggerConsumer | TDDebugConsumer | TDBatchConsumer
58
+ # @param error_handler [TDErrorHandler] custom error handler, process SDK error. It could be nil
59
+ # @param uuid [Boolean] Whether to automatically add uuid
60
+ def initialize(consumer, error_handler = nil, uuid: false)
61
+ @error_handler = error_handler || TDErrorHandler.new
62
+ @consumer = consumer
63
+ @super_properties = {}
64
+ @uuid_enable = uuid
65
+ @mutex = Mutex.new
66
+ TDLog.info("SDK init success.")
67
+ end
68
+
69
+ ##
70
+ # Set common properties
71
+ def set_super_properties(properties, skip_local_check = false)
72
+ @mutex.synchronize do
73
+ unless ThinkingData::get_stringent == false || skip_local_check || _check_properties(:track, properties)
74
+ @error_handler.handle(IllegalParameterError.new("Invalid super properties"))
75
+ return false
76
+ end
77
+ properties.each do |k, v|
78
+ if v.is_a?(Time)
79
+ @super_properties[k] = _format_time(v)
80
+ else
81
+ @super_properties[k] = v
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ ##
88
+ # Clear super properties
89
+ def clear_super_properties
90
+ @mutex.synchronize do
91
+ @super_properties = {}
92
+ end
93
+ end
94
+
95
+ ##
96
+ # Set dynamic super properties
97
+ def set_dynamic_super_properties(&block)
98
+ @mutex.synchronize do
99
+ @dynamic_block = block
100
+ end
101
+ end
102
+
103
+ ##
104
+ # Clear dynamic super properties
105
+ def clear_dynamic_super_properties
106
+ @mutex.synchronize do
107
+ @dynamic_block = nil
108
+ end
109
+ end
110
+
111
+ ##
112
+ # Report ordinary event
113
+ # event_name: (require) A string of 50 letters and digits that starts with '#' or a letter
114
+ # distinct_id: (optional) distinct ID
115
+ # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
116
+ # properties: (optional) string、number、Time、boolean
117
+ # time: (optional)Time
118
+ # ip: (optional) ip
119
+ # first_check_id: (optional) The value cannot be null for the first event
120
+ # skip_local_check: (optional) check data or not
121
+ def track(event_name: nil, distinct_id: nil, account_id: nil, properties: {}, time: nil, ip: nil,first_check_id:nil, skip_local_check: false)
122
+ begin
123
+ _check_name event_name
124
+ _check_id(distinct_id, account_id)
125
+ unless skip_local_check
126
+ _check_properties(:track, properties)
127
+ end
128
+ rescue TDAnalyticsError => e
129
+ @error_handler.handle(e)
130
+ return false
131
+ end
132
+
133
+ _internal_track(:track, event_name: event_name, distinct_id: distinct_id, account_id: account_id, properties: properties, time: time, ip: ip, first_check_id: first_check_id)
134
+ end
135
+
136
+ ##
137
+ # Report overridable event
138
+ # event_name: (require) A string of 50 letters and digits that starts with '#' or a letter
139
+ # event_id: (require) string
140
+ # distinct_id: (optional) distinct ID
141
+ # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
142
+ # properties: (optional) string、number、Time、boolean
143
+ # time: (optional)Time
144
+ # ip: (optional) ip
145
+ # skip_local_check: (optional) check data or not
146
+ def track_overwrite(event_name: nil,event_id: nil, distinct_id: nil, account_id: nil, properties: {}, time: nil, ip: nil, skip_local_check: false)
147
+ begin
148
+ _check_name event_name
149
+ _check_event_id event_id
150
+ _check_id(distinct_id, account_id)
151
+ unless skip_local_check
152
+ _check_properties(:track_overwrite, properties)
153
+ end
154
+ rescue TDAnalyticsError => e
155
+ @error_handler.handle(e)
156
+ return false
157
+ end
158
+
159
+ _internal_track(:track_overwrite, event_name: event_name, event_id: event_id, distinct_id: distinct_id, account_id: account_id, properties: properties, time: time, ip: ip)
160
+ end
161
+
162
+ ##
163
+ # Report updatable event
164
+ # event_name: (require) A string of 50 letters and digits that starts with '#' or a letter
165
+ # event_id: (require) string
166
+ # distinct_id: (optional) distinct ID
167
+ # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
168
+ # properties: (optional) string、number、Time、boolean
169
+ # time: (optional)Time
170
+ # ip: (optional) ip
171
+ # skip_local_check: (optional) check data or not
172
+ def track_update(event_name: nil,event_id: nil, distinct_id: nil, account_id: nil, properties: {}, time: nil, ip: nil, skip_local_check: false)
173
+ begin
174
+ _check_name event_name
175
+ _check_event_id event_id
176
+ _check_id(distinct_id, account_id)
177
+ unless skip_local_check
178
+ _check_properties(:track_update, properties)
179
+ end
180
+ rescue TDAnalyticsError => e
181
+ @error_handler.handle(e)
182
+ return false
183
+ end
184
+
185
+ _internal_track(:track_update, event_name: event_name, event_id: event_id, distinct_id: distinct_id, account_id: account_id, properties: properties, time: time, ip: ip)
186
+ end
187
+
188
+ ##
189
+ # Set user properties. would overwrite existing names
190
+ # distinct_id: (optional) distinct ID
191
+ # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
192
+ # properties: (optional) string、number、Time、boolean
193
+ # ip: (optional) ip
194
+ def user_set(distinct_id: nil, account_id: nil, properties: {}, ip: nil)
195
+ begin
196
+ _check_id(distinct_id, account_id)
197
+ _check_properties(:user_set, properties)
198
+ rescue TDAnalyticsError => e
199
+ @error_handler.handle(e)
200
+ return false
201
+ end
202
+
203
+ _internal_track(:user_set, distinct_id: distinct_id, account_id: account_id, properties: properties, ip: ip)
204
+ end
205
+
206
+ ##
207
+ # Set user properties, If such property had been set before, this message would be neglected
208
+ # distinct_id: (optional) distinct ID
209
+ # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
210
+ # properties: (optional) string、number、Time、boolean
211
+ # ip: (optional) ip
212
+ def user_set_once(distinct_id: nil, account_id: nil, properties: {}, ip: nil)
213
+ begin
214
+ _check_id(distinct_id, account_id)
215
+ _check_properties(:user_setOnce, properties)
216
+ rescue TDAnalyticsError => e
217
+ @error_handler.handle(e)
218
+ return false
219
+ end
220
+
221
+ _internal_track(:user_setOnce,
222
+ distinct_id: distinct_id,
223
+ account_id: account_id,
224
+ properties: properties,
225
+ ip: ip,
226
+ )
227
+ end
228
+
229
+ ##
230
+ # To append user properties of array type
231
+ # distinct_id: (optional) distinct ID
232
+ # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
233
+ # properties: (optional) string、number、Time、boolean
234
+ def user_append(distinct_id: nil, account_id: nil, properties: {})
235
+ begin
236
+ _check_id(distinct_id, account_id)
237
+ _check_properties(:user_append, properties)
238
+ rescue TDAnalyticsError => e
239
+ @error_handler.handle(e)
240
+ return false
241
+ end
242
+
243
+ _internal_track(:user_append,
244
+ distinct_id: distinct_id,
245
+ account_id: account_id,
246
+ properties: properties,
247
+ )
248
+ end
249
+
250
+ ##
251
+ # To append user properties of array type. It filters out duplicate values
252
+ # distinct_id: (optional) distinct ID
253
+ # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
254
+ # properties: (optional) string、number、Time、boolean
255
+ def user_uniq_append(distinct_id: nil, account_id: nil, properties: {})
256
+ begin
257
+ _check_id(distinct_id, account_id)
258
+ _check_properties(:user_uniq_append, properties)
259
+ rescue TDAnalyticsError => e
260
+ @error_handler.handle(e)
261
+ return false
262
+ end
263
+
264
+ _internal_track(:user_uniq_append,
265
+ distinct_id: distinct_id,
266
+ account_id: account_id,
267
+ properties: properties,
268
+ )
269
+ end
270
+
271
+ ##
272
+ # Clear the user properties of users
273
+ # distinct_id: (optional) distinct ID
274
+ # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
275
+ # properties: (optional) string、number、Time、boolean
276
+ def user_unset(distinct_id: nil, account_id: nil, property: nil)
277
+ properties = {}
278
+ if property.is_a?(Array)
279
+ property.each do |k|
280
+ properties[k] = 0
281
+ end
282
+ else
283
+ properties[property] = 0
284
+ end
285
+
286
+ begin
287
+ _check_id(distinct_id, account_id)
288
+ _check_properties(:user_unset, properties)
289
+ rescue TDAnalyticsError => e
290
+ @error_handler.handle(e)
291
+ return false
292
+ end
293
+
294
+ _internal_track(:user_unset,
295
+ distinct_id: distinct_id,
296
+ account_id: account_id,
297
+ properties: properties,
298
+ )
299
+ end
300
+
301
+ ##
302
+ # To accumulate operations against the property
303
+ # distinct_id: (optional) distinct ID
304
+ # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
305
+ # properties: (optional) string、number、Time、boolean
306
+ def user_add(distinct_id: nil, account_id: nil, properties: {})
307
+ begin
308
+ _check_id(distinct_id, account_id)
309
+ _check_properties(:user_add, properties)
310
+ rescue TDAnalyticsError => e
311
+ @error_handler.handle(e)
312
+ return false
313
+ end
314
+
315
+ _internal_track(:user_add,
316
+ distinct_id: distinct_id,
317
+ account_id: account_id,
318
+ properties: properties,
319
+ )
320
+ end
321
+
322
+ ##
323
+ # Delete a user, This operation cannot be undone
324
+ # distinct_id: (optional) distinct ID
325
+ # account_id: (optional) account ID. distinct_id, account_id can't both be empty.
326
+ def user_del(distinct_id: nil, account_id: nil)
327
+ begin
328
+ _check_id(distinct_id, account_id)
329
+ rescue TDAnalyticsError => e
330
+ @error_handler.handle(e)
331
+ return false
332
+ end
333
+
334
+ _internal_track(:user_del,
335
+ distinct_id: distinct_id,
336
+ account_id: account_id,
337
+ )
338
+ end
339
+
340
+ ##
341
+ # Report data immediately
342
+ def flush
343
+ TDLog.info("SDK flush data.")
344
+ return true unless defined? @consumer.flush
345
+ ret = true
346
+ begin
347
+ @consumer.flush
348
+ rescue TDAnalyticsError => e
349
+ @error_handler.handle(e)
350
+ ret = false
351
+ end
352
+ ret
353
+ end
354
+
355
+ ##
356
+ # Close and exit sdk
357
+ def close
358
+ return true unless defined? @consumer.close
359
+ ret = true
360
+ # Consumer 自身已有锁保护,无需在此加锁
361
+ begin
362
+ @consumer.close
363
+ rescue TDAnalyticsError => e
364
+ @error_handler.handle(e)
365
+ ret = false
366
+ end
367
+
368
+ TDLog.info("SDK close.")
369
+
370
+ ret
371
+ end
372
+
373
+ private
374
+
375
+ def _internal_track(type, properties: {}, event_name: nil, event_id:nil, account_id: nil, distinct_id: nil, ip: nil,first_check_id: nil, time: nil)
376
+ ret = true
377
+
378
+ # 只在访问共享状态时加锁
379
+ if type == :track || type == :track_update || type == :track_overwrite
380
+ @mutex.synchronize do
381
+ dynamic_properties = @dynamic_block.respond_to?(:call) ? @dynamic_block.call : {}
382
+ properties = LIB_PROPERTIES.merge(@super_properties).merge(dynamic_properties).merge(properties)
383
+ end
384
+ end
385
+
386
+ data = {
387
+ '#type' => type,
388
+ }
389
+
390
+ properties.each do |k, v|
391
+ if v.is_a?(Time)
392
+ properties[k] = _format_time(v)
393
+ end
394
+ end
395
+
396
+ _move_preset_properties([:'#ip', :"#time", :"#app_id", :"#uuid"], data, properties: properties)
397
+
398
+ if data[:'#time'] == nil
399
+ if time == nil
400
+ time = Time.now
401
+ end
402
+ data[:'#time'] = _format_time(time)
403
+ end
404
+
405
+ data['properties'] = properties
406
+ data['#event_name'] = event_name if (type == :track || type == :track_update || type == :track_overwrite)
407
+ data['#event_id'] = event_id if (type == :track_update || type == :track_overwrite)
408
+ data['#account_id'] = account_id if account_id
409
+ data['#distinct_id'] = distinct_id if distinct_id
410
+ data['#ip'] = ip if ip
411
+ data['#first_check_id'] = first_check_id if first_check_id
412
+ data[:'#uuid'] = SecureRandom.uuid if @uuid_enable and data[:'#uuid'] == nil
413
+
414
+ begin
415
+ @consumer.add(data)
416
+ rescue TDAnalyticsError => e
417
+ @error_handler.handle(e)
418
+ ret = false
419
+ end
420
+
421
+ ret
422
+ end
423
+
424
+ def _format_time(time)
425
+ time.strftime("%Y-%m-%d %H:%M:%S.#{((time.to_f * 1000.0).to_i % 1000).to_s.rjust(3, "0")}")
426
+ end
427
+
428
+ def _check_event_id(event_id)
429
+ if ThinkingData::get_stringent == false
430
+ return true
431
+ end
432
+
433
+ raise IllegalParameterError.new("the event_id or property cannot be nil") if event_id.nil?
434
+ true
435
+ end
436
+
437
+ def _check_name(name)
438
+ if ThinkingData::get_stringent == false
439
+ return true
440
+ end
441
+
442
+ raise IllegalParameterError.new("the name of event or property cannot be nil") if name.nil?
443
+
444
+ unless name.instance_of?(String) || name.instance_of?(Symbol)
445
+ raise IllegalParameterError.new("#{name} is invalid. It must be String or Symbol")
446
+ end
447
+ true
448
+ end
449
+
450
+ def _check_properties(type, properties)
451
+ if ThinkingData::get_stringent == false
452
+ return true
453
+ end
454
+
455
+ unless properties.instance_of? Hash
456
+ return false
457
+ end
458
+
459
+ properties.each do |k, v|
460
+ _check_name k
461
+ next if v.nil?
462
+ unless v.is_a?(Integer) || v.is_a?(Float) || v.is_a?(Symbol) || v.is_a?(String) || v.is_a?(Time) || !!v == v || v.is_a?(Array)
463
+ raise IllegalParameterError.new("The value of properties must be type in Integer, Float, Symbol, String, Array,and Time")
464
+ end
465
+
466
+ if type == :user_add
467
+ raise IllegalParameterError.new("Property value for user add must be numbers") unless v.is_a?(Integer) || v.is_a?(Float)
468
+ end
469
+ if v.is_a?(Array)
470
+ v.each_index do |i|
471
+ if v[i].is_a?(Time)
472
+ v[i] = _format_time(v[i])
473
+ end
474
+ end
475
+ end
476
+ end
477
+ true
478
+ end
479
+
480
+ def _check_id(distinct_id, account_id)
481
+ if ThinkingData::get_stringent == false
482
+ return true
483
+ end
484
+
485
+ raise IllegalParameterError.new("account id or distinct id must be provided.") if distinct_id.nil? && account_id.nil?
486
+ end
487
+
488
+ def _move_preset_properties(keys, data, properties: {})
489
+ property_keys = properties.keys
490
+ keys.each { |k|
491
+ if property_keys.include? k
492
+ data[k] = properties[k]
493
+ properties.delete(k)
494
+ end
495
+ }
496
+ end
497
+ end
498
+
499
+ ##
500
+ # SDK log module
501
+ class TDLog
502
+ def self.info(*msg)
503
+ if ThinkingData::get_enable_log
504
+ print("[ThinkingData][#{Time.now}] ")
505
+ puts(msg)
506
+ end
507
+ end
508
+ end
509
+ end