ahoy_matey 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1a219da19a81b96724452cfbdc4286539398e821
4
+ data.tar.gz: 59f20f3824b077697868421296e70833d3e11f95
5
+ SHA512:
6
+ metadata.gz: 79846a0acf1bb30aca352d9d5bd00a92ce4eb16aac5970ddbc1c7ca04046d7cb4789b0127d70371ed2652f0f778087eab3eba2134e9b7e6b72dd129eb61113ea
7
+ data.tar.gz: a21aa44815dca19d629f53a29bdcf37b239068c8238b9cda947a9eadb9bcc8514413655ae99da3b81d7ec9a0a906aa404636ddc227f112d43d2ccf6bd5c263cc
data/.gitignore ADDED
@@ -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
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ahoy.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -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.
data/README.md ADDED
@@ -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
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -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 = ["andrew@chartkick.com"]
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
@@ -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
@@ -0,0 +1,5 @@
1
+ module Ahoy
2
+ class Visit < ActiveRecord::Base
3
+ belongs_to :user, polymorphic: true
4
+ end
5
+ end
data/config/routes.rb ADDED
@@ -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
@@ -0,0 +1,3 @@
1
+ module Ahoy
2
+ VERSION = "0.0.1"
3
+ end
data/lib/ahoy_matey.rb ADDED
@@ -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
@@ -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
@@ -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,0 +1,107 @@
1
+ /*
2
+ * Ahoy.js - 0.0.1
3
+ * Super simple visit tracking
4
+ * https://github.com/ankane/ahoy
5
+ * MIT License
6
+ */
7
+
8
+ (function () {
9
+ "use strict";
10
+
11
+ var debugMode = true;
12
+ var visitTtl, visitorTtl;
13
+
14
+ if (debugMode) {
15
+ visitTtl = 0.2;
16
+ visitorTtl = 5; // 5 minutes
17
+ } else {
18
+ visitTtl = 4 * 60; // 4 hours
19
+ visitorTtl = 2 * 365 * 24 * 60; // 2 years
20
+ }
21
+
22
+ // cookies
23
+
24
+ // http://www.quirksmode.org/js/cookies.html
25
+ function setCookie(name, value, ttl) {
26
+ if (ttl) {
27
+ var date = new Date();
28
+ date.setTime(date.getTime()+(ttl*60*1000));
29
+ var expires = "; expires="+date.toGMTString();
30
+ }
31
+ else var expires = "";
32
+ document.cookie = name+"="+value+expires+"; path=/";
33
+ }
34
+
35
+ function getCookie(name) {
36
+ var nameEQ = name + "=";
37
+ var ca = document.cookie.split(';');
38
+ for(var i=0;i < ca.length;i++) {
39
+ var c = ca[i];
40
+ while (c.charAt(0)==' ') c = c.substring(1,c.length);
41
+ if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
42
+ }
43
+ return null;
44
+ }
45
+
46
+ // ids
47
+
48
+ // https://github.com/klughammer/node-randomstring
49
+ function generateToken() {
50
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz';
51
+ var length = 32;
52
+ var string = '';
53
+
54
+ for (var i = 0; i < length; i++) {
55
+ var randomNumber = Math.floor(Math.random() * chars.length);
56
+ string += chars.substring(randomNumber, randomNumber + 1);
57
+ }
58
+
59
+ return string;
60
+ }
61
+
62
+ function debug(message) {
63
+ console.log(message, visitToken, visitorToken);
64
+ }
65
+
66
+ // main
67
+
68
+ var visitToken = getCookie("ahoy_visit");
69
+ var visitorToken = getCookie("ahoy_visitor");
70
+
71
+ if (visitToken && visitorToken) {
72
+ // TODO keep visit alive?
73
+ debug("Active visit");
74
+ } else {
75
+ if (!visitorToken) {
76
+ visitorToken = generateToken();
77
+ setCookie("ahoy_visitor", visitorToken, visitorTtl);
78
+ }
79
+
80
+ // always generate a new visit id here
81
+ visitToken = generateToken();
82
+ setCookie("ahoy_visit", visitToken, visitTtl);
83
+
84
+ // make sure cookies are enabled
85
+ if (getCookie("ahoy_visit")) {
86
+ debug("Visit started");
87
+
88
+ var data = {
89
+ visit_token: visitToken,
90
+ visitor_token: visitorToken,
91
+ landing_page: window.location.href
92
+ };
93
+
94
+ // referrer
95
+ if (document.referrer.length > 0) {
96
+ data.referrer = document.referrer;
97
+ }
98
+
99
+ debug(data);
100
+
101
+ $.post("/ahoy/visits", data);
102
+ } else {
103
+ debug("Cookies disabled");
104
+ }
105
+ }
106
+
107
+ }());
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ahoy_matey
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Kane
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: browser
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.4.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: geocoder
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Simple, powerful visit tracking for Rails
84
+ email:
85
+ - andrew@chartkick.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - ahoy_matey.gemspec
96
+ - app/controllers/ahoy/visits_controller.rb
97
+ - app/models/ahoy/visit.rb
98
+ - config/routes.rb
99
+ - lib/ahoy/version.rb
100
+ - lib/ahoy_matey.rb
101
+ - lib/generators/ahoy/install_generator.rb
102
+ - lib/generators/ahoy/templates/install.rb
103
+ - vendor/assets/javascripts/ahoy.js
104
+ homepage: https://github.com/ankane/ahoy
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.2.0
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Simple, powerful visit tracking for Rails
128
+ test_files: []