├── .gitignore
├── .nodemonignore
├── Dockerfile
├── README.md
├── deploy
└── docker-compose.yaml
├── md
├── docs
│ ├── derby-0.10
│ │ ├── apps
│ │ │ ├── index.html
│ │ │ ├── overview.md
│ │ │ └── routes.md
│ │ ├── components
│ │ │ ├── component-class.md
│ │ │ ├── events.md
│ │ │ ├── index.html
│ │ │ ├── lifecycle.md
│ │ │ ├── overview.md
│ │ │ ├── scope.md
│ │ │ └── view-partials.md
│ │ ├── index.html
│ │ ├── models
│ │ │ ├── backends.md
│ │ │ ├── data-loading-contexts.md
│ │ │ ├── events.md
│ │ │ ├── filters-and-sorts.md
│ │ │ ├── getters.md
│ │ │ ├── index.html
│ │ │ ├── overview.md
│ │ │ ├── paths.md
│ │ │ ├── queries.md
│ │ │ ├── reactive-functions.md
│ │ │ ├── references.md
│ │ │ └── setters.md
│ │ ├── overview.md
│ │ └── views
│ │ │ ├── index.html
│ │ │ ├── namespaces-and-files.md
│ │ │ ├── overview.md
│ │ │ └── template-syntax
│ │ │ ├── blocks.md
│ │ │ ├── escaping.md
│ │ │ ├── functions-and-events.md
│ │ │ ├── index.html
│ │ │ ├── literals.md
│ │ │ ├── operators.md
│ │ │ ├── overview.md
│ │ │ ├── paths.md
│ │ │ └── view-attributes.md
│ └── index.html
├── faq.md
├── faq
│ ├── faq_en.md
│ └── faq_ru.md
├── index.html
├── resources.md
└── started.md
├── package-lock.json
├── package.json
├── public
├── .gitkeep
├── css
│ ├── bootstrap-theme.min.css
│ ├── bootstrap.min.css
│ ├── docs.min.css
│ ├── font-awesome.min.css
│ ├── github.min.css
│ └── rainbow.css
├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ └── glyphicons-halflings-regular.woff
├── images
│ ├── arrows.png
│ ├── book.png
│ ├── browser.png
│ ├── comments.png
│ ├── derby-medium-cream.png
│ ├── derby-medium-transparent.png
│ ├── derby.png
│ ├── derbyparty.jpg
│ ├── docs
│ │ └── charts-debug.png
│ ├── favicon.ico
│ ├── flag.png
│ ├── github.png
│ ├── hourse-logo.png
│ ├── html5logos.png
│ ├── logo.png
│ ├── love.png
│ ├── mail.png
│ ├── nodejs.png
│ ├── octocat.png
│ ├── octocat30.png
│ ├── responsive.png
│ ├── ticket.png
│ └── user.png
├── js
│ ├── app.js
│ ├── bootstrap.min.js
│ └── jquery.min.js
└── robots.txt
├── server.js
├── src
└── server
│ ├── derbyLanguage.js
│ ├── error.js
│ ├── index.js
│ ├── markdown.js
│ ├── outline.js
│ └── site.js
├── styles
├── app
│ ├── colors.styl
│ ├── docs.styl
│ ├── home.styl
│ ├── index.styl
│ └── syntax.styl
├── error
│ ├── index.styl
│ └── reset.styl
└── libs
│ ├── bootstrap.min.css
│ ├── docs.min.css
│ └── font-awesome.min.css
└── views
├── app
├── home.html
└── index.html
└── error
├── 403.html
├── 404.html
├── 500.html
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.swp
3 | node_modules
4 | views/gen
5 | npm-debug.log
6 | /.idea
7 | public/derby
8 | fabfile.pyc
9 |
--------------------------------------------------------------------------------
/.nodemonignore:
--------------------------------------------------------------------------------
1 | public/derby
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # BUILD-USING: docker build -t derbyjs/derby-site .
2 | # RUN-USING: docker run -p 4000:4000 --name derby-site --rm derbyjs/derby-site
3 |
4 | # specify base docker image
5 | FROM node:20
6 |
7 | # copy over dependencies
8 | WORKDIR /var
9 | RUN mkdir derby-site
10 |
11 | ADD package.json /var/derby-site/
12 | ADD server.js /var/derby-site/
13 |
14 | ADD md /var/derby-site/md
15 | ADD public /var/derby-site/public
16 | ADD src /var/derby-site/src
17 | ADD styles /var/derby-site/styles
18 | ADD views /var/derby-site/views
19 |
20 | # npm install all the things
21 | WORKDIR /var/derby-site
22 | RUN npm_config_spin=false npm_config_loglevel=warn npm install --production
23 |
24 | # expose any ports we need
25 | EXPOSE 4000
26 | ENV PORT 4000
27 | # the command that gets run inside the docker container
28 | CMD ["/usr/local/bin/node", "/var/derby-site/server.js"]
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | derby-site
2 | =============
3 | The [derbyjs.com](//derbyjs.com/) site, built with Derby.
4 |
5 | The docs in this repo are deprecated. Up-to-date docs are now maintained in the [main derby repo's docs directory](https://github.com/derbyjs/derby/tree/master/docs) and are automatically deployed to GitHub Pages at [derbyjs.github.io/derby/](https://derbyjs.github.io/derby/).
6 |
7 | Installation
8 | ------------
9 |
10 | Clone this repository and install the dependencies:
11 |
12 | ```shell
13 | npm install
14 | ```
15 |
16 | Development
17 | -----------
18 |
19 | To run the site locally:
20 |
21 | ```shell
22 | npm start
23 | ```
24 |
25 | To run the entire stack locally, you can use `docker-compose`. To do this,
26 | simply run:
27 |
28 | ```shell
29 | npm run compose-up
30 | ```
31 |
32 | Similarly, `npm run compose-stop` will stop all containers and
33 | `npm run compose-down` will stop and remove all containers and networks created.
34 |
35 | You can also use Docker Compose directly by running the following command:
36 |
37 | ```shell
38 | SWARM_MODE=false docker-compose -f ./deploy/docker-compose.yaml up
39 | ```
40 |
41 | To change the underlying versions of `derby-site` or `derby-examples`, simply
42 | adjust the tags for the `image`.
43 |
44 | Production
45 | ----------
46 |
47 | The Derby site can operate using the Docker Compose or, more ideally, Docker
48 | Swarm. The use Swarm, you must first initialize a swarm cluster. To do this,
49 | simply run `docker swarm init`. If you are prompted to include the
50 | `--advertise-addr` parameter, make sure this matches the instances **local** IP
51 | address, **not** the public address. Once you have done this, you can run the
52 | following command to create the stack from the `./deploy` directory:
53 |
54 | ```shell
55 | npm run deploy
56 | ```
57 |
58 | Alternatively, you can run this directly using the Docker CLI with the following
59 | command:
60 |
61 | ```shell
62 | SWARM_MODE=true docker stack deploy --compose-file docker-compose.yaml derbyjs
63 | ```
64 |
65 | This will create all necessary resources. If you are making changes to the
66 | configuration or want to update to a new version, you can simply edit the
67 | `docker-compose.yaml` file and run the command listed above again. This will
68 | initiate a rolling update.
69 |
70 | Note that the only container utilizing [rolling updates](https://docs.docker.com/engine/swarm/swarm-tutorial/rolling-update/) is the `derby-site`
71 | container.
72 |
73 |
74 | Build and Deploy
75 | ----------------
76 |
77 | To build the DerbyJS Site, clone this repo locally as described above.
78 |
79 | Build a docker image with a tag containing today's date, push to Docker Hub, and cleanup the local copy of the image:
80 | ```
81 | docker build --platform linux/amd64 . --tag "derbyjs/derby-site:$(date +%Y-%m-%d)"
82 | docker push "derbyjs/derby-site:$(date +%Y-%m-%d)"
83 | docker rmi "derbyjs/derby-site:$(date +%Y-%m-%d)"
84 | ```
85 |
86 |
87 | Next, update the deployed tag specified in [/deploy/docker.compose.yaml](https://github.com/derbyjs/derby-site/blob/master/deploy/docker-compose.yaml). The tag is defined in `services: derbysite: image: derbyjs/derby-site:YYYY-MM-DD`, and it should be updated to the current date. Commit and push your changes to GitHub.
88 |
89 | Finally, SSH to the derby-site server:
90 | ```
91 | ssh derbyjs-01.droplet.derbyjs.com
92 | ```
93 | and run the command to update:
94 | ```
95 | cd ~/derby-site/deploy/ && git pull && npm run deploy
96 | ```
97 |
--------------------------------------------------------------------------------
/deploy/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: "3"
2 |
3 | services:
4 | mongo:
5 | image: mongo:3.6
6 | ports:
7 | - "27017:27017"
8 | networks:
9 | - derbyjs_overlay
10 |
11 | redis:
12 | image: redis:3
13 | ports:
14 | - "6379:6379"
15 | networks:
16 | - derbyjs_overlay
17 |
18 | derby_site:
19 | image: derbyjs/derby-site:2024-05-31
20 | deploy:
21 | replicas: 2
22 | update_config:
23 | parallelism: 1
24 | delay: 10s
25 | labels: &derby_site_labels
26 | traefik.enable: "true"
27 | traefik.docker.network: derbyjs_derbyjs_overlay
28 | traefik.http.routers.derby-site.rule: Host(`derbyjs.com`) || Host(`www.derbyjs.com`) || Path(`/robots.txt`)
29 | traefik.http.services.derby-site.loadbalancer.server.port: 4000
30 | traefik.priority: 100
31 | ports:
32 | - "4000:4000"
33 | labels: *derby_site_labels
34 | networks:
35 | - derbyjs_overlay
36 |
37 | derby_examples:
38 | image: derbyjs/derby-examples:2024-05-20
39 | deploy:
40 | replicas: 1
41 | update_config:
42 | parallelism: 1
43 | delay: 10s
44 | labels: &derby_examples_labels
45 | traefik.enable: "true"
46 | traefik.docker.network: derbyjs_derbyjs_overlay
47 |
48 | traefik.http.routers.charts.rule: Host(`charts.derbyjs.com`)
49 | traefik.http.routers.charts.service: charts
50 | traefik.http.services.charts.loadbalancer.server.port: 8001
51 |
52 | traefik.http.routers.chat.rule: Host(`chat.derbyjs.com`)
53 | traefik.http.routers.chat.service: chat
54 | traefik.http.services.chat.loadbalancer.server.port: 8002
55 |
56 | traefik.http.routers.codemirror.rule: Host(`codemirror.derbyjs.com`)
57 | traefik.http.routers.codemirror.service: codemirror
58 | traefik.http.services.codemirror.loadbalancer.server.port: 8003
59 |
60 | traefik.http.routers.directory.rule: Host(`directory.derbyjs.com`)
61 | traefik.http.routers.directory.service: directory
62 | traefik.http.services.directory.loadbalancer.server.port: 8004
63 |
64 | traefik.http.routers.hello.rule: Host(`hello.derbyjs.com`)
65 | traefik.http.routers.hello.service: hello
66 | traefik.http.services.hello.loadbalancer.server.port: 8005
67 |
68 | traefik.http.routers.sink.rule: Host(`sink.derbyjs.com`)
69 | traefik.http.routers.sink.service: sink
70 | traefik.http.services.sink.loadbalancer.server.port: 8006
71 |
72 | traefik.http.routers.todos.rule: Host(`todos.derbyjs.com`)
73 | traefik.http.routers.todos.service: todos
74 | traefik.http.services.todos.loadbalancer.server.port: 8007
75 |
76 | traefik.http.routers.widgets.rule: Host(`widgets.derbyjs.com`)
77 | traefik.http.routers.widgets.service: widgets
78 | traefik.http.services.widgets.loadbalancer.server.port: 8008
79 |
80 | traefik.http.routers.render.rule: Host(`render.derbyjs.com`)
81 | traefik.http.routers.render.service: render
82 | traefik.http.services.render.loadbalancer.server.port: 8009
83 |
84 | ports:
85 | - "8001:8001"
86 | - "8002:8002"
87 | - "8003:8003"
88 | - "8004:8004"
89 | - "8005:8005"
90 | - "8006:8006"
91 | - "8007:8007"
92 | - "8008:8008"
93 | - "8009:8009"
94 | depends_on:
95 | - mongo
96 | - redis
97 | environment:
98 | MONGO_HOST: mongo
99 | REDIS_HOST: redis
100 | labels: *derby_examples_labels
101 | networks:
102 | - derbyjs_overlay
103 |
104 | traefik:
105 | image: traefik:v2.11
106 | deploy:
107 | replicas: 2
108 | update_config:
109 | parallelism: 1
110 | delay: 10s
111 | volumes:
112 | - /var/run/docker.sock:/var/run/docker.sock
113 | ports:
114 | - "0.0.0.0:8080:80"
115 | command:
116 | - --api=true
117 | - --ping=true
118 | - --ping.entrypoint=http
119 | - --providers.docker
120 | - --providers.docker.exposedbydefault=false
121 | - --providers.docker.swarmmode=${SWARM_MODE}
122 | networks:
123 | - derbyjs_overlay
124 |
125 | networks:
126 | derbyjs_overlay:
127 | driver: overlay
128 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/apps/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/apps/overview.md:
--------------------------------------------------------------------------------
1 | # Derby Apps
2 |
3 | Derby projects support one or more single-page apps.
4 | Apps have a full MVC structure, including a model provided by
5 | [Racer](https://github.com/derbyjs/racer), a template and styles based view, and controller
6 | code with application logic and routes (which map URLs to actions).
7 |
8 | On the server, apps provide a router middleware for Express. One or more app
9 | routers as well as server only routes can be included in the same Express
10 | server.
11 |
12 | Derby packages up all of an app's templates, routes, and application code when
13 | rendering. Regardless of which app URL the browser requests initially, the app
14 | is able to render any other state within the same application client-side. If
15 | the app cannot handle a URL, it will fall through and request from the server.
16 | Errors thrown during route handling also cause requests to fall through to the
17 | server.
18 |
19 | Derby works great with only a single app, though developers may wish to create
20 | separate apps if only certain sets of pages are likely to be used together. For
21 | example, a project might have a separate desktop web app and mobile web app. Or
22 | a project might have an internal administration panel app and a public content
23 | app.
24 |
25 |
26 | ## Creating apps
27 |
28 | Apps are created in the file that defines the app's controller code. They are
29 | then associated with a server by requiring the app within the server file.
30 |
31 | > `app = derby.createApp ( name, fileName )`
32 | >
33 | > * `name`: the name of the app
34 | > * `fileName`: the name of the file, typically node's default __filename is used.
35 | >
36 | > * `app`: Returns an app object, typically exported as `module.exports = app`
37 |
38 |
39 | App names are used to automatically associate an app with template and styles files of the same
40 | name.
41 |
42 | The `createApp` method adds a number of methods to the app. On both the client
43 | and the server, these are `use`, `get`, `post`, `put`, `del`,
44 | and `ready`. On the server only, Derby also adds `router`,
45 | for use with Express.
46 |
47 | ## Connecting servers to apps
48 |
49 | Because Derby shares most code between server and client, Derby server files
50 | can be very minimal.
51 |
52 | The server includes an app with a standard Node.js require statement. It can
53 | then use the `app.router()` method to create a router middleware for Express
54 | that handles all of the app's routes.
55 |
56 | The server also needs to create a `store` object, which is what creates models,
57 | coordinates data syncing, and interfaces with databases. Stores are created via
58 | the `derby.createStore()` method. See [Backends](models/backends).
59 |
60 | > A typical setup can be seen in the [derby-starter](https://github.com/derbyjs/derby-starter/blob/master/lib/server.js) project, which is a node module for getting started with Derby.
61 | >
62 | > The [derby-examples](https://github.com/derbyjs/derby-examples) make use of derby-starter to setup their apps.
--------------------------------------------------------------------------------
/md/docs/derby-0.10/apps/routes.md:
--------------------------------------------------------------------------------
1 | # Routes
2 |
3 | Routes map URL patterns to actions. Derby routes are powered by [Express](https://expressjs.com/). Within apps, routes are defined via the `get`, `post`, `put`, and `del` methods of the app created by `derby.createApp()`.
4 |
5 | > `app.get ( routePattern, callback(page, model, params, next) )`
6 | >
7 | > `app.post ( routePattern, callback(page, model, params, next) )`
8 | >
9 | > `app.put ( routePattern, callback(page, model, params, next) )`
10 | >
11 | > `app.del ( routePattern, callback(page, model, params, next) )`
12 | >
13 | > * `routePattern`: A string containing a literal URL, an Express route pattern, or a regular expression. See [Express's routing documentation](https://expressjs.com/guide/routing.html) for more info. Derby also supports transitional routes which can be passed in as an object with two string properties, `from` and `to`.
14 | >
15 | > * `callback`: Function invoked when a request for a URL matching the appropriate HTTP method and pattern is received. Note that this function is called both on the server and the client.
16 | >
17 | > * `page`: Object with the methods [`page.render()`](#pagerender) and `page.redirect()`. All app routes should call one of these two methods or pass control by calling `next()`.
18 | >
19 | > * `model`: Derby model object
20 | >
21 | > * `params`: An object containing the matching URL parameters. The `url`, `query`, and `body` properties typically available on `req` are also added to this object.
22 | >
23 | > * `next`: A function that can be called to pass control to the next matching route. If this is called on the client, control will be passed to the next route defined in the app. If no other routes in the same app match, it will fall through to a server request.
24 |
25 | Express is used directly on the server. On the client, Derby includes Express's route matching module. When a link is clicked or a form is submitted, Derby first tries to render the new URL on the client. AJAX requests will still go directly to the server.
26 |
27 | Derby can also capture form submissions client-side. It provides support for `post`, `put`, and `del` HTTP methods using the same hidden form field [override approach](https://expressjs.com/guide.html#http-methods) as Express.
28 |
29 | ## Page
30 |
31 | Unlike Express, which provides direct access to the `req` and `res` objects created by Node HTTP servers, Derby returns a `page` object. This provide the same interface on the client and the server, so that route handlers may be executed in both environments.
32 |
33 | > `page.render ( viewName )`
34 | >
35 | > * `viewName`: The name of the view to render, see [Namespaces and files](../views/namespaces-and-files) for more details.
36 | >
37 | >
38 | > `page.renderStatic ( statusCode, content )`
39 | >
40 | > * `statusCode`: The HTTP status code to return.
41 | >
42 | > * `content`: A string of HTML to render
43 | >
44 | > `page.redirect ( url, [status] )`
45 | >
46 | > * `url`: Destination of redirect. [Like Express][expressRedirect], may also be the string 'home' (which redirects to '/') or 'back' (which goes back to the previous URL).
47 | >
48 | > * `status`: *(optional)* Number specifying HTTP status code. Defaults to 302 on the server. Has no effect on the client.
49 |
50 | [expressRedirect]: https://expressjs.com/guide.html#res.redirect()
51 |
52 |
53 | ### Middleware
54 |
55 | It is possible to directly use [express middleware](https://expressjs.com/guide/using-middleware.html) and get access to a [Racer model](../models#methods).
56 |
57 |
58 | ## History
59 |
60 | For the most part, updating the URL client-side should be done with normal HTML links. The default action of requesting a new page from the server is canceled automatically if the app has a route that matches the new URL.
61 |
62 | To update the URL after an action other than clicking a link, scripts can call methods on `app.history`. For example, an app might update the URL as the user scrolls and the page loads more content from a paginated list.
63 |
64 | > `app.history.push ( url, [render], [state], [e] )`
65 | >
66 | > `app.history.replace ( url, [render], [state], [e] )`
67 | >
68 | > * `url`: New URL to set for the current window
69 | >
70 | > * `render`: *(optional)* Re-render the page after updating the URL if true. Defaults to true
71 | >
72 | > * `state`: *(optional)* A state object to pass to the `window.history.pushState` or `window.history.replaceState` method. `$render` and `$method` properties are added to this object for internal use when handling `popstate` events
73 | >
74 | > * `e`: *(optional)* An event object whose `stopPropogation` method will be called if the URL can be rendered client-side
75 |
76 | Derby's `history.push` and `history.replace` methods will update the URL via `window.history.pushState` or `window.history.replaceState`, respectively. They will fall back to setting `window.location` and server-rendering the new URL if a browser does not support these methods. The `push` method is used to update the URL and create a new entry in the browser's back/forward history. The `replace` method is used to only update the URL without creating an entry in the back/forward history.
77 |
78 | > `app.history.refresh ( )`
79 | >
80 | > Re-render the current URL client-side
81 |
82 | For convenience, the navigational methods of [`window.history`](https://developer.mozilla.org/en/DOM/window.history) can also be called on `app.history`.
83 |
84 | > `app.history.back ( )`
85 | >
86 | > * Call `window.history.back()`, which is equivalent to clicking the browser's back button
87 |
88 | > `view.history.forward ( )`
89 | >
90 | > * Call `window.history.forward()`, which is equivalent to clicking the browser's forward button
91 |
92 | > `view.history.go ( i )`
93 | >
94 | > * Call `window.history.go()`
95 | >
96 | > * `i`: An integer specifying the number of times to go back or forward. Navigates back if negative or forward if positive
97 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/components/component-class.md:
--------------------------------------------------------------------------------
1 | # Component class
2 |
3 | Derby provides a base class `Component`, from which all component classes inherit. When authoring a component, you can extend Derby's Component class with JavaScript, TypeScript, or CoffeeScript `extends` syntax.
4 |
5 | ```js
6 | const Component = require('derby').Component;
7 | class MyComponent extends Component {
8 | ...
9 | }
10 | app.component(MyComponent);
11 | ```
12 |
13 | For convenience, if you register a class that does not inherit from `Component`, Derby will add `Component.prototype` to your class's prototype chain. In other words, Derby will make sure that your class inherits from `Component` at the time that you call `app.component()`.
14 |
15 | ```js
16 | class MyComponent {
17 | ...
18 | }
19 | app.component(MyComponent);
20 | ```
21 |
22 | ## Component configuration
23 |
24 | Components are configured by defining the following static properties and methods:
25 |
26 | > `MyComponent.view = '/path/to/view'` The relative file path to a template file to load. If the view file is named *index.html* and in the same directory as the controller, `__dirname` can be used
27 |
28 | > `MyComponent.is = 'my-component'` The name to use for the component's view. Often this doesn't need to be specified, because it defaults to the basename of the file or directory.
29 |
30 | > `MyComponent.DataConstructor` Constructor function for setting default values in the component's model data. Properties will be overriden by view attributes.
31 |
32 | > `MyComponent.prototype.init = function(model)` Called immediately before the view is rendered. Data and reactive functions can be initialized on the component's scoped model. This method is invovked both on the server and on the client, so the DOM and browser-only methods may not be used within init().
33 |
34 | > `MyComponent.prototype.create = function(model, dom)` Called in the browser when a component is loaded and inserted into the DOM. This method is never called on the server. DOM-related code and model event listeners should be placed in create().
35 |
36 |
37 | ## Properties
38 |
39 | > `model`: The component's scoped model.
40 |
41 | > `dom`: An instance of Derby's wrapper around DOM methods. This should be used for adding and removing listeners to DOM events rather than native `addEventListener()`. This is important so that Derby can remove listeners when the component is destroyed.
42 |
43 | > `page`: Reference to the current page object, which is the top level controller. A new page object is created on navigation to a new URL.
44 |
45 | > `app`: Reference to the current app object. The app is persistent for the entire session.
46 |
47 | > `parent`: Reference to the containing controller.
48 |
49 | > `context`: The rendering context object.
50 |
51 | > `id`: The unique id assigned to the component.
52 |
53 | > `isDestroyed`: Initially set to `false`. Set to `true` when the component is fully destroyed.
54 |
55 |
56 | ## Methods
57 |
58 | ### Event emission
59 |
60 | Components are Node.js event emitters, so they inherit the `on`, `once`, `emit`, `removeListener`, etc. methods from [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter).
61 |
62 | ### Cleanup
63 |
64 | > `component.destroy()`
65 | >
66 | > Derby calls this method when removing a component's marker comment from the DOM. `destroy()` emits the `'destroy'` event on the component. Listen for the destroy event in order to implement custom cleanup logic. This method should not be invoked manually.
67 |
68 | ```derby
69 |
70 | ```
71 |
72 | ```js
73 | class MyComponent extends Component {
74 | create() {
75 | this.on('destroy', function() {
76 | // Custom cleanup logic
77 | });
78 | }
79 | }
80 | ```
81 |
82 | > `boundFn = component.bind(fn)`
83 | > * `fn` - _Function_ - A function to be invoked with the component as its `this` value. In addition, the function will no longer be invoked once the component is destroyed
84 | > * `boundFn` - _Function_ - Returns a bound function, similar to JavaScript's `Function.bind()`. This function is safer to use in asynchronous code, such as with setTimeout, requestAnimationFrame, or requests to the server, because it won't call back after the component is destroyed. Internally, references to `fn` and the component are removed on `'destroy'`, which allows them to be garbage collected even if a reference to `boundFn` is held.
85 |
86 | ```js
87 | class MyComponent extends Component {
88 | load() {
89 | this.set('loading', true);
90 | setTimeout(this.bind(function() {
91 | // This won't execute if the component has been destroyed
92 | this.set('loading', false);
93 | }), 200);
94 | }
95 | }
96 | ```
97 |
98 | ### Throttling and debouncing
99 |
100 | Derby components have built-in support for common throttling and debouncing patterns. These methods are similar to those provided by general-purpose libraries like Lodash, but they also bind the `this` value to the component, provide added safety by not calling back after a component is destroyed, and release references to `fn` and the component on `'destroy'`, same as `component.bind(fn)`.
101 |
102 | > `throttledFn = component.throttle(fn, [delayArg = 0])`
103 | >
104 | > When passing in a numeric delay, calls the function at most once per that many milliseconds. Like Lodash, the function will be called on the leading and the trailing edge of the delay as appropriate. Unlike Lodash, calls are consistently called via setTimeout and are never synchronous. This should be used for reducing the frequency of ongoing updates, such as scroll events or other continuous streams of events.
105 | >
106 | > Additionally, implements an interface intended to be used with [window.requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame), process.nextTick, or window.setImmediate. If one of these is passed, it will be used to create a single async call following any number of synchronous calls. This mode is typically used to coalesce many synchronous events (such as multiple model events) into a single async event.
107 | >
108 | > Like `component.bind()`, will no longer call back once the component is destroyed, which avoids possible bugs and memory leaks.
109 |
110 | ```js
111 | class MyComponent extends Component {
112 | create() {
113 | // Call this.update() at most once every 75 milliseconds
114 | this.dom.on('scroll', window, this.throttle(this.update, 75));
115 | }
116 | update() {
117 | // Update based on scroll location
118 | }
119 | }
120 | ```
121 |
122 | ```js
123 | class MyComponent extends Component {
124 | create() {
125 | // Call this.update() at most once before each paint (typically 60 times / second)
126 | this.dom.on('scroll', window, this.throttle(this.update, window.requestAnimationFrame));
127 | }
128 | update() {
129 | // Update based on scroll location
130 | }
131 | }
132 | ```
133 |
134 | > `debouncedFn = component.debounce(fn, [delay = 0])`
135 | >
136 | > Suppresses calls until the function is no longer called for that many milliseconds. This should be used for delaying updates triggered by user input, such as window resizing, or typing text that has a live preview or client-side validation. This should not be used for inputs that trigger server requests, such as search autocomplete; use debounceAsync for those cases instead.
137 | >
138 | > Like `component.bind()`, will no longer call back once the component is destroyed, which avoids possible bugs and memory leaks.
139 |
140 | ```derby
141 |
142 |
143 | ```
144 |
145 | ```js
146 | class MyComponent extends Component {
147 | create() {
148 | // Suppress calls until the user has stopped typing for 300 milliseconds
149 | this.dom.on('input', this.textInput, this.debounce(this.update, 300));
150 | }
151 | update() {
152 | // Update based on current value
153 | }
154 | }
155 | ```
156 |
157 | > `asyncDebouncedFn = component.debounceAsync(fn, [delay = 0])`
158 | >
159 | > Like debounce(), suppresses calls until the function is no longer called for that many milliseconds. In addition, suppresses calls while the callback function is running. In other words, the callback will not be called again until the supplied `done()` argument is called. When the debounced function is called while the callback is running, the callback will be called again immediately after `done()` is called. Thus, the callback will always receive the last value passed to the debounced function.
160 | >
161 | > This avoids the potential for multiple callbacks to execute in parallel and complete out of order. It also acts as an adaptive rate limiter. Use this method to debounce any field that triggers an async call as the user types.
162 | >
163 | > Like `component.bind()`, will no longer call back once the component is destroyed, which avoids possible bugs and memory leaks.
164 |
165 | ```derby
166 |
167 |
168 | ```
169 |
170 | ```js
171 | class MyComponent extends Component {
172 | create() {
173 | // Suppress calls until the user has stopped typing for 300 milliseconds
174 | // and the async function has completed
175 | this.dom.on('input', this.textInput, this.debounceAsync(this.search, 300));
176 | }
177 | search(done) {
178 | const query = this.model.get('value');
179 | fetch('/api/search?q=' + query)
180 | .then(response => {
181 | this.model.set('response', response);
182 | })
183 | .catch(err => console.error(err))
184 | // No additional calls to search will happen until done() is called
185 | .finally(done);
186 | }
187 | }
188 | ```
189 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/components/events.md:
--------------------------------------------------------------------------------
1 | # Events
2 |
3 | Functions defined on a property of a controller can be invoked from view expressions or in response to events. As a general pattern, view paths refer to the model when getting values and to the controller when calling functions.
4 |
5 | Functions are looked up on the current component's controller, the page, and the global, in that order. See the [view functions and events](../views/template-syntax/functions-and-events#controller-property-lookup) documentation for more detail.
6 |
7 | ## Lifecycle events
8 |
9 | Default events are triggered during the lifecycle of a component:
10 |
11 | * `init`: Emitted before the component's `init()` function is called.
12 | * `create`: Emitted before the component's `create()` function is called.
13 | * `destroy`: Emitted before the component's `destroy()` function is called.
14 |
15 | If the functions to be called aren't defined on the component, their respective events are still triggered unconditionally.
16 |
17 | ## Custom events
18 |
19 | Components support custom events. Dashes are transformed into camelCase.
20 | ```derby
21 |
22 | ```
23 | ```js
24 | // Equivalent to:
25 | modal.on('close', function() {
26 | self.reset();
27 | });
28 | modal.on('fullView', function() {
29 | back.fade();
30 | });
31 | ```
32 |
33 | ## Emitting events
34 | Components can emit custom events to be handled by their parents.
35 |
36 | ```derby
37 |
38 |
39 | ```
40 |
41 | ```js
42 | //listen
43 | modal.on('fullView', function(foo) {
44 | console.log(foo);
45 | })
46 | //...
47 | //emit
48 | modal.emit("fullView", foo);
49 | ```
50 |
51 |
52 | ## Calling peer component methods
53 |
54 | Components and elements can be set as a property on the current controller with the `as=` HTML attribute ([more detail](../views/template-syntax/paths#controller-properties)). This paired with how controller properties are looked up on function calls makes it easy to connect events on components or elements to methods on other components.
55 |
56 | ```derby
57 |
58 |
59 |
60 | ```
61 |
62 | ```derby
63 |
64 |
65 | ...
66 |
67 | ```
68 |
69 | ## Component event arguments
70 |
71 | Component events implicitly pass through any emitted arguments. These arguments are added after any explicitly specified arguments in the expression.
72 |
73 | ```derby
74 |
75 |
76 |
77 |
78 | ```
79 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/components/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/components/lifecycle.md:
--------------------------------------------------------------------------------
1 | # Component lifecycle
2 |
3 |
4 | Components are defined by writing a view template in HTML and a JavaScript controller class. Calling `app.component(MyComponent)` registers the view and controller with a Derby app. After that, an instance of the component class is created whenever its view is rendered.
5 |
6 |
7 | ## Rendering
8 |
9 | Derby components are designed for efficient server-side HTML and client-side DOM rendering with the same code.
10 |
11 |
12 | ### Client-side rendering
13 |
14 | *1. `new MyComponent.DataConstructor()`:* First, Derby instantiates an object that is used for the component's model data. This class should be defined to set default values that can be used in either the view or the controller of the component. Derby will set two properties, `id` and `$controller`, on this object. `id` is a string id unique to the component, and `$controller` is a reference to the component instance. These properties are set in the model data so that they can be used in the view.
15 |
16 | *2. `new MyComponent(context, data)`:* Derby calls the component's constructor with a rendering context object and the model data instance. As is idiomatic in JavaScript, it is recommended that `super(context, data)` is called at the top, and instance properties are set following that. Note that attributes passed in from the view are not yet set on the model.
17 |
18 | *3. `Component(context, data)`:* Derby's component constructor creates the component's `id` and `model`. If the custom constructor does not call super, Derby calls this method immediately after. Effectively, components behave like there was a call to super at the end of their constructor when super isn't called explicitly.
19 |
20 | *4. Attribute values from the view are set on the model:* Attribute values are passed into compoenents via the view. Derby sets and binds these values to the model after invoking the constructor.
21 |
22 | *5. `'init'` event:* Derby emits an `'init'` event on the component instance before calling the init method. This event is rarely used. However, it is provided in case containing components need to obtain a reference to a component instance before it renders.
23 |
24 | *6. `MyComponent.init(model)`:* Init is called once Derby has completed all steps to initialize the component and before rendering. All custom code that initializes data in the model prior to rendering, such as reactive functions, should be placed within the component's `init()` implementation.
25 |
26 | *7. Rendering:* Following `init()`, Derby renders the component's view and inserts it into the DOM. Hooks defined in the view, such as the `as` attribute for assigning elements and components to controller properties or `on-` attributes for adding event listeners happen at render time as well.
27 |
28 | *8. `'create'` event:* On the client only, Derby emits a `'create'` event on the component instance before calling the create method. Similar to the `'init'` event, this method is provided in case containing components need to obtain a reference to a component instance. However, the create event only happens on the client, and it is emitted after the component is rendered and inserted into the DOM.
29 |
30 | *9. `MyComponent.create(model, dom)`:* Create is called once the component is rendered and inserted into the DOM. Custom code that adds model or DOM event listeners should be placed within the component's `create()` implementation.
31 |
32 |
33 | ### Server-side rendering
34 |
35 | Steps 1-7 of the rendering process are the same for server-side rendering. However, instead of using DOM methods to render, Derby returns a string of HTML. The key difference between client-side and server-side rendering is that `create()` is called only on the client, and server-side rendering happens within Node.js instead of the browser.
36 |
37 | There are a number of differences between Node.js and a browser environment that must be taken into account:
38 |
39 | * Server time and client time will differ. Client-time may differ by small or large amounts, and its accuracy cannot be ensured.
40 |
41 | * Servers do not have a session-appropriate timezone or locale, so JavaScript's `Date` and `Intl` features cannot be used without ensuring that the server and client are using matching implementations and configurations.
42 |
43 | * No DOM methods are available on the server.
44 |
45 | * There is no need to create bindings and event listeners on the server, because data will not be dynamically changing.
46 |
47 | * Servers are multi-tenent and long lived, so be careful to avoid global state in components. This is also a best practice in client-only applications, but it is especially important when code is executed on both the server and the client. On the server, shared state could lead to data being leaked from one session to another, and minor memory leaks in long-lived processes can build up and crash a server.
48 |
49 |
50 | ### Server-side rendering + Client-side attachment
51 |
52 | Out of the box, Derby is optimized for server + client-side rendering. This can greatly improve perceived load time, since the browser can display the application before its scripts have loaded or executed on the client.
53 |
54 | In this type of rendering, the server renders HTML, and the browser creates DOM nodes from HTML. Then, Derby does a special kind of rendering called "attachment," where it does all of the client-side rendering steps. However, it uses the existing DOM nodes in the page rather than creating new DOM nodes.
55 |
56 | Therefore, component code must be deterministic. Pure code, where the same inputs return the same results and there are no side effects, is best. Common pitfalls:
57 |
58 | * Components should not rely on external inputs, such as `Date.now()` or the result of `Math.random()` in rendering, because their results will differ on subsequent calls. You can compute the values ahead of time and store them in the model on `_session` or `_page`, so that the values will be the same when rendered on the server and after the initial page load in the client. You may also choose to render certain values in the client only, by setting them in `create()`.
59 |
60 | * Sorting should use a stable comparison.
61 |
62 | * Rendering components should not modify persistent state or have other side effects.
63 |
64 |
65 | In addition, Derby requires that parsing the HTML in templates produces a matching DOM. Common pitfalls:
66 |
67 | * Templates must be valid HTML. For example, `
`, is invalid HTML and will produce a DOM such as ``. This is because the [`
` element](https://html.spec.whatwg.org/multipage/grouping-content.html#the-p-element) may contain only [phrasing content](https://html.spec.whatwg.org/multipage/dom.html#phrasing-content), and the start of a `
` closes the `
`.
68 |
69 | * Templates must explicitly include all [optional tags](https://html.spec.whatwg.org/multipage/syntax.html#optional-tags). For example, `
` is valid HTML, but it will produce the DOM `
`. For simplicity, Derby does not attempt to implement these rules and requires that optional tags be written out.
70 |
71 | * All non-void elements must be explicitly closed. For example, `
One
Two
` is valid HTML, because an `
` elements' end tags are implied. Derby requires that this be written out fully as `
One
Two
`. ([Void elements](https://html.spec.whatwg.org/multipage/syntax.html#void-elements) like `` only have a start tag and the end tag must not be specified.)
72 |
73 | To test whether an HTML fragment will work in a Derby template, use an [HTML validator](https://validator.nu/) and check that setting then reading it back as `innerHTML` returns the same string.
74 |
75 | ```js
76 | var html = '';
77 | var div = document.createElement('div');
78 | div.innerHTML = html;
79 | html === div.innerHTML;
80 | ```
81 |
82 |
83 | ## Cleanup
84 |
85 | When a binding causes a component to be removed from the DOM, Derby internally calls its `destroy()` method. (This method should not be invoked manually.) Destroying a component removes its DOM listeners, destroys its model data and model listeners, removes references created by `as` attributes in views, and removes all of Derby's internal references to the component and bindings within the component. Each of these is important for avoiding memory leaks.
86 |
87 | Using Derby's built-in features to add DOM listeners, model listeners, and bind asynchronous callbacks generally avoids the need to implement custom cleanup code. If custom cleanup code is needed, it can be implemented by listening to the component's `'destroy'` event or checking whether the component's `isDestroyed` property is `true`.
88 |
89 |
90 | ### Singleton (stateless) components
91 |
92 | Creating a model per component, binding component attributes, and cleaning up component models and bindings can add significant overhead. However, Derby's template syntax is very expressive, and many components can be written in a stateless manner with no need for their own model.
93 |
94 | In this case, it is best to declare the component as a "singleton" component. A singleton component is also implemented with a JavaScript class for a controller, but Derby will only instantiate the class once and reuse the same instance of the class each time the component's view is rendered. Derby will not create a model or other properties on the controller, since its instance can be used in multiple places simultaneously. In addition, rendering a singleton component does not invoke `init()`, `create()`, or `destroy()`.
95 |
96 | Since singleton components do not have a model, only attribute paths may be used in views. Singleton controllers should consist of only pure functions.
97 |
98 | When a component is used many times on a page, such as a repeated item in a list or a commonly used UI element, it is best to write it statelessly for better performance. View partials are the most lightweight, singleton components allow use of custom JavaScript, and full components have their own model state.
99 |
100 | ```derby
101 |
102 |
103 | {{getInitials(@user.fullName)}}
104 |
105 | ```
106 |
107 | ```js
108 | app.component('user-icon', class UserIcon {
109 | static singleton = true;
110 | getInitials(fullName) {
111 | return fullName.split(' ').map(name => name[0]).join('');
112 | }
113 | });
114 | ```
115 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/components/overview.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | Components are the building blocks of Derby applications. A component is a view associated with a controller class. The [view](views) is implemented as a Derby template and the controller is implemented as a JavaScript [class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) or [constructor function](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS). Derby creates an instance of the controller class each time it renders the component view.
4 |
5 |
6 | ## Reuse and organization
7 |
8 | Components are reusable UI pieces, similar to custom HTML elements. In addition, they are the recommended way to structure complex applications as modular parts with clear inputs and outputs. Each significant unit of UI functionality should be its own component.
9 |
10 | Components can be rendered on the server and the client, so the same code can produce static HTML, server-rendered dynamic applications, and client-rendered applications.
11 |
12 |
13 | ## Encapsulation
14 |
15 | Each component has a scoped model in its own namespace. Data or references to the component's parent are passed in via view attributes. If you're familiar with it, this structure is similar to the [Model-View-ViewModel (MVVM) pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel)—a component's scoped model is a ViewModel.
16 |
17 |
18 | ## Tabs Example
19 |
20 | ### index.html
21 | ```derby
22 |
23 |
24 |
25 |
145 |
146 |
147 |
150 | ```
151 |
152 | ```js
153 | app.component('todos-new', class TodosNew {
154 | submit() {
155 | const value = this.model.del('value');
156 | this.emit('submit', value);
157 | }
158 | });
159 |
160 | app.component('todos-list', class TodosList {
161 | add(text) {
162 | if (!text) return;
163 | this.model.push('items', {text});
164 | }
165 | remove(index) {
166 | this.model.remove('items', index);
167 | }
168 | });
169 |
170 | app.component('todos-footer', class TodosFooter {
171 | static singleton = true;
172 | remaining(items) {
173 | if (!items) return 0;
174 | return items.filter(item => !item.done).length;
175 | }
176 | });
177 | ```
178 |
179 |
180 |
181 |
182 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/components/scope.md:
--------------------------------------------------------------------------------
1 | # Scope
2 |
3 | Each component instance has its own scoped model, providing it isolation from model data for other components and remote collection data.
4 |
5 | ## Attributes and data bindings
6 |
7 | The most direct way to get data into a component is to pass in a reference or a literal as a view attribute.
8 |
9 | ```derby
10 |
11 |
12 |
13 |
14 |
15 | {{each data as #user}}
16 |
{{#user.name}}
17 | {{/each}}
18 |
19 | {{num + 10}}
20 | ```
21 |
22 | See [view attributes](../views/template-syntax/view-attributes) for more information.
23 |
24 |
25 | ## Root model
26 |
27 | There are times when accessing data in the root model is desirable from within the component. This can be achieved both in the template and in the controller.
28 |
29 | ```derby
30 |
31 |
32 | {{#root.users[userId]}}
33 | ```
34 |
35 | ```js
36 | var users = this.model.root.get("users");
37 | var user = users[userId];
38 | // or
39 | var $users = this.model.scope("users");
40 | var user = $users.get(userId);
41 | ```
42 |
43 |
44 | ### With block
45 | See the documentation for [with blocks](../views/template-syntax/blocks#with) to pass in data with an alias.
46 |
47 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/components/view-partials.md:
--------------------------------------------------------------------------------
1 | # View partials
2 |
3 | This page goes into more detail about how view partials relate to components. For more general concepts, see the [template syntax](../views/template-syntax) documentation.
4 |
5 | While a component's controller is associated with a single view, it can contain sub-views defined as view partials. Components can also accept other views passed in as attributes.
6 |
7 | ## Scope
8 | By default a view partial inherits the scope where it is instantiated.
9 |
10 | ```derby
11 |
12 | {{foo}}
13 | {{with #root.bar as #bar}}
14 |
15 | {{/with}}
16 |
17 |
18 | i can render {{foo}} and {{#bar}}
19 | ```
20 | A view partial associated with a component follows the [component scope](scope) rules. A view partial used inside a component will inherit the scope of the component.
21 |
22 | ### extend
23 | It is possible to override another component's functionality while preserving its view. You can do this with the `extend` keyword.
24 |
25 |
26 |
27 |
28 |
29 |
30 | ### import
31 | If you just want to reuse a view partial the `import` keyword is probably more appropriate. See the [namespaces and files](../views/namespaces-and-files#structuring-views-in-multiple-files) documentation for more details.
32 |
33 |
34 | ## Programmatic view management
35 |
36 | > `view = this.getView(name)`
37 | > * `name` the name of the view
38 | > * `view` a template object representing the view
39 |
40 | It is possible to access the views in a component's namespace from the controller. This may be used in conjunction with `setAttribute` to override a component's default rendering.
41 | An example use case would be to set a default template and then allow the user of the component to pass in a template to override the default.
42 |
43 | See the [attributes](scope#attributes-vs-model-data) documentation for more information on using `setAttribute`.
44 |
45 |
46 | ## Component tracking
47 | Derby components are tracked in the DOM with an HTML comment tag. This allows components to be responsible for arbitrary DOM content, for example two table rows that otherwise cannot be wrapped by any other DOM elements.
48 |
49 | ```derby
50 |
51 | ```
52 |
53 | ## Debugging
54 |
55 | A relatively quick way to inspect a component for debugging is to find its comment in the browser's DOM inspector.
56 | In modern browsers, clicking on the comment allows you to reference it in the console with `$0`.
57 | Once you have a reference to the comment tag, you can access its controller with `$0.$component` and its model data with `$0.$component.model.get()`
58 |
59 |
60 |
61 | ### derby-debug
62 | There is a plugin which makes accessing your components from the console even more accessible that is recommended for development.
63 | Read more about [derby-debug](https://github.com/derbyjs/derby-debug).
64 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/backends.md:
--------------------------------------------------------------------------------
1 | # Backends
2 |
3 | Racer stores are backed by ShareDB, which is used to persist data, perform queries, keep a journal of all operations, and pub/sub operations and changes to queries. Currently, ShareDB has [two pub/sub adapters](https://github.com/share/sharedb#database-adapters): one for in memory and one for Redis based pub/sub. ShareDB supports in memory or MongoDB storage. The database adapter [ShareDBMongo](https://github.com/share/sharedb-mongo)
4 | is backed by a real Mongo database and full query support. ShareDB is written with support for additional database adapters in mind.
5 |
6 | Getting started with a single-process server and MongoDB:
7 |
8 | ```js
9 | var derby = require('derby');
10 | var ShareDbMongo = require('sharedb-mongo');
11 |
12 | var db = new ShareDbMongo('mongodb://localhost:27017/test');
13 | var backend = derby.createBackend({db: db});
14 | var model = backend.createModel();
15 | ```
16 |
17 | The above examples use the in-process driver by default. In a production environment, you'll want to scale across multiple frontend servers and support updating data in other processes, such as migration scripts and additional services. For this, you should use the [ShareDB Redis pub/sub adapter](https://github.com/share/sharedb-redis-pubsub). ShareDB requires Redis 2.6 or newer, since it uses Lua scripting commands.
18 |
19 | ```js
20 | var derby = require('derby');
21 | var ShareDbMongo = require('sharedb-mongo');
22 | var RedisPubSub = require('sharedb-redis-pubsub');
23 |
24 | var db = new ShareDbMongo('mongodb://localhost:27017/test');
25 | var backend = derby.createBackend({
26 | db: db,
27 | pubsub: new RedisPubSub()
28 | });
29 | var model = backend.createModel();
30 | ```
31 |
32 | See [ShareDBMongo](https://github.com/share/sharedb-mongo) and [ShareDB Redis](https://github.com/share/sharedb-redis-pubsub) documentation for more information on configuration options.
33 |
34 | The Redis driver supports flushing all data from Redis or starting with an empty Redis database with journal and snapshot data in MongoDB. Thus, it is OK to start with a basic deployment using only a single process and add Redis later or to flush the Redis database if it becomes corrupt.
35 |
36 | ## Mapping between database and model
37 |
38 | Racer paths are translated into database collections and documents using a natural mapping:
39 |
40 | ```bash
41 | collection.documentId.documentProperty
42 | ```
43 |
44 | ShareDB Mongo will add the following properties to Mongo documents for internal use:
45 | * `_m.ctime` - Timestamp when the ShareDB document was created
46 | * `_m.mtime` - Timestamp when the ShareDB document was last modified
47 | * `_type` - [OT type](https://github.com/share/sharedb#data-model)
48 | * `_v` - [Snapshot version](https://github.com/share/sharedb#data-model)
49 |
50 | In addition to `ctime` and `mtime`, custom metadata properties can be added to `_m` with middleware that modifies `snapshot.m` in apply or commit.
51 |
52 | Since these underscore-prefixed properties are for ShareDB's internal use, ShareDB Mongo will strip out these properties (`_m`, `_type`, and `_v`) as well as `_id` when it returns the document from Mongo. The `_id` is removed because Racer adds an `id` alias to all local documents. This alias references the `_id` property of the original Mongo document.
53 |
54 | If a document is an object, it will be stored as the Mongo document directly. For example,
55 |
56 | ```js
57 | {
58 | make: "Ford",
59 | model: "Mustang",
60 | year: 1969,
61 | _m: {
62 | ctime: 1494381632731,
63 | mtime: 1494381635994
64 | },
65 | _type: "http://sharejs.org/types/JSONv0",
66 | _v: 12
67 | }
68 | ```
69 |
70 | If it is another type (e.g. [Plaintext OT Type](https://github.com/ottypes/text)), the value will be nested under a property on the Mongo document called `_data`.
71 |
72 | ```js
73 | {
74 | _data: "This is a text message.",
75 | _m: {
76 | ctime: 1494381632731,
77 | mtime: 1494381635994
78 | },
79 | _type: "http://sharejs.org/types/text",
80 | _v: 12
81 | }
82 | ```
83 |
84 | It is not possible to set or delete an entire collection, or get the list of collections via the Racer API.
85 |
86 | ## Loading data into a model
87 |
88 | The `subscribe`, `fetch`, `unsubscribe`, and `unfetch` methods are used to load and unload data from ShareJS. These methods don't return data directly. Rather, they load the data into a model. Once loaded, the data are then accessed via model getter methods.
89 |
90 | `subscribe` and `fetch` both return data initially, but subscribe also registers with pub/sub on the server to receive ongoing updates as the data change.
91 |
92 | > `model.subscribe(items..., callback(err))`
93 | > `model.fetch(items..., callback(err))`
94 | > `model.unsubscribe(items..., callback(err))`
95 | > `model.unfetch(items..., callback(err))`
96 | > * `items` Accepts one or more subscribe-able items, including a document path, scoped model, or query
97 | > * `callback` Calls back once all of the data for each query and document has been loaded or when an error is encountered
98 |
99 | Avoid subscribing or fetching queries by document id like `model.query('users', {_id: xxx})`. You can achieve the same result passing `'users.xxx'` or `model.at('users.xxx')` to subscribe or fetch, and it is much more efficient.
100 |
101 | If you only have one argument in your call to subscribe or fetch, you can also call `subscribe`, `fetch`, `unsubscribe`, and `unfetch` on the query or scoped model directly.
102 |
103 | ```js
104 | var user = model.at('users.' + userId);
105 | var todosQuery = model.query('todos', {creatorId: userId});
106 | model.subscribe(user, todosQuery, function(err) {
107 | if (err) return next(err);
108 | console.log(user.get(), todosQuery.get());
109 | page.render();
110 | });
111 | ```
112 |
113 | Racer internally keeps track of the context in which you call subscribe or fetch, and it counts the number of times that each item is subscribed or fetched. To actually unload a document from the model, you must call the unsubscribe method the same number of times that subscribe is called and the unfetch method the same number of times that fetch is called. However, you generally don't need to worry about calling unsubscribe and unfetch manually.
114 |
115 | Instead, the `model.unload()` method can be called to unsubscribe and unfetch from all of the subscribes and fetches performed since the last call to unload. Derby calls this method on every full page render right before entering a route. By default, the actual unsubscribe and unfetch happens after a short delay, so if something gets resubscribed during routing, the item will never end up getting unsubscribed and it will callback immediately.
116 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/data-loading-contexts.md:
--------------------------------------------------------------------------------
1 | # Data loading contexts
2 |
3 | As data is loaded into a model with calls to fetch and subscribe, Racer tracks the number of fetches and subscribes per document path and query. Data is not removed from a model until it is released by calling unfetch and unsubscribe the matching number of times for each document or query. For example, after calling `subscribe()` on a query twice, then `unsubscribe()` once, the query would remain subscribed. It would be unsubscribed and its data would be removed from the model only after `unsubscribe()` was called once more.
4 |
5 | This behavior is helpful, since multiple parts of an application may need the same resource, but they may want perform data loading and unloading independently. For example, an edit dialog may be opened and closed while some of the same data may be displayed in a list; or a migration script may fetch data in batches in order to process a large amount of data without loading all of it into memory simultaneously.
6 |
7 | Contexts provide a way to track a group of related fetches and subscribes. In addition, they provide an `unload()` method that unfetches and unsubscribes the corresponding number of times. By default, all fetches and subscribes happen within the `'root'` context. Additional context names may be used to isolate the loading and unloading of data within the same model for independent purposes.
8 |
9 | > `childModel = model.context(name)`
10 | > * `name` A string uniquely identifying a context. Calling `model.context()` again with the same string will refer to the same context. By default, models have the context name `'root'`
11 | > * `childModel` Returns a model with a context of `name`, overriding the parent model's context name. All fetch, subscribe, and unload actions performed on this childModel will have this context
12 |
13 | > `model.unload([name])`
14 | > * `name` *(optional)* Unfetch and unsubscribe from all documents and queries for the corresponding number of times they were fetched and subscribed. This will end subscriptions and remove the data from the model if no remaining fetches or subscribes hold the data in the model under a different context. Defaults to the current model context name. Specifying a `name` argument overrides the default
15 |
16 | > `model.unloadAll()`
17 | > * Unload each context within a model. Results in all remotely loaded data being removed from a model. (Data within [local collections](paths#local-and-remote-collections) will remain.)
18 |
19 | ## Usage example
20 |
21 | ```js
22 | function openTodos(model) {
23 | // Create a model with a load context inheriting from the current model
24 | var dialogModel = model.context('todosDialog');
25 | // Load data
26 | var userId = dialogModel.scope('_session.userId').get();
27 | var user = dialogModel.scope('users.' + userId);
28 | var todosQuery = dialogModel.query('todos', {creatorId: userId});
29 | dialogModel.subscribe(user, todosQuery, function(err) {
30 | if (err) throw err;
31 | // Delay display until required data is loaded
32 | dialogModel.set('showTodos', true);
33 | });
34 | }
35 |
36 | function closeTodos(model) {
37 | model.set('showTodos', false);
38 | // Use the same context name to unsubscribe
39 | model.unload('todosDialog');
40 | }
41 | ```
42 |
43 | ## Automatic unloading on page navigation
44 |
45 | Derby uses Racer model contexts to unload the data for the previous page render when it performs client-side routing and a full-page render. When moving away from a page and before calling into the route for the new page, Derby calls `unloadAll()`, removing the data from all subscribes and fetches performed on the prior page.
46 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/events.md:
--------------------------------------------------------------------------------
1 | # Events
2 |
3 | Model events are based on the standard [Node.js EventEmitter](https://nodejs.org/docs/latest/api/events.html) methods, and they support the same methods: `on`, `once`, `removeListener`, `emit`, etc.
4 |
5 | ## Mutation events
6 |
7 | Racer emits events whenever it mutates data via `model.set()`, `model.push()`, etc. It also emits events when data is remotely updated via a subscription. These events provide an entry point for an app to react to a specific data mutation or pattern of data mutations. The events might not be exactly the same as the methods that created them, since they can be transformed via OT.
8 |
9 | `model.on()` and `model.once()` accept a second argument for these mutation events. The second argument is a path pattern that will filter emitted events, calling the handler function only when a mutator matches the pattern. Path patterns support a single segment wildcard (`*`) anywhere in a path, and a multi-segment wildcard (`**`) at the end of the path. The multi-segment wildcard alone (`'**'`) matches all paths.
10 |
11 | > `listener = model.on(method, path, [options], eventCallback)`
12 | > * `method` Name of the mutator method: `'change'`, `'insert'`, `'remove'`, `'move'`, `'load'`, `'unload'`, or `'all'`
13 | > * `path` Pattern matching the path being mutated. For example: `'_page.user'`, `'users.*.name'`, `'users.*'`, `'users.**'` / `'users**'`, or `'**'`. `**` is valid only by itself or at the end of the path.
14 | > * `options` (optional)
15 | > * `useEventObjects` - If true, the callback is called with a structured event object instead of with a variable number of arguments. _Introduced in [racer@0.9.6](https://github.com/derbyjs/racer/releases/tag/v0.9.6)._
16 | > * `eventCallback` Function to call when a matching method and path are mutated
17 | > * Returns `listener` - the listener function subscribed to the event emitter. This is the function that should be passed to `model.removeListener`
18 |
19 | ### `eventCallback` with `{useEventObjects: true}`
20 |
21 | _Introduced in [racer@0.9.6](https://github.com/derbyjs/racer/releases/tag/v0.9.6)._
22 |
23 | > `eventCallback(event, captures)`
24 | > * `event` - _Object_ - An instance of an Event object (see below)
25 | > * `captures` - _Array_ - The captured path segments, one item per wildcard in the pattern. Each `'*'` results in a string, and a `'**'` results in a sub-array of strings.
26 |
27 | Event objects:
28 |
29 | > `ChangeEvent { value, previous, passed }`
30 | > * `type: 'change'`
31 | > * `value` The current value at the path that was changed. Will be `undefined` for a deletion.
32 | > * `previous` The previous value at the path. Will be `undefined` if the path was previously unset.
33 |
34 | > `InsertEvent { index, values, passed }`
35 | > * `type: 'insert'`
36 | > * `index` The index at which items were inserted
37 | > * `values` An array of values that were inserted. Always an array, even if only one item was pushed, unshifted, or inserted.
38 |
39 | > `RemoveEvent { index, removed, passed }`
40 | > * `type: 'remove'`
41 | > * `index` The index at which items were removed
42 | > * `removed` An array of values that were removed. Always an array, even if only one item was popped, shifted, or removed
43 |
44 | > `MoveEvent { from, to, howMany, passed }`
45 | > * `type: 'move'`
46 | > * `from` The index from which items were moved
47 | > * `to` The index to which items were moved
48 | > * `howMany` How many items were moved
49 |
50 | > `LoadEvent { document, passed }`
51 | > * `type: 'load'`
52 | > * `document` This event fires when a document is loaded via a subscription or fetch. This the value of the newly loaded document object.
53 |
54 | > `UnloadEvent { previousDocument, passed }`
55 | > * `type: 'unload'`
56 | > * `previousDocument` This event fires when a document is removed from the model via unsubscribe or unfetch. This is the value of the document object that was unloaded.
57 |
58 | The `event.type` is useful for distinguising the actual event type when listening to `'all'`.
59 |
60 | ```js
61 | // Matches model.push('messages', message)
62 | model.on('insert', 'messages', {useEventObjects: true}, function(insertEvent) {
63 | console.log(insertEvent.values, 'inserted at index', insertEvent.index);
64 | });
65 |
66 | // Matches model.set('todos.4.completed', true), etc.
67 | model.on('change', 'todos.*.completed', {useEventObjects: true}, function(changeEvent, captures) {
68 | console.log('todos.' + captures[0] + ' set to ' + changeEvent.value);
69 | });
70 |
71 | // Matches all events
72 | model.on('all', '**', {useEventObjects: true}, function(event, captures) {
73 | var starStarSegments = captures[0];
74 | console.log(event.type + ' at ' + starStarSegments.join('.') + ':', event);
75 | });
76 | ```
77 |
78 | ### `eventCallback` when `useEventObjects` is false or undefined
79 |
80 | The event callback receives a number of arguments based on the path pattern and method. The arguments are:
81 |
82 | > `eventCallback([captures...], [eventType], args..., passed)`
83 | > * `captures` The path segment or segments that is passed in only when matching wildcards in the path pattern
84 | > * `eventType` Only the `'all'` event adds the emitted event name after the captures and before the args
85 | > * `args` Event specific arguments. See below
86 | > * `passed` An object with properties provided via `model.pass()`. See description below
87 |
88 | Callbacks for each event type:
89 |
90 | > `changeCallback([captures...], value, previous, passed)`
91 | > * `value` The current value at the path that was changed. Will be `undefined` for objects that were deleted
92 | > * `previous` The previous value at the path. Will be `undefined` for paths set for the first time
93 |
94 | > `insertCallback([captures...], index, values, passed)`
95 | > * `index` The index at which items were inserted
96 | > * `values` An array of values that were inserted. Always an array, even if only one item was pushed, unshifted, or inserted
97 |
98 | > `removeCallback([captures...], index, removed, passed)`
99 | > * `index` The index at which items were removed
100 | > * `removed` An array of values that were removed. Always an array, even if only one item was popped, shifted, or removed
101 |
102 | > `moveCallback([captures...], from, to, howMany, passed)`
103 | > * `from` The index from which items were moved
104 | > * `to` The index to which items were moved
105 | > * `howMany` How many items were moved
106 |
107 | > `loadCallback([captures...], document, passed)`
108 | > * `document` This event fires when a document is loaded via a subscription or fetch. It emits the value of the newly loaded document object
109 |
110 | > `unloadCallback([captures...], previousDocument, passed)`
111 | > * `previousDocument` This event fires when a document is removed from the model via unsubscribe or unfetch. It emits the value of the document object that was unloaded
112 |
113 | ```js
114 | // Matches model.push('messages', message)
115 | model.on('insert', 'messages', function(index, [message]) {
116 | ...
117 | });
118 |
119 | // Matches model.set('todos.4.completed', true), etc.
120 | model.on('change', 'todos.*.completed', function(todoId, isComplete) {
121 | ...
122 | });
123 |
124 | // Matches all events - `path` and `event` are passed in to the event callback
125 | model.on('all', '**', function(path, event, args...) {
126 | ...
127 | });
128 | ```
129 |
130 | ### Passing data to event listeners
131 |
132 | > `model.pass(object)`
133 | > * `object` An object whose properties will each be set on the `passed` argument
134 |
135 | `model.pass()` can be chained before calling a mutator method to pass an argument to model event listeners. You must pass it an object with a property that identifies the name of the parameter.
136 |
137 | This value is only passed to local listeners, and it is not sent to the server or other clients. It is typically used to identify the originator of a particular mutation so that multiple responses to the same change and infinite loops may be avoided. Such loops could occur for listeners that respond to paths that they may modify.
138 |
139 | On a string insert or string remove mutation, a `'change`' event is emitted, since strings are immutable values, and inserting or removing from a string requires changing its entire value. However, detail on what specifically was inserted or removed is neccessary to implement view bindings properly for realtime collaborative text editing. This additional information is added to the `passed` object. On a string insert, the passed object has an additional property of `$stringInsert: {index: Number, text: String}`. On a string remove, the passed object has an additional property of `$stringRemove: {index: Number, howMany: Number}`.
140 |
141 | ```js
142 | // Logs:
143 | // 'red', {}
144 | // 'green', {message: 'hi'}
145 |
146 | model.on('change', 'color', function(value, previous, passed) {
147 | console.log(value, passed);
148 | });
149 | model.set('color', 'red');
150 | model.pass({message: 'hi'}).set('color', 'green');
151 | ```
152 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/filters-and-sorts.md:
--------------------------------------------------------------------------------
1 | # Filters and sorts
2 |
3 | Filters create a live-updating list from items in an object. The results automatically update as the input items change.
4 |
5 | > `filter = model.filter(inputPath, [additionalInputPaths...], [options], fn)`
6 | > * `inputPath` A path pointing to an object or array. The path's values will be retrieved from the model via `model.get()`, and then each item will be checked against the filter function
7 | > * `additionalInputPaths` *(optional)* Other parameters can be set in the model, and the filter function will be re-evaluated when these parameters change as well
8 | > * `options:`
9 | > * `skip` The number of first results to skip
10 | > * `limit` The maximum number of results. A limit of zero is equivalent to no limit
11 | > * `fn` A function or the name of a function defined via `model.fn()`. The function should have the arguments `function(item, key, object, additionalInputs...)`. Like functions for [`array.filter()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), the function should return true for values that match the filter
12 |
13 | ```js
14 | app.get('/search-pants', function(page, model, params, next) {
15 | model.subscribe('pants', function(err) {
16 | if (err) return next(err);
17 | model.filter('pants', 'pricing', 'color',
18 | // evaluate whether a pants item matches the search options
19 | function(item, pantsId, pants, pricing, color) {
20 | return item.price >= pricing.minimum
21 | && item.price <= pricing.maximum
22 | && item.color == color;
23 | }
24 | ).ref('_page.pantsList'); // bind the output of the filter
25 | page.render('pants');
26 | });
27 | });
28 | ```
29 |
30 | If `model.filter()` is called with `null` for the function, it will create a list out of all items in the input object. This can be handy as a way to render all subscribed items in a collection, since only arrays can be used as an input to `{{each}}` template tags.
31 |
32 | > `filter = model.sort(inputPath, [options], fn)`
33 | > * `inputPath` A path pointing to an object or array. The path's values will be retrieved from the model via `model.get()`, and then each item will be checked against the filter function
34 | > * `options:`
35 | > * `skip` The number of first results to skip
36 | > * `limit` The maximum number of results. A limit of zero is equivalent to no limit
37 | > * `fn` A function or the name of a function defined via `model.fn()`. The function should should be a compare function with the arguments `function(a, b)`. It should return the same values as compare functions for [`array.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
38 |
39 | There are two default named functions defined for sorting, `'asc'` and `'desc'`. These functions compare each item with Javascript's less than and greater than operators. See MDN for more info on [sorting non-ASCII characters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Sorting_non-ASCII_characters).
40 |
41 | You may define functions to be used in `model.filter()` and `model.sort()` via [`model.fn()`](functions#named-functions).
42 |
43 | A filter may have both a filter function and a sort function by chaining the two calls:
44 |
45 | ```js
46 | app.on('model', function(model) {
47 | model.fn('expensiveItem', function(item) {
48 | return item.price > 100;
49 | });
50 | model.fn('priceSort', function(a, b) {
51 | return b.price - a.price;
52 | });
53 | });
54 |
55 | app.get('/expensive-pants', function(page, model, params, next) {
56 | model.subscribe('pants', function(err) {
57 | if (err) return next(err);
58 | var filter = model.filter('pants', 'expensiveItem')
59 | .sort('priceSort');
60 | filter.ref('_page.expensivePants');
61 | page.render('pants');
62 | });
63 | });
64 | ```
65 |
66 | ## Methods
67 |
68 | The output of a filter is typically used by creating a reference from it. This sets the data in the model and keeps it updated.
69 |
70 | > `scoped = filter.ref(path)`
71 | > * `path` The path at which to create a refList of the filter's output
72 | > * `scoped` Returns a model scoped to the output path of the ref
73 |
74 | The filter's current value can also be retrieved directly via `filter.get()`.
75 |
76 | > `results = filter.get()`
77 | > * `results` Returns an array of results matching the filter
78 |
79 | As well as by updating its input paths, a filter can be recomputed manually by calling its `filter.update()` method. This can also be used to perform pagination, since the the `filter.skip` and `filter.limit` properties can be modified followed by calling `filter.update()`.
80 |
81 | > `filter.update()`
82 |
83 | ```js
84 | var filter = model.sort('items', {skip: 0, limit: 10}, function(a, b) {
85 | if (a && b) return a.score - b.score;
86 | });
87 | // Logs first 10 items
88 | console.log(filter.get());
89 |
90 | filter.skip += filter.limit;
91 | filter.update();
92 | // Logs next 10 items
93 | console.log(filter.get());
94 | ```
95 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/getters.md:
--------------------------------------------------------------------------------
1 | # Getters
2 |
3 | Data in the model is accessed with `model.get()`. This method returns values by reference, based on the model's scope and/or the path passed to the get method. Models allow getting undefined paths. The get method will return `undefined` and will not throw when looking up a property below another property that is undefined.
4 |
5 | Internally, model data is represented as collections of documents. Collections must be objects, and documents may be of any type, but are also typically objects. A document's data at a point in time is referred to as its snapshot. This structure allows for some documents to be remote documents synced with the server and some to be local documents only in the client. It also means that models are broken into a similar structure as database collections or tables.
6 |
7 | As model document snapshots change from local or remote mutations, the `model.root.data` object is updated. `model.get()` traverses through the properties of the model's data to lookup and return the appropriate value.
8 |
9 | ```js
10 | model.get('_session.account') === model.root.data._session.account;
11 | ```
12 |
13 | > `value = model.get([path])`
14 | > * `path` *(optional)* Path of object to get. Not supplying a path will return all data in the model starting from the current scope
15 | > * `value` Current value of the object at the given path. Note that objects are returned by reference and should not be modified directly
16 |
17 | > `shallowCopy = model.getCopy([path])`
18 | > * `path` *(optional)* Path of object to get
19 | > * `shallowCopy` Shallow copy of current value, going only one level deep when returning an object or array
20 |
21 | > `deepCopy = model.getDeepCopy([path])`
22 | > * `path` *(optional)* Path of object to get
23 | > * `deepCopy` Deep copy of current value
24 |
25 | ## Values returned by reference
26 |
27 | `model.get()` returns values by reference. Racer will fail to function correctly if data in the model is manipulated directly instead of via its mutator methods, such as `model.set()`. You should *never* mutate objects returned from `model.get()` directly.
28 |
29 | As a convenience, Racer also provides a `model.getCopy()` method that returns a shallow copy and `model.getDeepCopy()` method that returns a deep copy. It is safe to mutate copied objects. Changes in these objects can then be updated back into the model using `model.setDiffDeep()`.
30 |
31 | ```js
32 | // WARNING: Do NOT directly manipulate objects in the model
33 | var user = model.get('users.' + userId);
34 | user.name = 'John';
35 |
36 | // Instead, use the model setter methods
37 | var user = model.get('users.' + userId);
38 | model.set('users.' + userId + '.name', 'John');
39 |
40 | // Or, get a copy and set the difference
41 | var user = model.getDeepCopy('users.' + userId);
42 | user.name = 'John';
43 | model.setDiffDeep('users.' + userId, user);
44 | ```
45 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/overview.md:
--------------------------------------------------------------------------------
1 | # Models
2 |
3 | DerbyJS models are provided by [Racer](https://github.com/derbyjs/racer), a realtime model synchronization engine. By building on ShareDB, Racer enables multiple users and services to interact with the same data objects with realtime conflict resolution. Racer models have a simple getter/setter and event interface for writing application logic.
4 |
5 | ## Racer and ShareDB
6 |
7 | Racer provides a single interface for working with local data stored in memory and remote data synced via ShareDB. It works equally well on the server and the browser, and it is designed to be used independently from DerbyJS. This is useful when writing migrations, data-only services, or integrating with different view libraries.
8 |
9 | Remotely synced data is stored via [ShareDB](https://github.com/share/sharedb), which means that different clients can modify the same data at the same time. ShareDB uses [Operational Transformation (OT)](https://en.wikipedia.org/wiki/Operational_transformation) to automatically resolve conflicts in realtime or offline.
10 |
11 | On the server, Racer provides a `store`, which configures a connection to a database and pub/sub adapter. Every store connected to the same database and pub/sub system is synchronized in realtime.
12 |
13 | Stores create `model` objects. Models have a synchronous interface similar to interacting directly with objects. They maintain their own copy of a subset of the global state. This subset is defined via [subscriptions](models/backends#loading-data-into-a-model) to certain queries or documents. Models perform operations independently, and they automatically synchronize their state.
14 |
15 | Models emit events when their contents are updated, which DerbyJS uses to update the view in realtime.
16 |
17 | ## Creating models
18 |
19 | Derby provides a model when calling application routes. On the server, it creates an empty model from the `store` associated with an app. When the server renders the page, the model is serialized. It is then reinitialized into the same state on the client. This model object is passed to app routes rendered on the client.
20 |
21 | Derby uses the model supplied by the store.modelMiddleware by calling `req.getModel()`. To pass data from server-side express middleware or routes, the model can be retrieved via this same method and data can be set on it before passing control to the app router.
22 |
23 | If you would like to get or set data outside of the app on the server, you can create models directly via `store.createModel()`.
24 |
25 | > `model = store.createModel(options)`
26 | > * `options:`
27 | > * `fetchOnly` Set to true to make model.subscribe calls perform a fetch instead
28 | > * `model` Returns a model instance associated with the given store
29 |
30 | ## Store
31 |
32 | Typically, a project will have only one store, even if it has multiple apps. It is possible to have multiple stores, but a model can be associated with only a single store, and a page can have only a single model.
33 |
34 | > `store = derby.createStore(options)`
35 | > * `options` See the [Backends](backends) section for information on configuration
36 | > * `store` Returns a Racer store instance
37 |
38 | ### Methods
39 |
40 | > `middleware = store.modelMiddleware()`
41 | > * `middleware` Returns a connect middleware function
42 |
43 | The model middleware adds a `req.getModel()` function which can be called to create or get a model (if one was already created) for a given request. It also closes this model automatically at the end of the request.
44 |
45 | Model's created from `req.getModel()` specify the option `{fetchOnly: true}`. This means that calls to `model.subscribe()` actually only fetch data and don't subscribe. This is more efficient during server-side rendering, since the model is only created for long enough to handle the route and render the page. The model then gets subscribed when it initializes in the browser.
46 |
47 | ```js
48 | var expressApp = express();
49 | expressApp.use(store.modelMiddleware());
50 |
51 | expressApp.get('/', function(req, res, next) {
52 | var model = req.getModel();
53 | });
54 | ```
55 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/paths.md:
--------------------------------------------------------------------------------
1 | # Paths
2 |
3 | All model operations happen on paths which represent nested JSON objects. These paths must be globally unique within a particular database and Redis journal.
4 |
5 | For example, the model data:
6 |
7 | ```js
8 | {
9 | title: 'Fruit store',
10 | fruits: [
11 | { name: 'banana', color: 'yellow' },
12 | { name: 'apple', color: 'red' },
13 | { name: 'lime', color: 'green' }
14 | ]
15 | }
16 | ```
17 |
18 | Would have paths like `title`, `fruits.1`, and `fruits.0.color`. Any path segment that is a number must be an index of an array.
19 |
20 | > **WARNING** If you want to use an id value that is a number as a path segment, be careful to prefix this with another character, such as `_` before setting it. Otherwise, you will accidentally create a gigantic array and probably run out of memory. For example, use a path like: `items._1239182389123.name` and never `items.1239182389123.name`.
21 |
22 | ## Local and remote collections
23 |
24 | Collection names (i.e. the first path segment) that start with an underscore (`_`) or dollar sign (`$`) are local to a given model and are not synced. All paths that start with another character are remote, and will be synced to servers and other clients via ShareJS. Collections that begin with dollar signs are reserved for use by Racer, Derby, or extensions, and should not be used for application data.
25 |
26 | Almost all non-synced data within an application should be stored underneath the `_page` local collection. This enables Derby to automatically cleanup as the user navigates between pages. Right before rendering a new page, Derby calls `model.destroy('_page')`, which removes all data, references, event listeners, and reactive functions underneath the `_page` collection. If you have some data that you would like to be maintained between page renders, it can be stored underneath a different local collection. This is useful for setting data on the server, such as setting `_session.userId` in authentication code. However, be very careful when storing local data outside of `_page`, since bleeding state between pages is likely to be a source of unexpected bugs.
27 |
28 | ## Scoped models
29 |
30 | Scoped models provide a more convenient way to interact with commonly used paths. They support the same methods, and they provide the path argument to accessors, mutators, event methods, and subscription methods. Also, wherever a path is accepted in a racer method, a scoped model can typically be used as well.
31 |
32 | > `scoped = model.at(subpath)`
33 | > * `subpath` The relative reference path to set. The path is appended if called on a scoped model
34 | > * `scoped` Returns a scoped model
35 |
36 | > `scoped = model.scope([path])`
37 | > * `path` *(optional)* The absolute reference path to set, or the root path by default. This will become the scope even if called on a scoped model. May be called without a path to get a model scoped to the root
38 | > * `scoped` Returns a scoped model
39 |
40 | > `scoped = model.parent([levels])`
41 | > * `levels` *(optional)* Defaults to 1. The number of path segments to remove from the end of the reference path
42 | > * `scoped` Returns a scoped model
43 |
44 | > `path = model.path([subpath])`
45 | > * `subpath` *(optional)* A relative reference path to append. Defaults to the current path
46 | > * `path` Returns the reference path if applicable
47 |
48 | > `isPath = model.isPath(subpath)`
49 | > * `subpath` A relative reference path or scoped model
50 | > * `isPath` Returns true if the argument can be interpreted as a path, false otherwise
51 |
52 | > `segment = model.leaf()`
53 | > * `segment` Returns the last segment for the reference path. Useful for getting indices, ids, or other properties set at the end of a path
54 |
55 | ```js
56 | room = model.at('_page.room');
57 |
58 | // These are equivalent:
59 | room.at('name').set('Fun room');
60 | room.set('name', 'Fun room');
61 |
62 | // Logs: {name: 'Fun room'}
63 | console.log(room.get());
64 | // Logs: 'Fun room'
65 | console.log(room.get('name'));
66 | ```
67 |
68 | ## GUIDs
69 |
70 | Models provide a method to create globally unique ids. These can be used as part of a path or within mutator methods.
71 |
72 | > `guid = model.id()`
73 | > * `guid` Returns a globally unique identifier that can be used for model operations
74 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/queries.md:
--------------------------------------------------------------------------------
1 | # Queries
2 |
3 | Racer can fetch or subscribe to queries based on a model value or a database-specific query. When fetching or subscribing to a query, all of the documents associated with that query are also fetched or subscribed.
4 |
5 | > `query = model.query(collectionName, path)`
6 | > * `collectionName` The name of a collection from which to get documents
7 | > * `path` A model path whose value contains a documentId or an array of documentIds
8 |
9 | > `query = model.query(collectionName, databaseQuery)`
10 | > * `collectionName` The name of a collection from which to get documents
11 | > * `databaseQuery` A query in the database native format, such as a MonogDB query
12 |
13 | # MongoDB query format
14 |
15 | The `sharedb-mongo` adapter supports most MongoDB queries that you could pass to the Mongo `find()` method. See the [Mongo DB query documentation](https://docs.mongodb.org/manual/core/read-operations/#read-operations-query-document) and the [query selectors reference](https://docs.mongodb.org/manual/reference/operator/#query-selectors). Supported MongoDB cursor methods must be passed in as part of the query. `$sort` should be used for sorting, and skips and limits should be specified as `$skip` and `$limit`. There is no `findOne()` equivalent—use `$limit: 1` instead.
16 |
17 | Note that projections, which are used to limit the fields that a query returns, may not be defined in the query. Please refer to the [guide on using projections](https://github.com/derbyparty/derby-faq/tree/master/en#i-dont-need-all-collections-fields-in-a-browser-how-to-get-only-particular-fields-collections-projection), which you can follow if you only want specific fields of a document transferred to the browser.
18 |
19 | ## Query results
20 |
21 | After a query is subscribed or fetched, its results can be returned directly via `query.get()`. It is also possible to create a live-updating results set in the model via `query.ref()`.
22 |
23 | > `results = query.get()`
24 | > * `results` Creates and returns an array of each of the document objects matching the query
25 |
26 | > `scoped = query.ref(path)`
27 | > * `path` Local path at which to create an updating refList of the queries results
28 | > * `scoped` Returns a model scoped to the path at which results are output
29 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/reactive-functions.md:
--------------------------------------------------------------------------------
1 | # Reactive functions
2 |
3 | Reactive functions provide a simple way to update a computed value whenever one or more objects change. While model events respond to specific model methods and path patterns, reactive functions will be re-evaluated whenever any of their inputs or nested properties change in any way.
4 |
5 | Reactive functions may be run any number of times, so they should be [pure functions](https://en.wikipedia.org/wiki/Pure_function). In other words, they should always return the same results given the same input arguments, and they should be side effect free. By default, the inputs to the function are retrieved directly from the model, so be sure not to modify any object or array input arguments. For example, slice an array input before you sort it. The output of the model function is deep cloned by default.
6 |
7 | To execute a model function, you then call `model.start()` or `model.evaluate()`.
8 | * `evaluate()` runs a function once and returns the result.
9 | * `start()` also sets up event listeners that continually re-evaluate the
10 | * function whenever any of its input or output paths are changed.
11 |
12 | > ```
13 | > value = model.start(path, inputPaths, [options], fn)
14 | > value = model.evaluate(inputPaths, [options], fn)
15 | > ```
16 | > ```
17 | > // Legacy (racer <= 0.9.5)
18 | > value = model.start(path, inputPaths..., [options], fn)
19 | > value = model.evaluate(inputPaths..., [options], fn)
20 | > ```
21 | >
22 | > * `path` - _string | ChildModel_ - The output path at which to set the value,
23 | > keeping it updated as input paths change
24 | > * `inputPaths` - _Array_ - One or more paths whose values
25 | > will be retrieved from the model and passed to the function as inputs
26 | > * `options` - _Object_ (optional)
27 | > * `copy` - Controls automatic deep copying of the inputs and output of the
28 | > function. _Model#evaluate never deep-copies output, since the return
29 | > value is not set onto the model._
30 | > - `'output'` (default) - Deep-copy the return value of the function
31 | > - `'input'` - Deep-copy the inputs to the function
32 | > - `'both'` - Deep-copy both inputs and output
33 | > - `'none'` - Do not automatically copy anything
34 | > * `mode` - The `model.set*` method to use when setting the output. _This has
35 | > no effect in Model#evaluate._
36 | > - `'diffDeep'` (default) - Do a recursive deep-equal comparison on old
37 | > and new output values, attempting to issue fine-grained ops on subpaths
38 | > where possible.
39 | > - `'diff` - Do an identity comparison (`===`) on the output value, and do
40 | > a simple set if old and new outputs are different.
41 | > - `'arrayDeep'` - Compare old and new arrays item-by-item using a
42 | > deep-equal comparison for each item, issuing top-level array insert,
43 | > remove, and move ops as needed. Unlike `'diffDeep'`, this will _not_
44 | > issue ops deep inside array items.
45 | > - `'array'` - Compare old and new arrays item-by-item using identity
46 | > comparison (`===`) for each item, issuing top-level array insert,
47 | > remove, and move ops as needed.
48 | > * `async` - _boolean_ - If true, then upon input changes, defer evaluation
49 | > of the function to the next tick, instead of immediately evaluating the
50 | > function upon each input change. _Introduced in [racer@0.9.5](https://github.com/derbyjs/racer/releases/tag/v0.9.5)._
51 | > - This can improve UI performance when multiple inputs to a reactive
52 | > function will change in the same event loop, as `async: true` will
53 | > mean the function only needs be evaluated once instead of N times.
54 | > - _Warning:_ Avoid using `async: true` if there's any controller code
55 | > that does a `model.get()` on the output path or any paths downstream
56 | > of the output, since changes to an input path won't immediately result
57 | > in the output being updated.
58 | > * `fn` - _Function | string_ - A function or the name of a function defined
59 | > via `model.fn()`
60 | > * The function gets invoked with the values at the input paths, one input
61 | > per argument, and should return the computed output value.
62 | > * It should be a synchronous [pure function](https://en.wikipedia.org/wiki/Pure_function).
63 | > - One common side effect to avoid is `Array#sort` on an input array, since
64 | > that sorts the array in-place. If you need to do a sort, make a shallow
65 | > copy via `array.slice()` first, or use a sorting library that returns a
66 | > new array instead of sorting in-place.
67 | > - The function will be called both in Node and in the browser, so avoid
68 | > using functions whose behavior is implementation-dependent, such as the
69 | > one-argument form of [`String#localeCompare`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
70 | > - The function might get called with some inputs `undefined`, so be
71 | > defensive and check inputs' existence before using them.
72 | > * Return `value` - The initial value computed by the function
73 |
74 | > `model.stop(path)`
75 | > * `path` The path at which the output should no longer update. Note that the value is not deleted; it is just no longer updated
76 |
77 | In DerbyJS, `model.start()` functions should typically be established in the `init` method of a component. This method is called both before rendering on the server and then again before rendering in the browser. These reactive functions will be stopped as soon as the component is destroyed, which happens automatically when the component is removed from the page.
78 |
79 | ```js
80 | MyComponent.prototype.init = function(model) {
81 | model.start('total', 'first', 'second', function sum(x, y) {
82 | return (x || 0) + (y || 0);
83 | });
84 | };
85 | ```
86 |
87 | ## Two-way reactive functions
88 |
89 | Most reactive functions define a getter only. You should treat their output as read only. In addition, it is possible to define two-way reactive functions with both a setter and a getter. Note that this is a more advanced pattern and should not be used unless you are confident that it is a strong fit for your use case.
90 |
91 | ```js
92 | // Model functions created with just a function act as getters only.
93 | // These functions update the output path when any input changes
94 | model.fn('expensiveItems', function(items) {
95 | return items && items.filter(function(item) {
96 | return item.price > 100;
97 | });
98 | });
99 |
100 | // It is also possible to define both a getter and a setter function
101 | // if the input values may be computed from setting the output
102 | model.fn('fullName', {
103 | // The getter function gets the current value of each of the input
104 | // arguments when any input might have changed
105 | get: function(firstName, lastName) {
106 | return firstName + ' ' + lastName;
107 | },
108 | // The setter function is called with the value that was set at
109 | // the output path as well as the current value of the inputs.
110 | // It should return an array or object where each property is an
111 | // index that corresponds to each input argument that should be set.
112 | // If the function returns null, no items will be set.
113 | set: function(value, firstName, lastName) {
114 | return value && value.split(' ');
115 | }
116 | });
117 | ```
118 |
119 | ## Named functions
120 |
121 | In addition to passing in a function directly, a function can be defined on a model via a name. This name can then be used in place of a function argument.
122 |
123 | > `model.fn(name, fn)`
124 | > * `name` A name that uniquely identifies the function
125 | > * `fn` A getter function or an object with the form `{get: function(), set: function()}`
126 |
127 | Reactive functions started on the server via a name are reinitialized when the page loads. In order to add functions for use in routes as well as in the client, use the `'model'` event emitted by apps, which occurs right before an app route is called on the server and once immediately upon initialization in the client. Then, you can safely start them in the appropriate route, and they will be re-established automatically on the client.
128 |
129 | In DerbyJS, this pattern is generally less preferable to initializing model functions in a component.
130 |
131 | ```js
132 | app.on('model', function(model) {
133 | // Sort the players by score and return the top X players. The
134 | // function will automatically update the value of '_page.leaders'
135 | // as players are added and removed, their scores change, and the
136 | // cutoff value changes.
137 | model.fn('topPlayers', function(players, cutoff) {
138 | // Note that the input array is copied with slice before sorting
139 | // it. The function should not modify the values of its inputs.
140 | return players.slice().sort(function (a, b) {
141 | return a.score - b.score;
142 | }).slice(0, cutoff - 1);
143 | });
144 | });
145 |
146 | app.get('/leaderboard/:gameId', function(page, model, params, next) {
147 | var game = model.at('game.' + params.gameId);
148 | game.subscribe(function(err) {
149 | if (err) return next(err);
150 | game.setNull('players', [
151 | {name: 'John', score: 4000},
152 | {name: 'Bill', score: 600},
153 | {name: 'Kim', score: 9000},
154 | {name: 'Megan', score: 3000},
155 | {name: 'Sam', score: 2000}
156 | ]);
157 | model.set('_page.cutoff', 3);
158 | model.start(
159 | // Output path
160 | '_page.topPlayers',
161 | // Input paths
162 | game.at('players'),
163 | '_page.cutoff',
164 | // Name of the function
165 | 'topPlayers'
166 | );
167 | page.render();
168 | });
169 | });
170 | ```
171 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/references.md:
--------------------------------------------------------------------------------
1 | # References
2 |
3 | References make it possible to write business logic and templates that interact with the model in a general way. They redirect model operations from a reference path to the underlying data, and they set up event listeners that emit model events on both the reference and the actual object's path.
4 |
5 | References must be declared per model, since calling `model.ref` creates a number of event listeners in addition to setting a ref object in the model. When a reference is created or removed, a `change` model event is emitted. References are not actually stored in the model data, but they can be used from getter and setter methods as if they are.
6 |
7 | > `scoped = model.ref(path, to, [options])`
8 | > * `path` The location at which to create a reference. This must be underneath a [local collection](paths#local-and-remote-collections) (typically `_page`), since references must be declared per model
9 | > * `to` The location that the reference links to. This is where the data is actually stored
10 | > * `options:`
11 | > * `updateIndices` Set true to update the ref's `to` path if it contains array indices whose parents are modified via array inserts, removes, or moves
12 | > * `scoped` Returns a model scoped to the output path for convenience
13 |
14 | > `model.removeRef(path)`
15 | > * `path` The location at which to remove the reference
16 |
17 | ```js
18 | model.set('colors', {
19 | red: {hex: '#f00'}
20 | , green: {hex: '#0f0'}
21 | , blue: {hex: '#00f'}
22 | });
23 |
24 | // Getting a reference returns the referenced data
25 | model.ref('_page.green', 'colors.green');
26 | // Logs {hex: '#0f0'}
27 | console.log(model.get('_page.green'));
28 |
29 | // Setting a property of the reference path modifies
30 | // the underlying data
31 | model.set('_page.green.rgb', [0, 255, 0]);
32 | // Logs {hex: '#0f0', rgb: [0, 255, 0]}
33 | console.log(model.get('colors.green'));
34 |
35 | // Removing the reference has no effect on the underlying data
36 | model.removeRef('_page.green');
37 | // Logs undefined
38 | console.log(model.get('_page.green'));
39 | // Logs {hex: '#0f0', rgb: [0, 255, 0]}
40 | console.log(model.get('colors.green'));
41 | ```
42 |
43 | Racer also supports a special reference type created via `model.refList`. This type of reference is useful when a number of objects need to be rendered or manipulated as a list even though they are stored as properties of another object. This is also the type of reference created by a query. A reference list supports the same mutator methods as an array, so it can be bound in a view template just like an array.
44 |
45 | > `scoped = model.refList(path, collection, ids, [options])`
46 | > * `path` The location at which to create a reference list. This must be underneath a [local collection](paths#local-and-remote-collections) (typically `_page`), since references must be declared per model
47 | > * `collection` The path of an object that has properties to be mapped onto an array. Each property must be an object with a unique `id` property of the same value
48 | > * `ids` A path whose value is an array of ids that map the `collection` object's properties to a given order
49 | > * `options:`
50 | > * `deleteRemoved` Set true to delete objects from the `collection` path if the corresponding item is removed from the refList's output path
51 | > * `scoped` Returns a model scoped to the output path for convenience
52 |
53 | > `model.removeRefList(path)`
54 | > * `path` The location at which to remove the reference
55 |
56 | Note that if objects are inserted into a refList without an `id` property, a unique id from [`model.id()`](#guids) will be automatically added to the object.
57 |
58 | ```js
59 | // refLists should consist of objects with an id matching
60 | // their property on their parent
61 | model.setEach('colors', {
62 | red: {hex: '#f00', id: 'red'},
63 | green: {hex: '#0f0', id: 'green'},
64 | blue: {hex: '#00f', id: 'blue'}
65 | });
66 | model.set('_page.colorIds', ['blue', 'red']);
67 | model.refList('_page.myColors', 'colors', '_page.colorIds');
68 |
69 | model.push('_page.myColors', {hex: '#ff0', id: 'yellow'});
70 |
71 | // Logs: [
72 | // {hex: '#00f', id: 'blue'},
73 | // {hex: '#f00', id: 'red'},
74 | // {hex: '#ff0', id: 'yellow'}
75 | // ]
76 | console.log(model.get('_page.myColors'));
77 | ```
78 |
79 | When a collection is cleaned up by `model.destroy()`, the `model.removeAllRefs()` method is invoked to remove all refs and refLists underneath the collection.
80 |
81 | > `model.removeAllRefs(from)`
82 | > * `from` Path underneath which to remove all refs and refLists
83 |
84 | It isn't neccessary to manually dereference model paths, but for debugging, testing, or special cases there is a `model.dereference()` method.
85 |
86 | > `resolved = model.dereference(from)`
87 | > * `from` Path to dereference
88 | > * `resolved` Returns the fully dereferenced path, possibly passing through multiple refs or refLists. Will return the input path if no references are found
89 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/models/setters.md:
--------------------------------------------------------------------------------
1 | # Setters
2 |
3 | Models allow getting and setting to nested undefined paths. Setting such a path first sets each undefined or null parent to an empty object or empty array. Whether or not a path segment is a number determines whether the implied parent is created as an object or array.
4 |
5 | ```js
6 | model.set('cars.DeLorean.DMC12.color', 'silver');
7 | // Logs: { cars: { DeLorean: { DMC12: { color: 'silver' }}}}
8 | console.log(model.get());
9 | ```
10 |
11 | All model mutators modify data and emit events synchronously. This is only safe to do, because all remote data is synchronized with Operational Transformation, and every client will eventually see a consistent view of the same data. With a more naive approach to syncing data to the server and other clients, updates to the data would need to wait until they were confirmed successful from the server.
12 |
13 | As well as a synchronous interface, model mutators have an optional callback with an error argument `callback(err)`. This callback is called when the operation is confirmed from the server, which may be desired to confirm that data was saved before updating the UI in some rare cases. This callback should be used very rarely in practice, and data updates should be treated as sychronous, so that the UI responds immediately even if a user has a high latency connection or is currently disconnected.
14 |
15 | ## General methods
16 |
17 | > `previous = model.set(path, value, [callback])`
18 | > * `path` Model path to set
19 | > * `value` Value to assign
20 | > * `previous` Returns the value that was set at the path previously
21 | > * `callback` *(optional)* Invoked upon completion of a successful or failed operation
22 |
23 | > `obj = model.del(path, [callback])`
24 | > * `path` Model path of object to delete
25 | > * `obj` Returns the deleted object
26 |
27 | > `obj = model.setNull(path, value, [callback])`
28 | > * `path` Model path to set
29 | > * `value` Value to assign only if the path is null or undefined
30 | > * `obj` Returns the object at the path if it is not null or undefined. Otherwise, returns the new value
31 |
32 | > `previous = model.setDiff(path, value, [callback])`
33 | > * `path` Model path to set
34 | > * `value` Value to be set if not strictly equal to the current value
35 | > * `previous` Returns the value that was set at the path previously
36 |
37 | > `model.setDiffDeep(path, value, [callback])`
38 | > `model.setArrayDiff(path, value, [callback])`
39 | > `model.setArrayDiffDeep(path, value, [callback])`
40 | > * `path` Model path to set
41 | > * `value` Value to be set if different. Deep methods will do a deep traversal and deep equality check. Array methods should be used when diffing two different arrays and only inserts, removes, and moves at the top level are desired. `setDiffDeep` can diff arrays as well but may produce a set on a particular property within an array. These methods may end up performing zero or many mutations.
42 |
43 | ## Object methods
44 |
45 | > `id = model.add(path, object, [callback])`
46 | > * `path` Model path to set
47 | > * `object` A document to add. If the document has an `id` property, it will be set at that value underneath the path. Otherwise, an id from `model.id()` will be set on the object first
48 | > * `id` Returns `object.id`
49 |
50 | > `model.setEach(path, object, [callback])`
51 | > * `path` Model path underneath which each property will be set
52 | > * `object` An object whose properties are each set individually
53 |
54 | ## Number methods
55 |
56 | > `number = model.increment(path, [byNumber], [callback])`
57 | > * `path` Model path to set
58 | > * `byNumber` *(optional)* Number specifying amount to increment or decrement if negative. Defaults to 1
59 | > * `number` Returns the new value that was set after incrementing
60 |
61 | ## Array methods
62 |
63 | Array methods can only be used on paths set to arrays, null, or undefined. If the path is null or undefined, the path will first be set to an empty array before applying the method.
64 |
65 | > `length = model.push(path, value, [callback])`
66 | > * `path` Model path to an array
67 | > * `value` An item to add to the *end* of the array
68 | > * `length` Returns the length of the array with the new item added
69 |
70 | > `length = model.unshift(path, value, [callback])`
71 | > * `path` Model path to an array
72 | > * `value` An item to add to the *beginning* of the array
73 | > * `length` Returns the length of the array with the new item added
74 |
75 | > `length = model.insert(path, index, values, [callback])`
76 | > * `path` Model path to an array
77 | > * `index` Index at which to start inserting. This can also be specified by appending it to the path instead of as a separate argument
78 | > * `values` An array of items to insert at the index
79 | > * `length` Returns the length of the array with the new items added
80 |
81 | > `item = model.pop(path, [callback])`
82 | > * `path` Model path to an array
83 | > * `item` Removes the last item in the array and returns it
84 |
85 | > `item = model.shift(path, [callback])`
86 | > * `path` Model path to an array
87 | > * `item` Removes the first item in the array and returns it
88 |
89 | > `removed = model.remove(path, index, [howMany], [callback])`
90 | > * `path` Model path to an array
91 | > * `index` Index at which to start removing items
92 | > * `howMany` *(optional)* Number of items to remove. Defaults to 1
93 | > * `removed` Returns an array of removed items
94 |
95 | > `moved = model.move(path, from, to, [howMany], [callback])`
96 | > * `path` Model path to an array
97 | > * `from` Starting index of the item to move
98 | > * `to` New index where the item should be moved
99 | > * `howMany` *(optional)* Number of items to move. Defaults to 1
100 | > * `moved` Returns an arry of items that were moved
101 |
102 | ## String methods
103 |
104 | String methods can only be used on paths set to strings, null, or undefined. If the path is null or undefined, the path will first be set to an empty string before applying the method.
105 |
106 | The string methods support collaborative text editing, and Derby uses string methods to bind changes to all text inputs and textareas by default.
107 |
108 | > `model.stringInsert(path, index, text, [callback])`
109 | > * `path` Model path to a string
110 | > * `index` Character index within the string at which to insert
111 | > * `text` String to insert
112 |
113 | > `model.stringRemove(path, index, howMany, [callback])`
114 | > * `path` Model path to a string
115 | > * `index` Starting character index of the string at which to remove
116 | > * `howMany` Number of characters to remove
117 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/overview.md:
--------------------------------------------------------------------------------
1 | # DerbyJS
2 |
3 | Welcome to the documentation for DerbyJS!
4 |
5 | ## Related documentation
6 |
7 | * [ShareDB](https://github.com/share/sharedb)
8 |
9 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/namespaces-and-files.md:
--------------------------------------------------------------------------------
1 | # View namespaces
2 |
3 | View names have colon (`:`) separated namespaces. Lookups of views are relative to the namespace in which they are used. Thus, sub-views within components or different sections of large applications are well encapsulated and won't cause naming conflicts.
4 |
5 | ```derby
6 |
7 | ...
8 |
9 |
10 | ...
11 |
12 |
13 |
14 |
15 | ```
16 |
17 | In addition, similar to the way that CSS allows overriding of styles by using a more specific selector, you can define views at a general namespace and then redefine them at a more specific namespace.
18 |
19 | ```derby
20 |
21 | App
22 |
23 |
24 | About - App
25 |
26 |
27 | Mission statement - App
28 | ```
29 |
30 | ### Custom HTML tags
31 |
32 | A view can be turned into a custom HTML tag by specifying the `tag` property in it's definition. Custom tag names are global so care should be taken in their usage.
33 |
34 | ```derby
35 |
36 |
37 |
{{data}}
38 |
39 |
40 |
41 |
42 | ```
43 |
44 | ## Structuring views in multiple files
45 |
46 | Views should be broken into files that correspond to major pieces of functionality, different URLs, or components. Views are included from another file with the `` tag.
47 |
48 | ```derby
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | ```
58 |
59 | Typically, view namespaces have a one-to-one correspondence with directories and files. For example, a typical structure like:
60 |
61 | #### index.html
62 | ```derby
63 |
64 |
65 |
66 | App
67 | ```
68 |
69 | #### about/index.html
70 | ```derby
71 |
72 |
73 |
74 | About - App
75 | ```
76 |
77 | #### about/mission.html
78 | ```derby
79 |
80 | Mission statement - App
81 | ```
82 |
83 | would be equivalent to:
84 |
85 | `index.html`
86 | ```derby
87 |
88 | App
89 |
90 |
91 | About - App
92 |
93 |
94 | Mission statement - App
95 | ```
96 |
97 | Rules for importing views work the same way as [Node.js module loading](https://nodejs.org/api/modules.html) with `require()`. The `src` attribute uses the same syntax of relative paths or paths to `node_modules`. An `index.html` file can be imported via the name of the directory that it is in, just like `index.js` files in Node.js.
98 |
99 | As well, the name `index` can be used for a view that is returned for just the name of its namespace.
100 |
101 | #### index.html
102 | ```derby
103 |
104 |
105 |
106 |
107 | ```
108 |
109 | #### home.html
110 | ```derby
111 |
112 |
113 |
114 |
115 |
116 |
117 | Hello!
118 | ```
119 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/overview.md:
--------------------------------------------------------------------------------
1 | # Views
2 |
3 | When writing an app or new feature in Derby, you should typically start by writing its view. Derby templates can be written in HTML or Jade with [derby-jade](https://github.com/derbyparty/derby-jade). Templates define HTML/DOM output, data bindings, event listeners, and component parameters.
4 |
5 | ## Creating views
6 |
7 | Views are written in HTML files. These files are parsed and added to a Derby app with the `app.loadViews()` method. This method synchronously reads template files, traverses their includes, and calls `app.views.register()` for each view.
8 |
9 | > `app.loadViews(filename)`
10 | > * `filename` File path to root template file at which to start loading views
11 |
12 | > `app.views.register(name, source, options)`
13 | > * `name` View name to add
14 | > * `source` Derby HTML source
15 | > * `options:`
16 | > * `tag` Name of an HTML tag that will render this view
17 | > * `attributes` Space separated list of HTML tags interpreted as an attribute when directly within the view instance
18 | > * `arrays` Space separated list of HTML tags interpreted as an array of objects attribute when directly within the view instance
19 | > * `unminified` Whitespace is removed from templates by default. Set true to disable
20 | > * `string` True if the template should be interpreted as a string instead of HTML
21 |
22 | > `view = app.views.find(name, [namespace])`
23 | > * `name` View name to find
24 | > * `namespace` *(optional)* Namespace from which to start the name lookup
25 | > * `view` Returns the view template object
26 |
27 | Each view is wrapped in a tag that names it. This name must end in a colon to differentiate it from a normal HTML tag. These tags can't be nested, and they need not be closed.
28 |
29 | ```derby
30 |
31 |
Hello, sir.
32 |
33 |
34 |
Howdy!
35 | ```
36 |
37 | is equivalent to:
38 |
39 | ```js
40 | app.views.register('serious-title', '
Hello, sir.
');
41 | app.views.register('friendly-title', '
Howdy!
');
42 | ```
43 |
44 | ## Using views
45 |
46 | You can instantiate a view in a template with the `` tag, `{{view}}` expression, or by giving the view a tag name. Typically, you should use the `` tag in HTML templates. The `{{view}}` expression is useful when writing string templates or wish to include a view in an HTML attribute, script tag, or style tag. Custom tag names are global to an application. They are recommended for general purpose components, like `` or ``, but not for ordinary views.
47 |
48 | ```derby
49 |
50 |
Hello, sir.
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | {{view 'serious-title'}}
59 |
60 |
61 |
62 | ```
63 |
64 | Views may be looked up dynamically with an expression. If the view isn't found, nothing will be rendered.
65 |
66 | ```derby
67 |
68 |
69 |
70 | {{view type + '-title'}}
71 | ```
72 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/template-syntax/blocks.md:
--------------------------------------------------------------------------------
1 | # Blocks
2 |
3 | Blocks are template expressions that start with special keywords. They are used to conditionally render, repeat, or control the way in which sections of a template are rendered.
4 |
5 | Similar to HTML tags, blocks end in a forward slash followed by the same keyword that started them. The closing keyword is optional but recommended for clarity. For example, both `{{with}}...{{/with}}` and `{{with}}...{{/}}` are parsed correctly.
6 |
7 | # Conditionals
8 |
9 | Conditional blocks use the `if`, `else if`, `else`, and `unless` keywords. They render the first template section that matches a condition or nothing if none match. Like in Mustache and Handlebars, zero length arrays (`[]`) are treated as falsey. Other than that, falsey values are the same as JavaScript: `false`, `undefined`, `null`, `''`, and `0`.
10 |
11 | ```derby
12 | {{if user.name}}
13 |
user.name
14 | {{else if user}}
15 |
Unnamed user
16 | {{else}}
17 | No user
18 | {{/if}}
19 | ```
20 |
21 | The inverse of `if` is `unless`. For clarity, unless should only be used when there is no `else` condition. A block that has an unless and else condition can usually be writtern more clearly as an if and else.
22 |
23 | ```derby
24 | {{unless items}}
25 | Please add some items
26 | {{/unless}}
27 | ```
28 |
29 | The contents of a conditional block are only re-rendered when a different condition starts to match. If the values in the conditional change, the condition expression is evaluated, but the DOM is not updated if the same section matches.
30 |
31 | # Each
32 |
33 | Each blocks repeat for each of the items in an array. They cannot iterate over objects.
34 |
35 | ```derby
36 | {{each items}}
37 |
{{this.text}}
38 | {{else}}
39 | No items
40 | {{/each}}
41 | ```
42 |
43 | In addition to an alias to the array item, eaches support an alias for the index of the item. This index alias supports binding and will be updated as the array changes.
44 |
45 | ```derby
46 | {{each items as #item, #i}}
47 | {{#i + 1}}. {{#item.text}}
48 | {{/each}}
49 | ```
50 |
51 | Derby has very granular model events to describe array mutations as inserts, removes, and moves. It maps these directly into efficient DOM mutations of just what changed.
52 |
53 | # With
54 |
55 | With blocks set the path context of a block, but they do not trigger re-rendering. Their primary use is to set an alias to a path inside of their contents.
56 |
57 | Aliases can be a convenient way to set a name that can be used throughout a section of a template or many nested views and/or components.
58 |
59 | ```derby
60 |
61 | {{with _session.user as #user}}
62 |
63 | {{/with}}
64 |
65 | {{with {name: 'Jim', age: 32} as #user}}
66 |
67 | {{/with}}
68 |
69 |
70 |
{{#user.name}}
71 |
{{#user.age}}
72 | ```
73 |
74 | # On
75 |
76 | To clear UI state, to optimize performance by rendering larger sections, or to work around issues with template bindings not rendering often enough, an `{{on}}` block can provide more control. Its contents will re-render whenever any of its paths change.
77 |
78 | ```derby
79 | {{on #profile.id}}
80 |
{{#profile.name}}
81 |
82 | {{/on}}
83 |
84 | {{on first, second, third}}
85 |
86 | {{/on}}
87 | ```
88 |
89 | # Unbound and bound
90 |
91 | Bindings are created by default for all template expressions. To render an initial value only and not create bindings, the `{{unbound}}` block may be wrapped around a template section. Bindings can be toggled back on with a `{{bound}}` block.
92 |
93 | ```derby
94 | {{unbound}}
95 |
96 | {{bound}}
97 |
98 | {{/bound}}
99 | {{/unbound}}
100 | ```
101 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/template-syntax/escaping.md:
--------------------------------------------------------------------------------
1 | # Escaping
2 |
3 | Derby escapes values as required when it renders HTML. Escaping is relative to whether it is rendering inside of an HTML attribute or text. For example, Derby will escape `"` as `"` inside of an HTML attribute, and it will escape `<` as `<` inside of text.
4 |
5 | Derby's templates also follow HTML escaping rules. Derby will parse the string `{{` as the start of a template tag, so if you wish to write this value in an attribute or the text of a Derby template, you can use the HTML entity equivalent: `{{`.
6 |
7 | ## Rendering unescaped HTML
8 |
9 | The `unescaped` keyword may be used to render an HTML string without escaping. It is *very unlikely* that you should use this feature. Derby has many ways of dynamically creating views. Unescaped HTML is unsafe, is typically slower, and is rarely necessary with Derby. This feature is intended only for rendering the output of a well-tested library that produces sanitized HTML, such as [Google Caja](https://developers.google.com/caja/).
10 |
11 | ```derby
12 |
13 |
{{unescaped rawHtml}}
14 | ```
15 |
16 | Instead, prefer passing in a template as an attribute or dynamically selecting a view in most cases.
17 |
18 | ```derby
19 |
20 |
21 |
22 | Custom HTML for this user!
23 |
24 |
25 |
26 |
27 |
28 | {{@content}}
29 |
30 | ```
31 |
32 | ```derby
33 |
34 |
35 | ```
36 |
37 | If you need completely dynamic generation of HTML (such as implementing an HTML or template editor in your application), it is even possible to use Derby's HTML parser and pass the returned Template object to your views. Derby will render this HTML safely without any Cross-site Scripting (XSS) concerns. You'll even be able to use Derby's template syntax! See how this is done in the [Derby render example](https://github.com/derbyjs/derby-examples/blob/master/render/index.js#L29), which powers the live template editor on the [DerbyJS home page](https://derbyjs.com/).
38 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/template-syntax/functions-and-events.md:
--------------------------------------------------------------------------------
1 | # Functions and events
2 |
3 | Attributes beginning with `on-` add listeners to DOM events and component events. Under the hood, events on elements are added with `element.addEventListener()` and events on components are added with `component.on()`. Adding events declaritively with attributes is easier than CSS selectors and less prone to unexpectedly breaking when refactoring templates or classes for styling.
4 |
5 | ```derby
6 |
7 |
8 | ```
9 |
10 | ```js
11 | // Equivalent to:
12 | input.addEventListener('mousedown', function(event) {
13 | self.mousedownInput(event);
14 | }, false);
15 | input.addEventListener('blur', function(event) {
16 | self.blurInput();
17 | self.update();
18 | }, false);
19 | ```
20 |
21 | ## View functions
22 |
23 | Functions are looked up on the current component's controller, the page, and the global, in that order. The majority of functions are defined on component prototypes, generic shared utility functions are defined on the page prototype, and the global provides access to functions like `new Date()` and `console.log()`.
24 |
25 | ```derby
26 |
27 |
28 |
29 | {{sum(1, 2, 4)}}
30 |
31 | {{console.log('rendering value', value)}}
32 | ```
33 |
34 | ```js
35 | // component prototypes are where most functions are defined
36 | UserList.prototype.delUser = function(userId) {
37 | this.users.del(userId);
38 | };
39 |
40 | // app.proto is the prototype for all pages created by the app
41 | app.proto.sum = function() {
42 | var sum = 0;
43 | for (var i = 0; i < arguments.length; i++) {
44 | sum += arguments[i];
45 | }
46 | return sum;
47 | };
48 | ```
49 |
50 | ### Component events
51 | Components support custom events. Dashes are transformed into camelCase.
52 |
53 | See the [component events](components/events) documentation for more detail on using events and component functions.
54 | ```derby
55 |
56 | ```
57 |
58 | ```js
59 | // Equivalent to:
60 | modal.on('close', function() {
61 | self.reset();
62 | });
63 | modal.on('fullView', function() {
64 | back.fade();
65 | });
66 | ```
67 |
68 |
69 | ### Special HTML rules
70 |
71 | As a convenience, an `on-click` listener can be added to a link without an `href`. Derby will add an `href="#"` and prevent the default action automatically if no href is specified.
72 |
73 | ```derby
74 |
75 | Hi
76 | ```
77 |
78 | HTML forms have very useful behavior, but their default action on submit will navigate away from the current page. If an `on-submit` handler is added to a form with no `action` attribute, the default will be prevented.
79 |
80 | ```derby
81 |
88 | ```
89 |
90 | ### DOM event arguments
91 |
92 | For functions invoked by DOM events only, the special arguments `$event` or `$element` may be specified. The `$event` argument is the DOM Event object passed to the listener function for `addEventListener()`. The `$element` argument is a reference to the element on which the listener attribute is specified. These arguments are only passed to functions if explicitly specified.
93 |
94 | ```derby
95 |
105 | ```
106 |
107 | ```js
108 | UserList.prototype.clickRow = function(e, tr) {
109 | // Ignore clicks on or in links
110 | var node = e.target;
111 | while (node && node !== tr) {
112 | if (node.tagName === 'A') return;
113 | node = node.parentNode;
114 | }
115 | // Cancel the original click event inside of the row
116 | e.stopPropagation();
117 | // Pretend like the click happened on the first link in the row
118 | var event = new MouseEvent('click', e);
119 | var link = tr.querySelector('a');
120 | if (link) link.dispatchEvent(event);
121 | };
122 | ```
123 |
124 | ## Scoped model arguments
125 |
126 | Functions can be passed the value of any view path. In some cases, it can be convenient to get a [scoped model](../../models/paths#scoped-models) to the view name instead. To pass a scoped model, you can wrap the view path in `$at()`. Instead of getting the value for a view path, this will return a scoped model. It will return undefined if no scoped model can be created for a view path.
127 |
128 | ```derby
129 |
130 | ```
131 |
132 | ```js
133 | app.proto.toggle = function(scoped) {
134 | scoped.set(!scoped.get());
135 | };
136 | ```
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/template-syntax/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/template-syntax/literals.md:
--------------------------------------------------------------------------------
1 | # Literals
2 |
3 | Derby supports creating JavaScript literals in templates. The syntax is identical to JavaScript, except that identifiers within literals are parsed as [view paths](paths) instead of JavaScript variables. Derby parses template expressions with Esprima, so its coverage of JavaScript expression syntax is comprehensive.
4 |
5 | ## Simple literals
6 |
7 | ```derby
8 |
9 | {{0}}
10 | {{1.1e3}}
11 | {{0xff}}
12 |
13 | {{true}}
14 | {{false}}
15 |
16 | {{'Hi'}}
17 | {{"Hey"}}
18 |
19 | {{/([0-9])+/}}
20 |
21 | {{null}}
22 |
23 | {{undefined}}
24 |
25 | {{ [0, 1, 2] }}
26 |
27 | {{ {name: 'Jim'} }}
28 | ```
29 |
30 | For greater efficiency, simple literals are instantiated at the time of parsing. Object literals created at parse time will be passed by reference to controller functions, so be careful not to modify them.
31 |
32 | ```derby
33 |
34 |
35 | ```
36 |
37 | It is possible to iterate over object literals in template expressions. In most cases, it makes more sense to define constants in the controller or use HTML, but this can be handy when prototyping and debugging.
38 |
39 | ```derby
40 |
49 | {{/with}}
50 | ```
51 |
52 | ## Literals containing paths
53 |
54 | Literals containing paths are created at render time and populated with the appropriate values from the model.
55 |
56 | ```derby
57 |
58 | {{each [first, 1, 2, 3] as #item}}
59 |
{{#item}}
60 | {{/each}}
61 |
62 |
63 |
64 | ```
65 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/template-syntax/operators.md:
--------------------------------------------------------------------------------
1 | # Operators
2 |
3 | All non-assigment JavaScript operators and using parentheses for grouping expressions are supported. They work exactly the same as they do in Javascript. Operators that do an assignment, such as the `++` increment operator, are not supported. This avoids rendering having side effects.
4 |
5 | ```derby
6 |
7 | {{-value}}
8 |
9 | {{+value}}
10 |
11 |
12 | {{left + right}}
13 |
14 | {{left - right}}
15 |
16 | {{left * right}}
17 |
18 | {{left / right}}
19 |
20 | {{left % right}}
21 |
22 |
23 | {{!value}}
24 |
25 | {{left || right}}
26 |
27 | {{left && right}}
28 |
29 |
30 | {{~value}}
31 |
32 | {{left | right}}
33 |
34 | {{left & right}}
35 |
36 | {{left ^ right}}
37 |
38 |
39 | {{left << right}}
40 |
41 | {{left >> right}}
42 |
43 | {{left >>> right}}
44 |
45 |
46 | {{left === right}}
47 |
48 | {{left !== right}}
49 |
50 | {{left == right}}
51 |
52 | {{left != right}}
53 |
54 |
55 | {{left < right}}
56 |
57 | {{left > right}}
58 |
59 | {{left <= right}}
60 |
61 | {{left >= right}}
62 |
63 |
64 | {{typeof value}}
65 |
66 | {{left instanceof right}}
67 |
68 | {{left in right}}
69 |
70 |
71 | {{test ? consequent : alternate}}
72 |
73 |
74 | {{a, b, c, d}}
75 | ```
76 |
77 | ## Two-way operators
78 |
79 | In addition to getting values, operators for which there is a well defined opposite support two-way data bindings. These setters will make the relationship consistent with the value that is set.
80 |
81 | ```derby
82 |
83 |
86 |
87 |
92 | {{each options as #option}}
93 |
96 | {{/each}}
97 | ```
98 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/template-syntax/overview.md:
--------------------------------------------------------------------------------
1 | # Template syntax
2 |
3 | Derby’s template syntax is loosely based on [Handlebars](https://handlebarsjs.com/), a semantic templating language that extends [Mustache](https://mustache.github.io/mustache.5.html).
4 |
5 | Semantic templates encourage separation of logic from presentation. Instead of arbitrary code, there are a small set of template expressions. During rendering, data are passed to the template, and template expressions are replaced with the appropriate values. In Derby, this data comes from the model.
6 |
7 | Derby supports calling controller functions and the full JavaScript expression syntax. Expressions are parsed with [Esprima](http://esprima.org/) and do not use JavaScript's `eval()` or `new Function()`.
8 |
9 | ## HTML
10 |
11 | With the exception of templates that return strings and the contents of `
65 | ```
66 |
67 | HTML comments are removed, except for those with square brackets immediately inside of them. This syntax is used by Internet Explorer's conditional comments. It can also be used to include comments for copyright notices or the like.
68 |
69 | ```derby
70 |
71 |
72 |
73 |
74 | ```
75 |
--------------------------------------------------------------------------------
/md/docs/derby-0.10/views/template-syntax/paths.md:
--------------------------------------------------------------------------------
1 | # Paths
2 |
3 | Template paths use JavaScript syntax with a few small modifications.
4 |
5 | ## Model values
6 |
7 | What would be identifiers for variable names in JavaScript get a value from the model and bind to any updates. If the path returns null or undefined, nothing is rendered.
8 |
9 | Examples of rendering model values:
10 |
11 | ```derby
12 | {{user.name}}
13 |
14 | {{user.bestFriends[0].name}}
15 |
16 | {{users[userId].name}}
17 | ```
18 |
19 | ```js
20 | model.get('user.name');
21 |
22 | model.get('user.bestFriends.0.name');
23 |
24 | var userId = model.get('userId');
25 | model.get('users.' + userId + '.name');
26 | ```
27 |
28 | ## Attributes
29 |
30 | Values are passed into views with attributes. Within the view, these values are accessed via paths that start with an at sign (`@`). In addition, there is an `@content` attribute created for any content inside of a view tag.
31 |
32 | ```derby
33 |
34 |
47 | ```
48 |
49 | See [View attributes](view-attributes) for additional detail on passing data to views.
50 |
51 | ## Aliases
52 |
53 | Aliases label path expressions. They must begin with a hash (`#`) character to make it more obvious whether a path is an alias or a model value. Each of the block types support defining aliases with the `as` keyword.
54 |
55 | Aliases make it possible to refer to the scope of the current block or a parent block.
56 |
57 | ```derby
58 | {{with user as #user}}
59 |
{{#user.name}}
60 |
{{#user.headline}}
61 | {{if #user.friendList as #friendList}}
62 |
63 |
Friends of {{#user.name}}
64 |
65 | {{each #friendList as #friend}}
66 |
{{#friend.name}}
67 | {{/each}}
68 |
69 | {{/if}}
70 | {{/with}}
71 | ```
72 |
73 | ## Relative paths - DEPRECATED
74 |
75 | Relative view paths begin with `this`. They refer to the expression in the containing block.
76 |
77 | Aliases are preferred to relative paths, as they are more clear. Relative paths came from implementing a syntax inspired by Handlebars, but Derby has been moving toward increased consistency with JavaScript, and the alternate use of the keyword `this` is confusing. Expect that this feature will be removed in a future version of Derby.
78 |
79 | ```derby
80 | {{with user}}
81 |
{{this.name}}
82 |
{{this.headline}}
83 | {{if this.friendList}}
84 |
Friends
85 |
86 | {{each this}}
87 |
{{this.name}}
88 | {{/each}}
89 |
90 | {{/if}}
91 | {{/with}}
92 | ```
93 |
--------------------------------------------------------------------------------
/md/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/md/faq.md:
--------------------------------------------------------------------------------
1 | ### What is DerbyJS?
2 |
3 | DerbyJS is a framework for developing web applications, making it much easier to build real-time collaborative functionality.
4 |
5 | Built from a collection of standard Node.js modules, DerbyJS can be brought into existing projects, or existing node modules can easily be brought into a DerbyJS project.
6 |
7 | Derby provides server-side rendering through it's HTML-like templating language. Templates can be organized into components for better code reuse and faster application development.
8 | Templates and components are backed by a real-time data model called Racer.
9 |
10 |
11 | ### What is Racer?
12 |
13 | Racer is the data manipulation layer in Derby, it wraps the [ShareDB](https://github.com/share/sharedb) library. Racer provides a consistent API for managing data on the server and client, and seamlessly syncs the two with little thought from the user.
14 |
15 |
16 | ### How does Racer compare to Firebase?
17 |
18 | Racer provides a lot of the same functionality as Firebase. Besides the obvious tradeoffs between open source and proprietary (supported) software, Racer has more granular update information, while Firebase has more mature solutions around authentication and documentation.
19 |
20 |
21 | ### What is a Racer Model?
22 |
23 | The Model is a JavaScript object that provides an api for real-time data. All data manipulations are done through it, and the model is synced on both the server and the client. Models are created on server for each request and also you can create any number of Models directly from a Store anytime, but there is only one Model on client.
24 |
25 |
26 | ### What is a Racer Store?
27 |
28 | Store is singular JavaScript object on the server for managing database connections. All Models are created from Store. If a Model is on the client, a connection to the store is established by racer-browserchannel. The Store is wrapper on [ShareJS](http://sharejs.org) and a place where all OT conflict resolution magic is happen.
29 |
30 | ### What is OT?
31 |
32 | OT - [Operational Transformation](https://en.wikipedia.org/wiki/Operational_transformation) - is a conflict resolution technique used in [ShareJS](http://sharejs.org). The main idea is to resolve conflicts created by multiple clients trying to modify the same data in a distributed system.
33 | OT is different for different data types. ShareJS uses a json data type in Racer. There is also a text data type in ShareJS and plans to add a rich text data type.
34 | Using Derby you can treat OT like black box with some magic. ShareJS will merge operations for arrays, strings, number increments 'out of the box', but if two clients make set operation for same data at same time (data with same version) there is no way to capture this correcly and one of two operations will be lost. If it is critical (and you have a big head), you can create application specific data types for ShareJS.
35 |
36 | ### Why do we need Mongo and Redis?
37 |
38 | Derby, and hence Racer, are powered by [ShareDB](https://github.com/share/sharedb). ShareDB currently only has a production ready adapter for Mongo, and optionally uses Redis to enable scaling past a single node process. More backends can be added to ShareDB by writing adapters, see [sharedb-mongo](https://github.com/share/sharedb-mongo) for example.
39 |
40 |
41 | ### How can I use web sockets with Racer instead of long polling?
42 |
43 | Use [racer-ws](https://github.com/derbyparty/racer-ws) or [racer-highway](https://github.com/derbyparty/racer-highway) module.
44 |
45 | ### What is fetch and subscribe?
46 |
47 | A newly created Model has no data in it. Before you can use any data from the database, you need to bring it into Model. There are two methods: fetch - simply queries the database and loads it into the Model, subscribe - similarly queries the database and loads the data into the Model, but if this data changes in the database, data in your Model will be updated. You can fetch/subscribe whole collections, particular documents, or even just one field in a document.
48 |
49 |
50 | ### What if JavaScript is disabled?
51 |
52 | If js is disabled in browser, client app router triggers on server for every url change. In this case site looks more like static one.
53 | Search engine spiders also get html which is good for SEO.
54 |
55 | ### Can I use Derby in Phonegap?
56 |
57 | Starting from version 0.6 - yes, you can.
58 |
59 |
60 | ### What if I have more questions?
61 |
62 | These are just a few of the frequently asked questions. This is a work in progress. If your question is not answered here, please search over on the [Google Group](https://groups.google.com/forum/?fromgroups#!forum/derbyjs). If you don't see an existing question, then let us know what is wrong. Please provide any related code snippets.
63 |
64 | ------
65 |
--------------------------------------------------------------------------------
/md/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/md/resources.md:
--------------------------------------------------------------------------------
1 | ## Community
2 |
3 | [Github Issues](https://github.com/derbyjs/derby/issues)
4 | Submit a problem
5 |
6 |
7 | ### Modules
8 |
9 | [generator-derby](https://github.com/derbyparty/generator-derby)
10 | Yeoman generator that scaffolds out a Derby 0.6 app
11 |
12 | [derby-login](https://github.com/derbyparty/derby-login)
13 | Auth
14 |
15 | [racer-schema](https://github.com/derbyparty/racer-schema)
16 | Schema Validation
17 |
18 | [racer-ws](https://github.com/derbyparty/racer-ws)
19 | Web Sockets
20 |
21 | [racer-highway](https://github.com/derbyparty/racer-highway)
22 | Web Sockets which downgrades to Browser Channel
23 |
24 | [derby-hook](https://github.com/derbyparty/derby-hook)
25 | After submit hook wrapper
26 |
27 | [derby-i18n](https://github.com/jamesknelson/derby-i18n)
28 | Simple filesystem-based i18n support for derby
29 |
30 |
31 | ### Compilers
32 |
33 | [derby-jade](https://github.com/derbyparty/derby-jade)
34 | Jade
35 |
36 | [derby-less](https://github.com/derbyjs/derby-less)
37 | Less
38 |
39 | [derby-markdown](https://github.com/derbyparty/derby-markdown)
40 | Markdown
41 |
42 | [derby-stylus](https://github.com/derbyjs/derby-stylus)
43 | Stylus
44 |
45 |
46 | ## Sources
47 |
48 | [DerbyJS Github](https://github.com/derbyjs)
49 | Derby, Racer, etc sources
50 |
51 | [Share Github](https://github.com/share)
52 | ShareDB, etc sources
53 |
54 | [Derbyparty Github](https://github.com/derbyparty)
55 | Modules from Community
56 |
57 | [Derby Examples](https://github.com/derbyjs/derby-examples)
58 | Example applications
59 |
60 | [Derby Site](https://github.com/derbyjs/derby-site)
61 | This site
62 |
63 |
64 | ## Component demos
65 |
66 | Derby components from [ile](https://github.com/ile/)
67 |
68 |
69 | ## Projects in production
70 |
71 | [Lever](https://lever.co/)
72 | A modern web app for enterprise hiring
73 |
74 | [Type4Fame](http://type4fame.com/)
75 | Multiplayer Typing Game - [source code](https://github.com/cray0000/type4fame)
76 |
77 | Realtime forum for [bombermine](http://bombermine.com/) players
78 |
79 | All-in-one productivity tool [Aamu.app](https://aamu.app/) made by [ile](https://github.com/ile)
80 |
--------------------------------------------------------------------------------
/md/started.md:
--------------------------------------------------------------------------------
1 | ## Getting started
2 |
3 |
4 | In order to get started writing your first DerbyJS app, you need to have Node.js and MongoDB installed. While DerbyJS is written to support any database, the MongoDB adapter is the most complete. If you don't have those, get setup!
5 |