From b9b5614fe0fc7bd3cb55c7a7ec67e610f221abec Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Tue, 30 Oct 2018 19:00:32 +0200 Subject: [PATCH 01/23] generate rails app --- .gitignore | 23 ++ Gemfile | 54 +++++ Gemfile.lock | 196 ++++++++++++++++++ Rakefile | 6 + app/assets/config/manifest.js | 3 + app/assets/images/.keep | 0 app/assets/javascripts/application.js | 15 ++ app/assets/javascripts/cable.js | 13 ++ app/assets/javascripts/channels/.keep | 0 app/assets/stylesheets/application.css | 15 ++ app/channels/application_cable/channel.rb | 4 + app/channels/application_cable/connection.rb | 4 + app/controllers/application_controller.rb | 3 + app/controllers/concerns/.keep | 0 app/helpers/application_helper.rb | 2 + app/jobs/application_job.rb | 2 + app/mailers/application_mailer.rb | 4 + app/models/application_record.rb | 3 + app/models/concerns/.keep | 0 app/views/layouts/application.html.erb | 14 ++ app/views/layouts/mailer.html.erb | 13 ++ app/views/layouts/mailer.text.erb | 1 + bin/bundle | 3 + bin/rails | 9 + bin/rake | 9 + bin/setup | 38 ++++ bin/spring | 17 ++ bin/update | 29 +++ bin/yarn | 11 + config.ru | 5 + config/application.rb | 18 ++ config/boot.rb | 3 + config/cable.yml | 10 + config/database.yml | 25 +++ config/environment.rb | 5 + config/environments/development.rb | 54 +++++ config/environments/production.rb | 91 ++++++++ config/environments/test.rb | 42 ++++ .../application_controller_renderer.rb | 8 + config/initializers/assets.rb | 14 ++ config/initializers/backtrace_silencers.rb | 7 + config/initializers/cookies_serializer.rb | 5 + .../initializers/filter_parameter_logging.rb | 4 + config/initializers/inflections.rb | 16 ++ config/initializers/mime_types.rb | 4 + config/initializers/wrap_parameters.rb | 14 ++ config/locales/en.yml | 33 +++ config/puma.rb | 56 +++++ config/routes.rb | 3 + config/secrets.yml | 32 +++ config/spring.rb | 6 + db/seeds.rb | 7 + lib/assets/.keep | 0 lib/tasks/.keep | 0 log/.keep | 0 package.json | 5 + public/404.html | 67 ++++++ public/422.html | 67 ++++++ public/500.html | 66 ++++++ public/apple-touch-icon-precomposed.png | 0 public/apple-touch-icon.png | 0 public/favicon.ico | 0 public/robots.txt | 1 + test/application_system_test_case.rb | 5 + test/controllers/.keep | 0 test/fixtures/.keep | 0 test/fixtures/files/.keep | 0 test/helpers/.keep | 0 test/integration/.keep | 0 test/mailers/.keep | 0 test/models/.keep | 0 test/system/.keep | 0 test/test_helper.rb | 10 + tmp/.keep | 0 vendor/.keep | 0 75 files changed, 1174 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 Rakefile create mode 100644 app/assets/config/manifest.js create mode 100644 app/assets/images/.keep create mode 100644 app/assets/javascripts/application.js create mode 100644 app/assets/javascripts/cable.js create mode 100644 app/assets/javascripts/channels/.keep create mode 100644 app/assets/stylesheets/application.css create mode 100644 app/channels/application_cable/channel.rb create mode 100644 app/channels/application_cable/connection.rb create mode 100644 app/controllers/application_controller.rb create mode 100644 app/controllers/concerns/.keep create mode 100644 app/helpers/application_helper.rb create mode 100644 app/jobs/application_job.rb create mode 100644 app/mailers/application_mailer.rb create mode 100644 app/models/application_record.rb create mode 100644 app/models/concerns/.keep create mode 100644 app/views/layouts/application.html.erb create mode 100644 app/views/layouts/mailer.html.erb create mode 100644 app/views/layouts/mailer.text.erb create mode 100755 bin/bundle create mode 100755 bin/rails create mode 100755 bin/rake create mode 100755 bin/setup create mode 100755 bin/spring create mode 100755 bin/update create mode 100755 bin/yarn create mode 100644 config.ru create mode 100644 config/application.rb create mode 100644 config/boot.rb create mode 100644 config/cable.yml create mode 100644 config/database.yml create mode 100644 config/environment.rb create mode 100644 config/environments/development.rb create mode 100644 config/environments/production.rb create mode 100644 config/environments/test.rb create mode 100644 config/initializers/application_controller_renderer.rb create mode 100644 config/initializers/assets.rb create mode 100644 config/initializers/backtrace_silencers.rb create mode 100644 config/initializers/cookies_serializer.rb create mode 100644 config/initializers/filter_parameter_logging.rb create mode 100644 config/initializers/inflections.rb create mode 100644 config/initializers/mime_types.rb create mode 100644 config/initializers/wrap_parameters.rb create mode 100644 config/locales/en.yml create mode 100644 config/puma.rb create mode 100644 config/routes.rb create mode 100644 config/secrets.yml create mode 100644 config/spring.rb create mode 100644 db/seeds.rb create mode 100644 lib/assets/.keep create mode 100644 lib/tasks/.keep create mode 100644 log/.keep create mode 100644 package.json create mode 100644 public/404.html create mode 100644 public/422.html create mode 100644 public/500.html create mode 100644 public/apple-touch-icon-precomposed.png create mode 100644 public/apple-touch-icon.png create mode 100644 public/favicon.ico create mode 100644 public/robots.txt create mode 100644 test/application_system_test_case.rb create mode 100644 test/controllers/.keep create mode 100644 test/fixtures/.keep create mode 100644 test/fixtures/files/.keep create mode 100644 test/helpers/.keep create mode 100644 test/integration/.keep create mode 100644 test/mailers/.keep create mode 100644 test/models/.keep create mode 100644 test/system/.keep create mode 100644 test/test_helper.rb create mode 100644 tmp/.keep create mode 100644 vendor/.keep diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dcc5b36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-journal + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +/node_modules +/yarn-error.log + +.byebug_history diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..2091ba9 --- /dev/null +++ b/Gemfile @@ -0,0 +1,54 @@ +source 'https://rubygems.org' + +git_source(:github) do |repo_name| + repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") + "https://github.com/#{repo_name}.git" +end + + +# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' +gem 'rails', '~> 5.1.5' +# Use sqlite3 as the database for Active Record +gem 'sqlite3' +# Use Puma as the app server +gem 'puma', '~> 3.7' +# Use SCSS for stylesheets +gem 'sass-rails', '~> 5.0' +# Use Uglifier as compressor for JavaScript assets +gem 'uglifier', '>= 1.3.0' +# See https://github.com/rails/execjs#readme for more supported runtimes +# gem 'therubyracer', platforms: :ruby + +# Use CoffeeScript for .coffee assets and views +gem 'coffee-rails', '~> 4.2' +# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks +gem 'turbolinks', '~> 5' +# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder +gem 'jbuilder', '~> 2.5' +# Use Redis adapter to run Action Cable in production +# gem 'redis', '~> 4.0' +# Use ActiveModel has_secure_password +# gem 'bcrypt', '~> 3.1.7' + +# Use Capistrano for deployment +# gem 'capistrano-rails', group: :development + +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] + # Adds support for Capybara system testing and selenium driver + gem 'capybara', '~> 2.13' + gem 'selenium-webdriver' +end + +group :development do + # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. + gem 'web-console', '>= 3.3.0' + gem 'listen', '>= 3.0.5', '< 3.2' + # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring + gem 'spring' + gem 'spring-watcher-listen', '~> 2.0.0' +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..338205f --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,196 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.1.6) + actionpack (= 5.1.6) + nio4r (~> 2.0) + websocket-driver (~> 0.6.1) + actionmailer (5.1.6) + actionpack (= 5.1.6) + actionview (= 5.1.6) + activejob (= 5.1.6) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.1.6) + actionview (= 5.1.6) + activesupport (= 5.1.6) + rack (~> 2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.1.6) + activesupport (= 5.1.6) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (5.1.6) + activesupport (= 5.1.6) + globalid (>= 0.3.6) + activemodel (5.1.6) + activesupport (= 5.1.6) + activerecord (5.1.6) + activemodel (= 5.1.6) + activesupport (= 5.1.6) + arel (~> 8.0) + activesupport (5.1.6) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + arel (8.0.0) + bindex (0.5.0) + builder (3.2.3) + byebug (10.0.2) + capybara (2.18.0) + addressable + mini_mime (>= 0.1.3) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (>= 2.0, < 4.0) + childprocess (0.9.0) + ffi (~> 1.0, >= 1.0.11) + coffee-rails (4.2.2) + coffee-script (>= 2.2.0) + railties (>= 4.0.0) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.12.2) + concurrent-ruby (1.0.5) + crass (1.0.4) + erubi (1.7.1) + execjs (2.7.0) + ffi (1.9.25) + globalid (0.4.1) + activesupport (>= 4.2.0) + i18n (1.1.1) + concurrent-ruby (~> 1.0) + jbuilder (2.7.0) + activesupport (>= 4.2.0) + multi_json (>= 1.2) + listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) + loofah (2.2.3) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + mail (2.7.1) + mini_mime (>= 0.1.1) + method_source (0.9.0) + mini_mime (1.0.1) + mini_portile2 (2.3.0) + minitest (5.11.3) + multi_json (1.13.1) + nio4r (2.3.1) + nokogiri (1.8.5) + mini_portile2 (~> 2.3.0) + public_suffix (3.0.3) + puma (3.12.0) + rack (2.0.5) + rack-test (1.1.0) + rack (>= 1.0, < 3) + rails (5.1.6) + actioncable (= 5.1.6) + actionmailer (= 5.1.6) + actionpack (= 5.1.6) + actionview (= 5.1.6) + activejob (= 5.1.6) + activemodel (= 5.1.6) + activerecord (= 5.1.6) + activesupport (= 5.1.6) + bundler (>= 1.3.0) + railties (= 5.1.6) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) + railties (5.1.6) + actionpack (= 5.1.6) + activesupport (= 5.1.6) + method_source + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + rake (12.3.1) + rb-fsevent (0.10.3) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + ruby_dep (1.5.0) + rubyzip (1.2.2) + sass (3.6.0) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sass-rails (5.0.7) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + selenium-webdriver (3.14.1) + childprocess (~> 0.5) + rubyzip (~> 1.2, >= 1.2.2) + spring (2.0.2) + activesupport (>= 4.2) + spring-watcher-listen (2.0.1) + listen (>= 2.7, < 4.0) + spring (>= 1.2, < 3.0) + sprockets (3.7.2) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.2.1) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + sqlite3 (1.3.13) + thor (0.20.0) + thread_safe (0.3.6) + tilt (2.0.8) + turbolinks (5.2.0) + turbolinks-source (~> 5.2) + turbolinks-source (5.2.0) + tzinfo (1.2.5) + thread_safe (~> 0.1) + uglifier (4.1.19) + execjs (>= 0.3.0, < 3) + web-console (3.7.0) + actionview (>= 5.0) + activemodel (>= 5.0) + bindex (>= 0.4.0) + railties (>= 5.0) + websocket-driver (0.6.5) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.3) + xpath (3.2.0) + nokogiri (~> 1.8) + +PLATFORMS + ruby + +DEPENDENCIES + byebug + capybara (~> 2.13) + coffee-rails (~> 4.2) + jbuilder (~> 2.5) + listen (>= 3.0.5, < 3.2) + puma (~> 3.7) + rails (~> 5.1.5) + sass-rails (~> 5.0) + selenium-webdriver + spring + spring-watcher-listen (~> 2.0.0) + sqlite3 + turbolinks (~> 5) + tzinfo-data + uglifier (>= 1.3.0) + web-console (>= 3.3.0) + +BUNDLED WITH + 1.16.1 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..e85f913 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +# 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' + +Rails.application.load_tasks diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 0000000..b16e53d --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,3 @@ +//= link_tree ../images +//= link_directory ../javascripts .js +//= link_directory ../stylesheets .css diff --git a/app/assets/images/.keep b/app/assets/images/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 0000000..46b2035 --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,15 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's +// vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require rails-ujs +//= require turbolinks +//= require_tree . diff --git a/app/assets/javascripts/cable.js b/app/assets/javascripts/cable.js new file mode 100644 index 0000000..739aa5f --- /dev/null +++ b/app/assets/javascripts/cable.js @@ -0,0 +1,13 @@ +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the `rails generate channel` command. +// +//= require action_cable +//= require_self +//= require_tree ./channels + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); diff --git a/app/assets/javascripts/channels/.keep b/app/assets/javascripts/channels/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css new file mode 100644 index 0000000..d05ea0f --- /dev/null +++ b/app/assets/stylesheets/application.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's + * vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb new file mode 100644 index 0000000..d672697 --- /dev/null +++ b/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb new file mode 100644 index 0000000..0ff5442 --- /dev/null +++ b/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 0000000..1c07694 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,3 @@ +class ApplicationController < ActionController::Base + protect_from_forgery with: :exception +end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 0000000..de6be79 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 0000000..a009ace --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 0000000..286b223 --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' +end diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 0000000..10a4cba --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb new file mode 100644 index 0000000..2c40ec4 --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,14 @@ + + + + Lesson3App + <%= csrf_meta_tags %> + + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> + <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 0000000..cbd34d2 --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 0000000..37f0bdd --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/bin/bundle b/bin/bundle new file mode 100755 index 0000000..66e9889 --- /dev/null +++ b/bin/bundle @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +load Gem.bin_path('bundler', 'bundle') diff --git a/bin/rails b/bin/rails new file mode 100755 index 0000000..5badb2f --- /dev/null +++ b/bin/rails @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/bin/rake b/bin/rake new file mode 100755 index 0000000..d87d5f5 --- /dev/null +++ b/bin/rake @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..78c4e86 --- /dev/null +++ b/bin/setup @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + # Install JavaScript dependencies if using Yarn + # system('bin/yarn') + + + # puts "\n== Copying sample files ==" + # unless File.exist?('config/database.yml') + # cp 'config/database.yml.sample', 'config/database.yml' + # end + + puts "\n== Preparing database ==" + system! 'bin/rails db:setup' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/bin/spring b/bin/spring new file mode 100755 index 0000000..fb2ec2e --- /dev/null +++ b/bin/spring @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +# This file loads spring without using Bundler, in order to be fast. +# It gets overwritten when you run the `spring binstub` command. + +unless defined?(Spring) + require 'rubygems' + require 'bundler' + + lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) + spring = lockfile.specs.detect { |spec| spec.name == "spring" } + if spring + Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path + gem 'spring', spring.version + require 'spring/binstub' + end +end diff --git a/bin/update b/bin/update new file mode 100755 index 0000000..a8e4462 --- /dev/null +++ b/bin/update @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a way to update your development environment automatically. + # Add necessary update steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + puts "\n== Updating database ==" + system! 'bin/rails db:migrate' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/bin/yarn b/bin/yarn new file mode 100755 index 0000000..c2bacef --- /dev/null +++ b/bin/yarn @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +VENDOR_PATH = File.expand_path('..', __dir__) +Dir.chdir(VENDOR_PATH) do + begin + exec "yarnpkg #{ARGV.join(" ")}" + rescue Errno::ENOENT + $stderr.puts "Yarn executable was not detected in the system." + $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + exit 1 + end +end diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..f7ba0b5 --- /dev/null +++ b/config.ru @@ -0,0 +1,5 @@ +# This file is used by Rack-based servers to start the application. + +require_relative 'config/environment' + +run Rails.application diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 0000000..184390b --- /dev/null +++ b/config/application.rb @@ -0,0 +1,18 @@ +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) + +module Lesson3App + class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 5.1 + + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 0000000..30f5120 --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,3 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 0000000..b7dcfd7 --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: redis://localhost:6379/1 + channel_prefix: lesson3_app_production diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..0d02f24 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,25 @@ +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +# +default: &default + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + +development: + <<: *default + database: db/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: db/test.sqlite3 + +production: + <<: *default + database: db/production.sqlite3 diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 0000000..426333b --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative 'application' + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb new file mode 100644 index 0000000..5187e22 --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,54 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable/disable caching. By default caching is disabled. + if Rails.root.join('tmp/caching-dev.txt').exist? + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Don't care if the mailer can't send. + config.action_mailer.raise_delivery_errors = false + + config.action_mailer.perform_caching = false + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # 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 + + # Suppress logger output for asset requests. + config.assets.quiet = true + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true + + # Use an evented file watcher to asynchronously detect changes in source code, + # routes, locales, etc. This feature depends on the listen gem. + config.file_watcher = ActiveSupport::EventedFileUpdateChecker +end diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 0000000..20be5fd --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,91 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Attempt to read encrypted secrets from `config/secrets.yml.enc`. + # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or + # `config/secrets.yml.key`. + config.read_encrypted_secrets = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + + # Compress JavaScripts and CSS. + config.assets.js_compressor = :uglifier + # config.assets.css_compressor = :sass + + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Mount Action Cable outside main process or domain + # config.action_cable.mount_path = nil + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "lesson3_app_#{Rails.env}" + config.action_mailer.perform_caching = false + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + + # Use a different logger for distributed setups. + # 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) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 0000000..8e5cbde --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,42 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Do not eager load code on boot. This avoids loading your whole application + # just for the purpose of running a single test. If you are using a tool that + # preloads Rails for running tests, you may have to set it to true. + config.eager_load = false + + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" + } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Raise exceptions instead of rendering exception templates. + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + config.action_mailer.perform_caching = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true +end diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb new file mode 100644 index 0000000..89d2efa --- /dev/null +++ b/config/initializers/application_controller_renderer.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# ActiveSupport::Reloader.to_prepare do +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) +# end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 0000000..4b828e8 --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,14 @@ +# 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. +Rails.application.config.assets.version = '1.0' + +# Add additional assets to the asset load path. +# Rails.application.config.assets.paths << Emoji.images_path +# Add Yarn node_modules folder to the asset load path. +Rails.application.config.assets.paths << Rails.root.join('node_modules') + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w( admin.js admin.css ) diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb new file mode 100644 index 0000000..59385cd --- /dev/null +++ b/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# 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. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb new file mode 100644 index 0000000..5a6a32d --- /dev/null +++ b/config/initializers/cookies_serializer.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Specify a serializer for the signed and encrypted cookie jars. +# Valid options are :json, :marshal, and :hybrid. +Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb new file mode 100644 index 0000000..4a994e1 --- /dev/null +++ b/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,4 @@ +# 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 += [:password] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb new file mode 100644 index 0000000..ac033bf --- /dev/null +++ b/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym 'RESTful' +# end diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb new file mode 100644 index 0000000..dc18996 --- /dev/null +++ b/config/initializers/mime_types.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb new file mode 100644 index 0000000..bbfc396 --- /dev/null +++ b/config/initializers/wrap_parameters.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. + +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters format: [:json] +end + +# To enable root element in JSON for ActiveRecord objects. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = true +# end diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..decc5a8 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,33 @@ +# Files in the config/locales directory are used for internationalization +# and are automatically loaded by Rails. If you want to use locales other +# than English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t 'hello' +# +# In views, this is aliased to just `t`: +# +# <%= t('hello') %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# The following keys must be escaped otherwise they will not be retrieved by +# the default I18n backend: +# +# true, false, on, off, yes, no +# +# Instead, surround them with single quotes. +# +# en: +# 'true': 'foo' +# +# To learn more, please read the Rails Internationalization guide +# available at http://guides.rubyonrails.org/i18n.html. + +en: + hello: "Hello world" diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000..1e19380 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,56 @@ +# 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. +# +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked webserver processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. If you use this option +# you need to make sure to reconnect any threads in the `on_worker_boot` +# block. +# +# preload_app! + +# If you are preloading your application and using Active Record, it's +# recommended that you close any connections to the database before workers +# are forked to prevent connection leakage. +# +# before_fork do +# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) +# end + +# The code in the `on_worker_boot` will be called if you are using +# clustered mode by specifying a number of `workers`. After each worker +# process is booted, this block will be run. If you are using the `preload_app!` +# option, you will want to use this block to reconnect to any threads +# or connections that may have been created at application boot, as Ruby +# cannot share connections between processes. +# +# on_worker_boot do +# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) +# end +# + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..787824f --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,3 @@ +Rails.application.routes.draw do + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html +end diff --git a/config/secrets.yml b/config/secrets.yml new file mode 100644 index 0000000..13bd8f3 --- /dev/null +++ b/config/secrets.yml @@ -0,0 +1,32 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key is used for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! + +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +# You can use `rails secret` to generate a secure secret key. + +# Make sure the secrets in this file are kept private +# if you're sharing your code publicly. + +# Shared secrets are available across all environments. + +# shared: +# api_key: a1B2c3D4e5F6 + +# Environmental secrets are only available for that specific environment. + +development: + secret_key_base: 05a66de7911856cbff0758f13e2ef4be1184e23c29adb303818d129e7f1e0312acfe38bd4fdae9b1e1646a0d1ef6481969fc14d28980ad43afff08390ba03549 + +test: + secret_key_base: 6b2aadfb6bebdb8721e529f7a0e8e8db344c3826bf251c25ded8d9ea3c849fe0dafb828aacecf22995b3398db9f7b4252857fb908bc10ab750f0b12be6377724 + +# Do not keep production secrets in the unencrypted secrets file. +# Instead, either read values from the environment. +# Or, use `bin/rails secrets:setup` to configure encrypted secrets +# and move the `production:` environment over there. + +production: + secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> diff --git a/config/spring.rb b/config/spring.rb new file mode 100644 index 0000000..c9119b4 --- /dev/null +++ b/config/spring.rb @@ -0,0 +1,6 @@ +%w( + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +).each { |path| Spring.watch(path) } diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 0000000..1beea2a --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,7 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). +# +# Examples: +# +# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) +# Character.create(name: 'Luke', movie: movies.first) diff --git a/lib/assets/.keep b/lib/assets/.keep new file mode 100644 index 0000000..e69de29 diff --git a/lib/tasks/.keep b/lib/tasks/.keep new file mode 100644 index 0000000..e69de29 diff --git a/log/.keep b/log/.keep new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json new file mode 100644 index 0000000..214bf98 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "name": "lesson3_app", + "private": true, + "dependencies": {} +} diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..2be3af2 --- /dev/null +++ b/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

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 new file mode 100644 index 0000000..c08eac0 --- /dev/null +++ b/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

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 new file mode 100644 index 0000000..78a030a --- /dev/null +++ b/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

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

+
+ + diff --git a/public/apple-touch-icon-precomposed.png b/public/apple-touch-icon-precomposed.png new file mode 100644 index 0000000..e69de29 diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..e69de29 diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..37b576a --- /dev/null +++ b/public/robots.txt @@ -0,0 +1 @@ +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb new file mode 100644 index 0000000..d19212a --- /dev/null +++ b/test/application_system_test_case.rb @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :chrome, screen_size: [1400, 1400] +end diff --git a/test/controllers/.keep b/test/controllers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/.keep b/test/fixtures/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/files/.keep b/test/fixtures/files/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/helpers/.keep b/test/helpers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/integration/.keep b/test/integration/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/mailers/.keep b/test/mailers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/models/.keep b/test/models/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/system/.keep b/test/system/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..92e39b2 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,10 @@ +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../../config/environment', __FILE__) +require 'rails/test_help' + +class ActiveSupport::TestCase + # 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 diff --git a/tmp/.keep b/tmp/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/.keep b/vendor/.keep new file mode 100644 index 0000000..e69de29 From 51b0c5d01c48d44dfa75fa3052f99bfd12db1c26 Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Tue, 30 Oct 2018 19:37:06 +0200 Subject: [PATCH 02/23] add gem devise --- Gemfile | 1 + Gemfile.lock | 14 + app/views/devise/confirmations/new.html.erb | 16 + .../mailer/confirmation_instructions.html.erb | 5 + .../devise/mailer/email_changed.html.erb | 7 + .../devise/mailer/password_change.html.erb | 3 + .../reset_password_instructions.html.erb | 8 + .../mailer/unlock_instructions.html.erb | 7 + app/views/devise/passwords/edit.html.erb | 25 ++ app/views/devise/passwords/new.html.erb | 16 + app/views/devise/registrations/edit.html.erb | 43 +++ app/views/devise/registrations/new.html.erb | 29 ++ app/views/devise/sessions/new.html.erb | 26 ++ app/views/devise/shared/_links.html.erb | 25 ++ app/views/devise/unlocks/new.html.erb | 16 + app/views/layouts/application.html.erb | 6 +- config/environments/development.rb | 3 + config/initializers/devise.rb | 290 ++++++++++++++++++ config/locales/devise.en.yml | 64 ++++ config/routes.rb | 3 + 20 files changed, 606 insertions(+), 1 deletion(-) create mode 100644 app/views/devise/confirmations/new.html.erb create mode 100644 app/views/devise/mailer/confirmation_instructions.html.erb create mode 100644 app/views/devise/mailer/email_changed.html.erb create mode 100644 app/views/devise/mailer/password_change.html.erb create mode 100644 app/views/devise/mailer/reset_password_instructions.html.erb create mode 100644 app/views/devise/mailer/unlock_instructions.html.erb create mode 100644 app/views/devise/passwords/edit.html.erb create mode 100644 app/views/devise/passwords/new.html.erb create mode 100644 app/views/devise/registrations/edit.html.erb create mode 100644 app/views/devise/registrations/new.html.erb create mode 100644 app/views/devise/sessions/new.html.erb create mode 100644 app/views/devise/shared/_links.html.erb create mode 100644 app/views/devise/unlocks/new.html.erb create mode 100644 config/initializers/devise.rb create mode 100644 config/locales/devise.en.yml diff --git a/Gemfile b/Gemfile index 2091ba9..b701400 100644 --- a/Gemfile +++ b/Gemfile @@ -52,3 +52,4 @@ end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem 'devise' diff --git a/Gemfile.lock b/Gemfile.lock index 338205f..d2a6645 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) + bcrypt (3.1.12) bindex (0.5.0) builder (3.2.3) byebug (10.0.2) @@ -62,6 +63,12 @@ GEM coffee-script-source (1.12.2) concurrent-ruby (1.0.5) crass (1.0.4) + devise (4.5.0) + bcrypt (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 4.1.0, < 6.0) + responders + warden (~> 1.2.3) erubi (1.7.1) execjs (2.7.0) ffi (1.9.25) @@ -89,6 +96,7 @@ GEM nio4r (2.3.1) nokogiri (1.8.5) mini_portile2 (~> 2.3.0) + orm_adapter (0.5.0) public_suffix (3.0.3) puma (3.12.0) rack (2.0.5) @@ -121,6 +129,9 @@ GEM rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) + responders (2.4.0) + actionpack (>= 4.2.0, < 5.3) + railties (>= 4.2.0, < 5.3) ruby_dep (1.5.0) rubyzip (1.2.2) sass (3.6.0) @@ -160,6 +171,8 @@ GEM thread_safe (~> 0.1) uglifier (4.1.19) execjs (>= 0.3.0, < 3) + warden (1.2.7) + rack (>= 1.0) web-console (3.7.0) actionview (>= 5.0) activemodel (>= 5.0) @@ -178,6 +191,7 @@ DEPENDENCIES byebug capybara (~> 2.13) coffee-rails (~> 4.2) + devise jbuilder (~> 2.5) listen (>= 3.0.5, < 3.2) puma (~> 3.7) diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb new file mode 100644 index 0000000..2dea366 --- /dev/null +++ b/app/views/devise/confirmations/new.html.erb @@ -0,0 +1,16 @@ +

Resend confirmation instructions

+ +<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> + <%= devise_error_messages! %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> +
+ +
+ <%= f.submit "Resend confirmation instructions" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb new file mode 100644 index 0000000..dc55f64 --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.erb @@ -0,0 +1,5 @@ +

Welcome <%= @email %>!

+ +

You can confirm your account email through the link below:

+ +

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb new file mode 100644 index 0000000..32f4ba8 --- /dev/null +++ b/app/views/devise/mailer/email_changed.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @email %>!

+ +<% if @resource.try(:unconfirmed_email?) %> +

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

+<% else %> +

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

+<% end %> diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb new file mode 100644 index 0000000..b41daf4 --- /dev/null +++ b/app/views/devise/mailer/password_change.html.erb @@ -0,0 +1,3 @@ +

Hello <%= @resource.email %>!

+ +

We're contacting you to notify you that your password has been changed.

diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 0000000..f667dc1 --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +

Hello <%= @resource.email %>!

+ +

Someone has requested a link to change your password. You can do this through the link below.

+ +

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

+ +

If you didn't request this, please ignore this email.

+

Your password won't change until you access the link above and create a new one.

diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 0000000..41e148b --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @resource.email %>!

+ +

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

+ +

Click the link below to unlock your account:

+ +

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb new file mode 100644 index 0000000..442036a --- /dev/null +++ b/app/views/devise/passwords/edit.html.erb @@ -0,0 +1,25 @@ +

Change your password

+ +<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> + <%= devise_error_messages! %> + <%= f.hidden_field :reset_password_token %> + +
+ <%= f.label :password, "New password" %>
+ <% if @minimum_password_length %> + (<%= @minimum_password_length %> characters minimum)
+ <% end %> + <%= f.password_field :password, autofocus: true, autocomplete: "new-password" %> +
+ +
+ <%= f.label :password_confirmation, "Confirm new password" %>
+ <%= f.password_field :password_confirmation, autocomplete: "off" %> +
+ +
+ <%= f.submit "Change my password" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb new file mode 100644 index 0000000..11b3d6a --- /dev/null +++ b/app/views/devise/passwords/new.html.erb @@ -0,0 +1,16 @@ +

Forgot your password?

+ +<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> + <%= devise_error_messages! %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ +
+ <%= f.submit "Send me reset password instructions" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb new file mode 100644 index 0000000..f465532 --- /dev/null +++ b/app/views/devise/registrations/edit.html.erb @@ -0,0 +1,43 @@ +

Edit <%= resource_name.to_s.humanize %>

+ +<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> + <%= devise_error_messages! %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ + <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> +
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
+ <% end %> + +
+ <%= f.label :password %> (leave blank if you don't want to change it)
+ <%= f.password_field :password, autocomplete: "new-password" %> + <% if @minimum_password_length %> +
+ <%= @minimum_password_length %> characters minimum + <% end %> +
+ +
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %> +
+ +
+ <%= f.label :current_password %> (we need your current password to confirm your changes)
+ <%= f.password_field :current_password, autocomplete: "current-password" %> +
+ +
+ <%= f.submit "Update" %> +
+<% end %> + +

Cancel my account

+ +

Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>

+ +<%= link_to "Back", :back %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb new file mode 100644 index 0000000..36c9ed7 --- /dev/null +++ b/app/views/devise/registrations/new.html.erb @@ -0,0 +1,29 @@ +

Sign up

+ +<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> + <%= devise_error_messages! %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ +
+ <%= f.label :password %> + <% if @minimum_password_length %> + (<%= @minimum_password_length %> characters minimum) + <% end %>
+ <%= f.password_field :password, autocomplete: "new-password" %> +
+ +
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %> +
+ +
+ <%= f.submit "Sign up" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb new file mode 100644 index 0000000..e3b9319 --- /dev/null +++ b/app/views/devise/sessions/new.html.erb @@ -0,0 +1,26 @@ +

Log in

+ +<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ +
+ <%= f.label :password %>
+ <%= f.password_field :password, autocomplete: "current-password" %> +
+ + <% if devise_mapping.rememberable? -%> +
+ <%= f.check_box :remember_me %> + <%= f.label :remember_me %> +
+ <% end -%> + +
+ <%= f.submit "Log in" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb new file mode 100644 index 0000000..e6a3e41 --- /dev/null +++ b/app/views/devise/shared/_links.html.erb @@ -0,0 +1,25 @@ +<%- if controller_name != 'sessions' %> + <%= link_to "Log in", new_session_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.registerable? && controller_name != 'registrations' %> + <%= link_to "Sign up", new_registration_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> + <%= link_to "Forgot your password?", new_password_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> + <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> + <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.omniauthable? %> + <%- resource_class.omniauth_providers.each do |provider| %> + <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %>
+ <% end -%> +<% end -%> diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb new file mode 100644 index 0000000..cfe8aa8 --- /dev/null +++ b/app/views/devise/unlocks/new.html.erb @@ -0,0 +1,16 @@ +

Resend unlock instructions

+ +<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> + <%= devise_error_messages! %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ +
+ <%= f.submit "Resend unlock instructions" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 2c40ec4..48c43cd 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -9,6 +9,10 @@ - <%= yield %> + +

<%= notice %>

+

<%= alert %>

+ + <%= yield %> diff --git a/config/environments/development.rb b/config/environments/development.rb index 5187e22..3d3b06d 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -45,6 +45,9 @@ # Suppress logger output for asset requests. config.assets.quiet = true + + config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } + # Raises error for missing translations # config.action_view.raise_on_missing_translations = true diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb new file mode 100644 index 0000000..c381c3a --- /dev/null +++ b/config/initializers/devise.rb @@ -0,0 +1,290 @@ +# frozen_string_literal: true + +# Use this hook to configure devise mailer, warden hooks and so forth. +# Many of these configuration options can be set straight in your model. +Devise.setup do |config| + # The secret key used by Devise. Devise uses this key to generate + # random tokens. Changing this key will render invalid all existing + # confirmation, reset password and unlock tokens in the database. + # Devise will use the `secret_key_base` as its `secret_key` + # by default. You can change it below and use your own secret key. + # config.secret_key = '8016b9f668f531ac9dec62b371eb97e8f2f7c4b71b9fc3f1f034411f28978d1301385144d18faa65850a1685074031dd0871e133dc2e539fbef5f26ff8c5eb7c' + + # ==> Controller configuration + # Configure the parent class to the devise controllers. + # config.parent_controller = 'DeviseController' + + # ==> Mailer Configuration + # Configure the e-mail address which will be shown in Devise::Mailer, + # note that it will be overwritten if you use your own mailer class + # with default "from" parameter. + config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com' + + # Configure the class responsible to send e-mails. + # config.mailer = 'Devise::Mailer' + + # Configure the parent class responsible to send e-mails. + # config.parent_mailer = 'ActionMailer::Base' + + # ==> ORM configuration + # Load and configure the ORM. Supports :active_record (default) and + # :mongoid (bson_ext recommended) by default. Other ORMs may be + # available as additional gems. + require 'devise/orm/active_record' + + # ==> Configuration for any authentication mechanism + # Configure which keys are used when authenticating a user. The default is + # just :email. You can configure it to use [:username, :subdomain], so for + # authenticating a user, both parameters are required. Remember that those + # parameters are used only when authenticating and not when retrieving from + # session. If you need permissions, you should implement that in a before filter. + # You can also supply a hash where the value is a boolean determining whether + # or not authentication should be aborted when the value is not present. + # config.authentication_keys = [:email] + + # Configure parameters from the request object used for authentication. Each entry + # given should be a request method and it will automatically be passed to the + # find_for_authentication method and considered in your model lookup. For instance, + # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. + # The same considerations mentioned for authentication_keys also apply to request_keys. + # config.request_keys = [] + + # Configure which authentication keys should be case-insensitive. + # These keys will be downcased upon creating or modifying a user and when used + # to authenticate or find a user. Default is :email. + config.case_insensitive_keys = [:email] + + # Configure which authentication keys should have whitespace stripped. + # These keys will have whitespace before and after removed upon creating or + # modifying a user and when used to authenticate or find a user. Default is :email. + config.strip_whitespace_keys = [:email] + + # Tell if authentication through request.params is enabled. True by default. + # It can be set to an array that will enable params authentication only for the + # given strategies, for example, `config.params_authenticatable = [:database]` will + # enable it only for database (email + password) authentication. + # config.params_authenticatable = true + + # Tell if authentication through HTTP Auth is enabled. False by default. + # It can be set to an array that will enable http authentication only for the + # given strategies, for example, `config.http_authenticatable = [:database]` will + # enable it only for database authentication. The supported strategies are: + # :database = Support basic authentication with authentication key + password + # config.http_authenticatable = false + + # If 401 status code should be returned for AJAX requests. True by default. + # config.http_authenticatable_on_xhr = true + + # The realm used in Http Basic Authentication. 'Application' by default. + # config.http_authentication_realm = 'Application' + + # It will change confirmation, password recovery and other workflows + # to behave the same regardless if the e-mail provided was right or wrong. + # Does not affect registerable. + # config.paranoid = true + + # By default Devise will store the user in session. You can skip storage for + # particular strategies by setting this option. + # Notice that if you are skipping storage for all authentication paths, you + # may want to disable generating routes to Devise's sessions controller by + # passing skip: :sessions to `devise_for` in your config/routes.rb + config.skip_session_storage = [:http_auth] + + # By default, Devise cleans up the CSRF token on authentication to + # avoid CSRF token fixation attacks. This means that, when using AJAX + # requests for sign in and sign up, you need to get a new CSRF token + # from the server. You can disable this option at your own risk. + # config.clean_up_csrf_token_on_authentication = true + + # When false, Devise will not attempt to reload routes on eager load. + # This can reduce the time taken to boot the app but if your application + # requires the Devise mappings to be loaded during boot time the application + # won't boot properly. + # config.reload_routes = true + + # ==> Configuration for :database_authenticatable + # For bcrypt, this is the cost for hashing the password and defaults to 11. If + # using other algorithms, it sets how many times you want the password to be hashed. + # + # Limiting the stretches to just one in testing will increase the performance of + # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use + # a value less than 10 in other environments. Note that, for bcrypt (the default + # algorithm), the cost increases exponentially with the number of stretches (e.g. + # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). + config.stretches = Rails.env.test? ? 1 : 11 + + # Set up a pepper to generate the hashed password. + # config.pepper = '968a8cdf2c740170f55f3b089d86f7cc9f96d05e7f1fd54b13d4553df5a4c0ca4157aa8ed31524fcb2a8b669b94d27555698547a2150841342ac90e05cbeafa6' + + # Send a notification to the original email when the user's email is changed. + # config.send_email_changed_notification = false + + # Send a notification email when the user's password is changed. + # config.send_password_change_notification = false + + # ==> Configuration for :confirmable + # A period that the user is allowed to access the website even without + # confirming their account. For instance, if set to 2.days, the user will be + # able to access the website for two days without confirming their account, + # access will be blocked just in the third day. Default is 0.days, meaning + # the user cannot access the website without confirming their account. + # config.allow_unconfirmed_access_for = 2.days + + # A period that the user is allowed to confirm their account before their + # token becomes invalid. For example, if set to 3.days, the user can confirm + # their account within 3 days after the mail was sent, but on the fourth day + # their account can't be confirmed with the token any more. + # Default is nil, meaning there is no restriction on how long a user can take + # before confirming their account. + # config.confirm_within = 3.days + + # If true, requires any email changes to be confirmed (exactly the same way as + # initial account confirmation) to be applied. Requires additional unconfirmed_email + # db field (see migrations). Until confirmed, new email is stored in + # unconfirmed_email column, and copied to email column on successful confirmation. + config.reconfirmable = true + + # Defines which key will be used when confirming an account + # config.confirmation_keys = [:email] + + # ==> Configuration for :rememberable + # The time the user will be remembered without asking for credentials again. + # config.remember_for = 2.weeks + + # Invalidates all the remember me tokens when the user signs out. + config.expire_all_remember_me_on_sign_out = true + + # If true, extends the user's remember period when remembered via cookie. + # config.extend_remember_period = false + + # Options to be passed to the created cookie. For instance, you can set + # secure: true in order to force SSL only cookies. + # config.rememberable_options = {} + + # ==> Configuration for :validatable + # Range for password length. + config.password_length = 6..128 + + # Email regex used to validate email formats. It simply asserts that + # one (and only one) @ exists in the given string. This is mainly + # to give user feedback and not to assert the e-mail validity. + config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ + + # ==> Configuration for :timeoutable + # The time you want to timeout the user session without activity. After this + # time the user will be asked for credentials again. Default is 30 minutes. + # config.timeout_in = 30.minutes + + # ==> Configuration for :lockable + # Defines which strategy will be used to lock an account. + # :failed_attempts = Locks an account after a number of failed attempts to sign in. + # :none = No lock strategy. You should handle locking by yourself. + # config.lock_strategy = :failed_attempts + + # Defines which key will be used when locking and unlocking an account + # config.unlock_keys = [:email] + + # Defines which strategy will be used to unlock an account. + # :email = Sends an unlock link to the user email + # :time = Re-enables login after a certain amount of time (see :unlock_in below) + # :both = Enables both strategies + # :none = No unlock strategy. You should handle unlocking by yourself. + # config.unlock_strategy = :both + + # Number of authentication tries before locking an account if lock_strategy + # is failed attempts. + # config.maximum_attempts = 20 + + # Time interval to unlock the account if :time is enabled as unlock_strategy. + # config.unlock_in = 1.hour + + # Warn on the last attempt before the account is locked. + # config.last_attempt_warning = true + + # ==> Configuration for :recoverable + # + # Defines which key will be used when recovering the password for an account + # config.reset_password_keys = [:email] + + # Time interval you can reset your password with a reset password key. + # Don't put a too small interval or your users won't have the time to + # change their passwords. + config.reset_password_within = 6.hours + + # When set to false, does not sign a user in automatically after their password is + # reset. Defaults to true, so a user is signed in automatically after a reset. + # config.sign_in_after_reset_password = true + + # ==> Configuration for :encryptable + # Allow you to use another hashing or encryption algorithm besides bcrypt (default). + # You can use :sha1, :sha512 or algorithms from others authentication tools as + # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20 + # for default behavior) and :restful_authentication_sha1 (then you should set + # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper). + # + # Require the `devise-encryptable` gem when using anything other than bcrypt + # config.encryptor = :sha512 + + # ==> Scopes configuration + # Turn scoped views on. Before rendering "sessions/new", it will first check for + # "users/sessions/new". It's turned off by default because it's slower if you + # are using only default views. + # config.scoped_views = false + + # Configure the default scope given to Warden. By default it's the first + # devise role declared in your routes (usually :user). + # config.default_scope = :user + + # Set this configuration to false if you want /users/sign_out to sign out + # only the current scope. By default, Devise signs out all scopes. + # config.sign_out_all_scopes = true + + # ==> Navigation configuration + # Lists the formats that should be treated as navigational. Formats like + # :html, should redirect to the sign in page when the user does not have + # access, but formats like :xml or :json, should return 401. + # + # If you have any extra navigational formats, like :iphone or :mobile, you + # should add them to the navigational formats lists. + # + # The "*/*" below is required to match Internet Explorer requests. + # config.navigational_formats = ['*/*', :html] + + # The default HTTP method used to sign out a resource. Default is :delete. + config.sign_out_via = :delete + + # ==> OmniAuth + # Add a new OmniAuth provider. Check the wiki for more information on setting + # up on your models and hooks. + # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' + + # ==> Warden configuration + # If you want to use other strategies, that are not supported by Devise, or + # change the failure app, you can configure them inside the config.warden block. + # + # config.warden do |manager| + # manager.intercept_401 = false + # manager.default_strategies(scope: :user).unshift :some_external_strategy + # end + + # ==> Mountable engine configurations + # When using Devise inside an engine, let's call it `MyEngine`, and this engine + # is mountable, there are some extra configurations to be taken into account. + # The following options are available, assuming the engine is mounted as: + # + # mount MyEngine, at: '/my_engine' + # + # The router that invoked `devise_for`, in the example above, would be: + # config.router_name = :my_engine + # + # When using OmniAuth, Devise cannot automatically set OmniAuth path, + # so you need to do it manually. For the users scope, it would be: + # config.omniauth_path_prefix = '/my_engine/users/auth' + + # ==> Turbolinks configuration + # If your app is using Turbolinks, Turbolinks::Controller needs to be included to make redirection work correctly: + # + # ActiveSupport.on_load(:devise_failure_app) do + # include Turbolinks::Controller + # end +end diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml new file mode 100644 index 0000000..0b8f130 --- /dev/null +++ b/config/locales/devise.en.yml @@ -0,0 +1,64 @@ +# Additional translations at https://github.com/plataformatec/devise/wiki/I18n + +en: + devise: + confirmations: + confirmed: "Your email address has been successfully confirmed." + send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." + send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." + failure: + already_authenticated: "You are already signed in." + inactive: "Your account is not activated yet." + invalid: "Invalid %{authentication_keys} or password." + locked: "Your account is locked." + last_attempt: "You have one more attempt before your account is locked." + not_found_in_database: "Invalid %{authentication_keys} or password." + timeout: "Your session expired. Please sign in again to continue." + unauthenticated: "You need to sign in or sign up before continuing." + unconfirmed: "You have to confirm your email address before continuing." + mailer: + confirmation_instructions: + subject: "Confirmation instructions" + reset_password_instructions: + subject: "Reset password instructions" + unlock_instructions: + subject: "Unlock instructions" + email_changed: + subject: "Email Changed" + password_change: + subject: "Password Changed" + omniauth_callbacks: + failure: "Could not authenticate you from %{kind} because \"%{reason}\"." + success: "Successfully authenticated from %{kind} account." + passwords: + no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." + send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." + send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." + updated: "Your password has been changed successfully. You are now signed in." + updated_not_active: "Your password has been changed successfully." + registrations: + destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." + signed_up: "Welcome! You have signed up successfully." + signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." + signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." + signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." + update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." + updated: "Your account has been updated successfully." + sessions: + signed_in: "Signed in successfully." + signed_out: "Signed out successfully." + already_signed_out: "Signed out successfully." + unlocks: + send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." + send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." + unlocked: "Your account has been unlocked successfully. Please sign in to continue." + errors: + messages: + already_confirmed: "was already confirmed, please try signing in" + confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" + expired: "has expired, please request a new one" + not_found: "not found" + not_locked: "was not locked" + not_saved: + one: "1 error prohibited this %{resource} from being saved:" + other: "%{count} errors prohibited this %{resource} from being saved:" diff --git a/config/routes.rb b/config/routes.rb index 787824f..fba417d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,6 @@ Rails.application.routes.draw do + + root to: "home#index" + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end From 422782c6ac0913fab9d0413a0277d7713dffe333 Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Tue, 30 Oct 2018 19:40:41 +0200 Subject: [PATCH 03/23] rails generate devise admin --- app/models/admin.rb | 6 +++ config/routes.rb | 1 + .../20181030173840_devise_create_admins.rb | 44 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 app/models/admin.rb create mode 100644 db/migrate/20181030173840_devise_create_admins.rb diff --git a/app/models/admin.rb b/app/models/admin.rb new file mode 100644 index 0000000..7a7be2f --- /dev/null +++ b/app/models/admin.rb @@ -0,0 +1,6 @@ +class Admin < ApplicationRecord + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :validatable +end diff --git a/config/routes.rb b/config/routes.rb index fba417d..0fb2be3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,6 @@ Rails.application.routes.draw do + devise_for :admins root to: "home#index" # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html diff --git a/db/migrate/20181030173840_devise_create_admins.rb b/db/migrate/20181030173840_devise_create_admins.rb new file mode 100644 index 0000000..5e61cbb --- /dev/null +++ b/db/migrate/20181030173840_devise_create_admins.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class DeviseCreateAdmins < ActiveRecord::Migration[5.1] + def change + create_table :admins do |t| + ## Database authenticatable + t.string :email, null: false, default: "" + t.string :encrypted_password, null: false, default: "" + + ## Recoverable + t.string :reset_password_token + t.datetime :reset_password_sent_at + + ## Rememberable + t.datetime :remember_created_at + + ## Trackable + # t.integer :sign_in_count, default: 0, null: false + # t.datetime :current_sign_in_at + # t.datetime :last_sign_in_at + # t.string :current_sign_in_ip + # t.string :last_sign_in_ip + + ## Confirmable + # t.string :confirmation_token + # t.datetime :confirmed_at + # t.datetime :confirmation_sent_at + # t.string :unconfirmed_email # Only if using reconfirmable + + ## Lockable + # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts + # t.string :unlock_token # Only if unlock strategy is :email or :both + # t.datetime :locked_at + + + t.timestamps null: false + end + + add_index :admins, :email, unique: true + add_index :admins, :reset_password_token, unique: true + # add_index :admins, :confirmation_token, unique: true + # add_index :admins, :unlock_token, unique: true + end +end From d2879900b392542b5ba451e6aa627cd43b0d4ebe Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Tue, 30 Oct 2018 19:46:01 +0200 Subject: [PATCH 04/23] rails generate devise user --- app/models/user.rb | 6 +++ config/routes.rb | 1 + .../20181030174359_devise_create_users.rb | 44 ++++++++++++++++ db/schema.rb | 51 +++++++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 app/models/user.rb create mode 100644 db/migrate/20181030174359_devise_create_users.rb create mode 100644 db/schema.rb diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..4756799 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,6 @@ +class User < ApplicationRecord + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :validatable +end diff --git a/config/routes.rb b/config/routes.rb index 0fb2be3..06979f1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,6 @@ Rails.application.routes.draw do + devise_for :users devise_for :admins root to: "home#index" diff --git a/db/migrate/20181030174359_devise_create_users.rb b/db/migrate/20181030174359_devise_create_users.rb new file mode 100644 index 0000000..b6e5c6e --- /dev/null +++ b/db/migrate/20181030174359_devise_create_users.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class DeviseCreateUsers < ActiveRecord::Migration[5.1] + def change + create_table :users do |t| + ## Database authenticatable + t.string :email, null: false, default: "" + t.string :encrypted_password, null: false, default: "" + + ## Recoverable + t.string :reset_password_token + t.datetime :reset_password_sent_at + + ## Rememberable + t.datetime :remember_created_at + + ## Trackable + # t.integer :sign_in_count, default: 0, null: false + # t.datetime :current_sign_in_at + # t.datetime :last_sign_in_at + # t.string :current_sign_in_ip + # t.string :last_sign_in_ip + + ## Confirmable + # t.string :confirmation_token + # t.datetime :confirmed_at + # t.datetime :confirmation_sent_at + # t.string :unconfirmed_email # Only if using reconfirmable + + ## Lockable + # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts + # t.string :unlock_token # Only if unlock strategy is :email or :both + # t.datetime :locked_at + + + t.timestamps null: false + end + + add_index :users, :email, unique: true + add_index :users, :reset_password_token, unique: true + # add_index :users, :confirmation_token, unique: true + # add_index :users, :unlock_token, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..ef549ae --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,51 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20181030174359) do + + create_table "admins", force: :cascade do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["email"], name: "index_admins_on_email", unique: true + t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true + end + + create_table "models", force: :cascade do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["email"], name: "index_models_on_email", unique: true + t.index ["reset_password_token"], name: "index_models_on_reset_password_token", unique: true + end + + create_table "users", force: :cascade do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["email"], name: "index_users_on_email", unique: true + t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true + end + +end From a668baa2906bd2926cb0713fb0cc426f0699536d Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Tue, 30 Oct 2018 21:41:04 +0200 Subject: [PATCH 05/23] Revert "rails generate devise admin" This reverts commit 422782c6ac0913fab9d0413a0277d7713dffe333. # Conflicts: # config/routes.rb --- app/models/admin.rb | 6 --- config/routes.rb | 2 +- .../20181030173840_devise_create_admins.rb | 44 ------------------- db/schema.rb | 13 ------ 4 files changed, 1 insertion(+), 64 deletions(-) delete mode 100644 app/models/admin.rb delete mode 100644 db/migrate/20181030173840_devise_create_admins.rb diff --git a/app/models/admin.rb b/app/models/admin.rb deleted file mode 100644 index 7a7be2f..0000000 --- a/app/models/admin.rb +++ /dev/null @@ -1,6 +0,0 @@ -class Admin < ApplicationRecord - # Include default devise modules. Others available are: - # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable - devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :validatable -end diff --git a/config/routes.rb b/config/routes.rb index 06979f1..3cff7d0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ Rails.application.routes.draw do devise_for :users - devise_for :admins + root to: "home#index" # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html diff --git a/db/migrate/20181030173840_devise_create_admins.rb b/db/migrate/20181030173840_devise_create_admins.rb deleted file mode 100644 index 5e61cbb..0000000 --- a/db/migrate/20181030173840_devise_create_admins.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -class DeviseCreateAdmins < ActiveRecord::Migration[5.1] - def change - create_table :admins do |t| - ## Database authenticatable - t.string :email, null: false, default: "" - t.string :encrypted_password, null: false, default: "" - - ## Recoverable - t.string :reset_password_token - t.datetime :reset_password_sent_at - - ## Rememberable - t.datetime :remember_created_at - - ## Trackable - # t.integer :sign_in_count, default: 0, null: false - # t.datetime :current_sign_in_at - # t.datetime :last_sign_in_at - # t.string :current_sign_in_ip - # t.string :last_sign_in_ip - - ## Confirmable - # t.string :confirmation_token - # t.datetime :confirmed_at - # t.datetime :confirmation_sent_at - # t.string :unconfirmed_email # Only if using reconfirmable - - ## Lockable - # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts - # t.string :unlock_token # Only if unlock strategy is :email or :both - # t.datetime :locked_at - - - t.timestamps null: false - end - - add_index :admins, :email, unique: true - add_index :admins, :reset_password_token, unique: true - # add_index :admins, :confirmation_token, unique: true - # add_index :admins, :unlock_token, unique: true - end -end diff --git a/db/schema.rb b/db/schema.rb index ef549ae..1052273 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,19 +11,6 @@ # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema.define(version: 20181030174359) do - - create_table "admins", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" - t.datetime "reset_password_sent_at" - t.datetime "remember_created_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["email"], name: "index_admins_on_email", unique: true - t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true - end - create_table "models", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false From 59db4adb7d89cc1b63a8e08c9cc82fa5fc1927ea Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Tue, 30 Oct 2018 21:59:24 +0200 Subject: [PATCH 06/23] add home controller --- app/controllers/home_controller.rb | 5 +++++ app/views/home/index.erb | 1 + 2 files changed, 6 insertions(+) create mode 100644 app/controllers/home_controller.rb create mode 100644 app/views/home/index.erb diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 0000000..669bb27 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,5 @@ +class HomeController < ApplicationController + def index + end + +end \ No newline at end of file diff --git a/app/views/home/index.erb b/app/views/home/index.erb new file mode 100644 index 0000000..5ab2f8a --- /dev/null +++ b/app/views/home/index.erb @@ -0,0 +1 @@ +Hello \ No newline at end of file From 549314ffc47d4c26fb4e6823ab375b5baa0416d4 Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Tue, 30 Oct 2018 22:08:46 +0200 Subject: [PATCH 07/23] add user roles --- app/models/user.rb | 2 ++ db/migrate/20181030200021_add_role_to_users.rb | 5 +++++ db/schema.rb | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20181030200021_add_role_to_users.rb diff --git a/app/models/user.rb b/app/models/user.rb index 4756799..cdc3e2d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,4 +3,6 @@ class User < ApplicationRecord # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable + + enum role: [:standard, :admin] end diff --git a/db/migrate/20181030200021_add_role_to_users.rb b/db/migrate/20181030200021_add_role_to_users.rb new file mode 100644 index 0000000..c76b51f --- /dev/null +++ b/db/migrate/20181030200021_add_role_to_users.rb @@ -0,0 +1,5 @@ +class AddRoleToUsers < ActiveRecord::Migration[5.1] + def change + add_column :users, :role, :integer, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 1052273..7479ddf 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,20 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20181030174359) do +ActiveRecord::Schema.define(version: 20181030200021) do + + create_table "admins", force: :cascade do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["email"], name: "index_admins_on_email", unique: true + t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true + end + create_table "models", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false @@ -31,6 +44,7 @@ t.datetime "remember_created_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "role", default: 0 t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end From effd56354b6e2101a02af39cfa5ab1909f0f649d Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Wed, 31 Oct 2018 18:16:26 +0200 Subject: [PATCH 08/23] rails g scaffold --- app/assets/javascripts/users.coffee | 3 + app/assets/stylesheets/scaffolds.scss | 84 ++++++++++++++++++++++ app/assets/stylesheets/users.scss | 3 + app/controllers/users_controller.rb | 74 +++++++++++++++++++ app/helpers/users_helper.rb | 2 + app/views/users/_form.html.erb | 42 +++++++++++ app/views/users/_user.json.jbuilder | 2 + app/views/users/edit.html.erb | 6 ++ app/views/users/index.html.erb | 29 ++++++++ app/views/users/index.json.jbuilder | 1 + app/views/users/new.html.erb | 5 ++ app/views/users/show.html.erb | 14 ++++ app/views/users/show.json.jbuilder | 1 + config/routes.rb | 3 +- db/migrate/20181031155448_add_user_name.rb | 5 ++ db/schema.rb | 3 +- 16 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 app/assets/javascripts/users.coffee create mode 100644 app/assets/stylesheets/scaffolds.scss create mode 100644 app/assets/stylesheets/users.scss create mode 100644 app/controllers/users_controller.rb create mode 100644 app/helpers/users_helper.rb create mode 100644 app/views/users/_form.html.erb create mode 100644 app/views/users/_user.json.jbuilder create mode 100644 app/views/users/edit.html.erb create mode 100644 app/views/users/index.html.erb create mode 100644 app/views/users/index.json.jbuilder create mode 100644 app/views/users/new.html.erb create mode 100644 app/views/users/show.html.erb create mode 100644 app/views/users/show.json.jbuilder create mode 100644 db/migrate/20181031155448_add_user_name.rb diff --git a/app/assets/javascripts/users.coffee b/app/assets/javascripts/users.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/users.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/scaffolds.scss b/app/assets/stylesheets/scaffolds.scss new file mode 100644 index 0000000..6045188 --- /dev/null +++ b/app/assets/stylesheets/scaffolds.scss @@ -0,0 +1,84 @@ +body { + background-color: #fff; + color: #333; + margin: 33px; + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { + color: #000; + + &:visited { + color: #666; + } + + &:hover { + color: #fff; + background-color: #000; + } +} + +th { + padding-bottom: 5px; +} + +td { + padding: 0 5px 7px; +} + +div { + &.field, &.actions { + margin-bottom: 10px; + } +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px 7px 0; + margin-bottom: 20px; + background-color: #f0f0f0; + + h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px -7px 0; + background-color: #c00; + color: #fff; + } + + ul li { + font-size: 12px; + list-style: square; + } +} + +label { + display: block; +} diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss new file mode 100644 index 0000000..1efc835 --- /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: http://sass-lang.com/ diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..b680722 --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,74 @@ +class UsersController < ApplicationController + before_action :set_user, only: [:show, :edit, :update, :destroy] + + # GET /users + # GET /users.json + def index + @users = User.all + end + + # GET /users/1 + # GET /users/1.json + def show + end + + # GET /users/new + def new + @user = User.new + end + + # GET /users/1/edit + def edit + end + + # POST /users + # POST /users.json + def create + @user = User.new(user_params) + + respond_to do |format| + if @user.save + format.html { redirect_to @user, notice: 'User was successfully created.' } + format.json { render :show, status: :created, location: @user } + else + format.html { render :new } + format.json { render json: @user.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /users/1 + # PATCH/PUT /users/1.json + def update + respond_to do |format| + if @user.update(user_params) + format.html { redirect_to @user, notice: 'User was successfully updated.' } + format.json { render :show, status: :ok, location: @user } + else + format.html { render :edit } + format.json { render json: @user.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /users/1 + # DELETE /users/1.json + def destroy + @user.destroy + respond_to do |format| + format.html { redirect_to users_url, notice: 'User was successfully destroyed.' } + format.json { head :no_content } + end + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_user + @user = User.find(params[:id]) + end + + # Never trust parameters from the scary internet, only allow the white list through. + def user_params + params.require(:user).permit(:name, :role, :email, :password, :password_confirmation) + end +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..2310a24 --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,2 @@ +module UsersHelper +end diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb new file mode 100644 index 0000000..a6d15dd --- /dev/null +++ b/app/views/users/_form.html.erb @@ -0,0 +1,42 @@ +<%= form_with(model: user, local: true) do |form| %> + <% if user.errors.any? %> +
+

<%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:

+ +
    + <% user.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+ <% end %> + +
+ <%= form.label :name %> + <%= form.text_field :name, id: :user_name %> +
+ +
+ <%= form.label :email %> + <%= form.text_field :email, id: :user_email %> +
+ +
+ <%= form.label :password %> + <%= form.text_field :password, id: :user_password %> +
+ +
+ <%= form.label :password_confirmation %> + <%= form.text_field :password_confirmation, id: :password_confirmation %> +
+ +
+ <%= form.label :role %> + <%= form.text_field :role, id: :user_role %> +
+ +
+ <%= form.submit %> +
+<% end %> diff --git a/app/views/users/_user.json.jbuilder b/app/views/users/_user.json.jbuilder new file mode 100644 index 0000000..235336a --- /dev/null +++ b/app/views/users/_user.json.jbuilder @@ -0,0 +1,2 @@ +json.extract! user, :id, :name, :role, :created_at, :updated_at +json.url user_url(user, format: :json) diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb new file mode 100644 index 0000000..1a5c2a6 --- /dev/null +++ b/app/views/users/edit.html.erb @@ -0,0 +1,6 @@ +

Editing User

+ +<%= render 'form', user: @user %> + +<%= link_to 'Show', @user %> | +<%= link_to 'Back', users_path %> diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb new file mode 100644 index 0000000..20eaa20 --- /dev/null +++ b/app/views/users/index.html.erb @@ -0,0 +1,29 @@ +

<%= notice %>

+ +

Users

+ + + + + + + + + + + + <% @users.each do |user| %> + + + + + + + + <% end %> + +
NameRole
<%= user.name %><%= user.role %><%= link_to 'Show', user %><%= link_to 'Edit', edit_user_path(user) %><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %>
+ +
+ +<%= link_to 'New User', new_user_path %> diff --git a/app/views/users/index.json.jbuilder b/app/views/users/index.json.jbuilder new file mode 100644 index 0000000..2faf5af --- /dev/null +++ b/app/views/users/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @users, partial: 'users/user', as: :user diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb new file mode 100644 index 0000000..844c39b --- /dev/null +++ b/app/views/users/new.html.erb @@ -0,0 +1,5 @@ +

New User

+ +<%= render 'form', user: @user %> + +<%= link_to 'Back', users_path %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb new file mode 100644 index 0000000..5175ca9 --- /dev/null +++ b/app/views/users/show.html.erb @@ -0,0 +1,14 @@ +

<%= notice %>

+ +

+ Name: + <%= @user.name %> +

+ +

+ Role: + <%= @user.role %> +

+ +<%= link_to 'Edit', edit_user_path(@user) %> | +<%= link_to 'Back', users_path %> diff --git a/app/views/users/show.json.jbuilder b/app/views/users/show.json.jbuilder new file mode 100644 index 0000000..ff40bb9 --- /dev/null +++ b/app/views/users/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "users/user", user: @user diff --git a/config/routes.rb b/config/routes.rb index 3cff7d0..4881de3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,7 @@ Rails.application.routes.draw do - devise_for :users + resources :users + #devise_for :users root to: "home#index" diff --git a/db/migrate/20181031155448_add_user_name.rb b/db/migrate/20181031155448_add_user_name.rb new file mode 100644 index 0000000..4dc84fd --- /dev/null +++ b/db/migrate/20181031155448_add_user_name.rb @@ -0,0 +1,5 @@ +class AddUserName < ActiveRecord::Migration[5.1] + def change + add_column :users, :name, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 7479ddf..21cccb6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20181030200021) do +ActiveRecord::Schema.define(version: 20181031155448) do create_table "admins", force: :cascade do |t| t.string "email", default: "", null: false @@ -45,6 +45,7 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "role", default: 0 + t.string "name" t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end From 8d9973c712e3fef3aad9e63d240905be84851777 Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Thu, 1 Nov 2018 19:03:02 +0200 Subject: [PATCH 09/23] configure devise & scaffold logic --- Gemfile | 1 + Gemfile.lock | 5 ++++ app/controllers/users_controller.rb | 16 +++++++++--- app/views/devise/registrations/edit.html.erb | 4 +-- app/views/devise/registrations/new.html.erb | 2 +- app/views/devise/sessions/new.html.erb | 2 +- app/views/devise/shared/_links.html.erb | 10 ++++---- app/views/users/_edit_form.erb | 27 ++++++++++++++++++++ app/views/users/edit.html.erb | 2 +- config/routes.rb | 3 ++- 10 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 app/views/users/_edit_form.erb diff --git a/Gemfile b/Gemfile index b701400..a16ce2a 100644 --- a/Gemfile +++ b/Gemfile @@ -53,3 +53,4 @@ end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem 'devise' +gem 'pry' diff --git a/Gemfile.lock b/Gemfile.lock index d2a6645..bc84940 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -54,6 +54,7 @@ GEM xpath (>= 2.0, < 4.0) childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) + coderay (1.1.2) coffee-rails (4.2.2) coffee-script (>= 2.2.0) railties (>= 4.0.0) @@ -97,6 +98,9 @@ GEM nokogiri (1.8.5) mini_portile2 (~> 2.3.0) orm_adapter (0.5.0) + pry (0.11.3) + coderay (~> 1.1.0) + method_source (~> 0.9.0) public_suffix (3.0.3) puma (3.12.0) rack (2.0.5) @@ -194,6 +198,7 @@ DEPENDENCIES devise jbuilder (~> 2.5) listen (>= 3.0.5, < 3.2) + pry puma (~> 3.7) rails (~> 5.1.5) sass-rails (~> 5.0) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index b680722..5ad02f0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,4 +1,6 @@ class UsersController < ApplicationController + before_action :authenticate_user! + before_action :authenticate_admin! before_action :set_user, only: [:show, :edit, :update, :destroy] # GET /users @@ -24,7 +26,7 @@ def edit # POST /users # POST /users.json def create - @user = User.new(user_params) + @user = User.new(user_params_for_create) respond_to do |format| if @user.save @@ -41,7 +43,7 @@ def create # PATCH/PUT /users/1.json def update respond_to do |format| - if @user.update(user_params) + if @user.update(user_params_for_update) format.html { redirect_to @user, notice: 'User was successfully updated.' } format.json { render :show, status: :ok, location: @user } else @@ -68,7 +70,15 @@ def set_user end # Never trust parameters from the scary internet, only allow the white list through. - def user_params + def user_params_for_create params.require(:user).permit(:name, :role, :email, :password, :password_confirmation) end + + def user_params_for_update + params.require(:user).permit(:name, :role) + end + + def authenticate_admin! + redirect_to (root_path) if current_user.role == "standard" + end end diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index f465532..5054ed0 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -1,6 +1,6 @@

Edit <%= resource_name.to_s.humanize %>

-<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> +<%= form_for(resource, as: resource_name, url: user_registration_path(resource_name), html: { method: :put }) do |f| %> <%= devise_error_messages! %>
@@ -38,6 +38,6 @@

Cancel my account

-

Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>

+

Unhappy? <%= button_to "Cancel my account", user_registration_path(), data: { confirm: "Are you sure?" }, method: :delete %>

<%= link_to "Back", :back %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index 36c9ed7..4cdfd1f 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -1,6 +1,6 @@

Sign up

-<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> +<%= form_for(resource, as: resource_name, url: user_registration_path()) do |f| %> <%= devise_error_messages! %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index e3b9319..3d56c6c 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -1,6 +1,6 @@

Log in

-<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> +<%= form_for(resource, as: resource_name, url: user_session_path(resource_name)) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: "email" %> diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb index e6a3e41..b4c90a9 100644 --- a/app/views/devise/shared/_links.html.erb +++ b/app/views/devise/shared/_links.html.erb @@ -1,21 +1,21 @@ <%- if controller_name != 'sessions' %> - <%= link_to "Log in", new_session_path(resource_name) %>
+ <%= link_to "Log in", new_user_session_path(resource_name) %>
<% end -%> <%- if devise_mapping.registerable? && controller_name != 'registrations' %> - <%= link_to "Sign up", new_registration_path(resource_name) %>
+ <%= link_to "Sign up", new_user_registration_path() %>
<% end -%> <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> - <%= link_to "Forgot your password?", new_password_path(resource_name) %>
+ <%= link_to "Forgot your password?", new_user_password_path(resource_name) %>
<% end -%> <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> - <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+ <%= link_to "Didn't receive confirmation instructions?", new_user_confirmation_path(resource_name) %>
<% end -%> <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> - <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
+ <%= link_to "Didn't receive unlock instructions?", new_user_unlock_path(resource_name) %>
<% end -%> <%- if devise_mapping.omniauthable? %> diff --git a/app/views/users/_edit_form.erb b/app/views/users/_edit_form.erb new file mode 100644 index 0000000..3339522 --- /dev/null +++ b/app/views/users/_edit_form.erb @@ -0,0 +1,27 @@ +<%= form_with(model: user, local: true) do |form| %> + <% if user.errors.any? %> +
+

<%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:

+ +
    + <% user.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+ <% end %> + +
+ <%= form.label :name %> + <%= form.text_field :name, id: :user_name %> +
+ +
+ <%= form.label :role %> + <%= form.text_field :role, id: :user_role %> +
+ +
+ <%= form.submit %> +
+<% end %> diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index 1a5c2a6..de8f6b5 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -1,6 +1,6 @@

Editing User

-<%= render 'form', user: @user %> +<%= render 'edit_form', user: @user %> <%= link_to 'Show', @user %> | <%= link_to 'Back', users_path %> diff --git a/config/routes.rb b/config/routes.rb index 4881de3..54e1de5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,8 @@ Rails.application.routes.draw do + + devise_for :users, path: 'auth' resources :users - #devise_for :users root to: "home#index" From 903b8a5f30eceabc8c900cc2390c40be1972a0e3 Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Thu, 1 Nov 2018 19:32:34 +0200 Subject: [PATCH 10/23] add links for admin --- app/views/devise/sessions/new.html.erb | 2 +- app/views/devise/shared/_links.html.erb | 13 ++++--------- app/views/layouts/application.html.erb | 18 +++++++++++++++--- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 3d56c6c..456fe1b 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -1,6 +1,6 @@

Log in

-<%= form_for(resource, as: resource_name, url: user_session_path(resource_name)) do |f| %> +<%= form_for(resource, as: resource_name, url: user_session_path) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: "email" %> diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb index b4c90a9..5dce6f3 100644 --- a/app/views/devise/shared/_links.html.erb +++ b/app/views/devise/shared/_links.html.erb @@ -1,5 +1,5 @@ <%- if controller_name != 'sessions' %> - <%= link_to "Log in", new_user_session_path(resource_name) %>
+ <%= link_to "Log in", new_user_session_path %>
<% end -%> <%- if devise_mapping.registerable? && controller_name != 'registrations' %> @@ -7,19 +7,14 @@ <% end -%> <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> - <%= link_to "Forgot your password?", new_user_password_path(resource_name) %>
+ <%= link_to "Forgot your password?", new_user_password_path %>
<% end -%> <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> - <%= link_to "Didn't receive confirmation instructions?", new_user_confirmation_path(resource_name) %>
+ <%= link_to "Didn't receive confirmation instructions?", new_user_confirmation_path %>
<% end -%> <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> - <%= link_to "Didn't receive unlock instructions?", new_user_unlock_path(resource_name) %>
+ <%= link_to "Didn't receive unlock instructions?", new_user_unlock_path %>
<% end -%> -<%- if devise_mapping.omniauthable? %> - <%- resource_class.omniauth_providers.each do |provider| %> - <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %>
- <% end -%> -<% end -%> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 48c43cd..671fdc6 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -9,9 +9,21 @@ - -

<%= notice %>

-

<%= alert %>

+ <% if user_signed_in? %> + + <%= current_user.email %> <%= "(#{current_user.role})" if current_user.role =="admin" %> + <%= link_to('users CRUD', users_path) if current_user.role =="admin" %> + +
  • + <%= link_to('Logout', destroy_user_session_path, method: :delete) %> +
  • + <% else %> +
  • + <%= link_to('Login', new_user_session_path) %> +
  • + <% end %> +

    <%= notice %>

    +

    <%= alert %>

    <%= yield %> From 00a787c9694f28cd55c0250b6918473a6e39460c Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Thu, 1 Nov 2018 20:03:31 +0200 Subject: [PATCH 11/23] add dropdown for role. rails part complited --- app/views/users/_edit_form.erb | 9 ++++++++- app/views/users/_form.html.erb | 2 +- app/views/users/show.html.erb | 5 +++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/views/users/_edit_form.erb b/app/views/users/_edit_form.erb index 3339522..4bfef8d 100644 --- a/app/views/users/_edit_form.erb +++ b/app/views/users/_edit_form.erb @@ -11,6 +11,11 @@
    <% end %> +
    + <%= form.label :email %> + <%= form.text_field :email, id: :user_email, disabled: :true%> +
    +
    <%= form.label :name %> <%= form.text_field :name, id: :user_name %> @@ -18,9 +23,11 @@
    <%= form.label :role %> - <%= form.text_field :role, id: :user_role %> + <%= form.select :role, ["standard" , "admin"] %>
    + +
    <%= form.submit %>
    diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb index a6d15dd..adfd67b 100644 --- a/app/views/users/_form.html.erb +++ b/app/views/users/_form.html.erb @@ -33,7 +33,7 @@
    <%= form.label :role %> - <%= form.text_field :role, id: :user_role %> + <%= form.select :role, ["standard" , "admin"] %>
    diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 5175ca9..fa6522c 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -10,5 +10,10 @@ <%= @user.role %>

    +

    + Email: + <%= @user.email %> +

    + <%= link_to 'Edit', edit_user_path(@user) %> | <%= link_to 'Back', users_path %> From 5e0399a4599fd9cfd1396956a0a1eb4821e3088f Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Sat, 3 Nov 2018 21:58:03 +0200 Subject: [PATCH 12/23] add bootstrap, style home-page, add header & footer --- Gemfile | 4 +++ Gemfile.lock | 17 +++++++++++ .../{application.css => application.scss} | 6 ++-- app/assets/stylesheets/main.scss | 12 ++++++++ app/views/home/index.erb | 4 ++- app/views/layouts/application.html.erb | 27 ++++++++++++++---- config/application.rb | 1 + public/home.jpg | Bin 0 -> 63151 bytes 8 files changed, 62 insertions(+), 9 deletions(-) rename app/assets/stylesheets/{application.css => application.scss} (93%) create mode 100644 app/assets/stylesheets/main.scss create mode 100644 public/home.jpg diff --git a/Gemfile b/Gemfile index a16ce2a..07e6de2 100644 --- a/Gemfile +++ b/Gemfile @@ -54,3 +54,7 @@ end gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem 'devise' gem 'pry' + +gem 'bootstrap', '~> 4.1.3' +gem "jquery-rails" +gem 'mini_racer' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index bc84940..d0711cb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,8 +41,14 @@ GEM addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) + autoprefixer-rails (9.3.1) + execjs bcrypt (3.1.12) bindex (0.5.0) + bootstrap (4.1.3) + autoprefixer-rails (>= 6.0.3) + popper_js (>= 1.12.9, < 2) + sass (>= 3.5.2) builder (3.2.3) byebug (10.0.2) capybara (2.18.0) @@ -80,6 +86,11 @@ GEM jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) + jquery-rails (4.3.3) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) + libv8 (6.7.288.46.1) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -92,12 +103,15 @@ GEM method_source (0.9.0) mini_mime (1.0.1) mini_portile2 (2.3.0) + mini_racer (0.2.4) + libv8 (>= 6.3) minitest (5.11.3) multi_json (1.13.1) nio4r (2.3.1) nokogiri (1.8.5) mini_portile2 (~> 2.3.0) orm_adapter (0.5.0) + popper_js (1.14.3) pry (0.11.3) coderay (~> 1.1.0) method_source (~> 0.9.0) @@ -192,12 +206,15 @@ PLATFORMS ruby DEPENDENCIES + bootstrap (~> 4.1.3) byebug capybara (~> 2.13) coffee-rails (~> 4.2) devise jbuilder (~> 2.5) + jquery-rails listen (>= 3.0.5, < 3.2) + mini_racer pry puma (~> 3.7) rails (~> 5.1.5) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.scss similarity index 93% rename from app/assets/stylesheets/application.css rename to app/assets/stylesheets/application.scss index d05ea0f..e70000a 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.scss @@ -10,6 +10,8 @@ * files in this directory. Styles in this file should be added after the last require_* statement. * It is generally better to create a new file per style scope. * - *= require_tree . - *= require_self + //= require_tree . + */ +@import "bootstrap" + diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss new file mode 100644 index 0000000..8ff0036 --- /dev/null +++ b/app/assets/stylesheets/main.scss @@ -0,0 +1,12 @@ +.right { + float: right; + +} +#main{ + min-height: 400px; + padding: 30px; + width: 100%; +} +#home_img{ + text-align: center; +} \ No newline at end of file diff --git a/app/views/home/index.erb b/app/views/home/index.erb index 5ab2f8a..2c3705b 100644 --- a/app/views/home/index.erb +++ b/app/views/home/index.erb @@ -1 +1,3 @@ -Hello \ No newline at end of file +
    + +
    \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 671fdc6..2783eef 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -2,29 +2,44 @@ Lesson3App + <%= csrf_meta_tags %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + - <% if user_signed_in? %> - + + <% if user_signed_in? %> + <% else %>
  • <%= link_to('Login', new_user_session_path) %>
  • <% end %> + +

    <%= notice %>

    <%= alert %>

    +
    <%= yield %> +
    + +
    +

    © Liubov Tarasenko. GeekHub 2018

    +
    diff --git a/config/application.rb b/config/application.rb index 184390b..f1dbd05 100644 --- a/config/application.rb +++ b/config/application.rb @@ -10,6 +10,7 @@ module Lesson3App class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.1 + config.assets.precompile += %w( main.css ) # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers diff --git a/public/home.jpg b/public/home.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ad54c48edb4bd4b5345e18a061f10c86a5f16c5e GIT binary patch literal 63151 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 literal 0 HcmV?d00001 From a97207fd299df96bfd94c196cb99d8a61bf5fb7f Mon Sep 17 00:00:00 2001 From: lu Date: Sat, 3 Nov 2018 22:09:04 +0200 Subject: [PATCH 13/23] add screenshot home page --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8e0fd8f..0395cc0 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,4 @@ Users managament - Admin should be able to CRUD other users and admin (scaffold) - User with role `user` should be able to see only welcome page - Use device gem for authentication +![Home page](https://preview.ibb.co/c8rF3L/1.png) From 8a7e5666826916a6590c1b84d0c8439739cfcbe4 Mon Sep 17 00:00:00 2001 From: lu Date: Sat, 3 Nov 2018 22:34:18 +0200 Subject: [PATCH 14/23] add CRUD screen --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 0395cc0..cd4373b 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,9 @@ Users managament - Admin should be able to CRUD other users and admin (scaffold) - User with role `user` should be able to see only welcome page - Use device gem for authentication + +# Home page ![Home page](https://preview.ibb.co/c8rF3L/1.png) + +# CRUD page +![CRUD page](https://preview.ibb.co/mRG1Sf/2.png) From e1aa6aae124258f811e1a682b6a965c90caf1d15 Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Sat, 3 Nov 2018 22:35:44 +0200 Subject: [PATCH 15/23] add style on CRUD page --- app/assets/stylesheets/main.scss | 3 +++ app/views/layouts/application.html.erb | 11 +++++------ app/views/users/index.html.erb | 12 +++++++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 8ff0036..4fccc32 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -9,4 +9,7 @@ } #home_img{ text-align: center; +} +.create{ + border: 2px solid #ffafce !important; } \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 2783eef..776757e 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -11,9 +11,9 @@ - +

    <%= notice %>

    diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 20eaa20..1e92aac 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -2,18 +2,20 @@

    Users

    - +
    - - - + + + + <% @users.each do |user| %> + @@ -26,4 +28,4 @@
    -<%= link_to 'New User', new_user_path %> +<%= link_to 'New User', new_user_path , class: "btn create"%> From 34162b1f2e3a0d90d55fdc7477475916a1ca3c51 Mon Sep 17 00:00:00 2001 From: lu Date: Sat, 3 Nov 2018 22:55:59 +0200 Subject: [PATCH 16/23] write about difficulties --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index cd4373b..41c28c5 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,23 @@ Users managament - User with role `user` should be able to see only welcome page - Use device gem for authentication +# Difficulties + логичный конфликт devise vs scaffold сгенерировались одни и те же модели. нужно было переименовать models/user.rb и + закоментировавать в routes.rb "devise_for :users" иначе scaffold не добавлялся и вылетал с ошибкой (потом вернула обратно + , удалила юзера созданого скаффолдом). + + совпали некоторые роуты девайса и скаффолда (users/sign_in и users/:id) пришлось добавить кастомный path("auth") для + роутов девайса, дальше попрвить хелперы урлов девайса (помогали ошибки при рендере). + + переписала миграцию скаффолда, так, чтобы оно добавляла одно только поле в уже созданную девайсом таблицу (скаффолд + предлагал свою таблицу юзеров). + + было бы легче использовать только девайс, а CRUD методы сделать самой (но делала по заданию). + + при подключении gem bootstrap выпала ошибка "Autoprefixer doesn’t support Node v4.2.6. Update it." добавила gem "mini-racer". + + + # Home page ![Home page](https://preview.ibb.co/c8rF3L/1.png) From 98bcc654a99f636153acb6c17b881fd491a54955 Mon Sep 17 00:00:00 2001 From: lu Date: Sat, 3 Nov 2018 23:20:39 +0200 Subject: [PATCH 17/23] add create user page screenshot --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 41c28c5..755b0a2 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,6 @@ Users managament # CRUD page ![CRUD page](https://preview.ibb.co/mRG1Sf/2.png) + +# Create user page +![Create user page](https://preview.ibb.co/k4037f/3.png) From 42960dd6641d22c03bb93a17ac2c2d45a14bdbb6 Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Sat, 3 Nov 2018 23:22:02 +0200 Subject: [PATCH 18/23] some style --- app/assets/stylesheets/main.scss | 9 +++++++++ app/views/users/_form.html.erb | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 4fccc32..1fa7c0e 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -12,4 +12,13 @@ } .create{ border: 2px solid #ffafce !important; +} +form label{ + width: 150px; + text-align: left; +} +select{ + background-color: lightgray; + padding: 4px; + width: 195px; } \ No newline at end of file diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb index adfd67b..45e3235 100644 --- a/app/views/users/_form.html.erb +++ b/app/views/users/_form.html.erb @@ -33,10 +33,10 @@
    <%= form.label :role %> - <%= form.select :role, ["standard" , "admin"] %> + <%= form.select :role, ["standard" , "admin"]%>
    - <%= form.submit %> + <%= form.submit class: "btn btn-primary" %>
    <% end %> From be76b7689ace101c47cf44ef7bce35fbfc44242a Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Sat, 3 Nov 2018 23:28:11 +0200 Subject: [PATCH 19/23] style links --- app/assets/stylesheets/main.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 1fa7c0e..bc26b86 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -21,4 +21,9 @@ select{ background-color: lightgray; padding: 4px; width: 195px; +} + + +a:hover{ + background: none !important; } \ No newline at end of file From 481ff6aa8c5240f37a838ac8a5f8836dad039eed Mon Sep 17 00:00:00 2001 From: lu Date: Sun, 4 Nov 2018 21:40:00 +0200 Subject: [PATCH 20/23] update CRUD page screenshot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 755b0a2..aa65f5c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Users managament ![Home page](https://preview.ibb.co/c8rF3L/1.png) # CRUD page -![CRUD page](https://preview.ibb.co/mRG1Sf/2.png) +![CRUD page](https://preview.ibb.co/hmSZhf/4.png) # Create user page ![Create user page](https://preview.ibb.co/k4037f/3.png) From 0fafe74ea3b16fd9913b7e7ed268e8a61c916752 Mon Sep 17 00:00:00 2001 From: lu Date: Sun, 4 Nov 2018 21:54:57 +0200 Subject: [PATCH 21/23] add edit user page screenshot --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index aa65f5c..a9778bd 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,6 @@ Users managament # Create user page ![Create user page](https://preview.ibb.co/k4037f/3.png) + +# Edit user page +![edit user page](https://preview.ibb.co/nm29hf/5.png) From 9287d1d76a1abb1ad16fc547261d40e51b14eaf2 Mon Sep 17 00:00:00 2001 From: lu Date: Mon, 5 Nov 2018 15:24:01 +0200 Subject: [PATCH 22/23] add show user page screenshot, update screenshots --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a9778bd..38fd0e9 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,13 @@ Users managament ![Home page](https://preview.ibb.co/c8rF3L/1.png) # CRUD page -![CRUD page](https://preview.ibb.co/hmSZhf/4.png) +![CRUD page](https://preview.ibb.co/ejyZYL/9.png) # Create user page -![Create user page](https://preview.ibb.co/k4037f/3.png) +![Create user page](https://preview.ibb.co/hcuV7f/12.png) # Edit user page -![edit user page](https://preview.ibb.co/nm29hf/5.png) +![edit user page](https://preview.ibb.co/gnaWDL/11.png) + +# Show user page +![show user page](https://preview.ibb.co/dC6cnf/10.png) From 7767b4321a392f059dffad6710028a938f991456 Mon Sep 17 00:00:00 2001 From: "luba.t" Date: Mon, 5 Nov 2018 15:24:40 +0200 Subject: [PATCH 23/23] style --- Gemfile | 13 ++-- app/assets/javascripts/application.js | 3 + app/assets/stylesheets/main.scss | 36 +++++++++- app/controllers/home_controller.rb | 6 +- app/controllers/users_controller.rb | 2 +- app/models/user.rb | 2 +- app/views/layouts/application.html.erb | 2 +- app/views/users/_edit_form.erb | 2 +- app/views/users/index.html.erb | 6 +- app/views/users/show.html.erb | 29 ++++---- app/views/users/show.json.jbuilder | 2 +- bin/setup | 1 - bin/spring | 2 +- bin/yarn | 6 +- config/application.rb | 2 +- config/environments/development.rb | 1 - config/environments/production.rb | 4 +- config/initializers/devise.rb | 2 +- config/puma.rb | 6 +- config/routes.rb | 6 +- config/spring.rb | 4 +- .../20181030174359_devise_create_users.rb | 5 +- db/schema.rb | 68 +++++++++---------- test/application_system_test_case.rb | 2 +- 24 files changed, 118 insertions(+), 94 deletions(-) diff --git a/Gemfile b/Gemfile index 07e6de2..bd6072a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,11 +1,10 @@ source 'https://rubygems.org' git_source(:github) do |repo_name| - repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") + repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/') "https://github.com/#{repo_name}.git" end - # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.1.5' # Use sqlite3 as the database for Active Record @@ -35,7 +34,7 @@ gem 'jbuilder', '~> 2.5' 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] # Adds support for Capybara system testing and selenium driver gem 'capybara', '~> 2.13' gem 'selenium-webdriver' @@ -43,18 +42,18 @@ end group :development do # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. - gem 'web-console', '>= 3.3.0' gem 'listen', '>= 3.0.5', '< 3.2' + gem 'web-console', '>= 3.3.0' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem 'devise' gem 'pry' +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] gem 'bootstrap', '~> 4.1.3' -gem "jquery-rails" -gem 'mini_racer' \ No newline at end of file +gem 'jquery-rails' +gem 'mini_racer' diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 46b2035..208db61 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,3 +13,6 @@ //= require rails-ujs //= require turbolinks //= require_tree . +//= require jquery3 +//= require popper +//= require bootstrap diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index bc26b86..121d3f6 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -26,4 +26,38 @@ select{ a:hover{ background: none !important; -} \ No newline at end of file +} + +table a, table a:visited{ + color: #ff00bf; +} + + + #center_th{ + text-align: center; + } + #show_tb{ + border:2px solid #ffafce; + width: 800px; + margin-bottom: 50px; + th{ + background:rgba(255, 198, 198, 0.38); + border-right:2px solid #ffafce; + } + } + + form{ + margin-bottom: 50px; + padding-top: 30px; + } + +#index_tb{ + border:2px solid #ffafce; + margin-bottom: 50px; + +} +#index_tb th{ + background:rgba(255, 198, 198, 0.38); + border-bottom:2px solid #ffafce; +} + diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 669bb27..6d3fe90 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,5 +1,3 @@ class HomeController < ApplicationController - def index - end - -end \ No newline at end of file + def index; end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 5ad02f0..316f90d 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -79,6 +79,6 @@ def user_params_for_update end def authenticate_admin! - redirect_to (root_path) if current_user.role == "standard" + redirect_to (root_path) if current_user.role == "standard" end end diff --git a/app/models/user.rb b/app/models/user.rb index cdc3e2d..b7764a6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -4,5 +4,5 @@ class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable - enum role: [:standard, :admin] + enum role: %i[standard admin] end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 776757e..58b7f32 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -16,7 +16,7 @@ <%= current_user.email %> <%= "(#{current_user.role})" if current_user.role =="admin" %>
    - <%= link_to('users CRUD', users_path) if current_user.role =="admin" %> + <%= link_to('Users CRUD', users_path) if current_user.role =="admin" %> | <%= link_to('Logout', destroy_user_session_path, method: :delete) %> | diff --git a/app/views/users/_edit_form.erb b/app/views/users/_edit_form.erb index 4bfef8d..8e92df4 100644 --- a/app/views/users/_edit_form.erb +++ b/app/views/users/_edit_form.erb @@ -29,6 +29,6 @@
    - <%= form.submit %> + <%= form.submit class: "btn btn-primary"%>
    <% end %> diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 1e92aac..ec41dad 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -1,14 +1,12 @@ -

    <%= notice %>

    -

    Users

    -
    NameRoleEmailNameRoleActions
    <%= user.email %> <%= user.name %> <%= user.role %> <%= link_to 'Show', user %>
    +
    - + diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index fa6522c..0b42064 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -1,19 +1,18 @@ -

    <%= notice %>

    -

    - Name: - <%= @user.name %> -

    - -

    - Role: - <%= @user.role %> -

    - -

    - Email: - <%= @user.email %> -

    +
    Email Name RoleActionsActions
    + + + + + + + + + + + + +
    Name: <%= @user.name %>
    Role:<%= @user.role %>
    Email:<%= @user.email %>
    <%= link_to 'Edit', edit_user_path(@user) %> | <%= link_to 'Back', users_path %> diff --git a/app/views/users/show.json.jbuilder b/app/views/users/show.json.jbuilder index ff40bb9..2a33f71 100644 --- a/app/views/users/show.json.jbuilder +++ b/app/views/users/show.json.jbuilder @@ -1 +1 @@ -json.partial! "users/user", user: @user +json.partial! 'users/user', user: @user diff --git a/bin/setup b/bin/setup index 78c4e86..ca842c1 100755 --- a/bin/setup +++ b/bin/setup @@ -21,7 +21,6 @@ chdir APP_ROOT do # Install JavaScript dependencies if using Yarn # system('bin/yarn') - # puts "\n== Copying sample files ==" # unless File.exist?('config/database.yml') # cp 'config/database.yml.sample', 'config/database.yml' diff --git a/bin/spring b/bin/spring index fb2ec2e..991bd4e 100755 --- a/bin/spring +++ b/bin/spring @@ -8,7 +8,7 @@ unless defined?(Spring) require 'bundler' lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) - spring = lockfile.specs.detect { |spec| spec.name == "spring" } + spring = lockfile.specs.detect { |spec| spec.name == 'spring' } if spring Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path gem 'spring', spring.version diff --git a/bin/yarn b/bin/yarn index c2bacef..329aee9 100755 --- a/bin/yarn +++ b/bin/yarn @@ -2,10 +2,10 @@ VENDOR_PATH = File.expand_path('..', __dir__) Dir.chdir(VENDOR_PATH) do begin - exec "yarnpkg #{ARGV.join(" ")}" + exec "yarnpkg #{ARGV.join(' ')}" rescue Errno::ENOENT - $stderr.puts "Yarn executable was not detected in the system." - $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + $stderr.puts 'Yarn executable was not detected in the system.' + $stderr.puts 'Download Yarn at https://yarnpkg.com/en/docs/install' exit 1 end end diff --git a/config/application.rb b/config/application.rb index f1dbd05..513f120 100644 --- a/config/application.rb +++ b/config/application.rb @@ -10,7 +10,7 @@ module Lesson3App class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.1 - config.assets.precompile += %w( main.css ) + config.assets.precompile += %w[main.css] # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers diff --git a/config/environments/development.rb b/config/environments/development.rb index 3d3b06d..3f603da 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -45,7 +45,6 @@ # Suppress logger output for asset requests. config.assets.quiet = true - config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } # Raises error for missing translations diff --git a/config/environments/production.rb b/config/environments/production.rb index 20be5fd..1b09a1a 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -52,7 +52,7 @@ config.log_level = :debug # 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 @@ -80,7 +80,7 @@ # require 'syslog/logger' # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') - if ENV["RAILS_LOG_TO_STDOUT"].present? + if ENV['RAILS_LOG_TO_STDOUT'].present? logger = ActiveSupport::Logger.new(STDOUT) logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index c381c3a..c409b09 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -9,7 +9,7 @@ # Devise will use the `secret_key_base` as its `secret_key` # by default. You can change it below and use your own secret key. # config.secret_key = '8016b9f668f531ac9dec62b371eb97e8f2f7c4b71b9fc3f1f034411f28978d1301385144d18faa65850a1685074031dd0871e133dc2e539fbef5f26ff8c5eb7c' - + # ==> Controller configuration # Configure the parent class to the devise controllers. # config.parent_controller = 'DeviseController' diff --git a/config/puma.rb b/config/puma.rb index 1e19380..ccda173 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -4,16 +4,16 @@ # 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. # -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 } threads threads_count, threads_count # 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 number of `workers` to boot in clustered mode. # Workers are forked webserver processes. If using threads and workers together diff --git a/config/routes.rb b/config/routes.rb index 54e1de5..a8c33e1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,10 +1,8 @@ Rails.application.routes.draw do - - devise_for :users, path: 'auth' resources :users - - root to: "home#index" + + root to: 'home#index' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end diff --git a/config/spring.rb b/config/spring.rb index c9119b4..9fa7863 100644 --- a/config/spring.rb +++ b/config/spring.rb @@ -1,6 +1,6 @@ -%w( +%w[ .ruby-version .rbenv-vars tmp/restart.txt tmp/caching-dev.txt -).each { |path| Spring.watch(path) } +].each { |path| Spring.watch(path) } diff --git a/db/migrate/20181030174359_devise_create_users.rb b/db/migrate/20181030174359_devise_create_users.rb index b6e5c6e..5b6336f 100644 --- a/db/migrate/20181030174359_devise_create_users.rb +++ b/db/migrate/20181030174359_devise_create_users.rb @@ -4,8 +4,8 @@ class DeviseCreateUsers < ActiveRecord::Migration[5.1] def change create_table :users do |t| ## Database authenticatable - t.string :email, null: false, default: "" - t.string :encrypted_password, null: false, default: "" + t.string :email, null: false, default: '' + t.string :encrypted_password, null: false, default: '' ## Recoverable t.string :reset_password_token @@ -32,7 +32,6 @@ def change # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at - t.timestamps null: false end diff --git a/db/schema.rb b/db/schema.rb index 21cccb6..6d2bb02 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,44 +10,42 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20181031155448) do - - create_table "admins", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" - t.datetime "reset_password_sent_at" - t.datetime "remember_created_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["email"], name: "index_admins_on_email", unique: true - t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true +ActiveRecord::Schema.define(version: 20_181_031_155_448) do + create_table 'admins', force: :cascade do |t| + t.string 'email', default: '', null: false + t.string 'encrypted_password', default: '', null: false + t.string 'reset_password_token' + t.datetime 'reset_password_sent_at' + t.datetime 'remember_created_at' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + t.index ['email'], name: 'index_admins_on_email', unique: true + t.index ['reset_password_token'], name: 'index_admins_on_reset_password_token', unique: true end - create_table "models", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" - t.datetime "reset_password_sent_at" - t.datetime "remember_created_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["email"], name: "index_models_on_email", unique: true - t.index ["reset_password_token"], name: "index_models_on_reset_password_token", unique: true + create_table 'models', force: :cascade do |t| + t.string 'email', default: '', null: false + t.string 'encrypted_password', default: '', null: false + t.string 'reset_password_token' + t.datetime 'reset_password_sent_at' + t.datetime 'remember_created_at' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + t.index ['email'], name: 'index_models_on_email', unique: true + t.index ['reset_password_token'], name: 'index_models_on_reset_password_token', unique: true end - create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" - t.datetime "reset_password_sent_at" - t.datetime "remember_created_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "role", default: 0 - t.string "name" - t.index ["email"], name: "index_users_on_email", unique: true - t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true + create_table 'users', force: :cascade do |t| + t.string 'email', default: '', null: false + t.string 'encrypted_password', default: '', null: false + t.string 'reset_password_token' + t.datetime 'reset_password_sent_at' + t.datetime 'remember_created_at' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + t.integer 'role', default: 0 + t.string 'name' + t.index ['email'], name: 'index_users_on_email', unique: true + t.index ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true end - end diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb index d19212a..23701b4 100644 --- a/test/application_system_test_case.rb +++ b/test/application_system_test_case.rb @@ -1,4 +1,4 @@ -require "test_helper" +require 'test_helper' class ApplicationSystemTestCase < ActionDispatch::SystemTestCase driven_by :selenium, using: :chrome, screen_size: [1400, 1400]