diff --git a/.byebug_history b/.byebug_history
new file mode 100644
index 0000000..a3abe50
--- /dev/null
+++ b/.byebug_history
@@ -0,0 +1 @@
+exit
diff --git a/Gemfile b/Gemfile
index 562fecb..1b63ace 100644
--- a/Gemfile
+++ b/Gemfile
@@ -23,6 +23,7 @@ gem 'rake'
# Adding rspec for running unit testing
gem 'rspec'
+gem 'byebug'
group :development, :test do
# Adding shotgun for local web hosting
diff --git a/Gemfile.lock b/Gemfile.lock
index ae38c75..8dd1bd2 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,32 +1,29 @@
GEM
remote: https://rubygems.org/
specs:
- activemodel (4.2.5)
- activesupport (= 4.2.5)
- builder (~> 3.1)
- activerecord (4.2.5)
- activemodel (= 4.2.5)
- activesupport (= 4.2.5)
- arel (~> 6.0)
- activesupport (4.2.5)
+ activemodel (5.0.1)
+ activesupport (= 5.0.1)
+ activerecord (5.0.1)
+ activemodel (= 5.0.1)
+ activesupport (= 5.0.1)
+ arel (~> 7.0)
+ activesupport (5.0.1)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
- json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
- thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
- arel (6.0.3)
- backports (3.6.7)
- builder (3.2.2)
- daemons (1.2.3)
- diff-lcs (1.2.5)
- eventmachine (1.0.8)
- i18n (0.7.0)
- json (1.8.3)
- minitest (5.8.3)
- multi_json (1.11.2)
- pg (0.18.4)
- puma (2.15.3)
- rack (1.6.4)
+ arel (7.1.4)
+ backports (3.6.8)
+ concurrent-ruby (1.0.4)
+ daemons (1.2.4)
+ diff-lcs (1.3)
+ eventmachine (1.2.2)
+ i18n (0.8.0)
+ minitest (5.10.1)
+ multi_json (1.12.1)
+ pg (0.19.0)
+ puma (3.7.0)
+ rack (1.6.5)
rack-protection (1.5.3)
rack
rack-test (0.6.3)
@@ -34,46 +31,49 @@ GEM
rails_12factor (0.0.3)
rails_serve_static_assets
rails_stdout_logging
- rails_serve_static_assets (0.0.4)
- rails_stdout_logging (0.0.4)
- rake (10.4.2)
- rspec (3.4.0)
- rspec-core (~> 3.4.0)
- rspec-expectations (~> 3.4.0)
- rspec-mocks (~> 3.4.0)
- rspec-core (3.4.1)
- rspec-support (~> 3.4.0)
- rspec-expectations (3.4.0)
+ rails_serve_static_assets (0.0.5)
+ rails_stdout_logging (0.0.5)
+ rake (12.0.0)
+ rspec (3.5.0)
+ rspec-core (~> 3.5.0)
+ rspec-expectations (~> 3.5.0)
+ rspec-mocks (~> 3.5.0)
+ rspec-core (3.5.4)
+ rspec-support (~> 3.5.0)
+ rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.4.0)
- rspec-mocks (3.4.0)
+ rspec-support (~> 3.5.0)
+ rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.4.0)
- rspec-support (3.4.1)
- shotgun (0.9.1)
+ rspec-support (~> 3.5.0)
+ rspec-support (3.5.0)
+ shotgun (0.9.2)
rack (>= 1.0)
- sinatra (1.4.6)
- rack (~> 1.4)
+ sinatra (1.4.8)
+ rack (~> 1.5)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
- sinatra-activerecord (2.0.9)
+ sinatra-activerecord (2.0.11)
activerecord (>= 3.2)
sinatra (~> 1.0)
- sinatra-contrib (1.4.6)
+ sinatra-contrib (1.4.7)
backports (>= 2.0)
multi_json
rack-protection
rack-test
sinatra (~> 1.4.0)
tilt (>= 1.3, < 3)
- thin (1.6.4)
+ thin (1.7.0)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
- rack (~> 1.0)
+ rack (>= 1, < 3)
thread_safe (0.3.5)
- tilt (2.0.1)
+ tilt (2.0.6)
tzinfo (1.2.2)
thread_safe (~> 0.1)
+ valid_url(0.0.4)
+ addressable
+ rails
PLATFORMS
ruby
@@ -91,6 +91,7 @@ DEPENDENCIES
sinatra-activerecord
sinatra-contrib
thin
+ valid_url
BUNDLED WITH
- 1.10.6
+ 1.13.7
diff --git a/app/controllers/static.rb b/app/controllers/static.rb
index 33c1897..8bbce36 100644
--- a/app/controllers/static.rb
+++ b/app/controllers/static.rb
@@ -1,3 +1,33 @@
get '/' do
+ @urls = Url.all.last(5)
+ @total_click = Url.total_click
erb :"static/index"
-end
\ No newline at end of file
+end
+# let user create new short URL, display a list of shortened URLs
+
+
+post '/urls' do
+ @urls=Url.all.last(5)
+ @url = Url.create(long_url: params[:long_url])
+ @error_message = @url.errors.messages[:long_url]
+ if @url.save
+ # redirect to '/'
+ # @url.to_json
+ else
+ # erb :"static/index"
+ @error = "Please enter a valid URL"
+ redirect to "/"
+ end
+ @url.to_json
+end
+# create a new Url
+
+
+get '/:short_url' do
+ @url = Url.find_by(short_url: params[:short_url])
+ @url.click_count +=1
+ @url.save
+ redirect @url.long_url
+ end
+
+# redirect to appropriate "long" URL
diff --git a/app/models/.byebug_history b/app/models/.byebug_history
new file mode 100644
index 0000000..a3abe50
--- /dev/null
+++ b/app/models/.byebug_history
@@ -0,0 +1 @@
+exit
diff --git a/app/models/url.rb b/app/models/url.rb
new file mode 100644
index 0000000..55bfac7
--- /dev/null
+++ b/app/models/url.rb
@@ -0,0 +1,38 @@
+require 'SecureRandom'
+
+class Url < ActiveRecord::Base
+ # This is Sinatra! Remember to create a migration!
+
+ before_create :shorten
+
+ validates_presence_of :long_url, message: "can't be blank!"
+ validate :validate_url
+
+
+ # validates :long_url, :format => { :with => URI::regexp(%w(http https)), :message => "Valid URL required"}
+
+ def shorten
+ self.short_url = SecureRandom.hex(3)
+ end
+
+ def count
+ self.click_count += 1
+ self.save
+ end
+
+ def self.total_click
+ total_click = 0
+ click_array = Url.select(:click_count)
+ click_array.each do |click|
+ total_click += click.click_count
+ end
+ total_click
+ end
+
+ def validate_url
+ unless self.long_url =~ /(http:\/\/|https:\/\/)(www.)([a-z]*.)(com|edu|org|net|gov|mil|biz|info|co.uk)/
+ errors.add(:long_url, "Please enter a valid URL")
+ end
+ end
+
+end
diff --git a/app/views/layouts/application.erb b/app/views/layouts/application.erb
index 4b47fec..cb8562b 100644
--- a/app/views/layouts/application.erb
+++ b/app/views/layouts/application.erb
@@ -1,8 +1,17 @@
- Sinatra Framework
+ zub.me | the bitly clone
+
+
+
+
+
+
+
<%= yield %>
\ No newline at end of file
diff --git a/app/views/static/index.erb b/app/views/static/index.erb
index 622f8bb..f990202 100644
--- a/app/views/static/index.erb
+++ b/app/views/static/index.erb
@@ -1,2 +1,48 @@
-Hello World
-See Me in views/static/index.html.erb
\ No newline at end of file
+
+
+
+
+
+ "Measure you links with ZubMe, the world's leading link management platform."
+
+
+
+
+
+
+
+
+
+
+
+
+ <%=@error%>
+
+
+
+
+
+ ID
+ Long URL
+ Short URL
+ Created At
+ Click No.
+
+ <% @urls.each do |url| %>
+
+ <%= url.id %>
+ <%= url.long_url %>
+
+ <%= url.created_at.strftime("%d %b. %Y at %H:%M") %>
+ <%= url.click_count %>
+
+ <% end %>
+
+
+
+
+
+
diff --git a/config/environments/init.rb b/config/environments/init.rb
index 44a8a15..bef7de1 100644
--- a/config/environments/init.rb
+++ b/config/environments/init.rb
@@ -20,6 +20,9 @@
# embedded ruby
require 'erb'
require 'uri'
+
+ # byebug
+require 'byebug'
######################################################################
diff --git a/db/migrate/20170206142646_create_urls.rb b/db/migrate/20170206142646_create_urls.rb
new file mode 100644
index 0000000..50d30fe
--- /dev/null
+++ b/db/migrate/20170206142646_create_urls.rb
@@ -0,0 +1,9 @@
+class CreateUrls < ActiveRecord::Migration
+ def change
+ create_table :url do |t|
+ t.string :original
+ t.string :shortened
+ t.timestamps
+ end
+ end
+end
\ No newline at end of file
diff --git a/db/migrate/20170207094708_rename_table_urls.rb b/db/migrate/20170207094708_rename_table_urls.rb
new file mode 100644
index 0000000..f9891c8
--- /dev/null
+++ b/db/migrate/20170207094708_rename_table_urls.rb
@@ -0,0 +1,7 @@
+class RenameTableUrls < ActiveRecord::Migration
+ def change
+ rename_table :url, :Urls
+ rename_column :Urls, :original, :long_url
+ rename_column :Urls, :shortened, :short_url
+ end
+end
\ No newline at end of file
diff --git a/db/migrate/20170207103806_rename_urls.rb b/db/migrate/20170207103806_rename_urls.rb
new file mode 100644
index 0000000..7f7ed79
--- /dev/null
+++ b/db/migrate/20170207103806_rename_urls.rb
@@ -0,0 +1,5 @@
+class RenameUrls < ActiveRecord::Migration
+ def change
+ rename_table :Urls, :urls
+ end
+end
diff --git a/db/migrate/20170207113001_add_column.rb b/db/migrate/20170207113001_add_column.rb
new file mode 100644
index 0000000..fb956db
--- /dev/null
+++ b/db/migrate/20170207113001_add_column.rb
@@ -0,0 +1,5 @@
+class AddColumn < ActiveRecord::Migration
+ def change
+ add_column :urls, :click_count, :integer, default: 0
+ end
+end
diff --git a/public/css/application.css b/public/css/application.css
index e69de29..6598795 100644
--- a/public/css/application.css
+++ b/public/css/application.css
@@ -0,0 +1,196 @@
+/*.container img {
+ float: left;
+ width: 100px;
+ height: 100px;
+ background: #555;
+ overflow: auto
+}*/
+
+
+h1 {
+ text-align: center;
+ color: #fffa31;
+ font-size: 85px;
+ margin-left: 20;
+ margin-top: 20;
+ clear: both;
+}
+
+.lead {
+ text-align: center;
+ color: #fffa31;
+ font-size: 20px;
+}
+
+.overme {
+ width: 265px;
+ overflow:hidden;
+ white-space:nowrap;
+ text-overflow: ellipsis;
+ margin-left: 10;
+}
+
+/*.colour {
+color: #fcd04b;
+}
+.red {color: #d3313a;}
+.blue {color: #00acc8;}
+.green {color: #61a844;}
+.yellow {color: #fcb325;}
+.orange {color: #e75424;}
+.black {color: #FFFFFF;}
+*/
+
+body {
+ background-color: #333;
+ font-family: 'Lato', Calibri, Arial, sans-serif;
+ background-image: url("/img/background_grey.jpg");
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+ background-position: center;
+ background-size: cover;
+ height: 100%;
+}
+
+input[type="url"] {
+ display: block;
+ box-sizing: border-box;
+ color: #333;
+ margin: 3;
+ width: 60%;
+ font-family: "Open Sans", sans-serif;
+ font-size: 14px;
+ text-align: left;
+}
+
+.style-2 input[type="text"] {
+ padding: 14px;
+ width: 400;
+ /*border: solid 5px;*/
+}
+.style-2 input[type="text"]:focus, .style-2 input[type="text"].focus {
+ border: solid 5px #969696;
+}
+
+#btn-default,
+#btn-default:hover,
+#btn-default:focus {
+ color: #333;
+ text-shadow: none; /* Prevent inheritance from `body` */
+ background-color: #fff;
+ border: 40px solid #fff;
+}
+
+/*.btn {
+ color: #333;
+ text-shadow: none; Prevent inheritance from `body`
+ background-color: #fff;
+ border: 1px solid #fff;
+ cursor: pointer;
+ border-radius: 4px;*/
+
+ /*border: none;
+ font-family: inherit;
+ font-size: inherit;
+ color: inherit;
+ background: #fcd04b;
+ box-shadow: 0 6px #DEAF00;
+ cursor: pointer;
+ padding: 18px 50px;
+ display: inline-block;
+ margin: 15px 30px;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ font-weight: 700;
+ outline: none;
+ position: relative;*/
+}
+
+/*.btn:hover {
+background: #fcd357;
+ box-shadow: 0 4px #DEAF00;
+}*/
+
+/* unvisited link */
+a:link {
+ color: black;
+}
+
+/* visited link */
+a:visited {
+ color: #333;
+}
+
+/* mouse over link */
+a:hover {
+ color: gray;
+}
+
+/* selected link */
+a:active {
+ color: #333;
+}
+
+#error {
+ text-align: center;
+ color: #ff5120;
+ font-size: 15px;
+ margin-bottom: 20px;
+}
+
+table {
+color: #333;
+font-family: Helvetica, Arial, sans-serif;
+width: 100%;
+float:none;
+display: inline;
+border-collapse:
+collapse; border-spacing: 0;
+opacity: 0.85;
+background: transparent;
+}
+
+td, th {
+border: 5px solid transparent;
+height: 30px;
+margin: 8px;
+font-size: 16px;
+}
+
+.th2 {
+background: #0e1013;
+opacity:0.7;
+color: white;
+ /* Darken header a bit */
+font-weight: bold;
+text-align: left;
+padding: 10px;
+}
+
+td {
+background: #FAFAFA;
+text-align: left;
+}
+
+.required_notification {
+ color:#d45252;
+ margin:5px 0 0 0;
+ display:inline;
+ float:right;
+}
+::-webkit-validation-bubble-message {
+ padding: 1em;
+}
+input:required:invalid, input:focus:valid {
+ /*-moz-box-shadow: all;*/
+ /*background: #fff url(/img/warning.png) no-repeat 98% center;*/
+box-shadow: 0 0 5px #d45252;
+ border-color: #b03535
+}
+
+input:required:valid {
+ /* insert your own styles for valid form input */
+ background: #fff url(https://cdn0.iconfinder.com/data/icons/fatcow/32x32/tick.png) no-repeat 98% center;
+box-shadow: 0 0 5px #2eb82e;
+ border-color: #29a329
+}
\ No newline at end of file
diff --git a/public/img/application.css b/public/img/application.css
new file mode 100644
index 0000000..17be91f
--- /dev/null
+++ b/public/img/application.css
@@ -0,0 +1,173 @@
+h1 {
+ text-align: center;
+ color: #fffa31;;
+ font-size: 85px;
+ margin-left:15;
+ clear: both;
+}
+
+.lead {
+ text-align: center;
+ color: #fffa31;
+ font-size: 20px;
+}
+
+.overme {
+ width: 450px;
+ overflow:hidden;
+ white-space:nowrap;
+ text-overflow: ellipsis;
+}
+
+/*.colour {
+color: #fcd04b;
+}
+.red {color: #d3313a;}
+.blue {color: #00acc8;}
+.green {color: #61a844;}
+.yellow {color: #fcb325;}
+.orange {color: #e75424;}
+.black {color: #FFFFFF;}
+*/
+
+body {
+ background-color: #333;
+ font-family: 'Lato', Calibri, Arial, sans-serif;
+ background-image: url("/img/background_grey.jpg");
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+ background-position: center;
+ background-size: cover;
+ height: 100%;
+}
+
+input[type="url"] {
+ display: block;
+ margin: 3;
+ width: 60%;
+ font-family: "Open Sans", sans-serif;
+ font-size: 33px;
+ text-align: left;
+}
+
+.style-2 input[type="text"] {
+ padding: 10px;
+ border: solid 5px # ;
+}
+.style-2 input[type="text"]:focus, .style-2 input[type="text"].focus {
+ border: solid 5px #969696;
+}
+
+.btn-default,
+.btn-default:hover,
+.btn-default:focus {
+ color: #333;
+ text-shadow: none; /* Prevent inheritance from `body` */
+ background-color: #fff;
+ border: 1px solid #fff;
+}
+
+
+/*.btn {
+ color: #333;
+ text-shadow: none; Prevent inheritance from `body`
+ background-color: #fff;
+ border: 1px solid #fff;
+ cursor: pointer;*/
+
+ /*border: none;
+ font-family: inherit;
+ font-size: inherit;
+ color: inherit;
+ background: #fcd04b;
+ box-shadow: 0 6px #DEAF00;
+ cursor: pointer;
+ padding: 18px 50px;
+ display: inline-block;
+ margin: 15px 30px;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ font-weight: 700;
+ outline: none;
+ position: relative;*/
+}
+
+/*.btn:hover {
+background: #fcd357;
+ box-shadow: 0 4px #DEAF00;
+}*/
+
+/* unvisited link */
+a:link {
+ color: black;
+}
+
+/* visited link */
+a:visited {
+ color: #333;
+}
+
+/* mouse over link */
+a:hover {
+ color: gray;
+}
+
+/* selected link */
+a:active {
+ color: #333;
+}
+
+table {
+color: #333;
+font-family: Helvetica, Arial, sans-serif;
+width: 95%;
+float:none;
+display: inline;
+border-collapse:
+collapse; border-spacing: 0;
+opacity: 0.85;
+background: transparent;
+}
+
+td, th {
+border: 1px solid transparent;
+height: 30px;
+margin: 8px;
+font-size: 16px;
+}
+
+.th2 {
+background: #000000;
+opacity:0.7;
+color: white;
+ /* Darken header a bit */
+font-weight: bold;
+}
+
+td {
+background: #FAFAFA;
+text-align: center;
+}
+
+.required_notification {
+ color:#d45252;
+ margin:5px 0 0 0;
+ display:inline;
+ float:right;
+}
+::-webkit-validation-bubble-message {
+ padding: 1em;
+}
+input:required:invalid, input:focus:valid {
+ /*-moz-box-shadow: all;*/
+ /*background: #fff url(/img/warning.png) no-repeat 98% center;*/
+box-shadow: 0 0 5px #d45252;
+ border-color: #b03535
+}
+
+input:required:valid {
+ /* insert your own styles for valid form input */
+ background: #fff url(https://cdn0.iconfinder.com/data/icons/fatcow/32x32/tick.png) no-repeat 98% center;
+box-shadow: 0 0 5px #2eb82e;
+ border-color: #29a329
+}
\ No newline at end of file
diff --git a/public/img/background_grey.jpg b/public/img/background_grey.jpg
new file mode 100644
index 0000000..48175fb
Binary files /dev/null and b/public/img/background_grey.jpg differ
diff --git a/public/img/zubme_logo.png b/public/img/zubme_logo.png
new file mode 100644
index 0000000..dee0c38
Binary files /dev/null and b/public/img/zubme_logo.png differ
diff --git a/public/js/application.js b/public/js/application.js
index e69de29..80933b7 100644
--- a/public/js/application.js
+++ b/public/js/application.js
@@ -0,0 +1,38 @@
+// $(document).ready(function(){
+// $( ".paste" ).on( "paste", function() {
+// setTimeout(function() {
+// $('.btn').trigger('click')
+// });
+// });
+// });
+
+
+$(document).ready(function(){
+ $("#input").submit(function(e){
+ e.preventDefault();
+ $.ajax({
+ url: '/urls',
+ method: 'POST',
+ data: $(this).serialize(),
+ dataType: 'json',
+ success: function(data){
+ $(".table-url-links").append("" +
+ ""+ data.id + " " +
+ ""+ data.long_url + " " +
+ ""+ "" + data.short_url + " " +
+ ""+ data.created_at + " " +
+ ""+ data.click_count + " " +
+ " ");
+ }
+ });
+ });
+});
+
+
+// 1. What this.serialize(): creates a URL encoded text string by serializing form values. JSON encodes objects in a string. Serialization: convert an object into string in order to be stored.
+
+// 2. data key in ajax = The data to send to the server when performing the Ajax request
+
+// 3. dataType? The type of data expected back from the server. It screenthrough the data that you expect to return
+
+// 4. What do you expect out of the response? to return string of data in the table prior page reloaded
\ No newline at end of file
diff --git a/url b/url
new file mode 100644
index 0000000..e69de29