From 817a193713761cf5dcfe895f8a0ddad0eb73f3ce Mon Sep 17 00:00:00 2001 From: Lucas Nestor Date: Mon, 14 May 2018 13:41:48 -0400 Subject: [PATCH 1/9] Complete Release 0 --- source/.rspec | 2 + source/Gemfile | 2 +- source/Gemfile.lock | 109 ++++++++++-------- source/app/controllers/urls_controller.rb | 20 ++++ source/app/models/url.rb | 2 + source/app/views/urls/index.html.erb | 5 + source/config/routes.rb | 2 + .../db/migrate/20180514142357_create_urls.rb | 9 ++ source/db/schema.rb | 22 ++++ .../spec/controllers/url_controller_spec.rb | 42 +++++++ source/spec/models/url_spec.rb | 5 + source/spec/rails_helper.rb | 50 ++++++++ source/spec/spec_helper.rb | 85 ++++++++++++++ 13 files changed, 305 insertions(+), 50 deletions(-) create mode 100644 source/.rspec create mode 100644 source/app/models/url.rb create mode 100644 source/app/views/urls/index.html.erb create mode 100644 source/db/migrate/20180514142357_create_urls.rb create mode 100644 source/db/schema.rb create mode 100644 source/spec/controllers/url_controller_spec.rb create mode 100644 source/spec/models/url_spec.rb create mode 100644 source/spec/rails_helper.rb create mode 100644 source/spec/spec_helper.rb diff --git a/source/.rspec b/source/.rspec new file mode 100644 index 0000000..83e16f8 --- /dev/null +++ b/source/.rspec @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/source/Gemfile b/source/Gemfile index 9627b8b..e2ca7a3 100644 --- a/source/Gemfile +++ b/source/Gemfile @@ -38,4 +38,4 @@ gem 'spring', group: :development # Use debugger # gem 'debugger', group: [:development, :test] gem 'rspec-rails', group: [:development, :test] - + gem 'pry' diff --git a/source/Gemfile.lock b/source/Gemfile.lock index fcf8b98..0d09b2a 100644 --- a/source/Gemfile.lock +++ b/source/Gemfile.lock @@ -28,33 +28,40 @@ GEM thread_safe (~> 0.1) tzinfo (~> 1.1) arel (5.0.1.20140414130214) - builder (3.2.2) + builder (3.2.3) + coderay (1.1.2) coffee-rails (4.0.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) - coffee-script (2.3.0) + coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.8.0) - diff-lcs (1.2.5) + coffee-script-source (1.12.2) + concurrent-ruby (1.0.5) + diff-lcs (1.3) erubis (2.7.0) - execjs (2.2.1) + execjs (2.7.0) hike (1.2.3) - i18n (0.6.11) - jbuilder (2.2.2) - activesupport (>= 3.0.0, < 5) - multi_json (~> 1.2) - jquery-rails (3.1.2) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + jbuilder (2.6.4) + activesupport (>= 3.0.0) + multi_json (>= 1.2) + jquery-rails (3.1.5) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.8.1) - mail (2.6.1) - mime-types (>= 1.16, < 3) - mime-types (2.4.1) - minitest (5.4.2) - multi_json (1.10.1) - rack (1.5.2) - rack-test (0.6.2) + json (1.8.6) + mail (2.7.0) + mini_mime (>= 0.1.1) + method_source (0.9.0) + mini_mime (1.0.0) + minitest (5.11.3) + multi_json (1.13.1) + pry (0.11.3) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + rack (1.5.5) + rack-test (0.6.3) rack (>= 1.0) rails (4.1.6) actionmailer (= 4.1.6) @@ -71,55 +78,55 @@ GEM activesupport (= 4.1.6) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (10.3.2) - rdoc (4.1.2) - json (~> 1.4) - rspec-core (3.1.6) - rspec-support (~> 3.1.0) - rspec-expectations (3.1.2) + rake (12.3.1) + rdoc (4.3.0) + rspec-core (3.7.1) + rspec-support (~> 3.7.0) + rspec-expectations (3.7.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.1.0) - rspec-mocks (3.1.3) - rspec-support (~> 3.1.0) - rspec-rails (3.1.0) + rspec-support (~> 3.7.0) + rspec-mocks (3.7.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.7.0) + rspec-rails (3.7.2) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.1.0) - rspec-expectations (~> 3.1.0) - rspec-mocks (~> 3.1.0) - rspec-support (~> 3.1.0) - rspec-support (3.1.2) + rspec-core (~> 3.7.0) + rspec-expectations (~> 3.7.0) + rspec-mocks (~> 3.7.0) + rspec-support (~> 3.7.0) + rspec-support (3.7.1) sass (3.2.19) - sass-rails (4.0.3) + sass-rails (4.0.5) railties (>= 4.0.0, < 5.0) - sass (~> 3.2.0) - sprockets (~> 2.8, <= 2.11.0) + sass (~> 3.2.2) + sprockets (~> 2.8, < 3.0) sprockets-rails (~> 2.0) - sdoc (0.4.1) + sdoc (0.4.2) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) - spring (1.1.3) - sprockets (2.11.0) + spring (1.7.2) + sprockets (2.12.4) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.2.0) + sprockets-rails (2.3.3) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) - sqlite3 (1.3.9) - thor (0.19.1) - thread_safe (0.3.4) + sqlite3 (1.3.13) + thor (0.20.0) + thread_safe (0.3.6) tilt (1.4.1) - turbolinks (2.4.0) - coffee-rails - tzinfo (1.2.2) + turbolinks (5.1.1) + turbolinks-source (~> 5.1) + turbolinks-source (5.1.0) + tzinfo (1.2.5) thread_safe (~> 0.1) - uglifier (2.5.3) - execjs (>= 0.3.0) - json (>= 1.8.0) + uglifier (4.1.10) + execjs (>= 0.3.0, < 3) PLATFORMS ruby @@ -128,6 +135,7 @@ DEPENDENCIES coffee-rails (~> 4.0.0) jbuilder (~> 2.0) jquery-rails + pry rails (= 4.1.6) rspec-rails sass-rails (~> 4.0.3) @@ -136,3 +144,6 @@ DEPENDENCIES 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..026d232 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -1,2 +1,22 @@ class UrlsController < ApplicationController + def index + @urls = Url.all + end + + def create + url = Url.new(url_params) + url.save + redirect_to urls_path + end + + def show + url = Url.find(params[:id]) + redirect_to url.long_url + 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..e4834ff --- /dev/null +++ b/source/app/models/url.rb @@ -0,0 +1,2 @@ +class Url < ActiveRecord::Base +end diff --git a/source/app/views/urls/index.html.erb b/source/app/views/urls/index.html.erb new file mode 100644 index 0000000..0f1dd68 --- /dev/null +++ b/source/app/views/urls/index.html.erb @@ -0,0 +1,5 @@ +Url Index + +<% @urls.each do |url| %> +

<%= url.long_url %>

+<% end %> diff --git a/source/config/routes.rb b/source/config/routes.rb index 3f66539..8b1aada 100644 --- a/source/config/routes.rb +++ b/source/config/routes.rb @@ -53,4 +53,6 @@ # # (app/controllers/admin/products_controller.rb) # resources :products # end + + resources :urls, only: [:index, :create, :show] end diff --git a/source/db/migrate/20180514142357_create_urls.rb b/source/db/migrate/20180514142357_create_urls.rb new file mode 100644 index 0000000..604bf55 --- /dev/null +++ b/source/db/migrate/20180514142357_create_urls.rb @@ -0,0 +1,9 @@ +class CreateUrls < ActiveRecord::Migration + def change + create_table :urls do |t| + t.string :long_url + + t.timestamps + end + end +end diff --git a/source/db/schema.rb b/source/db/schema.rb new file mode 100644 index 0000000..3162cf7 --- /dev/null +++ b/source/db/schema.rb @@ -0,0 +1,22 @@ +# 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: 20180514142357) do + + create_table "urls", force: true do |t| + t.string "long_url" + t.datetime "created_at" + t.datetime "updated_at" + end + +end diff --git a/source/spec/controllers/url_controller_spec.rb b/source/spec/controllers/url_controller_spec.rb new file mode 100644 index 0000000..c0369ca --- /dev/null +++ b/source/spec/controllers/url_controller_spec.rb @@ -0,0 +1,42 @@ +require 'rails_helper' + +describe UrlsController do + describe 'GET index' do + it 'responds with a 200 OK' do + get :index + expect(response.status).to eq 200 + end + + it 'assigns @urls' do + url = Url.create + get :index + expect(assigns[:urls]).to eq [url] + end + + it 'renders the index template' do + get :index + expect(response).to render_template('index') + end + end + + describe 'POST create' do + it 'creates a url entry' do + expect do + post :create, url: { long_url: 'https://www.google.com/' } + end.to change { Url.count }.by 1 + end + + it 'redirects to the index page' do + post :create, url: { long_url: 'https://www.google.com/' } + expect(response).to redirect_to urls_path + end + end + + describe 'GET show' do + it 'redirects to the long url' do + url = Url.create(long_url: 'https://www.google.com/') + get :show, { id: url.id } + expect(response).to redirect_to 'https://www.google.com/' + end + end +end diff --git a/source/spec/models/url_spec.rb b/source/spec/models/url_spec.rb new file mode 100644 index 0000000..9469fc2 --- /dev/null +++ b/source/spec/models/url_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Url, :type => :model do + let(:url) { Url.new(long_url: 'https://www.google.com/') } +end diff --git a/source/spec/rails_helper.rb b/source/spec/rails_helper.rb new file mode 100644 index 0000000..e6c0b68 --- /dev/null +++ b/source/spec/rails_helper.rb @@ -0,0 +1,50 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +ENV["RAILS_ENV"] ||= 'test' +require 'spec_helper' +require File.expand_path("../../config/environment", __FILE__) +require 'rspec/rails' +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } + +# Checks for pending migrations before tests are run. +# If you are not using ActiveRecord, you can remove this line. +ActiveRecord::Migration.maintain_test_schema! + +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # RSpec Rails can automatically mix in different behaviours to your tests + # based on their file location, for example enabling you to call `get` and + # `post` in specs under `spec/controllers`. + # + # You can disable this behaviour by removing the line below, and instead + # explicitly tag your specs with their type, e.g.: + # + # RSpec.describe UsersController, :type => :controller do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://relishapp.com/rspec/rspec-rails/docs + config.infer_spec_type_from_file_location! +end diff --git a/source/spec/spec_helper.rb b/source/spec/spec_helper.rb new file mode 100644 index 0000000..275ba49 --- /dev/null +++ b/source/spec/spec_helper.rb @@ -0,0 +1,85 @@ +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause this +# file to always be loaded, without a need to explicitly require it in any files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need it. +# +# The `.rspec` file also contains a few flags that are not defaults but that +# users commonly want. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # These two settings work together to allow you to limit a spec run + # to individual examples or groups you care about by tagging them with + # `:focus` metadata. When nothing is tagged with `:focus`, all examples + # get run. + config.filter_run :focus + config.run_all_when_everything_filtered = true + + # Limits the available syntax to the non-monkey patched syntax that is recommended. + # For more details, see: + # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax + # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = 'doc' + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end From 4eca9cc8178a630e195a2742a5a6da0d9b3fec00 Mon Sep 17 00:00:00 2001 From: Lucas Nestor Date: Mon, 14 May 2018 13:55:57 -0400 Subject: [PATCH 2/9] Add form for user to shorten urls --- source/app/views/urls/index.html.erb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/source/app/views/urls/index.html.erb b/source/app/views/urls/index.html.erb index 0f1dd68..5221ebf 100644 --- a/source/app/views/urls/index.html.erb +++ b/source/app/views/urls/index.html.erb @@ -1,5 +1,12 @@ -Url Index +

Shorten URL:

+<%= form_for :url do |f| %> + <%= f.label :long_url %> + <%= f.text_field :long_url %> +<% end %> + + From 94b5e0b1d598483cd364340a7d6b697a9feca9c7 Mon Sep 17 00:00:00 2001 From: Lucas Nestor Date: Mon, 14 May 2018 14:15:04 -0400 Subject: [PATCH 3/9] Complete Release 2 --- source/app/controllers/urls_controller.rb | 3 +++ .../20180514175827_add_click_counter_to_urls.rb | 5 +++++ source/db/schema.rb | 3 ++- source/spec/controllers/url_controller_spec.rb | 14 +++++++++++++- 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 source/db/migrate/20180514175827_add_click_counter_to_urls.rb diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb index 026d232..46e4266 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -5,12 +5,15 @@ def index def create url = Url.new(url_params) + url.click_count = 0 url.save redirect_to urls_path end def show url = Url.find(params[:id]) + url.click_count += 1 + url.save redirect_to url.long_url end diff --git a/source/db/migrate/20180514175827_add_click_counter_to_urls.rb b/source/db/migrate/20180514175827_add_click_counter_to_urls.rb new file mode 100644 index 0000000..47e4d97 --- /dev/null +++ b/source/db/migrate/20180514175827_add_click_counter_to_urls.rb @@ -0,0 +1,5 @@ +class AddClickCounterToUrls < ActiveRecord::Migration + def change + add_column :urls, :click_count, :integer + end +end diff --git a/source/db/schema.rb b/source/db/schema.rb index 3162cf7..ff89ae6 100644 --- a/source/db/schema.rb +++ b/source/db/schema.rb @@ -11,12 +11,13 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180514142357) do +ActiveRecord::Schema.define(version: 20180514175827) do create_table "urls", force: true do |t| t.string "long_url" t.datetime "created_at" t.datetime "updated_at" + t.integer "click_count" end end diff --git a/source/spec/controllers/url_controller_spec.rb b/source/spec/controllers/url_controller_spec.rb index c0369ca..cb62336 100644 --- a/source/spec/controllers/url_controller_spec.rb +++ b/source/spec/controllers/url_controller_spec.rb @@ -30,13 +30,25 @@ post :create, url: { long_url: 'https://www.google.com/' } expect(response).to redirect_to urls_path end + + it 'sets the click counter to 0' do + post :create, url: { long_url: 'https://www.google.com/' } + expect(Url.first.click_count).to eq 0 + end end describe 'GET show' do + let(:url) { Url.create(long_url: 'https://www.google.com/', click_count: 0) } + it 'redirects to the long url' do - url = Url.create(long_url: 'https://www.google.com/') get :show, { id: url.id } expect(response).to redirect_to 'https://www.google.com/' end + + it 'increases the click_counter' do + expect do + get :show, { id: url.id } + end.to change { url.reload.click_count }.by 1 + end end end From bb19c2a46a524880652252f674ab1c96ce750244 Mon Sep 17 00:00:00 2001 From: Lucas Nestor Date: Mon, 14 May 2018 14:33:22 -0400 Subject: [PATCH 4/9] Complete Release 2 --- source/app/models/url.rb | 1 + source/spec/controllers/url_controller_spec.rb | 5 ++--- source/spec/models/url_spec.rb | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/source/app/models/url.rb b/source/app/models/url.rb index e4834ff..3291b62 100644 --- a/source/app/models/url.rb +++ b/source/app/models/url.rb @@ -1,2 +1,3 @@ class Url < ActiveRecord::Base + validates :long_url, presence: true, format: { with: /http(|s):\/\/.+/ } end diff --git a/source/spec/controllers/url_controller_spec.rb b/source/spec/controllers/url_controller_spec.rb index cb62336..7087167 100644 --- a/source/spec/controllers/url_controller_spec.rb +++ b/source/spec/controllers/url_controller_spec.rb @@ -1,6 +1,8 @@ require 'rails_helper' describe UrlsController do + let(:url) { Url.create(long_url: 'https://www.google.com/', click_count: 0) } + describe 'GET index' do it 'responds with a 200 OK' do get :index @@ -8,7 +10,6 @@ end it 'assigns @urls' do - url = Url.create get :index expect(assigns[:urls]).to eq [url] end @@ -38,8 +39,6 @@ end describe 'GET show' do - let(:url) { Url.create(long_url: 'https://www.google.com/', click_count: 0) } - it 'redirects to the long url' do get :show, { id: url.id } expect(response).to redirect_to 'https://www.google.com/' diff --git a/source/spec/models/url_spec.rb b/source/spec/models/url_spec.rb index 9469fc2..1bc3e0a 100644 --- a/source/spec/models/url_spec.rb +++ b/source/spec/models/url_spec.rb @@ -2,4 +2,22 @@ RSpec.describe Url, :type => :model do let(:url) { Url.new(long_url: 'https://www.google.com/') } + + describe 'long_url' do + it 'is not an empty string' do + url.long_url = '' + expect(url).to_not be_valid + end + + it 'has a prefix http:// or https://' do + url.long_url = 'www.google.com' + expect(url).to_not be_valid + + url.long_url = 'http://www.google.com' + expect(url).to be_valid + + url.long_url = 'https://www.google.com' + expect(url).to be_valid + end + end end From fb947e006de9dacb1f9636984871abb13ff0446b Mon Sep 17 00:00:00 2001 From: Lucas Nestor Date: Mon, 14 May 2018 14:41:10 -0400 Subject: [PATCH 5/9] Complete Release 3 --- source/app/controllers/urls_controller.rb | 4 +- source/app/views/urls/index.html.erb | 2 + .../spec/controllers/url_controller_spec.rb | 40 ++++++++++++++----- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb index 46e4266..2fd7c61 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -6,7 +6,9 @@ def index def create url = Url.new(url_params) url.click_count = 0 - url.save + if !url.save + flash[:error] = 'Invalid URL' + end redirect_to urls_path end diff --git a/source/app/views/urls/index.html.erb b/source/app/views/urls/index.html.erb index 5221ebf..642cb19 100644 --- a/source/app/views/urls/index.html.erb +++ b/source/app/views/urls/index.html.erb @@ -1,5 +1,7 @@

Shorten URL:

+<%= flash[:error] if !nil? %> + <%= form_for :url do |f| %> <%= f.label :long_url %> <%= f.text_field :long_url %> diff --git a/source/spec/controllers/url_controller_spec.rb b/source/spec/controllers/url_controller_spec.rb index 7087167..d507aef 100644 --- a/source/spec/controllers/url_controller_spec.rb +++ b/source/spec/controllers/url_controller_spec.rb @@ -21,20 +21,40 @@ end describe 'POST create' do - it 'creates a url entry' do - expect do + context 'with valid params' do + it 'creates a url entry' do + expect do + post :create, url: { long_url: 'https://www.google.com/' } + end.to change { Url.count }.by 1 + end + + it 'redirects to the index page' do post :create, url: { long_url: 'https://www.google.com/' } - end.to change { Url.count }.by 1 - end + expect(response).to redirect_to urls_path + end - it 'redirects to the index page' do - post :create, url: { long_url: 'https://www.google.com/' } - expect(response).to redirect_to urls_path + it 'sets the click counter to 0' do + post :create, url: { long_url: 'https://www.google.com/' } + expect(Url.first.click_count).to eq 0 + end end - it 'sets the click counter to 0' do - post :create, url: { long_url: 'https://www.google.com/' } - expect(Url.first.click_count).to eq 0 + context 'with invalid params' do + it 'does not create a url entry' do + expect do + post :create, url: { long_url: 'invalid URL' } + end.to_not change { Url.count } + end + + it 'redirects to the index page' do + post :create, url: { long_url: 'invalid URL' } + expect(response).to redirect_to urls_path + end + + it 'displays an error message' do + post :create, url: { long_url: 'invalid URL' } + expect(flash[:error]).to eq 'Invalid URL' + end end end From 8ad65a05abc36eb5c37e2abe93614741c890a645 Mon Sep 17 00:00:00 2001 From: Lucas Nestor Date: Tue, 15 May 2018 09:03:42 -0400 Subject: [PATCH 6/9] Add click count to view --- source/app/views/urls/index.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/app/views/urls/index.html.erb b/source/app/views/urls/index.html.erb index 642cb19..e3894f5 100644 --- a/source/app/views/urls/index.html.erb +++ b/source/app/views/urls/index.html.erb @@ -9,6 +9,6 @@
    <% @urls.each do |url| %> -
  • <%= "0.0.0.0:3000/urls/#{url.id} ====> " + url.long_url %>
  • +
  • <%= "0.0.0.0:3000/urls/#{url.id} ====> #{url.long_url} (clicked #{url.click_count} times)" %>
  • <% end %>
From 0fa1957369dd33afbb863ab645c432752cc76f20 Mon Sep 17 00:00:00 2001 From: Lucas Nestor Date: Wed, 16 May 2018 10:47:11 -0400 Subject: [PATCH 7/9] Add additional url validations and moved behavior from controller to model --- source/app/controllers/urls_controller.rb | 5 +++- source/app/models/url.rb | 27 +++++++++++++++++++ source/app/views/urls/index.html.erb | 2 +- .../spec/controllers/url_controller_spec.rb | 20 +++++++++++++- source/spec/models/url_spec.rb | 12 ++++++++- 5 files changed, 62 insertions(+), 4 deletions(-) diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb index 2fd7c61..858bc99 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -1,3 +1,5 @@ +require 'net/http' + class UrlsController < ApplicationController def index @urls = Url.all @@ -5,10 +7,11 @@ def index def create url = Url.new(url_params) - url.click_count = 0 + if !url.save flash[:error] = 'Invalid URL' end + redirect_to urls_path end diff --git a/source/app/models/url.rb b/source/app/models/url.rb index 3291b62..b7d564b 100644 --- a/source/app/models/url.rb +++ b/source/app/models/url.rb @@ -1,3 +1,30 @@ +require 'net/http' + class Url < ActiveRecord::Base + before_create :reset_click_count + validates :long_url, presence: true, format: { with: /http(|s):\/\/.+/ } + validate :long_url_is_uri_valid + validate :long_url_is_reachable + + private + + def reset_click_count + self.click_count = 0 + end + + def long_url_is_uri_valid + unless long_url =~ /\A#{URI::regexp(['http', 'https'])}\z/ + errors.add(:long_url, 'must be valid uri') + end + end + + def long_url_is_reachable + begin + url = URI(long_url) + Net::HTTP.get_response(url) + rescue + errors.add(:long_url, 'must be accessible') + end + end end diff --git a/source/app/views/urls/index.html.erb b/source/app/views/urls/index.html.erb index e3894f5..6ab69fa 100644 --- a/source/app/views/urls/index.html.erb +++ b/source/app/views/urls/index.html.erb @@ -9,6 +9,6 @@
    <% @urls.each do |url| %> -
  • <%= "0.0.0.0:3000/urls/#{url.id} ====> #{url.long_url} (clicked #{url.click_count} times)" %>
  • +
  • <%= url_url(url) ====> #{url.long_url} (clicked #{url.click_count} times)" %>
  • <% end %>
diff --git a/source/spec/controllers/url_controller_spec.rb b/source/spec/controllers/url_controller_spec.rb index d507aef..4f8b509 100644 --- a/source/spec/controllers/url_controller_spec.rb +++ b/source/spec/controllers/url_controller_spec.rb @@ -39,7 +39,7 @@ end end - context 'with invalid params' do + context 'with blantantly invalid params' do it 'does not create a url entry' do expect do post :create, url: { long_url: 'invalid URL' } @@ -56,6 +56,24 @@ expect(flash[:error]).to eq 'Invalid URL' end end + + context 'with valid params but unaccessible webpage' do + it 'does not create a url entry' do + expect do + post :create, url: { long_url: 'https://blah.com' } + end.to_not change { Url.count } + end + + it 'redirects to the index page' do + post :create, url: { long_url: 'https://blah.com' } + expect(response).to redirect_to urls_path + end + + it 'displays an error message' do + post :create, url: { long_url: 'https://blah.com' } + expect(flash[:error]).to eq 'Invalid URL' + end + end end describe 'GET show' do diff --git a/source/spec/models/url_spec.rb b/source/spec/models/url_spec.rb index 1bc3e0a..38409b9 100644 --- a/source/spec/models/url_spec.rb +++ b/source/spec/models/url_spec.rb @@ -16,8 +16,18 @@ url.long_url = 'http://www.google.com' expect(url).to be_valid - url.long_url = 'https://www.google.com' + url.long_url = 'https://www.google.com/' expect(url).to be_valid end + + it 'is a valid uri' do + url.long_url = 'invalid uri' + expect(url).to_not be_valid + end + + it 'is accessible' do + url.long_url = 'https://invalid.com' + expect(url).to_not be_valid + end end end From 0d7bc536128a5fa03123d97b2cc3960cb01d003b Mon Sep 17 00:00:00 2001 From: Lucas Nestor Date: Wed, 16 May 2018 13:27:31 -0400 Subject: [PATCH 8/9] Switch to HTTParty gem to decrease spec running times --- source/Gemfile | 2 ++ source/Gemfile.lock | 4 ++++ source/app/models/url.rb | 7 +++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/source/Gemfile b/source/Gemfile index e2ca7a3..c511dfa 100644 --- a/source/Gemfile +++ b/source/Gemfile @@ -29,6 +29,8 @@ gem 'spring', group: :development # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' +gem 'httparty' + # Use unicorn as the app server # gem 'unicorn' diff --git a/source/Gemfile.lock b/source/Gemfile.lock index 0d09b2a..74399e9 100644 --- a/source/Gemfile.lock +++ b/source/Gemfile.lock @@ -42,6 +42,8 @@ GEM erubis (2.7.0) execjs (2.7.0) hike (1.2.3) + httparty (0.15.6) + multi_xml (>= 0.5.2) i18n (0.9.5) concurrent-ruby (~> 1.0) jbuilder (2.6.4) @@ -57,6 +59,7 @@ GEM mini_mime (1.0.0) minitest (5.11.3) multi_json (1.13.1) + multi_xml (0.6.0) pry (0.11.3) coderay (~> 1.1.0) method_source (~> 0.9.0) @@ -133,6 +136,7 @@ PLATFORMS DEPENDENCIES coffee-rails (~> 4.0.0) + httparty jbuilder (~> 2.0) jquery-rails pry diff --git a/source/app/models/url.rb b/source/app/models/url.rb index b7d564b..0a5f29c 100644 --- a/source/app/models/url.rb +++ b/source/app/models/url.rb @@ -21,8 +21,11 @@ def long_url_is_uri_valid def long_url_is_reachable begin - url = URI(long_url) - Net::HTTP.get_response(url) + response = HTTParty.get(long_url, timeout: 2) + + if response.code.to_i != 200 + errors.add(:long_url, 'must return a 200 code') + end rescue errors.add(:long_url, 'must be accessible') end From 006bf22c973ac966f6f6dbeffd55c72eeca642fd Mon Sep 17 00:00:00 2001 From: Lucas Nestor Date: Wed, 16 May 2018 14:34:12 -0400 Subject: [PATCH 9/9] Remove unnecessary validations and let rails do the work for me --- source/app/models/url.rb | 13 ++----------- ...211_add_default_value_to_click_count_for_urls.rb | 5 +++++ source/db/schema.rb | 13 +++++++++++-- 3 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 source/db/migrate/20180516183211_add_default_value_to_click_count_for_urls.rb diff --git a/source/app/models/url.rb b/source/app/models/url.rb index 0a5f29c..eeb42e5 100644 --- a/source/app/models/url.rb +++ b/source/app/models/url.rb @@ -1,18 +1,9 @@ -require 'net/http' - class Url < ActiveRecord::Base - before_create :reset_click_count - - validates :long_url, presence: true, format: { with: /http(|s):\/\/.+/ } + validates :long_url, presence: true validate :long_url_is_uri_valid validate :long_url_is_reachable private - - def reset_click_count - self.click_count = 0 - end - def long_url_is_uri_valid unless long_url =~ /\A#{URI::regexp(['http', 'https'])}\z/ errors.add(:long_url, 'must be valid uri') @@ -21,7 +12,7 @@ def long_url_is_uri_valid def long_url_is_reachable begin - response = HTTParty.get(long_url, timeout: 2) + response = HTTParty.get(long_url, timeout: 1) if response.code.to_i != 200 errors.add(:long_url, 'must return a 200 code') diff --git a/source/db/migrate/20180516183211_add_default_value_to_click_count_for_urls.rb b/source/db/migrate/20180516183211_add_default_value_to_click_count_for_urls.rb new file mode 100644 index 0000000..7784cdd --- /dev/null +++ b/source/db/migrate/20180516183211_add_default_value_to_click_count_for_urls.rb @@ -0,0 +1,5 @@ +class AddDefaultValueToClickCountForUrls < ActiveRecord::Migration + def change + change_column_default :urls, :click_count, 0 + end +end diff --git a/source/db/schema.rb b/source/db/schema.rb index ff89ae6..f68b2e4 100644 --- a/source/db/schema.rb +++ b/source/db/schema.rb @@ -11,13 +11,22 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180514175827) do +ActiveRecord::Schema.define(version: 20180516183211) do create_table "urls", force: true do |t| t.string "long_url" t.datetime "created_at" t.datetime "updated_at" - t.integer "click_count" + t.integer "click_count", default: 0 + t.string "short_url" + end + + create_table "users", force: true do |t| + t.string "username" + t.string "password" + t.boolean "admin" + t.datetime "created_at" + t.datetime "updated_at" end end