Аренда электромобилей — приложение Ruby on Rails

Будущее выглядит все более электрическим. Поскольку почти каждый крупный производитель автомобилей хочет выйти на рынок электромобилей или расширить свои существующие предложения, количество продуктов и услуг, поддерживающих рынок, также должно увеличиться. Одна идея, которая пришла на ум, заключалась в потенциальном росте аппетита к аренде электромобилей, а не к собственности, что на самом деле раздражает потребителей-миллениалов, даже если они предпочитают электромобили. Экономика здесь довольно проста; лучше платить меньшую ежемесячную плату за доступ к машине определенное количество раз в месяц, чем платить обременительный ежемесячный платеж, связанный с владением автомобилем. Я думаю, на первый взгляд, этого можно было бы достичь за счет экономии за счет масштаба при рассмотрении целого парка транспортных средств, еще более усиленного потенциалом автономного вождения — хотя это мост, который нужно перейти позже. Хотя для запуска этого бизнеса потребуется более глубокий анализ текущих основ бизнеса и существующего рынка для продукта, достаточно только идеи, чтобы создать приложение rails, которое может или не может стать будущим веб-сайтом компании! Вот ссылка на готовое приложение для бизнеса по аренде электромобилей.

Основная концепция и пошаговое руководство

Здесь — это краткий видеоролик, посвященный базовому бэкенду приложения. По сути, это приложение для бронирования, позволяющее кому-то бронировать ресурс на определенный период времени. В настоящее время я решил просто показать ключевые внутренние функции, но планирую добавить загрузочный интерфейс в ближайшем будущем.

Ключевые модели и ассоциации

Структура приложения соответствует MVC. Короче говоря, пользователь — это арендатор, автомобиль — арендуемый ресурс. Оба они являются автономными табличными объектами. Посередине объект booking действует как join-table для создания соответствующих ассоциаций с атрибутами car_id и user_id которые используются для создания belongs_to ассоциаций с автомобилем и пользователем. Таким образом, общие ассоциации состоят в том, что пользовательhas_many бронирует и has_many автомобиль through: бронирует. Автомобиль has_many бронированийи много пользователей through: бронирований. Файл схемы показан ниже с соответствующими моделями и атрибутами.

 Users
    t.string "name"
    t.string "email"
    t.boolean "admin", default: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "provider"
    t.string "uid"
    t.string "oauth_token"
    t.datetime "oauth_expires_at"
    t.string "password_digest"
Bookings
    t.date "start_date"
    t.date "end_date"
    t.integer "user_id"
    t.integer "car_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false

Cars
    t.string "name"
    t.integer "top_speed"
    t.integer "range"
    t.integer "charge_time"
    t.integer "price_per_day"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false

Ключевая особенность

Видео выше должно было прояснить основные функции приложения. Особенности, которые я хотел бы выделить, — это аутентификация Google, важные проверки, а также создание и использование admin_controller с использованием методов namespace и scope rails для просмотра статистики и добавления автомобилей.

Google-аутентификация

Я выбрал эту стратегию OAuth главным образом из-за ее относительной простоты (особенно по сравнению с Facebook). Хотя в целом это довольно просто, одна неровность на дороге гарантирует, что он работает в сочетании с драгоценным камнемbcrypt. Это было достигнуто путем добавления условия, которое оценивало, был ли запущен omniauth.auth в sessions_controller, как показано ниже.

class SessionsController < ApplicationController
def new
    @user = User.new
  end
def create
    if auth
      user = User.from_omniauth(request.env["omniauth.auth"])
      session[:user_id] = user.id
      redirect_to user_path(id: user.id)
    else
      @user = User.find_by(name: params[:user][:name])
       if @user && @user.authenticate(params[:user][:password])
          session[:user_id] = @user.id
          redirect_to user_path(@user), notice: "Welcome to E-Cars Rentals!"
       else
          flash[:notice] = "Incorrect username and/or password combination."
          redirect_to signin_path
       end
    end
  end
def destroy
    session[:user_id] = nil
    redirect_to root_url
  end
private
def auth
    request.env['omniauth.auth']
  end
end

Без этого условия приложение выдаст ошибку о том, что user недопустимо. Кроме того, в файле user.rb был создан метод класса, который заполнил атрибуты OAuth, а также password_digest, который нельзя оставить пустым из-за того, что has_secure_password имеет presence валидацию, которую нельзя отключить. Чтобы обойти это, для заполнения атрибута использовался генератор случайных паролей, как показано ниже.

def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.name = auth.info.name
      user.oauth_token = auth.credentials.token
      user.oauth_expires_at = Time.at(auth.credentials.expires_at)
      user.password = SecureRandom.urlsafe_base64
      user.save!
    end
  end

Это была основная проблема с аутентификацией, вышеперечисленные модификации исправили проблемы. Теперь эта функция работает отлично и повышает доверие к приложению.

Важные проверки

Основными входными данными, необходимыми для приложения, являются фактические даты бронирования автомобилей. Таким образом, очевидной проверкой является то, что даты имеют смысл (т. е. они в будущем, а «Конец» больше, чем «Начало») и что пользователь не может случайно дважды забронировать автомобиль, который уже забронирован на определенную дату. . Предыдущая проверка была довольно простой — функция под названием valid_date гарантировала, что атрибут end_date больше, чем атрибут start_date. Последнее было более сложным, так как приходилось перебирать все бронирования, чтобы убедиться, что дубликатов нет. Далее, если машина уже была забронирована, мне нужно было вывести даты, когда машина недоступна. Это стало возможным благодаря двум функциям для модели booking, как показано ниже.

def duplicate_booking
    Booking.all.each do |booking|
      if booking.id != self.id
        if booking.car == self.car
          if self.start_date <= booking.end_date && self.start_date >= booking.start_date || self.end_date <= booking.end_date && self.end_date >= booking.start_date
            return true
          end
        end
      end
    end
  end
def return_available_dates
    Booking.all.each do |booking|
      if self.start_date <= booking.end_date && self.start_date >= booking.start_date || self.end_date <= booking.end_date && self.end_date >= booking.start_date
          return "This car is not available between #{booking.start_date} and #{booking.end_date}. Choose another time! Thanks."
      end
    end
  end

По сути, первая функция гарантирует, что ни одна дата не попадает между датами существующего бронирования. Второй выполняет ту же итерацию, но возвращает даты в сообщении, чтобы предупредить пользователя. На данный момент это хорошо работает для решения проблемы.

Функция администратора

Последняя функция, которую я хотел бы выделить, — предоставление доступа владельцам приложения через маршрут admin. Текущая версия приложения предполагает, что администраторы являются владельцами бизнеса, а не коллегами, которые не могут добавлять автомобили. Опция становится доступной во время процесса регистрации с помощью флажка. Хотя это не «настоящая» аутентификация, было бы легко представить случай, когда пользователь с правами администратора отображается только для предварительно выбранных пользователем пользователей. Приступая к делу — основные функции, доступные на странице, — это просмотр всех бронирований и некоторых связанных статистических данных, а также управление инвентарем автомобилей — в частности, мини-приложение CRUD внутри для изменения и обновления деталей.

Это стало возможным благодаря функциональности, предоставляемой опцией маршрутизации namespace в Rails. admin controller выглядит почти как приложение внутри приложения, с копиями контроллеров booking и cars, которые различны и доступны только администратору. Код ниже детализирует настройку:

namespace :admin do
    resources :bookings, :only => [:index, :show]
    resources :cars, only: [:new, :create, :edit, :update, :destroy, :show, :index]
    resources :home, only: [:index]
  end

namespaceroute настроил имя администратора для маршрута. Внутри страницы были по существу воссозданы с настроенными маршрутами для бронирования и автомобилями, со специфическими функциями для администраторов.

Наконец, хотя и не такая важная функция, scope заслуживает внимания, поскольку она использовалась для упрощения вызова функции для объекта, в частности, чтобы просто сказать booking.user.admin, которая вернула бы либо true, если атрибут admin был истинным. В целом, это повысило читабельность кода, хотя это лишь заменяет скромный труд простого добавления функции класса. Фактический код ниже:

scope :admin, -> { where(admin: true) }

Будущие шаги

На данный момент приложение представляет собой первую часть завершенных веб-приложений. Я инкапсулировал основные базовые функции, которыми будет обладать действительно классное приложение будущего. Я планирую создать внешний интерфейс для этого приложения, а также со временем улучшить внутренний пользовательский интерфейс и UX. На данный момент это было фантастическое усилие, благодаря которому я стал чрезвычайно комфортно планировать, запускать и отлаживать приложение Rails. Скорее всего, в этом посте будет вторая часть, в которой подробно описываются мои усилия по внешнему интерфейсу, особенно по мере того, как я углубляюсь в Javascript. Быть в курсе!