snowplow-tracker 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23047d83eb7b5f22343e668d97e0885fbf1cacf684706b8bb83465e98fc78e40
4
- data.tar.gz: edf65b28b3264412a3c11f2f0e9ee3a440647fbece99228ab450e9a01148b0f5
3
+ metadata.gz: 828096deb0a370f527d14f17e2e48eb1481428b5cdc1b2897235b539be64f327
4
+ data.tar.gz: e0b5b026cfad092a2c4269876bc0b7e5d240ba5ec8b18b67b8a1e97d7c127a93
5
5
  SHA512:
6
- metadata.gz: d996e91a98e1ce69d459dd91655ff052d578e0dfed0c41c69731d1f37b93dfa95ca50e11b82d43df78b599197f8e94b5f5ad6a7fad5f4f8498d886c1dfdeae95
7
- data.tar.gz: 88053e746e569713d537bfbbd75cb61ec4e38bbf19a0e8f36de13d057450fec5ab4f3e5a13715b13df1dc8188791e6e9dc38d9aed27b47bcc99c415d2edc6df5
6
+ metadata.gz: b75d45c5b03f55398ab27a9945d9ddbe91340348d803e8be8d4c74b108f39931cd7a7e17a661a818ffd9e3d81aac71bdf3a8d5486c397881a741bfc5e1b881b0
7
+ data.tar.gz: 650468a842f96ab6951aa54bc374115ee0dc3a466b6f15807c8a0e8fb1e86127b630ee226f8c0ba49b3316ae20d342c24c6211810e2c2eac65791d7f4fa9f478
data/README.md CHANGED
@@ -9,30 +9,28 @@
9
9
 
10
10
  ## Overview
11
11
 
12
- Add analytics to your Ruby and Rails apps and gems with the **[Snowplow][snowplow]** event tracker for **[Ruby][ruby]**.
12
+ Add analytics to your **[Ruby][ruby]** and **[Ruby on Rails][rails]** apps and **[gems][rubygems]** with the **[Snowplow][snowplow]** event tracker for **[Ruby][ruby]**.
13
13
 
14
14
  Snowplow is a scalable open-source platform for rich, high quality, low-latency data collection. It is designed to collect high quality, complete behavioral data for enterprise business.
15
15
 
16
16
  **To find out more, please check out the [Snowplow website][snowplow] and our [documentation][docs].**
17
17
 
18
- With this tracker you can collect event data from your **[Ruby][ruby]** applications, **[Ruby on Rails][rails]** web applications and **[Ruby gems][rubygems]**.
19
-
20
18
  ## Quickstart
21
19
 
22
20
  Add this gem to your Gemfile. It is compatible with Ruby versions 2.1 to 3.0+.
23
21
 
24
22
  ```ruby
25
- gem "snowplow-tracker", "~> 0.7.0"
23
+ gem "snowplow-tracker", "~> 0.8.0"
26
24
  ```
27
25
 
28
26
  See our [demo app][demoapp] for an example of implementing the Ruby tracker in a Rails app.
29
27
 
30
28
  ## Find out more
31
29
 
32
- | Technical Docs | API Docs | Contributing |
30
+ | Snowplow Docs | API Docs | Contributing |
33
31
  | ------------------------------ | ----------------------- | ----------------------------------- |
34
32
  | ![i1][techdocs-image] | ![i1][techdocs-image] | ![i4][contributing-image] |
35
- | **[Technical Docs][techdocs]** | **[API Docs][apidocs]** | **[Contributing](Contributing.md)** |
33
+ | **[Snowplow Docs][techdocs]** | **[API Docs][apidocs]** | **[Contributing](Contributing.md)** |
36
34
 
37
35
  ## Maintainer Quickstart
38
36
 
@@ -47,11 +45,16 @@ The `-v` flag for `docker run` creates a bind mount for the project directory. T
47
45
 
48
46
  Alternatively, test directly by installing Ruby 2.1+ and [Bundler][bundler]. Then run:
49
47
 
50
- ```
48
+ ```bash
51
49
  bundle install
52
50
  rspec
53
51
  ```
54
52
 
53
+ To generate documentation using YARD, make sure the YARD and redcarpet gems are installed locally. Then run:
54
+ ```bash
55
+ yard doc
56
+ ```
57
+
55
58
  ## Contributing
56
59
 
57
60
  Feedback and contributions are welcome - if you have identified a bug, please log an issue on this repo. For all other feedback, discussion or questions please open a thread on our [Discourse forum][discourse].
@@ -72,7 +75,7 @@ limitations under the License.
72
75
  [license-image]: https://img.shields.io/badge/license-Apache--2-blue.svg?style=flat
73
76
  [license]: https://www.apache.org/licenses/LICENSE-2.0
74
77
  [gh-actions]: https://github.com/snowplow/snowplow-ruby-tracker/actions
75
- [gh-actions-image]: https://github.com/snowplow/snowplow-ruby-tracker/workflows/test.yml/badge.svg
78
+ [gh-actions-image]: https://github.com/snowplow/snowplow-ruby-tracker/workflows/Test/badge.svg
76
79
  [tracker-classification]: https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/tracker-maintenance-classification/
77
80
  [early-release]: https://img.shields.io/static/v1?style=flat&label=Snowplow&message=Early%20Release&color=014477&labelColor=9ba0aa&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAeFBMVEVMaXGXANeYANeXANZbAJmXANeUANSQAM+XANeMAMpaAJhZAJeZANiXANaXANaOAM2WANVnAKWXANZ9ALtmAKVaAJmXANZaAJlXAJZdAJxaAJlZAJdbAJlbAJmQAM+UANKZANhhAJ+EAL+BAL9oAKZnAKVjAKF1ALNBd8J1AAAAKHRSTlMAa1hWXyteBTQJIEwRgUh2JjJon21wcBgNfmc+JlOBQjwezWF2l5dXzkW3/wAAAHpJREFUeNokhQOCA1EAxTL85hi7dXv/E5YPCYBq5DeN4pcqV1XbtW/xTVMIMAZE0cBHEaZhBmIQwCFofeprPUHqjmD/+7peztd62dWQRkvrQayXkn01f/gWp2CrxfjY7rcZ5V7DEMDQgmEozFpZqLUYDsNwOqbnMLwPAJEwCopZxKttAAAAAElFTkSuQmCC
78
81
 
@@ -17,7 +17,6 @@
17
17
  require 'net/https'
18
18
  require 'set'
19
19
  require 'logger'
20
- require 'contracts'
21
20
 
22
21
  module SnowplowTracker
23
22
  # @see Emitter
@@ -38,7 +37,10 @@ module SnowplowTracker
38
37
  # | Buffer size | 1 |
39
38
  # | Path | `/i` |
40
39
  #
41
- # The buffer size is 1 because GET requests can only contain one event.
40
+ # The buffer size is the number of events which will be buffered before they
41
+ # are all sent simultaneously. The process of sending all buffered events is
42
+ # called "flushing". The default buffer size is 1 because GET requests can
43
+ # only contain one event.
42
44
  #
43
45
  # If you choose to use POST requests, the buffer_size defaults to 10, and the
44
46
  # buffered events are all sent together in a single request. The default path
@@ -61,42 +63,15 @@ module SnowplowTracker
61
63
  # require 'logger'
62
64
  # SnowplowTracker::LOGGER.level = Logger::DEBUG
63
65
  class Emitter
64
- include Contracts
65
-
66
- # Contract types
67
-
68
- # @private
69
- CONFIG_HASH = {
70
- path: Maybe[String],
71
- protocol: Maybe[Or['http', 'https']],
72
- port: Maybe[Num],
73
- method: Maybe[Or['get', 'post']],
74
- buffer_size: Maybe[Num],
75
- on_success: Maybe[Func[Num => Any]],
76
- on_failure: Maybe[Func[Num, Hash => Any]],
77
- thread_count: Maybe[Num],
78
- logger: Maybe[Logger]
79
- }
80
-
81
- # @private
82
- STRICT_CONFIG_HASH = And[CONFIG_HASH, ->(x) {
83
- (x.class == Hash) && Set.new(x.keys).subset?(Set.new(CONFIG_HASH.keys))
84
- }]
85
-
86
- # @!group Public constants
87
-
88
66
  # Default Emitter settings
89
67
  DEFAULT_CONFIG = {
90
68
  protocol: 'http',
91
69
  method: 'get'
92
70
  }
93
71
 
94
- # @!endgroup
95
-
96
72
  # @private
97
73
  attr_reader :logger
98
74
 
99
- Contract KeywordArgs[endpoint: String, options: Optional[STRICT_CONFIG_HASH]] => Any
100
75
  # Create a new Emitter instance. The endpoint is required.
101
76
  #
102
77
  # @example Initializing an Emitter with all the possible extra configuration.
@@ -105,7 +80,7 @@ module SnowplowTracker
105
80
  # puts "#{success_count} events sent successfully, #{failures.size} sent unsuccessfully"
106
81
  # end
107
82
  #
108
- # Emitter.new(endpoint: 'collector.example.com',
83
+ # SnowplowTracker::Emitter.new(endpoint: 'collector.example.com',
109
84
  # options: { path: '/my-pipeline/1',
110
85
  # protocol: 'https',
111
86
  # port: 443,
@@ -124,8 +99,8 @@ module SnowplowTracker
124
99
  # | port | The port for the connection | Integer |
125
100
  # | method | 'get' or 'post' | String |
126
101
  # | buffer_size | Number of events to send at once | Integer |
127
- # | on_success | A function to call if events were sent successfully | Function |
128
- # | on_failure | A function to call if events did not send | Function |
102
+ # | on_success | A method to call if events were sent successfully | Method |
103
+ # | on_failure | A method to call if events did not send | Method |
129
104
  # | thread_count | Number of threads to use | Integer |
130
105
  # | logger | Log somewhere other than STDERR | Logger |
131
106
  #
@@ -134,6 +109,13 @@ module SnowplowTracker
134
109
  #
135
110
  # If you choose to use HTTPS, we recommend using port 443.
136
111
  #
112
+ # Only 2xx and 3xx status codes are considered successes.
113
+ #
114
+ # The `on_success` callback should accept one argument: the number of
115
+ # requests sent this way. The `on_failure` callback should accept two
116
+ # arguments: the number of successfully sent events, and an array containing
117
+ # the unsuccessful events.
118
+ #
137
119
  # @param endpoint [String] the endpoint to send the events to
138
120
  # @param options [Hash] allowed configuration options
139
121
  #
@@ -153,7 +135,6 @@ module SnowplowTracker
153
135
  logger.info("#{self.class} initialized with endpoint #{@collector_uri}")
154
136
  end
155
137
 
156
- Contract Hash => Num
157
138
  # Creates the `@buffer_size` variable during initialization. Unless
158
139
  # otherwise defined, it's 1 for Emitters using GET and 10 for Emitters using
159
140
  # POST requests.
@@ -164,7 +145,6 @@ module SnowplowTracker
164
145
  config[:method] == 'get' ? 1 : 10
165
146
  end
166
147
 
167
- Contract Hash => String
168
148
  # Creates the `@path` variable during initialization. Allows a non-standard
169
149
  # path to be provided.
170
150
  # @private
@@ -174,9 +154,6 @@ module SnowplowTracker
174
154
  config[:method] == 'get' ? '/i' : '/com.snowplowanalytics.snowplow/tp2'
175
155
  end
176
156
 
177
- # Build the collector URI from the configuration hash
178
- #
179
- Contract String, String, Maybe[Num], String => String
180
157
  # Creates the `@collector_uri` variable during initialization.
181
158
  # The default is "http://{endpoint}/i".
182
159
  # @private
@@ -186,7 +163,6 @@ module SnowplowTracker
186
163
  "#{protocol}://#{endpoint}#{port_string}#{path}"
187
164
  end
188
165
 
189
- Contract Hash => nil
190
166
  # Add an event to the buffer and flush it if maximum size has been reached.
191
167
  # This method is not required for standard Ruby tracker usage. A {Tracker}
192
168
  # privately calls this method once the event payload is ready to send.
@@ -197,6 +173,9 @@ module SnowplowTracker
197
173
  # to send. You could use {#input} as part of your callback to immediately
198
174
  # retry the failed event.
199
175
  #
176
+ # The `on_failure` callback should accept two arguments: the number of
177
+ # successfully sent events, and an array containing the unsuccessful events.
178
+ #
200
179
  # @example A possible `on_failure` method using `#input`
201
180
  # def retry_on_failure(failed_event_count, failed_events)
202
181
  # # possible backoff-and-retry timeout here
@@ -216,7 +195,6 @@ module SnowplowTracker
216
195
  nil
217
196
  end
218
197
 
219
- Contract Bool => nil
220
198
  # Flush the Emitter, forcing it to send all the events in its
221
199
  # buffer, even if the buffer is not full. {Emitter} objects, unlike
222
200
  # {AsyncEmitter}s, can only `flush` synchronously. A {Tracker} can manually flush all
@@ -237,7 +215,6 @@ module SnowplowTracker
237
215
  nil
238
216
  end
239
217
 
240
- Contract ArrayOf[Hash] => nil
241
218
  # Send all events in the buffer to the collector
242
219
  # @private
243
220
  def send_requests(events)
@@ -262,7 +239,6 @@ module SnowplowTracker
262
239
  nil
263
240
  end
264
241
 
265
- Contract ArrayOf[Hash] => nil
266
242
  # Part of {#send_requests}.
267
243
  # @private
268
244
  def send_requests_with_post(events)
@@ -286,7 +262,6 @@ module SnowplowTracker
286
262
  nil
287
263
  end
288
264
 
289
- Contract ArrayOf[Hash] => nil
290
265
  # Part of {#send_requests}.
291
266
  # @private
292
267
  def send_requests_with_get(events)
@@ -307,7 +282,6 @@ module SnowplowTracker
307
282
  nil
308
283
  end
309
284
 
310
- Contract Hash => Bool
311
285
  # Part of {#send_requests_with_get}.
312
286
  # @private
313
287
  def process_get_event(event)
@@ -321,7 +295,6 @@ module SnowplowTracker
321
295
  get_succeeded
322
296
  end
323
297
 
324
- Contract Hash => ->(x) { x.is_a? Net::HTTPResponse }
325
298
  # Part of {#process_get_event}. This sends a GET request.
326
299
  # @private
327
300
  def http_get(payload)
@@ -339,7 +312,6 @@ module SnowplowTracker
339
312
  response
340
313
  end
341
314
 
342
- Contract Hash => ->(x) { x.is_a? Net::HTTPResponse }
343
315
  # Part of {#send_requests_with_post}. This sends a POST request.
344
316
  # @private
345
317
  def http_post(payload)
@@ -359,7 +331,6 @@ module SnowplowTracker
359
331
  response
360
332
  end
361
333
 
362
- Contract String => Bool
363
334
  # Check if the response is good.
364
335
  # Only 2xx and 3xx status codes are considered successes.
365
336
  # @private
@@ -381,7 +352,6 @@ module SnowplowTracker
381
352
  # @see Emitter
382
353
  # @api public
383
354
  class AsyncEmitter < Emitter
384
- Contract KeywordArgs[endpoint: String, options: Optional[STRICT_CONFIG_HASH]] => Any
385
355
  # Create a new AsyncEmitter object. The endpoint is required.
386
356
  #
387
357
  # @example Initializing an AsyncEmitter with all the possible extra configuration.
@@ -390,7 +360,7 @@ module SnowplowTracker
390
360
  # puts "#{success_count} events sent successfully, #{failures.size} sent unsuccessfully"
391
361
  # end
392
362
  #
393
- # Emitter.new(endpoint: 'collector.example.com',
363
+ # SnowplowTracker::Emitter.new(endpoint: 'collector.example.com',
394
364
  # options: { path: '/my-pipeline/1',
395
365
  # protocol: 'https',
396
366
  # port: 443,
@@ -420,6 +390,13 @@ module SnowplowTracker
420
390
  #
421
391
  # If you choose to use HTTPS, we recommend using port 443.
422
392
  #
393
+ # Only 2xx and 3xx status codes are considered successes.
394
+ #
395
+ # The `on_success` callback should accept one argument: the number of
396
+ # requests sent this way. The `on_failure` callback should accept two
397
+ # arguments: the number of successfully sent events, and an array containing
398
+ # the unsuccessful events.
399
+ #
423
400
  # @note if you test the AsyncEmitter by using a short script to send an
424
401
  # event, you may find that the event fails to send. This is because the
425
402
  # process exits before the flushing thread is finished. You can get round
@@ -14,8 +14,6 @@
14
14
  # License:: Apache License Version 2.0
15
15
 
16
16
 
17
- require 'contracts'
18
-
19
17
  module SnowplowTracker
20
18
  # If the Ruby tracker is incorporated into a website server, the events
21
19
  # tracked will describe user activity on specific webpages. Knowing on which
@@ -33,18 +31,15 @@ module SnowplowTracker
33
31
  # @note For {Tracker#track_page_view}, properties set in the Page object will
34
32
  # override those properties given as arguments.
35
33
  class Page
36
- include Contracts
37
-
38
34
  # @return [Hash] the stored page properties
39
35
  attr_reader :details
40
36
 
41
- Contract KeywordArgs[page_url: Maybe[String], page_title: Maybe[String], referrer: Maybe[String]] => Any
42
37
  # Create a Page object for attaching page properties to events.
43
38
  #
44
39
  # Page properties will directly populate the event's `page_url`, `page_title` and `referrer` parameters.
45
40
  #
46
41
  # @example Creating a Page
47
- # Page.new(page_url: 'http://www.example.com/second-page',
42
+ # SnowplowTracker::Page.new(page_url: 'http://www.example.com/second-page',
48
43
  # page_title: 'Example title',
49
44
  # referrer: 'http://www.example.com/first-page')
50
45
  #
@@ -17,7 +17,6 @@
17
17
  require 'base64'
18
18
  require 'json'
19
19
  require 'net/http'
20
- require 'contracts'
21
20
 
22
21
  module SnowplowTracker
23
22
  # @private
@@ -26,28 +25,22 @@ module SnowplowTracker
26
25
  # hash. These properties form the raw event, after the completed hash is
27
26
  # given to the Emitter.
28
27
  class Payload
29
- include Contracts
30
-
31
28
  attr_reader :data
32
29
 
33
- Contract nil => Any
34
30
  def initialize
35
31
  @data = {}
36
32
  end
37
33
 
38
- Contract String, Or[String, Bool, Num, nil] => Or[String, Bool, Num, nil]
39
34
  # Add a single name-value pair to @data.
40
35
  def add(name, value)
41
36
  @data[name] = value if (value != '') && !value.nil?
42
37
  end
43
38
 
44
- Contract Hash => Hash
45
39
  # Add each name-value pair in a hash to @data.
46
40
  def add_hash(hash)
47
41
  hash.each { |key, value| add(key, value) }
48
42
  end
49
43
 
50
- Contract Maybe[Hash], Bool, String, String => Maybe[String]
51
44
  # Stringify a JSON and add it to @data.
52
45
  #
53
46
  # In practice, the JSON provided will be a SelfDescribingJson. This method
@@ -77,7 +77,7 @@ module SnowplowTracker
77
77
  # # Creating the SelfDescribingJson
78
78
  # schema_name = "iglu:com.snowplowanalytics/ad_click/jsonschema/1-0-0"
79
79
  # event_data = { bannerId: "4acd518feb82" }
80
- # SelfDescribingJson.new(schema_name, event_data)
80
+ # SnowplowTracker::SelfDescribingJson.new(schema_name, event_data)
81
81
  #
82
82
  # # The self-describing JSON that will be sent (stringified) with the event
83
83
  # {
@@ -14,8 +14,6 @@
14
14
  # License:: Apache License Version 2.0
15
15
 
16
16
 
17
- require 'contracts'
18
-
19
17
  module SnowplowTracker
20
18
  # Subject objects store information about the user associated with the event,
21
19
  # such as their `user_id`, what type of device they used, or what size screen
@@ -68,7 +66,7 @@ module SnowplowTracker
68
66
  #
69
67
  # Since many of the Subject parameters describe the user, different Subject
70
68
  # properties may often be desired for each event, if there are multiple users.
71
- # This can be achieved in one of two ways:
69
+ # This can be achieved in one of three ways:
72
70
  #
73
71
  # 1. the properties of the Tracker-associated Subject can be overriden by the
74
72
  # properties of an event-specific Subject. A Subject can be added to any
@@ -76,19 +74,22 @@ module SnowplowTracker
76
74
  # set the platform for the event Subject if you're not using `srv`.
77
75
  # 2. the Tracker-associated Subject can be swapped for another Subject, using
78
76
  # the Tracker method {Tracker#set_subject}.
77
+ # 3. the properties of the Tracker-associated Subject can be changed before
78
+ # every `#track_x_event`, by calling the Subject methods via the Tracker.
79
79
  #
80
80
  # @see Tracker#set_subject
81
81
  # @see
82
82
  # https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/snowplow-tracker-protocol
83
83
  # the Snowplow Tracker Protocol
84
+ # @see
85
+ # https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/ruby-tracker/enriching-your-events/
86
+ # the Snowplow docs page about adding context and other extra data to events
84
87
  # @api public
85
88
  #
86
89
  # @note All the Subject instance methods return the Subject object, allowing
87
90
  # method chaining, e.g.
88
- # `Subject.new.set_timezone('Europe/London').set_user_id('12345')`
91
+ # `SnowplowTracker::Subject.new.set_timezone('Europe/London').set_user_id('12345')`
89
92
  class Subject
90
- include Contracts
91
-
92
93
  # @private
93
94
  DEFAULT_PLATFORM = 'srv'
94
95
 
@@ -108,22 +109,22 @@ module SnowplowTracker
108
109
 
109
110
  # Access the Subject parameters
110
111
  # @example
111
- # Subject.new.set_user_id('12345').details
112
+ # SnowplowTracker::Subject.new.set_user_id('12345').details
112
113
  # => {"p"=>"srv", "uid"=>"12345"}
113
114
  # @api public
114
115
  attr_reader :details
115
116
 
116
- Contract None => Any
117
117
  # @api public
118
118
  def initialize
119
119
  @details = { 'p' => DEFAULT_PLATFORM }
120
120
  end
121
121
 
122
- Contract String => Subject
123
122
  # Set the platform to one of the supported platform values.
124
123
  # @note Value is sent in the event as `p` (raw event) or `platform` (processed event).
125
124
  # @see Subject::SUPPORTED_PLATFORMS
126
125
  # @param [String] platform a valid platform choice
126
+ # @example
127
+ # subject.set_platform('app')
127
128
  # @return self
128
129
  # @api public
129
130
  def set_platform(platform)
@@ -133,7 +134,6 @@ module SnowplowTracker
133
134
  self
134
135
  end
135
136
 
136
- Contract String => Subject
137
137
  # Set the unique business-defined user ID for a user.
138
138
  # @note Value is sent in the event as `uid` (raw event) or `user_id` (processed event).
139
139
  # For example, an email address.
@@ -152,45 +152,49 @@ module SnowplowTracker
152
152
  self
153
153
  end
154
154
 
155
- Contract Num => Subject
156
155
  # Set a business-defined fingerprint for a user.
157
156
  # @note Value is sent in the event as `fp` (raw event) or `user_fingerprint` (processed event).
158
157
  # @param [Num] fingerprint a user fingerprint
159
158
  # @return self
159
+ # @example
160
+ # subject.set_fingerprint(4048966212)
160
161
  # @api public
161
162
  def set_fingerprint(fingerprint)
162
163
  @details['fp'] = fingerprint
163
164
  self
164
165
  end
165
166
 
166
- Contract KeywordArgs[width: Num, height: Num] => Subject
167
167
  # Set the device screen resolution.
168
168
  # @note Value is sent in the event as `res` (raw event) or `dvce_screenheight` and `dvce_screenwidth` (processed event).
169
- # @param [Num] width the screen width, in pixels (must be a positive integer)
170
- # @param [Num] height the screen height, in pixels (must be a positive integer)
169
+ # @param [Integer] width the screen width, in pixels (must be a positive integer)
170
+ # @param [Integer] height the screen height, in pixels (must be a positive integer)
171
171
  # @return self
172
+ # @example
173
+ # subject.set_screen_resolution(width: 2880, height: 1800)
172
174
  # @api public
173
175
  def set_screen_resolution(width:, height:)
174
176
  @details['res'] = "#{width}x#{height}"
175
177
  self
176
178
  end
177
179
 
178
- Contract KeywordArgs[width: Num, height: Num] => Subject
179
180
  # Set the dimensions of the current viewport.
180
181
  # @note Value is sent in the event as `vp` (raw event) or `br_viewwidth` and `br_viewheight` (processed event).
181
- # @param [Num] width the viewport width, in pixels (must be a positive integer)
182
- # @param [Num] height the viewport height, in pixels (must be a positive integer)
182
+ # @param [Integer] width the viewport width, in pixels (must be a positive integer)
183
+ # @param [Integer] height the viewport height, in pixels (must be a positive integer)
183
184
  # @return self
185
+ # @example
186
+ # subject.set_viewport(width: 1440, height: 762)
184
187
  # @api public
185
188
  def set_viewport(width:, height:)
186
189
  @details['vp'] = "#{width}x#{height}"
187
190
  self
188
191
  end
189
192
 
190
- Contract Num => Subject
191
- # Set the color depth of the device, in bits per pixel.
193
+ # Set the color depth of the browser, in bits per pixel.
192
194
  # @note Value is sent in the event as `cd` (raw event) or `br_colordepth` (processed event).
193
195
  # @param [Num] depth the colour depth
196
+ # @example
197
+ # subject.set_color_depth(24)
194
198
  # @return self
195
199
  # @api public
196
200
  def set_color_depth(depth)
@@ -198,7 +202,6 @@ module SnowplowTracker
198
202
  self
199
203
  end
200
204
 
201
- Contract String => Subject
202
205
  # Set the timezone to that of the user's OS.
203
206
  # @note Value is sent in the event as `tz` (raw event) or `os_timezone` (processed event).
204
207
  # @example
@@ -211,7 +214,6 @@ module SnowplowTracker
211
214
  self
212
215
  end
213
216
 
214
- Contract String => Subject
215
217
  # Set the language.
216
218
  # @note Value is sent in the event as `lang` (raw event) or `br_lang` (processed event).
217
219
  # @example Setting the language to Spanish
@@ -224,7 +226,6 @@ module SnowplowTracker
224
226
  self
225
227
  end
226
228
 
227
- Contract String => Subject
228
229
  # Set the domain user ID.
229
230
  # @note Value is sent in the event as `duid` (raw event) or `domain_userid` (processed event).
230
231
  # @see Subject#set_network_user_id
@@ -260,7 +261,6 @@ module SnowplowTracker
260
261
  self
261
262
  end
262
263
 
263
- Contract String => Subject
264
264
  # Set the domain session ID.
265
265
  # @note Value is sent in the event as `sid` (raw event) or `domain_sessionid` (processed event).
266
266
  # @see Subject#set_network_user_id
@@ -283,7 +283,6 @@ module SnowplowTracker
283
283
  self
284
284
  end
285
285
 
286
- Contract Num => Subject
287
286
  # Set the domain session index.
288
287
  # @note Value is sent in the event as `vid` (raw event) or `domain_sessionidx` (processed event).
289
288
  # @see Subject#set_network_user_id
@@ -307,18 +306,18 @@ module SnowplowTracker
307
306
  self
308
307
  end
309
308
 
310
- Contract String => Subject
311
309
  # Set the user's IP address.
312
310
  # @note Value is sent in the event as `ip` (raw event) or `user_ipaddress` (processed event).
313
311
  # @param [String] ip the IP address
314
312
  # @return self
313
+ # @example
314
+ # subject.set_ip_address('37.157.33.178')
315
315
  # @api public
316
316
  def set_ip_address(ip)
317
317
  @details['ip'] = ip
318
318
  self
319
319
  end
320
320
 
321
- Contract String => Subject
322
321
  # Set the browser user agent.
323
322
  # @note Value is sent in the event as `ua` (raw event) or `useragent` (processed event).
324
323
  # @example
@@ -331,7 +330,6 @@ module SnowplowTracker
331
330
  self
332
331
  end
333
332
 
334
- Contract String => Subject
335
333
  # Set the network user ID.
336
334
  # @note Value is sent in the event as `tnuid` (raw event) and `network_userid` (processed event).
337
335
  # @see Subject#set_domain_user_id
@@ -38,41 +38,39 @@ module SnowplowTracker
38
38
  # DeviceTimestamp by the Tracker, and will still be recorded as `dtm` in
39
39
  # the event.
40
40
  # 2. Manually create a DeviceTimestamp (e.g.
41
- # `DeviceTimestamp.new(1633596554978)`), and provide this to the
41
+ # `SnowplowTracker::DeviceTimestamp.new(1633596554978)`), and provide this to the
42
42
  # `#track_x_event` method. This will still be recorded as `dtm` in the
43
43
  # event.
44
44
  # 3. Provide a TrueTimestamp object to the `track_x_event` method (e.g.
45
- # `TrueTimestamp.new(1633596554978)`). This will result in a `ttm` field in
45
+ # `SnowplowTracker::TrueTimestamp.new(1633596554978)`). This will result in a `ttm` field in
46
46
  # the event.
47
47
  #
48
48
  # The timestamps that are added to the event once it has been emitted are not
49
49
  # the responsibility of this class. The collector receives the event and adds a
50
50
  # `collector_tstamp`. A later part of the pipeline adds the `etl_tstamp` when
51
- # the event enrichment has finished. A `derived_tstamp` is also calculated and
52
- # added to the event, which represents how long it took the event to be
53
- # processed. This is calculated by `collector_tstamp - (dvce_sent_tstamp -
54
- # dvce_created_tstamp)`.
51
+ # the event enrichment has finished.
52
+ #
53
+ # When DeviceTimestamp is used, a `derived_tstamp` is also calculated and
54
+ # added to the event. This timestamp attempts to take latency and possible
55
+ # inaccuracy of the device clock into account. It is calculated by
56
+ # `collector_tstamp - (dvce_sent_tstamp - dvce_created_tstamp)`. When
57
+ # TrueTimestamp is used, the `derived_stamp` will be the same as
58
+ # `true_tstamp`.
55
59
  #
56
60
  # @see
57
61
  # https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/snowplow-tracker-protocol
58
62
  # the Snowplow Tracker Protocol
63
+ # @see
64
+ # https://discourse.snowplowanalytics.com/t/which-timestamp-is-the-best-to-see-when-an-event-occurred/538
65
+ # A Discourse forums post explaining timestamps
59
66
  # @api public
60
67
  class Timestamp
61
- include Contracts
62
-
63
68
  # @private
64
69
  attr_reader :type
65
70
 
66
71
  # @private
67
72
  attr_reader :value
68
73
 
69
- # @private
70
- # Contract type
71
- TIMESTAMP = ->(x) {
72
- return false unless x.is_a? Integer
73
- x.to_s.length == 13
74
- }
75
-
76
74
  # @private
77
75
  def initialize(type, value)
78
76
  @type = type
@@ -92,10 +90,9 @@ module SnowplowTracker
92
90
  # it is, namely `ttm`. This raw event `ttm` field will be processed into
93
91
  # `true_tstamp` in the completed event.
94
92
  class TrueTimestamp < Timestamp
95
- Contract TIMESTAMP => Any
96
93
  # @param [Num] value timestamp in milliseconds since the Unix epoch
97
94
  # @example
98
- # TrueTimestamp.new(1633596346786)
95
+ # SnowplowTracker::TrueTimestamp.new(1633596346786)
99
96
  def initialize(value)
100
97
  super 'ttm', value
101
98
  end
@@ -107,10 +104,9 @@ module SnowplowTracker
107
104
  # it is, namely `dtm`. This raw event `dtm` field will be processed into
108
105
  # `dvce_created_tstamp` in the completed event.
109
106
  class DeviceTimestamp < Timestamp
110
- Contract TIMESTAMP => Any
111
107
  # @param [Num] value timestamp in milliseconds since the Unix epoch
112
108
  # @example
113
- # DeviceTimestamp.new(1633596346786)
109
+ # SnowplowTracker::DeviceTimestamp.new(1633596346786)
114
110
  def initialize(value)
115
111
  super 'dtm', value
116
112
  end
@@ -14,7 +14,6 @@
14
14
  # License:: Apache License Version 2.0
15
15
 
16
16
 
17
- require 'contracts'
18
17
  require 'securerandom'
19
18
  require 'set'
20
19
 
@@ -118,62 +117,16 @@ module SnowplowTracker
118
117
  # https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/snowplow-tracker-protocol
119
118
  # the Snowplow Tracker Protocol
120
119
  # @see
120
+ # https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/ruby-tracker/tracking-events/
121
+ # the Snowplow docs page about tracking events
122
+ # @see
123
+ # https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/ruby-tracker/enriching-your-events/
124
+ # the Snowplow docs page about adding context and other extra data to events
125
+ # @see
121
126
  # https://docs.snowplowanalytics.com/docs/understanding-tracking-design/introduction-to-tracking-design/
122
127
  # introduction to Snowplow tracking design
123
128
  # @api public
124
129
  class Tracker
125
- include Contracts
126
-
127
- # Contract types
128
-
129
- # @private
130
- EMITTER_INPUT = Or[->(x) { x.is_a? Emitter }, ArrayOf[->(x) { x.is_a? Emitter }]]
131
-
132
- # @private
133
- REQUIRED_TRANSACTION_KEYS = Set.new(%w[order_id total_value])
134
- # @private
135
- RECOGNISED_TRANSACTION_KEYS = Set.new(%w[
136
- order_id total_value affiliation tax_value
137
- shipping city state country currency
138
- ])
139
-
140
- # @private
141
- TRANSACTION = ->(x) {
142
- return false unless x.class == Hash
143
- transaction_keys = Set.new(x.keys.map(&:to_s))
144
- REQUIRED_TRANSACTION_KEYS.subset?(transaction_keys) &&
145
- transaction_keys.subset?(RECOGNISED_TRANSACTION_KEYS)
146
- }
147
- # @private
148
- REQUIRED_ITEM_KEYS = Set.new(%w[sku price quantity])
149
- # @private
150
- RECOGNISED_ITEM_KEYS = Set.new(%w[sku price quantity name category context])
151
-
152
- # @private
153
- ITEM = ->(x) {
154
- return false unless x.class == Hash
155
- item_keys = Set.new(x.keys.map(&:to_s))
156
- REQUIRED_ITEM_KEYS.subset?(item_keys) &&
157
- item_keys.subset?(RECOGNISED_ITEM_KEYS)
158
- }
159
- # @private
160
- REQUIRED_AUGMENTED_ITEM_KEYS = Set.new(%w[sku price quantity tstamp order_id])
161
- # @private
162
- RECOGNISED_AUGMENTED_ITEM_KEYS = Set.new(%w[sku price quantity name category context tstamp order_id currency])
163
-
164
- # @private
165
- AUGMENTED_ITEM = ->(x) {
166
- return false unless x.class == Hash
167
- augmented_item_keys = Set.new(x.keys)
168
- REQUIRED_AUGMENTED_ITEM_KEYS.subset?(augmented_item_keys) &&
169
- augmented_item_keys.subset?(RECOGNISED_AUGMENTED_ITEM_KEYS)
170
- }
171
-
172
- # @private
173
- CONTEXTS_INPUT = ArrayOf[SelfDescribingJson]
174
-
175
- # Other constants
176
-
177
130
  # @!group Public constants
178
131
 
179
132
  # SelfDescribingJson objects are sent encoded by default
@@ -192,8 +145,6 @@ module SnowplowTracker
192
145
 
193
146
  # @!endgroup
194
147
 
195
- Contract KeywordArgs[emitters: EMITTER_INPUT, subject: Maybe[Subject], namespace: Maybe[String],
196
- app_id: Maybe[String], encode_base64: Optional[Bool]] => Any
197
148
  # Create a new Tracker. `emitters` is the only strictly required parameter.
198
149
  #
199
150
  # @param emitters [Emitter, Array<Emitter>] one or more Emitter objects
@@ -202,14 +153,18 @@ module SnowplowTracker
202
153
  # @param app_id [String] the app ID
203
154
  # @param encode_base64 [Bool] whether JSONs will be base64-encoded or not
204
155
  # @example Initializing a Tracker with all possible options
205
- # Tracker.new(
206
- # emitters: Emitter.new('collector.example.com'),
207
- # subject: Subject.new,
156
+ # SnowplowTracker::Tracker.new(
157
+ # emitters: SnowplowTracker::Emitter.new(endpoint: 'collector.example.com'),
158
+ # subject: SnowplowTracker::Subject.new,
208
159
  # namespace: 'tracker_no_encode',
209
160
  # app_id: 'rails_main',
210
161
  # encode_base64: false
211
162
  # )
212
163
  # @api public
164
+ #
165
+ # @note All the Tracker instance methods return the Tracker object, allowing
166
+ # method chaining, e.g.
167
+ # `SnowplowTracker::Tracker.new.set_user_id('12345').track_page_view(page_url: 'www.example.com`
213
168
  def initialize(emitters:, subject: nil, namespace: nil, app_id: nil, encode_base64: DEFAULT_ENCODE_BASE64)
214
169
  @emitters = Array(emitters)
215
170
  @subject = if subject.nil?
@@ -269,14 +224,12 @@ module SnowplowTracker
269
224
  end
270
225
  end
271
226
 
272
- Contract nil => String
273
227
  # Generates a type-4 UUID to identify this event
274
228
  # @private
275
229
  def event_id
276
230
  SecureRandom.uuid
277
231
  end
278
232
 
279
- Contract CONTEXTS_INPUT => Hash
280
233
  # Builds a single self-describing JSON from an array of custom contexts
281
234
  # @private
282
235
  def build_context(context)
@@ -286,7 +239,6 @@ module SnowplowTracker
286
239
  ).to_json
287
240
  end
288
241
 
289
- Contract Payload => nil
290
242
  # Sends the payload hash as a request to the Emitter(s)
291
243
  # @private
292
244
  def track(payload)
@@ -295,7 +247,6 @@ module SnowplowTracker
295
247
  nil
296
248
  end
297
249
 
298
- Contract Or[Timestamp, Num, nil] => Timestamp
299
250
  # Ensures that either a DeviceTimestamp or TrueTimestamp is associated with
300
251
  # every event.
301
252
  # @private
@@ -305,7 +256,6 @@ module SnowplowTracker
305
256
  tstamp
306
257
  end
307
258
 
308
- Contract Payload, Maybe[CONTEXTS_INPUT], Timestamp, Maybe[Subject], Maybe[Page] => nil
309
259
  # Attaches the more generic fields to the event payload. This includes
310
260
  # context, Subject, and Page if they are present. The timestamp is added, as
311
261
  # well as all fields from `@settings`.
@@ -329,10 +279,11 @@ module SnowplowTracker
329
279
  nil
330
280
  end
331
281
 
332
- Contract KeywordArgs[page_url: String, page_title: Maybe[String], referrer: Maybe[String],
333
- context: Maybe[CONTEXTS_INPUT], tstamp: Or[Timestamp, Num, nil],
334
- subject: Maybe[Subject], page: Maybe[Page]] => Tracker
335
282
  # Track a visit to a page.
283
+ # @example
284
+ # SnowplowTracker::Tracker.new.track_page_view(page_url: 'www.example.com',
285
+ # page_title: 'example',
286
+ # referrer: 'www.referrer.com')
336
287
  #
337
288
  # @param page_url [String] the URL of the page
338
289
  # @param page_title [String] the page title
@@ -359,9 +310,6 @@ module SnowplowTracker
359
310
  self
360
311
  end
361
312
 
362
- Contract KeywordArgs[transaction: TRANSACTION, items: ArrayOf[ITEM],
363
- context: Maybe[CONTEXTS_INPUT], tstamp: Or[Timestamp, Num, nil],
364
- subject: Maybe[Subject], page: Maybe[Page]] => Tracker
365
313
  # Track an eCommerce transaction, and all the items in it.
366
314
  #
367
315
  # This method is unique in sending multiple events: one `transaction` event,
@@ -482,7 +430,6 @@ module SnowplowTracker
482
430
  hash.keys.each { |key| hash[key.to_s] = hash.delete key }
483
431
  end
484
432
 
485
- Contract AUGMENTED_ITEM, Maybe[Subject], Maybe[Page] => self
486
433
  # Track a single item within an ecommerce transaction.
487
434
  # @private
488
435
  def track_ecommerce_transaction_item(details, subject, page)
@@ -502,10 +449,6 @@ module SnowplowTracker
502
449
  self
503
450
  end
504
451
 
505
- Contract KeywordArgs[category: String, action: String, label: Maybe[String],
506
- property: Maybe[String], value: Maybe[Num],
507
- context: Maybe[CONTEXTS_INPUT], tstamp: Or[Timestamp, Num, nil],
508
- subject: Maybe[Subject], page: Maybe[Page]] => Tracker
509
452
  # Track a structured event. `category` and `action` are required.
510
453
  #
511
454
  # This event type can be used to track many types of user activity, as it is
@@ -516,6 +459,15 @@ module SnowplowTracker
516
459
  # For fully customizable event tracking, we recommend you use
517
460
  # self-describing events.
518
461
  #
462
+ # @example
463
+ # SnowplowTracker::Tracker.new.track_struct_event(
464
+ # category: 'shop',
465
+ # action: 'add-to-basket',
466
+ # property: 'pcs',
467
+ # value: 2
468
+ # )
469
+ #
470
+ #
519
471
  # @see #track_self_describing_event
520
472
  #
521
473
  # @param category [String] the event category
@@ -547,9 +499,6 @@ module SnowplowTracker
547
499
  self
548
500
  end
549
501
 
550
- Contract KeywordArgs[name: Maybe[String], id: Maybe[String],
551
- context: Maybe[CONTEXTS_INPUT], tstamp: Or[Timestamp, Num, nil],
552
- subject: Maybe[Subject], page: Maybe[Page]] => Tracker
553
502
  # Track a screen view event. Note that while the `name` and `id` parameters
554
503
  # are both optional, you must provided at least one of them to create a
555
504
  # valid event.
@@ -560,6 +509,11 @@ module SnowplowTracker
560
509
  # "iglu:com.snowplowanalytics.snowplow/screen_view/jsonschema/1-0-0", and
561
510
  # the data field will contain the name and/or ID.
562
511
  #
512
+ # @example
513
+ # SnowplowTracker::Tracker.new.track_screen_view(name: 'HUD > Save Game',
514
+ # id: 'screen23')
515
+ #
516
+ #
563
517
  # @see #track_page_view
564
518
  # @see #track_self_describing_event
565
519
  #
@@ -583,9 +537,6 @@ module SnowplowTracker
583
537
  self
584
538
  end
585
539
 
586
- Contract KeywordArgs[event_json: SelfDescribingJson, context: Maybe[CONTEXTS_INPUT],
587
- tstamp: Or[Timestamp, Num, nil], subject: Maybe[Subject],
588
- page: Maybe[Page]] => Tracker
589
540
  # Track a self-describing event. These are custom events based on
590
541
  # {SelfDescribingJson}, i.e. a JSON schema and a defined set of properties.
591
542
  #
@@ -595,6 +546,20 @@ module SnowplowTracker
595
546
  # This method creates an `unstruct` event type. It is actually an alias for
596
547
  # {#track_unstruct_event}, which is depreciated due to its unhelpful name.
597
548
  #
549
+ # @example
550
+ # self_desc_json = SnowplowTracker::SelfDescribingJson.new(
551
+ # "iglu:com.example_company/save_game/jsonschema/1-0-2",
552
+ # {
553
+ # "saveId" => "4321",
554
+ # "level" => 23,
555
+ # "difficultyLevel" => "HARD",
556
+ # "dlContent" => true
557
+ # }
558
+ # )
559
+ #
560
+ # SnowplowTracker::Tracker.new.track_self_describing_event(event_json: self_desc_json)
561
+ #
562
+ #
598
563
  # @param event_json [SelfDescribingJson] a SelfDescribingJson object
599
564
  # @param context [Array<SelfDescribingJson>] an array of SelfDescribingJson objects
600
565
  # @param tstamp [DeviceTimestamp, TrueTimestamp, Num] override the default DeviceTimestamp of the event
@@ -607,9 +572,6 @@ module SnowplowTracker
607
572
  tstamp: tstamp, subject: subject, page: page)
608
573
  end
609
574
 
610
- Contract KeywordArgs[event_json: SelfDescribingJson, context: Maybe[CONTEXTS_INPUT],
611
- tstamp: Or[Timestamp, Num, nil], subject: Maybe[Subject],
612
- page: Maybe[Page]] => Tracker
613
575
  # @deprecated Use {#track_self_describing_event} instead.
614
576
  #
615
577
  # @api public
@@ -628,7 +590,6 @@ module SnowplowTracker
628
590
  self
629
591
  end
630
592
 
631
- Contract KeywordArgs[async: Optional[Bool]] => Tracker
632
593
  # Manually flush all events stored in all Tracker-associated Emitters. By
633
594
  # default, this happens synchronously. {Emitter}s can only send events
634
595
  # synchronously, while {AsyncEmitter}s can send either synchronously or
@@ -645,7 +606,6 @@ module SnowplowTracker
645
606
  self
646
607
  end
647
608
 
648
- Contract Subject => Tracker
649
609
  # Replace the existing Tracker-associated Subject with the provided one. All
650
610
  # subsequent events will have the properties of the new Subject, unless they
651
611
  # are overriden by event-specific Subject parameters.
@@ -658,7 +618,6 @@ module SnowplowTracker
658
618
  self
659
619
  end
660
620
 
661
- Contract Emitter => Tracker
662
621
  # Add a new Emitter to the internal array of Tracker-associated Emitters.
663
622
  #
664
623
  # @param emitter [Emitter] an Emitter object
@@ -24,13 +24,6 @@
24
24
  # see an example of how to incorporate the Snowplow Ruby tracker in Ruby on
25
25
  # Rails app.
26
26
  #
27
- # # Type checking
28
- #
29
- # This gem uses the [Contracts](https://github.com/egonSchiele/contracts.ruby)
30
- # gem for typechecking. This cannot be disabled. The {Tracker} `track_x_event`
31
- # methods expect arguments of a certain type. If a check fails, a runtime error
32
- # is thrown.
33
- #
34
27
  # @see https://github.com/snowplow/snowplow-ruby-tracker
35
28
  # Ruby tracker on Github
36
29
  # @see https://github.com/snowplow-incubator/snowplow-ruby-tracker-examples
@@ -41,7 +34,7 @@
41
34
  # @api public
42
35
  module SnowplowTracker
43
36
  # The version of Ruby Snowplow tracker you are using
44
- VERSION = '0.7.0'
37
+ VERSION = '0.8.0'
45
38
 
46
39
  # All events from this tracker will have this string
47
40
  TRACKER_VERSION = "rb-#{VERSION}"
metadata CHANGED
@@ -1,35 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snowplow-tracker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Snowplow Analytics Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-14 00:00:00.000000000 Z
11
+ date: 2021-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: contracts
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '0.7'
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '0.17'
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '0.7'
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '0.17'
33
13
  - !ruby/object:Gem::Dependency
34
14
  name: rspec
35
15
  requirement: !ruby/object:Gem::Requirement