diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..c99d2e7 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/Gemfile b/Gemfile index 8947f2e..a3cd5db 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } @@ -20,19 +22,26 @@ gem 'jbuilder', '~> 2.7' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 4.0' # Use Active Model has_secure_password -# gem 'bcrypt', '~> 3.1.7' +gem 'bcrypt', '~> 3.1.7' gem 'materialize-sass', '~> 1.0.0' +gem 'jquery-rails' + # Use Active Storage variant # gem 'image_processing', '~> 1.2' # Reduces boot times through caching; required in config/boot.rb gem 'bootsnap', '>= 1.4.4', require: false +gem 'rubocop', require: false + group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console - gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] + gem 'byebug', platforms: %i[mri mingw x64_mingw] + gem 'rails-controller-testing' + gem 'rspec' + gem 'rspec-rails', '>= 2.0.0.beta' end group :development do @@ -40,8 +49,8 @@ group :development do gem 'web-console', '>= 4.1.0' # Display performance information such as SQL time and flame graphs for each request in your browser. # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md - gem 'rack-mini-profiler', '~> 2.0' gem 'listen', '~> 3.3' + gem 'rack-mini-profiler', '~> 2.0' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' end @@ -55,4 +64,4 @@ group :test do end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] diff --git a/Gemfile.lock b/Gemfile.lock index 2ada529..a2efec0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -62,8 +62,10 @@ GEM zeitwerk (~> 2.3) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) + ast (2.4.2) autoprefixer-rails (10.2.4.0) execjs + bcrypt (3.1.16) bindex (0.8.1) bootsnap (1.7.2) msgpack (~> 1.0) @@ -80,6 +82,7 @@ GEM childprocess (3.0.0) concurrent-ruby (1.1.8) crass (1.0.6) + diff-lcs (1.4.4) erubi (1.10.0) execjs (2.7.0) ffi (1.15.0) @@ -89,6 +92,10 @@ GEM concurrent-ruby (~> 1.0) jbuilder (2.11.2) activesupport (>= 5.0.0) + jquery-rails (4.4.0) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) listen (3.4.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -111,6 +118,9 @@ GEM nokogiri (1.11.2) mini_portile2 (~> 2.5.0) racc (~> 1.4) + parallel (1.20.1) + parser (3.0.0.0) + ast (~> 2.4.1) public_suffix (4.0.6) puma (5.2.2) nio4r (~> 2.0) @@ -137,6 +147,10 @@ GEM bundler (>= 1.15.0) railties (= 6.1.3) sprockets-rails (>= 2.0.0) + rails-controller-testing (1.0.5) + actionpack (>= 5.0.1.rc1) + actionview (>= 5.0.1.rc1) + activesupport (>= 5.0.1.rc1) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) @@ -148,11 +162,46 @@ GEM method_source rake (>= 0.8.7) thor (~> 1.0) + rainbow (3.0.0) rake (13.0.3) rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) regexp_parser (2.1.1) + rexml (3.2.4) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.1) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-mocks (3.10.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-rails (5.0.1) + actionpack (>= 5.2) + activesupport (>= 5.2) + railties (>= 5.2) + rspec-core (~> 3.10) + rspec-expectations (~> 3.10) + rspec-mocks (~> 3.10) + rspec-support (~> 3.10) + rspec-support (3.10.2) + rubocop (1.11.0) + parallel (~> 1.10) + parser (>= 3.0.0.0) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml + rubocop-ast (>= 1.2.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.4.1) + parser (>= 2.7.1.5) + ruby-progressbar (1.11.0) rubyzip (2.3.0) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) @@ -184,6 +233,7 @@ GEM turbolinks-source (5.2.0) tzinfo (2.0.4) concurrent-ruby (~> 1.0) + unicode-display_width (2.0.0) web-console (4.1.0) actionview (>= 6.0.0) activemodel (>= 6.0.0) @@ -209,15 +259,21 @@ PLATFORMS ruby DEPENDENCIES + bcrypt (~> 3.1.7) bootsnap (>= 1.4.4) byebug capybara (>= 3.26) jbuilder (~> 2.7) + jquery-rails listen (~> 3.3) materialize-sass (~> 1.0.0) puma (~> 5.0) rack-mini-profiler (~> 2.0) rails (~> 6.1.3) + rails-controller-testing + rspec + rspec-rails (>= 2.0.0.beta) + rubocop sass-rails (>= 6) selenium-webdriver spring diff --git a/Rakefile b/Rakefile index 9a5ea73..488c551 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,8 @@ +# frozen_string_literal: true + # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. -require_relative "config/application" +require_relative 'config/application' Rails.application.load_tasks diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index e1a69d8..64cc60f 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -1,2 +1,6 @@ @import "materialize"; @import "https://fonts.googleapis.com/icon?family=Material+Icons"; + +body { + margin: 10px; +} \ No newline at end of file diff --git a/app/assets/stylesheets/sessions.scss b/app/assets/stylesheets/sessions.scss new file mode 100644 index 0000000..f986bd4 --- /dev/null +++ b/app/assets/stylesheets/sessions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the sessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss new file mode 100644 index 0000000..aaf17f7 --- /dev/null +++ b/app/assets/stylesheets/users.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the users controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb index d672697..9aec230 100644 --- a/app/channels/application_cable/channel.rb +++ b/app/channels/application_cable/channel.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationCable class Channel < ActionCable::Channel::Base end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb index 0ff5442..8d6c2a1 100644 --- a/app/channels/application_cable/connection.rb +++ b/app/channels/application_cable/connection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationCable class Connection < ActionCable::Connection::Base end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09705d1..97eed14 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,28 @@ +# frozen_string_literal: true + +# Base for application controllers class ApplicationController < ActionController::Base + helper_method :current_user + helper_method :logged_in? + + def current_user + User.find_by(id: session[:user_id]) + end + + def logged_in? + !current_user.nil? + end + + protected + + def notices_from_errors(record) + errors = record.errors.messages.map do |attribute, messages| + messages.map { |message| "#{attribute} #{message}".capitalize } + end + errors.flatten + end + + def ensure_admin + redirect_to '/welcome', notice: 'You are not allowed to perform this action' unless current_user&.admin? + end end diff --git a/app/controllers/authors_controller.rb b/app/controllers/authors_controller.rb new file mode 100644 index 0000000..47bfa01 --- /dev/null +++ b/app/controllers/authors_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# Authors controller +class AuthorsController < ApplicationController + before_action :ensure_admin + before_action :set_author, only: %i[edit update] + + def index + @authors = Author.all + end + + def edit + @author = Author.find(params[:id]) + end + + def update + redirect_to '/authors' if @author.update(author_params) + end + + private + + def set_author + @author = Author.find(params[:id]) + end + + def author_params + params.require(:author).permit(:first_name, :last_name) + end +end diff --git a/app/controllers/books_controller.rb b/app/controllers/books_controller.rb index bafab99..88479ed 100644 --- a/app/controllers/books_controller.rb +++ b/app/controllers/books_controller.rb @@ -1,5 +1,47 @@ +# frozen_string_literal: true + +# Books controller class BooksController < ApplicationController + before_action :set_book, only: %i[show edit update add_to_cart] + before_action :ensure_admin, only: %i[edit update] + def index - @books = Book.published.map { |book| BooksPresenter.new(book) } + books = if current_user&.admin? + Book.all + else + Book.published + end + @books = books.map { |book| BooksPresenter.new(book) } + end + + def edit; end + + def update + redirect_to '/books' if @book.update(book_params) + end + + def add_to_cart + @book = Book.find(params[:id]) + return unless @book.quantity.positive? + + current_user.books << @book + @book.decrement!(:quantity) + redirect_to '/books', notice: 'Book added to your cart' + end + + def shopping_cart + @books = current_user.books.map { |book| BooksPresenter.new(book) } + end + + private + + def set_book + @book = BooksPresenter.new(Book.find(params[:id])) + end + + def book_params + result = params.require(:book).permit(:title, :price, :published) + result['price'] = result['price'].to_d * 100 + result end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..d89d31d --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# Sessions controller +class SessionsController < ApplicationController + def new; end + + def create + @user = User.find_by(email: params[:email]) + problem = problem_with_login + if problem + redirect_to '/welcome', notice: problem + else + session[:user_id] = @user.id + redirect_to '/welcome' + end + end + + def delete + session.delete(:user_id) + redirect_to '/welcome', notice: 'Logged out properly' + end + + def welcome; end + + private + + def problem_with_login + if !@user + 'Wrong email address' + elsif !@user.authenticate(params[:password]) + 'Wrong password' + elsif @user.blocked? + 'You are blocked, please contact support' + end + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..fc77082 --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +# Users controller +class UsersController < ApplicationController + before_action :ensure_admin, only: %i[destroy block] + + def index + @users = User.all + end + + def new + @user = User.new + end + + def create + @user = User.create(params.require(:user).permit(:email, :password)) + if @user.invalid? + redirect_to '/welcome', notice: notices_from_errors(@user) + else + session[:user_id] = @user.id + redirect_to '/welcome', notice: 'Account has been created' + end + end + + def password_recovery_request + @user = User.where(email: params['email']).first + recovery_password = ('a'..'z').to_a.sample(8).join + @user.recovery_password = recovery_password + @user.save + UserMailer.with(user: @user, recovery_password: recovery_password).password_recovery.deliver_now + redirect_to '/welcome', notice: "Recovery email sent to #{params['email']}" + end + + def password_recovery_request_form; end + + def recover_password_form + @recovery_password = params[:recovery_password] + @user_id = params[:id] + end + + def recover_password + @user = User.find(params[:user_id]) + if recovery_password_proper? + set_new_password + else + redirect_to '/welcome', notice: 'Recovery link expired or invalid' + end + end + + def destroy + User.destroy(params[:id]) + redirect_to '/users' + end + + def block + User.find(params[:id]).update(status: :blocked) + redirect_to '/users' + end + + private + + def recovery_password_proper? + @user.recovery_password_digest && + @user.authenticate_recovery_password(params[:recovery_password]) + end + + def set_new_password + @user.password = params[:password] + @user.password_confirmation = params[:password_confirmation] + if @user.save + @user.update(recovery_password: nil) + redirect_to '/welcome', notice: 'Password changed' + else + redirect_to '/welcome', notice: 'Passwords don\'t match' + end + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index de6be79..292788a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,2 +1,5 @@ +# frozen_string_literal: true + +# Application helper module ApplicationHelper end diff --git a/app/helpers/books_helper.rb b/app/helpers/books_helper.rb index 4b9311e..84f1d25 100644 --- a/app/helpers/books_helper.rb +++ b/app/helpers/books_helper.rb @@ -1,2 +1,12 @@ +# frozen_string_literal: true + +# Books helper module BooksHelper + def cart_summary + number_to_currency(@books.map(&:price).reduce(:+)) + end + + def can_book_be_added?(book) + logged_in? && book.quantity.positive? && !current_user.books.exists?(book.id) + end end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb new file mode 100644 index 0000000..dc9b8a1 --- /dev/null +++ b/app/helpers/sessions_helper.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +# Sessions helper +module SessionsHelper +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..f9820e4 --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +# Users helper +module UsersHelper +end diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index a5843e7..2d92af0 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -4,6 +4,8 @@ // that code so it'll be compiled. //= require materialize +//= require jquery +//= require jquery_ujs import Rails from "@rails/ujs" import Turbolinks from "turbolinks" diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index d394c3d..bef3959 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationJob < ActiveJob::Base # Automatically retry jobs that encountered a deadlock # retry_on ActiveRecord::Deadlocked diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 286b223..d84cb6e 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationMailer < ActionMailer::Base default from: 'from@example.com' layout 'mailer' diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 0000000..169ef04 --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# User mailer +class UserMailer < ApplicationMailer + def password_recovery + @user = params[:user] + @recovery_password = params[:recovery_password] + @url = "http://localhost:18210/recover_password/#{@user.id}/#{@recovery_password}" + mail(to: @user.email, subject: 'Password recovery') + end +end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 10a4cba..536baf0 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,3 +1,29 @@ +# frozen_string_literal: true + +# Application record class ApplicationRecord < ActiveRecord::Base self.abstract_class = true + def update(*args) + result = super(*args) + AuditRecord.create(model: self.class, action: 'update', params: to_json) + result + end + + def save(*args) + result = super(*args) + AuditRecord.create(model: self.class, action: 'save', params: to_json) + result + end + + def self.create(*args) + result = super(*args) + AuditRecord.create(model: self.class, action: 'create', params: result.to_json) + result + end + + def decrement!(*args) + result = super(*args) + AuditRecord.create(model: self.class, action: 'decrement!', params: to_json) + result + end end diff --git a/app/models/audit_record.rb b/app/models/audit_record.rb new file mode 100644 index 0000000..48a1aab --- /dev/null +++ b/app/models/audit_record.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class AuditRecord < ActiveRecord::Base +end diff --git a/app/models/author.rb b/app/models/author.rb index e399202..e7a3204 100644 --- a/app/models/author.rb +++ b/app/models/author.rb @@ -1,2 +1,6 @@ +# frozen_string_literal: true + class Author < ApplicationRecord + validates :first_name, presence: true + validates :last_name, presence: true end diff --git a/app/models/book.rb b/app/models/book.rb index 1b16bc8..67cd8ec 100644 --- a/app/models/book.rb +++ b/app/models/book.rb @@ -1,5 +1,9 @@ +# frozen_string_literal: true + class Book < ApplicationRecord has_and_belongs_to_many :authors scope :published, -> { where(published: true) } + validates :title, presence: true + validates :price, presence: true end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..9da6781 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class User < ApplicationRecord + has_and_belongs_to_many :books + + has_secure_password + has_secure_password :recovery_password, validations: false + enum role: %i[customer admin], _default: :customer + enum status: %i[ready blocked], _default: :ready + + validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP } + validates :role, presence: true + validates :status, presence: true + validates :password, { + presence: true, + length: { minimum: 8 }, + if: -> { new_record? || !password.nil? } + } +end diff --git a/app/presenters/books_presenter.rb b/app/presenters/books_presenter.rb index 49888bb..b5762ba 100644 --- a/app/presenters/books_presenter.rb +++ b/app/presenters/books_presenter.rb @@ -1,8 +1,15 @@ +# frozen_string_literal: true + +# Books presenter class BooksPresenter < SimpleDelegator include ActiveSupport::NumberHelper def price - number_to_currency(super / 100) + super / 100 + end + + def price_with_currency + number_to_currency(price) end def authors diff --git a/app/views/authors/edit.html.erb b/app/views/authors/edit.html.erb new file mode 100644 index 0000000..57de572 --- /dev/null +++ b/app/views/authors/edit.html.erb @@ -0,0 +1,10 @@ +
+

Edit author

+ <%= form_for @author do |f|%> + <%= f.label :first_name %> + <%= f.text_field :first_name %> + <%= f.label :last_name %> + <%= f.text_field :last_name %> + <%= f.submit 'Save changes', class: 'btn' %> + <%end%> +
\ No newline at end of file diff --git a/app/views/authors/index.html.erb b/app/views/authors/index.html.erb new file mode 100644 index 0000000..f0509a1 --- /dev/null +++ b/app/views/authors/index.html.erb @@ -0,0 +1,13 @@ +
+ <% @authors.each do |author| %> +
+
+ <%= author.first_name %> <%= author.last_name %> +
+ +
+ <%= link_to 'Edit', edit_author_path(author), class: "btn" %> +
+
+ <% end %> +
diff --git a/app/views/books/edit.html.erb b/app/views/books/edit.html.erb new file mode 100644 index 0000000..78e654f --- /dev/null +++ b/app/views/books/edit.html.erb @@ -0,0 +1,16 @@ +
+

Edit book

+ <%= form_for @book do |f|%> + <%= f.label :title %> + <%= f.text_field :title %> + <%= f.label :price %> + <%= f.number_field :price, step: 0.01 %> + + <%= f.submit 'Save changes', class: 'btn' %> + <%end%> +
\ No newline at end of file diff --git a/app/views/books/index.html.erb b/app/views/books/index.html.erb index 904091b..8dd3a2b 100644 --- a/app/views/books/index.html.erb +++ b/app/views/books/index.html.erb @@ -1,17 +1,42 @@
+
+
Title
+
Authors
+
Price
+
Quantity
+ +
<% @books.each do |book| %>
-
- Title: <%= book.title %> +
+ <%= book.title %> +
+ +
+ <%= book.authors %>
-
- Price: <%= book.price %> +
+ <%= book.price_with_currency %>
-
- Authors: <%= book.authors %> +
+ <%= book.quantity %>
+
+ <% if can_book_be_added?(book) %> + <%= link_to 'Add', "book/#{book.id}/add_to_cart", method: :post, class: "btn" %> + <% end %> +
+ + <% if current_user&.admin? %> +
+ <%= book.published ? 'published' : 'unpublished' %> +
+
+ <%= link_to 'Edit', edit_book_path(book), class: "btn" %> +
+ <% end %>
<% end %>
diff --git a/app/views/books/shopping_cart.erb b/app/views/books/shopping_cart.erb new file mode 100644 index 0000000..dc6121d --- /dev/null +++ b/app/views/books/shopping_cart.erb @@ -0,0 +1,26 @@ +
+
+
Title
+
Authors
+
Price
+
+ <% @books.each do |book| %> +
+
+ <%= book.title %> +
+ +
+ <%= book.authors %> +
+ +
+ <%= book.price_with_currency %> +
+
+ <% end %> +
+
Summary
+
<%= cart_summary %>
+
+
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 7ca2457..904da48 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -2,15 +2,53 @@ Book store - + <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> + <%= javascript_include_tag 'rails-ujs' %> +
+
+
+
+ <%= link_to 'Home', '/welcome', method: :get%> +
+ +
+ <%= link_to 'Books', '/books', method: :get%> +
+ <% if current_user&.admin? %> +
+ <%= link_to 'Authors', '/authors', method: :get%> +
+
+ <%= link_to 'Users', '/users', method: :get%> +
+ <% end %> + <% if logged_in? %> +
+ <%= link_to 'Shopping cart', '/shopping_cart', method: :get%> +
+ <% end %> +
+
+ <% flash.each do |type, notice| %> +
+ <% if notice.is_a? String %> + <%= notice %> + <% else %> + <% notice.each do |msg| %> +
<%= msg %>
+ <% end %> + <% end %> +
+ <% end %> +
<%= yield %> diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb new file mode 100644 index 0000000..9d3fca7 --- /dev/null +++ b/app/views/sessions/new.html.erb @@ -0,0 +1,11 @@ +
+

Login

+ <%= form_tag '/login' do %> + <%= label_tag :email%> + <%= text_field_tag :email %> + <%= label_tag :password%> + <%= password_field_tag :password%> + <%= submit_tag "Login", class: 'btn' %> + <%end%> + <%= link_to "Password recovery", '/password_recovery_request', method: :get %> +
diff --git a/app/views/sessions/welcome.html.erb b/app/views/sessions/welcome.html.erb new file mode 100644 index 0000000..f2a1bcd --- /dev/null +++ b/app/views/sessions/welcome.html.erb @@ -0,0 +1,13 @@ +
+

Welcome

+
+ <% if logged_in? %> +

You are logged in, <%= current_user.email %>

+ <%= link_to 'Logout', '/logout', method: :get, class: 'btn' %> + <% else %> +

You are not logged in

+ <%= link_to 'Login', '/login', method: :get, class: 'btn' %> + <%= link_to 'Sign Up', '/users/new', method: :get, class: 'btn' %> + <% end %> +
+
diff --git a/app/views/user_mailer/password_recovery.html.erb b/app/views/user_mailer/password_recovery.html.erb new file mode 100644 index 0000000..b89c984 --- /dev/null +++ b/app/views/user_mailer/password_recovery.html.erb @@ -0,0 +1 @@ +Please click the link to recover your password: <%= @url %> \ No newline at end of file diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb new file mode 100644 index 0000000..bc5ecb1 --- /dev/null +++ b/app/views/users/index.html.erb @@ -0,0 +1,21 @@ +
+ <% @users.each do |user| %> +
+
+ <%= user.email %> +
+
+ <%= user.role %> +
+
+ <%= user.status %> +
+
+ <%= link_to 'Delete', user, method: :delete, class: "btn" %> +
+
+ <%= link_to 'Block', "/user/#{user.id}/block", method: :post, class: "btn" %> +
+
+ <% end %> +
diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb new file mode 100644 index 0000000..a4ce410 --- /dev/null +++ b/app/views/users/new.html.erb @@ -0,0 +1,10 @@ +
+

Sign Up

+ <%= form_for @user do |f|%> + <%= f.label :email%> + <%= f.text_field :email%> + <%= f.label :password%> + <%= f.password_field :password%> + <%= f.submit 'Sign up', class: 'btn' %> + <%end%> +
diff --git a/app/views/users/password_recovery_request_form.erb b/app/views/users/password_recovery_request_form.erb new file mode 100644 index 0000000..b267001 --- /dev/null +++ b/app/views/users/password_recovery_request_form.erb @@ -0,0 +1,9 @@ +
+

Password recovery

+ Provide an email to password recovery + <%= form_with url: "/password_recovery_request", method: :post do |f| %> + <%= f.label :email%>
+ <%= f.text_field :email %> + <%= f.submit 'Send email', class: 'btn' %> + <% end %> +
diff --git a/app/views/users/recover_password_form.html.erb b/app/views/users/recover_password_form.html.erb new file mode 100644 index 0000000..29e7b58 --- /dev/null +++ b/app/views/users/recover_password_form.html.erb @@ -0,0 +1,13 @@ +
+

Provide new password

+ + <%= form_with url: '/recover_password', method: :post do |f| %> + <%= f.label :password%> + <%= f.password_field :password %> + <%= f.label :password_confirmation%> + <%= f.password_field :password_confirmation %> + <%= f.hidden_field :recovery_password, :value => @recovery_password %> + <%= f.hidden_field :user_id, :value => @user_id %> + <%= f.submit 'Change password', class: 'btn' %> + <% end %> +
diff --git a/bin/bundle b/bin/bundle index a71368e..73ec947 100755 --- a/bin/bundle +++ b/bin/bundle @@ -8,46 +8,46 @@ # this file is here to facilitate running it. # -require "rubygems" +require 'rubygems' m = Module.new do module_function def invoked_as_script? - File.expand_path($0) == File.expand_path(__FILE__) + File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__) end def env_var_version - ENV["BUNDLER_VERSION"] + ENV['BUNDLER_VERSION'] end def cli_arg_version return unless invoked_as_script? # don't want to hijack other binstubs - return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + return unless 'update'.start_with?(ARGV.first || ' ') # must be running `bundle update` + bundler_version = nil update_index = nil ARGV.each_with_index do |a, i| - if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN - bundler_version = a - end + bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ - bundler_version = $1 + + bundler_version = Regexp.last_match(1) update_index = i end bundler_version end def gemfile - gemfile = ENV["BUNDLE_GEMFILE"] + gemfile = ENV['BUNDLE_GEMFILE'] return gemfile if gemfile && !gemfile.empty? - File.expand_path("../../Gemfile", __FILE__) + File.expand_path('../Gemfile', __dir__) end def lockfile lockfile = case File.basename(gemfile) - when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) + when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile) else "#{gemfile}.lock" end File.expand_path(lockfile) @@ -55,15 +55,17 @@ m = Module.new do def lockfile_version return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) end def bundler_version @bundler_version ||= env_var_version || cli_arg_version || - lockfile_version + lockfile_version end def bundler_requirement @@ -73,28 +75,32 @@ m = Module.new do requirement = bundler_gem_version.approximate_recommendation - return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0") + return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new('2.7.0') - requirement += ".a" if bundler_gem_version.prerelease? + requirement += '.a' if bundler_gem_version.prerelease? requirement end def load_bundler! - ENV["BUNDLE_GEMFILE"] ||= gemfile + ENV['BUNDLE_GEMFILE'] ||= gemfile activate_bundler end def activate_bundler gem_error = activation_error_handling do - gem "bundler", bundler_requirement + gem 'bundler', bundler_requirement end return if gem_error.nil? + require_error = activation_error_handling do - require "bundler/version" + require 'bundler/version' end - return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + return + end + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" exit 42 end @@ -109,6 +115,4 @@ end m.load_bundler! -if m.invoked_as_script? - load Gem.bin_path("bundler", "bundle") -end +load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script? diff --git a/bin/rails b/bin/rails index 21d3e02..33ffd90 100755 --- a/bin/rails +++ b/bin/rails @@ -1,5 +1,7 @@ #!/usr/bin/env ruby -load File.expand_path("spring", __dir__) +# frozen_string_literal: true + +load File.expand_path('spring', __dir__) APP_PATH = File.expand_path('../config/application', __dir__) -require_relative "../config/boot" -require "rails/commands" +require_relative '../config/boot' +require 'rails/commands' diff --git a/bin/rake b/bin/rake index 7327f47..05ee931 100755 --- a/bin/rake +++ b/bin/rake @@ -1,5 +1,7 @@ #!/usr/bin/env ruby -load File.expand_path("spring", __dir__) -require_relative "../config/boot" -require "rake" +# frozen_string_literal: true + +load File.expand_path('spring', __dir__) +require_relative '../config/boot' +require 'rake' Rake.application.run diff --git a/bin/setup b/bin/setup index 90700ac..0e38d30 100755 --- a/bin/setup +++ b/bin/setup @@ -1,5 +1,7 @@ #!/usr/bin/env ruby -require "fileutils" +# frozen_string_literal: true + +require 'fileutils' # path to your application root. APP_ROOT = File.expand_path('..', __dir__) diff --git a/bin/spring b/bin/spring index b4147e8..e52a4f8 100755 --- a/bin/spring +++ b/bin/spring @@ -1,13 +1,15 @@ #!/usr/bin/env ruby -if !defined?(Spring) && [nil, "development", "test"].include?(ENV["RAILS_ENV"]) - gem "bundler" - require "bundler" +# frozen_string_literal: true + +if !defined?(Spring) && [nil, 'development', 'test'].include?(ENV['RAILS_ENV']) + gem 'bundler' + require 'bundler' # Load Spring without loading other gems in the Gemfile, for speed. - Bundler.locked_gems&.specs&.find { |spec| spec.name == "spring" }&.tap do |spring| + Bundler.locked_gems&.specs&.find { |spec| spec.name == 'spring' }&.tap do |spring| Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path - gem "spring", spring.version - require "spring/binstub" + gem 'spring', spring.version + require 'spring/binstub' rescue Gem::LoadError # Ignore when Spring is not installed. end diff --git a/bin/webpack b/bin/webpack index 1031168..62114ba 100755 --- a/bin/webpack +++ b/bin/webpack @@ -1,18 +1,19 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" -ENV["NODE_ENV"] ||= "development" +ENV['RAILS_ENV'] ||= ENV['RACK_ENV'] || 'development' +ENV['NODE_ENV'] ||= 'development' -require "pathname" -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", - Pathname.new(__FILE__).realpath) +require 'pathname' +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', + Pathname.new(__FILE__).realpath) -require "bundler/setup" +require 'bundler/setup' -require "webpacker" -require "webpacker/webpack_runner" +require 'webpacker' +require 'webpacker/webpack_runner' -APP_ROOT = File.expand_path("..", __dir__) +APP_ROOT = File.expand_path('..', __dir__) Dir.chdir(APP_ROOT) do Webpacker::WebpackRunner.run(ARGV) end diff --git a/bin/webpack-dev-server b/bin/webpack-dev-server index dd96627..e4f41cc 100755 --- a/bin/webpack-dev-server +++ b/bin/webpack-dev-server @@ -1,18 +1,19 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" -ENV["NODE_ENV"] ||= "development" +ENV['RAILS_ENV'] ||= ENV['RACK_ENV'] || 'development' +ENV['NODE_ENV'] ||= 'development' -require "pathname" -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", - Pathname.new(__FILE__).realpath) +require 'pathname' +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', + Pathname.new(__FILE__).realpath) -require "bundler/setup" +require 'bundler/setup' -require "webpacker" -require "webpacker/dev_server_runner" +require 'webpacker' +require 'webpacker/dev_server_runner' -APP_ROOT = File.expand_path("..", __dir__) +APP_ROOT = File.expand_path('..', __dir__) Dir.chdir(APP_ROOT) do Webpacker::DevServerRunner.run(ARGV) end diff --git a/bin/yarn b/bin/yarn index 9fab2c3..9da8e5c 100755 --- a/bin/yarn +++ b/bin/yarn @@ -1,17 +1,19 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + APP_ROOT = File.expand_path('..', __dir__) Dir.chdir(APP_ROOT) do - yarn = ENV["PATH"].split(File::PATH_SEPARATOR). - select { |dir| File.expand_path(dir) != __dir__ }. - product(["yarn", "yarn.cmd", "yarn.ps1"]). - map { |dir, file| File.expand_path(file, dir) }. - find { |file| File.executable?(file) } + yarn = ENV['PATH'].split(File::PATH_SEPARATOR) + .reject { |dir| File.expand_path(dir) == __dir__ } + .product(['yarn', 'yarn.cmd', 'yarn.ps1']) + .map { |dir, file| File.expand_path(file, dir) } + .find { |file| File.executable?(file) } if yarn exec yarn, *ARGV else - $stderr.puts "Yarn executable was not detected in the system." - $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + warn 'Yarn executable was not detected in the system.' + warn 'Download Yarn at https://yarnpkg.com/en/docs/install' exit 1 end end diff --git a/config.ru b/config.ru index 4a3c09a..6dc8321 100644 --- a/config.ru +++ b/config.ru @@ -1,6 +1,8 @@ +# frozen_string_literal: true + # This file is used by Rack-based servers to start the application. -require_relative "config/environment" +require_relative 'config/environment' run Rails.application Rails.application.load_server diff --git a/config/application.rb b/config/application.rb index 1dba805..a55fbe9 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,11 +1,14 @@ -require_relative "boot" +# frozen_string_literal: true -require "rails/all" +require_relative 'boot' + +require 'rails/all' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) +# App module App class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. diff --git a/config/boot.rb b/config/boot.rb index 3cda23b..c04863f 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,4 +1,6 @@ +# frozen_string_literal: true + ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -require "bundler/setup" # Set up gems listed in the Gemfile. -require "bootsnap/setup" # Speed up boot time by caching expensive operations. +require 'bundler/setup' # Set up gems listed in the Gemfile. +require 'bootsnap/setup' # Speed up boot time by caching expensive operations. diff --git a/config/environment.rb b/config/environment.rb index cac5315..d5abe55 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Load the Rails application. -require_relative "application" +require_relative 'application' # Initialize the Rails application. Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index 7a9f6c3..7f56706 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,4 +1,6 @@ -require "active_support/core_ext/integer/time" +# frozen_string_literal: true + +require 'active_support/core_ext/integer/time' Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -56,7 +58,8 @@ Rails.application.configure do # Debug mode disables concatenation and preprocessing of assets. # This option may cause significant delays in view rendering with a large # number of complex assets. - config.assets.debug = true + config.assets.debug = false + config.assets.check_precompiled_asset = false # Suppress logger output for asset requests. config.assets.quiet = true diff --git a/config/environments/production.rb b/config/environments/production.rb index 36dd980..bceeda0 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,4 +1,6 @@ -require "active_support/core_ext/integer/time" +# frozen_string_literal: true + +require 'active_support/core_ext/integer/time' Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -53,7 +55,7 @@ Rails.application.configure do config.log_level = :info # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] + config.log_tags = [:request_id] # Use a different cache store in production. # config.cache_store = :mem_cache_store @@ -88,8 +90,8 @@ Rails.application.configure do # require "syslog/logger" # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') - if ENV["RAILS_LOG_TO_STDOUT"].present? - logger = ActiveSupport::Logger.new(STDOUT) + if ENV['RAILS_LOG_TO_STDOUT'].present? + logger = ActiveSupport::Logger.new($stdout) logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) end diff --git a/config/environments/test.rb b/config/environments/test.rb index 93ed4f1..74b1be2 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,4 +1,6 @@ -require "active_support/core_ext/integer/time" +# frozen_string_literal: true + +require 'active_support/core_ext/integer/time' # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb index 89d2efa..f4556db 100644 --- a/config/initializers/application_controller_renderer.rb +++ b/config/initializers/application_controller_renderer.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # ActiveSupport::Reloader.to_prepare do diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 4b828e8..a9b0d0f 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Version of your assets, change this if you want to expire all your assets. diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb index 33699c3..d43cc1d 100644 --- a/config/initializers/backtrace_silencers.rb +++ b/config/initializers/backtrace_silencers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. @@ -5,4 +7,4 @@ # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'". -Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"] +Rails.backtrace_cleaner.remove_silencers! if ENV['BACKTRACE'] diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 35d0f26..f3bcce5 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # Define an application-wide content security policy diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb index 5a6a32d..ee8dff9 100644 --- a/config/initializers/cookies_serializer.rb +++ b/config/initializers/cookies_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Specify a serializer for the signed and encrypted cookie jars. diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 4b34a03..3babc73 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += [ - :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn +Rails.application.config.filter_parameters += %i[ + passw secret token _key crypt salt certificate otp ssn ] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index ac033bf..aa7435f 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format. Inflections diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index dc18996..6e1d16f 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # Add new mime types for use in respond_to blocks: diff --git a/config/initializers/permissions_policy.rb b/config/initializers/permissions_policy.rb index 00f64d7..50bcf4e 100644 --- a/config/initializers/permissions_policy.rb +++ b/config/initializers/permissions_policy.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Define an application-wide HTTP permissions policy. For further # information see https://developers.google.com/web/updates/2018/06/feature-policy # diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index bbfc396..2f3c0db 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # This file contains settings for ActionController::ParamsWrapper which diff --git a/config/puma.rb b/config/puma.rb index d9b3e83..8ae6a78 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,28 +1,30 @@ +# frozen_string_literal: true + # Puma can serve each request in a thread from an internal thread pool. # The `threads` method setting takes two numbers: a minimum and maximum. # Any libraries that use thread pools should be configured to match # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } -min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } +max_threads_count = ENV.fetch('RAILS_MAX_THREADS', 5) +min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } threads min_threads_count, max_threads_count # Specifies the `worker_timeout` threshold that Puma will use to wait before # terminating a worker in development environments. # -worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" +worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development' # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # -port ENV.fetch("PORT") { 3000 } +port ENV.fetch('PORT', 3000) # Specifies the `environment` that Puma will run in. # -environment ENV.fetch("RAILS_ENV") { "development" } +environment ENV.fetch('RAILS_ENV', 'development') # Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } +pidfile ENV.fetch('PIDFILE', 'tmp/pids/server.pid') # Specifies the number of `workers` to boot in clustered mode. # Workers are forked web server processes. If using threads and workers together diff --git a/config/routes.rb b/config/routes.rb index af3f220..dfe1a61 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,19 @@ +# frozen_string_literal: true + Rails.application.routes.draw do + resources :users + get 'login', to: 'sessions#new' + get 'logout', to: 'sessions#delete' + post 'login', to: 'sessions#create' + get 'welcome', to: 'sessions#welcome' + get 'password_recovery_request', to: 'users#password_recovery_request_form' + post 'password_recovery_request', to: 'users#password_recovery_request' + get 'recover_password/:id/:recovery_password', to: 'users#recover_password_form' + post 'recover_password', to: 'users#recover_password' + post 'user/:id/block', to: 'users#block' + post 'book/:id/add_to_cart', to: 'books#add_to_cart' + get 'shopping_cart', to: 'books#shopping_cart' + resources :books + resources :authors end diff --git a/config/spring.rb b/config/spring.rb index db5bf13..93cd0ff 100644 --- a/config/spring.rb +++ b/config/spring.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + Spring.watch( - ".ruby-version", - ".rbenv-vars", - "tmp/restart.txt", - "tmp/caching-dev.txt" + '.ruby-version', + '.rbenv-vars', + 'tmp/restart.txt', + 'tmp/caching-dev.txt' ) diff --git a/db/migrate/20210319142051_create_authors.rb b/db/migrate/20210319142051_create_authors.rb index 1265628..100b87c 100644 --- a/db/migrate/20210319142051_create_authors.rb +++ b/db/migrate/20210319142051_create_authors.rb @@ -1,3 +1,6 @@ +# frozen_string_literal: true + +# Create authors class CreateAuthors < ActiveRecord::Migration[6.1] def change create_table :authors do |t| @@ -7,6 +10,6 @@ class CreateAuthors < ActiveRecord::Migration[6.1] t.timestamps end - add_index :authors, [ :first_name, :last_name ] + add_index :authors, %i[first_name last_name] end end diff --git a/db/migrate/20210319142054_create_books.rb b/db/migrate/20210319142054_create_books.rb index 868e375..f87d7cc 100644 --- a/db/migrate/20210319142054_create_books.rb +++ b/db/migrate/20210319142054_create_books.rb @@ -1,3 +1,6 @@ +# frozen_string_literal: true + +# Create books class CreateBooks < ActiveRecord::Migration[6.1] def change create_table :books do |t| diff --git a/db/migrate/20210319142059_create_authors_books.rb b/db/migrate/20210319142059_create_authors_books.rb index f26bea6..d0fe027 100644 --- a/db/migrate/20210319142059_create_authors_books.rb +++ b/db/migrate/20210319142059_create_authors_books.rb @@ -1,3 +1,6 @@ +# frozen_string_literal: true + +# Create association table between authors and books class CreateAuthorsBooks < ActiveRecord::Migration[6.1] def change create_table :authors_books do |t| diff --git a/db/migrate/20210320130542_create_users.rb b/db/migrate/20210320130542_create_users.rb new file mode 100644 index 0000000..d264856 --- /dev/null +++ b/db/migrate/20210320130542_create_users.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Create users +class CreateUsers < ActiveRecord::Migration[6.1] + def change + create_table :users do |t| + t.string :username + t.string :password_digest + + t.timestamps + end + end +end diff --git a/db/migrate/20210320212922_change_username_to_email.rb b/db/migrate/20210320212922_change_username_to_email.rb new file mode 100644 index 0000000..3f6cd5e --- /dev/null +++ b/db/migrate/20210320212922_change_username_to_email.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ChangeUsernameToEmail < ActiveRecord::Migration[6.1] + def change + rename_column :users, :username, :email + end +end diff --git a/db/migrate/20210320233401_add_password_recovery_code_to_users.rb b/db/migrate/20210320233401_add_password_recovery_code_to_users.rb new file mode 100644 index 0000000..a0afccc --- /dev/null +++ b/db/migrate/20210320233401_add_password_recovery_code_to_users.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddPasswordRecoveryCodeToUsers < ActiveRecord::Migration[6.1] + def change + add_column :users, :password_recovery_code, :string + end +end diff --git a/db/migrate/20210321093857_change_password_recovery_code_to_recovery_password_digest.rb b/db/migrate/20210321093857_change_password_recovery_code_to_recovery_password_digest.rb new file mode 100644 index 0000000..a88ab42 --- /dev/null +++ b/db/migrate/20210321093857_change_password_recovery_code_to_recovery_password_digest.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ChangePasswordRecoveryCodeToRecoveryPasswordDigest < ActiveRecord::Migration[6.1] + def change + rename_column :users, :password_recovery_code, :recovery_password_digest + end +end diff --git a/db/migrate/20210321135711_add_role_to_users.rb b/db/migrate/20210321135711_add_role_to_users.rb new file mode 100644 index 0000000..c6d293e --- /dev/null +++ b/db/migrate/20210321135711_add_role_to_users.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddRoleToUsers < ActiveRecord::Migration[6.1] + def change + add_column :users, :role, :integer + end +end diff --git a/db/migrate/20210321213901_add_status_to_users.rb b/db/migrate/20210321213901_add_status_to_users.rb new file mode 100644 index 0000000..09e39e2 --- /dev/null +++ b/db/migrate/20210321213901_add_status_to_users.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddStatusToUsers < ActiveRecord::Migration[6.1] + def change + add_column :users, :status, :integer + end +end diff --git a/db/migrate/20210321225317_create_audit_records.rb b/db/migrate/20210321225317_create_audit_records.rb new file mode 100644 index 0000000..3efe7bd --- /dev/null +++ b/db/migrate/20210321225317_create_audit_records.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class CreateAuditRecords < ActiveRecord::Migration[6.1] + def change + create_table :audit_records do |t| + t.string :model + t.string :action + t.string :params + + t.timestamps + end + end +end diff --git a/db/migrate/20210321235625_add_quantity_to_books.rb b/db/migrate/20210321235625_add_quantity_to_books.rb new file mode 100644 index 0000000..2cd1cb3 --- /dev/null +++ b/db/migrate/20210321235625_add_quantity_to_books.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddQuantityToBooks < ActiveRecord::Migration[6.1] + def change + add_column :books, :quantity, :integer + end +end diff --git a/db/migrate/20210322002803_create_books_users.rb b/db/migrate/20210322002803_create_books_users.rb new file mode 100644 index 0000000..b870a0a --- /dev/null +++ b/db/migrate/20210322002803_create_books_users.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateBooksUsers < ActiveRecord::Migration[6.1] + def change + create_table :books_users do |t| + t.belongs_to :book + t.belongs_to :user + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f539461..ed53db2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # 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. @@ -10,32 +12,58 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_03_19_142059) do - - create_table "authors", force: :cascade do |t| - t.string "first_name" - t.string "last_name" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false - t.index ["first_name", "last_name"], name: "index_authors_on_first_name_and_last_name" +ActiveRecord::Schema.define(version: 20_210_322_002_803) do + create_table 'audit_records', force: :cascade do |t| + t.string 'model' + t.string 'action' + t.string 'params' + t.datetime 'created_at', precision: 6, null: false + t.datetime 'updated_at', precision: 6, null: false end - create_table "authors_books", force: :cascade do |t| - t.integer "book_id" - t.integer "author_id" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false - t.index ["author_id"], name: "index_authors_books_on_author_id" - t.index ["book_id"], name: "index_authors_books_on_book_id" + create_table 'authors', force: :cascade do |t| + t.string 'first_name' + t.string 'last_name' + t.datetime 'created_at', precision: 6, null: false + t.datetime 'updated_at', precision: 6, null: false + t.index %w[first_name last_name], name: 'index_authors_on_first_name_and_last_name' end - create_table "books", force: :cascade do |t| - t.string "title" - t.decimal "price", precision: 10, scale: 2 - t.boolean "published" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false - t.index ["published"], name: "index_books_on_published" + create_table 'authors_books', force: :cascade do |t| + t.integer 'book_id' + t.integer 'author_id' + t.datetime 'created_at', precision: 6, null: false + t.datetime 'updated_at', precision: 6, null: false + t.index ['author_id'], name: 'index_authors_books_on_author_id' + t.index ['book_id'], name: 'index_authors_books_on_book_id' end + create_table 'books', force: :cascade do |t| + t.string 'title' + t.decimal 'price', precision: 10, scale: 2 + t.boolean 'published' + t.datetime 'created_at', precision: 6, null: false + t.datetime 'updated_at', precision: 6, null: false + t.integer 'quantity' + t.index ['published'], name: 'index_books_on_published' + end + + create_table 'books_users', force: :cascade do |t| + t.integer 'book_id' + t.integer 'user_id' + t.datetime 'created_at', precision: 6, null: false + t.datetime 'updated_at', precision: 6, null: false + t.index ['book_id'], name: 'index_books_users_on_book_id' + t.index ['user_id'], name: 'index_books_users_on_user_id' + end + + create_table 'users', force: :cascade do |t| + t.string 'email' + t.string 'password_digest' + t.datetime 'created_at', precision: 6, null: false + t.datetime 'updated_at', precision: 6, null: false + t.string 'recovery_password_digest' + t.integer 'role' + t.integer 'status' + end end diff --git a/db/seeds.rb b/db/seeds.rb index 1f3af18..92b4b80 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,51 +1,74 @@ +# frozen_string_literal: true + books = Book.create([ - { - title: 'Journey to the Center of the Earth', - price: 10900, - published: true - }, - { - title: 'From the Earth to the Moon', - price: 6300, - published: false - }, - { - title: 'Imaginary trip', - price: 3600, - published: true - }, -]) + { + title: 'Journey to the Center of the Earth', + price: 10_900, + published: true, + quantity: 100 + }, + { + title: 'From the Earth to the Moon', + price: 6300, + published: false, + quantity: 0 + }, + { + title: 'Imaginary trip', + price: 3600, + published: true, + quantity: 1 + }, + { + title: 'Winnie the Pooh', + price: 3700, + published: true, + quantity: 5 + } + ]) authors = Author.create([ - { - first_name: 'Jules', - last_name: 'Verne' - }, - { - first_name: 'Dick', - last_name: 'Pick', - }, - { - first_name: 'Rick', - last_name: 'Pickle' - }, -]) + { + first_name: 'Jules', + last_name: 'Verne' + }, + { + first_name: 'Dick', + last_name: 'Pick' + }, + { + first_name: 'Rick', + last_name: 'Pickle' + }, + { + first_name: 'Alan', + last_name: 'Milne' + } + ]) -BookAuthor.create([ - { - book: books.first, - author: authors.first - }, - { - book: books[1], - author: authors.first - }, - { - book: books[2], - author: authors[1] - }, - { - book: books[2], - author: authors[2] - }, -]) \ No newline at end of file +books.first.authors << authors.first +books.second.authors << authors.first +books.third.authors << authors.second +books.third.authors << authors.third +books.fourth.authors << authors.fourth + +User.create([ + { + email: 'abc@o2.pl', + password: 'aaaaaaaa', + role: :admin, + status: :ready + }, + { + email: 'abcd@o2.pl', + password: 'aaaaaaaa', + role: :customer, + status: :ready + }, + { + email: 'abcde@o2.pl', + password: 'aaaaaaaa', + role: :customer, + status: :ready + } + ]) diff --git a/public/404.html b/public/404.html index 2be3af2..3e2d224 100644 --- a/public/404.html +++ b/public/404.html @@ -58,7 +58,7 @@
-

The page you were looking for doesn't exist.

+

The page you were looking for doesn't exist.

You may have mistyped the address or the page may have moved.

If you are the application owner check the logs for more information.

diff --git a/public/422.html b/public/422.html index c08eac0..badeeb8 100644 --- a/public/422.html +++ b/public/422.html @@ -58,7 +58,7 @@
-

The change you wanted was rejected.

+

The change you wanted was rejected.

Maybe you tried to change something you didn't have access to.

If you are the application owner check the logs for more information.

diff --git a/public/500.html b/public/500.html index 78a030a..1fd70e3 100644 --- a/public/500.html +++ b/public/500.html @@ -58,7 +58,7 @@
-

We're sorry, but something went wrong.

+

We're sorry, but something went wrong.

If you are the application owner check the logs for more information.

diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb new file mode 100644 index 0000000..7b028e7 --- /dev/null +++ b/spec/controllers/application_controller_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ApplicationController do + describe 'current_user' do + before(:all) do + User.create(email: 'test2@example.com', password: 'abcde') + end + context 'when a user is logged in' do + it 'returns the user' do + # TODO + # expect(current_user.email).to eql('test2@example.com') + end + end + end + describe 'logged_in?' do + # TODO + end +end diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb new file mode 100644 index 0000000..e34a98d --- /dev/null +++ b/spec/controllers/sessions_controller_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe SessionsController do + describe 'get new' do + subject { get 'new' } + it 'renders the sessions/new template' do + expect(subject).to render_template('sessions/new') + end + end + describe 'get create' do + # TODO: test session status + subject { get 'create' } + it 'redirects to /welcome' do + expect(subject).to redirect_to('/welcome') + end + end + describe 'get delete' do + # TODO: test session status + subject { get 'delete' } + it 'redirects to /welcome' do + expect(subject).to redirect_to('/welcome') + end + end + describe 'get welcome' do + subject { get 'welcome' } + it 'renders the sessions/welcome template' do + expect(subject).to render_template('sessions/welcome') + end + end +end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb new file mode 100644 index 0000000..8595b6c --- /dev/null +++ b/spec/controllers/users_controller_spec.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe UsersController do + before(:all) do # TODO: turn it into cleanup after each test + User.destroy_all + end + let(:user1) do + User.create( + email: 'test1@example.com', + password: 'abcdefgh', + recovery_password: 'recovery password', + role: :admin + ) + end + describe 'get index' do + subject { get :index } + it 'renders the users/index template' do + expect(subject).to render_template('users/index') + end + end + describe 'get new' do + subject { get :new } + it 'renders the users/new template' do + expect(subject).to render_template('users/new') + end + end + describe 'get create' do + subject do + get :create, params: { user: { email: 'test2@example.com', password: 'abcdefgh' } } + end + it 'creates a user' do + subject + expect(User.where(email: 'test2@example.com').size).to eql(1) + end + it 'redirects to /welcome' do + subject + expect(subject).to redirect_to('/welcome') + end + end + describe 'get password_recovery_request_form' do + subject { get :password_recovery_request_form } + it 'renders the users/password_recovery_request_form template' do + expect(subject).to render_template('users/password_recovery_request_form') + end + end + describe 'post password_recovery_request' do + subject do + get :password_recovery_request, params: { email: user1.email } + end + it 'sends the proper recovery email' do + srand(10) + subject + email_text = ActionMailer::Base.deliveries.last.body.raw_source + expect(email_text).to match("recover_password/#{user1.id}/jeravuxl") + end + it 'sends a recovery email to the proper email' do + subject + expect(ActionMailer::Base.deliveries.last.to).to eql([user1.email]) + end + it 'flashes a notice' do + subject + expect(subject.request.flash[:notice]).to match("Recovery email sent to #{user1.email}") + end + end + describe 'get recover_password_form' do + subject do + get :recover_password_form, params: { id: user1.id, recovery_password: 'recovery password' } + end + it 'renders proper form' do + subject + expect(subject).to render_template('users/recover_password_form') + end + end + describe 'post recover_password' do + context 'when passwords match and recovery password is proper' do + subject do + post :recover_password, params: { + user_id: user1.id, + recovery_password: 'recovery password', + password: 'new password', + password_confirmation: 'new password' + } + end + it 'flashes a notice about success' do + subject + expect(subject.request.flash[:notice]).to match('Password changed') + end + end + context 'when recovery password is inproper' do + subject do + post :recover_password, params: { + user_id: user1.id, + recovery_password: 'improper recovery password', + password: 'new password', + password_confirmation: 'new password' + } + end + it 'flashes a notice about failure' do + subject + expect(subject.request.flash[:notice]).to match('Recovery link expired or invalid') + end + end + context 'when passwords match and recovery password is improper' do + subject do + post :recover_password, params: { + user_id: user1.id, + recovery_password: 'recovery password', + password: 'new password', + password_confirmation: 'bad password' + } + end + it 'flashes a notice about failure' do + subject + expect(subject.request.flash[:notice]).to match('Passwords don\'t match') + end + end + end + describe 'delete destroy' do + context 'when admin is logged in' do + it 'deletes the user' do + # TODO + end + end + end +end diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb new file mode 100644 index 0000000..b8ce9ed --- /dev/null +++ b/spec/mailers/previews/user_mailer_preview.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +# Preview all emails at http://localhost:3000/rails/mailers/user +class UserMailerPreview < ActionMailer::Preview +end diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb new file mode 100644 index 0000000..ca6cf07 --- /dev/null +++ b/spec/mailers/user_mailer_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe UserMailer, type: :mailer do + # TODO +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb new file mode 100644 index 0000000..edf8b47 --- /dev/null +++ b/spec/rails_helper.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +# This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../config/environment', __dir__) +# Prevent database truncation if the environment is production +abort('The Rails environment is running in production mode!') if Rails.env.production? +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')].sort.each { |f| require f } + +# Checks for pending migrations and applies them before tests are run. +# If you are not using ActiveRecord, you can remove these lines. +begin + ActiveRecord::Migration.maintain_test_schema! +rescue ActiveRecord::PendingMigrationError => e + puts e.to_s.strip + exit 1 +end +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 + + # You can uncomment this line to turn off ActiveRecord support entirely. + # config.use_active_record = false + + # 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! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..01f7c97 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +# 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. +# +# 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 + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + + # The settings below are suggested to provide a good initial experience + # with RSpec, but feel free to customize to your heart's content. + # # This allows 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. RSpec also provides + # # aliases for `it`, `describe`, and `context` that include `:focus` + # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + # config.filter_run_when_matching :focus + # + # # Allows RSpec to persist some state between runs in order to support + # # the `--only-failures` and `--next-failure` CLI options. We recommend + # # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + # + # # Limits the available syntax to the non-monkey patched syntax that is + # # recommended. For more details, see: + # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + # 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 diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb deleted file mode 100644 index d19212a..0000000 --- a/test/application_system_test_case.rb +++ /dev/null @@ -1,5 +0,0 @@ -require "test_helper" - -class ApplicationSystemTestCase < ActionDispatch::SystemTestCase - driven_by :selenium, using: :chrome, screen_size: [1400, 1400] -end diff --git a/test/channels/application_cable/connection_test.rb b/test/channels/application_cable/connection_test.rb deleted file mode 100644 index 800405f..0000000 --- a/test/channels/application_cable/connection_test.rb +++ /dev/null @@ -1,11 +0,0 @@ -require "test_helper" - -class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase - # test "connects with cookies" do - # cookies.signed[:user_id] = 42 - # - # connect - # - # assert_equal connection.user_id, "42" - # end -end diff --git a/test/controllers/.keep b/test/controllers/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/controllers/books_controller_test.rb b/test/controllers/books_controller_test.rb deleted file mode 100644 index 3927d43..0000000 --- a/test/controllers/books_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class BooksControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/test/fixtures/authors.yml b/test/fixtures/authors.yml deleted file mode 100644 index 5181636..0000000 --- a/test/fixtures/authors.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value diff --git a/test/fixtures/book_authors.yml b/test/fixtures/book_authors.yml deleted file mode 100644 index 5181636..0000000 --- a/test/fixtures/book_authors.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value diff --git a/test/fixtures/books.yml b/test/fixtures/books.yml deleted file mode 100644 index 5181636..0000000 --- a/test/fixtures/books.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value diff --git a/test/fixtures/files/.keep b/test/fixtures/files/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/helpers/.keep b/test/helpers/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/integration/.keep b/test/integration/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/mailers/.keep b/test/mailers/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/models/.keep b/test/models/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/models/author_test.rb b/test/models/author_test.rb deleted file mode 100644 index b2d7df1..0000000 --- a/test/models/author_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class AuthorTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/test/models/book_author_test.rb b/test/models/book_author_test.rb deleted file mode 100644 index b308c05..0000000 --- a/test/models/book_author_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class BookAuthorTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/test/models/book_test.rb b/test/models/book_test.rb deleted file mode 100644 index 69ab9de..0000000 --- a/test/models/book_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class BookTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/test/system/.keep b/test/system/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/test_helper.rb b/test/test_helper.rb deleted file mode 100644 index 47b598d..0000000 --- a/test/test_helper.rb +++ /dev/null @@ -1,13 +0,0 @@ -ENV['RAILS_ENV'] ||= 'test' -require_relative "../config/environment" -require "rails/test_help" - -class ActiveSupport::TestCase - # Run tests in parallel with specified workers - parallelize(workers: :number_of_processors) - - # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. - fixtures :all - - # Add more helper methods to be used by all tests here... -end