touchstone 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +4 -3
- data/app/assets/stylesheets/touchstone/touchstone.css.scss +71 -0
- data/app/controllers/touchstone/campaigns_controller.rb +15 -0
- data/app/views/layouts/touchstone/touchstone.html.erb +27 -11
- data/app/views/touchstone/campaigns/_campaign_form.html.erb +42 -0
- data/app/views/touchstone/campaigns/edit.html.erb +3 -0
- data/app/views/touchstone/campaigns/index.html.erb +5 -4
- data/app/views/touchstone/campaigns/new.html.erb +2 -28
- data/app/views/touchstone/campaigns/show.html.erb +16 -12
- data/config/routes.rb +1 -0
- data/lib/touchstone/engine.rb +4 -0
- data/lib/touchstone/version.rb +1 -1
- metadata +38 -4
- data/app/assets/stylesheets/touchstone/touchstone.css +0 -113
data/README.markdown
CHANGED
@@ -18,7 +18,7 @@ Add the following line to your Gemfile:
|
|
18
18
|
|
19
19
|
Then run `bundle install`
|
20
20
|
|
21
|
-
Copy the migrations across to your application by running `touchstone:install:migrations`. This will add
|
21
|
+
Copy the migrations across to your application by running `touchstone:install:migrations`. This will add models for the 3 elements set out above.
|
22
22
|
|
23
23
|
You will need to mount Touchstone in your application by adding the following line to your `routes.rb` file:
|
24
24
|
|
@@ -42,7 +42,7 @@ Finally, add the following to your `application_controller.rb` file:
|
|
42
42
|
## Usage
|
43
43
|
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
44
|
|
45
|
-
* Campaigns don't need to be paid campaigns, it can be as simple as a link
|
45
|
+
* Campaigns don't need to be paid campaigns, it can be as simple as a link on your homepage
|
46
46
|
* You can update the campaign cost at any time. All calculations are "on the fly" so you're metrics will update automatically
|
47
47
|
|
48
48
|
The installation steps described above will get you going with tracking visits from particular campaigns but you still have some work to do in linking this to your users. Touchstone only really provides a benefit when you track how much revenue your users bring in and then it will compare that to the cost of your campaigns. By way of examples only, you could adopt the following pattern to record a campaign signup:
|
@@ -81,4 +81,5 @@ Tracking a user's lifetime value will depend on your transactional logic but ass
|
|
81
81
|
* Don't constrain Touchstone to an application that only has a Users table. Allow this as a configuration option
|
82
82
|
* Automatically load routes and application controller content
|
83
83
|
* Improve design
|
84
|
-
* Add some tests
|
84
|
+
* Add some tests
|
85
|
+
* Allow customisation of currency (only displays dollars at moment although amounts are not stored in a particular currency)
|
@@ -0,0 +1,71 @@
|
|
1
|
+
$iconSpritePath: asset-url('glyphicons-halflings.png',image);
|
2
|
+
$iconWhiteSpritePath: asset-url('glyphicons-halflings-white.png',image);
|
3
|
+
@import "bootstrap";
|
4
|
+
body { padding-top: 60px; }
|
5
|
+
@import "bootstrap-responsive";
|
6
|
+
|
7
|
+
/* Mixins */
|
8
|
+
|
9
|
+
@mixin float-right {
|
10
|
+
float: right;
|
11
|
+
margin: 0 5px;
|
12
|
+
}
|
13
|
+
|
14
|
+
/* Bootstrap Extensions */
|
15
|
+
|
16
|
+
.touchstone .page-header .btn {
|
17
|
+
@include float-right;
|
18
|
+
}
|
19
|
+
|
20
|
+
.alert {
|
21
|
+
text-align: center;
|
22
|
+
padding-bottom: 0px;
|
23
|
+
}
|
24
|
+
|
25
|
+
/* Styles */
|
26
|
+
|
27
|
+
.touchstone .navbar .brand {
|
28
|
+
font-weight: bold;
|
29
|
+
}
|
30
|
+
|
31
|
+
.touchstone .navbar .brand a {
|
32
|
+
color: rgb(0,0,0);
|
33
|
+
text-shadow: rgba(255,255,255,0.125) 0px 1px 0px;
|
34
|
+
text-decoration: none;
|
35
|
+
}
|
36
|
+
|
37
|
+
.touchstone .navbar .brand a:hover {
|
38
|
+
text-shadow: rgba(255,255,255,0.25) 0 0px 2px;
|
39
|
+
}
|
40
|
+
|
41
|
+
.touchstone {
|
42
|
+
font: 62.5% "Helvetica Neue", "Lucida Grande", "Trebuchet MS", Verdana, sans-serif;
|
43
|
+
font-weight: normal;
|
44
|
+
-webkit-text-shadow: 0px -1px 0px rgb(68,68,68);
|
45
|
+
}
|
46
|
+
|
47
|
+
.touchstone th,
|
48
|
+
.touchstone td {
|
49
|
+
font-size: 1.4em;
|
50
|
+
}
|
51
|
+
|
52
|
+
.touchstone .controls select:first-child,
|
53
|
+
.touchstone .controls select:last-child {
|
54
|
+
width: 60px;
|
55
|
+
}
|
56
|
+
|
57
|
+
.touchstone .controls select:nth-child(2) {
|
58
|
+
width: 140px;
|
59
|
+
}
|
60
|
+
|
61
|
+
.touchstone #financials {
|
62
|
+
margin-bottom: 10px;
|
63
|
+
}
|
64
|
+
|
65
|
+
.touchstone .stats span {
|
66
|
+
color: rgb(0,115,204);
|
67
|
+
}
|
68
|
+
|
69
|
+
.touchstone #visits {
|
70
|
+
margin-bottom: 20px;
|
71
|
+
}
|
@@ -25,4 +25,19 @@ class Touchstone::CampaignsController < ApplicationController
|
|
25
25
|
@campaign = Campaign.find(params[:id])
|
26
26
|
end
|
27
27
|
|
28
|
+
def edit
|
29
|
+
@campaign = Campaign.find(params[:id])
|
30
|
+
end
|
31
|
+
|
32
|
+
def update
|
33
|
+
@campaign = Campaign.find(params[:id])
|
34
|
+
if @campaign.update_attributes(params[:campaign]) && @campaign.save
|
35
|
+
flash[:notice] = "Campaign details updated"
|
36
|
+
redirect_to campaign_path(@campaign)
|
37
|
+
else
|
38
|
+
flash[:error] = "Could not update details at this time."
|
39
|
+
redirect_to campaign_path(@campaign)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
28
43
|
end
|
@@ -7,16 +7,32 @@
|
|
7
7
|
<%= csrf_meta_tag %>
|
8
8
|
</head>
|
9
9
|
<body class="touchstone">
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
10
|
+
<header class="navbar navbar-fixed-top">
|
11
|
+
<nav class="navbar-inner">
|
12
|
+
<div class="container">
|
13
|
+
<h1 class="brand"><%= link_to "Touchstone Metrics", root_url %></h1>
|
14
|
+
</div>
|
15
|
+
</nav>
|
16
|
+
</header>
|
17
|
+
<div id="main" role="main">
|
18
|
+
<div class="container">
|
19
|
+
<div class="content">
|
20
|
+
<div class="row">
|
21
|
+
<div class="span12">
|
22
|
+
<% flash.each do |key, value| -%>
|
23
|
+
<div class="alert alert-<%= key %>">
|
24
|
+
<a class="close" data-dismiss="alert">×</a>
|
25
|
+
<p><%= value.html_safe %></p>
|
26
|
+
</div><!-- eo .alert -->
|
27
|
+
<% end -%>
|
28
|
+
|
29
|
+
<%= yield %>
|
30
|
+
</div><!-- eo .span12 -->
|
31
|
+
</div><!-- eo .row -->
|
32
|
+
</div><!-- eo .content -->
|
33
|
+
<footer>
|
34
|
+
</footer>
|
35
|
+
</div> <!--! end of .container -->
|
36
|
+
</div> <!--! end of #main -->
|
21
37
|
</body>
|
22
38
|
</html>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<%= form_for @campaign, :html => {:class => "well span6"} do |f| %>
|
2
|
+
|
3
|
+
<div class="control-group">
|
4
|
+
<%= f.label :name, :class => "control-label" %>
|
5
|
+
<div class="controls">
|
6
|
+
<%= f.text_field :name %>
|
7
|
+
</div><!-- eo .controls -->
|
8
|
+
</div><!-- eo .control-group -->
|
9
|
+
|
10
|
+
<div class="control-group">
|
11
|
+
<%= f.label :notes, :class => "control-label" %>
|
12
|
+
<div class="controls">
|
13
|
+
<%= f.text_area :notes, :class => "span6", :rows => 5 %>
|
14
|
+
</div><!-- eo .controls -->
|
15
|
+
</div><!-- eo .control-group -->
|
16
|
+
|
17
|
+
<div class="control-group">
|
18
|
+
<%= f.label :start_date, :class => "control-label" %>
|
19
|
+
<div class="controls">
|
20
|
+
<%= f.date_select :start_date, :order => [:day, :month, :year] %>
|
21
|
+
</div><!-- eo .controls -->
|
22
|
+
</div><!-- eo .control-group -->
|
23
|
+
|
24
|
+
<div class="control-group">
|
25
|
+
<%= f.label :end_date, :class => "control-label" %>
|
26
|
+
<div class="controls">
|
27
|
+
<%= f.date_select :end_date, :order => [:day, :month, :year] %>
|
28
|
+
</div><!-- eo .controls -->
|
29
|
+
</div><!-- eo .control-group -->
|
30
|
+
|
31
|
+
<div class="control-group">
|
32
|
+
<%= f.label :spend_amount, :class => "control-label" %>
|
33
|
+
<div class="controls">
|
34
|
+
<%= f.text_field :spend_amount %>
|
35
|
+
</div><!-- eo .controls -->
|
36
|
+
</div><!-- eo .control-group -->
|
37
|
+
|
38
|
+
<p>
|
39
|
+
<%= f.submit :class => "btn btn-primary" %> or <%= link_to "cancel", campaigns_path %>
|
40
|
+
</p>
|
41
|
+
|
42
|
+
<% end %>
|
@@ -1,8 +1,9 @@
|
|
1
|
-
<
|
1
|
+
<div class="page-header">
|
2
|
+
<%= link_to "<i class='icon-plus icon-white'></i> New campaign".html_safe, new_campaign_url, :class => "btn btn-primary" %>
|
3
|
+
<h1>Campaigns</h1>
|
4
|
+
</div><!-- eo .page-header -->
|
2
5
|
|
3
|
-
<
|
4
|
-
|
5
|
-
<table>
|
6
|
+
<table class="table table-striped">
|
6
7
|
<thead>
|
7
8
|
<tr>
|
8
9
|
<th scope="col" width="15%">Campaign ID</th>
|
@@ -1,29 +1,3 @@
|
|
1
|
-
<h1>New campaign</h1>
|
1
|
+
<div class="page-header"><h1>New campaign</h1></div>
|
2
2
|
|
3
|
-
<%=
|
4
|
-
|
5
|
-
<p>
|
6
|
-
<%= f.label :name %>
|
7
|
-
<%= f.text_field :name %>
|
8
|
-
</p>
|
9
|
-
<p>
|
10
|
-
<%= f.label :notes %>
|
11
|
-
<%= f.text_area :notes %>
|
12
|
-
</p>
|
13
|
-
<p>
|
14
|
-
<%= f.label :start_date %>
|
15
|
-
<%= f.date_select :start_date, :order => [:day, :month, :year] %>
|
16
|
-
</p>
|
17
|
-
<p>
|
18
|
-
<%= f.label :end_date %>
|
19
|
-
<%= f.date_select :end_date, :order => [:day, :month, :year] %>
|
20
|
-
</p>
|
21
|
-
<p>
|
22
|
-
<%= f.label :spend_amount %>
|
23
|
-
<%= f.text_field :spend_amount %>
|
24
|
-
</p>
|
25
|
-
<p>
|
26
|
-
<%= f.submit "Create campaign" %>
|
27
|
-
</p>
|
28
|
-
|
29
|
-
<% end %>
|
3
|
+
<%= render :partial => 'campaign_form' %>
|
@@ -1,27 +1,31 @@
|
|
1
|
-
<
|
2
|
-
|
3
|
-
<
|
1
|
+
<div class="page-header">
|
2
|
+
<%= link_to "<i class='icon-pencil'></i> Edit".html_safe, edit_campaign_url(@campaign), :class => "btn" %>
|
3
|
+
<%= link_to "<i class='icon-home icon-white'></i> Home".html_safe, campaigns_url, :class => "btn btn-primary" %>
|
4
|
+
<h1>Campaign - <%= @campaign.name %> <small><abbr title="Append this to the end of each link used in this campaign e.g. http://yourdomain.com/?cid=1">?cid=<%= @campaign.id %></abbr></small></h1>
|
5
|
+
</div><!-- eo .page-header -->
|
4
6
|
|
5
7
|
<section id="campaign-info">
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
<% if @campaign.end_date < Date.today %>
|
9
|
+
<p class="ended">This campaign has ended.</p>
|
10
|
+
<% else %>
|
11
|
+
<p class="running">This campaign is currently running.</p>
|
12
|
+
<% end %>
|
9
13
|
</section><!-- #campaign-info -->
|
10
14
|
|
11
15
|
<section id="financials" class="stats">
|
12
|
-
<h3
|
13
|
-
<h3
|
14
|
-
<h3
|
16
|
+
<h3>Campaign Spend: <span>$<%= sprintf("%.2f", @campaign.spend_amount) %></span></h3>
|
17
|
+
<h3>Campaign Revenue: <span>$<%= sprintf("%.2f", @campaign.revenue) %></span></h3>
|
18
|
+
<h3>Return on investment: <span><%= sprintf("%.2f", @campaign.roi) %>%</span></h3>
|
15
19
|
</section>
|
16
20
|
|
17
21
|
<section id="visits" class="stats">
|
18
|
-
<h3
|
19
|
-
<h3
|
22
|
+
<h3>Unique Campaign Visits: <span><%= @campaign.visits %></span></h3>
|
23
|
+
<h3>Conversion Rate: <span><%= sprintf("%.2f",@campaign.conversion_rate) %>%</span></h3>
|
20
24
|
</section>
|
21
25
|
|
22
26
|
<h2>Signups via this campaign</h2>
|
23
27
|
|
24
|
-
<table>
|
28
|
+
<table class="table table-striped">
|
25
29
|
<thead>
|
26
30
|
<tr>
|
27
31
|
<th scope="col" width="25%">Name</th>
|
data/config/routes.rb
CHANGED
data/lib/touchstone/engine.rb
CHANGED
data/lib/touchstone/version.rb
CHANGED
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.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -27,6 +27,38 @@ dependencies:
|
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '3.2'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sass-rails
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '3.1'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.1'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bootstrap-sass
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
30
62
|
- !ruby/object:Gem::Dependency
|
31
63
|
name: mysql2
|
32
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,7 +85,7 @@ extra_rdoc_files: []
|
|
53
85
|
files:
|
54
86
|
- app/assets/javascripts/touchstone/application.js
|
55
87
|
- app/assets/stylesheets/touchstone/application.css
|
56
|
-
- app/assets/stylesheets/touchstone/touchstone.css
|
88
|
+
- app/assets/stylesheets/touchstone/touchstone.css.scss
|
57
89
|
- app/controllers/touchstone/application_controller.rb
|
58
90
|
- app/controllers/touchstone/campaigns_controller.rb
|
59
91
|
- app/helpers/touchstone/application_helper.rb
|
@@ -62,6 +94,8 @@ files:
|
|
62
94
|
- app/models/campaign_visit.rb
|
63
95
|
- app/views/layouts/touchstone/application.html.erb
|
64
96
|
- app/views/layouts/touchstone/touchstone.html.erb
|
97
|
+
- app/views/touchstone/campaigns/_campaign_form.html.erb
|
98
|
+
- app/views/touchstone/campaigns/edit.html.erb
|
65
99
|
- app/views/touchstone/campaigns/index.html.erb
|
66
100
|
- app/views/touchstone/campaigns/new.html.erb
|
67
101
|
- app/views/touchstone/campaigns/show.html.erb
|
@@ -122,7 +156,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
156
|
version: '0'
|
123
157
|
segments:
|
124
158
|
- 0
|
125
|
-
hash:
|
159
|
+
hash: 2775789728872251427
|
126
160
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
161
|
none: false
|
128
162
|
requirements:
|
@@ -131,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
165
|
version: '0'
|
132
166
|
segments:
|
133
167
|
- 0
|
134
|
-
hash:
|
168
|
+
hash: 2775789728872251427
|
135
169
|
requirements: []
|
136
170
|
rubyforge_project:
|
137
171
|
rubygems_version: 1.8.20
|
@@ -1,113 +0,0 @@
|
|
1
|
-
.touchstone {
|
2
|
-
font: 62.5% "Helvetica Neue", "Lucida Grande", "Trebuchet MS", Verdana, sans-serif;
|
3
|
-
font-weight: normal;
|
4
|
-
-webkit-text-shadow: 0px -1px 0px rgb(68,68,68);
|
5
|
-
}
|
6
|
-
|
7
|
-
.touchstone .wrapper {
|
8
|
-
width: 960px;
|
9
|
-
margin: 10px auto;
|
10
|
-
}
|
11
|
-
|
12
|
-
.touchstone h1 {
|
13
|
-
font-size: 3em;
|
14
|
-
margin-bottom: 1em;
|
15
|
-
}
|
16
|
-
|
17
|
-
.touchstone h2 {
|
18
|
-
font-size: 2.4em;
|
19
|
-
margin-bottom: 1em;
|
20
|
-
}
|
21
|
-
|
22
|
-
.touchstone h3 {
|
23
|
-
font-size: 2em;
|
24
|
-
margin-bottom: 1em;
|
25
|
-
}
|
26
|
-
|
27
|
-
.touchstone label {
|
28
|
-
float: left;
|
29
|
-
width: 120px;
|
30
|
-
margin-top: 2px;
|
31
|
-
font-weight: bold;
|
32
|
-
}
|
33
|
-
|
34
|
-
.touchstone p {
|
35
|
-
font-size: 1.4em;
|
36
|
-
margin-bottom: 1em;
|
37
|
-
}
|
38
|
-
|
39
|
-
.touchstone table {
|
40
|
-
font-size: 1.4em;
|
41
|
-
border: 1px solid rgb(153,178,183);
|
42
|
-
border-spacing: 0px 0px;
|
43
|
-
width: 100%;
|
44
|
-
}
|
45
|
-
|
46
|
-
.touchstone thead th {
|
47
|
-
padding: 8px 5px;
|
48
|
-
vertical-align: middle;
|
49
|
-
text-align: left;
|
50
|
-
text-transform: uppercase;
|
51
|
-
border-bottom: 1px solid rgb(153,178,183);
|
52
|
-
border-right: 1px solid rgb(153,178,183);
|
53
|
-
background-color: rgb(213,222,217);
|
54
|
-
}
|
55
|
-
|
56
|
-
.touchstone td {
|
57
|
-
font-size: 1.2em;
|
58
|
-
padding: 5px;
|
59
|
-
border-bottom: 1px solid rgb(153,178,183);
|
60
|
-
border-right: 1px solid rgb(153,178,183);
|
61
|
-
}
|
62
|
-
|
63
|
-
.touchstone #new-campaign {
|
64
|
-
float: right;
|
65
|
-
margin-top: 10px;
|
66
|
-
}
|
67
|
-
|
68
|
-
.touchstone #flash_notice {
|
69
|
-
background-color: #65ce6f;
|
70
|
-
border: 1px solid #00cf13;
|
71
|
-
font-weight: bold;
|
72
|
-
text-align: center;
|
73
|
-
padding: 5px 0;
|
74
|
-
margin: 0 200px 10px 200px;
|
75
|
-
}
|
76
|
-
|
77
|
-
.touchstone #flash_error {
|
78
|
-
background-color: #ff7272;
|
79
|
-
border: 1px solid #ff1515;
|
80
|
-
font-weight: bold;
|
81
|
-
text-align: center;
|
82
|
-
padding: 5px 0;
|
83
|
-
margin: 0 200px 10px 200px;
|
84
|
-
}
|
85
|
-
|
86
|
-
.touchstone #campaign-info {
|
87
|
-
margin: -3em 0 0 0;
|
88
|
-
font-size: 0.8em;
|
89
|
-
}
|
90
|
-
|
91
|
-
.touchstone #campaign-info p {
|
92
|
-
margin-top: -1em;
|
93
|
-
}
|
94
|
-
|
95
|
-
.touchstone #financials {
|
96
|
-
margin-bottom: 10px;
|
97
|
-
}
|
98
|
-
|
99
|
-
.touchstone .stats h3 {
|
100
|
-
display: inline;
|
101
|
-
}
|
102
|
-
|
103
|
-
.touchstone .stats span {
|
104
|
-
color: rgb(153,178,183);
|
105
|
-
}
|
106
|
-
|
107
|
-
.touchstone .stats h3:not(:first-of-type) {
|
108
|
-
margin-left: 15px;
|
109
|
-
}
|
110
|
-
|
111
|
-
.touchstone #visits {
|
112
|
-
margin-bottom: 20px;
|
113
|
-
}
|