sojourn 0.0.7

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 05e30ba3490b5cb18a66e3ecf4bdb0048e0ce529
4
+ data.tar.gz: 055a2accda2519d306ed020f9df334694cea68c0
5
+ SHA512:
6
+ metadata.gz: 8c8eaa338c482230664097aae7910ebae14f9ebace498dc9feff819d96fce1b66e65546c1416f3e23e3dee6342c0936fe209e081eb11a1a0aeff62de82a74947
7
+ data.tar.gz: 70f60a8e7ba6a81055acd0306736b4feff026eed0f2dbac10d18510bd2e6e4dec6711a9c96eacac5eef6767ef12e1ce2b042d0739f31285ec95bdb9286f25f24
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sojourn.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # Sojourn
2
+
3
+ Simple source & event tracking for Rails. This gem automatically tracks *sojourners*
4
+ (i.e. unique visitors) based on:
5
+
6
+ * Referer
7
+ * UTM parameters
8
+ * Browser (User Agent)
9
+ * The currently logged-in user (i.e. `current_user`)
10
+ * Various other request data
11
+
12
+ ## How It Works
13
+
14
+ **Everything is tracked in the form of events.** Yep, events all the way down.
15
+ (See 'Why Events?' below for the reasoning behind this.)
16
+
17
+ Sojourn assigns each *sojourner* a UUID, which is tracked across requests. All events are
18
+ associated with this UUID and with the current user's ID (if logged-in).
19
+
20
+ Events (`Sojourn::Event`) consist of a name, a set of properties (key-value hash) and information
21
+ about the request. In the current ActiveRecord implementation, requests (`Sojourn::Request`) can
22
+ be queried separately and may have many events. Requests also track browser (`Sojourn::Browser`)
23
+ and campaign (`Sojourn::Campaign`) info as unique models. See 'Usage' below for the details
24
+ of these models.
25
+
26
+
27
+ ## Usage
28
+
29
+ ```ruby
30
+ # Track a custom event (highly encouraged!):
31
+ sojourn.track! 'clicked call-to-action', plan_choice: 'enterprise'
32
+
33
+ e = Sojourn::Event.last
34
+ e.name # event name (e.g. 'clicked call-to-action')
35
+ e.sojourner_uuid # uuid tracked across requests, stored in cookie
36
+ e.user # User or nil
37
+ e.properties # key-value hash (e.g. "{ plan_choice: 'enterprise' }")
38
+ e.request # Sojourn::Request object
39
+
40
+ r = Sojourn::Request.last
41
+ r.referer
42
+ r.host
43
+ r.path
44
+ r.controller
45
+ r.action
46
+ r.params
47
+ r.method
48
+ r.ip_address
49
+ r.campaign # Sojourn::Campaign object (nil if no campaign detected)
50
+ r.browser # Sojourn::Browser object
51
+
52
+ b = Sojourn::Browser.last
53
+ b.known? # whether or not the browser was detected successfully
54
+ b.user_agent
55
+ b.name
56
+ b.version
57
+ b.platform
58
+ b.bot?
59
+
60
+ c = Sojourn::Campaign.last
61
+ c.path # Base path (e.g. '/posts/2')
62
+ c.params # Notable (tracked) params, sorted. (Typically utm-style, but configurable.)
63
+
64
+ ```
65
+
66
+ ## Default Events
67
+
68
+ The three built-in events are as follows:
69
+
70
+ ```ruby
71
+ '!sojourning' # The sojourner has arrived from an external source.
72
+ '!logged_in' # The sojourner has logged-in.
73
+ '!logged_out' # The sojourner has logged-out.
74
+ ```
75
+
76
+ A `'!sojourning'` event takes place whenever any of the following is true:
77
+
78
+ * The sojourner has never been seen before (i.e. direct traffic of some kind)
79
+ * The referer is from an external source (i.e. not the current `request.host`)
80
+ * The request contains tracked (utm-style) parameters. (These can be configured in the `sojourn.rb`
81
+ initializer.)
82
+
83
+
84
+ ## Installation
85
+
86
+ Add this line to your application's Gemfile:
87
+
88
+ ```ruby
89
+ gem 'sojourn'
90
+ ```
91
+
92
+ And then execute:
93
+
94
+ $ bundle
95
+
96
+ To install migrations and the `sojourn.rb` initializer, execute:
97
+
98
+ $ rails g sojourn:install
99
+
100
+ ## Why Events? Why not track visits/visitors as their own objects?
101
+
102
+ The idea is that, at a certain scale, this kind of tracking should be dumped directly into
103
+ append-only logs (or an event bus / messaging queue) for asynchronous processing.
104
+
105
+ This is made easier when everything can be represented, at a basic level, as discrete events.
106
+ In theory, it works with just about any data store, and makes for easy time series and funnel
107
+ analysis. I'd like to move away from ActiveRecord at some point and open up the door for other,
108
+ highly scalable data backends.
109
+
110
+ ## Current Limitations (i.e. the 'todo' list)
111
+
112
+ * Tested only on rails 3.2.18 and ruby 2.0.0 with ActiveRecord and PostgreSQL.
113
+ * Assumes `User` and `current_user` convention for user tracking.
114
+ * Assumes that if `request.referer` does not match `request.host`, the referer is external to your
115
+ website.
116
+ * Relies solely on cookies to track visitor UUID across requests (no JS, fingerprinting, etc)
117
+ * Relies on ActiveRecord for storage. (At a bigger scale, append-only logs are preferred)
118
+ * There are no tests.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,35 @@
1
+ require 'rails/generators/active_record'
2
+ module Sojourn
3
+ module Generators
4
+ class InstallGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+
7
+ desc 'Copies sojourn migrations to your file.'
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ def self.next_migration_number(dirname)
11
+ next_migration_number = current_migration_number(dirname) + 1
12
+ if ::ActiveRecord::Base.timestamped_migrations
13
+ [Time.now.utc.strftime('%Y%m%d%H%M%S'), format('%.14d', next_migration_number)].max
14
+ else
15
+ format('%.3d', next_migration_number)
16
+ end
17
+ end
18
+
19
+ def create_config_file
20
+ template 'config_initializer.rb', 'config/initializers/sojourn.rb'
21
+ end
22
+
23
+ def create_migrations
24
+ %w(campaigns browsers requests events).map { |m| "create_sojourn_#{m}" }.each do |name|
25
+ if self.class.migration_exists?('db/migrate', name)
26
+ say " #{set_color('skip', :yellow)} #{name}.rb (migration already exists)"
27
+ else
28
+ migration_template "#{name}.rb", "db/migrate/#{name}.rb"
29
+ sleep 1
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ Sojourn.configure do |config|
2
+
3
+ # A new '!sojourning' event is created whenever:
4
+ # 1. The 'referer' is external
5
+ # 2. There are utm-style parameters in the request.
6
+ # 3. The visitor is new and has never been assigned a 'sojourner_id' (i.e. direct traffic)
7
+ # The two events '!logged_in' and '!logged_out' are created whenever sojourn detects a
8
+ # change to `current_user`.
9
+
10
+ # To disable all automatic event tracking, uncomment the following line:
11
+ # config.tracking_enabled = false
12
+
13
+ # You may customize the list of tracked (utm-style) parameters here.
14
+ # Note: Using tracked params on internal links within your site is NOT recommended, as this will
15
+ # result in many repeated '!sojourning' events. Instead, you should use a different set
16
+ # of parameters (such as 'from', 'source', or 'referer') and NOT include them here.
17
+ # Default: [:utm_source, :utm_medium, :utm_term, :utm_content, :utm_campaign]
18
+ #
19
+ # config.campaign_params += [:my_custom_tracking_param]
20
+
21
+ # By default, sojourn will attach `user_id`, `sojourner_uuid`, and a `created_at` timestamp
22
+ # to every event. If you would like to add additional properties, you may do so as follows.
23
+ # Note: This block will be called in the context of your controller.
24
+ #
25
+ # config.default_properties do |p|
26
+ # p[:rails_env] = Rails.env
27
+ # p[:my_custom_property] = method_defined_in_controller
28
+ # end
29
+
30
+ # By default, sojourn uses a signed, permanent cookie to store the sojourner uuid. You
31
+ # may specifiy an alternate/custom session_store, or change the name of the cookie that
32
+ # gets created. (default is `:_sojourn`)
33
+ #
34
+ # config.session_store = Sojourn::SessionStores::Cookie
35
+ # config.cookie_name = :my_custom_cookie_name
36
+
37
+ end
@@ -0,0 +1,13 @@
1
+ class CreateSojournBrowsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :sojourn_browsers do |t|
4
+ t.text :user_agent, unique: true
5
+ t.string :name
6
+ t.string :version
7
+ t.string :platform
8
+ t.boolean :known
9
+ t.boolean :bot
10
+ end
11
+ add_index :sojourn_browsers, [:user_agent]
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ class CreateSojournCampaigns < ActiveRecord::Migration
2
+ def change
3
+ create_table :sojourn_campaigns do |t|
4
+ t.string :path, limit: 2048
5
+ t.string :params, limit: 2048
6
+ t.timestamp :created_at
7
+ end
8
+ add_index :sojourn_campaigns, [:path, :params]
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ class CreateSojournEvents < ActiveRecord::Migration
2
+ def change
3
+ create_table :sojourn_events do |t|
4
+ t.string :sojourner_uuid, limit: 36, null: false
5
+ t.string :name
6
+ t.text :properties
7
+ t.references :sojourn_request
8
+ t.references :user
9
+ t.timestamp :created_at
10
+ end
11
+ add_index :sojourn_events, [:sojourner_uuid]
12
+ add_index :sojourn_events, [:sojourn_request_id]
13
+ add_index :sojourn_events, [:user_id]
14
+ add_index :sojourn_events, [:created_at]
15
+ add_index :sojourn_events, [:name]
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ class CreateSojournRequests < ActiveRecord::Migration
2
+ def change
3
+ create_table :sojourn_requests do |t|
4
+ t.references :sojourn_campaign
5
+ t.references :sojourn_browser
6
+ t.string :host, limit: 2048
7
+ t.string :path, limit: 2048
8
+ t.string :method
9
+ t.string :controller
10
+ t.string :action
11
+ t.string :ip_address
12
+ t.text :params
13
+ t.text :referer
14
+ t.timestamp :created_at
15
+ end
16
+ add_index :sojourn_requests, [:sojourn_campaign_id]
17
+ add_index :sojourn_requests, [:sojourn_browser_id]
18
+ end
19
+ end
data/lib/sojourn.rb ADDED
@@ -0,0 +1,24 @@
1
+ require_relative 'sojourn/version'
2
+ require_relative 'sojourn/configuration'
3
+ require_relative 'sojourn/controller'
4
+
5
+ module Sojourn
6
+ def self.table_name_prefix
7
+ 'sojourn_'
8
+ end
9
+
10
+ def self.configure(&block)
11
+ block.call(config)
12
+ end
13
+
14
+ def self.config
15
+ @config ||= Configuration.new
16
+ end
17
+
18
+ def self.tables_exist?
19
+ @tables_exist ||= %w(sojourn_events sojourn_requests sojourn_browsers sojourn_campaigns)
20
+ .map { |t| ActiveRecord::Base.connection.table_exists?(t) }.all?
21
+ end
22
+ end
23
+
24
+ ActionController::Base.send :include, Sojourn::Controller
@@ -0,0 +1,27 @@
1
+ require 'browser'
2
+
3
+ module Sojourn
4
+ class Browser < ActiveRecord::Base
5
+ has_many :requests, foreign_key: :sojourn_browser_id
6
+ has_many :events, through: :requests
7
+
8
+ def self.from_request(request)
9
+ where(user_agent: request.user_agent).first_or_initialize
10
+ end
11
+
12
+ before_validation do
13
+ self.name ||= browser.name
14
+ self.version ||= browser.version
15
+ self.platform ||= browser.platform
16
+ self.known ||= browser.known?
17
+ self.bot ||= browser.bot?
18
+ true # otherwise .valid? will return false
19
+ end
20
+
21
+ private
22
+
23
+ def browser
24
+ @browser ||= ::Browser.new(user_agent: user_agent)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Sojourn
2
+ class Campaign < ActiveRecord::Base
3
+
4
+ has_many :requests, foreign_key: :sojourn_campaign_id
5
+ has_many :events, through: :requests
6
+
7
+ class << self
8
+
9
+ def from_request(request)
10
+ return unless (params = tracked_params(request.params)).any?
11
+ where(path: request.path.downcase.try(:truncate, 2048))
12
+ .where(params: params.to_param.try(:truncate, 2048)).first_or_initialize
13
+ end
14
+
15
+ private
16
+
17
+ def tracked_params(params)
18
+ Hash[filter_params(params).sort.map { |k, v| [k.downcase, v.downcase] }]
19
+ end
20
+
21
+ def filter_params(params)
22
+ params.slice(*Sojourn.config.campaign_params).delete_if { |_, v| v.blank? }
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'session_stores/cookie'
2
+
3
+ class Configuration
4
+
5
+ attr_accessor :campaign_params
6
+ attr_accessor :session_store
7
+ attr_accessor :cookie_name
8
+ attr_accessor :tracking_enabled
9
+ attr_accessor :default_properties_block
10
+
11
+ def initialize
12
+ set_defaults
13
+ end
14
+
15
+ def default_properties(&block)
16
+ self.default_properties_block = block
17
+ end
18
+
19
+ private
20
+
21
+ def set_defaults
22
+ self.campaign_params = [:utm_source, :utm_medium, :utm_term, :utm_content, :utm_campaign]
23
+ self.session_store = Sojourn::SessionStores::Cookie
24
+ self.cookie_name = :_sojourn
25
+ self.tracking_enabled = true
26
+ end
27
+
28
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'tracker'
2
+
3
+ module Sojourn
4
+ module Controller
5
+
6
+ def self.included(base)
7
+ base.before_filter :track_sojourning
8
+ base.before_filter :save_sojourn_session
9
+ end
10
+
11
+ def sojourn
12
+ @sojourn ||= Tracker.new(self)
13
+ end
14
+
15
+ private
16
+
17
+ def track_sojourning
18
+ sojourn.sojourning!
19
+ end
20
+
21
+ def save_sojourn_session
22
+ sojourn.update_session!
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ module Sojourn
2
+ class Event < ActiveRecord::Base
3
+ DEFAULT_FIELDS = [:id, :sojourner_uuid, :name, :properties, :sojourn_request_id, :user_id, :created_at]
4
+
5
+ belongs_to :request, foreign_key: :sojourn_request_id
6
+ belongs_to :user
7
+ has_one :campaign, through: :request
8
+ has_one :browser, through: :request
9
+
10
+ serialize :properties
11
+
12
+ before_save do
13
+ properties.keys.map(&:to_sym).each do |key|
14
+ send("#{key}=", properties[key]) if self.class.available_fields.include?(key)
15
+ end
16
+ end
17
+
18
+ def self.available_fields
19
+ @available_fields ||= column_names.map(&:to_sym) - DEFAULT_FIELDS
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,42 @@
1
+ require_relative 'campaign'
2
+ require_relative 'browser'
3
+ require_relative 'serializers/symbol'
4
+ require 'addressable/uri'
5
+
6
+ module Sojourn
7
+ class Request < ActiveRecord::Base
8
+ attr_accessor :user_agent
9
+
10
+ serialize :method, Serializers::Symbol
11
+ serialize :params
12
+
13
+ belongs_to :campaign, foreign_key: :sojourn_campaign_id
14
+ belongs_to :browser, foreign_key: :sojourn_browser_id
15
+ has_many :events, foreign_key: :sojourn_request_id
16
+
17
+ def self.from_request(request)
18
+ new referer: request.referer.try(:truncate, 2048),
19
+ host: request.host.try(:truncate, 2048),
20
+ path: request.path.try(:truncate, 2048),
21
+ controller: request.params[:controller],
22
+ action: request.params[:action],
23
+ params: request.filtered_parameters.with_indifferent_access.except(:controller, :action),
24
+ method: request.request_method_symbol,
25
+ ip_address: request.remote_ip,
26
+ user_agent: request.user_agent
27
+ end
28
+
29
+ before_validation do
30
+ self.campaign ||= Campaign.from_request(self)
31
+ self.browser ||= Browser.from_request(self) if user_agent
32
+ end
33
+
34
+ def outside_referer?
35
+ referer.present? && Addressable::URI.parse(referer).host != host
36
+ end
37
+
38
+ def any_utm_data?
39
+ Sojourn.config.campaign_params.map { |p| params[p].present? }.any?
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ module Sojourn
2
+ module Serializers
3
+ class Symbol
4
+ def self.load(string)
5
+ string.to_sym
6
+ end
7
+
8
+ def self.dump(symbol)
9
+ symbol.to_s
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,49 @@
1
+ module Sojourn
2
+ module SessionStores
3
+ class Cookie
4
+
5
+ def initialize(ctx)
6
+ @ctx = ctx
7
+ end
8
+
9
+ def sojourner_uuid
10
+ cookie_data[:uuid]
11
+ end
12
+
13
+ def sojourner_uuid=(value)
14
+ cookies.permanent.signed[cookie_name] = { value: cookie_data.merge(uuid: value) }
15
+ end
16
+
17
+ def sojourner_tracked?
18
+ cookie_data.key?(:uuid)
19
+ end
20
+
21
+ def user_id
22
+ cookie_data[:user_id]
23
+ end
24
+
25
+ def user_id=(value)
26
+ cookies.permanent.signed[cookie_name] = { value: cookie_data.merge(user_id: value) }
27
+ end
28
+
29
+ def user_tracked?
30
+ cookie_data.key?(:user_id)
31
+ end
32
+
33
+ private
34
+
35
+ def cookie_data
36
+ cookies.signed[cookie_name] || {}
37
+ end
38
+
39
+ def cookies
40
+ @ctx.send(:cookies)
41
+ end
42
+
43
+ def cookie_name
44
+ @cookie_name ||= Sojourn.config.cookie_name
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,76 @@
1
+ require_relative 'request'
2
+ require_relative 'event'
3
+
4
+ module Sojourn
5
+ class Tracker
6
+ attr_accessor :ctx
7
+ delegate :current_user, to: :ctx
8
+
9
+ def initialize(ctx)
10
+ self.ctx = ctx
11
+ end
12
+
13
+ def track!(event_name, properties = {}, user_id = current_user_id)
14
+ return unless Sojourn.tables_exist?
15
+ properties = default_event_properties.merge(properties)
16
+ Event.create! sojourner_uuid: sojourner_uuid, name: event_name, request: request,
17
+ properties: properties, user_id: user_id
18
+ end
19
+
20
+ def sojourning!
21
+ return unless Sojourn.config.tracking_enabled && Sojourn.tables_exist?
22
+ track!('!sojourning') if sojourning?
23
+ track_user_change! if user_changed?
24
+ end
25
+
26
+ def track_user_change!
27
+ return unless user_changed?
28
+ track!('!logged_out', {}, session.user_id) if session.user_id
29
+ track!('!logged_in', {}, current_user_id) if current_user_id
30
+ end
31
+
32
+ def update_session!
33
+ session.sojourner_uuid ||= sojourner_uuid
34
+ session.user_id = current_user_id
35
+ end
36
+
37
+ private
38
+
39
+ def request
40
+ @request ||= Request.from_request(ctx.request)
41
+ end
42
+
43
+ def session
44
+ @session ||= Sojourn.config.session_store.new(ctx)
45
+ end
46
+
47
+ def sojourner_uuid
48
+ @sojourner_uuid ||= session.sojourner_uuid || SecureRandom.uuid
49
+ end
50
+
51
+ def sojourning?
52
+ request.outside_referer? || request.any_utm_data? || !session.sojourner_tracked?
53
+ end
54
+
55
+ def user_changed?
56
+ session.user_tracked? && session.user_id != current_user_id
57
+ end
58
+
59
+ def current_user_id
60
+ current_user.try(:id)
61
+ end
62
+
63
+ def default_event_properties
64
+ @default_event_properties ||= fetch_default_properties
65
+ end
66
+
67
+ def fetch_default_properties(properties = {})
68
+ if Sojourn.config.default_properties_block
69
+ @ctx.define_singleton_method :sojourn_event_properties,
70
+ Sojourn.config.default_properties_block
71
+ end
72
+ @ctx.sojourn_event_properties(properties) if @ctx.respond_to? :sojourn_event_properties
73
+ properties
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ module Sojourn
2
+ VERSION = '0.0.7'
3
+ end
data/sojourn.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'sojourn/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'sojourn'
7
+ spec.version = Sojourn::VERSION
8
+ spec.authors = ['Smudge']
9
+ spec.email = ['nathan@ngriffith.com']
10
+ spec.summary = 'Simple user source tracking for Rails.'
11
+ spec.description = %(
12
+ Sojourn tracks site visitors and sources, with the ability to recognise
13
+ multiple sources per visitor. Each time a new source is detected,
14
+ sojourn tracks the referer, utm data, and logged-in user (if any)).gsub("\n", ' ')
15
+ spec.homepage = ''
16
+ spec.license = ''
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(/^(test|spec|features)/)
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.7'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+
26
+ spec.add_dependency 'browser', '>= 0.8.0'
27
+ spec.add_dependency 'addressable', '>= 2.3.1'
28
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sojourn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
5
+ platform: ruby
6
+ authors:
7
+ - Smudge
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: browser
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.8.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.8.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: addressable
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 2.3.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.3.1
69
+ description: " Sojourn tracks site visitors and sources, with the ability to recognise
70
+ multiple sources per visitor. Each time a new source is detected, sojourn tracks
71
+ the referer, utm data, and logged-in user (if any)"
72
+ email:
73
+ - nathan@ngriffith.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".gitignore"
79
+ - Gemfile
80
+ - README.md
81
+ - Rakefile
82
+ - lib/generators/sojourn/install_generator.rb
83
+ - lib/generators/sojourn/templates/config_initializer.rb
84
+ - lib/generators/sojourn/templates/create_sojourn_browsers.rb
85
+ - lib/generators/sojourn/templates/create_sojourn_campaigns.rb
86
+ - lib/generators/sojourn/templates/create_sojourn_events.rb
87
+ - lib/generators/sojourn/templates/create_sojourn_requests.rb
88
+ - lib/sojourn.rb
89
+ - lib/sojourn/browser.rb
90
+ - lib/sojourn/campaign.rb
91
+ - lib/sojourn/configuration.rb
92
+ - lib/sojourn/controller.rb
93
+ - lib/sojourn/event.rb
94
+ - lib/sojourn/request.rb
95
+ - lib/sojourn/serializers/symbol.rb
96
+ - lib/sojourn/session_stores/cookie.rb
97
+ - lib/sojourn/tracker.rb
98
+ - lib/sojourn/version.rb
99
+ - sojourn.gemspec
100
+ homepage: ''
101
+ licenses:
102
+ - ''
103
+ metadata: {}
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 2.2.2
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: Simple user source tracking for Rails.
124
+ test_files: []
125
+ has_rdoc: