├── .gitignore ├── bin ├── compile └── leanpub ├── book.md ├── chapters ├── 01-introduction.md ├── 02-whatisdevenv.md ├── 03-vagrant.md ├── 04-terminal.md ├── 05-editors.md ├── 06-git.md ├── 07-github.md ├── 08-setup.md ├── 09-ruby.md ├── 10-javascript.md ├── 11-python.md ├── 12-recap.md ├── Book.txt ├── changelog.md ├── packages.md └── x01-next.md ├── package.json └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .dropbox 4 | Icon* 5 | preview 6 | releases/**/* 7 | published 8 | site 9 | preface.md 10 | convert_html 11 | dist 12 | leanpub 13 | npm-debug.log -------------------------------------------------------------------------------- /bin/compile: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | var build = require('manuscript-builder') 4 | 5 | build({ 6 | target: '/book.md', 7 | bookDir: '/chapters/', 8 | tableOfContents: '/chapters/Book.txt' 9 | }, function () { 10 | console.log('book.md updated') 11 | }) 12 | -------------------------------------------------------------------------------- /bin/leanpub: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | var fs = require('fs'); 4 | 5 | var book = fs.readFileSync('book.md'); 6 | book = book.toString().replace(/```/g, '\n~~~~~~~~\n'); 7 | fs.writeFileSync('leanpub/manuscript/chapters.txt', book); -------------------------------------------------------------------------------- /chapters/01-introduction.md: -------------------------------------------------------------------------------- 1 | # Hello, dear reader. 2 | 3 | ## Thank you 4 | 5 | Thank you for buying this book! It is independently published, and each sale makes a significant difference. 6 | 7 | ## Why write this? 8 | When I first started programming, learning the languages was the easy part compared to figuring out text editors, version control, testing, and all the different tools that come along with writing code for a real project. 9 | 10 | My goal for this book is to help flatten the learning curve a little, so that going from hello world to a complex application isn't quite as difficult for you as it was for me when I started out. 11 | 12 | ## About the book 13 | If you're not sure how to choose between programming in ruby, python, or javascript, this guide will get you familiar with the tools, syntax, and workflow associated with each language. 14 | 15 | By comparing ruby, python, and javascript development environments you'll get a strong sense of which language best fits your style. 16 | 17 | Maybe you'll learn that you want to work with all three of these languages, or two out of three. Whatever your decision, you'll be able to use this guide as a reference if you need help remembering how to set up a development environment for any one of these three languages. 18 | 19 | ### Main concepts you'll learn: 20 | - command-line tools 21 | - virtual machines 22 | - text editors 23 | - version control 24 | - package managers 25 | - documentation 26 | - testing 27 | - language basics 28 | - intro to web frameworks 29 | 30 | ## This book is open source 31 | Contribute errata or content requests at the GitHub repository for this book: [github.com/civicmakerlab/development-environments-for-beginners](https://github.com/civicmakerlab/development-environments-for-beginners). 32 | -------------------------------------------------------------------------------- /chapters/02-whatisdevenv.md: -------------------------------------------------------------------------------- 1 | # What is a development environment? 2 | 3 | ### Wait, what is this development environment thing? 4 | 5 | A development environment is like a workshop. A space and set of tools for building projects before they are released into the world wide web. Everything you need to build your project should exist in your development environment, and ideally your development environment will be similar or identical to the production environment on which your project will be released. 6 | 7 | A development environment is a stage in the workflow, or release cycle, of a project. 8 | 9 | ### Other stages can include: 10 | - testing 11 | - staging 12 | - production 13 | 14 | There can be other stages, too, depending on the complexity of the project and the requirements you must meet for quality assurance and user testing. 15 | 16 | In this book we take a look at setting up development environments for Ruby, JavaScript, and Python, and introduce the basics of each of those three languages. -------------------------------------------------------------------------------- /chapters/03-vagrant.md: -------------------------------------------------------------------------------- 1 | # Vagrant: install an operating system inside of your operating system! 2 | 3 | Vagrant is a tool for running "virtual machines" on a computer. Let's say your computer is running the Windows operating system. With vagrant, you can run a virtual machine on your computer that is running another operating system, like Ubuntu. 4 | 5 | It's almost like dual booting, like having both Windows and Ubuntu installed on a computer, except you have a lot more control over a virtual machine. You can spin one up temporarily to work on a project, and when you're done, destroy the virtual machine. 6 | 7 | Vagrant makes this process easy. It relies on virtual machine software – there are a number of options, but we'll use software called virtualbox. 8 | 9 | In a way, vagrant is a wrapper around virtualbox – making it easy to create, run, and destroy virtual machines that run from the command line. 10 | 11 | The primary operating system on your computer is called the "host" operating system. Any virtual machines you create are called "guest" operating systems. 12 | 13 | We'll use the term **box** to describe the virtual machines that we create using vagrant. 14 | 15 | ## Reasons for using vagrant: 16 | - Keep your host OS clean by installing dependencies for your project in a virtual machine rather than having everything installed on your host OS 17 | - Have the same operating system and same dependencies set up in your virtual machine as you have on the production server, making it easier to deploy your application because there's little difference between the two environments. 18 | - The people on your team can use vagrant so that all of your development environments match, easing issues with some people having issues with developing on a particular operating system. 19 | 20 | ## Vagrantfiles 21 | 22 | Vagrant uses a file named `Vagrantfile` in your project directory as a config file for your vagrant box. 23 | 24 | There are many config options you can set. To learn more check out the Vagrantfile section of the vagrant documentation: [http://docs.vagrantup.com/v2/vagrantfile/index.html](http://docs.vagrantup.com/v2/vagrantfile/index.html). 25 | 26 | ## Website 27 | http://www.vagrantup.com/ 28 | 29 | ## Install 30 | 31 | ### Virtualbox: 32 | 33 | First we install [virtualbox](https://www.virtualbox.org/). 34 | 35 | Go to the virtualbox downloads folder: [https://www.virtualbox.org/wiki/Downloads](https://www.virtualbox.org/wiki/Downloads). 36 | 37 | Click the link for your operating system to download the virtualbox installer. Once it's finished downloading, run the installer. 38 | 39 | If needed, you can follow the **virtualbox installation instructions:** [http://www.virtualbox.org/manual/ch02.html](http://www.virtualbox.org/manual/ch02.html) 40 | 41 | #### Documentation 42 | 43 | You probably don't need to, but if you want to dig into virtualbox in depth, check out the documentation: [https://www.virtualbox.org/wiki/Documentation](https://www.virtualbox.org/wiki/Documentation). 44 | 45 | 46 | 47 | ### Vagrant 48 | 49 | Installing vagrant is pretty easy. Just grab the downloader for your operating system from the vagrant downloads page: 50 | 51 | **download vagrant:** 52 | 53 | [http://www.vagrantup.com/downloads](http://www.vagrantup.com/downloads) 54 | 55 | Choose the installer package for your operating system. 56 | 57 | **installation instructions:** 58 | 59 | [docs.vagrantup.com/v2/installation](http://docs.vagrantup.com/v2/installation/index.html) 60 | 61 | #### Documentation 62 | [docs.vagrantup.com/v2](http://docs.vagrantup.com/v2/) 63 | 64 | ## Set up your first vagrant machine 65 | 66 | Let's get started with the basics of using vagrant. 67 | 68 | Open the terminal on your computer and run this command: 69 | 70 | ~~~~~~~~ 71 | vagrant 72 | ~~~~~~~~ 73 | 74 | Running the command by itself will show you all the possible sub-commands and options you can pass. 75 | 76 | Let's try this thing out. 77 | 78 | Create and navigate to your `dev-envs` folder: 79 | 80 | ~~~~~~~~ 81 | mkdir ~/dev-envs 82 | cd ~/dev-envs 83 | ~~~~~~~~ 84 | 85 | Create a folder named `tmp` and change directory into it: 86 | 87 | ~~~~~~~~ 88 | mkdir tmp && cd tmp 89 | ~~~~~~~~ 90 | 91 | Run `vagrant init` to create a Vagrantfile in our tmp directory: 92 | 93 | ~~~~~~~~ 94 | vagrant init precise32 http://files.vagrantup.com/precise32.box 95 | ~~~~~~~~ 96 | 97 | By passing the name `precise32` and the url of the box we want to use, we prepopulate the Vagrantfile with the vagrant box we intend to use for our project. And by passing a url we're letting vagrant know that we want to download the vagrant box, as it isn't on our computer yet. 98 | 99 | precise32 is the name for Ubuntu 12.04 LTS 32-bit. 100 | 101 | You should see output on the terminal like this: 102 | 103 | ~~~~~~~~ 104 | A `Vagrantfile` has been placed in this directory. You are now 105 | ready to `vagrant up` your first virtual environment! Please read 106 | the comments in the Vagrantfile as well as documentation on 107 | `vagrantup.com` for more information on using Vagrant. 108 | ~~~~~~~~ 109 | 110 | Now, run `vagrant up` to boot your vagrant box: 111 | 112 | ~~~~~~~~ 113 | vagrant up 114 | ~~~~~~~~ 115 | 116 | This does a couple things: because we passed a url to vagrant init, and we don't already have a box on our machine named precise32, `vagrant up` will first download the precise32 box, then boot it with any configuration that's been set in the Vagrantfile. 117 | 118 | You should see output on the terminal like this: 119 | 120 | ~~~~~~~~ 121 | Bringing machine 'default' up with 'virtualbox' provider... 122 | [default] Box 'precise32' was not found. Fetching box from specified URL for 123 | the provider 'virtualbox'. Note that if the URL does not have 124 | a box for this provider, you should interrupt Vagrant now and add 125 | the box yourself. Otherwise Vagrant will attempt to download the 126 | full box prior to discovering this error. 127 | Downloading or copying the box... 128 | Extracting box...te: 1727k/s, Estimated time remaining: 0:00:01) 129 | Successfully added box 'precise32' with provider 'virtualbox'! 130 | [default] Importing base box 'precise32'... 131 | [default] Matching MAC address for NAT networking... 132 | [default] Setting the name of the VM... 133 | [default] Clearing any previously set forwarded ports... 134 | [default] Fixed port collision for 22 => 2222. Now on port 2201. 135 | [default] Creating shared folders metadata... 136 | [default] Clearing any previously set network interfaces... 137 | [default] Preparing network interfaces based on configuration... 138 | [default] Forwarding ports... 139 | [default] -- 22 => 2201 (adapter 1) 140 | [default] Booting VM... 141 | [default] Waiting for VM to boot. This can take a few minutes. 142 | [default] VM booted and ready for use! 143 | [default] Configuring and enabling network interfaces... 144 | [default] Mounting shared folders... 145 | [default] -- /vagrant 146 | ~~~~~~~~ 147 | 148 | Now that the box is up and running, we can ssh into this instance of Ubuntu that we just set up! 149 | 150 | We do that by running `vagrant ssh` in the terminal: 151 | 152 | ~~~~~~~~ 153 | vagrant ssh 154 | ~~~~~~~~ 155 | 156 | You should see output on the terminal like this: 157 | 158 | ~~~~~~~~ 159 | Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic-pae i686) 160 | 161 | * Documentation: https://help.ubuntu.com/ 162 | Welcome to your Vagrant-built virtual machine. 163 | Last login: Fri Sep 14 06:22:31 2012 from 10.0.2.2 164 | ~~~~~~~~ 165 | 166 | You have now entered your vagrant box. Every command you type in your terminal now happens in this instance of Ubuntu Linux. 167 | 168 | To exit from the vagrant box, and take the terminal back to your host operating system, run the `exit` command: 169 | 170 | ~~~~~~~~ 171 | exit 172 | ~~~~~~~~ 173 | 174 | You prompt should now look the same as before you ran `vagrant ssh`. 175 | 176 | Now that we downloaded that precise32 box once, we won't have to do it again. We'll be able to use it for each new project we start. 177 | 178 | Next time we're about to create a vagrant box we'll navigate to the project folder and run 'vagrant init' again, like this: 179 | 180 | ~~~~~~~~ 181 | vagrant init precise32 182 | ~~~~~~~~ 183 | 184 | We don't have to pass in the url, because we've already downloaded the box. 185 | 186 | When we run `vagrant up`, the box won't be downloaded, because we've already got a copy of it sitting on our computer. This speeds things up. 187 | 188 | To see which boxes you've currently got downloaded run this command: 189 | 190 | ~~~~~~~~ 191 | vagrant box list 192 | ~~~~~~~~ 193 | 194 | Let's stop this vagrant instance we created in our ~/dev-envs/tmp folder. 195 | 196 | Run this command: 197 | 198 | ~~~~~~~~ 199 | vagrant halt 200 | ~~~~~~~~ 201 | 202 | We can start the box back up again any time by running `vagrant up` from inside this folder. 203 | 204 | Let's now destroy the vagrant instance. 205 | 206 | To learn about this use the following command: 207 | 208 | ~~~~~~~~ 209 | vagrant destroy --help 210 | ~~~~~~~~ 211 | 212 | You'll see output like this: 213 | 214 | ~~~~~~~~ 215 | Usage: vagrant destroy [vm-name] 216 | 217 | -f, --force Destroy without confirmation. 218 | -h, --help Print this help 219 | ~~~~~~~~ 220 | 221 | 222 | ## Alternatives to vagrant/virtualbox 223 | 224 | **docker** 225 | 226 | Docker is a cool new approach that uses lightweight containers for encapsulating applications. Definitely worth checking out: [http://www.docker.io](http://www.docker.io/). 227 | 228 | Go through their getting started tutorial to get a sense of how it works: [http://www.docker.io/gettingstarted](http://www.docker.io/gettingstarted/). 229 | 230 | It's also possible to use docker on one of your vagrant machines, which seems like an awesome option. If you're feeling adventurous, check out docker's documentation for integrating with vagrant and virtualbox: [http://docs.docker.io/en/latest/installation/vagrant/](http://docs.docker.io/en/latest/installation/vagrant/). 231 | 232 | **nitrous.io** 233 | 234 | You can have a development environment that you access through the web using nitrous.io: [https://www.nitrous.io](https://www.nitrous.io/). 235 | 236 | This is great if you're on a machine that runs an operating system like ChromeOS, or it's something you can't install software on for whatever reason. 237 | 238 | You can set up one box on nitrous.io for free, and beyond that it costs money. 239 | -------------------------------------------------------------------------------- /chapters/04-terminal.md: -------------------------------------------------------------------------------- 1 | # Terminal: conquer the command line 2 | 3 | Get excited, it's time to use the terminal. 4 | 5 | The most important thing to keep in mind: **don't be afraid of the terminal.** 6 | 7 | You can only break your computer using the terminal if you do really weird stuff. I mean, you mostly have to go out of your way to break your computer. There are a few ways you can crunch your machine, so it's worthwhile to be skeptical about new commands and research what they do before you use them. 8 | 9 | Each command that you run on the terminal will use a pattern similar to this: 10 | 11 | ~~~~~~~~ 12 | name-of-command --options input-file-or-text output 13 | ~~~~~~~~ 14 | 15 | The `name-of-command` is the actual command. Options are often preceeded by two dashes, or they can likely be shorted to one dash and the first letter or an abbreviation. Then, there will occasionally be some kind of input text or file that the command is acting on, or changing. Similarly, you might specify a filename for the output of the command. You'll see that many of the commands below are more simple. 16 | 17 | ## Vagrant 18 | 19 | Because we'll be using vagrant to create a virtual machine running Ubuntu throughout this book, this chapter will teach usage of Linux / Unix terminal commands. 20 | 21 | ## Vagrant 22 | 23 | Let's create a vagrant machine in your javascript dev-envs folder: 24 | 25 | ~~~~~~~~ 26 | mkdir ~/dev-envs/terminal 27 | cd ~/dev-envs/terminal 28 | ~~~~~~~~ 29 | 30 | Create a new vagrant machine using the Ubuntu Precise box: 31 | 32 | ~~~~~~~~ 33 | vagrant init precise32 34 | ~~~~~~~~ 35 | 36 | Now start the vagrant machine: 37 | 38 | ~~~~~~~~ 39 | vagrant up 40 | ~~~~~~~~ 41 | 42 | If all goes well that'll result in output similar to the following: 43 | 44 | ~~~~~~~~ 45 | Bringing machine 'default' up with 'virtualbox' provider... 46 | [default] Importing base box 'precise32'... 47 | [default] Matching MAC address for NAT networking... 48 | [default] Setting the name of the VM... 49 | [default] Clearing any previously set forwarded ports... 50 | [default] Fixed port collision for 22 => 2222. Now on port 2200. 51 | [default] Creating shared folders metadata... 52 | [default] Clearing any previously set network interfaces... 53 | [default] Preparing network interfaces based on configuration... 54 | [default] Forwarding ports... 55 | [default] -- 22 => 2200 (adapter 1) 56 | [default] Booting VM... 57 | [default] Waiting for VM to boot. This can take a few minutes. 58 | [default] VM booted and ready for use! 59 | [default] Configuring and enabling network interfaces... 60 | [default] Mounting shared folders... 61 | [default] -- /vagrant 62 | ~~~~~~~~ 63 | 64 | Now we will log in to the vagrant machine. This will be very much like using the `ssh` command to log in to a remote server. 65 | 66 | Use this command: 67 | 68 | ~~~~~~~~ 69 | vagrant ssh 70 | ~~~~~~~~ 71 | 72 | You should see output similar to the following: 73 | 74 | ~~~~~~~~ 75 | Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic-pae i686) 76 | 77 | * Documentation: https://help.ubuntu.com/ 78 | Welcome to your Vagrant-built virtual machine. 79 | Last login: Fri Sep 14 06:22:31 2012 from 10.0.2.2 80 | ~~~~~~~~ 81 | 82 | Now we can experiment with terminal commands 83 | 84 | ### Basic commands: 85 | 86 | Changing directory: 87 | 88 | ~~~~~~~~ 89 | cd path/of/directories 90 | ~~~~~~~~ 91 | 92 | `cd` on its own, or `cd ~` will take you to your home directory. 93 | 94 | Create a directory: 95 | 96 | ~~~~~~~~ 97 | mkdir directory-name 98 | ~~~~~~~~ 99 | 100 | Show the directory you're currently in: 101 | 102 | ~~~~~~~~ 103 | pwd 104 | ~~~~~~~~ 105 | 106 | List the files in the directory: 107 | 108 | ~~~~~~~~ 109 | ls 110 | ~~~~~~~~ 111 | 112 | List the files in a more readable way, with useful information like permissions included: 113 | 114 | ~~~~~~~~ 115 | ls -al 116 | ~~~~~~~~ 117 | 118 | Open a file with its default application: 119 | 120 | ~~~~~~~~ 121 | open filename 122 | ~~~~~~~~ 123 | 124 | Open current directory in the finder: 125 | 126 | ~~~~~~~~ 127 | open . 128 | ~~~~~~~~ 129 | 130 | or open some other directory: 131 | 132 | ~~~~~~~~ 133 | open path/to/directory 134 | ~~~~~~~~ 135 | 136 | Create an empty file if it doesn't already exist: 137 | 138 | ~~~~~~~~ 139 | touch file-name 140 | ~~~~~~~~ 141 | 142 | Open and edit a file with the simple nano editor: 143 | 144 | ~~~~~~~~ 145 | nano file-name 146 | ~~~~~~~~ 147 | 148 | Move a file or directory: 149 | 150 | ~~~~~~~~ 151 | mv file new/path/to/file 152 | ~~~~~~~~ 153 | 154 | Rename a file or directory: 155 | 156 | ~~~~~~~~ 157 | mv file new-file-name 158 | ~~~~~~~~ 159 | 160 | Copy a file: 161 | 162 | ~~~~~~~~ 163 | cp file name-of-file-copy 164 | ~~~~~~~~ 165 | 166 | Copy a directory 167 | 168 | ~~~~~~~~ 169 | cp -R directory directory-copy 170 | ~~~~~~~~ 171 | 172 | The `-R` option allows for recursive copying of files inside the directory. 173 | 174 | 175 | Delete a file: 176 | 177 | ~~~~~~~~ 178 | rm file-name 179 | ~~~~~~~~ 180 | 181 | Delete a directory and its contents: 182 | 183 | ~~~~~~~~ 184 | rm -rf path/to/directory 185 | ~~~~~~~~ 186 | 187 | Let's dissect this command: 188 | 189 | `rm` is for deleting things. 190 | 191 | `-rf` means that files will be recursively deleted, and the deletion will be forced. `r` is for recursive, `f` is for forced. 192 | 193 | **Never do this:** 194 | 195 | ~~~~~~~~ 196 | rm -rf / 197 | ~~~~~~~~ 198 | 199 | Be very careful with the rm command. You can easily delete things on accident. 200 | 201 | This command is deleting with the `rm` command, recursively forcing the deletion of files and folders with `-rf`, and we've passed the root directory, `/` as the thing to delete. This means it will delete everything on your hard drive. Some operating systems protect against this mistake, and if you're not the root user you would need to prefix this command with `sudo` to make it work. Be careful, and be nice. 202 | 203 | **Clear the terminal screen of previous activity:** 204 | 205 | ~~~~~~~~ 206 | clear 207 | ~~~~~~~~ 208 | 209 | **Reset the terminal:** 210 | 211 | ~~~~~~~~ 212 | reset 213 | ~~~~~~~~ 214 | 215 | **Stop a running process:** 216 | 217 | ~~~~~~~~ 218 | control+C 219 | ~~~~~~~~ 220 | 221 | If a process is running in the terminal and you need to stop it, press the `control` key and the `C` key at the same time. 222 | 223 | **Run multiple commands on one line:** 224 | 225 | ~~~~~~~~ 226 | && 227 | ~~~~~~~~ 228 | 229 | With `&&` you can chain together multiple commands that execute one after the other. This example creates a directory, then moves you into that new directory: 230 | 231 | ~~~~~~~~ 232 | mkdir new-folder && cd new-folder 233 | ~~~~~~~~ 234 | 235 | 236 | ## Aliases and environment variables 237 | 238 | 239 | ### Aliases 240 | 241 | Aliases allow you to create abbreviated commands that alias long, complex, or regularly used commands. 242 | 243 | Here is an example: 244 | 245 | ~~~~~~~~ 246 | alias l="ls -al" 247 | ~~~~~~~~ 248 | 249 | The above aliases the `ls -al` command to a shortened `l`. 250 | 251 | To create an alias you will open the .bashrc file in your home folder. 252 | 253 | Open the .bashrc file with nano: 254 | 255 | ~~~~~~~~ 256 | nano ~/.bashrc 257 | ~~~~~~~~ 258 | 259 | Add the following alias to the bottom of the file: 260 | 261 | ~~~~~~~~ 262 | alias pizza="echo 'pizza is awesome!'" 263 | ~~~~~~~~ 264 | 265 | Save the file by pressing `control + O`. 266 | 267 | Exit nano by pressing `control + x`. 268 | 269 | ### Environment variables 270 | 271 | Environment variables represent values that are useful across for processes running on your computer. 272 | 273 | #### Reading an environment variable: 274 | 275 | In the terminal, run the following: 276 | 277 | ~~~~~~~~ 278 | echo $HOME 279 | ~~~~~~~~ 280 | 281 | If you're logged into a vagrant machine, you'll see output like this: 282 | 283 | ~~~~~~~~ 284 | /home/vagrant 285 | ~~~~~~~~ 286 | 287 | This is your home folder, also known as your user folder. 288 | 289 | On a Mac you'll see output like this: 290 | 291 | ~~~~~~~~ 292 | /Users/your-user-name 293 | ~~~~~~~~ 294 | 295 | #### Setting an environment variable 296 | 297 | In the terminal, set an environment variable like this: 298 | 299 | ~~~~~~~~ 300 | PIZZA="ooooooh, pizza" 301 | ~~~~~~~~ 302 | 303 | Now, you can read the variable the same as we did before: 304 | 305 | ~~~~~~~~ 306 | echo $PIZZA 307 | ~~~~~~~~ 308 | 309 | If you close or reset your terminal session, you'll lose this temporary variable. To save an environment variable so it can be accessed in all your sessions, we'll place the definition of the variable in the ~/.bashrc file. 310 | 311 | Open the ~/.bashrc file: 312 | 313 | ~~~~~~~~ 314 | nano ~/.bashrc 315 | ~~~~~~~~ 316 | 317 | Add the following to the bottom of the ~/.bashrc file: 318 | 319 | ~~~~~~~~ 320 | export PIZZA="ooooooh, pizza" 321 | ~~~~~~~~ 322 | 323 | Source the .bashrc file: 324 | 325 | ~~~~~~~~ 326 | source ~/.bashrc 327 | ~~~~~~~~ 328 | 329 | Now, you can close the terminal window, open a new one, and run the following command: 330 | 331 | ~~~~~~~~ 332 | echo $PIZZA 333 | ~~~~~~~~ 334 | 335 | And you'll still see the following output: 336 | 337 | ~~~~~~~~ 338 | ooooooh, pizza 339 | ~~~~~~~~ 340 | 341 | -------------------------------------------------------------------------------- /chapters/05-editors.md: -------------------------------------------------------------------------------- 1 | # Text editors 2 | 3 | ## Sublime Text Editor 4 | 5 | Sublime is a popular text editor with versions for Mac, Windows, and Linux. 6 | 7 | You can download an evaluation copy for free, and pay for a license when you're ready. 8 | 9 | In this book we'll be using version 2 of Sublime, in future updates to the book we'll switch to version 3. 10 | 11 | ### Install 12 | Go to [sublimetext.com](http://www.sublimetext.com/) and click the download button. 13 | 14 | This will download a .dmg file. Once the download has completed, double-click the .dmg file. Next, drag the Sublime Text application into your Applications folder. 15 | 16 | ### Use Sublime from the command line 17 | 18 | To use Sublime from the command line using Mac or Linux, you'll need to create a symbolic link: 19 | 20 | ~~~~~~~~ 21 | ln -s "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl" ~/bin/subl 22 | ~~~~~~~~ 23 | 24 | If you get an error you may need to create the `bin` folder: 25 | 26 | ~~~~~~~~ 27 | mkdir ~/bin 28 | ~~~~~~~~ 29 | 30 | And add `~/bin` to your path: 31 | 32 | Add: 33 | 34 | ~~~~~~~~ 35 | export PATH="$PATH:~/bin" 36 | ~~~~~~~~ 37 | 38 | To your `~/.profile` file. 39 | 40 | 41 | #### About `ln -s` 42 | 43 | `ln -s` is a command used for creating symbolic links, which you can think of like aliases. 44 | 45 | The first argument is the location of the original file, the second argument is the new, alias location. 46 | 47 | Now, you can run the `subl` command on the terminal. 48 | 49 | When you run `subl --help`, you'll get help information for the command that looks like this: 50 | 51 | ~~~~~~~~ 52 | Usage: subl [arguments] [files] edit the given files 53 | or: subl [arguments] [directories] open the given directories 54 | or: subl [arguments] - edit stdin 55 | 56 | Arguments: 57 | --project : Load the given project 58 | --command : Run the given command 59 | -n or --new-window: Open a new window 60 | -a or --add: Add folders to the current window 61 | -w or --wait: Wait for the files to be closed before returning 62 | -b or --background: Don't activate the application 63 | -s or --stay: Keep the application activated after closing the file 64 | -h or --help: Show help (this message) and exit 65 | -v or --version: Show version and exit 66 | 67 | --wait is implied if reading from stdin. Use --stay to not switch back 68 | to the terminal when a file is closed (only relevant if waiting for a file). 69 | 70 | Filenames may be given a :line or :line:column suffix to open at a specific 71 | location. 72 | ~~~~~~~~ 73 | 74 | ### Tips for using Sublime 75 | 76 | #### Searching files 77 | 78 | ##### Basic Search 79 | To do a basic text search, press `Command + f` to open the search bar. 80 | 81 | Here you can choose to use regular expressions or simple string searches. 82 | 83 | You can cycle through instances of a search phrase by clicking **Find** and **Find Prev**, and you can highlight all instances of a search phrase by clicking **Find All**. 84 | 85 | Exit the search bar by pressing the `esc` key. 86 | 87 | ##### Search all project files 88 | To search all files in your project, press `Shift + Command + f` to open the search panel. 89 | 90 | After you enter a search phrase in the **Find** input, press enter or click **Find**. A new tab will then open with all relevant search results. 91 | 92 | In the **Where** input you can specify files and folders that you want to search. Click the `...` button to the right of the input field to use a GUI to add and remove files and folders. 93 | 94 | Optionally you can use the **Replace** input to specify a phrase to replace the search phrase. 95 | 96 | To exit this search panel press the `esc` key. 97 | 98 | ##### Finding and switching between files 99 | Use the `Command + P` keyboard shortcut to bring up a menu that allows you to quickly navigate to other files in the project. Start typing the name of the file to filter the results. 100 | 101 | ##### Finding specific methods or functions 102 | Use the `Command + R` keyboard shortcut to get a menu that allows you to find classes, methods, functions, variable declarations, and other important pieces of code. Like the file menu mentioned above, you can start typing a name to filter the results. 103 | 104 | ##### Jumping to a specific line number 105 | There are two ways: `Control + G`, enter the line number, and hit return. Or `Command + P`, type a colon, then the line number, then hit return. 106 | 107 | #### Fun with multiple cursors 108 | One of the most enjoyable features of Sublime (and similar text editors) is being able to use multiple cursors for quickly editing multiple parts of a text file. 109 | 110 | ##### Search and Find All 111 | One way to get multiple cursors in a file is by searching using `Command + f`, entering a search phrase, and clicking **Find All**. This will heighlight all instances of the search phrase. Next you can press the left and right arrows to navigate around the text and make revisions like normal, only you'll be editing the text in multiple places. 112 | 113 | ##### Selecting multiple instances of a word with `Command + d` 114 | An even faster way of selecting multiple instances of a word is by clicking a word, then pressing `Command + d`. Pressing it once will select the word that the cursor was on. Pressing it repeatedly will select the next instance of the word in the text document, and eventually wrap around to the top of the file until it has searched up to the original word you clicked on. Hold down `Command + d` to quickly select all instances of the word the cursor is on. 115 | 116 | A shortcut to selecting all instances of a word at once is through the use of `Command + Control + G` on Mac or `Alt + F3` on Windows. 117 | 118 | ##### Using the `option` key 119 | By holding the `option` key and dragging up and down in a straight line over rows of text you'll create a cursor on each row of text. 120 | 121 | ##### Indenting quickly with `Command + {` and `Command + }` 122 | Select a block of text and indent it to the left with `Command + {` and to the right with `Command + }`. 123 | 124 | #### Start and end of files and lines with the `Command` and arrow keys. 125 | By holding `Command` and using the arrow keys you can quickly move to the start and end of the file and lines of text. 126 | 127 | `Command + Left Arrow` moves the cursor to the left side of the text line the cursor is on. 128 | 129 | `Command + Right Arrow` moves the cursor to the right side of the text line the cursor is on. 130 | 131 | `Command + Up Arrow` moves the cursor to the top of the text document. 132 | 133 | `Command + Down Arrow` moves the cursor to the bottom of the text document. 134 | 135 | By holding the `Shift` key along with any of the above four key combinations allows you to select text from the cursor's starting location to ending location of the cursor determined by the command. 136 | 137 | This is particularly useful for selecting a line of text. Use `Command + Right Arrow` to go to the end of the line, then `Shift + Command + Left Arrow` to select that entire line of text. 138 | 139 | #### Using the Sublime console 140 | 141 | Open the console using `ctrl + ` (control plus the backtick key) or by going to **View > Show Console** in the top menu. 142 | 143 | This is a Python console that uses Sublime's embedded version of Python intended for interacting with the Sublime plugin API. 144 | 145 | 146 | #### Compatibility with TextMate 147 | You can use TextMate snippets, color schemes, `.tmLanguage` files, and `.tmPreferences` files with Sublime 2. 148 | 149 | 150 | ### Installing Sublime packages with Package Control 151 | 152 | You can easily install third-party packages in Sublime to add new functionality by using a package manager called Package Control. This is useful for adding new color schemes, language syntax highlighters, 153 | 154 | The Package Control website: [sublime.wbond.net](https://sublime.wbond.net) 155 | 156 | #### Installing Package Control 157 | 158 | Install Package control by copying and pasting the following code into the Sublime console (press `Control + ` to open the console). 159 | 160 | ~~~~~~~~ 161 | import urllib2,os; pf='Package Control.sublime-package'; ipp = sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener( urllib2.ProxyHandler( ))); open( os.path.join( ipp, pf), 'wb' ).write( urllib2.urlopen( 'http://sublime.wbond.net/' +pf.replace( ' ','%20' )).read()); print( 'Please restart Sublime Text to finish installation') 162 | ~~~~~~~~ 163 | 164 | Next you'll need to restart Sublime. 165 | 166 | #### Finding and installing Packages 167 | 168 | 169 | ### Resources 170 | 171 | #### Website 172 | Check out the main Sublime website: [sublimetext.com](http://www.sublimetext.com/) 173 | 174 | #### Documentation 175 | Official Sublime documentation: [sublimetext.com/docs/2](http://www.sublimetext.com/docs/2/) 176 | 177 | Sublime Text unofficial documentation: [docs.sublimetext.info/en/sublime-text-2](http://docs.sublimetext.info/en/sublime-text-2) 178 | 179 | 180 | ### Alternative text editors: 181 | There are too many to list. Two that I recommend learning about are vim & nano. 182 | 183 | #### vim 184 | 185 | vim has a steep learning curve, so save this for later after you've mastered other tools. To learn more about vim, check out this list of resources: [net.tutsplus.com/articles/web-roundups/25-vim-tutorials-screencasts-and-resources/](http://net.tutsplus.com/articles/web-roundups/25-vim-tutorials-screencasts-and-resources/) 186 | 187 | #### nano 188 | 189 | nano is a simple, easy to use editor that you'll usually find on unix/linux operating systems. 190 | 191 | There are a few basic key commands you need to know to start out with nano: 192 | 193 | Edit something with nano: 194 | 195 | ~~~~~~~~ 196 | nano filename.txt 197 | ~~~~~~~~ 198 | 199 | Save a file: 200 | 201 | ~~~~~~~~ 202 | control+O 203 | ~~~~~~~~ 204 | 205 | Exit from nano (you'll be prompted to save): 206 | 207 | ~~~~~~~~ 208 | control+X 209 | ~~~~~~~~ 210 | 211 | Search for some text: 212 | 213 | ~~~~~~~~ 214 | control+W 215 | ~~~~~~~~ -------------------------------------------------------------------------------- /chapters/06-git.md: -------------------------------------------------------------------------------- 1 | # Git: it's like File > Save, only collaborative 2 | 3 | Seriously. You know how important it is to save your work. We've all been beaten into a sad sack of anger and disappointment when we've lost our work. 4 | 5 | Consider git to be the equivalent of File > Save that keeps track of every version of your work, and allows you to share those versions with other people and collaborate in a way that won't have you overwriting each other's changes. 6 | 7 | Git is version control software. 8 | 9 | There are many alternatives to git, but it has become a standard for developers in large part because of github.com, a service for hosting code using git. 10 | 11 | The best way to start learning git (and GitHub) is to visit [try.github.com](http://try.github.com). You should also try [githug, a game for learning git](https://github.com/Gazler/githug). 12 | 13 | ## Project website: 14 | http://git-scm.com/ 15 | 16 | ## Install 17 | download / install: http://git-scm.com/downloads 18 | 19 | If you are using a Mac, you can install using [homebrew](http://brew.sh/): 20 | 21 | ~~~~~~~~ 22 | brew install git 23 | ~~~~~~~~ 24 | 25 | > For more information about homebrew, check out the project's homepage: [brew.sh](http://brew.sh/). 26 | 27 | On Debian/Ubuntu, install using apt-get: 28 | 29 | ~~~~~~~~ 30 | apt-get install git 31 | ~~~~~~~~ 32 | 33 | For Windows machines, download git from the git website: [git-scm.com/downloads](http://git-scm.com/downloads) 34 | 35 | ## Documentation 36 | docs: http://git-scm.com/documentation 37 | 38 | ## Basics 39 | 40 | Here are some basics of using git: 41 | 42 | Create a git repository: 43 | 44 | ~~~~~~~~ 45 | cd name-of-folder 46 | git init 47 | ~~~~~~~~ 48 | 49 | Add files: 50 | 51 | ~~~~~~~~ 52 | git add name-of-file 53 | 54 | // or add all files in directory: 55 | 56 | git add . 57 | ~~~~~~~~ 58 | 59 | When you add files to a git repository they are "staged" and ready to be committed. 60 | 61 | Remove files: 62 | 63 | ~~~~~~~~ 64 | git rm name-of-file 65 | 66 | // force removal of files: 67 | 68 | git rm -rf name-of-file-or-directory 69 | ~~~~~~~~ 70 | 71 | Commit files and add a message using the `-m` option: 72 | 73 | ~~~~~~~~ 74 | git commit -m 'a message describing the commit' 75 | ~~~~~~~~ 76 | 77 | Create a branch: 78 | 79 | ~~~~~~~~ 80 | git branch name-of-branch 81 | ~~~~~~~~ 82 | 83 | Checkout a branch: 84 | 85 | ~~~~~~~~ 86 | git checkout name-of-branch 87 | ~~~~~~~~ 88 | 89 | Shortcut for creating a new branch and checking it out: 90 | 91 | ~~~~~~~~ 92 | git checkout -b name-of-branch 93 | ~~~~~~~~ 94 | 95 | Merge a branch into the master branch: 96 | 97 | ~~~~~~~~ 98 | git checkout master 99 | git merge name-of-branch 100 | ~~~~~~~~ 101 | 102 | Add a remote repository: 103 | 104 | ~~~~~~~~ 105 | git remote add origin git@github.com:yourname/projectname.git 106 | ~~~~~~~~ 107 | 108 | List associated repositories: 109 | 110 | ~~~~~~~~ 111 | git remote -v 112 | ~~~~~~~~ 113 | 114 | Pull changes from a remote repository: 115 | 116 | ~~~~~~~~ 117 | git pull origin master 118 | ~~~~~~~~ 119 | 120 | Push changes to a remote repository 121 | 122 | ~~~~~~~~ 123 | git push origin master 124 | ~~~~~~~~ 125 | 126 | Checkout a remote branch: 127 | 128 | ~~~~~~~~ 129 | git checkout -t origin/haml 130 | ~~~~~~~~ 131 | 132 | ## Resources 133 | 134 | **Official documentation** 135 | 136 | The [official git documentation](http://git-scm.com/doc) has an [API reference](http://git-scm.com/docs), a [book about git](http://git-scm.com/book), [videos](http://git-scm.com/videos) instructing on basic usage of git, and a [page of links](http://git-scm.com/doc/ext) to useful resources. 137 | 138 | **[try.github.io](http://try.github.io/)** 139 | 140 | This interactive tutorial is a great introduction to both git and GitHub. Highly recommended. 141 | 142 | **[gitready.com](http://gitready.com/)** 143 | 144 | I often refer to gitready.com for various tips and tricks for using git. 145 | 146 | **[help.github.com](https://help.github.com/)** 147 | 148 | The help section of GitHub's site has instructions for using github.com, but it also has some great documentation for using git. -------------------------------------------------------------------------------- /chapters/07-github.md: -------------------------------------------------------------------------------- 1 | # GitHub.com: a social network for git users 2 | 3 | GitHub is a great place to host your code. Many employers hiring for developer and designer positions will ask for a GitHub profile, and they'll use your GitHub activity as part of the criteria in their decision-making process. 4 | 5 | In fact, if you're looking to get a job with a particular company, try to find _their_ GitHub profile and start contributing to their open source projects. This will help you stand out, and they'll already know your technical abilities based on your open source contributions. That's a big win. 6 | 7 | GitHub has become the de facto code hosting service for most open source communities. 8 | 9 | ## Website 10 | http://github.com 11 | 12 | ## Create an account 13 | If you haven't already, create an account at [github.com](http://github.com). 14 | 15 | ## Create your first repository 16 | 17 | ## GitHub Pages 18 | 19 | GitHub Pages is a web hosting service offered by GitHub typically used for hosting websites for open source projects that have code hosted on GitHub. In practice, people use it for all kinds of sites. The only limitation is that it is strictly for static sites composed from html, css, and javascript. 20 | 21 | Learn more about GitHub Pages: [http://pages.github.com](http://pages.github.com) 22 | 23 | ### With GitHub Pages you can: 24 | - design a website any way you want by having complete control over the html, css, and javascript. 25 | - use simple templates for getting started using GitHub Pages. 26 | - create sites for yourself and all of your projects hosted on GitHub. 27 | - use a custom domain name if you want! 28 | 29 | 30 | 31 | Visit the [help section for GitHub Pages](https://help.github.com/categories/20/articles) to learn more details about hosting sites on GitHub. 32 | 33 | ### Host a site for free 34 | 35 | GitHub has a useful service called [GitHub Pages](http://pages.github.com) that allows you to host a simple site on their servers for free. 36 | 37 | To get started, fork this simple template: [github.com/maxogden/gh-pages-template](https://github.com/maxogden/gh-pages-template). 38 | 39 | Visit that github project, make sure you're logged in, and click Fork in the upper right side of the screen. 40 | 41 | Fork gh-pages-template to your personal account. 42 | 43 | Rename the repository from gh-pages-template to whatever you want by clicking on Settings on the right side of your fork of the repository, and changing the name there. 44 | 45 | That's it! You now have a website hosted through GitHub Pages. 46 | 47 | You'll be able to visit your site at __YOUR-USERNAME__.github.com/__YOUR-PROJECT-NAME__. 48 | 49 | You'll want to edit the content though, right? Add your cat pictures or resume or pizza recipes? You can do that. 50 | 51 | You can create, edit, move, rename, and delete files all through the GitHub website. Check out these blog posts on GitHub for details on how to do those things: 52 | - [Create files](https://github.com/blog/1327-creating-files-on-github) 53 | - [Edit files](https://github.com/blog/143-inline-file-editing) 54 | - [Move and rename files](https://github.com/blog/1436-moving-and-renaming-files-on-github) 55 | - [Delete files](https://github.com/blog/1545-deleting-files-on-github) 56 | 57 | You can also clone the project repository onto your computer: 58 | 59 | ~~~~~~~~ 60 | git clone git@github.com:__YOUR-USERNAME__/__YOUR-PROJECT-NAME__.git 61 | ~~~~~~~~ 62 | 63 | You can copy the git url to clone from the right-hand sidebar of your project repository. 64 | 65 | After cloning the repository, `cd` into it and make some changes: 66 | 67 | ~~~~~~~~ 68 | cd __YOUR-PROJECT-NAME__ 69 | nano index.html 70 | ~~~~~~~~ 71 | 72 | Add a bunch of content to index.html, and change the styles in style.css. 73 | 74 | After you've made some changes, add them to the repo and commit the changes: 75 | 76 | ~~~~~~~~ 77 | git add . 78 | git commit -m 'include a brief, clear message about the changes' 79 | ~~~~~~~~ 80 | 81 | Now, push your changes back to GitHub: 82 | 83 | ~~~~~~~~ 84 | git push origin gh-pages 85 | ~~~~~~~~ 86 | -------------------------------------------------------------------------------- /chapters/08-setup.md: -------------------------------------------------------------------------------- 1 | # Get started 2 | 3 | To get ready for working through the upcoming examples, we'll set up a few things. 4 | 5 | ## Make sure all the software we installed is running 6 | 7 | ### vagrant 8 | 9 | Running `vagrant --help` should return the help text of vagrant. 10 | 11 | ### git 12 | 13 | Running the command `git --help` should return the help text of git. 14 | 15 | ### sublime 16 | 17 | Open up the Sublime text editor to ensure that it is installed properly. 18 | 19 | ## Create a folder for working through examples 20 | 21 | Let's make a folder named dev-envs in your home directory. 22 | 23 | ### On Macs the home directory is: 24 | 25 | ~~~~~~~~ 26 | /Users/YOUR-USERNAME 27 | ~~~~~~~~ 28 | 29 | ### On Windows: 30 | 31 | ~~~~~~~~ 32 | %userprofile% 33 | ~~~~~~~~ 34 | 35 | ### On Linux: 36 | 37 | ~~~~~~~~ 38 | /home/YOUR-USERNAME 39 | ~~~~~~~~ 40 | 41 | ### Shortcut 42 | 43 | On Mac, Linux, and recent versions of Windows (in the Powershell terminal / in Windows 7+), there's a useful alias for a user's home directory, the tilde: 44 | 45 | ~~~~~~~~ 46 | ~ 47 | ~~~~~~~~ 48 | 49 | So, you can run a command like `cd ~`, and that'll take you to your home directory. 50 | 51 | Once you've navigated to your home folder, create the dev-envs folder: 52 | 53 | ~~~~~~~~ 54 | mkdir dev-envs 55 | ~~~~~~~~ 56 | 57 | Change directory into dev-envs, then create directories for javascript, ruby, and python: 58 | 59 | ~~~~~~~~ 60 | cd dev-envs 61 | mkdir javascript ruby python 62 | ~~~~~~~~ 63 | 64 | We'll use these directories to store the examples we work through later in the book. -------------------------------------------------------------------------------- /chapters/09-ruby.md: -------------------------------------------------------------------------------- 1 | # Ruby 2 | 3 | Ruby is a pleasant language that gets about as close to the English language as possible. 4 | 5 | It's popular in large part because of the web development framework Ruby on Rails. 6 | 7 | ## Language website 8 | [http://www.ruby-lang.org/en](http://www.ruby-lang.org/en) 9 | 10 | ## Documentation 11 | docs: [http://www.ruby-lang.org/en/documentation](http://www.ruby-lang.org/en/documentation) 12 | 13 | ## Vagrant 14 | 15 | Let's create a vagrant machine in your ruby dev-envs folder: 16 | 17 | ~~~~~~~~ 18 | mkdir ~/dev-envs/ruby 19 | cd ~/dev-envs/ruby 20 | ~~~~~~~~ 21 | 22 | Create a new vagrant machine using the Ubuntu Precise box: 23 | 24 | ~~~~~~~~ 25 | vagrant init precise32 26 | ~~~~~~~~ 27 | 28 | Forward a port for viewing your site: 29 | 30 | Open the Vagrant file: 31 | 32 | ~~~~~~~~ 33 | nano Vagrantfile 34 | ~~~~~~~~ 35 | 36 | Find this section: 37 | 38 | ~~~~~~~~ 39 | # Create a forwarded port mapping which allows access to a specific port 40 | # within the machine from a port on the host machine. In the example below, 41 | # accessing "localhost:8080" will access port 80 on the guest machine. 42 | # config.vm.network "forwarded_port", guest: 80, host: 8080 43 | ~~~~~~~~ 44 | 45 | And change this line: 46 | 47 | ~~~~~~~~ 48 | # config.vm.network "forwarded_port", guest: 80, host: 8080 49 | ~~~~~~~~ 50 | 51 | To this: 52 | 53 | ~~~~~~~~ 54 | config.vm.network "forwarded_port", guest: 9393 , host: 9393 55 | ~~~~~~~~ 56 | 57 | Make sure to uncomment the line by removing the `#`. 58 | 59 | Now start the vagrant machine: 60 | 61 | ~~~~~~~~ 62 | vagrant up 63 | ~~~~~~~~ 64 | 65 | If all goes well that'll result in output similar to the following: 66 | 67 | ~~~~~~~~ 68 | Bringing machine 'default' up with 'virtualbox' provider... 69 | ==> default: Importing base box 'precise32'... 70 | ==> default: Matching MAC address for NAT networking... 71 | ==> default: Setting the name of the VM: dev-envs_default_1400021636069_14720 72 | ==> default: Clearing any previously set network interfaces... 73 | ==> default: Preparing network interfaces based on configuration... 74 | default: Adapter 1: nat 75 | ==> default: Forwarding ports... 76 | default: 9393 => 9393 (adapter 1) 77 | default: 22 => 2222 (adapter 1) 78 | ==> default: Booting VM... 79 | ==> default: Waiting for machine to boot. This may take a few minutes... 80 | default: SSH address: 127.0.0.1:2222 81 | default: SSH username: vagrant 82 | default: SSH auth method: private key 83 | default: Error: Connection timout. Retrying... 84 | ==> default: Machine booted and ready! 85 | ==> default: Checking for guest additions in VM... 86 | default: The guest additions on this VM do not match the installed version of 87 | default: VirtualBox! In most cases this is fine, but in rare cases it can 88 | default: prevent things such as shared folders from working properly. If you see 89 | default: shared folder errors, please make sure the guest additions within the 90 | default: virtual machine match the version of VirtualBox you have installed on 91 | default: your host and reload your VM. 92 | default: 93 | default: Guest Additions Version: 4.2.0 94 | default: VirtualBox Version: 4.3 95 | ==> default: Mounting shared folders... 96 | default: /vagrant => /Users/sethvincent/dev-envs 97 | ~~~~~~~~ 98 | 99 | Now we will log in to the vagrant machine. This will be very much like using the `ssh` command to log in to a remote server. 100 | 101 | Use this command: 102 | 103 | ~~~~~~~~ 104 | vagrant ssh 105 | ~~~~~~~~ 106 | 107 | You should see output similar to the following: 108 | 109 | ~~~~~~~~ 110 | Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic-pae i686) 111 | 112 | * Documentation: https://help.ubuntu.com/ 113 | Welcome to your Vagrant-built virtual machine. 114 | Last login: Fri Sep 14 06:22:31 2012 from 10.0.2.2 115 | ~~~~~~~~ 116 | 117 | You'll now find your project folder at `/vagrant` when logged in to the vagrant machine. 118 | 119 | So navigate there like this: 120 | 121 | ~~~~~~~~ 122 | cd /vagrant 123 | ~~~~~~~~ 124 | 125 | We'll now install ruby and related tools, and get started building applications. 126 | 127 | **Complete all the following instructions while logged in to the vagrant machine.** 128 | 129 | ## Install git & dependencies 130 | 131 | To get started, we'll need to install git and some necessary system dependencies while logged in to the virtual machine: 132 | 133 | ~~~~~~~~ 134 | sudo apt-get install git gcc make zlib1g zlib1g-dev 135 | ~~~~~~~~ 136 | 137 | ## Installing ruby 138 | 139 | We'll be using rbenv to install ruby: [https://github.com/sstephenson/rbenv](https://github.com/sstephenson/rbenv). 140 | 141 | We'll also need ruby-build: [https://github.com/sstephenson/ruby-build](https://github.com/sstephenson/ruby-build). 142 | 143 | ### Install rbenv into `~/.rbenv`. 144 | 145 | ~~~~~~~~ 146 | git clone https://github.com/sstephenson/rbenv.git ~/.rbenv 147 | ~~~~~~~~ 148 | 149 | ### Make sure `~/.rbenv/bin` is in your `$PATH` so you can use the `rbenv` command-line utility. 150 | 151 | ~~~~~~~~ 152 | echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc 153 | ~~~~~~~~ 154 | 155 | ### To use shims and autocompletion with rbenv, add `rbenv init` to your ~/.bashrc file. 156 | 157 | ~~~~~~~~ 158 | echo 'eval "$(rbenv init -)"' >> ~/.bashrc 159 | ~~~~~~~~ 160 | 161 | ### Source the ~/.bashrc file so that the `rbenv` command is available. 162 | 163 | ~~~~~~~~ 164 | source ~/.bashrc 165 | ~~~~~~~~ 166 | 167 | ### Check if rbenv was set up by running the `rbenv` command: 168 | 169 | ~~~~~~~~ 170 | rbenv 171 | ~~~~~~~~ 172 | 173 | If `rbenv` was successfully installed, you'll see the following help output: 174 | 175 | ~~~~~~~~ 176 | Usage: rbenv [] 177 | 178 | Some useful rbenv commands are: 179 | commands List all available rbenv commands 180 | local Set or show the local application-specific Ruby version 181 | global Set or show the global Ruby version 182 | shell Set or show the shell-specific Ruby version 183 | rehash Rehash rbenv shims (run this after installing executables) 184 | version Show the current Ruby version and its origin 185 | versions List all Ruby versions available to rbenv 186 | which Display the full path to an executable 187 | whence List all Ruby versions that contain the given executable 188 | 189 | See `rbenv help ' for information on a specific command. 190 | For full documentation, see: https://github.com/sstephenson/rbenv#readme 191 | ~~~~~~~~ 192 | 193 | ### Install ruby-build 194 | 195 | In order to install different versions of ruby using rbenv, we'll install the ruby-build tool. 196 | 197 | ~~~~~~~~ 198 | git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build 199 | ~~~~~~~~ 200 | 201 | Now we can install a new version of ruby using the `ruby install` command. 202 | 203 | To see a list of the names of all possible ruby versions and implementations you can install, run this command: 204 | 205 | ~~~~~~~~ 206 | rbenv install -l 207 | ~~~~~~~~ 208 | 209 | Let's install the latest version of ruby 2.0, which as of this writing is 2.0.0-p247 using this command: 210 | 211 | ~~~~~~~~ 212 | rbenv install 2.0.0-p247 213 | ~~~~~~~~ 214 | 215 | This will download and install the latest ruby. It'll take a while, so take a break for a few minutes. 216 | 217 | If you want to set this new ruby version as the default, which I recommend doing for now, run this command: 218 | 219 | ~~~~~~~~ 220 | rbenv global 2.0.0-p247 221 | ~~~~~~~~ 222 | 223 | This sets ruby 2.0.0-p247 as the global ruby, so it'll always be the version used with your projects in this vagrant machine. 224 | 225 | 226 | ### Install rbenv-gem-rehash 227 | 228 | By default you would need to run `rbenv rehash` every time you install new gems to set up rbenv shims for each of the bin commands associated with the new gems. This rbenv plugin makes it so you don't have to run `rbenv rehash` each time. 229 | 230 | ~~~~~~~~ 231 | git clone https://github.com/sstephenson/rbenv-gem-rehash.git ~/.rbenv/plugins/rbenv-gem-rehash 232 | ~~~~~~~~ 233 | 234 | ## Package manager: rubygems 235 | 236 | To install ruby packages, you'll use the `gem` command. 237 | 238 | For example, basic `gem` command usage looks like this: 239 | 240 | ~~~~~~~~ 241 | gem install some-package-name 242 | ~~~~~~~~ 243 | 244 | As an example, we'll install the bundler gem, which we'll put to use later: 245 | 246 | ~~~~~~~~ 247 | gem install bundler 248 | ~~~~~~~~ 249 | 250 | ## Build tools / automating repetitive tasks 251 | 252 | For automating tasks in ruby development, use [rake](http://rake.rubyforge.org/). 253 | 254 | ### Install 255 | 256 | First, install the rake gem: 257 | 258 | ~~~~~~~~ 259 | gem install rake 260 | ~~~~~~~~ 261 | 262 | Create a Rakefile for your project: 263 | 264 | ~~~~~~~~ 265 | touch Rakefile 266 | ~~~~~~~~ 267 | 268 | Add this simple example to your Rakefile: 269 | 270 | ~~~~~~~~ 271 | task :default => [:start] 272 | 273 | task :start do 274 | ruby "app.rb" 275 | end 276 | ~~~~~~~~ 277 | 278 | When you run this command on the terminal: 279 | 280 | ~~~~~~~~ 281 | rake 282 | ~~~~~~~~ 283 | 284 | The start task defined in your Rakefile will be executed. 285 | 286 | Learn more about rake by reading the [project documentation](http://rake.rubyforge.org/). 287 | 288 | 289 | ## Testing: minitest 290 | https://github.com/seattlerb/minitest 291 | 292 | ## Language basics 293 | 294 | ### variables 295 | 296 | Create a variable like this: 297 | 298 | ~~~~~~~~ 299 | a = 1 300 | ~~~~~~~~ 301 | 302 | ### numbers 303 | 304 | In ruby there are integers and floats. An integer is a whole number, a float is a decimal. 305 | 306 | Integers: 307 | 308 | ~~~~~~~~ 309 | 1 310 | 100 311 | 223239 312 | ~~~~~~~~ 313 | 314 | Floats: 315 | 316 | ~~~~~~~~ 317 | 1.0 318 | 5.132 319 | 3.14 320 | ~~~~~~~~ 321 | 322 | ### string 323 | 324 | A string is a _string of characters_ wrapped in single or double quotes: 325 | 326 | ~~~~~~~~ 327 | "this is a string" 328 | 'this is also a string' 329 | ~~~~~~~~ 330 | 331 | When using double quotes, you can use string interpolation to insert the values of variables into a string: 332 | 333 | ~~~~~~~~ 334 | food = 'pizza' 335 | sentence = "#{food} is yummy." 336 | ~~~~~~~~ 337 | 338 | The `sentence` variable will return this: `pizza is yummy`. 339 | 340 | 341 | ### array 342 | 343 | An array is like a list of values. You can put anything in an array: strings, numbers, other arrays, hashes, etc. 344 | 345 | They look like this: 346 | 347 | ~~~~~~~~ 348 | things = ['pizza is great.', 30, ['yep', 'ok']] 349 | ~~~~~~~~ 350 | 351 | #### Accessing values in an array: 352 | 353 | You can access values in an array by typing the variable name followed by square brackets and a number that represents the value you'd like to access. 354 | 355 | Arrays are zero-indexed, so the first item is represented by a zero: 356 | 357 | ~~~~~~~~ 358 | things[0] 359 | ~~~~~~~~ 360 | 361 | This returns `'pizza is great'`. 362 | 363 | To get at nested arrays, you add another set of square brackets, like this: 364 | 365 | ~~~~~~~~ 366 | things[2][1] 367 | ~~~~~~~~ 368 | 369 | The above statement returns `'ok'`. 370 | 371 | 372 | ### hash 373 | 374 | A hash is much like an array, except instead of the values being indexed by numbers, they have names. 375 | 376 | A simple hash looks like this: 377 | 378 | ~~~~~~~~ 379 | pizza = { :tastes => 'really good', :slices_i_can_eat => 100 } 380 | ~~~~~~~~ 381 | 382 | There's also an alternate, more concise syntax for creating a hash that looks like this: 383 | 384 | ~~~~~~~~ 385 | pizza = { tastes: 'really good', slices_i_can_eat: 100 } 386 | ~~~~~~~~ 387 | 388 | #### Accessing hash values 389 | 390 | Accessing values in a hash looks similar to arrays: 391 | 392 | ~~~~~~~~ 393 | pizza[:tastes] 394 | ~~~~~~~~ 395 | 396 | The above statement returns `'really good'`. 397 | 398 | ~~~~~~~~ 399 | pizza[:slices_i_can_eat] 400 | ~~~~~~~~ 401 | 402 | The above statement returns `100`. That's a lot of slices of pizza. 403 | 404 | ### function 405 | 406 | A function definition is super simple: 407 | 408 | ~~~~~~~~ 409 | def eat(food) 410 | return "I ate #{food}." 411 | end 412 | ~~~~~~~~ 413 | 414 | `def` indicates that we're about to define a function. 415 | 416 | `eat` is the name of the function. 417 | 418 | In parentheses, we indicate that one argument is expected, `food`. 419 | 420 | This line: `return "I ate #{food}."` indicates that `"I ate #{food}."` will be returned when the function is called. 421 | 422 | We can actually rewrite that line to exclude the `return` keyword. The last statement in a function will get returned automatically. Since we only have one line in this function we don't need `return'. 423 | 424 | #### Calling a function 425 | 426 | Now that the function is defined, you can call it like this: 427 | 428 | ~~~~~~~~ 429 | eat('pizza') 430 | ~~~~~~~~ 431 | 432 | That statement will return: `'I ate pizza.'`. 433 | 434 | The parenthese are optional, so we can write the function call like this: 435 | 436 | ~~~~~~~~ 437 | eat 'pizza' 438 | ~~~~~~~~ 439 | 440 | ### class 441 | 442 | Define a class in ruby like this: 443 | 444 | ~~~~~~~~ 445 | class Meal 446 | # here we'll define methods on the class 447 | end 448 | ~~~~~~~~ 449 | 450 | ### class methods 451 | 452 | Class methods are basically functions that exist inside of a class namespace. 453 | 454 | ~~~~~~~~ 455 | class Meal 456 | 457 | def initialize(food) 458 | @food = food 459 | end 460 | 461 | def prepare 462 | @prepared = true 463 | "#{@food} is ready!" 464 | end 465 | 466 | def eat 467 | if @prepared == true 468 | "dang, that #{@food} sure was good." 469 | else 470 | "relax, the #{@food} isn't prepared yet." 471 | end 472 | end 473 | 474 | end 475 | ~~~~~~~~ 476 | 477 | ### class instance 478 | 479 | Now, to use our Meal class and call the `prepare` and `eat` methods, we do this: 480 | 481 | ~~~~~~~~ 482 | dinner = Meal.new 'pizza' 483 | ~~~~~~~~ 484 | 485 | We can call a method by typing the name of the class instance, followed by a period and the name of the method. Let's try out the `eat` method: 486 | 487 | ~~~~~~~~ 488 | dinner.eat 489 | ~~~~~~~~ 490 | 491 | That's return this string: `"relax, the pizza isn't prepared yet."`. 492 | 493 | So let's `prepare` the dinner: 494 | 495 | ~~~~~~~~ 496 | dinner.prepare 497 | ~~~~~~~~ 498 | 499 | That'll return this string: `"pizza is ready!"`. 500 | 501 | Now run `dinner.eat` and we'll see this string: `"dang, that pizza sure was good."`. 502 | 503 | 504 | ### importing/requiring code 505 | 506 | We can require the functionality of ruby gems and code from other files by using the `require` statement, typically at the top of the file. An example of requiring the sinatra gem: 507 | 508 | ~~~~~~~~ 509 | require 'sinatra' 510 | ~~~~~~~~ 511 | 512 | ## Web framework: sinatra 513 | Project website: [http://www.sinatrarb.com](http://www.sinatrarb.com/). 514 | 515 | ### Install 516 | 517 | Navigate to your ruby projects folder: 518 | 519 | ~~~~~~~~ 520 | cd ~/dev-envs/ruby 521 | ~~~~~~~~ 522 | 523 | Create a folder named hello-sinatra and navigate into it: 524 | 525 | ~~~~~~~~ 526 | mkdir hello-sinatra && cd hello-sinatra 527 | ~~~~~~~~ 528 | 529 | ~~~~~~~~ 530 | gem install sinatra 531 | ~~~~~~~~ 532 | 533 | ### Simple example 534 | 535 | Inside your hello-sinatra directory, create a file named app.rb: 536 | 537 | ~~~~~~~~ 538 | touch app.rb 539 | ~~~~~~~~ 540 | 541 | Here's a simple example of a sinatra app: 542 | 543 | ~~~~~~~~ 544 | require 'sinatra' 545 | 546 | get '/' do 547 | 'pizza is awesome' 548 | end 549 | ~~~~~~~~ 550 | 551 | Type that code into your app.rb file. 552 | 553 | Now you can run your app with this command: 554 | 555 | ~~~~~~~~ 556 | ruby app.rb 557 | ~~~~~~~~ 558 | 559 | Let's go through this app line by line: 560 | 561 | **Import the sinatra functionality into our app:** 562 | 563 | ~~~~~~~~ 564 | require 'sinatra' 565 | ~~~~~~~~ 566 | 567 | **Call `get`, to respond to requests for the root url:** 568 | 569 | ~~~~~~~~ 570 | get '/' do 571 | ~~~~~~~~ 572 | 573 | **Respond to requests with some text:** 574 | 575 | ~~~~~~~~ 576 | 'pizza is awesome' 577 | ~~~~~~~~ 578 | 579 | **Close the `get` block:** 580 | 581 | ~~~~~~~~ 582 | end 583 | ~~~~~~~~ 584 | 585 | 586 | ### Extended example 587 | 588 | Let's make a small website with [sinatra](http://sinatrarb.com) to explore how it works. 589 | 590 | In this example our site will do three things: 591 | 592 | - serve html at the root route from a view that has a list of posts 593 | - serve html for a single post at `/post/:id` 594 | - serve json at `/api/posts` that has a list of posts 595 | 596 | We won't be using a database for this example, but instead will use a json file with a list of posts. 597 | 598 | To get started, create and change directory into a new project folder. 599 | 600 | ~~~~~~~~ 601 | mkdir sinatra-example 602 | cd sinatra-example 603 | ~~~~~~~~ 604 | 605 | We'll be using sinatra and will utilize the default template language, erb. Let's install sinatra by creating a Gemfile: 606 | 607 | ~~~~~~~~ 608 | touch Gemfile 609 | ~~~~~~~~ 610 | 611 | Add the sinatra gem to the Gemfile: 612 | 613 | ~~~~~~~~ 614 | gem 'sinatra' 615 | ~~~~~~~~ 616 | 617 | Now run bundle to install sinatra: 618 | 619 | ~~~~~~~~ 620 | bundle 621 | ~~~~~~~~ 622 | 623 | We will use [shotgun](https://github.com/rtomayko/shotgun "shotgun") to run the app -- shotgun will automatically restart the server each time you edit a file in the project. 624 | 625 | Install shotgun: 626 | 627 | ~~~~~~~~ 628 | gem install shotgun 629 | ~~~~~~~~ 630 | 631 | To run the sinatra app you'll use this command: 632 | 633 | ~~~~~~~~ 634 | shotgun app.rb 635 | ~~~~~~~~ 636 | 637 | Create a file named posts.json with the following json: 638 | 639 | ~~~~~~~~ 640 | [ 641 | { 642 | "title": "This is the first post", 643 | "slug": "first-post", 644 | "content": "The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome." 645 | }, 646 | { 647 | "title": "Another post that you might like", 648 | "slug": "second-post", 649 | "content": "Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great." 650 | }, 651 | { 652 | "title": "The third and last post", 653 | "slug": "third-post", 654 | "content": "The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out." 655 | } 656 | ] 657 | ~~~~~~~~ 658 | 659 | Now create the app.rb file: 660 | 661 | ~~~~~~~~ 662 | require 'sinatra' 663 | require 'json' 664 | 665 | before do 666 | @title = 'Extended Sinatra example' 667 | @posts = JSON.parse( IO.read('posts.json') ) 668 | end 669 | 670 | get '/' do 671 | erb :index 672 | end 673 | 674 | get '/post/:slug' do 675 | @posts.each do |post| 676 | if post['slug'] == params[:slug] 677 | @post = post 678 | end 679 | end 680 | erb :post 681 | end 682 | 683 | get '/api/posts' do 684 | data = { 685 | meta: { name: @title }, 686 | posts: @posts 687 | } 688 | data.to_json 689 | end 690 | ~~~~~~~~ 691 | 692 | Let's break down this example code chunk by chunk: 693 | 694 | Require the necessary ruby libraries: 695 | 696 | ~~~~~~~~ 697 | require 'sinatra' 698 | require 'json' 699 | ~~~~~~~~ 700 | 701 | 702 | Create global variables that are available to our views using the `before` method, which runs before a request is processed: 703 | 704 | ~~~~~~~~ 705 | before do 706 | @title = 'Extended Sinatra example' 707 | @posts = JSON.parse( IO.read('posts.json') ) 708 | end 709 | ~~~~~~~~ 710 | 711 | Serve the index.erb view on the root url with the following code block. Note that an erb view is rendered using the `erb` method, and you don't have to include the .erb file suffix. Sinatra automatically looks in a folder named views, so you only have to pass the file name: 712 | 713 | ~~~~~~~~ 714 | get '/' do 715 | erb :index 716 | end 717 | ~~~~~~~~ 718 | 719 | The following code block listens for requests for a specific blog post. We iterate through each of the items in our posts array, and if the slug that's passed in the url matches a slug in the posts array, that post is set to a global `@post` variable that's available in our post view. 720 | 721 | ~~~~~~~~ 722 | get '/post/:slug' do 723 | @posts.each do |post| 724 | if post['slug'] == params[:slug] 725 | @post = post 726 | end 727 | end 728 | erb :post 729 | end 730 | ~~~~~~~~ 731 | 732 | The following is a simple example of exposing a simple json feed of the posts: 733 | 734 | ~~~~~~~~ 735 | get '/api/posts' do 736 | data = { 737 | meta: { name: @title }, 738 | posts: @posts 739 | } 740 | data.to_json 741 | end 742 | ~~~~~~~~ 743 | 744 | 745 | Next, we'll need the erb views for rendering html. 746 | 747 | Let's make a views folder for all the views to live in: 748 | 749 | ~~~~~~~~ 750 | mkdir views 751 | ~~~~~~~~ 752 | 753 | And create all the view files that we need: 754 | 755 | ~~~~~~~~ 756 | touch views/layout.erb views/index.erb views/post.erb 757 | ~~~~~~~~ 758 | 759 | Add this content to the layout.erb file: 760 | 761 | ~~~~~~~~ 762 | 763 | 764 | 765 | 766 | 767 | <%= @title %> 768 | 769 | 770 | 771 | 772 |
773 |
774 |

<%= @title %>

775 |
776 |
777 | 778 |
779 |
780 | <%= yield %> 781 |
782 |
783 | 784 |
785 |
786 |

Posts are also available via json at /api/posts/ 787 |

788 |
789 | 790 | 791 | 792 | ~~~~~~~~ 793 | 794 | Add this content to the index.erb file: 795 | 796 | ~~~~~~~~ 797 | <% for @post in @posts %> 798 |

799 | 800 | <%= @post['title'] %> 801 | 802 |

803 |
<%= @post['content'] %>
804 | <% end %> 805 | ~~~~~~~~ 806 | 807 | Add this content to the post.erb file: 808 | 809 | ~~~~~~~~ 810 |

<%= @post['title'] %>

811 |
<%= @post['content'] %>
812 | ~~~~~~~~ 813 | 814 | Let's add some css styling so this looks a little more readable. 815 | 816 | First create the public folder and the styles.css file: 817 | 818 | ~~~~~~~~ 819 | mkdir public 820 | touch public/styles.css 821 | ~~~~~~~~ 822 | 823 | Now add this content to the styles.css file: 824 | 825 | ~~~~~~~~ 826 | body { 827 | font: 16px/1.5 'Helvetica Neue', Helvetica, Arial, sans-serif; 828 | color: #787876; 829 | } 830 | 831 | h1, h3 { 832 | font-weight: 300; 833 | margin-bottom: 5px; 834 | } 835 | 836 | a { 837 | text-decoration: none; 838 | color: #EA6045; 839 | } 840 | 841 | a:hover { 842 | color: #2F3440; 843 | } 844 | 845 | .container { 846 | width: 90%; 847 | margin: 0px auto; 848 | } 849 | 850 | footer { 851 | margin-top: 30px; 852 | border-top: 1px solid #efefef; 853 | padding-top: 20px; 854 | font-style: italic; 855 | } 856 | 857 | @media (min-width: 600px){ 858 | .container { 859 | width: 60%; 860 | } 861 | } 862 | ~~~~~~~~ 863 | 864 | You should now be able to navigate the home page, three blog post pages, and the posts json feed. Run the project with the nodemon command: 865 | 866 | ~~~~~~~~ 867 | shotgun app.rb 868 | ~~~~~~~~ 869 | 870 | ### Accessing the site in the browser 871 | 872 | After starting the app with `shotgun`, you should see this output on the command line: 873 | 874 | ~~~~~~~~ 875 | Listening on 127.0.0.1:9393, CTRL+C to stop 876 | ~~~~~~~~ 877 | 878 | You can now open a browser and navigate to http://localhost:9393 to view the site. 879 | 880 | 881 | ### Sinatra resources 882 | 883 | Learn more about sinatra at the sinatra website: [http://www.sinatrarb.com](http://www.sinatrarb.com/) 884 | 885 | ## Resources 886 | 887 | http://tryruby.org/ 888 | -------------------------------------------------------------------------------- /chapters/10-javascript.md: -------------------------------------------------------------------------------- 1 | # Javascript 2 | 3 | Javascript is a ubiquitous language. It's getting first-class support in various operating sytems, you can use it on the server with node.js, and developing javascript applications that are largely front-end code is becoming a popular and pragmatic practice. 4 | 5 | We'll cover javascript on the server using node.js and briefly cover javascript in the browser. 6 | 7 | Javascript development tools have recently seen a big burst of growth thanks to node.js. 8 | 9 | 10 | 11 | ## Language website 12 | The website for node.js is pretty great: [http://nodejs.org](http://nodejs.org). 13 | 14 | It includes the API documentation, a blog that announces new releases and a link to the website for the package manager used by node developers, npm. 15 | 16 | There isn't really a website for the javascript language in the same way ruby and python have websites. For general javascript information, look to the documentation websites listed below: 17 | 18 | 19 | 20 | ## Documentation 21 | 22 | One of the best places to start learning Node.js is [nodeschool.io](http://nodeschool.io/). These are a set of interactive workshops you complete using the terminal. Highly recommended. 23 | 24 | Here is the documentation for the node.js API: 25 | [http://nodejs.org/api/index.html](http://nodejs.org/api/index.html) 26 | 27 | There are a lot of different resources for documentation on client-side javascript. 28 | 29 | Here are two I recommend: 30 | 31 | **Web Platform Docs:** [http://www.webplatform.org/](http://www.webplatform.org/) 32 | 33 | The Web Platform Docs is a relatively new set of documentation that includes coverage of html, css, and javscript. It's pretty good. 34 | 35 | **Mozilla Developer Network documentation:** [https://developer.mozilla.org/en-US/docs/Web/JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) 36 | 37 | The MDN docs are great. There are bits that are specific to Mozilla, but the majority of the content is relevant to html, css, and javascript in general. 38 | 39 | ## Vagrant 40 | 41 | Let's create a vagrant machine in your javascript dev-envs folder: 42 | 43 | ~~~~~~~~ 44 | mkdir ~/dev-envs/javascript 45 | cd ~/dev-envs/javascript 46 | ~~~~~~~~ 47 | 48 | Create a new vagrant machine using the Ubuntu Precise box: 49 | 50 | ~~~~~~~~ 51 | vagrant init precise32 52 | ~~~~~~~~ 53 | 54 | Forward a port for viewing your site: 55 | 56 | Open the Vagrant file: 57 | 58 | ~~~~~~~~ 59 | nano Vagrantfile 60 | ~~~~~~~~ 61 | 62 | Find this section: 63 | 64 | ~~~~~~~~ 65 | # Create a forwarded port mapping which allows access to a specific port 66 | # within the machine from a port on the host machine. In the example below, 67 | # accessing "localhost:8080" will access port 80 on the guest machine. 68 | # config.vm.network "forwarded_port", guest: 80, host: 8080 69 | ~~~~~~~~ 70 | 71 | And change this line: 72 | 73 | ~~~~~~~~ 74 | # config.vm.network "forwarded_port", guest: 80, host: 8080 75 | ~~~~~~~~ 76 | 77 | To this: 78 | 79 | ~~~~~~~~ 80 | config.vm.network "forwarded_port", guest: 3000 , host: 3000 81 | ~~~~~~~~ 82 | 83 | Make sure to uncomment the line by removing the `#`. 84 | 85 | Now start the vagrant machine: 86 | 87 | ~~~~~~~~ 88 | vagrant up 89 | ~~~~~~~~ 90 | 91 | If all goes well that'll result in output similar to the following: 92 | 93 | ~~~~~~~~ 94 | Bringing machine 'default' up with 'virtualbox' provider... 95 | ==> default: Importing base box 'precise32'... 96 | ==> default: Matching MAC address for NAT networking... 97 | ==> default: Setting the name of the VM: dev-envs_default_1400021636069_14720 98 | ==> default: Clearing any previously set network interfaces... 99 | ==> default: Preparing network interfaces based on configuration... 100 | default: Adapter 1: nat 101 | ==> default: Forwarding ports... 102 | default: 3000 => 3000 (adapter 1) 103 | default: 22 => 2222 (adapter 1) 104 | ==> default: Booting VM... 105 | ==> default: Waiting for machine to boot. This may take a few minutes... 106 | default: SSH address: 127.0.0.1:2222 107 | default: SSH username: vagrant 108 | default: SSH auth method: private key 109 | default: Error: Connection timout. Retrying... 110 | ==> default: Machine booted and ready! 111 | ==> default: Checking for guest additions in VM... 112 | default: The guest additions on this VM do not match the installed version of 113 | default: VirtualBox! In most cases this is fine, but in rare cases it can 114 | default: prevent things such as shared folders from working properly. If you see 115 | default: shared folder errors, please make sure the guest additions within the 116 | default: virtual machine match the version of VirtualBox you have installed on 117 | default: your host and reload your VM. 118 | default: 119 | default: Guest Additions Version: 4.2.0 120 | default: VirtualBox Version: 4.3 121 | ==> default: Mounting shared folders... 122 | default: /vagrant => /Users/sethvincent/dev-envs 123 | ~~~~~~~~ 124 | 125 | Now we will log in to the vagrant machine. This will be very much like using the `ssh` command to log in to a remote server. 126 | 127 | Use this command: 128 | 129 | ~~~~~~~~ 130 | vagrant ssh 131 | ~~~~~~~~ 132 | 133 | You should see output similar to the following: 134 | 135 | ~~~~~~~~ 136 | Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic-pae i686) 137 | 138 | * Documentation: https://help.ubuntu.com/ 139 | Welcome to your Vagrant-built virtual machine. 140 | Last login: Fri Sep 14 06:22:31 2012 from 10.0.2.2 141 | ~~~~~~~~ 142 | 143 | You'll now find your project folder at `/vagrant` when logged in to the vagrant machine. 144 | 145 | So navigate there like this: 146 | 147 | ~~~~~~~~ 148 | cd /vagrant 149 | ~~~~~~~~ 150 | 151 | We'll now install Node.js, its dependencies, and related tools, and get started building applications. Complete all the following instructions while logged in to the vagrant machine. 152 | 153 | ## Install git & dependencies 154 | 155 | To get started, we'll need to install git and some necessary system dependencies while logged in to the virtual machine: 156 | 157 | ~~~~~~~~ 158 | sudo apt-get install git gcc make curl 159 | ~~~~~~~~ 160 | 161 | 162 | ## Installing node.js 163 | 164 | I recommend using a tool called `nvm` for installing node.js if you're on mac or linux. It's very similar to the rbenv tool we used in the last chapter for installing ruby. 165 | 166 | If you're on Windows, install node.js using the .msi package on the nodejs.org downloads page: [http://nodejs.org/downloads](http://nodejs.org/downloads). 167 | 168 | **nvm:** [https://github.com/creationix/nvm](https://github.com/creationix/nvm). 169 | 170 | We have git installed, so we can clone nvm to our home folder: 171 | 172 | ~~~~~~~~ 173 | git clone https://github.com/creationix/nvm.git ~/.nvm 174 | ~~~~~~~~ 175 | 176 | Source nvm to make the `nvm` command available in the terminal by typing into your terminal: 177 | 178 | ~~~~~~~~ 179 | source ~/.nvm/nvm.sh 180 | ~~~~~~~~ 181 | 182 | To ensure that `nvm` is available at all times in the terminal, add the above line to your ~/.bashrc file: 183 | 184 | ~~~~~~~~ 185 | nano ~/.bashrc 186 | ~~~~~~~~ 187 | 188 | Add `source ~/.nvm/nvm.sh` to the ~/.bashrc file. 189 | 190 | To get the `nvm` command after adding that line to your ~/.bashrc file, source your ~/.bashrc file: 191 | 192 | ~~~~~~~~ 193 | source ~/.bashrc 194 | ~~~~~~~~ 195 | 196 | To ensure `nvm` is working, run the command without options: 197 | 198 | ~~~~~~~~ 199 | nvm 200 | ~~~~~~~~ 201 | 202 | You should see output like this: 203 | 204 | ~~~~~~~~ 205 | Node Version Manager 206 | 207 | Usage: 208 | nvm help Show this message 209 | nvm install [-s] Download and install a 210 | nvm uninstall Uninstall a version 211 | nvm use Modify PATH to use 212 | nvm run [] Run with as arguments 213 | nvm ls List installed versions 214 | nvm ls List versions matching a given description 215 | nvm ls-remote List remote versions available for install 216 | nvm deactivate Undo effects of NVM on current shell 217 | nvm alias [] Show all aliases beginning with 218 | nvm alias Set an alias named pointing to 219 | nvm unalias Deletes the alias named 220 | nvm copy-packages Install global NPM packages contained in to current version 221 | 222 | Example: 223 | nvm install v0.4.12 Install a specific version number 224 | nvm use 0.2 Use the latest available 0.2.x release 225 | nvm run 0.4.12 myApp.js Run myApp.js using node v0.4.12 226 | nvm alias default 0.4 Auto use the latest installed v0.4.x version 227 | ~~~~~~~~ 228 | 229 | The above help text gives a good overview of usage of the `nvm` command. 230 | 231 | 232 | ### Now we install node.js 233 | 234 | Install the latest version of node v0.10.x: 235 | 236 | ~~~~~~~~ 237 | nvm install 0.10 238 | ~~~~~~~~ 239 | 240 | You'll see output like this: 241 | 242 | ~~~~~~~~ 243 | ######################################################################## 100.0% 244 | Now using node v0.10.21 245 | ~~~~~~~~ 246 | 247 | We can switch to that new version using this command: 248 | 249 | ~~~~~~~~ 250 | nvm use 0.10.21 251 | ~~~~~~~~ 252 | 253 | And to set that version as the default, set the default alias: 254 | 255 | ~~~~~~~~ 256 | nvm alias default 0.10.21 257 | ~~~~~~~~ 258 | 259 | 260 | ## Javascript in the browser 261 | 262 | You don't need to install anything for javascript in the browser. The browser takes care of that for you. I recommend using Chrome for the examples in this book. Firefox is also excellent, and if you choose to use it, there will be just slight differences between the developer tools compared to Chrome. 263 | 264 | Download Chrome here: [https://www.google.com/intl/en/chrome/browser/](https://www.google.com/intl/en/chrome/browser/) 265 | 266 | 267 | ## Package manager: npm 268 | When you install node.js, you get npm. 269 | 270 | **npm:** [http://npmjs.org](http://npmjs.org) 271 | 272 | You may also want to use [bower](http://bower.io/) or [component](http://component.io), two package managers that specifically target client-side code. Remember that javascript packages distributed via npm are not limited to node.js, and can also be used in the browser in many cases through the use of module bundlers like [browserify](http://browserify.org) and [webpack](http://webpack.github.io/). 273 | 274 | ## Build tools / automating repetitive tasks 275 | 276 | There are a few ways to automate repetitive tasks in JavaScript projects. 277 | 278 | ### npm scripts 279 | 280 | Using npm scripts and the `npm run` command is a clean, simple method for organizing the build tools of your JavaScript project. 281 | 282 | You specify npm scripts by adding to the `scripts` field of a package.json file in your JavaScript project. 283 | 284 | Take this example: 285 | 286 | ~~~~~~~~ 287 | "scripts": { 288 | "test": "node test.js", 289 | "start": "node server.js", 290 | "bundle": "browserify main.js -o bundle.js" 291 | } 292 | ~~~~~~~~ 293 | 294 | We would run `npm test` to test the code, `npm start` to run a development server, and `npm run bundle` to create a bundled JavaScript file using the `browserify` command. 295 | 296 | ### Grunt 297 | 298 | Another, more complicated option is [grunt.js](http://gruntjs.com). 299 | 300 | ### Install 301 | 302 | First, install the grunt command-line tool: 303 | 304 | ~~~~~~~~ 305 | npm install -g grunt-cli 306 | ~~~~~~~~ 307 | 308 | Next, you'll create a Gruntfile.js in your project. 309 | 310 | Learn more about grunt.js by reading the [project documentation](http://gruntjs.com/getting-started). 311 | 312 | ### More information about npm scripts and Grunt 313 | 314 | Check out this blog post for more information about npm scripts, Grunt, and how I choose between the two: [http://superbigtree.tumblr.com/post/59519017137/introduction-to-grunt-js-and-npm-scripts-and-choosing](http://superbigtree.tumblr.com/post/59519017137/introduction-to-grunt-js-and-npm-scripts-and-choosing) 315 | 316 | ## Testing: tape 317 | For testing, we'll use a library named tape. 318 | 319 | **tape:** [https://github.com/substack/tape](https://github.com/substack/tape). 320 | 321 | ### Installing tape 322 | 323 | To install tape, we'll use `npm` on the command line. 324 | 325 | Open a terminal. 326 | 327 | Change directory to your projects folder. 328 | 329 | ~~~~~~~~ 330 | cd ~/Projects 331 | ~~~~~~~~ 332 | 333 | Create a directory for our first javascript project: 334 | 335 | ~~~~~~~~ 336 | mkdir learn-javascript-one 337 | ~~~~~~~~ 338 | 339 | Change directory into our new project folder: 340 | 341 | ~~~~~~~~ 342 | cd learn-javascript-one 343 | ~~~~~~~~ 344 | 345 | Run this command to create a package.json file: 346 | 347 | ~~~~~~~~ 348 | npm init 349 | ~~~~~~~~ 350 | 351 | Answer the questions that pop up. 352 | 353 | Now, to really install tape: 354 | 355 | ~~~~~~~~ 356 | npm install --save-dev tape 357 | ~~~~~~~~ 358 | 359 | `npm install` is used to install packages from npm. 360 | 361 | `--save-dev` saves the package to your package.json as a development dependency. 362 | 363 | `tape` is the package name. You can pass multiple packages at once, separated by commas. 364 | 365 | A simple example of a test written with tape: 366 | 367 | ~~~~~~~~ 368 | var test = require('tape'); 369 | 370 | var p = 'pizza'; 371 | 372 | test('pizza test', function (t) { 373 | t.plan(1); 374 | 375 | t.equal(p, 'pizza'); 376 | }); 377 | ~~~~~~~~ 378 | 379 | Let's go through this line-by-line in a high-level way. 380 | 381 | Here we assign the tape functionality to a variable named `test`: 382 | 383 | ~~~~~~~~ 384 | var test = require('tape'); 385 | ~~~~~~~~ 386 | 387 | Here `p` is assigned to the string `'pizza'`: 388 | 389 | ~~~~~~~~ 390 | var p = 'pizza'; 391 | ~~~~~~~~ 392 | 393 | Now we're calling `test`, and describing it as a `pizza test`: 394 | 395 | ~~~~~~~~ 396 | test('pizza test', function (t) { 397 | ~~~~~~~~ 398 | 399 | We're given the argument `t` to use to call testing methods. 400 | 401 | Here we specify that we plan to have 1 test in our code: 402 | 403 | ~~~~~~~~ 404 | t.plan(1); 405 | ~~~~~~~~ 406 | 407 | Here's that one test, making sure that the `p` variable is equal to the string `pizza`: 408 | 409 | ~~~~~~~~ 410 | t.equal(p, 'pizza'); 411 | ~~~~~~~~ 412 | 413 | This closes the function: 414 | 415 | ~~~~~~~~ 416 | }); 417 | ~~~~~~~~ 418 | 419 | Those are the very basics of using tape. Next, we'll dive deeper into some javascript basics, and use tape to test our code. 420 | 421 | 422 | ## Language basics 423 | 424 | ### Variables 425 | 426 | #### Creating a variable: 427 | 428 | ~~~~~~~~ 429 | var nameOfVariable; 430 | ~~~~~~~~ 431 | 432 | > Variables are camelCase, meaning first letter is lowercase, and if the variable is made of multiple words, the first letter of following words are capitalized. 433 | 434 | #### Creating a variable that references a string: 435 | 436 | ~~~~~~~~ 437 | var thisIsAString = 'this is a string'; 438 | ~~~~~~~~ 439 | 440 | Surround strings with single quotes. 441 | 442 | 443 | #### Creating a variable that references a number: 444 | 445 | ~~~~~~~~ 446 | var thisIsANumber = 3.14; 447 | ~~~~~~~~ 448 | 449 | Numbers do not have quotes around them. 450 | 451 | #### Creating a variable that references an array: 452 | 453 | ~~~~~~~~ 454 | var thisIsAnArray = [1, "two", [3, 4]]; 455 | ~~~~~~~~ 456 | 457 | Note that one of the values in the array is a number, one is a string, and another is an array. Arrays can hold any value in any order. 458 | 459 | #### Accessing the values in an array: 460 | 461 | ~~~~~~~~ 462 | thisIsAnArray[0]; 463 | ~~~~~~~~ 464 | 465 | The above will return the number `1`. Arrays use numbers as the index of their values, and with javascript an array's index always start at `0`, making `0` reference the first value of the array. 466 | 467 | ~~~~~~~~ 468 | thisIsAnArray[1]; 469 | ~~~~~~~~ 470 | 471 | This returns the string 'two'; 472 | 473 | ##### How would you return the number `4` from the nested array? 474 | 475 | Like this: 476 | 477 | ~~~~~~~~ 478 | thisIsAnArray[2][1]; 479 | ~~~~~~~~ 480 | 481 | #### Creating a variable that references an object: 482 | 483 | var thisIsAnObject = { 484 | someString: 'some string value', 485 | someNumber: 1234, 486 | someFunction: function(){ 487 | return 'a function that belongs to an object'; 488 | } 489 | } 490 | 491 | Here we're setting `someString` to `'some string value'`, `someNumber' to `1234`, and we're creating a function named `someFunction` that returns the string `'a function that belongs to an object'`. So how do we access these values? 492 | 493 | To get the value of `someString` using dot notation: 494 | 495 | ~~~~~~~~ 496 | thisIsAnObject.someString; 497 | ~~~~~~~~ 498 | 499 | Or using bracket notation: 500 | 501 | ~~~~~~~~ 502 | thisIsAnObject['someString']; 503 | ~~~~~~~~ 504 | 505 | To get the value of `someNumber` using dot notation: 506 | 507 | ~~~~~~~~ 508 | thisIsAnObject.someNumber; 509 | ~~~~~~~~ 510 | 511 | Or using bracket notation: 512 | 513 | ~~~~~~~~ 514 | thisIsAnObject['someNumber']; 515 | ~~~~~~~~ 516 | 517 | To use the function `someFunction` using dot notation: 518 | 519 | ~~~~~~~~ 520 | thisIsAnObject.someFunction(); 521 | ~~~~~~~~ 522 | 523 | Or using bracket notation: 524 | 525 | ~~~~~~~~ 526 | thisIsAnObject['someFunction'](); 527 | ~~~~~~~~ 528 | 529 | Using square bracket notations with functions looks a little wacky. It will be useful if you are storing function names in variables as strings, and need to use the variable to call the function being stored. Otherwise, stick with dot notation. 530 | That goes for other attributes on an object, too: stick with dot notation unless there's a good reason to use bracket notation. 531 | 532 | For instance, it's more clear to use bracket notation in a situation like this: 533 | 534 | ~~~~~~~~ 535 | for (var key in object){ 536 | thisIsAnObject[key]; 537 | } 538 | ~~~~~~~~ 539 | 540 | This gives you an idea of how to iterate through an object using a for...in loop. 541 | 542 | ### importing/requiring code 543 | 544 | #### Node.js 545 | 546 | When using Node.js we can require the functionality of Node.js modules distributed via npm and code from other files by using the `require` function, typically at the top of the file. An example of requiring the express module: 547 | 548 | ~~~~~~~~ 549 | var express = require('express'); 550 | ~~~~~~~~ 551 | 552 | #### Browser 553 | 554 | For browser side code we might add a script tag into the HTML file of our project. Here's an example of a script tag: 555 | 556 | ~~~~~~~~ 557 | 558 | ~~~~~~~~ 559 | 560 | Alternately we might use a tool like browserify to require packages using the same method as Node.js. Learn more about browserify at the project website, [browserify.org](http://browserify.org). 561 | 562 | 563 | ## Web framework: express 564 | Express is a small web framework for node.js, originally inspired by sinatra. 565 | 566 | **express:** [http://expressjs.com/](http://expressjs.com/). 567 | 568 | ### Install 569 | 570 | Navigate to your javascript projects folder: 571 | 572 | ~~~~~~~~ 573 | cd ~/dev-envs/javascript 574 | ~~~~~~~~ 575 | 576 | Create a folder named hello-express and navigate into it: 577 | 578 | ~~~~~~~~ 579 | mkdir hello-express && cd hello-express 580 | ~~~~~~~~ 581 | 582 | ~~~~~~~~ 583 | npm install express 584 | ~~~~~~~~ 585 | 586 | This installs express locally so you can use it in your app. 587 | 588 | ### Simple example 589 | 590 | Inside your hello-express directory, create a file named app.js: 591 | 592 | ~~~~~~~~ 593 | touch app.js 594 | ~~~~~~~~ 595 | 596 | Here's a simple example of an express app: 597 | 598 | ~~~~~~~~ 599 | var express = require('express'); 600 | var app = express(); 601 | 602 | app.get('/', function(req, res){ 603 | res.send('pizza is awesome.'); 604 | }); 605 | 606 | app.listen(3000); 607 | 608 | console.log('app is listening at localhost:3000'); 609 | ~~~~~~~~ 610 | 611 | Type that code into your app.js file 612 | 613 | You can run your app with this command: 614 | 615 | ~~~~~~~~ 616 | node app.js 617 | ~~~~~~~~ 618 | 619 | Now let's run through app.js one line at a time: 620 | 621 | Save the express module to a variable named express: 622 | 623 | ~~~~~~~~ 624 | var express = require('express'); 625 | ~~~~~~~~ 626 | 627 | Create our app by calling `express()` and assigning the returned object to the variable `app`: 628 | 629 | ~~~~~~~~ 630 | var app = express(); 631 | ~~~~~~~~ 632 | 633 | Exposing a route for the root url using `app.get()`: 634 | 635 | ~~~~~~~~ 636 | app.get('/', function(req, res){ 637 | ~~~~~~~~ 638 | 639 | `req` is an argument we get from the callback that represents the request. `res` represents the response that we'll be sending back to the user. 640 | 641 | Sending a text response: 642 | 643 | ~~~~~~~~ 644 | res.send('pizza is awesome.'); 645 | ~~~~~~~~ 646 | 647 | Closing the `app.get()` function: 648 | 649 | ~~~~~~~~ 650 | }); 651 | ~~~~~~~~ 652 | 653 | Setting up the app to listen for requests on port 3000: 654 | 655 | ~~~~~~~~ 656 | app.listen(3000); 657 | ~~~~~~~~ 658 | 659 | Logging a message to the user on the console so that the user knows that the app has started and things are happening: 660 | 661 | ~~~~~~~~ 662 | console.log('app is listening at localhost:3000'); 663 | ~~~~~~~~ 664 | 665 | ### Installing express globally 666 | 667 | ~~~~~~~~ 668 | npm install -g express 669 | ~~~~~~~~ 670 | 671 | The `-g` option installs express globally. This means that there is now an `express` command available in your terminal you can use to create a new express app. 672 | 673 | Go ahead and install express globally using the above command, and we'll continue experimenting with express. 674 | 675 | ### Create an app using the `express` command. 676 | 677 | Navigate to your Projects folder and run this command: 678 | 679 | ~~~~~~~~ 680 | express new-app 681 | ~~~~~~~~ 682 | 683 | This will generate a bunch of files for you. I won't go into the details of what's created, but it's good to know express has this functionality available. 684 | 685 | 686 | ### Extended example 687 | 688 | Let's make a small website with [express](http://expressjs.com) to explore how it works. 689 | 690 | In this example our site will do three things: 691 | - serve html at the root route from a view that has a list of posts 692 | - serve html for a single post at `/post/:id` 693 | - serve json at `/api/posts` that has a list of posts 694 | 695 | We won't be using a database for this example, but instead will use a json file with a list of posts. 696 | 697 | To get started, create and change directory into a new project folder, then run `npm init` to create a package.json file. 698 | 699 | ~~~~~~~~ 700 | mkdir express-example 701 | cd express-example 702 | ~~~~~~~~ 703 | 704 | We'll be using express and for templates we'll use the [ejs](https://github.com/visionmedia/ejs "ejs") module, so let's install those dependencies: 705 | 706 | ~~~~~~~~ 707 | npm install --save express ejs 708 | ~~~~~~~~ 709 | 710 | We will use [nodemon](https://github.com/remy/nodemon "nodemon") to run the app – nodemon will automatically restart the server each time you edit a file in the project. 711 | 712 | Install nodemon: 713 | 714 | ~~~~~~~~ 715 | npm install -g nodemon 716 | ~~~~~~~~ 717 | 718 | Run nodemon with these options so that changes to ejs views and public files also trigger the restart: 719 | 720 | ~~~~~~~~ 721 | nodemon -e js,css,html,ejs app.js 722 | ~~~~~~~~ 723 | 724 | Create a file named posts.json with the following json: 725 | 726 | ~~~~~~~~ 727 | [ 728 | { 729 | "title": "This is the first post", 730 | "slug": "first-post", 731 | "content": "The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome." 732 | }, 733 | { 734 | "title": "Another post that you might like", 735 | "slug": "second-post", 736 | "content": "Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great." 737 | }, 738 | { 739 | "title": "The third and last post", 740 | "slug": "third-post", 741 | "content": "The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out." 742 | } 743 | ] 744 | ~~~~~~~~ 745 | 746 | First we'll create the app.js file: 747 | 748 | ~~~~~~~~ 749 | var express = require('express'); 750 | var fs = require('fs'); 751 | var app = express(); 752 | 753 | app.use('/public', express.static(__dirname + '/public')); 754 | 755 | app.locals.title= 'Extended Express Example'; 756 | 757 | app.all('*', function(req, res, next){ 758 | fs.readFile('posts.json', function(err, data){ 759 | app.locals.posts = JSON.parse(data); 760 | next(); 761 | }); 762 | }); 763 | 764 | app.get('/', function(req, res){ 765 | res.render('index.ejs'); 766 | }); 767 | 768 | app.get('/post/:slug', function(req, res, next){ 769 | app.locals.posts.forEach(function(post){ 770 | if (req.params.slug === post.slug){ 771 | res.render('post.ejs', { post: post }); 772 | } 773 | }) 774 | }); 775 | 776 | app.get('/api/posts', function(req, res){ 777 | var data = { 778 | meta: { name: app.locals.title }, 779 | posts: app.locals.posts 780 | } 781 | res.json(data); 782 | }); 783 | 784 | app.listen(3000); 785 | console.log('app is listening at localhost:3000'); 786 | ~~~~~~~~ 787 | 788 | Let's break down this example code chunk by chunk: 789 | 790 | Require the needed modules and create the app variable: 791 | 792 | ~~~~~~~~ 793 | var express = require('express'); 794 | var fs = require('fs'); 795 | var app = express(); 796 | ~~~~~~~~ 797 | 798 | Set up the app to serve whatever is in the public folder at the url `/public/:filename`. You may need to delete a pre-existing app.use() function that serves from the routes folder: 799 | 800 | ~~~~~~~~ 801 | app.use('/public', express.static(__dirname + '/public')); 802 | ~~~~~~~~ 803 | 804 | You can add local variables that can be used in views and throughout the app. See more examples in the [express](http://expressjs.com/api.html#app.locals "express") api. 805 | 806 | ~~~~~~~~ 807 | app.locals.title='Extended Express Example'; 808 | ~~~~~~~~ 809 | 810 | In this example we're loading the posts from the json file before responding to routes: 811 | 812 | ~~~~~~~~ 813 | app.all('*', function(req, res, next){ 814 | fs.readFile('posts.json', function(err, data){ 815 | app.locals.posts = JSON.parse(data); 816 | next(); 817 | }); 818 | }); 819 | ~~~~~~~~ 820 | 821 | When a browser requests the root url, our app responds with the index.ejs file. Express automatically looks in a folder named views, so you only have to pass the file name: 822 | 823 | ~~~~~~~~ 824 | app.get('/', function(req, res){ 825 | res.render('index.ejs'); 826 | }); 827 | ~~~~~~~~ 828 | 829 | The following code block listens for requests for a specific blog post. We iterate through each of the items in our posts array, and if the slug that's passed in the url matches a slug in the posts array, that post is returned: 830 | 831 | ~~~~~~~~ 832 | app.get('/post/:slug', function(req, res, next){ 833 | app.locals.posts.forEach(function(post){ 834 | if (req.params.slug === post.slug){ 835 | res.render('post.ejs', { post: post }); 836 | } 837 | }) 838 | }); 839 | ~~~~~~~~ 840 | 841 | The following is a simple example of exposing a simple json feed of the posts. 842 | 843 | ~~~~~~~~ 844 | app.get('/api/posts', function(req, res){ 845 | var data = { 846 | meta: { name: app.locals.title }, 847 | posts: app.locals.posts 848 | } 849 | res.json(data); 850 | }); 851 | ~~~~~~~~ 852 | And finally, we make the app listen on port 3000, and print a message to the terminal: 853 | 854 | ~~~~~~~~ 855 | app.listen(3000); 856 | console.log('app is listening at localhost:3000'); 857 | ~~~~~~~~ 858 | 859 | Next, we'll need the views for rendering html. We'll use a templating language named ejs for our views. 860 | 861 | The only downside to ejs is that it doesn't allow us to specify a layout view like we did with sinatra and erb. 862 | 863 | To get around that we'll create header and footer views that we later include on other views. 864 | 865 | Let's make a views folder for all the views to live in: 866 | 867 | ~~~~~~~~ 868 | mkdir views 869 | ~~~~~~~~ 870 | 871 | And create all the view files that we need: 872 | 873 | ~~~~~~~~ 874 | touch views/header.ejs views/footer.ejs views/index.ejs views/post.ejs 875 | ~~~~~~~~ 876 | 877 | Add this content to the header.ejs file: 878 | 879 | ~~~~~~~~ 880 | 881 | 882 | 883 | 884 | 885 | <%= title %> 886 | 887 | 888 | 889 | 890 |
891 |
892 |

<%= title %>

893 |
894 |
895 | ~~~~~~~~ 896 | 897 | Add this content to the footer.ejs file: 898 | 899 | ~~~~~~~~ 900 |
901 |
902 |

Posts are also available via json at /api/posts/ 903 |

904 |
905 | 906 | 907 | 908 | ~~~~~~~~ 909 | 910 | Add this content to the index.ejs file: 911 | 912 | ~~~~~~~~ 913 | <% include header %> 914 | 915 |
916 |
917 | <% posts.forEach(function(post){ %> 918 |

919 | 920 | <%= post.title %> 921 | 922 |

923 |
<%= post.content %>
924 | <% }); %> 925 |
926 |
927 | 928 | <% include footer %> 929 | ~~~~~~~~ 930 | 931 | Add this content to the post.ejs file: 932 | 933 | ~~~~~~~~ 934 | <% include header %> 935 | 936 |
937 |
938 |

<%= post.title %>

939 |
<%= post.content %>
940 |
941 |
942 | 943 | <% include footer %> 944 | ~~~~~~~~ 945 | 946 | Let's add some css styling so this looks a little more readable. 947 | 948 | First create the public folder and the styles.css file: 949 | 950 | ~~~~~~~~ 951 | mkdir public 952 | touch public/styles.css 953 | ~~~~~~~~ 954 | 955 | Now add this content to the styles.css file: 956 | 957 | ~~~~~~~~ 958 | body { 959 | font: 16px/1.5 'Helvetica Neue', Helvetica, Arial, sans-serif; 960 | color: #787876; 961 | } 962 | 963 | h1, h3 { 964 | font-weight: 300; 965 | margin-bottom: 5px; 966 | } 967 | 968 | a { 969 | text-decoration: none; 970 | color: #EA6045; 971 | } 972 | 973 | a:hover { 974 | color: #2F3440; 975 | } 976 | 977 | .container { 978 | width: 90%; 979 | margin: 0px auto; 980 | } 981 | 982 | footer { 983 | margin-top: 30px; 984 | border-top: 1px solid #efefef; 985 | padding-top: 20px; 986 | font-style: italic; 987 | } 988 | 989 | @media (min-width: 600px){ 990 | .container { 991 | width: 60%; 992 | } 993 | } 994 | ~~~~~~~~ 995 | 996 | You should now be able to navigate on the home page, three blog post pages, and the posts json feed. Run the project with the nodemon command: 997 | 998 | ~~~~~~~~ 999 | nodemon -e js,css,html,ejs app.js 1000 | ~~~~~~~~ 1001 | 1002 | ### Accessing the site in the browser 1003 | 1004 | After starting the app with `nodemon`, you should see this output on the command line: 1005 | 1006 | ~~~~~~~~ 1007 | app is listening at localhost:3000 1008 | ~~~~~~~~ 1009 | 1010 | You can now open a browser and navigate to http://localhost:3000 to view the site. 1011 | 1012 | 1013 | ### Express resources 1014 | 1015 | Learn more about express at the express website: [http://expressjs.com](http://expressjs.com/) 1016 | 1017 | Try out the ExpressWorks workshop on nodeschool.io: [nodeschool.io/#expressworks](http://nodeschool.io/#expressworks) 1018 | -------------------------------------------------------------------------------- /chapters/11-python.md: -------------------------------------------------------------------------------- 1 | # Python 2 | 3 | Python is a language that is readable, quick to learn, and used for a wide range of purposes, including web development, science, and in academia. 4 | 5 | In this chapter we'll review some basics of the Python language, testing with UnitTest, creating dev environments with pip and virtualenv, and building apps with flask, a small web development framework. 6 | 7 | We'll be working with Python version 2.7, which should be installed by default on most systems, and is still best supported by various Python libraries. In the future we'll do an update to this book to support Python 3. 8 | 9 | 10 | ## Language website 11 | http://www.python.org/ 12 | 13 | ## Documentation 14 | http://www.python.org/doc/ 15 | 16 | 17 | ## Vagrant 18 | 19 | Let's create a vagrant machine in your python dev-envs folder: 20 | 21 | ~~~~~~~~ 22 | mkdir ~/dev-envs/python 23 | cd ~/dev-envs/python 24 | ~~~~~~~~ 25 | 26 | Create a new vagrant machine using the Ubuntu Precise box: 27 | 28 | ~~~~~~~~ 29 | vagrant init precise32 30 | ~~~~~~~~ 31 | 32 | Forward a port for viewing your site: 33 | 34 | Open the Vagrant file: 35 | 36 | ~~~~~~~~ 37 | nano Vagrantfile 38 | ~~~~~~~~ 39 | 40 | Find this section: 41 | 42 | ~~~~~~~~ 43 | # Create a forwarded port mapping which allows access to a specific port 44 | # within the machine from a port on the host machine. In the example below, 45 | # accessing "localhost:8080" will access port 80 on the guest machine. 46 | # config.vm.network "forwarded_port", guest: 80, host: 8080 47 | ~~~~~~~~ 48 | 49 | And change this line: 50 | 51 | ~~~~~~~~ 52 | # config.vm.network "forwarded_port", guest: 80, host: 8080 53 | ~~~~~~~~ 54 | 55 | To this: 56 | 57 | ~~~~~~~~ 58 | config.vm.network "forwarded_port", guest: 5000 , host: 5000 59 | ~~~~~~~~ 60 | 61 | Make sure to uncomment the line by removing the `#`. 62 | 63 | Now start the vagrant machine: 64 | 65 | ~~~~~~~~ 66 | vagrant up 67 | ~~~~~~~~ 68 | 69 | If all goes well that'll result in output similar to the following: 70 | 71 | ~~~~~~~~ 72 | Bringing machine 'default' up with 'virtualbox' provider... 73 | ==> default: Importing base box 'precise32'... 74 | ==> default: Matching MAC address for NAT networking... 75 | ==> default: Setting the name of the VM: dev-envs_default_1400021636069_14720 76 | ==> default: Clearing any previously set network interfaces... 77 | ==> default: Preparing network interfaces based on configuration... 78 | default: Adapter 1: nat 79 | ==> default: Forwarding ports... 80 | default: 5000 => 5000 (adapter 1) 81 | default: 22 => 2222 (adapter 1) 82 | ==> default: Booting VM... 83 | ==> default: Waiting for machine to boot. This may take a few minutes... 84 | default: SSH address: 127.0.0.1:2222 85 | default: SSH username: vagrant 86 | default: SSH auth method: private key 87 | default: Error: Connection timout. Retrying... 88 | ==> default: Machine booted and ready! 89 | ==> default: Checking for guest additions in VM... 90 | default: The guest additions on this VM do not match the installed version of 91 | default: VirtualBox! In most cases this is fine, but in rare cases it can 92 | default: prevent things such as shared folders from working properly. If you see 93 | default: shared folder errors, please make sure the guest additions within the 94 | default: virtual machine match the version of VirtualBox you have installed on 95 | default: your host and reload your VM. 96 | default: 97 | default: Guest Additions Version: 4.2.0 98 | default: VirtualBox Version: 4.3 99 | ==> default: Mounting shared folders... 100 | default: /vagrant => /Users/sethvincent/dev-envs 101 | ~~~~~~~~ 102 | 103 | Now we will log in to the vagrant machine. This will be very much like using the `ssh` command to log in to a remote server. 104 | 105 | Use this command: 106 | 107 | ~~~~~~~~ 108 | vagrant ssh 109 | ~~~~~~~~ 110 | 111 | You should see output similar to the following: 112 | 113 | ~~~~~~~~ 114 | Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic-pae i686) 115 | 116 | * Documentation: https://help.ubuntu.com/ 117 | Welcome to your Vagrant-built virtual machine. 118 | Last login: Fri Sep 14 06:22:31 2012 from 10.0.2.2 119 | ~~~~~~~~ 120 | 121 | You'll now find your project folder at `/vagrant` when logged in to the vagrant machine. 122 | 123 | So navigate there like this: 124 | 125 | ~~~~~~~~ 126 | cd /vagrant 127 | ~~~~~~~~ 128 | 129 | 130 | We'll now install python, its dependencies, and related tools, and get started building applications. Complete all the following instructions while logged in to the vagrant machine. 131 | 132 | ## Install git & dependencies 133 | 134 | To get started, we'll need to install git and some necessary system dependencies while logged in to the virtual machine: 135 | 136 | ~~~~~~~~ 137 | sudo apt-get install git python-setuptools python-dev build-essential 138 | ~~~~~~~~ 139 | 140 | 141 | ## Installing python 142 | Python is most likely already installed on your machine. 143 | 144 | 145 | 146 | ## Package manager: pip 147 | pip: [http://www.pip-installer.org/en/latest/](http://www.pip-installer.org/en/latest/) 148 | https://github.com/pypa/pip 149 | 150 | 151 | 152 | ### Use virtualenv 153 | virtualenv: [http://www.virtualenv.org/en/latest/](http://www.virtualenv.org/en/latest/) 154 | 155 | 156 | ## Build tools / automating repetitive tasks 157 | 158 | For automating tasks in python development, use [fabric](http://fabfile.org). 159 | 160 | ### Install 161 | 162 | First, install fabric: 163 | 164 | ~~~~~~~~ 165 | pip install fabric 166 | ~~~~~~~~ 167 | 168 | Create a fabfile.py in your project directory: 169 | 170 | ~~~~~~~~ 171 | touch fabfile.py 172 | ~~~~~~~~ 173 | 174 | Add this example to your fabfile.py: 175 | 176 | ~~~~~~~~ 177 | from fabric.api import local 178 | 179 | def start(): 180 | local("python app.py") 181 | ~~~~~~~~ 182 | 183 | Run this command: 184 | 185 | ~~~~~~~~ 186 | fab start 187 | ~~~~~~~~ 188 | 189 | The start task defined in your fabfile.py will be executed. 190 | 191 | 192 | Learn more about fabric by reading the [project documentation](http://docs.fabfile.org/en/1.7/). 193 | 194 | 195 | ## Testing: unittest 196 | We'll be using unittest as the testing framework with python. It comes bundled with python so it doesn't have to be installed separately. 197 | 198 | unittest documentation: [http://docs.python.org/2.7/library/unittest.html](http://docs.python.org/2.7/library/unittest.html) 199 | 200 | Here's a very simple example of unittest usage: 201 | 202 | A simple example of a test written with tape: 203 | 204 | ~~~~~~~~ 205 | import unittest 206 | 207 | class PizzaTest(unittest.TestCase): 208 | 209 | def setUp(self): 210 | self.pizza = 'pizza' 211 | 212 | def test_pizza(self): 213 | self.assertEqual(self.pizza, 'pizza') 214 | 215 | if __name__ -- '__main__': 216 | unittest.main() 217 | ~~~~~~~~ 218 | 219 | ## Language basics 220 | 221 | ### variables 222 | 223 | Create a variable like this: 224 | 225 | ~~~~~~~~ 226 | some_variable = 'some value' 227 | ~~~~~~~~ 228 | 229 | ### numbers 230 | 231 | ~~~~~~~~ 232 | some_number = 3 233 | ~~~~~~~~ 234 | 235 | A number is any digit, including decimals, or floating point numbers. 236 | 237 | ### string 238 | 239 | ~~~~~~~~ 240 | some_string = 'this is a string' 241 | ~~~~~~~~ 242 | 243 | You can create multi-line strings like this: 244 | 245 | ~~~~~~~~ 246 | some_big_string = """ 247 | This is one line of the string. 248 | And this is another. 249 | This line is also part of the string. 250 | """ 251 | ~~~~~~~~ 252 | 253 | A string is text surrounded by single or double quotes. 254 | 255 | ### list 256 | 257 | A list in python is very similar to an array in javascript and ruby. Create a list like this: 258 | 259 | ~~~~~~~~ 260 | some_list = ['a', 1, 'b'] 261 | ~~~~~~~~ 262 | 263 | It's possible to nest lists like this: 264 | 265 | ~~~~~~~~ 266 | some_nested_list = [1, ['a', 'b', 'c'], 'pizza']; 267 | ~~~~~~~~ 268 | 269 | ### dictionary 270 | 271 | Dictionaries in python are similar to objects in javascript or hashes in ruby. 272 | 273 | Create a dictionary like this: 274 | 275 | ~~~~~~~~ 276 | some_dictionary = { 'thing': 'one', 'otherthing': 'two' } 277 | ~~~~~~~~ 278 | 279 | ### function 280 | 281 | Define a function in python like this: 282 | 283 | ~~~~~~~~ 284 | def eat(food): 285 | return 'I ate ' + food 286 | ~~~~~~~~ 287 | 288 | ### class 289 | 290 | Create a class in python like this: 291 | 292 | ~~~~~~~~ 293 | class Meal: 294 | # here we define methods on the class 295 | ~~~~~~~~ 296 | 297 | 298 | ### method 299 | 300 | Defining methods in python looks like this: 301 | 302 | ~~~~~~~~ 303 | class Meal: 304 | def __init__(self, food): 305 | self.food = food 306 | 307 | def eat(self) 308 | return 'I ate ' + self.food 309 | ~~~~~~~~ 310 | 311 | ### class instance 312 | 313 | Create an instance of the class like this: 314 | 315 | ~~~~~~~~ 316 | dinner = Meal('pizza') 317 | dinner.eat() 318 | ~~~~~~~~ 319 | 320 | ### importing/requiring code 321 | 322 | To import code into your program, use this syntax: 323 | 324 | ~~~~~~~~ 325 | import PACKAGENAME 326 | ~~~~~~~~ 327 | 328 | You can import specific classes with this syntax: 329 | 330 | ~~~~~~~~ 331 | from PACKAGENAME import CLASS 332 | ~~~~~~~~ 333 | 334 | For instance, with the flask library we use later, importing flask looks like this: 335 | 336 | ~~~~~~~~ 337 | from flask import Flask 338 | ~~~~~~~~ 339 | 340 | ## Web framework: flask 341 | flask [http://flask.pocoo.org/](http://flask.pocoo.org/) 342 | 343 | Navigate to your python projects folder: 344 | 345 | ~~~~~~~~ 346 | cd ~/dev-envs/python 347 | ~~~~~~~~ 348 | 349 | Create a folder named hello-flask and navigate into it: 350 | 351 | ~~~~~~~~ 352 | mkdir hello-flask && cd hello-flask 353 | ~~~~~~~~ 354 | 355 | ~~~~~~~~ 356 | pip install flask 357 | ~~~~~~~~ 358 | 359 | ### Simple example 360 | 361 | ~~~~~~~~ 362 | from flask import Flask 363 | app = Flask(__name__) 364 | 365 | @app.route('/') 366 | def pizza(): 367 | return 'pizza is awesome' 368 | 369 | if __name__ == '__main__': 370 | app.run() 371 | ~~~~~~~~ 372 | 373 | Type that code into your app.py file. 374 | 375 | Now you can run your app with this command: 376 | 377 | ~~~~~~~~ 378 | python app.py 379 | ~~~~~~~~ 380 | 381 | Let's look at this little app line by line: 382 | 383 | **Import the flask functionality into our app:** 384 | 385 | ~~~~~~~~ 386 | from flask import Flask 387 | ~~~~~~~~ 388 | 389 | Create the app by creating an instance of the Flask class: 390 | 391 | ~~~~~~~~ 392 | app = Flask(__name__) 393 | ~~~~~~~~ 394 | 395 | Define a route for the root url: 396 | 397 | ~~~~~~~~ 398 | @app.route('/') 399 | ~~~~~~~~ 400 | 401 | Define a function that responds to requests at the 402 | 403 | ~~~~~~~~ 404 | def pizza(): 405 | return 'pizza is awesome' 406 | ~~~~~~~~ 407 | 408 | The function that immediately follows the `route()` call defines what we'll return when someone visits the root url. 409 | 410 | **Run the app:** 411 | 412 | ~~~~~~~~ 413 | if __name__ == "__main__": 414 | app.run() 415 | ~~~~~~~~ 416 | 417 | `app.run()` kicks off a server to serve our app. The `if` statement checks if this code is being executed by the Python interpreter or being included as a module, and app.run() is only called if the code is being executed by the Python interpreter. 418 | 419 | ### Extended example 420 | 421 | Let's make a small website with [flask](http://flask.pocoo.org/) to explore how it works. 422 | 423 | In this example our site will do three things: 424 | - serve html at the root route from a view that has a list of posts 425 | - serve html for a single post at `/post/:id` 426 | - serve json at `/api/posts` that has a list of posts 427 | 428 | We won't be using a database for this example, but instead will use a json file with a list of posts. 429 | 430 | We'll be using the default template language that flask uses, [jinja](http://jinja.pocoo.org/). Let's install flask by creating a virtualenv and using pip: 431 | 432 | ~~~~~~~~ 433 | virtualenv flask-example 434 | cd flask-example 435 | ~~~~~~~~ 436 | 437 | Activate the virtualenv: 438 | 439 | ~~~~~~~~ 440 | source bin/activate 441 | ~~~~~~~~ 442 | 443 | Now use pip to install flask and its dependencies 444 | 445 | ~~~~~~~~ 446 | pip install flask 447 | ~~~~~~~~ 448 | 449 | For this project we'll put the source files inside the virtualenv folder. Create a new folder called source for our application code: 450 | 451 | ~~~~~~~~ 452 | mkdir source 453 | cd source 454 | ~~~~~~~~ 455 | 456 | Your directory structure should now look like this: 457 | 458 | ~~~~~~~~ 459 | flask-example 460 | - bin 461 | - source 462 | - include 463 | - lib 464 | ~~~~~~~~ 465 | 466 | Make sure you put new files inside the source folder. 467 | 468 | We'll run the application with this command: 469 | 470 | ~~~~~~~~ 471 | python app.py 472 | ~~~~~~~~ 473 | 474 | Create a file named posts.json with the following json: 475 | 476 | ~~~~~~~~ 477 | [ 478 | { 479 | "title": "This is the first post", 480 | "slug": "first-post", 481 | "content": "The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome. The pizza is awesome." 482 | }, 483 | { 484 | "title": "Another post that you might like", 485 | "slug": "second-post", 486 | "content": "Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great. Eating pizza is great." 487 | }, 488 | { 489 | "title": "The third and last post", 490 | "slug": "third-post", 491 | "content": "The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out. The pizza always runs out." 492 | } 493 | ] 494 | ~~~~~~~~ 495 | 496 | Now create the app.py file: 497 | 498 | ~~~~~~~~ 499 | from flask import Flask, render_template, g, jsonify 500 | import json 501 | 502 | 503 | app = Flask('extended-flask-example') 504 | app.config['DEBUG'] = True 505 | 506 | 507 | @app.before_request 508 | def before_request(): 509 | g.title = 'Extended flask example' 510 | posts = open('posts.json') 511 | g.posts = json.load(posts) 512 | posts.close() 513 | 514 | 515 | @app.route('/') 516 | def index(): 517 | posts = getattr(g, 'posts', None) 518 | return render_template('index.html', posts=posts) 519 | 520 | 521 | @app.route('/post/') 522 | def show_post(slug): 523 | for post in g.posts: 524 | if slug == post['slug']: 525 | return render_template('post.html', post=post) 526 | 527 | 528 | @app.route('/api/posts') 529 | def show_json(): 530 | meta = { 'name': g.title } 531 | return jsonify(posts=g.posts, meta=meta) 532 | 533 | 534 | if __name__ == '__main__': 535 | app.run() 536 | ~~~~~~~~ 537 | 538 | Let's break down this example code chunk by chunk: 539 | 540 | Require the necessary python libraries. Note that there are multiple classes from the flask library that we're importing individually: 541 | 542 | ~~~~~~~~ 543 | from flask import Flask, render_template, g, jsonify 544 | import json 545 | ~~~~~~~~ 546 | 547 | 548 | Create the application with a name and turn on debug so the application will reload each time you make changes to the app.py file and provide useful error messages: 549 | 550 | ~~~~~~~~ 551 | app = Flask('extended-flask-example') 552 | app.config['DEBUG'] = True 553 | ~~~~~~~~ 554 | 555 | Create global variables that are available to our views using the before method, which runs before a request is processed. Here we're loading the posts.json file into the application so we can use it in our views: 556 | 557 | ~~~~~~~~ 558 | @app.before_request 559 | def before_request(): 560 | g.title = 'Extended flask example' 561 | posts = open('posts.json') 562 | g.posts = json.load(posts) 563 | posts.close() 564 | ~~~~~~~~ 565 | 566 | Serve the index.html template on the root url with the following code block. Note that with flask, templates are the equivalent to views in sinatra or express. Flask automatically looks in a folder named templates, so you only have to specify the filename: 567 | 568 | ~~~~~~~~ 569 | @app.route('/') 570 | def index(): 571 | posts = getattr(g, 'posts', None) 572 | return render_template('index.html', posts=posts) 573 | ~~~~~~~~ 574 | 575 | The following code block listens for requests for a specific blog post. We iterate through each of the items in our posts array, and if the slug that's passed in the url matches a slug in the posts array, we render the page with that post set as the `post` variable. 576 | 577 | ~~~~~~~~ 578 | @app.route('/post/') 579 | def show_post(slug): 580 | for post in g.posts: 581 | if slug == post['slug']: 582 | return render_template('post.html', post=post) 583 | ~~~~~~~~ 584 | 585 | The following is a simple example of exposing a simple json feed of the posts: 586 | 587 | ~~~~~~~~ 588 | @app.route('/api/posts') 589 | def show_json(): 590 | meta = { 'name': g.title } 591 | return jsonify(posts=g.posts, meta=meta) 592 | ~~~~~~~~ 593 | 594 | Finally, this code block checks to make sure our application is not a module being loaded into another application, and then runs the app. This is particularly useful in the case that your app might be used on its own or as part of another application: 595 | 596 | ~~~~~~~~ 597 | if __name__ == '__main__': 598 | app.run() 599 | ~~~~~~~~ 600 | 601 | 602 | Next, we'll need the templates for rendering html. 603 | 604 | Let's make a templates folder for all the templates to live in: 605 | 606 | ~~~~~~~~ 607 | mkdir templates 608 | ~~~~~~~~ 609 | 610 | And create all the template files that we need: 611 | 612 | ~~~~~~~~ 613 | touch templates/base.html templates/index.html templates/post.html 614 | ~~~~~~~~ 615 | 616 | Add this content to the base.html file: 617 | 618 | ~~~~~~~~ 619 | 620 | 621 | 622 | 623 | 624 | {{ g.title }} 625 | 626 | 627 | 628 | 629 |
630 |
631 |

{{ g.title }}

632 |
633 |
634 | 635 |
636 |
637 | {% block content %}{% endblock %} 638 |
639 |
640 | 641 |
642 |
643 |

Posts are also available via json at /api/posts/ 644 |

645 |
646 | 647 | 648 | 649 | ~~~~~~~~ 650 | 651 | Add this content to the index.html file: 652 | 653 | ~~~~~~~~ 654 | {% extends "base.html" %} 655 | 656 | {% block content %} 657 | 658 | {% for post in posts %} 659 |

660 | 661 | {{ post.title }} 662 | 663 |

664 |
{{ post.content }}
665 | {% endfor %} 666 | 667 | {% endblock %} 668 | ~~~~~~~~ 669 | 670 | Add this content to the post.erb file: 671 | 672 | ~~~~~~~~ 673 | {% extends "base.html" %} 674 | 675 | {% block content %} 676 | 677 |

{{ post.title }}

678 |
{{ post.content }}
679 | 680 | {% endblock %} 681 | ~~~~~~~~ 682 | 683 | Let's add some css styling so this looks a little more readable. By default flask will look in a folder named static for static files. 684 | 685 | First create the static folder and the styles.css file: 686 | 687 | ~~~~~~~~ 688 | mkdir static 689 | touch static/styles.css 690 | ~~~~~~~~ 691 | 692 | Now add this content to the styles.css file: 693 | 694 | ~~~~~~~~ 695 | body { 696 | font: 16px/1.5 'Helvetica Neue', Helvetica, Arial, sans-serif; 697 | color: #787876; 698 | } 699 | 700 | h1, h3 { 701 | font-weight: 300; 702 | margin-bottom: 5px; 703 | } 704 | 705 | a { 706 | text-decoration: none; 707 | color: #EA6045; 708 | } 709 | 710 | a:hover { 711 | color: #2F3440; 712 | } 713 | 714 | .container { 715 | width: 90%; 716 | margin: 0px auto; 717 | } 718 | 719 | footer { 720 | margin-top: 30px; 721 | border-top: 1px solid #efefef; 722 | padding-top: 20px; 723 | font-style: italic; 724 | } 725 | 726 | @media (min-width: 600px){ 727 | .container { 728 | width: 60%; 729 | } 730 | } 731 | ~~~~~~~~ 732 | 733 | You should now be able to navigate on the home page, three blog post pages, and the posts json feed. Run the project with the nodemon command: 734 | 735 | ~~~~~~~~ 736 | python app.py 737 | ~~~~~~~~ 738 | 739 | ### Accessing the site in the browser 740 | 741 | After starting the app with `shotgun`, you should see this output on the command line: 742 | 743 | ~~~~~~~~ 744 | Running on http://127.0.0.1:5000/ 745 | ~~~~~~~~ 746 | 747 | You can now open a browser and navigate to http://localhost:5000 to view the site. 748 | 749 | ### Flask resources 750 | 751 | Learn more about flask at the project website: [http://flask.pocoo.org/](http://flask.pocoo.org/) 752 | 753 | ## Resources 754 | 755 | Dive into Python: [http://www.diveintopython.net/](http://www.diveintopython.net/) 756 | 757 | Test-driven development in python: [http://net.tutsplus.com/tutorials/python-tutorials/test-driven-development-in-python/](http://net.tutsplus.com/tutorials/python-tutorials/test-driven-development-in-python/) 758 | -------------------------------------------------------------------------------- /chapters/12-recap.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | Let's recap some of what we covered by comparing a few of the aspects of developing with ruby, javascript, and python. 4 | 5 | ## Package managers & dependency management 6 | 7 | **Ruby:** 8 | 9 | Ruby developers use **rubygems** for installing packages, and use **bundler** & the **Gemfile** for defining dependencies. 10 | 11 | **Javascript & Node.js:** 12 | 13 | Javascript has **npm** for installing packages, and you can define dependencies in a **package.json** file. Remember that npm can be used for browser-side code (particulary when using a tool like browserify or webpack for bundling javascript for the browser). 14 | 15 | **Python:** 16 | 17 | Python has **pip** for installing packages. With pip you'll define dependencies in a **requirements.txt** file. It's also common to use pip in combination with **virtualenv**, which keeps the dependencies of your application separate from what's installed globally on your operating system. 18 | 19 | ## Build tools / task automation 20 | 21 | We took a quick look at npm scripts and Grunt for JavaScript, rake for Ruby, and fabric for Python, providing a glimpse of the differences in the way the build tools work with each language. 22 | 23 | ## Simple web framework examples 24 | 25 | **Ruby:** 26 | 27 | ~~~~~~~~ 28 | require 'sinatra' 29 | 30 | get '/' do 31 | 'pizza is awesome' 32 | end 33 | ~~~~~~~~ 34 | 35 | Look at how tiny and pleasant that ruby code is! 36 | 37 | **Javascript:** 38 | 39 | ~~~~~~~~ 40 | var express = require('express'); 41 | var app = express(); 42 | 43 | app.get('/', function(req, res){ 44 | res.send('pizza is awesome.'); 45 | }); 46 | 47 | app.listen(3000); 48 | 49 | console.log('app is listening at localhost:3000'); 50 | ~~~~~~~~ 51 | 52 | Express doesn't automatically take care of listening on a default port, or telling the user that the app is listening, so that adds just a small amount of extra code compared to the ruby/sinatra example. 53 | 54 | **Python:** 55 | 56 | ~~~~~~~~ 57 | from flask import Flask 58 | app = Flask(__name__) 59 | 60 | @app.route('/') 61 | def pizza(): 62 | return 'pizza is awesome' 63 | 64 | if __name__ == '__main__': 65 | app.run() 66 | ~~~~~~~~ 67 | 68 | Python feels different because of it's use of meaningful whitespace and lack of curly brackets or `do end` for blocks. Everything is just indented 4 spaces instead to represent blocks of code. 69 | 70 | These three examples should feel very similar. That's no coincidence. Both express and flask were inspired by sinatra's clean and simple API. 71 | -------------------------------------------------------------------------------- /chapters/Book.txt: -------------------------------------------------------------------------------- 1 | 01-introduction.md 2 | 02-whatisdevenv.md 3 | 03-vagrant.md 4 | 04-terminal.md 5 | 05-editors.md 6 | 06-git.md 7 | 07-github.md 8 | 08-setup.md 9 | 09-ruby.md 10 | 10-javascript.md 11 | 11-python.md 12 | 12-recap.md 13 | changelog.md -------------------------------------------------------------------------------- /chapters/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.4.2 – May 13, 2014 4 | - typo fixes submitted by [Jason Li](http://www.hongkonggong.com) 5 | - fixes from [suisea](https://github.com/suisea) 6 | - add instructions for viewing the site in the browser to ruby, js, python chapters 7 | - improve vagrant instructions in ruby, js, python chapters 8 | 9 | ## v0.4.1 – February 28, 2014 10 | - small typo fixes 11 | 12 | ## v0.4.0 – February 26, 2014 13 | - revise introduction 14 | - expand 02-whatisdevenv 15 | - improve windows instructions 16 | - add to vagrant section 17 | - update editors chapter 18 | - add resources to git section 19 | - change `DevEnvs` folder to `dev-envs` 20 | - add npm scripts info to javascript automating tasks section 21 | - update js import/require section 22 | - add resources to javascript section 23 | - the automating tasks sections of ruby & python chapters need actual content 24 | - add to ending summary. 25 | - fix typos and clean up resource link text styles 26 | 27 | ## v0.3.0 - Feruary 3, 2014 28 | - add sublime text editor tips and small typo edits/revisions 29 | - add info about package control to sublime section 30 | - fixes from [suisea](https://github.com/suisea) 31 | - update git and javascript chapters 32 | 33 | ## v0.2.0 - October 26, 2013 34 | - Expand terminal and vagrant sections 35 | - Add vagrant instructions to ruby, javascript, and python chapters 36 | 37 | ## v0.1.4 - September 30, 2013 38 | - Add ruby/sinatra extended example 39 | - Add python/flask extended example 40 | - fix some typos 41 | 42 | ## v0.1.3 - September 27, 2013 43 | - Add to unittest and other edits in python section 44 | - Add extended express example to javascript section 45 | 46 | ## v0.1.2 - September 3, 2013 47 | - added rake, grunt, and fabric sections 48 | - initial work on python language basics 49 | 50 | ## v0.1.1 - August 18, 2013 51 | - added language basics to javascript and ruby chapters 52 | - added a bunch to vagrant chapter 53 | - added to terminal chapter 54 | 55 | ## v0.1.0 - August 17, 2013 56 | - started all chapters! -------------------------------------------------------------------------------- /chapters/packages.md: -------------------------------------------------------------------------------- 1 | # Package managers 2 | 3 | ## Wait, what's a package? 4 | 5 | ## Homebrew 6 | 7 | ### Website 8 | http://mxcl.github.io/homebrew/ 9 | 10 | ### Documentation 11 | docs: https://github.com/mxcl/homebrew/wiki 12 | 13 | ### Install 14 | download / install: http://mxcl.github.io/homebrew/ 15 | 16 | ## Chocolatey 17 | 18 | ### Website 19 | http://chocolatey.org/ 20 | 21 | ### Documentation 22 | docs: https://github.com/chocolatey/chocolatey/wiki 23 | 24 | ### Install 25 | download / install instructions: https://github.com/chocolatey/chocolatey/wiki/Installation 26 | 27 | ## apt-get 28 | https://help.ubuntu.com/community/AptGet/Howto -------------------------------------------------------------------------------- /chapters/x01-next.md: -------------------------------------------------------------------------------- 1 | # What happens next? 2 | 3 | Next, you build an app. 4 | 5 | Now that you have a development environment set up, the best way to learn a language in depth is to just use it. 6 | And most important: define a goal for a simple application you can build. Something small, but that solves a real problem for you. Something that automates a repetitive task you find yourself doing regularly. 7 | 8 | You'll need a guide to language syntax and API documentation. And doing some further reading about the language of your choice is a good idea. 9 | 10 | There are many resources that can help you along the way. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "development-environments-for-beginners", 3 | "version": "0.4.2", 4 | "repository": { 5 | "url": "git://github.com/civicmakerlab/development-environments-for-beginners.git" 6 | }, 7 | "description": "A guide to creating development environments for ruby, javascript, and python.", 8 | "main": "index.js", 9 | "dependencies": { 10 | "manuscript-builder": "^1.0.0" 11 | }, 12 | "devDependencies": { 13 | "manuscript-builder": "^0.0.3", 14 | "ebook-convert": "^0.1.0" 15 | }, 16 | "scripts": { 17 | "leanpub": "./bin/leanpub", 18 | "compile": "./bin/compile", 19 | "dist": "./bin/dist" 20 | }, 21 | "author": "Seth Vincent http://sethvincent.com", 22 | "license": "MIT" 23 | } 24 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Development environments for beginners 2 | 3 | [![http://superbigtree.com/img/dev-envs.cover.300x388.jpg](http://superbigtree.com/img/dev-envs.cover.300x388.jpg)](http://superbigtree.com/books/dev-envs) 4 | 5 | Thanks for checking out the book! 6 | 7 | ## Please consider supporting the work by purchasing the book 8 | 9 | You can purchase it through leanpub: [leanpub.com/dev-envs](https://leanpub.com/dev-envs) 10 | 11 | You can view the in-progress book in book.md or the chapters folder. If you like it, consider buying it to support the work! 12 | --------------------------------------------------------------------------------