glengarry 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +60 -0
- data/Rakefile +12 -0
- data/app/assets/images/glengarry/bg.png +0 -0
- data/app/assets/javascripts/glengarry/application.js +15 -0
- data/app/assets/javascripts/glengarry/dashboard.js +2 -0
- data/app/assets/javascripts/glengarry/email_leads.js +2 -0
- data/app/assets/stylesheets/glengarry/application.css +13 -0
- data/app/assets/stylesheets/glengarry/dashboard.css +4 -0
- data/app/assets/stylesheets/glengarry/email_leads.css.erb +147 -0
- data/app/assets/stylesheets/scaffold.css +56 -0
- data/app/controllers/glengarry/application_controller.rb +12 -0
- data/app/controllers/glengarry/email_leads_controller.rb +78 -0
- data/app/helpers/glengarry/application_helper.rb +10 -0
- data/app/helpers/glengarry/dashboard_helper.rb +4 -0
- data/app/helpers/glengarry/email_leads_helper.rb +4 -0
- data/app/models/glengarry/email_lead.rb +34 -0
- data/app/views/glengarry/email_leads/_form.html.erb +45 -0
- data/app/views/glengarry/email_leads/edit.html.erb +6 -0
- data/app/views/glengarry/email_leads/index.html.erb +39 -0
- data/app/views/glengarry/email_leads/new.html.erb +5 -0
- data/app/views/glengarry/email_leads/show.html.erb +40 -0
- data/app/views/layouts/glengarry/application.html.erb +21 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20120804175235_create_glengarry_email_leads.rb +15 -0
- data/lib/email_validator.rb +36 -0
- data/lib/glengarry/engine.rb +12 -0
- data/lib/glengarry/version.rb +3 -0
- data/lib/glengarry.rb +4 -0
- data/lib/tasks/glengarry_tasks.rake +0 -0
- metadata +193 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
![Glengarry](http://f.cl.ly/items/3X0b2u0Z2F2d3E1A3q0v/Screen%20Shot%202012-09-04%20at%202.50.35%20PM.png)
|
2
|
+
|
3
|
+
## Glengarry
|
4
|
+
|
5
|
+
Glengarry is a Rails 3.2 engine that allows you to collect and provides an interface for collecting email leads, CAUSE YOU SHOULD ALWAYS BE CLOSIN'. It's a hack that I wrote quickly to give me what I
|
6
|
+
needed - it may do the same for you, but I make no representations to its quality or completeness.
|
7
|
+
|
8
|
+
The email address, IP Address submitting the email address, and reverse geo-coded latitude, longitude, city and country are stored and presented to you in this lovely UI:
|
9
|
+
![UI](http://f.cl.ly/items/2p2y0L3g3V0B1S0h1A37/Screen%20Shot%202012-09-04%20at%203.45.21%20PM.png)
|
10
|
+
|
11
|
+
You can also download all of the captured emails as a CSV file. JOY.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add the gem to your gemfile as follows:
|
16
|
+
|
17
|
+
gem 'glengarry'
|
18
|
+
|
19
|
+
Then run ```bundle install```. Now install the migrations:
|
20
|
+
|
21
|
+
bundle exec glengarry:install:migrations
|
22
|
+
|
23
|
+
Now simply mount the engine at the path of your choice in your ```config\routes.rb``` file as follows:
|
24
|
+
|
25
|
+
mount Glengarry::Engine => "/glengarry"
|
26
|
+
|
27
|
+
## Using Glengarry in you App
|
28
|
+
|
29
|
+
Just make a simple form like so:
|
30
|
+
|
31
|
+
= form_for(Glengarry::EmailLead.new, :url=>glengarry.email_leads_path) do |f|
|
32
|
+
- if flash[:notice].present?
|
33
|
+
= content_tag :div, flash[:notice], :id => "flash_notice" if flash[:notice].is_a?(String)
|
34
|
+
= f.text_field :email
|
35
|
+
= f.submit "Submit"
|
36
|
+
|
37
|
+
After ```POST```ing to the Engine's path, a redirect is issued back to the location the ```POST``` came from. You probably want to use an ```AJAX``` request anyway.
|
38
|
+
|
39
|
+
## Lockin' that down
|
40
|
+
|
41
|
+
You can add basic HTTP authentication by creating an initializer in your ```config``` directory like so:
|
42
|
+
|
43
|
+
Glengarry::ApplicationController.authenticator = proc {
|
44
|
+
authenticate_or_request_with_http_basic 'Glengarry' do |user_name, password|
|
45
|
+
user_name == 'username' && password == 'password'
|
46
|
+
end
|
47
|
+
}
|
48
|
+
|
49
|
+
## Specs
|
50
|
+
|
51
|
+
You'll have to use the dummy application under spec/dummy to first create the test database. Then simply do the usual:
|
52
|
+
|
53
|
+
bundle exec rake spec
|
54
|
+
|
55
|
+
## Complainin', bro?
|
56
|
+
|
57
|
+
You can get me on Twitter as @MrMcDowall or on App.net as @jmd - have at it, hopefully I'll see you at the Country Club.
|
58
|
+
|
59
|
+
## License
|
60
|
+
It's all MIT License. Do what you want, check the MIT-LICENSE file, and I'm not responsible for anything you do with it. Bon appetit.
|
data/Rakefile
ADDED
Binary file
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// the compiled file.
|
9
|
+
//
|
10
|
+
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
11
|
+
// GO AFTER THE REQUIRES BELOW.
|
12
|
+
//
|
13
|
+
//= require jquery
|
14
|
+
//= require jquery_ujs
|
15
|
+
//= require_tree .
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
@@ -0,0 +1,147 @@
|
|
1
|
+
body {
|
2
|
+
background: url("<%= asset_path('glengarry/bg.png') %>");
|
3
|
+
font-family: "PT Sans Narrow", "Verdana";
|
4
|
+
font-weight: 400;
|
5
|
+
padding: 0;
|
6
|
+
margin: 0;
|
7
|
+
letter-spacing: .03em;
|
8
|
+
line-height: 1.5;
|
9
|
+
}
|
10
|
+
|
11
|
+
header {
|
12
|
+
background: #FFF;
|
13
|
+
height: 120px;
|
14
|
+
padding: 0;
|
15
|
+
margin: 0 0 1em 0;
|
16
|
+
}
|
17
|
+
|
18
|
+
h1 {
|
19
|
+
width: 800px;
|
20
|
+
height: 120px;
|
21
|
+
padding-top: 30px;
|
22
|
+
text-align: right;
|
23
|
+
margin: 0px auto;
|
24
|
+
}
|
25
|
+
|
26
|
+
h1 small {
|
27
|
+
display: block;
|
28
|
+
font-size: 12px;
|
29
|
+
font-weight: 100;
|
30
|
+
font-family: helvetica, sans-serif;
|
31
|
+
}
|
32
|
+
|
33
|
+
#page {
|
34
|
+
width: 800px;
|
35
|
+
margin: 0px auto;
|
36
|
+
}
|
37
|
+
|
38
|
+
#page h1 {
|
39
|
+
width: 100%;
|
40
|
+
text-align: center;
|
41
|
+
}
|
42
|
+
|
43
|
+
table {
|
44
|
+
border: 1px solid #DFDFDF;
|
45
|
+
background-color: #F9F9F9;
|
46
|
+
width: 100%;
|
47
|
+
margin-top: 1.6em;
|
48
|
+
-moz-border-radius: 3px;
|
49
|
+
-webkit-border-radius: 3px;
|
50
|
+
border-radius: 3px;
|
51
|
+
font-family: Arial,"Bitstream Vera Sans",Helvetica,Verdana,sans-serif;
|
52
|
+
color: #333;
|
53
|
+
margin-bottom: 2em;
|
54
|
+
}
|
55
|
+
table td, table th {
|
56
|
+
border-top-color: white;
|
57
|
+
border-bottom: 1px solid #DFDFDF;
|
58
|
+
color: #555;
|
59
|
+
}
|
60
|
+
table th {
|
61
|
+
text-shadow: rgba(255, 255, 255, 0.796875) 0px 1px 0px;
|
62
|
+
font-family: Georgia,"Times New Roman","Bitstream Charter",Times,serif;
|
63
|
+
font-weight: normal;
|
64
|
+
padding: 7px 7px 8px;
|
65
|
+
text-align: left;
|
66
|
+
line-height: 1.3em;
|
67
|
+
font-size: 14px;
|
68
|
+
height: 50px;
|
69
|
+
}
|
70
|
+
|
71
|
+
table tr {
|
72
|
+
height: 3em;
|
73
|
+
}
|
74
|
+
|
75
|
+
table td {
|
76
|
+
font-size: 12px;
|
77
|
+
padding: 4px 7px 2px;
|
78
|
+
vertical-align: top;
|
79
|
+
}
|
80
|
+
|
81
|
+
.pagination {
|
82
|
+
text-align: center;
|
83
|
+
padding: 1em 0 0 0 ;
|
84
|
+
}
|
85
|
+
|
86
|
+
.pagination .page, .first, .prev, .next, .last {
|
87
|
+
width: 32px;
|
88
|
+
height: 32px;
|
89
|
+
display: inline-block;
|
90
|
+
}
|
91
|
+
|
92
|
+
.pagination .first, .last {
|
93
|
+
width: 64px;
|
94
|
+
}
|
95
|
+
|
96
|
+
/* cupid blue (inspired by okcupid.com)
|
97
|
+
*******************************************************************************/
|
98
|
+
button {
|
99
|
+
float: right;
|
100
|
+
background-color: #d7e5f5;
|
101
|
+
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #d7e5f5), color-stop(100%, #cbe0f5));
|
102
|
+
background-image: -webkit-linear-gradient(top, #d7e5f5, #cbe0f5);
|
103
|
+
background-image: -moz-linear-gradient(top, #d7e5f5, #cbe0f5);
|
104
|
+
background-image: -ms-linear-gradient(top, #d7e5f5, #cbe0f5);
|
105
|
+
background-image: -o-linear-gradient(top, #d7e5f5, #cbe0f5);
|
106
|
+
background-image: linear-gradient(top, #d7e5f5, #cbe0f5);
|
107
|
+
border-top: 1px solid #abbbcc;
|
108
|
+
border-left: 1px solid #a7b6c7;
|
109
|
+
border-bottom: 1px solid #a1afbf;
|
110
|
+
border-right: 1px solid #a7b6c7;
|
111
|
+
-webkit-border-radius: 12px;
|
112
|
+
-moz-border-radius: 12px;
|
113
|
+
border-radius: 12px;
|
114
|
+
-webkit-box-shadow: inset 0 1px 0 0 white;
|
115
|
+
-moz-box-shadow: inset 0 1px 0 0 white;
|
116
|
+
box-shadow: inset 0 1px 0 0 white;
|
117
|
+
color: #1a3e66;
|
118
|
+
font: normal 11px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
|
119
|
+
line-height: 1;
|
120
|
+
padding: 6px 0 7px 0;
|
121
|
+
text-align: center;
|
122
|
+
text-shadow: 0 1px 1px #fff;
|
123
|
+
width: 150px; }
|
124
|
+
button:hover {
|
125
|
+
background-color: #ccd9e8;
|
126
|
+
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ccd9e8), color-stop(100%, #c1d4e8));
|
127
|
+
background-image: -webkit-linear-gradient(top, #ccd9e8, #c1d4e8);
|
128
|
+
background-image: -moz-linear-gradient(top, #ccd9e8, #c1d4e8);
|
129
|
+
background-image: -ms-linear-gradient(top, #ccd9e8, #c1d4e8);
|
130
|
+
background-image: -o-linear-gradient(top, #ccd9e8, #c1d4e8);
|
131
|
+
background-image: linear-gradient(top, #ccd9e8, #c1d4e8);
|
132
|
+
border-top: 1px solid #a1afbf;
|
133
|
+
border-left: 1px solid #9caaba;
|
134
|
+
border-bottom: 1px solid #96a3b3;
|
135
|
+
border-right: 1px solid #9caaba;
|
136
|
+
-webkit-box-shadow: inset 0 1px 0 0 #f2f2f2;
|
137
|
+
-moz-box-shadow: inset 0 1px 0 0 #f2f2f2;
|
138
|
+
box-shadow: inset 0 1px 0 0 #f2f2f2;
|
139
|
+
color: #163659;
|
140
|
+
cursor: pointer; }
|
141
|
+
button:active {
|
142
|
+
border: 1px solid #8c98a7;
|
143
|
+
-webkit-box-shadow: inset 0 0 4px 2px #abbccf, 0 0 1px 0 #eeeeee;
|
144
|
+
-moz-box-shadow: inset 0 0 4px 2px #abbccf, 0 0 1px 0 #eeeeee;
|
145
|
+
box-shadow: inset 0 0 4px 2px #abbccf, 0 0 1px 0 #eeeeee;
|
146
|
+
}
|
147
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
body { background-color: #fff; color: #333; }
|
2
|
+
|
3
|
+
body, p, ol, ul, td {
|
4
|
+
font-family: verdana, arial, helvetica, sans-serif;
|
5
|
+
font-size: 13px;
|
6
|
+
line-height: 18px;
|
7
|
+
}
|
8
|
+
|
9
|
+
pre {
|
10
|
+
background-color: #eee;
|
11
|
+
padding: 10px;
|
12
|
+
font-size: 11px;
|
13
|
+
}
|
14
|
+
|
15
|
+
a { color: #000; }
|
16
|
+
a:visited { color: #666; }
|
17
|
+
a:hover { color: #fff; background-color:#000; }
|
18
|
+
|
19
|
+
div.field, div.actions {
|
20
|
+
margin-bottom: 10px;
|
21
|
+
}
|
22
|
+
|
23
|
+
#notice {
|
24
|
+
color: green;
|
25
|
+
}
|
26
|
+
|
27
|
+
.field_with_errors {
|
28
|
+
padding: 2px;
|
29
|
+
background-color: red;
|
30
|
+
display: table;
|
31
|
+
}
|
32
|
+
|
33
|
+
#error_explanation {
|
34
|
+
width: 450px;
|
35
|
+
border: 2px solid red;
|
36
|
+
padding: 7px;
|
37
|
+
padding-bottom: 0;
|
38
|
+
margin-bottom: 20px;
|
39
|
+
background-color: #f0f0f0;
|
40
|
+
}
|
41
|
+
|
42
|
+
#error_explanation h2 {
|
43
|
+
text-align: left;
|
44
|
+
font-weight: bold;
|
45
|
+
padding: 5px 5px 5px 15px;
|
46
|
+
font-size: 12px;
|
47
|
+
margin: -7px;
|
48
|
+
margin-bottom: 0px;
|
49
|
+
background-color: #c00;
|
50
|
+
color: #fff;
|
51
|
+
}
|
52
|
+
|
53
|
+
#error_explanation ul li {
|
54
|
+
font-size: 12px;
|
55
|
+
list-style: square;
|
56
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'glengarry/application_controller'
|
2
|
+
|
3
|
+
module Glengarry
|
4
|
+
class ApplicationController < ActionController::Base
|
5
|
+
cattr_accessor :authenticator
|
6
|
+
|
7
|
+
# This is a useful pattern I found in the Tolk source: https://github.com/tolk/tolk/blob/master/app/controllers/tolk/application_controller.rb
|
8
|
+
def authenticate
|
9
|
+
self.authenticator.bind(self).call if self.authenticator && self.authenticator.respond_to?(:call)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require_dependency "glengarry/application_controller"
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
module Glengarry
|
5
|
+
class EmailLeadsController < ApplicationController
|
6
|
+
|
7
|
+
before_filter :authenticate, :except=>:create
|
8
|
+
|
9
|
+
def index
|
10
|
+
@email_leads = EmailLead.page(params[:page]).per(10)
|
11
|
+
@email_lead_count = EmailLead.count
|
12
|
+
|
13
|
+
respond_to do |format|
|
14
|
+
format.html # index.html.erb
|
15
|
+
format.json { render json: @email_leads }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def show
|
20
|
+
@email_lead = EmailLead.find(params[:id])
|
21
|
+
|
22
|
+
respond_to do |format|
|
23
|
+
format.html # show.html.erb
|
24
|
+
format.json { render json: @email_lead }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def create
|
29
|
+
@email_lead = EmailLead.new(params[:email_lead])
|
30
|
+
@email_lead.ip_address = request.ip
|
31
|
+
@email_lead.referer = request.referer
|
32
|
+
|
33
|
+
respond_to do |format|
|
34
|
+
if @email_lead.save
|
35
|
+
format.html { redirect_to :back, notice: ['Thank you.'] }
|
36
|
+
format.json { render json: @email_lead, status: :created, location: @email_lead }
|
37
|
+
else
|
38
|
+
format.html { redirect_to :back, notice: @email_lead.errors }
|
39
|
+
format.json { render json: @email_lead.errors, status: :invalid_email }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def destroy
|
45
|
+
@email_lead = EmailLead.find(params[:id])
|
46
|
+
@email_lead.destroy
|
47
|
+
|
48
|
+
respond_to do |format|
|
49
|
+
format.html { redirect_to email_leads_url }
|
50
|
+
format.json { head :no_content }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def download
|
55
|
+
respond_to do |format|
|
56
|
+
format.csv {
|
57
|
+
@emails = EmailLead.all
|
58
|
+
@columns = ["Email", "IP Address", "Referer", "Latitude", "Longitude", "City", "Country"].to_csv
|
59
|
+
@filename = "emails-#{Date.today.to_s(:db)}"
|
60
|
+
|
61
|
+
self.response.headers["Content-Type"] ||= 'text/csv'
|
62
|
+
self.response.headers["Content-Disposition"] = "attachment; filename=#{@filename}"
|
63
|
+
self.response.headers["Content-Transfer-Encoding"] = "binary"
|
64
|
+
|
65
|
+
self.response_body = Enumerator.new do |y|
|
66
|
+
@emails.each_with_index do |email, i|
|
67
|
+
if i == 0
|
68
|
+
y << @columns
|
69
|
+
end
|
70
|
+
y << [email.email, email.ip_address, email.referer, email.lat, email.long, email.city, email.country].to_csv
|
71
|
+
end
|
72
|
+
end
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Glengarry
|
2
|
+
module ApplicationHelper
|
3
|
+
def pagination_count
|
4
|
+
count = content_for(:email_count).to_i
|
5
|
+
phrase = count < 10 ? "Showing #{count} of #{count}" : "Showing 10 of #{count}"
|
6
|
+
return phrase if content_for?(:email_count)
|
7
|
+
"No Leads Yet"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative '../../../lib/email_validator'
|
2
|
+
|
3
|
+
module Glengarry
|
4
|
+
|
5
|
+
class EmailLead < ActiveRecord::Base
|
6
|
+
extend Geocoder::Model::ActiveRecord
|
7
|
+
|
8
|
+
attr_accessible :email
|
9
|
+
|
10
|
+
validates :email, :presence => true, :uniqueness => true, :'Glengarry::Email'=>true
|
11
|
+
|
12
|
+
geocoded_by :ip_address, :latitude => :lat, :longitude => :long
|
13
|
+
reverse_geocoded_by :lat, :long do |obj, results|
|
14
|
+
obj.set_reversed_location(results)
|
15
|
+
end
|
16
|
+
|
17
|
+
after_validation :full_geocode
|
18
|
+
|
19
|
+
def set_reversed_location(results)
|
20
|
+
if geo = results.first
|
21
|
+
self.city = geo.city
|
22
|
+
self.country = geo.country
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def full_geocode
|
29
|
+
geocode
|
30
|
+
reverse_geocode
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<%= form_for(@email_lead) do |f| %>
|
2
|
+
<% if @email_lead.errors.any? %>
|
3
|
+
<div id="error_explanation">
|
4
|
+
<h2><%= pluralize(@email_lead.errors.count, "error") %> prohibited this email_lead from being saved:</h2>
|
5
|
+
|
6
|
+
<ul>
|
7
|
+
<% @email_lead.errors.full_messages.each do |msg| %>
|
8
|
+
<li><%= msg %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<div class="field">
|
15
|
+
<%= f.label :email %><br />
|
16
|
+
<%= f.text_field :email %>
|
17
|
+
</div>
|
18
|
+
<div class="field">
|
19
|
+
<%= f.label :ip_address %><br />
|
20
|
+
<%= f.text_field :ip_address %>
|
21
|
+
</div>
|
22
|
+
<div class="field">
|
23
|
+
<%= f.label :referer %><br />
|
24
|
+
<%= f.text_field :referer %>
|
25
|
+
</div>
|
26
|
+
<div class="field">
|
27
|
+
<%= f.label :lat %><br />
|
28
|
+
<%= f.text_field :lat %>
|
29
|
+
</div>
|
30
|
+
<div class="field">
|
31
|
+
<%= f.label :long %><br />
|
32
|
+
<%= f.text_field :long %>
|
33
|
+
</div>
|
34
|
+
<div class="field">
|
35
|
+
<%= f.label :city %><br />
|
36
|
+
<%= f.text_field :city %>
|
37
|
+
</div>
|
38
|
+
<div class="field">
|
39
|
+
<%= f.label :country %><br />
|
40
|
+
<%= f.text_field :country %>
|
41
|
+
</div>
|
42
|
+
<div class="actions">
|
43
|
+
<%= f.submit %>
|
44
|
+
</div>
|
45
|
+
<% end %>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<% content_for(:email_count) do %>
|
2
|
+
<%= @email_lead_count if @email_lead_count > 0%>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<% if @email_lead_count >0 %>
|
6
|
+
<a href="<%=download_csv_path%>.csv"><button>Download all as CSV</button></a>
|
7
|
+
<table>
|
8
|
+
<tr>
|
9
|
+
<th>Email</th>
|
10
|
+
<th>Ip address</th>
|
11
|
+
<th>Referer</th>
|
12
|
+
<th>Lat</th>
|
13
|
+
<th>Long</th>
|
14
|
+
<th>City</th>
|
15
|
+
<th>Country</th>
|
16
|
+
<th></th>
|
17
|
+
</tr>
|
18
|
+
|
19
|
+
<% @email_leads.each do |email_lead| %>
|
20
|
+
<tr>
|
21
|
+
<td><%= email_lead.email %></td>
|
22
|
+
<td><%= email_lead.ip_address %></td>
|
23
|
+
<td><%= email_lead.referer %></td>
|
24
|
+
<td><%= email_lead.lat %></td>
|
25
|
+
<td><%= email_lead.long %></td>
|
26
|
+
<td><%= email_lead.city %></td>
|
27
|
+
<td><%= email_lead.country %></td>
|
28
|
+
<td><%= link_to 'Destroy', email_lead, method: :delete, data: { confirm: 'Are you sure?' } %></td>
|
29
|
+
</tr>
|
30
|
+
<% end %>
|
31
|
+
<tr>
|
32
|
+
<td colspan="9">
|
33
|
+
<%= paginate @email_leads %>
|
34
|
+
</td>
|
35
|
+
</tr>
|
36
|
+
</table>
|
37
|
+
<% else %>
|
38
|
+
<h1>No Email Leads captured yet :(</h1>
|
39
|
+
<% end %>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<p id="notice"><%= notice %></p>
|
2
|
+
|
3
|
+
<p>
|
4
|
+
<b>Email:</b>
|
5
|
+
<%= @email_lead.email %>
|
6
|
+
</p>
|
7
|
+
|
8
|
+
<p>
|
9
|
+
<b>Ip address:</b>
|
10
|
+
<%= @email_lead.ip_address %>
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<p>
|
14
|
+
<b>Referer:</b>
|
15
|
+
<%= @email_lead.referer %>
|
16
|
+
</p>
|
17
|
+
|
18
|
+
<p>
|
19
|
+
<b>Lat:</b>
|
20
|
+
<%= @email_lead.lat %>
|
21
|
+
</p>
|
22
|
+
|
23
|
+
<p>
|
24
|
+
<b>Long:</b>
|
25
|
+
<%= @email_lead.long %>
|
26
|
+
</p>
|
27
|
+
|
28
|
+
<p>
|
29
|
+
<b>City:</b>
|
30
|
+
<%= @email_lead.city %>
|
31
|
+
</p>
|
32
|
+
|
33
|
+
<p>
|
34
|
+
<b>Country:</b>
|
35
|
+
<%= @email_lead.country %>
|
36
|
+
</p>
|
37
|
+
|
38
|
+
|
39
|
+
<%= link_to 'Edit', edit_email_lead_path(@email_lead) %> |
|
40
|
+
<%= link_to 'Back', email_leads_path %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Glengarry</title>
|
5
|
+
<%= stylesheet_link_tag "glengarry/application", :media => "all" %>
|
6
|
+
<link href='http://fonts.googleapis.com/css?family=PT+Sans+Narrow:regular,bold' rel='stylesheet' type='text/css'>
|
7
|
+
<%= javascript_include_tag "glengarry/application" %>
|
8
|
+
<%= csrf_meta_tags %>
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
<header>
|
12
|
+
<h1>Captured Email Leads
|
13
|
+
<small><%= pagination_count %></small>
|
14
|
+
</h1>
|
15
|
+
</header>
|
16
|
+
<div id="page">
|
17
|
+
<%= yield %>
|
18
|
+
</div>
|
19
|
+
|
20
|
+
</body>
|
21
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateGlengarryEmailLeads < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :glengarry_email_leads do |t|
|
4
|
+
t.string :email
|
5
|
+
t.string :ip_address
|
6
|
+
t.string :referer
|
7
|
+
t.float :lat
|
8
|
+
t.float :long
|
9
|
+
t.string :city
|
10
|
+
t.string :country
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'mail'
|
2
|
+
|
3
|
+
module Glengarry
|
4
|
+
class EmailValidator < ActiveModel::EachValidator
|
5
|
+
def validate_each(record, attribute, value)
|
6
|
+
unless valid_email?(value)
|
7
|
+
msg = options[:message]
|
8
|
+
msg ||= I18n.t(:"validators.email.invalid", :default => "has invalid format")
|
9
|
+
record.errors[attribute] << msg
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
# Internal: Takes an email and returns whether is invalid or not.
|
16
|
+
#
|
17
|
+
# email - A String email address to be validated.
|
18
|
+
#
|
19
|
+
# Returns true if email is valid.
|
20
|
+
def valid_email?(email)
|
21
|
+
mail = Mail::Address.new(email)
|
22
|
+
|
23
|
+
# We must check that value contains a domain and that value is
|
24
|
+
# an email address
|
25
|
+
result = mail.domain && mail.address == email
|
26
|
+
tree = mail.__send__(:tree)
|
27
|
+
|
28
|
+
# We need to dig into treetop. A valid domain must have
|
29
|
+
# dot_atom_text elements size > 1. Addresses like user@localhost
|
30
|
+
# are excluded. Treetop must respond to domain.
|
31
|
+
result && (tree.domain.dot_atom_text.elements.size > 1)
|
32
|
+
rescue => e
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/glengarry.rb
ADDED
File without changes
|
metadata
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: glengarry
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- John McDowall
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.7
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.7
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: jquery-rails
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
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: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: kaminari
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '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: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: geocoder
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: sqlite3
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rspec-rails
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: factory_girl_rails
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: I HATE THIS FIELD
|
127
|
+
email:
|
128
|
+
- john@mcdowall.info
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- app/assets/images/glengarry/bg.png
|
134
|
+
- app/assets/javascripts/glengarry/application.js
|
135
|
+
- app/assets/javascripts/glengarry/dashboard.js
|
136
|
+
- app/assets/javascripts/glengarry/email_leads.js
|
137
|
+
- app/assets/stylesheets/glengarry/application.css
|
138
|
+
- app/assets/stylesheets/glengarry/dashboard.css
|
139
|
+
- app/assets/stylesheets/glengarry/email_leads.css.erb
|
140
|
+
- app/assets/stylesheets/scaffold.css
|
141
|
+
- app/controllers/glengarry/application_controller.rb
|
142
|
+
- app/controllers/glengarry/email_leads_controller.rb
|
143
|
+
- app/helpers/glengarry/application_helper.rb
|
144
|
+
- app/helpers/glengarry/dashboard_helper.rb
|
145
|
+
- app/helpers/glengarry/email_leads_helper.rb
|
146
|
+
- app/models/glengarry/email_lead.rb
|
147
|
+
- app/views/glengarry/email_leads/_form.html.erb
|
148
|
+
- app/views/glengarry/email_leads/edit.html.erb
|
149
|
+
- app/views/glengarry/email_leads/index.html.erb
|
150
|
+
- app/views/glengarry/email_leads/new.html.erb
|
151
|
+
- app/views/glengarry/email_leads/show.html.erb
|
152
|
+
- app/views/layouts/glengarry/application.html.erb
|
153
|
+
- config/routes.rb
|
154
|
+
- db/migrate/20120804175235_create_glengarry_email_leads.rb
|
155
|
+
- lib/email_validator.rb
|
156
|
+
- lib/glengarry/engine.rb
|
157
|
+
- lib/glengarry/version.rb
|
158
|
+
- lib/glengarry.rb
|
159
|
+
- lib/tasks/glengarry_tasks.rake
|
160
|
+
- MIT-LICENSE
|
161
|
+
- Rakefile
|
162
|
+
- README.md
|
163
|
+
homepage: https://github.com/johnmcdowall/glengarry
|
164
|
+
licenses: []
|
165
|
+
post_install_message:
|
166
|
+
rdoc_options: []
|
167
|
+
require_paths:
|
168
|
+
- lib
|
169
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
170
|
+
none: false
|
171
|
+
requirements:
|
172
|
+
- - ! '>='
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
segments:
|
176
|
+
- 0
|
177
|
+
hash: -281059192529344362
|
178
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
|
+
none: false
|
180
|
+
requirements:
|
181
|
+
- - ! '>='
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: '0'
|
184
|
+
segments:
|
185
|
+
- 0
|
186
|
+
hash: -281059192529344362
|
187
|
+
requirements: []
|
188
|
+
rubyforge_project:
|
189
|
+
rubygems_version: 1.8.23
|
190
|
+
signing_key:
|
191
|
+
specification_version: 3
|
192
|
+
summary: A Rails 3.2 engine for capturing email leads.
|
193
|
+
test_files: []
|