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

zub*me.

+

+ "Measure you links with ZubMe, the world's leading link management platform." +

+
+ +
+
+
+ + +
+
+ + +

+ +
+ <%=@error%> +
+ +
+ + + + + + + + + <% @urls.each do |url| %> + + + + + + + + <% 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