sojourn 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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: