staccato 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|