forked from rbobbins/olinjs-2-hw
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathREADME.md~
More file actions
593 lines (444 loc) · 21.7 KB
/
README.md~
File metadata and controls
593 lines (444 loc) · 21.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
<<<<<<< HEAD
#olin.js #2 — express & mongo
We'll be covering the basics of Express and MongoDB (through Mongoose). Install MongoDB with the following commands on Ubuntu:
```
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10;
echo "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen" | sudo tee /etc/apt/sources.list.d/10gen.list;
sudo apt-get update;
sudo apt-get install mongodb-10gen;
```
If you're on OSX, run:
```
brew install mongodb
```
## Express
Now that we've explored Node.js a little, we will abstract the details away with the [express](http://expressjs.com/) development framework.
Before, in the Node Beginner's Book, our code looked a lot like this:
```js
var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);
```
Let's write the equivalent code in Express. First, fork the repository to your account as we did in the last room. Then `git clone https://github.com/<YOUR GITHUB USERNAME>/olinjs-2.git` as in the last lesson to clone this repository. Next, in the `olinjs-2/` folder, run the command `npm install`. (If this triggers lots of
lines with errors, run `sudo npm install` instead, or send us an email).
Next, create a file named `app.js` in that folder and paste in the following:
```js
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send('hello world');
});
app.listen(3000);
```
Inside the `olinjs-2/` folder, run the command `node app`. While that's running,
go to `http://localhost:3000` in your web browser and you should see the following:
```
hello world
```
Great! You're so cool.
Express makes writing web servers in Node much easier. Here are some of the important differences between the examples you did in the book and Express:
* **Routing**. Instead of trying to parse the URL the user is at ourselves, we can just tell Express to match an individual path with a function.
* **Sending a response**. Express takes care of setting many obvious response headers for you. Express will also handle sending files like images, music, audio, or `.html` files from a folder easily.
* **Handling templates (kinda).** We haven't gotten here yet, but we'll touch on this later.
Let's go back to our `app.js`. We want more than just hello world. Let's make Express show the string `hello olin` when we go to `http://localhost:3000/olin`.
Delete what was in `app.js` and replace it with the following:
```js
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send('hello world');
});
app.get('/olin', function(req, res){
res.send('hello olin');
});
app.listen(3000);
```
So what does `app.get` do? It tells express that every time we have a `GET` request from a client (the browser). We'll learn more about this in Routing.
###Routing
Before we talk about routing, let's talk a bit about clients vs servers. For our purposes, we can think of **clients** as the things that view webpages, such as our browsers. On the other hand, **servers** are the things that give our browsers the things to render.
Routing is the process of serving up different pages for different urls. When you go to www.mycoolsite.com/ your computer goes out on the internet and asks mycoolsite's server for a page. Mycoolsite's server then sees that request and sends back information to your computer in the form of html. This html is then rendered on your browser.
If you go to www.mycoolsite.com/ mycoolsite's servers obviously can't send you the same data it sent www.mycoolsite.com/olin. So mycoolsite's servers needs to differentiate ```/``` from ```/olin```. This process is known as routing.
In the Node beginner book, we did routing through something like
```js
var pathname = url.parse(request.url).pathname;
route(handle, pathname, response, request);
```
Instead of writing code ourselves to handle the route, Express can do this for us:
```js
app.get('/', function (req, res){
res.send('hello world');
});
app.get('/olin', function (req, res){
res.send('hello olin');
});
```
These two routes we created are for the index page (/) and the olin page (/olin).
So what does `app.get` do? It tells express that every time that particular route (the first string argument) receives a `HTTP GET` request, we want to execute the anonymous function (the second argument). `HTTP` lets you perform different *types* of requests for a particular route, and these types are called **methods**. `GET` is just one of the methods you can perform, and is the most common (every time request an image or a script for example). You can [view the other types of HTTP methods on wikipedia](http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods). But for now, let's only consider these two:
* `GET` returns a resource (such as an image or an html page). This is used for when your browser wants to read information from a server.
* `POST` is used for when your browser wants to send information over to the server. For example, when you fill out an online form, that data is sent over to the server as `POST` data.
###Automatically Generate an Express app
Express also comes with a set of nifty tools to get you started. You can make a new Express app by going into a directory and running
```
$ express
create : .
create : ./package.json
create : ./app.js
create : ./public
create : ./public/javascripts
create : ./public/images
create : ./public/stylesheets
create : ./public/stylesheets/style.css
create : ./routes
create : ./routes/index.js
create : ./routes/user.js
create : ./views
create : ./views/layout.jade
create : ./views/index.jade
install dependencies:
$ cd . && npm install
run the app:
$ node app
```
This creates a series of directories `public`, `routes`, and `views`. The public directory is used for client side assets such as images and client side javascript. Views are used to store the .jade template files which we will cover later. Right now our focus will be on the routes directory.
The routes directory holds all of the logic behind our routes. The actual routing is done in `app.js` with the following lines
```js
app.get('/', routes.index);
app.get('/users', user.list);
```
If you go to the `routes/index` file, you'll see
```js
exports.index = function(req, res){
res.render('index', { title: 'Express' });
};
```
###App configuration
There are some initial configurations we want our app to do, such as running on a specific port, using routes, and setting up the public directory. This is all done in the `app.configure` function.
```js
app.configure(function(){
app.set('port', process.env.PORT || 3000); // sets up the port
app.set('views', __dirname + '/views'); // sets the path for views
app.set('view engine', 'jade'); // sets the engine that the views are rendered with
app.use(express.favicon()); // default favicon
app.use(express.logger('dev')); // error logging
app.use(express.bodyParser()); //
app.use(express.methodOverride());
app.use(app.router); //
app.use(express.static(path.join(__dirname, 'public'))); // sets the path for public files (css & js)
});
```
## Mongo
[MongoDB](http://en.wikipedia.org/wiki/Mongodb) is a database (system for storing data every time you run an application) that stores data in a form like JSON. We'll be using it as our primary method of storage.
In order to use Mongo locally, we need to start up the Mongo [daemon](http://en.wikipedia.org/wiki/Daemon_(computing). Open up your terminal and type this in
```
$ mongod
all output going to: /usr/local/var/log/mongodb/mongo.log
```
As long as ```mongod``` is running, you'll be able to access your MongoDB locally through the default 27017 Mongo port and through your console. Access it through the console by opening up a new tab in your terminal and typing
```
$ mongo
MongoDB shell version: 2.2.0
connecting to: test
>
```
We can see all the dbs we have on our box by typing
```
> show dbs
local (empty)
```
Let's create a new database and a new entry in that database.
```
> use test
switched to db test
> db.users.insert({'name':'alice'})
> db.users.find()
{ "_id" : ObjectId("51007865e481634f390b162f"), "name" : "alice" }
> db.users.insert({'name': 'bob', 'grade': 'A', 'assignments':[{1: 'A', 2: 'B'}]})
> db.users.find()
{ "_id" : ObjectId("51007865e481634f390b162f"), "name" : "alice" }
{ "_id" : ObjectId("510078bee481634f390b1630"), "name" : "bob", "grade" : "A",
"assignments" : [ { "1" : "A", "2" : "B" } ] }
> show dbs
local (empty)
test 0.203125GB
```
Mongo creates a database for us as soon as we start inserting items into it. It stores data in what's known as a `collection`. So in our case, users would be a collection. Items within a collection don't have to be consistant with each other (alice only has a name while bob has a name and a grade).
We can also delete items
```
> db.users.remove({'name': 'alice'})
> db.users.find()
{ "_id" : ObjectId("510078bee481634f390b1630"), "name" : "bob", "grade" : "A",
"assignments" : [ { "grade" : "A", "grade" : "B" } ] }
```
modify items
```
> db.users.update({'name': 'bob'}, {$set: {'class': 2013}})
> db.users.find()
{ "_id" : ObjectId("510078bee481634f390b1630"), "assignments" : [ { "grade" : "A", "grade" : "B" } ],
"class" : 2013, "grade" : "A", "name" : "bob" }
```
and search for items
```
> db.users.find({'grade': 'A'})
{ "_id" : ObjectId("510078bee481634f390b1630"), "assignments" : [ { "grade" : "A", "grade" : "B" } ],
"class" : 2013, "grade" : "A", "name" : "bob" }
```
There are loads more Mongo commands that can be found through [their documentation](http://docs.mongodb.org/manual/).
## Mongoose
[Mongoose](http://mongoosejs.com/) is a javascript wrapper for MongoDB that allows us to save javascript objects into our db without having to deal with the underlying Mongo commands. Install it by opening up a console and typing
```
npm install mongoose
```
You configure Mongoose to save objects using a [schema](http://mongoosejs.com/docs/guide.html). In a schema we define what kind of data we expect an object to have.
```js
var userSchema = mongoose.Schema({
name: String,
grade: String,
class: Number
})
```
We can now use this schema to create and save users
```
var User = mongoose.model('User', userSchema);
var bob = new User({name: 'bob', grade: 'A', class: '2013'});
bob.save(function (err) {
if (err)
console.log("Problem saving bob", err);
});
```
## Deploying
If we want Heroku to use a MongoDB, we have to use a 3rd party extension, Mongolab. Mongolab gives you a small amount of space on their cloud database servers. You get a special [URI](http://en.wikipedia.org/wiki/URI) that you can use to connect to your Mongolab database. Fork this repo and type in
```
$ heroku create
Creating arcane-dusk-9739... done, stack is cedar
$ heroku addons:add mongolab:starter
Adding mongolab:starter on arcane-dusk-9739... done, v6 (free)
```
Now you can check the Heroku configuration variables to check the URI of your mongo instance
```
=== arcane-dusk-9739 Config Vars
MONGOLAB_URI: mongodb://heroku_app<app number>:<magic secret stuff>.mongolab.com:<port>/heroku_app<app number>
PATH: bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin
```
The MONGOLAB_URI is the URI that you need to use to connect to your database.
=======
#olin.js #2 — homework
**Due Monday Jan 29 before class**
After the second lesson, we should be able to create and deploy an Express app that saves data to a Mongo database.
##Reading
The [olinjs-2 repo](https://github.com/olinjs/olinjs-2) should be updated with new code that shows a few examples of things we didn't get to in class
* Breakout out our database models into it's own folder
* Showing data in HTML through jade
Go to your olinjs-2 repo folder and run `git pull`. You should see a message like
```
remote: Counting objects: 40, done.
remote: Compressing objects: 100% (28/28), done.
remote: Total 37 (delta 13), reused 29 (delta 6)
Unpacking objects: 100% (37/37), done.
From github.com:olinjs/olinjs-2
e2b77b6..6caddd9 master -> origin/master
Auto-merging routes/user.js
CONFLICT (add/add): Merge conflict in routes/user.js
Auto-merging routes/index.js
CONFLICT (add/add): Merge conflict in routes/index.js
Auto-merging package.json
CONFLICT (content): Merge conflict in package.json
Auto-merging app.js
CONFLICT (add/add): Merge conflict in app.js
Automatic merge failed; fix conflicts and then commit the result.
```
This is because we edited a lot of files during class and they were edited again by me later on. If you guys have used Subversion before, you've probably seen these errors. This just means that Git tried to combine our two changes together, but since we changed the same things, Git doesn't know which change should be kept and which one should be thrown away. So open up one of the files Git says it has a merge conflict for (such as app.js) and you should see something like this
```
<<<<<<< HEAD
, user = require('./routes/user')
, http = require('http')
, path = require('path');
var mongoose = require('mongoose');
mongoose.connect(process.env.MONGOLAB_URI || 'localhost');
var schema = mongoose.Schema(
{ name: 'string' }
);
var Cat = mongoose.model('cat', schema);
=======
, User = require('./models/user')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, mongoose = require('mongoose');
>>>>>>> 6caddd920a478eacc1f9598f2647f7fddc251ea7
```
The first part is the stuff you had in your file. The second part is the stuff I modified. The long string of numbers is the commit id of the change that conflicted with your local copy of files. You get to choose which part you want to keep. Just delete the lines you don't want.
Now if you `git add app.js` and then `git commit -m "fixing merge"` and `git push` you can now push the updated code without conflicts back to your own repository.
###Path parameters
Before starting on the assignment we need to learn about *path parameters*. Path parameters are routes that have variables in them. So let's say that we have a `/users` route. What if we want to show Bob's homepage every time we went to `/users/bob`? How would we tell our server to route that to Bob's homepage? We could do something like
```js
app.get('/users/bob', function (req, res) {
// render bob's homepage here
});
```
No. Don't do that. What happens when our app has a million users? We'd be too busy writing all the routes to do more fun things such as
* sleeping in our piles of money
* hookers & blow
* beating up David
* going to Vegas and betting all your money on blackjack because blackjack is an easy game to play the odds but having bad luck and losing lots of money anyway
So instead, we can tell Express to make the argument after `/users` a variable, like so
```js
app.get('/users/:user', function (req, res) {
// render bob's homepage here
});
```
Now when our server gets hit with `/users/bob` we can get the `:user` parameter
```js
app.get('/users/:user', function (req, res) {
console.log(req.params.user); // this logs bob if we go to /users/bob
});
```
Every route request we do in both Express and Node have two arguments, usually designated by `req` and `res`.
* `req` is the [request object](http://expressjs.com/api.html#req.params). It is what our servers get as input when a route is hit. In this case,
* `res` is the [response object](http://expressjs.com/api.html#res.status). It is what our servers send back to the clients.
### A bit about jade
So by now you've probably noticed the .jade files in your `/views` directory. These .jade files are eventually what's going to get rendered into the HTML that your browser sees.
Your app turns the .jade files into HTML through the [jade templating engine](http://jade-lang.com/). We specified that we're going to use jade in our app.js file through
```js
app.configure(function(){
app.set('port', process.env.PORT || 5000);
...
app.set('view engine', 'jade');
...
});
```
Why use jade over regular html? There are lots of reasons, but the ones I can think of right now are
**Less typing**
because typing is a skill that we are all terrible at. We don't notice only because we are also terrible at reading.
**Reuse HTML**
Check out facebook.com. Notice that the site has the same header and right ticker bar no matter what page you're on? Wouldn't it suck if you had to copy and paste the same HTML for that every time a developer wanted to create a new facebook page? Even if you did, what happens when you want to make the header bar have an extra button? You're going to have go to through every single file and add in that button.
With jade, we can tell our app that we have a `template` that we want the site to look like and only write that HTML once.
To see how this works, open up `views/index.jade` in an Express app that you created. There should be this
```jade
extends layout
block content
h1= title
p Welcome to #{title}
```
Now run the express app with `node app.js` and go to your localhost site. Inspect the page with your browser and you should find html like
```html
<!DOCTYPE html>
<html>
<head>
<title>Express</title>
<link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body>
<h1>Express</h1>
<p>Welcome to Express</p>
</body>
</html>
```
If you notice in `index.jade` we didn't specify the html tags, doctype, or the title and yet it's there. It's rendering that html through the `layout.jade` file which has the following
```jade
doctype 5
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content
```
We're *extending* `layout.jade` through the `extends layout` line in our `index.jade` file. This means that everything that appears in `layout.jade` will appear in `index.jade`.
After the `extends layout` line in `index.jade` we have a line that says `block content`. This line tells jade that anything *inside* of that block should go in the `block content` area in `layout.jade`.
So the combination of `index.jade` and `layout.jade` gives us something like
```jade
doctype 5
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
body
h1= title
p Welcome to #{title}
```
Which is the same thing that we see on the HTML page when we go to our localhost.
**Embedded variables**
Another nice thing that jade gives us is the ability to embed variables inside of our HTML files. Let's go back to facebook.com. It would suck if we had to manually write in every user's name on their own page. Jade handles this for us. We can do something like
```jade
p Welcome to #{title}
```
When we go to our localhost, we see
```
Welcome to Express
```
We passed the title variable into jade through the following line in index.js
```js
exports.index = function(req, res){
res.render('index', { title: 'Express' });
};
```
The first argument that we passed to res.render is `index` which refers to the `index.jade` file. Our second argument is a Javascript Object (javascript objects are just key-value stores. They look like Python dictionaries).
Jade gives us multiple ways we can embed these variables into our page. We've already seen
```jade
#{title}
```
Another valid form of syntax is
```jade
h1= title
```
This sets the value of the \<h1\> tag equal to whatever the title variable is.
**Programming functionality**
You can also have things like if statements, for loops, etc in your templates. Want to display an arbitrarly long list of users in a page? Use a for loop that renders html.
Let's suppose we have a list of users in our user.js file
```js
res.render('users', {users: [
{name: 'bob', class: 2013},
{name: 'joe', class: 2011},
{name: 'alice', class: 2011}
],
title: 'My First app'});
```
We can show an entire list of users with the following `user.jade` file
```jade
div
each user, i in users
div(value=user)
#{user.name} graduates in #{user.class}
```
## Some parting words
In order to complete this assignment, you're also going to have to use arrays & sorting in Mongoose. We didn't cover this in class, but it's not that much of a stretch to Google.
We encourage you to look at documentation and search for code/answers to problems that you run into. Attribute from where you copy, not just for honesty but because you'll probably run into the same issue again someday. This way, it'll be way easier to go back to where you found that answer.
Are you running into errors that the first page of Google results doesn't solve? Email out to the mailing list. Chances are, we've already had this error before.
##Assignment
* Fork this repo to your own account
* Run `express` in the folder that you just forked to create a new express app
* Edit your `package.json` file to look like this
```json
{
"name": "olinjs-2-hw",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app"
},
"dependencies": {
"express": "3.x",
"jade": "*",
"mongoose": "3.x"
},
"engines": {
"node": "0.8.14 ",
"npm": "1.1.65"
}
}
```
* Run `npm install`
* Create an app that has the following routes
* GET `/cats/new` => creates a new cat. Cats have a random age, a list of colors, and a name. Don't hardcode these values.
* GET `/cats` => shows a sorted list of cats by age. This should display their names, colors, and age
* GET `/cats/color/:color` => shows a sorted list of cats by age that have that specific color
* GET `/cats/delete/old` => deletes the oldest cat :c The cat should no longer appear on any lists
* deploy this to Heroku and add your app to the [homework 2 sheet](https://docs.google.com/spreadsheet/ccc?key=0AjqGw-pw5UuudFhQSmJhZlRZWEhRTWcwYmxBVld6c1E#gid=0)
##References
* [MongooseJS](http://mongoosejs.com/)
* [MongoDB](http://www.mongodb.org/)
* [Jade](http://jade-lang.com/)
>>>>>>> 01ddae7c452e8bbef95583b2be96f926b8bae4c2