• Stars
    star
    110
  • Rank 316,713 (Top 7 %)
  • Language
    Go
  • Created over 5 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🌶️ Create reactive frontends without ever writing frontend code.

🌶️ pepper

Create reactive frontends without ever writing frontend code.

How Does It Work?

pepper runs a HTTP server that returns a tiny empty HTML page with just a few lines of inline javascript. Immediately after the initial page loads it will connect through a websocket.

All event triggered on the browser will be sent through the websocket where state changes and rerendering occurs. The result is passed back to the websocket to update the UI.

At the moment it returns the whole rendered component. However, this could be optimized in the future to only return the differences.

What Should/Shouldn't I Use It For?

pepper requires a constant connection to the server (for the websocket) so it wouldn't work for anything that must function offline, or used in cases where the internet is flaky.

I imagine some good use cases for pepper would be:

  1. Showing real time data. Streaming metrics, graphs, logs, dashboards, etc.
  2. Apps that rely on a persistent connection. Such as chat clients, timed interactive exams, etc.
  3. Apps that would benefit from persistent state. The entire state can be saved or restored into a serialized format like JSON. Great for forms or surveys with many questions/steps.
  4. Prototyping a frontend app. It's super easy to get up and running and iterate changes without setting up a complex environment, build tools and dependencies.

Handling Offline and Reconnecting

When creating the server you may configure how you want disconnects to be handled. For example:

server := pepper.NewServer()
server.OfflineAction = pepper.OfflineActionDisableForms

By default clients will try to reconnect every second. This can be changed with server.ReconnectInterval.

Important: When reconnecting the server treats the new request as a new client, so all state on the page will be lost.

Testing

Unit Testing

The peppertest package provides tools to make unit testing easier.

RenderToDocument renders then parses the component into a *Document from the github.com/PuerkitoBio/goquery package:

import (
	"github.com/elliotchance/pepper/peppertest"
	"github.com/stretchr/testify/require"
	"testing"
)

func TestPeople_Add(t *testing.T) {
	c := &People{
		Names: []string{"Jack", "Jill"},
	}

	c.Name = "Bob"
	c.Add()

	doc, err := peppertest.RenderToDocument(c)
	require.NoError(t, err)

	rows := doc.Find("tr")
	require.Equal(t, 3, rows.Length())
	require.Contains(t, rows.Eq(0).Text(), "Jack")
	require.Contains(t, rows.Eq(1).Text(), "Jill")
	require.Contains(t, rows.Eq(2).Text(), "Bob")
}

Each of the examples in the examples/ directory include unit tests.

Examples

#1: A Simple Counter

package main

import "github.com/elliotchance/pepper"

type Counter struct {
	Number int
}

func (c *Counter) Render() (string, error) {
	return `
		Counter: {{ .Number }}
		<button @click="AddOne">+</button>
	`, nil
}

func (c *Counter) AddOne() {
	c.Number++
}

func main() {
	panic(pepper.NewServer().Start(func(_ *pepper.Connection) pepper.Component {
		return &Counter{}
	}))
}
  • The Render method returns a html/template syntax, or an error.
  • @click will trigger AddOne to be called when the button is clicked.
  • Any event triggered from the browser will cause the component to rerender automatically.

Try it now:

go get -u github.com/elliotchance/pepper/examples/ex01_counter
ex01_counter

Then open: http://localhost:8080/

#2: Forms

package main

import (
	"github.com/elliotchance/pepper"
	"strconv"
)

type People struct {
	Names []string
	Name  string
}

func (c *People) Render() (string, error) {
	return `
		<table>
			{{ range $i, $name := .Names }}
				<tr><td>
					{{ $name }}
					<button key="{{ $i }}" @click="Delete">Delete</button>
				</td></tr>
			{{ end }}
		</table>
		Add name: <input type="text" @value="Name">
		<button @click="Add">Add</button>
	`, nil
}

func (c *People) Delete(key string) {
	index, _ := strconv.Atoi(key)
	c.Names = append(c.Names[:index], c.Names[index+1:]...)
}

func (c *People) Add() {
	c.Names = append(c.Names, c.Name)
	c.Name = ""
}

func main() {
	panic(pepper.NewServer().Start(func(_ *pepper.Connection) pepper.Component {
		return &People{
			Names: []string{"Jack", "Jill"},
		}
	}))
}
  • Any html/template syntax will work, including loops with {{ range }}.
  • @value will cause the Name property to be bound with the text box in both directions.
  • Since there are multiple "Delete" buttons (one for each person), you should specify a key. The key is passed as the first argument to the Delete function.

Try it now:

go get -u github.com/elliotchance/pepper/examples/ex02_form
ex02_form

Then open: http://localhost:8080/

#3: Nested Components

type Counters struct {
	Counters []*Counter
}

func (c *Counters) Render() (string, error) {
	return `
		<table>
			{{ range .Counters }}
				<tr><td>
					{{ render . }}
				</td></tr>
			{{ end }}
			<tr><td>
				Total: {{ call .Total }}
			</td></tr>
		</table>
	`, nil
}

func (c *Counters) Total() int {
	total := 0
	for _, counter := range c.Counters {
		total += counter.Number
	}

	return total
}

func main() {
	panic(pepper.NewServer().Start(func(_ *pepper.Connection) pepper.Component {
		return &Counters{
			Counters: []*Counter{
				{}, {}, {},
			},
		}
	}))
}
  • This example uses three Counter components (from Example #1) and includes a live total.
  • Components can be nested with the render function. The nested components do not need to be modified in any way.
  • Invoke methods with the call function.

Try it now:

go get -u github.com/elliotchance/pepper/examples/ex03_nested
ex03_nested

Then open: http://localhost:8080/

#4: Ticker

package main

import (
	"github.com/elliotchance/pepper"
	"time"
)

type Clock struct{}

func (c *Clock) Render() (string, error) {
	return `
		The time now is {{ call .Now }}.
	`, nil
}

func (c *Clock) Now() string {
	return time.Now().Format(time.RFC1123)
}

func main() {
	panic(pepper.NewServer().Start(func(conn *pepper.Connection) pepper.Component {
		go func() {
			for range time.NewTicker(time.Second).C {
				conn.Update()
			}
		}()

		return &Clock{}
	}))
}
  • The component is updated once per second so the client sees the active time.

Try it now:

go get -u github.com/elliotchance/pepper/examples/ex04_ticker
ex04_ticker

Then open: http://localhost:8080/

More Repositories

1

c2go

⚖️ A tool for transpiling C to Go.
Go
1,954
star
2

pie

🍕 Enjoy a slice! A utility library for dealing with slices and maps that focuses on type safety and performance.
Go
1,655
star
3

orderedmap

🔃 An ordered map in Go with amortized O(1) for Set, Get, Delete and Len.
Go
614
star
4

sshtunnel

🚇 Ultra simple SSH tunnelling for Go programs.
Go
253
star
5

vsql

✌️ Single-file or PostgreSQL-server compatible transactional SQL database written in pure V.
V
236
star
6

dingo

🐺 Easy, fast and type-safe dependency injection for Go.
Go
185
star
7

redismock

🕋 Mocking Redis in unit tests in Go.
Go
139
star
8

tf

✔️ tf is a microframework for parameterized testing of functions and HTTP in Go.
Go
136
star
9

phpserialize

📑 PHP serialize() and unserialize() for Go
Go
108
star
10

mbzdb

🎵 Port of the MusicBrainz database to run on other RDBMSs with replication (previously named MB_MySQL.)
Perl
88
star
11

gedcom

👪 A Go library and CLI tools for encoding, decoding, traversing, merging, comparing, querying and publishing GEDCOM files.
Go
72
star
12

ghost

👻 Locate and fix overly complex lines of code in Go.
Go
53
star
13

concise

✅ Concise is test framework for using plain English and minimal code, built on PHPUnit.
PHP
47
star
14

CollectionFactory

🏭 Translation between native collections in Objective-C and serialized formats like JSON.
Objective-C
41
star
15

sqltest

📝 A comprehensive suite of SQL tests for testing the conformance of databases.
Python
39
star
16

bento

🍱 bento is an English-based automation language designed to be used by non-technical people.
Go
32
star
17

redis-usage

👁️ A non-blocking way to count the number of keys or size of Redis key prefixes
Go
30
star
18

vlang-sublime

Sublime Text support for the V programming language
Python
29
star
19

reflect

🪞 Runtime reflection for V (vlang)
V
27
star
20

ok

🆗 - a strongly-duck-typed language.
Go
17
star
21

mocksqs

📤 In-memory implementation of SQS ideal for unit testing.
Go
12
star
22

sqlite3x

100% compatible sqlite3 fork with more features
C
10
star
23

gedcompare

Compare GEDCOM files
Python
9
star
24

go-named-params

Named parameters for Go functions
Go
9
star
25

testify-stats

🔢 testify: print test and assertions statistics at the end of the test suite
Go
7
star
26

independentreserve

💸 PHP API for independentreserve.com
PHP
6
star
27

tesseract

🔳 tesseract is a SQL object database with Redis as the backend, think of it like a document store that you run SQL statements against.
Python
6
star
28

GoogleMusicClient

🎶 Google Music Client in Objective-C
Objective-C
6
star
29

tui

🎨 Simple Go library for building complex text user interfaces
Go
5
star
30

wikitranslate

Easier translation of Wikipedia pages with CAT tools.
Go
5
star
31

multiselector

Multi Selector for Paw
JavaScript
4
star
32

iterator

Iterator builders for PHP
PHP
4
star
33

postgresql-partitioning

🖖 Automatic tools for managing partitions
4
star
34

switch-check

Validate switch statements contain all enum values.
Go
4
star
35

jsonrpc

💬 Simple JSON-RPC server for Go
Go
3
star
36

vscode-ok

Language highlighting for the ok programming language in VSCode
2
star
37

tracklist-editor

A tool for editing track lists
JavaScript
2
star
38

sql2mql

☕ Pure coffeescript SQL to MongoDB statement parser.
CoffeeScript
2
star
39

eagle

🦅 Eagle is a highly parallel column-oriented embedded SQL database engine.
C
2
star
40

chartbrainz

The unofficial way to view charts from MusicBrainz.
Vue
2
star
41

Z

🇿Java to native C compiler.
Java
2
star
42

Hoard

🐿️ A PSR-compliant caching library for holding objects in nested pools with scripting ability.
PHP
2
star
43

Sentinel

Non-blocking Java web server.
Java
2
star
44

dandy

🔬 The handy, dandy test generation tool for Go.
Go
2
star
45

play.getok.dev

🕹️ play.getok.dev
HTML
1
star
46

Pluralizer

Objective-C Library/Cocoa Pod for Simple String Pluralization
Objective-C
1
star
47

maven

⚚ maven programming language
C++
1
star
48

csv

Reading and writing CSV files in OK.
1
star
49

delta

🔺 The fastest scripting language on the planet based on PHP (pre-alpha)
C
1
star
50

construe

Language conversion and maintaining tool.
CoffeeScript
1
star
51

toy

🧸 A toy language
JavaScript
1
star
52

EagleDB

🐦 Concept column-oriented MVCC SQL daemon.
Java
1
star
53

intuit-quickbooks

The PHP SDK for QuickBooks v3 is set of PHP classes that make it easier to call QuickBooks APIs.
PHP
1
star
54

independentreserve-python

Python API for independentreserve.com
1
star
55

codekata.io

Online and realtime Kata for TDD
PHP
1
star
56

rateyourmusic-todo

Discussion and queue for entities that need editing help
1
star