Skip to content
This repository was archived by the owner on Jan 9, 2024. It is now read-only.

Commit b3b86b2

Browse files
committed
The Game object is decorated before being rendered
I'd like to let the rendered object (`@game) to tells us how it wants to be rendered, instead of letting the controller decide (ie. leaving the default call to `render` in the controller action, and calling `render @game` from the view). To do so, we have to override the default method `to_partial_path` from `ActiveModel::Conversion`. However, the choice of the relevant partial depends on both the game's current state (is it running, over, etc.) **and** the current context, namely, _who_ is looking at the game: is it one of the current players, a new player waiting to join the game, or a guest. Since we don't want the `Game` model to be context-aware, we need a new object to handle this responsibility; something that would represent a game _as seen by a certain person_. This looks like a good role for a _decorator_. I chose to implement this decorator with the simplest object possible: a `SimpleDelegator`. Since we're following Rails' convention of naming objects (expect for models) after their _nature_ (ie. `foo_controller`, `foo_helper`, etc.), I named this object `GameExhibit`. (I chose the `Exhibit` suffix after Avdi Grimm's [Objects on Rails](http://objectsonrails.com/#ID-2656c30c-080a-4a4e-a53e-4fbaad39c262), where Avdi makes a good case against using more familiar terms such as `Decorator` or `Presenter`.) A note on the specs: I tried to follow the apparent convention of large contexts and minimal expectations. I'm not entirely convinced (it looks a bit too cryptic and magical to me), so feedback is especially welcome here.
1 parent 124ee7d commit b3b86b2

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

app/exhibits/game_exhibit.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
class GameExhibit < SimpleDelegator
2+
def initialize(game, current_player)
3+
@current_player = current_player
4+
super(game)
5+
end
6+
7+
def to_partial_path
8+
case state
9+
when "finished"
10+
"games/finished_game"
11+
when "awaiting_opponent"
12+
player_in_game? ? super : "games/awaiting_opponent_game"
13+
when "running"
14+
player_in_game? ? super : "games/full_game"
15+
end
16+
end
17+
18+
def to_model
19+
__getobj__
20+
end
21+
22+
def class
23+
__getobj__.class
24+
end
25+
26+
private
27+
28+
def player_in_game?
29+
@current_player && @current_player.game == __getobj__
30+
end
31+
end

spec/exhibits/game_exhibit_spec.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
require "rails_helper"
2+
3+
RSpec.describe "GameExhibit" do
4+
let(:game) { Game.new }
5+
let(:player) { Player.new }
6+
subject(:game_exhibit) { GameExhibit.new(game, player) }
7+
8+
describe "#to_partial_path" do
9+
subject { game_exhibit.to_partial_path }
10+
11+
context "when the current player is actually playing the game" do
12+
before { player.game = game }
13+
14+
context "if the game is waiting for an opponent" do
15+
before { allow(game).to receive(:state).and_return("awaiting_opponent") }
16+
it { is_expected.to eql("games/game") }
17+
end
18+
19+
context "if the game is running" do
20+
before { allow(game).to receive(:state).and_return("running") }
21+
it { is_expected.to eql("games/game") }
22+
end
23+
24+
context "if the game is over" do
25+
before { allow(game).to receive(:state).and_return("finished") }
26+
it { is_expected.to eql("games/finished_game") }
27+
end
28+
end
29+
30+
context "when the current player is not part of the game" do
31+
before { player.game = Game.new }
32+
33+
context "if the game is waiting for an opponent" do
34+
before { allow(game).to receive(:state).and_return("awaiting_opponent") }
35+
it { is_expected.to eql("games/awaiting_opponent_game") }
36+
end
37+
38+
context "if the game is running" do
39+
before { allow(game).to receive(:state).and_return("running") }
40+
it { is_expected.to eql("games/full_game") }
41+
end
42+
43+
context "if the game is over" do
44+
before { allow(game).to receive(:state).and_return("finished") }
45+
it { is_expected.to eql("games/finished_game") }
46+
end
47+
end
48+
end
49+
end

0 commit comments

Comments
 (0)