diff --git a/source/app/controllers/application_controller.rb b/source/app/controllers/application_controller.rb
index d83690e..99ee3ea 100644
--- a/source/app/controllers/application_controller.rb
+++ b/source/app/controllers/application_controller.rb
@@ -2,4 +2,7 @@ class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
+
+ def index
+ end
end
diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb
index ef26710..0151246 100644
--- a/source/app/controllers/urls_controller.rb
+++ b/source/app/controllers/urls_controller.rb
@@ -1,2 +1,50 @@
class UrlsController < ApplicationController
+ def index
+ @urls = Url.all
+ end
+
+ def new
+ @url = Url.new
+ end
+
+ def create
+ @url = Url.new(url_params)
+
+ if @url.save
+ redirect_to @url
+ else
+ render 'new'
+ end
+ end
+
+ def show
+ @url = Url.find(params[:id])
+ end
+
+ def edit
+ @url = Url.find(params[:id])
+ end
+
+ def destroy
+ @url = Url.find(params[:id])
+ @url.destroy
+
+ redirect_to urls_path
+ end
+
+ def jump
+ url = Url.find_by_short_url(params[:key])
+
+ if url.nil?
+ redirect_to root_path
+ else
+ url.increment!(:click_count)
+ redirect_to url.long_url
+ end
+ end
+
+ private
+ def url_params
+ params.require(:url).permit(:long_url)
+ end
end
diff --git a/source/app/models/url.rb b/source/app/models/url.rb
new file mode 100644
index 0000000..1a4cce4
--- /dev/null
+++ b/source/app/models/url.rb
@@ -0,0 +1,36 @@
+require 'uri'
+require 'net/http'
+
+class Url < ActiveRecord::Base
+ validate :long_url_must_be_valid_uri, :long_url_must_be_reachable
+ validates :long_url, presence: true
+ before_save :shorten_url
+
+ private
+ def shorten_url
+ range = [*'0'..'9',*'A'..'Z',*'a'..'z']
+ self.short_url = Array.new(6){ range.sample }.join if self.short_url.nil?
+ end
+
+ def long_url_must_be_valid_uri
+ return if long_url.empty?
+
+ unless long_url =~ /\A#{URI.regexp(['http', 'https'])}\z/
+ errors.add(:long_url, "must be a valid url prefaced with http(s)://")
+ end
+ end
+
+ def long_url_must_be_reachable
+ return if long_url.empty?
+
+ url = URI.parse(long_url)
+ http = Net::HTTP.new(url.host, url.port)
+ http.read_timeout = 1
+ http.start { |http|
+ http.get(url)
+ }
+
+ rescue
+ errors.add(:long_url, "must be a reachable url")
+ end
+end
diff --git a/source/app/views/application/index.html.erb b/source/app/views/application/index.html.erb
new file mode 100644
index 0000000..1e4e6c0
--- /dev/null
+++ b/source/app/views/application/index.html.erb
@@ -0,0 +1,4 @@
+
URL Shortener Home
+
+<%= link_to 'New Url', new_url_path %>
+<%= link_to 'All Urls', urls_path %>
\ No newline at end of file
diff --git a/source/app/views/urls/edit.html.erb b/source/app/views/urls/edit.html.erb
new file mode 100644
index 0000000..57352a1
--- /dev/null
+++ b/source/app/views/urls/edit.html.erb
@@ -0,0 +1,3 @@
+Editing a Url
+
+<%= link_to 'Back', urls_path %>
\ No newline at end of file
diff --git a/source/app/views/urls/index.html.erb b/source/app/views/urls/index.html.erb
new file mode 100644
index 0000000..d150a87
--- /dev/null
+++ b/source/app/views/urls/index.html.erb
@@ -0,0 +1,23 @@
+Listing Urls:
+
+
+
+ | Short Url |
+ Times Visited |
+ Original Url |
+
+
+ <% @urls.each do |url| %>
+
+ | <%= url.short_url %> |
+ <%= url.click_count %> |
+ <%= url.long_url %> |
+ <%= link_to 'Show', url_path(url) %> |
+ <%= link_to 'Edit', edit_url_path(url) %> |
+ <%= link_to 'Delete', url_path(url), method: :delete, data: {confirm: 'Are you sure?'} %> |
+
+ <% end %>
+
+
+<%= link_to 'New Url', new_url_path %>
+<%= link_to 'Home', root_path %>
\ No newline at end of file
diff --git a/source/app/views/urls/new.html.erb b/source/app/views/urls/new.html.erb
new file mode 100644
index 0000000..16104bd
--- /dev/null
+++ b/source/app/views/urls/new.html.erb
@@ -0,0 +1,27 @@
+Make a new url here
+
+<%= form_for :url, url: urls_path, local: true do |form| %>
+ <% if @url.errors.any? %>
+
+
+ <%= pluralize(@url.errors.count, "error") %> prohibited this url from being saved:
+
+
+ <%= @url.errors.full_messages.each do |msg| %>
+ - <%= msg %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= form.label :long_url %>
+ <%= form.text_field :long_url %>
+
+
+
+ <%= form.submit %>
+
+<% end %>
+
+<%= link_to 'Home', root_path %>
\ No newline at end of file
diff --git a/source/app/views/urls/show.html.erb b/source/app/views/urls/show.html.erb
new file mode 100644
index 0000000..ac0037f
--- /dev/null
+++ b/source/app/views/urls/show.html.erb
@@ -0,0 +1,18 @@
+Showing a URL here
+
+ Original Url:
+ <%= @url.long_url %>
+
+
+
+ Visit Count:
+ <%= @url.click_count %>
+
+
+
+ Short Url:
+ <%= @url.short_url %>
+
+
+<%= link_to 'Back', urls_path %>
+<%= link_to 'Home', root_path %>
diff --git a/source/config/routes.rb b/source/config/routes.rb
index 3f66539..d02b98d 100644
--- a/source/config/routes.rb
+++ b/source/config/routes.rb
@@ -53,4 +53,10 @@
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
+ #
+
+ get 'application/index'
+ resources :urls
+ get '/:key' => 'urls#jump'
+ root 'application#index'
end
diff --git a/source/db/migrate/20180123203210_create_urls.rb b/source/db/migrate/20180123203210_create_urls.rb
new file mode 100644
index 0000000..7c172a8
--- /dev/null
+++ b/source/db/migrate/20180123203210_create_urls.rb
@@ -0,0 +1,8 @@
+class CreateUrls < ActiveRecord::Migration
+ def change
+ create_table :urls do |t|
+
+ t.timestamps
+ end
+ end
+end
diff --git a/source/db/migrate/20180123215003_add_long_url_to_url.rb b/source/db/migrate/20180123215003_add_long_url_to_url.rb
new file mode 100644
index 0000000..9d890a9
--- /dev/null
+++ b/source/db/migrate/20180123215003_add_long_url_to_url.rb
@@ -0,0 +1,5 @@
+class AddLongUrlToUrl < ActiveRecord::Migration
+ def change
+ add_column :urls, :long_url, :text
+ end
+end
diff --git a/source/db/migrate/20180124143738_add_short_url_to_url.rb b/source/db/migrate/20180124143738_add_short_url_to_url.rb
new file mode 100644
index 0000000..5885fcd
--- /dev/null
+++ b/source/db/migrate/20180124143738_add_short_url_to_url.rb
@@ -0,0 +1,5 @@
+class AddShortUrlToUrl < ActiveRecord::Migration
+ def change
+ add_column :urls, :short_url, :text
+ end
+end
diff --git a/source/db/migrate/20180124160940_add_count_to_url.rb b/source/db/migrate/20180124160940_add_count_to_url.rb
new file mode 100644
index 0000000..8582ba8
--- /dev/null
+++ b/source/db/migrate/20180124160940_add_count_to_url.rb
@@ -0,0 +1,5 @@
+class AddCountToUrl < 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
new file mode 100644
index 0000000..a4ed327
--- /dev/null
+++ b/source/db/schema.rb
@@ -0,0 +1,24 @@
+# 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: 20180124160940) do
+
+ create_table "urls", force: true do |t|
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.text "long_url"
+ t.text "short_url"
+ t.integer "click_count", default: 0
+ end
+
+end