live

package module
v0.15.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 20, 2023 License: MIT Imports: 25 Imported by: 28

README

⚡ live

Go Reference

Real-time user experiences with server-rendered HTML in Go. Inspired by and borrowing from Phoenix LiveViews.

Live is intended as a replacement for React, Vue, Angular etc. You can write an interactive web app just using Go and its templates.

The structures provided in this package are compatible with net/http, so will play nicely with middleware and other frameworks.

Other implementations

Community

For bugs please use github issues. If you have a question about design or adding features, I am happy to chat about it in the discussions tab.

Discord server is here.

Getting Started

Install
go get github.com/jfyne/live

See the examples for usage.

First handler

Here is an example demonstrating how we would make a simple thermostat. Live is compatible with net/http.

package live

import (
	"bytes"
	"context"
	"html/template"
	"io"
	"net/http"
)

// Model of our thermostat.
type ThermoModel struct {
	C float32
}

// Helper function to get the model from the socket data.
func NewThermoModel(s Socket) *ThermoModel {
	m, ok := s.Assigns().(*ThermoModel)
	// If we haven't already initialised set up.
	if !ok {
		m = &ThermoModel{
			C: 19.5,
		}
	}
	return m
}

// 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) {
	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) {
	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) {
	model := NewThermoModel(s)
	model.C -= 0.1
	return model, nil
}

// Example shows a simple temperature control using the
// "live-click" event.
func Example() {

	// Setup the handler.
	h := NewHandler()

	// Mount function is called on initial HTTP load and then initial web
	// socket connection. This should be used to create the initial state,
	// the socket Connected func will be true if the mount call is on a web
	// socket connection.
	h.HandleMount(thermoMount)

	// Provide a render function. Here we are doing it manually, but there is a
	// provided WithTemplateRenderer which can be used to work with `html/template`
	h.HandleRender(func(ctx context.Context, data *RenderContext) (io.Reader, error) {
		tmpl, err := template.New("thermo").Parse(`
            <div>{{.Assigns.C}}</div>
            <button live-click="temp-up">+</button>
            <button live-click="temp-down">-</button>
            <!-- Include to make live work -->
            <script src="/live.js"></script>
        `)
		if err != nil {
			return nil, err
		}
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, data); err != nil {
			return nil, err
		}
		return &buf, nil
	})

	// This handles the `live-click="temp-up"` button. First we load the model from
	// the socket, increment the temperature, and then return the new state of the
	// model. Live will now calculate the diff between the last time it rendered and now,
	// produce a set of diffs and push them to the browser to update.
	h.HandleEvent("temp-up", tempUp)

	// This handles the `live-click="temp-down"` button.
	h.HandleEvent("temp-down", tempDown)

	http.Handle("/thermostat", NewHttpHandler(NewCookieStore("session-name", []byte("weak-secret")), h))

	// This serves the JS needed to make live work.
	http.Handle("/live.js", Javascript{})

	http.ListenAndServe(":8080", nil)
}

Notice the script tag. Live's javascript is embedded within the library for ease of use, and is required to be included for it to work. You can also use the companion npm package to add to any existing web app build pipeline.

Live components

Live can also render components. These are an easy way to encapsulate event logic and make it repeatable across a page. The components examples show how to create components. Those are then used in the world clocks example.

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)
		}),
	)
}

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(),
	)

	http.Handle("/", live.NewHttpHandler(live.NewCookieStore("session-name", []byte("weak-secret")), h))
	http.Handle("/live.js", live.Javascript{})
	http.ListenAndServe(":8080", nil)
}

Navigation

Live provides functionality to use the browsers pushState API to update its query parameters. This can be done from both the client side and the server side.

Client side

The live-patch handler should be placed on an a tag element as it reads the href attribute in order to apply the URL patch.

<a live-patch href="?page=2">Next page</a>

Clicking on this tag will result in the browser URL being updated, and then an event sent to the backend which will trigger the handler's HandleParams callback. With the query string being available in the params map of the handler.

h.HandleParams(func(s *live.Socket, p live.Params) (interface{}, error) {
    ...
    page := p.Int("page")
    ...
})
Server side

Using the Socket's PatchURL func the serverside can make the client update the browsers URL, which will then trigger the HandleParams func.

Redirect

The server can also trigger a redirect if the Socket's Redirect func is called. This will simulate an HTTP redirect using window.location.replace.

Features

Click Events
  • live-capture-click
  • live-click
  • live-value-*

The live-click binding is used to send click events to the server.

<div live-click="inc" live-value-myvar1="val1" live-value-myvar2="val2"></div>

See the buttons example for usage.

Focus / Blur Events
  • live-window-focus
  • live-window-blur
  • live-focus
  • live-blur

Focus and blur events may be bound to DOM elements that emit such events, using the live-blur, and live-focus bindings, for example:

<input name="email" live-focus="myfocus" live-blur="myblur"/>
Key Events
  • live-window-keyup
  • live-window-keydown
  • live-keyup
  • live-keydown
  • live-key

The onkeydown, and onkeyup events are supported via the live-keydown, and live-keyup bindings. Each binding supports a live-key attribute, which triggers the event for the specific key press. If no live-key is provided, the event is triggered for any key press. When pushed, the value sent to the server will contain the "key" that was pressed.

See the buttons example for usage.

Form Events
  • live-auto-recover
  • live-trigger-action
  • live-disable-with
  • live-feedback-for
  • live-submit
  • live-change

To handle form changes and submissions, use the live-change and live-submit events. In general, it is preferred to handle input changes at the form level, where all form fields are passed to the handler's event handler given any single input change. For example, to handle real-time form validation and saving, your template would use both live-change and live-submit bindings.

See the form example for usage.

Rate Limiting
  • live-debounce
  • live-throttle

All events can be rate-limited on the client by using the live-debounce and live-throttle bindings, with the following behavior:

live-debounce accepts either an integer timeout value (in milliseconds), or "blur". When an integer is provided, emitting the event is delayed by the specified milliseconds. When "blur" is provided, emitting the event is delayed until the field is blurred by the user. Debouncing is typically used for input elements.

live-throttle accepts an integer timeout value to throttle the event in milliseconds. Unlike debounce, throttle will immediately emit the event, then rate limit it at once per provided timeout. Throttling is typically used to rate limit clicks, mouse and keyboard actions.

Dom Patching
  • live-update

A container can be marked with live-update, allowing the DOM patch operations to avoid updating or removing portions of the view, or to append or prepend the updates rather than replacing the existing contents. This is useful for client-side interop with existing libraries that do their own DOM operations. The following live-update values are supported:

  • replace - replaces the element with the contents
  • ignore - ignores updates to the DOM regardless of new content changes
  • append - append the new DOM contents instead of replacing
  • prepend - prepend the new DOM contents instead of replacing

When using live-update If using "append" or "prepend", a DOM ID must be set for each child.

See the chat example for usage.

JS Interop
  • live-hook
Hooks

Hooks take the following form. They allow additional javascript to hook into the live lifecycle. These should be used to implement custom behavior and bind additional events which are not supported out of the box.

/**
 * Hooks supplied for interop.
 */
export interface Hooks {
    [id: string]: Hook;
}

/**
 * A hook for running external JS.
 */
export interface Hook {
    /**
     * The element has been added to the DOM and its server
     * LiveHandler has finished mounting
     */
    mounted?: () => void;

    /**
     * The element is about to be updated in the DOM.
     * Note: any call here must be synchronous as the operation
     * cannot be deferred or cancelled.
     */
    beforeUpdate?: () => void;

    /**
     * The element has been updated in the DOM by the server
     */
    updated?: () => void;

    /**
     * The element is about to be removed from the DOM.
     * Note: any call here must be synchronous as the operation
     * cannot be deferred or cancelled.
     */
    beforeDestroy?: () => void;

    /**
     * The element has been removed from the page, either by
     * a parent update, or by the parent being removed entirely
     */
    destroyed?: () => void;

    /**
     * The element's parent LiveHandler has disconnected from
     * the server
     */
    disconnected?: () => void;

    /**
     * The element's parent LiveHandler has reconnected to the
     * server
     */
    reconnected?: () => void;
}

/**
 * The DOM management interface. This allows external JS libraries to
 * interop with Live.
 */
export interface DOM {
    /**
     * The fromEl and toEl DOM nodes are passed to the function
     * just before the DOM patch operations occurs in Live. This
     * allows external libraries to (re)initialize DOM elements
     * or copy attributes as necessary as Live performs its own
     * patch operations. The update operation cannot be cancelled
     * or deferred, and the return value is ignored.
     */
    onBeforeElUpdated?: (fromEl: Element, toEl: Element) => void;
}

In scope when these functions are called:

  • el - attribute referencing the bound DOM node,
  • pushEvent(event: { t: string, d: any }) - method to push an event from the client to the Live server
  • handleEvent(event: string, cb: ((payload: any) => void)) - method to handle an event pushed from the server.

See the chat example for usage.

Integrating with your app

There are two ways to integrate javascript into your applications. The first is the simplest, using the built in javascript handler. This includes client side code to initialise the live handler and automatically looks for hooks at window.Hooks. All of the examples use this method.

To add a custom hook register it before including the live.js file.

window.Hooks = window.Hooks || {};
window.Hooks['my-hook'] = {
	mount: function() {
		// ...
	}
};

Use the live-hook attribute to wire the hook with live.

<div live-hook="my-hook"></div>

See the chat example for usage.

The second method is suited for more complex apps, there is a companion package published on npm. The version should be kept in sync with the current go version.

> npm i @jfyne/live

This can then be used to initialise the live handler on a page

import { Live } from '@jfyne/live';

const hooks = {};

const live = new Live(hooks);
live.init();

This allows more control over how hooks are passed to live, and when it should be initialised. It is expected that you would then build your compiled javsacript and serve it. See the alpine example.

Errors and exceptions

There are two types of errors in a live handler, and how these are handled are separate.

Unexpected errors

Errors that occur during the initial mount, initial render and web socket upgrade process are handled by the handler ErrorHandler func.

Errors that occur while handling incoming web socket messages will trigger a response back with the error.

Expected errors

In general errors which you expect to happen such as form validations etc. should be handled by just updating the data on the socket and re-rendering.

If you return an error in the event handler live will send an "err" event to the socket. You can handle this with a hook. An example of this can be seen in the error example.

Loading state and errors

By default, the following classes are applied to the handlers body:

  • live-connected - applied when the view has connected to the server
  • live-disconnected - applied when the view is not connected to the server
  • live-error - applied when an error occurs on the server. Note, this class will be applied in conjunction with live-disconnected if connection to the server is lost.

All live- event bindings apply their own css classes when pushed. For example the following markup:

<button live-click="clicked" live-window-keydown="key">...</button>

On click, would receive the live-click-loading class, and on keydown would receive the live-keydown-loading class. The css loading classes are maintained until an acknowledgement is received on the client for the pushed event.

The following events receive css loading classes:

  • live-click - live-click-loading
  • live-change - live-change-loading
  • live-submit - live-submit-loading
  • live-focus - live-focus-loading
  • live-blur - live-blur-loading
  • live-window-keydown - live-keydown-loading
  • live-window-keyup - live-keyup-loading

Broadcasting to different nodes

In production it is often required to have multiple instances of the same application running, in order to handle this live has a PubSub element. This allows nodes to publish onto topics and receive those messages as if they were all running as the same instance. See the cluster example for usage.

Uploads

Live supports interactive file uploads with progress indication. See the uploads example for usage.

Features

Accept specification - Define accepted file types, max number of entries, max file size, etc. When the client selects file(s), the file metadata can be validated with a helper function.

Reactive entries - Uploads are populated in the .Uploads template context. Entries automatically respond to progress and errors.

Entry validation

File selection triggers the usual form change event and there is a helper function to validate the uploads. Use live.ValidateUploads to validate the incoming files. Any validation errors will be available in the .Uploads context in the template.

Consume the uploads

When a form is submitted files will first be uploaded to a staging area, then the submit event is triggered. Within the event handler use the live.ConsumeUploads helper function to then move the uploaded files to where you need them.

Documentation

Overview

Example

Example shows a simple temperature control using the "live-click" event.

package main

import (
	"bytes"
	"context"
	"html/template"
	"io"
	"net/http"
)

// Model of our thermostat.
type ThermoModel struct {
	C float32
}

// Helper function to get the model from the socket data.
func NewThermoModel(s Socket) *ThermoModel {
	m, ok := s.Assigns().(*ThermoModel)
	// If we haven't already initialised set up.
	if !ok {
		m = &ThermoModel{
			C: 19.5,
		}
	}
	return m
}

// 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) {
	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) {
	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) {
	model := NewThermoModel(s)
	model.C -= 0.1
	return model, nil
}

// Example shows a simple temperature control using the
// "live-click" event.
func main() {

	// Setup the handler.
	h := NewHandler()

	// Mount function is called on initial HTTP load and then initial web
	// socket connection. This should be used to create the initial state,
	// the socket Connected func will be true if the mount call is on a web
	// socket connection.
	h.HandleMount(thermoMount)

	// Provide a render function. Here we are doing it manually, but there is a
	// provided WithTemplateRenderer which can be used to work with `html/template`
	h.HandleRender(func(ctx context.Context, data *RenderContext) (io.Reader, error) {
		tmpl, err := template.New("thermo").Parse(`
            <div>{{.Assigns.C}}</div>
            <button live-click="temp-up">+</button>
            <button live-click="temp-down">-</button>
            <!-- Include to make live work -->
            <script src="/live.js"></script>
        `)
		if err != nil {
			return nil, err
		}
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, data); err != nil {
			return nil, err
		}
		return &buf, nil
	})

	// This handles the `live-click="temp-up"` button. First we load the model from
	// the socket, increment the temperature, and then return the new state of the
	// model. Live will now calculate the diff between the last time it rendered and now,
	// produce a set of diffs and push them to the browser to update.
	h.HandleEvent("temp-up", tempUp)

	// This handles the `live-click="temp-down"` button.
	h.HandleEvent("temp-down", tempDown)

	http.Handle("/thermostat", NewHttpHandler(NewCookieStore("session-name", []byte("weak-secret")), h))

	// This serves the JS needed to make live work.
	http.Handle("/live.js", Javascript{})

	http.ListenAndServe(":8080", nil)
}
Output:

Index

Examples

Constants

View Source
const (
	// EventError indicates an error has occurred.
	EventError = "err"
	// EventPatch a patch event containing a diff.
	EventPatch = "patch"
	// EventAck sent when an event is acknowledged.
	EventAck = "ack"
	// EventConnect sent as soon as the server accepts the
	// WS connection.
	EventConnect = "connect"
	// EventParams sent for a URL parameter update. Can be
	// sent both directions.
	EventParams = "params"
	// EventRedirect sent in order to trigger a browser
	// redirect.
	EventRedirect = "redirect"
)
View Source
const LiveRendered = "live-rendered"

LiveRendered an attribute key to show that a DOM has been rendered by live.

Variables

View Source
var (
	ErrUploadNotFound     = errors.New("uploads not found")
	ErrUploadTooLarge     = errors.New("upload too large")
	ErrUploadNotAccepted  = errors.New("upload not accepted")
	ErrUploadTooManyFiles = errors.New("upload too many files")
	ErrUploadMalformed    = errors.New("upload malformed")
)
View Source
var ErrMessageMalformed = errors.New("message malformed")

ErrMessageMalformed returned when a message could not be parsed correctly.

View Source
var ErrNoEventHandler = errors.New("view missing event handler")

ErrNoEventHandler returned when a handler has no event handler for that event.

View Source
var ErrNoRenderer = errors.New("no renderer has been set on the handler")

ErrNoRenderer returned when no renderer has been set on the handler.

View Source
var ErrNoSocket = errors.New("no socket")

ErrNoSocket returned when a socket doesn't exist.

View Source
var ErrNotImplemented = errors.New("not implemented")

ErrNotImplemented returned when an interface has not been implemented correctly.

Functions

func ConsumeUploads added in v0.15.0

func ConsumeUploads(s Socket, name string, ch ConsumeHandler) []error

ConsumeUploads helper function to consume the staged uploads.

func NewID

func NewID() string

NewID returns a new ID.

func RenderSocket added in v0.13.0

func RenderSocket(ctx context.Context, e Engine, s Socket) (*html.Node, error)

RenderSocket takes the engine and current socket and renders it to html.

func Request added in v0.13.0

func Request(ctx context.Context) *http.Request

Request pulls out an initiating request from a context.

func SessionID added in v0.12.0

func SessionID(session Session) string

SessionID helper to get the sessions live ID.

func ValidateUploads added in v0.15.0

func ValidateUploads(s Socket, p Params)

ValidateUploads checks proposed uploads for errors, should be called in a validation check function.

func Writer added in v0.13.0

func Writer(ctx context.Context) http.ResponseWriter

Writer pulls out a response writer from a context.

Types

type BaseEngine added in v0.13.0

type BaseEngine struct {

	// IgnoreFaviconRequest setting to ignore requests for /favicon.ico.
	IgnoreFaviconRequest bool

	// MaxUploadSize the maximum upload size in bytes to allow. This defaults
	// too 100MB.
	MaxUploadSize int64

	// UploadStagingLocation where uploads are stored before they are consumed. This defaults
	// too the default OS temp directory.
	UploadStagingLocation string
	// contains filtered or unexported fields
}

BaseEngine handles live inner workings.

func NewBaseEngine added in v0.13.0

func NewBaseEngine(h Handler) *BaseEngine

NewBaseEngine creates a new base engine.

func (*BaseEngine) AddSocket added in v0.13.0

func (e *BaseEngine) AddSocket(sock Socket)

AddSocket add a socket to the engine.

func (*BaseEngine) Broadcast added in v0.13.0

func (e *BaseEngine) Broadcast(event string, data interface{}) error

Broadcast send a message to all sockets connected to this engine.

func (*BaseEngine) CallEvent added in v0.13.0

func (e *BaseEngine) CallEvent(ctx context.Context, t string, sock Socket, msg Event) error

CallEvent route an event to the correct handler.

func (*BaseEngine) CallParams added in v0.13.0

func (e *BaseEngine) CallParams(ctx context.Context, sock Socket, msg Event) error

CallParams on params change run the handler.

func (*BaseEngine) DeleteSocket added in v0.13.0

func (e *BaseEngine) DeleteSocket(sock Socket)

DeleteSocket remove a socket from the engine.

func (*BaseEngine) Error added in v0.13.0

func (e *BaseEngine) Error() ErrorHandler

func (*BaseEngine) GetSocket added in v0.15.0

func (e *BaseEngine) GetSocket(session Session) (Socket, error)

GetSocket get a socket from a session.

func (*BaseEngine) HandleBroadcast added in v0.13.0

func (e *BaseEngine) HandleBroadcast(f BroadcastHandler)

func (*BaseEngine) Handler added in v0.13.0

func (e *BaseEngine) Handler(hand Handler)

func (*BaseEngine) Mount added in v0.13.0

func (e *BaseEngine) Mount() MountHandler

func (*BaseEngine) Params added in v0.13.0

func (e *BaseEngine) Params() []EventHandler

func (*BaseEngine) Render added in v0.13.0

func (e *BaseEngine) Render() RenderHandler

func (*BaseEngine) Unmount added in v0.15.4

func (e *BaseEngine) Unmount() UnmountHandler

type BaseHandler added in v0.13.0

type BaseHandler struct {
	// contains filtered or unexported fields
}

BaseHandler.

func NewHandler

func NewHandler(configs ...HandlerConfig) *BaseHandler

NewHandler sets up a base handler for live.

func (*BaseHandler) HandleError added in v0.13.0

func (h *BaseHandler) HandleError(f ErrorHandler)

func (*BaseHandler) HandleEvent added in v0.13.0

func (h *BaseHandler) HandleEvent(t string, handler EventHandler)

HandleEvent handles an event that comes from the client. For example a click from `live-click="myevent"`.

func (*BaseHandler) HandleMount added in v0.13.0

func (h *BaseHandler) HandleMount(f MountHandler)

func (*BaseHandler) HandleParams added in v0.13.0

func (h *BaseHandler) HandleParams(handler EventHandler)

HandleParams handles a URL query parameter change. This is useful for handling things like pagincation, or some filtering.

func (*BaseHandler) HandleRender added in v0.13.0

func (h *BaseHandler) HandleRender(f RenderHandler)

func (*BaseHandler) HandleSelf added in v0.13.0

func (h *BaseHandler) HandleSelf(t string, handler SelfHandler)

HandleSelf handles an event that comes from the server side socket. For example calling h.Self(socket, msg) will be handled here.

func (*BaseHandler) HandleUnmount added in v0.15.4

func (h *BaseHandler) HandleUnmount(f UnmountHandler)

type BaseSocket added in v0.13.0

type BaseSocket struct {
	// contains filtered or unexported fields
}

BaseSocket describes a socket from the outside.

func NewBaseSocket added in v0.13.0

func NewBaseSocket(s Session, e Engine, connected bool) *BaseSocket

NewBaseSocket creates a new default socket.

func (*BaseSocket) AllowUploads added in v0.15.0

func (s *BaseSocket) AllowUploads(config *UploadConfig)

AllowUploads indicates that his socket should accept uploads.

func (*BaseSocket) Assign added in v0.13.0

func (s *BaseSocket) Assign(data interface{})

Assign sets data to this socket. This will happen automatically if you return data from an `EventHander`.

func (*BaseSocket) AssignUpload added in v0.15.0

func (s *BaseSocket) AssignUpload(config string, upload *Upload)

AssignUpload sets uploads to this socket.

func (*BaseSocket) Assigns added in v0.13.0

func (s *BaseSocket) Assigns() interface{}

Assigns returns the data currently assigned to this socket.

func (*BaseSocket) Broadcast added in v0.13.0

func (s *BaseSocket) Broadcast(event string, data interface{}) error

Broadcast sends an event to all sockets on this same engine.

func (*BaseSocket) ClearUpload added in v0.15.0

func (s *BaseSocket) ClearUpload(config string, upload *Upload)

ClearUpload clears a specific upload from this socket.

func (*BaseSocket) ClearUploads added in v0.15.0

func (s *BaseSocket) ClearUploads()

ClearUploads clears this sockets upload map.

func (*BaseSocket) Connected added in v0.13.0

func (s *BaseSocket) Connected() bool

Connected returns if this socket is connected via the websocket.

func (*BaseSocket) ID added in v0.13.0

func (s *BaseSocket) ID() SocketID

ID generates a unique ID for this socket.

func (*BaseSocket) LatestRender added in v0.13.0

func (s *BaseSocket) LatestRender() *html.Node

LastRender returns the last render result of this socket.

func (*BaseSocket) Messages added in v0.13.0

func (s *BaseSocket) Messages() chan Event

Messages returns a channel of event messages sent and received by this socket.

func (*BaseSocket) PatchURL added in v0.13.0

func (s *BaseSocket) PatchURL(values url.Values)

PatchURL sends an event to the client to update the query params in the URL.

func (*BaseSocket) Redirect added in v0.13.0

func (s *BaseSocket) Redirect(u *url.URL)

Redirect sends a redirect event to the client. This will trigger the browser to redirect to a URL.

func (*BaseSocket) Self added in v0.13.0

func (s *BaseSocket) Self(ctx context.Context, event string, data interface{}) error

Self sends an event to this socket itself. Will be handled in the handlers HandleSelf function.

func (*BaseSocket) Send added in v0.13.0

func (s *BaseSocket) Send(event string, data interface{}, options ...EventConfig) error

Send an event to this socket's client, to be handled there.

func (*BaseSocket) Session added in v0.13.0

func (s *BaseSocket) Session() Session

Session returns the session of this socket.

func (*BaseSocket) UpdateRender added in v0.13.0

func (s *BaseSocket) UpdateRender(render *html.Node)

UpdateRender replaces the last render result of this socket.

func (*BaseSocket) UploadConfigs added in v0.15.0

func (s *BaseSocket) UploadConfigs() []*UploadConfig

UploadConfigs returns the configs for this socket.

func (*BaseSocket) Uploads added in v0.15.0

func (s *BaseSocket) Uploads() UploadContext

Uploads returns the sockets uploads.

type BroadcastHandler added in v0.13.0

type BroadcastHandler func(ctx context.Context, e Engine, msg Event)

BroadcastHandler a way for processes to communicate.

type ConsumeHandler added in v0.15.0

type ConsumeHandler func(u *Upload) error

ConsumeHandler callback type when uploads are consumed.

type CookieStore added in v0.2.0

type CookieStore struct {
	Store *sessions.CookieStore
	// contains filtered or unexported fields
}

CookieStore a `gorilla/sessions` based cookie store.

func NewCookieStore added in v0.2.0

func NewCookieStore(sessionName string, keyPairs ...[]byte) *CookieStore

NewCookieStore create a new `gorilla/sessions` based cookie store.

func (CookieStore) Clear added in v0.12.3

Clear a session.

func (CookieStore) Get added in v0.2.0

func (c CookieStore) Get(r *http.Request) (Session, error)

Get get a session.

func (CookieStore) Save added in v0.2.0

func (c CookieStore) Save(w http.ResponseWriter, r *http.Request, session Session) error

Save a session.

type Engine added in v0.13.0

type Engine interface {
	// Handler takes a handler to configure the lifecycle.
	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.
	Mount() MountHandler
	// UnmountHandler the func that is called by a handler to report that a connection
	// is closed. This is called on websocket close. Can be used to track number of
	// connected users.
	Unmount() UnmountHandler
	// Params called to handle any incoming paramters after mount.
	Params() []EventHandler
	// Render is called to generate the HTML of a Socket. It is defined
	// by default and will render any template provided.
	Render() RenderHandler
	// Error is called when an error occurs during the mount and render
	// stages of the handler lifecycle.
	Error() ErrorHandler
	// AddSocket add a socket to the engine.
	AddSocket(sock Socket)
	// GetSocket from a session get an already connected
	// socket.
	GetSocket(session Session) (Socket, error)
	// DeleteSocket remove a socket from the engine.
	DeleteSocket(sock Socket)
	// CallParams on params change run the handlers.
	CallParams(ctx context.Context, sock Socket, msg Event) error
	// CallEvent route an event to the correct handler.
	CallEvent(ctx context.Context, t string, sock Socket, msg Event) error
	// 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
	// contains filtered or unexported methods
}

Engine methods.

type EngineConfig added in v0.15.0

type EngineConfig func(e Engine) error

EngineConfig applies configuration to an engine.

func WithMaxUploadSize added in v0.15.0

func WithMaxUploadSize(size int64) EngineConfig

WithMaxUploadSize set the handler engine to have a maximum upload size.

func WithUploadStagingLocation added in v0.15.0

func WithUploadStagingLocation(stagingLocation string) EngineConfig

WithUploadStagingLocation set the handler engine with a specific upload staging location.

func WithWebsocketAcceptOptions added in v0.15.2

func WithWebsocketAcceptOptions(options *websocket.AcceptOptions) EngineConfig

WithWebsocketAcceptOptions apply websocket accept options to the HTTP engine.

type ErrorEvent added in v0.13.0

type ErrorEvent struct {
	Source Event  `json:"source"`
	Err    string `json:"err"`
}

type ErrorHandler added in v0.3.0

type ErrorHandler func(ctx context.Context, err error)

ErrorHandler if an error occurs during the mount and render cycle a handler of this type will be called.

type Event

type Event struct {
	T        string          `json:"t"`
	ID       int             `json:"i,omitempty"`
	Data     json.RawMessage `json:"d,omitempty"`
	SelfData interface{}     `json:"s,omitempty"`
}

Event messages that are sent and received by the socket.

func (Event) Params

func (e Event) Params() (Params, error)

Params extract params from inbound message.

type EventConfig added in v0.10.0

type EventConfig func(e *Event) error

EventConfig configures an event.

func WithID added in v0.10.0

func WithID(ID int) EventConfig

WithID sets an ID on an event.

type EventHandler

type EventHandler func(context.Context, Socket, Params) (interface{}, error)

EventHandler a function to handle events, returns the data that should be set to the socket after handling.

type Handler

type Handler interface {
	// HandleMount handles initial setup on first request, and then later when
	// the socket first connets.
	HandleMount(handler MountHandler)
	// HandleUnmount used to track webcocket disconnections.
	HandleUnmount(handler UnmountHandler)
	// 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)
	// contains filtered or unexported methods
}

Handler methods.

type HandlerConfig

type HandlerConfig func(h Handler) error

HandlerConfig applies config to a handler.

func WithTemplateRenderer added in v0.6.0

func WithTemplateRenderer(t *template.Template) HandlerConfig

WithTemplateRenderer set the handler to use an `html/template` renderer.

type HttpEngine added in v0.13.0

type HttpEngine struct {
	*BaseEngine
	// contains filtered or unexported fields
}

HttpEngine serves live for net/http.

func NewHttpHandler added in v0.13.0

func NewHttpHandler(store HttpSessionStore, handler Handler, configs ...EngineConfig) *HttpEngine

NewHttpHandler returns the net/http handler for live.

func (*HttpEngine) ServeHTTP added in v0.13.0

func (h *HttpEngine) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP serves this handler.

type HttpSessionStore added in v0.13.0

type HttpSessionStore interface {
	Get(*http.Request) (Session, error)
	Save(http.ResponseWriter, *http.Request, Session) error
	Clear(http.ResponseWriter, *http.Request) error
}

HttpSessionStore handles storing and retrieving sessions.

type HttpSocket added in v0.13.0

type HttpSocket struct {
	*BaseSocket
}

func NewHttpSocket added in v0.13.0

func NewHttpSocket(s Session, e Engine, connected bool) *HttpSocket

NewHttpSocket creates a new http socket.

type Javascript

type Javascript struct {
}

Javascript handles serving the client side portion of live.

func (Javascript) ServeHTTP

func (j Javascript) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP.

type JavascriptMap

type JavascriptMap struct {
}

JavascriptMap handles serving source map.

func (JavascriptMap) ServeHTTP

func (j JavascriptMap) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP.

type LocalTransport added in v0.10.0

type LocalTransport struct {
	// contains filtered or unexported fields
}

LocalTransport a pubsub transport that allows handlers to communicate locally.

func NewLocalTransport added in v0.10.0

func NewLocalTransport() *LocalTransport

NewLocalTransport create a new LocalTransport.

func (*LocalTransport) Listen added in v0.10.0

func (l *LocalTransport) Listen(ctx context.Context, p *PubSub) error

Listen listen for new published messages.

func (*LocalTransport) Publish added in v0.10.0

func (l *LocalTransport) Publish(ctx context.Context, topic string, msg Event) error

Publish send a message to all handlers subscribed to a topic.

type MountHandler

type MountHandler func(ctx context.Context, c Socket) (interface{}, 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 Params added in v0.10.0

type Params map[string]interface{}

Params event params.

func NewParamsFromRequest added in v0.10.0

func NewParamsFromRequest(r *http.Request) Params

NewParamsFromRequest helper to generate Params from an http request.

func (Params) Checkbox added in v0.10.0

func (p Params) Checkbox(key string) bool

Checkbox helper to return a boolean from params referring to a checkbox input.

func (Params) Float32 added in v0.10.0

func (p Params) Float32(key string) float32

Float32 helper to return a float32 from the params.

func (Params) Int added in v0.10.0

func (p Params) Int(key string) int

Int helper to return and int from the params.

func (Params) String added in v0.10.0

func (p Params) String(key string) string

String helper to get a string from the params.

type Patch

type Patch struct {
	Anchor string
	Action PatchAction
	HTML   string
}

Patch a location in the frontend dom.

func Diff

func Diff(current, proposed *html.Node) ([]Patch, error)

Diff compare two node states and return patches.

func (Patch) String added in v0.6.2

func (p Patch) String() string

type PatchAction

type PatchAction uint32

PatchAction available actions to take by a patch.

const (
	Noop PatchAction = iota
	Replace
	Append
	Prepend
)

Actions available.

type PubSub added in v0.10.0

type PubSub struct {
	// contains filtered or unexported fields
}

PubSub handles communication between handlers. Depending on the given transport this could be between handlers in an application, or across nodes in a cluster.

func NewPubSub added in v0.10.0

func NewPubSub(ctx context.Context, t PubSubTransport) *PubSub

NewPubSub creates a new PubSub handler.

func (*PubSub) Publish added in v0.10.0

func (p *PubSub) Publish(ctx context.Context, topic string, msg Event) error

Publish send a message on a topic.

func (*PubSub) Recieve added in v0.10.0

func (p *PubSub) Recieve(topic string, msg Event)

Recieve a message from the transport.

func (*PubSub) Subscribe added in v0.10.0

func (p *PubSub) Subscribe(topic string, h Engine)

Subscribe adds a handler to a PubSub topic.

type PubSubTransport added in v0.10.0

type PubSubTransport interface {
	// Publish a message onto the given topic.
	Publish(ctx context.Context, topic string, msg Event) error
	// Listen will be called in a go routine so should be written to
	// block.
	Listen(ctx context.Context, p *PubSub) error
}

PubSubTransport is how the messages should be sent to the listeners.

type RenderContext added in v0.15.0

type RenderContext struct {
	Socket  Socket
	Uploads UploadContext
	Assigns interface{}
}

RenderContext contains the sockets current data for rendering.

type RenderHandler

type RenderHandler func(ctx context.Context, rc *RenderContext) (io.Reader, error)

RenderHandler the func that is called to render the current state of the data for the socket.

type SelfHandler added in v0.14.0

type SelfHandler func(context.Context, Socket, interface{}) (interface{}, error)

SelfHandler a function to handle self events, returns the data that should be set to the socket after handling.

type Session

type Session map[string]interface{}

Session persisted over page loads.

func NewSession

func NewSession() Session

NewSession create a new session.

type Socket

type Socket interface {
	// ID return an ID for this socket.
	ID() SocketID
	// Assigns returns the data currently assigned to this
	// socket.
	Assigns() interface{}
	// Assign set data to this socket. This will happen automatically
	// if you return data from an `EventHander`.
	Assign(data interface{})
	// Connected returns true if this socket is connected via the websocket.
	Connected() bool
	// Self send an event to this socket itself. Will be handled in the
	// handlers HandleSelf function.
	Self(ctx context.Context, event string, data interface{}) error
	// Broadcast send an event to all sockets on this same engine.
	Broadcast(event string, data interface{}) error
	// Send an event to this socket's client, to be handled there.
	Send(event string, data interface{}, options ...EventConfig) error
	// PatchURL sends an event to the client to update the
	// query params in the URL.
	PatchURL(values url.Values)
	// Redirect sends a redirect event to the client. This will trigger the browser to
	// redirect to a URL.
	Redirect(u *url.URL)
	// AllowUploads indicates that his socket should allow uploads.
	AllowUploads(config *UploadConfig)
	// UploadConfigs return the list of configures uploads for this socket.
	UploadConfigs() []*UploadConfig
	// Uploads returns uploads to this socket.
	Uploads() UploadContext
	// AssignUploads set uploads to a upload config on this socket.
	AssignUpload(config string, upload *Upload)
	// ClearUploads clears the sockets upload map.
	ClearUploads()
	// ClearUpload clear a specific upload.
	ClearUpload(config string, upload *Upload)
	// LatestRender return the latest render that this socket generated.
	LatestRender() *html.Node
	// UpdateRender set the latest render.
	UpdateRender(render *html.Node)
	// Session returns the sockets session.
	Session() Session
	// Messages returns the channel of events on this socket.
	Messages() chan Event
}

Socket describes a connected user, and the state that they are in.

type SocketID added in v0.13.0

type SocketID string

type TransportMessage added in v0.10.0

type TransportMessage struct {
	Topic string
	Msg   Event
}

TransportMessage a useful container to send live events.

type UnmountHandler added in v0.15.4

type UnmountHandler func(c Socket) error

UnmountHandler the func that is called by a handler to report that a connection is closed. This is called on websocket close. Can be used to track number of connected users.

type Upload added in v0.15.0

type Upload struct {
	Name         string
	Size         int64
	Type         string
	LastModified string
	Errors       []error
	Progress     float32
	// contains filtered or unexported fields
}

Upload describes an upload from the client.

func (Upload) File added in v0.15.0

func (u Upload) File() (*os.File, error)

File gets an open file reader.

type UploadConfig added in v0.15.0

type UploadConfig struct {
	// The form input name to accept from.
	Name string
	// The max number of files to allow to be uploaded.
	MaxFiles int
	// The maximum size of all files to accept.
	MaxSize int64
	// Which type of files to accept.
	Accept []string
}

UploadConfig describes an upload to accept on the socket.

type UploadContext added in v0.15.0

type UploadContext map[string][]*Upload

UploadContext the context which we render to templates.

func (UploadContext) HasErrors added in v0.15.0

func (u UploadContext) HasErrors() bool

HasErrors does the upload context have any errors.

type UploadError added in v0.15.0

type UploadError struct {
	// contains filtered or unexported fields
}

func (*UploadError) Error added in v0.15.0

func (u *UploadError) Error() string

func (*UploadError) Unwrap added in v0.15.0

func (u *UploadError) Unwrap() error

type UploadProgress added in v0.15.0

type UploadProgress struct {
	Upload *Upload
	Engine Engine
	Socket Socket
}

UploadProgress tracks uploads and updates an upload object with progress.

func (*UploadProgress) Write added in v0.15.0

func (u *UploadProgress) Write(p []byte) (n int, err error)

Write interface to track progress of an upload.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL