thinkingdata-ruby 1.2.0 → 2.0.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 +4 -4
- data/.gitignore +59 -0
- data/CHANGELOG.md +22 -14
- data/Gemfile +7 -7
- data/LICENSE +201 -201
- data/README.md +11 -202
- data/demo/demo.rb +104 -142
- data/lib/thinkingdata-ruby/td_analytics.rb +495 -0
- data/lib/thinkingdata-ruby/{batch_consumer.rb → td_batch_consumer.rb} +142 -120
- data/lib/thinkingdata-ruby/td_debug_consumer.rb +73 -0
- data/lib/thinkingdata-ruby/td_errors.rb +38 -0
- data/lib/thinkingdata-ruby/td_logger_consumer.rb +77 -0
- data/lib/thinkingdata-ruby/td_version.rb +3 -0
- data/lib/thinkingdata-ruby.rb +5 -5
- data/thinkingdata-ruby.gemspec +16 -16
- metadata +13 -12
- data/lib/thinkingdata-ruby/debug_consumer.rb +0 -61
- data/lib/thinkingdata-ruby/errors.rb +0 -35
- data/lib/thinkingdata-ruby/logger_consumer.rb +0 -57
- data/lib/thinkingdata-ruby/tracker.rb +0 -402
- data/lib/thinkingdata-ruby/version.rb +0 -3
@@ -0,0 +1,495 @@
|
|
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
|