ahoy_matey 0.3.2 → 1.0.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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +324 -189
- data/ahoy_matey.gemspec +1 -0
- data/app/controllers/ahoy/base_controller.rb +5 -0
- data/app/controllers/ahoy/events_controller.rb +9 -7
- data/app/controllers/ahoy/visits_controller.rb +2 -1
- data/lib/ahoy.rb +37 -28
- data/lib/ahoy/controller.rb +12 -20
- data/lib/ahoy/deckhands/location_deckhand.rb +39 -0
- data/lib/ahoy/deckhands/request_deckhand.rb +41 -0
- data/lib/ahoy/deckhands/technology_deckhand.rb +49 -0
- data/lib/ahoy/deckhands/traffic_source_deckhand.rb +24 -0
- data/lib/ahoy/deckhands/utm_parameter_deckhand.rb +24 -0
- data/lib/ahoy/engine.rb +3 -1
- data/lib/ahoy/model.rb +19 -82
- data/lib/ahoy/stores/active_record_store.rb +63 -0
- data/lib/ahoy/stores/active_record_token_store.rb +113 -0
- data/lib/ahoy/stores/base_store.rb +65 -0
- data/lib/ahoy/stores/log_store.rb +47 -0
- data/lib/ahoy/stores/mongoid_store.rb +59 -0
- data/lib/ahoy/tracker.rb +108 -67
- data/lib/ahoy/version.rb +1 -1
- data/lib/ahoy/visit_properties.rb +58 -0
- data/lib/ahoy/warden.rb +1 -10
- data/lib/generators/ahoy/stores/active_record_events_generator.rb +44 -0
- data/lib/generators/ahoy/stores/active_record_generator.rb +17 -0
- data/lib/generators/ahoy/{events/active_record_generator.rb → stores/active_record_visits_generator.rb} +14 -6
- data/lib/generators/ahoy/stores/custom_generator.rb +16 -0
- data/lib/generators/ahoy/stores/log_generator.rb +16 -0
- data/lib/generators/ahoy/stores/mongoid_events_generator.rb +20 -0
- data/lib/generators/ahoy/stores/mongoid_generator.rb +16 -0
- data/lib/generators/ahoy/stores/mongoid_visits_generator.rb +20 -0
- data/{app/models/ahoy/event.rb → lib/generators/ahoy/stores/templates/active_record_event_model.rb} +2 -2
- data/lib/generators/ahoy/stores/templates/active_record_events_migration.rb +20 -0
- data/lib/generators/ahoy/stores/templates/active_record_initializer.rb +3 -0
- data/lib/generators/ahoy/stores/templates/active_record_visit_model.rb +4 -0
- data/lib/generators/ahoy/{templates/install.rb → stores/templates/active_record_visits_migration.rb} +6 -8
- data/lib/generators/ahoy/stores/templates/custom_initializer.rb +12 -0
- data/lib/generators/ahoy/stores/templates/log_initializer.rb +3 -0
- data/lib/generators/ahoy/stores/templates/mongoid_event_model.rb +12 -0
- data/lib/generators/ahoy/stores/templates/mongoid_initializer.rb +3 -0
- data/lib/generators/ahoy/stores/templates/mongoid_visit_model.rb +41 -0
- data/vendor/assets/javascripts/ahoy.js +19 -8
- metadata +45 -8
- data/lib/generators/ahoy/events/templates/create_events.rb +0 -20
- data/lib/generators/ahoy/events/templates/initializer.rb +0 -1
- data/lib/generators/ahoy/install_generator.rb +0 -38
data/lib/ahoy/version.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
module Ahoy
|
2
|
+
class VisitProperties
|
3
|
+
|
4
|
+
REQUEST_KEYS = [:ip, :user_agent, :referrer, :landing_page, :platform, :app_version, :os_version]
|
5
|
+
TRAFFIC_SOURCE_KEYS = [:referring_domain, :search_keyword]
|
6
|
+
UTM_PARAMETER_KEYS = [:utm_source, :utm_medium, :utm_term, :utm_content, :utm_campaign]
|
7
|
+
TECHNOLOGY_KEYS = [:browser, :os, :device_type]
|
8
|
+
LOCATION_KEYS = [:country, :region, :city]
|
9
|
+
|
10
|
+
KEYS = REQUEST_KEYS + TRAFFIC_SOURCE_KEYS + UTM_PARAMETER_KEYS + TECHNOLOGY_KEYS + LOCATION_KEYS
|
11
|
+
|
12
|
+
delegate *REQUEST_KEYS, to: :request_deckhand
|
13
|
+
delegate *TRAFFIC_SOURCE_KEYS, to: :traffic_source_deckhand
|
14
|
+
delegate *(UTM_PARAMETER_KEYS + [:landing_params]), to: :utm_parameter_deckhand
|
15
|
+
delegate *TECHNOLOGY_KEYS, to: :technology_deckhand
|
16
|
+
delegate *LOCATION_KEYS, to: :location_deckhand
|
17
|
+
|
18
|
+
def initialize(request, options = {})
|
19
|
+
@request = request
|
20
|
+
@options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](key)
|
24
|
+
send(key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def keys
|
28
|
+
KEYS
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_hash
|
32
|
+
keys.inject({}){|memo, key| memo[key] = send(key); memo }
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def request_deckhand
|
38
|
+
@request_deckhand ||= Deckhands::RequestDeckhand.new(@request, @options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def traffic_source_deckhand
|
42
|
+
@traffic_source_deckhand ||= Deckhands::TrafficSourceDeckhand.new(request_deckhand.referrer)
|
43
|
+
end
|
44
|
+
|
45
|
+
def utm_parameter_deckhand
|
46
|
+
@utm_parameter_deckhand ||= Deckhands::UtmParameterDeckhand.new(request_deckhand.landing_page)
|
47
|
+
end
|
48
|
+
|
49
|
+
def technology_deckhand
|
50
|
+
@technology_deckhand ||= Deckhands::TechnologyDeckhand.new(request_deckhand.user_agent)
|
51
|
+
end
|
52
|
+
|
53
|
+
def location_deckhand
|
54
|
+
@location_deckhand ||= Deckhands::LocationDeckhand.new(request_deckhand.ip)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/lib/ahoy/warden.rb
CHANGED
@@ -1,14 +1,5 @@
|
|
1
1
|
Warden::Manager.after_set_user except: :fetch do |user, auth, opts|
|
2
2
|
request = ActionDispatch::Request.new(auth.env)
|
3
|
-
visit_token = request.cookies["ahoy_visit"] || request.headers["Ahoy-Visit"]
|
4
|
-
visit = nil
|
5
|
-
if visit_token
|
6
|
-
visit = Ahoy.visit_model.where(visit_token: visit_token).first
|
7
|
-
if visit and !visit.user
|
8
|
-
visit.user = user
|
9
|
-
visit.save!
|
10
|
-
end
|
11
|
-
end
|
12
3
|
ahoy = Ahoy::Tracker.new(request: request)
|
13
|
-
ahoy.
|
4
|
+
ahoy.authenticate(user)
|
14
5
|
end
|
@@ -0,0 +1,44 @@
|
|
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 Stores
|
9
|
+
module Generators
|
10
|
+
class ActiveRecordEventsGenerator < Rails::Generators::Base
|
11
|
+
include Rails::Generators::Migration
|
12
|
+
source_root File.expand_path("../templates", __FILE__)
|
13
|
+
|
14
|
+
class_option :database, type: :string, aliases: "-d"
|
15
|
+
|
16
|
+
# Implement the required interface for Rails::Generators::Migration.
|
17
|
+
def self.next_migration_number(dirname) #:nodoc:
|
18
|
+
next_migration_number = current_migration_number(dirname) + 1
|
19
|
+
if ::ActiveRecord::Base.timestamped_migrations
|
20
|
+
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
|
21
|
+
else
|
22
|
+
"%.3d" % next_migration_number
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def copy_migration
|
27
|
+
unless options["database"].in?([nil, "postgresql"])
|
28
|
+
raise Thor::Error, "Unknown database option"
|
29
|
+
end
|
30
|
+
migration_template "active_record_events_migration.rb", "db/migrate/create_ahoy_events.rb"
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_model
|
34
|
+
template "active_record_event_model.rb", "app/models/ahoy/event.rb"
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_initializer
|
38
|
+
template "active_record_initializer.rb", "config/initializers/ahoy.rb"
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Stores
|
5
|
+
module Generators
|
6
|
+
class ActiveRecordGenerator < Rails::Generators::Base
|
7
|
+
class_option :database, type: :string, aliases: "-d"
|
8
|
+
|
9
|
+
def boom
|
10
|
+
invoke "ahoy:stores:active_record_visits", nil, options
|
11
|
+
invoke "ahoy:stores:active_record_events", nil, options
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -5,17 +5,18 @@ require "active_record"
|
|
5
5
|
require "rails/generators/active_record"
|
6
6
|
|
7
7
|
module Ahoy
|
8
|
-
module
|
8
|
+
module Stores
|
9
9
|
module Generators
|
10
|
-
class
|
10
|
+
class ActiveRecordVisitsGenerator < Rails::Generators::Base
|
11
11
|
include Rails::Generators::Migration
|
12
|
-
|
13
12
|
source_root File.expand_path("../templates", __FILE__)
|
14
13
|
|
14
|
+
class_option :database, type: :string, aliases: "-d"
|
15
|
+
|
15
16
|
# Implement the required interface for Rails::Generators::Migration.
|
16
17
|
def self.next_migration_number(dirname) #:nodoc:
|
17
18
|
next_migration_number = current_migration_number(dirname) + 1
|
18
|
-
if ActiveRecord::Base.timestamped_migrations
|
19
|
+
if ::ActiveRecord::Base.timestamped_migrations
|
19
20
|
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
|
20
21
|
else
|
21
22
|
"%.3d" % next_migration_number
|
@@ -23,11 +24,18 @@ module Ahoy
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def copy_migration
|
26
|
-
|
27
|
+
unless options["database"].in?([nil, "postgresql"])
|
28
|
+
raise Thor::Error, "Unknown database option"
|
29
|
+
end
|
30
|
+
migration_template "active_record_visits_migration.rb", "db/migrate/create_visits.rb"
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_model
|
34
|
+
template "active_record_visit_model.rb", "app/models/visit.rb"
|
27
35
|
end
|
28
36
|
|
29
37
|
def create_initializer
|
30
|
-
template "
|
38
|
+
template "active_record_initializer.rb", "config/initializers/ahoy.rb"
|
31
39
|
end
|
32
40
|
|
33
41
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Stores
|
5
|
+
module Generators
|
6
|
+
class CustomGenerator < Rails::Generators::Base
|
7
|
+
source_root File.expand_path("../templates", __FILE__)
|
8
|
+
|
9
|
+
def create_initializer
|
10
|
+
template "custom_initializer.rb", "config/initializers/ahoy.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Stores
|
5
|
+
module Generators
|
6
|
+
class LogGenerator < Rails::Generators::Base
|
7
|
+
source_root File.expand_path("../templates", __FILE__)
|
8
|
+
|
9
|
+
def create_initializer
|
10
|
+
template "log_initializer.rb", "config/initializers/ahoy.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Stores
|
5
|
+
module Generators
|
6
|
+
class MongoidEventsGenerator < Rails::Generators::Base
|
7
|
+
source_root File.expand_path("../templates", __FILE__)
|
8
|
+
|
9
|
+
def generate_model
|
10
|
+
template "mongoid_event_model.rb", "app/models/ahoy/event.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_initializer
|
14
|
+
template "mongoid_initializer.rb", "config/initializers/ahoy.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Stores
|
5
|
+
module Generators
|
6
|
+
class MongoidGenerator < Rails::Generators::Base
|
7
|
+
|
8
|
+
def boom
|
9
|
+
invoke "ahoy:stores:mongoid_visits"
|
10
|
+
invoke "ahoy:stores:mongoid_events"
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Stores
|
5
|
+
module Generators
|
6
|
+
class MongoidVisitsGenerator < Rails::Generators::Base
|
7
|
+
source_root File.expand_path("../templates", __FILE__)
|
8
|
+
|
9
|
+
def generate_model
|
10
|
+
template "mongoid_visit_model.rb", "app/models/visit.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_initializer
|
14
|
+
template "mongoid_initializer.rb", "config/initializers/ahoy.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :ahoy_events, id: false do |t|
|
4
|
+
t.uuid :id, primary_key: true
|
5
|
+
t.uuid :visit_id
|
6
|
+
|
7
|
+
# user
|
8
|
+
t.integer :user_id
|
9
|
+
# add t.string :user_type if polymorphic
|
10
|
+
|
11
|
+
t.string :name
|
12
|
+
t.<% if options["database"] == "postgresql" %>json<% else %>text<% end %> :properties
|
13
|
+
t.timestamp :time
|
14
|
+
end
|
15
|
+
|
16
|
+
add_index :ahoy_events, [:visit_id]
|
17
|
+
add_index :ahoy_events, [:user_id]
|
18
|
+
add_index :ahoy_events, [:time]
|
19
|
+
end
|
20
|
+
end
|
data/lib/generators/ahoy/{templates/install.rb → stores/templates/active_record_visits_migration.rb}
RENAMED
@@ -1,9 +1,8 @@
|
|
1
1
|
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
2
|
def change
|
3
|
-
create_table :visits do |t|
|
4
|
-
|
5
|
-
t.
|
6
|
-
t.string :visitor_token
|
3
|
+
create_table :visits, id: false do |t|
|
4
|
+
t.uuid :id, primary_key: true
|
5
|
+
t.uuid :visitor_id
|
7
6
|
|
8
7
|
# the rest are recommended but optional
|
9
8
|
# simply remove the columns you don't want
|
@@ -16,7 +15,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration
|
|
16
15
|
|
17
16
|
# user
|
18
17
|
t.integer :user_id
|
19
|
-
t.string :user_type
|
18
|
+
# add t.string :user_type if polymorphic
|
20
19
|
|
21
20
|
# traffic source
|
22
21
|
t.string :referring_domain
|
@@ -44,10 +43,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration
|
|
44
43
|
# t.string :app_version
|
45
44
|
# t.string :os_version
|
46
45
|
|
47
|
-
t.timestamp :
|
46
|
+
t.timestamp :started_at
|
48
47
|
end
|
49
48
|
|
50
|
-
add_index :visits, [:
|
51
|
-
add_index :visits, [:user_id, :user_type]
|
49
|
+
add_index :visits, [:user_id]
|
52
50
|
end
|
53
51
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Visit
|
2
|
+
include Mongoid::Document
|
3
|
+
|
4
|
+
# associations
|
5
|
+
belongs_to :user
|
6
|
+
|
7
|
+
# required
|
8
|
+
field :visitor_id, type: BSON::Binary
|
9
|
+
|
10
|
+
# the rest are recommended but optional
|
11
|
+
# simply remove the columns you don't want
|
12
|
+
|
13
|
+
# standard
|
14
|
+
field :ip, type: String
|
15
|
+
field :user_agent, type: String
|
16
|
+
field :referrer, type: String
|
17
|
+
field :landing_page, type: String
|
18
|
+
|
19
|
+
# traffic source
|
20
|
+
field :referring_domain, type: String
|
21
|
+
field :search_keyword, type: String
|
22
|
+
|
23
|
+
# technology
|
24
|
+
field :browser, type: String
|
25
|
+
field :os, type: String
|
26
|
+
field :device_type, type: String
|
27
|
+
|
28
|
+
# location
|
29
|
+
field :country, type: String
|
30
|
+
field :region, type: String
|
31
|
+
field :city, type: String
|
32
|
+
|
33
|
+
# utm parameters
|
34
|
+
field :utm_source, type: String
|
35
|
+
field :utm_medium, type: String
|
36
|
+
field :utm_term, type: String
|
37
|
+
field :utm_content, type: String
|
38
|
+
field :utm_campaign, type: String
|
39
|
+
|
40
|
+
field :started_at, type: Time
|
41
|
+
end
|
@@ -13,7 +13,7 @@
|
|
13
13
|
|
14
14
|
var ahoy = window.ahoy || window.Ahoy || {};
|
15
15
|
var $ = window.jQuery || window.Zepto || window.$;
|
16
|
-
var visitId, visitorId;
|
16
|
+
var visitId, visitorId, track;
|
17
17
|
var visitTtl = 4 * 60; // 4 hours
|
18
18
|
var visitorTtl = 2 * 365 * 24 * 60; // 2 years
|
19
19
|
var isReady = false;
|
@@ -21,6 +21,8 @@
|
|
21
21
|
var canStringify = typeof(JSON) !== "undefined" && typeof(JSON.stringify) !== "undefined";
|
22
22
|
var eventQueue = [];
|
23
23
|
var page = ahoy.page || window.location.pathname;
|
24
|
+
var visitsUrl = ahoy.visitsUrl || "/ahoy/visits"
|
25
|
+
var eventsUrl = ahoy.eventsUrl || "/ahoy/events"
|
24
26
|
|
25
27
|
// cookies
|
26
28
|
|
@@ -102,7 +104,7 @@
|
|
102
104
|
if (canStringify) {
|
103
105
|
$.ajax({
|
104
106
|
type: "POST",
|
105
|
-
url:
|
107
|
+
url: eventsUrl,
|
106
108
|
data: JSON.stringify([event]),
|
107
109
|
contentType: "application/json; charset=utf-8",
|
108
110
|
dataType: "json",
|
@@ -136,14 +138,21 @@
|
|
136
138
|
|
137
139
|
visitId = getCookie("ahoy_visit");
|
138
140
|
visitorId = getCookie("ahoy_visitor");
|
141
|
+
track = getCookie("ahoy_track");
|
139
142
|
|
140
|
-
if (visitId && visitorId) {
|
143
|
+
if (visitId && visitorId && !track) {
|
141
144
|
// TODO keep visit alive?
|
142
145
|
log("Active visit");
|
143
146
|
setReady();
|
144
147
|
} else {
|
145
|
-
|
146
|
-
|
148
|
+
if (track) {
|
149
|
+
destroyCookie("ahoy_track");
|
150
|
+
}
|
151
|
+
|
152
|
+
if (!visitId) {
|
153
|
+
visitId = generateId();
|
154
|
+
setCookie("ahoy_visit", visitId, visitTtl);
|
155
|
+
}
|
147
156
|
|
148
157
|
// make sure cookies are enabled
|
149
158
|
if (getCookie("ahoy_visit")) {
|
@@ -170,24 +179,26 @@
|
|
170
179
|
|
171
180
|
log(data);
|
172
181
|
|
173
|
-
$.post(
|
182
|
+
$.post(visitsUrl, data, setReady, "json");
|
174
183
|
} else {
|
175
184
|
log("Cookies disabled");
|
176
185
|
setReady();
|
177
186
|
}
|
178
187
|
}
|
179
188
|
|
180
|
-
ahoy.getVisitToken = function () {
|
189
|
+
ahoy.getVisitId = ahoy.getVisitToken = function () {
|
181
190
|
return visitId;
|
182
191
|
};
|
183
192
|
|
184
|
-
ahoy.getVisitorToken = function () {
|
193
|
+
ahoy.getVisitorId = ahoy.getVisitorToken = function () {
|
185
194
|
return visitorId;
|
186
195
|
};
|
187
196
|
|
188
197
|
ahoy.reset = function () {
|
189
198
|
destroyCookie("ahoy_visit");
|
190
199
|
destroyCookie("ahoy_visitor");
|
200
|
+
destroyCookie("ahoy_events");
|
201
|
+
destroyCookie("ahoy_track");
|
191
202
|
return true;
|
192
203
|
};
|
193
204
|
|