Skip to content

Commit 97eee13

Browse files
committed
First commit
0 parents  commit 97eee13

File tree

14 files changed

+420
-0
lines changed

14 files changed

+420
-0
lines changed

.gitignore

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
*.gem
2+
*.rbc
3+
.bundle
4+
.config
5+
.yardoc
6+
Gemfile.lock
7+
InstalledFiles
8+
_yardoc
9+
coverage
10+
doc/
11+
lib/bundler/man
12+
pkg
13+
rdoc
14+
spec/reports
15+
test/tmp
16+
test/version_tmp
17+
tmp

Gemfile

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
source 'https://rubygems.org'
2+
3+
# Specify your gem's dependencies in ahoy.gemspec
4+
gemspec

LICENSE.txt

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2014 Andrew Kane
2+
3+
MIT License
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Ahoy
2+
3+
Simple, powerful visit tracking for Rails.
4+
5+
## Get Started
6+
7+
Add this line to your application’s Gemfile:
8+
9+
```ruby
10+
gem "ahoy_matey"
11+
```
12+
13+
And run the generator. This creates a migration to store visits.
14+
15+
```sh
16+
rails generate ahoy:install
17+
rake db:migrate
18+
```
19+
20+
Next, include the javascript file in your `app/assets/javascripts/application.js` after jQuery.
21+
22+
```javascript
23+
//= require jquery
24+
//= require ahoy
25+
```
26+
27+
That’s it.
28+
29+
## What You Get
30+
31+
When a person visits your website, Ahoy creates a visit with lots of useful information.
32+
33+
- source (referrer, referring domain, campaign, landing page)
34+
- location (country, region, and city)
35+
- technology (browser, OS, and device type)
36+
37+
This information is great on it’s own, but super powerful when combined with other models.
38+
39+
You can store the visit id on any model. For instance, when someone places an order:
40+
41+
```ruby
42+
Order.create!(
43+
visit_id: ahoy_visit.id,
44+
# ... more attributes ...
45+
)
46+
```
47+
48+
When you want to explore where most orders are coming from, you can do a number of queries.
49+
50+
```ruby
51+
Order.joins(:ahoy_visits).group("referring_domain").count
52+
Order.joins(:ahoy_visits).group("city").count
53+
Order.joins(:ahoy_visits).group("device_type").count
54+
```
55+
56+
## Features
57+
58+
- Excludes search engines
59+
- Gracefully degrades when cookies are disabled
60+
- Gets campaign from utm_campaign parameter
61+
62+
# How It Works
63+
64+
When a user visits your website for the first time, the Javascript library generates a unique visit and visitor id.
65+
66+
It sends the event to the server.
67+
68+
A visit cookie is set for 4 hours, and a visitor cookie is set for 2 years.
69+
70+
## Contributing
71+
72+
Everyone is encouraged to help improve this project. Here are a few ways you can help:
73+
74+
- [Report bugs](https://github.com/ankane/ahoy/issues)
75+
- Fix bugs and [submit pull requests](https://github.com/ankane/ahoy/pulls)
76+
- Write, clarify, or fix documentation
77+
- Suggest or add new features

Rakefile

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
require "bundler/gem_tasks"

ahoy_matey.gemspec

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# coding: utf-8
2+
lib = File.expand_path('../lib', __FILE__)
3+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4+
require 'ahoy/version'
5+
6+
Gem::Specification.new do |spec|
7+
spec.name = "ahoy_matey"
8+
spec.version = Ahoy::VERSION
9+
spec.authors = ["Andrew Kane"]
10+
spec.email = ["[email protected]"]
11+
spec.summary = %q{Simple, powerful visit tracking for Rails}
12+
spec.description = %q{Simple, powerful visit tracking for Rails}
13+
spec.homepage = "https://github.com/ankane/ahoy"
14+
spec.license = "MIT"
15+
16+
spec.files = `git ls-files -z`.split("\x0")
17+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19+
spec.require_paths = ["lib"]
20+
21+
spec.add_dependency "addressable"
22+
spec.add_dependency "browser", ">= 0.4.0"
23+
spec.add_dependency "geocoder"
24+
25+
spec.add_development_dependency "bundler", "~> 1.5"
26+
spec.add_development_dependency "rake"
27+
end
+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
module Ahoy
2+
class VisitsController < ActionController::Base
3+
4+
def create
5+
visit =
6+
Ahoy::Visit.new do |v|
7+
v.visit_token = params[:visit_token]
8+
v.visitor_token = params[:visitor_token]
9+
v.ip = request.remote_ip
10+
v.user_agent = request.user_agent
11+
v.referrer = params[:referrer]
12+
v.landing_page = params[:landing_page]
13+
v.user = current_user if respond_to?(:current_user)
14+
end
15+
16+
referring_uri = Addressable::URI.parse(params[:referrer]) rescue nil
17+
if referring_uri
18+
visit.referring_domain = referring_uri.host
19+
end
20+
21+
landing_uri = Addressable::URI.parse(params[:landing_page]) rescue nil
22+
if landing_uri
23+
visit.campaign = (landing_uri.query_values || {})["utm_campaign"]
24+
end
25+
26+
browser = Browser.new(ua: request.user_agent)
27+
visit.browser = browser.name
28+
29+
# TODO add more
30+
visit.os =
31+
if browser.android?
32+
"Android"
33+
elsif browser.ios?
34+
"iOS"
35+
elsif browser.windows_phone?
36+
"Windows Phone"
37+
elsif browser.blackberry?
38+
"Blackberry"
39+
elsif browser.chrome_os?
40+
"Chrome OS"
41+
elsif browser.mac?
42+
"Mac"
43+
elsif browser.windows?
44+
"Windows"
45+
elsif browser.linux?
46+
"Linux"
47+
end
48+
49+
visit.device_type =
50+
if browser.tv?
51+
"TV"
52+
elsif browser.console?
53+
"Console"
54+
elsif browser.tablet?
55+
"Tablet"
56+
elsif browser.mobile?
57+
"Mobile"
58+
else
59+
"Desktop"
60+
end
61+
62+
# location
63+
location = Geocoder.search(request.remote_ip).first rescue nil
64+
if location
65+
visit.country = location.country.presence
66+
visit.region = location.state.presence
67+
visit.city = location.city.presence
68+
end
69+
70+
visit.save!
71+
render json: {id: visit.id}
72+
end
73+
74+
end
75+
end

app/models/ahoy/visit.rb

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Ahoy
2+
class Visit < ActiveRecord::Base
3+
belongs_to :user, polymorphic: true
4+
end
5+
end

config/routes.rb

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Rails.application.routes.draw do
2+
mount Ahoy::Engine => "/ahoy"
3+
end
4+
5+
Ahoy::Engine.routes.draw do
6+
resources :visits, only: [:create]
7+
end

lib/ahoy/version.rb

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module Ahoy
2+
VERSION = "0.0.1"
3+
end

lib/ahoy_matey.rb

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
require "ahoy/version"
2+
require "addressable/uri"
3+
require "browser"
4+
require "geocoder"
5+
6+
module Ahoy
7+
class Engine < ::Rails::Engine
8+
isolate_namespace Ahoy
9+
end
10+
end
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 Generators
9+
class InstallGenerator < Rails::Generators::Base
10+
include Rails::Generators::Migration
11+
12+
source_root File.expand_path("../templates", __FILE__)
13+
14+
# Implement the required interface for Rails::Generators::Migration.
15+
def self.next_migration_number(dirname) #:nodoc:
16+
next_migration_number = current_migration_number(dirname) + 1
17+
if ActiveRecord::Base.timestamped_migrations
18+
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
19+
else
20+
"%.3d" % next_migration_number
21+
end
22+
end
23+
24+
def copy_migration
25+
migration_template "install.rb", "db/migrate/install_ahoy.rb"
26+
end
27+
end
28+
end
29+
end
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
class <%= migration_class_name %> < ActiveRecord::Migration
2+
def change
3+
create_table :ahoy_visits do |t|
4+
t.string :visit_token
5+
t.string :visitor_token
6+
t.integer :user_id
7+
t.string :user_type
8+
t.string :ip
9+
t.text :user_agent
10+
11+
# acquisition
12+
t.text :referrer
13+
t.string :referring_domain
14+
t.string :campaign
15+
# t.string :social_network
16+
# t.string :search_engine
17+
# t.string :search_keyword
18+
t.text :landing_page
19+
20+
# technology
21+
t.string :browser
22+
t.string :os
23+
t.string :device_type
24+
25+
# location
26+
t.string :country
27+
t.string :region
28+
t.string :city
29+
30+
t.timestamp :created_at
31+
end
32+
33+
add_index :ahoy_visits, [:visit_token], unique: true
34+
add_index :ahoy_visits, [:user_id, :user_type]
35+
end
36+
end

0 commit comments

Comments
 (0)