diff --git a/.gitignore b/.gitignore
index 18b43c9..39bfdbb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@
# Ignore master key for decrypting credentials and more.
/config/master.key
+/config/skylight.yml
diff --git a/Gemfile b/Gemfile
index 33017fd..1dd1990 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,6 +7,11 @@ gem 'rails', '~> 5.2.3'
gem 'pg', '>= 0.18', '< 2.0'
gem 'puma', '~> 3.11'
gem 'bootsnap', '>= 1.1.0', require: false
+gem 'pghero'
+gem 'pg_query', '>= 0.9.0'
+gem 'skylight'
+gem 'activerecord-import'
+gem 'strong_migrations'
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
diff --git a/Gemfile.lock b/Gemfile.lock
index eb22e16..aaeb2b0 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -33,6 +33,8 @@ GEM
activemodel (= 5.2.3)
activesupport (= 5.2.3)
arel (>= 9.0)
+ activerecord-import (1.0.1)
+ activerecord (>= 3.2)
activestorage (5.2.3)
actionpack (= 5.2.3)
activerecord (= 5.2.3)
@@ -43,8 +45,8 @@ GEM
minitest (~> 5.1)
tzinfo (~> 1.1)
arel (9.0.0)
- bindex (0.6.0)
- bootsnap (1.4.2)
+ bindex (0.7.0)
+ bootsnap (1.4.3)
msgpack (~> 1.0)
builder (3.2.3)
byebug (11.0.1)
@@ -77,8 +79,11 @@ GEM
nokogiri (1.10.2)
mini_portile2 (~> 2.4.0)
pg (1.1.4)
+ pg_query (1.1.0)
+ pghero (2.2.0)
+ activerecord
puma (3.12.1)
- rack (2.0.6)
+ rack (2.0.7)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (5.2.3)
@@ -110,6 +115,10 @@ GEM
rb-inotify (0.10.0)
ffi (~> 1.0)
ruby_dep (1.5.0)
+ skylight (3.1.5)
+ skylight-core (= 3.1.5)
+ skylight-core (3.1.5)
+ activesupport (>= 4.2.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
@@ -117,6 +126,8 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
+ strong_migrations (0.3.1)
+ activerecord (>= 3.2.0)
thor (0.20.3)
thread_safe (0.3.6)
tzinfo (1.2.5)
@@ -134,12 +145,17 @@ PLATFORMS
ruby
DEPENDENCIES
+ activerecord-import
bootsnap (>= 1.1.0)
byebug
listen (>= 3.0.5, < 3.2)
pg (>= 0.18, < 2.0)
+ pg_query (>= 0.9.0)
+ pghero
puma (~> 3.11)
rails (~> 5.2.3)
+ skylight
+ strong_migrations
tzinfo-data
web-console (>= 3.3.0)
diff --git a/app/controllers/trips_controller.rb b/app/controllers/trips_controller.rb
index acb38be..ad823f6 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 1dcc54c..956cfa6 100644
--- a/app/models/bus.rb
+++ b/app/models/bus.rb
@@ -13,7 +13,7 @@ class Bus < ApplicationRecord
].freeze
has_many :trips
- has_and_belongs_to_many :services, join_table: :buses_services
+ has_and_belongs_to_many :services, join_table: :buses_services, autosave: true
validates :number, presence: true, uniqueness: true
validates :model, inclusion: { in: MODELS }
diff --git a/app/models/service.rb b/app/models/service.rb
index 9cbb2a3..bb44974 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -12,7 +12,7 @@ class Service < ApplicationRecord
'Можно не печатать билет',
].freeze
- has_and_belongs_to_many :buses, join_table: :buses_services
+ has_and_belongs_to_many :buses, join_table: :buses_services, autosave: true
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 3f845ad..0000000
--- 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 178ea8c..0000000
--- 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 2de639f..0000000
--- 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 fa1de9a..0000000
--- 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 a60bce4..7b18873 100644
--- a/app/views/trips/index.html.erb
+++ b/app/views/trips/index.html.erb
@@ -4,13 +4,23 @@
<%= "В расписании #{@trips.count} рейсов" %>
-
-<% @trips.each do |trip| %>
+<% @trips.each do |trip|%>
- <%= render "trip", trip: trip %>
- <% if trip.bus.services.present? %>
- <%= render "services", services: trip.bus.services %>
+ - <%= "Отправление: #{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}" %>
+
+ <% services = trip.bus.services %>
+ <% if services.any? %>
+ - Сервисы в автобусе:
+
+ <% services.each do |service|%>
+ - <%= "#{service.name}" %>
+ <%end%>
+
<% end %>
- <%= render "delimiter" %>
+ ====================================================
<% end %>
diff --git a/config/application.rb b/config/application.rb
index 9c33109..e08aeef 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -11,9 +11,6 @@ class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2
- # Settings in config/environments/* take precedence over those specified here.
- # Application configuration can go into files in config/initializers
- # -- all .rb files in that directory are automatically loaded after loading
- # the framework and any gems in your application.
+ config.skylight.environments += ["development"]
end
end
diff --git a/config/routes.rb b/config/routes.rb
index a2da6a7..581873d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,5 @@
Rails.application.routes.draw do
+ # mount PgHero::Engine, at: 'pghero'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
get "/" => "statistics#index"
get "автобусы/:from/:to" => "trips#index"
diff --git a/db/migrate/20190412192616_add_trip_indices.rb b/db/migrate/20190412192616_add_trip_indices.rb
new file mode 100644
index 0000000..1d00e6a
--- /dev/null
+++ b/db/migrate/20190412192616_add_trip_indices.rb
@@ -0,0 +1,7 @@
+class AddTripIndices < ActiveRecord::Migration[5.2]
+ disable_ddl_transaction!
+
+ def change
+ add_index :trips, [:from_id, :to_id], algorithm: :concurrently
+ end
+end
diff --git a/db/migrate/20190412195139_add_bus_index.rb b/db/migrate/20190412195139_add_bus_index.rb
new file mode 100644
index 0000000..ef6021e
--- /dev/null
+++ b/db/migrate/20190412195139_add_bus_index.rb
@@ -0,0 +1,7 @@
+class AddBusIndex < ActiveRecord::Migration[5.2]
+ disable_ddl_transaction!
+
+ def change
+ add_index :buses, :number, algorithm: :concurrently
+ end
+end
diff --git a/db/migrate/20190412203601_add_bus_service_indices.rb b/db/migrate/20190412203601_add_bus_service_indices.rb
new file mode 100644
index 0000000..34dcdd8
--- /dev/null
+++ b/db/migrate/20190412203601_add_bus_service_indices.rb
@@ -0,0 +1,7 @@
+class AddBusServiceIndices < ActiveRecord::Migration[5.2]
+ disable_ddl_transaction!
+
+ def change
+ add_index :buses_services, [:bus_id, :service_id], algorithm: :concurrently
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f6921e4..4c45580 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: 2019_04_12_211708) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -18,15 +18,18 @@
create_table "buses", force: :cascade do |t|
t.string "number"
t.string "model"
+ t.index ["number"], name: "index_buses_on_number"
end
create_table "buses_services", force: :cascade do |t|
t.integer "bus_id"
t.integer "service_id"
+ t.index ["bus_id", "service_id"], name: "index_buses_services_on_bus_id_and_service_id"
end
create_table "cities", force: :cascade do |t|
t.string "name"
+ t.index ["name"], name: "index_cities_on_name"
end
create_table "services", force: :cascade do |t|
@@ -40,6 +43,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 540fe87..f76c2df 100644
--- a/lib/tasks/utils.rake
+++ b/lib/tasks/utils.rake
@@ -1,34 +1,77 @@
# Наивная загрузка данных из json-файла в БД
# rake reload_json[fixtures/small.json]
+require 'benchmark'
+
+def create_services
+ new_services = []
+ all_services = Service.const_get('SERVICES').each_with_object({}) do |name, result|
+ new_services << result[name] = Service.new(name: name)
+ end
+ Service.import(new_services)
+ all_services
+end
+
+def create_cities(data)
+ uniq_cities = data.each_with_object([]) do |trip, result|
+ result << trip['from']
+ result << trip['to']
+ end.uniq
+ new_cities = []
+ all_cities = uniq_cities.each_with_object({}) do |name, result|
+ new_cities << result[name] = City.new(name: name)
+ end
+ City.import(new_cities)
+ all_cities
+end
+
+def create_buses(data, all_services)
+ new_buses = []
+ all_buses = data.each_with_object({}) do |trip, result|
+ next if result[trip['bus']['number']]
+
+ services = trip['bus']['services'].map {|service| all_services[service] }
+ new_buses << result[trip['bus']['number']] = Bus.new(
+ number: trip['bus']['number'],
+ model: trip['bus']['model'],
+ services: services
+ )
+ end
+ #Bus.import(new_buses)
+ new_buses.each(&:save!)
+ all_buses
+end
+
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;')
-
- 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
+ time = Benchmark.realtime do
+ 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;')
+
+ all_services = create_services
+ all_cities = create_cities(json)
+ all_buses = create_buses(json, all_services)
+
+ new_records = json.map do |trip|
+ from = all_cities[trip['from']]
+ to = all_cities[trip['to']]
+ bus = all_buses[trip['bus']['number']]
+
+ Trip.new(
+ from: from,
+ to: to,
+ bus: bus,
+ start_time: trip['start_time'],
+ duration_minutes: trip['duration_minutes'],
+ price_cents: trip['price_cents'],
+ )
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.import(new_records)
end
end
+ puts "Finish in #{time.round(2)}"
end