git ssb

1+

dinoworm ๐Ÿ› / catstack



Commit b0260714125b1a814ed76eaca838d164ae23101d

Merge pull request #13 from enspiral-craftworks/api

fetch and render the todos!
Mikey committed on 1/8/2016, 6:06:59 AM
Parent: c8f38fdc12fc63564d14f8dc103917bdbbfcadcd
Parent: 76f81afa54247dc961664e888a428d28dbffb096

Files changed

README.mdchanged
api.jschanged
app/api.jschanged
app/config.jschanged
app/package.jsonchanged
app/render-browser.jschanged
app/render.jschanged
app/static.jschanged
app/store.jschanged
app/todos/reducer.jschanged
app/todos/routes.jschanged
app/todos/container.jsdeleted
app/todos/actions.jsadded
app/todos/components/todo-list.jsadded
app/todos/components/todo.jsadded
app/todos/containers/index.jsadded
app/todos/models.jsadded
app/todos/service.jsadded
app/util/fetch-element.jschanged
app/client.jsadded
app/db.jsadded
app/services/index.jsdeleted
app/stack/index.jsdeleted
app/stack/services.jsdeleted
config/development.jschanged
package.jsonchanged
render.jschanged
static.jschanged
knexfile.jsadded
migrations/20160108004846_create_todos.jsadded
README.mdView
@@ -9,10 +9,10 @@
99 - task runner: [npm scripts](http://substack.net/task_automation_with_npm_run)
1010 - client bundler: [browserify](https://github.com/substack/browserify-handbook)
1111 - es6/jsx transform: [babelify](https://www.npmjs.com/package/babelify)
1212 - css transform: [cssify](https://www.npmjs.com/package/cssify) and [css-modules-require-hook](https://www.npmjs.com/package/css-modules-require-hook)
13- - configuration: [evalify](https://www.npmjs.org/package/evalify)
1413 - bulk require: [bulkify](https://www.npmjs.org/package/bulkify)
14+- configuration: [simple-rc](https://www.npmjs.org/package/simple-rc)
1515 - utility functions: [ramda](http://ramdajs.com/docs/)
1616 - directory structure:
1717 - `/config/`
1818 - `/config/defaults.js`
@@ -84,4 +84,27 @@
8484 - `components/*.js`: exports [dumb component](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)
8585 - `components/*.css`: exports [css modules](http://glenmaddern.com/articles/css-modules) for respective component
8686 - `service.js`: exports [`feathers`](http://feathersjs.com) service (recommended to use [`feathers-knex`](https://github.com/feathersjs/feathers-knex) and [`feathers-tcomb`](https://github.com/ahdinosaur/feathers-tcomb))
8787 - `client.js`: exports [`feathers-client`](https://github.com/feathersjs/feathers-client)
88+
89+### setup postgres database
90+
91+install `docker`
92+
93+- to install db, run `npm run pg:pull`
94+- to create db, run `npm run pg:run`
95+- to start db, run `npm run pg:start`
96+- to stop db, run `npm run pg:stop`
97+- to remove db, run `npm run pg:rm`
98+- to show db logs, run `npm run pg:logs`
99+
100+run latest migrations with
101+
102+```shell
103+npm run knex -- migrate:latest
104+```
105+
106+or run [any other `knex` command] with `npm run knex -- [command] [args]`
107+
108+## known issues
109+
110+- adding a new file won't always be noticed by `node-dev` or `watchify` due to usage of `bulk-require`). potential fix is to use `chokidar-cli` and some transform to watch for new files and re-run the script command
api.jsView
@@ -1,8 +1,8 @@
11 require('babel-core/register')
22
3-const config = require('app/config').default
4-const createApi = require('app/api').default
3+const config = require('app/config')
4+const createApi = require('app/api')
55 const Url = require('url')
66
77 const server = createApi(config)
88
app/api.jsView
@@ -1,12 +1,34 @@
1+const bulk = require('bulk-require')
12 import feathers from 'feathers'
2-import { mapObjIndexed, reduce, toPairs } from 'ramda'
3+import hooks from 'feathers-hooks'
4+import rest from 'feathers-rest'
5+import bodyParser from 'body-parser'
6+import cors from 'cors'
7+import { map, mapObjIndexed, reduce, toPairs } from 'ramda'
38
4-import services from 'app/services'
5-import config from 'app/config'
9+import memory from 'feathers-memory'
610
7-export default function createServer (config) {
11+const services = Object.assign(
12+ map(
13+ (module) => module.service.default,
14+ bulk(__dirname, '*/service.js')
15+ ),
16+ map(
17+ (module) => module.services.map(m => m.default),
18+ bulk(__dirname, '*/services/*.js')
19+ )
20+)
21+
22+export default module.exports = createApi
23+
24+function createApi (config) {
825 const app = feathers()
26+ .use(cors())
27+ .configure(rest())
28+ .use(bodyParser.json())
29+ .use(bodyParser.urlencoded({ extended: true }))
30+ .configure(hooks())
931
1032 useAll(app, services)
1133
1234 return app
app/config.jsView
@@ -1,3 +1,2 @@
1-import getConfig from 'simple-rc'
2-
3-export default getConfig()
1+process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2+module.exports = module.exports['default'] = require('simple-rc')()
app/package.jsonView
@@ -7,9 +7,10 @@
77 "transform": [
88 ["cssify", { "modules": true } ],
99 "babelify",
1010 "envify",
11- "bulkify"
11+ "bulkify",
12+ ["evalify", { "files": "**/app/config.js" } ]
1213 ]
1314 },
1415 "rc": {
1516 "files": [
app/render-browser.jsView
@@ -18,9 +18,9 @@
1818
1919 syncReduxAndRouter(history, store)
2020
2121 const main = (
22- <Router createElement={fetchElement} history={history}>
22+ <Router createElement={fetchElement(store)} history={history}>
2323 { routes }
2424 </Router>
2525 )
2626
app/render.jsView
@@ -14,9 +14,11 @@
1414 import createStore from 'app/store'
1515 import routes from 'app/routes'
1616 import fetchAllData from 'app/util/fetch-all-data'
1717
18-export default function createRender (config) {
18+export default module.exports = createRender
19+
20+function createRender (config) {
1921 const staticUrl = Url.format(config.static.url)
2022
2123 return http.createServer(render)
2224
app/static.jsView
@@ -1,7 +1,7 @@
11 import http from 'http'
22
3-export default function createStatic (config) {
3+export default module.exports = function createStatic (config) {
44 const ecstatic = config.livereload ?
55 require('ecstatic-lr') : require('ecstatic')
66
77 return http.createServer(
app/store.jsView
@@ -18,11 +18,13 @@
1818 applyMiddleware(...middleware)
1919 )
2020
2121 if (process.env.NODE_ENV === 'development') {
22- storeEnhancers.push(
23- applyMiddleware(logger())
24- )
22+ if (process.browser) {
23+ storeEnhancers.push(
24+ applyMiddleware(logger())
25+ )
26+ }
2527
2628 storeEnhancers.push(DevTools.instrument())
2729
2830 if (module.browser) {
app/todos/reducer.jsView
@@ -1,11 +1,5 @@
1-export default function todos (state = {}, action) {
2- switch (action.type) {
3- case 'CREATE_TODO':
4- return {
5- ...state,
6- [action.payload.id]: action.payload
7- }
8- default:
9- return state
10- }
11-}
1+import { createReducer } from 'feathers-action'
2+
3+import { Todos } from './models'
4+
5+export default createReducer(Todos)
app/todos/routes.jsView
@@ -1,6 +1,8 @@
11 import React from 'react'
2-import { Route } from 'react-router'
2+import { Route, IndexRoute } from 'react-router'
33
4-import TodosContainer from './container'
4+import IndexContainer from './containers/index'
55
6-export default <Route path="todos" component={TodosContainer} />
6+export default <Route path="todos">
7+ <IndexRoute component={IndexContainer} />
8+</Route>
app/todos/container.jsView
@@ -1,14 +1,0 @@
1-import React from 'react'
2-import { connect } from 'react-redux'
3-
4-class TodosContainer extends React.Component {
5- render () {
6- return <div>
7- todo list!
8- </div>
9- }
10-}
11-
12-export default connect(
13- (state) => ({})
14-)(TodosContainer)
app/todos/actions.jsView
@@ -1,0 +1,7 @@
1+import { createActions } from 'feathers-action'
2+
3+import client from 'app/client'
4+
5+import { Todos } from './models'
6+
7+export default createActions(client, Todos)
app/todos/components/todo-list.jsView
@@ -1,0 +1,13 @@
1+import React from 'react'
2+
3+export default class TodoList extends React.Component {
4+ render () {
5+ return <ul>
6+ {
7+ React.Children.map(this.props.children, todo => {
8+ return <li>{ todo }</li>
9+ })
10+ }
11+ </ul>
12+ }
13+}
app/todos/components/todo.jsView
@@ -1,0 +1,9 @@
1+import React from 'react'
2+
3+export default class Todo extends React.Component {
4+ render () {
5+ return <div>
6+ { this.props.todo.text }
7+ </div>
8+ }
9+}
app/todos/containers/index.jsView
@@ -1,0 +1,29 @@
1+import React from 'react'
2+import { connect } from 'react-redux'
3+import { map, values } from 'ramda'
4+
5+import actions from '../actions'
6+import TodoList from '../components/todo-list'
7+import Todo from '../components/todo'
8+
9+class TodosContainer extends React.Component {
10+ static fetchData = (getState, dispatch, location, params) => {
11+ return dispatch(actions.find())
12+ }
13+
14+ render () {
15+ return <TodoList>
16+ {
17+ values(map(todo => {
18+ return <Todo todo={todo} />
19+ }, this.props.todos))
20+ }
21+ </TodoList>
22+ }
23+}
24+
25+export default connect(
26+ (state) => ({
27+ todos: state.todos.records
28+ })
29+)(TodosContainer)
app/todos/models.jsView
@@ -1,0 +1,9 @@
1+import t from 'tcomb'
2+
3+export const Todo = t.struct({
4+ id: t.Number,
5+ text: t.String,
6+ complete: t.Boolean
7+}, 'Todo')
8+
9+export const Todos = t.list(Todo, 'Todos')
app/todos/service.jsView
@@ -1,0 +1,16 @@
1+import knexService from 'feathers-knex'
2+import validate from 'feathers-tcomb'
3+
4+import db from 'app/db'
5+
6+import { Todo } from './models'
7+
8+export default knexService({
9+ Model: db,
10+ name: 'todos'
11+})
12+//.extend({
13+// setup: function (app) {
14+// validate(app.service('todos'), Todo)
15+// }
16+//})
app/util/fetch-element.jsView
@@ -1,12 +1,15 @@
11 import React from 'react'
22
3-export default function fetchElement (Component, props) {
4- if (Component.fetchData) {
5- Component.fetchData(
6- store.getState, store.dispatch,
7- props.location, props.params
8- )
3+export default function fetchElement (store) {
4+ return function (Component, props) {
5+ if (Component.fetchData) {
6+ process.nextTick(function () {
7+ Component.fetchData(
8+ store.getState, store.dispatch,
9+ props.location, props.params
10+ )
11+ })
12+ }
13+ return React.createElement(Component, props)
914 }
10- return React.createElement(Component, props)
1115 }
12-
app/client.jsView
@@ -1,0 +1,11 @@
1+import feathers from 'feathers-client'
2+import fetch from 'isomorphic-fetch'
3+import Url from 'url'
4+
5+import config from 'app/config'
6+
7+const clientUrl = Url.format(config.api.url)
8+const client = feathers(clientUrl)
9+ .configure(feathers.fetch(fetch))
10+
11+export default client
app/db.jsView
@@ -1,0 +1,5 @@
1+import knex from 'knex'
2+
3+import config from 'app/config'
4+
5+export default knex(config.db)
app/services/index.jsView
@@ -1,13 +1,0 @@
1-const bulk = require('bulk-require')
2-import { map } from 'ramda'
3-
4-export default {
5- ...map(
6- (module) => m.service.default,
7- bulk(__dirname, '*/service.js')
8- ),
9- ...map(
10- (module) => module.services.map(m => m.default),
11- bulk(__dirname, '*/services/*.js')
12- )
13-}
app/stack/index.jsView
@@ -1,34 +1,0 @@
1-import feathers from 'feathers'
2-import { mapObjIndexed, reduce, toPairs } from 'ramda'
3-
4-const stackCreators = {
5- services: require('./services'),
6- static: require('./static'),
7- render: require('./render')
8-}
9-
10-export default function createStack(config) {
11- const stacks = createStacks(config)
12-
13- const app = feathers()
14-
15- useAll(app, stacks)
16-
17- return app
18-}
19-
20-function createStacks (config) {
21- return mapObjIndexed(
22- (stackCreator, name) => {
23- return stackCreator(config[name])
24- },
25- stackCreators
26- )
27-}
28-
29-function useAll (app, services) {
30- return reduce((app, [name, service]) => {
31- return app.use(service)
32- }, app, toPairs(services))
33-}
34-
app/stack/services.jsView
config/development.jsView
@@ -1,2 +1,17 @@
1+const join = require('path').join
2+
13 module.exports = {
4+ db: {
5+ client: 'pg',
6+ connection: {
7+ host : 'localhost',
8+ user : 'postgres',
9+ //password : 'postgres',
10+ database : 'postgres'
11+ },
12+ pool: {
13+ min: 0,
14+ max: 1
15+ }
16+ }
217 }
package.jsonView
@@ -2,14 +2,21 @@
22 "name": "business-stack",
33 "version": "0.0.0",
44 "description": "real-world production-quality TodoMVC example",
55 "scripts": {
6+ "knex": "knex",
67 "postinstall": "lnfs app node_modules/app",
78 "lint": "snazzy",
89 "format": "snazzy --format",
910 "test": "npm-run-all -p test:*",
1011 "test:spec": "node app/spec",
1112 "test:feature": "node app/features",
13+ "pg:pull": "docker pull postgres",
14+ "pg:run": "docker run -d -p 5432:5432 --name=business-postgres postgres",
15+ "pg:start": "docker start business-postgres",
16+ "pg:stop": "docker stop business-postgres",
17+ "pg:rm": "docker rm business-postgres",
18+ "pg:logs": "docker logs business-postgres",
1219 "dev:render-browser": "BABEL_ENV=hot watchify app/render -o build/bundle.js -dv -p browserify-hmr",
1320 "dev:render-node": "node-dev render",
1421 "dev:assets": "cpx \"app/assets/**/*\" build -w",
1522 "dev:livereload": "wtch -d build -e html,css,png,gif,jpg | garnish --level debug",
@@ -55,8 +62,9 @@
5562 "babel-plugin-react-transform": "^2.0.0",
5663 "browserify-hmr": "^0.3.1",
5764 "cuke-tap": "^1.0.2",
5865 "ecstatic-lr": "^1.0.1",
66+ "feathers-memory": "^0.5.1",
5967 "garnish": "^5.0.1",
6068 "glob": "^6.0.2",
6169 "jsdom": "^7.1.0",
6270 "node-dev": "^2.7.1",
@@ -83,18 +91,27 @@
8391 "babelify": "^7.2.0",
8492 "browserify": "github:ahdinosaur/node-browserify",
8593 "bulk-require": "^0.2.1",
8694 "bulkify": "^1.1.1",
95+ "cors": "^2.7.1",
8796 "cpx": "^1.2.1",
8897 "css-modules-require-hook": "^2.1.0",
8998 "cssify": "github:ahdinosaur/cssify",
9099 "ecstatic": "^1.4.0",
91100 "envify": "^3.4.0",
92- "evalify": "^1.0.1",
93- "feathers": "^1.2.0",
101+ "evalify": "github:ahdinosaur/evalify#minimatch",
102+ "feathers": "^2.0.0-pre.1",
103+ "feathers-action": "^1.0.1",
104+ "feathers-client": "^0.5.0",
105+ "feathers-hooks": "^0.5.1",
106+ "feathers-knex": "^2.0.0",
107+ "feathers-rest": "^1.0.0",
108+ "feathers-tcomb": "^1.0.0",
94109 "history": "^1.13.1",
110+ "isomorphic-fetch": "^2.2.0",
95111 "lnfs-cli": "^1.0.1",
96112 "npm-run-all": "^1.3.2",
113+ "pg": "^4.4.3",
97114 "pinkie-promise": "^2.0.0",
98115 "predirect": "^1.1.0",
99116 "ramda": "^0.18.0",
100117 "react": "^0.14.3",
@@ -105,7 +122,8 @@
105122 "redux-simple-router": "0.0.10",
106123 "redux-thunk": "^1.0.0",
107124 "send-data": "^8.0.0",
108125 "simple-rc": "^1.0.0",
126+ "tcomb": "^2.5.2",
109127 "uglifyify": "^3.0.1"
110128 }
111129 }
render.jsView
@@ -1,9 +1,9 @@
11 require('babel-core/register')
22 require('css-modules-require-hook')
33
4-const config = require('app/config').default
5-const createRender = require('app/render').default
4+const config = require('app/config')
5+const createRender = require('app/render')
66 const Url = require('url')
77
88 const server = createRender(config)
99
static.jsView
@@ -1,8 +1,8 @@
11 require('babel-core/register')
22
3-const config = require('app/config').default
4-const createStatic = require('app/static').default
3+const config = require('app/config')
4+const createStatic = require('app/static')
55 const Url = require('url')
66
77 const server = createStatic(config)
88
knexfile.jsView
@@ -1,0 +1,1 @@
1+module.exports = require('app/config').db
migrations/20160108004846_create_todos.jsView
@@ -1,0 +1,11 @@
1+exports.up = function(knex, Promise) {
2+ return knex.schema.createTableIfNotExists('todos', function(table) {
3+ table.increments('id')
4+ table.string('text')
5+ table.boolean('complete')
6+ })
7+}
8+
9+exports.down = function(knex, Promise) {
10+ return knex.schema.dropTableIfExists('todos')
11+}

Built with git-ssb-web