staccato 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ coverage
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p448
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ script: bundle exec rake
data/CHANGELOG.md CHANGED
@@ -0,0 +1,20 @@
1
+ ## Staccato 0.0.2 ##
2
+
3
+ * adds timing tracking with block timing
4
+ * adds ecommerce tracking
5
+ * adds non-interactive tracking option
6
+ * automate building a random client id with SecureRandom.uuid
7
+ * adds no-op tracking for unconfigured and testing environments
8
+ * adds travis CI
9
+ * adds coverage metric
10
+ * adds code climate
11
+
12
+ *Tony Pitale*
13
+
14
+ ## Staccato 0.0.1 ##
15
+
16
+ * Initial implementation
17
+ * Track a pageview
18
+ * Track an event
19
+
20
+ *Tony Pitale*
data/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  Ruby Google Analytics Measurement
4
4
 
5
+ [![Build Status](https://travis-ci.org/tpitale/staccato.png?branch=master)](https://travis-ci.org/tpitale/staccato)
6
+ [![Code Climate](https://codeclimate.com/github/tpitale/staccato.png)](https://codeclimate.com/github/tpitale/staccato)
7
+
5
8
  ## Installation
6
9
 
7
10
  Add this line to your application's Gemfile:
@@ -16,13 +19,15 @@ Or install it yourself as:
16
19
 
17
20
  $ gem install staccato
18
21
 
19
- ## Usage
22
+ ## Usage ##
20
23
 
21
24
  tracker = Staccato.tracker('UA-XXXX-Y') # REQUIRED, your Google Analytics Tracking ID
22
25
 
23
26
  `#tracker` optionally takes a second param for the `client_id` value
24
27
  By default, the `client_id` is set to a random UUID with `SecureRandom.uuid`
25
28
 
29
+ ### Track some data ###
30
+
26
31
  # Track a Pageview (all values optional)
27
32
  tracker.pageview(path: '/page-path', hostname: 'mysite.com', title: 'A Page!')
28
33
 
@@ -35,6 +40,44 @@ By default, the `client_id` is set to a random UUID with `SecureRandom.uuid`
35
40
  # Track exceptions (all values optional)
36
41
  tracker.exception(description: 'RuntimeException', fatal: true)
37
42
 
43
+ # Track timing (all values optional, but should include time)
44
+ tracker.timing(category: 'runtime', variable: 'db', label: 'query', time: 50) # time in milliseconds
45
+
46
+ tracker.timing(category: 'runtime', variable: 'db', label: 'query') do
47
+ some_code_here
48
+ end
49
+
50
+ # Track transaction (transaction_id REQUIRED)
51
+ tracker.transaction({
52
+ transaction_id: 12345,
53
+ affiliation: 'clothing',
54
+ revenue: 17.98,
55
+ shipping: 2.00,
56
+ tax: 2.50,
57
+ currency: 'EUR'
58
+ })
59
+
60
+ # Track transaction item (matching transaction_id and item name REQUIRED)
61
+ tracker.transaction_item({
62
+ transaction_id: 12345,
63
+ name: 'Shirt',
64
+ price: 8.99,
65
+ quantity: 2,
66
+ code: 'afhcka1230',
67
+ variation: 'red',
68
+ currency: 'EUR'
69
+ })
70
+
71
+ ### "Global" Options ###
72
+
73
+ # Track a Non-Interactive Event
74
+ tracker.event(category: 'video', action: 'play', non_interactive: true)
75
+
76
+ Non-Interactive events are useful for tracking things like emails sent, or other
77
+ events that are not directly the result of a user's interaction.
78
+
79
+ The option `non_interactive` is accepted for all methods on `tracker`.
80
+
38
81
  ## Google Documentation
39
82
 
40
83
  https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide
data/lib/staccato.rb CHANGED
@@ -7,17 +7,29 @@ require "staccato/version"
7
7
 
8
8
  module Staccato
9
9
  def self.tracker(id, client_id = nil)
10
- Staccato::Tracker.new(id, client_id)
10
+ if id.nil?
11
+ Staccato::NoopTracker.new
12
+ else
13
+ Staccato::Tracker.new(id, client_id)
14
+ end
15
+ end
16
+
17
+ def self.build_client_id
18
+ SecureRandom.uuid
11
19
  end
12
20
 
13
21
  def self.tracking_uri
14
- URI('http://www.')
22
+ URI('http://www.google-analytics.com/collect')
15
23
  end
16
24
  end
17
25
 
26
+ require 'staccato/option_set'
18
27
  require 'staccato/hit'
19
28
  require 'staccato/pageview'
20
29
  require 'staccato/event'
21
30
  require 'staccato/social'
22
31
  require 'staccato/exception'
32
+ require 'staccato/timing'
33
+ require 'staccato/transaction'
34
+ require 'staccato/transaction_item'
23
35
  require 'staccato/tracker'
data/lib/staccato/hit.rb CHANGED
@@ -12,7 +12,7 @@ module Staccato
12
12
 
13
13
  def initialize(tracker, options = {})
14
14
  self.tracker = tracker
15
- self.options = OpenStruct.new(options)
15
+ self.options = OptionSet.new(options)
16
16
  end
17
17
 
18
18
  def fields
@@ -24,12 +24,17 @@ module Staccato
24
24
  'v' => 1,
25
25
  'tid' => tracker.id,
26
26
  'cid' => tracker.client_id,
27
+ 'ni' => non_interactive,
27
28
  't' => type.to_s
28
29
  }.merge(Hash[
29
30
  fields.map {|field,key| [key, options[field]]}
30
31
  ]).reject {|_,v| v.nil?}
31
32
  end
32
33
 
34
+ def non_interactive
35
+ 1 if options[:non_interactive] # defaults to nil
36
+ end
37
+
33
38
  def track!
34
39
  post(Staccato.tracking_uri, params)
35
40
  end
@@ -0,0 +1,9 @@
1
+ module Staccato
2
+ class OptionSet < OpenStruct
3
+ unless OpenStruct.instance_methods.include?(:[])
4
+ extend Forwardable
5
+
6
+ def_delegators :@table, :[], :[]=
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ module Staccato
2
+ class Timing
3
+ FIELDS = {
4
+ category: 'utc',
5
+ variable: 'utv',
6
+ label: 'utl',
7
+ time: 'utt'
8
+ }
9
+
10
+ include Hit
11
+
12
+ def type
13
+ :timing
14
+ end
15
+
16
+ def track!(&block)
17
+ if block_given?
18
+ start_at = Time.now
19
+ block.call
20
+ end_at = Time.now
21
+
22
+ self.options.time = (end_at - start_at).to_i*1000
23
+ end
24
+
25
+ super
26
+ end
27
+ end
28
+ end
@@ -10,7 +10,7 @@ module Staccato
10
10
  end
11
11
 
12
12
  def client_id
13
- @client_id ||= SecureRandom.uuid
13
+ @client_id ||= Staccato.build_client_id
14
14
  end
15
15
 
16
16
  def pageview(options = {})
@@ -28,5 +28,37 @@ module Staccato
28
28
  def exception(options = {})
29
29
  Staccato::Exception.new(self, options).track!
30
30
  end
31
+
32
+ def timing(options = {}, &block)
33
+ Staccato::Timing.new(self, options).track!(&block)
34
+ end
35
+
36
+ def transaction(options = {})
37
+ Staccato::Transaction.new(self, options).track!
38
+ end
39
+
40
+ def transaction_item(options = {})
41
+ Staccato::TransactionItem.new(self, options).track!
42
+ end
43
+ end
44
+
45
+ class NoopTracker
46
+ def initialize(*); end
47
+
48
+ def id
49
+ nil
50
+ end
51
+
52
+ def client_id
53
+ nil
54
+ end
55
+
56
+ def pageview(*); end
57
+ def event(*); end
58
+ def social(*); end
59
+ def exception(*); end
60
+ def timing(*)
61
+ yield if block_given?
62
+ end
31
63
  end
32
64
  end
@@ -0,0 +1,18 @@
1
+ module Staccato
2
+ class Transaction
3
+ FIELDS = {
4
+ transaction_id: 'ti',
5
+ affiliation: 'ta',
6
+ revenue: 'tr',
7
+ shipping: 'ts',
8
+ tax: 'tt',
9
+ currency: 'cu'
10
+ }
11
+
12
+ include Hit
13
+
14
+ def type
15
+ :transaction
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ module Staccato
2
+ class TransactionItem
3
+ FIELDS = {
4
+ transaction_id: 'ti',
5
+ name: 'in',
6
+ price: 'ip',
7
+ quantity: 'iq',
8
+ code: 'ic',
9
+ variation: 'iv',
10
+ currency: 'cu'
11
+ }
12
+
13
+ include Hit
14
+
15
+ def type
16
+ :item
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module Staccato
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe Staccato::NoopTracker do
4
+ let(:uri) {Staccato.tracking_uri}
5
+ let(:tracker) {Staccato.tracker(nil)}
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
+ describe "#pageview" do
14
+ before(:each) do
15
+ tracker.pageview(path: '/foobar', title: 'FooBar', hostname: 'mysite.com')
16
+ end
17
+
18
+ it 'does not track page path and page title' do
19
+ Net::HTTP.should have_received(:post_form).never
20
+ end
21
+ end
22
+
23
+ describe "#event" do
24
+ before(:each) do
25
+ tracker.event({
26
+ category: 'video',
27
+ action: 'play',
28
+ label: 'cars',
29
+ value: 1
30
+ })
31
+ end
32
+
33
+ it 'does not track event category, action, label, value' do
34
+ Net::HTTP.should have_received(:post_form).never
35
+ end
36
+ end
37
+
38
+ describe "#social" do
39
+ before(:each) do
40
+ tracker.social({
41
+ action: 'like',
42
+ network: 'facebook',
43
+ target: '/blog'
44
+ })
45
+ end
46
+
47
+ it 'does not track social action, network, target' do
48
+ Net::HTTP.should have_received(:post_form).never
49
+ end
50
+ end
51
+
52
+ describe "#exception" do
53
+ before(:each) do
54
+ tracker.exception({
55
+ description: 'RuntimeException',
56
+ fatal: true
57
+ })
58
+ end
59
+
60
+ it 'does not track exception description and fatality' do
61
+ Net::HTTP.should have_received(:post_form).never
62
+ end
63
+ end
64
+
65
+ describe "#timing" do
66
+ before(:each) do
67
+ tracker.timing({
68
+ category: 'view',
69
+ variable: 'runtime',
70
+ label: 'rails',
71
+ time: 1000
72
+ })
73
+ end
74
+
75
+ it 'does not track user timing category, variable, label, and time' do
76
+ Net::HTTP.should have_received(:post_form).never
77
+ end
78
+ end
79
+
80
+ describe "#timing with block" do
81
+ let(:codez) {stub(:test => true)}
82
+
83
+ before(:each) do
84
+ start_at = Time.now
85
+ end_at = start_at + 1 # 1 second
86
+
87
+ Time.stubs(:now).returns(start_at).returns(end_at)
88
+
89
+ tracker.timing({ category: 'view', variable: 'runtime', label: 'rails' }) do
90
+ codez.test
91
+ end
92
+ end
93
+
94
+ it 'does not track user timing category, variable, label, and time' do
95
+ Net::HTTP.should have_received(:post_form).never
96
+ end
97
+
98
+ it 'yields to the block' do
99
+ codez.should have_received(:test)
100
+ end
101
+ end
102
+ end
@@ -78,19 +78,141 @@ describe Staccato::Tracker do
78
78
  before(:each) do
79
79
  tracker.exception({
80
80
  description: 'RuntimeException',
81
- fatal: true
81
+ fatal: true,
82
+ non_interactive: true
82
83
  })
83
84
  end
84
85
 
85
- it 'tracks social action, network, target' do
86
+ it 'tracks exception description and fatality' do
86
87
  Net::HTTP.should have_received(:post_form).with(uri, {
87
88
  'v' => 1,
88
89
  'tid' => 'UA-XXXX-Y',
89
90
  'cid' => '555',
91
+ 'ni' => 1,
90
92
  't' => 'exception',
91
93
  'exd' => 'RuntimeException',
92
94
  'exf' => 1
93
95
  })
94
96
  end
95
97
  end
98
+
99
+ describe "#timing" do
100
+ before(:each) do
101
+ tracker.timing({
102
+ category: 'view',
103
+ variable: 'runtime',
104
+ label: 'rails',
105
+ time: 1000
106
+ })
107
+ end
108
+
109
+ it 'tracks user timing category, variable, label, and time' do
110
+ Net::HTTP.should have_received(:post_form).with(uri, {
111
+ 'v' => 1,
112
+ 'tid' => 'UA-XXXX-Y',
113
+ 'cid' => '555',
114
+ 't' => 'timing',
115
+ 'utc' => 'view',
116
+ 'utv' => 'runtime',
117
+ 'utl' => 'rails',
118
+ 'utt' => 1000 # value in milliseconds
119
+ })
120
+ end
121
+ end
122
+
123
+ describe "#timing with block" do
124
+ let(:codez) {stub(:test => true)}
125
+
126
+ before(:each) do
127
+ start_at = Time.now
128
+ end_at = start_at + 1 # 1 second
129
+
130
+ Time.stubs(:now).returns(start_at).returns(end_at)
131
+
132
+ tracker.timing({ category: 'view', variable: 'runtime', label: 'rails' }) do
133
+ codez.test
134
+ end
135
+ end
136
+
137
+ it 'tracks user timing category, variable, label, and time' do
138
+ Net::HTTP.should have_received(:post_form).with(uri, {
139
+ 'v' => 1,
140
+ 'tid' => 'UA-XXXX-Y',
141
+ 'cid' => '555',
142
+ 't' => 'timing',
143
+ 'utc' => 'view',
144
+ 'utv' => 'runtime',
145
+ 'utl' => 'rails',
146
+ 'utt' => 1000
147
+ })
148
+ end
149
+
150
+ it 'yields to the block' do
151
+ codez.should have_received(:test)
152
+ end
153
+ end
154
+
155
+ describe "Transactions" do
156
+ describe "#transaction" do
157
+ let(:transaction_id) {1293281}
158
+
159
+ before(:each) do
160
+ tracker.transaction({
161
+ transaction_id: transaction_id,
162
+ affiliation: 'western',
163
+ revenue: 5.99,
164
+ shipping: 12.00,
165
+ tax: 1.40,
166
+ currency: 'USD'
167
+ })
168
+ end
169
+
170
+ it 'tracks the transaction values' do
171
+ Net::HTTP.should have_received(:post_form).with(uri, {
172
+ 'v' => 1,
173
+ 'tid' => 'UA-XXXX-Y',
174
+ 'cid' => '555',
175
+ 't' => 'transaction',
176
+ 'ti' => transaction_id,
177
+ 'ta' => 'western',
178
+ 'tr' => 5.99,
179
+ 'ts' => 12.0,
180
+ 'tt' => 1.4,
181
+ 'cu' => 'USD'
182
+ })
183
+ end
184
+ end
185
+
186
+ describe "#transaction_item" do
187
+ let(:transaction_id) {1293281}
188
+
189
+ before(:each) do
190
+ tracker.transaction_item({
191
+ transaction_id: transaction_id,
192
+ name: 'Sofa',
193
+ price: 804.99,
194
+ quantity: 2,
195
+ code: 'afhcka1230',
196
+ variation: 'furniture',
197
+ currency: 'USD'
198
+ })
199
+ end
200
+
201
+ it 'tracks the item values' do
202
+ Net::HTTP.should have_received(:post_form).with(uri, {
203
+ 'v' => 1,
204
+ 'tid' => 'UA-XXXX-Y',
205
+ 'cid' => '555',
206
+ 't' => 'item',
207
+ 'ti' => transaction_id,
208
+ 'in' => 'Sofa',
209
+ 'ip' => 804.99,
210
+ 'iq' => 2,
211
+ 'ic' => 'afhcka1230',
212
+ 'iv' => 'furniture',
213
+ 'cu' => 'USD'
214
+ })
215
+ end
216
+ end
217
+ end
96
218
  end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,6 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
1
4
  require 'bundler/setup'
2
5
 
3
6
  require 'rspec'
data/staccato.gemspec CHANGED
@@ -23,6 +23,5 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "rspec"
24
24
  spec.add_development_dependency "mocha"
25
25
  spec.add_development_dependency "bourne"
26
-
27
- # spec.add_runtime_dependency "uuid"
26
+ spec.add_development_dependency "simplecov"
28
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: staccato
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-04 00:00:00.000000000 Z
12
+ date: 2014-01-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -91,6 +91,22 @@ dependencies:
91
91
  - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: simplecov
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
94
110
  description: Ruby Google Analytics Measurement
95
111
  email:
96
112
  - tpitale@gmail.com
@@ -100,6 +116,8 @@ extra_rdoc_files: []
100
116
  files:
101
117
  - .gitignore
102
118
  - .rspec
119
+ - .ruby-version
120
+ - .travis.yml
103
121
  - CHANGELOG.md
104
122
  - Gemfile
105
123
  - LICENSE.txt
@@ -109,10 +127,15 @@ files:
109
127
  - lib/staccato/event.rb
110
128
  - lib/staccato/exception.rb
111
129
  - lib/staccato/hit.rb
130
+ - lib/staccato/option_set.rb
112
131
  - lib/staccato/pageview.rb
113
132
  - lib/staccato/social.rb
133
+ - lib/staccato/timing.rb
114
134
  - lib/staccato/tracker.rb
135
+ - lib/staccato/transaction.rb
136
+ - lib/staccato/transaction_item.rb
115
137
  - lib/staccato/version.rb
138
+ - spec/integration/noop_tracker_spec.rb
116
139
  - spec/integration/tracker_spec.rb
117
140
  - spec/lib/stacatto_spec.rb
118
141
  - spec/lib/staccato/event_spec.rb
@@ -146,9 +169,11 @@ signing_key:
146
169
  specification_version: 3
147
170
  summary: Ruby Google Analytics Measurement
148
171
  test_files:
172
+ - spec/integration/noop_tracker_spec.rb
149
173
  - spec/integration/tracker_spec.rb
150
174
  - spec/lib/stacatto_spec.rb
151
175
  - spec/lib/staccato/event_spec.rb
152
176
  - spec/lib/staccato/pageview_spec.rb
153
177
  - spec/lib/staccato/tracker_spec.rb
154
178
  - spec/spec_helper.rb
179
+ has_rdoc: