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..5c711958 100644 --- a/Gemfile +++ b/Gemfile @@ -8,18 +8,33 @@ gem 'pg', '>= 0.18', '< 2.0' gem 'puma', '~> 3.11' gem 'bootsnap', '>= 1.1.0', require: false +gem 'pghero' +# pg_query is used by pghero to provide suggestions to add indexes +gem 'pg_query', '>= 0.9.0' +gem 'activerecord-import' +gem 'rack-mini-profiler' +gem "strong_migrations" +# gem 'meta_request' +# gem 'rails_performance' + + 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', '~> 4.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 'database_cleaner-active_record', '~> 2.0.1' + gem 'bullet' + gem 'stackprof' end group :test do + 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 fccf6f5f..4f0f1440 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.7.0) + activerecord (>= 4.2) activestorage (5.2.3) actionpack (= 5.2.3) activerecord (= 5.2.3) @@ -43,17 +45,29 @@ GEM minitest (~> 5.1) tzinfo (~> 1.1) arel (9.0.0) + benchmark-malloc (0.2.0) + benchmark-perf (0.6.0) + benchmark-trend (0.4.0) bindex (0.6.0) bootsnap (1.4.2) msgpack (~> 1.0) builder (3.2.3) + bullet (7.1.6) + activesupport (>= 3.0.0) + uniform_notifier (~> 1.11) byebug (11.0.1) concurrent-ruby (1.1.5) crass (1.0.4) + database_cleaner-active_record (2.0.1) + activerecord (>= 5.a) + database_cleaner-core (~> 2.0.0) + database_cleaner-core (2.0.1) + diff-lcs (1.5.1) erubi (1.8.0) ffi (1.10.0) globalid (0.4.2) activesupport (>= 4.2.0) + google-protobuf (3.23.4) i18n (1.6.0) concurrent-ruby (~> 1.0) listen (3.1.5) @@ -68,7 +82,9 @@ GEM marcel (0.3.3) mimemagic (~> 0.3.2) method_source (0.9.2) - mimemagic (0.3.3) + mimemagic (0.3.10) + nokogiri (~> 1) + rake mini_mime (1.0.1) mini_portile2 (2.4.0) minitest (5.11.3) @@ -77,8 +93,14 @@ GEM nokogiri (1.10.2) mini_portile2 (~> 2.4.0) pg (1.1.4) + pg_query (5.1.0) + google-protobuf (>= 3.22.3) + pghero (2.8.3) + activerecord (>= 5) puma (3.12.1) rack (2.0.6) + rack-mini-profiler (3.1.1) + rack (>= 1.2.0) rack-test (1.1.0) rack (>= 1.0, < 3) rails (5.2.3) @@ -109,6 +131,32 @@ GEM rb-fsevent (0.10.3) rb-inotify (0.10.0) ffi (~> 1.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.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-rails (4.0.2) + actionpack (>= 4.2) + activesupport (>= 4.2) + railties (>= 4.2) + rspec-core (~> 3.10) + rspec-expectations (~> 3.10) + rspec-mocks (~> 3.10) + rspec-support (~> 3.10) + rspec-support (3.13.1) ruby_dep (1.5.0) sprockets (3.7.2) concurrent-ruby (~> 1.0) @@ -117,10 +165,14 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) + stackprof (0.2.26) + strong_migrations (1.8.0) + activerecord (>= 5.2) thor (0.20.3) thread_safe (0.3.6) tzinfo (1.2.5) thread_safe (~> 0.1) + uniform_notifier (1.16.0) web-console (3.7.0) actionview (>= 5.0) activemodel (>= 5.0) @@ -134,12 +186,22 @@ PLATFORMS ruby DEPENDENCIES + activerecord-import bootsnap (>= 1.1.0) + bullet byebug + database_cleaner-active_record (~> 2.0.1) listen (>= 3.0.5, < 3.2) pg (>= 0.18, < 2.0) + pg_query (>= 0.9.0) + pghero puma (~> 3.11) + rack-mini-profiler rails (~> 5.2.3) + rspec-benchmark + rspec-rails (~> 4.0.0) + stackprof + strong_migrations tzinfo-data web-console (>= 3.3.0) diff --git a/app/controllers/trips_controller.rb b/app/controllers/trips_controller.rb index acb38be2..f3afcb1d 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.where(from: @from, to: @to).includes([bus: [:services]]).order(:start_time) end end 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/trip.rb b/app/models/trip.rb index 9d63dfff..82920422 100644 --- a/app/models/trip.rb +++ b/app/models/trip.rb @@ -17,16 +17,16 @@ class Trip < ApplicationRecord def to_h { - from: from.name, - to: to.name, - start_time: start_time, - duration_minutes: duration_minutes, - price_cents: price_cents, - bus: { - number: bus.number, - model: bus.model, - services: bus.services.map(&:name), + 'bus' => { + 'model' => bus.model, + 'number' => bus.number, + 'services' => bus.services.map(&:name), }, + 'duration_minutes' => duration_minutes, + 'from' => from.name, + 'price_cents' => price_cents, + 'start_time' => start_time, + 'to' => to.name } end end diff --git a/app/services/import_trips_service.rb b/app/services/import_trips_service.rb new file mode 100644 index 00000000..511f438a --- /dev/null +++ b/app/services/import_trips_service.rb @@ -0,0 +1,49 @@ +class ImportTripsService + def self.call(filename) + json = JSON.parse(File.read(filename)) + + ActiveRecord::Base.transaction do + City.delete_all + Bus.delete_all + Service.delete_all + Trip.delete_all + ActiveRecord::Base.connection.execute('delete from buses_services;') + + cities = {} + services = {} + buses = {} + buses_services = {} + + trips = [] + + json.each do |trip| + from = cities[trip['from']] ||= City.new(name: trip['from']) + to = cities[trip['to']] ||= City.new(name: trip['to']) + + bus = buses[trip['bus']['number']] ||= Bus.new(number: trip['bus']['number'], model: trip['bus']['model']) + + trip['bus']['services'].each do |service| + bus_service = services[service] ||= Service.new(name: service) + buses_services[[bus, bus_service]] ||= BusesService.new(bus: bus, service: bus_service) + end + + trips << Trip.new( + from: from, + to: to, + bus: bus, + start_time: trip['start_time'], + duration_minutes: trip['duration_minutes'], + price_cents: trip['price_cents'], + ) + end + + City.import cities.values + Bus.import buses.values + Service.import services.values + BusesService.import buses_services.values + Trip.import trips + + cities, services, buses, trips = nil + end + end +end \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index e64170ee..bfc813b1 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -10,6 +10,8 @@
+ <%= link_to "Database Analyzer", pg_hero_path, target: :blank %> +