ahoy_matey 0.1.8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +151 -49
- data/app/controllers/ahoy/events_controller.rb +14 -0
- data/app/controllers/ahoy/visits_controller.rb +1 -1
- data/app/models/ahoy/event.rb +10 -0
- data/config/routes.rb +1 -0
- data/lib/ahoy/controller.rb +5 -0
- data/lib/ahoy/subscribers/active_record.rb +21 -0
- data/lib/ahoy/tracker.rb +30 -0
- data/lib/ahoy/version.rb +1 -1
- data/lib/ahoy_matey.rb +4 -0
- data/lib/generators/ahoy/events/active_record_generator.rb +36 -0
- data/lib/generators/ahoy/events/templates/create_events.rb +20 -0
- data/lib/generators/ahoy/events/templates/initializer.rb +1 -0
- data/vendor/assets/javascripts/ahoy.js +145 -22
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c1ddf7a214c83d60f35b1d7a39653ef65d5ab3ee
|
4
|
+
data.tar.gz: e3c9af288c825faffda2cb7bf97cd8aa248c41a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3c068287e17bc73b51ce57af242a91013720b64dceefb00330a85ac2c586ba9b11b3fdd6395b9d8adf3d952b9dee1a73557d16a6a8792a49fa62d9c1893ba3c
|
7
|
+
data.tar.gz: 137594a001bb62a37274b94fe7776074b0880e74ddfdd0d48cc305d67847d13ca056de3f6fe38161d180d13cf22f79d6d363888a949d2d3abbdea8eed056d7cd
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Ahoy
|
2
2
|
|
3
|
-
:fire: Simple, powerful
|
3
|
+
:fire: Simple, powerful analytics for Rails
|
4
4
|
|
5
5
|
Visits are stored in **your database** so you can easily combine them with other data.
|
6
6
|
|
@@ -11,16 +11,18 @@ You get:
|
|
11
11
|
- **technology** - browser, OS, and device type
|
12
12
|
- **utm parameters** - source, medium, term, content, campaign
|
13
13
|
|
14
|
-
|
14
|
+
Track events in:
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
- JavaScript
|
17
|
+
- Ruby
|
18
|
+
- Native apps
|
19
19
|
|
20
|
-
|
20
|
+
And store them wherever you’d like - your database, logs, external services, or all of them.
|
21
21
|
|
22
22
|
:postbox: To track emails, check out [Ahoy Email](https://github.com/ankane/ahoy_email).
|
23
23
|
|
24
|
+
No Ruby? Check out [Ahoy.js](https://github.com/ankane/ahoy.js).
|
25
|
+
|
24
26
|
## Installation
|
25
27
|
|
26
28
|
Add this line to your application’s Gemfile:
|
@@ -93,8 +95,6 @@ Order.joins(:visit).group("device_type").count
|
|
93
95
|
|
94
96
|
Ahoy automatically attaches the `current_user` to the `current_visit`.
|
95
97
|
|
96
|
-
If you define your own `current_user` method, be sure to add it to `ActionController::Base`, not `ApplicationController`.
|
97
|
-
|
98
98
|
With [Devise](https://github.com/plataformatec/devise), it will attach the user even if he / she signs in after the visit starts.
|
99
99
|
|
100
100
|
With other authentication frameworks, add this to the end of your sign in method:
|
@@ -135,19 +135,104 @@ or
|
|
135
135
|
http://datakick.org/?utm_medium=twitter&utm_campaign=social&utm_source=tweet123
|
136
136
|
```
|
137
137
|
|
138
|
-
###
|
138
|
+
### Native Apps
|
139
139
|
|
140
|
-
|
140
|
+
When a user launches the app, create a visit. Send a `POST` request to `/ahoy/visits` with:
|
141
141
|
|
142
|
-
|
142
|
+
- platform - `iOS`, `Android`, etc.
|
143
|
+
- app_version - `1.0.0`
|
144
|
+
- os_version - `7.0.6`
|
145
|
+
- visitor_token - if you have one
|
143
146
|
|
144
|
-
|
147
|
+
The endpoint will return a JSON response like:
|
148
|
+
|
149
|
+
```json
|
150
|
+
{
|
151
|
+
"visit_token": "8tx2ziymkwa1WlppnkqxyaBaRlXrEQ3K",
|
152
|
+
"visitor_token": "hYBIV0rBfrIUAiArWweiECt4N9pyiygN"
|
153
|
+
}
|
154
|
+
```
|
155
|
+
|
156
|
+
Send the visit token in the `Ahoy-Visit` header for all requests.
|
157
|
+
|
158
|
+
After 4 hours, create another visit and use the updated visit token.
|
159
|
+
|
160
|
+
## Events
|
161
|
+
|
162
|
+
Each event has a `name` and `properties`.
|
163
|
+
|
164
|
+
There are three ways to track events.
|
165
|
+
|
166
|
+
#### JavaScript
|
145
167
|
|
146
168
|
```javascript
|
147
|
-
|
169
|
+
ahoy.track("Viewed book", {title: "The World is Flat"});
|
170
|
+
```
|
171
|
+
|
172
|
+
or track all views and clicks with:
|
173
|
+
|
174
|
+
```javascript
|
175
|
+
ahoy.trackAll();
|
176
|
+
```
|
177
|
+
|
178
|
+
#### Ruby
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
ahoy.track "Viewed book", title: "Hot, Flat, and Crowded"
|
148
182
|
```
|
149
183
|
|
150
|
-
|
184
|
+
#### Native Apps
|
185
|
+
|
186
|
+
Send a `POST` request to `/ahoy/events` with:
|
187
|
+
|
188
|
+
- name
|
189
|
+
- properties
|
190
|
+
- user token (depends on your authentication framework)
|
191
|
+
- `Ahoy-Visit` header
|
192
|
+
|
193
|
+
Requests should have `Content-Type: application/json`.
|
194
|
+
|
195
|
+
### Storing Events
|
196
|
+
|
197
|
+
You choose how to store events.
|
198
|
+
|
199
|
+
#### ActiveRecord
|
200
|
+
|
201
|
+
Create an `Ahoy::Event` model to store events.
|
202
|
+
|
203
|
+
```sh
|
204
|
+
rails generate ahoy:events:active_record
|
205
|
+
rake db:migrate
|
206
|
+
```
|
207
|
+
|
208
|
+
#### Custom
|
209
|
+
|
210
|
+
Create your own subscribers in `config/initializers/ahoy.rb`.
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
class LogSubscriber
|
214
|
+
|
215
|
+
def track(name, properties, options = {})
|
216
|
+
data = {
|
217
|
+
name: name,
|
218
|
+
properties: properties,
|
219
|
+
time: options[:time].to_i,
|
220
|
+
visit_id: options[:visit].try(:id),
|
221
|
+
user_id: options[:user].try(:id),
|
222
|
+
ip: options[:controller].try(:request).try(:remote_ip)
|
223
|
+
}
|
224
|
+
Rails.logger.info data.to_json
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
# and add it
|
230
|
+
Ahoy.subscribers << LogSubscriber.new
|
231
|
+
```
|
232
|
+
|
233
|
+
Add as many subscribers as you’d like.
|
234
|
+
|
235
|
+
## Development
|
151
236
|
|
152
237
|
Ahoy is built with developers in mind. You can run the following code in your browser’s console.
|
153
238
|
|
@@ -169,27 +254,12 @@ Turn off logging
|
|
169
254
|
ahoy.debug(false);
|
170
255
|
```
|
171
256
|
|
172
|
-
###
|
173
|
-
|
174
|
-
When a user launches the app, create a visit. Send a `POST` request to `/ahoy/visits` with:
|
175
|
-
|
176
|
-
- platform - `iOS`, `Android`, etc.
|
177
|
-
- app_version - `1.0.0`
|
178
|
-
- os_version - `7.0.6`
|
179
|
-
- visitor_token - if you have one
|
180
|
-
|
181
|
-
The endpoint will return a JSON response like:
|
182
|
-
|
183
|
-
```json
|
184
|
-
{
|
185
|
-
"visit_token": "8tx2ziymkwa1WlppnkqxyaBaRlXrEQ3K",
|
186
|
-
"visitor_token": "hYBIV0rBfrIUAiArWweiECt4N9pyiygN"
|
187
|
-
}
|
188
|
-
```
|
189
|
-
|
190
|
-
Send the visit token in the `Ahoy-Visit` header for all requests.
|
257
|
+
### More
|
191
258
|
|
192
|
-
|
259
|
+
- Excludes bots
|
260
|
+
- Degrades gracefully when cookies are disabled
|
261
|
+
- Don’t need a field? Just remove it from the migration
|
262
|
+
- Visits are 4 hours by default
|
193
263
|
|
194
264
|
### Doorkeeper
|
195
265
|
|
@@ -207,24 +277,12 @@ class ApplicationController < ActionController::Base
|
|
207
277
|
end
|
208
278
|
```
|
209
279
|
|
210
|
-
### More
|
211
|
-
|
212
|
-
- Excludes bots
|
213
|
-
- Degrades gracefully when cookies are disabled
|
214
|
-
- Don’t need a field? Just remove it from the migration
|
215
|
-
- Visits are 4 hours by default
|
216
|
-
|
217
280
|
## Reference
|
218
281
|
|
219
|
-
|
220
|
-
|
221
|
-
```ruby
|
222
|
-
Ahoy.visit_model = UserVisit
|
282
|
+
To track visits across multiple subdomains, add this **before** the javascript files.
|
223
283
|
|
224
|
-
|
225
|
-
|
226
|
-
Ahoy.visit_model = UserVisit
|
227
|
-
end
|
284
|
+
```javascript
|
285
|
+
var ahoy = {"domain": "yourdomain.com"};
|
228
286
|
```
|
229
287
|
|
230
288
|
Change the platform on the web
|
@@ -266,6 +324,49 @@ Customize visitable
|
|
266
324
|
visitable :sign_up_visit, class_name: "Visit"
|
267
325
|
```
|
268
326
|
|
327
|
+
Track view
|
328
|
+
|
329
|
+
```javascript
|
330
|
+
ahoy.trackView();
|
331
|
+
```
|
332
|
+
|
333
|
+
Track clicks
|
334
|
+
|
335
|
+
```javascript
|
336
|
+
ahoy.trackClicks();
|
337
|
+
```
|
338
|
+
|
339
|
+
Track all Rails actions
|
340
|
+
|
341
|
+
```ruby
|
342
|
+
class ApplicationController < ActionController::Base
|
343
|
+
after_filter :track_action
|
344
|
+
|
345
|
+
protected
|
346
|
+
|
347
|
+
def track_action
|
348
|
+
ahoy.track "Hit action", request.filtered_parameters
|
349
|
+
end
|
350
|
+
end
|
351
|
+
```
|
352
|
+
|
353
|
+
Use a different model for visits
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
Ahoy.visit_model = UserVisit
|
357
|
+
|
358
|
+
# fix for Rails reloader in development
|
359
|
+
ActionDispatch::Reloader.to_prepare do
|
360
|
+
Ahoy.visit_model = UserVisit
|
361
|
+
end
|
362
|
+
```
|
363
|
+
|
364
|
+
Use a different model for events
|
365
|
+
|
366
|
+
```ruby
|
367
|
+
Ahoy.subscribers << Ahoy::Subscribers::ActiveRecord.new(model: Event)
|
368
|
+
```
|
369
|
+
|
269
370
|
## Upgrading
|
270
371
|
|
271
372
|
In `0.1.6`, a big improvement was made to `browser` and `os`. Update existing visits with:
|
@@ -279,6 +380,7 @@ end
|
|
279
380
|
|
280
381
|
## TODO
|
281
382
|
|
383
|
+
- better readme
|
282
384
|
- simple dashboard
|
283
385
|
- turn off modules
|
284
386
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Ahoy
|
2
|
+
class EventsController < Ahoy::BaseController
|
3
|
+
|
4
|
+
def create
|
5
|
+
options = {}
|
6
|
+
if params[:time] and (time = Time.at(params[:time].to_f) rescue nil) and (1.minute.ago..Time.now).cover?(time)
|
7
|
+
options[:time] = time
|
8
|
+
end
|
9
|
+
ahoy.track params[:name], params[:properties], options
|
10
|
+
render json: {}
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
data/config/routes.rb
CHANGED
data/lib/ahoy/controller.rb
CHANGED
@@ -3,6 +3,7 @@ module Ahoy
|
|
3
3
|
|
4
4
|
def self.included(base)
|
5
5
|
base.helper_method :current_visit
|
6
|
+
base.helper_method :ahoy
|
6
7
|
base.before_filter do
|
7
8
|
RequestStore.store[:ahoy_controller] ||= self
|
8
9
|
end
|
@@ -15,5 +16,9 @@ module Ahoy
|
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
19
|
+
def ahoy
|
20
|
+
@ahoy ||= Ahoy::Tracker.new(controller: self)
|
21
|
+
end
|
22
|
+
|
18
23
|
end
|
19
24
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Ahoy
|
2
|
+
module Subscribers
|
3
|
+
class ActiveRecord
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@model = options[:model] || Ahoy::Event
|
7
|
+
end
|
8
|
+
|
9
|
+
def track(name, properties, options = {})
|
10
|
+
@model.create! do |e|
|
11
|
+
e.visit = options[:visit]
|
12
|
+
e.user = options[:user]
|
13
|
+
e.name = name
|
14
|
+
e.properties = properties
|
15
|
+
e.time = options[:time]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/ahoy/tracker.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Ahoy
|
2
|
+
class Tracker
|
3
|
+
|
4
|
+
def initialize(options = {})
|
5
|
+
@controller = options[:controller]
|
6
|
+
end
|
7
|
+
|
8
|
+
def track(name, properties, options = {})
|
9
|
+
# publish to each subscriber
|
10
|
+
if @controller
|
11
|
+
options[:controller] ||= @controller
|
12
|
+
options[:user] ||= Ahoy.fetch_user(@controller)
|
13
|
+
if @controller.respond_to?(:current_visit)
|
14
|
+
options[:visit] ||= @controller.current_visit
|
15
|
+
end
|
16
|
+
end
|
17
|
+
options[:time] ||= Time.zone.now
|
18
|
+
|
19
|
+
subscribers = Ahoy.subscribers
|
20
|
+
if subscribers.any?
|
21
|
+
subscribers.each do |subscriber|
|
22
|
+
subscriber.track(name, properties, options)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
$stderr.puts "No subscribers"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/lib/ahoy/version.rb
CHANGED
data/lib/ahoy_matey.rb
CHANGED
@@ -5,8 +5,10 @@ require "referer-parser"
|
|
5
5
|
require "user_agent_parser"
|
6
6
|
require "request_store"
|
7
7
|
require "ahoy/version"
|
8
|
+
require "ahoy/tracker"
|
8
9
|
require "ahoy/controller"
|
9
10
|
require "ahoy/model"
|
11
|
+
require "ahoy/subscribers/active_record"
|
10
12
|
require "ahoy/engine"
|
11
13
|
|
12
14
|
module Ahoy
|
@@ -43,6 +45,8 @@ module Ahoy
|
|
43
45
|
(controller.respond_to?(:current_user) && controller.current_user) || (controller.respond_to?(:current_resource_owner, true) && controller.send(:current_resource_owner)) || nil
|
44
46
|
end
|
45
47
|
|
48
|
+
mattr_accessor :subscribers
|
49
|
+
self.subscribers = []
|
46
50
|
end
|
47
51
|
|
48
52
|
ActionController::Base.send :include, Ahoy::Controller
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# taken from https://github.com/collectiveidea/audited/blob/master/lib/generators/audited/install_generator.rb
|
2
|
+
require "rails/generators"
|
3
|
+
require "rails/generators/migration"
|
4
|
+
require "active_record"
|
5
|
+
require "rails/generators/active_record"
|
6
|
+
|
7
|
+
module Ahoy
|
8
|
+
module Events
|
9
|
+
module Generators
|
10
|
+
class ActiveRecordGenerator < Rails::Generators::Base
|
11
|
+
include Rails::Generators::Migration
|
12
|
+
|
13
|
+
source_root File.expand_path("../templates", __FILE__)
|
14
|
+
|
15
|
+
# Implement the required interface for Rails::Generators::Migration.
|
16
|
+
def self.next_migration_number(dirname) #:nodoc:
|
17
|
+
next_migration_number = current_migration_number(dirname) + 1
|
18
|
+
if ActiveRecord::Base.timestamped_migrations
|
19
|
+
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
|
20
|
+
else
|
21
|
+
"%.3d" % next_migration_number
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def copy_migration
|
26
|
+
migration_template "create_events.rb", "db/migrate/create_ahoy_events.rb"
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_initializer
|
30
|
+
template "initializer.rb", "config/initializers/ahoy.rb"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :ahoy_events do |t|
|
4
|
+
# visit
|
5
|
+
t.references :visit
|
6
|
+
|
7
|
+
# user
|
8
|
+
t.integer :user_id
|
9
|
+
t.string :user_type
|
10
|
+
|
11
|
+
t.string :name
|
12
|
+
t.text :properties
|
13
|
+
t.timestamp :time
|
14
|
+
end
|
15
|
+
|
16
|
+
add_index :ahoy_events, [:visit_id]
|
17
|
+
add_index :ahoy_events, [:user_id, :user_type]
|
18
|
+
add_index :ahoy_events, [:time]
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Ahoy.subscribers << Ahoy::Subscribers::ActiveRecord.new
|
@@ -10,6 +10,8 @@
|
|
10
10
|
var visitorTtl = 2 * 365 * 24 * 60; // 2 years
|
11
11
|
var isReady = false;
|
12
12
|
var queue = [];
|
13
|
+
var canStringify = typeof(JSON) !== "undefined" && typeof(JSON.stringify) !== "undefined";
|
14
|
+
var eventQueue = [];
|
13
15
|
|
14
16
|
// cookies
|
15
17
|
|
@@ -62,25 +64,98 @@
|
|
62
64
|
isReady = true;
|
63
65
|
}
|
64
66
|
|
67
|
+
function ready(callback) {
|
68
|
+
if (isReady) {
|
69
|
+
callback();
|
70
|
+
} else {
|
71
|
+
queue.push(callback);
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
// https://github.com/klughammer/node-randomstring
|
76
|
+
function generateId() {
|
77
|
+
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz';
|
78
|
+
var length = 32;
|
79
|
+
var string = '';
|
80
|
+
|
81
|
+
for (var i = 0; i < length; i++) {
|
82
|
+
var randomNumber = Math.floor(Math.random() * chars.length);
|
83
|
+
string += chars.substring(randomNumber, randomNumber + 1);
|
84
|
+
}
|
85
|
+
|
86
|
+
return string;
|
87
|
+
}
|
88
|
+
|
89
|
+
function saveEventQueue() {
|
90
|
+
// TODO add stringify method for IE 7 and under
|
91
|
+
if (canStringify) {
|
92
|
+
setCookie("ahoy_events", JSON.stringify(eventQueue), 1);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
function trackEvent(event) {
|
97
|
+
ready( function () {
|
98
|
+
// ensure JSON is defined
|
99
|
+
if (canStringify) {
|
100
|
+
$.ajax({
|
101
|
+
type: "POST",
|
102
|
+
url: "/ahoy/events",
|
103
|
+
data: JSON.stringify(event),
|
104
|
+
contentType: "application/json; charset=utf-8",
|
105
|
+
dataType: "json",
|
106
|
+
success: function() {
|
107
|
+
// remove from queue
|
108
|
+
for (var i = 0; i < eventQueue.length; i++) {
|
109
|
+
if (eventQueue[i].id == event.id) {
|
110
|
+
eventQueue.splice(i, 1);
|
111
|
+
break;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
saveEventQueue();
|
115
|
+
}
|
116
|
+
});
|
117
|
+
}
|
118
|
+
});
|
119
|
+
}
|
120
|
+
|
121
|
+
function eventProperties(e) {
|
122
|
+
var $target = $(e.currentTarget);
|
123
|
+
return {
|
124
|
+
tag: $target.get(0).tagName.toLowerCase(),
|
125
|
+
id: $target.attr("id"),
|
126
|
+
class: $target.attr("class")
|
127
|
+
};
|
128
|
+
}
|
129
|
+
|
65
130
|
// main
|
66
131
|
|
67
132
|
visitToken = getCookie("ahoy_visit");
|
68
133
|
visitorToken = getCookie("ahoy_visitor");
|
69
134
|
|
70
|
-
if (visitToken && visitorToken
|
135
|
+
if (visitToken && visitorToken) {
|
71
136
|
// TODO keep visit alive?
|
72
137
|
log("Active visit");
|
73
138
|
setReady();
|
74
139
|
} else {
|
75
|
-
|
140
|
+
visitToken = generateId();
|
141
|
+
setCookie("ahoy_visit", visitToken, visitTtl);
|
76
142
|
|
77
143
|
// make sure cookies are enabled
|
78
144
|
if (getCookie("ahoy_visit")) {
|
79
145
|
log("Visit started");
|
80
146
|
|
147
|
+
if (!visitorToken) {
|
148
|
+
visitorToken = generateId();
|
149
|
+
setCookie("ahoy_visitor", visitorToken, visitorTtl);
|
150
|
+
}
|
151
|
+
|
81
152
|
var data = {
|
153
|
+
visit_token: visitToken,
|
154
|
+
visitor_token: visitorToken,
|
82
155
|
platform: ahoy.platform || "Web",
|
83
|
-
landing_page: window.location.href
|
156
|
+
landing_page: window.location.href,
|
157
|
+
screen_width: window.screen.width,
|
158
|
+
screen_height: window.screen.height
|
84
159
|
};
|
85
160
|
|
86
161
|
// referrer
|
@@ -88,17 +163,9 @@
|
|
88
163
|
data.referrer = document.referrer;
|
89
164
|
}
|
90
165
|
|
91
|
-
if (visitorToken) {
|
92
|
-
data.visitor_token = visitorToken;
|
93
|
-
}
|
94
|
-
|
95
166
|
log(data);
|
96
167
|
|
97
|
-
$.post("/ahoy/visits", data,
|
98
|
-
setCookie("ahoy_visit", response.visit_token, visitTtl);
|
99
|
-
setCookie("ahoy_visitor", response.visitor_token, visitorTtl);
|
100
|
-
setReady();
|
101
|
-
}, "json");
|
168
|
+
$.post("/ahoy/visits", data, setReady, "json");
|
102
169
|
} else {
|
103
170
|
log("Cookies disabled");
|
104
171
|
setReady();
|
@@ -120,18 +187,74 @@
|
|
120
187
|
return true;
|
121
188
|
};
|
122
189
|
|
123
|
-
ahoy.
|
124
|
-
|
125
|
-
|
126
|
-
|
190
|
+
ahoy.track = function (name, properties) {
|
191
|
+
// generate unique id
|
192
|
+
var event = {
|
193
|
+
id: generateId(),
|
194
|
+
name: name,
|
195
|
+
properties: properties,
|
196
|
+
time: (new Date()).getTime() / 1000.0
|
197
|
+
};
|
198
|
+
log(event);
|
127
199
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
200
|
+
eventQueue.push(event);
|
201
|
+
saveEventQueue();
|
202
|
+
|
203
|
+
// wait in case navigating to reduce duplicate events
|
204
|
+
setTimeout( function () {
|
205
|
+
trackEvent(event);
|
206
|
+
}, 1000);
|
207
|
+
};
|
208
|
+
|
209
|
+
ahoy.trackView = function () {
|
210
|
+
var properties = {
|
211
|
+
url: window.location.href,
|
212
|
+
title: document.title
|
213
|
+
};
|
214
|
+
ahoy.track("$view", properties);
|
215
|
+
};
|
216
|
+
|
217
|
+
ahoy.trackClicks = function () {
|
218
|
+
$(document).on("click", "a, button, input[type=submit]", function (e) {
|
219
|
+
var $target = $(e.currentTarget);
|
220
|
+
var properties = eventProperties(e);
|
221
|
+
properties.text = properties.tag == "input" ? $target.val() : $.trim($target.text());
|
222
|
+
properties.href = $target.attr("href");
|
223
|
+
ahoy.track("$click", properties);
|
224
|
+
});
|
225
|
+
};
|
226
|
+
|
227
|
+
ahoy.trackSubmits = function () {
|
228
|
+
$(document).on("submit", "form", function (e) {
|
229
|
+
var properties = eventProperties(e);
|
230
|
+
ahoy.track("$submit", properties);
|
231
|
+
});
|
134
232
|
};
|
135
233
|
|
234
|
+
ahoy.trackChanges = function () {
|
235
|
+
$(document).on("change", "input, textarea, select", function (e) {
|
236
|
+
var properties = eventProperties(e);
|
237
|
+
ahoy.track("$change", properties);
|
238
|
+
});
|
239
|
+
};
|
240
|
+
|
241
|
+
ahoy.trackAll = function() {
|
242
|
+
ahoy.trackView();
|
243
|
+
ahoy.trackClicks();
|
244
|
+
ahoy.trackSubmits();
|
245
|
+
ahoy.trackChanges();
|
246
|
+
};
|
247
|
+
|
248
|
+
// push events from queue
|
249
|
+
try {
|
250
|
+
eventQueue = JSON.parse(getCookie("ahoy_events") || "[]");
|
251
|
+
} catch (e) {
|
252
|
+
// do nothing
|
253
|
+
}
|
254
|
+
|
255
|
+
for (var i = 0; i < eventQueue.length; i++) {
|
256
|
+
trackEvent(eventQueue[i]);
|
257
|
+
}
|
258
|
+
|
136
259
|
window.ahoy = ahoy;
|
137
260
|
}(window));
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ahoy_matey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -137,13 +137,20 @@ files:
|
|
137
137
|
- Rakefile
|
138
138
|
- ahoy_matey.gemspec
|
139
139
|
- app/controllers/ahoy/base_controller.rb
|
140
|
+
- app/controllers/ahoy/events_controller.rb
|
140
141
|
- app/controllers/ahoy/visits_controller.rb
|
142
|
+
- app/models/ahoy/event.rb
|
141
143
|
- config/routes.rb
|
142
144
|
- lib/ahoy/controller.rb
|
143
145
|
- lib/ahoy/engine.rb
|
144
146
|
- lib/ahoy/model.rb
|
147
|
+
- lib/ahoy/subscribers/active_record.rb
|
148
|
+
- lib/ahoy/tracker.rb
|
145
149
|
- lib/ahoy/version.rb
|
146
150
|
- lib/ahoy_matey.rb
|
151
|
+
- lib/generators/ahoy/events/active_record_generator.rb
|
152
|
+
- lib/generators/ahoy/events/templates/create_events.rb
|
153
|
+
- lib/generators/ahoy/events/templates/initializer.rb
|
147
154
|
- lib/generators/ahoy/install_generator.rb
|
148
155
|
- lib/generators/ahoy/templates/install.rb
|
149
156
|
- vendor/assets/javascripts/ahoy.js
|