Introduce the Cart model. When a visitor first makes a request to any
controller, a Cart record is generated with a secure, unique token by
utilizing the has_secure_token macro . Once created,
that token is stored between requests in cookies[:cart_token].
The Cart model associates Seat records that the visitor has selected
through the SeatSelection model.
This commit adds support for selecting a Seat and adding it to their
Cart. Tests are included that ensure that the same Seat cannot be
selected multiple times.
Selected Seat information is rendered in the page’s sidebar.
Desktop
Collapse app/controllers/application_controller.rb
Expand app/controllers/application_controller.rb
app/controllers/application_controller.rb
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 09705d1..8533404 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,2 +1,9 @@
class ApplicationController < ActionController::Base
+ before_action do
+ cart_token = cookies[:cart_token]
+
+ Current.cart ||= Cart.create_or_find_by(token: cart_token)
+
+ cookies[:cart_token] ||= Current.cart.token
+ end
end
Collapse app/controllers/selections_controller.rb
Expand app/controllers/selections_controller.rb
app/controllers/selections_controller.rb
diff --git a/app/controllers/selections_controller.rb b/app/controllers/selections_controller.rb
new file mode 100644
index 0000000..2d6f971
--- /dev/null
+++ b/app/controllers/selections_controller.rb
@@ -0,0 +1,9 @@
+class SelectionsController < ApplicationController
+ def create
+ seat = Seat.find(params[:seat_id])
+
+ Current.cart.seat_selections.create_or_find_by(seat: seat)
+
+ redirect_to venue_floor_seats_url(seat.venue, seat.floor)
+ end
+end
Collapse app/models/cart.rb
Expand app/models/cart.rb
app/models/cart.rb
diff --git a/app/models/cart.rb b/app/models/cart.rb
new file mode 100644
index 0000000..62b78c9
--- /dev/null
+++ b/app/models/cart.rb
@@ -0,0 +1,6 @@
+class Cart < ApplicationRecord
+ has_secure_token
+
+ has_many :seat_selections
+ has_many :seats, through: :seat_selections
+end
Collapse app/models/current.rb
Expand app/models/current.rb
app/models/current.rb
diff --git a/app/models/current.rb b/app/models/current.rb
new file mode 100644
index 0000000..1624f40
--- /dev/null
+++ b/app/models/current.rb
@@ -0,0 +1,3 @@
+class Current < ActiveSupport::CurrentAttributes
+ attribute :cart
+end
Collapse app/models/seat.rb
Expand app/models/seat.rb
app/models/seat.rb
diff --git a/app/models/seat.rb b/app/models/seat.rb
index 64992de..ac98874 100644
--- a/app/models/seat.rb
+++ b/app/models/seat.rb
@@ -1,5 +1,7 @@
class Seat < ApplicationRecord
belongs_to :section
+ has_one :floor, through: :section
+ has_one :venue, through: :floor
def self.find_by_row_number!(row_number)
row, number = row_number.split("-")
Collapse app/models/seat_selection.rb
Expand app/models/seat_selection.rb
app/models/seat_selection.rb
diff --git a/app/models/seat_selection.rb b/app/models/seat_selection.rb
new file mode 100644
index 0000000..008a302
--- /dev/null
+++ b/app/models/seat_selection.rb
@@ -0,0 +1,4 @@
+class SeatSelection < ApplicationRecord
+ belongs_to :seat
+ belongs_to :cart
+end
Collapse app/views/seats/index.html.erb
Expand app/views/seats/index.html.erb
app/views/seats/index.html.erb
diff --git a/app/views/seats/index.html.erb b/app/views/seats/index.html.erb
index b88ad58..1fa0c41 100644
--- a/app/views/seats/index.html.erb
+++ b/app/views/seats/index.html.erb
@@ -71,6 +71,26 @@
</thead>
<tbody>
+ <% Current.cart.seats.each do |seat| %>
+ <tr>
+ <td>
+ <%= seat.row_number %>
+ </td>
+ <td class="syos-table__cell--numerals">
+ <%= number_to_currency(seat.section.price / 100.0) %>
+ </td>
+ <td class="syos-u-text-align-right">
+ <button class="syos-button syos-button--transparent">
+ <%= inline_svg_tag(
+ "icons/x-circle.svg",
+ aria: true,
+ class: "syos-icon",
+ title: "Remove",
+ ) %>
+ </button>
+ </td>
+ </tr>
+ <% end %>
</tbody>
</table>
</div>
Collapse app/views/seats/show.html.erb
Expand app/views/seats/show.html.erb
app/views/seats/show.html.erb
diff --git a/app/views/seats/show.html.erb b/app/views/seats/show.html.erb
index 666b316..bfdd3d9 100644
--- a/app/views/seats/show.html.erb
+++ b/app/views/seats/show.html.erb
@@ -59,11 +59,11 @@
</div>
<div class="syos-inline-stack__item">
- <button
- class="syos-button"
- >
- Select
- </button>
+ <%= button_to(
+ "Select",
+ seat_selections_path(seat),
+ class: "syos-button",
+ ) %>
</div>
</div>
</footer>
Collapse config/routes.rb
Expand config/routes.rb
config/routes.rb
diff --git a/config/routes.rb b/config/routes.rb
index 384d8e3..1f8d5ed 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -6,5 +6,9 @@ Rails.application.routes.draw do
end
end
+ resources :seats, only: [] do
+ resources :selections, only: [:create]
+ end
+
root to: redirect("/venues/benedum_center/floors/orchestra/seats")
end
Collapse db/migrate/20190525190929_create_carts.rb
Expand db/migrate/20190525190929_create_carts.rb
db/migrate/20190525190929_create_carts.rb
diff --git a/db/migrate/20190525190929_create_carts.rb b/db/migrate/20190525190929_create_carts.rb
new file mode 100644
index 0000000..025714d
--- /dev/null
+++ b/db/migrate/20190525190929_create_carts.rb
@@ -0,0 +1,10 @@
+class CreateCarts < ActiveRecord::Migration[6.0]
+ def change
+ create_table :carts do |t|
+ t.string :token, null: false
+
+ t.timestamps
+ end
+ add_index :carts, :token, unique: true
+ end
+end
Collapse db/migrate/20190525190937_create_seat_selections.rb
Expand db/migrate/20190525190937_create_seat_selections.rb
db/migrate/20190525190937_create_seat_selections.rb
diff --git a/db/migrate/20190525190937_create_seat_selections.rb b/db/migrate/20190525190937_create_seat_selections.rb
new file mode 100644
index 0000000..c6a9987
--- /dev/null
+++ b/db/migrate/20190525190937_create_seat_selections.rb
@@ -0,0 +1,12 @@
+class CreateSeatSelections < ActiveRecord::Migration[6.0]
+ def change
+ create_table :seat_selections do |t|
+ t.references :seat, null: false, foreign_key: true
+ t.references :cart, null: false, foreign_key: true
+
+ t.timestamps
+ end
+
+ add_index :seat_selections, [:seat_id, :cart_id], unique: true
+ end
+end
Collapse test/controllers/seats_controller_test.rb
Expand test/controllers/seats_controller_test.rb
test/controllers/seats_controller_test.rb
diff --git a/test/controllers/seats_controller_test.rb b/test/controllers/seats_controller_test.rb
new file mode 100644
index 0000000..67e9ce7
--- /dev/null
+++ b/test/controllers/seats_controller_test.rb
@@ -0,0 +1,24 @@
+require "test_helper"
+
+class SeatsControllerTest < ActionDispatch::IntegrationTest
+ test "#index when a Cart exists, does not create a new one" do
+ venue = create(:benedum_center)
+ floor = create(:orchestra, venue: venue)
+ cart = create(:cart)
+
+ cookies[:cart_token] = cart.token
+ get venue_floor_seats_path(venue, floor)
+
+ assert_equal Cart.count, 1
+ assert_equal cookies[:cart_token], cart.token
+ end
+
+ test "#index when a Cart does not exist, creates a new one" do
+ venue = create(:benedum_center)
+ floor = create(:orchestra, venue: venue)
+
+ get venue_floor_seats_path(venue, floor)
+
+ assert_equal Cart.last.token, cookies[:cart_token]
+ end
+end
Collapse test/controllers/selections_controller_test.rb
Expand test/controllers/selections_controller_test.rb
test/controllers/selections_controller_test.rb
diff --git a/test/controllers/selections_controller_test.rb b/test/controllers/selections_controller_test.rb
new file mode 100644
index 0000000..f4a817d
--- /dev/null
+++ b/test/controllers/selections_controller_test.rb
@@ -0,0 +1,22 @@
+require "test_helper"
+
+class SelectionsControllerTest < ActionDispatch::IntegrationTest
+ test "#create when a Seat is not selected" do
+ seat = create(:seat)
+
+ post seat_selections_path(seat)
+
+ assert_equal SeatSelection.pluck(:seat_id), [seat.id]
+ end
+
+ test "#create when a Seat is already selected" do
+ seat_selection = create(:seat_selection)
+ cart = seat_selection.cart
+ seat = seat_selection.seat
+ cookies[:cart_token] = cart.token
+
+ post seat_selections_path(seat)
+
+ assert_equal cart.seats.ids, [seat.id]
+ end
+end
Collapse test/factories.rb
Expand test/factories.rb
test/factories.rb
diff --git a/test/factories.rb b/test/factories.rb
index 45a2575..44cf93c 100644
--- a/test/factories.rb
+++ b/test/factories.rb
@@ -19,6 +19,9 @@ FactoryBot.define do
price { 10_00 }
end
+ factory :cart do
+ end
+
factory :seat do
association :section
@@ -27,4 +30,9 @@ FactoryBot.define do
x { 0 }
y { 0 }
end
+
+ factory :seat_selection do
+ association(:cart)
+ association(:seat)
+ end
end
Collapse test/system/visitor_selects_seat_test.rb
Expand test/system/visitor_selects_seat_test.rb
test/system/visitor_selects_seat_test.rb
diff --git a/test/system/visitor_selects_seat_test.rb b/test/system/visitor_selects_seat_test.rb
new file mode 100644
index 0000000..db805d2
--- /dev/null
+++ b/test/system/visitor_selects_seat_test.rb
@@ -0,0 +1,17 @@
+require "application_system_test_case"
+
+class VisitorSelectsSeatTest < ApplicationSystemTestCase
+ test "visiting the seat page" do
+ venue = create(:benedum_center)
+ floor = create(:orchestra, venue: venue)
+ section = create(:section, floor: floor, price: 10_00)
+ seat = create(:seat, row: "AA", number: "101", section: section)
+
+ visit("/venues/benedum_center/floors/orchestra/seats/AA-101")
+ click_on("Select")
+
+ within("#cart-summary") do
+ assert_text "$10.00"
+ end
+ end
+end