diff --git a/source/.byebug_history b/source/.byebug_history new file mode 100644 index 0000000..bd778e4 --- /dev/null +++ b/source/.byebug_history @@ -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 diff --git a/source/.gitignore b/source/.gitignore index 6a502e9..e6f33a3 100644 --- a/source/.gitignore +++ b/source/.gitignore @@ -7,6 +7,9 @@ # Ignore bundler config. /.bundle +# Ignore byebug history +/.byebug_history + # Ignore the default SQLite database. /db/*.sqlite3 /db/*.sqlite3-journal diff --git a/source/Gemfile b/source/Gemfile index 9627b8b..61b9127 100644 --- a/source/Gemfile +++ b/source/Gemfile @@ -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. @@ -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] diff --git a/source/Gemfile.lock b/source/Gemfile.lock index fcf8b98..8145aea 100644 --- a/source/Gemfile.lock +++ b/source/Gemfile.lock @@ -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) @@ -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) @@ -125,6 +124,7 @@ PLATFORMS ruby DEPENDENCIES + byebug coffee-rails (~> 4.0.0) jbuilder (~> 2.0) jquery-rails @@ -134,5 +134,7 @@ DEPENDENCIES sdoc (~> 0.4.0) spring sqlite3 - turbolinks uglifier (>= 1.3.0) + +BUNDLED WITH + 1.16.1 diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb index ef26710..664e90d 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -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 diff --git a/source/app/helpers/urls_helper.rb b/source/app/helpers/urls_helper.rb index 83216b1..2224082 100644 --- a/source/app/helpers/urls_helper.rb +++ b/source/app/helpers/urls_helper.rb @@ -1,2 +1,5 @@ module UrlsHelper + def url_params + params.require(:url).permit(:address) + end end diff --git a/source/app/models/url.rb b/source/app/models/url.rb new file mode 100644 index 0000000..d7e80dc --- /dev/null +++ b/source/app/models/url.rb @@ -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) + 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 + #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 diff --git a/source/app/views/layouts/application.html.erb b/source/app/views/layouts/application.html.erb index f946432..b3995a2 100644 --- a/source/app/views/layouts/application.html.erb +++ b/source/app/views/layouts/application.html.erb @@ -2,8 +2,6 @@ Source - <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> - <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> diff --git a/source/app/views/urls/index.html.erb b/source/app/views/urls/index.html.erb new file mode 100644 index 0000000..89df642 --- /dev/null +++ b/source/app/views/urls/index.html.erb @@ -0,0 +1,18 @@ +

Index Page

+ +<% if @url.errors %> + +<% end %> + +<% @urls.each do |url| %> +

<%= url.address %> - <%= "#{request.base_url}/#{url.slug}" %> - Clicks: <%= url.click_count %>

+<% end %> + +<%= form_for @url do |f| %> + <%= f.text_field :address %> + <%= f.submit "Shorten URL" %> +<% end %> \ No newline at end of file diff --git a/source/config/routes.rb b/source/config/routes.rb index 3f66539..4ce6c58 100644 --- a/source/config/routes.rb +++ b/source/config/routes.rb @@ -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' end diff --git a/source/db/migrate/20180123175724_create_urls.rb b/source/db/migrate/20180123175724_create_urls.rb new file mode 100644 index 0000000..fb4a6f4 --- /dev/null +++ b/source/db/migrate/20180123175724_create_urls.rb @@ -0,0 +1,9 @@ +class CreateUrls < ActiveRecord::Migration + def change + create_table :urls do |t| + t.string :address + + t.timestamps + end + end +end diff --git a/source/db/migrate/20180123194146_add_click_count_to_urls.rb b/source/db/migrate/20180123194146_add_click_count_to_urls.rb new file mode 100644 index 0000000..f70faec --- /dev/null +++ b/source/db/migrate/20180123194146_add_click_count_to_urls.rb @@ -0,0 +1,5 @@ +class AddClickCountToUrls < ActiveRecord::Migration + def change + add_column :urls, :click_count, :integer + end +end diff --git a/source/db/migrate/20180124154857_add_shortened_address_suffix_to_url.rb b/source/db/migrate/20180124154857_add_shortened_address_suffix_to_url.rb new file mode 100644 index 0000000..19a6902 --- /dev/null +++ b/source/db/migrate/20180124154857_add_shortened_address_suffix_to_url.rb @@ -0,0 +1,5 @@ +class AddShortenedAddressSuffixToUrl < ActiveRecord::Migration + def change + add_column :urls, :shortened_address_suffix, :string + end +end diff --git a/source/db/migrate/20180124180333_change_shortened_address_suffix_to_slug.rb b/source/db/migrate/20180124180333_change_shortened_address_suffix_to_slug.rb new file mode 100644 index 0000000..1f00ed0 --- /dev/null +++ b/source/db/migrate/20180124180333_change_shortened_address_suffix_to_slug.rb @@ -0,0 +1,5 @@ +class ChangeShortenedAddressSuffixToSlug < ActiveRecord::Migration + def change + rename_column :urls, :shortened_address_suffix, :slug + end +end diff --git a/source/db/migrate/20180125162712_add_index_to_slug.rb b/source/db/migrate/20180125162712_add_index_to_slug.rb new file mode 100644 index 0000000..b0f62ae --- /dev/null +++ b/source/db/migrate/20180125162712_add_index_to_slug.rb @@ -0,0 +1,5 @@ +class AddIndexToSlug < ActiveRecord::Migration + def change + add_index :urls, :slug + end +end diff --git a/source/db/migrate/20180130212955_add_default_value_to_click_count.rb b/source/db/migrate/20180130212955_add_default_value_to_click_count.rb new file mode 100644 index 0000000..042820d --- /dev/null +++ b/source/db/migrate/20180130212955_add_default_value_to_click_count.rb @@ -0,0 +1,5 @@ +class AddDefaultValueToClickCount < ActiveRecord::Migration + def change + change_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..90e75a0 --- /dev/null +++ b/source/db/schema.rb @@ -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 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