fix: parsed_url + stripped_url
- store and redirect urls without protocol - update README - increment counter with SQL COALESCE - add linksHelper - update tests
This commit is contained in:
@@ -40,44 +40,10 @@ docker-compose run --rm app bundle exec rails test
|
||||
docker-compose run --rm app bundle exec rubocop
|
||||
```
|
||||
|
||||
# Results
|
||||
|
||||
## Testing
|
||||
## Dokku deployment
|
||||
```bash
|
||||
Finished in 2.211344s, 9.9487 runs/s, 10.4009 assertions/s.
|
||||
22 runs, 23 assertions, 0 failures, 0 errors, 0 skips
|
||||
Coverage report generated for Minitest to /usr/src/app/coverage. 81 / 81 LOC (100.0%) covered.
|
||||
|
||||
COVERAGE: 100.00% -- 81/81 lines in 8 files
|
||||
BRANCH COVERAGE: 100.00% -- 20/20 branches in 8 branches
|
||||
|
||||
+----------+-------------------------------------------+-------+--------+---------+-----------------+----------+-----------------+------------------+
|
||||
| coverage | file | lines | missed | missing | branch coverage | branches | branches missed | branches missing |
|
||||
+----------+-------------------------------------------+-------+--------+---------+-----------------+----------+-----------------+------------------+
|
||||
| 100.00% | app/controllers/application_controller.rb | 3 | 0 | | 100.00% | 0 | 0 | |
|
||||
| 100.00% | app/controllers/links_controller.rb | 23 | 0 | | 100.00% | 8 | 0 | |
|
||||
| 100.00% | app/controllers/sessions_controller.rb | 17 | 0 | | 100.00% | 4 | 0 | |
|
||||
| 100.00% | app/controllers/users_controller.rb | 16 | 0 | | 100.00% | 4 | 0 | |
|
||||
| 100.00% | app/helpers/sessions_helper.rb | 3 | 0 | | 100.00% | 0 | 0 | |
|
||||
| 100.00% | app/models/application_record.rb | 2 | 0 | | 100.00% | 0 | 0 | |
|
||||
| 100.00% | app/models/link.rb | 13 | 0 | | 100.00% | 4 | 0 | |
|
||||
| 100.00% | app/models/user.rb | 4 | 0 | | 100.00% | 0 | 0 | |
|
||||
+----------+-------------------------------------------+-------+--------+---------+-----------------+----------+-----------------+------------------+
|
||||
```
|
||||
|
||||
## Rubocop
|
||||
```bash
|
||||
Inspecting 43 files
|
||||
...........................................
|
||||
|
||||
43 files inspected, no offenses detected
|
||||
```
|
||||
|
||||
- Dokku deployment
|
||||
```bash
|
||||
bundle exec rails assets:precompile
|
||||
bundle exec rails db:migrate
|
||||
```
|
||||
|
||||
- Production link
|
||||
## Production link
|
||||
https://url-shortener.sjdonado.de
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class LinksController < ApplicationController
|
||||
include LinksHelper
|
||||
|
||||
before_action :authenticate, only: %i[create]
|
||||
before_action :set_link, only: %i[redirect counter]
|
||||
|
||||
def redirect
|
||||
if @link
|
||||
@link.update(click_counter: @link.click_counter + 1)
|
||||
redirect_to @link.url
|
||||
@link.increment!(:click_counter) # rubocop:disable Rails/SkipsModelValidations
|
||||
redirect_to @link.parsed_url
|
||||
else
|
||||
render file: Rails.root.join('/public/404'), status: :not_found
|
||||
end
|
||||
@@ -22,7 +24,9 @@ class LinksController < ApplicationController
|
||||
end
|
||||
|
||||
def create
|
||||
@link = Link.find_or_create_by(url: link_params[:url]) do |link|
|
||||
url = stripped_url(link_params[:url])
|
||||
|
||||
@link = Link.find_or_create_by(url: url) do |link|
|
||||
link.user = @current_user if @current_user
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module LinksHelper
|
||||
def stripped_url(url)
|
||||
url.sub(%r{^.*://(www\.)?}, '').sub(/^www\./, '')
|
||||
end
|
||||
end
|
||||
+9
-5
@@ -1,18 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
VALID_URL_REGEX = /\A(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)? \
|
||||
([a-zA-Z0-9-]+\.[a-zA-Z]{2,}(?:\.[a-zA-Z]{2,})?) \
|
||||
(?:\/[^\s]*)?\z/
|
||||
|
||||
class Link < ApplicationRecord
|
||||
validates :url, presence: true
|
||||
validates :slug, uniqueness: true
|
||||
|
||||
validates :url, format: { with: VALID_URL_REGEX, message: 'invalid format' }
|
||||
validates :url,
|
||||
format: {
|
||||
with: %r{\A(https?://)?(www\.)?([a-zA-Z0-9_-]+\.)+[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?([/?#][^\s]*)?\z}i,
|
||||
message: 'must be a valid URL'
|
||||
}
|
||||
validates :url, length: { within: 3..30_000, on: :create, message: 'max length is 30000' }
|
||||
|
||||
before_validation :generate_slug
|
||||
|
||||
def parsed_url
|
||||
"https://#{url}"
|
||||
end
|
||||
|
||||
def generate_slug(attempts = 0)
|
||||
return if slug.present? || attempts == 3
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
URL Shortener
|
||||
</label>
|
||||
<div class="mt-4 flex rounded-md shadow-sm">
|
||||
<%= f.text_field :url, pattern: '(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([a-zA-Z0-9-]+\.[a-zA-Z]{2,}(?:\.[a-zA-Z]{2,})?)(?:\/[^\s]*)?', data: { target: "links.url" }, placeholder: "https://google.com", class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
|
||||
<%= f.text_field :url, pattern: '(https?:\/\/)?(www\.)?([a-zA-Z0-9_-]+\.)+[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?([\/?#][^\s]*)?', data: { target: "links.url" }, placeholder: "https://google.com", class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
Website
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
<a href="<%= link.url %>" class="mt-1 text-sm text-gray-600 underline" target="_blank" rel="noreferrer"><%= link.url %></a>
|
||||
<a href="<%= link.parsed_url %>" class="mt-1 text-sm text-gray-600 underline" target="_blank" rel="noreferrer"><%= link.url %></a>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="bg-white px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
|
||||
@@ -42,9 +42,10 @@ class LinksControllerTest < ActionDispatch::IntegrationTest
|
||||
test 'Should redirect from slug to url' do
|
||||
link = links(:one)
|
||||
slug = link.slug
|
||||
|
||||
get short_url(slug: slug)
|
||||
|
||||
assert_redirected_to link.url
|
||||
assert_redirected_to link.parsed_url
|
||||
end
|
||||
|
||||
test 'Should get link counter' do
|
||||
|
||||
Vendored
+6
-1
@@ -7,5 +7,10 @@ one:
|
||||
|
||||
two:
|
||||
slug: qwu62l
|
||||
url: 'https://gapfish.com'
|
||||
url: 'https://www.google.com'
|
||||
click_counter: 1
|
||||
|
||||
three:
|
||||
slug: rwi43l
|
||||
url: 'google.com'
|
||||
click_counter: 1
|
||||
|
||||
@@ -9,21 +9,31 @@ class LinkTest < ActiveSupport::TestCase
|
||||
assert_not link.save, 'Saved the link without a url'
|
||||
end
|
||||
|
||||
test 'Should not save a link with a invalid url' do
|
||||
test 'Should not save a link with a invalid url - number' do
|
||||
link = Link.new
|
||||
link.url = 'test.com'
|
||||
link.url = 0
|
||||
assert_not link.save, 'Saved the link with invalid url format'
|
||||
end
|
||||
|
||||
test 'Should create a link with a valid url' do
|
||||
test 'Should not save a link with a invalid url - string' do
|
||||
link = Link.new
|
||||
link.url = 'https://test.com'
|
||||
assert link.save, 'Link with valid url not saved'
|
||||
link.url = 'test'
|
||||
assert_not link.save, 'Saved the link with invalid url format'
|
||||
end
|
||||
|
||||
test 'Should generate a slug on save a new link' do
|
||||
test 'Should generate a slug on save a new link - format https://google.com' do
|
||||
link = links(:one)
|
||||
assert link.slug, 'Slug not generated on save a new link'
|
||||
assert link.save, 'Slug generated on save a new link'
|
||||
end
|
||||
|
||||
test 'Should generate a slug on save a new link - format http://www.google.com' do
|
||||
link = links(:two)
|
||||
assert link.save, 'Slug generated on save a new link'
|
||||
end
|
||||
|
||||
test 'Should generate a slug on save a new link - format google.com' do
|
||||
link = links(:three)
|
||||
assert link.save, 'Slug generated on save a new link'
|
||||
end
|
||||
|
||||
test 'Should generate an unique slug' do
|
||||
|
||||
Reference in New Issue
Block a user