This commit is contained in:
Mark Nadal 2017-03-11 07:04:40 -08:00
commit 209bf2a214
18 changed files with 492 additions and 12 deletions

View File

@ -57,12 +57,13 @@ gun.get('mark').on(function(data, key){
- Chat with us: https://gitter.im/amark/gun .
## Documentation
- [**A must read crash course on using GUN**](https://github.com/amark/gun/wiki/graphs).
- [API reference](https://github.com/amark/gun/wiki/API).
- [**A must read crash course on using GUN**](https://github.com/amark/gun/wiki/graphs).
- [Introductory Guide](https://github.com/amark/gun/wiki/getting-started-(v0.3.x)).
- [What tradeoffs does GUN make](https://github.com/amark/gun/wiki/CAP-Theorem)? It is an AP system, so banking apps aren't a good fit.
- [How the Conflict Resolution algorithm works](http://gun.js.org/distributed/matters.html).
- Check out and add example code [snippets](https://github.com/amark/gun/wiki/snippets-(v0.3.x)) —including micro-modules— to address specific situations.
- Loose [roadmap](https://github.com/amark/gun/wiki/roadmap).
## Deploy
@ -123,7 +124,7 @@ Then visit [http://localhost:8080](http://localhost:8080) in your browser.
### Videos
- [Fault tolerance](https://www.youtube.com/watch?v=-i-11T5ZI9o&feature=youtu.be) (01:01)
- [Saving relational or document based data](https://www.youtube.com/watch?v=cOO6wz1rZVY&feature=youtu.be) (06:59)
- [Everything you want to know about GUN](https://youtu.be/qJNDplwJ8aQ) (57:50) 2x speed recommended.
- [Everything you want to know about GUN](https://youtu.be/qJNDplwJ8aQ) (57:50) 1.25x speed recommended.
- [GUN's YouTube channel](https://www.youtube.com/channel/UCQAtpf-zi9Pp4__2nToOM8g/playlists) also has videos.
### <a name="gun-projects"></a>Projects

View File

@ -24,12 +24,12 @@ var server = require('http').createServer(function(req, res){
// gun.wsp(server);
var ws = require( 'ws' ); // default websocket provider gun used...
var WebSocketServer = ws.server;
var WebSocketServer = ws.Server;
var wss = new WebSocketServer( {
server: server, // 'ws' npm
autoAcceptConnections : false // want to handle the request (websocket npm?)
}
});
wss.on('connection',acceptConnection )
@ -42,13 +42,14 @@ Gun.on('out', function(msg){
function acceptConnection( connection ) {
// connection.upgradeReq.headers['sec-websocket-protocol'] === (if present) protocol requested by client
// connection.upgradeReq.url === url request
console.log( "connect?", req.upgradeReq.headers, req.upgradeReq.url )
console.log( "connect?", connection.upgradeReq.headers, connection.upgradeReq.url )
gunPeers.push( connection );
connection.on( 'error',function(error){console.log( "WebSocket Error:", error } );
connection.on( 'message',function(msg){gun.on('in',JSON.parse( msg.utf8Data).body)})
connection.on( 'error',function(error){console.log( "WebSocket Error:", error) } );
connection.on( 'message',function(msg){gun.on('in',JSON.parse( msg).body)})
connection.on( 'close', function(reason,desc){
// gunpeers gone.
var i = peers.findIndex( function(p){return p===connection} );
var i = gunPeers.findIndex( function(p){return p===connection} );
if( i >= 0 )
gunPeers.splice( i, 1 );

175
examples/react.html Normal file
View File

@ -0,0 +1,175 @@
<!doctype html>
<html>
<head>
<title>gun - react examples</title>
<style>
html, body { font-size: 14pt; padding: 10px 2.5%;}
.hide { display: none; }
form .who { width: 10%; }
form .what { width: 80%; }
ul { list-style: none; padding: 0; }
ul .when {color: #555; font-size: 12pt; float: right; display: none; }
li:hover .when {display: inline;}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.23.1/babel.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.js"></script>
<script src="../gun.js"></script>
<script type="text/babel" data-presets="react,latest,stage-0">
const { Component } = React
const { render } = ReactDOM
const todos = Gun().get('todos')
const formatTodos = todos => Object.keys(todos)
.map(key => ({ key, val: todos[key] }))
.filter(t => Boolean(t.val) && t.key !== '_')
class Todos extends Component {
constructor() {
super()
this.state = {newTodo: '', todos: []}
}
componentWillMount() {
todos.on(todos => this.setState({
todos: formatTodos(todos)
}))
}
add = _ => {
todos.path(Gun.text.random()).put(this.state.newTodo)
this.setState({newTodo: ''})
}
del = key => gun.path(key).put(null)
handleChange = e => this.setState({ newTodo: e.target.value})
render() {
return <div>
<input value={this.state.newTodo} onChange={this.handleChange} />
<button onClick={this.add}>Add</button>
<br />
<ul>
{this.state.todos.map(todo => <li key={todo.key} onClick={_=>this.del(todo.key)}>{todo.val}</li>)}
</ul>
</div>
}
}
const json = Gun().get('json')
const formatJson = json =>
Object.keys(json)
.map(key => ({ key, val: json[key]}))
.filter(el => el.key !== '_')
class Json extends Component {
constructor() {
super()
this.state = { newField: '', json: [] }
}
componentWillMount() {
json.on(json => this.setState({ json: formatJson(json) }))
}
edit = key => e => {
e.preventDefault()
json.path(key).put(e.target.value)
}
add = e => {
e.preventDefault()
json.path(this.state.newField).put('value')
this.setState({newField: ''})
}
render() {
return <div>
<ul>
{this.state.json.map(({ key, val }) =>
<li key={key}><b>{key}:</b> <input value={val} onChange={this.edit(key)} /></li>
)}
</ul>
<form onSubmit={this.add}>
<input value={this.state.newField} onChange={e => this.setState({ newField: e.target.value})} />
<button onClick={this.add}>Add Field</button>
</form>
</div>
}
}
const chat = Gun().get('chat')
const formatMsgs = msgs => Object.keys(msgs)
.map(key => ({ key, ...msgs[key] }))
.filter(m => Boolean(m.when) && m.key !== '_')
.sort((a, b) => a.when - b.when)
.map(m => ((m.whenFmt = new Date(m.when).toLocaleString().toLowerCase()), m))
class Chat extends Component {
constructor() {
super()
this.state = {
newMsg: '',
name: (document.cookie.match(/alias\=(.*?)(\&|$|\;)/i)||[])[1]||'',
msgs: {},
}
}
componentWillMount() {
const tmpState = {}
chat.map().val((msg, key) => {
tmpState[key] = msg
this.setState({msgs: Object.assign({}, this.state.msgs, tmpState)})
})
}
send = e => {
e.preventDefault()
const who = this.state.name || 'user' + Gun.text.random(6)
this.setState({name: who})
document.cookie = ('alias=' + who)
const when = Gun.time.is()
const key = `${when}_${Gun.text.random(4)}`
chat.path(key).put({
who,
when,
what: this.state.newMsg,
})
this.setState({newMsg: ''})
}
render() {
const msgs = formatMsgs(this.state.msgs)
return <div>
<ul>
{msgs.map(msg =>
<li key={msg.key}><b>{msg.who}:</b> {msg.what}<span className="when">{msg.whenFmt}</span></li>
)}
</ul>
<form onSubmit={this.send}>
<input value={this.state.name} className="who" onChange={e => this.setState({ name: e.target.value})} />
<input value={this.state.newMsg} className="what" onChange={e => this.setState({ newMsg: e.target.value})} />
<button onClick={this.send}>Send</button>
</form>
</div>
}
}
const App = _ =>
<div>
<h1>React Examples</h1>
<h2>Todo</h2>
<Todos />
<br />
<hr />
<h2>Chat</h2>
<Chat />
<br />
<hr />
<h2>Json</h2>
<Json />
</div>
render(<App />, document.getElementById('app'))
</script>
</body>

11
examples/react/.babelrc Normal file
View File

@ -0,0 +1,11 @@
{
"presets": [
"react",
"env",
"stage-0"
],
"plugins": [
"transform-decorators-legacy",
"transform-eval"
]
}

3
examples/react/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
data.json
node_modules
npm-debug.log

8
examples/react/README.md Normal file
View File

@ -0,0 +1,8 @@
# React Gun Examples
These are the react examples for gun. They are separate from the other examples, because they require a lot of additional npm modules.
To run these modules, change to this directory, and run `npm install` and then `npm start` to start the server. Then navigate to `localhost:3000` to view the examples.
![examples](https://i.imgur.com/ZXOHWNN.gif)

23
examples/react/app.js Normal file
View File

@ -0,0 +1,23 @@
import React, { Component } from 'react'
import { render } from 'react-dom'
import Todos from './todos'
import Chat from './chat'
import Json from './json'
const App = _ =>
<div>
<h1>React Examples</h1>
<h2>Todo</h2>
<Todos />
<br />
<hr />
<h2>Chat</h2>
<Chat />
<br />
<hr />
<h2>Json</h2>
<Json />
</div>
render(<App />, document.getElementById('app'))

57
examples/react/chat.js Normal file
View File

@ -0,0 +1,57 @@
import React, { Component } from 'react'
import Gun from '../../gun'
const gun = Gun().get('chat')
const formatMsgs = msgs => Object.keys(msgs)
.map(key => ({ key, ...msgs[key] }))
.filter(m => Boolean(m.when) && m.key !== '_')
.sort((a, b) => a.when - b.when)
.map(m => ((m.whenFmt = new Date(m.when).toLocaleString().toLowerCase()), m))
export default class Chat extends Component {
constructor() {
super()
this.state = {
newMsg: '',
name: (document.cookie.match(/alias\=(.*?)(\&|$|\;)/i)||[])[1]||'',
msgs: {},
}
}
componentWillMount() {
const tmpState = {}
gun.map().val((msg, key) => {
tmpState[key] = msg
this.setState({msgs: Object.assign({}, this.state.msgs, tmpState)})
})
}
send = e => {
e.preventDefault()
const who = this.state.name || 'user' + Gun.text.random(6)
this.setState({name: who})
document.cookie = ('alias=' + who)
const when = Gun.time.is()
const key = `${when}_${Gun.text.random(4)}`
gun.path(key).put({
who,
when,
what: this.state.newMsg,
})
this.setState({newMsg: ''})
}
render() {
const msgs = formatMsgs(this.state.msgs)
return <div>
<ul>
{msgs.map(msg =>
<li key={msg.key}><b>{msg.who}:</b> {msg.what}<span className="when">{msg.whenFmt}</span></li>
)}
</ul>
<form onSubmit={this.send}>
<input value={this.state.name} className="who" onChange={e => this.setState({ name: e.target.value})} />
<input value={this.state.newMsg} className="what" onChange={e => this.setState({ newMsg: e.target.value})} />
<button onClick={this.send}>Send</button>
</form>
</div>
}
}

12
examples/react/index.html Normal file
View File

@ -0,0 +1,12 @@
<!doctype html>
<html>
<head>
<title>Gun - React Examples</title>
<meta charset="utf-8" />
<link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet">
</head>
<body>
<div id="app"></div>
</body>
</html>

45
examples/react/json.js Normal file
View File

@ -0,0 +1,45 @@
import React, { Component } from 'react'
import Gun from '../../gun'
const gun = Gun().get('json')
const formatJson = json =>
Object.keys(json)
.map(key => ({ key, val: json[key]}))
.filter(el => el.key !== '_')
export default class Json extends Component {
constructor() {
super()
this.state = { newField: '', json: [] }
}
componentWillMount() {
gun.on(json => this.setState({ json: formatJson(json) }))
}
edit = key => e => {
e.preventDefault()
gun.path(key).put(e.target.value)
}
add = e => {
e.preventDefault()
gun.path(this.state.newField).put('value')
this.setState({newField: ''})
}
render() {
return <div>
<ul>
{this.state.json.map(({ key, val }) =>
<li key={key}><b>{key}:</b> <input value={val} onChange={this.edit(key)} /></li>
)}
</ul>
<form onSubmit={this.add}>
<input value={this.state.newField} onChange={e => this.setState({ newField: e.target.value})} />
<button onClick={this.add}>Add Field</button>
</form>
</div>
}
}

View File

@ -0,0 +1,38 @@
{
"name": "gun-react-examples",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "node start",
"dev": "nodemon --config .nodemon.json start"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel": "^6.5.2",
"babel-core": "^6.21.0",
"babel-loader": "^6.2.10",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-eval": "^6.8.0",
"babel-preset-env": "^1.2.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-0": "^6.16.0",
"css-loader": "^0.26.1",
"file-loader": "^0.9.0",
"html-webpack-plugin": "^2.26.0",
"json-loader": "^0.5.4",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"webpack": "^1.14.0"
},
"dependencies": {
"babel": "^6.5.2",
"babel-polyfill": "^6.20.0",
"express": "^4.14.1",
"lodash": "^4.17.4",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"webpack-dev-middleware": "^1.9.0"
}
}

19
examples/react/server.js Normal file
View File

@ -0,0 +1,19 @@
import express from 'express'
import Gun from '../..'
import webpack from 'webpack'
import WebpackDevMiddleware from 'webpack-dev-middleware'
import config from './webpack.config'
const app = express()
const gun = Gun()
gun.wsp(app)
const compiler = webpack(config)
const devMiddleware = WebpackDevMiddleware(compiler)
app.use(devMiddleware)
app.listen(4000)

3
examples/react/start.js Normal file
View File

@ -0,0 +1,3 @@
require('source-map-support').install()
require('babel-register')
require('./server')

7
examples/react/style.css Normal file
View File

@ -0,0 +1,7 @@
html, body { font-size: 14pt; padding: 10px 2.5%;}
.hide { display: none; }
form .who { width: 10%; }
form .what { width: 80%; }
ul { list-style: none; padding: 0; }
ul .when {color: #555; font-size: 12pt; float: right; display: none; }
li:hover .when {display: inline;}

40
examples/react/todos.js Normal file
View File

@ -0,0 +1,40 @@
import './style.css'
import React, { Component } from 'react'
import Gun from '../../gun'
const gun = Gun().get('todos')
const formatTodos = todos => Object.keys(todos)
.map(key => ({ key, val: todos[key] }))
.filter(t => Boolean(t.val) && t.key !== '_')
export default class Todos extends Component {
constructor() {
super()
this.state = {newTodo: '', todos: []}
}
componentWillMount() {
gun.on(todos => this.setState({
todos: formatTodos(todos)
}))
}
add = e => {
e.preventDefault()
gun.path(Gun.text.random()).put(this.state.newTodo)
this.setState({newTodo: ''})
}
del = key => gun.path(key).put(null)
handleChange = e => this.setState({ newTodo: e.target.value})
render() {
return <div>
<form onSubmit={this.add}>
<input value={this.state.newTodo} onChange={this.handleChange} />
<button onClick={this.add}>Add</button>
</form>
<br />
<ul>
{this.state.todos.map(todo => <li key={todo.key} onClick={_=>this.del(todo.key)}>{todo.val}</li>)}
</ul>
</div>
}
}

View File

@ -0,0 +1,38 @@
var webpack = require('webpack')
var path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './app.js',
devtool: 'source-map',
output: {
filename: '[name].js?[hash]',
path: path.join(__dirname, 'public'),
},
module: {
loaders: [
{
test: /\.css$/,
loader: 'style!css',
},
{
test: /\.(png|gif|jpg|jpeg|woff)$/,
loader: 'url',
},
{
test: /\.(eot|ttf|svg|ico)$/,
loader: 'file',
},
{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/,
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
template: './index.html'
})
],
}

5
gun.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,7 @@
"version": "0.6.4",
"description": "Graph engine",
"main": "index.js",
"browser": "gun.min.js",
"browser": "gun.js",
"scripts": {
"start": "node examples/http.js 8080",
"prepublish": "npm run unbuild",