0

Я создаю приложение Events с помощью Ruby on Rails. Мне нужно создать систему для бронирования, чтобы гарантировать, что Событие не будет забронировано. Каждое событие имеет конечное количество доступных пространств - как я могу гарантировать, что, если доступно, например, 100 пробелов, 105 заказов не принимаются.Rails - Обеспечение того, что событие не переутомилось

Это мои мысли до сих пор, а также некоторый код, который я пробовал, но на самом деле не работал.

bookings_controller

 def create 
    @event = Event.find(params[:event_id) 
    if @event.bookings.count >= @event.total_spaces 
    flash[:warning] = "Sorry, this event is fully booked." 
    redirect_to root_path 
    else 
    #code to save the booking 
    end 
end 

В воззрениях -

<% if @event.bookings.count > @event.total_spaces %> 

    # flash: "This event is fully booked" 

    <% else %> 

    # code to make the booking 

Я не уверен, что этого достаточно, чтобы достичь своей цели. Нужен ли мне более надежный метод в моей модели бронирования и некоторые валидации для покрытия этого?

Я пробовал код транзакции блок -

Booking.transaction do 

     @event.reload 
     if @event.bookings.count > @event.number_of_spaces 
     flash[:warning] = "Sorry, this event is fully booked." 
     raise ActiveRecord::Rollback, "event is fully booked" 
     end 
    end 

, но это не сработало, как это до сих пор разрешено пользователю обработать платеж перед сообщением вспышки появился & после сделки была завершена.

Я никогда не строил ничего подобного раньше, поэтому немного в тупике. Любые указания, оцененные.

ОБНОВЛЕНИЕ -

Booking.rb

def set_booking 
return {result: false, flash: :warning, msg: 'Sorry, this event is fully booked'} if event.bookings.count >= event.total_spaces 
    if self.event.is_free? 
     self.total_amount = 0 
     save! 
    else 
     self.total_amount = event.price_pennies * self.quantity 
     begin 
     charge = Stripe::Charge.create(
      amount: total_amount, 
      currency: "gbp", 
      source: stripe_token, 
      description: "Booking created for amount #{total_amount}") 
     self.stripe_charge_id = charge.id 
     save! 
     rescue Stripe::CardError => e 
     # if this fails stripe_charge_id will be null, but in case of update we just set it to nil again 
     self.stripe_charge_id = nil 
     # we check in validatition if nil 

     end 

    end 
    {result: true, flash: :success, msg: 'Booking successful!'} 

конец

bookings_conroller.rb

def create 

    # actually process the booking 
    @event = Event.find(params[:event_id]) 
    # as above, the association between events and bookings means - 
    @booking = @event.bookings.new(booking_params) 
    @booking.user = current_user 
    handler = BookingHandler.new(@event) 
    booking = handler.set_booking(booking_params) 
    flash[booking[:flash]] = booking[:msg] 
    redirect_to root_path 

    # rest of controller code for booking 
+0

Пожалуйста, прочтите http://stackoverflow.com/help/how-to-ask. Было бы хорошо, если бы вы сузились до одной конкретной проблемы и более четко описывали свои намерения, чтобы люди могли вам помочь. –

+0

Хорошо. Соответственно, я буду исправлять. Я пытаюсь обеспечить, чтобы событие не принимало заказы свыше и над количеством доступных им мест. Можете ли вы помочь с этим? –

+0

Независимо от того, какой метод вы используете, вам понадобится способ блокировки/сериализации активности добавления человека к событию, чтобы избежать состояния гонки. –

ответ

0

Прежде всего, это лучше двигаться ва лизинг модели:

class Event < ActiveRecord::Base 
validate :validate_availability 

private 

def validate_availability 
    errors.add(:base, 'event is fully booked') if bookings.count >= total_spaces 
end 
end 

Также советую ознакомиться с шаблоном служебного объекта и использовать его в контроллере.

https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services

+0

Будет ли это проходить в моей модели событий или модели бронирования? Кроме того, следует ли хранить оператор if/else? –

+0

@ Mike.Whitehead Поскольку вы управляете моделью событий, она должна перейти к событию. – Anton

+0

В своем обновлении вы помещаете слишком много вещей в один метод. Попробуйте следующее: - используйте исключения в вашем обработчике: 'raise ApplicationError, 'Извините, это событие полностью забронировано'' и поймайте их в контроллере:' rescue ApplicationError => e; flash [: error] = e.message' - извлечь вещи, связанные с Stripe, в отдельную службу: 'BillingService.new (...). charge (total_amount)' - использовать rubocop. он поможет вам с кодом стиля - https://github.com/bbatsov/rubocop – Anton

0

Моя первая мысль здесь есть, удалить логику бронирования от контроллера. Контроллер должен только заботиться о том, чтобы отвечать на запросы с переданными ему данными, поэтому bookings.count> = events.total_spaces следует переместить в какой-то классный класс, например BookingsHandler?

code--

псевдо

Этот обработчик может принять событие как один аргумент,

handler = BookingHandler.new(@event) 

С методом внутри, что делает логику для вас:

def book_event(booking_details) 
return {result: false, flash: :warning, msg: 'Sorry, this event is fully booked'} if event.bookings.count >= event.total_spaces 
. . . # booking code 
{result: true, flash: :success, msg: 'Booking successful!'} 
end 

С более простой контроллер

handler = BookingHandler.new(@event) 
booking = handler.book_event(params[:booking_details]) 
flash[booking[:flash]] = booking[:msg] 
redirect_to root_path 

Что касается блока транзакций - это не будет иметь никакого отношения к вашей ситуации, поскольку оно используется для обеспечения ссылочной целостности во время связанных атак. Так, например, только изменение записи A, если запись B также успешно изменена, откат любых изменений внутри транзакции, если они либо сбой.

Надеюсь, это поможет.

+0

Спасибо. Я пытаюсь реализовать этот код, но я нахожу ошибку: uninitialized constant BookingsController :: BookingHandler - относительно строки handler = BookingHandler.new (@event) в моем контроллере бронирования. –

+0

Судя по вашему пространству имен, вы, вероятно, просто получили файлы не в том месте. Я предпочитаю хранить классы в каталоге моделей, поэтому в моделях вы можете создать папку под названием «controller_services». Затем создайте класс ruby ​​под названием 'booking_handler.rb', который будет определять класс ControllerServices :: BookingHandler. Затем вы должны создать экземпляр класса ControllerServices :: BookingHandler.new (@ booking) в своем контроллере. Вы можете поместить класс в любую структуру каталогов, к которой относится ваше приложение, если пространство имен является правильным, лучше всего сохранить классы в моделях или lib. – JayJay

+0

А, я думаю, теперь я ухожу от этого. Я обновил свой код выше, чтобы показать вам, где я разместил код, который вы предложили, но вы говорите, что все это должно идти в другом файле модели, поскольку это отдельный класс сам по себе? –

Смежные вопросы