From f50aa6d05a3bdab4e103ea18c1fb77e2eeb21f08 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Mon, 18 Sep 2023 22:55:06 +0300 Subject: [PATCH 01/54] create a new rails app --- .gitattributes | 7 + .gitignore | 33 +++ .ruby-version | 1 + Gemfile | 72 ++++++ Gemfile.lock | 237 ++++++++++++++++++ README.md | 24 ++ Rakefile | 6 + app/assets/config/manifest.js | 4 + app/assets/images/.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 | 2 + app/controllers/concerns/.keep | 0 app/helpers/application_helper.rb | 2 + app/javascript/application.js | 3 + app/javascript/controllers/application.js | 9 + .../controllers/hello_controller.js | 7 + app/javascript/controllers/index.js | 11 + app/jobs/application_job.rb | 7 + app/mailers/application_mailer.rb | 4 + app/models/application_record.rb | 3 + app/models/concerns/.keep | 0 app/views/layouts/application.html.erb | 16 ++ app/views/layouts/mailer.html.erb | 13 + app/views/layouts/mailer.text.erb | 1 + bin/bundle | 109 ++++++++ bin/bundle.cmd | 112 +++++++++ bin/importmap | 4 + bin/rails | 4 + bin/rake | 4 + bin/setup | 33 +++ config.ru | 6 + config/application.rb | 22 ++ config/boot.rb | 4 + config/cable.yml | 10 + config/credentials.yml.enc | 1 + config/database.yml | 86 +++++++ config/environment.rb | 5 + config/environments/development.rb | 70 ++++++ config/environments/production.rb | 93 +++++++ config/environments/test.rb | 60 +++++ config/importmap.rb | 7 + config/initializers/assets.rb | 12 + .../initializers/content_security_policy.rb | 25 ++ .../initializers/filter_parameter_logging.rb | 8 + config/initializers/inflections.rb | 16 ++ config/initializers/permissions_policy.rb | 11 + config/locales/en.yml | 33 +++ config/puma.rb | 43 ++++ config/routes.rb | 6 + config/storage.yml | 34 +++ db/seeds.rb | 7 + lib/assets/.keep | 0 lib/tasks/.keep | 0 log/.keep | 0 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 + storage/.keep | 0 test/application_system_test_case.rb | 5 + .../application_cable/connection_test.rb | 11 + test/controllers/.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 | 13 + tmp/.keep | 0 tmp/pids/.keep | 0 tmp/storage/.keep | 0 vendor/.keep | 0 vendor/javascript/.keep | 0 79 files changed, 1540 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .ruby-version create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 README.md create mode 100644 Rakefile create mode 100644 app/assets/config/manifest.js create mode 100644 app/assets/images/.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/javascript/application.js create mode 100644 app/javascript/controllers/application.js create mode 100644 app/javascript/controllers/hello_controller.js create mode 100644 app/javascript/controllers/index.js 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 100644 bin/bundle create mode 100644 bin/bundle.cmd create mode 100644 bin/importmap create mode 100644 bin/rails create mode 100644 bin/rake create mode 100644 bin/setup 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/credentials.yml.enc 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/importmap.rb create mode 100644 config/initializers/assets.rb create mode 100644 config/initializers/content_security_policy.rb create mode 100644 config/initializers/filter_parameter_logging.rb create mode 100644 config/initializers/inflections.rb create mode 100644 config/initializers/permissions_policy.rb create mode 100644 config/locales/en.yml create mode 100644 config/puma.rb create mode 100644 config/routes.rb create mode 100644 config/storage.yml 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 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 storage/.keep create mode 100644 test/application_system_test_case.rb create mode 100644 test/channels/application_cable/connection_test.rb create mode 100644 test/controllers/.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 tmp/pids/.keep create mode 100644 tmp/storage/.keep create mode 100644 vendor/.keep create mode 100644 vendor/javascript/.keep diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..31eeee0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# See https://git-scm.com/docs/gitattributes for more about git attribute files. + +# Mark the database schema as having been generated. +db/schema.rb linguist-generated + +# Mark any vendored files as having been vendored. +vendor/* linguist-vendored diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0908b0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# 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 all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/ +!/tmp/pids/.keep + +# Ignore uploaded files in development. +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/ +!/tmp/storage/.keep + +/public/assets + +# Ignore master key for decrypting credentials and more. +/config/master.key + +/node_modules diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..9e79f6c --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-3.2.2 diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..b559af4 --- /dev/null +++ b/Gemfile @@ -0,0 +1,72 @@ +source "https://rubygems.org" +git_source(:github) { |repo| "https://github.com/#{repo}.git" } + +ruby "3.2.2" + +# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" +gem "rails", "~> 7.0.8" + +# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] +gem "sprockets-rails" + +# Use postgresql as the database for Active Record +gem "pg", "~> 1.1" + +# Use the Puma web server [https://github.com/puma/puma] +gem "puma", "~> 5.0" + +# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails] +gem "importmap-rails" + +# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev] +gem "turbo-rails" + +# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] +gem "stimulus-rails" + +# Build JSON APIs with ease [https://github.com/rails/jbuilder] +gem "jbuilder" + +# Use Redis adapter to run Action Cable in production +# gem "redis", "~> 4.0" + +# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] +# gem "kredis" + +# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] +# gem "bcrypt", "~> 3.1.7" + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] + +# Reduces boot times through caching; required in config/boot.rb +gem "bootsnap", require: false + +# Use Sass to process CSS +# gem "sassc-rails" + +# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] +# gem "image_processing", "~> 1.2" + +group :development, :test do + # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem + gem "debug", platforms: %i[ mri mingw x64_mingw ] +end + +group :development do + # Use console on exceptions pages [https://github.com/rails/web-console] + gem "web-console" + + # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler] + # gem "rack-mini-profiler" + + # Speed up commands on slow machines / big apps [https://github.com/rails/spring] + # gem "spring" +end + +group :test do + # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] + gem "capybara" + gem "selenium-webdriver" + +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..456249c --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,237 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (7.0.8) + actionpack (= 7.0.8) + activesupport (= 7.0.8) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailbox (7.0.8) + actionpack (= 7.0.8) + activejob (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) + mail (>= 2.7.1) + net-imap + net-pop + net-smtp + actionmailer (7.0.8) + actionpack (= 7.0.8) + actionview (= 7.0.8) + activejob (= 7.0.8) + activesupport (= 7.0.8) + mail (~> 2.5, >= 2.5.4) + net-imap + net-pop + net-smtp + rails-dom-testing (~> 2.0) + actionpack (7.0.8) + actionview (= 7.0.8) + activesupport (= 7.0.8) + rack (~> 2.0, >= 2.2.4) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (7.0.8) + actionpack (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.0.8) + activesupport (= 7.0.8) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + activejob (7.0.8) + activesupport (= 7.0.8) + globalid (>= 0.3.6) + activemodel (7.0.8) + activesupport (= 7.0.8) + activerecord (7.0.8) + activemodel (= 7.0.8) + activesupport (= 7.0.8) + activestorage (7.0.8) + actionpack (= 7.0.8) + activejob (= 7.0.8) + activerecord (= 7.0.8) + activesupport (= 7.0.8) + marcel (~> 1.0) + mini_mime (>= 1.1.0) + activesupport (7.0.8) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + addressable (2.8.5) + public_suffix (>= 2.0.2, < 6.0) + bindex (0.8.1) + bootsnap (1.16.0) + msgpack (~> 1.2) + builder (3.2.4) + capybara (3.39.2) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) + concurrent-ruby (1.2.2) + crass (1.0.6) + date (3.3.3) + debug (1.8.0) + irb (>= 1.5.0) + reline (>= 0.3.1) + erubi (1.12.0) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + importmap-rails (1.2.1) + actionpack (>= 6.0.0) + railties (>= 6.0.0) + io-console (0.6.0) + irb (1.8.1) + rdoc + reline (>= 0.3.8) + jbuilder (2.11.5) + actionview (>= 5.0.0) + activesupport (>= 5.0.0) + loofah (2.21.3) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.2) + matrix (0.4.2) + method_source (1.0.0) + mini_mime (1.1.5) + minitest (5.20.0) + msgpack (1.7.2) + net-imap (0.3.7) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.1) + timeout + net-smtp (0.3.3) + net-protocol + nio4r (2.5.9) + nokogiri (1.15.4-x64-mingw-ucrt) + racc (~> 1.4) + pg (1.5.4-x64-mingw-ucrt) + psych (5.1.0) + stringio + public_suffix (5.0.3) + puma (5.6.7) + nio4r (~> 2.0) + racc (1.7.1) + rack (2.2.8) + rack-test (2.1.0) + rack (>= 1.3) + rails (7.0.8) + actioncable (= 7.0.8) + actionmailbox (= 7.0.8) + actionmailer (= 7.0.8) + actionpack (= 7.0.8) + actiontext (= 7.0.8) + actionview (= 7.0.8) + activejob (= 7.0.8) + activemodel (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) + bundler (>= 1.15.0) + railties (= 7.0.8) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.0.8) + actionpack (= 7.0.8) + activesupport (= 7.0.8) + method_source + rake (>= 12.2) + thor (~> 1.0) + zeitwerk (~> 2.5) + rake (13.0.6) + rdoc (6.5.0) + psych (>= 4.0.0) + regexp_parser (2.8.1) + reline (0.3.8) + io-console (~> 0.5) + rexml (3.2.6) + rubyzip (2.3.2) + selenium-webdriver (4.12.0) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + sprockets (4.2.1) + concurrent-ruby (~> 1.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.4.2) + actionpack (>= 5.2) + activesupport (>= 5.2) + sprockets (>= 3.0.0) + stimulus-rails (1.2.2) + railties (>= 6.0.0) + stringio (3.0.8) + thor (1.2.2) + timeout (0.4.0) + turbo-rails (1.4.0) + actionpack (>= 6.0.0) + activejob (>= 6.0.0) + railties (>= 6.0.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + tzinfo-data (1.2023.3) + tzinfo (>= 1.0.0) + web-console (4.2.1) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) + bindex (>= 0.4.0) + railties (>= 6.0.0) + websocket (1.2.9) + websocket-driver (0.7.6) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) + zeitwerk (2.6.11) + +PLATFORMS + x64-mingw-ucrt + +DEPENDENCIES + bootsnap + capybara + debug + importmap-rails + jbuilder + pg (~> 1.1) + puma (~> 5.0) + rails (~> 7.0.8) + selenium-webdriver + sprockets-rails + stimulus-rails + turbo-rails + tzinfo-data + web-console + +RUBY VERSION + ruby 3.2.2p53 + +BUNDLED WITH + 2.4.19 diff --git a/README.md b/README.md new file mode 100644 index 0000000..7db80e4 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# README + +This README would normally document whatever steps are necessary to get the +application up and running. + +Things you may want to cover: + +* Ruby version + +* System dependencies + +* Configuration + +* Database creation + +* Database initialization + +* How to run the test suite + +* Services (job queues, cache servers, search engines, etc.) + +* Deployment instructions + +* ... diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..9a5ea73 --- /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..ddd546a --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,4 @@ +//= link_tree ../images +//= link_directory ../stylesheets .css +//= link_tree ../../javascript .js +//= link_tree ../../../vendor/javascript .js diff --git a/app/assets/images/.keep b/app/assets/images/.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..288b9ab --- /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, if configured) 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 + * 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..09705d1 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,2 @@ +class ApplicationController < ActionController::Base +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/javascript/application.js b/app/javascript/application.js new file mode 100644 index 0000000..0d7b494 --- /dev/null +++ b/app/javascript/application.js @@ -0,0 +1,3 @@ +// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails +import "@hotwired/turbo-rails" +import "controllers" diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js new file mode 100644 index 0000000..1213e85 --- /dev/null +++ b/app/javascript/controllers/application.js @@ -0,0 +1,9 @@ +import { Application } from "@hotwired/stimulus" + +const application = Application.start() + +// Configure Stimulus development experience +application.debug = false +window.Stimulus = application + +export { application } diff --git a/app/javascript/controllers/hello_controller.js b/app/javascript/controllers/hello_controller.js new file mode 100644 index 0000000..5975c07 --- /dev/null +++ b/app/javascript/controllers/hello_controller.js @@ -0,0 +1,7 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + connect() { + this.element.textContent = "Hello World!" + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js new file mode 100644 index 0000000..54ad4ca --- /dev/null +++ b/app/javascript/controllers/index.js @@ -0,0 +1,11 @@ +// Import and register all your controllers from the importmap under controllers/* + +import { application } from "controllers/application" + +// Eager load all controllers defined in the import map under controllers/**/*_controller +import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading" +eagerLoadControllersFrom("controllers", application) + +// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!) +// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading" +// lazyLoadControllersFrom("controllers", application) diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 0000000..d394c3d --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,7 @@ +class ApplicationJob < ActiveJob::Base + # Automatically retry jobs that encountered a deadlock + # retry_on ActiveRecord::Deadlocked + + # Most jobs are safe to ignore if the underlying records are no longer available + # discard_on ActiveJob::DeserializationError +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 0000000..3c34c81 --- /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..b63caeb --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + primary_abstract_class +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..e586610 --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,16 @@ + + + + RailsBudgetAppV2 + + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> + <%= javascript_importmap_tags %> + + + + <%= 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 100644 index 0000000..42c7fd7 --- /dev/null +++ b/bin/bundle @@ -0,0 +1,109 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundle' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "rubygems" + +m = Module.new do + module_function + + def invoked_as_script? + File.expand_path($0) == File.expand_path(__FILE__) + end + + def env_var_version + ENV["BUNDLER_VERSION"] + end + + def cli_arg_version + return unless invoked_as_script? # don't want to hijack other binstubs + return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + bundler_version = nil + update_index = nil + ARGV.each_with_index do |a, i| + if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN + bundler_version = a + end + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ + bundler_version = $1 + update_index = i + end + bundler_version + end + + def gemfile + gemfile = ENV["BUNDLE_GEMFILE"] + return gemfile if gemfile && !gemfile.empty? + + File.expand_path("../Gemfile", __dir__) + end + + def lockfile + lockfile = + case File.basename(gemfile) + when "gems.rb" then gemfile.sub(/\.rb$/, ".locked") + else "#{gemfile}.lock" + end + File.expand_path(lockfile) + end + + def lockfile_version + return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) + end + + def bundler_requirement + @bundler_requirement ||= + env_var_version || + cli_arg_version || + bundler_requirement_for(lockfile_version) + end + + def bundler_requirement_for(version) + return "#{Gem::Requirement.default}.a" unless version + + bundler_gem_version = Gem::Version.new(version) + + bundler_gem_version.approximate_recommendation + end + + def load_bundler! + ENV["BUNDLE_GEMFILE"] ||= gemfile + + activate_bundler + end + + def activate_bundler + gem_error = activation_error_handling do + gem "bundler", bundler_requirement + end + return if gem_error.nil? + require_error = activation_error_handling do + require "bundler/version" + end + return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" + exit 42 + end + + def activation_error_handling + yield + nil + rescue StandardError, LoadError => e + e + end +end + +m.load_bundler! + +if m.invoked_as_script? + load Gem.bin_path("bundler", "bundle") +end diff --git a/bin/bundle.cmd b/bin/bundle.cmd new file mode 100644 index 0000000..32a58d8 --- /dev/null +++ b/bin/bundle.cmd @@ -0,0 +1,112 @@ +@ruby -x "%~f0" %* +@exit /b %ERRORLEVEL% + +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundle' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "rubygems" + +m = Module.new do + module_function + + def invoked_as_script? + File.expand_path($0) == File.expand_path(__FILE__) + end + + def env_var_version + ENV["BUNDLER_VERSION"] + end + + def cli_arg_version + return unless invoked_as_script? # don't want to hijack other binstubs + return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + bundler_version = nil + update_index = nil + ARGV.each_with_index do |a, i| + if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN + bundler_version = a + end + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ + bundler_version = $1 + update_index = i + end + bundler_version + end + + def gemfile + gemfile = ENV["BUNDLE_GEMFILE"] + return gemfile if gemfile && !gemfile.empty? + + File.expand_path("../Gemfile", __dir__) + end + + def lockfile + lockfile = + case File.basename(gemfile) + when "gems.rb" then gemfile.sub(/\.rb$/, ".locked") + else "#{gemfile}.lock" + end + File.expand_path(lockfile) + end + + def lockfile_version + return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) + end + + def bundler_requirement + @bundler_requirement ||= + env_var_version || + cli_arg_version || + bundler_requirement_for(lockfile_version) + end + + def bundler_requirement_for(version) + return "#{Gem::Requirement.default}.a" unless version + + bundler_gem_version = Gem::Version.new(version) + + bundler_gem_version.approximate_recommendation + end + + def load_bundler! + ENV["BUNDLE_GEMFILE"] ||= gemfile + + activate_bundler + end + + def activate_bundler + gem_error = activation_error_handling do + gem "bundler", bundler_requirement + end + return if gem_error.nil? + require_error = activation_error_handling do + require "bundler/version" + end + return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" + exit 42 + end + + def activation_error_handling + yield + nil + rescue StandardError, LoadError => e + e + end +end + +m.load_bundler! + +if m.invoked_as_script? + load Gem.bin_path("bundler", "bundle") +end diff --git a/bin/importmap b/bin/importmap new file mode 100644 index 0000000..36502ab --- /dev/null +++ b/bin/importmap @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby + +require_relative "../config/application" +require "importmap/commands" diff --git a/bin/rails b/bin/rails new file mode 100644 index 0000000..7bcc36e --- /dev/null +++ b/bin/rails @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby.exe +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 100644 index 0000000..01f7fc0 --- /dev/null +++ b/bin/rake @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby.exe +require_relative "../config/boot" +require "rake" +Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100644 index 0000000..5e463ee --- /dev/null +++ b/bin/setup @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby.exe +require "fileutils" + +# path to your application root. +APP_ROOT = File.expand_path("..", __dir__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. + # Add necessary setup steps to this file. + + puts "== Installing dependencies ==" + system! "gem install bundler --conservative" + system("bundle check") || system!("bundle install") + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" + # end + + puts "\n== Preparing database ==" + system! "bin/rails db:prepare" + + 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/config.ru b/config.ru new file mode 100644 index 0000000..4a3c09a --- /dev/null +++ b/config.ru @@ -0,0 +1,6 @@ +# This file is used by Rack-based servers to start the application. + +require_relative "config/environment" + +run Rails.application +Rails.application.load_server diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 0000000..a2b9c53 --- /dev/null +++ b/config/application.rb @@ -0,0 +1,22 @@ +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 RailsBudgetAppV2 + class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 7.0 + + # Configuration for the application, engines, and railties goes here. + # + # These settings can be overridden in specific environments using the files + # in config/environments, which are processed later. + # + # config.time_zone = "Central Time (US & Canada)" + # config.eager_load_paths << Rails.root.join("extras") + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 0000000..988a5dd --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,4 @@ +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "bundler/setup" # Set up gems listed in the Gemfile. +require "bootsnap/setup" # Speed up boot time by caching expensive operations. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 0000000..c76f30f --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: test + +production: + adapter: redis + url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> + channel_prefix: rails_budget_app_v2_production diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc new file mode 100644 index 0000000..7fd3ffa --- /dev/null +++ b/config/credentials.yml.enc @@ -0,0 +1 @@ +mAS+2uLXihBVqlbqLOZS5nDQf1OlIsiR7gl1uiQOd5AGVJLsq4nDaBGLe9wlpwde1Bs/vAOfQEqqF3SAQuomDY2p2JBbC7eBfPnfqXToCG6m/1HfziG4P8HbXPLNp9xH3UrCMUsF6OLwjIPZjFBWaMdtARnQ32iot3ayvsEF/DGlUhkIu0YAQKfrur00w4pyDAgAYRQX5i5CDvHmS0lC5ZK+z2KfaM3a9W8MF8IR+oToLye26Qc6DKWpTWpaUC7PU89N0wEdfn6fPpnksoheHynQpGQe/K5SMyu7iHnqw2A+5DTbQiorrpR1WLiBpEP29cZ/ZZb6GR3rlNe4Cr4R7Cz9iy35VhFz16lVHxrzR8tZaN39Il51gHGISlxBCQXh3dMxta+BwE6IuRlPeozbEEDYTIPs9zsXMD9L--PWuJIcjXbCTad903--iEFjL1+ftNuUZW4/ylMqmg== \ No newline at end of file diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..9fce15a --- /dev/null +++ b/config/database.yml @@ -0,0 +1,86 @@ +# PostgreSQL. Versions 9.3 and up are supported. +# +# Install the pg driver: +# gem install pg +# On macOS with Homebrew: +# gem install pg -- --with-pg-config=/usr/local/bin/pg_config +# On macOS with MacPorts: +# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config +# On Windows: +# gem install pg +# Choose the win32 build. +# Install PostgreSQL and put its /bin directory on your path. +# +# Configure Using Gemfile +# gem "pg" +# +default: &default + adapter: postgresql + encoding: unicode + # For details on connection pooling, see Rails configuration guide + # https://guides.rubyonrails.org/configuring.html#database-pooling + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + +development: + <<: *default + database: rails_budget_app_v2_development + + # The specified database role being used to connect to postgres. + # To create additional roles in postgres see `$ createuser --help`. + # When left blank, postgres will use the default role. This is + # the same name as the operating system user running Rails. + #username: rails_budget_app_v2 + + # The password associated with the postgres role (username). + #password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + + # The TCP port the server listens on. Defaults to 5432. + # If your server runs on a different port number, change accordingly. + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # Defaults to warning. + #min_messages: notice + +# 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: rails_budget_app_v2_test + +# As with config/credentials.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password or a full connection URL as an environment +# variable when you boot the app. For example: +# +# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" +# +# If the connection URL is provided in the special DATABASE_URL environment +# variable, Rails will automatically merge its configuration values on top of +# the values provided in this file. Alternatively, you can specify a connection +# URL environment variable explicitly: +# +# production: +# url: <%= ENV["MY_APP_DATABASE_URL"] %> +# +# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full overview on how database connection configuration can be specified. +# +production: + <<: *default + database: rails_budget_app_v2_production + username: rails_budget_app_v2 + password: <%= ENV["RAILS_BUDGET_APP_V2_DATABASE_PASSWORD"] %> diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 0000000..cac5315 --- /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..8500f45 --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,70 @@ +require "active_support/core_ext/integer/time" + +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 any time + # it changes. 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 server timing + config.server_timing = true + + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join("tmp/caching-dev.txt").exist? + config.action_controller.perform_caching = true + config.action_controller.enable_fragment_cache_logging = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + "Cache-Control" => "public, max-age=#{2.days.to_i}" + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + + # 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 exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + + # Suppress logger output for asset requests. + config.assets.quiet = true + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true + + # Uncomment if you wish to allow Action Cable access from any origin. + # config.action_cable.disable_request_forgery_protection = true +end diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 0000000..16cc91d --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,93 @@ +require "active_support/core_ext/integer/time" + +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 + + # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] + # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = 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 CSS using a preprocessor. + # config.assets.css_compressor = :sass + + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.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 + + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + + # 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 + + # Include generic and useful information about system operation, but avoid logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). + config.log_level = :info + + # 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 = "rails_budget_app_v2_production" + + 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 + + # Don't log any deprecations. + config.active_support.report_deprecations = false + + # 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..6ea4d1e --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,60 @@ +require "active_support/core_ext/integer/time" + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# 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! + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Turn false under Spring and add config.action_view.cache_template_loading = true. + config.cache_classes = true + + # Eager loading loads your whole application. When running a single test locally, + # this probably isn't necessary. It's a good idea to do in a continuous integration + # system, or in some way before deploying your code. + config.eager_load = ENV["CI"].present? + + # 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.to_i}" + } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + config.cache_store = :null_store + + # 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 + + # Store uploaded files on the local file system in a temporary directory. + config.active_storage.service = :test + + 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 + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true +end diff --git a/config/importmap.rb b/config/importmap.rb new file mode 100644 index 0000000..8dce42d --- /dev/null +++ b/config/importmap.rb @@ -0,0 +1,7 @@ +# Pin npm packages by running ./bin/importmap + +pin "application", preload: true +pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true +pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true +pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true +pin_all_from "app/javascript/controllers", under: "controllers" diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 0000000..2eeef96 --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,12 @@ +# 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 + +# 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/content_security_policy.rb b/config/initializers/content_security_policy.rb new file mode 100644 index 0000000..54f47cf --- /dev/null +++ b/config/initializers/content_security_policy.rb @@ -0,0 +1,25 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy. +# See the Securing Rails Applications Guide for more information: +# https://guides.rubyonrails.org/security.html#content-security-policy-header + +# Rails.application.configure do +# config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end +# +# # Generate session nonces for permitted importmap and inline scripts +# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } +# config.content_security_policy_nonce_directives = %w(script-src) +# +# # Report violations without enforcing the policy. +# # config.content_security_policy_report_only = true +# end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb new file mode 100644 index 0000000..adc6568 --- /dev/null +++ b/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# Configure parameters to be filtered from the log file. Use this to limit dissemination of +# sensitive information. See the ActiveSupport::ParameterFilter documentation for supported +# notations and behaviors. +Rails.application.config.filter_parameters += [ + :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn +] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb new file mode 100644 index 0000000..3860f65 --- /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/permissions_policy.rb b/config/initializers/permissions_policy.rb new file mode 100644 index 0000000..00f64d7 --- /dev/null +++ b/config/initializers/permissions_policy.rb @@ -0,0 +1,11 @@ +# Define an application-wide HTTP permissions policy. For further +# information see https://developers.google.com/web/updates/2018/06/feature-policy +# +# Rails.application.config.permissions_policy do |f| +# f.camera :none +# f.gyroscope :none +# f.microphone :none +# f.usb :none +# f.fullscreen :self +# f.payment :self, "https://secure.example.com" +# end diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..8ca56fc --- /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 https://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..daaf036 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,43 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers: a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum; this matches the default thread size of Active Record. +# +max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } +threads min_threads_count, max_threads_count + +# Specifies the `worker_timeout` threshold that Puma will use to wait before +# terminating a worker in development environments. +# +worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" + +# 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 `pidfile` that Puma will use. +pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked web server processes. If using threads and workers together +# 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. +# +# preload_app! + +# Allow puma to be restarted by `bin/rails restart` command. +plugin :tmp_restart diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..262ffd5 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,6 @@ +Rails.application.routes.draw do + # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html + + # Defines the root path route ("/") + # root "articles#index" +end diff --git a/config/storage.yml b/config/storage.yml new file mode 100644 index 0000000..4942ab6 --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,34 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket-<%= Rails.env %> + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket-<%= Rails.env %> + +# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name-<%= Rails.env %> + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 0000000..bc25fce --- /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 bin/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/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..c19f78a --- /dev/null +++ b/public/robots.txt @@ -0,0 +1 @@ +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/storage/.keep b/storage/.keep new file mode 100644 index 0000000..e69de29 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/channels/application_cable/connection_test.rb b/test/channels/application_cable/connection_test.rb new file mode 100644 index 0000000..800405f --- /dev/null +++ b/test/channels/application_cable/connection_test.rb @@ -0,0 +1,11 @@ +require "test_helper" + +class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase + # test "connects with cookies" do + # cookies.signed[:user_id] = 42 + # + # connect + # + # assert_equal connection.user_id, "42" + # end +end diff --git a/test/controllers/.keep b/test/controllers/.keep 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..973526e --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,13 @@ +ENV["RAILS_ENV"] ||= "test" +require_relative "../config/environment" +require "rails/test_help" + +class ActiveSupport::TestCase + # Run tests in parallel with specified workers + parallelize(workers: :number_of_processors, with: :threads) + + # 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/tmp/pids/.keep b/tmp/pids/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tmp/storage/.keep b/tmp/storage/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/.keep b/vendor/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/javascript/.keep b/vendor/javascript/.keep new file mode 100644 index 0000000..e69de29 From 87f99f2d01b86b3627fcfd7600b758263aa5bcbf Mon Sep 17 00:00:00 2001 From: cherelemma Date: Wed, 20 Sep 2023 19:53:49 +0300 Subject: [PATCH 02/54] update database configuration --- config/database.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/config/database.yml b/config/database.yml index 9fce15a..51d43ea 100644 --- a/config/database.yml +++ b/config/database.yml @@ -23,7 +23,9 @@ default: &default development: <<: *default - database: rails_budget_app_v2_development + database: budgetApp_development + username: postgres + password: postgres#13579 # The specified database role being used to connect to postgres. # To create additional roles in postgres see `$ createuser --help`. @@ -57,7 +59,9 @@ development: # Do not set this db to the same as development or production. test: <<: *default - database: rails_budget_app_v2_test + database: budgetApp_test + username: postgres + password: postgres#13579 # As with config/credentials.yml, you never want to store sensitive information, # like your database password, in your source code. If your source code is @@ -81,6 +85,6 @@ test: # production: <<: *default - database: rails_budget_app_v2_production - username: rails_budget_app_v2 + database: budgetApp_production + username: postgres password: <%= ENV["RAILS_BUDGET_APP_V2_DATABASE_PASSWORD"] %> From 71221ebad1a8b0a09d47f85d01b98ff5a2750cdf Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:14:11 +0300 Subject: [PATCH 03/54] install all required gem --- Gemfile | 44 +++++++++++++++++++------------- Gemfile.lock | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 96 insertions(+), 20 deletions(-) diff --git a/Gemfile b/Gemfile index b559af4..84c1640 100644 --- a/Gemfile +++ b/Gemfile @@ -1,31 +1,32 @@ -source "https://rubygems.org" +source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby "3.2.2" +ruby '3.2.2' # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" -gem "rails", "~> 7.0.8" +gem 'rails', '~> 7.0.6' # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] -gem "sprockets-rails" +gem 'sprockets-rails' -# Use postgresql as the database for Active Record -gem "pg", "~> 1.1" +# Use sqlite3 as the database for Active Record +gem 'sqlite3', '~> 1.4' # Use the Puma web server [https://github.com/puma/puma] -gem "puma", "~> 5.0" +gem 'pg' +gem 'puma', '~> 5.0' # Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails] -gem "importmap-rails" +gem 'importmap-rails' # Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev] -gem "turbo-rails" +gem 'turbo-rails' # Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] -gem "stimulus-rails" +gem 'stimulus-rails' # Build JSON APIs with ease [https://github.com/rails/jbuilder] -gem "jbuilder" +gem 'jbuilder' # Use Redis adapter to run Action Cable in production # gem "redis", "~> 4.0" @@ -37,10 +38,10 @@ gem "jbuilder" # gem "bcrypt", "~> 3.1.7" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] # Reduces boot times through caching; required in config/boot.rb -gem "bootsnap", require: false +gem 'bootsnap', require: false # Use Sass to process CSS # gem "sassc-rails" @@ -50,12 +51,13 @@ gem "bootsnap", require: false group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem - gem "debug", platforms: %i[ mri mingw x64_mingw ] + gem 'debug', platforms: %i[mri mingw x64_mingw] end group :development do # Use console on exceptions pages [https://github.com/rails/web-console] - gem "web-console" + gem 'web-console' + gem 'rspec-rails' # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler] # gem "rack-mini-profiler" @@ -66,7 +68,13 @@ end group :test do # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] - gem "capybara" - gem "selenium-webdriver" - + gem 'capybara' + gem 'selenium-webdriver' + gem 'webdrivers' end + +gem 'rubocop', '>= 1.0', '< 2.0' + +gem 'devise', '~> 4.9' + +gem 'cancancan', '~> 3.5' diff --git a/Gemfile.lock b/Gemfile.lock index 456249c..a548922 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -68,10 +68,14 @@ GEM tzinfo (~> 2.0) addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) + ast (2.4.2) + base64 (0.1.1) + bcrypt (3.1.19) bindex (0.8.1) bootsnap (1.16.0) msgpack (~> 1.2) builder (3.2.4) + cancancan (3.5.0) capybara (3.39.2) addressable matrix @@ -87,6 +91,13 @@ GEM debug (1.8.0) irb (>= 1.5.0) reline (>= 0.3.1) + devise (4.9.2) + bcrypt (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 4.1.0) + responders + warden (~> 1.2.3) + diff-lcs (1.5.0) erubi (1.12.0) globalid (1.2.1) activesupport (>= 6.1) @@ -102,6 +113,8 @@ GEM jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) + json (2.6.3) + language_server-protocol (3.17.0.3) loofah (2.21.3) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -128,6 +141,11 @@ GEM nio4r (2.5.9) nokogiri (1.15.4-x64-mingw-ucrt) racc (~> 1.4) + orm_adapter (0.5.0) + parallel (1.23.0) + parser (3.2.2.3) + ast (~> 2.4.1) + racc pg (1.5.4-x64-mingw-ucrt) psych (5.1.0) stringio @@ -166,13 +184,49 @@ GEM rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) + rainbow (3.1.1) rake (13.0.6) rdoc (6.5.0) psych (>= 4.0.0) regexp_parser (2.8.1) reline (0.3.8) io-console (~> 0.5) + responders (3.1.0) + actionpack (>= 5.2) + railties (>= 5.2) rexml (3.2.6) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.6) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-rails (6.0.3) + actionpack (>= 6.1) + activesupport (>= 6.1) + railties (>= 6.1) + rspec-core (~> 3.12) + rspec-expectations (~> 3.12) + rspec-mocks (~> 3.12) + rspec-support (~> 3.12) + rspec-support (3.12.1) + rubocop (1.56.3) + base64 (~> 0.1.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.3) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.28.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.29.0) + parser (>= 3.2.1.0) + ruby-progressbar (1.13.0) rubyzip (2.3.2) selenium-webdriver (4.12.0) rexml (~> 3.2, >= 3.2.5) @@ -185,6 +239,7 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) + sqlite3 (1.6.6-x64-mingw-ucrt) stimulus-rails (1.2.2) railties (>= 6.0.0) stringio (3.0.8) @@ -198,11 +253,18 @@ GEM concurrent-ruby (~> 1.0) tzinfo-data (1.2023.3) tzinfo (>= 1.0.0) + unicode-display_width (2.4.2) + warden (1.2.9) + rack (>= 2.0.9) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) + webdrivers (5.2.0) + nokogiri (~> 1.6) + rubyzip (>= 1.3.0) + selenium-webdriver (~> 4.0) websocket (1.2.9) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) @@ -216,19 +278,25 @@ PLATFORMS DEPENDENCIES bootsnap + cancancan (~> 3.5) capybara debug + devise (~> 4.9) importmap-rails jbuilder - pg (~> 1.1) + pg puma (~> 5.0) - rails (~> 7.0.8) + rails (~> 7.0.6) + rspec-rails + rubocop (>= 1.0, < 2.0) selenium-webdriver sprockets-rails + sqlite3 (~> 1.4) stimulus-rails turbo-rails tzinfo-data web-console + webdrivers RUBY VERSION ruby 3.2.2p53 From ddd8563011c452004b75303dcc31531be9d41bae Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:14:52 +0300 Subject: [PATCH 04/54] add Github wirkflow and linter config files --- .github/workflows/linters.yml | 44 +++++++++++++++++++++++++ .rspec | 1 + .rubocop.yml | 60 +++++++++++++++++++++++++++++++++++ .stylelintrc.json | 32 +++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 .github/workflows/linters.yml create mode 100644 .rspec create mode 100644 .rubocop.yml create mode 100644 .stylelintrc.json diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 0000000..4671d69 --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,44 @@ +name: Linters + +on: pull_request + +env: + FORCE_COLOR: 1 + +jobs: + rubocop: + name: Rubocop + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-ruby@v1 + with: + ruby-version: 3.1.x + - name: Setup Rubocop + run: | + gem install --no-document rubocop -v '>= 1.0, < 2.0' # https://docs.rubocop.org/en/stable/installation/ + [ -f .rubocop.yml ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/ror/.rubocop.yml + - name: Rubocop Report + run: rubocop --color + stylelint: + name: Stylelint + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: "18.x" + - name: Setup Stylelint + run: | + npm install --save-dev stylelint@13.x stylelint-scss@3.x stylelint-config-standard@21.x stylelint-csstree-validator@1.x + [ -f .stylelintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/ror/.stylelintrc.json + - name: Stylelint Report + run: npx stylelint "**/*.{css,scss}" + nodechecker: + name: node_modules checker + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Check node_modules existence + run: | + if [ -d "node_modules/" ]; then echo -e "\e[1;31mThe node_modules/ folder was pushed to the repo. Please remove it from the GitHub repository and try again."; echo -e "\e[1;32mYou can set up a .gitignore file with this folder included on it to prevent this from happening in the future." && exit 1; fi \ No newline at end of file diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..c99d2e7 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..07baeb4 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,60 @@ +AllCops: + NewCops: enable + Exclude: + - "db/**/*" + - "bin/*" + - "config/**/*" + - "Guardfile" + - "Rakefile" + - "node_modules/**/*" + + DisplayCopNames: true + +Layout/LineLength: + Max: 120 +Metrics/MethodLength: + Include: + - "app/controllers/*" + - "app/models/*" + Max: 20 +Metrics/AbcSize: + Include: + - "app/controllers/*" + - "app/models/*" + Max: 50 +Metrics/ClassLength: + Max: 150 +Metrics/BlockLength: + AllowedMethods: ['describe'] + Max: 30 + +Style/Documentation: + Enabled: false +Style/ClassAndModuleChildren: + Enabled: false +Style/EachForSimpleLoop: + Enabled: false +Style/AndOr: + Enabled: false +Style/DefWithParentheses: + Enabled: false +Style/FrozenStringLiteralComment: + EnforcedStyle: never + +Layout/HashAlignment: + EnforcedColonStyle: key +Layout/ExtraSpacing: + AllowForAlignment: false +Layout/MultilineMethodCallIndentation: + Enabled: true + EnforcedStyle: indented +Lint/RaiseException: + Enabled: false +Lint/StructNewOverride: + Enabled: false +Style/HashEachMethods: + Enabled: false +Style/HashTransformKeys: + Enabled: false +Style/HashTransformValues: + Enabled: false \ No newline at end of file diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 0000000..f5a0c0a --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,32 @@ +{ + "extends": ["stylelint-config-standard"], + "plugins": ["stylelint-scss", "stylelint-csstree-validator"], + "rules": { + "at-rule-no-unknown": [ + true, + { + "ignoreAtRules": [ + "tailwind", + "apply", + "variants", + "responsive", + "screen" + ] + } + ], + "scss/at-rule-no-unknown": [ + true, + { + "ignoreAtRules": [ + "tailwind", + "apply", + "variants", + "responsive", + "screen" + ] + } + ], + "csstree/validator": true + }, + "ignoreFiles": ["build/**", "dist/**", "**/reset*.css", "**/bootstrap*.css"] + } \ No newline at end of file From bf0f2998cc1893630ac9f4ad6d94bef82076d5c1 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:21:13 +0300 Subject: [PATCH 05/54] install stylelint package --- package-lock.json | 3252 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 8 + 2 files changed, 3260 insertions(+) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..69bd8f5 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3252 @@ +{ + "name": "rails-budget-app-v2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "stylelint": "^13.13.1", + "stylelint-config-standard": "^21.0.0", + "stylelint-csstree-validator": "^1.9.0", + "stylelint-scss": "^3.21.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.20.tgz", + "integrity": "sha512-Y6jd1ahLubuYweD/zJH+vvOY141v4f9igNQAQ+MBgq9JlHS2iTsZKn1aMsb3vGccZsXI16VzTBw52Xx0DWmtnA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.22.20", + "@babel/helpers": "^7.22.15", + "@babel/parser": "^7.22.16", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.20", + "@babel/types": "^7.22.19", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", + "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.20.tgz", + "integrity": "sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", + "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.22.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", + "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.20.tgz", + "integrity": "sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.16", + "@babel/types": "^7.22.19", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.22.19", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.19.tgz", + "integrity": "sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.19", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@stylelint/postcss-css-in-js": { + "version": "0.37.3", + "resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.3.tgz", + "integrity": "sha512-scLk3cSH1H9KggSniseb2KNAU5D9FWc3H7BxCSAIdtU9OWIyw0zkEZ9qEKHryRM+SExYXRKNb7tOOVNAsQ3iwg==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "dependencies": { + "@babel/core": "^7.17.9" + }, + "peerDependencies": { + "postcss": ">=7.0.0", + "postcss-syntax": ">=0.36.2" + } + }, + "node_modules/@stylelint/postcss-markdown": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/@stylelint/postcss-markdown/-/postcss-markdown-0.36.2.tgz", + "integrity": "sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ==", + "deprecated": "Use the original unforked package instead: postcss-markdown", + "dev": true, + "dependencies": { + "remark": "^13.0.0", + "unist-util-find-all-after": "^3.0.2" + }, + "peerDependencies": { + "postcss": ">=7.0.0", + "postcss-syntax": ">=0.36.2" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.12.tgz", + "integrity": "sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==", + "dev": true, + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/unist": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.8.tgz", + "integrity": "sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==", + "dev": true + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/brace-expansion/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.11", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.11.tgz", + "integrity": "sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001538", + "electron-to-chromium": "^1.4.526", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001538", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz", + "integrity": "sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/clone-regexp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", + "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "dev": true, + "dependencies": { + "is-regexp": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.527", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.527.tgz", + "integrity": "sha512-EafxEiEDzk2aLrdbtVczylHflHdHkNrpGNHIgDyA63sUQLQVS2ayj2hPw3RsVB42qkwURH+T2OxV7kGPUuYszA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/execall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", + "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", + "dev": true, + "dependencies": { + "clone-regexp": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", + "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "dev": true, + "dependencies": { + "flatted": "^3.2.7", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true + }, + "node_modules/gonzales-pe": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", + "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "gonzales": "bin/gonzales.js" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regexp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", + "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/known-css-properties": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.21.0.tgz", + "integrity": "sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==", + "dev": true + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/longest-streak": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", + "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", + "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "longest-streak": "^2.0.0", + "mdast-util-to-string": "^2.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-selector": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", + "integrity": "sha512-dxvWdI8gw6eAvk9BlPffgEoGfM7AdijoCwOEJge3e3ulT2XLgmU7KvvxprOaCu05Q1uGRHmOhHe1r6emZoKyFw==", + "dev": true + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-html": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.36.0.tgz", + "integrity": "sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==", + "dev": true, + "dependencies": { + "htmlparser2": "^3.10.0" + }, + "peerDependencies": { + "postcss": ">=5.0.0", + "postcss-syntax": ">=0.36.0" + } + }, + "node_modules/postcss-less": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.4.tgz", + "integrity": "sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">=6.14.4" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", + "dev": true + }, + "node_modules/postcss-safe-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", + "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.26" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-sass": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.4.4.tgz", + "integrity": "sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg==", + "dev": true, + "dependencies": { + "gonzales-pe": "^4.3.0", + "postcss": "^7.0.21" + } + }, + "node_modules/postcss-scss": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz", + "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-syntax": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz", + "integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==", + "dev": true, + "peerDependencies": { + "postcss": ">=5.0.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/remark": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz", + "integrity": "sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==", + "dev": true, + "dependencies": { + "remark-parse": "^9.0.0", + "remark-stringify": "^9.0.0", + "unified": "^9.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", + "dev": true, + "dependencies": { + "mdast-util-from-markdown": "^0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.1.tgz", + "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", + "dev": true, + "dependencies": { + "mdast-util-to-markdown": "^0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.15.tgz", + "integrity": "sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==", + "dev": true + }, + "node_modules/specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "dev": true, + "bin": { + "specificity": "bin/specificity" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", + "dev": true + }, + "node_modules/stylelint": { + "version": "13.13.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.13.1.tgz", + "integrity": "sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ==", + "dev": true, + "dependencies": { + "@stylelint/postcss-css-in-js": "^0.37.2", + "@stylelint/postcss-markdown": "^0.36.2", + "autoprefixer": "^9.8.6", + "balanced-match": "^2.0.0", + "chalk": "^4.1.1", + "cosmiconfig": "^7.0.0", + "debug": "^4.3.1", + "execall": "^2.0.0", + "fast-glob": "^3.2.5", + "fastest-levenshtein": "^1.0.12", + "file-entry-cache": "^6.0.1", + "get-stdin": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.0.3", + "globjoin": "^0.1.4", + "html-tags": "^3.1.0", + "ignore": "^5.1.8", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "known-css-properties": "^0.21.0", + "lodash": "^4.17.21", + "log-symbols": "^4.1.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.4", + "normalize-selector": "^0.2.0", + "postcss": "^7.0.35", + "postcss-html": "^0.36.0", + "postcss-less": "^3.1.4", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^4.0.2", + "postcss-sass": "^0.4.4", + "postcss-scss": "^2.1.1", + "postcss-selector-parser": "^6.0.5", + "postcss-syntax": "^0.36.2", + "postcss-value-parser": "^4.1.0", + "resolve-from": "^5.0.0", + "slash": "^3.0.0", + "specificity": "^0.4.1", + "string-width": "^4.2.2", + "strip-ansi": "^6.0.0", + "style-search": "^0.1.0", + "sugarss": "^2.0.0", + "svg-tags": "^1.0.0", + "table": "^6.6.0", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^3.0.3" + }, + "bin": { + "stylelint": "bin/stylelint.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-4.0.0.tgz", + "integrity": "sha512-sgna89Ng+25Hr9kmmaIxpGWt2LStVm1xf1807PdcWasiPDaOTkOHRL61sINw0twky7QMzafCGToGDnHT/kTHtQ==", + "dev": true, + "peerDependencies": { + "stylelint": "^13.12.0" + } + }, + "node_modules/stylelint-config-standard": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-21.0.0.tgz", + "integrity": "sha512-Yf6mx5oYEbQQJxWuW7X3t1gcxqbUx52qC9SMS3saC2ruOVYEyqmr5zSW6k3wXflDjjFrPhar3kp68ugRopmlzg==", + "dev": true, + "dependencies": { + "stylelint-config-recommended": "^4.0.0" + }, + "peerDependencies": { + "stylelint": "^13.12.0" + } + }, + "node_modules/stylelint-csstree-validator": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/stylelint-csstree-validator/-/stylelint-csstree-validator-1.9.0.tgz", + "integrity": "sha512-fVbtWDEWzux/bZSPBk9tD/bvyc8bSmb52BvUDjcduOzXqKqOyFHUvFayVr9ic88l8KJEVV0Ujab9ah5oTdX4Uw==", + "dev": true, + "dependencies": { + "css-tree": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "stylelint": ">=7.0.0 <14.0.0" + } + }, + "node_modules/stylelint-scss": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.21.0.tgz", + "integrity": "sha512-CMI2wSHL+XVlNExpauy/+DbUcB/oUZLARDtMIXkpV/5yd8nthzylYd1cdHeDMJVBXeYHldsnebUX6MoV5zPW4A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "stylelint": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0" + } + }, + "node_modules/sugarss": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", + "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "dev": true, + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/unist-util-find-all-after": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", + "integrity": "sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ==", + "dev": true, + "dependencies": { + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-browserslist-db/node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..710333e --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "devDependencies": { + "stylelint": "^13.13.1", + "stylelint-config-standard": "^21.0.0", + "stylelint-csstree-validator": "^1.9.0", + "stylelint-scss": "^3.21.0" + } +} From 80a9fb28e1e6966ac78d8ba153da58dcce411a94 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:31:23 +0300 Subject: [PATCH 06/54] generate rspec helpers --- Gemfile | 2 +- spec/rails_helper.rb | 63 ++++++++++++++++++++++++++++ spec/spec_helper.rb | 98 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 spec/rails_helper.rb create mode 100644 spec/spec_helper.rb diff --git a/Gemfile b/Gemfile index 84c1640..7c02a22 100644 --- a/Gemfile +++ b/Gemfile @@ -58,7 +58,7 @@ group :development do # Use console on exceptions pages [https://github.com/rails/web-console] gem 'web-console' gem 'rspec-rails' - + # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler] # gem "rack-mini-profiler" diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb new file mode 100644 index 0000000..c718176 --- /dev/null +++ b/spec/rails_helper.rb @@ -0,0 +1,63 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' +ENV['RAILS_ENV'] ||= 'test' +require_relative '../config/environment' +# Prevent database truncation if the environment is production +abort("The Rails environment is running in production mode!") if Rails.env.production? +require 'rspec/rails' +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } + +# Checks for pending migrations and applies them before tests are run. +# If you are not using ActiveRecord, you can remove these lines. +begin + ActiveRecord::Migration.maintain_test_schema! +rescue ActiveRecord::PendingMigrationError => e + abort e.to_s.strip +end +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # You can uncomment this line to turn off ActiveRecord support entirely. + # config.use_active_record = false + + # RSpec Rails can automatically mix in different behaviours to your tests + # based on their file location, for example enabling you to call `get` and + # `post` in specs under `spec/controllers`. + # + # You can disable this behaviour by removing the line below, and instead + # explicitly tag your specs with their type, e.g.: + # + # RSpec.describe UsersController, type: :controller do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://rspec.info/features/6-0/rspec-rails + config.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..35de9f0 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,98 @@ +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ + config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + config.warnings = true + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end From 74c8cb5e66cc4a9c354b4632a548293341f8e114 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:31:57 +0300 Subject: [PATCH 07/54] craete user model --- app/models/user.rb | 2 ++ db/migrate/20230921223143_create_users.rb | 9 +++++++++ spec/models/user_spec.rb | 5 +++++ 3 files changed, 16 insertions(+) create mode 100644 app/models/user.rb create mode 100644 db/migrate/20230921223143_create_users.rb create mode 100644 spec/models/user_spec.rb diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..379658a --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,2 @@ +class User < ApplicationRecord +end diff --git a/db/migrate/20230921223143_create_users.rb b/db/migrate/20230921223143_create_users.rb new file mode 100644 index 0000000..dd02b09 --- /dev/null +++ b/db/migrate/20230921223143_create_users.rb @@ -0,0 +1,9 @@ +class CreateUsers < ActiveRecord::Migration[7.0] + def change + create_table :users do |t| + t.string :name + + t.timestamps + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 0000000..47a31bb --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe User, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 7d0299d176d6d9af166152aaf0b38c3e8738d2a6 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:34:52 +0300 Subject: [PATCH 08/54] create category mode --- app/models/category.rb | 3 +++ db/migrate/20230921223432_create_categories.rb | 11 +++++++++++ spec/models/category_spec.rb | 5 +++++ 3 files changed, 19 insertions(+) create mode 100644 app/models/category.rb create mode 100644 db/migrate/20230921223432_create_categories.rb create mode 100644 spec/models/category_spec.rb diff --git a/app/models/category.rb b/app/models/category.rb new file mode 100644 index 0000000..213cdf7 --- /dev/null +++ b/app/models/category.rb @@ -0,0 +1,3 @@ +class Category < ApplicationRecord + belongs_to :user +end diff --git a/db/migrate/20230921223432_create_categories.rb b/db/migrate/20230921223432_create_categories.rb new file mode 100644 index 0000000..d8fc280 --- /dev/null +++ b/db/migrate/20230921223432_create_categories.rb @@ -0,0 +1,11 @@ +class CreateCategories < ActiveRecord::Migration[7.0] + def change + create_table :categories do |t| + t.string :name + t.string :icon + t.references :user, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb new file mode 100644 index 0000000..0c9fefa --- /dev/null +++ b/spec/models/category_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Category, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 26b9ee5c5d885579e54c1880d157c1f72f4ef9c2 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:42:18 +0300 Subject: [PATCH 09/54] create entity model --- app/models/entity.rb | 4 ++++ db/migrate/20230921223432_create_categories.rb | 2 +- db/migrate/20230921223926_create_entities.rb | 12 ++++++++++++ spec/models/entity_spec.rb | 5 +++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 app/models/entity.rb create mode 100644 db/migrate/20230921223926_create_entities.rb create mode 100644 spec/models/entity_spec.rb diff --git a/app/models/entity.rb b/app/models/entity.rb new file mode 100644 index 0000000..43b3d76 --- /dev/null +++ b/app/models/entity.rb @@ -0,0 +1,4 @@ +class Entity < ApplicationRecord + belongs_to :user + belongs_to :category +end diff --git a/db/migrate/20230921223432_create_categories.rb b/db/migrate/20230921223432_create_categories.rb index d8fc280..abc2bf9 100644 --- a/db/migrate/20230921223432_create_categories.rb +++ b/db/migrate/20230921223432_create_categories.rb @@ -3,7 +3,7 @@ def change create_table :categories do |t| t.string :name t.string :icon - t.references :user, null: false, foreign_key: true + t.references :author, null: false, foreign_key: { to_table: :users } t.timestamps end diff --git a/db/migrate/20230921223926_create_entities.rb b/db/migrate/20230921223926_create_entities.rb new file mode 100644 index 0000000..ec9877d --- /dev/null +++ b/db/migrate/20230921223926_create_entities.rb @@ -0,0 +1,12 @@ +class CreateEntities < ActiveRecord::Migration[7.0] + def change + create_table :entities do |t| + t.string :name + t.decimal :amount + t.references :user, null: false, foreign_key: true { to_table: :users} + t.references :category, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/spec/models/entity_spec.rb b/spec/models/entity_spec.rb new file mode 100644 index 0000000..f513ed6 --- /dev/null +++ b/spec/models/entity_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Entity, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 2f305416d2545038e589bd4f5ceae07b05b7a6e6 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:46:59 +0300 Subject: [PATCH 10/54] run migration and create a schema --- db/migrate/20230921223926_create_entities.rb | 2 +- db/schema.rb | 46 ++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 db/schema.rb diff --git a/db/migrate/20230921223926_create_entities.rb b/db/migrate/20230921223926_create_entities.rb index ec9877d..94f86c2 100644 --- a/db/migrate/20230921223926_create_entities.rb +++ b/db/migrate/20230921223926_create_entities.rb @@ -3,7 +3,7 @@ def change create_table :entities do |t| t.string :name t.decimal :amount - t.references :user, null: false, foreign_key: true { to_table: :users} + t.references :user, null: false, foreign_key: { to_table: :users} t.references :category, null: false, foreign_key: true t.timestamps diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..5e13129 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,46 @@ +# 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. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.0].define(version: 2023_09_21_223926) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "categories", force: :cascade do |t| + t.string "name" + t.string "icon" + t.bigint "author_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["author_id"], name: "index_categories_on_author_id" + end + + create_table "entities", force: :cascade do |t| + t.string "name" + t.decimal "amount" + t.bigint "user_id", null: false + t.bigint "category_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["category_id"], name: "index_entities_on_category_id" + t.index ["user_id"], name: "index_entities_on_user_id" + end + + create_table "users", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_foreign_key "categories", "users", column: "author_id" + add_foreign_key "entities", "categories" + add_foreign_key "entities", "users" +end From 69378c99bd48f9851539c15fbd66448c916fa385 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:50:32 +0300 Subject: [PATCH 11/54] generate devise install and configure --- config/environments/development.rb | 2 + config/environments/test.rb | 2 + config/initializers/devise.rb | 313 +++++++++++++++++++++++++++++ config/locales/devise.en.yml | 65 ++++++ config/routes.rb | 2 +- 5 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 config/initializers/devise.rb create mode 100644 config/locales/devise.en.yml diff --git a/config/environments/development.rb b/config/environments/development.rb index 8500f45..bac607a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -41,6 +41,8 @@ config.action_mailer.perform_caching = false + config.action_mailer.default_url_options = { host: "localhost", port: 3000 } + # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log diff --git a/config/environments/test.rb b/config/environments/test.rb index 6ea4d1e..9af1553 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -38,6 +38,8 @@ config.action_mailer.perform_caching = false + config.action_mailer.default_url_options = { host: "localhost", port: 3000 } + # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb new file mode 100644 index 0000000..6a78cf4 --- /dev/null +++ b/config/initializers/devise.rb @@ -0,0 +1,313 @@ +# frozen_string_literal: true + +# Assuming you have not yet modified this file, each configuration option below +# is set to its default value. Note that some are commented out while others +# are not: uncommented lines are intended to protect your configuration from +# breaking changes in upgrades (i.e., in the event that future versions of +# Devise change the default values for those options). +# +# 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 = 'ef1b33660721bccf7e6c6ddffc088d9a86a96c68da19a69732464c2ca621c9a533110713182f368e2f60696a29986118b9c6bd6c4a3ae65caff2b1f3371e9cda' + + # ==> 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. + # For API-only applications to support authentication "out-of-the-box", you will likely want to + # enable this with :database unless you are using a custom strategy. + # 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 12. If + # using other algorithms, it sets how many times you want the password to be hashed. + # The number of stretches used for generating the hashed password are stored + # with the hashed password. This allows you to change the stretches without + # invalidating existing passwords. + # + # 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 : 12 + + # Set up a pepper to generate the hashed password. + # config.pepper = '0bd53fdfecec3fa14db28b0ac1a2bbc33df13e2bcca12c9869356db37069d0eeda4474bb9a269fb60a5281ab701b62189530f38b47c59639a6a58a2db1f84af3' + + # 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. + # You can also set it to nil, which will allow the user to access the website + # without confirming their account. + # 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, :turbo_stream] + + # 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' + + # ==> Hotwire/Turbo configuration + # When using Devise with Hotwire/Turbo, the http status for error responses + # and some redirects must match the following. The default in Devise for existing + # apps is `200 OK` and `302 Found respectively`, but new apps are generated with + # these new defaults that match Hotwire/Turbo behavior. + # Note: These might become the new default in future versions of Devise. + config.responder.error_status = :unprocessable_entity + config.responder.redirect_status = :see_other + + # ==> Configuration for :registerable + + # When set to false, does not sign a user in automatically after their password is + # changed. Defaults to true, so a user is signed in automatically after changing a password. + # config.sign_in_after_change_password = true +end diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml new file mode 100644 index 0000000..260e1c4 --- /dev/null +++ b/config/locales/devise.en.yml @@ -0,0 +1,65 @@ +# Additional translations at https://github.com/heartcombo/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 confirmation link to confirm your new email address." + updated: "Your account has been updated successfully." + updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again." + 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 262ffd5..a512f40 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,5 +2,5 @@ # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html # Defines the root path route ("/") - # root "articles#index" + root "home#index" end From ee1a799de1cb0800f5c5dfa7c3c9354fb4fccd55 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:50:43 +0300 Subject: [PATCH 12/54] generate a devise views --- 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 +++++++++++ .../devise/shared/_error_messages.html.erb | 15 +++++++ app/views/devise/shared/_links.html.erb | 25 +++++++++++ app/views/devise/unlocks/new.html.erb | 16 +++++++ 14 files changed, 241 insertions(+) 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/_error_messages.html.erb create mode 100644 app/views/devise/shared/_links.html.erb create mode 100644 app/views/devise/unlocks/new.html.erb diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb new file mode 100644 index 0000000..b12dd0c --- /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| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+ <%= 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..5fbb9ff --- /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| %> + <%= render "devise/shared/error_messages", resource: resource %> + <%= 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: "new-password" %> +
+ +
+ <%= 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..9b486b8 --- /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| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+ <%= 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..b82e336 --- /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| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+ <%= 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?", turbo_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..d655b66 --- /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| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+ <%= 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..5ede964 --- /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/_error_messages.html.erb b/app/views/devise/shared/_error_messages.html.erb new file mode 100644 index 0000000..cabfe30 --- /dev/null +++ b/app/views/devise/shared/_error_messages.html.erb @@ -0,0 +1,15 @@ +<% if resource.errors.any? %> +
+

+ <%= I18n.t("errors.messages.not_saved", + count: resource.errors.count, + resource: resource.class.model_name.human.downcase) + %> +

+
    + <% resource.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+<% end %> diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb new file mode 100644 index 0000000..7a75304 --- /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| %> + <%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false } %>
+ <% 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..ffc34de --- /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| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ +
+ <%= f.submit "Resend unlock instructions" %> +
+<% end %> + +<%= render "devise/shared/links" %> From 0601ae4368c0d51e7620911c5dff09c715e86921 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:53:21 +0300 Subject: [PATCH 13/54] ceate a devise user model --- app/models/user.rb | 4 ++ config/initializers/devise.rb | 2 +- config/routes.rb | 1 + .../20230921225144_add_devise_to_users.rb | 51 +++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20230921225144_add_devise_to_users.rb diff --git a/app/models/user.rb b/app/models/user.rb index 379658a..4756799 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,2 +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/initializers/devise.rb b/config/initializers/devise.rb index 6a78cf4..7d14566 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -143,7 +143,7 @@ # without confirming their account. # Default is 0.days, meaning the user cannot access the website without # confirming their account. - # config.allow_unconfirmed_access_for = 2.days + config.allow_unconfirmed_access_for = nil # 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 diff --git a/config/routes.rb b/config/routes.rb index a512f40..ecf33f0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,5 @@ Rails.application.routes.draw do + devise_for :users # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html # Defines the root path route ("/") diff --git a/db/migrate/20230921225144_add_devise_to_users.rb b/db/migrate/20230921225144_add_devise_to_users.rb new file mode 100644 index 0000000..98ed9dc --- /dev/null +++ b/db/migrate/20230921225144_add_devise_to_users.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +class AddDeviseToUsers < ActiveRecord::Migration[7.0] + def self.up + change_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 + + + # Uncomment below if timestamps were not included in your original model. + # 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 + + def self.down + # By default, we don't want to make any assumption about how to roll back a migration when your + # model already existed. Please edit below which fields you would like to remove in this migration. + raise ActiveRecord::IrreversibleMigration + end +end From 0fcd60a97d6f8f4eda5e6d44762d97161e5ef0aa Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 01:56:57 +0300 Subject: [PATCH 14/54] update schema and setup default page --- app/controllers/home_controller.rb | 3 +++ app/views/home/index.html.erb | 1 + db/schema.rb | 9 ++++++++- 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 app/controllers/home_controller.rb create mode 100644 app/views/home/index.html.erb diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 0000000..7b56dd3 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,3 @@ +class HomeController < ApplicationController + def index; end +end \ No newline at end of file diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb new file mode 100644 index 0000000..0d77f2e --- /dev/null +++ b/app/views/home/index.html.erb @@ -0,0 +1 @@ +Welcome To Budget App \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index 5e13129..3e79c70 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[7.0].define(version: 2023_09_21_223926) do +ActiveRecord::Schema[7.0].define(version: 2023_09_21_225144) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -38,6 +38,13 @@ t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false + 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.index ["email"], name: "index_users_on_email", unique: true + t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end add_foreign_key "categories", "users", column: "author_id" From 9f5b7b9c7ee315ad622b7cf60b7eabfe7b9e5fca Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 02:04:14 +0300 Subject: [PATCH 15/54] create splash screen --- app/assets/stylesheets/application.css | 94 ++++++++++++++++++++++---- app/views/home/index.html.erb | 8 ++- 2 files changed, 86 insertions(+), 16 deletions(-) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 288b9ab..21413d1 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -1,15 +1,79 @@ -/* - * This is a manifest file that'll be compiled into application.css, which will include all the files - * listed below. - * - * Any CSS (and SCSS, if configured) 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 - * 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 - */ +@import url('https://fonts.googleapis.com/css?family=Tangerine'); + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: lato, sans-serif; +} + +p, +h1, +h2, +h3 { + font-weight: 600; + font-family: Arial, Helvetica, sans-serif; +} + +a { + text-decoration: none; + color: gray; +} + +.container { + background-color: whitesmoke; + height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.title { + color: #434b54; + font-size: 2.5rem; + margin: auto; + text-align: center; + font-family: 'Tangerine', serif; + font-weight: bold; +} + +.auth-btns { + display: flex; + flex-direction: column; + margin-inline: auto; + align-items: center; + gap: 5px; + width: 100%; + padding: 1em; +} + +.auth-btns .signin { + background-color: #3778c2; + color: white; +} + +.auth-btns .signup { + color: #434b54; +} + +.auth-btns .signin, +.auth-btns .signup { + padding: 1em; + width: 100%; + display: flex; + justify-content: center; + font-family: Arial, Helvetica, sans-serif; + cursor: pointer; + text-decoration: none; + text-transform: uppercase; +} + +.signin:hover, +.signup:hover { + background-color: #434b54; + color: #fff; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + transform: translateY(-2px); + transition: background-color 0.3s ease-in-out, transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out; +} diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 0d77f2e..b0c9464 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -1 +1,7 @@ -Welcome To Budget App \ No newline at end of file +
+

Budget Tracker App

+
+ <%= link_to "Log In" , new_user_session_path, class: "signin" %> + <%= link_to "Sign Up" , new_user_registration_path, class: "signup" %> +
+
\ No newline at end of file From 36e3b8cb2911321e5587bf749b081580a8eadacc Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 02:10:35 +0300 Subject: [PATCH 16/54] add style and format sign and signup pages --- app/assets/stylesheets/application.css | 198 ++++++++++++++++++ app/views/devise/passwords/new.html.erb | 22 +- app/views/devise/registrations/new.html.erb | 46 ++-- app/views/devise/sessions/new.html.erb | 39 ++-- .../devise/shared/_error_messages.html.erb | 2 +- app/views/devise/shared/_links.html.erb | 8 +- 6 files changed, 258 insertions(+), 57 deletions(-) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 21413d1..026c0a3 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -77,3 +77,201 @@ a { transform: translateY(-2px); transition: background-color 0.3s ease-in-out, transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out; } + +.app_header { + display: flex; + justify-content: space-between; + align-items: center; + color: white; + padding: 1rem 0.5rem; + background-color: #3778c2; +} + +.app_header p, +.app_header h2 { + margin: 0.5rem 0; + font-size: 1.25rem; + font-weight: bold; +} + +.app_header i { + font-size: 10.5em; + color: #000; +} + +.forget-pass-title { + font-size: 1.2rem; + font-weight: normal; + width: 100%; + text-align: center; + margin: 1rem auto; +} + +.login-btn { + background-color: #3778c2; + border: none; + color: #fff; + font-size: 1.05rem; +} + +.signup_form { + background-color: whitesmoke; + padding: 10px 10px; + display: flex; + border-radius: 3px; + height: 70vh; + flex-direction: column; + gap: 2px; +} + +.link-btns { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: 1rem; +} + +.link-btns a { + color: #434b54; + text-decoration: none; +} + +.form-container { + display: flex; + flex-direction: column; + justify-content: center; + margin-top: 5%; + gap: 10px; +} + +.form-container .submit-button { + width: 98%; + background-color: #434b54; + padding: 0.5em 1em; + color: white; + border: none; + font-size: 1.1em; + border-radius: 3px; +} + +.form-container input { + width: 98%; + padding: 0.5em 1em; + margin-bottom: 10px; +} + +.field_with_errors input[type="text"] { + border: 1px solid red; +} + +#left-arrow, +#logout { + color: white; + font-weight: bold; + text-decoration: none; +} + +#left-arrow { + font-size: 1.75rem; +} + +#logout { + font-size: 1.05rem; +} + +.field { + margin-bottom: 2px; + color: #fff; +} + +.navbar > button { + border: none; +} + +.field input[type="email"], +.field input[type="text"], +.field input[type="password"], +.field input[type="checkbox"] { + width: 100%; + padding: 12px; + border: none; + border-bottom: 2px solid #999; + background-color: #fff; + color: #434b54; +} + +.field input[type="email"]:focus, +.field input[type="password"]:focus { + border-bottom-color: #434b54; +} + +.alert { + text-align: center; +} + +#error_explanation h2 { + color: red; + font-size: 1.15rem; + font-weight: bolder; + padding: 10px; +} + +.error_msg, +.alert { + color: red; + font-size: 1rem; + margin-top: 5px; +} + +.nav-item { + border-bottom: 1px solid #434b54; +} + +.navbar-nav :last-child { + border-bottom: none !important; +} + +.nav-link { + color: #fff !important; + font-size: 1.2rem; + font-weight: bold; + margin-left: 1rem; +} + +.navbar-brand { + margin-left: 1rem; + font-size: 1.5rem; + font-weight: bold; +} + +.footer { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + background-color: #434b54; + color: white; + gap: 10px; + border: 1px solid white; + width: 100%; + margin-inline: auto; + padding: 1em 0; + bottom: 0; + position: fixed; +} + +.footer a { + color: white; + font-weight: bold; + text-decoration: none; +} + +.footer i { + font-size: 1.3em; +} + +.footer_link { + color: white; + font-size: 100%; +} diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb index 9b486b8..e7d52d5 100644 --- a/app/views/devise/passwords/new.html.erb +++ b/app/views/devise/passwords/new.html.erb @@ -1,16 +1,20 @@ -

Forgot your password?

+ <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> +
+ + + +

Password Reset

+ <%= f.submit "Send", class:"login-btn"%> +
+
+

Forgot your password?

+
+ <%= render "devise/shared/error_messages", resource: resource %>
- <%= f.label :email %>
- <%= f.email_field :email, autofocus: true, autocomplete: "email" %> -
- -
- <%= f.submit "Send me reset password instructions" %> + <%= f.email_field :email, autofocus: true, autocomplete: "email", placeholder: "Email Address" %>
<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index d655b66..12b310e 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -1,29 +1,27 @@ -

Sign up

- <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> - <%= render "devise/shared/error_messages", resource: resource %> - -
- <%= 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" %> +
+ +

REGISTER

+ <%= f.submit "Next", class:"login-btn"%>
-
- <%= f.label :password_confirmation %>
- <%= f.password_field :password_confirmation, autocomplete: "new-password" %> -
+ <%= render "devise/shared/error_messages", resource: resource %> -
- <%= f.submit "Sign up" %> + -<% end %> - -<%= render "devise/shared/links" %> +<% end %> \ No newline at end of file diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 5ede964..8d254ed 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -1,26 +1,25 @@ -

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? %> +
+ + + +

LOG IN

+ <%= f.submit "Log In", class:"login-btn"%> +
+ +
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", placeholder: "Email" %> +
+
- <%= f.check_box :remember_me %> - <%= f.label :remember_me %> + <%= f.password_field :password, autocomplete: "current-password", placeholder: "Password" %>
<% end %> - -
- <%= f.submit "Log in" %> + + -<% end %> -<%= render "devise/shared/links" %> +

<%= alert %>

+ + \ No newline at end of file diff --git a/app/views/devise/shared/_error_messages.html.erb b/app/views/devise/shared/_error_messages.html.erb index cabfe30..55143fc 100644 --- a/app/views/devise/shared/_error_messages.html.erb +++ b/app/views/devise/shared/_error_messages.html.erb @@ -6,7 +6,7 @@ resource: resource.class.model_name.human.downcase) %> -
    +
      <% resource.errors.full_messages.each do |message| %>
    • <%= message %>
    • <% end %> diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb index 7a75304..9595f74 100644 --- a/app/views/devise/shared/_links.html.erb +++ b/app/views/devise/shared/_links.html.erb @@ -2,9 +2,11 @@ <%= 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) %>
      From f701028c0dec3bfa0aec65faac4d26f651168d56 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 10:20:10 +0300 Subject: [PATCH 17/54] add model validations --- app/models/category.rb | 14 +++++++++++++- app/models/entity.rb | 7 +++++-- app/models/user.rb | 5 +++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/models/category.rb b/app/models/category.rb index 213cdf7..e7cd690 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -1,3 +1,15 @@ class Category < ApplicationRecord - belongs_to :user + belongs_to :author, class_name: 'User', foreign_key: 'author_id' + has_many :entities, dependent: :destroy + + validates :name, presence: true, uniqueness: true, length: { minimum: 3, maximum: 60 } + validates :icon, presence: true + + def self.default_icon + 'fas fa-question-circle' + end + + def total_transactions + entities.where(category_id: id).sum(:amount).to_f + end end diff --git a/app/models/entity.rb b/app/models/entity.rb index 43b3d76..44c5241 100644 --- a/app/models/entity.rb +++ b/app/models/entity.rb @@ -1,4 +1,7 @@ class Entity < ApplicationRecord - belongs_to :user - belongs_to :category + belongs_to :author, class_name: 'User', foreign_key: 'author_id' + belongs_to :category, class_name: 'Category', foreign_key: 'category_id' + + validates :name, presence: true, uniqueness: true, length: { minimum: 3, maximum: 60 } + validates :amount, presence: true, numericality: { greater_than: 0 }, length: { minimum: 1, maximum: 100 } end diff --git a/app/models/user.rb b/app/models/user.rb index 4756799..91d28fc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,4 +3,9 @@ class User < ApplicationRecord # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable + + has_many categories, dependent: :destroy + has_many entities, dependent: :destroy + + validates :name, presence: true, uniqueness: true, length: { minimum: 3, maximum: 60 } end From 3436f2894b5912e51761d464e7ea937c20638649 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 10:40:44 +0300 Subject: [PATCH 18/54] update schema --- app/assets/stylesheets/application.css | 89 +++++++++++++++++++ app/models/user.rb | 4 +- app/views/devise/sessions/new.html.erb | 2 +- app/views/layouts/application.html.erb | 1 + ...s.rb => 20230922072553_create_entities.rb} | 4 +- db/schema.rb | 8 +- 6 files changed, 99 insertions(+), 9 deletions(-) rename db/migrate/{20230921223926_create_entities.rb => 20230922072553_create_entities.rb} (75%) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 026c0a3..7b73dec 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -245,6 +245,95 @@ a { font-weight: bold; } +.categories { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + gap: 1rem; + width: 100vw; + height: 100vh; +} + +.category { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + gap: 2.5rem; + margin-top: 7.5px; + padding: 0.5rem; + width: 90%; + border-bottom: 1px solid #434b54; + margin-inline: auto; +} + +.category p { + color: #434b54; + font-size: 1.5em; + font-weight: normal; + padding: 0 !important; +} + +.categories .category:last-child { + border-bottom: none; +} + +.category-details { + display: flex; + flex-direction: column; + justify-content: center; + width: 100%; +} + +.category-details a { + font-size: 1.65rem; + color: #000; + text-decoration: none; + font-weight: bold; +} + +.category-details span { + font-size: 1.35rem; + font-weight: normal; +} + +.category img { + width: 70px; + height: 70px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +.total-amount { + display: flex; + justify-content: space-between; + width: 100%; + padding: 0.5rem; + border-bottom: 2px solid #434b54; +} + +.total-amount p { + margin: 1rem 1.5rem 0 0; + align-self: flex-end; + font-size: 1.75rem; + font-weight: bold; +} + +.no-record { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + margin-top: 2rem; + font-size: 1.25rem; + font-weight: bold; + color: green; +} + .footer { display: flex; flex-direction: row; diff --git a/app/models/user.rb b/app/models/user.rb index 91d28fc..1983a52 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -4,8 +4,8 @@ class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable - has_many categories, dependent: :destroy - has_many entities, dependent: :destroy + has_many :categories, dependent: :destroy + has_many :entities, dependent: :destroy validates :name, presence: true, uniqueness: true, length: { minimum: 3, maximum: 60 } end diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 8d254ed..7cbc569 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -1,7 +1,7 @@ <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
      - +

      LOG IN

      <%= f.submit "Log In", class:"login-btn"%> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index e586610..18f3dee 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -8,6 +8,7 @@ <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %> + diff --git a/db/migrate/20230921223926_create_entities.rb b/db/migrate/20230922072553_create_entities.rb similarity index 75% rename from db/migrate/20230921223926_create_entities.rb rename to db/migrate/20230922072553_create_entities.rb index 94f86c2..2c87230 100644 --- a/db/migrate/20230921223926_create_entities.rb +++ b/db/migrate/20230922072553_create_entities.rb @@ -3,10 +3,10 @@ def change create_table :entities do |t| t.string :name t.decimal :amount - t.references :user, null: false, foreign_key: { to_table: :users} + t.references :author, null: false, foreign_key: { to_table: :users } t.references :category, null: false, foreign_key: true t.timestamps end end -end +end \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index 3e79c70..9b02362 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[7.0].define(version: 2023_09_21_225144) do +ActiveRecord::Schema[7.0].define(version: 2023_09_22_072553) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -26,12 +26,12 @@ create_table "entities", force: :cascade do |t| t.string "name" t.decimal "amount" - t.bigint "user_id", null: false + t.bigint "author_id", null: false t.bigint "category_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.index ["author_id"], name: "index_entities_on_author_id" t.index ["category_id"], name: "index_entities_on_category_id" - t.index ["user_id"], name: "index_entities_on_user_id" end create_table "users", force: :cascade do |t| @@ -49,5 +49,5 @@ add_foreign_key "categories", "users", column: "author_id" add_foreign_key "entities", "categories" - add_foreign_key "entities", "users" + add_foreign_key "entities", "users", column: "author_id" end From e1cff9e4b333cb207c3902cadafeb64e84c31cff Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 14:51:27 +0300 Subject: [PATCH 19/54] enable confirmable module --- app/models/user.rb | 2 +- db/migrate/20230921225144_add_devise_to_users.rb | 8 ++++---- db/schema.rb | 4 ++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 1983a52..a70aafa 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,7 +1,7 @@ class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable - devise :database_authenticatable, :registerable, + devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :validatable has_many :categories, dependent: :destroy diff --git a/db/migrate/20230921225144_add_devise_to_users.rb b/db/migrate/20230921225144_add_devise_to_users.rb index 98ed9dc..d143713 100644 --- a/db/migrate/20230921225144_add_devise_to_users.rb +++ b/db/migrate/20230921225144_add_devise_to_users.rb @@ -22,10 +22,10 @@ def self.up # 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 + 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 diff --git a/db/schema.rb b/db/schema.rb index 9b02362..f705f81 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -43,6 +43,10 @@ t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" + t.string "confirmation_token" + t.datetime "confirmed_at" + t.datetime "confirmation_sent_at" + t.string "unconfirmed_email" 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 21752f65779bc34f3e26ab74f3289e47eeefb9e7 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 14:51:36 +0300 Subject: [PATCH 20/54] permit name attribute for User model --- app/controllers/application_controller.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09705d1..ee9c84a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,12 @@ class ApplicationController < ActionController::Base + protect_from_forgery with: :exception + before_action :configure_permitted_parameters, if: :devise_controller? + + protected + + def configure_permitted_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: %i[name email password password_confirmation]) + devise_parameter_sanitizer.permit(:account_update, + keys: %i[name email password password_confirmation current_password]) + end end From 69e4a811b5b83f27ad309b6d53d339988e0f7b0b Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 14:51:59 +0300 Subject: [PATCH 21/54] add categories and entities route --- app/controllers/home_controller.rb | 4 +++- config/routes.rb | 12 +++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 7b56dd3..69fb976 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,3 +1,5 @@ class HomeController < ApplicationController - def index; end + def index + redirect_to categories_path if user_signed_in? + end end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index ecf33f0..3a081d3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,13 @@ Rails.application.routes.draw do - devise_for :users - # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html - # Defines the root path route ("/") + resources :categories do + resources :entities + end + + devise_scope :user do + get 'logout', to: 'devise/sessions#destroy' + end + + devise_for :users root "home#index" end From 6f5133c73dae895b08ff5d736e4ef7f1405376f7 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 14:52:24 +0300 Subject: [PATCH 22/54] create Categories_controller and add actions --- app/controllers/categories_controller.rb | 67 ++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 app/controllers/categories_controller.rb diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb new file mode 100644 index 0000000..7a6055c --- /dev/null +++ b/app/controllers/categories_controller.rb @@ -0,0 +1,67 @@ +class CategoriesController < ApplicationController + before_action :set_category, only: %i[show edit update destroy] + before_action :authenticate_user! + + def index + @categories = Category.where(author_id: current_user.id).order(created_at: :desc) || [] + end + + def show + @categories = Category.includes(:entities).find(params[:id]) + @entities = @category.entities.order(created_at: :desc) + end + + def new + @category = Category.new + end + + def create + @category = Category.new(category_params) + @category.author_id = current_user.id + + respond_to do |format| + if @category.save + format.html { redirect_to categories_path(@category.id), notice: 'Category was successfully created.' } + else + format.html { render :new, status: :unprocessable_entity } + end + end + end + + def edit; end + + # PATCH/PUT /categories/1 or /categories/1.json + def update + respond_to do |format| + if @category.update(category_params) + format.html { redirect_to category_url(@category), notice: 'Category was successfully updated.' } + format.json { render :show, status: :ok, location: @category } + else + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @category.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /categories/1 or /categories/1.json + def destroy + @category = Category.find(params[:id]) + respond_to do |format| + if @category.destroy + format.html { redirect_to categories_path, notice: 'Category was successfully destroyed.' } + else + format.html { redirect_to categories_path, alert: 'Category was not destroyed.' } + end + end + end + + private + + def set_category + @category = current_user.categories.find(params[:id]) + end + + def category_params + params.require(:category).permit(:name, :icon) + end +end From f72ebfbe632717ee121c9c5374dc3c6e83450c18 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 14:52:58 +0300 Subject: [PATCH 23/54] create and format Categories view --- app/views/categories/index.html.erb | 40 +++++++++++++++++++++++++++ app/views/categories/mobile-menu.html | 27 ++++++++++++++++++ app/views/categories/new.html.erb | 32 +++++++++++++++++++++ app/views/categories/show.html.erb | 8 ++++++ 4 files changed, 107 insertions(+) create mode 100644 app/views/categories/index.html.erb create mode 100644 app/views/categories/mobile-menu.html create mode 100644 app/views/categories/new.html.erb create mode 100644 app/views/categories/show.html.erb diff --git a/app/views/categories/index.html.erb b/app/views/categories/index.html.erb new file mode 100644 index 0000000..a9c4746 --- /dev/null +++ b/app/views/categories/index.html.erb @@ -0,0 +1,40 @@ +
      +

      CARTEGORIES

      + Logout +
      + +
      + <% if @categories.nil? || @categories.empty? %> +
      +

      No Record in the Database.

      +

      Please Add a Category

      +
      + <% else %> + <% @categories.each do |category| %> +
      + <%= link_to category_entities_path(category.id) do %> + <% def valid_image_url?(url) %> + <% url.present? && url =~ /\Ahttps?:\/\/\S+\z/ %> + <% end %> + + <% if valid_image_url?(category.icon) %> + <%= image_tag category.icon, alt: 'Category image' %> + <% else %> + <%= image_tag 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTJcT7PQqrhP-fcTi2K3UTvx1CZMmLvFPvupUZivUCJMFDRsX4LxhTRuenl5VHpbhs7elI&usqp=CAU', alt: 'My Image' %> + <% end %> + <% end %> + +
      + <%= link_to category.name.capitalize, category_entities_path(category.id) %> + Total: <%= category.total_transactions%>$ +
      +
      + <% end %> + <% end %> +
      + + + diff --git a/app/views/categories/mobile-menu.html b/app/views/categories/mobile-menu.html new file mode 100644 index 0000000..0aec37d --- /dev/null +++ b/app/views/categories/mobile-menu.html @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/app/views/categories/new.html.erb b/app/views/categories/new.html.erb new file mode 100644 index 0000000..8eed2cc --- /dev/null +++ b/app/views/categories/new.html.erb @@ -0,0 +1,32 @@ +
      + + + +

      New Category

      + + Logout + +
      + +<% if @category.errors.any? %> +
      +

      + <%= pluralize(@category.errors.count, "error") %> + prohibited this recipe from being saved: +

      + +
        + <% @category.errors.each do |error| %> +
      • <%= error.full_message %>
      • + <% end %> +
      +
      +<% end %> + +<%= form_with(model: @category, url: categories_path) do |form| %> +
      + <%= form.text_field :name, placeholder: "Name" %> + <%= form.text_field :icon, placeholder: "Icon Link" %> + <%= form.submit "Save", class: "submit-button" %> +
      +<% end %> \ No newline at end of file diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb new file mode 100644 index 0000000..574bbd6 --- /dev/null +++ b/app/views/categories/show.html.erb @@ -0,0 +1,8 @@ +<%= link_to "Back", categories_path%> + +<% render @category%> + +
      + <%= link_to, edit_category_path(@category)%> + <% link_to "Destroy", @category, method: :delete %> +
      \ No newline at end of file From 4e92ea570efae85369fb53e9fa5880d14b182c48 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 14:53:15 +0300 Subject: [PATCH 24/54] create Entities_controller and add actions --- app/controllers/entities_controller.rb | 77 ++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 app/controllers/entities_controller.rb diff --git a/app/controllers/entities_controller.rb b/app/controllers/entities_controller.rb new file mode 100644 index 0000000..80e18ea --- /dev/null +++ b/app/controllers/entities_controller.rb @@ -0,0 +1,77 @@ +class EntitiesController < ApplicationController + before_action :set_entity, only: %i[show edit update destroy] + + # GET /entities or /entities.json + def index + @category = Category.find(params[:category_id]) + @entities = Entity.where(category_id: @category.id).order('created_at DESC') + end + + # GET /entities/1 or /entities/1.json + def show; end + + # GET /entities/new + def new + @entity = Entity.new + @categories = Category.where(author_id: current_user.id) || [] + @category_id = params[:category_id].to_i + end + + # POST /entities or /entities.json + def create + @entity = Entity.new(entity_params) + @entity.author_id = current_user.id + @entity.category_id = params[:category_id].to_i + + respond_to do |format| + if @entity.save + format.html do + redirect_to category_entities_path, + notice: 'Transaction was successfully created.' + end + format.json { render :show, status: :created, location: @entity } + else + format.html { render :new, status: :unprocessable_entity } + format.json { render json: @entity.errors, status: :unprocessable_entity } + end + end + end + + # GET /entities/1/edit + def edit; end + + # PATCH/PUT /entities/1 or /entities/1.json + def update + respond_to do |format| + if @entity.update(entity_params) + format.html { redirect_to entity_url(@entity), notice: 'Entity was successfully updated.' } + format.json { render :show, status: :ok, location: @entity } + else + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @entity.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /entities/1 or /entities/1.json + def destroy + @entity.destroy + + respond_to do |format| + format.html { redirect_to entities_url, notice: 'Entity was successfully destroyed.' } + format.json { head :no_content } + end + end + + private + + # Use callbacks to share common setup or constraints between actions. + def set_entity + @entity = Entity.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def entity_params + params.require(:entity).permit(:name, :amount, :category_id, category_ids: []).merge(author_id: current_user.id) + end +end From 822f34749db500582035f3ae8aa9d52b318fcc0a Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 14:53:35 +0300 Subject: [PATCH 25/54] create Entity views --- app/views/entities/index.html.erb | 34 +++++++++++++++++++++++++++++++ app/views/entities/new.html.erb | 32 +++++++++++++++++++++++++++++ app/views/entities/show.html.erb | 8 ++++++++ 3 files changed, 74 insertions(+) create mode 100644 app/views/entities/index.html.erb create mode 100644 app/views/entities/new.html.erb create mode 100644 app/views/entities/show.html.erb diff --git a/app/views/entities/index.html.erb b/app/views/entities/index.html.erb new file mode 100644 index 0000000..7d3cbef --- /dev/null +++ b/app/views/entities/index.html.erb @@ -0,0 +1,34 @@ +
      + + + +

      Transactions

      + Logout +
      + +
      + <% if @entities.nil? || @entities.empty?%> +
      +

      No Transaction for this Category.

      +

      Please Add a Transaction

      +
      + <% else %> +
      +

      Total:

      +

      <%= @category.total_transactions%>$

      +
      + + <% @entities.each do |entity| %> +
      +

      <%= entity.name %>

      +

      Price: <%= entity.amount %>$

      +
      + <% end %> + <% end %> +
      + + + diff --git a/app/views/entities/new.html.erb b/app/views/entities/new.html.erb new file mode 100644 index 0000000..e3d377c --- /dev/null +++ b/app/views/entities/new.html.erb @@ -0,0 +1,32 @@ +<%= form_with(model: @entity, url: category_entities_path) do |form| %> +
      + + + +

      New Transaction

      + + Logout + +
      + + <% if @entity.errors.any? %> +
      +

      + <%= pluralize(@entity.errors.count, "error") %> + prohibited this recipe from being saved: +

      + +
        + <% @entity.errors.each do |error| %> +
      • <%= error.full_message %>
      • + <% end %> +
      +
      + <% end %> + +
      + <%= form.text_field :name, placeholder: " Name "%> + <%= form.text_field :amount, placeholder: " Price "%> + <%= form.submit "Save", class: "submit-button" %> +
      +<% end %> diff --git a/app/views/entities/show.html.erb b/app/views/entities/show.html.erb new file mode 100644 index 0000000..0a1ff7f --- /dev/null +++ b/app/views/entities/show.html.erb @@ -0,0 +1,8 @@ +<%= render @payment %> + +
      + <%= link_to "Edit", edit_payment_path(@payment) %> | + <%= link_to "Back", payments_path %> + + <%= button_to "Destroy", @payment, method: :delete %> +
      \ No newline at end of file From 9dac5885864e7437a361e975f3ea3c7ebe1ce1b3 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 15:11:54 +0300 Subject: [PATCH 26/54] add fontawesome CDN link --- app/views/layouts/application.html.erb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 18f3dee..a077803 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,17 +1,18 @@ - RailsBudgetAppV2 + Rails Budget App <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %> - <%= yield %> + + From 006d6e30653cf481df4f4d045e054de318e30b1e Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 16:43:32 +0300 Subject: [PATCH 27/54] fix ruby and style linter errors --- Gemfile | 4 +- app/controllers/home_controller.rb | 2 +- app/mailers/application_mailer.rb | 4 +- app/models/user.rb | 4 +- app/views/categories/new.html.erb | 34 +++--- app/views/devise/confirmations/new.html.erb | 25 +++-- app/views/devise/passwords/new.html.erb | 23 ++-- app/views/devise/registrations/new.html.erb | 8 +- app/views/devise/sessions/new.html.erb | 28 ++--- app/views/layouts/application.html.erb | 4 +- config.ru | 2 +- spec/rails_helper.rb | 4 +- spec/spec_helper.rb | 100 +++++++++--------- test/application_system_test_case.rb | 2 +- .../application_cable/connection_test.rb | 2 +- test/test_helper.rb | 6 +- 16 files changed, 129 insertions(+), 123 deletions(-) diff --git a/Gemfile b/Gemfile index 7c02a22..67b6f71 100644 --- a/Gemfile +++ b/Gemfile @@ -56,9 +56,9 @@ end group :development do # Use console on exceptions pages [https://github.com/rails/web-console] - gem 'web-console' gem 'rspec-rails' - + gem 'web-console' + # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler] # gem "rack-mini-profiler" diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 69fb976..90021a6 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -2,4 +2,4 @@ class HomeController < ApplicationController def index redirect_to categories_path if user_signed_in? end -end \ No newline at end of file +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 3c34c81..286b223 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,4 +1,4 @@ class ApplicationMailer < ActionMailer::Base - default from: "from@example.com" - layout "mailer" + default from: 'from@example.com' + layout 'mailer' end diff --git a/app/models/user.rb b/app/models/user.rb index a70aafa..ee174c7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -4,8 +4,8 @@ class User < ApplicationRecord devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :validatable - has_many :categories, dependent: :destroy - has_many :entities, dependent: :destroy + has_many :categories, dependent: :destroy + has_many :entities, dependent: :destroy validates :name, presence: true, uniqueness: true, length: { minimum: 3, maximum: 60 } end diff --git a/app/views/categories/new.html.erb b/app/views/categories/new.html.erb index 8eed2cc..0622728 100644 --- a/app/views/categories/new.html.erb +++ b/app/views/categories/new.html.erb @@ -8,25 +8,25 @@
      -<% if @category.errors.any? %> -
      -

      - <%= pluralize(@category.errors.count, "error") %> - prohibited this recipe from being saved: -

      +
      + <% if @category.errors.any? %> +
      +

      + <%= pluralize(@category.errors.count, "error") %> + prohibited this recipe from being saved: +

      -
        - <% @category.errors.each do |error| %> -
      • <%= error.full_message %>
      • - <% end %> -
      -
      -<% end %> +
        + <% @category.errors.each do |error| %> +
      • <%= error.full_message %>
      • + <% end %> +
      +
      + <% end %> -<%= form_with(model: @category, url: categories_path) do |form| %> -
      + <%= form_with(model: @category, url: categories_path) do |form| %> <%= form.text_field :name, placeholder: "Name" %> <%= form.text_field :icon, placeholder: "Icon Link" %> <%= form.submit "Save", class: "submit-button" %> -
      -<% end %> \ No newline at end of file + <% end %> +
      \ No newline at end of file diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb index b12dd0c..b46f660 100644 --- a/app/views/devise/confirmations/new.html.erb +++ b/app/views/devise/confirmations/new.html.erb @@ -1,16 +1,23 @@ -

      Resend confirmation instructions

      - <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> +
      + + + +

      Password Reset

      + <%= f.submit "Send", class:"login-btn"%> +
      + +
      +

      Resend confirmation instructions

      +
      + <%= render "devise/shared/error_messages", resource: resource %>
      - <%= 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" %> + <%= f.email_field :email, autofocus: true, autocomplete: "email", placeholder: "Email Address", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
      <% end %> -<%= render "devise/shared/links" %> + diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb index e7d52d5..82e9da0 100644 --- a/app/views/devise/passwords/new.html.erb +++ b/app/views/devise/passwords/new.html.erb @@ -1,16 +1,15 @@ - - <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> -
      - - - -

      Password Reset

      - <%= f.submit "Send", class:"login-btn"%> -
      -
      -

      Forgot your password?

      -
      +
      + + + +

      Password Reset

      + <%= f.submit "Send", class:"login-btn"%> +
      + +
      +

      Forgot your password?

      +
      <%= render "devise/shared/error_messages", resource: resource %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index 12b310e..40c44de 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -1,8 +1,8 @@ <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
      - -

      REGISTER

      - <%= f.submit "Next", class:"login-btn"%> + +

      REGISTER

      + <%= f.submit "Next", class:"login-btn"%>
      <%= render "devise/shared/error_messages", resource: resource %> @@ -24,4 +24,4 @@ <%= f.password_field :password_confirmation, autocomplete: "new-password", placeholder: " Comfirm Password"%>
-<% end %> \ No newline at end of file +<% end %> diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 7cbc569..2a8bd6b 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -1,25 +1,25 @@ <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> -
- +
+ - -

LOG IN

- <%= f.submit "Log In", class:"login-btn"%> -
+ +

LOG IN

+ <%= f.submit "Log In", class:"login-btn"%> +
+ <% else %>
-

Total:

-

<%= @category.total_transactions%>$

+

Total: <%= @category.total_transactions%>$

<% @entities.each do |entity| %> -
+

<%= entity.name %>

Price: <%= entity.amount %>$

From f4419bbe3f510b7f377b3ef591d186b75a9e4d1b Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 21:52:34 +0300 Subject: [PATCH 30/54] add locale error message and display inline with form elment --- app/assets/stylesheets/application.css | 16 ++++---- app/models/entity.rb | 2 +- app/views/categories/new.html.erb | 31 ++++++++-------- app/views/devise/registrations/new.html.erb | 14 ++++++- app/views/devise/sessions/new.html.erb | 12 +++--- app/views/entities/new.html.erb | 31 ++++++++-------- config/locales/en.yml | 41 ++++++++++++++++++++- 7 files changed, 100 insertions(+), 47 deletions(-) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index e763a56..d88559b 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -158,10 +158,14 @@ a { .form-container input { width: 98%; - padding: 0.5em 1em; + padding: 0.5em; margin-bottom: 10px; } +.form-element { + margin-bottom: 15px; +} + .field_with_errors input[type="text"] { border: 1px solid red; } @@ -195,7 +199,7 @@ a { .field input[type="password"], .field input[type="checkbox"] { width: 100%; - padding: 12px; + padding: 0.75em 0.5em; border: none; border-bottom: 2px solid #999; background-color: #fff; @@ -207,10 +211,6 @@ a { border-bottom-color: #434b54; } -.alert { - text-align: center; -} - #error_explanation h2 { color: red; font-size: 1.15rem; @@ -221,8 +221,8 @@ a { .error_msg, .alert { color: red; - font-size: 1rem; - margin-top: 5px; + font-size: 0.85rem; + margin-top: 0.15rem; } .nav-item { diff --git a/app/models/entity.rb b/app/models/entity.rb index 44c5241..b0937a9 100644 --- a/app/models/entity.rb +++ b/app/models/entity.rb @@ -3,5 +3,5 @@ class Entity < ApplicationRecord belongs_to :category, class_name: 'Category', foreign_key: 'category_id' validates :name, presence: true, uniqueness: true, length: { minimum: 3, maximum: 60 } - validates :amount, presence: true, numericality: { greater_than: 0 }, length: { minimum: 1, maximum: 100 } + validates :amount, presence: true, numericality: { greater_than: 0, less_than: 10**5 } end diff --git a/app/views/categories/new.html.erb b/app/views/categories/new.html.erb index 0622728..aadb486 100644 --- a/app/views/categories/new.html.erb +++ b/app/views/categories/new.html.erb @@ -9,24 +9,25 @@
- <% if @category.errors.any? %> -
-

- <%= pluralize(@category.errors.count, "error") %> - prohibited this recipe from being saved: -

+ <%= form_with(model: @category, url: categories_path) do |form| %> +
+ <%= form.text_field :name, placeholder: "Name" %> + <% if @category.errors.any? %> + + <%= @category.errors[:name].first %> + + <% end %> +
-
    - <% @category.errors.each do |error| %> -
  • <%= error.full_message %>
  • - <% end %> -
+
+ <%= form.text_field :icon, placeholder: "Icon Link" %> + <% if @category.errors.any? %> + + <%= @category.errors[:icon].first %> + + <% end %>
- <% end %> - <%= form_with(model: @category, url: categories_path) do |form| %> - <%= form.text_field :name, placeholder: "Name" %> - <%= form.text_field :icon, placeholder: "Icon Link" %> <%= form.submit "Save", class: "submit-button" %> <% end %>
\ No newline at end of file diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index 40c44de..f271d12 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -5,23 +5,33 @@ <%= f.submit "Next", class:"login-btn"%>
- <%= render "devise/shared/error_messages", resource: resource %> - <% end %> diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 2a8bd6b..8ec4276 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -15,11 +15,13 @@
<%= f.password_field :password, autocomplete: "current-password", placeholder: "Password" %>
+

<%= alert %>

+ + +
<% end %> - - -

<%= alert %>

+ \ No newline at end of file diff --git a/app/views/entities/new.html.erb b/app/views/entities/new.html.erb index e3d377c..d5ab9d7 100644 --- a/app/views/entities/new.html.erb +++ b/app/views/entities/new.html.erb @@ -9,24 +9,25 @@ - <% if @entity.errors.any? %> -
-

- <%= pluralize(@entity.errors.count, "error") %> - prohibited this recipe from being saved: -

+
+
+ <%= form.text_field :name, placeholder: "Name "%> + <% if @entity.errors.any? %> + + <%= @entity.errors[:name].first %> + + <% end %> +
-
    - <% @entity.errors.each do |error| %> -
  • <%= error.full_message %>
  • - <% end %> -
+
+ <%= form.text_field :amount, placeholder: "Price "%> + <% if @entity.errors.any? %> + + <%= @entity.errors[:amount].first %> + + <% end %>
- <% end %> -
- <%= form.text_field :name, placeholder: " Name "%> - <%= form.text_field :amount, placeholder: " Price "%> <%= form.submit "Save", class: "submit-button" %>
<% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 8ca56fc..15364ec 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -30,4 +30,43 @@ # available at https://guides.rubyonrails.org/i18n.html. en: - hello: "Hello world" + activerecord: + errors: + models: + user: + attributes: + name: + blank: "Name cannot be blank" + too_short: "Name must be at least %{count} characters long" + too_long: "Name must be at most %{count} characters long" + taken: "Name already in use" + email: + blank: "Email cannot be blank" + taken: "Email already in use" + password: + blank: "Password cannot be blank" + too_short: "Password must be at least %{count} characters long" + password_confirmation: + blank: "Password confirmation cannot be blank" + confirmation: "Passwords do not match" + category: + attributes: + name: + blank: "Name cannot be blank" + too_short: "Name must be at least %{count} characters long" + too_long: "Name must be at most %{count} characters long" + taken: "Name already in use" + icon: + blank: "Icon cannot be blank" + entity: + attributes: + name: + blank: "Name cannot be blank" + too_short: "Name must be at least %{count} characters long" + too_long: "Name must be at most %{count} characters long" + taken: "Name already in use" + amount: + blank: "Amount cannot be blank" + not_a_number: "Amount must be a number" + greater_than: "Amount must be greater than %{count}" + less_than: "Amount must be less than %{count}" \ No newline at end of file From 04ffff2a30c03c48eefec09b31639eac412fcdb5 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 22:35:05 +0300 Subject: [PATCH 31/54] install testing gem and configure rspec --- Gemfile | 4 ++++ Gemfile.lock | 9 +++++++++ spec/rails_helper.rb | 6 +++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 67b6f71..5a264d8 100644 --- a/Gemfile +++ b/Gemfile @@ -78,3 +78,7 @@ gem 'rubocop', '>= 1.0', '< 2.0' gem 'devise', '~> 4.9' gem 'cancancan', '~> 3.5' + +gem 'factory_bot_rails', '~> 6.2' + +gem 'faker', '~> 3.2' diff --git a/Gemfile.lock b/Gemfile.lock index a548922..70c62fd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,6 +99,13 @@ GEM warden (~> 1.2.3) diff-lcs (1.5.0) erubi (1.12.0) + factory_bot (6.2.1) + activesupport (>= 5.0.0) + factory_bot_rails (6.2.0) + factory_bot (~> 6.2.0) + railties (>= 5.0.0) + faker (3.2.1) + i18n (>= 1.8.11, < 2) globalid (1.2.1) activesupport (>= 6.1) i18n (1.14.1) @@ -282,6 +289,8 @@ DEPENDENCIES capybara debug devise (~> 4.9) + factory_bot_rails (~> 6.2) + faker (~> 3.2) importmap-rails jbuilder pg diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 6584c98..2c6da56 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -5,6 +5,7 @@ # Prevent database truncation if the environment is production abort('The Rails environment is running in production mode!') if Rails.env.production? require 'rspec/rails' +require_relative 'support/factory_bot' # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in @@ -20,7 +21,7 @@ # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # -# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } +Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. @@ -30,6 +31,9 @@ abort e.to_s.strip end RSpec.configure do |config| + config.include Devise::Test::IntegrationHelpers, type: :request + config.include Devise::Test::IntegrationHelpers, type: :feature + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{Rails.root}/spec/fixtures" From f834cc7bdac82a82ad19bd948679a56778187ba3 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 22:36:25 +0300 Subject: [PATCH 32/54] create factoryBot for user model --- spec/factories/users.rb | 8 ++++++++ spec/support/factory_bot.rb | 3 +++ 2 files changed, 11 insertions(+) create mode 100644 spec/factories/users.rb create mode 100644 spec/support/factory_bot.rb diff --git a/spec/factories/users.rb b/spec/factories/users.rb new file mode 100644 index 0000000..ab436fc --- /dev/null +++ b/spec/factories/users.rb @@ -0,0 +1,8 @@ +FactoryBot.define do + factory :user do + sequence(:name) { |n| "User #{n}" } + email { Faker::Internet.email } + password { 'password123' } + password_confirmation { 'password123' } + end +end diff --git a/spec/support/factory_bot.rb b/spec/support/factory_bot.rb new file mode 100644 index 0000000..c7890e4 --- /dev/null +++ b/spec/support/factory_bot.rb @@ -0,0 +1,3 @@ +RSpec.configure do |config| + config.include FactoryBot::Syntax::Methods +end From a291f23f35f1d97ab7b2913ff191bdf7d033b6a6 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 22:57:18 +0300 Subject: [PATCH 33/54] add a user model unit test --- app/controllers/application_controller.rb | 4 ++ spec/models/user_spec.rb | 67 ++++++++++++++++++++++- spec/rails_helper.rb | 3 +- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ee9c84a..3b41460 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,6 +1,10 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception before_action :configure_permitted_parameters, if: :devise_controller? + + def after_sign_out_path_for(*) + new_user_session_path + end protected diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 47a31bb..e421ac8 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,5 +1,70 @@ +# Test for User model +# spec/modules/user_spec.rb +# require 'rails_helper' RSpec.describe User, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + + let(:user) { FactoryBot.build(:user) } + + it 'is valid with valid attributes' do + expect(user).to be_valid + end + + it 'is not valid without a name' do + user.name = nil + expect(user).to_not be_valid + end + + it 'is not valid with a name longer than 60 characters' do + user.name = ('a' * 61) + expect(user).to_not be_valid + end + + it 'is valid with a name of 60 characters or less' do + user.name = ('a' * 60) + expect(user).to be_valid + + user.name = ('a' * 40) + expect(user).to be_valid + end + + it 'is not valid with a name of less than characters' do + user.name = 'a' + expect(user).to_not be_valid + end + + it 'is not valid with a password shorter than 6 characters' do + user.name = 'John Doe' + user.password = '12345' + user.password_confirmation = '12345' + expect(user).to_not be_valid + end + + it 'is valid with a password of 6 characters or more' do + user.password = '1234569' + user.password_confirmation = '1234569' + expect(user).to be_valid + end + + it 'is not valid with a password confirmation that does not match' do + user.password = '1234569' + user.password_confirmation = '1234567' + expect(user).to_not be_valid + end + + it 'is valid with a password confirmation that matches' do + user.password = '1234569' + user.password_confirmation = '1234569' + expect(user).to be_valid + end + + describe 'Associations' do + it 'should have many categories and entities' do + user = User.reflect_on_association(:categories) + expect(user.macro).to eq(:has_many) + user = User.reflect_on_association(:entities) + expect(user.macro).to eq(:has_many) + end + end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 2c6da56..8074ae4 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -6,8 +6,8 @@ abort('The Rails environment is running in production mode!') if Rails.env.production? require 'rspec/rails' require_relative 'support/factory_bot' -# Add additional requires below this line. Rails is not loaded until this point! +# Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # run as spec files by default. This means that files in spec/support that end @@ -30,6 +30,7 @@ rescue ActiveRecord::PendingMigrationError => e abort e.to_s.strip end + RSpec.configure do |config| config.include Devise::Test::IntegrationHelpers, type: :request config.include Devise::Test::IntegrationHelpers, type: :feature From ce3aa8de84a8c3764d1e255ab31ce554ebf61df3 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Fri, 22 Sep 2023 23:58:16 +0300 Subject: [PATCH 34/54] add unit test for category model --- spec/models/category_spec.rb | 80 +++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index 0c9fefa..56043f5 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -1,5 +1,83 @@ +# Tests for Category model +# /spec/models/category_spec.rb +# require 'rails_helper' RSpec.describe Category, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + let (:user) { FactoryBot.build(:user) } + let (:category) { Category.new(name: 'Test Category', icon: 'fa fa-bolt', author: user) } + + + it 'is valid with valid attributes' do + expect(category).to be_valid + end + + it 'is not valid without a name' do + category.name = nil + expect(category).to_not be_valid + end + + it 'is not valid with a name longer than 60 characters' do + category.name = ('a' * 61) + expect(category).to_not be_valid + end + + it 'is valid with a name of 60 characters or less' do + category.name = ('a' * 60) + expect(category).to be_valid + + category.name = ('a' * 40) + expect(category).to be_valid + end + + it 'is not valid with a name of less than 3 characters' do + category.name = 'a' + expect(category).to_not be_valid + end + + it 'is valid with name of length between 3 and 60 characters' do + category.name = 'aaa' + expect(category).to be_valid + + category.name = 'a' * 59 + expect(category).to be_valid + end + + it 'is not valid without an icon' do + category.icon = nil + expect(category).to_not be_valid + end + + it 'is valid with a default icon' do + category.icon = Category.default_icon + expect(category).to be_valid + end + + it 'is valid with a custom icon' do + category.icon = 'fa fa-bolt' + expect(category).to be_valid + end + + it 'is not valid without an author' do + category.author = nil + expect(category).to_not be_valid + end + + it 'is valid with an author' do + category.author = user + expect(category).to be_valid + end + + + describe 'Associations' do + it 'should belong to author' do + category = Category.reflect_on_association(:author) + expect(category.macro).to eq(:belongs_to) + end + + it 'should have many entities' do + category = Category.reflect_on_association(:entities) + expect(category.macro).to eq(:has_many) + end + end end From 2fdaf3c2cd6302979d36c7fd36989d8393042130 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 00:01:50 +0300 Subject: [PATCH 35/54] create unit test for Entity model --- app/models/entity.rb | 2 +- spec/models/entity_spec.rb | 91 +++++++++++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/app/models/entity.rb b/app/models/entity.rb index b0937a9..6b7b116 100644 --- a/app/models/entity.rb +++ b/app/models/entity.rb @@ -3,5 +3,5 @@ class Entity < ApplicationRecord belongs_to :category, class_name: 'Category', foreign_key: 'category_id' validates :name, presence: true, uniqueness: true, length: { minimum: 3, maximum: 60 } - validates :amount, presence: true, numericality: { greater_than: 0, less_than: 10**5 } + validates :amount, presence: true, numericality: { greater_than: 0, less_than_or_equal_to: 10**5 } end diff --git a/spec/models/entity_spec.rb b/spec/models/entity_spec.rb index f513ed6..a3ac7fe 100644 --- a/spec/models/entity_spec.rb +++ b/spec/models/entity_spec.rb @@ -1,5 +1,94 @@ +# Tests for the Entity model +# /spec/models/entity_spec.rb +# require 'rails_helper' RSpec.describe Entity, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + let (:user) { FactoryBot.build(:user) } + let (:category) { Category.new(name: 'Test Category', icon: 'fa fa-bolt', author: user) } + let (:entity) { Entity.new(name: 'Test Entity', amount: 453, category: category, author: user) } + + it 'is valid with valid attributes' do + expect(entity).to be_valid + end + + it 'is not valid without a name' do + entity.name = nil + expect(entity).to_not be_valid + end + + it 'is not valid with a name longer than 60 characters' do + entity.name = ('a' * 61) + expect(entity).to_not be_valid + end + + it 'is not valid with a name of less than 3 characters' do + entity.name = 'a' + expect(entity).to_not be_valid + end + + it 'is valid with name of length between 3 and 60 characters' do + entity.name = 'aaa' + expect(entity).to be_valid + + entity.name = 'a' * 59 + expect(entity).to be_valid + end + + it 'is not valid without an amount' do + entity.amount = nil + expect(entity).to_not be_valid + end + + it 'is not valid with an amount less than 0' do + entity.amount = -1 + expect(entity).to_not be_valid + end + + it 'is not valid with an amount of 0' do + entity.amount = 0 + expect(entity).to_not be_valid + end + + it 'is not valid with an amount greater than 10**5' do + entity.amount = 10**5 + 1 + expect(entity).to_not be_valid + end + + it 'is valid with an amount between 1 and 10**5 or less' do + entity.amount = 10**5 + expect(entity).to be_valid + + entity.amount = 10**4 + expect(entity).to be_valid + end + + it 'is not valid without a category' do + entity.category = nil + expect(entity).to_not be_valid + end + + it 'is valid with a category' do + entity.category = category + expect(entity).to be_valid + end + + it 'is not valid without an author' do + entity.author = nil + expect(entity).to_not be_valid + end + + it 'is valid with an author' do + entity.author = user + expect(entity).to be_valid + end + + describe 'Associations' do + it 'should belong to a category and an author' do + entity = Entity.reflect_on_association(:category) + expect(entity.macro).to eq(:belongs_to) + entity = Entity.reflect_on_association(:author) + expect(entity.macro).to eq(:belongs_to) + end + end end From 62acdeafe0bdecc8c2fb098a5fbae59137987808 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 07:42:31 +0300 Subject: [PATCH 36/54] fix linters error wirh rubocop --- app/controllers/application_controller.rb | 2 +- spec/models/category_spec.rb | 7 +++---- spec/models/entity_spec.rb | 8 ++++---- spec/models/user_spec.rb | 3 +-- spec/rails_helper.rb | 4 ++-- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3b41460..9b2333f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,7 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception before_action :configure_permitted_parameters, if: :devise_controller? - + def after_sign_out_path_for(*) new_user_session_path end diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index 56043f5..bdcbb1e 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -4,9 +4,8 @@ require 'rails_helper' RSpec.describe Category, type: :model do - let (:user) { FactoryBot.build(:user) } - let (:category) { Category.new(name: 'Test Category', icon: 'fa fa-bolt', author: user) } - + let(:user) { FactoryBot.build(:user) } + let(:category) { Category.new(name: 'Test Category', icon: 'fa fa-bolt', author: user) } it 'is valid with valid attributes' do expect(category).to be_valid @@ -68,7 +67,7 @@ expect(category).to be_valid end - + describe 'Associations' do it 'should belong to author' do category = Category.reflect_on_association(:author) diff --git a/spec/models/entity_spec.rb b/spec/models/entity_spec.rb index a3ac7fe..74506aa 100644 --- a/spec/models/entity_spec.rb +++ b/spec/models/entity_spec.rb @@ -4,9 +4,9 @@ require 'rails_helper' RSpec.describe Entity, type: :model do - let (:user) { FactoryBot.build(:user) } - let (:category) { Category.new(name: 'Test Category', icon: 'fa fa-bolt', author: user) } - let (:entity) { Entity.new(name: 'Test Entity', amount: 453, category: category, author: user) } + let(:user) { FactoryBot.build(:user) } + let(:category) { Category.new(name: 'Test Category', icon: 'fa fa-bolt', author: user) } + let(:entity) { Entity.new(name: 'Test Entity', amount: 453, category:, author: user) } it 'is valid with valid attributes' do expect(entity).to be_valid @@ -51,7 +51,7 @@ end it 'is not valid with an amount greater than 10**5' do - entity.amount = 10**5 + 1 + entity.amount = (10**5) + 1 expect(entity).to_not be_valid end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index e421ac8..a834155 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -4,9 +4,8 @@ require 'rails_helper' RSpec.describe User, type: :model do - let(:user) { FactoryBot.build(:user) } - + it 'is valid with valid attributes' do expect(user).to be_valid end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 8074ae4..8e7057b 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -21,7 +21,7 @@ # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # -Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } +Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. @@ -34,7 +34,7 @@ RSpec.configure do |config| config.include Devise::Test::IntegrationHelpers, type: :request config.include Devise::Test::IntegrationHelpers, type: :feature - + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{Rails.root}/spec/fixtures" From b8adcedd731987aca46dd12750c1c584fca41e96 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 19:54:29 +0300 Subject: [PATCH 37/54] add rails-controller-testing gem --- Gemfile | 2 ++ Gemfile.lock | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/Gemfile b/Gemfile index 5a264d8..d4d804a 100644 --- a/Gemfile +++ b/Gemfile @@ -82,3 +82,5 @@ gem 'cancancan', '~> 3.5' gem 'factory_bot_rails', '~> 6.2' gem 'faker', '~> 3.2' + +gem "rails-controller-testing", "~> 1.0" diff --git a/Gemfile.lock b/Gemfile.lock index 70c62fd..411af1c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -177,6 +177,10 @@ GEM activesupport (= 7.0.8) bundler (>= 1.15.0) railties (= 7.0.8) + rails-controller-testing (1.0.5) + actionpack (>= 5.0.1.rc1) + actionview (>= 5.0.1.rc1) + activesupport (>= 5.0.1.rc1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -296,6 +300,7 @@ DEPENDENCIES pg puma (~> 5.0) rails (~> 7.0.6) + rails-controller-testing (~> 1.0) rspec-rails rubocop (>= 1.0, < 2.0) selenium-webdriver From edb348b3b37346ee83f6db1f43956be1a103921f Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 19:56:31 +0300 Subject: [PATCH 38/54] create controllers testing --- app/controllers/categories_controller.rb | 2 +- spec/requests/categories_contoller_spec.rb | 101 ++++++++++++++++++++ spec/requests/entities_controller_spec.rb | 105 +++++++++++++++++++++ spec/requests/users_contoller_spec.rb | 42 +++++++++ 4 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 spec/requests/categories_contoller_spec.rb create mode 100644 spec/requests/entities_controller_spec.rb create mode 100644 spec/requests/users_contoller_spec.rb diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index 7a6055c..45ab7ce 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -21,7 +21,7 @@ def create respond_to do |format| if @category.save - format.html { redirect_to categories_path(@category.id), notice: 'Category was successfully created.' } + format.html { redirect_to categories_path, notice: 'Category was successfully created.' } else format.html { render :new, status: :unprocessable_entity } end diff --git a/spec/requests/categories_contoller_spec.rb b/spec/requests/categories_contoller_spec.rb new file mode 100644 index 0000000..a23601d --- /dev/null +++ b/spec/requests/categories_contoller_spec.rb @@ -0,0 +1,101 @@ +# Test Categories controller +# /spec/requests/categories_controller_spec.rb + +require 'rails_helper' + +RSpec.describe 'Categories', type: :request do + include Devise::Test::IntegrationHelpers + + before do + user = User.new(name: 'User-1', email: 'local@host', password: 'password', password_confirmation: 'password') + user.save + category = Category.new(name: 'Category-1x', icon: 'fa fa-bolt', author: user) + category.save + sign_in user + end + + describe 'GET /categories' do + before do + get categories_path + end + + it 'returns a success response' do + expect(response).to have_http_status(:success) + end + + it 'displays the index template' do + expect(response).to render_template(:index) + end + + it 'includes category name in the response body' do + expect(response.body).to include('Category-1') + end + + it 'inludes Add Category button' do + expect(response.body).to include('Add Category') + end + + it 'should have a link to add new category' do + expect(response.body).to include(new_category_path) + end + end + + describe 'GET /categories/new' do + before do + get new_category_path + end + + it 'returns a success response' do + expect(response).to have_http_status(:success) + end + + it 'displays the new template' do + expect(response).to render_template(:new) + end + + it 'inludes placeholder text in the response body' do + expect(response.body).to include('Name') + expect(response.body).to include('Icon Link') + end + + it 'includes New Category in the response body' do + expect(response.body).to include('New Category') + end + + it 'includes Save button' do + expect(response.body).to include('Save') + end + + it 'should have a link to categories create' do + expect(response.body).to include(categories_path) + end + end + + describe 'Post /categories' do + before do + post categories_path, params: { category: { name: 'Category-1', icon: 'fa fa-bolt' } } + end + + it 'returns a success response' do + expect(response).to have_http_status(302) + end + + it 'should create a new category' do + expect(Category.count).to eq(2) + end + + it 'redirects to categories index' do + expect(response).to redirect_to(categories_path) + end + + it 'displays the index template' do + follow_redirect! + expect(response).to render_template(:index) + end + + it 'includes category name in the response body' do + follow_redirect! + expect(response.body).to include('Category-1') + end + end +end diff --git a/spec/requests/entities_controller_spec.rb b/spec/requests/entities_controller_spec.rb new file mode 100644 index 0000000..52ef3a2 --- /dev/null +++ b/spec/requests/entities_controller_spec.rb @@ -0,0 +1,105 @@ +# Test Entities controller +# /spec/requests/entities_controller_spec.rb + +require 'rails_helper' + +RSpec.describe 'Entities', type: :request do + include Devise::Test::IntegrationHelpers + + before do + user = User.new(name: 'User-1', email: 'local@host', password: 'password', password_confirmation: 'password') + user.save + category = Category.new(name: 'Category-1x', icon: 'fa fa-bolt', author: user) + category.save + entity = Entity.new(name: 'Entity-1x', amount: 2345, author: user, category: category) + entity.save + sign_in user + end + + describe 'GET categories/:category_id/entities' do + before do + get category_entities_path(Category.first) + end + + it 'returns a success response' do + expect(response).to have_http_status(:success) + end + + it 'displays the index template' do + expect(response).to render_template(:index) + end + + it 'includes entity name and amount in the response body' do + expect(response.body).to include('Entity-1') + expect(response.body).to include('2345') + end + + it 'inludes New Transaction button' do + expect(response.body).to include('New Transaction') + end + + it 'should have a link to add new transaction' do + expect(response.body).to include(new_category_entity_path(Category.first)) + end + end + + describe 'GET categories/:category_id/entities/new' do + before do + get new_category_entity_path(Category.first) + end + + it 'returns a success response' do + expect(response).to have_http_status(:success) + end + + it 'displays the new template' do + expect(response).to render_template(:new) + end + + it 'inludes placeholder text in the response body' do + expect(response.body).to include('Name') + expect(response.body).to include('Price') + end + + it 'includes New Transaction in the response body' do + expect(response.body).to include('New Transaction') + end + + it 'includes Save button' do + expect(response.body).to include('Save') + end + + it 'should have a link to categories create' do + expect(response.body).to include(category_entities_path(Category.first)) + end + end + + describe 'Post categories/:category_id/entities' do + before do + post category_entities_path(Category.first), params: { entity: { name: 'Entity-2', amount: 651 } } + end + + it 'returns a success response' do + expect(response).to have_http_status(:redirect) + end + + it 'should create a new entity' do + expect(Entity.count).to eq(2) + end + + it 'should redirect to category entities' do + expect(response).to redirect_to(category_entities_path(Category.first)) + end + + it 'should display index template' do + follow_redirect! + expect(response).to render_template(:index) + end + + it 'should display entity name and amount in the response body' do + follow_redirect! + expect(response.body).to include('Entity-2') + expect(response.body).to include('651') + end + end +end diff --git a/spec/requests/users_contoller_spec.rb b/spec/requests/users_contoller_spec.rb new file mode 100644 index 0000000..96cc192 --- /dev/null +++ b/spec/requests/users_contoller_spec.rb @@ -0,0 +1,42 @@ +# Test users controller +# /spec/requests/users_controller_spec.rb + +require 'rails_helper' + +RSpec.describe 'Users', type: :request do + include Devise::Test::IntegrationHelpers + + describe 'GET /root' do + before do + get root_path + end + + it 'returns a success response' do + expect(response).to have_http_status(:success) + end + + it 'displays the home index template' do + expect(response).to render_template(:index) + end + + it 'includes "Budget Tracker App" in the response body' do + expect(response.body).to include('Budget Tracker App') + end + + it 'includes "Sign in" in the response body' do + expect(response.body).to include('Log In') + end + + it 'includes "Sign up" in the response body' do + expect(response.body).to include('Sign Up') + end + + it 'should have a link to sign up' do + expect(response.body).to include(new_user_registration_path) + end + + it 'should have a link to sign in' do + expect(response.body).to include(new_user_session_path) + end + end +end \ No newline at end of file From 8d85f2ce5389c65fa5853500657cbbd1aa49a3c7 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 19:56:56 +0300 Subject: [PATCH 39/54] fix linter errors --- Gemfile | 2 +- spec/requests/categories_contoller_spec.rb | 2 +- spec/requests/entities_controller_spec.rb | 6 +++--- spec/requests/users_contoller_spec.rb | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index d4d804a..38c868f 100644 --- a/Gemfile +++ b/Gemfile @@ -83,4 +83,4 @@ gem 'factory_bot_rails', '~> 6.2' gem 'faker', '~> 3.2' -gem "rails-controller-testing", "~> 1.0" +gem 'rails-controller-testing', '~> 1.0' diff --git a/spec/requests/categories_contoller_spec.rb b/spec/requests/categories_contoller_spec.rb index a23601d..d9b5af9 100644 --- a/spec/requests/categories_contoller_spec.rb +++ b/spec/requests/categories_contoller_spec.rb @@ -8,7 +8,7 @@ before do user = User.new(name: 'User-1', email: 'local@host', password: 'password', password_confirmation: 'password') - user.save + user.save category = Category.new(name: 'Category-1x', icon: 'fa fa-bolt', author: user) category.save sign_in user diff --git a/spec/requests/entities_controller_spec.rb b/spec/requests/entities_controller_spec.rb index 52ef3a2..5eee128 100644 --- a/spec/requests/entities_controller_spec.rb +++ b/spec/requests/entities_controller_spec.rb @@ -8,15 +8,15 @@ before do user = User.new(name: 'User-1', email: 'local@host', password: 'password', password_confirmation: 'password') - user.save + user.save category = Category.new(name: 'Category-1x', icon: 'fa fa-bolt', author: user) category.save - entity = Entity.new(name: 'Entity-1x', amount: 2345, author: user, category: category) + entity = Entity.new(name: 'Entity-1x', amount: 2345, author: user, category:) entity.save sign_in user end - describe 'GET categories/:category_id/entities' do + describe 'GET categories/:category_id/entities' do before do get category_entities_path(Category.first) end diff --git a/spec/requests/users_contoller_spec.rb b/spec/requests/users_contoller_spec.rb index 96cc192..9d7d14f 100644 --- a/spec/requests/users_contoller_spec.rb +++ b/spec/requests/users_contoller_spec.rb @@ -39,4 +39,4 @@ expect(response.body).to include(new_user_session_path) end end -end \ No newline at end of file +end From 969635f183f360598f236102e72eaf6b9c2829d5 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 20:52:41 +0300 Subject: [PATCH 40/54] create HomePage feature tests --- spec/features/home/index._spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 spec/features/home/index._spec.rb diff --git a/spec/features/home/index._spec.rb b/spec/features/home/index._spec.rb new file mode 100644 index 0000000..b763e97 --- /dev/null +++ b/spec/features/home/index._spec.rb @@ -0,0 +1,16 @@ +# Test Home features with Capybara and RSpec +# /spec/features/home/index_spec.rb + +require 'rails_helper' + +RSpec.describe 'Home', type: :feature do + it 'displays the home page' do + visit root_path + + expect(page).to have_content('Budget Tracker App') + expect(page).to have_content('Log In') + expect(page).to have_content('Sign Up') + expect(page).to have_link('Log In', href: new_user_session_path) + expect(page).to have_link('Sign Up', href: new_user_registration_path) + end +end From 12624986c1167150da97ae48e3b09c3c9181977f Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 20:52:57 +0300 Subject: [PATCH 41/54] create Category feature tests --- spec/features/category/index_spec.rb | 30 +++++++++++++++++++++++++ spec/features/category/new_spec.rb | 33 ++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 spec/features/category/index_spec.rb create mode 100644 spec/features/category/new_spec.rb diff --git a/spec/features/category/index_spec.rb b/spec/features/category/index_spec.rb new file mode 100644 index 0000000..6737212 --- /dev/null +++ b/spec/features/category/index_spec.rb @@ -0,0 +1,30 @@ +# Test the category index page +# /spec/integrations/category/index_spec.rb +# ----------------------------------------------------------- +require 'rails_helper' + +RSpec.describe 'Category index page', type: :feature do + before(:each) do + user = User.new(name: 'User-1', email: 'local@host', password: 'password', password_confirmation: 'password') + user.save + sign_in user + + @category = Category.new(name: 'Test category', icon: 'test-icon.png', author: user) + @category.save + visit categories_path + end + + it 'displays the category index page' do + expect(page).to have_content('CARTEGORIES') + expect(page).to have_content('Logout') + end + + it 'displays a list of categories' do + expect(page).to have_content('Test category') + expect(page).to have_content('Total: 0.0$') + end + + it 'displays a link to create a new category' do + expect(page).to have_link('Add Category', href: new_category_path) + end +end diff --git a/spec/features/category/new_spec.rb b/spec/features/category/new_spec.rb new file mode 100644 index 0000000..b1eb588 --- /dev/null +++ b/spec/features/category/new_spec.rb @@ -0,0 +1,33 @@ +# Test the category new page +# /spec/integrations/category/new_spec.rb +# ----------------------------------------------------------- + +require 'rails_helper' + +RSpec.describe 'Category new page', type: :feature do + before(:each) do + user = User.new(name: 'User-1', email: 'local@host', password: 'password', password_confirmation: 'password') + user.save + sign_in user + + visit new_category_path + end + + it 'displays the category new page' do + expect(page).to have_content('New Category') + expect(page).to have_content('Logout') + end + + it 'displays the category form' do + expect(page).to have_field('Name') + expect(page).to have_field('Icon Link') + end + + it 'creates a new category' do + fill_in 'Name', with: 'Test category' + fill_in 'Icon Link', with: 'test-icon.png' + click_button 'Save' + expect(current_path).to eq(categories_path) + expect(page).to have_content('Test category') + end +end From a09c7529755f4eb60188a6c6f3518394588959fd Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 20:53:06 +0300 Subject: [PATCH 42/54] create Entity feature tests --- spec/features/entity/index_spec.rb | 35 ++++++++++++++++++++++++++++++ spec/features/entity/new_spec.rb | 35 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 spec/features/entity/index_spec.rb create mode 100644 spec/features/entity/new_spec.rb diff --git a/spec/features/entity/index_spec.rb b/spec/features/entity/index_spec.rb new file mode 100644 index 0000000..ef98b61 --- /dev/null +++ b/spec/features/entity/index_spec.rb @@ -0,0 +1,35 @@ +# Test the entity index page +# /spec/features/entity/index_spec.rb +# ----------------------------------------------------------- + +require 'rails_helper' + +RSpec.describe 'Entity index page', type: :feature do + before(:each) do + user = User.new(name: 'User-1', email: 'local@host', password: 'password', password_confirmation: 'password') + user.save + sign_in user + + category = Category.new(name: 'Test category', icon: 'test-icon.png', author: user) + category.save + + @entity = Entity.new(name: 'Test entity', amount: 653, category:, author: user) + @entity.save + + visit category_entities_path(category) + end + + it 'displays the entity index page' do + expect(page).to have_content('Transactions') + expect(page).to have_content('Logout') + end + + it 'displays a list of entities' do + expect(page).to have_content('Test entity') + expect(page).to have_content('Price: 653.0$') + end + + it 'displays a link to create a new entity' do + expect(page).to have_link('New Transaction', href: new_category_entity_path(@entity.category)) + end +end diff --git a/spec/features/entity/new_spec.rb b/spec/features/entity/new_spec.rb new file mode 100644 index 0000000..4f2bd75 --- /dev/null +++ b/spec/features/entity/new_spec.rb @@ -0,0 +1,35 @@ +# Test the entity new page +# /spec/features/entity/new_spec.rb +# ----------------------------------------------------------- + +require 'rails_helper' + +RSpec.describe 'Entity new page', type: :feature do + before(:each) do + user = User.new(name: 'User-1', email: 'local@host', password: 'password', password_confirmation: 'password') + user.save + sign_in user + + category = Category.new(name: 'Category-1x', icon: 'fa fa-bolt', author: user) + category.save + + visit new_category_entity_path(Category) + end + + it 'displays the entity new page' do + expect(page).to have_content('New Transaction') + expect(page).to have_content('Logout') + end + + it 'displays the entity form' do + expect(page).to have_field('entity[name]') + expect(page).to have_field('entity[amount]') + end + + it 'creates a new entity' do + fill_in 'entity[name]', with: 'Test entity' + fill_in 'entity[amount]', with: '1234' + click_button 'Save' + expect(current_path).to eq(category_entities_path(Category)) + end +end From 52fa328e5da049ec5270dd86f112c2fc5de3b2b2 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 20:56:35 +0300 Subject: [PATCH 43/54] Update the default README --- README.md | 163 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 150 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 7db80e4..d9bb95f 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,161 @@ -# README -This README would normally document whatever steps are necessary to get the -application up and running. -Things you may want to cover: + -* Ruby version +
+
-* System dependencies +

Budget Tracker App

+
+
-* Configuration +# 📗 Table of Contents -* Database creation +- [📗 Table of Contents](#-table-of-contents) +- [📖 Budget App](#-budget-app) + - [🛠 Built With ](#-built-with-) + - [Tech Stack ](#tech-stack-) + - [Key Features ](#key-features-) + - [Live Demo](#live-demo) + - [💻 Getting Started ](#-getting-started-) + - [Prerequisites](#prerequisites) + - [Setup](#setup) + - [Install](#install) + - [Usage](#usage) + - [Test](#test) + - [Authors ](#authors-) + - [🔭 Future Features ](#-future-features-) + - [🤝 Contributing ](#-contributing-) + - [⭐️ Show your support ](#️-show-your-support-) + - [🙏 Acknowledgments ](#-acknowledgments-) + - [📝 License ](#-license-) -* Database initialization -* How to run the test suite +# 📖 Budget App -* Services (job queues, cache servers, search engines, etc.) +The Budget App is a web app developed with mobile-first approach that used for efficient budget management. This app allows users to track their expenses by maintaining a list of transactions associated with a category. By utilizing the app, users can easily visualize the amount of money they have spent and on what. -* Deployment instructions +## 🛠 Built With -* ... +### Tech Stack + +
+ Ruby on Rails + + +
+ +### Key Features + +- Record all expenses by creating a category +- List all transactions associated with a category +- All users to visualize how much money they spent on a category + +## Live Demo + +- [Live Demo](https://budget-tracker-pzur.onrender.com/) +- [Project video Link](https://www.loom.com/share/752c6b57b76b42ea8b5528a24d00c47b?sid=39967d9d-27d2-4d08-ab28-12008daed00f) + +

(back to top)

+ +## 💻 Getting Started + +To get a local copy up and running follow these simple example steps. + +### Prerequisites + +In order to run this project you need: + +- Code Editor (Vs Code) or any editor of your choice + +- Terminal + +- Install Ruby on your computer + +- Install Rails on your computer + +### Setup + +Clone this repository by using the command line to your desired folder: + +```sh +git clone https://github.com/Microverse-Fullstack-Program/rails-budget-app-v2 + +cd rails-budget-app +``` + +### Install + +Install this project with: + +```sh +cd rails-budget-app + bundle install (install all dependencies) +``` + +### Usage + +To run the project, execute the following command: + +```sh +rails server +``` + +### Test + +```sh + rspec +``` + +

(back to top)

+ +## Authors + +👤 **Chere Lemma** + +- GitHub: [Chere Lemma](https://github.com/cherelemma) +- LinkedIn: [@chere-lemma27211613](https://www.linkedin.com/in/chere-lemma-urgaya) +- Twitter: [@Chere21271613](https://twitter.com/Chere21271613) + + +## 🔭 Future Features + +- [] Implement some UX improvements: like transitions and/or animations, etc. +- [] Create a decent desktop design for the webapp. +- [] Implement the left side menu to improve the navigability of the app. + +

(back to top)

+ + +## 🤝 Contributing + +Contributions, issues, and feature requests are welcome! + +Feel free to check the [issues page](https://github.com/Microverse-Fullstack-Program/rails-budget-app/issues) + + +

(back to top)

+ + +## ⭐️ Show your support + +Leave a star if you like the project. + +

(back to top)

+ +## 🙏 Acknowledgments + +design credits go to: +[@Gregoire Vella](https://www.behance.net/gallery/19759151/Snapscan-iOs-design-and-branding?tracking_source=) - author of design + +

(back to top)

+ +## 📝 License + +This project is [MIT](./LICENSE.md) licensed. + +

(back to top)

From 5a904e0697613ef3e78cd68ce49ac619e13ffc3c Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 20:59:40 +0300 Subject: [PATCH 44/54] create render github action --- bin/render-build.sh | 8 ++++++++ render.yaml | 19 +++++++++++++++++++ spec/models/category_spec.rb | 1 - 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 bin/render-build.sh create mode 100644 render.yaml diff --git a/bin/render-build.sh b/bin/render-build.sh new file mode 100644 index 0000000..24b31ca --- /dev/null +++ b/bin/render-build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# exit on error +set -o errexit + +bundle install +bundle exec rake assets:precompile +bundle exec rake assets:clean +bundle exec rake db:migrate \ No newline at end of file diff --git a/render.yaml b/render.yaml new file mode 100644 index 0000000..d48d2d2 --- /dev/null +++ b/render.yaml @@ -0,0 +1,19 @@ +databases: + - name: budgetapp + databaseName: budget_app_w2n3 + user: postgress + +services: + - type: web + name: budgetapp + runtime: ruby + buildCommand: "./bin/render-build.sh" + startCommand: "bundle exec puma -C config/puma.rb" + envVars: + - fromGroup: staging + - key: DATABASE_URL + fromDatabase: + name: budgetapp + property: connectionString + - key: RAILS_MASTER_KEY + sync: false diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index bdcbb1e..ffde9b6 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -67,7 +67,6 @@ expect(category).to be_valid end - describe 'Associations' do it 'should belong to author' do category = Category.reflect_on_association(:author) From b6bbcc826766109250ce1c71982a162b2af27d90 Mon Sep 17 00:00:00 2001 From: Chere Lemma Date: Sat, 23 Sep 2023 21:03:02 +0300 Subject: [PATCH 45/54] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d9bb95f..ea3a8a7 100644 --- a/README.md +++ b/README.md @@ -108,9 +108,14 @@ rails server ### Test ```sh - rspec + rspec ./spec ``` +**Test Screenshot** + +![image](https://github.com/Microverse-Fullstack-Program/rails-budget-app-v2/assets/73167960/e09d3aaa-7b83-407e-9d45-24c6128625f7) + +

(back to top)

## Authors From aafaef5a959291793ceb365e0b3cc67362302472 Mon Sep 17 00:00:00 2001 From: Chere Lemma Date: Sat, 23 Sep 2023 21:04:12 +0300 Subject: [PATCH 46/54] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea3a8a7..fa359f3 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,6 @@ design credits go to: ## 📝 License -This project is [MIT](./LICENSE.md) licensed. +This project is [MIT](./LICENSE) licensed.

(back to top)

From 0f0e530144622511fb447cab093979f608a43479 Mon Sep 17 00:00:00 2001 From: Chere Lemma Date: Sat, 23 Sep 2023 21:06:35 +0300 Subject: [PATCH 47/54] Update README.md --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fa359f3..65a722e 100644 --- a/README.md +++ b/README.md @@ -123,24 +123,22 @@ rails server 👤 **Chere Lemma** - GitHub: [Chere Lemma](https://github.com/cherelemma) -- LinkedIn: [@chere-lemma27211613](https://www.linkedin.com/in/chere-lemma-urgaya) -- Twitter: [@Chere21271613](https://twitter.com/Chere21271613) +- LinkedIn: [@chere-lemma](https://www.linkedin.com/in/chere-lemma-urgaya) +- Twitter: [@Chere](https://twitter.com/Chere21271613) ## 🔭 Future Features - [] Implement some UX improvements: like transitions and/or animations, etc. -- [] Create a decent desktop design for the webapp. -- [] Implement the left side menu to improve the navigability of the app. +- [] Implement the left-side menu to improve the navigability of the app.

(back to top)

- ## 🤝 Contributing Contributions, issues, and feature requests are welcome! -Feel free to check the [issues page](https://github.com/Microverse-Fullstack-Program/rails-budget-app/issues) +Feel free to check the [issues page](https://github.com/Microverse-Fullstack-Program/rails-budget-app-v2/issues)

(back to top)

From 4f7ecc1105385c5a76553feab325f7b5cf294973 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 21:14:58 +0300 Subject: [PATCH 48/54] add lock file and fix deploy platform --- Gemfile.lock | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 411af1c..4d9f4a1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -134,6 +134,7 @@ GEM matrix (0.4.2) method_source (1.0.0) mini_mime (1.1.5) + mini_portile2 (2.8.4) minitest (5.20.0) msgpack (1.7.2) net-imap (0.3.7) @@ -146,13 +147,19 @@ GEM net-smtp (0.3.3) net-protocol nio4r (2.5.9) + nokogiri (1.15.4) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) nokogiri (1.15.4-x64-mingw-ucrt) racc (~> 1.4) + nokogiri (1.15.4-x86_64-linux) + racc (~> 1.4) orm_adapter (0.5.0) parallel (1.23.0) parser (3.2.2.3) ast (~> 2.4.1) racc + pg (1.5.4) pg (1.5.4-x64-mingw-ucrt) psych (5.1.0) stringio @@ -250,7 +257,10 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) + sqlite3 (1.6.6) + mini_portile2 (~> 2.8.0) sqlite3 (1.6.6-x64-mingw-ucrt) + sqlite3 (1.6.6-x86_64-linux) stimulus-rails (1.2.2) railties (>= 6.0.0) stringio (3.0.8) @@ -285,7 +295,9 @@ GEM zeitwerk (2.6.11) PLATFORMS + ruby x64-mingw-ucrt + x86_64-linux DEPENDENCIES bootsnap From 3a08df35f699d15f078fc135e53458bab2e76f44 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 21:22:46 +0300 Subject: [PATCH 49/54] update database production config --- config/database.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/config/database.yml b/config/database.yml index 51d43ea..f01b280 100644 --- a/config/database.yml +++ b/config/database.yml @@ -84,7 +84,5 @@ test: # for a full overview on how database connection configuration can be specified. # production: - <<: *default - database: budgetApp_production - username: postgres - password: <%= ENV["RAILS_BUDGET_APP_V2_DATABASE_PASSWORD"] %> + <<: *default + url: <%= ENV['DATABASE_URL'] %> From b9e915288ce183aa0323b0418cdb27b501435f15 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 21:39:05 +0300 Subject: [PATCH 50/54] update deployment config --- config/environments/production.rb | 2 +- config/puma.rb | 2 +- render.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index 16cc91d..a7e2b7d 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -22,7 +22,7 @@ # 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? + config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? || || ENV['RENDER'].present? # Compress CSS using a preprocessor. # config.assets.css_compressor = :sass diff --git a/config/puma.rb b/config/puma.rb index daaf036..f7cc67f 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -30,7 +30,7 @@ # Workers do not work on JRuby or Windows (both of which do not support # processes). # -# workers ENV.fetch("WEB_CONCURRENCY") { 2 } +workers ENV.fetch("WEB_CONCURRENCY") { 4 } # Use the `preload_app!` method when specifying a `workers` number. # This directive tells Puma to first boot the application and load code diff --git a/render.yaml b/render.yaml index d48d2d2..c96d1c2 100644 --- a/render.yaml +++ b/render.yaml @@ -6,7 +6,7 @@ databases: services: - type: web name: budgetapp - runtime: ruby + env: ruby buildCommand: "./bin/render-build.sh" startCommand: "bundle exec puma -C config/puma.rb" envVars: From f6d35a1a9b228e412327efd8478d77d7fff8a91f Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 21:49:39 +0300 Subject: [PATCH 51/54] update render.yml --- render.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/render.yaml b/render.yaml index c96d1c2..284648d 100644 --- a/render.yaml +++ b/render.yaml @@ -10,7 +10,6 @@ services: buildCommand: "./bin/render-build.sh" startCommand: "bundle exec puma -C config/puma.rb" envVars: - - fromGroup: staging - key: DATABASE_URL fromDatabase: name: budgetapp From 00203a8327ebb64b6359dca4f0496d171192174e Mon Sep 17 00:00:00 2001 From: Chere Lemma Date: Sat, 23 Sep 2023 21:55:51 +0300 Subject: [PATCH 52/54] Update production.rb --- config/environments/production.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index a7e2b7d..bec4c58 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -22,7 +22,7 @@ # 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? || || ENV['RENDER'].present? + config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? || ENV['RENDER'].present? # Compress CSS using a preprocessor. # config.assets.css_compressor = :sass From c9cd680cb19bb11463ad1c185bdb21077bb94494 Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 22:28:22 +0300 Subject: [PATCH 53/54] upgrade the responders version --- config/initializers/devise.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 7d14566..30b18d4 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -302,8 +302,9 @@ # apps is `200 OK` and `302 Found respectively`, but new apps are generated with # these new defaults that match Hotwire/Turbo behavior. # Note: These might become the new default in future versions of Devise. - config.responder.error_status = :unprocessable_entity - config.responder.redirect_status = :see_other + # config.responder.error_status = :unprocessable_entity + # config.responder.redirect_status = :see_other + config.responder = MyApplicationResponder # ==> Configuration for :registerable From de80ef8585d967656b601505e58619f34624bc3e Mon Sep 17 00:00:00 2001 From: cherelemma Date: Sat, 23 Sep 2023 23:14:12 +0300 Subject: [PATCH 54/54] create new credentials.yml.enc --- config/credentials.yml.enc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index 7fd3ffa..b3481c8 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -mAS+2uLXihBVqlbqLOZS5nDQf1OlIsiR7gl1uiQOd5AGVJLsq4nDaBGLe9wlpwde1Bs/vAOfQEqqF3SAQuomDY2p2JBbC7eBfPnfqXToCG6m/1HfziG4P8HbXPLNp9xH3UrCMUsF6OLwjIPZjFBWaMdtARnQ32iot3ayvsEF/DGlUhkIu0YAQKfrur00w4pyDAgAYRQX5i5CDvHmS0lC5ZK+z2KfaM3a9W8MF8IR+oToLye26Qc6DKWpTWpaUC7PU89N0wEdfn6fPpnksoheHynQpGQe/K5SMyu7iHnqw2A+5DTbQiorrpR1WLiBpEP29cZ/ZZb6GR3rlNe4Cr4R7Cz9iy35VhFz16lVHxrzR8tZaN39Il51gHGISlxBCQXh3dMxta+BwE6IuRlPeozbEEDYTIPs9zsXMD9L--PWuJIcjXbCTad903--iEFjL1+ftNuUZW4/ylMqmg== \ No newline at end of file +3S2FXfrysgdqNMJVsaWd66XCJPSpQbUL3TJUGttMD2F8m5UAl148Tb/Uztcn1wZo368FI/UcaTwTY6J0iWjMEavuhgPVhpPn9G8cYwBnF+AyHm5aHdXKj8kLvqkM4Xnm8f7i5/EnmU1O6KSu+6rCZDxnKXV9jyZ4a1c+mBNXzSGYf3JqrdeUwng7olf1eaxVL4VWwqiJZDbMyOfH9x0+d1Fl/GLxkvlfX5LLJsNXd08pF6l+7S3zbeytBdKw3dSbqnXp07mKYoVN9P+ObFOlMT5UsP6tEjhHSB8fADJzFE8DgTeXwFdn5LqDco3mVW0I2I9mjBbjpH4uk2oFzpt5H/6rYqLvKb973nWuvA9TbBr5JrotrC6TeQRT2HLfVqFxlntdTu65LqzwLxxfGj5Zzue3OJ8JUz+dAM0M--gXorTlE8FXevS5jJ--hw93Yg2nI+rwkvgcXZE/KA== \ No newline at end of file