staccato 0.1.1 → 0.2.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 +7 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/README.md +211 -3
- data/lib/staccato.rb +2 -0
- data/lib/staccato/boolean_helpers.rb +40 -0
- data/lib/staccato/exception.rb +3 -1
- data/lib/staccato/hit.rb +63 -42
- data/lib/staccato/measurable.rb +44 -0
- data/lib/staccato/measurement.rb +43 -0
- data/lib/staccato/measurement/checkout.rb +20 -0
- data/lib/staccato/measurement/checkout_option.rb +20 -0
- data/lib/staccato/measurement/impression_list.rb +26 -0
- data/lib/staccato/measurement/product.rb +34 -0
- data/lib/staccato/measurement/product_impression.rb +33 -0
- data/lib/staccato/measurement/promotion.rb +29 -0
- data/lib/staccato/measurement/transaction.rb +25 -0
- data/lib/staccato/null_measurement.rb +12 -0
- data/lib/staccato/tracker.rb +42 -11
- data/lib/staccato/version.rb +1 -1
- data/spec/integration/measurement/checkout_option_spec.rb +47 -0
- data/spec/integration/measurement/checkout_spec.rb +47 -0
- data/spec/integration/measurement/product_impression_spec.rb +61 -0
- data/spec/integration/measurement/product_spec.rb +66 -0
- data/spec/integration/measurement/promotion_spec.rb +51 -0
- data/spec/integration/measurement/transaction_spec.rb +57 -0
- data/spec/lib/staccato/measurement_spec.rb +7 -0
- metadata +48 -37
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9a674a0fa88901074b28c03b98647b7751322412
|
4
|
+
data.tar.gz: d6db8298721e606f0855c54959eb42537e0d496c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 323fdd8c54466b98790b5f446c1d20464bb3ce8271a2785881d49d4edf3257602367e05cdfdc38db84d21d5fc5164684c1b25f87d422ee35933c5da7a840b0d5
|
7
|
+
data.tar.gz: 5b290b2d0602444e10562619feef4763161e546aea5dedad33ad0c3eb680633a22566cc37da718674a2587515fd4921e6b86c044a02c61f4a5fb9d273feaef3b
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## Staccato 0.2.0 ##
|
2
|
+
|
3
|
+
* Enhanced Ecommerce Measurements
|
4
|
+
* Measurable module for further extension of hits
|
5
|
+
* New global hit options for ecommerce
|
6
|
+
|
7
|
+
*Tony Pitale <@tpitale>*
|
8
|
+
|
1
9
|
## Staccato 0.1.1 ##
|
2
10
|
|
3
11
|
* fixes NoopTracker when track! is called on a hit *martin1keogh*
|
data/README.md
CHANGED
@@ -133,7 +133,10 @@ Staccato::Hit::GLOBAL_OPTIONS.keys # =>
|
|
133
133
|
:application_id,
|
134
134
|
:application_installer_id,
|
135
135
|
:experiment_id,
|
136
|
-
:experiment_variant
|
136
|
+
:experiment_variant,
|
137
|
+
:product_action,
|
138
|
+
:product_action_list,
|
139
|
+
:promotion_action]
|
137
140
|
```
|
138
141
|
|
139
142
|
Boolean options like `anonymize_ip` will be converted from `true`/`false` into `1`/`0` as per the tracking API docs.
|
@@ -200,13 +203,218 @@ tracker.pageview(path: '/videos/123')
|
|
200
203
|
tracker.pageview(path: '/videos/987')
|
201
204
|
```
|
202
205
|
|
203
|
-
##
|
206
|
+
## Additional Measurements ##
|
207
|
+
|
208
|
+
Additional Measurements can be added to any Hit type, but most commonly used with pageviews or events. The current set of measurements is for handling [Enhanced Ecommerce](https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide#enhancedecom) measurements. I've grouped these into ImpressionList, Product, ProductImpression, Promotion, Transaction, Checkout, and CheckoutOption (w/ ImpressionList). Each can be added and combined – per Google's documentation – onto an existing Hit.
|
209
|
+
|
210
|
+
**Note:** Certain Measurements require an `index`. This is an integer (usually) between 1 and 200 inclusive.
|
211
|
+
|
212
|
+
**Note:** Certain Measurements require a `product_action` to be set. This is a global option, and should be set on the original hit. The `product_action` can be any one of:
|
213
|
+
|
214
|
+
* `detail`
|
215
|
+
* `click`
|
216
|
+
* `add`
|
217
|
+
* `remove`
|
218
|
+
* `checkout`
|
219
|
+
* `checkout_option`
|
220
|
+
* `purchase`
|
221
|
+
* `refund`
|
222
|
+
|
223
|
+
### Transaction w/ Product ###
|
224
|
+
|
225
|
+
Using a pageview to track a transaction with a product (using the 'purchase' as the `product_action`:
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
pageview = tracker.build_pageview(path: '/receipt', hostname: 'mystore.com', title: 'Your Receipt', product_action: 'purchase')
|
229
|
+
|
230
|
+
pageview.add_measurement(:transaction, {
|
231
|
+
transaction_id: 'T12345',
|
232
|
+
affiliation: 'Your Store',
|
233
|
+
revenue: 37.39,
|
234
|
+
tax: 2.85,
|
235
|
+
shipping: 5.34,
|
236
|
+
currency: 'USD',
|
237
|
+
coupon_code: 'SUMMERSALE'
|
238
|
+
})
|
239
|
+
|
240
|
+
pageview.add_measurement(:product, {
|
241
|
+
index: 1, # this is our first product, value may be 1-200
|
242
|
+
id: 'P12345',
|
243
|
+
name: 'T-Shirt',
|
244
|
+
category: 'Apparel',
|
245
|
+
brand: 'Your Brand',
|
246
|
+
variant: 'Purple',
|
247
|
+
quantity: 2,
|
248
|
+
position: 1,
|
249
|
+
price: 14.60,
|
250
|
+
coupon_code: 'ILUVTEES'
|
251
|
+
})
|
252
|
+
|
253
|
+
pageview.track!
|
254
|
+
```
|
255
|
+
|
256
|
+
### Transaction Refund ###
|
257
|
+
|
258
|
+
The combination of `product_action: 'refund'` and `transaction` measurement setting a matching `id` to a previous transaction.
|
259
|
+
|
260
|
+
```ruby
|
261
|
+
event = tracker.build_event(category: 'order', action: 'refund', non_interactive: true, product_action: 'refund')
|
262
|
+
|
263
|
+
event.add_measurement(:transaction, id: 'T12345')
|
264
|
+
|
265
|
+
event.track!
|
266
|
+
```
|
267
|
+
|
268
|
+
### Transaction & Product Refund ###
|
269
|
+
|
270
|
+
The combination of `product_action: 'refund'` and `transaction` measurement setting a matching `id` to a previous transaction. You can also specify a product (or products, using `index`) with a `quantity` (for partial refunds) to refund.
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
event = tracker.build_event(category: 'order', action: 'refund', non_interactive: true, product_action: 'refund')
|
274
|
+
|
275
|
+
event.add_measurement(:transaction, id: 'T12345')
|
276
|
+
event.add_measurement(:product, index: 1, id: 'P12345', quantity: 1)
|
277
|
+
|
278
|
+
event.track!
|
279
|
+
```
|
280
|
+
|
281
|
+
### Promotion Impression ###
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
pageview = tracker.build_pageview(path: '/search', hostname: 'mystore.com', title: 'Search Results')
|
285
|
+
|
286
|
+
pageview.add_measurement(:promotion, {
|
287
|
+
index: 1,
|
288
|
+
id: 'PROMO_1234',
|
289
|
+
name: 'Summer Sale',
|
290
|
+
creative: 'summer_sale_banner',
|
291
|
+
position: 'banner_1'
|
292
|
+
})
|
293
|
+
|
294
|
+
pageview.track!
|
295
|
+
```
|
296
|
+
|
297
|
+
### Promotion Click ###
|
298
|
+
|
299
|
+
Promotion also supports a `promotion_action`, similar to `product_action`. This is another global option on `Hit`.
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
event = tracker.build_event(category: 'promotions', action: 'click', label: 'internal', promotion_action: 'click')
|
303
|
+
|
304
|
+
event.add_measurement(:promotion, {
|
305
|
+
index: 1,
|
306
|
+
id: 'PROMO_1234',
|
307
|
+
name: 'Summer Sale',
|
308
|
+
creative: 'summer_sale_banner',
|
309
|
+
position: 'banner_1'
|
310
|
+
})
|
311
|
+
|
312
|
+
event.track!
|
313
|
+
```
|
314
|
+
|
315
|
+
### Product Click ###
|
316
|
+
|
317
|
+
```ruby
|
318
|
+
event = tracker.build_event(category: 'search', action: 'click', label: 'results', product_action: 'click', product_action_list: 'Search Results')
|
319
|
+
|
320
|
+
event.add_measurement(:product, {
|
321
|
+
index: 1,
|
322
|
+
id: 'P12345',
|
323
|
+
name: 'T-Shirt',
|
324
|
+
category: 'Apparel',
|
325
|
+
brand: 'Your Brand',
|
326
|
+
variant: 'Purple',
|
327
|
+
quantity: 2,
|
328
|
+
position: 1,
|
329
|
+
price: 14.60,
|
330
|
+
coupon_code: 'ILUVTEES'
|
331
|
+
})
|
332
|
+
|
333
|
+
event.track!
|
334
|
+
```
|
335
|
+
|
336
|
+
### Checkout ###
|
337
|
+
|
338
|
+
```ruby
|
339
|
+
pageview = tracker.build_pageview(path: '/checkout', hostname: 'mystore.com', title: 'Complete Your Checkout', product_action: 'checkout')
|
340
|
+
|
341
|
+
pageview.add_measurement(:product, {
|
342
|
+
index: 1, # this is our first product, value may be 1-200
|
343
|
+
id: 'P12345',
|
344
|
+
name: 'T-Shirt',
|
345
|
+
category: 'Apparel',
|
346
|
+
brand: 'Your Brand',
|
347
|
+
variant: 'Purple',
|
348
|
+
quantity: 2,
|
349
|
+
position: 1,
|
350
|
+
price: 14.60,
|
351
|
+
coupon_code: 'ILUVTEES'
|
352
|
+
})
|
353
|
+
|
354
|
+
pageview.add_measurement(:checkout, {
|
355
|
+
step: 1,
|
356
|
+
step_option: 'Visa'
|
357
|
+
})
|
358
|
+
|
359
|
+
pageview.track!
|
360
|
+
```
|
361
|
+
|
362
|
+
### Checkout Option ###
|
363
|
+
|
364
|
+
```ruby
|
365
|
+
event = tracker.build_event(category: 'checkout', action: 'option', non_interactive: true, product_action: 'checkout_option')
|
366
|
+
|
367
|
+
event.add_measurement(:checkout_options, {
|
368
|
+
step: 2,
|
369
|
+
step_option: 'Fedex'
|
370
|
+
})
|
371
|
+
|
372
|
+
event.track!
|
373
|
+
```
|
374
|
+
|
375
|
+
### Impression List & Product Impression ###
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
pageview = tracker.build_pageview(path: '/home', hostname: 'mystore.com', title: 'Home Page')
|
379
|
+
|
380
|
+
pageview.add_measurement(:impression_list, index: 1, name: 'Search Results')
|
381
|
+
|
382
|
+
pageview.add_measurement(:product_impression, {
|
383
|
+
index: 1,
|
384
|
+
list_index: 1, # match the impression_list above
|
385
|
+
id: 'P12345',
|
386
|
+
name: 'T-Shirt',
|
387
|
+
category: 'Apparel',
|
388
|
+
brand: 'Your Brand',
|
389
|
+
variant: 'Purple',
|
390
|
+
position: 1,
|
391
|
+
price: 14.60
|
392
|
+
})
|
393
|
+
|
394
|
+
pageview.add_measurement(:impression_list, index: 2, name: 'Recommendations')
|
395
|
+
|
396
|
+
pageview.add_measurement(:product_impression, {
|
397
|
+
index: 1,
|
398
|
+
list_index: 2,
|
399
|
+
name: 'Yellow Tee'
|
400
|
+
})
|
401
|
+
|
402
|
+
pageview.add_measurement(:product_impression, {
|
403
|
+
index: 2,
|
404
|
+
list_index: 2,
|
405
|
+
name: 'Red Tee'
|
406
|
+
})
|
407
|
+
|
408
|
+
pageview.track!
|
409
|
+
```
|
410
|
+
|
411
|
+
## Google Documentation ##
|
204
412
|
|
205
413
|
https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide
|
206
414
|
https://developers.google.com/analytics/devguides/collection/protocol/v1/reference
|
207
415
|
https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters
|
208
416
|
|
209
|
-
## Contributing
|
417
|
+
## Contributing ##
|
210
418
|
|
211
419
|
1. Fork it
|
212
420
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
data/lib/staccato.rb
CHANGED
@@ -38,6 +38,7 @@ module Staccato
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
require 'staccato/boolean_helpers'
|
41
42
|
require 'staccato/option_set'
|
42
43
|
require 'staccato/hit'
|
43
44
|
require 'staccato/pageview'
|
@@ -48,3 +49,4 @@ require 'staccato/timing'
|
|
48
49
|
require 'staccato/transaction'
|
49
50
|
require 'staccato/transaction_item'
|
50
51
|
require 'staccato/tracker'
|
52
|
+
require 'staccato/measurement'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Collection of methods for converting to
|
2
|
+
# google's boolean integers from ruby's booleans
|
3
|
+
module BooleanHelpers
|
4
|
+
# Convert each boolean in the hash to integer
|
5
|
+
# if it is a boolean field
|
6
|
+
# @param hash [Hash]
|
7
|
+
# @return [Hash]
|
8
|
+
def convert_booleans(hash)
|
9
|
+
hash.each_pair.with_object({}, &method(:convert_boolean))
|
10
|
+
end
|
11
|
+
|
12
|
+
# Method to convert a single field from bool to int
|
13
|
+
# @param hash [#[]=] the collector object
|
14
|
+
def convert_boolean((k,v), hash)
|
15
|
+
hash[k] = boolean_field?(k) ? integer_for(v) : v
|
16
|
+
end
|
17
|
+
|
18
|
+
# Is this key one of the boolean fields
|
19
|
+
# @param key [Symbol] field key
|
20
|
+
# @return [Boolean]
|
21
|
+
def boolean_field?(key)
|
22
|
+
boolean_fields.include?(key)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convert a value to appropriate int
|
26
|
+
# @param value [nil, true, false, Integer]
|
27
|
+
# @return [nil, Integer]
|
28
|
+
def integer_for(value)
|
29
|
+
case value
|
30
|
+
when Integer
|
31
|
+
value
|
32
|
+
when TrueClass
|
33
|
+
1
|
34
|
+
when FalseClass
|
35
|
+
0
|
36
|
+
when NilClass
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/staccato/exception.rb
CHANGED
data/lib/staccato/hit.rb
CHANGED
@@ -18,6 +18,7 @@ module Staccato
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
# Hit global options may be set on any hit type options
|
21
22
|
GLOBAL_OPTIONS = {
|
22
23
|
anonymize_ip: 'aip', # boolean
|
23
24
|
queue_time: 'qt', # integer
|
@@ -63,14 +64,22 @@ module Staccato
|
|
63
64
|
|
64
65
|
# Content Experiments
|
65
66
|
experiment_id: 'xid',
|
66
|
-
experiment_variant: 'xvar'
|
67
|
-
}
|
67
|
+
experiment_variant: 'xvar',
|
68
68
|
|
69
|
+
# Product
|
70
|
+
product_action: 'pa',
|
71
|
+
product_action_list: 'pal',
|
72
|
+
|
73
|
+
# Promotion
|
74
|
+
promotion_action: 'promoa'
|
75
|
+
}.freeze
|
76
|
+
|
77
|
+
# Fields which should be converted to boolean for google
|
69
78
|
BOOLEAN_FIELDS = [
|
70
79
|
:non_interactive,
|
71
80
|
:anonymize_ip,
|
72
81
|
:java_enabled
|
73
|
-
]
|
82
|
+
].freeze
|
74
83
|
|
75
84
|
# sets up a new hit
|
76
85
|
# @param tracker [Staccato::Tracker] the tracker to collect to
|
@@ -88,31 +97,60 @@ module Staccato
|
|
88
97
|
|
89
98
|
# collects the parameters from options for this hit type
|
90
99
|
def params
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
100
|
+
{}.
|
101
|
+
merge!(base_params).
|
102
|
+
merge!(tracker_default_params).
|
103
|
+
merge!(global_options_params).
|
104
|
+
merge!(hit_params).
|
105
|
+
merge!(custom_dimensions).
|
106
|
+
merge!(custom_metrics).
|
107
|
+
merge!(measurement_params).
|
108
|
+
reject {|_,v| v.nil?}
|
109
|
+
end
|
110
|
+
|
111
|
+
# Set a custom dimension value at an index
|
112
|
+
# @param index [Integer]
|
113
|
+
# @param value
|
114
|
+
def add_custom_dimension(index, value)
|
115
|
+
self.custom_dimensions["cd#{index}"] = value
|
116
|
+
end
|
117
|
+
|
118
|
+
# Custom dimensions for this hit
|
119
|
+
# @return [Hash]
|
104
120
|
def custom_dimensions
|
105
121
|
@custom_dimensions ||= {}
|
106
122
|
end
|
107
123
|
|
108
|
-
|
109
|
-
|
124
|
+
# Set a custom metric value at an index
|
125
|
+
# @param index [Integer]
|
126
|
+
# @param value
|
127
|
+
def add_custom_metric(index, value)
|
128
|
+
self.custom_metrics["cm#{index}"] = value
|
110
129
|
end
|
111
130
|
|
131
|
+
# Custom metrics for this hit
|
132
|
+
# @return [Hash]
|
112
133
|
def custom_metrics
|
113
134
|
@custom_metrics ||= {}
|
114
135
|
end
|
115
136
|
|
137
|
+
# Add a measurement by its symbol name with options
|
138
|
+
#
|
139
|
+
# @param key [Symbol] any one of the measurable classes lookup key
|
140
|
+
# @param options [Hash] options for the measurement
|
141
|
+
def add_measurement(key, options = {})
|
142
|
+
self.measurements << Measurement.lookup(key).new(options)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Measurements for this hit
|
146
|
+
# @return [Array<Measurable>]
|
147
|
+
def measurements
|
148
|
+
@measurements ||= []
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns the value for session control
|
152
|
+
# based on options for session_start/_end
|
153
|
+
# @return ['start', 'end']
|
116
154
|
def session_control
|
117
155
|
case
|
118
156
|
when options[:session_start], options[:start_session]
|
@@ -128,34 +166,12 @@ module Staccato
|
|
128
166
|
end
|
129
167
|
|
130
168
|
private
|
131
|
-
|
132
|
-
hash.each_pair.with_object({}, &method(:convert_boolean))
|
133
|
-
end
|
134
|
-
|
135
|
-
def convert_boolean((k,v), hash)
|
136
|
-
hash[k] = boolean_field?(k) ? integer_for(v) : v
|
137
|
-
end
|
138
|
-
|
169
|
+
# @private
|
139
170
|
def boolean_fields
|
140
171
|
BOOLEAN_FIELDS
|
141
172
|
end
|
142
173
|
|
143
|
-
|
144
|
-
boolean_fields.include?(key)
|
145
|
-
end
|
146
|
-
|
147
|
-
def integer_for(value)
|
148
|
-
case value
|
149
|
-
when Integer
|
150
|
-
value
|
151
|
-
when TrueClass
|
152
|
-
1
|
153
|
-
when FalseClass
|
154
|
-
0
|
155
|
-
when NilClass
|
156
|
-
nil
|
157
|
-
end
|
158
|
-
end
|
174
|
+
include BooleanHelpers
|
159
175
|
|
160
176
|
# @private
|
161
177
|
def base_params
|
@@ -199,5 +215,10 @@ module Staccato
|
|
199
215
|
}.compact
|
200
216
|
]
|
201
217
|
end
|
218
|
+
|
219
|
+
# @private
|
220
|
+
def measurement_params
|
221
|
+
measurements.dup.map!(&:params).inject({}, &:merge!)
|
222
|
+
end
|
202
223
|
end
|
203
224
|
end
|