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