snowplow_ruby_duid 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +5 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +91 -0
- data/LICENSE.txt +9 -0
- data/README.md +35 -0
- data/lib/snowplow_ruby_duid.rb +10 -0
- data/lib/snowplow_ruby_duid/cookie.rb +58 -0
- data/lib/snowplow_ruby_duid/domain_userid.rb +23 -0
- data/lib/snowplow_ruby_duid/helper.rb +39 -0
- data/lib/snowplow_ruby_duid/version.rb +3 -0
- data/snowplow_ruby_duid.gemspec +20 -0
- data/spec/lib/snowplow_ruby_duid/cookie.feature +53 -0
- data/spec/lib/snowplow_ruby_duid/cookie_spec.rb +95 -0
- data/spec/lib/snowplow_ruby_duid/domain_userid_spec.rb +11 -0
- data/spec/lib/snowplow_ruby_duid/helper.feature +12 -0
- data/spec/lib/snowplow_ruby_duid/helper_spec.rb +81 -0
- data/spec/spec_helper.rb +92 -0
- metadata +72 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 644cd8ffeb6e17e8da58f514c6fa34926853ca3a
|
4
|
+
data.tar.gz: 7ad6e70b709b0456f5ec0ffc68e3e33937747458
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b2cd6f5d17443d916be7017483cc0ba108c2001e7bdac60fd275ee81253a93b248e77f708274efada54926498a9e85bd9333773f5475b4f8fcc38685ea8a8ff9
|
7
|
+
data.tar.gz: bc8683b0a2b9946fcf335f54cd0971f788dffd248896eff99d2e8696023ec2ff39466dac36d017a3c913c703b98f7736368868ad9fb6829c4816b8246ade30ae
|
data/.rspec
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in snowplow_ruby_duid.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development, :test do
|
7
|
+
gem 'pry-plus'
|
8
|
+
end
|
9
|
+
|
10
|
+
group :test do
|
11
|
+
gem 'rack'
|
12
|
+
gem 'rack-test'
|
13
|
+
gem 'rspec'
|
14
|
+
gem 'rutabaga'
|
15
|
+
gem 'timecop'
|
16
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
snowplow_ruby_duid (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
binding_of_caller (0.7.2)
|
10
|
+
debug_inspector (>= 0.0.1)
|
11
|
+
bond (0.5.1)
|
12
|
+
coderay (1.1.0)
|
13
|
+
columnize (0.9.0)
|
14
|
+
debug_inspector (0.0.2)
|
15
|
+
debugger (1.6.8)
|
16
|
+
columnize (>= 0.3.1)
|
17
|
+
debugger-linecache (~> 1.2.0)
|
18
|
+
debugger-ruby_core_source (~> 1.3.5)
|
19
|
+
debugger-linecache (1.2.0)
|
20
|
+
debugger-ruby_core_source (1.3.7)
|
21
|
+
diff-lcs (1.2.5)
|
22
|
+
gherkin (2.12.2)
|
23
|
+
multi_json (~> 1.3)
|
24
|
+
interception (0.5)
|
25
|
+
jist (1.5.1)
|
26
|
+
json
|
27
|
+
json (1.8.2)
|
28
|
+
method_source (0.8.2)
|
29
|
+
multi_json (1.10.1)
|
30
|
+
pry (0.10.1)
|
31
|
+
coderay (~> 1.1.0)
|
32
|
+
method_source (~> 0.8.1)
|
33
|
+
slop (~> 3.4)
|
34
|
+
pry-debugger (0.2.3)
|
35
|
+
debugger (~> 1.3)
|
36
|
+
pry (>= 0.9.10, < 0.11.0)
|
37
|
+
pry-doc (0.6.0)
|
38
|
+
pry (~> 0.9)
|
39
|
+
yard (~> 0.8)
|
40
|
+
pry-docmore (0.1.1)
|
41
|
+
pry
|
42
|
+
pry-doc
|
43
|
+
pry-plus (1.0.0)
|
44
|
+
bond
|
45
|
+
jist
|
46
|
+
pry-debugger
|
47
|
+
pry-doc
|
48
|
+
pry-docmore
|
49
|
+
pry-rescue
|
50
|
+
pry-stack_explorer
|
51
|
+
pry-rescue (1.4.1)
|
52
|
+
interception (>= 0.5)
|
53
|
+
pry
|
54
|
+
pry-stack_explorer (0.4.9.1)
|
55
|
+
binding_of_caller (>= 0.7)
|
56
|
+
pry (>= 0.9.11)
|
57
|
+
rack (1.6.0)
|
58
|
+
rack-test (0.6.2)
|
59
|
+
rack (>= 1.0)
|
60
|
+
rspec (3.1.0)
|
61
|
+
rspec-core (~> 3.1.0)
|
62
|
+
rspec-expectations (~> 3.1.0)
|
63
|
+
rspec-mocks (~> 3.1.0)
|
64
|
+
rspec-core (3.1.7)
|
65
|
+
rspec-support (~> 3.1.0)
|
66
|
+
rspec-expectations (3.1.2)
|
67
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
68
|
+
rspec-support (~> 3.1.0)
|
69
|
+
rspec-mocks (3.1.3)
|
70
|
+
rspec-support (~> 3.1.0)
|
71
|
+
rspec-support (3.1.2)
|
72
|
+
rutabaga (0.1.1)
|
73
|
+
turnip (>= 1.1.0, < 2.0)
|
74
|
+
slop (3.6.0)
|
75
|
+
timecop (0.7.1)
|
76
|
+
turnip (1.2.4)
|
77
|
+
gherkin (>= 2.5)
|
78
|
+
rspec (>= 2.14.0, < 4.0)
|
79
|
+
yard (0.8.7.6)
|
80
|
+
|
81
|
+
PLATFORMS
|
82
|
+
ruby
|
83
|
+
|
84
|
+
DEPENDENCIES
|
85
|
+
pry-plus
|
86
|
+
rack
|
87
|
+
rack-test
|
88
|
+
rspec
|
89
|
+
rutabaga
|
90
|
+
snowplow_ruby_duid!
|
91
|
+
timecop
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Copyright (c) 2015 Simply Business
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Snowplow Ruby Domain UserId
|
2
|
+
|
3
|
+
A gem that exposes the Snowplow domain userid in Rack applications. Also allows you to set your own domain userid that will be shared with the Snowplow Javascript tracker. Related Snowplow Google Group discussion [here](https://groups.google.com/forum/#!topic/snowplow-user/GFfhW25UuN8).
|
4
|
+
|
5
|
+
## Instructions
|
6
|
+
|
7
|
+
Add the gem to your Gemfile
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'snowplow_ruby_duid', git: 'git@github.com:simplybusiness/snowplow_ruby_duid'
|
11
|
+
```
|
12
|
+
|
13
|
+
### Rails
|
14
|
+
|
15
|
+
The helper will be included in `ActionController::Base` when the gem is loaded.
|
16
|
+
|
17
|
+
### Sinatra
|
18
|
+
|
19
|
+
Add the following to `Sinatra::Base`
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'snowplow_ruby_duid'
|
23
|
+
helpers SnowplowRubyDuid::Helper
|
24
|
+
```
|
25
|
+
|
26
|
+
### Usage
|
27
|
+
|
28
|
+
A method called `snowplow_domain_userid` will be exposed in your application's context. Calling this method results in the following behaviour:
|
29
|
+
|
30
|
+
- If there is already a Snowplow domain userid in the request's cookie, the value of the id will be returned.
|
31
|
+
- Else, a Snowplow domain userid will be generated and persisted in the response's cookie. This domain userid will continue to be used by the Snowplow Javascript tracker on the client side.
|
32
|
+
|
33
|
+
## Running the specs and features
|
34
|
+
|
35
|
+
Use this command to run the specs and features: `bundle exec rspec`
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require 'snowplow_ruby_duid/cookie'
|
3
|
+
require 'snowplow_ruby_duid/domain_userid'
|
4
|
+
require 'snowplow_ruby_duid/helper'
|
5
|
+
|
6
|
+
module SnowplowRubyDuid
|
7
|
+
KEY_PREFIX = '_sp_id'
|
8
|
+
end
|
9
|
+
|
10
|
+
ActionController::Base.send :include, SnowplowRubyDuid::Helper if defined?(ActionController)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module SnowplowRubyDuid
|
2
|
+
# Responsible for generating a cookie that emulates the Snowplow cookie as closely as possible
|
3
|
+
# Leverages the method used by ActionDispatch::Cookies::CookieJar to determine the top-level domain
|
4
|
+
class Cookie
|
5
|
+
|
6
|
+
# See: https://github.com/snowplow/snowplow-javascript-tracker/blob/d3d10067127eb5c95d0054c8ae60f3bdccba619d/src/js/tracker.js#L142
|
7
|
+
COOKIE_PATH = '/'
|
8
|
+
# See: https://github.com/snowplow/snowplow-javascript-tracker/blob/d3d10067127eb5c95d0054c8ae60f3bdccba619d/src/js/tracker.js#L156
|
9
|
+
COOKIE_DURATION_MONTHS = 24
|
10
|
+
# See: https://github.com/rails/rails/blob/b1124a2ac88778c0feb0157ac09367cbd204bf01/actionpack/lib/action_dispatch/middleware/cookies.rb#L214
|
11
|
+
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
12
|
+
|
13
|
+
def initialize host, domain_userid, request_created_at
|
14
|
+
@host = host
|
15
|
+
@domain_userid = domain_userid
|
16
|
+
@request_created_at = request_created_at
|
17
|
+
end
|
18
|
+
|
19
|
+
# See: https://github.com/snowplow/snowplow-javascript-tracker/blob/d3d10067127eb5c95d0054c8ae60f3bdccba619d/src/js/tracker.js#L358-L360
|
20
|
+
# See: https://github.com/snowplow/snowplow-javascript-tracker/blob/d3d10067127eb5c95d0054c8ae60f3bdccba619d/src/js/tracker.js#L372-L374
|
21
|
+
def key
|
22
|
+
domain = top_level_domain || @host
|
23
|
+
KEY_PREFIX + '.' + (Digest::SHA1.hexdigest (domain + COOKIE_PATH))[0..3]
|
24
|
+
end
|
25
|
+
|
26
|
+
def value
|
27
|
+
cookie_domain = ".#{top_level_domain}" unless top_level_domain.nil?
|
28
|
+
{
|
29
|
+
value: cookie_value,
|
30
|
+
expires: cookie_expiration,
|
31
|
+
domain: cookie_domain,
|
32
|
+
path: COOKIE_PATH,
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# See: https://github.com/rails/rails/blob/b1124a2ac88778c0feb0157ac09367cbd204bf01/actionpack/lib/action_dispatch/middleware/cookies.rb#L286-L294
|
39
|
+
def top_level_domain
|
40
|
+
if (@host !~ /^[\d.]+$/) && (@host =~ DOMAIN_REGEXP)
|
41
|
+
$&
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# See: https://github.com/snowplow/snowplow-javascript-tracker/blob/d3d10067127eb5c95d0054c8ae60f3bdccba619d/src/js/tracker.js#L476-L487
|
46
|
+
def cookie_value
|
47
|
+
visitCount = '0'
|
48
|
+
lastVisitTs = ''
|
49
|
+
createTs = nowTs = @request_created_at.to_i.to_s
|
50
|
+
[@domain_userid, createTs, visitCount, nowTs, lastVisitTs].join '.'
|
51
|
+
end
|
52
|
+
|
53
|
+
def cookie_expiration
|
54
|
+
expiration_date = @request_created_at.to_datetime >> COOKIE_DURATION_MONTHS
|
55
|
+
expiration_date.to_time
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SnowplowRubyDuid
|
2
|
+
# Generates a pseudo-unique ID to fingerprint the user
|
3
|
+
# Deviates from this Snowplow Javascript: https://github.com/snowplow/snowplow-javascript-tracker/blob/d3d10067127eb5c95d0054c8ae60f3bdccba619d/src/js/tracker.js#L468-L472
|
4
|
+
# in order to provide a more unique identifier
|
5
|
+
class DomainUserid
|
6
|
+
|
7
|
+
LENGTH_OF_DUID_IN_BYTES = 8
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@domain_user_id = domain_user_id
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
@domain_user_id
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def domain_user_id
|
20
|
+
SecureRandom.hex LENGTH_OF_DUID_IN_BYTES
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SnowplowRubyDuid
|
2
|
+
# Exposes a snowplow_domain_userid method in the context
|
3
|
+
# that will find or create a domain_userid, which will be
|
4
|
+
# saved in the response's cookie if it does not exist
|
5
|
+
module Helper
|
6
|
+
|
7
|
+
def snowplow_domain_userid
|
8
|
+
@snowplow_domain_userid ||= find_or_create_snowplow_domain_userid
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_or_create_snowplow_domain_userid
|
12
|
+
find_snowplow_domain_userid || create_snowplow_domain_userid
|
13
|
+
end
|
14
|
+
private :find_or_create_snowplow_domain_userid
|
15
|
+
|
16
|
+
def create_snowplow_domain_userid
|
17
|
+
request_created_at = Time.now
|
18
|
+
domain_userid = DomainUserid.new.to_s
|
19
|
+
snowplow_cookie = Cookie.new request.host, domain_userid, request_created_at
|
20
|
+
|
21
|
+
response.set_cookie snowplow_cookie.key, snowplow_cookie.value
|
22
|
+
domain_userid
|
23
|
+
end
|
24
|
+
private :create_snowplow_domain_userid
|
25
|
+
|
26
|
+
# See: https://github.com/snowplow/snowplow/wiki/Ruby-Tracker#310-setting-the-domain-user-id-with-set_domain_user_id
|
27
|
+
def find_snowplow_domain_userid
|
28
|
+
snowplow_cookie = find_snowplow_cookie
|
29
|
+
# The cookie value is in this format: domainUserId.createTs.visitCount.nowTs.lastVisitTs
|
30
|
+
snowplow_cookie.last.split('.').first unless snowplow_cookie.nil?
|
31
|
+
end
|
32
|
+
private :find_snowplow_domain_userid
|
33
|
+
|
34
|
+
def find_snowplow_cookie
|
35
|
+
request.cookies.find { |(key, value)| key =~ /^#{KEY_PREFIX}/ } # result will be an array containing: [key, value]
|
36
|
+
end
|
37
|
+
private :find_snowplow_cookie
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'snowplow_ruby_duid/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'snowplow_ruby_duid'
|
8
|
+
spec.version = SnowplowRubyDuid::VERSION
|
9
|
+
spec.authors = ['Simply Business']
|
10
|
+
spec.email = ['tech@simplybusiness.co.uk']
|
11
|
+
spec.description = %q{A gem that exposes the Snowplow domain userid in Rack applications. Also allows you to set your own domain userid that will be shared with the Snowplow Javascript tracker.}
|
12
|
+
spec.summary = %q{A gem that exposes the Snowplow domain userid in Rack applications. Also allows you to set your own domain userid that will be shared with the Snowplow Javascript tracker.}
|
13
|
+
spec.homepage = 'https://github.com/simplybusiness/snowplow_ruby_duid'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
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
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
Feature: Creating a Snowplow cookie
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given the host is 'www.simplybusiness.co.uk'
|
5
|
+
And the domain_userid is 'domain_user_id'
|
6
|
+
And the time is '2015-01-22 15:26:31 +0000'
|
7
|
+
|
8
|
+
Scenario: The cookie should apply to the root path (/)
|
9
|
+
When I create a Snowplow cookie
|
10
|
+
Then the cookie has the path '/'
|
11
|
+
|
12
|
+
Scenario Outline: The cookie should apply for all domains under the top-level domain (no domain for localhost or IP addresses, however)
|
13
|
+
Given the host is '<host>'
|
14
|
+
When I create a Snowplow cookie
|
15
|
+
Then the cookie has the domain '<cookie domain>'
|
16
|
+
|
17
|
+
Examples:
|
18
|
+
| host | cookie domain |
|
19
|
+
| localhost | |
|
20
|
+
| 127.0.0.1 | |
|
21
|
+
| 192.168.2.40 | |
|
22
|
+
| www.simplybusiness.co.uk | .simplybusiness.co.uk |
|
23
|
+
| www.quote.simplybusiness.co.uk | .simplybusiness.co.uk |
|
24
|
+
| simplybusiness.co.uk | .simplybusiness.co.uk |
|
25
|
+
| www.simplybusiness.com | .simplybusiness.com |
|
26
|
+
|
27
|
+
Scenario Outline: The cookie should be named using the _sp_id prefix, followed by a 4 character hash generated from the top-level domain and root path
|
28
|
+
Given the host is '<host>'
|
29
|
+
When I create a Snowplow cookie
|
30
|
+
Then the cookie has the name '<cookie name>'
|
31
|
+
|
32
|
+
Examples:
|
33
|
+
| host | cookie name |
|
34
|
+
| localhost | _sp_id.1fff |
|
35
|
+
| 127.0.0.1 | _sp_id.dc78 |
|
36
|
+
| 192.168.2.40 | _sp_id.f0ae |
|
37
|
+
| www.simplybusiness.co.uk | _sp_id.8fb9 |
|
38
|
+
| www.quote.simplybusiness.co.uk | _sp_id.8fb9 |
|
39
|
+
| simplybusiness.co.uk | _sp_id.8fb9 |
|
40
|
+
| www.simplybusiness.com | _sp_id.bdbc |
|
41
|
+
|
42
|
+
Scenario: The cookie should expire after 2 years
|
43
|
+
When I create a Snowplow cookie
|
44
|
+
Then the cookie expires at '2017-01-22 15:26:31 +0000'
|
45
|
+
|
46
|
+
Scenario: The cookie's value should be in the following format: domain_userid.createTs.visitCount.nowTs.lastVisitTs
|
47
|
+
When I create a Snowplow cookie
|
48
|
+
Then the cookie value is 'domain_user_id.1421940391.0.1421940391.'
|
49
|
+
And the cookie value for 'domain_userid' is 'domain_user_id'
|
50
|
+
And the cookie value for 'createTs' is '1421940391'
|
51
|
+
And the cookie value for 'visitCount' is '0'
|
52
|
+
And the cookie value for 'nowTs' is '1421940391'
|
53
|
+
And the cookie value for 'lastVisitTs' is ''
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module SnowplowRubyDuid
|
2
|
+
describe Cookie do
|
3
|
+
before do
|
4
|
+
@host = 'www.simplybusiness.co.uk'
|
5
|
+
@domain_userid = 'domain_user_id'
|
6
|
+
@request_created_at = (DateTime.parse '2015-01-22 15:26:31 +0000').to_time
|
7
|
+
end
|
8
|
+
|
9
|
+
subject { described_class.new @host, @domain_userid, @request_created_at }
|
10
|
+
|
11
|
+
describe '#key' do
|
12
|
+
|
13
|
+
it 'generates the key' do
|
14
|
+
expect(subject.key).to eq('_sp_id.8fb9')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#value' do
|
19
|
+
|
20
|
+
it 'generates a cookie' do
|
21
|
+
expect(subject.value).to eq(
|
22
|
+
{
|
23
|
+
domain: '.simplybusiness.co.uk',
|
24
|
+
expires: (DateTime.parse '2017-01-22 15:26:31 +0000').to_time,
|
25
|
+
path: '/',
|
26
|
+
value: 'domain_user_id.1421940391.0.1421940391.'
|
27
|
+
})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'runs the feature' do
|
32
|
+
feature
|
33
|
+
end
|
34
|
+
|
35
|
+
step 'the host is :host' do |host|
|
36
|
+
@host = host
|
37
|
+
end
|
38
|
+
|
39
|
+
step 'the domain_userid is :domain_userid' do |domain_userid|
|
40
|
+
@domain_userid = domain_userid
|
41
|
+
end
|
42
|
+
|
43
|
+
step 'the time is :time' do |time|
|
44
|
+
@request_created_at = (DateTime.parse time).to_time
|
45
|
+
end
|
46
|
+
|
47
|
+
step 'I create a Snowplow cookie' do; end
|
48
|
+
|
49
|
+
step 'the cookie has the path :path' do |path|
|
50
|
+
expect(subject.value[:path]).to eq(path)
|
51
|
+
end
|
52
|
+
|
53
|
+
step 'the cookie has the domain :domain' do |domain|
|
54
|
+
if domain == ''
|
55
|
+
expect(subject.value[:domain]).to be_nil
|
56
|
+
else
|
57
|
+
expect(subject.value[:domain]).to eq(domain)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
step 'the cookie has the name :name' do |name|
|
62
|
+
expect(subject.key).to eq(name)
|
63
|
+
end
|
64
|
+
|
65
|
+
step 'the cookie expires at :time' do |time|
|
66
|
+
expect(subject.value[:expires].to_s).to eq(time)
|
67
|
+
end
|
68
|
+
|
69
|
+
step 'the cookie value is :value' do |value|
|
70
|
+
expect(subject.value[:value]).to eq(value)
|
71
|
+
end
|
72
|
+
|
73
|
+
step 'the cookie value for :field is :value' do |field, value|
|
74
|
+
value_index = case field
|
75
|
+
when 'domain_userid'
|
76
|
+
then 0
|
77
|
+
when 'createTs'
|
78
|
+
then 1
|
79
|
+
when 'visitCount'
|
80
|
+
then 2
|
81
|
+
when 'nowTs'
|
82
|
+
then 3
|
83
|
+
when 'lastVisitTs'
|
84
|
+
then 4
|
85
|
+
else
|
86
|
+
raise "unknown field name: #{field}"
|
87
|
+
end
|
88
|
+
|
89
|
+
value = nil if value == ''
|
90
|
+
|
91
|
+
values = subject.value[:value].split '.'
|
92
|
+
expect(values[value_index]).to eq(value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Feature: Helper method for accessing and saving a Snowplow domain userid
|
2
|
+
|
3
|
+
Scenario Outline: The helper will return the Snowplow domain userid from the cookie, if it is available. If it is not available, it will generate a domain userid and set it in the cookie.
|
4
|
+
Given I set a Snowplow domain userid of '<original domain userid in cookie>' in my cookie
|
5
|
+
When I request the Snowplow domain userid
|
6
|
+
Then I receive the Snowplow domain userid '<domain userid>'
|
7
|
+
And I have the Snowplow domain userid '<current domain userid in cookie>' in my cookie
|
8
|
+
|
9
|
+
Examples:
|
10
|
+
| original domain userid in cookie | domain userid | current domain userid in cookie |
|
11
|
+
| 1111111111111111 | 1111111111111111 | 1111111111111111 |
|
12
|
+
| | 2222222222222222 | 2222222222222222 |
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rack/test'
|
3
|
+
require 'timecop'
|
4
|
+
|
5
|
+
module SnowplowRubyDuid
|
6
|
+
describe Helper do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
describe 'snowplow_domain_userid' do
|
10
|
+
let(:app) { App.new }
|
11
|
+
before do
|
12
|
+
Timecop.freeze(Time.local(1990))
|
13
|
+
end
|
14
|
+
after do
|
15
|
+
Timecop.return
|
16
|
+
end
|
17
|
+
subject { last_response.header['Set-Cookie'] }
|
18
|
+
|
19
|
+
it 'runs the feature' do
|
20
|
+
feature
|
21
|
+
end
|
22
|
+
|
23
|
+
step 'I set a Snowplow domain userid of :domain_userid in my cookie' do |domain_userid|
|
24
|
+
set_cookie("_sp_id.3678=#{domain_userid}.631152000.0.631152000.631152000") unless domain_userid == ''
|
25
|
+
end
|
26
|
+
|
27
|
+
step 'I request the Snowplow domain userid' do
|
28
|
+
allow_any_instance_of(DomainUserid).to receive(:to_s).and_return('2222222222222222')
|
29
|
+
get '/'
|
30
|
+
end
|
31
|
+
|
32
|
+
step 'I receive the Snowplow domain userid :domain_userid' do |domain_userid|
|
33
|
+
expect(app.domain_userid).to eq(domain_userid)
|
34
|
+
end
|
35
|
+
|
36
|
+
step 'I have the Snowplow domain userid :domain_userid in my cookie' do |domain_userid|
|
37
|
+
expect(subject).to include(domain_userid)
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when there is an existing snowplow cookie' do
|
41
|
+
before do
|
42
|
+
set_cookie('_sp_id.3678=111111111111.631152000.0.631152000.631152000')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'retrieves the domain userid from the cookie' do
|
46
|
+
get '/'
|
47
|
+
expect(app.domain_userid).to eq('111111111111')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does not modify the domain userid' do
|
51
|
+
get '/'
|
52
|
+
expect(subject).to include('111111111111')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when there is not an existing snowplow cookie' do
|
57
|
+
|
58
|
+
it 'generates a domain userid and saves it in the response cookie' do
|
59
|
+
get '/'
|
60
|
+
expect(subject).to include(app.domain_userid)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class App
|
68
|
+
include SnowplowRubyDuid::Helper
|
69
|
+
|
70
|
+
attr_reader :request, :response, :domain_userid
|
71
|
+
|
72
|
+
def call env
|
73
|
+
@request = Rack::Request.new env
|
74
|
+
@response = Rack::Response.new
|
75
|
+
request.cookies.each{|k,v| response.set_cookie k, v }
|
76
|
+
|
77
|
+
@domain_userid = snowplow_domain_userid
|
78
|
+
|
79
|
+
[response.status, response.header, response.body]
|
80
|
+
end
|
81
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'snowplow_ruby_duid'
|
3
|
+
|
4
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
5
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
6
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
7
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
8
|
+
#
|
9
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
10
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
11
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
12
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
13
|
+
# a separate helper file that requires the additional dependencies and performs
|
14
|
+
# the additional setup, and require it from the spec files that actually need it.
|
15
|
+
#
|
16
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
17
|
+
# users commonly want.
|
18
|
+
#
|
19
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
20
|
+
RSpec.configure do |config|
|
21
|
+
# rspec-expectations config goes here. You can use an alternate
|
22
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
23
|
+
# assertions if you prefer.
|
24
|
+
config.expect_with :rspec do |expectations|
|
25
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
26
|
+
# and `failure_message` of custom matchers include text for helper methods
|
27
|
+
# defined using `chain`, e.g.:
|
28
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
29
|
+
# # => "be bigger than 2 and smaller than 4"
|
30
|
+
# ...rather than:
|
31
|
+
# # => "be bigger than 2"
|
32
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
33
|
+
end
|
34
|
+
|
35
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
36
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
37
|
+
config.mock_with :rspec do |mocks|
|
38
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
39
|
+
# a real object. This is generally recommended, and will default to
|
40
|
+
# `true` in RSpec 4.
|
41
|
+
mocks.verify_partial_doubles = true
|
42
|
+
end
|
43
|
+
|
44
|
+
# The settings below are suggested to provide a good initial experience
|
45
|
+
# with RSpec, but feel free to customize to your heart's content.
|
46
|
+
=begin
|
47
|
+
# These two settings work together to allow you to limit a spec run
|
48
|
+
# to individual examples or groups you care about by tagging them with
|
49
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
50
|
+
# get run.
|
51
|
+
config.filter_run :focus
|
52
|
+
config.run_all_when_everything_filtered = true
|
53
|
+
|
54
|
+
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
55
|
+
# For more details, see:
|
56
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
57
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
58
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
59
|
+
config.disable_monkey_patching!
|
60
|
+
|
61
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
62
|
+
# be too noisy due to issues in dependencies.
|
63
|
+
config.warnings = true
|
64
|
+
|
65
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
66
|
+
# file, and it's useful to allow more verbose output when running an
|
67
|
+
# individual spec file.
|
68
|
+
if config.files_to_run.one?
|
69
|
+
# Use the documentation formatter for detailed output,
|
70
|
+
# unless a formatter has already been configured
|
71
|
+
# (e.g. via a command-line flag).
|
72
|
+
config.default_formatter = 'doc'
|
73
|
+
end
|
74
|
+
|
75
|
+
# Print the 10 slowest examples and example groups at the
|
76
|
+
# end of the spec run, to help surface which specs are running
|
77
|
+
# particularly slow.
|
78
|
+
config.profile_examples = 10
|
79
|
+
|
80
|
+
# Run specs in random order to surface order dependencies. If you find an
|
81
|
+
# order dependency and want to debug it, you can fix the order by providing
|
82
|
+
# the seed, which is printed after each run.
|
83
|
+
# --seed 1234
|
84
|
+
config.order = :random
|
85
|
+
|
86
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
87
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
88
|
+
# test failures related to randomization by passing the same `--seed` value
|
89
|
+
# as the one that triggered the failure.
|
90
|
+
Kernel.srand config.seed
|
91
|
+
=end
|
92
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: snowplow_ruby_duid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Simply Business
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-28 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A gem that exposes the Snowplow domain userid in Rack applications. Also
|
14
|
+
allows you to set your own domain userid that will be shared with the Snowplow Javascript
|
15
|
+
tracker.
|
16
|
+
email:
|
17
|
+
- tech@simplybusiness.co.uk
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- ".rspec"
|
23
|
+
- Gemfile
|
24
|
+
- Gemfile.lock
|
25
|
+
- LICENSE.txt
|
26
|
+
- README.md
|
27
|
+
- lib/snowplow_ruby_duid.rb
|
28
|
+
- lib/snowplow_ruby_duid/cookie.rb
|
29
|
+
- lib/snowplow_ruby_duid/domain_userid.rb
|
30
|
+
- lib/snowplow_ruby_duid/helper.rb
|
31
|
+
- lib/snowplow_ruby_duid/version.rb
|
32
|
+
- snowplow_ruby_duid.gemspec
|
33
|
+
- spec/lib/snowplow_ruby_duid/cookie.feature
|
34
|
+
- spec/lib/snowplow_ruby_duid/cookie_spec.rb
|
35
|
+
- spec/lib/snowplow_ruby_duid/domain_userid_spec.rb
|
36
|
+
- spec/lib/snowplow_ruby_duid/helper.feature
|
37
|
+
- spec/lib/snowplow_ruby_duid/helper_spec.rb
|
38
|
+
- spec/spec_helper.rb
|
39
|
+
homepage: https://github.com/simplybusiness/snowplow_ruby_duid
|
40
|
+
licenses:
|
41
|
+
- MIT
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.0.14
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: A gem that exposes the Snowplow domain userid in Rack applications. Also
|
63
|
+
allows you to set your own domain userid that will be shared with the Snowplow Javascript
|
64
|
+
tracker.
|
65
|
+
test_files:
|
66
|
+
- spec/lib/snowplow_ruby_duid/cookie.feature
|
67
|
+
- spec/lib/snowplow_ruby_duid/cookie_spec.rb
|
68
|
+
- spec/lib/snowplow_ruby_duid/domain_userid_spec.rb
|
69
|
+
- spec/lib/snowplow_ruby_duid/helper.feature
|
70
|
+
- spec/lib/snowplow_ruby_duid/helper_spec.rb
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
has_rdoc:
|