snowplow-tracker 0.4.2 → 0.5.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 +8 -8
- data/README.md +1 -1
- data/lib/snowplow-tracker.rb +1 -0
- data/lib/snowplow-tracker/contracts.rb +0 -1
- data/lib/snowplow-tracker/emitters.rb +114 -64
- data/lib/snowplow-tracker/payload.rb +2 -1
- data/lib/snowplow-tracker/self_describing_json.rb +34 -0
- data/lib/snowplow-tracker/subject.rb +10 -1
- data/lib/snowplow-tracker/tracker.rb +17 -26
- data/lib/snowplow-tracker/version.rb +1 -1
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDkzMWJkNzQyZjVmNjk0NmQ0YjI5ODA5YzkxM2Y1NmZhZmEwNTgzNA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NTU5NDI2M2UzYTQ0M2ZhNGEyYjM2ZDdjMzQwZjA4ZGYyNDQ3OWU0OA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NjJiM2Y1ZWM2Y2Y3MTNkZDNmNWM0N2IwMGNkOTAzMTlhNjAxZjk5MTk3ZmUz
|
10
|
+
MjcyNTYwNmZjZjhmMWVlZGNiMmJiNGZiOGZlMmJlY2YxNDE1MjY0MGFlYzI0
|
11
|
+
M2E2OGY5YTkwM2M4Y2M1NjQyNGEyNzIxNzYzNDk1ZDY1MGU0NDg=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YjgxODJjMWY0NmIwOTNjODNmZmQ1NTZkOTMwNDBkODMxODZhMzc1Njk2ODc1
|
14
|
+
NDkzZDYzNWMyZDQ0MzBjYzRiNzI3ZTdkNjI4OWY5ZjUyMTgzMjI3ZWYyOTI4
|
15
|
+
NGZhYTljNTFkOTRhYWQ5MzEyMGFmMmU0ZGNhNGFjZWQ0YzQ2NmI=
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Ruby Analytics for Snowplow
|
2
2
|
[](http://badge.fury.io/rb/snowplow-tracker)
|
3
|
-
[](https://travis-ci.org/snowplow/snowplow-ruby-tracker)
|
3
|
+
[](https://travis-ci.org/snowplow/snowplow-ruby-tracker)
|
4
4
|
[](https://codeclimate.com/github/snowplow/snowplow-ruby-tracker)
|
5
5
|
[](https://coveralls.io/r/snowplow/snowplow-ruby-tracker)
|
6
6
|
[![License][license-image]][license]
|
data/lib/snowplow-tracker.rb
CHANGED
@@ -15,6 +15,7 @@
|
|
15
15
|
|
16
16
|
require 'snowplow-tracker/contracts.rb'
|
17
17
|
require 'snowplow-tracker/version.rb'
|
18
|
+
require 'snowplow-tracker/self_describing_json.rb'
|
18
19
|
require 'snowplow-tracker/payload.rb'
|
19
20
|
require 'snowplow-tracker/subject.rb'
|
20
21
|
require 'snowplow-tracker/emitters.rb'
|
@@ -17,7 +17,6 @@ require 'net/https'
|
|
17
17
|
require 'set'
|
18
18
|
require 'logger'
|
19
19
|
require 'contracts'
|
20
|
-
include Contracts
|
21
20
|
|
22
21
|
module SnowplowTracker
|
23
22
|
|
@@ -26,13 +25,16 @@ module SnowplowTracker
|
|
26
25
|
|
27
26
|
class Emitter
|
28
27
|
|
28
|
+
include Contracts
|
29
|
+
|
29
30
|
@@ConfigHash = ({
|
30
31
|
:protocol => Maybe[Or['http', 'https']],
|
31
32
|
:port => Maybe[Num],
|
32
33
|
:method => Maybe[Or['get', 'post']],
|
33
34
|
:buffer_size => Maybe[Num],
|
34
35
|
:on_success => Maybe[Func[Num => Any]],
|
35
|
-
:on_failure => Maybe[Func[Num, Hash => Any]]
|
36
|
+
:on_failure => Maybe[Func[Num, Hash => Any]],
|
37
|
+
:thread_count => Maybe[Num]
|
36
38
|
})
|
37
39
|
|
38
40
|
@@StrictConfigHash = And[@@ConfigHash, lambda { |x|
|
@@ -47,19 +49,19 @@ module SnowplowTracker
|
|
47
49
|
Contract String, @@StrictConfigHash => lambda { |x| x.is_a? Emitter }
|
48
50
|
def initialize(endpoint, config={})
|
49
51
|
config = @@DefaultConfig.merge(config)
|
52
|
+
@lock = Monitor.new
|
50
53
|
@collector_uri = as_collector_uri(endpoint, config[:protocol], config[:port], config[:method])
|
51
54
|
@buffer = []
|
52
55
|
if not config[:buffer_size].nil?
|
53
56
|
@buffer_size = config[:buffer_size]
|
54
57
|
elsif config[:method] == 'get'
|
55
|
-
@buffer_size =
|
58
|
+
@buffer_size = 1
|
56
59
|
else
|
57
60
|
@buffer_size = 10
|
58
61
|
end
|
59
62
|
@method = config[:method]
|
60
63
|
@on_success = config[:on_success]
|
61
64
|
@on_failure = config[:on_failure]
|
62
|
-
@threads = []
|
63
65
|
LOGGER.info("#{self.class} initialized with endpoint #{@collector_uri}")
|
64
66
|
|
65
67
|
self
|
@@ -80,9 +82,11 @@ module SnowplowTracker
|
|
80
82
|
Contract Hash => nil
|
81
83
|
def input(payload)
|
82
84
|
payload.each { |k,v| payload[k] = v.to_s}
|
83
|
-
@
|
84
|
-
|
85
|
-
|
85
|
+
@lock.synchronize do
|
86
|
+
@buffer.push(payload)
|
87
|
+
if @buffer.size >= @buffer_size
|
88
|
+
flush
|
89
|
+
end
|
86
90
|
end
|
87
91
|
|
88
92
|
nil
|
@@ -91,58 +95,70 @@ module SnowplowTracker
|
|
91
95
|
# Flush the buffer
|
92
96
|
#
|
93
97
|
Contract Bool => nil
|
94
|
-
def flush(
|
95
|
-
|
96
|
-
|
98
|
+
def flush(async=true)
|
99
|
+
@lock.synchronize do
|
100
|
+
send_requests(@buffer)
|
101
|
+
@buffer = []
|
102
|
+
end
|
97
103
|
nil
|
98
104
|
end
|
99
105
|
|
100
106
|
# Send all events in the buffer to the collector
|
101
107
|
#
|
102
|
-
Contract
|
103
|
-
def send_requests
|
104
|
-
|
105
|
-
|
106
|
-
|
108
|
+
Contract ArrayOf[Hash] => nil
|
109
|
+
def send_requests(evts)
|
110
|
+
if evts.size < 1
|
111
|
+
LOGGER.info("Skipping sending events since buffer is empty")
|
112
|
+
return
|
113
|
+
end
|
114
|
+
LOGGER.info("Attempting to send #{evts.size} request#{evts.size == 1 ? '' : 's'}")
|
115
|
+
|
116
|
+
if @method == 'post'
|
117
|
+
post_succeeded = false
|
118
|
+
begin
|
119
|
+
request = http_post(SelfDescribingJson.new(
|
120
|
+
'iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-2',
|
121
|
+
evts
|
122
|
+
).to_json)
|
123
|
+
post_succeeded = is_good_status_code(request.code)
|
124
|
+
rescue StandardError => se
|
125
|
+
LOGGER.warn(se)
|
126
|
+
end
|
127
|
+
if post_succeeded
|
128
|
+
unless @on_success.nil?
|
129
|
+
@on_success.call(evts.size)
|
130
|
+
end
|
131
|
+
else
|
132
|
+
unless @on_failure.nil?
|
133
|
+
@on_failure.call(0, evts)
|
134
|
+
end
|
135
|
+
end
|
107
136
|
|
108
|
-
|
137
|
+
elsif @method == 'get'
|
109
138
|
success_count = 0
|
110
139
|
unsent_requests = []
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
140
|
+
evts.each do |evt|
|
141
|
+
get_succeeded = false
|
142
|
+
begin
|
143
|
+
request = http_get(evt)
|
144
|
+
get_succeeded = is_good_status_code(request.code)
|
145
|
+
rescue StandardError => se
|
146
|
+
LOGGER.warn(se)
|
117
147
|
end
|
118
|
-
if
|
119
|
-
|
120
|
-
@on_success.call(success_count)
|
121
|
-
end
|
148
|
+
if get_succeeded
|
149
|
+
success_count += 1
|
122
150
|
else
|
123
|
-
|
124
|
-
@on_failure.call(success_count, unsent_requests)
|
125
|
-
end
|
151
|
+
unsent_requests << evt
|
126
152
|
end
|
127
153
|
end
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
if request.code.to_i == 200
|
137
|
-
unless @on_success.nil?
|
138
|
-
@on_success.call(temp_buffer.size)
|
139
|
-
end
|
140
|
-
else
|
141
|
-
unless @on_failure.nil?
|
142
|
-
@on_failure.call(0, temp_buffer)
|
143
|
-
end
|
154
|
+
if unsent_requests.size == 0
|
155
|
+
unless @on_success.nil?
|
156
|
+
@on_success.call(success_count)
|
157
|
+
end
|
158
|
+
else
|
159
|
+
unless @on_failure.nil?
|
160
|
+
@on_failure.call(success_count, unsent_requests)
|
144
161
|
end
|
145
|
-
|
146
162
|
end
|
147
163
|
end
|
148
164
|
|
@@ -162,7 +178,7 @@ module SnowplowTracker
|
|
162
178
|
http.use_ssl = true
|
163
179
|
end
|
164
180
|
response = http.request(request)
|
165
|
-
LOGGER.add(response.code
|
181
|
+
LOGGER.add(is_good_status_code(response.code) ? Logger::INFO : Logger::WARN) {
|
166
182
|
"GET request to #{@collector_uri} finished with status code #{response.code}"
|
167
183
|
}
|
168
184
|
|
@@ -184,13 +200,20 @@ module SnowplowTracker
|
|
184
200
|
request.body = payload.to_json
|
185
201
|
request.set_content_type('application/json; charset=utf-8')
|
186
202
|
response = http.request(request)
|
187
|
-
LOGGER.add(response.code
|
203
|
+
LOGGER.add(is_good_status_code(response.code) ? Logger::INFO : Logger::WARN) {
|
188
204
|
"POST request to #{@collector_uri} finished with status code #{response.code}"
|
189
205
|
}
|
190
206
|
|
191
207
|
response
|
192
208
|
end
|
193
209
|
|
210
|
+
# Only 2xx and 3xx status codes are considered successes
|
211
|
+
#
|
212
|
+
Contract String => Bool
|
213
|
+
def is_good_status_code(status_code)
|
214
|
+
status_code.to_i >= 200 && status_code.to_i < 400
|
215
|
+
end
|
216
|
+
|
194
217
|
private :as_collector_uri,
|
195
218
|
:http_get,
|
196
219
|
:http_post
|
@@ -200,27 +223,54 @@ module SnowplowTracker
|
|
200
223
|
|
201
224
|
class AsyncEmitter < Emitter
|
202
225
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
if sync
|
215
|
-
LOGGER.info('Starting synchronous flush')
|
216
|
-
@threads.each do |thread|
|
217
|
-
thread.join(10)
|
226
|
+
Contract String, @@StrictConfigHash => lambda { |x| x.is_a? Emitter }
|
227
|
+
def initialize(endpoint, config={})
|
228
|
+
@queue = Queue.new()
|
229
|
+
# @all_processed_condition and @results_unprocessed are used to emulate Python's Queue.task_done()
|
230
|
+
@queue.extend(MonitorMixin)
|
231
|
+
@all_processed_condition = @queue.new_cond
|
232
|
+
@results_unprocessed = 0
|
233
|
+
(config[:thread_count] || 1).times do
|
234
|
+
t = Thread.new do
|
235
|
+
consume
|
218
236
|
end
|
219
237
|
end
|
238
|
+
super(endpoint, config)
|
239
|
+
end
|
220
240
|
|
221
|
-
|
241
|
+
def consume
|
242
|
+
loop do
|
243
|
+
work_unit = @queue.pop
|
244
|
+
send_requests(work_unit)
|
245
|
+
@queue.synchronize do
|
246
|
+
@results_unprocessed -= 1
|
247
|
+
@all_processed_condition.broadcast
|
248
|
+
end
|
249
|
+
end
|
222
250
|
end
|
223
251
|
|
252
|
+
# Flush the buffer
|
253
|
+
# If async is false, block until the queue is empty
|
254
|
+
#
|
255
|
+
def flush(async=true)
|
256
|
+
loop do
|
257
|
+
@lock.synchronize do
|
258
|
+
@queue.synchronize do
|
259
|
+
@results_unprocessed += 1
|
260
|
+
end
|
261
|
+
@queue << @buffer
|
262
|
+
@buffer = []
|
263
|
+
end
|
264
|
+
if not async
|
265
|
+
LOGGER.info('Starting synchronous flush')
|
266
|
+
@queue.synchronize do
|
267
|
+
@all_processed_condition.wait_while { @results_unprocessed > 0 }
|
268
|
+
LOGGER.info('Finished synchronous flush')
|
269
|
+
end
|
270
|
+
end
|
271
|
+
break if @buffer.size < 1
|
272
|
+
end
|
273
|
+
end
|
224
274
|
end
|
225
275
|
|
226
276
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright (c) 2013-2014 Snowplow Analytics Ltd. All rights reserved.
|
2
|
+
#
|
3
|
+
# This program is licensed to you under the Apache License Version 2.0,
|
4
|
+
# and you may not use this file except in compliance with the Apache License Version 2.0.
|
5
|
+
# You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing,
|
8
|
+
# software distributed under the Apache License Version 2.0 is distributed on an
|
9
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
|
11
|
+
|
12
|
+
# Author:: Alex Dean, Fred Blundun (mailto:support@snowplowanalytics.com)
|
13
|
+
# Copyright:: Copyright (c) 2013-2014 Snowplow Analytics Ltd
|
14
|
+
# License:: Apache License Version 2.0
|
15
|
+
|
16
|
+
module SnowplowTracker
|
17
|
+
|
18
|
+
class SelfDescribingJson
|
19
|
+
|
20
|
+
def initialize(schema, data)
|
21
|
+
@schema = schema
|
22
|
+
@data = data
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_json
|
26
|
+
{
|
27
|
+
:schema => @schema,
|
28
|
+
:data => @data
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -14,12 +14,13 @@
|
|
14
14
|
# License:: Apache License Version 2.0
|
15
15
|
|
16
16
|
require 'contracts'
|
17
|
-
include Contracts
|
18
17
|
|
19
18
|
module SnowplowTracker
|
20
19
|
|
21
20
|
class Subject
|
22
21
|
|
22
|
+
include Contracts
|
23
|
+
|
23
24
|
@@default_platform = 'srv'
|
24
25
|
@@supported_platforms = ['pc', 'tv', 'mob', 'cnsl', 'iot']
|
25
26
|
|
@@ -52,6 +53,14 @@ module SnowplowTracker
|
|
52
53
|
self
|
53
54
|
end
|
54
55
|
|
56
|
+
# Set fingerprint for the user
|
57
|
+
#
|
58
|
+
Contract Num => Subject
|
59
|
+
def set_fingerprint(fingerprint)
|
60
|
+
@standard_nv_pairs['fp'] = fingerprint
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
55
64
|
# Set the screen resolution for a device
|
56
65
|
#
|
57
66
|
Contract Num, Num => Subject
|
@@ -15,14 +15,14 @@
|
|
15
15
|
|
16
16
|
require 'contracts'
|
17
17
|
require 'set'
|
18
|
-
include Contracts
|
19
|
-
|
20
18
|
require 'uuid'
|
21
19
|
|
22
20
|
module SnowplowTracker
|
23
21
|
|
24
22
|
class Tracker
|
25
23
|
|
24
|
+
include Contracts
|
25
|
+
|
26
26
|
@@EmitterInput = Or[lambda {|x| x.is_a? Emitter}, ArrayOf[lambda {|x| x.is_a? Emitter}]]
|
27
27
|
|
28
28
|
@@required_transaction_keys = Set.new(%w(order_id total_value))
|
@@ -55,22 +55,14 @@ module SnowplowTracker
|
|
55
55
|
augmented_item_keys.subset? @@recognised_augmented_item_keys
|
56
56
|
}
|
57
57
|
|
58
|
-
@@
|
59
|
-
schema: String,
|
60
|
-
data: Any
|
61
|
-
}, {
|
62
|
-
'schema' => String,
|
63
|
-
'data' => Any
|
64
|
-
}]
|
65
|
-
|
66
|
-
@@ContextsInput = ArrayOf[@@SelfDescribingJson]
|
58
|
+
@@ContextsInput = ArrayOf[SelfDescribingJson]
|
67
59
|
|
68
60
|
@@version = TRACKER_VERSION
|
69
61
|
@@default_encode_base64 = true
|
70
62
|
|
71
63
|
@@base_schema_path = "iglu:com.snowplowanalytics.snowplow"
|
72
64
|
@@schema_tag = "jsonschema"
|
73
|
-
@@context_schema = "#{@@base_schema_path}/contexts/#{@@schema_tag}/1-0-
|
65
|
+
@@context_schema = "#{@@base_schema_path}/contexts/#{@@schema_tag}/1-0-1"
|
74
66
|
@@unstruct_event_schema = "#{@@base_schema_path}/unstruct_event/#{@@schema_tag}/1-0-0"
|
75
67
|
|
76
68
|
Contract @@EmitterInput, Maybe[Subject], Maybe[String], Maybe[String], Bool => Tracker
|
@@ -119,12 +111,12 @@ module SnowplowTracker
|
|
119
111
|
|
120
112
|
# Builds a self-describing JSON from an array of custom contexts
|
121
113
|
#
|
122
|
-
Contract @@ContextsInput =>
|
114
|
+
Contract @@ContextsInput => Hash
|
123
115
|
def build_context(context)
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
116
|
+
SelfDescribingJson.new(
|
117
|
+
@@context_schema,
|
118
|
+
context.map {|c| c.to_json}
|
119
|
+
).to_json
|
128
120
|
end
|
129
121
|
|
130
122
|
# Tracking methods
|
@@ -259,7 +251,8 @@ module SnowplowTracker
|
|
259
251
|
screen_view_properties['id'] = id
|
260
252
|
end
|
261
253
|
screen_view_schema = "#{@@base_schema_path}/screen_view/#{@@schema_tag}/1-0-0"
|
262
|
-
|
254
|
+
|
255
|
+
event_json = SelfDescribingJson.new(screen_view_schema, screen_view_properties)
|
263
256
|
|
264
257
|
self.track_unstruct_event(event_json, context, tstamp)
|
265
258
|
|
@@ -268,16 +261,14 @@ module SnowplowTracker
|
|
268
261
|
|
269
262
|
# Track an unstructured event
|
270
263
|
#
|
271
|
-
Contract
|
264
|
+
Contract SelfDescribingJson, Maybe[@@ContextsInput], Maybe[Num] => Tracker
|
272
265
|
def track_unstruct_event(event_json, context=nil, tstamp=nil)
|
273
266
|
pb = Payload.new
|
274
267
|
pb.add('e', 'ue')
|
275
268
|
|
276
|
-
envelope =
|
277
|
-
|
278
|
-
|
279
|
-
}
|
280
|
-
pb.add_json(envelope, @config['encode_base64'], 'ue_px', 'ue_pr')
|
269
|
+
envelope = SelfDescribingJson.new(@@unstruct_event_schema, event_json.to_json)
|
270
|
+
|
271
|
+
pb.add_json(envelope.to_json, @config['encode_base64'], 'ue_px', 'ue_pr')
|
281
272
|
|
282
273
|
unless context.nil?
|
283
274
|
pb.add_json(build_context(context), @config['encode_base64'], 'cx', 'co')
|
@@ -296,9 +287,9 @@ module SnowplowTracker
|
|
296
287
|
# Flush all events stored in all emitters
|
297
288
|
#
|
298
289
|
Contract Bool => Tracker
|
299
|
-
def flush(
|
290
|
+
def flush(async=false)
|
300
291
|
@emitters.each do |emitter|
|
301
|
-
emitter.flush(
|
292
|
+
emitter.flush(async)
|
302
293
|
end
|
303
294
|
|
304
295
|
self
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: snowplow-tracker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Dean
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-08-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: contracts
|
@@ -17,14 +17,20 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ~>
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '0.
|
20
|
+
version: '0.7'
|
21
|
+
- - <=
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '0.11'
|
21
24
|
type: :runtime
|
22
25
|
prerelease: false
|
23
26
|
version_requirements: !ruby/object:Gem::Requirement
|
24
27
|
requirements:
|
25
28
|
- - ~>
|
26
29
|
- !ruby/object:Gem::Version
|
27
|
-
version: '0.
|
30
|
+
version: '0.7'
|
31
|
+
- - <=
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.11'
|
28
34
|
- !ruby/object:Gem::Dependency
|
29
35
|
name: uuid
|
30
36
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +86,7 @@ files:
|
|
80
86
|
- lib/snowplow-tracker/contracts.rb
|
81
87
|
- lib/snowplow-tracker/emitters.rb
|
82
88
|
- lib/snowplow-tracker/payload.rb
|
89
|
+
- lib/snowplow-tracker/self_describing_json.rb
|
83
90
|
- lib/snowplow-tracker/subject.rb
|
84
91
|
- lib/snowplow-tracker/tracker.rb
|
85
92
|
- lib/snowplow-tracker/version.rb
|
@@ -103,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
110
|
version: '0'
|
104
111
|
requirements: []
|
105
112
|
rubyforge_project:
|
106
|
-
rubygems_version: 2.4.
|
113
|
+
rubygems_version: 2.4.8
|
107
114
|
signing_key:
|
108
115
|
specification_version: 4
|
109
116
|
summary: Ruby Analytics for Snowplow
|