Skip to content

Commit

Permalink
Mangle all of the headings
Browse files Browse the repository at this point in the history
@tmeasday plz review
  • Loading branch information
Sashko Stubailo committed Dec 4, 2015
1 parent b3e3298 commit 3b196ce
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 55 deletions.
32 changes: 16 additions & 16 deletions content/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ After reading this guide, you'll know:

## MongoDB Collections in Meteor

At its core, a web application offers its users a view into, and a way to modify, a persistent set of data. Whether managing a list of todos, or ordering a car to pick you up, you are, at some level, fundamentally interacting with a permanent (if changing) data layer.
At its core, a web application offers its users a view into, and a way to modify, a persistent set of data. Whether managing a list of todos, or ordering a car to pick you up, you are, at some level, fundamentally interacting with a permanent (if changing) data layer.

In Meteor, that data layer is typically stored in the MongoDB engine. A set of data in MongoDB is referred to as a "collection", and in Meteor, it's [collections](http://docs.meteor.com/#/full/mongo_collection) that forms the persistence layer.

Expand Down Expand Up @@ -64,18 +64,18 @@ The way that you move data from the server (and MongoDB-backed) collection into

To write data back to the server, you use a *method*, the subject of the "methods and forms" article.

### Local Collections
### Local collections

There is a third way to use a collection in Meteor. On the client or server, if you create a collection but pass `null` instead of a name:

```js
SelectedTodos = new Mongo.Collection(null);
```
You create what's known as a *local collection*. This is a Minimongo collection that has no database connection (ordinarly a named collection would either be directly connected to the database on the server, or via a subscription on the client).
You create what's known as a *local collection*. This is a Minimongo collection that has no database connection (ordinarly a named collection would either be directly connected to the database on the server, or via a subscription on the client).

A local collection is simply a convienent way to use the full power of the Minimongo library for in-memory storage. For instance, you might use it instead of a simple array if you'll need to execute sophisticated queries over your set of data. Or you may want to take advatange of its *reactivity* on the client to drive some UI in a way that feels natural in Meteor.

## Defining Collections with a Schema
## Defining a schema

Although MongoDB is a schema-less database, which allows maximum flexibility in data structuring, it is generally good practice to use a schema to constrain the contents of your collection to conform to a known format. If you don't, then you tend to end up needing to write defensive code to check and confirm the structure of your data as it *comes out* of the database, instead of when it *goes into* the database. As in most things, you tend to *read things more often than you write them*, and so it's usually easier, and less buggy to use a schema when writing.

Expand All @@ -100,7 +100,7 @@ In this example, from the Todos app, we are doing a few things that are interest

You can see from this example, that with relatively little code we've managed to restrict the format of a list significantly. You can read more in the [Simple Schema docs](http://atmospherejs.com/aldeed/simple-schema) about more complex things that can be done with schemas.

### Checking documents against a schema
### Validating against a schema

Now we have a schema, how do we use it?

Expand Down Expand Up @@ -162,15 +162,15 @@ In the case of the Todos application, as we want to display the number of unfini

Another denormalization that this architecture sometimes requires can be from the parent document onto sub-documents. For instance, in Todos, as we enforce privacy of the todo lists via the `list.userId` attribute, but we publish the todos separately, it makes sense to denormalize `todo.listId` also to ensure that we can do so easily.

### Designing schemas for the future
### Designing for the future

An application, especially a web application, is rarely finished, and it's useful to consider potential future changes when designing your data schema. As in most things, it's rarely a good idea to add fields before you actually need them (often what you anticipate doesn't actually end up happening, after all).

However, it's a good idea to think ahead to how the schema may change over time. For instance, you may have a list of strings on a document (perhaps a set of tags). Although it's tempting to leave them as a subfield on the document (assuming they don't change much), if there's a good chance that they'll end up becoming more complicated in the future (perhaps tags will have a creator, or subtags later on?), then it might be easier in the long run to make a separate collection from the beginning.

As with all things it depends, and can be a judgement call on your part.

## Using schemas -- writing data to collections
## Using schemas on write

Although there are a variety of ways that you can run data through a Simple Schema before sending it to your collection (for instance you could check a schema in every method call), ultimately, the simplest and most reliable is to use the [`aldeed:collection2`](https://atmospherejs.com/aldeed/collection2) package to run every mutator (`insert/update/upsert` call) through the schema.

Expand All @@ -180,9 +180,9 @@ To do so, we use `attachSchema()`:
Lists.attachSchema(Lists.schema);
```

What this means is that now every time we call `Lists.insert()`, `Lists.update()`, `Lists.upsert()`, first our document or modifier will be checked against the schema (in subtly different ways depending on the exact mutator).
What this means is that now every time we call `Lists.insert()`, `Lists.update()`, `Lists.upsert()`, first our document or modifier will be checked against the schema (in subtly different ways depending on the exact mutator).

### Using `defaultValue` and cleaning
### `defaultValue` and data cleaning

One thing that Collection2 does is "cleans" data before sending it to the schema. This means, for instance, making an attempt to coerce types (converting strings to numbers for instance) and removing attributes not in the schema.

Expand Down Expand Up @@ -213,7 +213,7 @@ class ListsCollection extends Mongo.Collection {
Lists = new ListsCollection('Lists');
```

### Writing "hooks"
### Hooks on insert/update/remove

The technique above can also be used to provide a location to "hook" extra functionality into the collection. For instance, when removing a list, we *always* want to remove all of its todos at the same time.

Expand All @@ -239,7 +239,7 @@ A way to deal with points 1. and 2. is to separate out the set of hooks into the

Point 3. can usually be resolved by placing the hook in the *Method* that calls the mutator, rather than the hook itself. Although this is an imperfect compromise (as we need to be careful if we ever add another Method that calls that mutator in the future), it is better than writing a bunch of code that is never actually called (which is guaranteed to not work!), or giving the impression that your hook is more general that it actually is.

## Changing your schema: using data migrations
## Migrating to a new schema

As we discussed above, trying to predict all future requirements of your data schema ahead of time is of course impossible. So inevitably as a project matures, there'll come a time when you need to change the schema of its data. To do so safely, you need to be careful about how you make the changes.

Expand Down Expand Up @@ -286,7 +286,7 @@ MIGRATION=latest meteor --production --settings path/to/production/settings.json
What this does is run the `up()` function of all outstanding migrations (by default apps are considered at migrations "zero"), against your production database. In our case, it should ensure all lists have a `todoCount` field set.


### Making breaking schema changes
### Breaking schema changes

Sometimes when we change the schema of an application, we do so in a breaking way -- so that the old schema doesn't work properly with the new code base. For instance, if we had some UI code that heavily relied on all lists having a `todoCount` set, there would be a period, before the migration runs, in which the UI of our app would be broken after we deployed.

Expand All @@ -301,17 +301,17 @@ A better approach is a multi-stage deployment. The basic idea is that:
3. Finally, you deploy the new code that relies on the new schema and no longer copes with the old schema.
- Now we are safe to rely on `list.todoCount` in our UI.

Another thing to be aware of, especially with such multi-stage deploys, is that being prepared to rollback is important! For this reason, the migrations package allows you to specify a `down()` function and set `MIGRATION=x` to migration _back_ to version `x`.
Another thing to be aware of, especially with such multi-stage deploys, is that being prepared to rollback is important! For this reason, the migrations package allows you to specify a `down()` function and set `MIGRATION=x` to migration _back_ to version `x`.

If you find you need to roll your code version back, you'll need to be careful about the data, and step carefully through your deployment steps in reverse.

## Relations between collections
## Associations between collections

As we discussed earlier, it's very common in Meteor applications to relate documents in different collections. Consequently, it's also very common to need to write queries fetching related documents once you have a document you are interested in (for instance all the todos that are on a single list).

To do so, we can attach functions to the prototype of the documents that belong to a given collection, to give us "methods" on the documents (in the object oriented sense). We can then use these methods to create new queries to find related documents.

### Collection Helpers
### Collection helpers

To do this, we can use the [`dburles:collection-helpers`](https://atmospherejs.com/dburles/collection-helpers) to easily attach such methods (or "helpers") to documents. For instance:

Expand All @@ -333,7 +333,7 @@ if (list.isPrivate()) {
}
```

### Relational helpers
### Association helpers

Now we can attach helpers to documents, it's simple to define a helper that fetches related documents

Expand Down
28 changes: 13 additions & 15 deletions content/data-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ After reading this guide, you'll know:
8. How to turn a 3rd-party REST endpoint into a publication.
9. How to turn a publication into your app into a REST endpoint.

## Publications and Subscriptions
## Publications and subscriptions

Unlike in Meteor, a traditional web application communicates between client and server in a "request-response" fashion. Typically the client makes RESTful HTTP requests to the server and receives data (either in pre-rendered HTML, or perhaps some on-the-wire data format) in response. However there's no way for the server to "push" data to the client when changes happen at the backend.

Expand Down Expand Up @@ -71,20 +71,19 @@ When we create a subscription to this piblication on the client, we can provide
Meteor.subscribe('list/todos', list._id);
```

### Organizing Publications
### Organizing publications

It makes sense to place a publication in a package alongside the feature that it's targeted. For instance, sometimes publications provide very specific data that's only really useful for the view that they are developed for. In that case, placing the publication in the same package as the view code makes perfect sense.

Often, however, a publication is more general. For example in the Todos example application, we create a `list/todos` publication, which publishes all the todos in a list. Although in the application we only use this in one place (in the `listsShow` template), in a larger app, there's a good chance we might need to access all the todos for a list in other places. So putting the publication in the `todos` package is a sensible approach.

## Using publications: Subscriptions
## Subscribing to data

To use publications, you need to create a subscription to it on the client. To do so, you call `Meteor.subscribe()` with the name of the publication. When you do this, it opens up a subscription to that publication, and the server starts sending data down the wire to ensure that your client collections contain up to date copies of the data that's pushed by the publication.

Also, `Meteor.subscribe()` returns a "subscription handle", with a property called `.ready()` defined -- a reactive function that returns `true` when the publication becomes ready (either you call `this.ready()` explicitly, or the current contents of a returned cursor are sent over).


### Organizing Subscriptions
### Organizing subscriptions

It is best to place the subscription as close as possible to the place where the data from the subscription is needed. This reduces "action at a distance" and makes it easier to understand the flow of data through your application. If the subscription and fetch are separated, then it's not always clear how and why changes to the subscriptions (such as changing arguments), will affect the contents of the cursor.

Expand All @@ -107,7 +106,7 @@ In this code snippet we can see two important techniques for subscribing in Blaz

2. Calling `this.autorun` sets up a reactive context which will re-initialize the subscription whenever the reactive variable `this.state.get('listId')` changes.

### Fetching subscription data
### Fetching data

Subscribing to data puts it in your client-side collections. To use the data in your templates, you need to query those collections for that data. There are a few important rules of thumb when doing this.

Expand All @@ -129,14 +128,13 @@ One place where you might be tempted to not subscribe inside a template is when

However, it's generally a good idea to use a layout template (which you wrap all your templates in) to subscribe to this subscription anyway. It's better to be consistent about such things, and it makes for a more flexible system if you ever decide you have a screen that *doesn't* need that data.


## Patterns for data loading

Across Meteor applications, there are some common patterns of data loading and and management on the client side that are worth knowing. We'll go into more detail about some of these in the {% post_link ui-ux "UI/UX Article" %}.

### Subscription readiness

A key thing to keep in mind is that a subscription will not instantly provide it's data. There'll be a latency between subscribing to the data on the client and it arriving from the publication on the server (and keep in that this time may be a lot longer for your users in production that for you locally in development!)
A key thing to keep in mind is that a subscription will not instantly provide its data. There'll be a latency between subscribing to the data on the client and it arriving from the publication on the server (and keep in that this time may be a lot longer for your users in production that for you locally in development!)

Although the Tracker system means you often don't *need* to think too much about this in building your apps, usually if you want to get the user experience right, you'll need to know when the data is ready.

Expand Down Expand Up @@ -184,7 +182,7 @@ The important detail in the above is in 4---that they system cleverly knows not

For instance if a user navigates between two pages that both subscribe to the exact same subscription, the same mechanism will kick in and no unnecessarily subscribing will happen.

### Publication behaviour when changing arguments
### Publication behavior when arguments change

It's also worth knowing a little about what happens on the server when the new subscription is started and the old one is stopped.

Expand Down Expand Up @@ -246,7 +244,7 @@ Meteor.publish('list/todoCount', function(listId) {

Then on the client, after subscribing to that publication, we can access the count with `Counts.get(`list/todoCount${listId}`)`.

## Client-side data management: Stores
## Client-side data with reactive stores

In Meteor, persistent or shared data comes over the wire on publications. However, there are some types of data which doesn't need to be persistent or shared between users. For instance, the "logged-in-ness" of the current user, or the route they are currently viewing.

Expand Down Expand Up @@ -308,7 +306,7 @@ WindowSize.simulateMobile = (device) => {
}
```

## Publishing Relational Data
## Publishing relational data

It's common to need a related sets of data from multiple collections on a given page. For instance, in the Todos app, when we render a todo list, we want the list itself, as well as the set of todos that belong to that list.

Expand Down Expand Up @@ -368,7 +366,7 @@ Meteor.publishComposite('list/todos', function(listId) {

In this example, we write a complicated query to make sure that we only ever find a list if we are allowed to see it, then, once per list we find (which can be one or zero times depending on access), we publish the todos for that list. Publish Composite takes care of stopping and starting the dependent cursors if the list stops matching the original query or otherwise.

## Complex Authorization in publications
## Complex authorization

We can also use `publish-composite` to perform complex authorization in publications. For instance, consider if we had a `admin/list/todos` publication that allowed an admin to bypass default publication's security for users with an `admin` flag set.

Expand Down Expand Up @@ -414,7 +412,7 @@ Meteor.publishComposite('admin/list/todos', function(listId) {
});
```

## Writing custom publications with the low level publish API
## Custom publications with the low level API

In all of our examples so far (outside of using`Meteor.publishComposite()`) we've returned a cursor from our `Meteor.publish()` handlers. Doing this ensures Meteor takes care of the job of keeping the contents of that cursor in sync between the server and the client. However, there's another API you can use for publish functions which is closer to the way the underlying Distributed Data Protocol (DDP) works.

Expand Down Expand Up @@ -449,7 +447,7 @@ Data published like this, from the client's perspective doesn't look any differe
One point to be aware of is that if you allow the user to *modify* data in the "psuedo-collection" you are publishing in this fashion, you'll want to be sure to re-publish the modifications to them via the publication.


## Turning a REST endpoint into a publication
## Loading data from a REST endpoint with a publication

As a concrete example of using the low-level API, consider the situation where you have some 3rd party REST endpoint which provides a changing set of data that's valuable to your users. How do you make that data available to your users?

Expand Down Expand Up @@ -493,7 +491,7 @@ Meteor.publish('polled-publication', function() {
Things can get more complicated; for instance you may want to deal with documents being removed, or share the polling between multiple users (in a case where the data being polled isn't private to that user).


## Turning a publication into a REST endpoint
## Accessing a publication as a REST endpoint

The alternate scenario occurs when you want to publish data to be consumed by a 3rd party, typically over REST. If the data we want to publish is the same as what we already publish via a publication, then we can use the [simple:rest](https://atmospherejs.com/simple/rest) package to do this really easily.

Expand Down
Loading

0 comments on commit 3b196ce

Please sign in to comment.