From 03dd0f9a84dcbb08af4cb9745364e1fd6ddb7fc0 Mon Sep 17 00:00:00 2001 From: Jonah Roth Date: Tue, 31 May 2016 15:34:30 -0400 Subject: [PATCH 1/6] Release 0 --- source/app/controllers/urls_controller.rb | 11 +++++++++ source/app/models/url.rb | 24 +++++++++++++++++++ source/config/routes.rb | 4 ++++ .../db/migrate/20160531190449_create_urls.rb | 10 ++++++++ source/db/schema.rb | 23 ++++++++++++++++++ source/spec/models/url_spec.rb | 5 ++++ 6 files changed, 77 insertions(+) create mode 100644 source/app/models/url.rb create mode 100644 source/db/migrate/20160531190449_create_urls.rb create mode 100644 source/db/schema.rb create mode 100644 source/spec/models/url_spec.rb diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb index ef26710..69ad3d9 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -1,2 +1,13 @@ class UrlsController < ApplicationController + skip_before_action :verify_authenticity_token + + def create + @url = Url.build_for_url(params[:address]) + render json: @url + end + + def get + @url = Url.find_by(:shortcode => params[:id]) + redirect_to @url.address + end end diff --git a/source/app/models/url.rb b/source/app/models/url.rb new file mode 100644 index 0000000..6ef3481 --- /dev/null +++ b/source/app/models/url.rb @@ -0,0 +1,24 @@ +require 'digest/sha1' + +class Url < ActiveRecord::Base + def self.build_for_url(url) + @url = new(:address => url) + @url.shortcode = self.get_code(url) + @url.save + @url + end + + protected + + def self.get_code(str) + full = Digest::SHA1.hexdigest(str) + start = 0 + code = full[start..(start+5)] + loop do + break if !self.find_by(:shortcode => code) || (start + 5 >= full.length) + start += 1 + code = full[start..(start+5)] + end + code + end +end diff --git a/source/config/routes.rb b/source/config/routes.rb index 3f66539..addeba6 100644 --- a/source/config/routes.rb +++ b/source/config/routes.rb @@ -1,4 +1,8 @@ Rails.application.routes.draw do + + post 'create' => 'urls#create' + get ':id' => 'urls#get' + # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes". diff --git a/source/db/migrate/20160531190449_create_urls.rb b/source/db/migrate/20160531190449_create_urls.rb new file mode 100644 index 0000000..03a3fb7 --- /dev/null +++ b/source/db/migrate/20160531190449_create_urls.rb @@ -0,0 +1,10 @@ +class CreateUrls < ActiveRecord::Migration + def change + create_table :urls do |t| + t.string :address + t.string :shortcode + + t.timestamps + end + end +end diff --git a/source/db/schema.rb b/source/db/schema.rb new file mode 100644 index 0000000..6b7bc73 --- /dev/null +++ b/source/db/schema.rb @@ -0,0 +1,23 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20160531190449) do + + create_table "urls", force: true do |t| + t.string "address" + t.string "shortcode" + t.datetime "created_at" + t.datetime "updated_at" + end + +end diff --git a/source/spec/models/url_spec.rb b/source/spec/models/url_spec.rb new file mode 100644 index 0000000..209ca4c --- /dev/null +++ b/source/spec/models/url_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Url, :type => :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 9a9b40e7f25178c7e8319beeb44372bc9bc26526 Mon Sep 17 00:00:00 2001 From: Jonah Roth Date: Tue, 31 May 2016 15:40:48 -0400 Subject: [PATCH 2/6] Release 0 re-implemented to follow challenge instructions --- source/app/controllers/urls_controller.rb | 7 ++++++- source/app/models/url.rb | 15 +++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb index 69ad3d9..43dfc16 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -2,7 +2,7 @@ class UrlsController < ApplicationController skip_before_action :verify_authenticity_token def create - @url = Url.build_for_url(params[:address]) + @url = Url.create(url_params) render json: @url end @@ -10,4 +10,9 @@ def get @url = Url.find_by(:shortcode => params[:id]) redirect_to @url.address end + + private + def url_params + params.permit(:address) + end end diff --git a/source/app/models/url.rb b/source/app/models/url.rb index 6ef3481..b296c51 100644 --- a/source/app/models/url.rb +++ b/source/app/models/url.rb @@ -1,24 +1,19 @@ require 'digest/sha1' class Url < ActiveRecord::Base - def self.build_for_url(url) - @url = new(:address => url) - @url.shortcode = self.get_code(url) - @url.save - @url - end + before_save :set_shortcode protected - def self.get_code(str) - full = Digest::SHA1.hexdigest(str) + def set_shortcode + full = Digest::SHA1.hexdigest(self.address) start = 0 code = full[start..(start+5)] loop do - break if !self.find_by(:shortcode => code) || (start + 5 >= full.length) + break if Url.find_by(:shortcode => code) || (start + 5 >= full.length) start += 1 code = full[start..(start+5)] end - code + self.shortcode = code end end From 8d0c33c8d73765674802a47ace75744b4af77618 Mon Sep 17 00:00:00 2001 From: Jonah Roth Date: Tue, 31 May 2016 16:02:01 -0400 Subject: [PATCH 3/6] Added basic front end that works as long as URLs begin with HTTP... but validation is in a later release --- source/Gemfile | 3 ++- source/Gemfile.lock | 12 ++++++++++++ .../{application.css => application.scss} | 6 ++++-- source/app/assets/stylesheets/urls.css.scss | 9 +++++++++ source/app/controllers/urls_controller.rb | 10 ++++++++-- source/app/views/urls/new.haml | 12 ++++++++++++ source/config/routes.rb | 3 ++- 7 files changed, 49 insertions(+), 6 deletions(-) rename source/app/assets/stylesheets/{application.css => application.scss} (89%) create mode 100644 source/app/views/urls/new.haml diff --git a/source/Gemfile b/source/Gemfile index 9627b8b..963dc97 100644 --- a/source/Gemfile +++ b/source/Gemfile @@ -13,6 +13,7 @@ gem 'uglifier', '>= 1.3.0' gem 'coffee-rails', '~> 4.0.0' # See https://github.com/sstephenson/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby +gem 'bootstrap-sass' # Use jquery as the JavaScript library gem 'jquery-rails' @@ -22,6 +23,7 @@ gem 'turbolinks' gem 'jbuilder', '~> 2.0' # bundle exec rake doc:rails generates the API under doc/api. gem 'sdoc', '~> 0.4.0', group: :doc +gem 'haml', group: :development # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring', group: :development @@ -38,4 +40,3 @@ gem 'spring', group: :development # Use debugger # gem 'debugger', group: [:development, :test] gem 'rspec-rails', group: [:development, :test] - diff --git a/source/Gemfile.lock b/source/Gemfile.lock index fcf8b98..9efd4a6 100644 --- a/source/Gemfile.lock +++ b/source/Gemfile.lock @@ -28,6 +28,11 @@ GEM thread_safe (~> 0.1) tzinfo (~> 1.1) arel (5.0.1.20140414130214) + autoprefixer-rails (6.3.6.2) + execjs + bootstrap-sass (3.3.5) + autoprefixer-rails (>= 5.0.0.1) + sass (>= 3.2.19) builder (3.2.2) coffee-rails (4.0.1) coffee-script (>= 2.2.0) @@ -39,6 +44,8 @@ GEM diff-lcs (1.2.5) erubis (2.7.0) execjs (2.2.1) + haml (4.0.6) + tilt hike (1.2.3) i18n (0.6.11) jbuilder (2.2.2) @@ -125,7 +132,9 @@ PLATFORMS ruby DEPENDENCIES + bootstrap-sass coffee-rails (~> 4.0.0) + haml jbuilder (~> 2.0) jquery-rails rails (= 4.1.6) @@ -136,3 +145,6 @@ DEPENDENCIES sqlite3 turbolinks uglifier (>= 1.3.0) + +BUNDLED WITH + 1.12.5 diff --git a/source/app/assets/stylesheets/application.css b/source/app/assets/stylesheets/application.scss similarity index 89% rename from source/app/assets/stylesheets/application.css rename to source/app/assets/stylesheets/application.scss index a443db3..cfb0ca8 100644 --- a/source/app/assets/stylesheets/application.css +++ b/source/app/assets/stylesheets/application.scss @@ -10,6 +10,8 @@ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new * file per style scope. * - *= require_tree . - *= require_self + */ +@import "bootstrap-sprockets"; +@import "bootstrap"; +@import "urls.css.scss"; diff --git a/source/app/assets/stylesheets/urls.css.scss b/source/app/assets/stylesheets/urls.css.scss index a4281ec..b2ada52 100644 --- a/source/app/assets/stylesheets/urls.css.scss +++ b/source/app/assets/stylesheets/urls.css.scss @@ -1,3 +1,12 @@ // Place all the styles related to the Urls controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ +body { + text-align: center; +} + +.form-group { + width: 50%; + margin-left: 25%; + margin-right: 25%; +} diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb index 43dfc16..662770d 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -1,9 +1,15 @@ class UrlsController < ApplicationController + require 'socket' skip_before_action :verify_authenticity_token + def new + @url = Url.new + end + def create @url = Url.create(url_params) - render json: @url + flash[:notice] = "Your URL: http://#{request.host_with_port}/#{@url.shortcode}" + redirect_to new_url_path end def get @@ -13,6 +19,6 @@ def get private def url_params - params.permit(:address) + params.require(:url).permit(:address) end end diff --git a/source/app/views/urls/new.haml b/source/app/views/urls/new.haml new file mode 100644 index 0000000..641c239 --- /dev/null +++ b/source/app/views/urls/new.haml @@ -0,0 +1,12 @@ +%h1 Jonah's Fantastic URL Shortener + +- if flash[:notice] + .notice + = flash[:notice] + += form_for @url do |u| + .form-group + = u.label :address, "Enter the address to shorten:" + = u.text_field :address, class: "form-control" + .form-group + = u.submit "Submit", class: "btn btn-submit" diff --git a/source/config/routes.rb b/source/config/routes.rb index addeba6..21812d7 100644 --- a/source/config/routes.rb +++ b/source/config/routes.rb @@ -1,6 +1,7 @@ Rails.application.routes.draw do - post 'create' => 'urls#create' + root 'urls#new' + resources :urls get ':id' => 'urls#get' # The priority is based upon order of creation: first created -> highest priority. From aab974e6076126c0ab53285c9969c94a505b1e3d Mon Sep 17 00:00:00 2001 From: Jonah Roth Date: Tue, 31 May 2016 16:05:10 -0400 Subject: [PATCH 4/6] Release 1: click_count field that increments added to urls table --- source/app/controllers/urls_controller.rb | 2 ++ source/db/migrate/20160531200238_add_click_count_to_url.rb | 5 +++++ source/db/schema.rb | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 source/db/migrate/20160531200238_add_click_count_to_url.rb diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb index 662770d..b4a6184 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -14,6 +14,8 @@ def create def get @url = Url.find_by(:shortcode => params[:id]) + @url.click_count += 1 + @url.save redirect_to @url.address end diff --git a/source/db/migrate/20160531200238_add_click_count_to_url.rb b/source/db/migrate/20160531200238_add_click_count_to_url.rb new file mode 100644 index 0000000..3e6efc8 --- /dev/null +++ b/source/db/migrate/20160531200238_add_click_count_to_url.rb @@ -0,0 +1,5 @@ +class AddClickCountToUrl < ActiveRecord::Migration + def change + add_column :urls, :click_count, :integer, default: 0 + end +end diff --git a/source/db/schema.rb b/source/db/schema.rb index 6b7bc73..564c43c 100644 --- a/source/db/schema.rb +++ b/source/db/schema.rb @@ -11,13 +11,14 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160531190449) do +ActiveRecord::Schema.define(version: 20160531200238) do create_table "urls", force: true do |t| t.string "address" t.string "shortcode" t.datetime "created_at" t.datetime "updated_at" + t.integer "click_count", default: 0 end end From 4d40871576c018e9b8ef2d736f54bce7fa51ffae Mon Sep 17 00:00:00 2001 From: Jonah Roth Date: Tue, 31 May 2016 16:47:33 -0400 Subject: [PATCH 5/6] Validates URLs but still can't show errors --- source/app/models/url.rb | 13 ++++++++++++- source/app/views/urls/new.haml | 9 +++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/source/app/models/url.rb b/source/app/models/url.rb index b296c51..fdb0eee 100644 --- a/source/app/models/url.rb +++ b/source/app/models/url.rb @@ -1,7 +1,9 @@ require 'digest/sha1' +require 'uri' class Url < ActiveRecord::Base before_save :set_shortcode + validate :is_url, on: :create protected @@ -10,10 +12,19 @@ def set_shortcode start = 0 code = full[start..(start+5)] loop do - break if Url.find_by(:shortcode => code) || (start + 5 >= full.length) + break if !Url.find_by(:shortcode => code) || (start + 5 >= full.length) start += 1 code = full[start..(start+5)] end self.shortcode = code end + + def is_url + errors.add(:address, "is not a valid URL") unless !!URI.parse(address) + rescue URI::InvalidURIError + errors.add(:address, "is not a valid URL") + end + + + end diff --git a/source/app/views/urls/new.haml b/source/app/views/urls/new.haml index 641c239..8f4bc78 100644 --- a/source/app/views/urls/new.haml +++ b/source/app/views/urls/new.haml @@ -5,6 +5,15 @@ = flash[:notice] = form_for @url do |u| + - if u.object.errors.any? + .alert.alert-error + The form contains + = pluralize(u.object.errors.count, "error") + \. + %ul + - u.object.errors.each do |msg| + %li + = msg .form-group = u.label :address, "Enter the address to shorten:" = u.text_field :address, class: "form-control" From cc0a93280bd70d4fc2b47ef50ab76b37081d3d16 Mon Sep 17 00:00:00 2001 From: Jonah Roth Date: Wed, 1 Jun 2016 09:57:21 -0400 Subject: [PATCH 6/6] errors display properly --- source/app/controllers/urls_controller.rb | 13 ++++++++++--- source/app/models/url.rb | 9 ++++++++- source/app/views/urls/new.haml | 17 ++++++++--------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb index b4a6184..d0d24d9 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -1,5 +1,5 @@ class UrlsController < ApplicationController - require 'socket' + #require 'socket' skip_before_action :verify_authenticity_token def new @@ -8,8 +8,15 @@ def new def create @url = Url.create(url_params) - flash[:notice] = "Your URL: http://#{request.host_with_port}/#{@url.shortcode}" - redirect_to new_url_path + if @url.errors.any? + @url.address = "" + errors = "" + @url.errors.full_messages.each { |msg| errors += msg + " " } + p errors + redirect_to root_path, alert: errors + else + redirect_to root_path, notice: "Your URL: http://#{request.host_with_port}/#{@url.shortcode}" + end end def get diff --git a/source/app/models/url.rb b/source/app/models/url.rb index fdb0eee..af8a77f 100644 --- a/source/app/models/url.rb +++ b/source/app/models/url.rb @@ -2,7 +2,8 @@ require 'uri' class Url < ActiveRecord::Base - before_save :set_shortcode + before_validation :smart_url_protocol + before_create :set_shortcode validate :is_url, on: :create protected @@ -25,6 +26,12 @@ def is_url errors.add(:address, "is not a valid URL") end + def smart_url_protocol + unless self.address[/\Ahttp:\/\//] || self.address[/\Ahttps:\/\//] + self.address = "http://#{self.address}" + end + end + end diff --git a/source/app/views/urls/new.haml b/source/app/views/urls/new.haml index 8f4bc78..1a26fe0 100644 --- a/source/app/views/urls/new.haml +++ b/source/app/views/urls/new.haml @@ -1,19 +1,18 @@ %h1 Jonah's Fantastic URL Shortener -- if flash[:notice] +- if flash[:notice] && !@url.errors.any? .notice = flash[:notice] +- if flash[:alert] + .alert + = flash[:alert] = form_for @url do |u| - - if u.object.errors.any? + - if @url.errors.any? .alert.alert-error - The form contains - = pluralize(u.object.errors.count, "error") - \. - %ul - - u.object.errors.each do |msg| - %li - = msg + - @url.errors.full_messages.each do |msg| + %strong + = msg .form-group = u.label :address, "Enter the address to shorten:" = u.text_field :address, class: "form-control"