glengarry 0.0.1
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.
- 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
|
+

|
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
|
+

|
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: []
|