ahoy_matey 0.3.2 → 1.0.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/CHANGELOG.md +8 -0
- data/README.md +324 -189
- data/ahoy_matey.gemspec +1 -0
- data/app/controllers/ahoy/base_controller.rb +5 -0
- data/app/controllers/ahoy/events_controller.rb +9 -7
- data/app/controllers/ahoy/visits_controller.rb +2 -1
- data/lib/ahoy.rb +37 -28
- data/lib/ahoy/controller.rb +12 -20
- data/lib/ahoy/deckhands/location_deckhand.rb +39 -0
- data/lib/ahoy/deckhands/request_deckhand.rb +41 -0
- data/lib/ahoy/deckhands/technology_deckhand.rb +49 -0
- data/lib/ahoy/deckhands/traffic_source_deckhand.rb +24 -0
- data/lib/ahoy/deckhands/utm_parameter_deckhand.rb +24 -0
- data/lib/ahoy/engine.rb +3 -1
- data/lib/ahoy/model.rb +19 -82
- data/lib/ahoy/stores/active_record_store.rb +63 -0
- data/lib/ahoy/stores/active_record_token_store.rb +113 -0
- data/lib/ahoy/stores/base_store.rb +65 -0
- data/lib/ahoy/stores/log_store.rb +47 -0
- data/lib/ahoy/stores/mongoid_store.rb +59 -0
- data/lib/ahoy/tracker.rb +108 -67
- data/lib/ahoy/version.rb +1 -1
- data/lib/ahoy/visit_properties.rb +58 -0
- data/lib/ahoy/warden.rb +1 -10
- data/lib/generators/ahoy/stores/active_record_events_generator.rb +44 -0
- data/lib/generators/ahoy/stores/active_record_generator.rb +17 -0
- data/lib/generators/ahoy/{events/active_record_generator.rb → stores/active_record_visits_generator.rb} +14 -6
- data/lib/generators/ahoy/stores/custom_generator.rb +16 -0
- data/lib/generators/ahoy/stores/log_generator.rb +16 -0
- data/lib/generators/ahoy/stores/mongoid_events_generator.rb +20 -0
- data/lib/generators/ahoy/stores/mongoid_generator.rb +16 -0
- data/lib/generators/ahoy/stores/mongoid_visits_generator.rb +20 -0
- data/{app/models/ahoy/event.rb → lib/generators/ahoy/stores/templates/active_record_event_model.rb} +2 -2
- data/lib/generators/ahoy/stores/templates/active_record_events_migration.rb +20 -0
- data/lib/generators/ahoy/stores/templates/active_record_initializer.rb +3 -0
- data/lib/generators/ahoy/stores/templates/active_record_visit_model.rb +4 -0
- data/lib/generators/ahoy/{templates/install.rb → stores/templates/active_record_visits_migration.rb} +6 -8
- data/lib/generators/ahoy/stores/templates/custom_initializer.rb +12 -0
- data/lib/generators/ahoy/stores/templates/log_initializer.rb +3 -0
- data/lib/generators/ahoy/stores/templates/mongoid_event_model.rb +12 -0
- data/lib/generators/ahoy/stores/templates/mongoid_initializer.rb +3 -0
- data/lib/generators/ahoy/stores/templates/mongoid_visit_model.rb +41 -0
- data/vendor/assets/javascripts/ahoy.js +19 -8
- metadata +45 -8
- data/lib/generators/ahoy/events/templates/create_events.rb +0 -20
- data/lib/generators/ahoy/events/templates/initializer.rb +0 -1
- data/lib/generators/ahoy/install_generator.rb +0 -38
data/ahoy_matey.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_dependency "referer-parser"
|
25
25
|
spec.add_dependency "user_agent_parser"
|
26
26
|
spec.add_dependency "request_store"
|
27
|
+
spec.add_dependency "uuidtools"
|
27
28
|
|
28
29
|
spec.add_development_dependency "bundler", "~> 1.5"
|
29
30
|
spec.add_development_dependency "rake"
|
@@ -4,13 +4,15 @@ module Ahoy
|
|
4
4
|
def create
|
5
5
|
events = params[:name] ? [params] : ActiveSupport::JSON.decode(request.body.read)
|
6
6
|
events.each do |event|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
time = Time.zone.parse(event["time"]) rescue nil
|
8
|
+
|
9
|
+
# timestamp is deprecated
|
10
|
+
time ||= Time.zone.at(event["time"].to_f) rescue nil
|
11
|
+
|
12
|
+
options = {
|
13
|
+
id: event["id"],
|
14
|
+
time: time
|
15
|
+
}
|
14
16
|
ahoy.track event["name"], event["properties"], options
|
15
17
|
end
|
16
18
|
render json: {}
|
data/lib/ahoy.rb
CHANGED
@@ -4,47 +4,61 @@ require "geocoder"
|
|
4
4
|
require "referer-parser"
|
5
5
|
require "user_agent_parser"
|
6
6
|
require "request_store"
|
7
|
+
require "uuidtools"
|
8
|
+
|
7
9
|
require "ahoy/version"
|
8
10
|
require "ahoy/tracker"
|
9
11
|
require "ahoy/controller"
|
10
12
|
require "ahoy/model"
|
11
|
-
require "ahoy/
|
13
|
+
require "ahoy/visit_properties"
|
14
|
+
require "ahoy/deckhands/location_deckhand"
|
15
|
+
require "ahoy/deckhands/request_deckhand"
|
16
|
+
require "ahoy/deckhands/technology_deckhand"
|
17
|
+
require "ahoy/deckhands/traffic_source_deckhand"
|
18
|
+
require "ahoy/deckhands/utm_parameter_deckhand"
|
19
|
+
require "ahoy/stores/base_store"
|
20
|
+
require "ahoy/stores/active_record_store"
|
21
|
+
require "ahoy/stores/active_record_token_store"
|
22
|
+
require "ahoy/stores/log_store"
|
23
|
+
require "ahoy/stores/mongoid_store"
|
12
24
|
require "ahoy/engine"
|
13
25
|
require "ahoy/warden" if defined?(Warden)
|
14
26
|
|
27
|
+
# deprecated
|
28
|
+
require "ahoy/subscribers/active_record"
|
29
|
+
|
15
30
|
module Ahoy
|
31
|
+
UUID_NAMESPACE = UUIDTools::UUID.parse("a82ae811-5011-45ab-a728-569df7499c5f")
|
16
32
|
|
17
|
-
|
18
|
-
|
19
|
-
end
|
33
|
+
mattr_accessor :visit_duration
|
34
|
+
self.visit_duration = 4.hours
|
20
35
|
|
21
|
-
|
22
|
-
|
23
|
-
end
|
36
|
+
mattr_accessor :visitor_duration
|
37
|
+
self.visitor_duration = 2.years
|
24
38
|
|
25
|
-
|
26
|
-
@visit_model = visit_model
|
27
|
-
end
|
39
|
+
mattr_accessor :cookie_domain
|
28
40
|
|
29
|
-
|
30
|
-
|
31
|
-
def self.referrer_parser
|
32
|
-
@referrer_parser ||= RefererParser::Referer.new("https://github.com/ankane/ahoy")
|
33
|
-
end
|
41
|
+
mattr_accessor :track_visits_immediately
|
42
|
+
self.track_visits_immediately = false
|
34
43
|
|
35
|
-
|
36
|
-
|
37
|
-
@user_agent_parser ||= UserAgentParser::Parser.new
|
38
|
-
end
|
44
|
+
mattr_accessor :quiet
|
45
|
+
self.quiet = true
|
39
46
|
|
40
|
-
def self.
|
41
|
-
|
42
|
-
|
47
|
+
def self.ensure_uuid(id)
|
48
|
+
valid = UUIDTools::UUID.parse(id) rescue nil
|
49
|
+
if valid
|
50
|
+
id
|
43
51
|
else
|
44
|
-
|
52
|
+
UUIDTools::UUID.sha1_create(UUID_NAMESPACE, id).to_s
|
45
53
|
end
|
46
54
|
end
|
47
55
|
|
56
|
+
# deprecated
|
57
|
+
|
58
|
+
mattr_accessor :domain
|
59
|
+
|
60
|
+
mattr_accessor :visit_model
|
61
|
+
|
48
62
|
mattr_accessor :user_method
|
49
63
|
self.user_method = proc do |controller|
|
50
64
|
(controller.respond_to?(:current_user) && controller.current_user) || (controller.respond_to?(:current_resource_owner, true) && controller.send(:current_resource_owner)) || nil
|
@@ -57,11 +71,6 @@ module Ahoy
|
|
57
71
|
|
58
72
|
mattr_accessor :track_bots
|
59
73
|
self.track_bots = false
|
60
|
-
|
61
|
-
mattr_accessor :quiet
|
62
|
-
self.quiet = true
|
63
|
-
|
64
|
-
mattr_accessor :domain
|
65
74
|
end
|
66
75
|
|
67
76
|
ActionController::Base.send :include, Ahoy::Controller
|
data/lib/ahoy/controller.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
+
require "request_store"
|
2
|
+
|
1
3
|
module Ahoy
|
2
4
|
module Controller
|
3
5
|
|
4
6
|
def self.included(base)
|
5
7
|
base.helper_method :current_visit
|
6
8
|
base.helper_method :ahoy
|
7
|
-
base.before_filter :
|
9
|
+
base.before_filter :set_ahoy_cookies
|
10
|
+
base.before_filter :track_ahoy_visit
|
8
11
|
base.before_filter do
|
9
|
-
RequestStore.store[:
|
12
|
+
RequestStore.store[:ahoy] ||= ahoy
|
10
13
|
end
|
11
14
|
end
|
12
15
|
|
@@ -15,28 +18,17 @@ module Ahoy
|
|
15
18
|
end
|
16
19
|
|
17
20
|
def current_visit
|
18
|
-
|
19
|
-
if visit_token
|
20
|
-
@current_visit ||= Ahoy.visit_model.where(visit_token: visit_token).first
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def current_visit_token
|
25
|
-
@current_visit_token ||= request.headers["Ahoy-Visit"] || cookies[:ahoy_visit]
|
21
|
+
ahoy.visit
|
26
22
|
end
|
27
23
|
|
28
|
-
def
|
29
|
-
|
24
|
+
def set_ahoy_cookies
|
25
|
+
ahoy.set_visitor_cookie
|
26
|
+
ahoy.set_visit_cookie
|
30
27
|
end
|
31
28
|
|
32
|
-
def
|
33
|
-
if
|
34
|
-
|
35
|
-
value: current_visitor_token,
|
36
|
-
expires: 2.years.from_now
|
37
|
-
}
|
38
|
-
cookie[:domain] = Ahoy.domain if Ahoy.domain
|
39
|
-
cookies[:ahoy_visitor] = cookie
|
29
|
+
def track_ahoy_visit
|
30
|
+
if ahoy.new_visit?
|
31
|
+
ahoy.track_visit(defer: !Ahoy.track_visits_immediately)
|
40
32
|
end
|
41
33
|
end
|
42
34
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Ahoy
|
2
|
+
module Deckhands
|
3
|
+
class LocationDeckhand
|
4
|
+
|
5
|
+
def initialize(ip)
|
6
|
+
@ip = ip
|
7
|
+
end
|
8
|
+
|
9
|
+
def country
|
10
|
+
location.try(:country).presence
|
11
|
+
end
|
12
|
+
|
13
|
+
def region
|
14
|
+
location.try(:state).presence
|
15
|
+
end
|
16
|
+
|
17
|
+
def city
|
18
|
+
location.try(:city).presence
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def location
|
24
|
+
if !@checked
|
25
|
+
@location =
|
26
|
+
begin
|
27
|
+
Geocoder.search(@ip).first
|
28
|
+
rescue => e
|
29
|
+
$stderr.puts e.message
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
@checked = true
|
33
|
+
end
|
34
|
+
@location
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Ahoy
|
2
|
+
module Deckhands
|
3
|
+
class RequestDeckhand
|
4
|
+
attr_reader :request
|
5
|
+
|
6
|
+
def initialize(request, options = {})
|
7
|
+
@request = request
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def ip
|
12
|
+
request.remote_ip
|
13
|
+
end
|
14
|
+
|
15
|
+
def user_agent
|
16
|
+
request.user_agent
|
17
|
+
end
|
18
|
+
|
19
|
+
def referrer
|
20
|
+
@options[:api] ? request.params["referrer"] : request.referer
|
21
|
+
end
|
22
|
+
|
23
|
+
def landing_page
|
24
|
+
@options[:api] ? request.params["landing_page"] : request.original_url
|
25
|
+
end
|
26
|
+
|
27
|
+
def platform
|
28
|
+
request.params["platform"]
|
29
|
+
end
|
30
|
+
|
31
|
+
def app_version
|
32
|
+
request.params["app_version"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def os_version
|
36
|
+
request.params["os_version"]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Ahoy
|
2
|
+
module Deckhands
|
3
|
+
class TechnologyDeckhand
|
4
|
+
|
5
|
+
def initialize(user_agent)
|
6
|
+
@user_agent = user_agent
|
7
|
+
end
|
8
|
+
|
9
|
+
def browser
|
10
|
+
agent.name
|
11
|
+
end
|
12
|
+
|
13
|
+
def os
|
14
|
+
agent.os.name
|
15
|
+
end
|
16
|
+
|
17
|
+
def device_type
|
18
|
+
@device_type ||= begin
|
19
|
+
browser = Browser.new(ua: @user_agent)
|
20
|
+
if browser.bot?
|
21
|
+
"Bot"
|
22
|
+
elsif browser.tv?
|
23
|
+
"TV"
|
24
|
+
elsif browser.console?
|
25
|
+
"Console"
|
26
|
+
elsif browser.tablet?
|
27
|
+
"Tablet"
|
28
|
+
elsif browser.mobile?
|
29
|
+
"Mobile"
|
30
|
+
else
|
31
|
+
"Desktop"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def agent
|
39
|
+
@agent ||= self.class.user_agent_parser.parse(@user_agent)
|
40
|
+
end
|
41
|
+
|
42
|
+
# performance
|
43
|
+
def self.user_agent_parser
|
44
|
+
@user_agent_parser ||= UserAgentParser::Parser.new
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ahoy
|
2
|
+
module Deckhands
|
3
|
+
class TrafficSourceDeckhand
|
4
|
+
|
5
|
+
def initialize(referrer)
|
6
|
+
@referrer = referrer
|
7
|
+
end
|
8
|
+
|
9
|
+
def referring_domain
|
10
|
+
@referring_domain ||= Addressable::URI.parse(@referrer).host.first(255) rescue nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def search_keyword
|
14
|
+
@search_keyword ||= (self.class.referrer_parser.parse(@referrer)[1].first(255) rescue nil).presence
|
15
|
+
end
|
16
|
+
|
17
|
+
# performance hack for referer-parser
|
18
|
+
def self.referrer_parser
|
19
|
+
@referrer_parser ||= RefererParser::Referer.new("https://github.com/ankane/ahoy")
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ahoy
|
2
|
+
module Deckhands
|
3
|
+
class UtmParameterDeckhand
|
4
|
+
|
5
|
+
def initialize(landing_page)
|
6
|
+
@landing_page = landing_page
|
7
|
+
end
|
8
|
+
|
9
|
+
def landing_params
|
10
|
+
@landing_params ||= begin
|
11
|
+
landing_uri = Addressable::URI.parse(@landing_page) rescue nil
|
12
|
+
(landing_uri && landing_uri.query_values) || {}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
%w[utm_source utm_medium utm_term utm_content utm_campaign].each do |name|
|
17
|
+
define_method name do
|
18
|
+
landing_params[name]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/ahoy/engine.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module Ahoy
|
2
2
|
class Engine < ::Rails::Engine
|
3
|
+
|
3
4
|
# from https://github.com/evrone/quiet_assets/blob/master/lib/quiet_assets.rb
|
4
|
-
initializer "ahoy", after: "sprockets.environment" do |app|
|
5
|
+
initializer "ahoy.middleware", after: "sprockets.environment" do |app|
|
5
6
|
next unless Ahoy.quiet
|
6
7
|
|
7
8
|
# Parse PATH_INFO by assets prefix
|
@@ -24,5 +25,6 @@ module Ahoy
|
|
24
25
|
alias_method_chain :call, :quiet_ahoy
|
25
26
|
end
|
26
27
|
end
|
28
|
+
|
27
29
|
end
|
28
30
|
end
|
data/lib/ahoy/model.rb
CHANGED
@@ -1,87 +1,6 @@
|
|
1
1
|
module Ahoy
|
2
2
|
module Model
|
3
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_domain = Addressable::URI.parse(referrer).host.first(255) rescue nil
|
16
|
-
self.referring_domain = referring_domain if respond_to?(:referring_domain=)
|
17
|
-
# performance hack for referer-parser
|
18
|
-
search_keyword = Ahoy.referrer_parser.parse(referrer)[1].first(255) rescue nil
|
19
|
-
self.search_keyword = search_keyword.presence if respond_to?(:search_keyword=)
|
20
|
-
true
|
21
|
-
end
|
22
|
-
|
23
|
-
def set_utm_parameters
|
24
|
-
%w[utm_source utm_medium utm_term utm_content utm_campaign].each do |name|
|
25
|
-
self[name] = landing_params[name] if respond_to?(:"#{name}=")
|
26
|
-
end
|
27
|
-
true
|
28
|
-
end
|
29
|
-
|
30
|
-
def set_technology
|
31
|
-
if respond_to?(:user_agent)
|
32
|
-
agent = Ahoy.user_agent_parser.parse(user_agent)
|
33
|
-
|
34
|
-
self.browser = agent.name if respond_to?(:browser=)
|
35
|
-
self.os = agent.os.name if respond_to?(:os=)
|
36
|
-
|
37
|
-
browser = Browser.new(ua: user_agent)
|
38
|
-
self.device_type =
|
39
|
-
if browser.bot?
|
40
|
-
"Bot"
|
41
|
-
elsif browser.tv?
|
42
|
-
"TV"
|
43
|
-
elsif browser.console?
|
44
|
-
"Console"
|
45
|
-
elsif browser.tablet?
|
46
|
-
"Tablet"
|
47
|
-
elsif browser.mobile?
|
48
|
-
"Mobile"
|
49
|
-
else
|
50
|
-
"Desktop"
|
51
|
-
end if respond_to?(:device_type=)
|
52
|
-
end
|
53
|
-
true
|
54
|
-
end
|
55
|
-
|
56
|
-
def set_location
|
57
|
-
if respond_to?(:ip) and [:country=, :region=, :city=].any?{|method| respond_to?(method) }
|
58
|
-
location =
|
59
|
-
begin
|
60
|
-
Geocoder.search(ip).first
|
61
|
-
rescue => e
|
62
|
-
$stderr.puts e.message
|
63
|
-
nil
|
64
|
-
end
|
65
|
-
|
66
|
-
if location
|
67
|
-
self.country = location.country.presence if respond_to?(:country=)
|
68
|
-
self.region = location.state.presence if respond_to?(:region=)
|
69
|
-
self.city = location.city.presence if respond_to?(:city=)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
true
|
73
|
-
end
|
74
|
-
|
75
|
-
def landing_params
|
76
|
-
@landing_params ||= begin
|
77
|
-
landing_uri = Addressable::URI.parse(landing_page) rescue nil
|
78
|
-
ActiveSupport::HashWithIndifferentAccess.new((landing_uri && landing_uri.query_values) || {})
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
end # end class_eval
|
83
|
-
end
|
84
|
-
|
85
4
|
def visitable(name = nil, options = {})
|
86
5
|
if name.is_a?(Hash)
|
87
6
|
name = nil
|
@@ -94,10 +13,28 @@ module Ahoy
|
|
94
13
|
end
|
95
14
|
class_eval %Q{
|
96
15
|
def set_visit
|
97
|
-
self.#{name} ||= RequestStore.store[:
|
16
|
+
self.#{name} ||= RequestStore.store[:ahoy].visit
|
98
17
|
end
|
99
18
|
}
|
100
19
|
end
|
101
20
|
|
21
|
+
# deprecated
|
22
|
+
|
23
|
+
def ahoy_visit
|
24
|
+
class_eval do
|
25
|
+
warn "[DEPRECATION] ahoy_visit is deprecated"
|
26
|
+
|
27
|
+
belongs_to :user, polymorphic: true
|
28
|
+
|
29
|
+
def landing_params
|
30
|
+
@landing_params ||= begin
|
31
|
+
warn "[DEPRECATION] landing_params is deprecated"
|
32
|
+
Deckhands::UtmParameterDeckhand.new(landing_page).landing_params
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
102
39
|
end
|
103
40
|
end
|