Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

task3. #30

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--require spec_helper
34 changes: 34 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
FROM ruby:2.6.3-alpine
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


ENV APP_PATH /var/www/schedule_app
ENV TZ Europe/Moscow
ENV LANG ru_RU.UTF-8
ENV LANGUAGE ru_RU.UTF-8
ENV LC_ALL ru_RU.UTF-8

RUN apk update && apk add --no-cache yarn \
build-base curl-dev git postgresql-dev \
yaml-dev zlib-dev nodejs gcc g++ make busybox ctags \
tzdata

RUN cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime && \
echo "Europe/Moscow" > /etc/timezone

RUN mkdir -p $APP_PATH

WORKDIR $APP_PATH

COPY Gemfile $APP_PATH/Gemfile
COPY Gemfile.lock $APP_PATH/Gemfile.lock
COPY package.json $APP_PATH/package.json

RUN yarn global add node-sass
RUN yarn
RUN gem install bundler:2.0.2
RUN bundle install

COPY . $APP_PATH

EXPOSE 3000/tcp
#CMD sh -c "bin/setup"
#CMD sh -c "bundle exec rails server -b 0.0.0.0 -p 3000"
19 changes: 19 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,37 @@ gem 'pg', '>= 0.18', '< 2.0'
gem 'puma', '~> 3.11'
gem 'bootsnap', '>= 1.1.0', require: false


# profilers
gem 'rack-mini-profiler', require: false
# For memory profiling
gem 'memory_profiler'

# For call-stack profiling flamegraphs
gem 'flamegraph'
gem 'stackprof'
gem "bullet"
gem 'meta_request'

gem 'activerecord-import', require: false
gem 'composite_primary_keys', '=11.2.0'
gem 'oj'

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]
end

group :development do
gem 'pry'
# 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'
end

group :test do
gem 'rspec-rails', '~> 3.8'
gem 'rspec-benchmark'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
Expand Down
65 changes: 65 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ GEM
activemodel (= 5.2.3)
activesupport (= 5.2.3)
arel (>= 9.0)
activerecord-import (1.0.4)
activerecord (>= 3.2)
activestorage (5.2.3)
actionpack (= 5.2.3)
activerecord (= 5.2.3)
Expand All @@ -43,15 +45,26 @@ GEM
minitest (~> 5.1)
tzinfo (~> 1.1)
arel (9.0.0)
benchmark-malloc (0.1.0)
benchmark-perf (0.5.0)
benchmark-trend (0.3.0)
bindex (0.6.0)
bootsnap (1.4.2)
msgpack (~> 1.0)
builder (3.2.3)
bullet (6.1.0)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
byebug (11.0.1)
coderay (1.1.2)
composite_primary_keys (11.2.0)
activerecord (~> 5.2.1)
concurrent-ruby (1.1.5)
crass (1.0.4)
diff-lcs (1.3)
erubi (1.8.0)
ffi (1.10.0)
flamegraph (0.9.5)
globalid (0.4.2)
activesupport (>= 4.2.0)
i18n (1.6.0)
Expand All @@ -67,6 +80,10 @@ GEM
mini_mime (>= 0.1.1)
marcel (0.3.3)
mimemagic (~> 0.3.2)
memory_profiler (0.9.14)
meta_request (0.7.2)
rack-contrib (>= 1.1, < 3)
railties (>= 3.0.0, < 7)
method_source (0.9.2)
mimemagic (0.3.3)
mini_mime (1.0.1)
Expand All @@ -76,9 +93,17 @@ GEM
nio4r (2.3.1)
nokogiri (1.10.2)
mini_portile2 (~> 2.4.0)
oj (3.10.5)
pg (1.1.4)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
puma (3.12.1)
rack (2.0.6)
rack-contrib (2.1.0)
rack (~> 2.0)
rack-mini-profiler (1.1.6)
rack (>= 1.2.0)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (5.2.3)
Expand Down Expand Up @@ -109,6 +134,32 @@ GEM
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
ffi (~> 1.0)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-benchmark (0.5.1)
benchmark-malloc (~> 0.1.0)
benchmark-perf (~> 0.5.0)
benchmark-trend (~> 0.3.0)
rspec (>= 3.0.0, < 4.0.0)
rspec-core (3.9.1)
rspec-support (~> 3.9.1)
rspec-expectations (3.9.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-rails (3.9.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-support (~> 3.9.0)
rspec-support (3.9.2)
ruby_dep (1.5.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
Expand All @@ -117,10 +168,12 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
stackprof (0.2.15)
thor (0.20.3)
thread_safe (0.3.6)
tzinfo (1.2.5)
thread_safe (~> 0.1)
uniform_notifier (1.13.0)
web-console (3.7.0)
actionview (>= 5.0)
activemodel (>= 5.0)
Expand All @@ -134,12 +187,24 @@ PLATFORMS
ruby

DEPENDENCIES
activerecord-import
bootsnap (>= 1.1.0)
bullet
byebug
composite_primary_keys (= 11.2.0)
flamegraph
listen (>= 3.0.5, < 3.2)
memory_profiler
meta_request
oj
pg (>= 0.18, < 2.0)
pry
puma (~> 3.11)
rack-mini-profiler
rails (~> 5.2.3)
rspec-benchmark
rspec-rails (~> 3.8)
stackprof
tzinfo-data
web-console (>= 3.3.0)

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/trips_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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).order(:start_time).includes(:bus).includes(bus: :services)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trips = Trip.includes(bus: :services).where(from: @from, to: @to).order(:start_time)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Спасибо. такой вариант я тоже пробовал. Не помогло.

end
end
6 changes: 4 additions & 2 deletions app/models/bus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ class Bus < ApplicationRecord
'Газель',
].freeze

has_many :trips
has_and_belongs_to_many :services, join_table: :buses_services
self.primary_keys = :number, :model

has_many :trips, :foreign_key => [:number, :model]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Интересное решение, вы первый так сделали

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В задании было написано первичным ключом для автобуса считаем (model, number), плюс в процессе реализации понял плюсы такого подхода - в процессе импорта данных отпадает необходимость в высчитыании id-шников и нет необходимости обеспечения их уникальности(id).

has_and_belongs_to_many :services, join_table: :buses_services, :foreign_key => [:number, :model]

validates :number, presence: true, uniqueness: true
validates :model, inclusion: { in: MODELS }
Expand Down
2 changes: 1 addition & 1 deletion app/models/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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, :foreign_key => :service_id, association_foreign_key: [:number, :model]

validates :name, presence: true
validates :name, inclusion: { in: SERVICES }
Expand Down
2 changes: 1 addition & 1 deletion app/models/trip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class Trip < ApplicationRecord

belongs_to :from, class_name: 'City'
belongs_to :to, class_name: 'City'
belongs_to :bus
belongs_to :bus, -> { includes :services }, :foreign_key => [:number, :model]

validates :from, presence: true
validates :to, presence: true
Expand Down
105 changes: 105 additions & 0 deletions app/services/schedule_loader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
require 'active_record'
require 'activerecord-import'

# frozen_string_literal: true
STrip = Struct.new(:from, :to, :start_time, :duration_minutes, :price_cents, :number, :model)
SBus = Struct.new(:number, :model, :services)

class ScheduleLoader
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Плюс за вынос в сервис 👍

def self.call(file_name)
@cities = {}
@services = {}
@buses = Set.new

@@services = {}
Service.pluck(:name, :id).map{|sr| @@services[sr[0].to_sym] = sr[1]}

# https://koshigoe.github.io/postgresql/ruby/2018/10/31/bulk-insert-vs-copy-in-postgres.html
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@base_connection = ActiveRecord::Base.connection
@raw_conn = ActiveRecord::Base.connection.raw_connection
@encoder = PG::TextEncoder::CopyRow.new

@trips_command_raw = "copy trips (from_id, to_id, start_time, duration_minutes, price_cents, model, number) from stdin"
@buses_services_command_raw = "copy buses_services (service_id, model, number) from stdin"
@bus_command_raw = "copy buses (model, number) from stdin"

@nr2 = Array.new(2)
@nr3 = Array.new(3)
@nr7 = Array.new(7)

ActiveRecord::Base.transaction do
@raw_conn.exec('delete from buses_services;')
@raw_conn.exec('delete from trips;')
@raw_conn.exec('delete from cities;')
@raw_conn.exec('delete from buses;')
@base_connection.reset_pk_sequence!('cities')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@base_connection.reset_pk_sequence!('buses')

File.open(file_name) do |ff|
nesting = 0
str = +''

until ff.eof?
ch = ff.read(1) # читаем по одному символу
if ch == '{' # начинается объект, повышается вложенность
nesting += 1
str << ch
elsif ch == '}' # заканчивается объект, понижается вложенность
nesting -= 1
str << ch
if nesting == 0 # если закончился объкет уровня trip, парсим и импортируем его

trip = Oj.load(str)

import_trip(trip)

str = +''
end
elsif nesting >= 1
str << ch
end
end
end
City.import @cities.map {|cc| ({id: cc[1], name: cc[0]})}
end
end

def self.import_trip(trip)
from_id = @cities[trip['from']]
unless from_id
from_id = @cities.size + 1
@cities[trip['from']] = from_id
end

to_id = @cities[trip['to']]
unless to_id
to_id = @cities.size + 1
@cities[trip['to']] = to_id
end

bus_number = trip['bus']['number']
bus_model = trip['bus']['model']

bus_key = "#{bus_number}-#{bus_model}"

unless @buses.include?(bus_key)
@raw_conn.copy_data @bus_command_raw, @encoder do
@nr2 = bus_number, bus_model
@raw_conn.put_copy_data(@nr2)
end
@buses << bus_key

@raw_conn.copy_data @buses_services_command_raw, @encoder do
trip["bus"]["services"].map do |srv|
@nr3 = @@services[srv.to_sym], bus_number, bus_model
@raw_conn.put_copy_data(@nr3)
end
end
end

@raw_conn.copy_data @trips_command_raw, @encoder do
@nr7 = from_id,to_id, trip['start_time'],trip['duration_minutes'],trip['price_cents'],bus_number,bus_model
@raw_conn.put_copy_data(@nr7)
end
end
end
1 change: 0 additions & 1 deletion app/views/trips/_delimiter.html.erb

This file was deleted.

1 change: 0 additions & 1 deletion app/views/trips/_service.html.erb

This file was deleted.

6 changes: 0 additions & 6 deletions app/views/trips/_services.html.erb

This file was deleted.

12 changes: 12 additions & 0 deletions app/views/trips/_trip.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@
<li><%= "В пути: #{trip.duration_minutes / 60}ч. #{trip.duration_minutes % 60}мин." %></li>
<li><%= "Цена: #{trip.price_cents / 100}р. #{trip.price_cents % 100}коп." %></li>
<li><%= "Автобус: #{trip.bus.model} №#{trip.bus.number}" %></li>

<li>Сервисы в автобусе:</li>
<ul>
<% trip.bus.services.pluck(:name).map do |service| %>
<%#= render "service", service: service %>
<li><%= service %></li>
<% end %>
</ul>

</ul>
<%# render plain: '====================================================' %>
====================================================
Loading