Skip to content

Commit

Permalink
feat: New components.
Browse files Browse the repository at this point in the history
feat: New components.

Refs:
 - #53

chore: 1.18 min go version.

chore: interface{} to any
  • Loading branch information
jfyne committed Jul 18, 2022
1 parent d4f4285 commit e387aa2
Show file tree
Hide file tree
Showing 23 changed files with 315 additions and 349 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ jobs:
test:
strategy:
matrix:
go-version: [1.17.x, 1.18.x]
go-version: [1.18.x]
node-version: [16.x, 17.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
Expand Down
54 changes: 25 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,22 @@ func NewThermoModel(s Socket) *ThermoModel {

// thermoMount initialises the thermostat state. Data returned in the mount function will
// automatically be assigned to the socket.
func thermoMount(ctx context.Context, s Socket) (interface{}, error) {
func thermoMount(ctx context.Context, s Socket) (any, error) {
return NewThermoModel(s), nil
}

// tempUp on the temp up event, increase the thermostat temperature by .1 C. An EventHandler function
// is called with the original request context of the socket, the socket itself containing the current
// state and and params that came from the event. Params contain query string parameters and any
// `live-value-` bindings.
func tempUp(ctx context.Context, s Socket, p Params) (interface{}, error) {
func tempUp(ctx context.Context, s Socket, p Params) (any, error) {
model := NewThermoModel(s)
model.C += 0.1
return model, nil
}

// tempDown on the temp down event, decrease the thermostat temperature by .1 C.
func tempDown(ctx context.Context, s Socket, p Params) (interface{}, error) {
func tempDown(ctx context.Context, s Socket, p Params) (any, error) {
model := NewThermoModel(s)
model.C -= 0.1
return model, nil
Expand Down Expand Up @@ -157,39 +157,35 @@ package page

import (
"context"
"io"
"net/http"

"github.com/jfyne/live"
)

// NewGreeter creates a new greeter component.
func NewGreeter(ID string, h live.Handler, s live.Socket, name string) (*Component, error) {
return NewComponent(
ID,
h,
s,
WithMount(func(ctx context.Context, c *Component) error {
c.State = name
return nil
}),
WithRender(func(w io.Writer, c *Component) error {
// Render the greeter, here we are including the script just to make this toy example work.
return HTML(`
<div class="greeter">Hello {{.}}</div>
<script src="/live.js"></script>
`, c).Render(w)
}),
)
type Greeter struct {
Name string

Component
}

func NewGreeter(name string) *Greeter {
return &Greeter{
Name: name,
}
}

func (g Greeter) Render() RenderFunc {
return HTML(`
<div class="greeter">Hello {{.Name}}</div>
<script src="/live.js"></script>
`, g)
}

func Example() {
h := live.NewHandler(
WithComponentMount(func(ctx context.Context, h live.Handler, s live.Socket) (*Component, error) {
return NewGreeter("hello-id", h, s, "World!")
}),
WithComponentRenderer(),
)
h := NewHandler(func(_ context.Context, _ *live.Handler, _ live.Socket) (ComponentLifecycle, error) {
root := NewGreeter("World!")
return root, nil
})

http.Handle("/", live.NewHttpHandler(live.NewCookieStore("session-name", []byte("weak-secret")), h))
http.Handle("/live.js", live.Javascript{})
Expand All @@ -215,7 +211,7 @@ Clicking on this tag will result in the browser URL being updated, and then an e
trigger the handler's `HandleParams` callback. With the query string being available in the params map of the handler.

```go
h.HandleParams(func(s *live.Socket, p live.Params) (interface{}, error) {
h.HandleParams(func(s *live.Socket, p live.Params) (any, error) {
...
page := p.Int("page")
...
Expand Down
24 changes: 12 additions & 12 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type BroadcastHandler func(ctx context.Context, e Engine, msg Event)
// Engine methods.
type Engine interface {
// Handler takes a handler to configure the lifecycle.
Handler(h Handler)
Handler(h *Handler)
// Mount a user should provide the mount function. This is what
// is called on initial GET request and later when the websocket connects.
// Data to render the handler should be fetched here and returned.
Expand All @@ -48,7 +48,7 @@ type Engine interface {
// HandleBroadcast allows overriding the broadcast functionality.
HandleBroadcast(handler BroadcastHandler)
// Broadcast send a message to all sockets connected to this engine.
Broadcast(event string, data interface{}) error
Broadcast(event string, data any) error

// self sends a message to the socket on this engine.
self(ctx context.Context, sock Socket, msg Event)
Expand All @@ -57,7 +57,7 @@ type Engine interface {
// BaseEngine handles live inner workings.
type BaseEngine struct {
// handler implements all the developer defined logic.
handler Handler
handler *Handler

// broadcastLimiter limit broadcast ratehandler.
broadcastLimiter *rate.Limiter
Expand All @@ -83,7 +83,7 @@ type BaseEngine struct {
}

// NewBaseEngine creates a new base engine.
func NewBaseEngine(h Handler) *BaseEngine {
func NewBaseEngine(h *Handler) *BaseEngine {
const maxUploadSize = 100 * 1024 * 1024
return &BaseEngine{
broadcastLimiter: rate.NewLimiter(rate.Every(time.Millisecond*100), 8),
Expand All @@ -92,36 +92,36 @@ func NewBaseEngine(h Handler) *BaseEngine {
},
socketMap: make(map[SocketID]Socket),
IgnoreFaviconRequest: true,
MaxUploadSize: 100 * 1024 * 1024,
MaxUploadSize: maxUploadSize,
handler: h,
}
}

func (e *BaseEngine) Handler(hand Handler) {
func (e *BaseEngine) Handler(hand *Handler) {
e.handler = hand
}
func (e *BaseEngine) HandleBroadcast(f BroadcastHandler) {
e.broadcastHandler = f
}

func (e *BaseEngine) Mount() MountHandler {
return e.handler.getMount()
return e.handler.mountHandler
}

func (e *BaseEngine) Params() []EventHandler {
return e.handler.getParams()
return e.handler.paramsHandlers
}

func (e *BaseEngine) Render() RenderHandler {
return e.handler.getRender()
return e.handler.renderHandler
}

func (e *BaseEngine) Error() ErrorHandler {
return e.handler.getError()
return e.handler.errorHandler
}

// Broadcast send a message to all sockets connected to this engine.
func (e *BaseEngine) Broadcast(event string, data interface{}) error {
func (e *BaseEngine) Broadcast(event string, data any) error {
ev := Event{T: event, SelfData: data}
ctx := context.Background()
e.broadcastLimiter.Wait(ctx)
Expand Down Expand Up @@ -229,7 +229,7 @@ func (e *BaseEngine) CallParams(ctx context.Context, sock Socket, msg Event) err
return fmt.Errorf("received params message and could not extract params: %w", err)
}

for _, ph := range e.handler.getParams() {
for _, ph := range e.handler.paramsHandlers {
data, err := ph(ctx, sock, params)
if err != nil {
return fmt.Errorf("handler params handler error: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion event.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type Event struct {
T string `json:"t"`
ID int `json:"i,omitempty"`
Data json.RawMessage `json:"d,omitempty"`
SelfData interface{} `json:"s,omitempty"`
SelfData any `json:"s,omitempty"`
}

// Params extract params from inbound message.
Expand Down
6 changes: 3 additions & 3 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,22 @@ func NewThermoModel(s Socket) *ThermoModel {

// thermoMount initialises the thermostat state. Data returned in the mount function will
// automatically be assigned to the socket.
func thermoMount(ctx context.Context, s Socket) (interface{}, error) {
func thermoMount(ctx context.Context, s Socket) (any, error) {
return NewThermoModel(s), nil
}

// tempUp on the temp up event, increase the thermostat temperature by .1 C. An EventHandler function
// is called with the original request context of the socket, the socket itself containing the current
// state and and params that came from the event. Params contain query string parameters and any
// `live-value-` bindings.
func tempUp(ctx context.Context, s Socket, p Params) (interface{}, error) {
func tempUp(ctx context.Context, s Socket, p Params) (any, error) {
model := NewThermoModel(s)
model.C += 0.1
return model, nil
}

// tempDown on the temp down event, decrease the thermostat temperature by .1 C.
func tempDown(ctx context.Context, s Socket, p Params) (interface{}, error) {
func tempDown(ctx context.Context, s Socket, p Params) (any, error) {
model := NewThermoModel(s)
model.C -= 0.1
return model, nil
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/jfyne/live

go 1.17
go 1.18

require (
github.com/google/go-cmp v0.5.7
Expand Down
75 changes: 17 additions & 58 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@ import (
"log"
)

var _ Handler = &BaseHandler{}

// HandlerConfig applies config to a handler.
type HandlerConfig func(h Handler) error
type HandlerConfig func(h *Handler) error

// MountHandler the func that is called by a handler to gather data to
// be rendered in a template. This is called on first GET and then later when
// the web socket first connects. It should return the state to be maintained
// in the socket.
type MountHandler func(ctx context.Context, c Socket) (interface{}, error)
type MountHandler func(ctx context.Context, c Socket) (any, error)

// RenderHandler the func that is called to render the current state of the
// data for the socket.
Expand All @@ -28,41 +26,14 @@ type ErrorHandler func(ctx context.Context, err error)

// EventHandler a function to handle events, returns the data that should
// be set to the socket after handling.
type EventHandler func(context.Context, Socket, Params) (interface{}, error)
type EventHandler func(context.Context, Socket, Params) (any, error)

// SelfHandler a function to handle self events, returns the data that should
// be set to the socket after handling.
type SelfHandler func(context.Context, Socket, interface{}) (interface{}, error)

// Handler methods.
type Handler interface {
// HandleMount handles initial setup on first request, and then later when
// the socket first connets.
HandleMount(handler MountHandler)
// HandleRender used to set the render method for the handler.
HandleRender(handler RenderHandler)
// HandleError for when an error occurs.
HandleError(handler ErrorHandler)
// HandleEvent handles an event that comes from the client. For example a click
// from `live-click="myevent"`.
HandleEvent(t string, handler EventHandler)
// HandleSelf handles an event that comes from the server side socket. For example calling
// h.Self(socket, msg) will be handled here.
HandleSelf(t string, handler SelfHandler)
// HandleParams handles a URL query parameter change. This is useful for handling
// things like pagincation, or some filtering.
HandleParams(handler EventHandler)

getMount() MountHandler
getRender() RenderHandler
getError() ErrorHandler
getEvent(t string) (EventHandler, error)
getSelf(t string) (SelfHandler, error)
getParams() []EventHandler
}
type SelfHandler func(context.Context, Socket, any) (any, error)

// BaseHandler.
type BaseHandler struct {
// Handler.
type Handler struct {
// mountHandler a user should provide the mount function. This is what
// is called on initial GET request and later when the websocket connects.
// Data to render the handler should be fetched here and returned.
Expand All @@ -82,12 +53,12 @@ type BaseHandler struct {
}

// NewHandler sets up a base handler for live.
func NewHandler(configs ...HandlerConfig) *BaseHandler {
h := &BaseHandler{
func NewHandler(configs ...HandlerConfig) *Handler {
h := &Handler{
eventHandlers: make(map[string]EventHandler),
selfHandlers: make(map[string]SelfHandler),
paramsHandlers: []EventHandler{},
mountHandler: func(ctx context.Context, s Socket) (interface{}, error) {
mountHandler: func(ctx context.Context, s Socket) (any, error) {
return nil, nil
},
renderHandler: func(ctx context.Context, rc *RenderContext) (io.Reader, error) {
Expand All @@ -109,57 +80,45 @@ func NewHandler(configs ...HandlerConfig) *BaseHandler {
return h
}

func (h *BaseHandler) HandleMount(f MountHandler) {
func (h *Handler) HandleMount(f MountHandler) {
h.mountHandler = f
}
func (h *BaseHandler) HandleRender(f RenderHandler) {
func (h *Handler) HandleRender(f RenderHandler) {
h.renderHandler = f
}
func (h *BaseHandler) HandleError(f ErrorHandler) {
func (h *Handler) HandleError(f ErrorHandler) {
h.errorHandler = f
}

// HandleEvent handles an event that comes from the client. For example a click
// from `live-click="myevent"`.
func (h *BaseHandler) HandleEvent(t string, handler EventHandler) {
func (h *Handler) HandleEvent(t string, handler EventHandler) {
h.eventHandlers[t] = handler
}

// HandleSelf handles an event that comes from the server side socket. For example calling
// h.Self(socket, msg) will be handled here.
func (h *BaseHandler) HandleSelf(t string, handler SelfHandler) {
func (h *Handler) HandleSelf(t string, handler SelfHandler) {
h.selfHandlers[t] = handler
}

// HandleParams handles a URL query parameter change. This is useful for handling
// things like pagincation, or some filtering.
func (h *BaseHandler) HandleParams(handler EventHandler) {
func (h *Handler) HandleParams(handler EventHandler) {
h.paramsHandlers = append(h.paramsHandlers, handler)
}

func (h *BaseHandler) getMount() MountHandler {
return h.mountHandler
}
func (h *BaseHandler) getRender() RenderHandler {
return h.renderHandler
}
func (h *BaseHandler) getError() ErrorHandler {
return h.errorHandler
}
func (h *BaseHandler) getEvent(t string) (EventHandler, error) {
func (h *Handler) getEvent(t string) (EventHandler, error) {
handler, ok := h.eventHandlers[t]
if !ok {
return nil, fmt.Errorf("no event handler for %s: %w", t, ErrNoEventHandler)
}
return handler, nil
}
func (h *BaseHandler) getSelf(t string) (SelfHandler, error) {
func (h *Handler) getSelf(t string) (SelfHandler, error) {
handler, ok := h.selfHandlers[t]
if !ok {
return nil, fmt.Errorf("no self handler for %s: %w", t, ErrNoEventHandler)
}
return handler, nil
}
func (h *BaseHandler) getParams() []EventHandler {
return h.paramsHandlers
}
Loading

0 comments on commit e387aa2

Please sign in to comment.