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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7973efa4435f37d54dfa0bd84b7441473a4e3ddf
4
- data.tar.gz: 3ceb16b3595ae8b258e63102ae996667652802af
3
+ metadata.gz: 443dfa85abf63a4f515ea9c6865beb09f77404ab
4
+ data.tar.gz: 9099a3d7a51f6426f0ebae09fd3d9c831385a1e5
5
5
  SHA512:
6
- metadata.gz: a16bd0b6d30f134a901d2574bc944d411e049e1a7fcaae46707245a98287013b2e4c26c69eca8fd77cf00394e34bd6576843b62d85fc7d93e92344565e7e58a1
7
- data.tar.gz: 6c228da084f4949c9fbd8618b1fdc2a04f83a75402d3f2eadbba954f6f02988b040b223181f4b7a34e5ec6d5e375c218a367ceb3457239966bff877b8b564503
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
- In under a minute, start learning more about your visitors.
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
- ## What You Get
42
+ ## How It Works
43
43
 
44
- When a person visits your website, Ahoy creates a visit with lots of useful information.
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
- The information is great on it’s own, but super powerful when combined with other models.
48
+ Explore your visits with queries like:
49
49
 
50
- You can store the visit id on any model. For instance, when someone places an order:
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 # needs better name
70
+ visitable
55
71
  end
56
72
  ```
57
73
 
58
- The visit_id column will be automatically set. Magic!
74
+ When a visitor places an order, the `visit_id` column will be automatically set. Magic!
59
75
 
60
- When you want to explore where most orders are coming from, you can do a number of queries.
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::Visit.new do |v|
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
@@ -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::Visit.where(visit_token: cookies[:ahoy_visit]).first
15
+ @current_visit ||= Ahoy.visit_model.where(visit_token: cookies[:ahoy_visit]).first
13
16
  end
14
17
  end
15
18
 
@@ -0,0 +1,5 @@
1
+ module Ahoy
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Ahoy
4
+ end
5
+ end
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
@@ -1,3 +1,3 @@
1
1
  module Ahoy
2
- VERSION = "0.0.4"
2
+ VERSION = "0.1.0"
3
3
  end
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
- class Engine < ::Rails::Engine
10
- isolate_namespace Ahoy
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::Visit.where(visit_token: request.cookies["ahoy_visit"]).first
24
- visit.user = user
25
- visit.save!
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 :ahoy_visits do |t|
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 :ahoy_visits, [:visit_token], unique: true
43
- add_index :ahoy_visits, [:user_id, :user_type]
42
+ add_index :visits, [:visit_token], unique: true
43
+ add_index :visits, [:user_id, :user_type]
44
44
  end
45
45
  end
@@ -63,7 +63,9 @@
63
63
  }
64
64
 
65
65
  function debug(message) {
66
- window.console.log(message, visitToken, visitorToken);
66
+ if (debugMode) {
67
+ window.console.log(message, visitToken, visitorToken);
68
+ }
67
69
  }
68
70
 
69
71
  // main
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
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-02 00:00:00.000000000 Z
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.0
154
+ rubygems_version: 2.2.2
140
155
  signing_key:
141
156
  specification_version: 4
142
157
  summary: Simple, powerful visit tracking for Rails
@@ -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