0

私は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 

をそれはまだトランザクションが完了した後のフラッシュメッセージが&を示した前にユーザーが支払いを処理することができて、それは動作しませんでした。

私は少し前倒しされる前にこのようなものを作ったことはありません。任意の指導、感謝します。

UPDATE - すべての

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

まず、それはVAを移動する方が良いでしょうモデルへのリッド:

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

更新プログラムでは、1つのメソッドにあまりにも多くのものを入れます。これを試してみてください: - あなたのハンドラで例外を使用してください: '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のようなハンドラークラスに移動する必要がありますか?このハンドラは、その内部の方法では、1つの引数として

handler = BookingHandler.new(@event) 

をイベントを取ることができる

code--

疑似はあなたのためのロジックを行います。

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 

トランザクションブロックに関しては、これは実際にあなたの状況には関係しません。なぜなら、関連する非活動的アクション中に参照整合性を強制するために使用されるからです。たとえば、レコードBが正常に変更された場合はレコードAのみを変更し、いずれかが失敗した場合はトランザクション内の変更をロールバックします。

これが役に立ちます。

+0

ありがとう。私はこのコードを実装しようとしていますが、次のエラーが発生しました - 私の予約コントローラのhandler = BookingHandler.new(@event)行に関して、初期化されていない定数BookingsController :: BookingHandler。 –

+0

あなたのネームスペースで判断すると、ファイルが間違った場所にある可能性があります。 モデルディレクトリにクラスを保持する傾向があるので、モデルでは、「controller_services」という名前のフォルダを作成できます。次に、ControllerServices :: BookingHandlerクラスを定義する 'booking_handler.rb'というRubyクラスを作成します。 これで、クラスをController内でControllerServices :: BookingHandler.new(@booking)としてインスタンス化できます。 名前空間が正しい限り、クラスをモデルやライブラリに保存するのに最適です。 – JayJay

+0

ああ、私はこれで何マイルも離れていると思う。上記のコードを更新して、あなたが示唆したコードをどこに置くかを示しましたが、これは別のクラスであるため、すべてが別のモデルファイルになければならないと言っていますか? –

関連する問題