ahoy_matey 1.6.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +7 -0
  3. data/CHANGELOG.md +22 -0
  4. data/CONTRIBUTING.md +40 -0
  5. data/LICENSE.txt +1 -1
  6. data/README.md +210 -489
  7. data/Rakefile +1 -0
  8. data/ahoy_matey.gemspec +6 -8
  9. data/app/controllers/ahoy/base_controller.rb +2 -6
  10. data/app/controllers/ahoy/events_controller.rb +7 -1
  11. data/app/controllers/ahoy/visits_controller.rb +7 -1
  12. data/app/jobs/ahoy/geocode_job.rb +10 -0
  13. data/app/jobs/ahoy/geocode_v2_job.rb +29 -0
  14. data/config/routes.rb +1 -1
  15. data/docs/Ahoy-2-Upgrade.md +147 -0
  16. data/docs/Data-Store-Examples.md +240 -0
  17. data/lib/ahoy.rb +30 -88
  18. data/lib/ahoy/base_store.rb +72 -0
  19. data/lib/ahoy/controller.rb +4 -10
  20. data/lib/ahoy/database_store.rb +72 -0
  21. data/lib/ahoy/engine.rb +5 -7
  22. data/lib/ahoy/model.rb +4 -26
  23. data/lib/ahoy/{properties.rb → query_methods.rb} +18 -4
  24. data/lib/ahoy/tracker.rb +60 -38
  25. data/lib/ahoy/version.rb +1 -1
  26. data/lib/ahoy/visit_properties.rb +65 -39
  27. data/lib/generators/ahoy/activerecord_generator.rb +58 -0
  28. data/lib/generators/ahoy/base_generator.rb +13 -0
  29. data/lib/generators/ahoy/install_generator.rb +44 -0
  30. data/lib/generators/ahoy/mongoid_generator.rb +20 -0
  31. data/lib/generators/ahoy/templates/active_record_event_model.rb +10 -0
  32. data/lib/generators/ahoy/{stores/templates/active_record_visits_migration.rb → templates/active_record_migration.rb} +19 -21
  33. data/lib/generators/ahoy/templates/active_record_visit_model.rb +6 -0
  34. data/lib/generators/ahoy/templates/base_store_initializer.rb +17 -0
  35. data/lib/generators/ahoy/templates/database_store_initializer.rb +5 -0
  36. data/lib/generators/ahoy/{stores/templates → templates}/mongoid_event_model.rb +4 -2
  37. data/lib/generators/ahoy/{stores/templates → templates}/mongoid_visit_model.rb +7 -3
  38. data/test/query_methods/mongoid_test.rb +23 -0
  39. data/test/{properties → query_methods}/mysql_json_test.rb +1 -1
  40. data/test/{properties → query_methods}/mysql_text_test.rb +1 -1
  41. data/test/{properties → query_methods}/postgresql_hstore_test.rb +1 -1
  42. data/test/{properties → query_methods}/postgresql_json_test.rb +1 -1
  43. data/test/{properties → query_methods}/postgresql_jsonb_test.rb +1 -1
  44. data/test/{properties → query_methods}/postgresql_text_test.rb +1 -1
  45. data/test/test_helper.rb +4 -3
  46. data/vendor/assets/javascripts/ahoy.js +551 -325
  47. metadata +67 -112
  48. data/lib/ahoy/deckhands/location_deckhand.rb +0 -49
  49. data/lib/ahoy/deckhands/request_deckhand.rb +0 -52
  50. data/lib/ahoy/deckhands/technology_deckhand.rb +0 -47
  51. data/lib/ahoy/deckhands/traffic_source_deckhand.rb +0 -22
  52. data/lib/ahoy/deckhands/utm_parameter_deckhand.rb +0 -23
  53. data/lib/ahoy/geocode_job.rb +0 -13
  54. data/lib/ahoy/logger_silencer.rb +0 -75
  55. data/lib/ahoy/stores/active_record_store.rb +0 -61
  56. data/lib/ahoy/stores/active_record_token_store.rb +0 -114
  57. data/lib/ahoy/stores/base_store.rb +0 -88
  58. data/lib/ahoy/stores/bunny_store.rb +0 -33
  59. data/lib/ahoy/stores/fluentd_store.rb +0 -17
  60. data/lib/ahoy/stores/kafka_store.rb +0 -42
  61. data/lib/ahoy/stores/kinesis_firehose_store.rb +0 -42
  62. data/lib/ahoy/stores/log_store.rb +0 -53
  63. data/lib/ahoy/stores/mongoid_store.rb +0 -63
  64. data/lib/ahoy/stores/nats_store.rb +0 -34
  65. data/lib/ahoy/stores/nsq_store.rb +0 -36
  66. data/lib/ahoy/subscribers/active_record.rb +0 -19
  67. data/lib/ahoy/throttle.rb +0 -17
  68. data/lib/generators/ahoy/stores/active_record_events_generator.rb +0 -59
  69. data/lib/generators/ahoy/stores/active_record_generator.rb +0 -16
  70. data/lib/generators/ahoy/stores/active_record_visits_generator.rb +0 -49
  71. data/lib/generators/ahoy/stores/bunny_generator.rb +0 -15
  72. data/lib/generators/ahoy/stores/custom_generator.rb +0 -15
  73. data/lib/generators/ahoy/stores/fluentd_generator.rb +0 -15
  74. data/lib/generators/ahoy/stores/kafka_generator.rb +0 -15
  75. data/lib/generators/ahoy/stores/kinesis_firehose_generator.rb +0 -15
  76. data/lib/generators/ahoy/stores/log_generator.rb +0 -15
  77. data/lib/generators/ahoy/stores/mongoid_events_generator.rb +0 -19
  78. data/lib/generators/ahoy/stores/mongoid_generator.rb +0 -14
  79. data/lib/generators/ahoy/stores/mongoid_visits_generator.rb +0 -27
  80. data/lib/generators/ahoy/stores/nats_generator.rb +0 -15
  81. data/lib/generators/ahoy/stores/nsq_generator.rb +0 -15
  82. data/lib/generators/ahoy/stores/templates/active_record_event_model.rb +0 -12
  83. data/lib/generators/ahoy/stores/templates/active_record_events_migration.rb +0 -20
  84. data/lib/generators/ahoy/stores/templates/active_record_initializer.rb +0 -3
  85. data/lib/generators/ahoy/stores/templates/active_record_visit_model.rb +0 -4
  86. data/lib/generators/ahoy/stores/templates/bunny_initializer.rb +0 -9
  87. data/lib/generators/ahoy/stores/templates/custom_initializer.rb +0 -10
  88. data/lib/generators/ahoy/stores/templates/fluentd_initializer.rb +0 -3
  89. data/lib/generators/ahoy/stores/templates/kafka_initializer.rb +0 -9
  90. data/lib/generators/ahoy/stores/templates/kinesis_firehose_initializer.rb +0 -17
  91. data/lib/generators/ahoy/stores/templates/log_initializer.rb +0 -3
  92. data/lib/generators/ahoy/stores/templates/mongoid_initializer.rb +0 -3
  93. data/lib/generators/ahoy/stores/templates/nats_initializer.rb +0 -9
  94. data/lib/generators/ahoy/stores/templates/nsq_initializer.rb +0 -9
  95. data/test/visit_properties_test.rb +0 -44
@@ -1,22 +0,0 @@
1
- module Ahoy
2
- module Deckhands
3
- class TrafficSourceDeckhand
4
- def initialize(referrer)
5
- @referrer = referrer
6
- end
7
-
8
- def referring_domain
9
- @referring_domain ||= Addressable::URI.parse(@referrer).host.first(255) rescue nil
10
- end
11
-
12
- def search_keyword
13
- @search_keyword ||= (self.class.referrer_parser.parse(@referrer)[:term][0..255] rescue nil).presence
14
- end
15
-
16
- # performance hack for referer-parser
17
- def self.referrer_parser
18
- @referrer_parser ||= RefererParser::Parser.new
19
- end
20
- end
21
- end
22
- end
@@ -1,23 +0,0 @@
1
- module Ahoy
2
- module Deckhands
3
- class UtmParameterDeckhand
4
- def initialize(landing_page, params = nil)
5
- @landing_page = landing_page
6
- @params = params || {}
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
- @params[name] || landing_params[name]
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,13 +0,0 @@
1
- module Ahoy
2
- class GeocodeJob < ActiveJob::Base
3
- queue_as :ahoy
4
-
5
- def perform(visit)
6
- deckhand = Deckhands::LocationDeckhand.new(visit.ip)
7
- Ahoy::VisitProperties::LOCATION_KEYS.each do |key|
8
- visit.send(:"#{key}=", deckhand.send(key)) if visit.respond_to?(:"#{key}=")
9
- end
10
- visit.save!
11
- end
12
- end
13
- end
@@ -1,75 +0,0 @@
1
- # from https://github.com/rails/activerecord-session_store/blob/master/lib/active_record/session_store/extension/logger_silencer.rb
2
- require "thread"
3
- require "active_support/core_ext/class/attribute_accessors"
4
- require "active_support/core_ext/module/aliasing"
5
- require "active_support/core_ext/module/attribute_accessors"
6
- require "active_support/concern"
7
-
8
- module Ahoy
9
- module LoggerSilencer
10
- extend ActiveSupport::Concern
11
-
12
- included do
13
- cattr_accessor :silencer
14
- self.silencer = true
15
- alias_method :level_without_threadsafety, :level
16
- alias_method :level, :level_with_threadsafety
17
- alias_method :add_without_threadsafety, :add
18
- alias_method :add, :add_with_threadsafety
19
- end
20
-
21
- def thread_level
22
- Thread.current[thread_hash_level_key]
23
- end
24
-
25
- def thread_level=(level)
26
- Thread.current[thread_hash_level_key] = level
27
- end
28
-
29
- def level_with_threadsafety
30
- thread_level || level_without_threadsafety
31
- end
32
-
33
- def add_with_threadsafety(severity, message = nil, progname = nil, &block)
34
- if (defined?(@logdev) && @logdev.nil?) || (severity || UNKNOWN) < level
35
- true
36
- else
37
- add_without_threadsafety(severity, message, progname, &block)
38
- end
39
- end
40
-
41
- # Silences the logger for the duration of the block.
42
- def silence_logger(temporary_level = Logger::ERROR)
43
- if silencer
44
- begin
45
- self.thread_level = temporary_level
46
- yield self
47
- ensure
48
- self.thread_level = nil
49
- end
50
- else
51
- yield self
52
- end
53
- end
54
-
55
- for severity in Logger::Severity.constants
56
- class_eval <<-EOT, __FILE__, __LINE__ + 1
57
- def #{severity.downcase}? # def debug?
58
- Logger::#{severity} >= level # DEBUG >= level
59
- end # end
60
- EOT
61
- end
62
-
63
- private
64
-
65
- def thread_hash_level_key
66
- @thread_hash_level_key ||= :"ThreadSafeLogger##{object_id}@level"
67
- end
68
- end
69
- end
70
-
71
- class NilLogger
72
- def self.silence_logger
73
- yield
74
- end
75
- end
@@ -1,61 +0,0 @@
1
- module Ahoy
2
- module Stores
3
- class ActiveRecordStore < BaseStore
4
- def track_visit(options, &block)
5
- @visit =
6
- visit_model.new do |v|
7
- v.id = ahoy.visit_id
8
- v.visitor_id = ahoy.visitor_id
9
- v.user = user if v.respond_to?(:user=)
10
- v.started_at = options[:started_at]
11
- end
12
-
13
- set_visit_properties(visit)
14
-
15
- yield(visit) if block_given?
16
-
17
- begin
18
- visit.save!
19
- geocode(visit)
20
- rescue *unique_exception_classes
21
- # reset to nil so subsequent calls to track_event will load visit from DB
22
- @visit = nil
23
- end
24
- end
25
-
26
- def track_event(name, properties, options, &block)
27
- event =
28
- event_model.new do |e|
29
- e.id = options[:id]
30
- e.visit_id = ahoy.visit_id
31
- e.user = user if e.respond_to?(:user=)
32
- e.name = name
33
- e.properties = properties
34
- e.time = options[:time]
35
- end
36
-
37
- yield(event) if block_given?
38
-
39
- begin
40
- event.save!
41
- rescue *unique_exception_classes
42
- # do nothing
43
- end
44
- end
45
-
46
- def visit
47
- @visit ||= visit_model.where(id: ahoy.visit_id).first if ahoy.visit_id
48
- end
49
-
50
- protected
51
-
52
- def visit_model
53
- ::Visit
54
- end
55
-
56
- def event_model
57
- ::Ahoy::Event
58
- end
59
- end
60
- end
61
- end
@@ -1,114 +0,0 @@
1
- module Ahoy
2
- module Stores
3
- class ActiveRecordTokenStore < BaseStore
4
- def track_visit(options, &block)
5
- @visit =
6
- visit_model.new do |v|
7
- v.visit_token = ahoy.visit_token
8
- v.visitor_token = ahoy.visitor_token
9
- v.user = user if v.respond_to?(:user=)
10
- v.started_at = options[:started_at] if v.respond_to?(:started_at)
11
- v.created_at = options[:started_at] if v.respond_to?(:created_at)
12
- end
13
-
14
- set_visit_properties(visit)
15
-
16
- yield(visit) if block_given?
17
-
18
- begin
19
- visit.save!
20
- geocode(visit)
21
- rescue *unique_exception_classes
22
- # reset to nil so subsequent calls to track_event will load visit from DB
23
- @visit = nil
24
- end
25
- end
26
-
27
- def track_event(name, properties, options, &block)
28
- if self.class.uses_deprecated_subscribers?
29
- options[:controller] ||= controller
30
- options[:user] ||= user
31
- options[:visit] ||= visit
32
- options[:visit_token] ||= ahoy.visit_token
33
- options[:visitor_token] ||= ahoy.visitor_token
34
-
35
- subscribers = Ahoy.subscribers
36
- if subscribers.any?
37
- subscribers.each do |subscriber|
38
- subscriber.track(name, properties, options.dup)
39
- end
40
- else
41
- $stderr.puts "No subscribers"
42
- end
43
- else
44
- event =
45
- event_model.new do |e|
46
- e.visit_id = visit.try(:id)
47
- e.user = user if e.respond_to?(:user=)
48
- e.name = name
49
- e.properties = properties
50
- e.time = options[:time]
51
- end
52
-
53
- yield(event) if block_given?
54
-
55
- event.save!
56
- end
57
- end
58
-
59
- def visit
60
- @visit ||= (visit_model.where(visit_token: ahoy.visit_token).first if ahoy.visit_token)
61
- end
62
-
63
- def exclude?
64
- (!Ahoy.track_bots && bot?) ||
65
- (
66
- if Ahoy.exclude_method
67
- warn "[DEPRECATION] Ahoy.exclude_method is deprecated - use exclude? instead"
68
- if Ahoy.exclude_method.arity == 1
69
- Ahoy.exclude_method.call(controller)
70
- else
71
- Ahoy.exclude_method.call(controller, request)
72
- end
73
- else
74
- false
75
- end
76
- )
77
- end
78
-
79
- def user
80
- @user ||= begin
81
- user_method = Ahoy.user_method
82
- if user_method.respond_to?(:call)
83
- user_method.call(controller)
84
- elsif user_method
85
- controller.send(user_method)
86
- else
87
- super
88
- end
89
- end
90
- end
91
-
92
- class << self
93
- def uses_deprecated_subscribers
94
- warn "[DEPRECATION] Ahoy subscribers are deprecated"
95
- @uses_deprecated_subscribers = true
96
- end
97
-
98
- def uses_deprecated_subscribers?
99
- @uses_deprecated_subscribers || false
100
- end
101
- end
102
-
103
- protected
104
-
105
- def visit_model
106
- Ahoy.visit_model || ::Visit
107
- end
108
-
109
- def event_model
110
- ::Ahoy::Event
111
- end
112
- end
113
- end
114
- end
@@ -1,88 +0,0 @@
1
- module Ahoy
2
- module Stores
3
- class BaseStore
4
- def initialize(options)
5
- @options = options
6
- end
7
-
8
- def track_visit(options)
9
- end
10
-
11
- def track_event(name, properties, options)
12
- end
13
-
14
- def visit
15
- end
16
-
17
- def authenticate(user)
18
- @user = user
19
- if visit && visit.respond_to?(:user) && !visit.user
20
- begin
21
- visit.user = user
22
- visit.save!
23
- rescue ActiveRecord::AssociationTypeMismatch
24
- # do nothing
25
- end
26
- end
27
- end
28
-
29
- def report_exception(e)
30
- raise e
31
- end
32
-
33
- def user
34
- @user ||= (controller.respond_to?(:current_user) && controller.current_user) || (controller.respond_to?(:current_resource_owner, true) && controller.send(:current_resource_owner)) || nil
35
- end
36
-
37
- def exclude?
38
- bot?
39
- end
40
-
41
- def generate_id
42
- SecureRandom.uuid
43
- end
44
-
45
- protected
46
-
47
- def bot?
48
- @bot ||= request ? Browser.new(request.user_agent).bot? : false
49
- end
50
-
51
- def request
52
- @request ||= @options[:request] || controller.try(:request)
53
- end
54
-
55
- def controller
56
- @controller ||= @options[:controller]
57
- end
58
-
59
- def ahoy
60
- @ahoy ||= @options[:ahoy]
61
- end
62
-
63
- def visit_properties
64
- ahoy.visit_properties
65
- end
66
-
67
- def set_visit_properties(visit)
68
- keys = visit_properties.keys
69
- keys.each do |key|
70
- visit.send(:"#{key}=", visit_properties[key]) if visit.respond_to?(:"#{key}=") && visit_properties[key]
71
- end
72
- end
73
-
74
- def geocode(visit)
75
- if Ahoy.geocode == :async
76
- Ahoy::GeocodeJob.set(queue: Ahoy.job_queue).perform_later(visit)
77
- end
78
- end
79
-
80
- def unique_exception_classes
81
- classes = []
82
- classes << ActiveRecord::RecordNotUnique if defined?(ActiveRecord::RecordNotUnique)
83
- classes << PG::UniqueViolation if defined?(PG::UniqueViolation)
84
- classes
85
- end
86
- end
87
- end
88
- end
@@ -1,33 +0,0 @@
1
- module Ahoy
2
- module Stores
3
- class BunnyStore < LogStore
4
- def log_visit(data)
5
- post(visits_queue, data)
6
- end
7
-
8
- def log_event(data)
9
- post(events_queue, data)
10
- end
11
-
12
- def channel
13
- @channel ||= begin
14
- conn = Bunny.new
15
- conn.start
16
- conn.create_channel
17
- end
18
- end
19
-
20
- def post(queue, message)
21
- channel.queue(queue, durable: true).publish(message.to_json)
22
- end
23
-
24
- def visits_queue
25
- "ahoy_visits"
26
- end
27
-
28
- def events_queue
29
- "ahoy_events"
30
- end
31
- end
32
- end
33
- end
@@ -1,17 +0,0 @@
1
- module Ahoy
2
- module Stores
3
- class FluentdStore < LogStore
4
- def log_visit(data)
5
- logger.post("visit", data)
6
- end
7
-
8
- def log_event(data)
9
- logger.post("event", data)
10
- end
11
-
12
- def logger
13
- @logger ||= Fluent::Logger::FluentLogger.new("ahoy", host: ENV["FLUENTD_HOST"] || "localhost", port: ENV["FLUENTD_PORT"] || 24224)
14
- end
15
- end
16
- end
17
- end