diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..e4b648c --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,38 @@ +AllCops: + Exclude: + - 'bin/*' + - 'config/**/*' + - 'db/**/*' + - 'vendor/bundle/**/*' + - '**/Gemfile' + - '**/Rakefile' + - 'spec/spec_helper.rb' + - 'spec/rails_helper.rb' + - 'spec/support/**/*' + - 'node_modules/**/*' + - 'app/views/**/*' + +Rails: + Enabled: true + +Naming/VariableNumber: + EnforcedStyle: 'snake_case' + +Layout/SpaceInsideHashLiteralBraces: + EnforcedStyle: no_space + +Style/Documentation: + Enabled: false + +Metrics/BlockLength: + Exclude: + - 'spec/**/*.rb' + +Metrics/LineLength: + Max: 105 + +Metrics/MethodLength: + Max: 20 + +Style/Lambda: + EnforcedStyle: literal \ No newline at end of file diff --git a/Gemfile b/Gemfile index 7c38de8..6dda514 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,7 @@ gem 'jbuilder', '~> 2.5' gem 'haml', '~> 5.0', '>= 5.0.4' gem 'bootstrap', '~> 4.1.3' gem 'jquery-rails' +gem 'rubocop', require: false group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] diff --git a/Gemfile.lock b/Gemfile.lock index 7f4c610..87c2816 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,6 +41,7 @@ GEM addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) + ast (2.4.0) autoprefixer-rails (9.1.4) execjs bindex (0.5.0) @@ -72,11 +73,6 @@ GEM diff-lcs (1.3) erubi (1.7.1) execjs (2.7.0) - factory_bot (4.11.1) - activesupport (>= 3.0.0) - factory_bot_rails (4.11.1) - factory_bot (~> 4.11.1) - railties (>= 3.0.0) ffi (1.9.25) globalid (0.4.1) activesupport (>= 4.2.0) @@ -85,6 +81,7 @@ GEM tilt i18n (1.1.0) concurrent-ruby (~> 1.0) + jaro_winkler (1.5.1) jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) @@ -109,8 +106,12 @@ GEM nio4r (2.3.1) nokogiri (1.8.5) mini_portile2 (~> 2.3.0) + parallel (1.12.1) + parser (2.5.3.0) + ast (~> 2.4.0) pg (1.1.3) popper_js (1.14.3) + powerpack (0.1.2) public_suffix (3.0.3) puma (3.12.0) rack (2.0.5) @@ -139,6 +140,7 @@ GEM method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) + rainbow (3.0.0) rake (12.3.1) rb-fsevent (0.10.3) rb-inotify (0.9.10) @@ -160,6 +162,15 @@ GEM rspec-mocks (~> 3.8.0) rspec-support (~> 3.8.0) rspec-support (3.8.0) + rubocop (0.60.0) + jaro_winkler (~> 1.5.1) + parallel (~> 1.10) + parser (>= 2.5, != 2.5.1.1) + powerpack (~> 0.1) + rainbow (>= 2.2.2, < 4.0) + ruby-progressbar (~> 1.7) + unicode-display_width (~> 1.4.0) + ruby-progressbar (1.10.0) ruby_dep (1.5.0) rubyzip (1.2.2) sass (3.6.0) @@ -201,6 +212,7 @@ GEM thread_safe (~> 0.1) uglifier (4.1.19) execjs (>= 0.3.0, < 3) + unicode-display_width (1.4.0) web-console (3.7.0) actionview (>= 5.0) activemodel (>= 5.0) @@ -221,7 +233,6 @@ DEPENDENCIES capybara (~> 2.13) coffee-rails (~> 4.2) database_cleaner - factory_bot_rails (~> 4.0) haml (~> 5.0, >= 5.0.4) jbuilder (~> 2.5) jquery-rails @@ -230,6 +241,7 @@ DEPENDENCIES puma (~> 3.7) rails (~> 5.1.6) rspec-rails (~> 3.5) + rubocop sass-rails (~> 5.0) selenium-webdriver shoulda-matchers (~> 3.1) @@ -241,4 +253,4 @@ DEPENDENCIES web-console (>= 3.3.0) BUNDLED WITH - 1.16.5 + 1.17.1 diff --git a/app/controllers/urls_controller.rb b/app/controllers/urls_controller.rb index 78e759e..f994daf 100644 --- a/app/controllers/urls_controller.rb +++ b/app/controllers/urls_controller.rb @@ -5,28 +5,20 @@ def new end def create - @url = Url.new(url_params) - @url.clean_url + @url = CreateShortUrlService.new(url_params).call - if @url.new_link? - if @url.save - render js: "document.getElementById('short_url_box').classList - .remove('d-none');document.getElementById('short_url') - .innerText='http://#{request.host}/#{@url.url_short}/';" - else - flash[:alert] = @url.errors.full_messages - redirect_to :root - end - else - @url_in_database = Url.find_by_url_clean(@url.url_clean) + if @url.save render js: "document.getElementById('short_url_box').classList - .remove('d-none');document.getElementById('short_url') - .innerText='http://#{request.host}/#{@url_in_database.url_short}/';" + .remove('d-none');document.getElementById('short_url') + .innerText='http://#{request.host}/#{@url.url_short}/';" + else + flash[:alert] = @url.errors.full_messages + redirect_to :root end end def show - @url = Url.find_by_url_short(params[:url_short]) + @url = Url.find_by(url_short: params[:url_short]) redirect_to "http://#{@url.url_clean}/" end diff --git a/app/models/concerns/links_generator.rb b/app/models/concerns/links_generator.rb new file mode 100644 index 0000000..92e8522 --- /dev/null +++ b/app/models/concerns/links_generator.rb @@ -0,0 +1,24 @@ +module LinksGenerator + extend ActiveSupport::Concern + + def new_link? + find_url_clean_duplicate.nil? + end + + def find_url_clean_duplicate + Url.find_by(url_clean: url_clean) + end + + def clean_url + clean_url = url_orginal.strip + clean_url = clean_url.downcase.gsub(%r{(https?:\/\/)|(www\.)}, '') + clean_url.slice!(-1) if clean_url[-1] == '/' + self.url_clean = clean_url + end + + def generate_short_url + short_url = generate_string_url(8) + short_url = generate_string_url(8) until Url.find_by(url_short: generate_string_url(8)).nil? + self.url_short = short_url + end +end diff --git a/app/models/url.rb b/app/models/url.rb index 683562e..18c1ecc 100644 --- a/app/models/url.rb +++ b/app/models/url.rb @@ -1,31 +1,9 @@ class Url < ApplicationRecord include CharsArray + include LinksGenerator validates :url_orginal, presence: true validates :url_orginal, url: true before_create :generate_short_url - - def new_link? - find_duplicate.nil? - end - - def find_duplicate - Url.find_by_url_clean(url_clean) - end - - def clean_url - clean_url = url_orginal.strip - clean_url = clean_url.downcase.gsub(/(https?:\/\/)|(www\.)/, '') - clean_url.slice!(-1) if clean_url[-1] == '/' - self.url_clean = clean_url - end - - def generate_short_url - short_url = generate_string_url(8) - until Url.find_by_url_short(generate_string_url(8)).nil? - short_url = generate_string_url(8) - end - self.url_short = short_url - end end diff --git a/app/services/create_short_url_service.rb b/app/services/create_short_url_service.rb new file mode 100644 index 0000000..fa0bcf2 --- /dev/null +++ b/app/services/create_short_url_service.rb @@ -0,0 +1,15 @@ +class CreateShortUrlService + def initialize(params) + @url_orginal = params[:url_orginal] + end + + def call + @url = Url.new(url_orginal: @url_orginal) + @url.clean_url + if @url.new_link? + @url + else + Url.find_by(url_clean: @url.url_clean) + end + end +end diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb index 8cadc0e..42aa74d 100644 --- a/app/validators/url_validator.rb +++ b/app/validators/url_validator.rb @@ -1,10 +1,14 @@ class UrlValidator < ActiveModel::EachValidator - def validate_each(record, attribute, value) - record.errors[attribute] << (options[:message] || "must be a valid URL") unless url_valid?(value) + def validate_each(record, attribute, value) + record.errors[attribute] << (options[:message] || 'must be a valid URL') unless url_valid?(value) + end + + def url_valid?(url) + begin + url = URI.parse(url) + rescue URI::InvalidURIError + false end - - def url_valid?(url) - url = URI.parse(url) rescue false - url.kind_of?(URI::HTTP) || url.kind_of?(URI::HTTPS) - end - end \ No newline at end of file + url.is_a?(URI::HTTP) || url.is_a?(URI::HTTPS) + end +end diff --git a/spec/controllers/url_controller_spec.rb b/spec/controllers/url_controller_spec.rb new file mode 100644 index 0000000..2d64e84 --- /dev/null +++ b/spec/controllers/url_controller_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +RSpec.describe UrlsController do + describe 'POST create' do + let(:valid_params) { {'url' => {'url_orginal' => 'http://wkostanski.pl'}} } + let(:invalid_params) { {'url' => {'url_orginal' => 'wkostanski.pl'}} } + it 'with valid params' do + expect do + post :create, params: valid_params + end.to change(Url, :count).by(1) + end + it 'with invalid params' do + expect do + post :create, params: invalid_params + end.to change(Url, :count).by(0) + end + it 'with duplicated params' do + post :create, params: valid_params + expect do + post :create, params: valid_params + end.to change(Url, :count).by(0) + end + end +end