Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions source/.byebug_history
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
continue
next
@url
next
@url
continue
@url.errors.first.essage
@url.errors.full_messages
@url.errors.each { |error| puts error.full_messages }
@url.errors.each do |error|
@url.errors
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should probably go in .gitignore - no reason for it to be part of the history of the repo.

3 changes: 3 additions & 0 deletions source/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
# Ignore bundler config.
/.bundle

# Ignore byebug history
/.byebug_history

# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
Expand Down
6 changes: 3 additions & 3 deletions source/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ gem 'coffee-rails', '~> 4.0.0'
# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'
# gem 'turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.0'
# bundle exec rake doc:rails generates the API under doc/api.
Expand All @@ -36,6 +36,6 @@ gem 'spring', group: :development
# gem 'capistrano-rails', group: :development

# Use debugger
# gem 'debugger', group: [:development, :test]
gem 'rspec-rails', group: [:development, :test]
gem 'byebug'
gem 'rspec-rails', group: [:development, :test]

8 changes: 5 additions & 3 deletions source/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ GEM
tzinfo (~> 1.1)
arel (5.0.1.20140414130214)
builder (3.2.2)
byebug (9.1.0)
coffee-rails (4.0.1)
coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.0)
Expand Down Expand Up @@ -113,8 +114,6 @@ GEM
thor (0.19.1)
thread_safe (0.3.4)
tilt (1.4.1)
turbolinks (2.4.0)
coffee-rails
tzinfo (1.2.2)
thread_safe (~> 0.1)
uglifier (2.5.3)
Expand All @@ -125,6 +124,7 @@ PLATFORMS
ruby

DEPENDENCIES
byebug
coffee-rails (~> 4.0.0)
jbuilder (~> 2.0)
jquery-rails
Expand All @@ -134,5 +134,7 @@ DEPENDENCIES
sdoc (~> 0.4.0)
spring
sqlite3
turbolinks
uglifier (>= 1.3.0)

BUNDLED WITH
1.16.1
26 changes: 26 additions & 0 deletions source/app/controllers/urls_controller.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
class UrlsController < ApplicationController
include UrlsHelper

def index
@urls = Url.all
@url = Url.new(address: "https://")
end

def create
@url = Url.new(url_params)
if @url.save
redirect_to root_path
else
@urls = Url.all
render 'index'
end
end

def show
@url = Url.find_by(slug: params[:slug])
if @url
Url.increment_counter(:click_count, @url.id)
redirect_to @url.address
else
redirect_to root_path
end
end
end
3 changes: 3 additions & 0 deletions source/app/helpers/urls_helper.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
module UrlsHelper
def url_params
params.require(:url).permit(:address)
end
end
40 changes: 40 additions & 0 deletions source/app/models/url.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class Url < ActiveRecord::Base
require 'net/http'
require 'securerandom'

before_save :generate_shortened_url

validates :address, presence: true
validates :address, format: { with: /\Ahttps?:\/\/.*/, message: "must begin with 'http://' or 'https://'" }
validates :address, uniqueness: true
validate :url_is_correct_format, :url_responds_to_http_request

def url_is_correct_format
unless address =~ /\A#{URI.regexp}\z/
errors.add(:address, "is not a valid url format")
end
end

def url_responds_to_http_request
url = URI.parse(address)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could a bad actor use this logic to DOS another site possibly?

Copy link
Copy Markdown
Author

@RupertSaxton RupertSaxton Jan 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jaybobo Yeah, they could. Are there any specific safe practices I could look up? Most of my generic searches just send me to security companies.

begin
response = Net::HTTP.get_response(url)

if response.code == "404"
errors.add(:address, "is not accessible via HTTP(S)")
end
rescue
errors.add(:address, "is not accessible via HTTP(S)")
end
end

def generate_shortened_url
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In most cases, we'd use a random string generator with the length we desire instead of mutating the original address. It's cleaner, easier & more secure.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

#This ensures that if a slug exists then a new one is not created
temp_slug = self.slug || SecureRandom.base64(4)
#Creates a slug if one doesn't exist
while !self.slug && Url.find_by(slug: temp_slug)
temp_slug = SecureRandom.base64(4)
end
self.slug = temp_slug
end
end
2 changes: 0 additions & 2 deletions source/app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
<html>
<head>
<title>Source</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
Expand Down
18 changes: 18 additions & 0 deletions source/app/views/urls/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<p>Index Page</p>

<% if @url.errors %>
<ul>
<% @url.errors.each do |attr, error| %>
<li><%= error %></li>
<% end %>
</ul>
<% end %>

<% @urls.each do |url| %>
<p><%= url.address %> - <%= "#{request.base_url}/#{url.slug}" %> - Clicks: <%= url.click_count %></p>
<% end %>

<%= form_for @url do |f| %>
<%= f.text_field :address %>
<%= f.submit "Shorten URL" %>
<% end %>
55 changes: 3 additions & 52 deletions source/config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,56 +1,7 @@
Rails.application.routes.draw do
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
root "urls#index"

# You can have the root of your site routed with "root"
# root 'welcome#index'
resources :urls, only: [:index, :create]

# Example of regular route:
# get 'products/:id' => 'catalog#view'

# Example of named route that can be invoked with purchase_url(id: product.id)
# get 'products/:id/purchase' => 'catalog#purchase', as: :purchase

# Example resource route (maps HTTP verbs to controller actions automatically):
# resources :products

# Example resource route with options:
# resources :products do
# member do
# get 'short'
# post 'toggle'
# end
#
# collection do
# get 'sold'
# end
# end

# Example resource route with sub-resources:
# resources :products do
# resources :comments, :sales
# resource :seller
# end

# Example resource route with more complex sub-resources:
# resources :products do
# resources :comments
# resources :sales do
# get 'recent', on: :collection
# end
# end

# Example resource route with concerns:
# concern :toggleable do
# post 'toggle'
# end
# resources :posts, concerns: :toggleable
# resources :photos, concerns: :toggleable

# Example resource route within a namespace:
# namespace :admin do
# # Directs /admin/products/* to Admin::ProductsController
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
get '/:slug', to: 'urls#show'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it.

end
9 changes: 9 additions & 0 deletions source/db/migrate/20180123175724_create_urls.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class CreateUrls < ActiveRecord::Migration
def change
create_table :urls do |t|
t.string :address

t.timestamps
end
end
end
5 changes: 5 additions & 0 deletions source/db/migrate/20180123194146_add_click_count_to_urls.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddClickCountToUrls < ActiveRecord::Migration
def change
add_column :urls, :click_count, :integer
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddShortenedAddressSuffixToUrl < ActiveRecord::Migration
def change
add_column :urls, :shortened_address_suffix, :string
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ChangeShortenedAddressSuffixToSlug < ActiveRecord::Migration
def change
rename_column :urls, :shortened_address_suffix, :slug
end
end
5 changes: 5 additions & 0 deletions source/db/migrate/20180125162712_add_index_to_slug.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddIndexToSlug < ActiveRecord::Migration
def change
add_index :urls, :slug
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddDefaultValueToClickCount < ActiveRecord::Migration
def change
change_column :urls, :click_count, :integer, default: 0
end
end
26 changes: 26 additions & 0 deletions source/db/schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 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: 20180130212955) do

create_table "urls", force: true do |t|
t.string "address"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "click_count", default: 0
t.string "slug"
end

add_index "urls", ["slug"], name: "index_urls_on_slug"

end
5 changes: 5 additions & 0 deletions source/spec/models/url_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require 'rails_helper'

RSpec.describe Url, :type => :model do
pending "add some examples to (or delete) #{__FILE__}"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how would you test the feature that you wrote?

end