Skip to content

Commit

Permalink
Publish
Browse files Browse the repository at this point in the history
  • Loading branch information
OKNoah committed Dec 31, 2017
1 parent 8e91ca7 commit a75ad58
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 33 deletions.
54 changes: 42 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
> ⚠️ NOTE: Future releases will be pre-release and have `alpha` and `beta` tags. NPM/yarn will probably not install these unless you specify.
# Final

This is a very experimental proof of concept for a sort of MC framework. It's meant to work a little like React, but for handling web requests. One of the posibilities would be very precise handling of how to respond to requests, possibly allowing redux store-like functionality.
This is a very experimental proof of concept for a server framework. It takes ideas from ES6+ and React to see if there's a more enjoyable, versatile way to create APIs. Key components are:

#### Classes

Each endpoint has a class, which extends the component class. The main method of the class is `respond` which decides what is returned upon each request.

#### Lifecycle

Each component has a lifecycle, with names similar to the lifecycle components of React components.

#### Decorators

Decorators are used to add actions and in-memory state to components. The example decorator that's included is for ArangoDB. A `reduxConnect` decorator is also currently part of the library.

#### WebSockets

WebSocket-functionality is built-in (at present). Depending on how you wish to use the library, you can have components that repeat their lifecycle whenever state is changed. This is most useful for WebSocket servers becuase there may be multiple responses per connection. An `http` request will fire `response` onece, but a WebSocket connection might do it many times.

## Usage

See the [`/examples`](examples) and files matching the patter [`**/*.test.js`](src) for more usage and explanation. Be warned the API will change a lot.

```js
import Final from './src/index'
import Final, { reduxConnect } from './src/index'
import { bindActionCreators } from 'redux'
import { findDecorator } from './test/ArangoDecorator'
import { middleware, store } from './example/middleware'
import { moveUp } from './redux/modules/player'

/*
The `findDecorator` adds a few funtions to the class, like `this.findOne`.
Expand All @@ -16,29 +38,37 @@ import { middleware, store } from './example/middleware'
// this decorator will verify collection or create new one
collection: 'Post'
})
@reduxConnect(
(state) => ({
players: state
}),
(dispatch) => ({
moveUp
})
)
class Post extends Final.Component {
/*
The path decides what requests will match this component and the params.
*/
path = '/post/:post?'
constructor () {
super()
}

/*
The respond function returns whatever the response will be. Notice the params and `this.findOne` are available.
*/
async respond () {
console.log('this.props.params', this.props.params)
const output = await this.findOne({"body": "Updated!"})
return output
console.log('this.actions.moveUp', this.action.moveUp)
const output = await this.actions.findOne({"body": "Updated!"})
return {
data: {
players: this.props.players,
output
}
}
}
}

Final.createServer({
components: [Post],
port: 3001,
middleware, // optional, see `examples/middleware-usage.js`
store // optional, see `examples/middleware-usage.js`
store, // optional, see `examples/game-server.js`
// middleware, /* Removed. Purpose needs to be decided. */
})
```
1 change: 1 addition & 0 deletions examples/game-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class User extends Component {

async messageReceived (msg) {
if (['moveUp', 'moveDown', 'moveLeft', 'moveRight', 'bug'].includes(msg)) {
console.log('this.actions', this.actions)
this.actions[msg](this.props.params.player)
} else {
throw "That's not a function"
Expand Down
28 changes: 23 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
{
"name": "final",
"version": "1.0.0-alpha.1",
"main": "index.js",
"author": "Noah",
"name": "final-server",
"description": "A component-based server framework for ES6+, with a React-like lifecycle. Experimental.",
"version": "0.9.1",
"main": "dist/index.js",
"author": "Noah Gray <[email protected]> (https://github.com/oknoah)",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/OKNoah/final.git"
},
"scripts": {
"test:path": "./node_modules/.bin/tape -r @babel/register",
"prepublish": "./node_modules/.bin/babel ./src -d ./dist --ignore node_modules/ --ignore dist/",
"test": "./node_modules/.bin/tape -r @babel/register './src/**/*.test.js' | ./node_modules/.bin/tap-notify | ./node_modules/.bin/faucet",
"test:path": "./node_modules/.bin/tape -r @babel/register",
"test:watch": "./node_modules/.bin/onchange '**/*.js' -- yarn run test",
"test:pathwatch": "./node_modules/.bin/onchange '**/*.js' -- yarn run test:path",
"test:game-server": "curl -s -X GET --header 'Accept: application/json' 'http://localhost:3001/map/1/player/1234' | node -r @babel/register -e \"process.stdin.setEncoding('utf8'); var chunks = ''; process.stdin.on('readable', () => { const chunk = process.stdin.read(); if (chunk !== null) { chunks += chunk }}); process.stdin.on('end', () => { process.stdout.write(JSON.stringify(JSON.parse(chunks), null, 2)) ;});\""
},
"keywords": [
"rest",
"restful",
"api",
"websockets",
"sockets",
"framework",
"server",
"arango",
"orm",
"web"
],
"devDependencies": {
"@babel/cli": "^7.0.0-beta.35",
"@babel/core": "^7.0.0-beta.35",
Expand Down
12 changes: 12 additions & 0 deletions src/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ export default class Final {
return
}

static async setProps (instance, state) {
instance.props = state

return
}

async setProps (state) {
await this.constructor.setProps(this, state)

return
}

async shouldComponentUpdate (newProps) {
if (newProps !== this.props) {
return true
Expand Down
6 changes: 2 additions & 4 deletions src/Component.test.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import test from 'tape'
import client from 'superagent'
import Final, { reduxConnect } from './index'
import { middleware, store } from '../examples/middleware'
import { store } from '../examples/middleware'
import { findDecorator } from '../test/ArangoDecorator'
import { createStore, bind, bindActionCreators } from 'redux'
import { bindActionCreators } from 'redux'
import WS from 'ws'

const HOST = 'localhost:3001'

function testo () { return { type: 'TEST' } }

@reduxConnect(
null,
(dispatch) => bindActionCreators({
Expand Down
9 changes: 3 additions & 6 deletions src/reduxConnect.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,15 @@ const reduxConnect = (mapStateToProps, mapActionsToDispatch) => (Component) => {
})
}

Component.prototype.actions = {
...Component.prototype.actions,
...actions
}
Object.assign(Component.prototype, { actions })
Component.prototype.store = store

const instance = new Component()

instance.props = {
instance.setProps({
...instance.props,
...props
}
})

function socketHandler (data) {
instance.lifecycleIncrement = 1
Expand Down
11 changes: 5 additions & 6 deletions src/updater.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,22 @@ async function init (instance, req, res) {
const pathname = parse(req.url).pathname
const match = route(instance.path)
const params = match(pathname)
instance.props = instance.props || {}
instance.props = {
instance.setProps({
...instance.props,
params
}
})
if (!instance.props.params) {
// logger('No match pathname', pathname)
throw 'No match'
}
}

if (name === 'shouldComponentUpdate') {
instance.props = {
instance.setProps({
...instance.props,
request: req,
response: res
}
})
if (instance.props.response instanceof WebSocket) {
logger('✅ is WebSocket')
}
Expand All @@ -61,7 +60,7 @@ async function updater (instance, data) {
throw "Should not update"
}

instance.props = nextProps
instance.setProps(nextProps)

const response = await instance.respond()
await instance.setState(response)
Expand Down

0 comments on commit a75ad58

Please sign in to comment.