ahoy_matey 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []