thinkingdata-ruby 1.2.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +2 -1
- data/demo/demo.rb +38 -39
- data/lib/thinkingdata-ruby/{tracker.rb → td_analytics.rb} +142 -42
- data/lib/thinkingdata-ruby/{batch_consumer.rb → td_batch_consumer.rb} +48 -12
- data/lib/thinkingdata-ruby/{debug_consumer.rb → td_debug_consumer.rb} +21 -15
- data/lib/thinkingdata-ruby/{errors.rb → td_errors.rb} +18 -10
- data/lib/thinkingdata-ruby/{logger_consumer.rb → td_logger_consumer.rb} +33 -12
- data/lib/thinkingdata-ruby/td_version.rb +3 -0
- data/lib/thinkingdata-ruby.rb +5 -5
- data/thinkingdata-ruby.gemspec +2 -2
- metadata +9 -9
- data/lib/thinkingdata-ruby/version.rb +0 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba7de0dac467fe8c90747854c7701e0af035463b9b48b4708308e4c28d242c90
|
|
4
|
+
data.tar.gz: fa29bd005cbe4b32cceecd85d726e79b7b0e0fb4e51346b649f25d7b44affcca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e73fadf565e9143fd67ad1c99e1472ee027d7eda707a4b2452705b620d05d1f223b71e99d61b84c0bbb353792f2f1f62212c7d2c04e670c736ee5e94db74291e
|
|
7
|
+
data.tar.gz: 39fce15278418ba6d92dface6276acee8760953954ff5521fb115f91c81bf83807e063e7caf6a59db0fd8e388407f74a7a0748d02b99086d3de91c8f2b0ffd31
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
### v2.0.1
|
|
2
|
+
**Date:** 2026/05/13
|
|
3
|
+
|
|
4
|
+
**Notes:**
|
|
5
|
+
|
|
6
|
+
* Supports multi-threaded invocation
|
|
7
|
+
|
|
8
|
+
### v2.0.0
|
|
9
|
+
**Date:** 2023/09/20
|
|
10
|
+
|
|
11
|
+
**Notes:**
|
|
12
|
+
|
|
13
|
+
* Modified module name, incompatible with older versions
|
|
14
|
+
|
|
1
15
|
### v1.2.1
|
|
2
16
|
**Date:** 2023/03/20
|
|
3
17
|
|
|
@@ -6,7 +20,6 @@
|
|
|
6
20
|
* Compatible ruby 3
|
|
7
21
|
* Supports a different '#app_id' for each event
|
|
8
22
|
|
|
9
|
-
|
|
10
23
|
### v1.2.0
|
|
11
24
|
**Date:** 2020/08/28
|
|
12
25
|
|
data/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# ThinkingData SDK for Ruby
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
<img src="https://user-images.githubusercontent.com/53337625/205621683-ed9b97ef-6a52-4903-a2c0-a955dddebb7d.png" alt="logo" width="50%"/>
|
|
3
4
|
|
|
4
5
|
This is the [ThinkingData](https://www.thinkingdata.cn)™ SDK for Ruby. Documentation is available on our help center in the following languages:
|
|
5
6
|
|
data/demo/demo.rb
CHANGED
|
@@ -4,38 +4,38 @@ require 'thinkingdata-ruby'
|
|
|
4
4
|
require 'time'
|
|
5
5
|
|
|
6
6
|
if __FILE__ == $0
|
|
7
|
-
DEMO_APPID = 'app id'
|
|
8
|
-
SERVER_URL = 'server url'
|
|
9
|
-
DEMO_ACCOUNT_ID = '123'
|
|
10
|
-
DEMO_DISTINCT_ID = 'aaa'
|
|
11
7
|
|
|
12
|
-
class MyErrorHandler <
|
|
8
|
+
class MyErrorHandler < ThinkingData::TDErrorHandler
|
|
13
9
|
def handle(error)
|
|
14
10
|
puts error
|
|
15
11
|
raise error
|
|
16
12
|
end
|
|
17
13
|
end
|
|
18
|
-
my_error_handler = MyErrorHandler.new
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
consumer =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
consumer = TDAnalytics::BatchConsumer.new(SERVER_URL, DEMO_APPID, 30)
|
|
33
|
-
#consumer._set_compress(false)
|
|
34
|
-
else
|
|
35
|
-
consumer = TDAnalytics::LoggerConsumer.new
|
|
15
|
+
def logger_consumer
|
|
16
|
+
ThinkingData::TDLoggerConsumer.new('./log', 'hourly')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def debug_consumer
|
|
20
|
+
ThinkingData::TDDebugConsumer.new("serverUrl", "appId", device_id: "123456789")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def batch_consumer
|
|
24
|
+
consumer = ThinkingData::TDBatchConsumer.new("serverUrl", "appId", 50)
|
|
25
|
+
consumer._set_compress(false)
|
|
26
|
+
consumer
|
|
36
27
|
end
|
|
37
28
|
|
|
38
|
-
|
|
29
|
+
ThinkingData::set_stringent(false)
|
|
30
|
+
ThinkingData::set_enable_log(true)
|
|
31
|
+
my_error_handler = MyErrorHandler.new
|
|
32
|
+
|
|
33
|
+
td_sdk = ThinkingData::TDAnalytics.new(logger_consumer, my_error_handler, uuid: false)
|
|
34
|
+
# td_sdk = ThinkingData::TDAnalytics.new(debug_consumer, my_error_handler, uuid: true)
|
|
35
|
+
# td_sdk = ThinkingData::TDAnalytics.new(batch_consumer, my_error_handler, uuid: true)
|
|
36
|
+
|
|
37
|
+
DEMO_ACCOUNT_ID = '123'
|
|
38
|
+
DEMO_DISTINCT_ID = 'aaa'
|
|
39
39
|
|
|
40
40
|
super_properties = {
|
|
41
41
|
super_string: 'super_string',
|
|
@@ -45,7 +45,7 @@ if __FILE__ == $0
|
|
|
45
45
|
'#app_id': "123123123123123"
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
td_sdk.set_super_properties(super_properties)
|
|
49
49
|
|
|
50
50
|
properties = {
|
|
51
51
|
array: ["str1", "11", Time.now, "2020-02-11 17:02:52.415"],
|
|
@@ -54,19 +54,18 @@ if __FILE__ == $0
|
|
|
54
54
|
prop_string: 'hello world',
|
|
55
55
|
prop_bool: true,
|
|
56
56
|
'#ip': '123.123.123.123',
|
|
57
|
-
'#uuid': 'aaabbbccc',
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
|
|
59
|
+
td_sdk.set_dynamic_super_properties do
|
|
61
60
|
{:dynamic_time => Time.now}
|
|
62
61
|
end
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
td_sdk.track(event_name: 'test_event', distinct_id: DEMO_DISTINCT_ID, account_id: DEMO_ACCOUNT_ID, properties: properties)
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
td_sdk.clear_dynamic_super_properties
|
|
66
|
+
td_sdk.clear_super_properties
|
|
68
67
|
|
|
69
|
-
|
|
68
|
+
td_sdk.track(event_name: 'test_event', distinct_id: DEMO_DISTINCT_ID, account_id: DEMO_ACCOUNT_ID, properties: properties)
|
|
70
69
|
|
|
71
70
|
user_data = {
|
|
72
71
|
array: ["str1", 11, 22.22],
|
|
@@ -75,31 +74,31 @@ if __FILE__ == $0
|
|
|
75
74
|
prop_string: 'hello',
|
|
76
75
|
prop_int: 666,
|
|
77
76
|
}
|
|
78
|
-
|
|
77
|
+
td_sdk.user_set(distinct_id: DEMO_DISTINCT_ID, account_id: DEMO_ACCOUNT_ID, properties: user_data)
|
|
79
78
|
|
|
80
79
|
user_append_data = {
|
|
81
80
|
array: %w[33 44]
|
|
82
81
|
}
|
|
83
|
-
|
|
82
|
+
td_sdk.user_append(distinct_id: DEMO_DISTINCT_ID, account_id: DEMO_ACCOUNT_ID, properties: user_append_data)
|
|
84
83
|
|
|
85
84
|
user_uniq_append_data = {
|
|
86
85
|
array: %w[44 55]
|
|
87
86
|
}
|
|
88
|
-
|
|
87
|
+
td_sdk.user_uniq_append(distinct_id: DEMO_DISTINCT_ID, account_id: DEMO_ACCOUNT_ID, properties: user_uniq_append_data)
|
|
89
88
|
|
|
90
89
|
user_set_once_data = {
|
|
91
90
|
prop_int_new: 888,
|
|
92
91
|
}
|
|
93
|
-
|
|
92
|
+
td_sdk.user_set_once(distinct_id: DEMO_DISTINCT_ID, account_id: DEMO_ACCOUNT_ID, properties: user_set_once_data)
|
|
94
93
|
|
|
95
|
-
|
|
94
|
+
td_sdk.user_add(distinct_id: DEMO_DISTINCT_ID, properties: {prop_int: 10, prop_double: 15.88})
|
|
96
95
|
|
|
97
|
-
|
|
96
|
+
td_sdk.user_unset(distinct_id: DEMO_DISTINCT_ID, property: [:prop_string, :prop_int])
|
|
98
97
|
|
|
99
|
-
|
|
98
|
+
td_sdk.user_del(distinct_id: DEMO_DISTINCT_ID)
|
|
100
99
|
|
|
101
|
-
|
|
100
|
+
td_sdk.flush
|
|
102
101
|
|
|
103
|
-
|
|
102
|
+
td_sdk.close
|
|
104
103
|
end
|
|
105
104
|
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
require 'securerandom'
|
|
2
|
-
require 'thinkingdata-ruby/
|
|
3
|
-
require 'thinkingdata-ruby/
|
|
2
|
+
require 'thinkingdata-ruby/td_errors'
|
|
3
|
+
require 'thinkingdata-ruby/td_version'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
##
|
|
6
|
+
# ThinkingData module
|
|
7
|
+
module ThinkingData
|
|
6
8
|
@is_enable_log = false
|
|
7
9
|
@is_stringent = false
|
|
8
10
|
|
|
11
|
+
##
|
|
12
|
+
# Enable SDK log or not
|
|
13
|
+
# @param enable [Boolean] true or false
|
|
9
14
|
def self.set_enable_log(enable)
|
|
10
15
|
unless [true, false].include? enable
|
|
11
16
|
enable = false
|
|
@@ -13,10 +18,16 @@ module TDAnalytics
|
|
|
13
18
|
@is_enable_log = enable
|
|
14
19
|
end
|
|
15
20
|
|
|
21
|
+
##
|
|
22
|
+
# Get log status
|
|
23
|
+
# @return [Boolean] enable or not
|
|
16
24
|
def self.get_enable_log
|
|
17
25
|
@is_enable_log
|
|
18
26
|
end
|
|
19
27
|
|
|
28
|
+
##
|
|
29
|
+
# Check or not parameter
|
|
30
|
+
# @param enable [Boolean] check or not
|
|
20
31
|
def self.set_stringent(enable)
|
|
21
32
|
unless [true, false].include? enable
|
|
22
33
|
enable = false
|
|
@@ -24,53 +35,81 @@ module TDAnalytics
|
|
|
24
35
|
@is_stringent = enable
|
|
25
36
|
end
|
|
26
37
|
|
|
38
|
+
##
|
|
39
|
+
# Get parameter check status of SDK
|
|
40
|
+
# @return [Boolean] check or not
|
|
27
41
|
def self.get_stringent
|
|
28
42
|
@is_stringent
|
|
29
43
|
end
|
|
30
44
|
|
|
31
|
-
|
|
45
|
+
##
|
|
46
|
+
# Analytics class。 Provides the function of tracking data
|
|
47
|
+
class TDAnalytics
|
|
32
48
|
LIB_PROPERTIES = {
|
|
33
49
|
'#lib' => 'ruby',
|
|
34
|
-
'#lib_version' =>
|
|
50
|
+
'#lib_version' => ThinkingData::VERSION,
|
|
35
51
|
}
|
|
36
52
|
|
|
37
|
-
|
|
53
|
+
@dynamic_block = nil
|
|
38
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
|
|
39
60
|
def initialize(consumer, error_handler = nil, uuid: false)
|
|
40
|
-
@error_handler = error_handler ||
|
|
61
|
+
@error_handler = error_handler || TDErrorHandler.new
|
|
41
62
|
@consumer = consumer
|
|
42
63
|
@super_properties = {}
|
|
43
|
-
@
|
|
64
|
+
@uuid_enable = uuid
|
|
65
|
+
@mutex = Mutex.new
|
|
66
|
+
TDLog.info("SDK init success.")
|
|
44
67
|
end
|
|
45
68
|
|
|
46
|
-
|
|
69
|
+
##
|
|
70
|
+
# Set common properties
|
|
47
71
|
def set_super_properties(properties, skip_local_check = false)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
57
83
|
end
|
|
58
84
|
end
|
|
59
85
|
end
|
|
60
86
|
|
|
87
|
+
##
|
|
88
|
+
# Clear super properties
|
|
61
89
|
def clear_super_properties
|
|
62
|
-
@
|
|
90
|
+
@mutex.synchronize do
|
|
91
|
+
@super_properties = {}
|
|
92
|
+
end
|
|
63
93
|
end
|
|
64
94
|
|
|
95
|
+
##
|
|
96
|
+
# Set dynamic super properties
|
|
65
97
|
def set_dynamic_super_properties(&block)
|
|
66
|
-
|
|
98
|
+
@mutex.synchronize do
|
|
99
|
+
@dynamic_block = block
|
|
100
|
+
end
|
|
67
101
|
end
|
|
68
102
|
|
|
103
|
+
##
|
|
104
|
+
# Clear dynamic super properties
|
|
69
105
|
def clear_dynamic_super_properties
|
|
70
|
-
|
|
106
|
+
@mutex.synchronize do
|
|
107
|
+
@dynamic_block = nil
|
|
108
|
+
end
|
|
71
109
|
end
|
|
72
110
|
|
|
73
|
-
|
|
111
|
+
##
|
|
112
|
+
# Report ordinary event
|
|
74
113
|
# event_name: (require) A string of 50 letters and digits that starts with '#' or a letter
|
|
75
114
|
# distinct_id: (optional) distinct ID
|
|
76
115
|
# account_id: (optional) account ID. distinct_id, account_id can't both be empty.
|
|
@@ -94,7 +133,16 @@ module TDAnalytics
|
|
|
94
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)
|
|
95
134
|
end
|
|
96
135
|
|
|
97
|
-
|
|
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
|
|
98
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)
|
|
99
147
|
begin
|
|
100
148
|
_check_name event_name
|
|
@@ -111,7 +159,16 @@ module TDAnalytics
|
|
|
111
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)
|
|
112
160
|
end
|
|
113
161
|
|
|
114
|
-
|
|
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
|
|
115
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)
|
|
116
173
|
begin
|
|
117
174
|
_check_name event_name
|
|
@@ -128,7 +185,12 @@ module TDAnalytics
|
|
|
128
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)
|
|
129
186
|
end
|
|
130
187
|
|
|
131
|
-
|
|
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
|
|
132
194
|
def user_set(distinct_id: nil, account_id: nil, properties: {}, ip: nil)
|
|
133
195
|
begin
|
|
134
196
|
_check_id(distinct_id, account_id)
|
|
@@ -141,7 +203,12 @@ module TDAnalytics
|
|
|
141
203
|
_internal_track(:user_set, distinct_id: distinct_id, account_id: account_id, properties: properties, ip: ip)
|
|
142
204
|
end
|
|
143
205
|
|
|
144
|
-
|
|
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
|
|
145
212
|
def user_set_once(distinct_id: nil, account_id: nil, properties: {}, ip: nil)
|
|
146
213
|
begin
|
|
147
214
|
_check_id(distinct_id, account_id)
|
|
@@ -159,7 +226,11 @@ module TDAnalytics
|
|
|
159
226
|
)
|
|
160
227
|
end
|
|
161
228
|
|
|
162
|
-
|
|
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
|
|
163
234
|
def user_append(distinct_id: nil, account_id: nil, properties: {})
|
|
164
235
|
begin
|
|
165
236
|
_check_id(distinct_id, account_id)
|
|
@@ -176,6 +247,11 @@ module TDAnalytics
|
|
|
176
247
|
)
|
|
177
248
|
end
|
|
178
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
|
|
179
255
|
def user_uniq_append(distinct_id: nil, account_id: nil, properties: {})
|
|
180
256
|
begin
|
|
181
257
|
_check_id(distinct_id, account_id)
|
|
@@ -192,7 +268,11 @@ module TDAnalytics
|
|
|
192
268
|
)
|
|
193
269
|
end
|
|
194
270
|
|
|
195
|
-
|
|
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
|
|
196
276
|
def user_unset(distinct_id: nil, account_id: nil, property: nil)
|
|
197
277
|
properties = {}
|
|
198
278
|
if property.is_a?(Array)
|
|
@@ -218,7 +298,11 @@ module TDAnalytics
|
|
|
218
298
|
)
|
|
219
299
|
end
|
|
220
300
|
|
|
221
|
-
|
|
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
|
|
222
306
|
def user_add(distinct_id: nil, account_id: nil, properties: {})
|
|
223
307
|
begin
|
|
224
308
|
_check_id(distinct_id, account_id)
|
|
@@ -235,7 +319,10 @@ module TDAnalytics
|
|
|
235
319
|
)
|
|
236
320
|
end
|
|
237
321
|
|
|
238
|
-
|
|
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.
|
|
239
326
|
def user_del(distinct_id: nil, account_id: nil)
|
|
240
327
|
begin
|
|
241
328
|
_check_id(distinct_id, account_id)
|
|
@@ -250,8 +337,10 @@ module TDAnalytics
|
|
|
250
337
|
)
|
|
251
338
|
end
|
|
252
339
|
|
|
253
|
-
|
|
340
|
+
##
|
|
341
|
+
# Report data immediately
|
|
254
342
|
def flush
|
|
343
|
+
TDLog.info("SDK flush data.")
|
|
255
344
|
return true unless defined? @consumer.flush
|
|
256
345
|
ret = true
|
|
257
346
|
begin
|
|
@@ -263,25 +352,35 @@ module TDAnalytics
|
|
|
263
352
|
ret
|
|
264
353
|
end
|
|
265
354
|
|
|
355
|
+
##
|
|
266
356
|
# Close and exit sdk
|
|
267
357
|
def close
|
|
268
358
|
return true unless defined? @consumer.close
|
|
269
359
|
ret = true
|
|
360
|
+
# Consumer 自身已有锁保护,无需在此加锁
|
|
270
361
|
begin
|
|
271
362
|
@consumer.close
|
|
272
363
|
rescue TDAnalyticsError => e
|
|
273
364
|
@error_handler.handle(e)
|
|
274
365
|
ret = false
|
|
275
366
|
end
|
|
367
|
+
|
|
368
|
+
TDLog.info("SDK close.")
|
|
369
|
+
|
|
276
370
|
ret
|
|
277
371
|
end
|
|
278
372
|
|
|
279
373
|
private
|
|
280
374
|
|
|
281
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
|
+
# 只在访问共享状态时加锁
|
|
282
379
|
if type == :track || type == :track_update || type == :track_overwrite
|
|
283
|
-
|
|
284
|
-
|
|
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
|
|
285
384
|
end
|
|
286
385
|
|
|
287
386
|
data = {
|
|
@@ -310,9 +409,8 @@ module TDAnalytics
|
|
|
310
409
|
data['#distinct_id'] = distinct_id if distinct_id
|
|
311
410
|
data['#ip'] = ip if ip
|
|
312
411
|
data['#first_check_id'] = first_check_id if first_check_id
|
|
313
|
-
data[:'#uuid'] = SecureRandom.uuid if @
|
|
412
|
+
data[:'#uuid'] = SecureRandom.uuid if @uuid_enable and data[:'#uuid'] == nil
|
|
314
413
|
|
|
315
|
-
ret = true
|
|
316
414
|
begin
|
|
317
415
|
@consumer.add(data)
|
|
318
416
|
rescue TDAnalyticsError => e
|
|
@@ -328,7 +426,7 @@ module TDAnalytics
|
|
|
328
426
|
end
|
|
329
427
|
|
|
330
428
|
def _check_event_id(event_id)
|
|
331
|
-
if
|
|
429
|
+
if ThinkingData::get_stringent == false
|
|
332
430
|
return true
|
|
333
431
|
end
|
|
334
432
|
|
|
@@ -337,7 +435,7 @@ module TDAnalytics
|
|
|
337
435
|
end
|
|
338
436
|
|
|
339
437
|
def _check_name(name)
|
|
340
|
-
if
|
|
438
|
+
if ThinkingData::get_stringent == false
|
|
341
439
|
return true
|
|
342
440
|
end
|
|
343
441
|
|
|
@@ -350,7 +448,7 @@ module TDAnalytics
|
|
|
350
448
|
end
|
|
351
449
|
|
|
352
450
|
def _check_properties(type, properties)
|
|
353
|
-
if
|
|
451
|
+
if ThinkingData::get_stringent == false
|
|
354
452
|
return true
|
|
355
453
|
end
|
|
356
454
|
|
|
@@ -380,7 +478,7 @@ module TDAnalytics
|
|
|
380
478
|
end
|
|
381
479
|
|
|
382
480
|
def _check_id(distinct_id, account_id)
|
|
383
|
-
if
|
|
481
|
+
if ThinkingData::get_stringent == false
|
|
384
482
|
return true
|
|
385
483
|
end
|
|
386
484
|
|
|
@@ -398,10 +496,12 @@ module TDAnalytics
|
|
|
398
496
|
end
|
|
399
497
|
end
|
|
400
498
|
|
|
401
|
-
|
|
499
|
+
##
|
|
500
|
+
# SDK log module
|
|
501
|
+
class TDLog
|
|
402
502
|
def self.info(*msg)
|
|
403
|
-
if
|
|
404
|
-
print("[
|
|
503
|
+
if ThinkingData::get_enable_log
|
|
504
|
+
print("[ThinkingData][#{Time.now}] ")
|
|
405
505
|
puts(msg)
|
|
406
506
|
end
|
|
407
507
|
end
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
require 'json'
|
|
2
2
|
require 'net/http'
|
|
3
|
+
require 'stringio'
|
|
3
4
|
|
|
4
|
-
module
|
|
5
|
-
|
|
5
|
+
module ThinkingData
|
|
6
|
+
##
|
|
7
|
+
# Upload data by http
|
|
8
|
+
class TDBatchConsumer
|
|
6
9
|
|
|
7
10
|
# buffer count
|
|
8
11
|
DEFAULT_LENGTH = 20
|
|
9
12
|
MAX_LENGTH = 2000
|
|
10
13
|
|
|
14
|
+
##
|
|
15
|
+
# Init batch consumer
|
|
11
16
|
def initialize(server_url, app_id, max_buffer_length = DEFAULT_LENGTH)
|
|
12
17
|
@server_uri = URI.parse(server_url)
|
|
13
18
|
@server_uri.path = '/sync_server'
|
|
@@ -15,24 +20,52 @@ module TDAnalytics
|
|
|
15
20
|
@compress = true
|
|
16
21
|
@max_length = [max_buffer_length, MAX_LENGTH].min
|
|
17
22
|
@buffers = []
|
|
23
|
+
@mutex = Mutex.new
|
|
24
|
+
TDLog.info("TDBatchConsumer init success. ServerUrl: #{server_url}, appId: #{app_id}")
|
|
18
25
|
end
|
|
19
26
|
|
|
27
|
+
##
|
|
28
|
+
# http request compress
|
|
29
|
+
# @param compress [Boolean] compress or not
|
|
30
|
+
# @deprecated please use: set_compress
|
|
20
31
|
def _set_compress(compress)
|
|
21
32
|
@compress = compress
|
|
22
33
|
end
|
|
23
34
|
|
|
35
|
+
##
|
|
36
|
+
# http request compress
|
|
37
|
+
# @param compress [Boolean] compress or not
|
|
38
|
+
def set_compress(compress)
|
|
39
|
+
@compress = compress
|
|
40
|
+
end
|
|
41
|
+
|
|
24
42
|
def add(message)
|
|
25
|
-
@buffers
|
|
26
|
-
|
|
43
|
+
TDLog.info("Enqueue data to buffer. buffer size: #{@buffers.length}, data: #{message}")
|
|
44
|
+
need_flush = false
|
|
45
|
+
@mutex.synchronize do
|
|
46
|
+
@buffers << message
|
|
47
|
+
need_flush = @buffers.length >= @max_length
|
|
48
|
+
end
|
|
49
|
+
flush if need_flush
|
|
27
50
|
end
|
|
28
51
|
|
|
29
52
|
def close
|
|
30
53
|
flush
|
|
54
|
+
TDLog.info("TDBatchConsumer close.")
|
|
31
55
|
end
|
|
32
56
|
|
|
33
57
|
def flush
|
|
58
|
+
TDLog.info("TDBatchConsumer flush data.")
|
|
59
|
+
data_to_send = nil
|
|
60
|
+
@mutex.synchronize do
|
|
61
|
+
data_to_send = @buffers.dup
|
|
62
|
+
@buffers = []
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
return if data_to_send.empty?
|
|
66
|
+
|
|
34
67
|
begin
|
|
35
|
-
|
|
68
|
+
data_to_send.each_slice(@max_length) do |chunk|
|
|
36
69
|
if @compress
|
|
37
70
|
wio = StringIO.new("w")
|
|
38
71
|
gzip_io = Zlib::GzipWriter.new(wio)
|
|
@@ -46,17 +79,19 @@ module TDAnalytics
|
|
|
46
79
|
headers = {'Content-Type' => 'application/plaintext',
|
|
47
80
|
'appid' => @app_id,
|
|
48
81
|
'compress' => compress_type,
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
82
|
+
'TE-Integration-Type'=>'Ruby',
|
|
83
|
+
'TE-Integration-Version'=>ThinkingData::VERSION,
|
|
84
|
+
'TE-Integration-Count'=>data_to_send.count,
|
|
52
85
|
'TA_Integration-Extra'=>'batch'}
|
|
53
86
|
request = CaseSensitivePost.new(@server_uri.request_uri, headers)
|
|
54
87
|
request.body = data
|
|
55
88
|
|
|
89
|
+
TDLog.info("Send data, request: #{data}")
|
|
56
90
|
begin
|
|
57
91
|
response_code, response_body = _request(@server_uri, request)
|
|
92
|
+
TDLog.info("Send data, response: #{response_body}")
|
|
58
93
|
rescue => e
|
|
59
|
-
raise ConnectionError.new("Could not connect to
|
|
94
|
+
raise ConnectionError.new("Could not connect to TE server, with error \"#{e.message}\".")
|
|
60
95
|
end
|
|
61
96
|
|
|
62
97
|
result = {}
|
|
@@ -64,18 +99,17 @@ module TDAnalytics
|
|
|
64
99
|
begin
|
|
65
100
|
result = JSON.parse(response_body.to_s)
|
|
66
101
|
rescue JSON::JSONError
|
|
67
|
-
raise ServerError.new("Could not interpret
|
|
102
|
+
raise ServerError.new("Could not interpret TE server response: '#{response_body}'")
|
|
68
103
|
end
|
|
69
104
|
end
|
|
70
105
|
|
|
71
106
|
if result['code'] != 0
|
|
72
|
-
raise ServerError.new("Could not write to
|
|
107
|
+
raise ServerError.new("Could not write to TE, server responded with #{response_code} returning: '#{response_body}'")
|
|
73
108
|
end
|
|
74
109
|
end
|
|
75
110
|
rescue
|
|
76
111
|
raise
|
|
77
112
|
end
|
|
78
|
-
@buffers = []
|
|
79
113
|
end
|
|
80
114
|
|
|
81
115
|
private
|
|
@@ -92,6 +126,8 @@ module TDAnalytics
|
|
|
92
126
|
end
|
|
93
127
|
end
|
|
94
128
|
|
|
129
|
+
##
|
|
130
|
+
# Private class. Send data tools
|
|
95
131
|
class CaseSensitivePost < Net::HTTP::Post
|
|
96
132
|
def initialize_http_header(headers)
|
|
97
133
|
@header = {}
|
|
@@ -1,37 +1,43 @@
|
|
|
1
1
|
require 'json'
|
|
2
2
|
require 'net/http'
|
|
3
3
|
|
|
4
|
-
module
|
|
4
|
+
module ThinkingData
|
|
5
|
+
##
|
|
5
6
|
# The data is reported one by one, and when an error occurs, the log will be printed on the console.
|
|
6
|
-
class
|
|
7
|
-
|
|
8
|
-
def test
|
|
9
|
-
|
|
10
|
-
end
|
|
7
|
+
class TDDebugConsumer
|
|
11
8
|
|
|
9
|
+
##
|
|
10
|
+
# Init debug consumer
|
|
11
|
+
# @param server_url: server url
|
|
12
|
+
# @param app_id: app id
|
|
13
|
+
# @param write_data: is write data to TE
|
|
14
|
+
# @param device_id: device id
|
|
12
15
|
def initialize(server_url, app_id, write_data = true, device_id: nil)
|
|
13
16
|
@server_uri = URI.parse(server_url)
|
|
14
17
|
@server_uri.path = '/data_debug'
|
|
15
18
|
@app_id = app_id
|
|
16
19
|
@write_data = write_data
|
|
17
20
|
@device_id = device_id
|
|
21
|
+
TDLog.info("TDDebugConsumer init success. ServerUrl: #{server_url}, appId: #{app_id}, deviceId: #{device_id}")
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
def add(message)
|
|
21
|
-
|
|
25
|
+
msg_json_str = message.to_json
|
|
26
|
+
TDLog.info("Send data, request: #{msg_json_str}")
|
|
22
27
|
headers = {
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
28
|
+
'TE-Integration-Type'=>'Ruby',
|
|
29
|
+
'TE-Integration-Version'=>ThinkingData::VERSION,
|
|
30
|
+
'TE-Integration-Count'=>'1',
|
|
26
31
|
'TA_Integration-Extra'=>'debug'
|
|
27
32
|
}
|
|
28
|
-
form_data = {"data" =>
|
|
33
|
+
form_data = {"data" => msg_json_str, "appid" => @app_id, "dryRun" => @write_data ? "0" : "1", "source" => "server"}
|
|
29
34
|
@device_id.is_a?(String) ? form_data["deviceId"] = @device_id : nil
|
|
30
35
|
|
|
31
36
|
begin
|
|
32
37
|
response_code, response_body = request(@server_uri, form_data,headers)
|
|
38
|
+
TDLog.info("Send data, response: #{response_body}")
|
|
33
39
|
rescue => e
|
|
34
|
-
raise ConnectionError.new("Could not connect to
|
|
40
|
+
raise ConnectionError.new("Could not connect to TE server, with error \"#{e.message}\".")
|
|
35
41
|
end
|
|
36
42
|
|
|
37
43
|
result = {}
|
|
@@ -39,17 +45,17 @@ module TDAnalytics
|
|
|
39
45
|
begin
|
|
40
46
|
result = JSON.parse(response_body.to_s)
|
|
41
47
|
rescue JSON::JSONError
|
|
42
|
-
raise ServerError.new("Could not interpret
|
|
48
|
+
raise ServerError.new("Could not interpret TE server response: '#{response_body}'")
|
|
43
49
|
end
|
|
44
50
|
end
|
|
45
51
|
|
|
46
52
|
if result['errorLevel'] != 0
|
|
47
|
-
raise ServerError.new("Could not write to
|
|
53
|
+
raise ServerError.new("Could not write to TE, server responded with #{response_code} returning: '#{response_body}'")
|
|
48
54
|
end
|
|
49
55
|
end
|
|
50
56
|
|
|
51
57
|
def request(uri, form_data,headers)
|
|
52
|
-
request = Net::HTTP::Post.new(uri.request_uri,headers)
|
|
58
|
+
request = Net::HTTP::Post.new(uri.request_uri, headers)
|
|
53
59
|
request.set_form_data(form_data)
|
|
54
60
|
|
|
55
61
|
client = Net::HTTP.new(uri.host, uri.port)
|
|
@@ -1,17 +1,25 @@
|
|
|
1
|
-
module
|
|
2
|
-
|
|
1
|
+
module ThinkingData
|
|
2
|
+
##
|
|
3
|
+
# SDK error
|
|
3
4
|
TDAnalyticsError = Class.new(StandardError)
|
|
4
5
|
|
|
6
|
+
##
|
|
7
|
+
# SDK error: illegal parameter
|
|
5
8
|
IllegalParameterError = Class.new(TDAnalyticsError)
|
|
6
|
-
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# SDK error: connection error
|
|
7
12
|
ConnectionError = Class.new(TDAnalyticsError)
|
|
8
|
-
|
|
9
|
-
ServerError = Class.new(TDAnalyticsError)
|
|
10
13
|
|
|
14
|
+
##
|
|
15
|
+
# SDK error: server error
|
|
16
|
+
ServerError = Class.new(TDAnalyticsError)
|
|
11
17
|
|
|
12
|
-
|
|
18
|
+
##
|
|
19
|
+
# Error handler
|
|
13
20
|
#
|
|
14
|
-
#
|
|
21
|
+
# e.g.
|
|
22
|
+
# class MyErrorHandler < ThinkingData::ErrorHandler
|
|
15
23
|
# def handle(error)
|
|
16
24
|
# puts error
|
|
17
25
|
# raise error
|
|
@@ -19,9 +27,9 @@ module TDAnalytics
|
|
|
19
27
|
# end
|
|
20
28
|
#
|
|
21
29
|
# my_error_handler = MyErrorHandler.new
|
|
22
|
-
# tracker = TDAnalytics
|
|
23
|
-
class
|
|
24
|
-
|
|
30
|
+
# tracker = ThinkingData::TDAnalytics.new(consumer, my_error_handler)
|
|
31
|
+
class TDErrorHandler
|
|
32
|
+
##
|
|
25
33
|
# Override #handle to customize error handling
|
|
26
34
|
def handle(error)
|
|
27
35
|
false
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
require 'logger'
|
|
2
|
-
require 'thinkingdata-ruby/
|
|
2
|
+
require 'thinkingdata-ruby/td_errors'
|
|
3
3
|
|
|
4
|
-
module
|
|
4
|
+
module ThinkingData
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
##
|
|
7
|
+
# Dismantle the header and save it under another name
|
|
7
8
|
class HeadlessLogger < Logger
|
|
8
9
|
def initialize(logdev, shift_age = 0, shift_size = 1048576)
|
|
9
|
-
super(nil
|
|
10
|
+
super(nil)
|
|
10
11
|
if logdev
|
|
11
12
|
@logdev = HeadlessLogger::LogDevice.new(logdev, shift_age: shift_age, shift_size: shift_size)
|
|
12
13
|
end
|
|
@@ -17,9 +18,15 @@ module TDAnalytics
|
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
##
|
|
22
|
+
# Write data to file, it works with LogBus
|
|
23
|
+
class TDLoggerConsumer
|
|
22
24
|
|
|
25
|
+
##
|
|
26
|
+
# Init logger consumer
|
|
27
|
+
# @param log_path: log file's path
|
|
28
|
+
# @param mode: file rotate mode
|
|
29
|
+
# @param prefix: file prefix
|
|
23
30
|
def initialize(log_path='.', mode='daily', prefix:'te.log')
|
|
24
31
|
case mode
|
|
25
32
|
when 'hourly'
|
|
@@ -35,20 +42,34 @@ module TDAnalytics
|
|
|
35
42
|
@current_suffix = Time.now.strftime(@suffix_mode)
|
|
36
43
|
@log_path = log_path
|
|
37
44
|
@full_prefix = "#{log_path}/#{prefix}"
|
|
45
|
+
@mutex = Mutex.new
|
|
46
|
+
TDLog.info("TDLoggerConsumer init success. LogPath: #{log_path}")
|
|
38
47
|
_reset
|
|
39
48
|
end
|
|
40
49
|
|
|
41
50
|
def add(msg)
|
|
42
|
-
|
|
43
|
-
@
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
@mutex.synchronize do
|
|
52
|
+
unless Time.now.strftime(@suffix_mode) == @current_suffix
|
|
53
|
+
@logger.close
|
|
54
|
+
@current_suffix = Time.now.strftime(@suffix_mode)
|
|
55
|
+
_reset
|
|
56
|
+
end
|
|
57
|
+
msg_json_str = msg.to_json
|
|
58
|
+
TDLog.info("Write data to file: #{msg_json_str}")
|
|
59
|
+
@logger.info(msg_json_str)
|
|
46
60
|
end
|
|
47
|
-
@logger.info(msg.to_json)
|
|
48
61
|
end
|
|
49
62
|
|
|
63
|
+
def flush
|
|
64
|
+
# Logger writes immediately on each call, no explicit flush needed
|
|
65
|
+
TDLog.info("TDLoggerConsumer flush.")
|
|
66
|
+
end
|
|
67
|
+
|
|
50
68
|
def close
|
|
51
|
-
@
|
|
69
|
+
@mutex.synchronize do
|
|
70
|
+
@logger.close
|
|
71
|
+
end
|
|
72
|
+
TDLog.info("TDLoggerConsumer close.")
|
|
52
73
|
end
|
|
53
74
|
|
|
54
75
|
private
|
data/lib/thinkingdata-ruby.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require 'thinkingdata-ruby/
|
|
2
|
-
require 'thinkingdata-ruby/
|
|
3
|
-
require 'thinkingdata-ruby/
|
|
4
|
-
require 'thinkingdata-ruby/
|
|
5
|
-
require 'thinkingdata-ruby/
|
|
1
|
+
require 'thinkingdata-ruby/td_logger_consumer'
|
|
2
|
+
require 'thinkingdata-ruby/td_debug_consumer'
|
|
3
|
+
require 'thinkingdata-ruby/td_batch_consumer'
|
|
4
|
+
require 'thinkingdata-ruby/td_analytics'
|
|
5
|
+
require 'thinkingdata-ruby/td_errors'
|
data/thinkingdata-ruby.gemspec
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'lib/thinkingdata-ruby/
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'lib/thinkingdata-ruby/td_version.rb')
|
|
2
2
|
|
|
3
3
|
spec = Gem::Specification.new do |spec|
|
|
4
4
|
spec.name = 'thinkingdata-ruby'
|
|
5
|
-
spec.version =
|
|
5
|
+
spec.version = ThinkingData::VERSION
|
|
6
6
|
spec.files = Dir.glob(`git ls-files`.split("\n"))
|
|
7
7
|
spec.require_paths = ['lib']
|
|
8
8
|
spec.summary = 'Official ThinkingData Analytics API for ruby'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: thinkingdata-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ThinkingData
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-05-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: The official ThinkingData Analytics API for ruby
|
|
14
14
|
email: sdk@thinkingdata.cn
|
|
@@ -23,12 +23,12 @@ files:
|
|
|
23
23
|
- README.md
|
|
24
24
|
- demo/demo.rb
|
|
25
25
|
- lib/thinkingdata-ruby.rb
|
|
26
|
-
- lib/thinkingdata-ruby/
|
|
27
|
-
- lib/thinkingdata-ruby/
|
|
28
|
-
- lib/thinkingdata-ruby/
|
|
29
|
-
- lib/thinkingdata-ruby/
|
|
30
|
-
- lib/thinkingdata-ruby/
|
|
31
|
-
- lib/thinkingdata-ruby/
|
|
26
|
+
- lib/thinkingdata-ruby/td_analytics.rb
|
|
27
|
+
- lib/thinkingdata-ruby/td_batch_consumer.rb
|
|
28
|
+
- lib/thinkingdata-ruby/td_debug_consumer.rb
|
|
29
|
+
- lib/thinkingdata-ruby/td_errors.rb
|
|
30
|
+
- lib/thinkingdata-ruby/td_logger_consumer.rb
|
|
31
|
+
- lib/thinkingdata-ruby/td_version.rb
|
|
32
32
|
- thinkingdata-ruby.gemspec
|
|
33
33
|
homepage: https://github.com/ThinkingDataAnalytics/ruby-sdk
|
|
34
34
|
licenses:
|
|
@@ -49,7 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
49
49
|
- !ruby/object:Gem::Version
|
|
50
50
|
version: '0'
|
|
51
51
|
requirements: []
|
|
52
|
-
rubygems_version: 3.0.
|
|
52
|
+
rubygems_version: 3.0.3.1
|
|
53
53
|
signing_key:
|
|
54
54
|
specification_version: 4
|
|
55
55
|
summary: Official ThinkingData Analytics API for ruby
|