source_track 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.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rbenv-version
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in source_track.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Bryan Rehbein
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # SourceTrack
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'source_track'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install source_track
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :default => :spec
8
+ task :test => :spec
@@ -0,0 +1,34 @@
1
+ require 'source_track/version'
2
+ require 'source_track/configuration'
3
+ require 'source_track/token_parser'
4
+
5
+ require 'source_track/railtie' if defined?(Rails::Railtie)
6
+
7
+ module SourceTrack
8
+ class << self
9
+
10
+ # Configuration object. Must act like a hash and return sensible
11
+ # values for all configuration options.
12
+ attr_writer :configuration
13
+
14
+ # Call this method to modify defaults in your initializers.
15
+ #
16
+ # @example
17
+ # SourceTrack.configure do |config|
18
+ # config.use_dates = true
19
+ # config.signed_cookies = false
20
+ # end
21
+ def configure(silent = false)
22
+ yield(configuration)
23
+ end
24
+
25
+ def configuration
26
+ @configuration ||= Configuration.new
27
+ end
28
+
29
+ def parser
30
+ @parser ||= TokenParser.new
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,76 @@
1
+ require 'date'
2
+
3
+ module SourceTrack
4
+ # Used to set up and modify settings for the notifier.
5
+ class Configuration
6
+
7
+ DEFAULT_SEPARATOR = "|"
8
+ DEFAULT_EPOCH = Date.new(2012,1,1)
9
+ DEFAULT_DATE_LENGTH = 4
10
+ OPTIONS = [:use_dates, :separator, :epoch, :date_length, :cookie_name, :http_only_cookies,
11
+ :cookie_path, :environment, :app_root, :logger].freeze
12
+
13
+ # Add a date component to the source token stored
14
+ attr_writer :use_dates
15
+
16
+ # Separator (can't be 0-9 A-F or other undesireables in cookies), default to pipe (|)
17
+ attr_writer :separator
18
+
19
+
20
+ # Epoch date to use for hexdates
21
+ attr_accessor :epoch
22
+
23
+ # Length of hexdate (defaults to 4)
24
+ attr_accessor :date_length
25
+
26
+ # HTTP cookies
27
+ attr_accessor :cookie_name
28
+ attr_accessor :http_only_cookies
29
+ attr_accessor :cookie_path
30
+
31
+ # Application specifics
32
+ attr_accessor :environment
33
+ attr_accessor :app_root
34
+ attr_accessor :logger
35
+
36
+ def initialize
37
+ @epoch = DEFAULT_EPOCH
38
+ @date_length = DEFAULT_DATE_LENGTH
39
+ @cookie_name = 'sct'
40
+ @http_only_cookies = true
41
+ @cookie_path = '/'
42
+ end
43
+
44
+ # Track data along with the source token
45
+ def use_dates
46
+ @use_dates || true
47
+ end
48
+ alias_method :use_dates?, :use_dates
49
+
50
+ def separator
51
+ @separator || DEFAULT_SEPARATOR
52
+ end
53
+
54
+ # Allows config options to be read like a hash
55
+ #
56
+ # @param [Symbol] option Key for a given attribute
57
+ def [](option)
58
+ send(option)
59
+ end
60
+
61
+ # Returns a hash of all configurable options
62
+ def to_hash
63
+ OPTIONS.inject({}) do |hash, option|
64
+ hash[option.to_sym] = self.send(option)
65
+ hash
66
+ end
67
+ end
68
+
69
+ # Returns a hash of all configurable options merged with +hash+
70
+ #
71
+ # @param [Hash] hash A set of configuration options that will take precedence over the defaults
72
+ def merge(hash)
73
+ to_hash.merge(hash)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,26 @@
1
+ require 'source_track'
2
+ require 'source_track/rails/controller_methods'
3
+
4
+ module SourceTrack
5
+ module Rails
6
+
7
+ def self.initialize
8
+ if defined?(ActionController::Base)
9
+ ActionController::Base.send(:include, SourceTrack::Rails::ControllerMethods)
10
+ end
11
+
12
+ track_logger = if defined?(::Rails.logger)
13
+ ::Rails.logger
14
+ elsif defined?(RAILS_DEFAULT_LOGGER)
15
+ RAILS_DEFAULT_LOGGER
16
+ end
17
+
18
+ SourceTrack.configure(true) do |config|
19
+ config.logger = track_logger
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+
26
+ SourceTrack::Rails.initialize
@@ -0,0 +1,51 @@
1
+ module SourceTrack
2
+ module Rails
3
+ module ControllerMethods
4
+
5
+ # TODO: It would be nice on page load (or before filter) to load an array of tokens accessible
6
+ # inside the controller (maybe with extra methods like has_token?)
7
+ # it would get parsed at the start of the request cycle (in the controller), any modifications would be
8
+ # stored in an instance variable (i.e. track, clear, etc), state of dirty? would become true, then
9
+ # when response is being generated, set the cookie if dirty? is true
10
+
11
+ # it would look similar to this (we would send a cookie with A9212 source token with today's date)
12
+ # before_filter :scene_track
13
+ # def show
14
+ # track_source("A9212")
15
+ # end
16
+ #
17
+ # def signup
18
+ # @user.sources = source_tokens
19
+ # @user.super_important_person = true if source_tokens.has_source("VIP") (should we also be able to filter by date?)
20
+ # @user.save!
21
+ # source_tokens.clear
22
+ # redirect root_url
23
+ # end
24
+
25
+ # TODO - make sure the cookie is a far future expires
26
+ def track_source(token, options = {})
27
+ tokens = source_tokens
28
+ tokens << {:token => token, :date => Date.today}
29
+ cookies[SourceTrack.configuration.cookie_name] = {
30
+ :value => SourceTrack.parser.encode(tokens),
31
+ :http_only => SourceTrack.configuration.http_only_cookies,
32
+ :expires => 10.years.from_now
33
+ }
34
+ end
35
+
36
+ def source_tokens
37
+ @tokens ||= SourceTrack.parser.parse(cookies[SourceTrack.configuration.cookie_name])
38
+ end
39
+
40
+ def clear_tokens
41
+ cookies.delete SourceTrack.configuration.cookie_name
42
+ end
43
+
44
+ def has_source(token)
45
+ source_tokens.map{|m| m[:token]}.include?(token)
46
+ end
47
+ alias_method :has_source?, :has_source
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,25 @@
1
+ require 'airbrake'
2
+ require 'rails'
3
+
4
+ module SourceTrack
5
+ class Railtie < ::Rails::Railtie
6
+ rake_tasks do
7
+ #require 'source_track/rails/rake_tasks'
8
+ end
9
+
10
+ config.after_initialize do
11
+ SourceTrack.configure(true) do |config|
12
+ config.logger ||= ::Rails.logger
13
+ config.environment ||= ::Rails.env
14
+ config.app_root ||= ::Rails.root
15
+ end
16
+
17
+ ActiveSupport.on_load(:action_controller) do
18
+ # Lazily load action_controller methods
19
+ #
20
+ require 'source_track/rails/controller_methods'
21
+ include SourceTrack::Rails::ControllerMethods
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,70 @@
1
+ require 'date'
2
+ require 'open-uri'
3
+
4
+ module SourceTrack
5
+ class TokenParser
6
+
7
+ # Take the string from the cookie and return the source tokens (token and optional date)
8
+ # ABC12300FA|CODE2300FB|CD2380 #( with dates, last 4 characters are hex )
9
+ # ABBC123|CODE23|CD #( no dates )
10
+ # we MUST preserve order
11
+ def parse(v)
12
+ return [] if v.nil? || v.strip.empty?
13
+
14
+ tokens = []
15
+ v.split(config.separator).each do |t|
16
+ # validate the token is valid
17
+
18
+ if config.use_dates?
19
+ token, token_date = split_token_date(t, config.date_length)
20
+ tokens << {:token => token, :date => get_date(token_date, config.epoch)}
21
+ else
22
+ tokens << {:token => t}
23
+ end
24
+ end
25
+
26
+ tokens
27
+ end
28
+
29
+ # returns a string that can be set in the cookie
30
+ # we may or may not need to encode so the cookies work in all situations
31
+ def encode(tokens)
32
+ return nil if tokens.nil?
33
+ tokens = [tokens] unless tokens.is_a?(Array)
34
+
35
+ # TODO: tokens need to be unique (duplicates need to remain at the end), remember
36
+ # the order from left to right is oldest to newest - any duplicates should always be newest
37
+ # (i.e. if we 'track' a user using the same token on the same day 2x - the second visit's position
38
+ # should be meaningful) -- i.e. order of tracking => A, F, B, F, C should encode as A|B|F|C, NOT A|F|B|C
39
+ tokens.map{|t| encode_token(t) }.join(config.separator)
40
+ end
41
+
42
+ private
43
+
44
+ def encode_token(token)
45
+ encoded_token = token[:token]
46
+ encoded_token += get_hex_date(token[:date], config.epoch) if (!token[:date].nil? && config.use_dates?)
47
+ encoded_token
48
+ end
49
+
50
+ def split_token_date(v, n)
51
+ # validate token
52
+ date_part = v[-n,n]
53
+ tok_part = v[0..(-n - 1)]
54
+ [tok_part, date_part]
55
+ end
56
+
57
+ def get_date(hex_date, epoch)
58
+ # validate hex_date
59
+ epoch + hex_date.hex
60
+ end
61
+
62
+ def get_hex_date(date, epoch)
63
+ sprintf("%04X", (date - epoch))
64
+ end
65
+
66
+ def config
67
+ SourceTrack.configuration
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ module SourceTrack
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/source_track/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Bryan Rehbein"]
6
+ gem.email = ["bryan@siliconsea.net"]
7
+ gem.description = %q{Gem to help understand a user's interaction with your site
8
+ and the influence of various referrals, promos, campaigns, etc.}
9
+ gem.summary = %q{Track the influence that your marketing campaigns and referrals
10
+ have on your users.}
11
+ gem.homepage = "https://github.com/redbeard0x0a/source_track"
12
+
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.name = "source_track"
17
+ gem.require_paths = ["lib"]
18
+ gem.version = SourceTrack::VERSION
19
+
20
+ gem.add_development_dependency 'rake'
21
+ gem.add_development_dependency 'rspec'
22
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe SourceTrack::Configuration do
4
+ let(:config) { SourceTrack.configuration }
5
+
6
+ it "should initialize with the defaults" do
7
+ config.use_dates.should be_true
8
+ config.separator.should eq '|'
9
+ config.epoch.should eq Date.new(2012,1,1)
10
+ config.date_length.should eq 4
11
+ config.cookie_name.should eq 'sct'
12
+ config.http_only_cookies.should be_true
13
+ config.cookie_path.should eq '/'
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe SourceTrack do
4
+ it "should initialize" do
5
+ SourceTrack.parser.should_not be_nil
6
+ SourceTrack.configuration.should_not be_nil
7
+ end
8
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe SourceTrack::TokenParser do
4
+
5
+ context "with use_dates" do
6
+ before(:each) do
7
+ @parser = SourceTrack.parser
8
+ end
9
+
10
+ it "should encode a single token value" do
11
+ @parser.encode({:token => "ABC", :date => Date.new(2012,9,7)}).should eq "ABC00FA"
12
+ end
13
+
14
+ it "should encode an array of tokens" do
15
+ @parser.encode([
16
+ {:token => "BBC", :date => Date.new(2012,9,7)},
17
+ {:token => "WOWZA9828", :date => Date.new(2012,10,9)}
18
+ ]).should eq "BBC00FA|WOWZA9828011A"
19
+ end
20
+ end
21
+
22
+ context "without use_dates" do
23
+ before(:each) do
24
+ @parser = SourceTrack.parser
25
+ end
26
+
27
+ it "encode a single token value" do
28
+ @parser.encode({:token => "ABC"}).should eq "ABC"
29
+ end
30
+
31
+ it "should encode an array of tokens" do
32
+ @parser.encode([
33
+ {:token => "39828ABO"}, {:token => "HOLF392"}
34
+ ]).should eq "39828ABO|HOLF392"
35
+ end
36
+
37
+ it "should encode an array of tokens" do
38
+ # @parser.encode([
39
+ # {:token => "39828ABO", :date => Date.new(2012,9,7)},
40
+ # {:token => "HOLF392", :date => Date.new(2012,10,9)}
41
+ # ]).should eq "39828ABO|HOLF392"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ require 'source_track'
2
+
3
+ ENV["RAILS_ENV"] ||= 'test'
4
+ ENV["RACK_ENV"] ||= 'test'
5
+
6
+ RSpec.configure do |config|
7
+ config.mock_with :rspec
8
+ config.color_enabled = true
9
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: source_track
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bryan Rehbein
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: ! "Gem to help understand a user's interaction with your site \n and
47
+ the influence of various referrals, promos, campaigns, etc."
48
+ email:
49
+ - bryan@siliconsea.net
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - LICENSE
57
+ - README.md
58
+ - Rakefile
59
+ - lib/source_track.rb
60
+ - lib/source_track/configuration.rb
61
+ - lib/source_track/rails.rb
62
+ - lib/source_track/rails/controller_methods.rb
63
+ - lib/source_track/railtie.rb
64
+ - lib/source_track/token_parser.rb
65
+ - lib/source_track/version.rb
66
+ - source_track.gemspec
67
+ - spec/lib/configuration_spec.rb
68
+ - spec/lib/source_track_spec.rb
69
+ - spec/lib/token_parser_spec.rb
70
+ - spec/spec_helper.rb
71
+ homepage: https://github.com/redbeard0x0a/source_track
72
+ licenses: []
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 1.8.23
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Track the influence that your marketing campaigns and referrals have on
95
+ your users.
96
+ test_files:
97
+ - spec/lib/configuration_spec.rb
98
+ - spec/lib/source_track_spec.rb
99
+ - spec/lib/token_parser_spec.rb
100
+ - spec/spec_helper.rb