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
@@ -0,0 +1,44 @@
|
|
1
|
+
module Staccato
|
2
|
+
# Measurable adds field mapping and param collection
|
3
|
+
# for Measurement classes to be add to Hit
|
4
|
+
module Measurable
|
5
|
+
def self.included(model)
|
6
|
+
model.extend Forwardable
|
7
|
+
|
8
|
+
model.class_eval do
|
9
|
+
attr_accessor :options
|
10
|
+
|
11
|
+
def_delegators :@options, *model::FIELDS.keys
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param options [Hash] options for the measurement fields
|
16
|
+
def initialize(options = {})
|
17
|
+
self.options = OptionSet.new(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
# fields from options for this measurement
|
21
|
+
def fields
|
22
|
+
self.class::FIELDS
|
23
|
+
end
|
24
|
+
|
25
|
+
# measurement option prefix
|
26
|
+
# @return [String]
|
27
|
+
def prefix
|
28
|
+
''
|
29
|
+
end
|
30
|
+
|
31
|
+
# collects the parameters from options for this measurement
|
32
|
+
# @return [Hash]
|
33
|
+
def params
|
34
|
+
Hash[
|
35
|
+
fields.map { |field,key|
|
36
|
+
next if key.nil?
|
37
|
+
key = (prefix+key.to_s)
|
38
|
+
|
39
|
+
[key, options[field]] unless options[field].nil?
|
40
|
+
}.compact
|
41
|
+
]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'staccato/measurable'
|
2
|
+
require 'staccato/null_measurement'
|
3
|
+
|
4
|
+
require 'staccato/measurement/checkout'
|
5
|
+
require 'staccato/measurement/checkout_option'
|
6
|
+
require 'staccato/measurement/impression_list'
|
7
|
+
require 'staccato/measurement/product'
|
8
|
+
require 'staccato/measurement/product_impression'
|
9
|
+
require 'staccato/measurement/promotion'
|
10
|
+
require 'staccato/measurement/transaction'
|
11
|
+
|
12
|
+
module Staccato
|
13
|
+
# Classes for measurements to be add to Hits
|
14
|
+
module Measurement
|
15
|
+
# List of measurement classes by lookup key
|
16
|
+
TYPES = Hash[
|
17
|
+
[
|
18
|
+
Checkout,
|
19
|
+
CheckoutOption,
|
20
|
+
ImpressionList,
|
21
|
+
Product,
|
22
|
+
ProductImpression,
|
23
|
+
Promotion,
|
24
|
+
Transaction
|
25
|
+
].map { |k| [k.lookup_key, k] }
|
26
|
+
].freeze
|
27
|
+
|
28
|
+
# Lookup a measurement class by its key
|
29
|
+
# @param key [Symbol]
|
30
|
+
# @return [Class] measurement class or NullMeasurement
|
31
|
+
def lookup(key)
|
32
|
+
measurement_types[key] || NullMeasurement
|
33
|
+
end
|
34
|
+
module_function :lookup
|
35
|
+
|
36
|
+
# List of measurement classes by lookup key
|
37
|
+
# @return [Hash]
|
38
|
+
def measurement_types
|
39
|
+
TYPES
|
40
|
+
end
|
41
|
+
module_function :measurement_types
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Staccato
|
2
|
+
module Measurement
|
3
|
+
# Measurement class for checkout action steps
|
4
|
+
class Checkout
|
5
|
+
# lookup key for use in Hit#add_measurement
|
6
|
+
# @return [Symbol]
|
7
|
+
def self.lookup_key
|
8
|
+
:checkout
|
9
|
+
end
|
10
|
+
|
11
|
+
# Checkout measurement options fields
|
12
|
+
FIELDS = {
|
13
|
+
step: 'cos', # integer
|
14
|
+
step_options: 'col' # text
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
include Measurable
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Staccato
|
2
|
+
module Measurement
|
3
|
+
# Measurement class for checkout options
|
4
|
+
class CheckoutOption
|
5
|
+
# lookup key for use in Hit#add_measurement
|
6
|
+
# @return [Symbol]
|
7
|
+
def self.lookup_key
|
8
|
+
:checkout_option
|
9
|
+
end
|
10
|
+
|
11
|
+
# Checkout option measurement options fields
|
12
|
+
FIELDS = {
|
13
|
+
step: 'cos', # integer
|
14
|
+
step_options: 'col' # text
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
include Measurable
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Staccato
|
2
|
+
module Measurement
|
3
|
+
# Measurement class for impressions in a list for products
|
4
|
+
class ImpressionList
|
5
|
+
# lookup key for use in Hit#add_measurement
|
6
|
+
# @return [Symbol]
|
7
|
+
def self.lookup_key
|
8
|
+
:impression_list
|
9
|
+
end
|
10
|
+
|
11
|
+
# impression list prefix
|
12
|
+
# @return [String]
|
13
|
+
def prefix
|
14
|
+
'il'+index.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
# Impression list measurement options fields
|
18
|
+
FIELDS = {
|
19
|
+
index: nil,
|
20
|
+
name: 'nm'
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
include Measurable
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Staccato
|
2
|
+
module Measurement
|
3
|
+
# Measurement class for products in a transaction or other action
|
4
|
+
class Product
|
5
|
+
# lookup key for use in Hit#add_measurement
|
6
|
+
# @return [Symbol]
|
7
|
+
def self.lookup_key
|
8
|
+
:product
|
9
|
+
end
|
10
|
+
|
11
|
+
# product prefix
|
12
|
+
# @return [String]
|
13
|
+
def prefix
|
14
|
+
'pr'+index.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
# Product measurement options fields
|
18
|
+
FIELDS = {
|
19
|
+
index: nil,
|
20
|
+
id: 'id', # text
|
21
|
+
name: 'nm', # text
|
22
|
+
brand: 'br', # text
|
23
|
+
category: 'ca', # text
|
24
|
+
variant: 'va', # text
|
25
|
+
price: 'pr', # currency (looks like a double?)
|
26
|
+
quantity: 'qt', # integer
|
27
|
+
coupon_code: 'cc', # text
|
28
|
+
position: 'ps' # integer
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
include Measurable
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Staccato
|
2
|
+
module Measurement
|
3
|
+
# Measurement class for product impressions in a list
|
4
|
+
class ProductImpression
|
5
|
+
# lookup key for use in Hit#add_measurement
|
6
|
+
# @return [Symbol]
|
7
|
+
def self.lookup_key
|
8
|
+
:product_impression
|
9
|
+
end
|
10
|
+
|
11
|
+
# product impress prefix
|
12
|
+
# @return [String]
|
13
|
+
def prefix
|
14
|
+
'il' + list_index.to_s + 'pr' + index.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
# Product impression measurement options fields
|
18
|
+
FIELDS = {
|
19
|
+
index: nil,
|
20
|
+
list_index: nil,
|
21
|
+
id: 'id', # text
|
22
|
+
name: 'nm', # text
|
23
|
+
brand: 'br', # text
|
24
|
+
category: 'ca', # text
|
25
|
+
variant: 'va', # text
|
26
|
+
price: 'pr', # currency (looks like a double?)
|
27
|
+
position: 'ps' # integer
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
include Measurable
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Staccato
|
2
|
+
module Measurement
|
3
|
+
# Measurement class for promotion impressions/clicks
|
4
|
+
class Promotion
|
5
|
+
# lookup key for use in Hit#add_measurement
|
6
|
+
# @return [Symbol]
|
7
|
+
def self.lookup_key
|
8
|
+
:promotion
|
9
|
+
end
|
10
|
+
|
11
|
+
# promotion prefix
|
12
|
+
# @return [String]
|
13
|
+
def prefix
|
14
|
+
'promo'+index.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
# Promotion measurement options fields
|
18
|
+
FIELDS = {
|
19
|
+
index: nil,
|
20
|
+
id: 'id', # text
|
21
|
+
name: 'nm', # text
|
22
|
+
creative: 'cr', # text
|
23
|
+
position: 'ps' # text
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
include Measurable
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Staccato
|
2
|
+
module Measurement
|
3
|
+
# Measurement class for transaction (non-hit data)
|
4
|
+
class Transaction
|
5
|
+
# lookup key for use in Hit#add_measurement
|
6
|
+
# @return [Symbol]
|
7
|
+
def self.lookup_key
|
8
|
+
:transaction
|
9
|
+
end
|
10
|
+
|
11
|
+
# Transaction measurement options fields
|
12
|
+
FIELDS = {
|
13
|
+
transaction_id: 'ti',
|
14
|
+
affiliation: 'ta',
|
15
|
+
revenue: 'tr',
|
16
|
+
shipping: 'ts',
|
17
|
+
tax: 'tt',
|
18
|
+
currency: 'cu',
|
19
|
+
coupon_code: 'tcc'
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
include Measurable
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Measurement class for when we need a standin
|
2
|
+
class NullMeasurement
|
3
|
+
# Measurement fields for a noop
|
4
|
+
FIELDS = {}.freeze
|
5
|
+
|
6
|
+
# Initialize a noop measurement standin
|
7
|
+
def initialize(*); end
|
8
|
+
|
9
|
+
# Params for this noop measurement
|
10
|
+
# @return [Hash]
|
11
|
+
def params(*); {}; end
|
12
|
+
end
|
data/lib/staccato/tracker.rb
CHANGED
@@ -9,6 +9,7 @@ module Staccato
|
|
9
9
|
# sets up a new tracker
|
10
10
|
# @param id [String] the GA tracker id
|
11
11
|
# @param client_id [String, nil] unique value to track user sessions
|
12
|
+
# @param hit_defaults [Hash]
|
12
13
|
def initialize(id, client_id = nil, hit_defaults = {})
|
13
14
|
@id = id
|
14
15
|
@client_id = client_id
|
@@ -28,6 +29,17 @@ module Staccato
|
|
28
29
|
@client_id ||= Staccato.build_client_id
|
29
30
|
end
|
30
31
|
|
32
|
+
# Build a pageview
|
33
|
+
#
|
34
|
+
# @param options [Hash] options include:
|
35
|
+
# * path (optional) the path of the current page view
|
36
|
+
# * hostname (optional) the hostname of the current page view
|
37
|
+
# * title (optional) the page title
|
38
|
+
# @return [Pageview]
|
39
|
+
def build_pageview(options = {})
|
40
|
+
Staccato::Pageview.new(self, options)
|
41
|
+
end
|
42
|
+
|
31
43
|
# Track a pageview
|
32
44
|
#
|
33
45
|
# @param options [Hash] options include:
|
@@ -36,7 +48,19 @@ module Staccato
|
|
36
48
|
# * title (optional) the page title
|
37
49
|
# @return [<Net::HTTPOK] the GA `/collect` endpoint always returns a 200
|
38
50
|
def pageview(options = {})
|
39
|
-
|
51
|
+
build_pageview(options).track!
|
52
|
+
end
|
53
|
+
|
54
|
+
# Build an event
|
55
|
+
#
|
56
|
+
# @param options [Hash] options include:
|
57
|
+
# * category (optional)
|
58
|
+
# * action (optional)
|
59
|
+
# * label (optional)
|
60
|
+
# * value (optional)
|
61
|
+
# @return [Event]
|
62
|
+
def build_event(options = {})
|
63
|
+
Staccato::Event.new(self, options)
|
40
64
|
end
|
41
65
|
|
42
66
|
# Track an event
|
@@ -48,7 +72,7 @@ module Staccato
|
|
48
72
|
# * value (optional)
|
49
73
|
# @return [<Net::HTTPOK] the GA `/collect` endpoint always returns a 200
|
50
74
|
def event(options = {})
|
51
|
-
|
75
|
+
build_event(options).track!
|
52
76
|
end
|
53
77
|
|
54
78
|
# Track a social event such as a Facebook Like or Twitter Share
|
@@ -123,8 +147,10 @@ module Staccato
|
|
123
147
|
# Useful in testing
|
124
148
|
class NoopTracker
|
125
149
|
# (see Tracker#initialize)
|
126
|
-
def initialize(
|
150
|
+
def initialize(id = nil, client_id = nil, hit_defaults = {}); end
|
127
151
|
|
152
|
+
# hit defaults for our noop
|
153
|
+
# @return [Hash]
|
128
154
|
def hit_defaults
|
129
155
|
{}
|
130
156
|
end
|
@@ -139,26 +165,31 @@ module Staccato
|
|
139
165
|
nil
|
140
166
|
end
|
141
167
|
|
168
|
+
# (see Tracker#build_pageview)
|
169
|
+
def build_pageview(options = {}); end
|
142
170
|
# (see Tracker#pageview)
|
143
|
-
def pageview(
|
171
|
+
def pageview(options = {}); end
|
172
|
+
# (see Tracker#build_event)
|
173
|
+
def build_event(options = {}); end
|
144
174
|
# (see Tracker#event)
|
145
|
-
def event(
|
175
|
+
def event(options = {}); end
|
146
176
|
# (see Tracker#social)
|
147
|
-
def social(
|
177
|
+
def social(options = {}); end
|
148
178
|
# (see Tracker#exception)
|
149
|
-
def exception(
|
179
|
+
def exception(options = {}); end
|
150
180
|
# (see Tracker#timing)
|
151
|
-
def timing(
|
181
|
+
def timing(options = {}, &block)
|
152
182
|
yield if block_given?
|
153
183
|
end
|
154
184
|
# (see Tracker#transaction)
|
155
|
-
def transaction(
|
185
|
+
def transaction(options = {})
|
156
186
|
end
|
157
187
|
# (see Tracker#transaction_item)
|
158
|
-
def transaction_item(
|
188
|
+
def transaction_item(options = {})
|
159
189
|
end
|
160
190
|
|
161
|
-
|
191
|
+
# (see Tracker#track)
|
192
|
+
def track(params = {})
|
162
193
|
end
|
163
194
|
end
|
164
195
|
end
|
data/lib/staccato/version.rb
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Staccato::Measurement::CheckoutOption do
|
4
|
+
let(:uri) {Staccato.tracking_uri}
|
5
|
+
let(:tracker) {Staccato.tracker('UA-XXXX-Y')}
|
6
|
+
let(:response) {stub(:body => '', :status => 201)}
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
SecureRandom.stubs(:uuid).returns('555')
|
10
|
+
Net::HTTP.stubs(:post_form).returns(response)
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'a pageview with a transaction' do
|
14
|
+
let(:pageview) {
|
15
|
+
tracker.build_pageview({
|
16
|
+
path: '/checkout', hostname: 'mystore.com',
|
17
|
+
title: 'Complete Your Checkout', product_action: 'checkout_option'
|
18
|
+
})
|
19
|
+
}
|
20
|
+
|
21
|
+
let(:measurment_options) {{
|
22
|
+
step: 1,
|
23
|
+
step_options: 'Visa'
|
24
|
+
}}
|
25
|
+
|
26
|
+
before(:each) do
|
27
|
+
pageview.add_measurement(:checkout_option, measurment_options)
|
28
|
+
|
29
|
+
pageview.track!
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'tracks the measurment' do
|
33
|
+
Net::HTTP.should have_received(:post_form).with(uri, {
|
34
|
+
'v' => 1,
|
35
|
+
'tid' => 'UA-XXXX-Y',
|
36
|
+
'cid' => '555',
|
37
|
+
't' => 'pageview',
|
38
|
+
'dh' => 'mystore.com',
|
39
|
+
'dp' => '/checkout',
|
40
|
+
'dt' => 'Complete Your Checkout',
|
41
|
+
'pa' => 'checkout_option',
|
42
|
+
'cos' => 1,
|
43
|
+
'col' => 'Visa'
|
44
|
+
})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|