From bb4f1d0dfcd0642c153ab7d41da4f7f452d0b222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A8=D0=BB=D1=8F=D0=BF=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=9D=D0=B8=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8=D1=87?= Date: Fri, 7 Feb 2025 01:12:09 +0300 Subject: [PATCH 1/7] optimaization task 3 (Shlaypnikov) --- .gitignore | 2 + .rspec | 1 + .ruby-version | 2 +- Dockerfile | 18 ++ Gemfile | 15 +- Gemfile.lock | 367 +++++++++++++++++++---------- Makefile | 48 ++++ config/database.yml | 8 +- config/environments/development.rb | 4 +- config/environments/production.rb | 4 +- config/initializers/assets.rb | 14 -- db/schema.rb | 14 +- docker-compose.yml | 31 +++ lib/tasks/utils.rake | 55 +++-- make-compose.mk | 25 ++ make-services-app.mk | 38 +++ spec/fixtures/data.json | 21 ++ spec/lib/tasks/utils_rake_spec.rb | 41 ++++ spec/rails_helper.rb | 72 ++++++ spec/spec_helper.rb | 94 ++++++++ 20 files changed, 687 insertions(+), 187 deletions(-) create mode 100644 .rspec create mode 100644 Dockerfile create mode 100644 Makefile delete mode 100644 config/initializers/assets.rb create mode 100644 docker-compose.yml create mode 100644 make-compose.mk create mode 100644 make-services-app.mk create mode 100644 spec/fixtures/data.json create mode 100644 spec/lib/tasks/utils_rake_spec.rb create mode 100644 spec/rails_helper.rb create mode 100644 spec/spec_helper.rb diff --git a/.gitignore b/.gitignore index 59c74047..fb9bdfb7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /tmp /log /public +/vendor/* +.byebug_history diff --git a/.rspec b/.rspec new file mode 100644 index 00000000..c99d2e73 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/.ruby-version b/.ruby-version index ec1cf33c..a0891f56 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.6.3 +3.3.4 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..a2c0d3e3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM ruby:3.3.4 + +ENV NODE_VERSION 19.x + +RUN curl -sL https://deb.nodesource.com/setup_${NODE_VERSION} | bash - + +RUN apt-get update \ + && apt-get install -y nodejs \ + && rm -rf /var/lib/apt/lists/* + +ENV PROJECT_ROOT /app +RUN mkdir -p ${PROJECT_ROOT} + +WORKDIR ${PROJECT_ROOT} + +ENV BUNDLE_APP_CONFIG ${PROJECT_ROOT}/bundle/config +ENV GEM_HOME ${PROJECT_ROOT}/vendor/bundle +ENV BUNDLE_PATH ${GEM_HOME} \ No newline at end of file diff --git a/Gemfile b/Gemfile index e20b1260..461ec0c4 100644 --- a/Gemfile +++ b/Gemfile @@ -1,22 +1,23 @@ source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby '2.6.3' +ruby '3.3.4' -gem 'rails', '~> 5.2.3' -gem 'pg', '>= 0.18', '< 2.0' -gem 'puma', '~> 3.11' -gem 'bootsnap', '>= 1.1.0', require: false +gem 'rails', '~> 7' +gem 'pg' +gem 'puma' +gem 'bootsnap', require: false group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] + gem 'rspec-rails', git: 'https://github.com/rspec/rspec-rails' end group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. - gem 'web-console', '>= 3.3.0' - gem 'listen', '>= 3.0.5', '< 3.2' + gem 'web-console' + gem 'listen' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index fccf6f5f..30ebe65a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,150 +1,269 @@ +GIT + remote: https://github.com/rspec/rspec-rails + revision: a5c742306581d93a46c19582c26526f86c4cbe5b + specs: + rspec-rails (7.2.0.pre) + actionpack (>= 7.0) + activesupport (>= 7.0) + railties (>= 7.0) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + GEM remote: https://rubygems.org/ specs: - actioncable (5.2.3) - actionpack (= 5.2.3) + actioncable (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.3) - actionpack (= 5.2.3) - actionview (= 5.2.3) - activejob (= 5.2.3) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) - actionpack (5.2.3) - actionview (= 5.2.3) - activesupport (= 5.2.3) - rack (~> 2.0) + zeitwerk (~> 2.6) + actionmailbox (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + actionmailer (7.2.2.1) + actionpack (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (7.2.2.1) + actionview (= 7.2.2.1) + activesupport (= 7.2.2.1) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4, < 3.2) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.3) - activesupport (= 5.2.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (7.2.2.1) + actionpack (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.2.2.1) + activesupport (= 7.2.2.1) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.2.3) - activesupport (= 5.2.3) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.2.2.1) + activesupport (= 7.2.2.1) globalid (>= 0.3.6) - activemodel (5.2.3) - activesupport (= 5.2.3) - activerecord (5.2.3) - activemodel (= 5.2.3) - activesupport (= 5.2.3) - arel (>= 9.0) - activestorage (5.2.3) - actionpack (= 5.2.3) - activerecord (= 5.2.3) - marcel (~> 0.3.1) - activesupport (5.2.3) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - arel (9.0.0) - bindex (0.6.0) - bootsnap (1.4.2) - msgpack (~> 1.0) - builder (3.2.3) - byebug (11.0.1) - concurrent-ruby (1.1.5) - crass (1.0.4) - erubi (1.8.0) - ffi (1.10.0) - globalid (0.4.2) - activesupport (>= 4.2.0) - i18n (1.6.0) + activemodel (7.2.2.1) + activesupport (= 7.2.2.1) + activerecord (7.2.2.1) + activemodel (= 7.2.2.1) + activesupport (= 7.2.2.1) + timeout (>= 0.4.0) + activestorage (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activesupport (= 7.2.2.1) + marcel (~> 1.0) + activesupport (7.2.2.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.9) + bindex (0.8.1) + bootsnap (1.18.4) + msgpack (~> 1.2) + builder (3.3.0) + byebug (11.1.3) + concurrent-ruby (1.3.5) + connection_pool (2.5.0) + crass (1.0.6) + date (3.4.1) + diff-lcs (1.5.1) + drb (2.2.1) + erubi (1.13.1) + ffi (1.17.1-aarch64-linux-gnu) + ffi (1.17.1-aarch64-linux-musl) + ffi (1.17.1-arm-linux-gnu) + ffi (1.17.1-arm-linux-musl) + ffi (1.17.1-arm64-darwin) + ffi (1.17.1-x86_64-darwin) + ffi (1.17.1-x86_64-linux-gnu) + ffi (1.17.1-x86_64-linux-musl) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.7) concurrent-ruby (~> 1.0) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) - loofah (2.2.3) + io-console (0.8.0) + irb (1.15.1) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + listen (3.9.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + logger (1.6.5) + loofah (2.24.0) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) - marcel (0.3.3) - mimemagic (~> 0.3.2) - method_source (0.9.2) - mimemagic (0.3.3) - mini_mime (1.0.1) - mini_portile2 (2.4.0) - minitest (5.11.3) - msgpack (1.2.9) - nio4r (2.3.1) - nokogiri (1.10.2) - mini_portile2 (~> 2.4.0) - pg (1.1.4) - puma (3.12.1) - rack (2.0.6) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rails (5.2.3) - actioncable (= 5.2.3) - actionmailer (= 5.2.3) - actionpack (= 5.2.3) - actionview (= 5.2.3) - activejob (= 5.2.3) - activemodel (= 5.2.3) - activerecord (= 5.2.3) - activestorage (= 5.2.3) - activesupport (= 5.2.3) - bundler (>= 1.3.0) - railties (= 5.2.3) - sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + net-imap + net-pop + net-smtp + marcel (1.0.4) + mini_mime (1.1.5) + minitest (5.25.4) + msgpack (1.8.0) + net-imap (0.5.5) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.1) + net-protocol + nio4r (2.7.4) + nokogiri (1.18.2-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.2-aarch64-linux-musl) + racc (~> 1.4) + nokogiri (1.18.2-arm-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.2-arm-linux-musl) + racc (~> 1.4) + nokogiri (1.18.2-arm64-darwin) + racc (~> 1.4) + nokogiri (1.18.2-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.18.2-x86_64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.2-x86_64-linux-musl) + racc (~> 1.4) + pg (1.5.9) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + psych (5.2.3) + date + stringio + puma (6.6.0) + nio4r (~> 2.0) + racc (1.8.1) + rack (3.1.9) + rack-session (2.1.0) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rackup (2.2.1) + rack (>= 3) + rails (7.2.2.1) + actioncable (= 7.2.2.1) + actionmailbox (= 7.2.2.1) + actionmailer (= 7.2.2.1) + actionpack (= 7.2.2.1) + actiontext (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activemodel (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + bundler (>= 1.15.0) + railties (= 7.2.2.1) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.0.4) - loofah (~> 2.2, >= 2.2.2) - railties (5.2.3) - actionpack (= 5.2.3) - activesupport (= 5.2.3) - method_source - rake (>= 0.8.7) - thor (>= 0.19.0, < 2.0) - rake (12.3.2) - rb-fsevent (0.10.3) - rb-inotify (0.10.0) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rake (13.2.1) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) ffi (~> 1.0) - ruby_dep (1.5.0) - sprockets (3.7.2) + rdoc (6.12.0) + psych (>= 4.0.0) + reline (0.6.0) + io-console (~> 0.5) + rspec-core (3.13.3) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) + securerandom (0.4.1) + stringio (3.1.2) + thor (1.3.2) + timeout (0.4.3) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.1) - actionpack (>= 4.0) - activesupport (>= 4.0) - sprockets (>= 3.0.0) - thor (0.20.3) - thread_safe (0.3.6) - tzinfo (1.2.5) - thread_safe (~> 0.1) - web-console (3.7.0) - actionview (>= 5.0) - activemodel (>= 5.0) + useragent (0.16.11) + web-console (4.2.1) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) bindex (>= 0.4.0) - railties (>= 5.0) - websocket-driver (0.7.0) + railties (>= 6.0.0) + websocket-driver (0.7.7) + base64 websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.3) + websocket-extensions (0.1.5) + zeitwerk (2.7.1) PLATFORMS - ruby + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl + arm64-darwin + x86_64-darwin + x86_64-linux-gnu + x86_64-linux-musl DEPENDENCIES - bootsnap (>= 1.1.0) + bootsnap byebug - listen (>= 3.0.5, < 3.2) - pg (>= 0.18, < 2.0) - puma (~> 3.11) - rails (~> 5.2.3) + listen + pg + puma + rails (~> 7) + rspec-rails! tzinfo-data - web-console (>= 3.3.0) + web-console RUBY VERSION - ruby 2.6.3p62 + ruby 3.3.4p94 BUNDLED WITH - 2.0.2 + 2.5.11 diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..ba87aa8e --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +include make-compose.mk +include make-services-app.mk + +test: + bin/rails test + + +backend: + rm -rf tmp/pids/server.pid + bundle exec rails s -p 3000 -b '0.0.0.0' + +setup: + make setup-app + +setup-app: + cp -n .env.example .env || true + bin/setup + +fixtures-load: + bin/rake utils:reload_json[fixtures/small.json] + +clean: + bin/rails db:drop + +console: + bin/rails c + +db-reset: + bin/rails db:drop + bin/rails db:create + bin/rails db:schema:load + bin/rails db:migrate + bin/rake reload_json[fixtures/small.json] + bin/rails log:clear tmp:clear + bin/rails restart + +start: + bin/rails s + +lint: lint-code lint-style + +linter-code-fix: + bundle exec rubocop -A + +test: + bundle exec rspec + +.PHONY: test \ No newline at end of file diff --git a/config/database.yml b/config/database.yml index e116cfa6..6070b7c0 100644 --- a/config/database.yml +++ b/config/database.yml @@ -15,11 +15,13 @@ # gem 'pg' # default: &default - adapter: postgresql encoding: unicode - # For details on connection pooling, see Rails configuration guide - # http://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + adapter: postgresql + host: postgres + username: postgres + password: development: <<: *default diff --git a/config/environments/development.rb b/config/environments/development.rb index 1311e3e4..bc3f8142 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -47,10 +47,10 @@ # Debug mode disables concatenation and preprocessing of assets. # This option may cause significant delays in view rendering with a large # number of complex assets. - config.assets.debug = true + # config.assets.debug = true # Suppress logger output for asset requests. - config.assets.quiet = true + # config.assets.quiet = true # Raises error for missing translations # config.action_view.raise_on_missing_translations = true diff --git a/config/environments/production.rb b/config/environments/production.rb index 613d8289..398c8489 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -23,11 +23,11 @@ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier + # config.assets.js_compressor = :uglifier # config.assets.css_compressor = :sass # Do not fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false + # config.assets.compile = false # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb deleted file mode 100644 index 4b828e80..00000000 --- a/config/initializers/assets.rb +++ /dev/null @@ -1,14 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = '1.0' - -# Add additional assets to the asset load path. -# Rails.application.config.assets.paths << Emoji.images_path -# Add Yarn node_modules folder to the asset load path. -Rails.application.config.assets.paths << Rails.root.join('node_modules') - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in the app/assets -# folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) diff --git a/db/schema.rb b/db/schema.rb index f6921e45..4b9516c5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2,16 +2,15 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). +# 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.define(version: 2019_03_30_193044) do - +ActiveRecord::Schema[7.2].define(version: 2019_03_30_193044) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -41,5 +40,4 @@ t.integer "price_cents" t.integer "bus_id" end - end diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..b999a751 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +--- +version: "3.7" + +services: + postgres: + image: postgres:13-alpine + environment: + POSTGRES_HOST_AUTH_METHOD: trust + volumes: + - pgdata:/var/lib/postgresql/data + ports: + - "5432:5432" + + app: + build: + context: . + dockerfile: Dockerfile + command: make backend + volumes: + - ~/.bash_history:/root/.bash_history + - .:/app + ports: + - "3000:3000" + depends_on: + - postgres + stdin_open: true + tty: true + + +volumes: + pgdata: \ No newline at end of file diff --git a/lib/tasks/utils.rake b/lib/tasks/utils.rake index 540fe871..4101bd23 100644 --- a/lib/tasks/utils.rake +++ b/lib/tasks/utils.rake @@ -1,34 +1,37 @@ # Наивная загрузка данных из json-файла в БД # rake reload_json[fixtures/small.json] -task :reload_json, [:file_name] => :environment do |_task, args| - json = JSON.parse(File.read(args.file_name)) - ActiveRecord::Base.transaction do - City.delete_all - Bus.delete_all - Service.delete_all - Trip.delete_all - ActiveRecord::Base.connection.execute('delete from buses_services;') +namespace :utils do + task :reload_json, [:file_name] => :environment do |_task, args| + json = JSON.parse(File.read(args.file_name)) - json.each do |trip| - from = City.find_or_create_by(name: trip['from']) - to = City.find_or_create_by(name: trip['to']) - services = [] - trip['bus']['services'].each do |service| - s = Service.find_or_create_by(name: service) - services << s - end - bus = Bus.find_or_create_by(number: trip['bus']['number']) - bus.update(model: trip['bus']['model'], services: services) + ActiveRecord::Base.transaction do + City.delete_all + Bus.delete_all + Service.delete_all + Trip.delete_all + ActiveRecord::Base.connection.execute('delete from buses_services;') + + json.each do |trip| + from = City.find_or_create_by(name: trip['from']) + to = City.find_or_create_by(name: trip['to']) + services = [] + trip['bus']['services'].each do |service| + s = Service.find_or_create_by(name: service) + services << s + end + bus = Bus.find_or_create_by(number: trip['bus']['number']) + bus.update(model: trip['bus']['model'], services: services) - Trip.create!( - from: from, - to: to, - bus: bus, - start_time: trip['start_time'], - duration_minutes: trip['duration_minutes'], - price_cents: trip['price_cents'], - ) + Trip.create!( + from: from, + to: to, + bus: bus, + start_time: trip['start_time'], + duration_minutes: trip['duration_minutes'], + price_cents: trip['price_cents'], + ) + end end end end diff --git a/make-compose.mk b/make-compose.mk new file mode 100644 index 00000000..b5c0a923 --- /dev/null +++ b/make-compose.mk @@ -0,0 +1,25 @@ +compose: + docker-compose up -d + +compose-build: + docker-compose build + +compose-clear: + docker-compose down -v --remove-orphans || true + +compose-down: + docker-compose down || true + +compose-install: + docker-compose run --rm app make setup + +compose-logs: + docker-compose logs -f + +compose-restart: + docker-compose restart + +compose-stop: + docker-compose stop || true + +compose-setup: compose-down compose-build compose-install \ No newline at end of file diff --git a/make-services-app.mk b/make-services-app.mk new file mode 100644 index 00000000..cc0dc9e9 --- /dev/null +++ b/make-services-app.mk @@ -0,0 +1,38 @@ +app-bash: + docker-compose run --rm app bash + +app-install-bundle: + docker-compose run --rm app bundle install --jobs $(shell nproc) + +app-update: app-update-bundle + +app-update-bundle: + docker-compose run --rm app bundle update --jobs $(shell nproc) + +app-debug: + docker attach --sig-proxy=false --detach-keys="ctrl-c" $(shell docker ps -q --filter publish=3000) + +app-lint: + docker-compose run --rm app make lint + +app-lint-fix: + docker-compose run --rm app make linter-code-fix + +app-rails-console: + docker-compose run --rm app make console + +app-test: + docker-compose run --rm app make test + +app-rails: + docker-compose run --rm app bin/rails $(T) + +app-make: + docker-compose run --rm app make $(T) + +app-test: + docker-compose run --rm app make test + +app-fixtures-load: + docker-compose run --rm app make fixtures-load + diff --git a/spec/fixtures/data.json b/spec/fixtures/data.json new file mode 100644 index 00000000..c25dcc84 --- /dev/null +++ b/spec/fixtures/data.json @@ -0,0 +1,21 @@ +[ + { + "from":"Сочи", + "to":"Тула", + "start_time":"16:11", + "duration_minutes":83, + "price_cents":23354, + "bus":{ + "number":"229", + "model":"Икарус", + "services":[ + "Ремни безопасности", + "Кондиционер общий", + "Кондиционер Индивидуальный", + "Телевизор индивидуальный", + "Стюардесса", + "Можно не печатать билет" + ] + } + } +] \ No newline at end of file diff --git a/spec/lib/tasks/utils_rake_spec.rb b/spec/lib/tasks/utils_rake_spec.rb new file mode 100644 index 00000000..5691f5bb --- /dev/null +++ b/spec/lib/tasks/utils_rake_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'rake' + +describe 'rake reload_json' do + before do + Rails.application.load_tasks + end + + after { task.reenable } + + let(:task) { Rake::Task['utils:reload_json'] } + let(:path) { 'spec/fixtures/data.json' } + let(:expected_result) do + { + from: 'Сочи', + to: 'Тула', + start_time:'16:11', + duration_minutes:83, + price_cents:23354, + bus:{ + number:'229', + model:'Икарус', + services:[ + 'Ремни безопасности', + 'Кондиционер общий', + 'Кондиционер Индивидуальный', + 'Телевизор индивидуальный', + 'Стюардесса', + 'Можно не печатать билет' + ] + } + } + end + + it 'fixtures load' do + task.invoke(path) + expect(Trip.last.to_h).to eq(expected_result) + end +end \ No newline at end of file diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb new file mode 100644 index 00000000..899a8a74 --- /dev/null +++ b/spec/rails_helper.rb @@ -0,0 +1,72 @@ +# 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? +# Uncomment the line below in case you have `--require rails_helper` in the `.rspec` file +# that will avoid rails generators crashing because migrations haven't been run yet +# return unless Rails.env.test? +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. +# +# Rails.root.glob('spec/support/**/*.rb').sort_by(&:to_s).each { |f| require f } + +# Ensures that the test database schema matches the current schema file. +# If there are pending migrations it will invoke `db:test:prepare` to +# recreate the test database by loading the schema. +# 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_paths = [ + Rails.root.join('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 uses metadata to mix in different behaviours to your tests, + # for example enabling you to call `get` and `post` in request specs. e.g.: + # + # RSpec.describe UsersController, type: :request do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://rspec.info/features/7-1/rspec-rails + # + # You can also this infer these behaviours automatically by location, e.g. + # /spec/models would pull in the same behaviour as `type: :model` but this + # behaviour is considered legacy and will be removed in a future version. + # + # To enable this behaviour uncomment the line below. + # 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 00000000..327b58ea --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,94 @@ +# 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! + + # 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 6f23688b3e609aa3d05e60b464b542d96871ff21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A8=D0=BB=D1=8F=D0=BF=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=9D=D0=B8=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8=D1=87?= Date: Mon, 10 Feb 2025 01:08:43 +0300 Subject: [PATCH 2/7] optimisation iter 2 --- .rspec | 2 + Gemfile | 11 +- Gemfile.lock | 48 ++++--- Makefile | 10 +- app/models/application_record.rb | 4 + app/models/bus.rb | 3 +- app/models/service.rb | 2 +- bin/setup | 2 +- config/initializers/strong_migrations.rb | 26 ++++ ...4520_add_index_number_column_from_buses.rb | 7 + .../20250209145721_add_index_from_cities.rb | 7 + .../20250209150005_add_index_from_services.rb | 7 + .../20250209150200_add_index_from_trips.rb | 9 ++ .../20250209215015_create_enum_from_bus.rb | 7 + ...20250209215912_create_enum_from_service.rb | 7 + db/schema.rb | 13 +- fixtures/micro.json | 5 + lib/benchmark.rb | 18 +++ lib/importer.rb | 129 ++++++++++++++++++ lib/profiler.rb | 38 ++++++ lib/reports_builder.rb | 19 +++ lib/tasks/reports.rake | 17 +++ lib/tasks/utils.rake | 34 +---- make-services-app.mk | 24 ++-- spec/rails_helper.rb | 72 ---------- spec/spec_helper.rb | 94 ------------- .../fixtures/files}/data.json | 0 .../lib/tasks/utils_test.rb | 26 ++-- 28 files changed, 386 insertions(+), 255 deletions(-) create mode 100644 config/initializers/strong_migrations.rb create mode 100644 db/migrate/20250209144520_add_index_number_column_from_buses.rb create mode 100644 db/migrate/20250209145721_add_index_from_cities.rb create mode 100644 db/migrate/20250209150005_add_index_from_services.rb create mode 100644 db/migrate/20250209150200_add_index_from_trips.rb create mode 100644 db/migrate/20250209215015_create_enum_from_bus.rb create mode 100644 db/migrate/20250209215912_create_enum_from_service.rb create mode 100644 fixtures/micro.json create mode 100755 lib/benchmark.rb create mode 100644 lib/importer.rb create mode 100644 lib/profiler.rb create mode 100755 lib/reports_builder.rb create mode 100644 lib/tasks/reports.rake delete mode 100644 spec/rails_helper.rb delete mode 100644 spec/spec_helper.rb rename {spec/fixtures => test/fixtures/files}/data.json (100%) rename spec/lib/tasks/utils_rake_spec.rb => test/lib/tasks/utils_test.rb (63%) diff --git a/.rspec b/.rspec index c99d2e73..a472683a 100644 --- a/.rspec +++ b/.rspec @@ -1 +1,3 @@ +--color --require spec_helper +--require rails_helper diff --git a/Gemfile b/Gemfile index 461ec0c4..2700de64 100644 --- a/Gemfile +++ b/Gemfile @@ -7,11 +7,19 @@ gem 'rails', '~> 7' gem 'pg' gem 'puma' gem 'bootsnap', require: false +gem 'yajl-ruby', require: 'yajl' +gem 'json-stream' +gem 'activerecord-import' + group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console + gem 'benchmark' gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] - gem 'rspec-rails', git: 'https://github.com/rspec/rspec-rails' + gem 'memory_profiler' + gem 'ruby-prof' + gem 'stackprof' + gem "strong_migrations" end group :development do @@ -21,6 +29,7 @@ group :development do end group :test do + gem 'minitest-power_assert' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem diff --git a/Gemfile.lock b/Gemfile.lock index 30ebe65a..975dac38 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,16 +1,3 @@ -GIT - remote: https://github.com/rspec/rspec-rails - revision: a5c742306581d93a46c19582c26526f86c4cbe5b - specs: - rspec-rails (7.2.0.pre) - actionpack (>= 7.0) - activesupport (>= 7.0) - railties (>= 7.0) - rspec-core (~> 3.13) - rspec-expectations (~> 3.13) - rspec-mocks (~> 3.13) - rspec-support (~> 3.13) - GEM remote: https://rubygems.org/ specs: @@ -67,6 +54,8 @@ GEM activemodel (= 7.2.2.1) activesupport (= 7.2.2.1) timeout (>= 0.4.0) + activerecord-import (2.1.0) + activerecord (>= 4.2) activestorage (7.2.2.1) actionpack (= 7.2.2.1) activejob (= 7.2.2.1) @@ -97,7 +86,6 @@ GEM connection_pool (2.5.0) crass (1.0.6) date (3.4.1) - diff-lcs (1.5.1) drb (2.2.1) erubi (1.13.1) ffi (1.17.1-aarch64-linux-gnu) @@ -117,6 +105,7 @@ GEM pp (>= 0.6.0) rdoc (>= 4.0.0) reline (>= 0.4.2) + json-stream (1.0.0) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -130,10 +119,14 @@ GEM net-pop net-smtp marcel (1.0.4) + memory_profiler (1.1.0) mini_mime (1.1.5) minitest (5.25.4) + minitest-power_assert (0.3.1) + minitest + power_assert (>= 1.1) msgpack (1.8.0) - net-imap (0.5.5) + net-imap (0.5.6) date net-protocol net-pop (0.1.2) @@ -160,6 +153,7 @@ GEM nokogiri (1.18.2-x86_64-linux-musl) racc (~> 1.4) pg (1.5.9) + power_assert (2.0.5) pp (0.6.2) prettyprint prettyprint (0.2.0) @@ -214,17 +208,12 @@ GEM psych (>= 4.0.0) reline (0.6.0) io-console (~> 0.5) - rspec-core (3.13.3) - rspec-support (~> 3.13.0) - rspec-expectations (3.13.3) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.13.0) - rspec-mocks (3.13.2) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.13.0) - rspec-support (3.13.2) + ruby-prof (1.7.1) securerandom (0.4.1) + stackprof (0.2.27) stringio (3.1.2) + strong_migrations (2.2.0) + activerecord (>= 7) thor (1.3.2) timeout (0.4.3) tzinfo (2.0.6) @@ -239,6 +228,7 @@ GEM base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) + yajl-ruby (1.4.3) zeitwerk (2.7.1) PLATFORMS @@ -252,15 +242,23 @@ PLATFORMS x86_64-linux-musl DEPENDENCIES + activerecord-import + benchmark bootsnap byebug + json-stream listen + memory_profiler + minitest-power_assert pg puma rails (~> 7) - rspec-rails! + ruby-prof + stackprof + strong_migrations tzinfo-data web-console + yajl-ruby RUBY VERSION ruby 3.3.4p94 diff --git a/Makefile b/Makefile index ba87aa8e..fe37d1d1 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,6 @@ include make-compose.mk include make-services-app.mk -test: - bin/rails test - backend: rm -rf tmp/pids/server.pid @@ -17,7 +14,7 @@ setup-app: bin/setup fixtures-load: - bin/rake utils:reload_json[fixtures/small.json] + bin/rake utils:reload_json[fixtures/medium.json] clean: bin/rails db:drop @@ -37,12 +34,11 @@ db-reset: start: bin/rails s -lint: lint-code lint-style - linter-code-fix: bundle exec rubocop -A test: - bundle exec rspec + bin/rails test + .PHONY: test \ No newline at end of file diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 10a4cba8..ba48690b 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,3 +1,7 @@ class ApplicationRecord < ActiveRecord::Base self.abstract_class = true + + def self.enum_from_array(array) + array.index_by(&:to_sym) + end end diff --git a/app/models/bus.rb b/app/models/bus.rb index 1dcc54cb..ba773b9e 100644 --- a/app/models/bus.rb +++ b/app/models/bus.rb @@ -15,6 +15,7 @@ class Bus < ApplicationRecord has_many :trips has_and_belongs_to_many :services, join_table: :buses_services + enum :model, enum_from_array(MODELS) + validates :number, presence: true, uniqueness: true - validates :model, inclusion: { in: MODELS } end diff --git a/app/models/service.rb b/app/models/service.rb index 9cbb2a32..450f2b57 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -15,5 +15,5 @@ class Service < ApplicationRecord has_and_belongs_to_many :buses, join_table: :buses_services validates :name, presence: true - validates :name, inclusion: { in: SERVICES } + enum :name, enum_from_array(SERVICES) end diff --git a/bin/setup b/bin/setup index f294207b..835e2ac9 100755 --- a/bin/setup +++ b/bin/setup @@ -29,7 +29,7 @@ chdir APP_ROOT do system! 'bin/rails db:setup' puts "\n== Loading data from fixtures/small.json ==" - system! 'bin/rake reload_json[fixtures/small.json]' + system! 'bin/rake utils:reload_json[fixtures/small.json]' puts "\n== Removing old logs and tempfiles ==" system! 'bin/rails log:clear tmp:clear' diff --git a/config/initializers/strong_migrations.rb b/config/initializers/strong_migrations.rb new file mode 100644 index 00000000..5d1cbf40 --- /dev/null +++ b/config/initializers/strong_migrations.rb @@ -0,0 +1,26 @@ +# Mark existing migrations as safe +StrongMigrations.start_after = 20250209144006 + +# Set timeouts for migrations +# If you use PgBouncer in transaction mode, delete these lines and set timeouts on the database user +StrongMigrations.lock_timeout = 10.seconds +StrongMigrations.statement_timeout = 1.hour + +# Analyze tables after indexes are added +# Outdated statistics can sometimes hurt performance +StrongMigrations.auto_analyze = true + +# Set the version of the production database +# so the right checks are run in development +# StrongMigrations.target_version = 10 + +# Add custom checks +# StrongMigrations.add_check do |method, args| +# if method == :add_index && args[0].to_s == "users" +# stop! "No more indexes on the users table" +# end +# end + +# Make some operations safe by default +# See https://github.com/ankane/strong_migrations#safe-by-default +# StrongMigrations.safe_by_default = true diff --git a/db/migrate/20250209144520_add_index_number_column_from_buses.rb b/db/migrate/20250209144520_add_index_number_column_from_buses.rb new file mode 100644 index 00000000..992d505d --- /dev/null +++ b/db/migrate/20250209144520_add_index_number_column_from_buses.rb @@ -0,0 +1,7 @@ +class AddIndexNumberColumnFromBuses < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + add_index :buses, %i[number model], unique: true, algorithm: :concurrently + end +end diff --git a/db/migrate/20250209145721_add_index_from_cities.rb b/db/migrate/20250209145721_add_index_from_cities.rb new file mode 100644 index 00000000..f0072b40 --- /dev/null +++ b/db/migrate/20250209145721_add_index_from_cities.rb @@ -0,0 +1,7 @@ +class AddIndexFromCities < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + add_index :cities, :name, unique: true, algorithm: :concurrently + end +end diff --git a/db/migrate/20250209150005_add_index_from_services.rb b/db/migrate/20250209150005_add_index_from_services.rb new file mode 100644 index 00000000..748301c4 --- /dev/null +++ b/db/migrate/20250209150005_add_index_from_services.rb @@ -0,0 +1,7 @@ +class AddIndexFromServices < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + add_index :services, :name, unique: true, algorithm: :concurrently + end +end diff --git a/db/migrate/20250209150200_add_index_from_trips.rb b/db/migrate/20250209150200_add_index_from_trips.rb new file mode 100644 index 00000000..584b2138 --- /dev/null +++ b/db/migrate/20250209150200_add_index_from_trips.rb @@ -0,0 +1,9 @@ +class AddIndexFromTrips < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + add_index :trips, :from_id, algorithm: :concurrently + add_index :trips, :to_id, algorithm: :concurrently + add_index :trips, :bus_id, algorithm: :concurrently + end +end diff --git a/db/migrate/20250209215015_create_enum_from_bus.rb b/db/migrate/20250209215015_create_enum_from_bus.rb new file mode 100644 index 00000000..7ab43cf5 --- /dev/null +++ b/db/migrate/20250209215015_create_enum_from_bus.rb @@ -0,0 +1,7 @@ +class CreateEnumFromBus < ActiveRecord::Migration[7.2] + def change + safety_assured do + create_enum :mode, Bus::MODELS + end + end +end diff --git a/db/migrate/20250209215912_create_enum_from_service.rb b/db/migrate/20250209215912_create_enum_from_service.rb new file mode 100644 index 00000000..6b9cd994 --- /dev/null +++ b/db/migrate/20250209215912_create_enum_from_service.rb @@ -0,0 +1,7 @@ +class CreateEnumFromService < ActiveRecord::Migration[7.2] + def change + safety_assured do + create_enum :name, Service::SERVICES + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 4b9516c5..c6be77fe 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,13 +10,19 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2019_03_30_193044) do +ActiveRecord::Schema[7.2].define(version: 2025_02_09_215912) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + # Custom types defined in this database. + # Note that some types may not work with other database engines. Be careful if changing database. + create_enum "mode", ["Икарус", "Мерседес", "Сканиа", "Буханка", "УАЗ", "Спринтер", "ГАЗ", "ПАЗ", "Вольво", "Газель"] + create_enum "name", ["WiFi", "Туалет", "Работающий туалет", "Ремни безопасности", "Кондиционер общий", "Кондиционер Индивидуальный", "Телевизор общий", "Телевизор индивидуальный", "Стюардесса", "Можно не печатать билет"] + create_table "buses", force: :cascade do |t| t.string "number" t.string "model" + t.index ["number", "model"], name: "index_buses_on_number_and_model", unique: true end create_table "buses_services", force: :cascade do |t| @@ -26,10 +32,12 @@ create_table "cities", force: :cascade do |t| t.string "name" + t.index ["name"], name: "index_cities_on_name", unique: true end create_table "services", force: :cascade do |t| t.string "name" + t.index ["name"], name: "index_services_on_name", unique: true end create_table "trips", force: :cascade do |t| @@ -39,5 +47,8 @@ t.integer "duration_minutes" t.integer "price_cents" t.integer "bus_id" + t.index ["bus_id"], name: "index_trips_on_bus_id" + t.index ["from_id"], name: "index_trips_on_from_id" + t.index ["to_id"], name: "index_trips_on_to_id" end end diff --git a/fixtures/micro.json b/fixtures/micro.json new file mode 100644 index 00000000..f9d20f7b --- /dev/null +++ b/fixtures/micro.json @@ -0,0 +1,5 @@ +[ + {"from":"Сочи","to":"Тула","start_time":"16:11","duration_minutes":83,"price_cents":23354,"bus":{"number":"229","model":"Икарус","services":["Ремни безопасности","Кондиционер общий","Кондиционер Индивидуальный","Телевизор индивидуальный","Стюардесса","Можно не печатать билет"]}}, + {"from":"Самара","to":"Самара","start_time":"13:13","duration_minutes":572,"price_cents":83861,"bus":{"number":"912","model":"Вольво","services":[]}} +] + diff --git a/lib/benchmark.rb b/lib/benchmark.rb new file mode 100755 index 00000000..9de2a529 --- /dev/null +++ b/lib/benchmark.rb @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby + +# frozen_string_literal: true + +require 'benchmark' + +path = ARGV[0] + + +time = Benchmark.realtime do + system "bin/rake utils:reload_json[fixtures/#{path}.json]" +end + +def printer(time) + pp "Processing time from file: #{time.round(4)}" +end + +printer(time) diff --git a/lib/importer.rb b/lib/importer.rb new file mode 100644 index 00000000..d0c8795e --- /dev/null +++ b/lib/importer.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +class Importer + TRUNCATE_SQL = <<~SQL.squish + TRUNCATE trips, services, cities, buses_services, buses CASCADE; + SQL + + TRIPS_COMMAND = "copy trips (from_id, to_id, start_time, duration_minutes, price_cents, bus_id) from stdin with csv delimiter ';'".freeze + CITIES_COMMAND = "copy cities (id, name) from stdin with csv delimiter ';'".freeze + BUSES_COMMAND = "copy buses (id, number, model) from stdin with csv delimiter ';'".freeze + BUSES_SERVICES_COMMAND = "copy buses_services (bus_id, service_id) from stdin with csv delimiter ';'".freeze + + @@city_by_name = {} + @@buses_by_number = {} + @@services = {} + + def self.call(stream) + ActiveRecord::Base.connection.execute(TRUNCATE_SQL) + ActiveRecord::Base.transaction do + Service::SERVICES.each_with_index do |s, i| + @@services[s] ||= {} + @@services[s] = {id: i, name: s} + end + + Service.import @@services.values + + ActiveRecord::Base.connection.raw_connection.copy_data TRIPS_COMMAND do + raw = ActiveRecord::Base.connection.raw_connection + current_key = '' + nested_key = '' + + current_obj = {} + nested_obj_lvl = 0 + nested_arr_lvl = 0 + + + parser = JSON::Stream::Parser.new + parser.start_object do + nested_obj_lvl += 1 + end + parser.end_object do + nested_obj_lvl -= 1 + if nested_obj_lvl.zero? + transaction(current_obj, raw) + current_obj = {} + end + end + parser.start_array { nested_arr_lvl += 1 } + parser.end_array { nested_arr_lvl -= 1 } + + parser.key do |k| + if nested_obj_lvl.eql? 1 + current_key = k + elsif nested_obj_lvl.eql? 2 + nested_key = k + end + end + + parser.value do |v| + if nested_arr_lvl.eql?(2) && nested_obj_lvl.eql?(2) + current_obj[current_key][nested_key] ||= [] + current_obj[current_key][nested_key] << v + elsif nested_obj_lvl.eql? 2 + current_obj[current_key] ||= {} + current_obj[current_key][nested_key] = v + elsif nested_obj_lvl.eql? 1 + current_obj[current_key] = v + end + end + + stream.each_char do |c| + parser << c + end + end + + ActiveRecord::Base.connection.raw_connection.copy_data CITIES_COMMAND do + raw = ActiveRecord::Base.connection.raw_connection + @@city_by_name.each do |_k, v| + raw.put_copy_data("#{v[:id]};#{v[:name]}\n") + end + end + + ActiveRecord::Base.connection.raw_connection.copy_data BUSES_COMMAND do + raw_bus = ActiveRecord::Base.connection.raw_connection + @@buses_by_number.each do |_k, v| + raw_bus.put_copy_data("#{v[:id]};#{v[:number]};#{v[:model]}\n") + end + end + + ActiveRecord::Base.connection.raw_connection.copy_data BUSES_SERVICES_COMMAND do + raw_services = ActiveRecord::Base.connection.raw_connection + @@buses_by_number.each do |_k, v| + next unless v[:services] + v[:services].each do |service| + raw_services.put_copy_data("#{v[:id]};#{@@services[service][:id]}\n") + end + end + end + end + end + + def self.transaction(trip, connection) + from_obj = @@city_by_name[trip['from']] + + if !from_obj + from_obj = {name: trip['from'], id: @@city_by_name.keys.size + 1} + @@city_by_name[trip['from']] = from_obj + end + + to_obj = @@city_by_name[trip['to']] + + if !to_obj + to_obj = {name: trip['to'], id: @@city_by_name.keys.size + 1} + @@city_by_name[trip['to']] = to_obj + end + + bus_number = trip['bus']['number'] + + buses_obj = @@buses_by_number[bus_number] + + if !buses_obj + buses_obj = { id: @@buses_by_number.keys.size + 1 ,number: bus_number, model: trip['bus']['model'], services: trip['bus']['services'] } + @@buses_by_number[bus_number] = buses_obj + end + + data = "#{from_obj[:id]};#{to_obj[:id]};#{trip['start_time']};#{trip['duration_minutes']};#{trip['price_cents']};#{buses_obj[:id]}\n" + connection.put_copy_data(data) + end +end \ No newline at end of file diff --git a/lib/profiler.rb b/lib/profiler.rb new file mode 100644 index 00000000..ab36539a --- /dev/null +++ b/lib/profiler.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'memory_profiler' +require 'stackprof' +require 'ruby-prof' + + +class Profiler + REPORTS_DIR = 'reports' + + class << self + def make_report(reporter_type) + send(reporter_type) + end + + def memeory_prof + report = MemoryProfiler.report do + system "bin/rake utils:reload_json[fixtures/medium.json]" + end + report.pretty_print(scale_bytes: true) + end + + def stack_prof + StackProf.run(mode: :object, out: "#{REPORTS_DIR}/stackprof.dump", raw: true) do + system "bin/rake utils:reload_json[fixtures/medium.json]" + end + end + + def ruby_prof + RubyProf.measure_mode = RubyProf::MEMORY + result = RubyProf.profile do + system "bin/rake utils:reload_json[fixtures/medium.json]" + end + printer = RubyProf::CallTreePrinter.new(result) + printer.print(path: REPORTS_DIR, profile: 'profile') + end + end +end \ No newline at end of file diff --git a/lib/reports_builder.rb b/lib/reports_builder.rb new file mode 100755 index 00000000..c9e6607a --- /dev/null +++ b/lib/reports_builder.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby + +# frozen_string_literal: true + +require_relative 'profiler' + +begin + prof_type = ARGV[0] + + available_types = %w[ + memeory_prof + stack_prof + ruby_prof + ] + +raise StandardError, "unknow profiler type: #{prof_type}" unless available_types.include?(prof_type) + + Profiler.make_report(prof_type) +end \ No newline at end of file diff --git a/lib/tasks/reports.rake b/lib/tasks/reports.rake new file mode 100644 index 00000000..60547589 --- /dev/null +++ b/lib/tasks/reports.rake @@ -0,0 +1,17 @@ +require 'profiler' + +namespace :reports do + task :build, [:reporter_type] => :environment do |_t, args| + reporter_type = args.reporter_type + + available_types = %w[ + memeory_prof + stack_prof + ruby_prof + ] + + abort "unknow profiler type: #{reporter_type}" unless available_types.include?(reporter_type) + + Profiler.make_report(reporter_type) + end +end \ No newline at end of file diff --git a/lib/tasks/utils.rake b/lib/tasks/utils.rake index 4101bd23..44038980 100644 --- a/lib/tasks/utils.rake +++ b/lib/tasks/utils.rake @@ -1,37 +1,15 @@ # Наивная загрузка данных из json-файла в БД # rake reload_json[fixtures/small.json] +require 'importer' namespace :utils do task :reload_json, [:file_name] => :environment do |_task, args| - json = JSON.parse(File.read(args.file_name)) + abort 'Send file path' unless args.file_name - ActiveRecord::Base.transaction do - City.delete_all - Bus.delete_all - Service.delete_all - Trip.delete_all - ActiveRecord::Base.connection.execute('delete from buses_services;') + # json = JSON.parse(File.read(args.file_name)) + # + stream = File.open(args.file_name) - json.each do |trip| - from = City.find_or_create_by(name: trip['from']) - to = City.find_or_create_by(name: trip['to']) - services = [] - trip['bus']['services'].each do |service| - s = Service.find_or_create_by(name: service) - services << s - end - bus = Bus.find_or_create_by(number: trip['bus']['number']) - bus.update(model: trip['bus']['model'], services: services) - - Trip.create!( - from: from, - to: to, - bus: bus, - start_time: trip['start_time'], - duration_minutes: trip['duration_minutes'], - price_cents: trip['price_cents'], - ) - end - end + Importer.call(stream) end end diff --git a/make-services-app.mk b/make-services-app.mk index cc0dc9e9..8b60a259 100644 --- a/make-services-app.mk +++ b/make-services-app.mk @@ -12,18 +12,9 @@ app-update-bundle: app-debug: docker attach --sig-proxy=false --detach-keys="ctrl-c" $(shell docker ps -q --filter publish=3000) -app-lint: - docker-compose run --rm app make lint - -app-lint-fix: - docker-compose run --rm app make linter-code-fix - app-rails-console: docker-compose run --rm app make console -app-test: - docker-compose run --rm app make test - app-rails: docker-compose run --rm app bin/rails $(T) @@ -36,3 +27,18 @@ app-test: app-fixtures-load: docker-compose run --rm app make fixtures-load +app-benchmark: + docker-compose run --rm app lib/benchmark.rb $(T) + +app-benchmark-small: + make app-benchmark T='small' + +app-benchmark-medium: + make app-benchmark T='medium' + +app-benchmark-large: + make app-benchmark T='large' + +app-report: + docker-compose run --rm app bin/rake reports:build[$(T)] + diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb deleted file mode 100644 index 899a8a74..00000000 --- a/spec/rails_helper.rb +++ /dev/null @@ -1,72 +0,0 @@ -# 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? -# Uncomment the line below in case you have `--require rails_helper` in the `.rspec` file -# that will avoid rails generators crashing because migrations haven't been run yet -# return unless Rails.env.test? -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. -# -# Rails.root.glob('spec/support/**/*.rb').sort_by(&:to_s).each { |f| require f } - -# Ensures that the test database schema matches the current schema file. -# If there are pending migrations it will invoke `db:test:prepare` to -# recreate the test database by loading the schema. -# 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_paths = [ - Rails.root.join('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 uses metadata to mix in different behaviours to your tests, - # for example enabling you to call `get` and `post` in request specs. e.g.: - # - # RSpec.describe UsersController, type: :request do - # # ... - # end - # - # The different available types are documented in the features, such as in - # https://rspec.info/features/7-1/rspec-rails - # - # You can also this infer these behaviours automatically by location, e.g. - # /spec/models would pull in the same behaviour as `type: :model` but this - # behaviour is considered legacy and will be removed in a future version. - # - # To enable this behaviour uncomment the line below. - # 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 deleted file mode 100644 index 327b58ea..00000000 --- a/spec/spec_helper.rb +++ /dev/null @@ -1,94 +0,0 @@ -# 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! - - # 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 diff --git a/spec/fixtures/data.json b/test/fixtures/files/data.json similarity index 100% rename from spec/fixtures/data.json rename to test/fixtures/files/data.json diff --git a/spec/lib/tasks/utils_rake_spec.rb b/test/lib/tasks/utils_test.rb similarity index 63% rename from spec/lib/tasks/utils_rake_spec.rb rename to test/lib/tasks/utils_test.rb index 5691f5bb..1ec3a659 100644 --- a/spec/lib/tasks/utils_rake_spec.rb +++ b/test/lib/tasks/utils_test.rb @@ -1,19 +1,13 @@ # frozen_string_literal: true -require 'rails_helper' +require 'test_helper' require 'rake' -describe 'rake reload_json' do - before do - Rails.application.load_tasks - end - - after { task.reenable } - - let(:task) { Rake::Task['utils:reload_json'] } - let(:path) { 'spec/fixtures/data.json' } - let(:expected_result) do - { +class UtilsTest < ActiveSupport::TestCase + def setup + Rails.application.load_tasks if Rake::Task.tasks.empty? + @path = 'test/fixtures/files/data.json' + @expected_result = { from: 'Сочи', to: 'Тула', start_time:'16:11', @@ -34,8 +28,10 @@ } end - it 'fixtures load' do - task.invoke(path) - expect(Trip.last.to_h).to eq(expected_result) + test 'fixtures load' do + + Rake::Task['utils:reload_json'].invoke(@path) + + assert_equal Trip.last.to_h, @expected_result end end \ No newline at end of file From ea549c7ae39a7ed5fe75b9731819d0cfb1bd5d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A8=D0=BB=D1=8F=D0=BF=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=9D=D0=B8=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8=D1=87?= Date: Wed, 12 Feb 2025 00:09:08 +0300 Subject: [PATCH 3/7] add system test and linter --- .gitignore | 1 + .rubocop.yml | 62 +++++++++++ Gemfile | 32 ++++-- Gemfile.lock | 100 ++++++++++++++++++ Makefile | 5 +- Rakefile | 2 + app/channels/application_cable/channel.rb | 2 + app/channels/application_cable/connection.rb | 2 + app/controllers/application_controller.rb | 2 + app/controllers/trips_controller.rb | 6 +- app/helpers/application_helper.rb | 2 + app/jobs/application_job.rb | 2 + app/mailers/application_mailer.rb | 2 + app/models/application_record.rb | 2 + app/models/bus.rb | 24 +++-- app/models/city.rb | 4 +- app/models/service.rb | 4 +- app/models/trip.rb | 12 +-- app/views/layouts/application.html.erb | 4 +- config.ru | 2 + config/application.rb | 2 + config/boot.rb | 2 + config/database.yml | 1 + config/environment.rb | 2 + config/environments/development.rb | 13 ++- config/environments/production.rb | 10 +- config/environments/test.rb | 8 ++ .../application_controller_renderer.rb | 2 + config/initializers/assets.rb | 14 +++ config/initializers/backtrace_silencers.rb | 2 + .../initializers/content_security_policy.rb | 2 + config/initializers/cookies_serializer.rb | 2 + .../initializers/filter_parameter_logging.rb | 2 + config/initializers/inflections.rb | 2 + config/initializers/mime_types.rb | 2 + config/initializers/strong_migrations.rb | 4 +- config/initializers/wrap_parameters.rb | 2 + config/puma.rb | 8 +- config/routes.rb | 8 +- config/spring.rb | 2 + db/migrate/20190330192820_create_cities.rb | 2 + db/migrate/20190330192933_create_trips.rb | 2 + db/migrate/20190330193017_create_buses.rb | 2 + db/migrate/20190330193027_create_services.rb | 2 + .../20190330193044_create_buses_services.rb | 2 + ...4520_add_index_number_column_from_buses.rb | 4 +- .../20250209145721_add_index_from_cities.rb | 2 + .../20250209150005_add_index_from_services.rb | 4 +- .../20250209150200_add_index_from_trips.rb | 4 +- .../20250209215015_create_enum_from_bus.rb | 2 + ...20250209215912_create_enum_from_service.rb | 2 + db/seeds.rb | 2 + lib/benchmark.rb | 3 +- lib/importer.rb | 47 ++++---- lib/profiler.rb | 11 +- lib/reports_builder.rb | 4 +- lib/tasks/reports.rake | 4 +- lib/tasks/utils.rake | 4 +- make-services-app.mk | 8 +- task-4_test | Bin 0 -> 69632 bytes test/application_system_test_case.rb | 11 +- test/controllers/trips_controller_test.rb | 12 +++ test/fixtures/buses.yml | 4 + test/fixtures/cities.yml | 8 ++ test/fixtures/services.yml | 2 + test/fixtures/trips.yml | 7 ++ test/lib/tasks/utils_test.rb | 17 ++- test/system/trips_test.rb | 21 ++++ test/test_helper.rb | 12 ++- 69 files changed, 467 insertions(+), 100 deletions(-) create mode 100644 .rubocop.yml create mode 100644 config/initializers/assets.rb create mode 100644 task-4_test create mode 100644 test/controllers/trips_controller_test.rb create mode 100644 test/fixtures/buses.yml create mode 100644 test/fixtures/cities.yml create mode 100644 test/fixtures/services.yml create mode 100644 test/fixtures/trips.yml create mode 100644 test/system/trips_test.rb diff --git a/.gitignore b/.gitignore index fb9bdfb7..7efd1f5d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /public /vendor/* .byebug_history +/fixtures/ diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 00000000..8c037a6f --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,62 @@ +--- +AllCops: + DisplayCopNames: true + Exclude: + - "bin/**" + - "db/schema.rb" + - "db/migrate/**" + - "vendor/**/*" + - "node_modules/**/*" + NewCops: enable + TargetRailsVersion: 6 + TargetRubyVersion: 3.2 + +Layout/LineLength: + Enabled: false + +Metrics/AbcSize: + Enabled: false + +Metrics/ModuleLength: + Enabled: false + +Metrics/BlockLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false + +Rails: + Enabled: true + +Rails/UnknownEnv: + Environments: + - production + - development + - staging + - test + +require: + - rubocop-performance + - rubocop-rails + +Style/AsciiComments: + Enabled: false + +Style/ClassAndModuleChildren: + Enabled: false + +Style/Documentation: + Enabled: false + +Style/IfUnlessModifier: + Enabled: false diff --git a/Gemfile b/Gemfile index 2700de64..fa72b171 100644 --- a/Gemfile +++ b/Gemfile @@ -1,36 +1,50 @@ +# frozen_string_literal: true + source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby '3.3.4' -gem 'rails', '~> 7' +gem 'activerecord-import' +gem 'bootsnap', require: false +gem 'flamegraph' +gem 'json-stream' +gem 'meta_request' gem 'pg' gem 'puma' -gem 'bootsnap', require: false +gem 'rack-mini-profiler' +gem 'rails', '~> 7' +gem 'rubocop-rails', require: false +gem 'sprockets-rails' gem 'yajl-ruby', require: 'yajl' -gem 'json-stream' -gem 'activerecord-import' - +gem "pghero" group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'benchmark' - gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] + gem 'bullet' + gem 'byebug', platforms: %i[mri mingw x64_mingw] gem 'memory_profiler' + gem 'rubocop-performance' gem 'ruby-prof' gem 'stackprof' - gem "strong_migrations" + gem 'strong_migrations' end group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. - gem 'web-console' gem 'listen' + gem 'web-console' end group :test do gem 'minitest-power_assert' + gem 'capybara' + gem 'selenium-webdriver' + # Easy installation and use of web drivers to run system tests with browsers + gem 'webdrivers' + gem 'sqlite3' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] diff --git a/Gemfile.lock b/Gemfile.lock index 975dac38..b149e3bb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -74,6 +74,9 @@ GEM minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + ast (2.4.2) base64 (0.2.0) benchmark (0.4.0) bigdecimal (3.1.9) @@ -81,7 +84,19 @@ GEM bootsnap (1.18.4) msgpack (~> 1.2) builder (3.3.0) + bullet (8.0.1) + activesupport (>= 3.0.0) + uniform_notifier (~> 1.11) byebug (11.1.3) + capybara (3.40.0) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.11) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) concurrent-ruby (1.3.5) connection_pool (2.5.0) crass (1.0.6) @@ -96,6 +111,7 @@ GEM ffi (1.17.1-x86_64-darwin) ffi (1.17.1-x86_64-linux-gnu) ffi (1.17.1-x86_64-linux-musl) + flamegraph (0.9.5) globalid (1.2.1) activesupport (>= 6.1) i18n (1.14.7) @@ -105,7 +121,9 @@ GEM pp (>= 0.6.0) rdoc (>= 4.0.0) reline (>= 0.4.2) + json (2.10.1) json-stream (1.0.0) + language_server-protocol (3.17.0.4) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -119,7 +137,11 @@ GEM net-pop net-smtp marcel (1.0.4) + matrix (0.4.2) memory_profiler (1.1.0) + meta_request (0.8.5) + rack-contrib (>= 1.1, < 3) + railties (>= 3.0.0, < 9) mini_mime (1.1.5) minitest (5.25.4) minitest-power_assert (0.3.1) @@ -152,7 +174,13 @@ GEM racc (~> 1.4) nokogiri (1.18.2-x86_64-linux-musl) racc (~> 1.4) + parallel (1.26.3) + parser (3.3.7.1) + ast (~> 2.4.1) + racc pg (1.5.9) + pghero (3.6.1) + activerecord (>= 6.1) power_assert (2.0.5) pp (0.6.2) prettyprint @@ -160,10 +188,15 @@ GEM psych (5.2.3) date stringio + public_suffix (6.0.1) puma (6.6.0) nio4r (~> 2.0) racc (1.8.1) rack (3.1.9) + rack-contrib (2.5.0) + rack (< 4) + rack-mini-profiler (3.3.1) + rack (>= 1.2.0) rack-session (2.1.0) base64 (>= 0.1.0) rack (>= 3.0.0) @@ -200,16 +233,60 @@ GEM rake (>= 12.2) thor (~> 1.0, >= 1.2.2) zeitwerk (~> 2.6) + rainbow (3.1.1) rake (13.2.1) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) rdoc (6.12.0) psych (>= 4.0.0) + regexp_parser (2.10.0) reline (0.6.0) io-console (~> 0.5) + rexml (3.4.0) + rubocop (1.71.2) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.38.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.38.0) + parser (>= 3.3.1.0) + rubocop-performance (1.23.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.29.1) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.52.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-prof (1.7.1) + ruby-progressbar (1.13.0) + rubyzip (2.4.1) securerandom (0.4.1) + selenium-webdriver (4.10.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.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) + sprockets (>= 3.0.0) + sqlite3 (2.5.0-aarch64-linux-gnu) + sqlite3 (2.5.0-aarch64-linux-musl) + sqlite3 (2.5.0-arm-linux-gnu) + sqlite3 (2.5.0-arm-linux-musl) + sqlite3 (2.5.0-arm64-darwin) + sqlite3 (2.5.0-x86_64-darwin) + sqlite3 (2.5.0-x86_64-linux-gnu) + sqlite3 (2.5.0-x86_64-linux-musl) stackprof (0.2.27) stringio (3.1.2) strong_migrations (2.2.0) @@ -218,16 +295,27 @@ GEM timeout (0.4.3) tzinfo (2.0.6) concurrent-ruby (~> 1.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + uniform_notifier (1.16.0) useragent (0.16.11) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) + webdrivers (5.3.1) + nokogiri (~> 1.6) + rubyzip (>= 1.3.0) + selenium-webdriver (~> 4.0, < 4.11) + websocket (1.2.11) websocket-driver (0.7.7) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) yajl-ruby (1.4.3) zeitwerk (2.7.1) @@ -245,19 +333,31 @@ DEPENDENCIES activerecord-import benchmark bootsnap + bullet byebug + capybara + flamegraph json-stream listen memory_profiler + meta_request minitest-power_assert pg + pghero puma + rack-mini-profiler rails (~> 7) + rubocop-performance + rubocop-rails ruby-prof + selenium-webdriver + sprockets-rails + sqlite3 stackprof strong_migrations tzinfo-data web-console + webdrivers yajl-ruby RUBY VERSION diff --git a/Makefile b/Makefile index fe37d1d1..4c12aa25 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ setup-app: cp -n .env.example .env || true bin/setup -fixtures-load: +fixtures-load-json: bin/rake utils:reload_json[fixtures/medium.json] clean: @@ -40,5 +40,8 @@ linter-code-fix: test: bin/rails test +system-test: + bin/rails test:system + .PHONY: test \ No newline at end of file diff --git a/Rakefile b/Rakefile index e85f9139..488c551f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb index d6726972..9aec2305 100644 --- a/app/channels/application_cable/channel.rb +++ b/app/channels/application_cable/channel.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationCable class Channel < ActionCable::Channel::Base end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb index 0ff5442f..8d6c2a1b 100644 --- a/app/channels/application_cable/connection.rb +++ b/app/channels/application_cable/connection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationCable class Connection < ActionCable::Connection::Base end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09705d12..7944f9f9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + class ApplicationController < ActionController::Base end diff --git a/app/controllers/trips_controller.rb b/app/controllers/trips_controller.rb index acb38be2..1490d713 100644 --- a/app/controllers/trips_controller.rb +++ b/app/controllers/trips_controller.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + class TripsController < ApplicationController def index - @from = City.find_by_name!(params[:from]) - @to = City.find_by_name!(params[:to]) + @from = City.find_by!(name: params[:from]) + @to = City.find_by!(name: params[:to]) @trips = Trip.where(from: @from, to: @to).order(:start_time) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index de6be794..15b06f0f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + module ApplicationHelper end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index a009ace5..d92ffddc 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + class ApplicationJob < ActiveJob::Base end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 286b2239..d84cb6e7 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationMailer < ActionMailer::Base default from: 'from@example.com' layout 'mailer' diff --git a/app/models/application_record.rb b/app/models/application_record.rb index ba48690b..68c1fef6 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationRecord < ActiveRecord::Base self.abstract_class = true diff --git a/app/models/bus.rb b/app/models/bus.rb index ba773b9e..e20e2ee7 100644 --- a/app/models/bus.rb +++ b/app/models/bus.rb @@ -1,15 +1,17 @@ +# frozen_string_literal: true + class Bus < ApplicationRecord - MODELS = [ - 'Икарус', - 'Мерседес', - 'Сканиа', - 'Буханка', - 'УАЗ', - 'Спринтер', - 'ГАЗ', - 'ПАЗ', - 'Вольво', - 'Газель', + MODELS = %w[ + Икарус + Мерседес + Сканиа + Буханка + УАЗ + Спринтер + ГАЗ + ПАЗ + Вольво + Газель ].freeze has_many :trips diff --git a/app/models/city.rb b/app/models/city.rb index 19ec7f36..19897544 100644 --- a/app/models/city.rb +++ b/app/models/city.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + class City < ApplicationRecord validates :name, presence: true, uniqueness: true validate :name_has_no_spaces def name_has_no_spaces - errors.add(:name, "has spaces") if name.include?(' ') + errors.add(:name, 'has spaces') if name.include?(' ') end end diff --git a/app/models/service.rb b/app/models/service.rb index 450f2b57..e092e086 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Service < ApplicationRecord SERVICES = [ 'WiFi', @@ -9,7 +11,7 @@ class Service < ApplicationRecord 'Телевизор общий', 'Телевизор индивидуальный', 'Стюардесса', - 'Можно не печатать билет', + 'Можно не печатать билет' ].freeze has_and_belongs_to_many :buses, join_table: :buses_services diff --git a/app/models/trip.rb b/app/models/trip.rb index 9d63dfff..d0bda301 100644 --- a/app/models/trip.rb +++ b/app/models/trip.rb @@ -1,14 +1,12 @@ +# frozen_string_literal: true + class Trip < ApplicationRecord - HHMM_REGEXP = /([0-1][0-9]|[2][0-3]):[0-5][0-9]/ + HHMM_REGEXP = /([0-1][0-9]|2[0-3]):[0-5][0-9]/ belongs_to :from, class_name: 'City' belongs_to :to, class_name: 'City' belongs_to :bus - validates :from, presence: true - validates :to, presence: true - validates :bus, presence: true - validates :start_time, format: { with: HHMM_REGEXP, message: 'Invalid time' } validates :duration_minutes, presence: true validates :duration_minutes, numericality: { greater_than: 0 } @@ -25,8 +23,8 @@ def to_h bus: { number: bus.number, model: bus.model, - services: bus.services.map(&:name), - }, + services: bus.services.map(&:name) + } } end end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index e64170ee..128f140d 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -5,8 +5,8 @@ <%= csrf_meta_tags %> <%= csp_meta_tag %> - <%= stylesheet_link_tag 'application', media: 'all' %> - <%= javascript_include_tag 'application' %> + <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" %> + <%= javascript_include_tag "application", "data-turbolinks-track": "reload" %> diff --git a/config.ru b/config.ru index f7ba0b52..842bccc3 100644 --- a/config.ru +++ b/config.ru @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is used by Rack-based servers to start the application. require_relative 'config/environment' diff --git a/config/application.rb b/config/application.rb index 9c331097..3cb92f3c 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'boot' require 'rails/all' diff --git a/config/boot.rb b/config/boot.rb index b9e460ce..c04863fa 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/config/database.yml b/config/database.yml index 6070b7c0..c46133a5 100644 --- a/config/database.yml +++ b/config/database.yml @@ -60,6 +60,7 @@ development: test: <<: *default database: task-4_test + adapter: sqlite3 # As with config/secrets.yml, you never want to store sensitive information, # like your database password, in your source code. If your source code is diff --git a/config/environment.rb b/config/environment.rb index 426333bb..d5abe558 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Load the Rails application. require_relative 'application' diff --git a/config/environments/development.rb b/config/environments/development.rb index bc3f8142..8543a99d 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,4 +1,15 @@ +# frozen_string_literal: true + Rails.application.configure do + config.after_initialize do + Bullet.enable = true + Bullet.alert = true + Bullet.bullet_logger = true + Bullet.console = true + Bullet.rails_logger = true + Bullet.add_footer = true + end + # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on @@ -14,7 +25,7 @@ # Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. - if Rails.root.join('tmp', 'caching-dev.txt').exist? + if Rails.root.join('tmp/caching-dev.txt').exist? config.action_controller.perform_caching = true config.cache_store = :memory_store diff --git a/config/environments/production.rb b/config/environments/production.rb index 398c8489..50fa05bb 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -54,7 +56,7 @@ config.log_level = :debug # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] + config.log_tags = [:request_id] # Use a different cache store in production. # config.cache_store = :mem_cache_store @@ -77,14 +79,14 @@ config.active_support.deprecation = :notify # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new + 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) + if ENV['RAILS_LOG_TO_STDOUT'].present? + logger = ActiveSupport::Logger.new($stdout) logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) end diff --git a/config/environments/test.rb b/config/environments/test.rb index 0a38fd3c..d928ac16 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,4 +1,12 @@ +# frozen_string_literal: true + Rails.application.configure do + config.after_initialize do + Bullet.enable = true + Bullet.bullet_logger = true + Bullet.raise = true # raise an error if n+1 query occurs + end + # Settings specified here will take precedence over those in config/application.rb. # The test environment is used exclusively to run your application's diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb index 89d2efab..6d56e439 100644 --- a/config/initializers/application_controller_renderer.rb +++ b/config/initializers/application_controller_renderer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # ActiveSupport::Reloader.to_prepare do diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 00000000..c8ea76c7 --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Add additional assets to the asset load path. +# Rails.application.config.assets.paths << Emoji.images_path +# Add Yarn node_modules folder to the asset load path. + +# 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 ) + +Rails.application.config.assets.paths << Rails.root.join('app/assets/javascripts') +Rails.application.config.assets.paths << Rails.root.join('app/assets/stylesheets') +Rails.application.config.assets.precompile += %w[application.js application.css] diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb index 59385cdf..4b63f289 100644 --- a/config/initializers/backtrace_silencers.rb +++ b/config/initializers/backtrace_silencers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index d3bcaa5e..e3c96496 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Define an application-wide content security policy diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb index 5a6a32d3..ee8dff9c 100644 --- a/config/initializers/cookies_serializer.rb +++ b/config/initializers/cookies_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Specify a serializer for the signed and encrypted cookie jars. diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 4a994e1e..7a4f47b4 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Configure sensitive parameters which will be filtered from the log file. diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index ac033bf9..dc847422 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format. Inflections diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index dc189968..be6fedc5 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Add new mime types for use in respond_to blocks: diff --git a/config/initializers/strong_migrations.rb b/config/initializers/strong_migrations.rb index 5d1cbf40..308870b1 100644 --- a/config/initializers/strong_migrations.rb +++ b/config/initializers/strong_migrations.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Mark existing migrations as safe -StrongMigrations.start_after = 20250209144006 +StrongMigrations.start_after = 20_250_209_144_006 # Set timeouts for migrations # If you use PgBouncer in transaction mode, delete these lines and set timeouts on the database user diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index bbfc3961..2f3c0db4 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # This file contains settings for ActionController::ParamsWrapper which diff --git a/config/puma.rb b/config/puma.rb index a5eccf81..d9a94f35 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,19 +1,21 @@ +# frozen_string_literal: true + # Puma can serve each request in a thread from an internal thread pool. # The `threads` method setting takes two numbers: a minimum and maximum. # Any libraries that use thread pools should be configured to match # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +threads_count = ENV.fetch('RAILS_MAX_THREADS', 5) threads threads_count, threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # -port ENV.fetch("PORT") { 3000 } +port ENV.fetch('PORT', 3000) # Specifies the `environment` that Puma will run in. # -environment ENV.fetch("RAILS_ENV") { "development" } +environment ENV.fetch('RAILS_ENV', 'development') # Specifies the number of `workers` to boot in clustered mode. # Workers are forked webserver processes. If using threads and workers together diff --git a/config/routes.rb b/config/routes.rb index a2da6a7b..0d75bbe0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,9 @@ +# frozen_string_literal: true + Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html - get "/" => "statistics#index" - get "автобусы/:from/:to" => "trips#index" + mount PgHero::Engine, at: "pghero" + + get '/' => 'statistics#index' + get 'buses/:from/:to' => 'trips#index' end diff --git a/config/spring.rb b/config/spring.rb index 9fa7863f..c5933e49 100644 --- a/config/spring.rb +++ b/config/spring.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + %w[ .ruby-version .rbenv-vars diff --git a/db/migrate/20190330192820_create_cities.rb b/db/migrate/20190330192820_create_cities.rb index 96f5b3e8..fcdfd0f3 100644 --- a/db/migrate/20190330192820_create_cities.rb +++ b/db/migrate/20190330192820_create_cities.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateCities < ActiveRecord::Migration[5.2] def change create_table :cities do |t| diff --git a/db/migrate/20190330192933_create_trips.rb b/db/migrate/20190330192933_create_trips.rb index 46f19479..c86743d7 100644 --- a/db/migrate/20190330192933_create_trips.rb +++ b/db/migrate/20190330192933_create_trips.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateTrips < ActiveRecord::Migration[5.2] def change create_table :trips do |t| diff --git a/db/migrate/20190330193017_create_buses.rb b/db/migrate/20190330193017_create_buses.rb index 274117ed..622af62d 100644 --- a/db/migrate/20190330193017_create_buses.rb +++ b/db/migrate/20190330193017_create_buses.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateBuses < ActiveRecord::Migration[5.2] def change create_table :buses do |t| diff --git a/db/migrate/20190330193027_create_services.rb b/db/migrate/20190330193027_create_services.rb index 243473b7..71831dc0 100644 --- a/db/migrate/20190330193027_create_services.rb +++ b/db/migrate/20190330193027_create_services.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateServices < ActiveRecord::Migration[5.2] def change create_table :services do |t| diff --git a/db/migrate/20190330193044_create_buses_services.rb b/db/migrate/20190330193044_create_buses_services.rb index 71da32bb..e8b0446e 100644 --- a/db/migrate/20190330193044_create_buses_services.rb +++ b/db/migrate/20190330193044_create_buses_services.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateBusesServices < ActiveRecord::Migration[5.2] def change create_table :buses_services do |t| diff --git a/db/migrate/20250209144520_add_index_number_column_from_buses.rb b/db/migrate/20250209144520_add_index_number_column_from_buses.rb index 992d505d..a0686e73 100644 --- a/db/migrate/20250209144520_add_index_number_column_from_buses.rb +++ b/db/migrate/20250209144520_add_index_number_column_from_buses.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + class AddIndexNumberColumnFromBuses < ActiveRecord::Migration[7.2] disable_ddl_transaction! - + def change add_index :buses, %i[number model], unique: true, algorithm: :concurrently end diff --git a/db/migrate/20250209145721_add_index_from_cities.rb b/db/migrate/20250209145721_add_index_from_cities.rb index f0072b40..4f26742c 100644 --- a/db/migrate/20250209145721_add_index_from_cities.rb +++ b/db/migrate/20250209145721_add_index_from_cities.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddIndexFromCities < ActiveRecord::Migration[7.2] disable_ddl_transaction! diff --git a/db/migrate/20250209150005_add_index_from_services.rb b/db/migrate/20250209150005_add_index_from_services.rb index 748301c4..6e0a99a2 100644 --- a/db/migrate/20250209150005_add_index_from_services.rb +++ b/db/migrate/20250209150005_add_index_from_services.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + class AddIndexFromServices < ActiveRecord::Migration[7.2] disable_ddl_transaction! - + def change add_index :services, :name, unique: true, algorithm: :concurrently end diff --git a/db/migrate/20250209150200_add_index_from_trips.rb b/db/migrate/20250209150200_add_index_from_trips.rb index 584b2138..657e75d4 100644 --- a/db/migrate/20250209150200_add_index_from_trips.rb +++ b/db/migrate/20250209150200_add_index_from_trips.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + class AddIndexFromTrips < ActiveRecord::Migration[7.2] disable_ddl_transaction! - + def change add_index :trips, :from_id, algorithm: :concurrently add_index :trips, :to_id, algorithm: :concurrently diff --git a/db/migrate/20250209215015_create_enum_from_bus.rb b/db/migrate/20250209215015_create_enum_from_bus.rb index 7ab43cf5..284e6a1e 100644 --- a/db/migrate/20250209215015_create_enum_from_bus.rb +++ b/db/migrate/20250209215015_create_enum_from_bus.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateEnumFromBus < ActiveRecord::Migration[7.2] def change safety_assured do diff --git a/db/migrate/20250209215912_create_enum_from_service.rb b/db/migrate/20250209215912_create_enum_from_service.rb index 6b9cd994..2cc41d9b 100644 --- a/db/migrate/20250209215912_create_enum_from_service.rb +++ b/db/migrate/20250209215912_create_enum_from_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateEnumFromService < ActiveRecord::Migration[7.2] def change safety_assured do diff --git a/db/seeds.rb b/db/seeds.rb index 1beea2ac..ebd18895 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file should contain all the record creation needed to seed the database with its default values. # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). # diff --git a/lib/benchmark.rb b/lib/benchmark.rb index 9de2a529..944b571c 100755 --- a/lib/benchmark.rb +++ b/lib/benchmark.rb @@ -6,13 +6,12 @@ path = ARGV[0] - time = Benchmark.realtime do system "bin/rake utils:reload_json[fixtures/#{path}.json]" end def printer(time) - pp "Processing time from file: #{time.round(4)}" + Rails.logger.debug { "Processing time from file: #{time.round(4)}" } end printer(time) diff --git a/lib/importer.rb b/lib/importer.rb index d0c8795e..2b50d752 100644 --- a/lib/importer.rb +++ b/lib/importer.rb @@ -5,10 +5,10 @@ class Importer TRUNCATE trips, services, cities, buses_services, buses CASCADE; SQL - TRIPS_COMMAND = "copy trips (from_id, to_id, start_time, duration_minutes, price_cents, bus_id) from stdin with csv delimiter ';'".freeze - CITIES_COMMAND = "copy cities (id, name) from stdin with csv delimiter ';'".freeze - BUSES_COMMAND = "copy buses (id, number, model) from stdin with csv delimiter ';'".freeze - BUSES_SERVICES_COMMAND = "copy buses_services (bus_id, service_id) from stdin with csv delimiter ';'".freeze + TRIPS_COMMAND = "copy trips (from_id, to_id, start_time, duration_minutes, price_cents, bus_id) from stdin with csv delimiter ';'" + CITIES_COMMAND = "copy cities (id, name) from stdin with csv delimiter ';'" + BUSES_COMMAND = "copy buses (id, number, model) from stdin with csv delimiter ';'" + BUSES_SERVICES_COMMAND = "copy buses_services (bus_id, service_id) from stdin with csv delimiter ';'" @@city_by_name = {} @@buses_by_number = {} @@ -17,9 +17,9 @@ class Importer def self.call(stream) ActiveRecord::Base.connection.execute(TRUNCATE_SQL) ActiveRecord::Base.transaction do - Service::SERVICES.each_with_index do |s, i| + Service::SERVICES.each_with_index do |s, i| @@services[s] ||= {} - @@services[s] = {id: i, name: s} + @@services[s] = { id: i, name: s } end Service.import @@services.values @@ -33,12 +33,11 @@ def self.call(stream) nested_obj_lvl = 0 nested_arr_lvl = 0 - parser = JSON::Stream::Parser.new - parser.start_object do + parser.start_object do nested_obj_lvl += 1 end - parser.end_object do + parser.end_object do nested_obj_lvl -= 1 if nested_obj_lvl.zero? transaction(current_obj, raw) @@ -75,22 +74,23 @@ def self.call(stream) ActiveRecord::Base.connection.raw_connection.copy_data CITIES_COMMAND do raw = ActiveRecord::Base.connection.raw_connection - @@city_by_name.each do |_k, v| + @@city_by_name.each_value do |v| raw.put_copy_data("#{v[:id]};#{v[:name]}\n") end end ActiveRecord::Base.connection.raw_connection.copy_data BUSES_COMMAND do raw_bus = ActiveRecord::Base.connection.raw_connection - @@buses_by_number.each do |_k, v| + @@buses_by_number.each_value do |v| raw_bus.put_copy_data("#{v[:id]};#{v[:number]};#{v[:model]}\n") end end ActiveRecord::Base.connection.raw_connection.copy_data BUSES_SERVICES_COMMAND do raw_services = ActiveRecord::Base.connection.raw_connection - @@buses_by_number.each do |_k, v| + @@buses_by_number.each_value do |v| next unless v[:services] + v[:services].each do |service| raw_services.put_copy_data("#{v[:id]};#{@@services[service][:id]}\n") end @@ -102,15 +102,15 @@ def self.call(stream) def self.transaction(trip, connection) from_obj = @@city_by_name[trip['from']] - if !from_obj - from_obj = {name: trip['from'], id: @@city_by_name.keys.size + 1} + unless from_obj + from_obj = { name: trip['from'], id: @@city_by_name.keys.size + 1 } @@city_by_name[trip['from']] = from_obj end - + to_obj = @@city_by_name[trip['to']] - if !to_obj - to_obj = {name: trip['to'], id: @@city_by_name.keys.size + 1} + unless to_obj + to_obj = { name: trip['to'], id: @@city_by_name.keys.size + 1 } @@city_by_name[trip['to']] = to_obj end @@ -118,12 +118,13 @@ def self.transaction(trip, connection) buses_obj = @@buses_by_number[bus_number] - if !buses_obj - buses_obj = { id: @@buses_by_number.keys.size + 1 ,number: bus_number, model: trip['bus']['model'], services: trip['bus']['services'] } + unless buses_obj + buses_obj = { id: @@buses_by_number.keys.size + 1, number: bus_number, model: trip['bus']['model'], + services: trip['bus']['services'] } @@buses_by_number[bus_number] = buses_obj end - - data = "#{from_obj[:id]};#{to_obj[:id]};#{trip['start_time']};#{trip['duration_minutes']};#{trip['price_cents']};#{buses_obj[:id]}\n" - connection.put_copy_data(data) + + data = "#{from_obj[:id]};#{to_obj[:id]};#{trip['start_time']};#{trip['duration_minutes']};#{trip['price_cents']};#{buses_obj[:id]}\n" + connection.put_copy_data(data) end -end \ No newline at end of file +end diff --git a/lib/profiler.rb b/lib/profiler.rb index ab36539a..b8c90712 100644 --- a/lib/profiler.rb +++ b/lib/profiler.rb @@ -4,10 +4,9 @@ require 'stackprof' require 'ruby-prof' - class Profiler REPORTS_DIR = 'reports' - + class << self def make_report(reporter_type) send(reporter_type) @@ -15,24 +14,24 @@ def make_report(reporter_type) def memeory_prof report = MemoryProfiler.report do - system "bin/rake utils:reload_json[fixtures/medium.json]" + system 'bin/rake utils:reload_json[fixtures/medium.json]' end report.pretty_print(scale_bytes: true) end def stack_prof StackProf.run(mode: :object, out: "#{REPORTS_DIR}/stackprof.dump", raw: true) do - system "bin/rake utils:reload_json[fixtures/medium.json]" + system 'bin/rake utils:reload_json[fixtures/medium.json]' end end def ruby_prof RubyProf.measure_mode = RubyProf::MEMORY result = RubyProf.profile do - system "bin/rake utils:reload_json[fixtures/medium.json]" + system 'bin/rake utils:reload_json[fixtures/medium.json]' end printer = RubyProf::CallTreePrinter.new(result) printer.print(path: REPORTS_DIR, profile: 'profile') end end -end \ No newline at end of file +end diff --git a/lib/reports_builder.rb b/lib/reports_builder.rb index c9e6607a..ec6913ed 100755 --- a/lib/reports_builder.rb +++ b/lib/reports_builder.rb @@ -13,7 +13,7 @@ ruby_prof ] -raise StandardError, "unknow profiler type: #{prof_type}" unless available_types.include?(prof_type) + raise StandardError, "unknow profiler type: #{prof_type}" unless available_types.include?(prof_type) Profiler.make_report(prof_type) -end \ No newline at end of file +end diff --git a/lib/tasks/reports.rake b/lib/tasks/reports.rake index 60547589..c2cec2df 100644 --- a/lib/tasks/reports.rake +++ b/lib/tasks/reports.rake @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'profiler' namespace :reports do @@ -14,4 +16,4 @@ namespace :reports do Profiler.make_report(reporter_type) end -end \ No newline at end of file +end diff --git a/lib/tasks/utils.rake b/lib/tasks/utils.rake index 44038980..f38b6abb 100644 --- a/lib/tasks/utils.rake +++ b/lib/tasks/utils.rake @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Наивная загрузка данных из json-файла в БД # rake reload_json[fixtures/small.json] require 'importer' @@ -7,7 +9,7 @@ namespace :utils do abort 'Send file path' unless args.file_name # json = JSON.parse(File.read(args.file_name)) - # + # stream = File.open(args.file_name) Importer.call(stream) diff --git a/make-services-app.mk b/make-services-app.mk index 8b60a259..ec05fffd 100644 --- a/make-services-app.mk +++ b/make-services-app.mk @@ -25,7 +25,7 @@ app-test: docker-compose run --rm app make test app-fixtures-load: - docker-compose run --rm app make fixtures-load + docker-compose run --rm app make fixtures-load-json app-benchmark: docker-compose run --rm app lib/benchmark.rb $(T) @@ -42,3 +42,9 @@ app-benchmark-large: app-report: docker-compose run --rm app bin/rake reports:build[$(T)] +app-lint-fix: + docker-compose run --rm app make linter-code-fix + +app-sistem-test: + docker-compose run --rm app bin/rails test:system + diff --git a/task-4_test b/task-4_test new file mode 100644 index 0000000000000000000000000000000000000000..19998d4e62ff7ae97b0ca2755573fec9882364aa GIT binary patch literal 69632 zcmeI*Uu@e%90zbacAEdaj0#x=O|vpUv$iqYNmg1UAdCv8buE+@G5%R@;%?FMr#Nl_ z@i(1>!~^UFAq3(DB;G(w0yL0-ClCT5r92D}NPrN62ZRKIHw4_-acXC6$Dfx)`$lbM z|L)Fr_wgm&B^T$`Tc)axB`-GZy6MPyF3UwC+;y_daa@G{Zlk~cEkid%e~13d2evoc zjBr;zaz`e)kITkqIr1>sJ^a!zmw764Mf%nBH7O(cNAj8ESi(;H6rW8fn;isk2tWV= z5P$##&Ubd%#oJ{MtoNC?LwO{F#r&*SlSihPnAnT$zev=)A& zS{9aDekG|bEAcY+)zJ+q?Yj~&Ns{;zl6$t@e!6$b@LBBvR4X}|JzkDlcJSQErTMv; zgVVIo;mK(_=(_`-4qU%CekmIA5CeghW7>|vMtyID&}@}uZ^+TX*I0I}Rx6DtS&7A@?Xp9qDxd4MDZuj2o*g`$;G^E_jQ=N8VUv2E{J_y4+#mn}2tWV= z5P$##AOHafKmY;|7;J%&)$d;Uf{5J6<(j5-p6$Hdd2Q{nwG(UY4EdUik{3AgD|vx* z$b00}V0RR)hX4d1009U<00Izz00bZa0SFA7z(o;3;l1?={*nxmjtY|KE*DZx)e=M$ zwu^q6_WN@q89^8oz11PwhZAi1{zxKxgfveN;D+`buboWN96b4vqd&Mo00Izz00bZa z0SG_<0uX=z1RyZr0+Kj#x!CvKKb!x1>;HeH@Ba^Y2hn^8KmY;|fB*y_009U<00Izz z00aU8Y5Fk$QDk#}cf9~jiI8vD`u}eNnXm}~2tWV=5P$##AOHafKmY;|fWW{Aq|@ou zkDmN=ufXlNtyw$Tq!*d3B>sH<<3|N<^l-Cvym=p6{~saWvibkF1Jf-u6ao-{00bZa z0SG_<0uX=z1Rwx`kU%yo>=1i%f8{`RZ-!nVz?=J%55E7)TmOHCBWFUzumb@IKmY;| zfB*y_009U<00IzzzacRTNNUhkY3F_~_hT6=;9 z-tw;h_Zde%JMTM%20{P=5P$##AOHafKmY;|fB*y_5Ej@a%!ucl`}?2&=h^yyto{!R zVh;ikfB*y_009U<00Izz00bZ~I0BbmGP?T7`=2M`^pby4JT9^&|Lj8mnm(bb>)-$P z8%KT{oKB&o5P$##AOHafKmY;|fB*y_009V`TOcKP*Z)^e)e_$Nf6V{St%e*BfB*y_ z009U<00Izz00bZafx!}B^M7|C0OtRLb>h%k2tWV=5P$##AOHafKmY;|fWW^HVDo?W zAprLNKfC^aicE0iXYv*Kh@2)blc&ilSt9q4DH_HN0uX=z1Rwwb2tWV=5P$##AaH>T zWK$7ESG939pQrcwRl1r@h9cwnd^Qn`(0pUrcsQbuWnbO=Y6zP$p z!ANmT*RoV!XY2n{MCFJ|4v^bOl{`dFk{8Jv zJr}{#4+Mf)E*t|c{AJWI- z{h>l3=7oY&|DkY*&HbaHkURMo`$BZ~F9bv0^gpsqP!j$t3p7OM|Lpz$2)T(PZ_+#5 zAOHafKmY;|fB*y_009U<00I!$QlJ)z?!3EHYL$;!b<=1aGc`-qto%ZGQQvRs6{}zt zti?sOyuVn`7EEotP%e&X1#SPL$>!sGRei6f$+|j`AD__jR~B^jszQF#l)_DXbmzsT zlGRwL+RaAYYB-M7ayH3NmnLkQQef}@N7(oO{eo`*-0}n?5dsi^00bZa0SG_<0uX=z Y1R!ue3lL!^A9z(FGA!&&1YS4z8^c)GU;qFB literal 0 HcmV?d00001 diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb index d19212ab..e0ab8a4b 100644 --- a/test/application_system_test_case.rb +++ b/test/application_system_test_case.rb @@ -1,5 +1,10 @@ -require "test_helper" +# frozen_string_literal: true + +require 'test_helper' class ApplicationSystemTestCase < ActionDispatch::SystemTestCase - driven_by :selenium, using: :chrome, screen_size: [1400, 1400] -end + driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400] do |driver| + driver.add_argument('--no-sandbox') + driver.add_argument('--disable-gpu') + end +end \ No newline at end of file diff --git a/test/controllers/trips_controller_test.rb b/test/controllers/trips_controller_test.rb new file mode 100644 index 00000000..8a48eb32 --- /dev/null +++ b/test/controllers/trips_controller_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +class TripsControllerTest < ActionDispatch::IntegrationTest + test '#index' do + from = cities(:moskow) + to = cities(:volgograd) + + get "/buses/#{from.name}/#{to.name}" + + assert_response :success + end +end \ No newline at end of file diff --git a/test/fixtures/buses.yml b/test/fixtures/buses.yml new file mode 100644 index 00000000..9b35c8cf --- /dev/null +++ b/test/fixtures/buses.yml @@ -0,0 +1,4 @@ +ickarus: + number: 111 + model: Икарус + services: split \ No newline at end of file diff --git a/test/fixtures/cities.yml b/test/fixtures/cities.yml new file mode 100644 index 00000000..273e06ec --- /dev/null +++ b/test/fixtures/cities.yml @@ -0,0 +1,8 @@ +DEFAULTS: &DEFAULTS + name: Moskow + +moskow: + <<: *DEFAULTS + +volgograd: + name: Volgograd \ No newline at end of file diff --git a/test/fixtures/services.yml b/test/fixtures/services.yml new file mode 100644 index 00000000..26dc7d4f --- /dev/null +++ b/test/fixtures/services.yml @@ -0,0 +1,2 @@ +split: + name: Кондиционер общий \ No newline at end of file diff --git a/test/fixtures/trips.yml b/test/fixtures/trips.yml new file mode 100644 index 00000000..5df16bb3 --- /dev/null +++ b/test/fixtures/trips.yml @@ -0,0 +1,7 @@ +moscow-volga: + from: moskow + to: volgograd + start_time: "12:00" + duration_minutes: 0 + price_cents: 0 + bus: ickarus \ No newline at end of file diff --git a/test/lib/tasks/utils_test.rb b/test/lib/tasks/utils_test.rb index 1ec3a659..0bdbf00a 100644 --- a/test/lib/tasks/utils_test.rb +++ b/test/lib/tasks/utils_test.rb @@ -10,13 +10,13 @@ def setup @expected_result = { from: 'Сочи', to: 'Тула', - start_time:'16:11', - duration_minutes:83, - price_cents:23354, - bus:{ - number:'229', - model:'Икарус', - services:[ + start_time: '16:11', + duration_minutes: 83, + price_cents: 23_354, + bus: { + number: '229', + model: 'Икарус', + services: [ 'Ремни безопасности', 'Кондиционер общий', 'Кондиционер Индивидуальный', @@ -29,9 +29,8 @@ def setup end test 'fixtures load' do - Rake::Task['utils:reload_json'].invoke(@path) assert_equal Trip.last.to_h, @expected_result end -end \ No newline at end of file +end diff --git a/test/system/trips_test.rb b/test/system/trips_test.rb new file mode 100644 index 00000000..fd40bb55 --- /dev/null +++ b/test/system/trips_test.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'application_system_test_case' + +class TripsTest < ApplicationSystemTestCase + test 'visit index page' do + from = cities(:moskow) + to = cities(:volgograd) + + visit "/buses/#{from.name}/#{to.name}" + + page.assert_selector('h1', text: "Автобусы #{from.name} – #{to.name}") + page.assert_selector('h2', text: "В расписании 1 рейс") + page.assert_selector('li', text: "Отправление: 12:00") + page.assert_selector('li', text: "Прибытие: 12:00") + page.assert_selector('li', text: "В пути: 0ч. 0мин.") + page.assert_selector('li', text: "Цена: 0р. 0коп.") + page.assert_selector('li', text: "Сервисы в автобусе:") + page.assert_selector('li', text: "Кондиционер общий") + end +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 3ab84e3d..3f273187 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,10 +1,14 @@ +# frozen_string_literal: true + ENV['RAILS_ENV'] ||= 'test' require_relative '../config/environment' require 'rails/test_help' -class ActiveSupport::TestCase - # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. - fixtures :all +module ActiveSupport + class TestCase + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all - # Add more helper methods to be used by all tests here... + # Add more helper methods to be used by all tests here... + end end From d15885e4b6b30ae719d048f8bcb39f8da7379975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A8=D0=BB=D1=8F=D0=BF=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=9D=D0=B8=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8=D1=87?= Date: Wed, 12 Feb 2025 10:01:17 +0300 Subject: [PATCH 4/7] optimization load page --- Gemfile | 1 + Gemfile.lock | 13 +++++++++++++ app/controllers/trips_controller.rb | 7 ++++++- app/views/trips/index.html.erb | 6 ++++-- config/initializers/kaminari_config.rb | 14 ++++++++++++++ db/schema.rb | 2 +- lib/benchmark.rb | 2 +- task-4_test | Bin 69632 -> 0 bytes 8 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 config/initializers/kaminari_config.rb delete mode 100644 task-4_test diff --git a/Gemfile b/Gemfile index fa72b171..e7973a16 100644 --- a/Gemfile +++ b/Gemfile @@ -9,6 +9,7 @@ gem 'activerecord-import' gem 'bootsnap', require: false gem 'flamegraph' gem 'json-stream' +gem 'kaminari' gem 'meta_request' gem 'pg' gem 'puma' diff --git a/Gemfile.lock b/Gemfile.lock index b149e3bb..a45cb160 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -123,6 +123,18 @@ GEM reline (>= 0.4.2) json (2.10.1) json-stream (1.0.0) + kaminari (1.2.2) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.2.2) + kaminari-activerecord (= 1.2.2) + kaminari-core (= 1.2.2) + kaminari-actionview (1.2.2) + actionview + kaminari-core (= 1.2.2) + kaminari-activerecord (1.2.2) + activerecord + kaminari-core (= 1.2.2) + kaminari-core (1.2.2) language_server-protocol (3.17.0.4) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) @@ -338,6 +350,7 @@ DEPENDENCIES capybara flamegraph json-stream + kaminari listen memory_profiler meta_request diff --git a/app/controllers/trips_controller.rb b/app/controllers/trips_controller.rb index 1490d713..c5ece113 100644 --- a/app/controllers/trips_controller.rb +++ b/app/controllers/trips_controller.rb @@ -4,6 +4,11 @@ class TripsController < ApplicationController def index @from = City.find_by!(name: params[:from]) @to = City.find_by!(name: params[:to]) - @trips = Trip.where(from: @from, to: @to).order(:start_time) + trips = Trip + .eager_load(bus: :services) + .where(from: @from, to: @to) + + @trips_count = trips.size + @trips = trips.order(:start_time).page(params[:page]).per(20) end end diff --git a/app/views/trips/index.html.erb b/app/views/trips/index.html.erb index a60bce41..cb3341d6 100644 --- a/app/views/trips/index.html.erb +++ b/app/views/trips/index.html.erb @@ -2,9 +2,9 @@ <%= "Автобусы #{@from.name} – #{@to.name}" %>

- <%= "В расписании #{@trips.count} рейсов" %> + <%= "В расписании #{@trips_count} рейсов" %>

- +<%= paginate @trips %> <% @trips.each do |trip| %>
    <%= render "trip", trip: trip %> @@ -14,3 +14,5 @@
<%= render "delimiter" %> <% end %> + +<%= paginate @trips %> diff --git a/config/initializers/kaminari_config.rb b/config/initializers/kaminari_config.rb new file mode 100644 index 00000000..a1950df8 --- /dev/null +++ b/config/initializers/kaminari_config.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +Kaminari.configure do |config| + # config.default_per_page = 25 + # config.max_per_page = nil + config.window = 2 + # config.outer_window = 0 + # config.left = 0 + # config.right = 0 + # config.page_method_name = :page + # config.param_name = :page + # config.max_pages = nil + # config.params_on_first_page = false +end diff --git a/db/schema.rb b/db/schema.rb index c6be77fe..12c90c21 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.2].define(version: 2025_02_09_215912) do +ActiveRecord::Schema[7.2].define(version: 2025_02_12_061158) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/lib/benchmark.rb b/lib/benchmark.rb index 944b571c..94126483 100755 --- a/lib/benchmark.rb +++ b/lib/benchmark.rb @@ -11,7 +11,7 @@ end def printer(time) - Rails.logger.debug { "Processing time from file: #{time.round(4)}" } + puts "Processing time from file: #{time.round(4)}" end printer(time) diff --git a/task-4_test b/task-4_test deleted file mode 100644 index 19998d4e62ff7ae97b0ca2755573fec9882364aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69632 zcmeI*Uu@e%90zbacAEdaj0#x=O|vpUv$iqYNmg1UAdCv8buE+@G5%R@;%?FMr#Nl_ z@i(1>!~^UFAq3(DB;G(w0yL0-ClCT5r92D}NPrN62ZRKIHw4_-acXC6$Dfx)`$lbM z|L)Fr_wgm&B^T$`Tc)axB`-GZy6MPyF3UwC+;y_daa@G{Zlk~cEkid%e~13d2evoc zjBr;zaz`e)kITkqIr1>sJ^a!zmw764Mf%nBH7O(cNAj8ESi(;H6rW8fn;isk2tWV= z5P$##&Ubd%#oJ{MtoNC?LwO{F#r&*SlSihPnAnT$zev=)A& zS{9aDekG|bEAcY+)zJ+q?Yj~&Ns{;zl6$t@e!6$b@LBBvR4X}|JzkDlcJSQErTMv; zgVVIo;mK(_=(_`-4qU%CekmIA5CeghW7>|vMtyID&}@}uZ^+TX*I0I}Rx6DtS&7A@?Xp9qDxd4MDZuj2o*g`$;G^E_jQ=N8VUv2E{J_y4+#mn}2tWV= z5P$##AOHafKmY;|7;J%&)$d;Uf{5J6<(j5-p6$Hdd2Q{nwG(UY4EdUik{3AgD|vx* z$b00}V0RR)hX4d1009U<00Izz00bZa0SFA7z(o;3;l1?={*nxmjtY|KE*DZx)e=M$ zwu^q6_WN@q89^8oz11PwhZAi1{zxKxgfveN;D+`buboWN96b4vqd&Mo00Izz00bZa z0SG_<0uX=z1RyZr0+Kj#x!CvKKb!x1>;HeH@Ba^Y2hn^8KmY;|fB*y_009U<00Izz z00aU8Y5Fk$QDk#}cf9~jiI8vD`u}eNnXm}~2tWV=5P$##AOHafKmY;|fWW{Aq|@ou zkDmN=ufXlNtyw$Tq!*d3B>sH<<3|N<^l-Cvym=p6{~saWvibkF1Jf-u6ao-{00bZa z0SG_<0uX=z1Rwx`kU%yo>=1i%f8{`RZ-!nVz?=J%55E7)TmOHCBWFUzumb@IKmY;| zfB*y_009U<00IzzzacRTNNUhkY3F_~_hT6=;9 z-tw;h_Zde%JMTM%20{P=5P$##AOHafKmY;|fB*y_5Ej@a%!ucl`}?2&=h^yyto{!R zVh;ikfB*y_009U<00Izz00bZ~I0BbmGP?T7`=2M`^pby4JT9^&|Lj8mnm(bb>)-$P z8%KT{oKB&o5P$##AOHafKmY;|fB*y_009V`TOcKP*Z)^e)e_$Nf6V{St%e*BfB*y_ z009U<00Izz00bZafx!}B^M7|C0OtRLb>h%k2tWV=5P$##AOHafKmY;|fWW^HVDo?W zAprLNKfC^aicE0iXYv*Kh@2)blc&ilSt9q4DH_HN0uX=z1Rwwb2tWV=5P$##AaH>T zWK$7ESG939pQrcwRl1r@h9cwnd^Qn`(0pUrcsQbuWnbO=Y6zP$p z!ANmT*RoV!XY2n{MCFJ|4v^bOl{`dFk{8Jv zJr}{#4+Mf)E*t|c{AJWI- z{h>l3=7oY&|DkY*&HbaHkURMo`$BZ~F9bv0^gpsqP!j$t3p7OM|Lpz$2)T(PZ_+#5 zAOHafKmY;|fB*y_009U<00I!$QlJ)z?!3EHYL$;!b<=1aGc`-qto%ZGQQvRs6{}zt zti?sOyuVn`7EEotP%e&X1#SPL$>!sGRei6f$+|j`AD__jR~B^jszQF#l)_DXbmzsT zlGRwL+RaAYYB-M7ayH3NmnLkQQef}@N7(oO{eo`*-0}n?5dsi^00bZa0SG_<0uX=z Y1R!ue3lL!^A9z(FGA!&&1YS4z8^c)GU;qFB From e8fa5b47cc6d5ca6b63ffb3c597ae39ad9f977bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A8=D0=BB=D1=8F=D0=BF=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=9D=D0=B8=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8=D1=87?= Date: Wed, 12 Feb 2025 22:42:09 +0300 Subject: [PATCH 5/7] optimization DB(Shlyapnikov) --- .rspec | 3 --- Gemfile | 1 + Gemfile.lock | 23 +++++++++++++++++ case-study.md | 56 ++++++++++++++++++++++++++++++++++++++++++ config/database.yml | 2 +- lib/tasks/utils.rake | 2 -- spec/importer_spec.rb | 13 ++++++++++ spec/spec_helper.rb | 5 ++++ task-4_test | Bin 0 -> 69632 bytes 9 files changed, 99 insertions(+), 6 deletions(-) delete mode 100644 .rspec create mode 100644 case-study.md create mode 100644 spec/importer_spec.rb create mode 100644 spec/spec_helper.rb create mode 100644 task-4_test diff --git a/.rspec b/.rspec deleted file mode 100644 index a472683a..00000000 --- a/.rspec +++ /dev/null @@ -1,3 +0,0 @@ ---color ---require spec_helper ---require rails_helper diff --git a/Gemfile b/Gemfile index e7973a16..3dc5759d 100644 --- a/Gemfile +++ b/Gemfile @@ -45,6 +45,7 @@ group :test do # Easy installation and use of web drivers to run system tests with browsers gem 'webdrivers' gem 'sqlite3' + gem 'rspec-benchmark' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem diff --git a/Gemfile.lock b/Gemfile.lock index a45cb160..40eb9ac0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -79,6 +79,9 @@ GEM ast (2.4.2) base64 (0.2.0) benchmark (0.4.0) + benchmark-malloc (0.2.0) + benchmark-perf (0.6.0) + benchmark-trend (0.4.0) bigdecimal (3.1.9) bindex (0.8.1) bootsnap (1.18.4) @@ -101,6 +104,7 @@ GEM connection_pool (2.5.0) crass (1.0.6) date (3.4.1) + diff-lcs (1.5.1) drb (2.2.1) erubi (1.13.1) ffi (1.17.1-aarch64-linux-gnu) @@ -256,6 +260,24 @@ GEM reline (0.6.0) io-console (~> 0.5) rexml (3.4.0) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-benchmark (0.6.0) + benchmark-malloc (~> 0.2) + benchmark-perf (~> 0.6) + benchmark-trend (~> 0.4) + rspec (>= 3.0) + rspec-core (3.13.3) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) rubocop (1.71.2) json (~> 2.3) language_server-protocol (>= 3.17.0) @@ -360,6 +382,7 @@ DEPENDENCIES puma rack-mini-profiler rails (~> 7) + rspec-benchmark rubocop-performance rubocop-rails ruby-prof diff --git a/case-study.md b/case-study.md new file mode 100644 index 00000000..113a37f0 --- /dev/null +++ b/case-study.md @@ -0,0 +1,56 @@ +# Case-study оптимизации + +## Актуальная проблема +1. Импорт данных из json занимает большое время(на 1млн записей импорт длился 7 минут) +2. Загрузка страници `автобусы/` занемает большое время, если данных много, на ней явно есть лишнии запросы(на начальном этпе ~55) и у сущностей ро которым идет поиск нет индексоа + +Я решил исправить эту проблему, оптимизировав эту программу. + + +## Формирование метрики +Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: +1. программа затрачивает на обработку больших файлов не больше 1 минуты +2. При открытии страници проискодит не более 4 запросов +Перед оптимизацией программа затрачивает на обработку файла в 50000 строк 335 MB + +## Гарантия корректности работы оптимизированной программы +1. На писан юнит тест гарантирующий корректную работу рейк таски `UtilsTest` +2. Написан системный тест который гарантирует сохранность структуры страници `TripsTest` + + +## Вникаем в детали системы, чтобы найти главные точки роста +Для того, чтобы найти "точки роста" для оптимизации я воспользовался: + +- pghero +- rack-mini-profiler +- benchmark +- byebug + + +Вот какие проблемы удалось найти и решить + +### Оптимизация загрузки данных +С помощью `benchmark` замерял время работы + +1. На первом этапе было внесенно изменение позволяющее последовательно читать json файл а не весь целиком(json-stream) +2. Затем что бы поиск сущностей для создагия Trip происходил быстрее добавил индексы автобусов и сервисов +3. Так как импорт данных все равно не укладывался в бюджет, воспользовался библиотекой `ActiveRecord::Import` и стал агрегировать города, автобусы и сервисы, и затем их импортить одной транзакцией(данных сущностей на 1м записей находится в памяти не больше 100 обектов) +4. Последним этапом перевел таску на стриминг данных в БД + +### Оптимизация страници +Вявил с помощю гема `bullet` что на странице есть N+1 запрос +rack-mini-profiler - показал что на странице есть много однотипных запросов(обращения к сервисам и автобусам) +1. Зделал предзагрузку связанных даннях `.eager_load(bus: :services)` +2. Добавил индекс для города на поле `name` проверил с помощью pghero что поиск города происходит по городу(визуализация explain) +3. Добавил пагинацию - это позволяло не загружать на страницу сразу все сущности + + + +## Результаты +1. В результате загрузка файла в 1m - длится 12 сек +2. При загрузке страници происходит всего 3 запроса +3. Загрузка занимает ~50 mls + +## Защита от регрессии производительности +1. Перформ тест проверющий что таска выполняется не более 60 сек +2. Тест контроллера - если будет n + 1 запрос тест упадет с ошибкой \ No newline at end of file diff --git a/config/database.yml b/config/database.yml index c46133a5..cc42398b 100644 --- a/config/database.yml +++ b/config/database.yml @@ -60,7 +60,7 @@ development: test: <<: *default database: task-4_test - adapter: sqlite3 + # adapter: sqlite3 # As with config/secrets.yml, you never want to store sensitive information, # like your database password, in your source code. If your source code is diff --git a/lib/tasks/utils.rake b/lib/tasks/utils.rake index f38b6abb..cf033d2f 100644 --- a/lib/tasks/utils.rake +++ b/lib/tasks/utils.rake @@ -8,8 +8,6 @@ namespace :utils do task :reload_json, [:file_name] => :environment do |_task, args| abort 'Send file path' unless args.file_name - # json = JSON.parse(File.read(args.file_name)) - # stream = File.open(args.file_name) Importer.call(stream) diff --git a/spec/importer_spec.rb b/spec/importer_spec.rb new file mode 100644 index 00000000..c9de6a7e --- /dev/null +++ b/spec/importer_spec.rb @@ -0,0 +1,13 @@ +require_relative 'rspec_helper' + +describe 'Performance reporter' do + let(:file_path) { 'fixtures/large.json' } + let(:time) { 1 } + let(:strem) { File.open(file_path) } + + it 'create report' do + expect { + Importer.call(stream) + }.to perform_under(time).sec.warmup(60).times.sample(10).times + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..f557a75f --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,5 @@ +require 'rspec-benchmark' + +RSpec.configure do |config| + config.include RSpec::Benchmark::Matchers +end \ No newline at end of file diff --git a/task-4_test b/task-4_test new file mode 100644 index 0000000000000000000000000000000000000000..840250c0922f6658b2fa6ccbad852817ce635e6e GIT binary patch literal 69632 zcmeI*Uu@gP9S3m!ll8}vPpL5welS8PZDAt+NLsY)6xgO^#u~)2Th|Vjt}Q`JbRtB5 zT#EKOY~3mc7|@35~Id0RgTMfsvtF~wF70aJq zo-eP=D=X#iEX*r~y|NUPslsLFM+Ifeb}N@`S6N(IQ5IJh7L&xZ8d5aUE8a*n(bQxV{w>U(wWMZ zIkE%ok_|iS_{8dEo+%MXS<%oMty&T&}`MsV93$I z*WC1+cE4hpw&B()j#Y7*UMw8;wbw$mnWE=ulJAU11lx|gMGN~wp=mdqa6jGa@#^B4 zhgav7*q8|Ou;IhX1e#%#4Gcf3ZY7iQB(3jT!DFXaQFX`lC_pB*dhAmOf6+h<;Ez8!nrT zHOIB=X4Pu6s!rWckPbKx`<4t&iJ$DB5&ysi+9?0H1@$3eL_T_yzYvVQ;4eVid2-Wf z($OB@6=PHbiBoKB9it`H4#)e?4jpS3->+YYl`r($6k+*l&kli3^idylCj1ksvPnHp z{?5@K+#mn}2tWV=5P$##AOHafKmY;|7;b^_?T>%`9+9~5`*dCJ{;K=C?rS^G>|ELD z>C0SG_<0uX=z1Rwwb2tZ)y1nv<;l@Hb{giA7n zY(kVJf4Pu)t)3>5I4Ol``WG*Z=R|Qr3RZ`xr)$S)m&eobBV>7c06%o>a{X$C=HSUc zIQoMd1Rwwb2tWV=5P$##AOHafKmYRhu>Suu`u_ircM#2o00bZa z0SG_<0uX=z1Rwwb2tXhrkQF3OlGxneUoSvY1o9zU|Nmhm6E-0L0SG_<0uX=z1Rwwb z2tWV=5EvqX?Djj){pGmG9X{WxUu@Bf%vRI?e(_H~6S;|Vt@h>C583*EfqcZ~{~rxe zx6o7wKmY;|fB*y_009U<00Izz00g1}dGWB+pZl{*0_5lg0)n|edHa+91nd8Aa^z;T z5Vj!z0SG_<0uX=z1Rwwb2tWV=5V-RN?!9vL=Jj!rTk5{h{X_Tn-Rs>OJ3sH<@V8&z zc}Afd*LI$zf!_w#|9h7s@80>nLJJ`P0SG_<0uX=z1Rwwb2tWV=5Qqs(iA&OL=lN1Y5P$##AOHafKmY;|fB*y_0D;>GjEcea|J7^tbg=#(^Z(nB zAsGS?fB*y_009U<00Izz00bZ~bOLPt?=J+v{D0`q9U2S)2tWV=5P$##AOHafKmY;| z_^JYI{?9%Hz~297*Zb2Qa%-r6pQ&Jix{S6 zXmfh8XwoB(L?dR&(DR8%#MCs+%uBI|p=o(>H)80frki;o7MatHJRe4MLz~fcbC#|D z&yq=wOcITpCg(|w{DfR3FO%2FAIaayr+3jQMpq#K0SG_<0uX=z1Rwwb2teR&6}U%K z`S_$iI}oC?{?T}dP5U#0A)}NY3>Ay1AQYYXC*vVD^&g3a{JDQ(AVeqsQZy9I{>6b1 zo&F1lL^U0}))3DB+57(jd5|M-&^z29009U<00Izz00bZa0SG_<0uVS*pe`hiJYFuh zE0>&xZM84idP!TW%xVT*F;FRLbJJR7)|gqZ*z0SyF{7=`=ys)~udPkf_xla=xMt8x z@+Da|}(YA5vBx#G<9zA42A`NWZLl*>+YtLC Date: Sat, 15 Feb 2025 00:18:31 +0300 Subject: [PATCH 6/7] fix typo --- lib/profiler.rb | 2 +- lib/reports_builder.rb | 2 +- lib/tasks/reports.rake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/profiler.rb b/lib/profiler.rb index b8c90712..3ba10899 100644 --- a/lib/profiler.rb +++ b/lib/profiler.rb @@ -12,7 +12,7 @@ def make_report(reporter_type) send(reporter_type) end - def memeory_prof + def memory_prof report = MemoryProfiler.report do system 'bin/rake utils:reload_json[fixtures/medium.json]' end diff --git a/lib/reports_builder.rb b/lib/reports_builder.rb index ec6913ed..89d39ad5 100755 --- a/lib/reports_builder.rb +++ b/lib/reports_builder.rb @@ -8,7 +8,7 @@ prof_type = ARGV[0] available_types = %w[ - memeory_prof + memory_prof stack_prof ruby_prof ] diff --git a/lib/tasks/reports.rake b/lib/tasks/reports.rake index c2cec2df..57969c22 100644 --- a/lib/tasks/reports.rake +++ b/lib/tasks/reports.rake @@ -7,7 +7,7 @@ namespace :reports do reporter_type = args.reporter_type available_types = %w[ - memeory_prof + memory_prof stack_prof ruby_prof ] From 9d288889499ab5bae95c9c35b07b9a3d54f8eefe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A8=D0=BB=D1=8F=D0=BF=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=9D=D0=B8=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8=D1=87?= Date: Mon, 17 Feb 2025 20:48:45 +0300 Subject: [PATCH 7/7] fix rendering partial --- app/views/trips/_service.html.erb | 5 ++++- app/views/trips/_services.html.erb | 6 ------ app/views/trips/_trip.html.erb | 8 ++++++++ app/views/trips/index.html.erb | 11 +---------- 4 files changed, 13 insertions(+), 17 deletions(-) delete mode 100644 app/views/trips/_services.html.erb diff --git a/app/views/trips/_service.html.erb b/app/views/trips/_service.html.erb index 178ea8c0..58a0e32f 100644 --- a/app/views/trips/_service.html.erb +++ b/app/views/trips/_service.html.erb @@ -1 +1,4 @@ -
  • <%= "#{service.name}" %>
  • + +
      +
    • <%= "#{service.name}" %>
    • +
    diff --git a/app/views/trips/_services.html.erb b/app/views/trips/_services.html.erb deleted file mode 100644 index 2de639fc..00000000 --- a/app/views/trips/_services.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -
  • Сервисы в автобусе:
  • -
      - <% services.each do |service| %> - <%= render "service", service: service %> - <% end %> -
    diff --git a/app/views/trips/_trip.html.erb b/app/views/trips/_trip.html.erb index fa1de9aa..58aad25e 100644 --- a/app/views/trips/_trip.html.erb +++ b/app/views/trips/_trip.html.erb @@ -3,3 +3,11 @@
  • <%= "В пути: #{trip.duration_minutes / 60}ч. #{trip.duration_minutes % 60}мин." %>
  • <%= "Цена: #{trip.price_cents / 100}р. #{trip.price_cents % 100}коп." %>
  • <%= "Автобус: #{trip.bus.model} №#{trip.bus.number}" %>
  • +<% if trip.bus.services.present? %> +
  • Сервисы в автобусе:
  • +
      + <%= render partial: "service", collection: trip.bus.services %> +
    +<% end %> + +<%= render "delimiter" %> diff --git a/app/views/trips/index.html.erb b/app/views/trips/index.html.erb index cb3341d6..256ff48d 100644 --- a/app/views/trips/index.html.erb +++ b/app/views/trips/index.html.erb @@ -5,14 +5,5 @@ <%= "В расписании #{@trips_count} рейсов" %> <%= paginate @trips %> -<% @trips.each do |trip| %> -
      - <%= render "trip", trip: trip %> - <% if trip.bus.services.present? %> - <%= render "services", services: trip.bus.services %> - <% end %> -
    - <%= render "delimiter" %> -<% end %> - +<%= render partial: "trip", collection: @trips%> <%= paginate @trips %>