diff --git a/.gitignore b/.gitignore
index 59c74047..41afc9ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
/tmp
/log
/public
+.ruby-gemset
+.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/Gemfile b/Gemfile
index e20b1260..94c2b223 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,16 +7,26 @@ gem 'rails', '~> 5.2.3'
gem 'pg', '>= 0.18', '< 2.0'
gem 'puma', '~> 3.11'
gem 'bootsnap', '>= 1.1.0', require: false
+gem 'activerecord-import'
+gem 'fast_jsonparser'
+gem 'strong_migrations'
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-benchmark'
+ gem 'rspec-rails', '~> 5.0.0'
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 'rack-mini-profiler'
+ gem 'flamegraph'
+ gem 'memory_profiler'
+ gem 'stackprof'
+ gem 'bullet'
end
group :test do
diff --git a/Gemfile.lock b/Gemfile.lock
index fccf6f5f..e7f913bf 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,145 +1,213 @@
GEM
remote: https://rubygems.org/
specs:
- actioncable (5.2.3)
- actionpack (= 5.2.3)
+ actioncable (5.2.8.1)
+ actionpack (= 5.2.8.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)
+ actionmailer (5.2.8.1)
+ actionpack (= 5.2.8.1)
+ actionview (= 5.2.8.1)
+ activejob (= 5.2.8.1)
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)
+ actionpack (5.2.8.1)
+ actionview (= 5.2.8.1)
+ activesupport (= 5.2.8.1)
+ rack (~> 2.0, >= 2.0.8)
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)
+ actionview (5.2.8.1)
+ activesupport (= 5.2.8.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)
+ activejob (5.2.8.1)
+ activesupport (= 5.2.8.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)
+ activemodel (5.2.8.1)
+ activesupport (= 5.2.8.1)
+ activerecord (5.2.8.1)
+ activemodel (= 5.2.8.1)
+ activesupport (= 5.2.8.1)
arel (>= 9.0)
- activestorage (5.2.3)
- actionpack (= 5.2.3)
- activerecord (= 5.2.3)
- marcel (~> 0.3.1)
- activesupport (5.2.3)
+ activerecord-import (1.4.1)
+ activerecord (>= 4.2)
+ activestorage (5.2.8.1)
+ actionpack (= 5.2.8.1)
+ activerecord (= 5.2.8.1)
+ marcel (~> 1.0.0)
+ activesupport (5.2.8.1)
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)
+ benchmark-malloc (0.2.0)
+ benchmark-perf (0.6.0)
+ benchmark-trend (0.4.0)
+ bindex (0.8.1)
+ bootsnap (1.16.0)
+ msgpack (~> 1.2)
+ builder (3.2.4)
+ bullet (7.0.7)
+ activesupport (>= 3.0.0)
+ uniform_notifier (~> 1.11)
+ byebug (11.1.3)
+ concurrent-ruby (1.2.2)
+ crass (1.0.6)
+ date (3.3.3)
+ diff-lcs (1.5.0)
+ erubi (1.12.0)
+ fast_jsonparser (0.6.0)
+ ffi (1.15.5)
+ flamegraph (0.9.5)
+ globalid (1.1.0)
+ activesupport (>= 5.0)
+ i18n (1.12.0)
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)
+ loofah (2.19.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
- mail (2.7.1)
+ 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)
+ net-imap
+ net-pop
+ net-smtp
+ marcel (1.0.2)
+ memory_profiler (1.0.1)
+ method_source (1.0.0)
+ mini_mime (1.1.2)
+ mini_portile2 (2.8.1)
+ minitest (5.18.0)
+ msgpack (1.7.0)
+ net-imap (0.3.4)
+ date
+ net-protocol
+ net-pop (0.1.2)
+ net-protocol
+ net-protocol (0.2.1)
+ timeout
+ net-smtp (0.3.3)
+ net-protocol
+ nio4r (2.5.8)
+ nokogiri (1.13.10)
+ mini_portile2 (~> 2.8.0)
+ racc (~> 1.4)
+ pg (1.4.6)
+ puma (3.12.6)
+ racc (1.6.2)
+ rack (2.2.6.4)
+ rack-mini-profiler (3.0.0)
+ rack (>= 1.2.0)
+ rack-test (2.1.0)
+ rack (>= 1.3)
+ rails (5.2.8.1)
+ actioncable (= 5.2.8.1)
+ actionmailer (= 5.2.8.1)
+ actionpack (= 5.2.8.1)
+ actionview (= 5.2.8.1)
+ activejob (= 5.2.8.1)
+ activemodel (= 5.2.8.1)
+ activerecord (= 5.2.8.1)
+ activestorage (= 5.2.8.1)
+ activesupport (= 5.2.8.1)
bundler (>= 1.3.0)
- railties (= 5.2.3)
+ railties (= 5.2.8.1)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
- rails-html-sanitizer (1.0.4)
- loofah (~> 2.2, >= 2.2.2)
- railties (5.2.3)
- actionpack (= 5.2.3)
- activesupport (= 5.2.3)
+ rails-html-sanitizer (1.5.0)
+ loofah (~> 2.19, >= 2.19.1)
+ railties (5.2.8.1)
+ actionpack (= 5.2.8.1)
+ activesupport (= 5.2.8.1)
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)
+ rake (13.0.6)
+ rb-fsevent (0.11.2)
+ rb-inotify (0.10.1)
ffi (~> 1.0)
+ rspec (3.12.0)
+ rspec-core (~> 3.12.0)
+ rspec-expectations (~> 3.12.0)
+ rspec-mocks (~> 3.12.0)
+ rspec-benchmark (0.6.0)
+ benchmark-malloc (~> 0.2)
+ benchmark-perf (~> 0.6)
+ benchmark-trend (~> 0.4)
+ rspec (>= 3.0)
+ rspec-core (3.12.1)
+ rspec-support (~> 3.12.0)
+ rspec-expectations (3.12.2)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.12.0)
+ rspec-mocks (3.12.5)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.12.0)
+ rspec-rails (5.0.3)
+ actionpack (>= 5.2)
+ activesupport (>= 5.2)
+ railties (>= 5.2)
+ rspec-core (~> 3.10)
+ rspec-expectations (~> 3.10)
+ rspec-mocks (~> 3.10)
+ rspec-support (~> 3.10)
+ rspec-support (3.12.0)
ruby_dep (1.5.0)
- sprockets (3.7.2)
+ sprockets (4.2.0)
concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (3.2.1)
- actionpack (>= 4.0)
- activesupport (>= 4.0)
+ rack (>= 2.2.4, < 4)
+ sprockets-rails (3.4.2)
+ actionpack (>= 5.2)
+ activesupport (>= 5.2)
sprockets (>= 3.0.0)
- thor (0.20.3)
+ stackprof (0.2.24)
+ strong_migrations (1.4.4)
+ activerecord (>= 5.2)
+ thor (1.2.1)
thread_safe (0.3.6)
- tzinfo (1.2.5)
+ timeout (0.3.2)
+ tzinfo (1.2.11)
thread_safe (~> 0.1)
+ uniform_notifier (1.16.0)
web-console (3.7.0)
actionview (>= 5.0)
activemodel (>= 5.0)
bindex (>= 0.4.0)
railties (>= 5.0)
- websocket-driver (0.7.0)
+ websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
- websocket-extensions (0.1.3)
+ websocket-extensions (0.1.5)
PLATFORMS
ruby
DEPENDENCIES
+ activerecord-import
bootsnap (>= 1.1.0)
+ bullet
byebug
+ fast_jsonparser
+ flamegraph
listen (>= 3.0.5, < 3.2)
+ memory_profiler
pg (>= 0.18, < 2.0)
puma (~> 3.11)
+ rack-mini-profiler
rails (~> 5.2.3)
+ rspec-benchmark
+ rspec-rails (~> 5.0.0)
+ stackprof
+ strong_migrations
tzinfo-data
web-console (>= 3.3.0)
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 09705d12..2127dc81 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,2 +1,9 @@
class ApplicationController < ActionController::Base
+ before_action :miniprofiler
+
+ private
+
+ def miniprofiler
+ Rack::MiniProfiler.authorize_request # if user.admin?
+ end
end
diff --git a/app/controllers/trips_controller.rb b/app/controllers/trips_controller.rb
index acb38be2..ad823f6d 100644
--- a/app/controllers/trips_controller.rb
+++ b/app/controllers/trips_controller.rb
@@ -2,6 +2,6 @@ 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.includes(:bus, bus: :services).where(from: @from, to: @to).order(:start_time)
end
end
diff --git a/app/models/bus.rb b/app/models/bus.rb
index 1dcc54cb..d97de31f 100644
--- a/app/models/bus.rb
+++ b/app/models/bus.rb
@@ -13,7 +13,8 @@ class Bus < ApplicationRecord
].freeze
has_many :trips
- has_and_belongs_to_many :services, join_table: :buses_services
+ has_many :buses_services
+ has_many :services, through: :buses_services
validates :number, presence: true, uniqueness: true
validates :model, inclusion: { in: MODELS }
diff --git a/app/models/buses_service.rb b/app/models/buses_service.rb
new file mode 100644
index 00000000..6219d44e
--- /dev/null
+++ b/app/models/buses_service.rb
@@ -0,0 +1,4 @@
+class BusesService < ApplicationRecord
+ belongs_to :bus
+ belongs_to :service
+end
diff --git a/app/models/service.rb b/app/models/service.rb
index 9cbb2a32..1781543c 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -12,7 +12,8 @@ class Service < ApplicationRecord
'Можно не печатать билет',
].freeze
- has_and_belongs_to_many :buses, join_table: :buses_services
+ has_many :buses_services
+ has_many :buses, through: :buses_services
validates :name, presence: true
validates :name, inclusion: { in: SERVICES }
diff --git a/app/views/trips/_delimiter.html.erb b/app/views/trips/_delimiter.html.erb
deleted file mode 100644
index 3f845ad0..00000000
--- a/app/views/trips/_delimiter.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-====================================================
diff --git a/app/views/trips/_service.html.erb b/app/views/trips/_service.html.erb
deleted file mode 100644
index 178ea8c0..00000000
--- a/app/views/trips/_service.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-
<%= "#{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
deleted file mode 100644
index fa1de9aa..00000000
--- a/app/views/trips/_trip.html.erb
+++ /dev/null
@@ -1,5 +0,0 @@
-<%= "Отправление: #{trip.start_time}" %>
-<%= "Прибытие: #{(Time.parse(trip.start_time) + trip.duration_minutes.minutes).strftime('%H:%M')}" %>
-<%= "В пути: #{trip.duration_minutes / 60}ч. #{trip.duration_minutes % 60}мин." %>
-<%= "Цена: #{trip.price_cents / 100}р. #{trip.price_cents % 100}коп." %>
-<%= "Автобус: #{trip.bus.model} №#{trip.bus.number}" %>
diff --git a/app/views/trips/index.html.erb b/app/views/trips/index.html.erb
index a60bce41..378d42cc 100644
--- a/app/views/trips/index.html.erb
+++ b/app/views/trips/index.html.erb
@@ -1,16 +1,27 @@
-
- <%= "Автобусы #{@from.name} – #{@to.name}" %>
-
-
- <%= "В расписании #{@trips.count} рейсов" %>
-
+<% cache([@from.name, @to.name]) do%>
+
+ <%= "Автобусы #{@from.name} – #{@to.name}" %>
+
+
+ <%= "В расписании #{@trips.count} рейсов" %>
+
-<% @trips.each do |trip| %>
-
- <%= render "trip", trip: trip %>
- <% if trip.bus.services.present? %>
- <%= render "services", services: trip.bus.services %>
- <% end %>
-
- <%= render "delimiter" %>
+ <% @trips.each do |trip| %>
+
+ - <%= "Отправление: #{trip.start_time}" %>
+ - <%= "Прибытие: #{(Time.parse(trip.start_time) + trip.duration_minutes.minutes).strftime('%H:%M')}" %>
+ - <%= "В пути: #{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.exists? %>
+ - Сервисы в автобусе:
+ <% trip.bus.services.each do |service| %>
+
+ - <%= "#{service.name}" %>
+
+ <% end %>
+ <% end %>
+
+ ====================================================
+ <% end %>
<% end %>
diff --git a/case-study.md b/case-study.md
new file mode 100644
index 00000000..d2748fe1
--- /dev/null
+++ b/case-study.md
@@ -0,0 +1,30 @@
+### Необходимо выполнить следующие задачи:
+- Оптимизировать импорт данных за время < 1 мин.
+- Оптимизировать отображение расписания.
+
+### Метрика
+- Тест на импорт файла large.json должен выполнится менее чем за 60 секунд.
+
+### Инструменты для оптимизации
+- гемы activerecord-import, rack-mini-profiler
+- Explain sql, pghero
+
+### Замеры производительности до оптимизации импорта
+- файл small.json импортируется за 17 сек.
+- файл medium.json импортируется за 137 сек.
+
+### Замеры производительности после оптимизации импорта
+- файд small.json - 2 сек.
+- файд medium.json - 5 сек.
+- файд large.json - 24 сек.
+
+Использовал так же стороний гем для парсинга json - 'fast_jsonparser', нашёл его при помощи гугла :)
+Считаю данный этап выполненным.
+
+### Задача 2
+
+## Проблема 1
+- Большое количество рендера паршелов, общее время отрисовки - 13688 мс. Решение - перенести всё в один template
+
+Так же добавил составной индекс, правда по времени загрузка особо быстрее не стала.
+После кэширования всей страницы, время отрисовки страницы стало 23 мс.
diff --git a/config/database.yml b/config/database.yml
index e116cfa6..27db5d25 100644
--- a/config/database.yml
+++ b/config/database.yml
@@ -20,6 +20,8 @@ default: &default
# For details on connection pooling, see Rails configuration guide
# http://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+ user: postgres
+ password: postgres
development:
<<: *default
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 1311e3e4..903a8661 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -1,4 +1,13 @@
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
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 0a38fd3c..bc5971ab 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -1,4 +1,10 @@
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/rack_mini_profiler.rb b/config/initializers/rack_mini_profiler.rb
new file mode 100644
index 00000000..7784d1f6
--- /dev/null
+++ b/config/initializers/rack_mini_profiler.rb
@@ -0,0 +1 @@
+Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
diff --git a/config/initializers/strong_migrations.rb b/config/initializers/strong_migrations.rb
new file mode 100644
index 00000000..531e771d
--- /dev/null
+++ b/config/initializers/strong_migrations.rb
@@ -0,0 +1,26 @@
+# Mark existing migrations as safe
+StrongMigrations.start_after = 20230403132428
+
+# 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/20230403132254_add_index_to_trip.rb b/db/migrate/20230403132254_add_index_to_trip.rb
new file mode 100644
index 00000000..fcdebadc
--- /dev/null
+++ b/db/migrate/20230403132254_add_index_to_trip.rb
@@ -0,0 +1,7 @@
+class AddIndexToTrip < ActiveRecord::Migration[5.2]
+ disable_ddl_transaction!
+
+ def change
+ add_index :trips, [:from_id, :to_id], algorithm: :concurrently
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f6921e45..e8d7515b 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2019_03_30_193044) do
+ActiveRecord::Schema.define(version: 2023_04_03_132254) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -40,6 +40,7 @@
t.integer "duration_minutes"
t.integer "price_cents"
t.integer "bus_id"
+ t.index ["from_id", "to_id"], name: "index_trips_on_from_id_and_to_id"
end
end
diff --git a/lib/tasks/utils.rake b/lib/tasks/utils.rake
index 540fe871..ad716069 100644
--- a/lib/tasks/utils.rake
+++ b/lib/tasks/utils.rake
@@ -1,34 +1,52 @@
# Наивная загрузка данных из json-файла в БД
# rake reload_json[fixtures/small.json]
task :reload_json, [:file_name] => :environment do |_task, args|
- json = JSON.parse(File.read(args.file_name))
+ json = FastJsonparser.parse(File.read(args.file_name), symbolize_keys: false)
ActiveRecord::Base.transaction do
- City.delete_all
- Bus.delete_all
- Service.delete_all
- Trip.delete_all
- ActiveRecord::Base.connection.execute('delete from buses_services;')
+ [
+ City.table_name, Bus.table_name, Service.table_name,
+ Trip.table_name, BusesService.table_name
+ ].each do |table|
+ ActiveRecord::Base.connection.truncate(table)
+ end
+
+ cities = {}
+ trips = []
+ services = {}
+ busses = {}
+ busses_services = {}
+ trips = []
json.each do |trip|
- from = City.find_or_create_by(name: trip['from'])
- to = City.find_or_create_by(name: trip['to'])
- services = []
+ cities[trip['from']] ||= City.create!(name: trip['from'])
+ cities[trip['to']] ||= City.create!(name: trip['to'])
+ from = cities[trip['from']]
+ to = cities[trip['to']]
+ busses[trip['bus']['number']] ||= Bus.create!(
+ number: trip['bus']['number'],
+ model: trip['bus']['model']
+ )
+
trip['bus']['services'].each do |service|
- s = Service.find_or_create_by(name: service)
- services << s
+ services[service] ||= Service.create!(name: service)
+ bs_key = "#{trip['bus']['number']}_#{trip['bus']['model']}"
+ busses_services[bs_key] ||= BusesService.create!(
+ bus: busses[trip['bus']['number']],
+ service: services[service]
+ )
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,
+ trips << {
+ from_id: from.id,
+ to_id: to.id,
+ bus_id: busses[trip['bus']['number']].id,
start_time: trip['start_time'],
duration_minutes: trip['duration_minutes'],
price_cents: trip['price_cents'],
- )
+ }
end
+
+ Trip.import!(trips)
end
end
diff --git a/spec/lib/tasks/utils_spec.rb b/spec/lib/tasks/utils_spec.rb
new file mode 100644
index 00000000..49688bf1
--- /dev/null
+++ b/spec/lib/tasks/utils_spec.rb
@@ -0,0 +1,9 @@
+require 'rails_helper'
+
+Rails.application.load_tasks
+
+describe 'utils.rake' do
+ it 'import large.json' do
+ expect { Rake::Task['reload_json'].invoke('fixtures/large.json') }.to perform_under(60).sec
+ end
+end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
new file mode 100644
index 00000000..d3c2ba31
--- /dev/null
+++ b/spec/rails_helper.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+ENV['RAILS_ENV'] ||= 'test'
+require_relative '../config/environment'
+
+abort("The Rails environment is running in production mode!") if Rails.env.production?
+require 'rspec/rails'
+
+begin
+ ActiveRecord::Migration.maintain_test_schema!
+rescue ActiveRecord::PendingMigrationError => e
+ puts e.to_s.strip
+ exit 1
+end
+RSpec.configure do |config|
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
+ config.use_transactional_fixtures = true
+ config.infer_spec_type_from_file_location!
+ config.filter_rails_from_backtrace!
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 00000000..9f9bed90
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,15 @@
+require 'rspec-benchmark'
+
+RSpec.configure do |config|
+ config.include RSpec::Benchmark::Matchers
+
+ config.expect_with :rspec do |expectations|
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+ end
+
+ config.mock_with :rspec do |mocks|
+ mocks.verify_partial_doubles = true
+ end
+
+ config.shared_context_metadata_behavior = :apply_to_host_groups
+end