ahoy_matey 0.0.4 → 0.1.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/README.md +71 -13
- data/ahoy_matey.gemspec +1 -0
- data/app/controllers/ahoy/visits_controller.rb +1 -6
- data/lib/ahoy/controller.rb +4 -1
- data/lib/ahoy/engine.rb +5 -0
- data/lib/ahoy/model.rb +99 -0
- data/lib/ahoy/version.rb +1 -1
- data/lib/ahoy_matey.rb +15 -10
- data/lib/generators/ahoy/install_generator.rb +9 -0
- data/lib/generators/ahoy/templates/install.rb +3 -3
- data/vendor/assets/javascripts/ahoy.js +3 -1
- metadata +19 -4
- data/app/models/ahoy/visit.rb +0 -71
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 443dfa85abf63a4f515ea9c6865beb09f77404ab
|
4
|
+
data.tar.gz: 9099a3d7a51f6426f0ebae09fd3d9c831385a1e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0967ab9bfbb9fe5a22214d1eb74705edbeede65bdb82a07ecf2982e49a8ce8c74bd22ddfe2b302e16d63d602b1d5058c30322d1b5011879f3fc407dee9e1b37
|
7
|
+
data.tar.gz: b1c10a691345e7078f017eb009b64f17106cb1324bac5432bd85cc4986a18016028c4a917fe58d8e1e664ab958db43989b0097e36666343755c2001c65df199d
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
:fire: Simple, powerful visit tracking for Rails
|
4
4
|
|
5
|
-
|
5
|
+
You get:
|
6
6
|
|
7
7
|
- traffic source - referrer, referring domain, landing page, search keyword
|
8
8
|
- location - country, region, and city
|
@@ -39,25 +39,49 @@ Lastly, include the javascript file in `app/assets/javascripts/application.js` a
|
|
39
39
|
//= require ahoy
|
40
40
|
```
|
41
41
|
|
42
|
-
##
|
42
|
+
## How It Works
|
43
43
|
|
44
|
-
When
|
44
|
+
When someone visits your website, Ahoy creates a visit with lots of useful information.
|
45
45
|
|
46
46
|
Use the `current_visit` method to access it.
|
47
47
|
|
48
|
-
|
48
|
+
Explore your visits with queries like:
|
49
49
|
|
50
|
-
|
50
|
+
```ruby
|
51
|
+
Visit.group(:search_keyword).count
|
52
|
+
Visit.group(:country).count
|
53
|
+
Visit.group(:referring_domain).count
|
54
|
+
```
|
55
|
+
|
56
|
+
[Chartkick](http://chartkick.com/) and [Groupdate](https://github.com/ankane/groupdate) make it super easy to visualize the data.
|
57
|
+
|
58
|
+
```erb
|
59
|
+
<%= line_chart Visit.group_by_day(:created_at).count %>
|
60
|
+
```
|
61
|
+
|
62
|
+
## The Power
|
63
|
+
|
64
|
+
This information is great on its own, but super powerful when combined with other models.
|
65
|
+
|
66
|
+
Let’s associate orders with visits.
|
51
67
|
|
52
68
|
```ruby
|
53
69
|
class Order < ActiveRecord::Base
|
54
|
-
visitable
|
70
|
+
visitable
|
55
71
|
end
|
56
72
|
```
|
57
73
|
|
58
|
-
|
74
|
+
When a visitor places an order, the `visit_id` column will be automatically set. Magic!
|
59
75
|
|
60
|
-
|
76
|
+
To see where most of your orders are coming from, create an association:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class Order < ActiveRecord::Base
|
80
|
+
belongs_to :visit
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
And query away:
|
61
85
|
|
62
86
|
```ruby
|
63
87
|
Order.joins(:visit).group("referring_domain").count
|
@@ -65,18 +89,52 @@ Order.joins(:visit).group("device_type").count
|
|
65
89
|
Order.joins(:visit).group("city").count
|
66
90
|
```
|
67
91
|
|
92
|
+
## Users
|
93
|
+
|
94
|
+
Ahoy automatically attaches the `current_user` to the `current_visit`.
|
95
|
+
|
96
|
+
With [Devise](https://github.com/plataformatec/devise), it will attach the user even if he / she signs in after the visit starts.
|
97
|
+
|
98
|
+
To see the visits for a given user, create an association:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
class User < ActiveRecord::Base
|
102
|
+
has_many :visits
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
And use:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
user = User.first
|
110
|
+
user.visits
|
111
|
+
```
|
112
|
+
|
113
|
+
## Location
|
114
|
+
|
115
|
+
Ahoy uses [Geocoder](https://github.com/alexreisner/geocoder) for IP-based geocoding.
|
116
|
+
|
117
|
+
## UTM Parameters
|
118
|
+
|
119
|
+
Use UTM Parameters to track campaigns. [This is great for emails and social media](http://www.thunderseo.com/blog/utm-parameters/). Just add them to your links and Ahoy will pick them up.
|
120
|
+
|
121
|
+
```
|
122
|
+
http://datakick.org/?utm_medium=email&utm_campaign=newsletter&utm_source=newsletter-2014-03
|
123
|
+
```
|
124
|
+
|
125
|
+
or
|
126
|
+
|
127
|
+
```
|
128
|
+
http://datakick.org/?utm_medium=twitter&utm_campaign=social&utm_source=tweet123
|
129
|
+
```
|
130
|
+
|
68
131
|
## Features
|
69
132
|
|
70
133
|
- Excludes bots
|
71
134
|
- Degrades gracefully when cookies are disabled
|
72
|
-
- Gets campaign from utm_campaign parameter
|
73
135
|
|
74
136
|
## TODO
|
75
137
|
|
76
|
-
- better readme
|
77
|
-
- model integration
|
78
|
-
- update visit when user logs in
|
79
|
-
- set visit_id automatically on `visitable` models
|
80
138
|
- simple dashboard
|
81
139
|
- hook to store additional fields
|
82
140
|
- turn off modules
|
data/ahoy_matey.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_dependency "browser", ">= 0.4.0"
|
23
23
|
spec.add_dependency "geocoder"
|
24
24
|
spec.add_dependency "referer-parser"
|
25
|
+
spec.add_dependency "request_store"
|
25
26
|
|
26
27
|
spec.add_development_dependency "bundler", "~> 1.5"
|
27
28
|
spec.add_development_dependency "rake"
|
@@ -4,7 +4,7 @@ module Ahoy
|
|
4
4
|
|
5
5
|
def create
|
6
6
|
visit =
|
7
|
-
Ahoy
|
7
|
+
Ahoy.visit_model.new do |v|
|
8
8
|
v.visit_token = params[:visit_token]
|
9
9
|
v.visitor_token = params[:visitor_token]
|
10
10
|
v.ip = request.remote_ip
|
@@ -14,11 +14,6 @@ module Ahoy
|
|
14
14
|
v.user = current_user if respond_to?(:current_user)
|
15
15
|
end
|
16
16
|
|
17
|
-
visit.set_traffic_source
|
18
|
-
visit.set_technology
|
19
|
-
visit.set_location
|
20
|
-
visit.set_utm_parameters
|
21
|
-
|
22
17
|
visit.save!
|
23
18
|
render json: {id: visit.id}
|
24
19
|
end
|
data/lib/ahoy/controller.rb
CHANGED
@@ -3,13 +3,16 @@ module Ahoy
|
|
3
3
|
|
4
4
|
def self.included(base)
|
5
5
|
base.helper_method :current_visit
|
6
|
+
base.before_filter do
|
7
|
+
RequestStore.store[:ahoy_controller] ||= self
|
8
|
+
end
|
6
9
|
end
|
7
10
|
|
8
11
|
protected
|
9
12
|
|
10
13
|
def current_visit
|
11
14
|
if cookies[:ahoy_visit]
|
12
|
-
@current_visit ||= Ahoy
|
15
|
+
@current_visit ||= Ahoy.visit_model.where(visit_token: cookies[:ahoy_visit]).first
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
data/lib/ahoy/engine.rb
ADDED
data/lib/ahoy/model.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
module Ahoy
|
2
|
+
module Model
|
3
|
+
|
4
|
+
def ahoy_visit
|
5
|
+
class_eval do
|
6
|
+
|
7
|
+
belongs_to :user, polymorphic: true
|
8
|
+
|
9
|
+
before_create :set_traffic_source
|
10
|
+
before_create :set_utm_parameters
|
11
|
+
before_create :set_technology
|
12
|
+
before_create :set_location
|
13
|
+
|
14
|
+
def set_traffic_source
|
15
|
+
referring_uri = Addressable::URI.parse(referrer) rescue nil
|
16
|
+
self.referring_domain = referring_uri.try(:host)
|
17
|
+
search_keyword = RefererParser::Referer.new(referrer).search_term rescue nil
|
18
|
+
self.search_keyword = search_keyword.present? ? search_keyword : nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_utm_parameters
|
22
|
+
landing_uri = Addressable::URI.parse(landing_page) rescue nil
|
23
|
+
if landing_uri
|
24
|
+
query_values = landing_uri.query_values || {}
|
25
|
+
%w[utm_source utm_medium utm_term utm_content utm_campaign].each do |name|
|
26
|
+
self[name] = query_values[name]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_technology
|
32
|
+
browser = Browser.new(ua: user_agent)
|
33
|
+
|
34
|
+
self.browser = browser.name
|
35
|
+
|
36
|
+
# TODO add more
|
37
|
+
self.os =
|
38
|
+
if browser.android?
|
39
|
+
"Android"
|
40
|
+
elsif browser.ios?
|
41
|
+
"iOS"
|
42
|
+
elsif browser.windows_phone?
|
43
|
+
"Windows Phone"
|
44
|
+
elsif browser.blackberry?
|
45
|
+
"Blackberry"
|
46
|
+
elsif browser.chrome_os?
|
47
|
+
"Chrome OS"
|
48
|
+
elsif browser.mac?
|
49
|
+
"Mac"
|
50
|
+
elsif browser.windows?
|
51
|
+
"Windows"
|
52
|
+
elsif browser.linux?
|
53
|
+
"Linux"
|
54
|
+
end
|
55
|
+
|
56
|
+
self.device_type =
|
57
|
+
if browser.tv?
|
58
|
+
"TV"
|
59
|
+
elsif browser.console?
|
60
|
+
"Console"
|
61
|
+
elsif browser.tablet?
|
62
|
+
"Tablet"
|
63
|
+
elsif browser.mobile?
|
64
|
+
"Mobile"
|
65
|
+
else
|
66
|
+
"Desktop"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_location
|
71
|
+
location = Geocoder.search(ip).first rescue nil
|
72
|
+
if location
|
73
|
+
self.country = location.country.presence
|
74
|
+
self.region = location.state.presence
|
75
|
+
self.city = location.city.presence
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end # end class_eval
|
80
|
+
end
|
81
|
+
|
82
|
+
def visitable
|
83
|
+
class_eval do
|
84
|
+
belongs_to :visit
|
85
|
+
|
86
|
+
before_create :set_visit
|
87
|
+
|
88
|
+
def set_visit
|
89
|
+
if !self.class.column_names.include?("visit_id")
|
90
|
+
raise "Add a visit_id column to this table to use visitable"
|
91
|
+
else
|
92
|
+
self.visit ||= RequestStore.store[:ahoy_controller].try(:send, :current_visit)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
data/lib/ahoy/version.rb
CHANGED
data/lib/ahoy_matey.rb
CHANGED
@@ -1,28 +1,33 @@
|
|
1
|
-
require "ahoy/version"
|
2
|
-
require "ahoy/controller"
|
3
1
|
require "addressable/uri"
|
4
2
|
require "browser"
|
5
3
|
require "geocoder"
|
6
4
|
require "referer-parser"
|
5
|
+
require "request_store"
|
6
|
+
require "ahoy/version"
|
7
|
+
require "ahoy/controller"
|
8
|
+
require "ahoy/model"
|
9
|
+
require "ahoy/engine"
|
7
10
|
|
8
11
|
module Ahoy
|
9
|
-
|
10
|
-
|
12
|
+
|
13
|
+
def self.visit_model
|
14
|
+
::Visit
|
11
15
|
end
|
16
|
+
|
12
17
|
end
|
13
18
|
|
14
19
|
ActionController::Base.send :include, Ahoy::Controller
|
20
|
+
ActiveRecord::Base.send(:extend, Ahoy::Model) if defined?(ActiveRecord)
|
15
21
|
|
16
22
|
if defined?(Warden)
|
17
23
|
Warden::Manager.after_authentication do |user, auth, opts|
|
18
|
-
p user
|
19
|
-
p auth.env
|
20
|
-
p opts
|
21
24
|
request = Rack::Request.new(auth.env)
|
22
25
|
if request.cookies["ahoy_visit"]
|
23
|
-
visit = Ahoy
|
24
|
-
visit
|
25
|
-
|
26
|
+
visit = Ahoy.visit_model.where(visit_token: request.cookies["ahoy_visit"]).first
|
27
|
+
if visit
|
28
|
+
visit.user = user
|
29
|
+
visit.save!
|
30
|
+
end
|
26
31
|
end
|
27
32
|
end
|
28
33
|
end
|
@@ -24,6 +24,15 @@ module Ahoy
|
|
24
24
|
def copy_migration
|
25
25
|
migration_template "install.rb", "db/migrate/install_ahoy.rb"
|
26
26
|
end
|
27
|
+
|
28
|
+
def generate_model
|
29
|
+
invoke "active_record:model", ["Visit"], migration: false
|
30
|
+
end
|
31
|
+
|
32
|
+
def inject_ahoy_content
|
33
|
+
inject_into_class "app/models/visit.rb", "Visit", " ahoy_visit\n"
|
34
|
+
end
|
35
|
+
|
27
36
|
end
|
28
37
|
end
|
29
38
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
2
|
def change
|
3
|
-
create_table :
|
3
|
+
create_table :visits do |t|
|
4
4
|
# cookies
|
5
5
|
t.string :visit_token
|
6
6
|
t.string :visitor_token
|
@@ -39,7 +39,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration
|
|
39
39
|
t.timestamp :created_at
|
40
40
|
end
|
41
41
|
|
42
|
-
add_index :
|
43
|
-
add_index :
|
42
|
+
add_index :visits, [:visit_token], unique: true
|
43
|
+
add_index :visits, [:user_id, :user_type]
|
44
44
|
end
|
45
45
|
end
|
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.0
|
4
|
+
version: 0.1.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-03-
|
11
|
+
date: 2014-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: request_store
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: bundler
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,9 +122,10 @@ files:
|
|
108
122
|
- Rakefile
|
109
123
|
- ahoy_matey.gemspec
|
110
124
|
- app/controllers/ahoy/visits_controller.rb
|
111
|
-
- app/models/ahoy/visit.rb
|
112
125
|
- config/routes.rb
|
113
126
|
- lib/ahoy/controller.rb
|
127
|
+
- lib/ahoy/engine.rb
|
128
|
+
- lib/ahoy/model.rb
|
114
129
|
- lib/ahoy/version.rb
|
115
130
|
- lib/ahoy_matey.rb
|
116
131
|
- lib/generators/ahoy/install_generator.rb
|
@@ -136,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
151
|
version: '0'
|
137
152
|
requirements: []
|
138
153
|
rubyforge_project:
|
139
|
-
rubygems_version: 2.2.
|
154
|
+
rubygems_version: 2.2.2
|
140
155
|
signing_key:
|
141
156
|
specification_version: 4
|
142
157
|
summary: Simple, powerful visit tracking for Rails
|
data/app/models/ahoy/visit.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
module Ahoy
|
2
|
-
class Visit < ActiveRecord::Base
|
3
|
-
belongs_to :user, polymorphic: true
|
4
|
-
|
5
|
-
def set_traffic_source
|
6
|
-
referring_uri = Addressable::URI.parse(referrer) rescue nil
|
7
|
-
self.referring_domain = referring_uri.try(:host)
|
8
|
-
search_keyword = RefererParser::Referer.new(referrer).search_term rescue nil
|
9
|
-
self.search_keyword = search_keyword.present? ? search_keyword : nil
|
10
|
-
end
|
11
|
-
|
12
|
-
def set_utm_parameters
|
13
|
-
landing_uri = Addressable::URI.parse(landing_page) rescue nil
|
14
|
-
if landing_uri
|
15
|
-
query_values = landing_uri.query_values || {}
|
16
|
-
%w[utm_source utm_medium utm_term utm_content utm_campaign].each do |name|
|
17
|
-
self[name] = query_values[name]
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def set_technology
|
23
|
-
browser = Browser.new(ua: user_agent)
|
24
|
-
|
25
|
-
self.browser = browser.name
|
26
|
-
|
27
|
-
# TODO add more
|
28
|
-
self.os =
|
29
|
-
if browser.android?
|
30
|
-
"Android"
|
31
|
-
elsif browser.ios?
|
32
|
-
"iOS"
|
33
|
-
elsif browser.windows_phone?
|
34
|
-
"Windows Phone"
|
35
|
-
elsif browser.blackberry?
|
36
|
-
"Blackberry"
|
37
|
-
elsif browser.chrome_os?
|
38
|
-
"Chrome OS"
|
39
|
-
elsif browser.mac?
|
40
|
-
"Mac"
|
41
|
-
elsif browser.windows?
|
42
|
-
"Windows"
|
43
|
-
elsif browser.linux?
|
44
|
-
"Linux"
|
45
|
-
end
|
46
|
-
|
47
|
-
self.device_type =
|
48
|
-
if browser.tv?
|
49
|
-
"TV"
|
50
|
-
elsif browser.console?
|
51
|
-
"Console"
|
52
|
-
elsif browser.tablet?
|
53
|
-
"Tablet"
|
54
|
-
elsif browser.mobile?
|
55
|
-
"Mobile"
|
56
|
-
else
|
57
|
-
"Desktop"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def set_location
|
62
|
-
location = Geocoder.search(ip).first rescue nil
|
63
|
-
if location
|
64
|
-
self.country = location.country.presence
|
65
|
-
self.region = location.state.presence
|
66
|
-
self.city = location.city.presence
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
end
|