touchstone 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +25 -9
- data/app/controllers/touchstone/campaigns_controller.rb +1 -0
- data/app/helpers/touchstone/campaigns_helper.rb +21 -0
- data/app/models/campaign.rb +1 -1
- data/app/models/campaign_signup.rb +2 -2
- data/app/views/touchstone/campaigns/show.html.erb +2 -8
- data/db/migrate/002_create_touchstone_campaign_signups.rb +4 -1
- data/lib/generators/touchstone/templates/touchstone.rb +19 -0
- data/lib/generators/touchstone/touchstone_generator.rb +27 -0
- data/lib/tasks/touchstone_tasks.rake +0 -10
- data/lib/touchstone/configuration.rb +10 -2
- data/lib/touchstone/version.rb +1 -1
- data/lib/touchstone.rb +11 -3
- metadata +7 -4
data/README.markdown
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
Touchstone is a Rails Engine that adds the ability to track advanced metrics for your web app. It is inspired by [this post on the Think Vitamin blog](http://thinkvitamin.com/business/marketing/how-to-get-more-customers/).
|
1
|
+
Touchstone is a Rails Engine that adds the ability to track advanced metrics for your web app. It is inspired by [this post on the Think Vitamin blog](http://thinkvitamin.com/business/marketing/how-to-get-more-customers/). I would recommend reading that article to understand what Touchstone does.
|
2
2
|
|
3
|
-
Touchstone consists of 3
|
3
|
+
Touchstone consists of 3 classes:
|
4
4
|
|
5
5
|
## Campaigns
|
6
6
|
A campaign records the details of a source from which visitors will sign up to your website. It can be a Google Adwords campaign or a link on your homepage.
|
@@ -9,7 +9,12 @@ A campaign records the details of a source from which visitors will sign up to y
|
|
9
9
|
By adding the parameter ?cid= to each link, the visit will be recorded.
|
10
10
|
|
11
11
|
## Campaign Signups
|
12
|
-
When a visitor visits your website via a link containing ?cid= and then signs up, the sign up is tied back to that visit and that campaign. You can then track the revenue obtained from each visitor against the cost of a particular campaign.
|
12
|
+
When a visitor visits your website via a link containing ?cid= and then signs up, the sign up is tied back to that visit and that campaign. You can then track the revenue obtained from each visitor against the cost of a particular campaign.
|
13
|
+
|
14
|
+
## Prerequisites & Dependencies
|
15
|
+
Touchstone requires your application to have something like a User model. The model doesn't need to be called User (you can configure this) but the purpose of Touchstone is to track users who signup to your application in response to a campaign.
|
16
|
+
|
17
|
+
The views are built using Bootstrap so installing Touchstone will introduce dependencies on the sass and sass-bootstrap gems.
|
13
18
|
|
14
19
|
## Installation
|
15
20
|
Add the following line to your Gemfile:
|
@@ -18,13 +23,25 @@ Add the following line to your Gemfile:
|
|
18
23
|
|
19
24
|
Then run `bundle install`
|
20
25
|
|
21
|
-
|
26
|
+
## Configuration
|
27
|
+
Once the gem has been installed run `rails g touchstone`
|
28
|
+
|
29
|
+
This will:
|
30
|
+
|
31
|
+
* Copy an initializer file to `config/initializers/touchstone.rb`. You should read this file and set your configuration options before proceeding
|
32
|
+
* Add a before filter to your application controller. *NB*: This defaults to setting a private method for the filter. If you already have `private` defined in your application controller, you will need to remove the duplicate declaration.
|
33
|
+
* Mounts the engine in your routes.rb file
|
34
|
+
|
35
|
+
## Installation continued
|
36
|
+
Copy the migrations across to your application by running `rake touchstone:install:migrations`. This will add migrations to create the tables for the 3 classes set out above.
|
37
|
+
|
38
|
+
If, for any reason the command `rails g touchstone` has not made the correct changes to your files, these are the changes that need to be made and can be made manually. If you do not have a `User` model, the configuration option `association_name` in the Touchstone initializer must be set before running the Touchstone migrations.
|
22
39
|
|
23
40
|
You will need to mount Touchstone in your application by adding the following line to your `routes.rb` file:
|
24
41
|
|
25
42
|
mount Touchstone::Engine, :at => "/touchstone/"
|
26
43
|
|
27
|
-
|
44
|
+
Add the following to your `application_controller.rb` file:
|
28
45
|
|
29
46
|
before_filter :set_cookie_and_record_visit
|
30
47
|
|
@@ -40,6 +57,8 @@ Finally, add the following to your `application_controller.rb` file:
|
|
40
57
|
end
|
41
58
|
|
42
59
|
## Usage
|
60
|
+
Restart your server, and if you now visit `http://yourdomain.com/touchstone`, you will see the default page.
|
61
|
+
|
43
62
|
Usage is very simple. Assume you are running a Google Adwords campaign for 1 week at a cost of $100. Create this campaign in Touchstone and it will return a campaign ID. Make sure all your links on Adwords contain the parameter at the end `?cid={campaign_id}` where `campaign_id` is the id generated by Touchstone. Please note:
|
44
63
|
|
45
64
|
* Campaigns don't need to be paid campaigns, it can be as simple as a link on your homepage
|
@@ -62,7 +81,7 @@ The installation steps described above will get you going with tracking visits f
|
|
62
81
|
end
|
63
82
|
end
|
64
83
|
|
65
|
-
Tracking a user's lifetime value will depend on your transactional logic but assuming a common pattern of a user having a subscription and that subscription containing many transactions, the following could be used:
|
84
|
+
Touchstone requires your user model to have a method called lifetime\_value in order to calculate the revenue that a user has generated. Tracking a user's lifetime value will depend on your transactional logic but assuming a common pattern of a user having a subscription and that subscription containing many transactions, the following could be used:
|
66
85
|
|
67
86
|
class User < ActiveRecord::Base
|
68
87
|
...
|
@@ -78,8 +97,5 @@ Tracking a user's lifetime value will depend on your transactional logic but ass
|
|
78
97
|
|
79
98
|
## Todo
|
80
99
|
|
81
|
-
* Don't constrain Touchstone to an application that only has a Users table. Allow this as a configuration option
|
82
|
-
* Automatically load routes and application controller content
|
83
|
-
* Improve design
|
84
100
|
* Add some tests
|
85
101
|
* Allow customisation of currency (only displays dollars at moment although amounts are not stored in a particular currency)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Touchstone
|
2
|
+
module CampaignsHelper
|
3
|
+
|
4
|
+
def signups_table_headers(columns)
|
5
|
+
item = ""
|
6
|
+
columns.each do |column|
|
7
|
+
item += "<th scope='col' id='#{column}'>#{column.humanize}</th>\n"
|
8
|
+
end
|
9
|
+
item.html_safe
|
10
|
+
end
|
11
|
+
|
12
|
+
def signups_table_content(columns,signup)
|
13
|
+
item = ""
|
14
|
+
columns.each do |column|
|
15
|
+
item += "<td class='#{column}'>#{signup.user.send(column.to_sym)}</td>\n"
|
16
|
+
end
|
17
|
+
item.html_safe
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/app/models/campaign.rb
CHANGED
@@ -12,7 +12,7 @@ class Campaign < ActiveRecord::Base
|
|
12
12
|
if self.campaign_signups.length > 0
|
13
13
|
total = Array.new
|
14
14
|
self.campaign_signups.each do |signup|
|
15
|
-
total << signup.user.lifetime_value
|
15
|
+
total << signup.user.lifetime_value
|
16
16
|
end
|
17
17
|
total.inject{|sum,x| sum + x}
|
18
18
|
else
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class CampaignSignup < ActiveRecord::Base
|
2
2
|
|
3
3
|
belongs_to :campaign, :class_name => "Campaign", :foreign_key => "campaign_id"
|
4
|
-
belongs_to :user, :class_name =>
|
4
|
+
belongs_to :user, :class_name => Touchstone.association_name.capitalize, :foreign_key => "#{Touchstone.association_name}_id" # TODO: Fix this
|
5
5
|
|
6
|
-
attr_accessible
|
6
|
+
attr_accessible "#{Touchstone.association_name}_id".to_sym, :campaign_id
|
7
7
|
|
8
8
|
end
|
@@ -28,19 +28,13 @@
|
|
28
28
|
<table class="table table-striped">
|
29
29
|
<thead>
|
30
30
|
<tr>
|
31
|
-
|
32
|
-
<th scope="col" width="30%">Email</th>
|
33
|
-
<th scope="col" width="30%">Registered</th>
|
34
|
-
<th scope="col" width="15%">LTV (inc. VAT)</th>
|
31
|
+
<%= signups_table_headers(@columns) %>
|
35
32
|
</tr>
|
36
33
|
</thead>
|
37
34
|
<tbody>
|
38
35
|
<% @campaign.campaign_signups.each do |signup| %>
|
39
36
|
<tr>
|
40
|
-
|
41
|
-
<td><%= signup.user.email %></td>
|
42
|
-
<td><%= time_ago_in_words(signup.user.created_at) %> ago</td>
|
43
|
-
<td>$<%= sprintf("%.2f",signup.user.lifetime_value) %></td>
|
37
|
+
<%= signups_table_content(@columns,signup)%>
|
44
38
|
</tr>
|
45
39
|
<% end %>
|
46
40
|
</tbody>
|
@@ -1,8 +1,11 @@
|
|
1
1
|
class CreateTouchstoneCampaignSignups < ActiveRecord::Migration
|
2
2
|
|
3
3
|
def change
|
4
|
+
|
5
|
+
column = Touchstone.association_name + "_id"
|
6
|
+
|
4
7
|
create_table :campaign_signups do |t|
|
5
|
-
t.integer
|
8
|
+
t.integer column.to_sym
|
6
9
|
t.integer :campaign_id
|
7
10
|
t.timestamps
|
8
11
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Touchstone.config do |c|
|
2
|
+
|
3
|
+
# By default, Touchstone assumes that you have a User model
|
4
|
+
# in your application and will associate campaign signups
|
5
|
+
# with a user_id in the campaign_signups table. You can
|
6
|
+
# customise the association by using this configuration
|
7
|
+
# option.
|
8
|
+
|
9
|
+
c.association_name = "User"
|
10
|
+
|
11
|
+
# When viewing a campaign, Touchstone will show a list of
|
12
|
+
# users who have signed up as a result of that campaign.
|
13
|
+
# Use this option to set an array of fields that you would
|
14
|
+
# like to appear in the view. By default, Touchstone will
|
15
|
+
# only show the id and created_at fields.
|
16
|
+
|
17
|
+
c.column_names = [:id,:created_at]
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class TouchstoneGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
def self.source_root
|
6
|
+
File.join(File.dirname(__FILE__), 'templates')
|
7
|
+
end
|
8
|
+
|
9
|
+
def copy_initializer_file
|
10
|
+
puts "Copying initializer file..."
|
11
|
+
copy_file 'touchstone.rb', 'config/initializers/touchstone.rb'
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_before_filter
|
15
|
+
puts "Setting before filter in application_controller.rb..."
|
16
|
+
insert_into_file 'app/controllers/application_controller.rb', "\n\n\tbefore_filter :set_cookie_and_record_visit", :after => 'ActionController::Base'
|
17
|
+
insert_into_file 'app/controllers/application_controller.rb', :before => "end" do
|
18
|
+
"\tprivate\n\n\tdef set_cookie_and_record_visit\n\t\tif params[:cid] && Campaign.find_by_id(params[:cid]) && !CampaignVisit.find_by_request_ip(request.remote_ip)\n\t\t\tif !cookies['touchstone_campaign_id']\n\t\t\t\tcookies['touchstone_campaign_id'] = params[:cid]\n\t\t\t\tCampaignVisit.create(:campaign_id => params[:cid], :request_ip => request.remote_ip)\n\t\t\tend\n\t\tend\n\tend\n"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def mount_engine
|
23
|
+
puts "Mounting engine into routes.rb..."
|
24
|
+
insert_into_file 'config/routes.rb', "\n\nmount Touchstone::Engine, :at => '/touchstone/'", :after => 'routes.draw do'
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -1,10 +0,0 @@
|
|
1
|
-
# require 'thor'
|
2
|
-
# include Thor
|
3
|
-
#
|
4
|
-
# namespace :touchstone do
|
5
|
-
#
|
6
|
-
# desc "Injects the necessary code into your application to run Touchstone"
|
7
|
-
# task :install do
|
8
|
-
# inject_into_file "#{Rails.root}/config/routes.rb", "mount Touchstone::Engine, :at => '/touchstone/'", :after => "routes.draw do"
|
9
|
-
# end
|
10
|
-
# end
|
@@ -4,15 +4,23 @@ module Touchstone
|
|
4
4
|
|
5
5
|
extend self
|
6
6
|
|
7
|
-
attr_accessor :association_name
|
7
|
+
attr_accessor :association_name, :column_names
|
8
8
|
|
9
9
|
def association_name=name
|
10
|
-
@name = name
|
10
|
+
@name = name.downcase
|
11
11
|
end
|
12
12
|
|
13
13
|
def association_name
|
14
14
|
@name || "user"
|
15
15
|
end
|
16
16
|
|
17
|
+
def column_names=columns
|
18
|
+
@columns = columns.map(&:to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
def column_names
|
22
|
+
@columns || [:id,:created_at]
|
23
|
+
end
|
24
|
+
|
17
25
|
end
|
18
26
|
end
|
data/lib/touchstone/version.rb
CHANGED
data/lib/touchstone.rb
CHANGED
@@ -5,12 +5,20 @@ module Touchstone
|
|
5
5
|
|
6
6
|
include Configuration
|
7
7
|
|
8
|
-
mattr_accessor :app_root
|
9
|
-
|
10
8
|
class << self
|
11
|
-
|
9
|
+
|
10
|
+
def config &block
|
12
11
|
yield Touchstone::Configuration
|
13
12
|
end
|
13
|
+
|
14
|
+
def association_name
|
15
|
+
Touchstone::Configuration.association_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def column_names
|
19
|
+
Touchstone::Configuration.column_names
|
20
|
+
end
|
21
|
+
|
14
22
|
end
|
15
23
|
|
16
24
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: touchstone
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -89,6 +89,7 @@ files:
|
|
89
89
|
- app/controllers/touchstone/application_controller.rb
|
90
90
|
- app/controllers/touchstone/campaigns_controller.rb
|
91
91
|
- app/helpers/touchstone/application_helper.rb
|
92
|
+
- app/helpers/touchstone/campaigns_helper.rb
|
92
93
|
- app/models/campaign.rb
|
93
94
|
- app/models/campaign_signup.rb
|
94
95
|
- app/models/campaign_visit.rb
|
@@ -103,6 +104,8 @@ files:
|
|
103
104
|
- db/migrate/001_create_touchstone_campaigns.rb
|
104
105
|
- db/migrate/002_create_touchstone_campaign_signups.rb
|
105
106
|
- db/migrate/003_create_touchstone_campaign_visits.rb
|
107
|
+
- lib/generators/touchstone/templates/touchstone.rb
|
108
|
+
- lib/generators/touchstone/touchstone_generator.rb
|
106
109
|
- lib/tasks/touchstone_tasks.rake
|
107
110
|
- lib/touchstone/configuration.rb
|
108
111
|
- lib/touchstone/engine.rb
|
@@ -156,7 +159,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
156
159
|
version: '0'
|
157
160
|
segments:
|
158
161
|
- 0
|
159
|
-
hash:
|
162
|
+
hash: -3745211063638779755
|
160
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
164
|
none: false
|
162
165
|
requirements:
|
@@ -165,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
168
|
version: '0'
|
166
169
|
segments:
|
167
170
|
- 0
|
168
|
-
hash:
|
171
|
+
hash: -3745211063638779755
|
169
172
|
requirements: []
|
170
173
|
rubyforge_project:
|
171
174
|
rubygems_version: 1.8.20
|