touchpoints 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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +36 -0
- data/Rakefile +6 -0
- data/app/controllers/concerns/touchpoints/tracker.rb +86 -0
- data/bin/rails +12 -0
- data/bin/setup +8 -0
- data/config/routes.rb +2 -0
- data/lib/touchpoints.rb +34 -0
- data/lib/touchpoints/engine.rb +11 -0
- data/lib/touchpoints/version.rb +3 -0
- data/touchpoints.gemspec +30 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 90c969ef338bf37fb2169bcea9077339a1ff1302567562c59c63ddf2f6622621
|
4
|
+
data.tar.gz: c80e1052ce904c814aa6a52e3afd3ff8c7b057eeba8fbe42d0971c368d0a50a5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e2b35d93675d8dfa395e747a5614c1148f3164d720ba94b76ac4c0e0a059e353afeb3169f3bed29c593ac0911c3682a95aff69c1908889e0e285ef9dce37b4f3
|
7
|
+
data.tar.gz: 28f295e6a06ff37c6ab5c6cfafd4b42555101ba36749705601ca358d08eadc345cb09c67b4492a9e5de29f4eb16e42582277387c91e8e3fcf453bcd419af48f9
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Daniel Cruz Horts
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
A channel is a source of traffic to our application. It could be:
|
2
|
+
- direct: users enter our bare URL in their browser. No referrer.
|
3
|
+
- organic: users click on a link on a search engine. Referrer is the search engine URL.
|
4
|
+
- marketing: users click on a link of an affiliate or paid search result. UTM params are usually set. Referrer is the affiliate URL.
|
5
|
+
- campaign: users click on a link of an email/site or enter a URL. UTM params are usually set. Referrer may not.
|
6
|
+
|
7
|
+
A touchpoint is a visit from the outside world, to our website or set of websites under the same domain.
|
8
|
+
|
9
|
+
Attribution is the process of assigning a channel to a customer, so we can know what
|
10
|
+
channel we'd better put our money on.
|
11
|
+
|
12
|
+
Usually, the attributed channel is the first touchpoint found 1 month after the user signed up (or any other event we decide).
|
13
|
+
|
14
|
+
```
|
15
|
+
bin/rails generate migration CreateTouchpoints user_entity_id:uuid utm_params:jsonb referer:string created_at:timestamp
|
16
|
+
```
|
17
|
+
|
18
|
+
If we have several hosts with same domain operating as a whole, we must share the session cookie between them. In your `config/initializers/session_store.rb`:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
Rails.application.config.session_store :cookie_store, key: '_creditspring_session', domain: ENV.fetch('DOMAIN', 'localhost')
|
22
|
+
```
|
23
|
+
|
24
|
+
In your ApplicationController:
|
25
|
+
```ruby
|
26
|
+
include Touchpoints::Tracker
|
27
|
+
```
|
28
|
+
|
29
|
+
Configure the gem, in `config/initializers/touchpoints.rb`:
|
30
|
+
```ruby
|
31
|
+
Touchpoints.configure do |config|
|
32
|
+
config.set :logging, true
|
33
|
+
config.set :model_id, :entity_id
|
34
|
+
config.set :model_foreign_id, :user_entity_id
|
35
|
+
end
|
36
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
module Touchpoints
|
2
|
+
module Tracker
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
before_action :_track_touchpoints
|
7
|
+
end
|
8
|
+
|
9
|
+
def _track_touchpoints
|
10
|
+
touchpoints = Array(session[get(:session_name)])
|
11
|
+
.then(&method(:keep_only_recent))
|
12
|
+
.then(&method(:add_if_different))
|
13
|
+
.then(&method(:persist_if_logged_in))
|
14
|
+
|
15
|
+
Touchpoints.debug("Touchpoints: #{touchpoints.inspect}")
|
16
|
+
|
17
|
+
session[get(:session_name)] = touchpoints.last(get(:capacity))
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def domain_from(string) # TODO: come up with a more resilient way of extracting the domain
|
23
|
+
uri = URI.parse string
|
24
|
+
return unless uri.host
|
25
|
+
|
26
|
+
uri.host.split('.').reverse[0..2].reverse.join('.')
|
27
|
+
end
|
28
|
+
|
29
|
+
def keep_only_recent(touchpoints)
|
30
|
+
touchpoints.select { |touchpoint| touchpoint['created_at'] > 60.days.ago }
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_if_different(touchpoints)
|
34
|
+
return touchpoints if request.domain == domain_from(request.referer.to_s)
|
35
|
+
|
36
|
+
last_touchpoint = Hash(touchpoints.last)
|
37
|
+
utm_params = params.permit(*get(:utm_params)).to_h
|
38
|
+
|
39
|
+
new_touchpoint = { 'utm_params' => utm_params, 'referer' => request.referer, 'created_at' => Time.current }
|
40
|
+
|
41
|
+
Touchpoints.debug("Touchpoint (new): #{new_touchpoint.inspect}")
|
42
|
+
Touchpoints.debug("Touchpoint (last): #{last_touchpoint.inspect}")
|
43
|
+
|
44
|
+
if !equivalent_touchpoints?(new_touchpoint, last_touchpoint)
|
45
|
+
touchpoints << new_touchpoint
|
46
|
+
Touchpoints.debug('Touchpoint noted!')
|
47
|
+
end
|
48
|
+
|
49
|
+
touchpoints
|
50
|
+
end
|
51
|
+
|
52
|
+
def persist_if_logged_in(touchpoints)
|
53
|
+
return touchpoints unless logged_in?
|
54
|
+
|
55
|
+
last_touchpoint_persisted = get(:model).constantize.where(get(:model_foreign_id) => user_id).last
|
56
|
+
last_touchpoint_attributes = last_touchpoint_persisted ? last_touchpoint_persisted.attributes.slice('utm_params', 'referer') : {}
|
57
|
+
|
58
|
+
touchpoints.each do |touchpoint|
|
59
|
+
next if equivalent_touchpoints?(touchpoint, last_touchpoint_attributes)
|
60
|
+
|
61
|
+
touchpoint[get(:model_foreign_id)] = user_id if logged_in?
|
62
|
+
get(:model).constantize.new(touchpoint).save
|
63
|
+
Touchpoints.debug('Touchpoint persisted!')
|
64
|
+
last_touchpoint_persisted = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
[]
|
68
|
+
end
|
69
|
+
|
70
|
+
def logged_in?
|
71
|
+
respond_to?(get(:current_user_method)) && send(get(:current_user_method)).present?
|
72
|
+
end
|
73
|
+
|
74
|
+
def user_id
|
75
|
+
send(get(:current_user_method)).send(get(:model_id))
|
76
|
+
end
|
77
|
+
|
78
|
+
def equivalent_touchpoints?(a, b)
|
79
|
+
a['utm_params'] == b['utm_params'] && a['referer'] == b['referer']
|
80
|
+
end
|
81
|
+
|
82
|
+
def get(option)
|
83
|
+
Touchpoints.get(option)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/bin/rails
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
|
3
|
+
|
4
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
5
|
+
ENGINE_PATH = File.expand_path('../../lib/sal/engine', __FILE__)
|
6
|
+
|
7
|
+
# Set up gems listed in the Gemfile.
|
8
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
9
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
10
|
+
|
11
|
+
require 'rails/all'
|
12
|
+
require 'rails/engine/commands'
|
data/bin/setup
ADDED
data/config/routes.rb
ADDED
data/lib/touchpoints.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "touchpoints/engine"
|
2
|
+
|
3
|
+
module Touchpoints
|
4
|
+
@@configuration = {
|
5
|
+
session_name: '_touchpoints'.freeze,
|
6
|
+
utm_params: %w(utm_source utm_medium utm_campaign utm_term utm_content utm_uid).freeze,
|
7
|
+
logging: false,
|
8
|
+
model: 'Touchpoint'.freeze,
|
9
|
+
model_id: :id,
|
10
|
+
model_foreign_id: :user_id,
|
11
|
+
current_user_method: :current_user,
|
12
|
+
capacity: 22,
|
13
|
+
}
|
14
|
+
|
15
|
+
def self.configure
|
16
|
+
yield Touchpoints
|
17
|
+
|
18
|
+
debug "Touchpoint configuration: #{@@configuration.inspect}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.set(option, value)
|
22
|
+
@@configuration[option] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.get(option)
|
26
|
+
@@configuration[option]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.debug(message)
|
30
|
+
return unless get(:logging)
|
31
|
+
|
32
|
+
puts message
|
33
|
+
end
|
34
|
+
end
|
data/touchpoints.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "touchpoints/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "touchpoints"
|
8
|
+
spec.version = Touchpoints::VERSION
|
9
|
+
spec.authors = ['Daniel Cruz Horts']
|
10
|
+
|
11
|
+
spec.summary = %q{Track touchpoints.}
|
12
|
+
spec.homepage = 'https://github.com/dncrht/touchpoints'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
# Specify which files should be added to the gem when it is released.
|
16
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
17
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency 'rails', '>= 4'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'pry'
|
27
|
+
spec.add_development_dependency 'pry-rails'
|
28
|
+
spec.add_development_dependency 'pry-byebug'
|
29
|
+
spec.add_development_dependency 'rspec-rails'
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: touchpoints
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Cruz Horts
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
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: pry-byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-rails
|
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:
|
84
|
+
email:
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- ".gitignore"
|
90
|
+
- ".rspec"
|
91
|
+
- Gemfile
|
92
|
+
- LICENSE.txt
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- app/controllers/concerns/touchpoints/tracker.rb
|
96
|
+
- bin/rails
|
97
|
+
- bin/setup
|
98
|
+
- config/routes.rb
|
99
|
+
- lib/touchpoints.rb
|
100
|
+
- lib/touchpoints/engine.rb
|
101
|
+
- lib/touchpoints/version.rb
|
102
|
+
- touchpoints.gemspec
|
103
|
+
homepage: https://github.com/dncrht/touchpoints
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubygems_version: 3.0.3
|
123
|
+
signing_key:
|
124
|
+
specification_version: 4
|
125
|
+
summary: Track touchpoints.
|
126
|
+
test_files: []
|