Skip to content

Commit 1ad396c

Browse files
committed
added all the new files
1 parent 9e2f3bd commit 1ad396c

File tree

109 files changed

+15952
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+15952
-0
lines changed

docs/.bookignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
specs/

docs/client-dsl/error-recovery.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
### When things go wrong...
2+
3+
The `rescues` lifecycle callbacks run when an error is raised from within or below a component.
4+
5+
At the end of the rescue the component tree will be completely re-rendered from scratch. In its
6+
simplest form we need nothing but an empty block.
7+
8+
```ruby
9+
class App < HyperComponent
10+
render(DIV) do
11+
H1 { "Welcome to Our App" }
12+
ContentWhichFailsSometimes()
13+
end
14+
15+
rescues do
16+
end
17+
end
18+
```
19+
When an error occurs it will be caught by
20+
the rescues block, and `App` and all lower components will be re-generated (not just re-rendered).
21+
22+
In most cases you may want to warn the user that something is going wrong, and also record some data
23+
about the event:
24+
```ruby
25+
class App < HyperComponent
26+
render(DIV) do
27+
H1 { "Welcome to Our App" }
28+
if @failure_fall_back
29+
DIV { 'Whoops we had a little problem' }
30+
BUTTON { 'retry' }.on(:click) { mutate @failure_fall_back = false }
31+
else
32+
ContentWhichFailsSometimes()
33+
end
34+
end
35+
36+
rescues do |err|
37+
@failure_fall_back = true
38+
ReportError.run(err: err)
39+
end
40+
end
41+
```
42+
43+
If you don't want to involve the user, then be careful: To prevent infinite loops the React engine
44+
will not rescue failures occurring during the re-generation of the component tree. If not involving
45+
the user you may want to consider how to insure that system state is completely reset in the rescue.
46+
47+
The rescues method can also take explicit Error types to be rescued:
48+
49+
```ruby
50+
rescues StandardError, MyInternalError do |err|
51+
...
52+
end
53+
```
54+
55+
Like other lifecycle methods you can have multiple rescues in the same component:
56+
57+
```ruby
58+
rescues StandardError do |err|
59+
# code for handling StandardError
60+
end
61+
rescues MyInternalError do |err|
62+
# different code for handling MyInternalError
63+
end
64+
```
65+
66+
Like Ruby's rescue keyword, errors will be caught by the innermost component with a `rescues` callback that
67+
handles that error.
68+
69+
The data passed to the rescue handler is an array of two items, the Ruby error that was thrown, and details generated by the React engine.
70+
71+
```ruby
72+
rescues do |e, info|
73+
# e is a Ruby error, and responds to backtrace, message, etc
74+
# info is a hash currently with the single key :componentStack
75+
# which is string representing the backtrace through the component
76+
# hierarchy
77+
end
78+
```
79+
80+
## Caveats
81+
82+
1. You cannot rescue errors raised in lifecycle handlers in the same component. Errors raised by lifecycle handlers in inner components are fine, just not
83+
in the same component as the rescue.
84+
2. Errors raised in event handlers will neither stop the rendering cycle, nor will they be caught by a rescue callback.
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
Params pass data downwards from owner to owned-by component. Data comes back upwards asynchronously
2+
via *callbacks*, which are simply *Procs* passed as params into the owned-by component.
3+
4+
> **[More on Ruby Procs here ...](notes.md#ruby-procs)**
5+
6+
The upwards flow of data via callbacks is triggered by some event such as a mouse click, or input change:
7+
8+
```ruby
9+
class ClickDemo2 < HyperComponent
10+
render do
11+
BUTTON { "click" }.on(:click) { |evt| puts "I was clicked"}
12+
end
13+
end
14+
```
15+
16+
When the `BUTTON` is clicked, the event (evt) is passed to the attached click handler.
17+
18+
The details of the event object will be discussed below.
19+
20+
### Firing Events from Components
21+
22+
You can also define events in your components to communicate back to the owner:
23+
24+
```ruby
25+
class Clicker < HyperComponent
26+
param title: "click"
27+
fires :clicked
28+
before_mount { @clicks = 0 }
29+
render do
30+
BUTTON { title }.on(:click) { clicked!(@clicks += 1) }
31+
end
32+
end
33+
34+
class ClickDemo3 < HyperComponent
35+
render(DIV) do
36+
DIV { "I have been clicked #{pluralize(@clicks, 'times')}" } if @clicks
37+
Clicker().on(:clicked) { |clicks| mutate @clicks = clicks }
38+
end
39+
end
40+
```
41+
42+
Each time the `Clicker's` button is clicked it *fires* the clicked event, indicated
43+
by the event name followed by a bang (!).
44+
45+
The `clicked` event is received by `ClickDemo3`, and it updates its state. As you
46+
can see events can send arbitrary data back out.
47+
48+
> Notice also that Clicker does not call mutate. It could, but since the change in
49+
`@clicks` is not used anywhere to control its display there is no need for Clicker
50+
to mutate.
51+
52+
### Relationship between Events and Params
53+
54+
Notice how events (and callbacks in general as we will see) move data upwards, while
55+
params move data downwards. We can emphasize this by updating our example:
56+
57+
```ruby
58+
class ClickDemo4 < HyperComponent
59+
def title
60+
@clicks ? "Click me again!" : "Let's start clicking!"
61+
end
62+
63+
render(DIV) do
64+
DIV { "I have been clicked #{pluralize(@clicks, 'times')}" } if @clicks
65+
Clicker(title: title).on(:clicked) { |clicks| mutate @clicks = clicks }
66+
end
67+
end
68+
```
69+
70+
When `ClickDemo4` is first rendered, the `title` method will return "Let's start clicking!", and
71+
will be passed to `Clicker`.
72+
73+
The user will (hopefully so we can get on with this chapter) click the button, which will
74+
fire the event. The handler in `ClickDemo4` will mutate its state, causing title to change
75+
to "Click me again!". The new value of the title param will be passed to `Clicker`, and `Clicker`
76+
will re-render with the new title.
77+
78+
**Events (and callbacks) push data up, params move data down.**
79+
80+
### Callbacks and Proc Params
81+
82+
Under the hood Events are simply params of type `Proc`, with the `on` and `fires` method
83+
using some naming conventions to clean things up:
84+
85+
```ruby
86+
class IntemittentButton < HyperComponent
87+
param :frequency
88+
param :pulse, type: Proc
89+
before_mount { @clicks = 0 }
90+
render do
91+
BUTTON(
92+
on_click: lambda {} do
93+
@clicks += 1
94+
pulse(@clicks) if (@clicks % frequency).zero?
95+
end
96+
) { 'click me' }
97+
end
98+
end
99+
100+
class ClickDemo5 < HyperComponent
101+
render do
102+
IntermittentButton(
103+
frequency: 5,
104+
pulse: -> (total_clicks) { alert "you are clicking a lot" }
105+
)
106+
end
107+
end
108+
```
109+
110+
There is really no reason not to use the `fires` method to declare Proc params, and
111+
no reason not use the `on` method to attach handlers. Both will keep your code clean and tidy.
112+
113+
### Naming Conventions
114+
115+
The notation `on(:click)` is short for passing a proc to a param named `on_click`. In general `on(:xxx)` will pass the
116+
given block as the `on_xxx` parameter in a Hyperstack component and `onXxx` in a JS component.
117+
118+
All the built-in events and many React libraries follow the `on_xxx` (or `onXxx` in JS.) However even if a library does not use
119+
this convention you can still attach the handler using `on('<name-of-param>')`. Whatever string is inside the `<..>` brackets will
120+
be used as the param name.
121+
122+
Likewise the `fires` method is shorthand for creating a `Proc` param following the `on_xxx` naming convention:
123+
124+
`fires :foo` is short for
125+
`param :foo, type: Proc, alias: :foo!`
126+
127+
### The `Event` Object
128+
129+
UI events like `click` send an object of class `Event` to the handler. Some of the data you can get from `Event` objects are:
130+
131+
+ `target` : the DOM object that was the target of the UI interaction
132+
+ `target.value` : the value of the DOM object
133+
+ `key_code` : the key pressed (for key_down and key_up events)
134+
135+
> **[Refer to the Predefined Events section for complete details...](predefined-events.md)**
136+
137+
### Other Sources of Events
138+
139+
Besides the UI there are several other sources of events:
140+
141+
+ Timers
142+
+ HTTP Requests
143+
+ Hyperstack Operations
144+
+ Websockets
145+
+ Web Workers
146+
147+
The way you receive events from these sources depends on the event. Typically though the method will either take a block, or callback proc, or in many cases will return a Promise.
148+
Regardless the event handler will do one of three things: mutate some state within the component, fire an event to a higher level component, or update some shared store.
149+
150+
> For details on updating shared stores, which is often the best answer **[see the chapter on HyperState...](/hyper-state.md)**
151+
152+
You have seen the `every` method used to create events throughout this chapter, here is an example with an HTTP post (which returns a promise.)
153+
154+
```ruby
155+
class SaveButton < HyperComponent
156+
fires :saved
157+
fires :failed
158+
render do
159+
BUTTON { "Save" }
160+
.on(:click) do
161+
# Posting to some non-hyperstack endpoint for example
162+
# Data is our class holding some data
163+
Hyperstack::HTTP.post(
164+
END_POINT, payload: Data.to_payload
165+
).then do |response|
166+
saved!(response.json)
167+
end.fail do |response|
168+
failed!(response.json)
169+
end
170+
end
171+
end
172+
end
173+
```

docs/client-dsl/methods.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
as_node
2+
force_update
3+
after
4+
every

0 commit comments

Comments
 (0)