Skip to content

Commit 04cf1de

Browse files
committed
ready for next gem release
1 parent f44779f commit 04cf1de

File tree

21 files changed

+277
-89
lines changed

21 files changed

+277
-89
lines changed

docs/client-dsl/interlude-tic-tac-toe.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ At this point if you have been reading sequentially through these chapters you k
44

55
The board is represented by an array of 9 cells. Cell 0 is the top left square, and cell 8 is the bottom right.
66

7-
Each cell will contain nil, an :X or an :O.
7+
Each cell will contain nil, an `:X` or an `:O`.
88

99
### Displaying the Board
1010

@@ -13,7 +13,7 @@ The `DisplayBoard` component displays a board. `DisplayBoard` accepts a `board`
1313
A small helper function `draw_squares` draws an individual square which is displayed as a `BUTTON`. A click handler is attached which
1414
will fire the `clicked_at` event with the appropriate cell id.
1515

16-
Notice that `DisplayBoard` has no internal state of its own. That is handled by the `Game` component.
16+
Notice that `DisplayBoard` has no internal state of its own. That is handled by the `DisplayGame` component.
1717

1818
```ruby
1919
class DisplayBoard < HyperComponent
@@ -37,7 +37,7 @@ end
3737

3838
### The Game State
3939

40-
The `Game` component has two state variables:
40+
The `DisplayGame` component has two state variables:
4141
+ `@history` which is an array of boards, each board being the array of cells.
4242
+ `@step` which is the current step in the history (we begin at zero)
4343

@@ -46,17 +46,17 @@ The `Game` component has two state variables:
4646
These are initialized in the `before_mount` callback. Because Ruby will adjust the array size as needed
4747
and return nil if an array value is not initialized, we can simply initialize the board to an empty array.
4848

49-
There are two reader methods that read the state:
49+
There are three *reader* methods that read the state:
5050

51-
+ `player` returns the current player's token. The first player is always :X so even steps
52-
are :X, and odd steps are :O.
51+
+ `player` returns the current player's token. The first player is always `:X` so even steps
52+
are `:X`, and odd steps are `:O`.
5353
+ `current` returns the board at the current step.
5454
+ `history` uses state_reader to encapsulate the history state.
5555

5656
Encapsulated access to state in reader methods like this is not necessary but is good practice
5757

5858
```ruby
59-
class Game < HyperComponent
59+
class DisplayGame < HyperComponent
6060
before_mount do
6161
@history = [[]]
6262
@step = 0
@@ -79,7 +79,7 @@ end
7979
We also have a `current_winner?` method that will return the winning player or nil based on the value of the current board:
8080

8181
```ruby
82-
class Game < HyperComponent
82+
class DisplayGame < HyperComponent
8383
WINNING_COMBOS = [
8484
[0, 1, 2],
8585
[3, 4, 5],
@@ -93,9 +93,7 @@ class Game < HyperComponent
9393

9494
def current_winner?
9595
WINNING_COMBOS.each do |a, b, c|
96-
return current[a] if current[a] &&
97-
current[a] == current[b] &&
98-
current[a] == current[c]
96+
return current[a] if current[a] && current[a] == current[b] && current[a] == current[c]
9997
end
10098
false
10199
end
@@ -110,7 +108,9 @@ There are two mutator methods that change state:
110108

111109
The `handle_click!` mutator first checks to make sure that no one has already won at the current step, and that
112110
no one has played in the cell that the user clicked on. If either of these conditions is true `handle_click!`
113-
returns and nothing changes.
111+
returns, no mutation is signaled and nothing changes.
112+
113+
> If we had wanted to return AND signal a state mutation we would use the Ruby `next` keyword instead of `return`.s
114114
115115
To update the board `handle_click!` duplicates the squares; adds the player's token to the cell; makes a new
116116
history with the new squares on the end, and finally updates the value of `@step`.
@@ -120,7 +120,7 @@ code are aware that these will change state.
120120

121121
```ruby
122122

123-
class Game < HyperComponent
123+
class DisplayGame < HyperComponent
124124
mutator :handle_click! do |id|
125125
board = history[@step]
126126
return if current_winner? || board[id]
@@ -143,7 +143,7 @@ Now we have a couple of helper methods to build parts of the game display.
143143
+ `status` provides the play state
144144

145145
```ruby
146-
class Game < HyperComponent
146+
class DisplayGame < HyperComponent
147147
def moves
148148
return unless history.length > 1
149149

@@ -166,7 +166,7 @@ end
166166
And finally our render method which displays the Board and the game info:
167167

168168
```ruby
169-
class Game < HyperComponent
169+
class DisplayGame < HyperComponent
170170
render(DIV, class: :game) do
171171
DIV(class: :game_board) do
172172
DisplayBoard(board: current)

docs/client-dsl/notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ Hyperstack.cancel_import 'hyperstack/component/auto-import'
297297
```
298298

299299
### The Enter Event
300+
The :enter event is short for catching :key_down and then checking for a key code of 13.
300301
```ruby
301302
class YouSaid < HyperComponent
302303
state_accessor :value

docs/client-dsl/state.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ is common enough that Hyperstack provides two ways to shorten this code. The fi
9494
In other words `mutator` defines a method that is wrapped in a call to `mutate`. It also has
9595
the advantage of clearly declaring that this method will be mutating the components state.
9696

97+
> Important note: If you do an early exit from the mutator using a `return` or `break` no mutation
98+
will occur. If you want to do an early exit then use the `next` keyword.
99+
97100
### The `state_accessor`, `state_reader` and `state_writer` Methods
98101

99102
Often all a mutator method will do is assign a new value to a state. For this case Hyperstack provides

docs/hyper-state/README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,101 @@
22
<img align="left" width="100" height="100" style="margin-right: 20px" src="https://github.com/hyperstack-org/hyperstack/blob/edge/docs/wip.png?raw=true" /> The `Hyperstack::State::Observable` module allows you to build classes that share their state with Hyperstack Components, and have those components update when objects in those classes change state.
33

44
## This Page Under Construction
5+
6+
The `Hyperstack::State::Observable` module allows you to build classes that share their state with Hyperstack Components, and have those components update when objects in those classes change state.
7+
8+
### Revisiting the Tic Tac Toe Game
9+
10+
The easiest way to understand how to use Hyperstate is by example. If you you did not see the Tic Tac Toe example, then please review it now, as we are going to use this to demonstrate how the `Hyperstack::State::Observable` can be used in any Ruby class, to make that class work as a **Store** for your Hyperstack components.
11+
12+
Here is the revised Tic Tac Toe game using a *Store* to hold the game data.
13+
14+
```ruby
15+
class Game
16+
include Hyperstack::State::Observable
17+
18+
receives Hyperstack::Application::Boot do
19+
@history = [[]]
20+
@step = 0
21+
end
22+
23+
class << self
24+
observer :player do
25+
@step.even? ? :X : :O
26+
end
27+
28+
observer :current do
29+
@history[@step]
30+
end
31+
32+
state_reader :history
33+
34+
WINNING_COMBOS = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]]
35+
36+
def current_winner?
37+
WINNING_COMBOS.each do |a, b, c|
38+
return current[a] if current[a] && current[a] == current[b] && current[a] == current[c]
39+
end
40+
false
41+
end
42+
43+
mutator :handle_click! do |id|
44+
board = history[@step]
45+
return if current_winner? || board[id]
46+
47+
board = board.dup
48+
board[id] = player
49+
@history = history[0..@step] + [board]
50+
@step += 1
51+
end
52+
53+
mutator(:jump_to!) { |step| @step = step }
54+
end
55+
end
56+
57+
class DisplayBoard < HyperComponent
58+
param :board
59+
60+
def draw_square(id)
61+
BUTTON(class: :square, id: id) { board[id] }
62+
.on(:click) { Game.handle_click!(id) }
63+
end
64+
65+
render(DIV) do
66+
(0..6).step(3) do |row|
67+
DIV(class: :board_row) do
68+
(row..row + 2).each { |id| draw_square(id) }
69+
end
70+
end
71+
end
72+
end
73+
74+
class DisplayGame < HyperComponent
75+
def moves
76+
return unless Game.history.length > 1
77+
78+
Game.history.length.times do |move|
79+
LI(key: move) { move.zero? ? "Go to game start" : "Go to move ##{move}" }
80+
.on(:click) { Game.jump_to!(move) }
81+
end
82+
end
83+
84+
def status
85+
if (winner = Game.current_winner?)
86+
"Winner: #{winner}"
87+
else
88+
"Next player: #{Game.player}"
89+
end
90+
end
91+
92+
render(DIV, class: :game) do
93+
DIV(class: :game_board) do
94+
DisplayBoard(board: Game.current)
95+
end
96+
DIV(class: :game_info) do
97+
DIV { status }
98+
OL { moves }
99+
end
100+
end
101+
end
102+
```

docs/rails-installation/prerequisites.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ If you don't have an existing Rails app you can create a new Rails app
2525
with the following command line:
2626

2727
```
28-
bundle exec rails new NameOfYourApp -T
28+
rails new NameOfYourApp -T
2929
```
3030

3131
To avoid much pain do not name your app `Application` as this will conflict with all sorts of

0 commit comments

Comments
 (0)