diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..9151357bd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# See https://help.github.com/articles/dealing-with-line-endings + +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto + diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml new file mode 100644 index 000000000..e62589a47 --- /dev/null +++ b/.github/workflows/rspec.yml @@ -0,0 +1,28 @@ +name: RSpec + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + rspec: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + + - name: Install dependencies + run: bundle install + + - name: Run test and linter + run: + bundle exec rake diff --git a/.gitignore b/.gitignore index e8dcdf163..5aa12c79e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ .DS_Store +.bundle +.env .idea -Thumbs.db .rvmrc .sass-cache +Thumbs.db +.byebug_history diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 000000000..be94e6f53 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.2.2 diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 000000000..f2a971aa7 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +ruby 3.2.2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..937117101 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,108 @@ +# Thanks! + +RailsBridge wouldn't be what it is without your help. Thanks for contributing. + +We ask that contributions be made as pull requests via GitHub. If those words +are totally foreign to you, +[see here](#its-my-first-time-on-github-ever-what-do-i-do). + +# When Submitting a Pull Request + +*Here's a couple of tricks to grease the wheels and make it easy for the +maintainers to love you. :heart:* + +## Before You Start! + +- If you have an existing fork, please make sure it's up to date. + It just makes your life easier! If not, make sure you fork *before* cloning, + otherwise you'll need to spend some time juggling remotes. + Look at the section "Keep your fork synced" in GitHub's + [Fork A Repo](https://help.github.com/articles/fork-a-repo) article. + +- Create a local topic branch before you start working. This branch is going to + be named for what you plan to change. `fix-typo-in-slides`, `move-resources`, + and `mountain-lion-support` are all good names for topic branches. If you've + never created a local branch before, you can use `git checkout -b + new-branch-name`. + +## Before Submitting + +- Please, please, *please* run `bundle exec rake` from the terminal before you submit. It + not only runs our test suite but also checks the docs for syntax errors. + +- Push to a branch on GitHub. Just like you developed in a local branch, you + should push to a branch of your repo on GitHub as well. The `master` branch is + best used as a clean copy of the upstream docs repo in case you need to make + some unrelated changes. To push to a branch, + if your branch is named "fix-typo-in-slides", + use `git push origin fix-typo-in-slides`. + +## Submitting a Pull Request + +- Read the GitHub Guide on [Forking](https://guides.github.com/activities/forking/), especially the part about + [Pull Requests](https://guides.github.com/activities/forking/#making-a-pull-request). + +- Remember, pull requests are submitted *from* your repo, but show up on the + *upstream* repo. + +## Discussion and Waiting On a Merge + +- Every pull request will receive a response from the team. +- Not every pull request will be merged as is. +- Not every pull request will be merged at all. +- If a pull request falls significantly behind master, we may ask that you close + it, rebase your changes off of master, and submit a new pull request. +- Feel free to "ping" the team by adding a short comment to your pull request + if it's been more than a week with no reply + +## After your merge has been accepted + +- go back to your fork and keep it up to date, e.g. + + git checkout master + git pull upstream master + git push origin master + +- you can also delete your topic branch if you like + + git branch -dr fix-typo-in-slides + +# It's My First Time on GitHub Ever What Do I Do? + +Relax, you came to the right place. In order to contribute, you'll need to be +able to familiarize yourself with some concepts from git and GitHub. It's going +to be a lot of information, but you're :sparkles:awesome:sparkles:! So you'll +be fine. + +First, you'll need a GitHub account, which is totally free. You can sign up +[here](https://github.com/join). + +Next, browse the [GitHub Help site](https://help.github.com) and the +[GitHub Guides](https://guides.github.com/). The Help Site is more technical, and the +Guides are very easy to understand tutorials. + +You'll want to read about +[forking](https://help.github.com/articles/fork-a-repo) and then make your own +fork of [railsbridge/docs](https://github.com/railsbridge/docs). Once you've +done so, you can clone it and get started by reading up on [what to do when +submitting a pull request](#when-submitting-a-pull-request), and read up on +[pull requests](https://help.github.com/articles/using-pull-requests) +themselves. + +If this is all too much, or you'd like a helping hand, +[@nuclearsandwich](https://github.com/nuclearsandwich) has volunteered to help +anyone who wants to contribute do so. His email is behind that profile link. + +# Closing + +If you haven't taken the time yet, go through the [Git Immersion lab](http://gitimmersion.com) +at . Do it. It's worth it no matter how much git-fu you have. + +Also, [Pro Git](http://git-scm.com/book) is a great (and free!) book about Git. + +We apologize for how long this document is! Hopefully, it addressed +most of your concerns about git, contributing, and GitHub. Feel free +to ask more questions on the +[railsbridge-workshops](http://groups.google.com/group/railsbridge-workshops) +mailing list. And we're open to any suggestions about improvements, +including to this document. diff --git a/Gemfile b/Gemfile index c41e28f8b..32eb465d5 100644 --- a/Gemfile +++ b/Gemfile @@ -1,21 +1,32 @@ -source :rubygems +source "https://rubygems.org/" -gem "erector", "~>0.9.0.pre1" +ruby '3.2.2' + +gem 'activesupport' +gem "erector", github: "erector/erector" gem "sinatra" +gem "sinatra-contrib" gem "nokogiri" -gem "rdiscount" gem "thin" gem 'rack-codehighlighter' gem 'coderay' -gem "deckrb", ">=0.3.0" +gem "deckrb" gem "sass" +gem "redcarpet" +gem "rubyzip" +gem "i18n" +gem 'font-awesome-sass' +gem 'bootstrap-sass' +gem 'jquery-cdn' +gem 'sprockets' +gem 'ffi' +gem 'backports' group :development do - gem "wrong", ">=0.6.2" gem "rspec" gem "rerun" gem "rake" gem "rack-test" - gem "files" - gem "bourbon" # do 'cd public/css; bourbon update' to get new bourbon + gem 'files', github: "alexch/files" + gem 'rb-fsevent', :platform => :ruby end diff --git a/Gemfile.lock b/Gemfile.lock index 6bed551d9..346a686d9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,109 +1,219 @@ +GIT + remote: https://github.com/alexch/files.git + revision: 4938dd85ed79f421b808e8798764661a71cab5a4 + specs: + files (0.4.0) + +GIT + remote: https://github.com/erector/erector.git + revision: 32cb38e3381889295183230caf8bf334d2f0e82a + specs: + erector (0.10.0) + haml + jeweler + nokogiri + sass + treetop (>= 1.2.3) + GEM - remote: http://rubygems.org/ + remote: https://rubygems.org/ specs: - ParseTree (3.0.8) - RubyInline (>= 3.7.0) - sexp_processor (>= 3.0.0) - RubyInline (3.11.2) - ZenTest (~> 4.3) - ZenTest (4.7.0) - bourbon (1.4.0) - sass (>= 3.1) - coderay (1.0.5) - daemons (1.1.8) - deckrb (0.3.0) + activesupport (6.0.6.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + zeitwerk (~> 2.2, >= 2.2.2) + addressable (2.4.0) + autoprefixer-rails (10.4.15.0) + execjs (~> 2) + backports (3.23.0) + bootstrap-sass (3.4.1) + autoprefixer-rails (>= 5.2.1) + sassc (>= 2.0.0) + builder (3.2.4) + coderay (1.1.3) + concurrent-ruby (1.1.9) + daemons (1.2.4) + deckrb (0.5.2) coderay - erector (>= 0.9.0.pre1) + erector (>= 0.9.0) json nokogiri - rack + rack (>= 1.4.1) rack-codehighlighter redcarpet (~> 2) + thin trollop - diff-lcs (1.1.3) - erector (0.9.0.pre1) - treetop (>= 1.2.3) - eventmachine (0.12.10) - file-tail (1.0.8) - tins (~> 0.3) - files (0.2.1) - json (1.6.6) - nokogiri (1.5.2) - polyglot (0.3.3) - predicated (0.2.6) - rack (1.4.1) - rack-codehighlighter (0.5.0) + descendants_tracker (0.0.4) + thread_safe (~> 0.3, >= 0.3.1) + diff-lcs (1.5.0) + eventmachine (1.2.3) + execjs (2.8.1) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) + ffi (1.15.5) + font-awesome-sass (6.4.2) + sassc (~> 2.0) + git (1.11.0) + rchardet (~> 1.8) + github_api (0.16.0) + addressable (~> 2.4.0) + descendants_tracker (~> 0.0.4) + faraday (~> 0.8, < 0.10) + hashie (>= 3.4) + mime-types (>= 1.16, < 3.0) + oauth2 (~> 1.0) + haml (6.2.3) + temple (>= 0.8.2) + thor + tilt + hashie (5.0.0) + highline (2.1.0) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + jeweler (2.3.9) + builder + bundler + git (>= 1.2.5) + github_api (~> 0.16.0) + highline (>= 1.6.15) + nokogiri (>= 1.5.10) + psych + rake + rdoc + semver2 + jquery-cdn (2.2.4) + sprockets (>= 2) + json (2.6.1) + jwt (2.7.1) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + mime-types (2.99.3) + mini_portile2 (2.8.0) + minitest (5.20.0) + multi_json (1.15.0) + multi_xml (0.6.0) + multipart-post (2.3.0) + mustermann (3.0.0) + ruby2_keywords (~> 0.0.1) + nokogiri (1.14.5) + mini_portile2 (~> 2.8.0) + racc (~> 1.4) + oauth2 (1.4.8) + faraday (>= 0.8, < 3.0) + jwt (>= 1.0, < 3.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + polyglot (0.3.5) + psych (5.1.1.1) + stringio + racc (1.7.2) + rack (2.2.4) + rack-codehighlighter (0.6.0) nokogiri (>= 1.4.1) - rack (>= 1.0.0) - rack-protection (1.2.0) - rack - rack-test (0.6.1) + rack (>= 2.0.0) + rack-protection (3.1.0) + rack (~> 2.2, >= 2.2.4) + rack-test (0.6.3) rack (>= 1.0) - rake (0.9.2.2) - rdiscount (1.6.8) - redcarpet (2.1.1) - rerun (0.6.5) - rspec (2.9.0) - rspec-core (~> 2.9.0) - rspec-expectations (~> 2.9.0) - rspec-mocks (~> 2.9.0) - rspec-core (2.9.0) - rspec-expectations (2.9.0) - diff-lcs (~> 1.1.3) - rspec-mocks (2.9.0) - ruby2ruby (1.3.1) - ruby_parser (~> 2.0) - sexp_processor (~> 3.0) - ruby_parser (2.0.6) - sexp_processor (~> 3.0) - sass (3.1.15) - sexp_processor (3.1.0) - sinatra (1.3.2) - rack (~> 1.3, >= 1.3.6) - rack-protection (~> 1.2) - tilt (~> 1.3, >= 1.3.3) - sourcify (0.5.0) - file-tail (>= 1.0.5) - ruby2ruby (>= 1.2.5) - ruby_parser (>= 2.0.5) - sexp_processor (>= 3.0.5) - thin (1.3.1) - daemons (>= 1.0.9) - eventmachine (>= 0.12.6) - rack (>= 1.0.0) - tilt (1.3.3) - tins (0.3.12) - treetop (1.4.10) - polyglot - polyglot (>= 0.3.1) - trollop (1.16.2) - wrong (0.6.2) - ParseTree (~> 3.0) - diff-lcs (~> 1.1.2) - file-tail (~> 1.0) - predicated (>= 0.2.3) - ruby2ruby (~> 1.2) - ruby_parser (~> 2.0.4) - sexp_processor (~> 3.0) - sourcify (>= 0.3.0) + rake (13.1.0) + rb-fsevent (0.9.8) + rb-inotify (0.9.8) + ffi (>= 0.5.0) + rchardet (1.8.0) + rdoc (6.5.0) + psych (>= 4.0.0) + redcarpet (2.3.0) + rerun (0.14.0) + listen (~> 3.0) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.4) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-mocks (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sassc (2.4.0) + ffi (~> 1.9) + semver2 (3.4.2) + sinatra (3.1.0) + mustermann (~> 3.0) + rack (~> 2.2, >= 2.2.4) + rack-protection (= 3.1.0) + tilt (~> 2.0) + sinatra-contrib (3.1.0) + multi_json + mustermann (~> 3.0) + rack-protection (= 3.1.0) + sinatra (= 3.1.0) + tilt (~> 2.0) + sprockets (3.7.2) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + stringio (3.0.8) + temple (0.10.3) + thin (1.8.1) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.3.0) + thread_safe (0.3.6) + tilt (2.0.11) + treetop (1.6.8) + polyglot (~> 0.3) + trollop (2.1.2) + tzinfo (1.2.10) + thread_safe (~> 0.1) + zeitwerk (2.6.12) PLATFORMS ruby DEPENDENCIES - bourbon + activesupport + backports + bootstrap-sass coderay - deckrb (>= 0.3.0) - erector (~> 0.9.0.pre1) - files + deckrb + erector! + ffi + files! + font-awesome-sass + i18n + jquery-cdn nokogiri rack-codehighlighter rack-test rake - rdiscount + rb-fsevent + redcarpet rerun rspec + rubyzip sass sinatra + sinatra-contrib + sprockets thin - wrong (>= 0.6.2) + +RUBY VERSION + ruby 3.2.2p53 + +BUNDLED WITH + 2.3.26 diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..3d55702ae --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,40 @@ +The documentation (including anything under the `sites` subdir as well as some hardcoded text elsewhere) is licensed under a Creative Commons license ([CC-BY, specifically](http://creativecommons.org/licenses/by/3.0/)), which means you're welcome to share, remix, or use our content commercially. We just ask for attribution. + + You are free to: + Share — copy and redistribute the material in any medium or format + Adapt — remix, transform, and build upon the material for any purpose, even commercially. + The licensor cannot revoke these freedoms as long as you follow the license terms. + + Under the following terms: + Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. + No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. + + Notices: + You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. + No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. + + +Source code is under an [MIT License](http://opensource.org/licenses/MIT) (MIT): + + Copyright (c) 2010-2014 by RailsBridge + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + diff --git a/Procfile b/Procfile new file mode 100644 index 000000000..e06ad9dc8 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: rackup -s thin -p $PORT diff --git a/README.md b/README.md index 51aeed527..6eeccd756 100644 --- a/README.md +++ b/README.md @@ -1,232 +1,77 @@ -# The Railsbridge Documentation Project +# The RailsBridge Documentation Project -Author: Alex Chaffee +[![Build Status](https://travis-ci.org/railsbridge/docs.png)](https://travis-ci.org/railsbridge/docs) -# Usage +## Overview - bundle install - rake run - -If the above fails (say, because `rerun` doesn't work on your system), try - - rackup - -Then open in a web browser. - -If you make any changes, and especially before a pull request, run +This is a Sinatra app, deployed at . The RailsBridge documentation project is home to a few subprojects, including the RailsBridge installfest instructions, which leads students through the various complicated setup instructions for getting Ruby, Rails, Git, etc. installed on their computer (whatever combination of computer, OS, and version they happened to bring to the workshop!), as well as the RailsBridge workshop "Suggestotron" curriculum. - rake spec - -which will run some unit tests and also do syntax validation on all pages, to make sure you didn't break anything. - -# Overview - -This is a Sinatra app, deployed at . The Railsbridge documentation project is home to a few subprojects, including the Railsbridge installfest instructions, which leads students through the various complicated setup instructions for getting Ruby, Rails, Git, etc. installed on their computer (whatever combination of computer, OS, and version they happened to bring the the workshop!), as well as the Railsbridge workshop "Suggestotron" curriculum. - -Each subproject (a "site") comprises files stored under the "sites" directory; for instance, the installfest instructions are located at ROOT/sites/installfest, while the curriculum can be found under ROOT/sites/curriculum. +Each subproject (a "site") comprises files stored under the "sites" directory; for instance, the installfest instructions are located at ROOT/sites/en/installfest, while the intro rails curriculum can be found under ROOT/sites/en/intro-to-rails. (The "en" means "English" -- see "Locales" below.) These files can be in any of these formats: -* `.step` for StepFile -* `.md` for Markdown -* `.mw` for MediaWiki -* `.deck.md` for deck.rb - -(If multiple files exist with the same base name, .step is preferred over .md, and .md over .mw.) - -Markdown is a lightweight markup language designed by John Gruber. The syntax is described at the [Daring Fireball Markdown Page](http://daringfireball.net/projects/markdown/syntax) plus [GitHub Flavored Markdown](http://github.github.com/github-flavored-markdown/) extensions. (This README is written in Markdown.) - -MediaWiki is the format of pages on the Devchix Wiki. This format is not fully supported and is provided as a temporary bridge while we move materials from the Devchix Wiki into this app. - -StepFile is a new, Ruby-based DSL for describing complex, nested instructions in clear, reusable chunks. - -[Deck.rb](https://github.com/alexch/deck.rb) converts Markdown files into an interactive in-browser HTML+JavaScript slide deck. - -#Organizer Instructions - -Slide contents that change with each workshop are contained in three files under the workshop project. The 'hello and welcome, this is when the breaks are' presentation slides are in current.deck.md. The 'this is what we will learn today' slides are in welcome.deck.md. And the 'this is what we have learned' slides are in closing.deck.md. - -To change those contents, clone this repo, make changes, and then to include your changes in the publicly available repo, send a pull request. - -##clone the repo - -On a command line cd into a directory for the installfest app to live on your machine. For example, to put it in your home directory: `cd ~` - -Then clone the repository from github: -`git clone git://github.com/railsbridge/docs.git` - -And open it in the editor of your choice. - -## changes +* `.step` for [StepFile](step_file_reference.md) +* `.md` for [Markdown](http://daringfireball.net/projects/markdown/syntax) +* `.mw` for MediaWiki (temporary) +* `.deck.md` for [deck.rb](https://github.com/alexch/deck.rb) -If you want to change the name of the hosts and sponsors in the intro slides, open current.deck.md, find and replace the name of the previous hosts and sponsors on the slide deck. To include their logos, drop an image inside the public/img folder. If their are more or fewer hosts and sponsors than in the previous workshop, create new slides! +(If multiple files exist with the same base name, `.step` is preferred over `.md`, and `.md` over `.mw`.) -Check that the current.deck.md and closing.deck.md files fit the workshop. +## Usage -## pull request - -When you're happy with how you've changed the repository, commit it. In the command line, add your changes with `git add .` and commit them locally with `git commit -m "super descriptive message "`. Push it to your own fork of the repository with`git push`. The next step, submitting a pull request is used to incorporate your changes into RailsBridge's version of the repository. Navigate to your forked version of the repository on github (and check the commits tab to see your changes!). There's a pull request button near the top of the page, and after clicking you can add a title and explanations of your changes. After submitting, it may take a while for people to review and accept your changes. Poke people. Make sure they check it out. Check out Github's help page on the pull requests. - -# StepFile Reference - -A StepFile is a [DSL](http://en.wikipedia.org/wiki/domain+specific+language) for describing a series of instructions, possibly nested inside other instructions. Technically speaking it is an *internal Ruby DSL* which means it parses as Ruby code. Nested blocks use Ruby's do..end structures, named options use Ruby's hash syntax, and string options may use any of Ruby's myriad string formats (double-quote, single-quote, here doc, percent-q, etc.) - -Here Docs are especially useful with `message`s since you can just dump in markdown between `<<-MARKDOWN` and `MARKDOWN` declarations. - -## steps - -* steps support nested content via `do` and `end` -* indents the nested block content -* inside the nested block, the step count is reset, then resumes afterwards, e.g. - - Step 1: steal underpants - Step 2: do the hokey pokey - | Step 1: put your left foot in - | Step 2: take your left foot out - | Step 3: put your left foot in again - | Step 4: shake it all about - Step 3: profit! - -`step "name"` - - * creates a new step heading - * maintains a count of steps at the same level - * prefixes name with e.g. "Step 1:" - -`link "name"` - - * links to a step whose file name is `filename` - -`next_step "name"` - - * makes a new step named "Next Step:" - * links to a step whose file name is `filename` - -`choice` - - * creates a step which is named "Choose between..." - * choice doesn't make any sense without a nested block - * nested block usually contains `option` steps - -`choice "name"` - - * same as above, but named "Choose name" - * if you want ellipses, add them yourself - -`option "name"` - - * creates a step which is named "Option 1: foo" instead "Step 1: foo" - * supports nested blocks, which reset the step count again - -`verify` + bundle install + bundle exec rake run -`verify "name"` +If the above fails (say, because `rerun` doesn't work on your system), try - * usually contains `console` and `result` notes - * kind of like a step, but doesn't increment the number count + rackup -`tip "name"` +Then open in a web browser, and verify that you can navigate the installfest slides. - * called out in a blue box - * the name is *not* markdown, but is a bold title for the tip box - * content should be inside a nested block +## Locales -## messages +To serve sites from "sites/en", use `bundle exec rake run` or a vanilla deploy. -`message "text"` +To server sites from another locale (say, "es" or Spanish)... - * makes a paragraph of text anywhere in the document - * the text parameter is passed through a Markdown converter - -`important "text"` +### Run Localized Site Locally - * like a message, but called out in a red box + $ SITE_LOCALE=es bundle exec rake run -`todo "text"` +The server listens on `0.0.0.0:9292`. - * meant as a note to future authors - * set aside from surrounding text (in brackets and italics) - * [should these be made invisible for students?] - -## special +Now you have to set up subdomain mappings for the site. If you have Pow, run: -Special elements do *not* format their text as Markdown. + $ echo 9292 > ~/.pow/railsbridge # works for any subdomain -`console "text"` +If you don't have Pow, add the following line to `/etc/hosts`: - * indicates that the student should type something in the terminal - * says "type this in the terminal:" and then puts the text in a `pre` block - * [should this be named `terminal` or `type` instead?] + 127.0.0.1 es.railsbridge.dev # works for single subdomain -`result "text"` +Now you can access `http://es.railsbridge.dev:9292` for debugging. - * indicates that the student should see some output in the terminal - * says "expected result:" and then puts the text in a `pre` block +### Running on a Server +Just make sure the server responds to a locale subdomain: `http://es.railsbridge.org` +### Temporary Testing -## erector elements +Use a `locale` or `l` parameter: `http://docs.railsbridge.org/?l=es`. -StepFile is an [Erector](http://erector.rubyforge.org)-based DSL, so if you want to insert HTML tags or other stuff, use the appropriate Erector methods, e.g. +Note that in this mode, links are not rewritten, so if they fail you will have to manually add the parameter again. - step "figure out your OS version" do - message "Mac OS has code names, including:" - table do - tr do - th "Leopard" - td "OS X 10.5" - end - tr do - th "Snow Leopard" - td "OS X 10.6" - end - end - end +## Contributing +Check out [CONTRIBUTING.md](CONTRIBUTING.md) to see how to join our [list of contributors](https://github.com/railsbridge/docs/contributors)! -# TODO (features) +## License -* big fat checkboxes -* checking the box should make the step go green -* unchecked steps should be orange -* maybe all unchecked but the next step should be gray -* JS expando-collapso doohickeys -* inlined steps, esp. verify, to share code without switching pages -* readme: examples -* `result` should take a regexp; then it will have an input field where the user can paste in what they got and it'll check it... for partial version strings -* `console` should start with a $ - * but what about multi-line commands (with e.g. config data on line 2), or multiple commands? -* put a [?] next to all the `terminal` boxes with a popup or link explaining what a terminal is -* links to other docs (the `link` directive) - * rounded boxes with arrows - * add a "back" link (or "next") to go back to the linking list - * side scrolling effect -* add a `tip` directive -* add a `details` block (nested, maybe collapsed) -* each page/step could say which OS it's for, either for information, or to actively hide other OS step -* "result" should take "or" -* "result" needs to distinguish between literal console output, and a description of what you will see -* "yay" directive ("Congratulations!") -* prerequisites -* "sudo" directive -- for danger color and popup "what is sudo?" help +The documentation (including anything under the `sites` subdir as well as some hardcoded text elsewhere) is licensed under a Creative Commons license ([CC-BY, specifically](http://creativecommons.org/licenses/by/3.0/)), which means you're welcome to share, remix, or use our content commercially. We just ask for attribution. -# TODO (technical) -* upgrade to Erector 0.9 -* test all pages during "startup" - * growl if broken -* 404 should show page with TOC and stuff -* StepFile object -* move fonts local +The code is licensed under an [MIT license](http://opensource.org/licenses/MIT), like Ruby itself. Copyright (c) 2010-2014 by RailsBridge. -# TODO (content) -* install ALL the operating systems! -* troubleshooting page -* look into installation scripts - https://github.com/joshfng/railsready - +## Other Resources - -# Credits - -some icons from https://github.com/kennethreitz/open-icons +- [StepFile Reference](step_file_reference.md) +- Workshop organizers: See http://docs.railsbridge.org/workshop for example slide decks you can use in your opening/closing presentations. diff --git a/Rakefile b/Rakefile index 1d88753a8..835c63778 100644 --- a/Rakefile +++ b/Rakefile @@ -1,13 +1,32 @@ -require 'rspec/core/rake_task' +def windows? + Rake::Win32.windows? +end + +begin + require 'rspec/core/rake_task' -task :default => :spec + task :default => :spec + + desc "Run all specs" + RSpec::Core::RakeTask.new(:spec) do |t| + t.pattern = "spec/**/*_spec.rb" + t.rspec_opts = + "--format d" + t.rspec_opts += " --color" unless windows? + # t.ruby_opts="-w" + end +rescue LoadError # swallow Heroku deploy error +end -RSpec::Core::RakeTask.new(:spec) do |t| - t.pattern = "spec/**/*_spec.rb" - t.rspec_opts = "--color --format d --fail-fast --backtrace" - # t.ruby_opts="-w" +def rerun cmd, rerun_opts = nil + if windows? + exec cmd + else + exec "rerun #{rerun_opts} -- #{cmd}" + end end +desc "run the site locally (visit http://localhost:9292)" task :run do - exec "rerun -- rackup -s thin" + rerun "rackup -s thin -p #{ENV['PORT'] || 9292}" end diff --git a/app.rb b/app.rb index 2de57674b..9070515e4 100755 --- a/app.rb +++ b/app.rb @@ -1,17 +1,13 @@ require 'sinatra' +require 'sinatra/cookies' require 'digest/md5' require 'erector' - -# require 'wrong' -# include Wrong::D - -begin - require 'rdiscount' -rescue LoadError - require 'bluecloth' - Object.send(:remove_const,:Markdown) - Markdown = BlueCloth -end +require 'i18n' +require 'i18n/backend/fallbacks' +require 'font-awesome-sass' +require 'bootstrap-sass' +require 'zip' +require 'tmpdir' here = File.expand_path File.dirname(__FILE__) lib = File.expand_path "#{here}/lib" @@ -24,25 +20,59 @@ require "raw_page" require "deck" require "deck/rack_app" +require "titleizer" +require "site" +require 'sprockets' +require 'jquery-cdn' -class InstallFest < Sinatra::Application # should this be Sinatra::Base instead? +class InstallFest < Sinatra::Application # todo: use Sinatra::Base instead, with more explicit config include Erector::Mixin + # Set available locales in Array of Strings; this is also used when + # checking availability in dynamic locale assignment, they must be strings. + AVAILABLE_LOCALES = %w[en es zh-tw].freeze + DEFAULT_SITE = "docs" + + set :assets, Sprockets::Environment.new + settings.assets.append_path "assets/stylesheets" + settings.assets.append_path "assets/javascripts" + settings.assets.append_path "public/fonts" + settings.assets.append_path Bootstrap.javascripts_path + JqueryCdn.install(settings.assets) + + if settings.environment == :development + set :cookie_options, domain: nil + end + + configure do + I18n::Backend::Simple.include(I18n::Backend::Fallbacks) + I18n.load_path = Dir[File.join(settings.root, 'locales', '**/*.yml')] + I18n.backend.load_translations + + I18n.available_locales = AVAILABLE_LOCALES + I18n.enforce_available_locales = true + I18n.default_locale = :en + end + def initialize super @here = File.expand_path(File.dirname(__FILE__)) - @default_site = "installfest" - set_downstream_app # todo: test end attr_reader :here attr_writer :default_site + # todo: test + # returns the most-specific hostname component, e.g. "foo" for "foo.example.com" + def subdomain + host.split(".").first + end + def default_site - if (host && sites.include?(site = host.split(".").first)) + if host && sites.include?(site = subdomain) site else - @default_site + DEFAULT_SITE end end @@ -54,20 +84,47 @@ def site_dir "#{sites_dir}/#{params[:site]}" end - def sites_dir= dir - @sites_dir = dir.tap { set_downstream_app } - end - - def set_downstream_app - @app = ::Deck::RackApp.public_file_server - end - def sites_dir - @sites_dir || "#{@here}/sites" + Site.sites_dir end def sites - Dir["#{sites_dir}/*"].map{|path| path.split('/').last} + Dir["#{sites_dir}/*"].map { |path| File.basename(path) } + end + + def redirect_sites + { + 'curriculum' => 'intro-to-rails', + 'intermediate-rails' => 'message-board' + } + end + + def redirect_pages + { + 'installfest/macintosh' => 'installfest/macOS' + } + end + + before do + begin + I18n.locale = dynamic_locale + rescue I18n::InvalidLocale + I18n.locale = I18n.default_locale + end + end + + after '/:site/*' do + # Any real page (starts with a site and doesn't end with an extension) + # gets saved as the 'back' for the next pageload. + if sites.include?(params[:site]) && !request.fullpath.match(/\..+\z/) + cookies[:docs_back_path] = request.fullpath + end + end + + def dynamic_locale + (params && (params[:locale] || params[:l])) || + (host && AVAILABLE_LOCALES.include?(subdomain) && subdomain) || + (ENV['SITE_LOCALE']) end def src @@ -75,13 +132,13 @@ def src end def ext - doc_path.split('.').last + $1 if doc_path.match(/\.(.*)/) end def doc_path @doc_path ||= begin base = "#{site_dir}/#{params[:name]}" - %w{step md deck.md mw}.each do |ext| + Site::DOC_TYPES.each do |ext| path = "#{base}.#{ext}" return path if File.exist?(path) end @@ -89,49 +146,64 @@ def doc_path end end - def title_for_page page_name - page_name.split('_').map do |w| - w == "osx" ? "OS X" : w.capitalize - end.join(' ') + def back_path + path_parts = cookies[:docs_back_path]&.split('/') + return unless path_parts && path_parts.length > 2 + + current_path_parts = request.fullpath.split('/') + prev_site, prev_page = path_parts[1..2] + this_site, this_page = current_path_parts[1..2] + + return unless prev_site == this_site + return if prev_page == this_page + + prev_page end def render_page begin options = { - site_name: params[:site], - doc_title: title_for_page(params[:name]), - doc_path: doc_path, - back: params[:back], - src: src, + site: Site.named(params[:site]), + page_name: params[:name], + doc_title: Titleizer.title_for_page(params[:name]), + doc_path: doc_path, + back: back_path, + src: src, + locale: I18n.locale, } case ext - - when "md" - if doc_path =~ /\.deck\.md$/ # todo: refactor - # todo: render with page nav elements too - slides = Deck::Slide.split(src) - Deck::SlideDeck.new(:slides => slides).to_pretty - else + + when "deck.md", "deck" + render_deck + + when "md" MarkdownPage.new(options).to_html - end - when "mw" - MediaWikiPage.new(options).to_html + when "mw" + MediaWikiPage.new(options).to_html - when "step" - StepPage.new(options).to_html + when "step" + StepPage.new(options).to_html - else - raise "unknown file type #{doc_path}" + else + raise "unknown file type #{doc_path}" end - rescue Errno::ENOENT => e p e + e.backtrace.each do |line| + break if line =~ /sinatra\/base.rb/ + puts "\t"+line + end halt 404 end end + def render_deck + slides = Deck::Slide.split(src) + Deck::SlideDeck.new(:slides => slides).to_pretty + end + before do expires 3600, :public end @@ -140,6 +212,22 @@ def render_page halt 404 end + get "/assets/:file.:ext" do + mime_type = { + 'js' => 'application/javascript', + 'css' => 'text/css', + 'ttf' => 'application/font-ttf', + 'woff' => 'application/font-woff' + }[params[:ext]] + content_type mime_type if mime_type + settings.assets["#{params[:file]}.#{params[:ext]}"] + end + + get '/fonts/font-awesome/:file' do + font_path = File.join(FontAwesome::Sass.gem_path, 'assets', 'fonts', 'font-awesome', params[:file]) + send_file font_path + end + get "/" do redirect "/#{default_site}/" end @@ -147,10 +235,12 @@ def render_page get "/:site/:name/src" do begin RawPage.new( - site_name: params[:site], - doc_title: doc_path.split('/').last, - doc_path: doc_path, - src: src + site: Site.named(params[:site], I18n.locale), + page_name: params[:name], + doc_title: File.basename(doc_path), + doc_path: doc_path, + src: src, + locale: I18n.locale, ).to_html rescue Errno::ENOENT => e p e @@ -158,53 +248,83 @@ def render_page end end + get "/:site/:name.zip" do + manifest_path = "#{site_dir}/#{params[:name]}.zip-manifest" + if File.exist?(manifest_path) + manifest_files = File.read(manifest_path).split("\n") + zip_path = File.join(Dir.tmpdir, "#{params[:name]}.zip") + FileUtils.rm_rf(zip_path) + Zip::File.open(zip_path, Zip::File::CREATE) do |zipfile| + manifest_files.each do |filename| + filename_without_first_directory = filename.split(File::SEPARATOR)[1..-1].join(File::SEPARATOR) + location_in_zip = File.join(params[:name], filename_without_first_directory) + location_on_disk = File.join(site_dir, filename) + zipfile.add(location_in_zip, location_on_disk) + end + end + send_file zip_path + end + end + get "/:site/:name.:ext" do - if not sites.include?(params[:site]) - forward # send it on to the downstream file server - else - send_file "#{site_dir}/#{params[:name]}.#{params[:ext]}" + if sites.include?(params[:site]) + if params[:ext] == "deck" # to show a markdown page as slides, change the ".md" to ".deck" + render_deck + else + send_file "#{site_dir}/#{params[:name]}.#{params[:ext]}" + end end end - # todo: make this work in a general way, without hardcoded 'img' - get "/:site/img/:name.:ext" do - if not sites.include?(params[:site]) - forward # send it on to the downstream file server - else - send_file "#{site_dir}/img/#{params[:name]}.#{params[:ext]}" + get "/:site/:subdir/:name.:ext" do + if sites.include?(params[:site]) + send_file "#{site_dir}/#{params[:subdir]}/#{params[:name]}.#{params[:ext]}" end end get "/:site/:name/" do # remove any extraneous slash from otherwise well-formed page URLs - redirect "#{params[:site]}/#{params[:name]}" + redirect request.fullpath.chomp('/') end get "/:site/:name" do - render_page + site_name = params[:site] + if redirect_sites[site_name] + redirect "#{redirect_sites[site_name]}/#{params[:name]}" + return + end + + page_url = "#{params[:site]}/#{params[:name]}" + if redirect_pages[page_url] + redirect redirect_pages[page_url] + else + render_page + end + end + + get "/:site/:name/:section/" do + # remove any extraneous slash from otherwise well-formed page URLs + redirect request.fullpath.chomp('/') end - get "/:file.:ext" do - # treat root URLs with dots in them like static assets and serve them - # from the downstream file server (coderay.css, jquery-1.6.1.js) - forward + get "/:site/:name/:section" do + render_page end get "/:site" do # add a slash to any URLs that contain only a site - # (otherwise paths in that site's pages would resolve - # relative to the root) - redirect "#{params[:site]}/" + # (otherwise paths in that site's pages would resolve relative to the root) + redirect "#{request.fullpath}/" end get "/:site/" do site_name = params[:site] - if sites.include? site_name + if redirect_sites[site_name] + redirect "#{redirect_sites[site_name]}/" + elsif sites.include? site_name # render the site's index page params[:name] = site_name render_page - else - forward # send it on to the downstream file server end end end diff --git a/assets/javascripts/analytics.js b/assets/javascripts/analytics.js new file mode 100644 index 000000000..dc021eb87 --- /dev/null +++ b/assets/javascripts/analytics.js @@ -0,0 +1,7 @@ +(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) +})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + +ga('create', 'UA-40977319-4', 'auto'); +ga('send', 'pageview'); diff --git a/assets/javascripts/application.js b/assets/javascripts/application.js new file mode 100644 index 000000000..18a61343f --- /dev/null +++ b/assets/javascripts/application.js @@ -0,0 +1,6 @@ +//= require 'jquery' +//= require 'doc_page' +//= require 'bootstrap/dropdown' +//= require 'analytics' +//= require 'md5' +//= require 'checkboxes' diff --git a/assets/javascripts/checkboxes.js b/assets/javascripts/checkboxes.js new file mode 100644 index 000000000..67778cfa3 --- /dev/null +++ b/assets/javascripts/checkboxes.js @@ -0,0 +1,30 @@ +$(document).ready(function () { + function saveCheckboxValue (hashcode, value) { + try { + localStorage['checkbox_' + hashcode] = value; + } catch (e) { } + } + + function fetchCheckboxValue (hashcode) { + try { + return localStorage['checkbox_' + hashcode]; + } catch (e) { + return false; + } + } + + var $checkboxes = $('.big_checkbox'); + + $checkboxes.each(function () { + var $checkbox = $(this); + var content = $checkbox.closest('.step').text(); + var hashcode = md5(content); + $checkbox.data('hashcode', hashcode); + $checkbox.prop('checked', fetchCheckboxValue(hashcode) === 'true'); + }); + + $checkboxes.on('change', function (event) { + var $checkbox = $(event.target); + saveCheckboxValue($checkbox.data('hashcode'), $checkbox.prop('checked')); + }); +}); diff --git a/assets/javascripts/doc_page.js b/assets/javascripts/doc_page.js new file mode 100644 index 000000000..bdd7412be --- /dev/null +++ b/assets/javascripts/doc_page.js @@ -0,0 +1,25 @@ +// HTML5 Shims... +document.createElement('main'); +document.createElement('footer'); + +$(document).ready(function () { + $('[data-toggle-selector]').on('click', function (event) { + event.preventDefault(); + var toToggle = $(event.target).data('toggle-selector'); + var originallyVisible = $(toToggle).hasClass('visible'); + $('.toc').removeClass('visible'); + $(toToggle).toggleClass('visible', !originallyVisible); + return false; + }); + + $('.toggler').on('click', function (e) { + e.preventDefault(); + $(this).closest('.collapsable').toggleClass('closed'); + }); + + $('.expand-all').on('click', function (e) { + e.preventDefault(); + $('.closed').removeClass('closed'); + $('.expand-all').remove(); + }); +}); diff --git a/assets/javascripts/md5.js b/assets/javascripts/md5.js new file mode 100644 index 000000000..114b8f82a --- /dev/null +++ b/assets/javascripts/md5.js @@ -0,0 +1,2 @@ +// https://github.com/blueimp/JavaScript-MD5 +!function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t),e=(n>>16)+(t>>16)+(r>>16);return e<<16|65535&r}function r(n,t){return n<>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<>>9<<4)+14]=r;var e,i,a,h,d,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t>5]|=(255&n.charCodeAt(t/8))<16&&(o=i(o,8*n.length)),r=0;16>r;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(h(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="0123456789abcdef",o="";for(r=0;r>>4&15)+e.charAt(15&t);return o}function v(n){return unescape(encodeURIComponent(n))}function m(n){return d(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this); diff --git a/assets/stylesheets/application.css b/assets/stylesheets/application.css new file mode 100644 index 000000000..c837fdd8d --- /dev/null +++ b/assets/stylesheets/application.css @@ -0,0 +1,10 @@ +//= require 'opensans' +//= require 'aleo' +//= require 'doc_page' +//= require 'flags' +//= require 'big_checkbox' +//= require 'header' +//= require 'toc' +//= require 'step' +//= require 'font_awesome' +//= require 'coderay' diff --git a/assets/stylesheets/big_checkbox.scss b/assets/stylesheets/big_checkbox.scss new file mode 100644 index 000000000..b2d9ec41d --- /dev/null +++ b/assets/stylesheets/big_checkbox.scss @@ -0,0 +1,28 @@ +$big_checkbox_size: 20px; + +input.big_checkbox[type=checkbox] { + display:none; + + label { + height: $big_checkbox_size; + width: $big_checkbox_size; + display:inline-block; + padding: 2px; + margin: 0 12px -8px 0; + background-color: white; + z-index: 2; + border: 2px solid #dadada; + + &:hover { + background-image: url(https://github.com/img/check-dim.png); + background-size: cover; + cursor: pointer; + } + } + + &:checked { + + label { + background-image: url(https://github.com/img/check.png); + background-size: cover; + } + } +} diff --git a/public/css/coderay.css b/assets/stylesheets/coderay.css similarity index 100% rename from public/css/coderay.css rename to assets/stylesheets/coderay.css diff --git a/assets/stylesheets/doc_page.scss b/assets/stylesheets/doc_page.scss new file mode 100644 index 000000000..4da6eaefe --- /dev/null +++ b/assets/stylesheets/doc_page.scss @@ -0,0 +1,145 @@ +body { + font-family: 'Open Sans', helvetica, arial, sans-serif; + padding: 0; + margin: 0; +} + +h1 { + font-size: 1.5em; + margin: 0; +} + +main, footer { + display: block; +} + +body > footer { + clear: both; + min-height: 60px; + text-align: center; + border-top: 1px solid #ccc; + font-size: 0.8em; +} + +.visible { + display: block !important; +} + +main { + position: relative; + margin-left: 300px; + margin-right: 20px; + padding-left: 15px; +} + +@media (max-width: 1100px) { + main { + margin-left: 40px; + } +} + +main h1.doc_title { + font-family: "Aleo Bold", serif; + color: #6c266f; + font-weight: 100; + font-size: 2em; + padding-top: 5px; + margin-bottom: .5em; + padding-bottom: .25em; + border-bottom: 1px solid #ccc; +} + +.doc { + max-width: 50em; + + pre { + background: #f2f2f2; + padding: .5em 1em; + font-size: 13pt; + overflow-x: auto; + } + + code { + font-size: 13pt; + background: #f2f2f2; + padding-left: 4px; + padding-right: 4px; + } + + h1 { + border-bottom: 2px solid #EEE; + } + + blockquote { + margin: 0.5em 0 .5em 20px; + } +} + +img { + margin: auto; + display: block; /* otherwise centering doesn't happen */ + max-width: 100%; +} + +.model-diagram { + margin: 0 auto; + border: 1px solid black; + border-collapse: collapse; + + th { + background-color: lightblue; + } + + td { + padding: 0 5px; + border: 1px solid black; + } +} + +.horiz-line { + position: absolute; + height: 1px; + border-bottom: 1px solid blue; +} + +.vert-line { + position: absolute; + width: 1px; + border-left: 1px solid blue; +} + +.arrow-left { + position: absolute; + width: 0; + height: 0; + border-top: 8px solid transparent; + border-bottom: 8px solid transparent; + + border-right: 8px solid blue; +} + +/* Clearfix */ +.cf:before, +.cf:after { + display: table; + line-height: 0; +} + +.cf:after { + clear: both; +} + +@media print { + main { + margin-left: 20px; + font-size: 13px; + } + + .doc pre { + font-size: 10px; + overflow-x: visible; + background-color: white; + border: 1px solid #ccc; + page-break-inside: avoid; + } +} diff --git a/assets/stylesheets/flags.scss b/assets/stylesheets/flags.scss new file mode 100644 index 000000000..200f9df35 --- /dev/null +++ b/assets/stylesheets/flags.scss @@ -0,0 +1,19 @@ +ul.flags { + min-width: 0; + position: relative; + + > li { + display: block; + + > a { + position: relative; + display: block; + padding: 10px 14px; + line-height: 20px; + } + + > img:hover { + background-color: #eee; + } + } +} diff --git a/assets/stylesheets/font_awesome.scss b/assets/stylesheets/font_awesome.scss new file mode 100644 index 000000000..6fccacddb --- /dev/null +++ b/assets/stylesheets/font_awesome.scss @@ -0,0 +1 @@ +$fa-font-path: '/fonts/font-awesome/'; diff --git a/assets/stylesheets/header.scss b/assets/stylesheets/header.scss new file mode 100644 index 000000000..f5c8a34b8 --- /dev/null +++ b/assets/stylesheets/header.scss @@ -0,0 +1,202 @@ +$railsbridge_purple: #6c266f; +$railsbridge_blue: #2493b8; +$railsbridge_darkerblue: #428bca; + +$header-height: 50px; + +nav *, nav *:before, nav *:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.dropdown { + position: relative; +} + +.dropdown-toggle:focus { + outline: 0; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + list-style: none; + background-color: #fff; + border: 1px solid #ccc; + border-color: rgba(0, 0, 0, 0.15); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-clip: padding-box; + color: #333; + border-radius: 4px; + + &:before { + content: ""; + position: absolute; + height: 0; + width: 0; + top: -19px; + right: 20px; + border: 10px solid transparent; + border-bottom-color: white; + } + + li { + a, &.current { + padding: 3px 25px; + color: #333; + } + &.current, &.category { + font-weight: bold; + } + &.category { + text-decoration: underline; + padding: 0 10px 2px 10px; + font-weight: bold; + } + + a { + display: block; + font-weight: normal; + line-height: 1.428571429; + white-space: nowrap; + } + } + + .active { + a, + a:hover, + a:focus { + color: #fff; + text-decoration: none; + background-color: $railsbridge_darkerblue; + outline: 0; + } + } +} + +.open { + > .dropdown-menu { + display: block; + } + + > a { + outline: 0; + } +} + +.nav { + padding: 0; + float: right; + margin: 0; + + .dropdown-menu { + right: 0; + left: auto; + } + + li, li a { + font-size: 18px; + } + + > li { + position: relative; + display: block; + float: left; + + > a { + height: $header-height; + line-height: $header-height; + position: relative; + display: block; + padding: 0 15px; + + img { + display: inline; + vertical-align: middle; + } + } + + > a:hover, + > a:focus { + text-decoration: none; + background-color: #eee; + } + + > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; + } + } + + li a:active, + li a:hover, + li a:focus { + background-color: $railsbridge_blue; + color: white; + } +} + +.navbar-header { + float: left; +} + +.top { + position: relative; + z-index: 1000; + min-height: $header-height; + margin-bottom: 20px; + border: 0; + background-color: $railsbridge_purple; + background-image: url("https://github.com/img/navbar_texture.gif"); + border-bottom-width: 12px; + border-bottom-color: $railsbridge_blue; + border-bottom-style: solid; + + .navbar-header { + min-height: $header-height; + } + + .title { + font-family: "Aleo Regular", serif; + font-size: 28px; + color: white; + font-weight: 100; + padding-top: 8px; + margin-left: 35px; + } + + a { + text-decoration: none; + color: white; + } +} + +@media print { + .top_links { + display: none; + } + + .top { + padding-left: 0; + margin-left: 20px; + margin-bottom: 0; + background-color: white; + } + + .top .title { + margin-left: 0; + padding: 0; + width: auto; + display: inline; + } +} diff --git a/assets/stylesheets/step.scss b/assets/stylesheets/step.scss new file mode 100644 index 000000000..031ddbd36 --- /dev/null +++ b/assets/stylesheets/step.scss @@ -0,0 +1,345 @@ +@charset "UTF-8"; + +span.prefix { + padding-left: 8px; +} + +h1 { + padding-top: 30px; + font-family: "Aleo Regular" ; +} + +.step h1 { + font-size: 1.5em; + margin-top: 10px; + padding-top: 10px; + border: 0; +} + +h1.option { + font-size: 1.3em; +} + +.step h1 span.prefix { + padding-left: 0; + padding-right: 8px; +} + +h2 { + font-size: 1.2em; +} + +img.noborder { + border: none; + display: inline; +} + +.centered { + text-align: center; +} + +.message img.icon { + float: left; + border: none; + margin-right: 1em; +} + +.message.todo { + margin: 1em; + background-color: #f5e1f0; + font-style: italic; + padding: .25em 1em; +} + +.important, .tip { + padding: .5em 1em; + margin: 1em 0; +} + +.important { + background-color: #FFEBDD; + border: solid 5px #E65D00; + border-radius: 4px; + min-height: 43px; +} + +.important > p { + margin: 0; +} + +.important > i { + color: #E65D00; + float: left; +} + +.important > i, .tip > i, .discussion > i { + padding-right: 20px; +} + +.tip { + padding: 1em; + background-color: #F7FDFF; + border: solid 5px #006E92; + border-radius: 4px; + min-height: 43px; +} + +.tip > i { + color: #006E92; + float: left; +} + +.tip > span { + font-size: 14pt; +} + +.tip span.name { + font-weight: bold; +} + +.tip span.prefix { + font-weight: bold; + color: #E77902; +} + +.discussion_box { + padding: 1em; + background-color: #FFF5AE; + border-radius: 4px; + margin: 30px 0 10px 0; +} + +.discussion_box > h4, .error_box > h4 { + text-align: center; + margin: 0; + font-family: 'Aleo Regular'; + font-weight: 100; + font-size: 1.2em; +} + +.discussion_box > hr { + border-color: black; + width: 60%; +} + +.error_box > h4 { + padding-bottom: 10px; +} + +.error_box { + padding: 1em; + color: black; + background-color: rgb(202, 255, 178); + border-radius: 4px; + min-height: 43px; +} + +.error_box > p:first-of-type { + background: white; + padding: 10px 5px; + margin: 2px; +} + +.step.next_step p.link:after { + content: " →"; +} + +div.back:before { + content: "← "; +} + +table.bordered td, +table.bordered tr { + border: 1px solid black; + padding-left: 5px; + padding-right: 5px; +} + +.requirements > h1:before, +.discussion > h1:before, +.hints > h1:before, +.tools_and_references > h1:before, +.goals > h1:before, +.steps > h1:before, +.explanation > h1:before, +.deploying > h1:before, +.overview > h1:before, +.challenge > h1:before, +.further-reading > h1:before { + content: "\00a0"; + display: inline-block; + width: 20px; + margin-right: 10px; +} + +.goals > h1:before { + background-color: #FECACD; +} + +.steps > h1:before { + background-color: #FBFFCA; +} + +.challenge > h1:before { + background-color: #FFD188; +} + +.explanation > h1:before { + background-color: #C8FFC9; +} + +.overview > h1:before { + background-color: rgb(176, 220, 250); +} + +.deploying > h1:before { + background-color: #B0DEE7; +} + +.further-reading > h1:before { + background-color: #87e5f2; +} + +.requirements > h1:before { + background-color: #93B5DA; +} + +.discussion > h1:before { + background-color: #92F59A; +} + +.hints > h1:before { + background-color: #86DFD4; +} + +.tools_and_references > h1:before { + background-color: #C8A9E0; +} + +.console > pre { + border: 4px solid #dde; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + background-color: black; + color: white; +} + +.console { + margin-top: 10px; +} + +.fuzzy-result { + margin: 20px; +} + +.fuzzy-result > pre { + margin-bottom: 0px; +} + +.fuzzy-result .fuzzy-hint { + font-size: 12px; + text-align: right; +} + +.fuzzy-result .fuzzy-lightened { + color: #aaa; +} + +.in_file > pre { + border: 4px solid #dde; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + background-color: gray; + color: white; +} + +.result > pre { + border: 4px solid #dde; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; +} + +.os-button { + width: 200px; + display: inline-block; + margin-right: 20px; +} + +.os-button img { + max-height: 150px; +} + +.os-button a { + text-align: center; +} + +.vertical-centerer { + display: table; + box-sizing: border-box; + -moz-box-sizing: border-box; + width: 100%; +} + +.vertically-centered { + display: table-cell; + vertical-align: middle; + width: 100%; +} + +.vertically-centered > *:first-child, +.vertically-centered > *:first-child > *:first-child { + margin-top: 0; +} + +.vertically-centered > *:last-child, +.vertically-centered > *:last-child > *:last-child { + margin-bottom: 0; +} + +.half-width { + display: inline-block; + width: calc(50% - 10px); + border: 1px solid grey; + border-radius: 5px; + padding: 10px; + box-sizing: border-box; + -moz-box-sizing: border-box; + vertical-align: top; + margin-right: 10px; + min-height: 145px; +} + +.half-width:last-child{ + margin-right: 0; +} + +.no-style-type { + list-style-type: none; + margin: 0; + padding: 0; +} + +.site-desc p { + margin: 0; +} + +.site-desc h2 { + margin: 25px 0 5px 0; +} + +#chunk-game { + background-color: #000000; + display: block; + margin-left: auto; + margin-right: auto; +} + diff --git a/assets/stylesheets/toc.scss b/assets/stylesheets/toc.scss new file mode 100644 index 000000000..76e47d99c --- /dev/null +++ b/assets/stylesheets/toc.scss @@ -0,0 +1,128 @@ +.toc { + display: none; + padding: 0 0 1em 12px; + margin: 0 0 1em 1em; + min-width: 200px; + position: absolute; + right: 0; + top: 55px; + + &.page-list { + position: static; + display: block; + float: left; + width: 17em; + } + + ul { + margin-left: 8px; + padding-left: 0; + list-style-type: none; + + ul { + margin-left: 3px; + padding-left: 6px; + } + } + + li a { + font-size: 11pt; + padding: 1px 0; + text-decoration: none; + + display: block; /* fill the entire containing li */ + } + + li.collapsable { + .toggler { + cursor: pointer; + display: inline-block; + text-align: center; + margin-right: 5px; + font-size: 15px; + color: black; + + .fa-plus-square-o { + display: none; + } + .fa-minus-square-o { + display: block; + } + } + + &.closed { + ul { + display: none; + } + .toggler { + .fa-plus-square-o { + display: block; + } + .fa-minus-square-o { + display: none; + } + } + } + } + + li a:hover { + cursor: pointer; + text-decoration: underline; + } + + .current { + font-weight: bold; + } + + .expand-all { + i { + margin-right: 5px; + } + } +} + +@media (max-width: 768px) { + .hidden-sm { + display: none !important; + } +} + +.show-when-small { + display: none !important; +} + +@media (max-width: 1100px) { + .show-when-small { + display: block !important; + } + + .toc, .toc.page-list { + display: none; + position: absolute; + right: 0; + top: 55px; + z-index: 2; + background-color: white; + border: 1px solid grey; + } +} + +body.no-toc { + .toc { + display: none; + } + + main { + margin-left: 40px; + } + + .show-when-small { + display: none !important; + } +} + +@media print { + .toc { + display: none; + } +} \ No newline at end of file diff --git a/config.ru b/config.ru index 5e325e6b5..9538bdb93 100644 --- a/config.ru +++ b/config.ru @@ -1,9 +1,6 @@ require 'rack/codehighlighter' require 'coderay' -# Fix Rack bug https://github.com/rack/rack/issues/301 -require './lib/rack_static_patch' - use Rack::ShowExceptions use Rack::ShowStatus use Rack::Static, :urls => ["/css", "/img"], :root => "public" @@ -13,9 +10,11 @@ use Rack::Codehighlighter, :coderay, :markdown => true, :pattern => /\A[:@]{3}\s?(\w+)\s*(\n| )/i - # require 'thin/logging' # Thin::Logging.debug = true require './app' -run InstallFest +run Rack::Cascade.new([ + Deck::RackApp.public_file_server, + InstallFest + ]) diff --git a/lib/big_checkbox.rb b/lib/big_checkbox.rb index 09d500328..3d358392e 100644 --- a/lib/big_checkbox.rb +++ b/lib/big_checkbox.rb @@ -1,38 +1,6 @@ require 'erector_scss' class BigCheckbox < Erector::Widget - # needs :size => "48px" # todo - - external :style, scss(<<-CSS) - $big_checkbox_size: 48px; - - input.big_checkbox[type=checkbox] { - display:none; - + label { - height: $big_checkbox_size; - width: $big_checkbox_size; - display:inline-block; - padding: 0 0 0 0px; - margin: 0 4px -8px 0; - background-color: white; - z-index: 2; - border: 4px solid #dadada; - @include border-radius(10px); - } - - + label:hover { - background-image: url(https://github.com/img/check-dim.png); - cursor: pointer; - } - } - - input.big_checkbox[type=checkbox]:checked { - + label { - background-image: url(https://github.com/img/check.png); - } - } - CSS - # for testing -- set the next number def self.number= checkbox_number @@checkbox_number = checkbox_number @@ -44,7 +12,7 @@ def content # and http://stackoverflow.com/questions/3772273/pure-css-checkbox-image-replacement # and https://gist.github.com/592332 checkbox_number = (@@checkbox_number ||= 0) - input.big_checkbox type:"checkbox", name: "big_checkbox_#{checkbox_number}", value:"valuable", id:"big_checkbox_#{checkbox_number}" + input.big_checkbox type: "checkbox", name: "big_checkbox_#{checkbox_number}", value: "valuable", id: "big_checkbox_#{checkbox_number}" label for: "big_checkbox_#{checkbox_number}" @@checkbox_number += 1 end diff --git a/lib/contents.rb b/lib/contents.rb index b2e99693f..9527890ec 100755 --- a/lib/contents.rb +++ b/lib/contents.rb @@ -1,45 +1,61 @@ +require 'titleizer' class Contents < Erector::Widget - attr_accessor :site_dir - needs :site_name, :site_dir => nil + attr_accessor :page_name, :site + needs :page_name, :site def initialize options super options - if options.include? :site_dir - self.site_dir = options[:site_dir] - else - here = File.expand_path File.dirname(__FILE__) - top = File.expand_path "#{here}/.." - self.site_dir = "#{top}/sites/#{@site_name}" - end + self.page_name = options[:page_name] end def site_files ext - Dir.glob("#{site_dir}/*.{#{ext}}").sort + Dir.glob(File.join(site.dir, "*.{#{ext}}")).sort + end + + def parseable_site_files + site_files("mw,md,step") + end + + def site_page_files + parseable_site_files.reject { |file| File.basename(file).start_with?('_') } + end + + def content_for filename + open(File.join(site.dir, filename)).read end def subpages_for filename + return [] if filename.match(/deck\.md/) + links = [] - content = open("#{site_dir}/#{filename}").read() + content = content_for(filename) # (markdown) links of the form: [link text](link_page) - content.scan /\[.*?\]\((.*?)\)/ do |link, _| + # but NOT images of the form ![alt text](image_link.jpg) + content.scan /[^!]\[.*?\]\((.*?)\)/ do |link, _| next if (link =~ /^http/) - next if (link =~ /(jpg|png)$/) - links.push(link) if !links.include? link + next if (link =~ %r(^.+/)) # cross-links to other sites, e.g. /installfest/editors + next if (link =~ %r(^//)) # protocol-less absolute links e.g. //google.com + links.push(link) end # (stepfiles) links of the form: link "next page" content.scan /link\s*["'](.*?)["']/ do |link, _| - links.push(link) if !links.include? link + links.push(link) + end + + # (stepfiles) links of the form: site_desc "some site" + content.scan /site_desc\s*["'](.*?)["']/ do |link, _| + links.push('/' + link) end - links + links.uniq end def next_step_for filename - content = open("#{site_dir}/#{filename}").read() + content = content_for(filename) # (stepfiles) links of the form: stepfile "next page" content.scan /next_step\s*["'](.*?)["']/ do |link, _| @@ -89,7 +105,7 @@ def find_next_page page # while another goes to next_step "that"). def hierarchy result = [] - next_page = File.basename(site_dir) + next_page = File.basename(site.dir) while next_page do this_page = next_page @@ -101,7 +117,7 @@ def hierarchy end def all_pages - site_files("mw,md,step").map { |file| File.basename(file).sub(/(\..*)$/, '') }.sort + site_page_files.map { |file| File.basename(file).sub(/(\..*)$/, '') }.sort end def orphans @@ -109,7 +125,7 @@ def orphans end def _page_links type="subpages" - site_files("mw,md,step").inject({}) do |result, filename| + site_page_files.inject({}) do |result, filename| page = File.basename(filename) page_no_ext = page.sub(/(\..*)$/, '') result[page_no_ext] = send("#{type}_for", page) @@ -125,40 +141,98 @@ def nextpages @nextpages ||= _page_links("next_step") end - def toc_link page - link_text = page.split('_').map{|s|s.capitalize}.join(' ') - path = "/#{@site_name}/" + page - li do - a(link_text, :href => path) + def _toggler + span(class: 'toggler') do + i(class: 'fa fa-plus-square-o') + i(class: 'fa fa-minus-square-o') + end + end + + def toc_link page, options = {} + link_text = Titleizer.title_for_page(page.sub(%r{^/}, '')) + path = page.start_with?('/') ? page : "/#{site.name}/" + page + collapse_classes = if options[:collapsable] + options[:collapsed] ? 'collapsable closed' : 'collapsable' + else + '' + end + li(class: collapse_classes) do + if page == page_name + _toggler if options[:collapsable] + span link_text, class: 'current' + else + a href: path do + _toggler if options[:collapsable] + text link_text + end + end yield if block_given? end end - def toc_list toc_items + # Given the nested set of arrays produced by the 'hierarchy' method above, + # annotate each item noting whether it should be collapsed + # An element is 'collapsed' if it does not contain the page currently + # being viewed. + def mark_open_and_closed hierarchy + result = {items: [], contains_current_page: false} + hierarchy.each do |toc_item| + contains_current_page = nil + if toc_item.instance_of? Array + parent = toc_item[0] + children = toc_item[1..toc_item.length] + inner_result = mark_open_and_closed(children) + contains_current_page = (self.page_name == parent) || inner_result[:contains_current_page] + result[:items] << [{title: parent, collapsed: !contains_current_page}] + inner_result[:items] + else + contains_current_page ||= (self.page_name == toc_item) + result[:items] << {title: toc_item, collapsed: !contains_current_page} + end + result[:contains_current_page] ||= contains_current_page + end + result + end + + def toc_list toc_items, level = 0 ul do toc_items.each do |toc_item| if toc_item.instance_of? Array - toc_link toc_item.first do - toc_list toc_item[1..toc_item.length] + item = toc_item.first + toc_link item[:title], collapsable: true, collapsed: item[:collapsed] do + toc_list toc_item[1..toc_item.length], level + 1 end else - toc_link toc_item + toc_link toc_item[:title] end end end end + def has_collapsables toc_items + toc_items.any? do |toc_item| + if toc_item.instance_of? Array + return true + end + end + end + def content - div class: "toc" do - h1 "Contents" - toc_list hierarchy + div id: "table_of_contents", class: "toc page-list" do + toc_list(mark_open_and_closed(hierarchy)[:items]) unless orphans.empty? - h1 "Other Pages" + h1 I18n.t("general.other_pages") ul do orphans.each { |orphan| toc_link orphan } end end + + if has_collapsables(hierarchy) + span class: "expand-all" do + i class: "fa fa-arrows-alt" + text I18n.t("general.expand_all") + end + end end end end diff --git a/lib/doc_page.rb b/lib/doc_page.rb old mode 100755 new mode 100644 index a2387d01a..9f4d9e813 --- a/lib/doc_page.rb +++ b/lib/doc_page.rb @@ -1,256 +1,130 @@ require 'erector' -require "github_flavored_markdown" require "contents" - -class InstallfestExternalRenderer < ExternalRenderer - # render " - message "Now refresh your browser. Everything should look the same." - end +CSS stands for *C*ascading *S*tyle*s*heets. It's a language for creating **rules** that +can **select** various elements on the page and change their **visual properties**. - step do - message "Write some styles that will apply to your `h1` and `p` tags." - source_code "HTML", "" - message "When you save and refresh your browser, you should see the styles you added:" - message "" +* *C*ascading - this is how the browser determines the correct style rules to apply to + each element on the page. As we'll see, CSS has many ways to match rules to elements, and + some of those ways count for more than others. The browser allows more important rules to + _cascade_ over less important ones. + +* *S*tyle*s*heets - CSS files are called 'stylesheets' because they are separate documents - their + file type is .css - that only deal with styling information. We add special tags in our HTML files to + link them to our stylesheets. + +Unlike HTML, which is concerned with the _meaning_ of the content, CSS is **presentational**: +it's concerned with how that content should look. + + +MARKDOWN + + tip do + message "## Why are HTML and CSS separate?" + message <<-MARKDOWN +There are many good reasons to keep HTML and CSS separate. A typical website will reuse styles +- like the color of links or the sizes of buttons - over and over again; by keeping them separate from the markup, we can +use the same styles across a site. + +This makes our code more efficient and easier to maintain, and keeps the site visually consistent. It also makes it easier to use +different styles for different devices, so a site can have different looks for different browsers. +MARKDOWN end + message <<-MARKDOWN +## CSS Rules Are Made of a Selector and Attributes + +The **selector** is the first part of a CSS rule - it tells the browser how to find, or *select*, the element we want to style. +On this page, we'll use `h1` and `p` as our selectors, which match the `h1` and `p` elements in our HTML. + + + +The **attributes** are the parts of the rule inside the curly braces - they tell the browser how the elements +we've selected should appear. + +Take a look at the attributes for the `h1` rule above. The part before the colon — `font-size` — is the **property**, +and the part after it — `20px` — is the **value**. This attribute tells the browser that all `h1` elements +should have a `font-size` of 20 pixels. + +The semicolons at the end of each attribute are important - if we forget them, the CSS won't work. + + +MARKDOWN end +steps do -explanation do + step do + message "Initially, we're going to add our CSS inside the `` tag, below the line with the page title." + message "A standard style tag might look like this:" + source_code :html, < +My Sample HTML page + + +HTML + message "Now save the file and refresh your browser. Everything should look the same." + end - message <<-MARKDOWN + step do + message "Add some styles that will apply to your `h1` and `p` tags." + source_code :html, < +h1 { + font-size: 20px; + border-bottom: 1px solid #cccccc; +} -## What is CSS? +p { + padding: 10px; + background-color: papayaWhip; +} + +HTML + message <<-MARKDOWN +When you save and refresh your browser, you should see the styles you added: -CSS stands for __C__ascading __S__tyle__s__heets. It's a language for creating **rules** that -can **select** various elements on the page and change their **visual properties**. + -* __C__ascading - The 'cascade' is one of the trickier elements of CSS to master, but it - basically means you can define a bunch of styling rules of different strengths, and CSS - will apply them all, resolving any conflicting rules by allowing stronger rules overwrite - (or _cascade_ over) weaker rules. +The `type` and `media` attributes inside of the `" + source_code :html, <<-HTML + + HTML message <<-MARKDOWN +In CSS you write comments by wrapping the comment in inside a `/*` (start a comment) and `*/` (finish a comment). A comment can go over several lines. + * A CSS tag looks like this when it's a link: MARKDOWN - source_code "HTML", "" - + source_code :html, <<-HTML + + HTML end +insert 'consider_deploying_to_github' + next_step "add_more_elements" diff --git a/sites/frontend/make_columns.step b/sites/frontend/make_columns.step index f1fc41c60..0d5d66720 100644 --- a/sites/frontend/make_columns.step +++ b/sites/frontend/make_columns.step @@ -1,53 +1,49 @@ goals do - message "These challenges will have you diving into the CSS side of things. Open up that layout.css file and take a look." + message "These challenges will have you diving into the CSS side of things. Open up that **layout.css** file and take a look." goal "Give your main content a border" goal "Make two columns and line them up side by side" end steps do - + step do - message "CHALLENGE: Find the div with the id 'main' in your HTML. See if you can give it a one-pixel border on its left hand side by modifying the existing stylesheet, 'layout.css'." - message "If you get hung up on what CSS property to use, see if you can look it up on [dochub](http://dochub.io/#css/)." + message "**Challenge:** Find the div with the id `main` in your HTML. See if you can give it a one-pixel border on its left hand side by modifying the existing stylesheet, **layout.css**." + message "**Hint**: If you get hung up on what CSS property to use, see if you can look it up on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference)." end - + step do - message "CHALLENGE: If you styled the #main div in the previous step by using a style rule like `#main { property: value; }`, see if you can come up with a second rule that could also apply the same style, but use a different selector." - message "Hint: You can select an element that's _inside_ of another element by listing one selector after another. The selector `.content div {}` would apply to any divs inside of an element with class `content`" - end - - step do - message "CHALLENGE: See if you can turn the page into a two-column layout. Make the 'nav' div the left column, and the 'main' div the right column." - message "Two hints: it may be helpful to give these divs a fixed width, and you may need the `float` property." + message "**Challenge:** If you styled the `#main` div in the previous step by using a style rule like `#main { property: value; }`, see if you can come up with a second rule that could also apply the same style, but use a different selector." + message "**Hint**: You can select an element that's _inside_ of another element by listing one selector after another. The selector `.content div {}` would apply to any `div`s inside of an element with class `content`." end - step do - message "Now that you made everything look great, don't forget to commit your changes and push them to github one more time." + step do + message "**Challenge:** See if you can turn the page into a two-column layout. Make the `#nav` `div` the left column, and the `#main` div the right column." + message "**Hints**: it may be helpful to give these divs a fixed width; you may need the `float` property." end message "" - end explanation do - + message <<-MARKDOWN ## More CSS! -It takes a long time to master CSS, but there are lots of great-looking resources out there -to help. One cool resource is the [CSS Zen Garden](http://www.csszengarden.com/), a site with dozens of examples of -the _same_ HTML page styled with different user-submitted stylesheets. It's a great place -to get a sense of how limitless the possibilities are with CSS. +For some great examples of the power of CSS, check out the [CSS Zen Garden](http://www.csszengarden.com/). It takes +the same HTML, but shows it with stylesheets from a bunch of different designers applied to +it. Here are two different designers' interpretations of the same HTML: + + ### CSS Reference Sites * [Sitepoint](http://reference.sitepoint.com/css/elements-structural) * [Mozilla Developer Network](https://developer.mozilla.org/en/CSS) -* [Dochub](http://dochub.io/#css/) MARKDOWN - end -next_step "jquery" +insert 'consider_deploying_to_github_again' + +next_step "basic_javascript" diff --git a/sites/frontend/resources.step b/sites/frontend/resources.step new file mode 100644 index 000000000..6e8e7b0cd --- /dev/null +++ b/sites/frontend/resources.step @@ -0,0 +1,28 @@ +message <" end - choice "your operating system" do + step "Choose your operating system" do option "Windows" do - message "On Windows, the easiest way to install git and set everything up is with the Rails Installer" - message "Stop after __Step 9: Install a Text Editor__" - a "Rails Installer Instructions", :href => "/installfest/windows", :class => 'link' + message "On Windows, the easiest way to install git and set everything up is with the Rails Installer. " + span "Head over to our " + a "Rails Installer instructions", :href => "/installfest/windows", :class => 'link' + span " (courtesy the Rails workshop Installfest instructions). Come back at the end of the page. Do not make a Heroku account!" + important "For real: come back at the bottom of the page, after step 9!" + span "After getting done with RailsInstaller, come back here and " + a 'create a GitHub account', :href => "#github-account" + span "." end option "Mac OS X" do - step "Install git" do - link "mac_os_git_installation" + step "Check if Git is already installed" do + message "If you've been to a RailsBridge before, you might have git installed. Try the following" + console "git --version" + message "If you see something like the git version below, then you can return to the mini-installfest" + fuzzy_result "git version {FUZZY}2.x.x{/FUZZY}" + message "If you see something like `git: command not found` then proceed" end - step "Install Komodo Edit" do - a "Install Komodo Edit for Mac OS X", :href => "/installfest/install_komodo_edit_for_mac" + step "Check if XCode is installed" do + message "Look in your Applications folder for an application called XCode." + message "If you see XCode, choose __Installing Homebrew__." + message "If you do __not__ see XCode, choose __Installing binary Git__." + + option_half "Installing Homebrew" do + div do + a "Instructions for Installing Homebrew", :href => "http://brew.sh/", :class => "link" + end + + message "After installing homebrew, use it to install git" + console "brew install git" + end + + option_half "Installing binary git" do + message "In another browser tab open the link below and download the latest (topmost) release of Git for OS X." + a "Git OS X Installer Downloads", :href => "http://code.google.com/p/git-osx-installer/downloads/list?can=3" + end + end + + step "Install a text editor" do + a "Download and install Atom", :href => "https://atom.io/" + message "Install Atom by double clicking the file you downloaded, then dragging the Atom icon into the Applications folder. Finish up by clicking the eject icon for Atom in your finder window." + a "or install a different editor", :href => "/installfest/editors" end end end + a :name => "github-account" step "Create a GitHub account" do message "In your web browser open ." message "GitHub is is a service for hosting and sharing source code with others. They also host static web pages for free! We'll be using them to put our web page up later today." diff --git a/sites/frontend/zip_content/.gitignore b/sites/frontend/zip_content/.gitignore new file mode 100644 index 000000000..e8dcdf163 --- /dev/null +++ b/sites/frontend/zip_content/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +.idea +Thumbs.db +.rvmrc +.sass-cache diff --git a/sites/frontend/zip_content/index.html b/sites/frontend/zip_content/index.html new file mode 100755 index 000000000..7c6963013 --- /dev/null +++ b/sites/frontend/zip_content/index.html @@ -0,0 +1,77 @@ + + + + About Me + + + + + + + +
+ + + + +
+

Projects

+

Describe the kinds of projects you like to work on and give a brief overview of how you accomplished them...

+ +

Project #1 Title

+

Project #1 Description...

+ +

Project #2 Title

+

Project #2 Description...

+ +

Hobbies

+ +

What else do you do in your spare time?

+ +

Item #1 Title

+

Use this paragraph to explain Item #1

+
    +
  • Cool thing I did
  • +
  • Another cool thing I did
  • +
  • Final cool thing I did
  • +
+ +

Item #1 Title

+

Use this paragraph to explain Item #1

+
    +
  • Cool thing I did
  • +
  • Another cool thing I did
  • +
  • Final cool thing I did
  • +
+ +

Fun Facts

+

Subhead

+

Use this list to tell us some fun things about yourself.

+
    +
  • Cool thing I did
  • +
  • Another cool thing I did
  • +
  • Final cool thing I did
  • +
+ + +
+
+ + + + diff --git a/sites/frontend/zip_content/resources/javascript.js b/sites/frontend/zip_content/resources/javascript.js new file mode 100755 index 000000000..a96422740 --- /dev/null +++ b/sites/frontend/zip_content/resources/javascript.js @@ -0,0 +1 @@ +// Your Javascript will go here! \ No newline at end of file diff --git a/sites/frontend/zip_content/resources/layout.css b/sites/frontend/zip_content/resources/layout.css new file mode 100755 index 000000000..3ffabc67f --- /dev/null +++ b/sites/frontend/zip_content/resources/layout.css @@ -0,0 +1,58 @@ +/* + * Add your CSS styles here + * + */ + +body { + font-family: Helvetica, Arial, sans-serif; + background: #f4f4f4; +} + +h1, h2, h3 { + color: #222; +} + +p { + line-height: 24px; + color: #555; +} + +a { + color: green; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +.content { + width: 940px; + margin: 0 auto; +} + +#header { + border-bottom: 1px solid #999; +} + +#nav { + margin: 0 20px 0 0; + /* CHALLENGE 3: how could you get this to float to the left side of the screen? */ +} + +#main { + margin: 0; + /* CHALLENGE 3: how could you get this to float to the left side of the screen? */ + /* CHALLENGE 2: how could you give this element a one-pixel border down its left side? */ +} + +#copyright { + margin-top: 30px; + margin-bottom: 30px; +} + + +/* Ignore this for now, it is special code needed to fix the layout after using the "float" rules */ +.clearfix:before, .clearfix:after { content:""; display:table; } +.clearfix:after { clear:both; } +.clearfix { zoom:1; } \ No newline at end of file diff --git a/sites/frontend/zip_content/resources/picture.jpg b/sites/frontend/zip_content/resources/picture.jpg new file mode 100755 index 000000000..ded62653d Binary files /dev/null and b/sites/frontend/zip_content/resources/picture.jpg differ diff --git a/sites/installfest/DeveloperToolsPage.png b/sites/installfest/DeveloperToolsPage.png deleted file mode 100644 index 39946c91f..000000000 Binary files a/sites/installfest/DeveloperToolsPage.png and /dev/null differ diff --git a/sites/installfest/Komodo-Edit-5.png b/sites/installfest/Komodo-Edit-5.png deleted file mode 100644 index a2b4b40b1..000000000 Binary files a/sites/installfest/Komodo-Edit-5.png and /dev/null differ diff --git a/sites/installfest/MacOSXSnowLeopard.png b/sites/installfest/MacOSXSnowLeopard.png deleted file mode 100644 index 6021a8fc0..000000000 Binary files a/sites/installfest/MacOSXSnowLeopard.png and /dev/null differ diff --git a/sites/installfest/WinXPRailsInstaller.jpg b/sites/installfest/WinXPRailsInstaller.jpg deleted file mode 100644 index 9697b2a4d..000000000 Binary files a/sites/installfest/WinXPRailsInstaller.jpg and /dev/null differ diff --git a/sites/installfest/_command-line-glossary.md b/sites/installfest/_command-line-glossary.md new file mode 100644 index 000000000..0084d032f --- /dev/null +++ b/sites/installfest/_command-line-glossary.md @@ -0,0 +1,37 @@ +**~** Called 'tilde'. On OS X or Linux, this is a shortcut to the **home directory** for your user. + +**\** When you use an 'escape sequence' in front of a character, the normal interpretation of that character is not applied. + +**=** Assignment to make whatever follows, to be treated as true (_Name="Veronica"_). + +**==** Boolean, a data type with only 2 possible values: True or False (_Is name=="Veronica"? True_). + +**cd (or cd ~)** Change into your **home directory**. + +**cd** _directory_ **(or cd ..)** Change into the parent directory of your current directory. + +**cd ../../** Go up 2 levels / multiple levels. + +**cd** _foo_ Change into the directory named _foo_. + +**cp** _original.rb copy.rb_ Makes a copy of the _original.rb_ file. + +**ls** List the contents of your current directory. + +**ls** _directory_ Shows all contents (files and folders) of the directory. + +**pwd** Shows the full path of the directory you are currently in (e.g. _/home/heidi/tehcodez/Railsbridge_). + +**-h (or --help)** Can be run with all commands to list more helpful information. + +**git branch** Shows you the branch that you're currently in. + +**git status** Shows you any pending changes that you've created since your last commit. + +**git add -A** Will add all your changes to your next commit. + +**git add** _file1.md file2.md file3.md_ Will add only the files you specify to your next commit. + +**git commit -m** _"some useful message for your future self"_ Commits all your changes with your descriptive message to git. + +**git push origin** remote\_branch\_name This pushes the code in your current branch to the remote\_branch\_name branch on the remote repo named 'origin'. diff --git a/sites/installfest/_general-glossary.md b/sites/installfest/_general-glossary.md new file mode 100644 index 000000000..e96914968 --- /dev/null +++ b/sites/installfest/_general-glossary.md @@ -0,0 +1,45 @@ +**Classes and Instances:** Types and objects. If we have a class (type) called _Car_, then _Peugeot 406_ and _Fiat Ritmo_ will be instances (objects) of this class. There may also be other instances unique to _Peugeot 406_, differentiated by, for example, their _Model number_. + +**Command Line Interface (or Console / Terminal):** Allows you to interact with your computer (add, delete, modify files and much more) solely by typing instructions (commands). + +**Function:** Code that can be re-used; if a formula has to change, you only need to change it in one place. + +**Git:** A type of **Version Control Software (VCS)**. + +**GitHub:** A site that hosts git repositories. GitHub also adds a number of tools that aid interaction between developers collaborating on software. + +**Grouping:** Indents and whitespaces are used for grouping code together and simplify readability. Ruby also uses do … end to group. + +**Heroku:** A site that allows you to host your Ruby on Rails applications. Heroku also supports other languages and frameworks. + +**Home directory:** On a OS X or Linux computer, this is where the files and settings belonging to your user are stored. + +**Loop:** While _something is true, do this_. When _something becomes not true_ then break the loop / stop. + +**Markdown:** A lightweight markup language that allows you to write using an easy-to-read, easy-to-write plain text format, and then convert it to structurally valid XHTML (or HTML). + +**Rake:** A Ruby application that can be used to perform a number of custom tasks. It is often used to carry out maintenance or ad-hoc jobs. + +**Repository (or Repo):** A grouping of files within **Version Control Software**. When using **Git**, you will need to have access to a particular repository before you can start making changes to the source code. + +**Ruby:** A programming language. + +**Ruby on Rails:** A web framework written in **Ruby**. It has been designed in such a way as to make web development as fast and as easy as possible. + +**RubyGem:** A package manager for the Ruby programming language that provides a standard format for distributing Ruby programs and libraries (in a self-contained format called a "gem"), a tool designed to easily manage the installation of gems and a server for distributing them. + +**RVM:** The Ruby Version Manager. An application that makes it easy to have multiple versions of Ruby on your computer at once. + +**Script:** A term often used to describe a text file containing a series of instructions, written in Ruby, that will be used to produce the output visible in a browser. + +**Shell:** The software that interprets your commandline instructions. Often used as a synonym for the **Command Line Interface**. + +**SSH:** Secure Shell. A way to securely connect to a remote computer. + +**String:** Text only, typed between inverted commas. + +**Text editor:** A program that allows you to create and modify the text files that will be used by **Ruby on Rails** to run your command. Some text editors are optimised for software development and provide tools that increase productivity. + +**Variable:** A storage location and an associated symbolic name (an identifier) which contains some known or unknown quantity or information (a value). The variable name is the usual way to reference the stored value; this separation of name and content allows the name to be used independently of the exact information it represents. + +**Version Control Software:** Software that tracks changes to your source code and also allows you to undo or combine changes. diff --git a/sites/installfest/_install_atom_for_mac.step b/sites/installfest/_install_atom_for_mac.step new file mode 100644 index 000000000..983f2a84d --- /dev/null +++ b/sites/installfest/_install_atom_for_mac.step @@ -0,0 +1,21 @@ +message "We'll be using the Atom text editor during the workshop, though you are free to use a different editor if you prefer. It must be a plain-text editor, such as vi or TextMate." + +important <<-MARKDOWN + Microsoft Word and other word processing programs, including TextEdit and Notepad, will not work. + + When in doubt, use Atom. + MARKDOWN + +step "Download Atom" do + message "Download the [Atom installer](https://atom.io/download/mac)." +end + +step 'Find the downloaded file in Finder' do + message "If you weren't asked where to save it, it's probably in the Downloads folder." +end + +step "Extract Atom and move it to your Applications folder." do + message "Double click the Atom `.zip` file to extract it. Drag Atom into your Applications folder." + + img src: 'img/install_atom_mac.gif', alt: 'Animation of extracting the Atom zip file and moving the contents to the Applications folder' +end diff --git a/sites/installfest/_install_homebrew.step b/sites/installfest/_install_homebrew.step new file mode 100644 index 000000000..094167a18 --- /dev/null +++ b/sites/installfest/_install_homebrew.step @@ -0,0 +1,14 @@ + +console %q{ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"} + +message "You may have to press 'ENTER' when prompted and type in your password." + +important "If that doesn't work, visit and follow the instructions there." + +verify do + console "brew -v" + fuzzy_result <<-TEXT + Homebrew 1{FUZZY}.1.13-19-g55c02ae77 + Homebrew/homebrew-core (git revision 4075; last commit 2017-04-18){/FUZZY} + TEXT +end diff --git a/sites/installfest/_install_ruby.step b/sites/installfest/_install_ruby.step new file mode 100644 index 000000000..b3e0a71fa --- /dev/null +++ b/sites/installfest/_install_ruby.step @@ -0,0 +1,29 @@ +console <<-BASH + rvm install #{version_string(:ruby_short)} +BASH + +message "This downloads and compiles Ruby, which takes a while." +console "rvm use #{version_string(:ruby_short)}" +console "rvm --default use #{version_string(:ruby_short)}" + +verify do + console "ruby -v" + fuzzy_result "ruby #{version_string(:ruby_short)}{FUZZY}.0p0 (2014-12-25 revision 49005) [x86_64-darwin13]{/FUZZY}" +end + +div do + h1 "Troubleshooting" + important "If `rvm install #{version_string(:ruby_short)}` says `autoreconf was not found in the PATH`" do + div do + option_half "Mac OS" do + console "brew install automake" + end + + option_half "Ubuntu Linux" do + console "sudo apt-get install automake" + end + end + + message "Once that completes, retry `rvm install #{version_string(:ruby_short)}`" + end +end diff --git a/sites/installfest/_install_rvm.step b/sites/installfest/_install_rvm.step new file mode 100644 index 000000000..17d857cca --- /dev/null +++ b/sites/installfest/_install_rvm.step @@ -0,0 +1,38 @@ +message <<-MARKDOWN +RVM stands for Ruby Version Manager and is the easiest way to install and manage Ruby. The [official RVM install instructions](https://rvm.io/rvm/install) are available here, but this should work for you: +MARKDOWN + +step "Install RVM" do + console "curl -L get.rvm.io | bash -s stable" + message "This will download and install RVM." + + important do + message <<-EOT + You might see an error like "**GPG signature verification failed**" + + If so, run the suggested command to add RVM's GPG keys: + + `gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3`" + + (the command may start with `gpg` or `gpg2` -- use whichever was provided in the RVM error message) + + Afterward, try installing RVM again: + + `curl -L get.rvm.io | bash -s stable` + EOT + end +end + +step "Configure your shell" do + + message "Every time you open a new terminal window, rvm will be active inside it. Close your terminal window and open a new one." + + verify do + console "type rvm | head -1" + result "rvm is a function" + + console "rvm -v" + fuzzy_result "rvm 1{FUZZY}.x.x (stable) by Wayne E. Seguin (wayneeseguin@gmail.com), Michal Papis [https://rvm.io/]{/FUZZY}" + end + +end diff --git a/sites/installfest/_install_textmate.step b/sites/installfest/_install_textmate.step new file mode 100644 index 000000000..22f586fd4 --- /dev/null +++ b/sites/installfest/_install_textmate.step @@ -0,0 +1,22 @@ +message <<-MARKDOWN + +Atom isn't compatible with versions of OS X lower than 10.6 so we'll use Textmate (which is still widely used by Rails developers), you are free to use a different editor if you prefer. It must be a plain-text editor. + +MARKDOWN + +important "Microsoft Word and other word processing programs, including TextEdit and Notepad, will not work." + +step "Download Textmate 1.5.11" do + message "Download the [Textmate installer](http://archive.textmate.org/TextMate_1.5.11_r1635.zip)." +end + +step 'Select "Open with Archive Utility" in the file save dialog' do + message "This should be the default." + + message "It will extract from the zip archive the Textmate application inside of your Downloads folder." + +end + +step "Drag the Textmate application into your Applications folder." do + message "Drag the Textmate application into your Applications folder." +end diff --git a/sites/installfest/_ruby-and-rails-glossary.md b/sites/installfest/_ruby-and-rails-glossary.md new file mode 100644 index 000000000..911ac0b55 --- /dev/null +++ b/sites/installfest/_ruby-and-rails-glossary.md @@ -0,0 +1,19 @@ +**puts** _something_ Prints its argument to the console. Can be used in Rails apps to print something in the console where the server is running. + +**rails new** _NameApp_ Creates a new Rails application with the entire Rails directory structure to run your application. + +**rails server (or rails s)** Launches a web server named Puma that you will use any time you want to access your application through a web browser. + +**rails generate (or rails g)** Uses templates to create a bunch of directories and files in your application. + +**rails generate scaffold** Creates a full set of model, database migration for that model, controller to manipulate it, views to view and manipulate the data and a test suite for each of the above. + +**rake** Rake is ‘Ruby Make', used to build up a list of tasks. + +**rails console (or rails c)** Lets you interact with your Rails application from the command line, useful for testing out quick ideas with code and changing data server-side without touching the website. + +**rails console --sandbox** If you wish to test out some code without changing any data. + +**rails dbconsole (or rails db)** Used to figure out which database you're using and drops you into whichever command line interface you would use with. It supports MySQL, PostgreSQL, SQLite and SQLite3. + +**rails destroy (or rails d)** Does the opposite of generate. It will figure out what generate did and undo it. diff --git a/sites/installfest/_switch_to_home_directory.step b/sites/installfest/_switch_to_home_directory.step new file mode 100644 index 000000000..93fcaf006 --- /dev/null +++ b/sites/installfest/_switch_to_home_directory.step @@ -0,0 +1,14 @@ +message "`cd` stands for change directory." + +message "If you are still in `irb`, type `quit` to exit." + +div do + option_half "Windows" do + console "cd c:\\Sites" + message "`cd c:\\Sites` sets our Sites directory to our current directory." + end + option_half "Mac or Linux" do + console "cd ~" + message "`cd ~` sets our home directory to our current directory." + end +end diff --git a/sites/installfest/appstore.jpg b/sites/installfest/appstore.jpg deleted file mode 100644 index be6e6bb34..000000000 Binary files a/sites/installfest/appstore.jpg and /dev/null differ diff --git a/sites/installfest/choose_your_operating_system.step b/sites/installfest/choose_your_operating_system.step index f01af6775..f42e8cbea 100644 --- a/sites/installfest/choose_your_operating_system.step +++ b/sites/installfest/choose_your_operating_system.step @@ -1,10 +1,20 @@ -div do -message <<-MARKDOWN +h2 "What's your operating system?" -## Click here if you are using a [Macintosh](macintosh) - -## Click here if you are using a [PC running Windows](windows) - -## Click here if you are using a [PC running Linux](linux) - MARKDOWN +div(class: "os-button") do + simple_link "macOS" do + img src: "/img/macos.png" + h3 "macOS" + end +end +div(class: "os-button") do + simple_link "windows" do + img src: "/img/windows.png" + h3 "Windows" + end +end +div(class: "os-button") do + simple_link "linux" do + img src: "/img/tux-trans.png" + h3 "Linux" + end end diff --git a/sites/installfest/clean_up.step b/sites/installfest/clean_up.step index 64ab57dc1..989f97c15 100644 --- a/sites/installfest/clean_up.step +++ b/sites/installfest/clean_up.step @@ -1,21 +1,22 @@ message <<-MARKDOWN -Ok, there is one more step. You won't be using the test application in the workshop; we just created it to make sure everything was working. **You should delete it now to reduce confusion during the workshop.** Don't worry about losing information - you'll repeat all the steps tomorrow, but more slowly so you can better understand what's going on under the hood. +Ok, there is one more step. You won't be using the test applications in the workshop; we just created them to make sure everything was working. **You should delete them now to reduce confusion during the workshop.** Don't worry about losing information - you'll repeat all the steps tomorrow, but more slowly so you can better understand what's going on under the hood. MARKDOWN -step "Delete the app from Heroku" do - message "Go to and then click on your app name. Scroll to the bottom of the page and click Destroy App." +step "Delete the apps from Heroku" do + message "Go to , login if needed, and then click on your first (lyrical) app name. Click on settings. Scroll to the bottom of the page and click Delete App. Enter app name, confirm, and then repeat for the second app." end -step "Delete test_app from your computer" do +step "Delete the test_app from your computer" do option "Windows" do - message "Right click on the test_app folder (inside C:\\Sites) and choose Delete." + message "From the Explorer, right click on the test_app folder (inside C:\\Sites\\railsbridge) and choose Delete." + message "**- or -**" + console_with_message "From the command prompt, run the following command:", "rmdir /s /q c:\\Sites\\railsbridge\\test_app" end option "Mac" do message "Open your home directory and drag the test_app folder to the trash." end option "Linux" do - console "rm -rf ~/test_app" + console "rm -rf ~/railsbridge/test_app" end end - diff --git a/sites/installfest/configure_git.step b/sites/installfest/configure_git.step index 5dde85a9f..28c749077 100644 --- a/sites/installfest/configure_git.step +++ b/sites/installfest/configure_git.step @@ -1,9 +1,9 @@ message "(If you used RailsInstaller on Windows then you should already have `user.name` and `user.email` configured.)" -console <<-BASH -git config --global user.name "Your Actual Name" -git config --global user.email "Your Actual Email" -BASH +message "In the next commands, don't copy-paste the whole thing. Use the name and email address you usually use. Don't use a username (like your github username)." + +console 'git config --global user.name "Your Actual Name, In Quotes"' +console 'git config --global user.email "Your Actual Email, In Quotes"' important "Use the same email address for heroku, git, github, and ssh." @@ -16,14 +16,12 @@ end tip "Colored output in Git" do console <<-BASH -git config --global color.diff auto -git config --global color.status auto -git config --global color.branch auto +git config --global color.ui auto BASH important do - message "If you are using Windows, you may need to install AnsiCon to see colors (otherwise you'll see gibberish)." - link "windows_colors" + message "If you are using Windows, the output of Git will not have colors." end end +next_step "create_an_ssh_key" diff --git a/sites/installfest/configure_the_windows_terminal.step b/sites/installfest/configure_the_windows_terminal.step deleted file mode 100644 index c721380b0..000000000 --- a/sites/installfest/configure_the_windows_terminal.step +++ /dev/null @@ -1,14 +0,0 @@ -step "Configure the Windows Terminal" do - message "Right-click on the menu bar and select **Properties**" - - message "Under the *Options* tab, check the box for **QuickEdit Mode**. This will let you visually cut and paste." - - message "Under the *Font* tab, select Lucida Console from the font chooser box. This lets you view non-ascii characters." - - message "In the *Layout* tab, adjust *Window Size* so the window is about half as wide as your screen, and is as tall as possible without covering the task bar (this may require some trial and error)." - - message "In the *Layout* tab, increase **Screen Buffer Size** Height to 1000." - - message "Click OK. In the dialog that comes up, select **Modify shortcut that started this window.**" -end - diff --git a/sites/installfest/create_a_github_account.step b/sites/installfest/create_a_github_account.step new file mode 100644 index 000000000..8fd22db10 --- /dev/null +++ b/sites/installfest/create_a_github_account.step @@ -0,0 +1,79 @@ +message "You can use GitHub to store your code online and access it from anywhere. This step isn't necessary to deploy your apps to the web though." + +step "Visit the GitHub web site" do + url "https://github.com/" +end + +step "Create an account" do + + message "Click the green **Sign Up for GitHub** button (it's about halfway down the page)" + message "Enter a username, your email address and a password." + important "Use the same email address for heroku, git, github, and ssh. Be sure to use an email account you can log into immediately." + +end + +step "Select a plan" do + + message "GitHub provides several levels of account plans but you can create unlimited public repositories with a free plan so for now, you can select that one. Hit **Finish sign up**." + +end + + +step "Set up SSH authentication with GitHub" do + + message "Adding an SSH key to GitHub allows you to pull and push data without typing in your password all the time. First we'll copy the key we generated in the **Create an SSH Key** step and add it to your GitHub account. We'll use a terminal command to do that, so that we don't add any newlines or whitespace that could cause an error." + + h2 "Mac users" + console "pbcopy < ~/.ssh/id_rsa.pub" + + h2 "Linux users" + console <<-BASH + sudo apt-get install xclip + xclip -sel clip < ~/.ssh/id_rsa.pub + BASH + + message "`sudo apt-get install xclip` installs a tiny application, xclip, that lets us copy the contents of a file without opening it. Mac and Windows users have similar things already installed (`pbcopy` and `clip`)." + + h2 "Windows users" + console "clip < \"%userprofile%\\.ssh\\id_rsa.pub\"" + + message "Now that you have copied the key to your clipboard, you can add it to the GitHub account you created earlier." + + h1 "Add your SSH key to GitHub" + + message "Navigate to github.com and make sure you are logged in. On any page on the GitHub site, click your profile photo in the top right corner to the right of the plus sign. In the dropdown menu, click **Settings** to go to the account settings page." + + message "On the account settings page, select **SSH and GPG keys** from the column on the left." + + message "At the top right of this page, click the button that says **New SSH key**. In the title field, give a name for your SSH key, you might call it **My PC** or **Personal MacBook**. In the key field, paste the key you copied." + + message "Click **Add SSH key**" + + message "Confirm the action by providing your GitHub Password" +end + +step "Confirm SSH Authentication" do + + message "Confirm that you have successfully set up SSH Authentication for GitHub" + + message "Windows users cannot perform this step." + + console "ssh -T git@github.com" + + result <<-OUTPUT + The authenticity of host 'github.com (207.97.227.239)' can't be established. + RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48. + Are you sure you want to continue connecting (yes/no)? + OUTPUT + + message "You might see a message like this to confirm your key, if it's your correct key continue connecting by typing yes" + + result <<-OUTPUT + Hi username! You've successfully authenticated, but GitHub does not + provide shell access. + OUTPUT + + message "If the username is your username, you've successfully authenticated GitHub with SSH!" + + next_step 'create_a_rails_app' +end diff --git a/sites/installfest/create_a_heroku_account.step b/sites/installfest/create_a_heroku_account.step index 91672e8fc..1f4d06294 100644 --- a/sites/installfest/create_a_heroku_account.step +++ b/sites/installfest/create_a_heroku_account.step @@ -1,11 +1,13 @@ +message "We'll be using Heroku to put our apps online because it's simple and free for the tier we'll be needing. If you know you already have a Heroku account, you can jump to step 4 on this page." + step "Visit the Heroku web site" do url "http://heroku.com/" end step "Create an account" do - message "click the big **Sign Up** button (it's about halfway down the page)" + message "Click the big **Sign Up** button (it's about halfway down the page)" message "Enter your email address." - important "Use the same email address for heroku, git, github, and ssh." + important "Use the same email address for heroku, git, github, and ssh. Be sure to use an email account you can log into immediately." end step "Activate your account" do @@ -14,30 +16,32 @@ step "Activate your account" do end -step "Install the heroku gem" do +step "Install the Heroku CLI" do - option "if you are using rvm" do - console "rvm @global gem install heroku" - end - - option "otherwise" do - console "gem install heroku" - end + message "Visit https://devcenter.heroku.com/articles/heroku-cli and follow the instructions relevant to your operating system. Afterwards:" verify do - console "heroku -v" - result "2.26.7" + console "heroku version" + fuzzy_result <<-TEXT + {FUZZY}heroku-toolbelt/3.30.6 (x86_64-darwin10.8.0) ruby/1.9.3{/FUZZY} + heroku{FUZZY}/7.6.0 darwin-x64 node-v10.6.0{/FUZZY} + {FUZZY}You have no installed plugins.{/FUZZY} + TEXT end + message "The output could be different, depending on whether this is the first time you've installed the Heroku CLI, but it should always have `heroku` in it." + message "If `heroku version` doesn't work, try the command again in a new terminal window." end step "Add your SSH key to your Heroku account" do - link "create_an_ssh_key" console "heroku keys:add" + + message "Enter your Heroku email and password, if prompted, and accept the defaults." end -tip "If you have further Heroku issues" do +section 'Optional Step: Create a GitHub account' do + message "Since by now you should have both Git and an SSH key, you can optionally [Create a GitHub account](create_a_github_account) to share code with your friends." - message "try following [these directions](http://support.heroku.com/forums/43117/entries/32505) to install (or re-install) the `heroku` client." + message "You don't need a GitHub account to complete the InstallFest, or the Intro To Rails, Job Board, or Message Board courses." end -next_step "create_and_deploy_a_rails_app" \ No newline at end of file +next_step "create_a_rails_app" diff --git a/sites/installfest/create_a_rails_app.step b/sites/installfest/create_a_rails_app.step new file mode 100755 index 000000000..f5a2f370f --- /dev/null +++ b/sites/installfest/create_a_rails_app.step @@ -0,0 +1,105 @@ +tip do + console_with_message "From here on, this guide assumes you have Rails 5.0.x. To check your Rails version, type this in the terminal:", "rails -v" + fuzzy_result "Rails 5.0{FUZZY}.x{/FUZZY}" + message "If your computer reports a Rails version less than 5.0, ask a TA help get you back on track." +end + +step "Change to your home directory" do + insert 'switch_to_home_directory' +end + +step "Create a railsbridge directory" do + console "mkdir railsbridge" + message "`mkdir` stands for make directory (folder)." + message "We've made a folder called `railsbridge`." +end + +step "Change to your new railsbridge directory" do + console "cd railsbridge" +end + +step "Create a new Rails app" do + + console "rails new test_app" + + message "The command's output is voluminous, and will take some time to complete, with a long pause in the middle, after all the 'create...' statements ending in 'bundle install'. When it fully completes, it will return you to your home prompt. Look for the 'Bundle complete!' message just above." + + console "cd test_app" + console "rails server" + + tip "In OS X, you may need to let Ruby accept incoming network connections through your firewall. Select 'allow' in the pop up." + + tip "In Windows, you may need to let Ruby and Rails communicate through your firewall. Say yes to the pop up." + + tip "Shortcut: Just type 'rails s'" do + message <<-MARKDOWN + Throughout your Rails programming career you're going to type `rails server` a + lot. Simply typing `rails s` is the same as `rails server`. + MARKDOWN + end + + message <<-MARKDOWN + The first command should produce no output. + If `rails server` starts up with no errors, you're golden! It'll look something like this: + MARKDOWN + + fuzzy_result <<-TEXT + => Booting Puma + => Rails 5.0{FUZZY}.0{/FUZZY} application starting in development on http://localhost:3000 + => Run `rails server -h` for more startup options + Puma starting in single mode... + * Version {FUZZY}3.4.0{/FUZZY} (ruby 2.{FUZZY}2.2-p95{/FUZZY}), codename: {FUZZY}Owl Bowl Brawl{/FUZZY} + * Min threads: 5, max threads: 5 + * Environment: development + * Listening on tcp://localhost:3000 + Use Ctrl-C to stop + TEXT + + message "If it does, congratulations! You've successfully installed Ruby AND Rails and started your server." + + tip "If it doesn't work, ask a TA for help." + message "* In your browser, go to " + img src: "img/successful_rails_install.png", alt: "Screenshot of the browser on localhost 3000 showing the rails intro page" + message <<-MARKDOWN + + * Back in the Terminal window where you ran rails server, type **Control-C** (don't type this into the console, but hold the Control and C keys at the same time) to kill(stop) the server. Windows will ask "Terminate batch job (Y/N)?". Type "Y". + MARKDOWN + + important "On Windows, sometimes Control-C doesn't work. In that case, look for the key called 'Break' or 'Pause' and press Control-Break, then answer Y at the prompt. If there is no Pause/Break key on your keyboard, you can run `ruby script/rails server` instead of `rails server` which should allow Control-C to stop the server." +end + +step "Generate a database model" do + tip "If your prompt doesn't already show that you are (still) in the test_app folder" do + console "cd test_app" + end + + console <<-BASH + rails generate scaffold drink name:string temperature:integer + BASH + console <<-BASH + rails db:migrate + BASH + console <<-BASH + rails server + BASH + + message <<-MARKDOWN + **Note:** the above are three separate commands. Type each line into the terminal separately, not as one single command. + + Wait until your console shows that the Puma server has started (just like before). Then, in the browser, visit + + 1. Click on "New drink" + 2. Enter Cappuccino for the name + 3. Enter 135 for the temperature. + 4. Click on "Create Drink". + + (The window where you ran `rails server` will display debugging information as you do this.) + + You should see: ![Screenshot of the drink detail page](img/get_a_sticker_you_should_see.png) + + In your terminal, Hold Control and hit C (or on Windows, Control-Break, Y) to stop the rails server. + MARKDOWN +end + +next_step "deploy_a_rails_app" + diff --git a/sites/installfest/create_an_ssh_key.step b/sites/installfest/create_an_ssh_key.step index 0387ceb4c..431cb02d3 100644 --- a/sites/installfest/create_an_ssh_key.step +++ b/sites/installfest/create_an_ssh_key.step @@ -1,45 +1,88 @@ message "An SSH key uniquely identifies you (and your computer) when your computer is communicating with other computers. Think of an SSH key as a fancy password." -message "You'll need one of these to create your Heroku and Github accounts." +message "You'll need one of these to create your Heroku and GitHub accounts." option "Did you use RailsInstaller on Windows?" do message "Congratulations, you already have an ssh key!" + link_without_toc 'create_a_heroku_account' end -option "otherwise" do -important "Use the same email address for heroku, git, github, and ssh." +option "Do you have a preexisting SSH key for some other reason?" do + message "Maybe you went to a previous RailsBridge workshop, or generated an SSH key to push some code to GitHub? You can check with the following command:" + console "ls ~/.ssh/id_rsa" + message "If you see the message **No such file or directory**, you don't have an SSH key yet." + option_half "'No such file or directory'" do + message "Go on to Generate an SSH key" + end + option_half "'/Users/[something]/.ssh/id_rsa'" do + link_without_toc 'create_a_heroku_account' + end +end -important "**REPLACE** `student@example.com` with your **actual email address** below." +a name: "generate-key" +option "Generate an SSH key" do + important "Use the same email address for heroku, git, github, and ssh." -console "ssh-keygen -C student@example.com -t rsa" + important "**REPLACE** `student@example.com` with your **actual email address** below." -choice "keyphrase" do - option "Hit enter to accept blank passphrase, then hit enter again" - option "If your computer is shared with other people, as in a work laptop, you should choose and enter a real passphrase. Twice." -end + console "ssh-keygen -C student@example.com -t rsa" + + message "Press enter to accept the default key save location." + + message "Next, you'll be asked for a passphrase." + + h1 "Choose whether to use a passphrase" + blockquote do + option_half "No passphrase" do + message "Hit enter to accept blank passphrase, then hit enter again." + end + option_half "Passphrase" do + message "If your computer is shared with other people, as in a work laptop, you should choose and enter a real passphrase. Twice." + end + end -result <<-OUTPUT + message "After key generation is complete, you'll have output that looks like this." + + result <<-OUTPUT Generating public/private rsa key pair. -Enter file in which to save the key (/Users/student/.ssh/id_rsa): +Enter file in which to save the key (/Users/student/.ssh/id_rsa): Created directory '/Users/student/.ssh'. -Enter passphrase (empty for no passphrase): -Enter same passphrase again: +Enter passphrase (empty for no passphrase): +Enter same passphrase again: Your identification has been saved in /Users/student/.ssh/id_rsa. Your public key has been saved in /Users/student/.ssh/id_rsa.pub. The key fingerprint is: 88:54:ab:77:fe:5c:c3:7s:14:37:28:8c:1d:ef:2a:8d student@example.com OUTPUT -verify do - message "Your brand-new public key is now stored at `~/.ssh/id_rsa.pub`" - todo "what is a home directory? what is tilde?" - tip "Public vs. Private Keys" do - message "If you look inside `~/.ssh/`, you will notice two files with the same name: `id_rsa` and `id_rsa.pub`." - message "`id_rsa.pub` is your **public key** and can be shared freely." - message "`id_rsa` is your **private key** and must be kept secret." - message "If someone else gets your private key and your passphrase, then they can pretend to be you and log on to your Heroku or Github accounts and cause mischief!" + verify do + message "Your brand-new public key is now stored at `~/.ssh/id_rsa.pub`" + tip "Public vs. Private Keys" do + message "If you look inside `~/.ssh/`, you will notice two files with the same name: `id_rsa` and `id_rsa.pub`." + message "`id_rsa.pub` is your **public key** and can be shared freely." + message "`id_rsa` is your **private key** and must be kept secret." + message "If someone else gets your private key and your passphrase, then they can pretend to be you and log on to your Heroku or GitHub accounts and cause mischief!" + end end -end + message "Add your generated key to the authentication agent using the following command:" + + console "ssh-add ~/.ssh/id_rsa" + + result <<-OUTPUT +Enter passphrase for /Users/student/.ssh/id_rsa: +Identity added: /Users/student/.ssh/id_rsa (/Users/student/.ssh/id_rsa)" + OUTPUT + + tip "Could not open a connection to your authentication agent" do + message "If the ssh-agent is not running, you will come across this error. Here are a few commands that you can try to use to start the ssh-agent:" + console_with_message "For some Windows machines:", "eval `ssh-agent -s`" + console_with_message "For others (confirmed on some Windows 7, 8, 8.1, and 10 setups):", "eval $(ssh-agent)" + console_with_message "For Linux:", "eval `ssh-agent`" + message <<-MARKDOWN +

For additional options, this StackOverflow thread has been helpful: http://stackoverflow.com/questions/17846529/could-not-open-a-connection-to-your-authentication-agent

+ MARKDOWN + end end +next_step 'create_a_heroku_account' diff --git a/sites/installfest/create_and_deploy_a_rails_app.step b/sites/installfest/create_and_deploy_a_rails_app.step deleted file mode 100644 index 39b89a491..000000000 --- a/sites/installfest/create_and_deploy_a_rails_app.step +++ /dev/null @@ -1,238 +0,0 @@ -step "Change to your home directory" do - option "Windows" do - console "cd c:\\Sites" - end - option "Mac or Linux" do - console "cd ~" - end -end - -step "Create a new Rails app" do - - console "rails new test_app" - - message "The command's output is voluminous. :-)" - - tip do - message "On Linux, you may have to add this line to your Gemfile:" - pre "gem 'therubyracer', :platforms => :ruby" - end - - console "cd test_app" - console "rails server" - - tip "Shortcut: Just type 'rails s'" do - message <<-MARKDOWN -Throughout your Rails programming career you're going to type `rails server` a -lot. In fact, you'll type this so much that DHH and the Rails Core team -decided to save you 5 keystrokes per server restart. Simply typing `rails s` -is the same as `rails server`. - MARKDOWN - end - - message <<-MARKDOWN -The first command should produce no output. -If "rails server" starts up with no errors, you're golden! It'll look something like this: - MARKDOWN - - result <<-TEXT -=> Booting WEBrick -=> Rails 3.2.6 application starting in development on http://0.0.0.0:3000 -=> Call with -d to detach -=> Ctrl-C to shutdown server -[2010-09-30 21:04:12] INFO WEBrick 1.3.1 -[2010-09-30 21:04:12] INFO ruby 1.9.2 (2010-08-18) [x86_64-darwin10.4.0] -[2010-09-30 21:04:12] INFO WEBrick::HTTPServer#start: pid=24805 port=3000 - TEXT - -message "If it does, congratulations! You've successfully installed Ruby AND Rails and started your server." - -tip "If it doesn't work..." do - link "diagnostics_for_windows" - link "diagnostics_for_rvm" -end - -message <<-MARKDOWN - -* In your browser, go to - -![Successful Rails Install](successful-rails-install.jpg) - -* Back in the Terminal window where you ran rails server, type **Control-C** to kill(stop) the server. - - MARKDOWN - important "On Windows, sometimes Control-C doesn't work. In that case, look for the key called 'Break' or 'Pause' and press Control-Break." - end - -step "Generate a database model" do - - console <<-BASH -cd test_app -rails generate scaffold user name:string email:string address:text active:boolean -rake db:migrate -rails server - BASH - - message "In the browser, visit " - - message "Click *New user* to create a user to make sure we can save to the database. (The window where you ran `rails server` will display debugging information as you do so.)" - - message "In your Terminal window where you ran `rails server`, type Control-C to kill(stop) the server." - -end - -step "Use git" do - - console <<-BASH -cd test_app -git init - BASH - - result "Initialized empty Git repository in c:/Sites/test_app/.git/" - - console "git add -A" - tip "git add" do - message <<-MARKDOWN -With Git, there are usually many ways to do very similar things. - - * `git add foo.txt` adds a file named `foo.txt` - * `git add .` ("git add dot") adds all new files and changed files, but *keeps* files that you've deleted - * `git add -A` adds everything, including deletions - -"Adding deletions" may sound weird, but if you think of a version control system as keeping track of *changes*, it might make more sense. - - MARKDOWN - end - - console "git commit -m \"initial commit\"" - result "a lot of lines like create mode 100644 Gemfile" - - console "git log" - result "(Your git name and \"initial commit\" message.)" -end - -step "Deploy your app to Heroku" do - - step "Create a Heroku application from this local Rails application." do - - message "The very first time you use `heroku` you must enter your Heroku email address and password." - - console "heroku create" - result <<-OUTPUT -Enter your Heroku credentials. -Email: myemail@example.com -Password: -Uploading ssh public key /Users/smei/.ssh/id_rsa.pub -Creating floating-winter-18... done, stack is cedar -http://floating-winter-18.heroku.com/ | git@heroku.com:floating-winter-18.git -Git remote heroku added - OUTPUT - - console "git remote show" - result "heroku" - - message "If you get messages here complaining about public keys it's probably due to some confusion with SSH key usage by another app on your computer. Call a volunteer over to help you figure it out. Luckily this only needs to be done the first time you create a Heroku app." - end - - step "Prepare your rails app for deploying to Heroku" do - message <<-MARKDOWN - -Find your test_app and open it in a text-editor. -Inside, find a file named Gemfile. -Edit your Gemfile and change the line: - - gem 'sqlite3' - -To this: - - group :development, :test do - gem 'sqlite3' - end - group :production do - gem 'pg' - end - - MARKDOWN - - console <<-BASH -bundle install --without production - BASH - - message "if this fails, edit `config/environments/production.rb` " - end - - step "Add the changes to git" do - - console <<-BASH -git add . -git commit -m "Updates for heroku deployment" - BASH - end - - step "Deploy (push) to heroku" do - - console "git push heroku master" - - message "It may ask: \"The authenticity of host 'heroku.com (75.101.145.87)' can't be established. RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad. Are you sure you want to continue connecting (yes/no)?\" Type yes and hit **." - - result <<-OUTPUT -The authenticity of host 'heroku.com (75.101.145.87)' can't be established. -RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad. -Are you sure you want to continue connecting (yes/no)? yes -Warning: Permanently added 'heroku.com,75.101.145.87' (RSA) to the list of known hosts. -Counting objects: 60, done. -Compressing objects: 100% (54/54), done. -Writing objects: 100% (60/60), 79.03 KiB, done. -Total 60 (delta 10), reused 0 (delta 0) - ------> Heroku receiving push ------> Rails app detected - Compiled slug size is 080K ------> Launching...... done - App deployed to Heroku - -To git@heroku.com:floating-winter-18.git - * [new branch] master -> master - OUTPUT - - important "Be sure to find and learn your Heroku application name in the output." - - message "[What to do if git seems stuck](what_to_do_if_git_seems_stuck)" - - console "heroku run rake db:migrate" - - result <<-OUTPUT -Running rake db:migrate attached to terminal... up, run.1 -Migrating to CreateUsers (20111204065949) -== CreateUsers: migrating ==================================================== --- create_table(:users) - -> 0.0122s -== CreateUsers: migrated (0.0123s) =========================================== - OUTPUT - - message "The long number after CreateUsers is a timestamp. Yours will be different!" - end - - step "Visit your new application" do - - message "In the browser, go to your application's URL. You'll need your Heroku application name." - - tip "To find your Heroku application name" do - console "heroku info" - end - - message "The URL for your app is *application name*.heroku.com -- so with the example output in the previous step, it would be floating-winter-18.heroku.com. Verify you see the welcome page. Leave this browser window open." - - tip "To quickly open your heroku application in a browser" do - console "heroku open" - end - - message "In the browser, add /users to the end of the URL. Verify you see the user list page." - - message "Create a new user to verify you can write to the database on Heroku." - - end -end - -next_step "get_a_sticker" - diff --git a/sites/installfest/deploy_a_rails_app.step b/sites/installfest/deploy_a_rails_app.step new file mode 100644 index 000000000..0de03a9ed --- /dev/null +++ b/sites/installfest/deploy_a_rails_app.step @@ -0,0 +1,185 @@ +step "Use git" do + tip "If your prompt doesn't already show that you are (still) in the test_app folder" do + console "cd test_app" + end + + console <<-BASH + git init + BASH + + result "Initialized empty Git repository in c:/Sites/railsbridge/test_app/.git/" + + console "git add -A" + tip do + message <<-MARKDOWN + With Git, there are usually many ways to do very similar things. + + * `git add foo.txt` adds a file named `foo.txt` + * `git add .` ("git add dot") adds all new files and changed files, but *keeps* files that you've deleted + * `git add -A` adds everything, including deletions + + "Adding deletions" may sound weird, but if you think of a version control system as keeping track of *changes*, it might make more sense. + MARKDOWN + end + + console "git commit -m \"initial commit\"" + result "a lot of lines like create mode 100644 Gemfile" + + console "git log" + result "(Your git name and \"initial commit\" message.)" +end + +step "Deploy your app to Heroku" do + + step "Create a Heroku application from this local Rails application." do + + message "The very first time you use `heroku` you must enter your Heroku email address and password. Your password may not be shown as you type it, but don't worry, it's being entered! If you have already provided your credentials before, you won't be prompted for them again." + + console "heroku create" + result <<-OUTPUT + Enter your Heroku credentials. + Email: myemail@example.com + Password: + Uploading ssh public key /Users/smei/.ssh/id_rsa.pub + Creating floating-winter-18... done, stack is cedar + http://floating-winter-18.heroku.com/ | git@heroku.com:floating-winter-18.git + Git remote heroku added + OUTPUT + + message "Heroku apps are automatically given lyrical names that look like '[adjective]-[noun]-[number]'. Each name is different." + + console "git remote show" + result "heroku" + + message "If you get messages here complaining about public keys it's probably due to some confusion with SSH key usage by another app on your computer. Call a volunteer over to help you figure it out. Luckily this only needs to be done the first time you create a Heroku app." + end + + step "Prepare your rails app for deploying to Heroku" do + message <<-MARKDOWN + Launch your text editor and open the "Gemfile" file located inside of your test_app folder. (On Windows, this should be in `C:\\Sites\\railsbridge\\test_app` and on Linux/OS X, it should be under `~/railsbridge/test_app`.) + + Inside this file, change the line: + MARKDOWN + source_code :ruby, <<-RUBY + gem 'sqlite3' + RUBY + + message "To this:" + + source_code :ruby, <<-RUBY + group :development, :test do + gem 'sqlite3' + end + + group :production do + gem 'pg', '~> 0.18' + end + RUBY + + message "Save the file." + + tip do + message 'When you saved the file, your text editor may have added colors to the code, much like the example above. It recognizes the file contains Ruby, and will mark up different kinds of words with different colors. This is called "syntax highlighting", which makes it easier to read code.' + end + tip "Why Sqlite (sqlite3) and PostgreSQL (pg)?" do + message "SQLite and PostgreSQL are different kinds of databases. We're using SQLite for our development and test environments because it's simple to install. We're using PostgreSQL in our production environment because Heroku has done the hard work of installing it for us and it's more powerful than SQLite. We have separate test, development and production databases by default in Rails." + end + + console "gem install bundler" + + fuzzy_result <<-OUTPUT + Successfully installed bundler-{FUZZY}1.14.3{/FUZZY} + 1 gem installed + OUTPUT + + console "bundle install --without production" + + message "Again, wait for the console prompt, and look for the 'Bundle complete!' message just above." + end + + step "Set the root route" do + message "Use your editor to open the file routes.rb (`C:\\sites\\railsbridge\\test_app\\config\\routes.rb` or `~/railsbridge/test_app/config/routes.rb`) and find the line containing:" + + source_code :ruby, <<-RUBY + Rails.application.routes.draw do + RUBY + + message "After this line, add a new line with the following:" + + source_code :ruby, <<-RUBY + root 'drinks#index' + RUBY + + message "Save the file." + end + + step "Add the changes to git" do + + message "Before running the following command (to add to your local git repository), make sure that you are in the `test_app` directory." + + console 'git add .' + console 'git commit -m "Updates for heroku deployment"' + end + + step "Deploy (push) to heroku" do + + console "git push heroku master" + + message "It may ask: \"The authenticity of host 'heroku.com (75.101.145.87)' can't be established. RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad. Are you sure you want to continue connecting (yes/no)?\" Type yes and hit *enter*." + + result <<-OUTPUT + The authenticity of host 'heroku.com (75.101.145.87)' can't be established. + RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad. + Are you sure you want to continue connecting (yes/no)? yes + Warning: Permanently added 'heroku.com,75.101.145.87' (RSA) to the list of known hosts. + Counting objects: 60, done. + Compressing objects: 100% (54/54), done. + Writing objects: 100% (60/60), 79.03 KiB, done. + Total 60 (delta 10), reused 0 (delta 0) + + -----> Heroku receiving push + -----> Rails app detected + Compiled slug size is 080K + -----> Launching...... done + App deployed to Heroku + + To git@heroku.com:floating-winter-18.git + * [new branch] master -> master + OUTPUT + + important "Be sure to find and learn your Heroku application name in the output." + + message "This process will probably take about twice as long as your 'bundle install' and then will return you to your console prompt. If it takes longer than that, talk to a TA." + + console "heroku run rails db:migrate" + + result <<-OUTPUT + Migrating to CreateDrinks (20160706063236) + == 20160706063236 CreateDrinks: migrating ===================================== + -- create_table(:drinks) + -> 0.0084s + == 20160706063236 CreateDrinks: migrated (0.0085s) ============================ + OUTPUT + + message "The long number after CreateDrinks is a timestamp. Yours will be different!" + end + + step "Visit your new application" do + + message "Now that the app is deployed, you can visit it in a browser." + + tip "To quickly open your heroku application in a browser" do + console "heroku open" + end + + message "The URL for your app is *application-name*.heroku.com -- something like floating-winter-18.heroku.com." + + message "Verify you see the welcome page. Leave this browser window open." + + message "Create and save a new drink to verify you can write to the database on Heroku." + + message "If you want to see a little more info about your app, you can run `heroku info`." + end +end + +next_step "get_a_sticker" diff --git a/sites/installfest/diagnostics_for_rvm.step b/sites/installfest/diagnostics_for_rvm.step deleted file mode 100644 index 402f01ecc..000000000 --- a/sites/installfest/diagnostics_for_rvm.step +++ /dev/null @@ -1,12 +0,0 @@ -console "which git" -result "/usr/local/bin/git" - -console "which ruby" -result "~/.rvm/rubies/ruby-1.9.3-p0/bin/ruby" - -console "which rails" -result "~/.rvm/gems/ruby-1.9.3-p0@global/bin/rails" - -console "gem list --local" -message "this will show a lot of gems... check with a TA to make sure you have all the gems you need" - diff --git a/sites/installfest/diagnostics_for_windows.step b/sites/installfest/diagnostics_for_windows.step deleted file mode 100644 index 74491ce84..000000000 --- a/sites/installfest/diagnostics_for_windows.step +++ /dev/null @@ -1,16 +0,0 @@ - -verify do - console "which git" - todo "result" - - console "which ruby" - todo "result" - - console "which rails" - todo "result" - - console "gem list --local" - message "this will show a lot of gems... check with a TA to make sure you have all the gems you need" -end - -link "what_to_do_if_git_seems_stuck" diff --git a/sites/installfest/editors.step b/sites/installfest/editors.step new file mode 100644 index 000000000..102e63955 --- /dev/null +++ b/sites/installfest/editors.step @@ -0,0 +1,23 @@ +message <<-MARKDOWN + +There are a number of different editors designed for programming. You may already have a favorite editor; if so you can continue using that editor. If you have never used a programming editor, there are a number of good editors available, several of them free of charge. Many RailsBridge instructors use the Atom editor, so you may want to install Atom; if you end up asking for help during your class it may be easier for an instructor or TA to help you if you are using Atom. Here is a list of some of the more widely used editors: + +* Atom is a free, open-source editor that can be customized to your liking using its rich ecosystem of add-on packages. A download is available for OS X 10.8+, Windows 7/8, and Ubuntu/RedHat linux. +* Sublime Text is popular with many Ruby and Rails users. You can use it free for evaluation, then must pay to continue using it. +* Komodo is a very good free programming editor, that is not used as widely as in the past. It is relatively easy to use. +* TextMate is very popular in the Ruby and Rails community. It is not free. +* Aptana Studio is a free, full-featured, development IDE (Integrated Development Environment) for Ruby and Rails. It has many powerful features to assist you while you develop your code. You can install Aptana as either a stand-along program or as an Eclipse plugin. +* RubyMine is used by many companies for their Ruby and Rails software development. Is is also a full-featured IDE, very similar to Aptana. RubyMine is not free, but has a 30-day evaluation period. + +There are other programming editors available, but these are among the most popular. Some people use simple non-programming editors, such as NotePad (or WordPad or TextEdit in "plain text" mode). It is fine to use a simple editor, though at some point you should look into using an editor designed for programming as using a programming editor or IDE will improve your programming productivity. + +MARKDOWN + +important do + message "Microsoft Word is **not** a text editor. It is a *word processor*." + message "(Word Processors add lots of weird invisible formatting stuff to the files they edit. You must use a text editor to write code.)" +end + +message "Some programmers use command-line text editors like `emacs` or `vim`. These have a steep learning curve, so we recommend you use a graphical editor like those listed above." + +next_step "installfest" diff --git a/sites/installfest/get_a_sticker.step b/sites/installfest/get_a_sticker.step index f8b9c2634..7f7c77edd 100644 --- a/sites/installfest/get_a_sticker.step +++ b/sites/installfest/get_a_sticker.step @@ -1,272 +1,39 @@ +step "Have a volunteer check your tool versions" do -message "Find a volunteer and have them watch the next steps." + message "Find a volunteer and have them watch the next steps." -message "If this works - get a sticker from them for your computer!" + verify "tool installation" do -verify "tool installation" do + tip "Most of the time, the version numbers don't have to match exactly. In general, if the *first two* numbers match, or if the full number you have is *greater* than the one below, then you're cool." - tip "Most of the time, the version numbers don't have to match exactly. In general, if the *first two* numbers match, or if the full number you have is *greater* than the one below, then you're cool." + h3 "If you're on OSX or Linux:" - h3 "If you're on OSX or Linux:" + console "rvm -v" + fuzzy_result "rvm 1{FUZZY}.x.x by Wayne E. Seguin (wayneeseguin@gmail.com) [https://rvm.io/]{/FUZZY}" - console "rvm -v" - result "rvm 1.11.2 by Wayne E. Seguin (wayneeseguin@gmail.com) [https://rvm.beginrescueend.com/]" + h3 "On all operating systems:" - console "sqlite3 --version" - result "3.7.5" + console "ruby -v" + fuzzy_result "ruby 2.3{FUZZY}.3p222 (2015-12-16 revision 53155) [x86_64-darwin13]{/FUZZY}" - h3 "On all operating systems:" + tip "As long as your Ruby version is #{version_string(:ruby_short)} or above, you're good to go." - console "ruby -v" - result "ruby 1.9.3p125 (2012-02-16 revision 34643) [x86_64-darwin11.3.0]" + console "bundle -v" + fuzzy_result "Bundler version 1{FUZZY}.x.x{/FUZZY}" - console "git --version" - result "git version 1.7.4.4" + console "rails -v" + fuzzy_result "Rails 5.0{FUZZY}.x{/FUZZY}" - console "gem -v" - result "1.8.21" - - console "rake --version" - result "rake, version 0.9.2.2" - - console "bundle -v" - result "Bundler version 1.1.3" - -end - -section "Change to your home directory" do - option "Windows" do - console "cd c:\\Sites" - end - option "Mac or Linux" do - console "cd ~" + tip 'The RailsBridge curriculum is written for Rails 5, so if you still have Rails 4.x or earlier, you need to install Rails 5 with `gem install rails`.' end end -verify "rails" do - - console "rails -v" - result "Rails 3.2.6" - - console "rails new sticker" - result "# ...most output omitted for brevity... -Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed." - - tip do - message "On Linux, you may have to add this line to your Gemfile:" - pre "gem 'therubyracer', :platforms => :ruby" - end - - console "cd sticker\nrails generate scaffold drink name:string temperature:integer" - result " - invoke active_record - create db/migrate/20120331005122_create_drinks.rb - create app/models/drink.rb - invoke test_unit - create test/unit/drink_test.rb - create test/fixtures/drinks.yml - route resources :drinks - invoke scaffold_controller - create app/controllers/drinks_controller.rb - invoke erb - create app/views/drinks - create app/views/drinks/index.html.erb - create app/views/drinks/edit.html.erb - create app/views/drinks/show.html.erb - create app/views/drinks/new.html.erb - create app/views/drinks/_form.html.erb - invoke test_unit - create test/functional/drinks_controller_test.rb - invoke helper - create app/helpers/drinks_helper.rb - invoke test_unit - create test/unit/helpers/drinks_helper_test.rb - invoke assets - invoke coffee - create app/assets/javascripts/drinks.js.coffee - invoke scss - create app/assets/stylesheets/drinks.css.scss - invoke scss - create app/assets/stylesheets/scaffolds.css.scss" - - console "rake db:migrate" - result " -== CreateDrinks: migrating =================================================== --- create_table(:drinks) - -> 0.0030s -== CreateDrinks: migrated (0.0033s) ========================================== -" - - console "rails server" - result " -=> Booting WEBrick -=> Rails 3.2.6 application starting in development on http://0.0.0.0:3000 -=> Call with -d to detach -=> Ctrl-C to shutdown server -[2012-03-30 17:56:02] INFO WEBrick 1.3.1 -[2012-03-30 17:56:02] INFO ruby 1.9.3 (2012-02-16) [x86_64-linux] -[2012-03-30 17:56:02] INFO WEBrick::HTTPServer#start: pid=27148 port=3000 - TERMINAL" - - message <<-BROWSER_VERIFY -In your web browser, go to - -1. Click on "New drink" -2. Enter Cappuccino for the name -3. Enter 135 for the temperature. -4. Click on "Create Drink". - -You should see: ![Drink was successfully created](get_a_sticker_you_should_see.png) - -In your terminal, Hold Control and hit C to stop the rails server. - BROWSER_VERIFY - - result <<-TERMINAL -=> Booting WEBrick -=> Rails 3.2.6 application starting in development on http://0.0.0.0:3000 -=> Call with -d to detach -=> Ctrl-C to shutdown server -[2012-03-30 17:56:02] INFO WEBrick 1.3.1 -[2012-03-30 17:56:02] INFO ruby 1.9.3 (2012-02-16) [x86_64-linux] -[2012-03-30 17:56:02] INFO WEBrick::HTTPServer#start: pid=27148 port=3000 -# ...most output omitted for brevity... -^C>> Stopping ... - -18:00:58 - Installfest stopping ->> Stopping ... - TERMINAL -end - -verify "git" do - message "Make sure you're still in the sticker directory." - - console "git init" - result "Initialized empty Git repository in /home/steven/Code/ruby/sticker/.git/" - - console "git add ." - console "git status" - result <<-RESULT -# On branch master -# -# Initial commit -# -# Changes to be committed: -# (use "git rm --cached ..." to unstage) -# -# new file: .gitignore -# new file: Gemfile -# new file: Gemfile.lock -# new file: README.rdoc -# new file: Rakefile -# ...most output omitted for brevity... - RESULT - - console 'git commit -m "Getting that sticker"' - result " -[master (root-commit) 8cd0e44] Getting that sticker - 54 files changed, 1531 insertions(+) - create mode 100644 .gitignore -# ...most output omitted for brevity... - create mode 100644 test/unit/helpers/drinks_helper_test.rb - create mode 100644 vendor/assets/javascripts/.gitkeep - create mode 100644 vendor/assets/stylesheets/.gitkeep - create mode 100644 vendor/plugins/.gitkeep" - - console "git status" - result " -# On branch master -nothing to commit (working directory clean)" -end - -verify "heroku deploy" do - - message "Use your editor to open the Gemfile and find the line containing:" - - source_code :ruby, <<-RUBY -gem 'sqlite3' - RUBY - - message "Remove this line and replace it with:" - - source_code :ruby, <<-RUBY -group :development do - gem 'sqlite3' -end - -group :production do - gem 'pg' -end - RUBY - - console "bundle install --without production" - result "Fetching gem metadata from https://rubygems.org/......... -# ...most output omitted for brevity... -Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed. -" - - console <<-GIT_COMMIT -git add . -git commit -m "Add pg gem for Heroku." -GIT_COMMIT - result "[master 4a275be] Add pg gem for Heroku. - 2 files changed, 6 insertions(+)" - - message "The name of your heroku app will be different. That is fine." - - console "heroku create" - result <<-HEROKU_CREATE -Creating evening-wind-5284... done, stack is cedar -http://evening-wind-5284.heroku.com/ | git@heroku.com:evening-wind-5284.git -Git remote heroku added - HEROKU_CREATE - - console "git push heroku master" - result <<-HEROKU_PUSH -Counting objects: 7, done. -Delta compression using up to 2 threads. -Compressing objects: 100% (4/4), done. -Writing objects: 100% (4/4), 453 bytes, done. -Total 4 (delta 3), reused 0 (delta 0) - ------> Heroku receiving push ------> Ruby/Rails app detected ------> Detected Rails is not set to serve static_assets - Installing rails3_serve_static_assets... done ------> Configure Rails 3 to disable x-sendfile - Installing rails3_disable_x_sendfile... done ------> Configure Rails to log to stdout - Installing rails_log_stdout... done ------> Gemfile detected, running Bundler version 1.0.7 - Unresolved dependencies detected; Installing... - Using --without development:test -# ...most output omitted for brevity... ------> Compiled slug size is 6.1MB ------> Launching... - HEROKU_PUSH - - console "heroku run rake db:migrate" - result <<-HEROKU_DB_MIGRATE -Migrating to CreateDrinks (20120428044153) -== CreateDrinks: migrating =================================================== --- create_table(:drinks) - -> 0.0084s -== CreateDrinks: migrated (0.0085s) ========================================== - HEROKU_DB_MIGRATE - - console "heroku open" - result "Opening http://evening-wind-5284.heroku.com/" - - message <<-BROWSER_VERIFY -Append `drinks` to the end of your heroku url. For example, if your url was -`http://evening-wind-5284.heroku.com/`, then change it to `http://evening-wind-5284.heroku.com/drinks`. - -You should see the same screen as you did at `http://localhost:3000/drinks` and be able to click "New Drink" and "Create Drink" as expected. - BROWSER_VERIFY - +step "Show your Heroku app to a volunteer" do + message "Visit the heroku site you made on the previous page and create a new drink." end step "Congratulations!" do - message "You got a sticker!" + message "You get a sticker! (Or a high-five if there aren't any stickers)." end next_step "clean_up" diff --git a/sites/installfest/glossary.step b/sites/installfest/glossary.step new file mode 100644 index 000000000..d11221fa0 --- /dev/null +++ b/sites/installfest/glossary.step @@ -0,0 +1,24 @@ +h2 "If you are ever stuck trying to make sense of all this alphabet soup - these glossaries may be helpful!" + +span "Have a look at the " +a 'general Glossary', href: '#general-glossary' +span ", the " +a 'Command Line Glossary', href: '#command-line-glossary' +span ", and the " +a 'Ruby and Rails Glossary', href: '#ruby-and-rails-glossary' +span "." + +a name: 'general-glossary' +situation "General Glossary" do + insert 'general-glossary' +end + +a name: 'command-line-glossary' +situation "Command Line Glossary" do + insert 'command-line-glossary' +end + +a name: 'ruby-and-rails-glossary' +situation "Ruby and Rails Glossary" do + insert 'ruby-and-rails-glossary' +end diff --git a/sites/installfest/img/AboutThisMac.png b/sites/installfest/img/AboutThisMac.png new file mode 100644 index 000000000..b884185dd Binary files /dev/null and b/sites/installfest/img/AboutThisMac.png differ diff --git a/sites/installfest/img/WinRailsInstaller.jpg b/sites/installfest/img/WinRailsInstaller.jpg new file mode 100644 index 000000000..33214260c Binary files /dev/null and b/sites/installfest/img/WinRailsInstaller.jpg differ diff --git a/sites/installfest/img/appstore.jpg b/sites/installfest/img/appstore.jpg new file mode 100644 index 000000000..31da13e35 Binary files /dev/null and b/sites/installfest/img/appstore.jpg differ diff --git a/sites/installfest/img/directory.png b/sites/installfest/img/directory.png new file mode 100644 index 000000000..a87c6ed77 Binary files /dev/null and b/sites/installfest/img/directory.png differ diff --git a/sites/installfest/get_a_sticker_you_should_see.png b/sites/installfest/img/get_a_sticker_you_should_see.png similarity index 100% rename from sites/installfest/get_a_sticker_you_should_see.png rename to sites/installfest/img/get_a_sticker_you_should_see.png diff --git a/sites/installfest/img/install_atom_mac.gif b/sites/installfest/img/install_atom_mac.gif new file mode 100644 index 000000000..35a83eb5f Binary files /dev/null and b/sites/installfest/img/install_atom_mac.gif differ diff --git a/sites/installfest/img/railsbridge_ubuntu12-checkbox.png b/sites/installfest/img/railsbridge_ubuntu12-checkbox.png new file mode 100644 index 000000000..f95651b5d Binary files /dev/null and b/sites/installfest/img/railsbridge_ubuntu12-checkbox.png differ diff --git a/sites/installfest/img/railsbridge_windowsScreenshot-commandprompt-pinnedtotaskbar.png b/sites/installfest/img/railsbridge_windowsScreenshot-commandprompt-pinnedtotaskbar.png new file mode 100644 index 000000000..0b948b3f9 Binary files /dev/null and b/sites/installfest/img/railsbridge_windowsScreenshot-commandprompt-pinnedtotaskbar.png differ diff --git a/sites/installfest/img/railsbridge_windowsScreenshot-commandprompt_ror.png b/sites/installfest/img/railsbridge_windowsScreenshot-commandprompt_ror.png new file mode 100644 index 000000000..a2e955bda Binary files /dev/null and b/sites/installfest/img/railsbridge_windowsScreenshot-commandprompt_ror.png differ diff --git a/sites/installfest/img/railsbridge_windows_findingCommandPrompt_win7.png b/sites/installfest/img/railsbridge_windows_findingCommandPrompt_win7.png new file mode 100644 index 000000000..c1ab27d97 Binary files /dev/null and b/sites/installfest/img/railsbridge_windows_findingCommandPrompt_win7.png differ diff --git a/sites/installfest/img/railsbridge_windows_findingCommandPrompt_win8.png b/sites/installfest/img/railsbridge_windows_findingCommandPrompt_win8.png new file mode 100644 index 000000000..f3c8cfaac Binary files /dev/null and b/sites/installfest/img/railsbridge_windows_findingCommandPrompt_win8.png differ diff --git a/sites/installfest/img/successful_rails_install.png b/sites/installfest/img/successful_rails_install.png new file mode 100644 index 000000000..397e03a19 Binary files /dev/null and b/sites/installfest/img/successful_rails_install.png differ diff --git a/sites/installfest/img/xcode-prefs.jpg b/sites/installfest/img/xcode-prefs.jpg new file mode 100644 index 000000000..260af6b52 Binary files /dev/null and b/sites/installfest/img/xcode-prefs.jpg differ diff --git a/sites/installfest/img/xcode-tools-install.png b/sites/installfest/img/xcode-tools-install.png new file mode 100644 index 000000000..bf5917e0d Binary files /dev/null and b/sites/installfest/img/xcode-tools-install.png differ diff --git a/sites/installfest/install_homebrew.step b/sites/installfest/install_homebrew.step deleted file mode 100644 index 5521b7d90..000000000 --- a/sites/installfest/install_homebrew.step +++ /dev/null @@ -1,16 +0,0 @@ - -console %q{/usr/bin/ruby -e "$(/usr/bin/curl -fksSL https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb)"} - -important "If that doesn't work, visit and follow the instructions there." - -verify do - console "brew -v" - result "0.9" -end - -tip "Homebrew uses `sudo` to prepare your `/usr/local` directory. 'sudo' stands for 'super user do' and it's a way to get your computer to do anything you ask it to do. It is the subject of a famous cartoon: - -![sudo make me a sandwich](http://imgs.xkcd.com/comics/sandwich.png) - -$USER is a variable that contains your username. You can also run the command **whoami** in your terminal and it'll output your username." - diff --git a/sites/installfest/install_komodo_edit_for_mac.step b/sites/installfest/install_komodo_edit_for_mac.step deleted file mode 100644 index 17387ff10..000000000 --- a/sites/installfest/install_komodo_edit_for_mac.step +++ /dev/null @@ -1,39 +0,0 @@ -message <<-MARKDOWN - -We'll be using the KomodoEdit text editor during the workshop, though you are free to use a different editor if you prefer. It must be a plain-text editor, such as vi or Textmate. - -MARKDOWN - -important "Microsoft Word and other word processing programs, including TextEdit and Notepad, will not work." - -step "Download KomodoEdit" do - - link "intel_or_ppc_mac" - - option "Intel Mac CPU" do - message "Download the [KomodoEdit installer](http://downloads.activestate.com/Komodo/releases/7.0.2/Komodo-Edit-7.0.2-9923-macosx-x86.dmg)" - end - - option "PowerPC" do - message "Download the [KomodoEdit installer for PowerPC](http://downloads.activestate.com/Komodo/releases/5.2.4/Komodo-Edit-5.2.4-4343-macosx-powerpc.dmg)." - end -end - -step 'Select "Open with DiskImageMounter" in the file save dialog' do - message "This should be the default." - - message "It will open an installer with a KomodoEdit icon and a picture of your Applications folder. Something like this:" - - message "![Komodo-Edit-5.png](Komodo-Edit-5.png)" - -end - -step "Drag KomodoEdit into your Applications folder." do - message "It will open an installer with a KomodoEdit icon and a picture of your Applications folder. Drag KomodoEdit into your Applications folder." -end - -step "Unmount the installer disk image" do - message "by dragging it from your desktop to the trash" -end - - diff --git a/sites/installfest/install_rvm_and_ruby.step b/sites/installfest/install_rvm_and_ruby.step deleted file mode 100644 index 4c166bfe3..000000000 --- a/sites/installfest/install_rvm_and_ruby.step +++ /dev/null @@ -1,53 +0,0 @@ -message <<-MARKDOWN -RVM stands for Ruby Version Manager and is the easiest way to install and manage Ruby. The [official RVM install instructions](http://rvm.beginrescueend.com/rvm/install/) are available here, but this should work for you: -MARKDOWN - -step "Install RVM" do - console "curl -L get.rvm.io | bash -s stable" - message "This will do some stuff and print a long message which you can ignore." -end - -step "Configure your shell" do - - message "Every time you open a new terminal window, rvm will be active inside it. Close your terminal window and open a new one." - - verify do - console "type rvm | head -1" - result "rvm is a function" - - console "rvm -v" - result "rvm 1.14.5 (stable) by Wayne E. Seguin (wayneeseguin@gmail.com) [https://rvm.beginrescueend.com/]" - end - -end - -step "Install Ruby 1.9.3 using RVM" do - console "rvm install 1.9.3" - - important "If you get an entire page of instructions, beginning with 'Ruby (and needed base gems) for your selection...', you should press Q to skip." - - message "This downloads and compiles Ruby, which takes a while." - - important "If `rvm install 1.9.3` says `autoreconf was not found in the PATH`" do - option "Mac OS" do - console "brew install automake" - end - - option "Ubuntu Linux" do - console "sudo apt-get install automake" - end - - message "Once that completes, retry `rvm install 1.9.3`" - end - - console <<-BASH -rvm use 1.9.3 -rvm --default use 1.9.3 - BASH - - verify do - console "ruby -v" - result "1.9.3" - end -end - diff --git a/sites/installfest/install_xcode.step b/sites/installfest/install_xcode.step index 83e1bf88c..4f9573021 100644 --- a/sites/installfest/install_xcode.step +++ b/sites/installfest/install_xcode.step @@ -1,26 +1,25 @@ -message "Installing XCode is straightforward with one caveat. When it asks you about the components to install you need to make sure \"Unix Tools\" is selected. Other than that just click Continue/Okay/Yes/etc." - -message "**Special note for folks who upgraded from Leopard to Snow Leopard:** The SQLite gem does not compile with an older version of XCode installed. If you upgraded your machine from an older version of OS X, and you have an older XCode, you still need to reinstall it from your **Snow Leopard** DVD. The steps below should work fine to upgrade it." - -choice "installing from a drive or from the App Store" do +option "OSX 10.9 Mavericks - OSX 10.11 El Capitan" do + link "install_xcode_command_line_tools" +end - option "install XCode from a thumb drive or DVD" do - link "install_xcode_from_dvd" - end +option "OSX 10.7 Lion - OSX 10.8 Mountain Lion" do + link "install_xcode_from_app_store" +end - option "Install XCode from the Apple App Store" do - link "install_xcode_from_app_store" - end - - option "Install Command Line Tools for XCode" do - message "Visit the [Apple Developer Downloads Page](https://developer.apple.com/downloads)" - message "Download and install the latest **Command Line Tools for Xcode** package" - end +option "OSX 10.6 Snow Leopard" do + link "install_xcode_from_dvd" +end +option "Install Command Line Tools for XCode" do + message "Visit the [Apple Developer Downloads Page](https://developer.apple.com/downloads)" + message "Download and install the latest **Command Line Tools for Xcode** package" end verify "your compiler was installed" do console "gcc --version" result "i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)" + + message "Your results might differ somewhat. Basically, as long as it doesn't say something like 'command not found', you're probably cool." end +next_step 'osx_rvm' diff --git a/sites/installfest/install_xcode_command_line_tools.step b/sites/installfest/install_xcode_command_line_tools.step new file mode 100644 index 000000000..5cd5cc040 --- /dev/null +++ b/sites/installfest/install_xcode_command_line_tools.step @@ -0,0 +1,30 @@ +message "Xcode Command Line Tools include lots of tools your computer needs to set up new software." + +verify "if you already have the tools installed" do + console "gcc --version" + result "Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 +Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn) +Target: x86_64-apple-darwin14.0.0 +Thread model: posix" + message "If you see something about Darwin and LLVM, you are good to go. If not, use one of the steps below to install the tools. Come back afterwords and run this command to make sure it works!" +end + +option "Install XCode Command Line Tools using software update" do + message "You can install these tools by trying to run a command that requires them." + + step "Run the xcode-select command" do + console "xcode-select --install" + end + + step "Install the tools" do + message "Click on the \"Install\" button. When the download finishes, go back and run the test to make sure it works." + img src: 'img/xcode-tools-install.png', alt: "install dialog" + end +end + +option "Install Command Line Tools for XCode" do + message "Visit the [Apple Developer Downloads Page](https://developer.apple.com/downloads)" + message "Download and install the latest **Command Line Tools for Xcode** package for your operating system. This requires you to register for an Apple Developer account." +end + +next_step 'osx_rvm' diff --git a/sites/installfest/install_xcode_from_app_store.step b/sites/installfest/install_xcode_from_app_store.step index c211da39d..414a2fa59 100644 --- a/sites/installfest/install_xcode_from_app_store.step +++ b/sites/installfest/install_xcode_from_app_store.step @@ -6,7 +6,7 @@ end step "Install XCode" do message "Search for 'XCode' and click the 'Install' button" - message "![XCode in the App Store](appstore.jpg)" + img src: 'img/appstore.jpg', alt: 'XCode in the App Store' end step "Launch XCode" @@ -14,6 +14,8 @@ step "Launch XCode" step "Install the Command Line Tools" do message "Open the Preferences menu and select the Downloads tab." message "Then click the 'Install' button next to 'Command Line Tools'." - message "![XCode Preferences](xcode-prefs.jpg)" + img src: 'img/xcode-prefs.jpg', alt: 'XCode Preferences showing the location of the install button for command line tools' end +next_step 'osx_rvm' + diff --git a/sites/installfest/install_xcode_from_dvd.step b/sites/installfest/install_xcode_from_dvd.step index 9cebe7748..fb67a1cc2 100644 --- a/sites/installfest/install_xcode_from_dvd.step +++ b/sites/installfest/install_xcode_from_dvd.step @@ -1,3 +1,5 @@ +message "Installing XCode is straightforward with one caveat. When it asks you about the components to install you need to make sure \"Unix Tools\" is selected. Other than that just click Continue/Okay/Yes/etc." + step "Insert your OS X DVD or Thumb Drive" step "Locate Xcode.mpkg" do @@ -11,15 +13,17 @@ end step "Keep on clicking..." do li 'Click "Continue" at the Introduction screen.' li 'Click "Continue" at the License screen.' - li 'Click "Agree" in the popup to agree to the license.' + li 'Click "Agree" in the pop up to agree to the license.' li 'Click "Continue" at the Destination Select screen.' li "Click \"Continue\" at the Installation Type screen. (Don't change the checkboxes.)" end step 'Click "Install".' do - message "Enter your user password in the popup to REALLY start installing. It takes a while." + message "Enter your user password in the pop up to REALLY start installing. It takes a while." end step 'Click "Close"' do message 'once you get "The installation was successful" on the Summary screen.' end + +next_step 'osx_rvm' diff --git a/sites/installfest/installfest.step b/sites/installfest/installfest.step index 54eab46c0..7aced5e1d 100644 --- a/sites/installfest/installfest.step +++ b/sites/installfest/installfest.step @@ -1,14 +1,15 @@ -message "This [InstallFest](http://installfest.railsbridge.org) is a set of step-by-step instructions for installing Ruby, Rails, and other important tools on Mac, Windows, or Linux computers. Originally developed for the [Railsbridge Workshops](http://railsbridge.org), it is a community-supported open-source documentation project. If you want to contribute, click the [git] link at the top-right corner of any page and submit your change on GitHub." +message "This is a set of step-by-step instructions for installing Ruby, Rails, and other important tools on Mac, Windows, or Linux computers. Originally developed for the [Railsbridge Workshops](http://railsbridge.org), it is a community-supported open-source documentation project. If you want to contribute, click the [git] link at the top-right corner of any page and submit your change on GitHub." -step "Prepare for the Install Fest" do +message "**You'll repeat all these steps tomorrow, but more slowly so you can better understand what's going on under the hood.** Today we just want to check that everything's set up and ready for tomorrow." + +step "Prepare for the Installfest" do message <<-MARKDOWN ## You must bring: -* **Your laptop.** You need to have a working wifi connection and a browser. +* **Your laptop.** You need to have a working wifi connection, a browser and an email account you can readily access. * If you have a choice between a Mac and a Windows laptop, please bring the Mac. - * Mac users: your **OS X DVD** * Linux is an acceptable alternative, but the Installfest is only tested on Ubuntu. * **Power cord for your laptop** * If you already have accounts on Heroku or GitHub, make sure you know your username and password. @@ -37,20 +38,17 @@ end step "Read This Overview" do message <<-MARKDOWN -You will be installing the following tools: - -* Ruby -* Rails -* Git -* Heroku -* KomodoEdit (or some editor) -* Various useful "ruby gems", including... - * bundler - * sqlite +Here's a list of tools you'll be installing. As you go through the workshop, we'll explain what each one is for and how to use it. -You will also create an account on Heroku, an application hosting platform. +* **Ruby**. A programming language. +* **Rails**. A framework for making web applications with Ruby. It does a lot of the setting up work for you, which is really handy, but we'll cover this in more detail a little later. +* **Git**. A revision or source control system. It creates a _repository_ (sometimes called a _repo_ for short), which is a complete history of your changes to what you're working on. This means you can undo changes and roll back to previous versions of your work if something has gone wrong. +* **GitHub** (optional). A place to store your Git repository online, so you can access it from any computer. +* **Heroku**. An _application server_, which hosts your application while you're working on it. This allows you to access your app online and interact with it from any browser, instead of just on your local computer. +* **Atom** (or [some other editor](editors)). To write programs in Ruby, you need a text editor to create, edit and save Ruby files. +* A couple of useful **Ruby gems**. Ruby gems are useful bits of Ruby code that someone has created for reuse, so you don't have to write it yourself. -If you already have an account on Heroku, make sure you know your username and password. +If you've already installed the above tools and are confident they are setup correctly, skip ahead to the Get a Sticker step. MARKDOWN end diff --git a/sites/installfest/intel_or_ppc_mac.step b/sites/installfest/intel_or_ppc_mac.step deleted file mode 100644 index 6d71bb8a4..000000000 --- a/sites/installfest/intel_or_ppc_mac.step +++ /dev/null @@ -1,9 +0,0 @@ -message <<-MARKDOWN -PowerPC (PPC) Macs are Apple's older line of machines with Motorola chips. Macs purchased since 2006 are almost all Intel. To find out for sure: - -* Click on the Apple menu in the top left corner of your screen. -* Select "About this Mac." -* It should have a line that starts with "Processor." If somewhere in that line it says "Intel," then you have an Intel Mac. Otherwise, you have a PPC Mac. - -For more information, see the [Apple Transition Timeline](http://en.wikipedia.org/wiki/Apple%E2%80%93Intel_transition#Timeline) or the [exhaustive list of Macintosh models](http://en.wikipedia.org/wiki/Timeline_of_Macintosh_models#2000s) on Wikipedia. -MARKDOWN diff --git a/sites/installfest/linux.step b/sites/installfest/linux.step index 860f3ded6..5aedf715a 100644 --- a/sites/installfest/linux.step +++ b/sites/installfest/linux.step @@ -1,7 +1,89 @@ -div do +important <<-MARKDOWN + These steps apply to **Ubuntu Linux** only. If you are using some other flavor of Linux, get help from a TA. +MARKDOWN + message <<-MARKDOWN -## Click here if you are using [Ubuntu Linux](ubuntu) +You will need sudo access in order to complete these directions (The first user account has this by default). +MARKDOWN + +step "Install packaged software and libraries" do + message <<-MARKDOWN +Open a terminal (Applications > Accessories > Terminal). You may want to right-click on the terminal icon and select "Add to panel" so the icon will appear next to the default help and Firefox icons in the top panel. + MARKDOWN + + apts = %w{ + autoconf + automake + bison + build-essential + curl + git-core + libapr1 + libaprutil1 + libc6-dev + libltdl-dev + libreadline6 + libreadline6-dev + libsqlite3-0 + libsqlite3-dev + libssl-dev + libtool + libxml2-dev + libxslt-dev + libxslt1-dev + libyaml-dev + ncurses-dev + nodejs + openssl + sqlite3 + zlib1g + zlib1g-dev + } + + console "sudo apt-get install #{apts.join(' ')}" + + important <<-MARKDOWN + If you get an error during `apt-get` resembling -If you are using any other version of Linux, ask for help now! + `Failed to fetch cdrom://Ubuntu`... + + it is because `apt-get` is trying to install packages from your Ubuntu install CD. If you don't have that CD on hand, you can try disabling it as a package source by following this Stack Overflow post: + + http://askubuntu.com/questions/125111/failed-to-download-repository-information-due-to-missing-cdrom MARKDOWN end + +step "Install git" do + console "sudo apt-get install git" + + message "If this results in an error, you should be ok, as the prior step should have installed `git-core`" +end + +step "Install RVM" do + important do + message "If you're using Ubuntu 12.04, Ubuntu 14.04 or the latest version of Mint, ensure that the Run command as login shell option is checked under the Title and Command tab in Profile Preferences (located in Terminal's Edit menu). After changing this setting, you may need to exit your console session and start a new one before the changes take effect." + img src: 'img/railsbridge_ubuntu12-checkbox.png', alt: "Ubuntu 12.04 terminal settings" + end + insert "install_rvm" +end + +step "Install Ruby" do + insert "install_ruby" +end + +step "Install Rails" do + message "Using a terminal again, execute the following command to install rails." + + console "gem install rails" +end + +step "Atom" do + message <<-MARKDOWN + +If you don't have a preferred code editor, you can install Atom. + +Checkout this section of the [Atom manual](http://flight-manual.atom.io/getting-started/sections/installing-atom/#platform-linux) for the latest instructions. +MARKDOWN +end + +next_step "configure_git" diff --git a/sites/installfest/macOS.step b/sites/installfest/macOS.step new file mode 100644 index 000000000..16dd27abc --- /dev/null +++ b/sites/installfest/macOS.step @@ -0,0 +1,43 @@ +step "Learn your Mac OS X Version" do + message <<-MARKDOWN + +* Click on the Apple icon in the top left of your screen. +* Select "About This Mac" +* In the window that comes up, under the title "Mac OS X" there will be a version number. + * If it starts with 10.13, you have **High Sierra**. + * If it starts with 10.12, you have **Sierra**. + * If it starts with 10.11, you have **El Capitan**. + * If it starts with 10.10, you have **Yosemite**. + * If it starts with 10.9, you have **Mavericks**. + * If it starts with 10.8, you have **Mountain Lion**. + * If it starts with 10.7, you have **Lion**. + * If it starts with 10.6, you have **Snow Leopard**. + * If it starts with 10.5, you have **Leopard**. + * If it starts with 10.4, you have **Tiger**. + * If it starts with 10.3, you have **Panther**. + +* Write down the Mac OS X version you have. +* In addition, to the right of the "Processor", there will be the a processor type. + * If it ends with **Intel Core i7**, **Intel Core i5**, **Intel Core i3**, or **Intel Core m3** you are good to go. + * If it ends with **Intel Core 2 Duo**, you are good to go. + * If it ends with **Intel Core Duo** or something else, you are **NOT** good to go. Please flag down a volunteer. + +* Once complete, you may close the "About This Mac" window. + +Below is an example. + +![OS X Snow Leopard](img/AboutThisMac.png) + + MARKDOWN +end + +step "Choose your instructions" do + + option "High Sierra/Sierra/El Capitan/Yosemite/Mavericks/Mountain Lion/Lion/Snow Leopard" do + link "osx_rvm" + end + option "Earlier than Snow Leopard" do + important "You should try upgrading to at least **Snow Leopard**. Apple doesn't sell it the Apple store anymore, so you'll have to [buy it from them online](http://store.apple.com/us/product/MC573Z/A/mac-os-x-106-snow-leopard) or find a copy from a friend." + message "It may be possible to get Ruby and Rails installed if your OS is older than Snow Leopard, but you're likely to encounter a lot of tough roadblocks that will require some intense Googling." + end +end diff --git a/sites/installfest/macintosh.step b/sites/installfest/macintosh.step deleted file mode 100644 index 9f1f92df9..000000000 --- a/sites/installfest/macintosh.step +++ /dev/null @@ -1,48 +0,0 @@ -div do -message <<-MARKDOWN - MARKDOWN -end - -step "Learn your Mac OS X Version" do - message <<-MARKDOWN - -* Click on the Apple icon in the top left of your screen. -* Select "About this Mac" -* In the window that comes up, under the title "Mac OS X" there will be a version number. - * If it starts with 10.7, you have **Lion**. - * If it starts with 10.6, you have **Snow Leopard**. - * If it starts with 10.5, you have **Leopard**. - * If it starts with 10.4, you have **Tiger**. - * If it starts with 10.3, you have **Panther**. - -* Write down the one you have and close the "About this Mac" window. - -Below is an example. - -![OS X Snow Leopard](MacOSXSnowLeopard.png) - - MARKDOWN -end - -step "Choose your instructions" do - - option "Lion" do - link "osx_lion" - end - - option "Snow Leopard" do - link "osx_lion" - message "The instructions for Lion and Snow Leopard are mostly the same." - end - - option "Panther" do - link "osx_panther" - end - - option "Tiger, or Leopard" do - important "Panther and Tiger are **very** old and you should seriously consider upgrading, or buying a new computer!" - link "osx_panther" - message "Follow the Panther instructions but be prepared for some things to be difficult." - end -end - diff --git a/sites/installfest/osx_lion.step b/sites/installfest/osx_lion.step deleted file mode 100644 index ef860776e..000000000 --- a/sites/installfest/osx_lion.step +++ /dev/null @@ -1,79 +0,0 @@ -step "Open Terminal" do - message "Look for **Terminal.app** inside Applications -> Utilities." - - message "Add it to your dock; you'll be using it a lot. (To add it to the dock, click and hold the dock icon once Terminal is open. Select options -> keep in dock.)" - - tip "Arrange your windows so that Terminal and your web browser are next to each other. You will want to *read* from your browser and *type into* your terminal *at the same time*." -end - -step "Install A Compiler (XCode or GCC)" do - link "install_xcode" -end - -step "Install Homebrew" do - console "which git" - result "/usr/bin/git" - message "If you get the result above skip to: Install RVM" - link "install_homebrew" -end - -step "Install Git" do - message "Next we'll install Git, the most popular version control system in the Ruby world:" - - console "brew install git" - - verify do - console "git --version" - result "git version 1.7.7.5" - end - - link "configure_git" -end - -step "Install RVM, the Ruby Version Manager ", {:anchor_name => 'install_rvm_and_ruby'} do - link "install_rvm_and_ruby" -end - -step "Install Rails" do - console "gem install rails" - verify do - console "rails -v" - result "Rails 3.2.6" - end -end - -step "Install KomodoEdit" do - message "We'll be using the KomodoEdit text editor during the workshop, though you are free to use a different editor if you prefer. It must be a plain-text editor, such as vi or TextMate. Microsoft Word and other word processing programs won't work." - - important "When in doubt, use KomodoEdit." - - link "install_komodo_edit_for_mac" -end - -step "Install the heroku tool" do - console "gem install heroku" -end - -verify "successful installation" do - console "which git" - result "/usr/bin/git" - - console "which ruby" - result "/Users/alex/.rvm/rubies/ruby-1.9.2-p290/bin/ruby" - - console "which rails" - result "/Users/alex/.rvm/gems/ruby-1.9.2-p290/bin/rails" - - console "which heroku" - result "/Users/alex/.rvm/gems/ruby-1.9.2-p290/bin/heroku" -end - -step "Generate an ssh public key" do - link "create_an_ssh_key" -end - -step "Congratulations!" do - message "You have everything you need to write a Ruby on Rails application." -end - -next_step "create_a_heroku_account" diff --git a/sites/installfest/osx_panther.step b/sites/installfest/osx_panther.step deleted file mode 100644 index cfcc63cde..000000000 --- a/sites/installfest/osx_panther.step +++ /dev/null @@ -1,102 +0,0 @@ -div do -message <<-MARKDOWN -OS X 10.3-10.5 (Panther, Tiger, Leopard) - - MARKDOWN -end - -step "XCode tools " do - message <<-MARKDOWN - -These are part of OS X, but are not installed by default. - -* Insert your OS X DVD. - -* Look for Xcode.mkpg - it may be under "Optional Installs" - -* Double click to run the installer. It will take a while - the XCode tools are large. - - MARKDOWN -end - -step "Git " do - - option "Leopard or Panther" do - url "http://code.google.com/p/git-osx-installer/" - - message %q{Click "Download the packages here" which will take you to a list of downloads. Pick Intel if you have an Intel Mac, and Universal Binary if you have a PowerPC Mac.} - - link "intel_or_ppc_mac" - end - - option "Tiger" do - message "ask a TA to help you install git on Tiger" - message "(you probably want to use MacPorts)" - end - -end - -step "Ruby, RubyGems, and Rails " do - - option "Tiger (10.4) and Panther (10.3)" do - message "visit " - message "Select the correct download in the right side of the page" - end - - option "Leopard (10.5)" do - message "use the built-in version of Ruby" - important "Whenever you install a gem using the built-in Ruby you should precede 'gem install' with 'sudo'" - end - console <<-BASH -sudo gem install rubygems-update -sudo update_rubygems -sudo gem update --system -sudo gem install rails - BASH - - message <<-MARKDOWN - -If you are having this error: - ERROR: Error installing rubygems-update: hoe requires RubyGems version >= 1.3.1 - -Try the steps in this order: - - sudo gem update --system - sudo gem install rubygems-update - sudo update_rubygems - sudo gem install rails - -For Tiger ONLY: - -* Install the "X11 User" optional package from your OS X DVD. - -* Install [ MacPorts]. - -* Ruby: sudo port install ruby - -* Gems: - * download [ RubyGems] - * unpack: tar xzf rubygems-1.3.5.tgz - * cd rubygems-1.3.5 - * Install:ruby setup.rb - -* Rails: sudo gem install rails --version 2.3.9 --no-rdoc --no-ri - - MARKDOWN -end - -step "Install KomodoEdit" do - link "install_komodo_edit_for_mac" -end - -step "Other required libraries" do - console <<-BASH -sudo gem install heroku --no-rdoc --no-ri - BASH -end - -step "Create an SSH key" do - link "create_an_ssh_key" -end - -next_step "create_a_heroku_account" diff --git a/sites/installfest/osx_rvm.step b/sites/installfest/osx_rvm.step new file mode 100644 index 000000000..da0f3da10 --- /dev/null +++ b/sites/installfest/osx_rvm.step @@ -0,0 +1,71 @@ +step "Open Terminal" do + message "Look for **Terminal.app** inside Applications -> Utilities, or use Spotlight search (Command + Space Bar) to find it." + + message "Add it to your dock; you'll be using it a lot. (To add it to the dock, click and hold the dock icon once Terminal is open. Select options -> keep in dock.)" + + tip "Arrange your windows so that Terminal and your web browser are next to each other. You will want to *read* from your browser and *type into* your terminal *at the same time*." +end + +step "Install A Compiler (XCode or GCC)" do + link "install_xcode" +end + +step "Install Homebrew" do + insert "install_homebrew" +end + +step "Install Git" do + message "Next we'll install Git, the most popular version control system in the Ruby world:" + + console "git --version" + fuzzy_result "git version {FUZZY}2.x.x{/FUZZY}" + + message <<-MARKDOWN +If you see that output, you have Git. Move on to the next step. + MARKDOWN + + console_with_message "Otherwise, type this in the terminal:", "brew install git" + + verify do + console "git --version" + fuzzy_result "git version {FUZZY}2.x.x{/FUZZY}" + end +end + +a name: "install-rvm" +step "Install RVM, the Ruby Version Manager ", {:anchor_name => 'install_rvm_and_ruby'} do + insert "install_rvm" +end + +step "Configure RVM to use Homebrew" do + console "rvm autolibs homebrew" +end + +step "Install Ruby" do + insert "install_ruby" +end + +step "Install Rails" do + console "gem install rails" + verify do + console "rails -v" + fuzzy_result "Rails 5.{FUZZY}0.x{/FUZZY}" + end +end + +step "Install Atom" do + insert "install_atom_for_mac" +end + +verify "successful installation" do + console "git --version" + fuzzy_result "git version {FUZZY}2.x.x{/FUZZY}" + + console "which ruby" + fuzzy_result "/Users/{FUZZY}alex{/FUZZY}/.rvm/rubies/ruby-{FUZZY}2.0.0-p247{/FUZZY}/bin/ruby" + + console "which rails" + fuzzy_result "/Users/{FUZZY}alex{/FUZZY}/.rvm/gems/ruby-{FUZZY}2.0.0-p247{/FUZZY}/bin/rails" +end + +next_step "configure_git" diff --git a/sites/installfest/railsbridge_findingCommandPrompt.png b/sites/installfest/railsbridge_findingCommandPrompt.png deleted file mode 100644 index 8208053a3..000000000 Binary files a/sites/installfest/railsbridge_findingCommandPrompt.png and /dev/null differ diff --git a/sites/installfest/railsbridge_setupScreenshot-2TerminalWindows.jpg b/sites/installfest/railsbridge_setupScreenshot-2TerminalWindows.jpg deleted file mode 100644 index eee0e5815..000000000 Binary files a/sites/installfest/railsbridge_setupScreenshot-2TerminalWindows.jpg and /dev/null differ diff --git a/sites/installfest/railsbridge_windowsScreenshot-command-railss.png b/sites/installfest/railsbridge_windowsScreenshot-command-railss.png deleted file mode 100644 index f686dcdfb..000000000 Binary files a/sites/installfest/railsbridge_windowsScreenshot-command-railss.png and /dev/null differ diff --git a/sites/installfest/railsbridge_windowsScreenshot-commandprompt-pinnedtotaskbar.png b/sites/installfest/railsbridge_windowsScreenshot-commandprompt-pinnedtotaskbar.png deleted file mode 100644 index 96d7e1cdc..000000000 Binary files a/sites/installfest/railsbridge_windowsScreenshot-commandprompt-pinnedtotaskbar.png and /dev/null differ diff --git a/sites/installfest/railsbridge_windowsScreenshot-commandprompt_ror.jpg b/sites/installfest/railsbridge_windowsScreenshot-commandprompt_ror.jpg deleted file mode 100644 index 4e6c65e78..000000000 Binary files a/sites/installfest/railsbridge_windowsScreenshot-commandprompt_ror.jpg and /dev/null differ diff --git a/sites/installfest/railsbridge_windowsScreenshot-railsdefault.png b/sites/installfest/railsbridge_windowsScreenshot-railsdefault.png deleted file mode 100644 index 5e816c799..000000000 Binary files a/sites/installfest/railsbridge_windowsScreenshot-railsdefault.png and /dev/null differ diff --git a/sites/installfest/successful-rails-install.jpg b/sites/installfest/successful-rails-install.jpg deleted file mode 100644 index 778a88a83..000000000 Binary files a/sites/installfest/successful-rails-install.jpg and /dev/null differ diff --git a/sites/installfest/ubuntu.step b/sites/installfest/ubuntu.step deleted file mode 100644 index 4427ce289..000000000 --- a/sites/installfest/ubuntu.step +++ /dev/null @@ -1,72 +0,0 @@ -message <<-MARKDOWN -You will need sudo access in order to complete these directions (The first user account has this by default). -MARKDOWN - -step "Install packaged software and libraries" do - message <<-MARKDOWN -Open a terminal (Applications > Accessories > Terminal). You may want to right-click on the terminal icon and select "Add to panel" so the icon will appear next to the default help and firefox icons in the top panel. - MARKDOWN - - apts = %w{ - autoconf - automake - bison - build-essential - curl - libc6-dev - libreadline6 - libreadline6-dev - libsqlite3-0 - libsqlite3-dev - libssl-dev - libxml2-dev - libxslt-dev - libxslt1-dev - libyaml-dev - ncurses-dev - openssl - sqlite3 - zlib1g - zlib1g-dev - } - - console "sudo apt-get install #{apts.join(' ')}" - -end - -step "Install git" do - console "sudo apt-get install git" - - message "If this results in an error, you may try installing `git-core` instead of `git`" -end - -step "Install RVM" do - link "install_rvm_and_ruby" - message "Restart your terminal before continuing." -end - -step "Install Rails" do - message "Using a terminal again, execute the following command to install rails." - - console "gem install rails" -end - -step "Install Heroku" do - console "gem install heroku" - tip "This only installs a local command-line `heroku` tool. You will still need to sign up for a Heroku account later." -end - -step "KomodoEdit" do - message <<-MARKDOWN - -If you don't have a preferred code editor, you can install KomodoEdit. It's available for [32-bit machines](http://downloads.activestate.com/Komodo/releases/7.0.2/Komodo-Edit-7.0.2-9923-linux-x86.tar.gz) and [64-bit machines](http://downloads.activestate.com/Komodo/releases/7.0.2/Komodo-Edit-7.0.2-9923-linux-x86_64.tar.gz) - -Check for the latest version. -MARKDOWN - - tip "How many bits?" do - message %q{If you're not sure, you're probably 32-bit. Run `uname -i` and see. "x86" in the filename means 32-bit, and "x86_64" means 64-bit.} - end -end - -next_step "create_a_heroku_account" diff --git a/sites/installfest/what_to_do_if_git_seems_stuck.md b/sites/installfest/what_to_do_if_git_seems_stuck.md deleted file mode 100644 index 962c81f68..000000000 --- a/sites/installfest/what_to_do_if_git_seems_stuck.md +++ /dev/null @@ -1,12 +0,0 @@ -Sometimes during a git remote push (i.e., to github or heroku), the client seems to stall. - - smei@NINOHE ~/Desktop/ruby_on_rails/test_app_2 (master) - $ git push heroku master - Warning: Permanently added the RSA host key for IP address '75.101.163.44' to the list of known hosts. - Counting objects: 63, done. - Compressing objects: 100% (57/57), done. - Writing objects: 25% (15/63), 19.30 KiB - -When this happens, control-c to end the process, then close the Git Bash window. Open the task manager (right click on the lower menu bar, select Task Manager). Switch tab to "Processes" and click "Image Name" to sort by process name. Scroll down and see if there are any zombie ssh.exe processes. If there are, select them, and hit End Process. - -Then, reopen Git Bash. cd to the test_app directory and do a git remote. Make sure the remote you're trying to push to (origin or heroku) is there - if not, re-add it before trying the push again. diff --git a/sites/installfest/windows.step b/sites/installfest/windows.step index 61c4b8912..729cec802 100644 --- a/sites/installfest/windows.step +++ b/sites/installfest/windows.step @@ -1,28 +1,23 @@ - message "These instructions should work for all versions of Windows from XP on to Windows 7." +message <<-MARKDOWN +These instructions should work for all versions of Windows from XP to Windows 8. - message "You may need to login as Administrator, or give the Administrator password when installing some programs, depending on your Windows version and user settings." +You may need to login as Administrator, or give the Administrator password when installing some programs, depending on your Windows version and user settings. +MARKDOWN step "Run RailsInstaller" do - - message "RailsInstaller includes Rails, Ruby, Git, and SQLite." - - message "Go to and download the kit." - - important "**Don't watch the video!** After you download the file, come back to this site!" - + message "RailsInstaller includes Rails, Ruby, Git and SQLite." + message "Go to , scroll to the 'Downloads' section, and download the RailsInstaller for Windows/Ruby #{version_string(:ruby_short)}." message "Click on the downloaded file to run the install wizard. Click Next at each step to accept the defaults." + message "Be sure to check the boxes for *Install git (recommended)* and *Add executables for Ruby, DevKit Git (if checked above) to the PATH*" - message "Be sure to check the box for *Add executables for Ruby, Git and DevKit to the PATH*" - - message "![WinXPRailsInstaller.jpg](WinXPRailsInstaller.jpg)" + img src: "img/WinRailsInstaller.jpg", alt: "Screenshot showing location of checkboxes for ensuring that Git is installed" message "Rails will be installed in C:\\RailsInstaller and the directory for your Rails projects will be C:\\Sites" - end step "Configure your git and ssh environment" do - important "At the end of the installer there will be a checkbox asking 'configure your git and ssh environment'. **Leave this box checked.** It will open a terminal window that you need to **type into**." + important "At the end of the installer there will be a checkbox asking 'Configure git and ssh when installation has completed'. **Leave this box checked.** It will open a terminal window that you need to **type into**." message "When it asks *Please enter your name, for example mine is: Wayne E. Seguin*" message "Type *your actual full name* into the console and press **[enter]**" @@ -36,15 +31,26 @@ step "Configure your git and ssh environment" do message "After this step you will have some **git config settings** and you will also have an **ssh key**." end + message "Here is a summary of your settings after this process:" + + img src: 'img/railsbridge_windowsScreenshot-commandprompt_ror.png', alt: "Screenshot showing correct git, ruby, rails and ssh settings" + + message "Close the command prompt window." end -step "Sanity Check" do +step "Update Git" do - console "ruby -v" - result "ruby 1.9.3p125" + message "The version of Git that comes with RailsInstaller is old, so we will be updating that next." - console "rails -v" - result "Rails 3.2.6" + message "Go to and download the installer. You want version 1.9.5 or newer." + + message "Run the installer, and it will ask you where you want to install it. Change it FROM C:\\Program Files\\Git TO, C:\\RailsInstaller\\Git like the picture below." + + img src: 'img/directory.png', alt: "Screenshot showing the step of installation where the location to install git is set" + + message "It will warn you that the directory already exists. Click yes to install to that folder anyway." + + message "Keep going using the default selections and you'll have the latest version of Git installed!" end @@ -55,52 +61,111 @@ step "Open a Terminal" do message "RailsInstaller made a special Terminal for you to use when you're working on Ruby, Rails, or Git." - message "To open the special Terminal, choose **All Programs** on the Start menu, then choose **RailsInstaller** and then **Command Prompt with Ruby and Rails**. You should do all your Rails work in this RailsInstaller DOS window." + message "To open the special Terminal, search for **Command Prompt with Ruby and Rails**." - message "![railsbridge_findingCommandPrompt.png](railsbridge_findingCommandPrompt.png)" + div do + option_half "Windows 7" do + img src: 'img/railsbridge_windows_findingCommandPrompt_win7.png', alt: "Screenshot showing search for command prompt" + end - tip "Pin It to Win It" do - message "![Pin the Command Prompt to the Task Bar](railsbridge_windowsScreenshot-commandprompt-pinnedtotaskbar.png)" + option_half "Windows 8" do + img src: 'img/railsbridge_windows_findingCommandPrompt_win8.png', alt: "Screenshot showing search for command prompt" + end end - message "Here is a summary of your settings after this process:" + message "You should do all your Rails work in this RailsInstaller DOS window." - message "![railsbridge_windowsScreenshot-commandprompt_ror.jpg](railsbridge_windowsScreenshot-commandprompt_ror.jpg)" + tip "Pin It to Win It" do + message 'Pinning the Command Prompt to the taskbar is a handy shortcut.' + img src: 'img/railsbridge_windowsScreenshot-commandprompt-pinnedtotaskbar.png', alt: 'Pin the Command Prompt to the Task Bar' + end end -step "Configure the Windows Terminal" do - message "Take a moment to configure the *Command Prompt with Ruby and Rails* so that it is a good height and width and has a nice font." +step "Update RubyGems" do + message "The version of RubyGems that comes with RailsInstaller may be outdated. Follow these steps to upgrade it!" - link "configure_the_windows_terminal" + step "Check to see if you need to update" do + console "gem -v" - tip "clearing the terminal" do - message "If you ever want to clear the terminal of all its cluttered output" - console "cls" + message "If the output is **#{version_string(:windows_rubygems_min)}** or later, skip to the next step!" end - tip "command history" do - message "The terminal window stores a \"command history.\" To view and re-run previous commands, use the <up arrow> and <down arrow> keys. You can also edit a previous command and run it -- this is handy for long commands, or fixing mistakes." - end + step "Install the latest version of RubyGems" do + message "Otherwise, run the following command:" - tip "copy and paste" do + console_without_message "gem update --system --no-document" - message "It's usually better to type commands in yourself. It takes a little longer but you learn more." + message "**Close and reopen your command prompt**, then verify you have the upgraded RubyGems by typing this in the terminal:" - message "But if you must... in these instructions, where it says: \"Type this in the terminal:\", you can usually copy the command from this page, and right click in the menu bar or terminal window, then click on \"Paste\", then hit the **[enter]** key." + console_without_message "gem -v" + fuzzy_result "#{version_string(:windows_rubygems_min_short)}{FUZZY}.9{/FUZZY}" + + message "As long as the version is at least **#{version_string(:windows_rubygems_min)}**, you should be fine!" end end -step "Install Windows Colors" do +a name: 'install-node' +step "Install Node.js" do - message "This step is optional, but **highly** recommended." + message "Go to and download the installer. Pick the one that is \"recommended for most users.\"" - link "windows_colors" + message "Click on the downloaded file to run the install wizard. Click Next at each step to accept the defaults." + message "**Reboot your computer.**" + + message "Once your computer is back up, load **Command Prompt with Ruby and Rails** and..." + + console "node -v" + fuzzy_result "v6{FUZZY}.x.x{/FUZZY}" end -step "Configure Git" do - link "configure_git" +step "Update Rails" do + message "Currently, RailsInstaller installs Rails 4.1.x, but we want the latest. Upgrading Rails is pretty easy:" + + console "gem install rails --no-document" + + message "...and you're done. New Rails! Woo." +end + +step "Sanity Check" do + + console "ruby -v" + fuzzy_result "ruby #{version_string(:ruby_short)}{FUZZY}.4{/FUZZY}" + + console "rails -v" + fuzzy_result "Rails 5.0{FUZZY}.x{/FUZZY}" + + console "git --version" + fuzzy_result "git version 2{FUZZY}.9.2.windows.1{/FUZZY}" +end + +step "Configure the Windows Terminal" do + message "Take a moment to configure the *Command Prompt with Ruby and Rails* so that it is a good height and width and has a nice font." + + message "Right-click on the menu bar and select **Properties**" + + message "Under the *Font* tab, select Lucida Console from the font chooser box. This lets you view non-ascii characters." + + message "In the *Layout* tab, adjust *Window Size* so the window is about half as wide as your screen, and is as tall as possible without covering the task bar (this may require some trial and error)." + + message "Click OK." + + tip "Clearing the Terminal" do + message "If you ever want to clear the terminal of all its cluttered output" + console "cls" + end + + tip "Command History" do + message "The terminal window stores a \"command history.\" To view and re-run previous commands, use the <up arrow> and <down arrow> keys. You can also edit a previous command and run it -- this is handy for long commands, or fixing mistakes, or for cycles (series of commands) that you repeat." + end + + tip "Copy and Paste" do + + message "It's usually better to type commands in yourself. It takes a little longer but you learn more." + + message "But if you must... in these instructions, where it says: \"Type this in the terminal:\", you can usually copy the command from this page, and right click in the menu bar or terminal window, then click on \"Paste\", then hit the **[enter]** key." + end end step "Install a Text Editor" do @@ -108,13 +173,11 @@ step "Install a Text Editor" do message "Notepad is not a good programming editor, but it will work in a pinch." - important "**When in doubt, use KomodoEdit.**" + important "**When in doubt, use Atom.**" - message "Download KomodoEdit here: " - - message "[Komodo Edit](http://www.activestate.com/komodo-edit) is a good open source option, if you don't have one yet." - + message "[Atom](https://atom.io) is a good option, if you don't have one yet." + message "Download Atom here: " end next_step "create_a_heroku_account" diff --git a/sites/installfest/windows_colors.step b/sites/installfest/windows_colors.step deleted file mode 100644 index 73f95fc7e..000000000 --- a/sites/installfest/windows_colors.step +++ /dev/null @@ -1,30 +0,0 @@ -message "The Rails server logging and test results are much easier to read with colored output but the Console with Ruby and Rails defaults to white on black. Add colors by following these steps." - -step "Download ANSICON" do - message "In the browser, visit " - message "Click the `ansi153.zip` link to download the file (or whatever the most recent version is)" - message "Double-click the file to unzip it" -end - -step "Determine if you have a 32 or 64 bit Operating System" do - message <<-MARKDOWN - * In Windows 7 or Vista, from the Start menu, type system in the Start Search box, and then click system in the Programs list. System Type under System will say whether it's a 32 or 64 bit Operating System - * In WIndows XP, right click on the "My Computer" icon, choose Properties, then General - * if System says Windows XP Professional x64 Edition Version then yours is 64 bit - * if it says Windows XP Professional Version then it's 32 bit - MARKDOWN -end - -option "if your OS is 32 bit" do - message "copy all the files from the subdirectory 'x86' of the downloaded files to C:\\Windows\\system32" -end - -option "if your OS is 64 bit" do - message "copy all the files from the subdirectory 'x64' of the downloaded files to C:\\Windows\\system32" -end - -step "Set up colors" do - message "open Console with Ruby and Rails" - console "ansicon" - message "now, when you use git and rspec, the output will not be all white-on-black" -end diff --git a/sites/installfest/xcode-prefs.jpg b/sites/installfest/xcode-prefs.jpg deleted file mode 100644 index 18bf445a7..000000000 Binary files a/sites/installfest/xcode-prefs.jpg and /dev/null differ diff --git a/sites/intro-to-rails/CRUD_with_scaffolding.step b/sites/intro-to-rails/CRUD_with_scaffolding.step new file mode 100644 index 000000000..c4a0fbb6d --- /dev/null +++ b/sites/intro-to-rails/CRUD_with_scaffolding.step @@ -0,0 +1,109 @@ +--- +goals { + + message <<-MARKDOWN + At the core, most database driven web sites are the same. They need to store records and provide a way to do the following: + + * **C**reate new records in the database + * **R**ead or show the records in the database + * **U**pdate existing records + * **D**estroy or delete records + + Because these 4 actions (CRUD) are so common, Rails includes the scaffold command to make creating them easier. + MARKDOWN +} + +steps { + + step { + console "rails server" + + tip "Now is a good time to figure out how to have multiple tabs or windows of your terminal or command prompt. Starting and stopping the Rails server all day is tedious, so it's good to have one terminal tab or window for running commands, and a separate one for the server." + } + + step { + message "Point your browser to [http://localhost:3000/topics](http://localhost:3000/topics)" + message "You should see the \"Topics\" page with headers for title and description, and a link to add a new topic:" + + img src: "img/Seattle_topic_list_page.png", alt: "Screenshot of topic list page" + } + + step { + message <<-MARKDOWN + * Click "New Topic" + * Fill in the form and click "Create Topic" + * You should see a page showing your new topic with a message that your topic was successfully created: + MARKDOWN + + img alt: "Screenshot of topic detail page with confirmation message", src: "img/Seattle_topic_created.png" + } + + step { + message <<-MARKDOWN + * Click "Back" + * You should see the topic list again, this time with your new topic listed: + + ![Screenshot of topic list with new topic](img/Seattle_list_with_topic.png) + + * Try the "Show", "Edit", and "Destroy" links to see what they do + * You've created a basic database driven web site, congrats! + MARKDOWN + } +} + +explanation { + + message <<-MARKDOWN + How did all those pages get created and hooked together? The Rails scaffold did it for you. + + Let's take a closer look at some of the files Rails created: + + * `app/models/topic.rb` + * This file contains code for our topic model. If you look at it, + it's nearly blank. Creating, reading, updating, and deleting + records are built into Rails. + + * `app/views/topics` + * This folder contains all the views for our topics model. This is + where the code for the forms you used above is stored. Rails + created all of these pages as part of the scaffold. + * If you've written HTML before, many lines in the views should + look familiar. Rails views are HTML with some extra code added + to display data from the database. + + * `app/views/topics/index.html.erb` + * This is the code for the page that lists all the topics. + * Index is the name given to the "default" page for a web site or a + section of a web site. When you navigate to + http://localhost:3000/topics the topics index page is what is + sent to your computer. + + * `app/views/topics/show.html.erb` + * This is the page you get when you click the "Show" link on the + "Listing topics" page. + + * `app/views/topics/new.html.erb` + * This is the page you get when you click "New Topic". + + * `app/views/topics/edit.html.erb` + * This is the page you get when you click "Edit". + + * `app/views/topics/_form.html.erb` + * You may have noticed that the page for new topics and the page + to edit topics looked similar. That's because they both use the + code from this file to show a form. This file is called a + partial since it only contains code for part of a page. Partials + always have filenames starting with an underscore character. + + * Challenge question: Can you find the line of code in new.html.erb + and edit.html.erb that makes the form partial appear? + + * `app/controllers/topics_controller.rb` + * This is the controller file that Rails created as part of the scaffold + * If you look you'll see a method (a line beginning with + def) for each of the views listed above (except + `_form.html.erb`) + MARKDOWN +} + +next_step "setting_the_default_page" diff --git a/sites/intro-to-rails/_consider_deploying.step b/sites/intro-to-rails/_consider_deploying.step new file mode 100644 index 000000000..79dcf7e68 --- /dev/null +++ b/sites/intro-to-rails/_consider_deploying.step @@ -0,0 +1,7 @@ +div :class => "deploying" do + h1 "Deploying" + blockquote do + message "Before the next step, you could try deploying your app to Heroku!" + link 'deploying_to_heroku' + end +end diff --git a/sites/intro-to-rails/_consider_deploying_again.step b/sites/intro-to-rails/_consider_deploying_again.step new file mode 100644 index 000000000..93c2bee43 --- /dev/null +++ b/sites/intro-to-rails/_consider_deploying_again.step @@ -0,0 +1,13 @@ +div :class => "deploying" do + h1 "Deploying" + blockquote do + + message <<-MARKDOWN + Before the next step, you could try deploying your app to Heroku! + + * If you have already deployed your app to Heroku, go on to [Deploying to Heroku again](deploying_to_heroku_again). + * If this is your first time deploying your app, start at [Deploying to Heroku](deploying_to_heroku) + MARKDOWN + + end +end diff --git a/sites/intro-to-rails/_deploying_to_heroku.step b/sites/intro-to-rails/_deploying_to_heroku.step new file mode 100644 index 000000000..7fd11fb29 --- /dev/null +++ b/sites/intro-to-rails/_deploying_to_heroku.step @@ -0,0 +1,43 @@ +step "Create a Heroku application" do + console "heroku create" + message "`heroku create` registers a new application on Heroku's system. You should see some output including your new app's URL." +end + +step "Edit the Gemfile" do + important "Each application has its own `Gemfile`. Be sure you're opening the one inside your app's folder." + + message "Heroku will run our application slightly differently than our development computer does, which requires us to make a small change to our `Gemfile`." + + message "Open the file called `Gemfile` in Atom, or your preferred editor, and find the line containing:" + + source_code :ruby, <<-RUBY +gem 'sqlite3' + RUBY + + message "**Remove that line** and replace it with:" + + source_code :ruby, <<-RUBY +group :development, :test do + gem 'sqlite3' +end + +group :production do + gem 'pg', '~> 0.18' +end + RUBY +end + +step "Apply the Gemfile changes" do + console "bundle install --without production" + message "Every time the `Gemfile` changes, you need to run ``bundle install`` for the changes to be processed. The processed version of the changes is stored in another file called ``Gemfile.lock``." + message "Here we are bundling without the gems in the group ``production``." +end + +step "Commit the Gemfile changes" do + message "There are now changes to `Gemfile` and `Gemfile.lock` that need to be committed before we can push to Heroku." + console <<-SHELL +git add . +git commit -m "Changed Gemfile for Heroku" + SHELL + tip "There is a period after the word add in the first line." +end diff --git a/sites/intro-to-rails/_deploying_to_heroku_again.step b/sites/intro-to-rails/_deploying_to_heroku_again.step new file mode 100644 index 000000000..753f43c7a --- /dev/null +++ b/sites/intro-to-rails/_deploying_to_heroku_again.step @@ -0,0 +1,61 @@ +step "Commit any pending changes to git" do + message "Heroku will only receive the files we've committed into our local git repository. So we need to make sure all changed files have been committed." + console "git status" + message "`git status` shows you any pending changes you've created. If it has no output, you're already ready to deploy! Otherwise..." + + console <<-SHELL +git add . +git commit -m "Some helpful message for your future self" + SHELL + message "Your commit message should reference whatever your outstanding +changes are: something like \"Added votes to the topics index\"." +end + +step "Push changes to Heroku" do + console "git push heroku main" + message "This takes all changes you've committed locally and pushes them to Heroku." +end + +step "Run database migrations on Heroku" do + console "heroku run rails db:migrate" + message "This tells Heroku to run your migrations on its database, like +running `rails db:migrate` locally." + message "Heroku's database is separate from the one on your computer, which +means it needs to be updated every time you make changes to the structure of +your database." + message "It also means that you'll not see any of the data you entered into +the `sqlite3` database on your computer." +end + +step "Visit your application" do + console "heroku open" + message "This opens the new application in your browser." +end + +explanation do + message <<-MARKDOWN + First, we had to do some work to make Heroku happy with our application. This +required updating the `Gemfile` and bundling. + + * The `Gemfile` is a list of all the Ruby libraries your application needs. + What we've declared here is that we want to use the `sqlite3` library + while we're developing on our computer (the development group) but when + deploying to Heroku (the production group) we want to use the `pg` library, + which is made for the type of database that Heroku uses. + + * Bundler is how Ruby projects keep track of the gems that they use. We told + Bundler what we wanted to use in the `Gemfile`, now we need to make sure those + gems are installed. Since we don't have the type of database Heroku does, we + skip the production gems. Don't worry though! Bundler still logs them so + Heroku will install them when they get your code. + MARKDOWN + + message "You should be able to deploy your application any time it's in a good, working state. Your typical workflow will look like:" + img src: "img/workflow.png", alt: "Diagram showing git workflow of making changes, committing them, and pushing to Heroku.", style: "border: none" + ol do + li { message "Add or change some code" } + li { message "Commit your changes (`git commit`)" } + li { message "(repeat)" } + end + message "Any time your changes are committed, you should feel free to `git push heroku main` and boom! Your changes are live!" +end diff --git a/sites/intro-to-rails/_switch_to_home_directory.step b/sites/intro-to-rails/_switch_to_home_directory.step new file mode 100644 index 000000000..01a5603e1 --- /dev/null +++ b/sites/intro-to-rails/_switch_to_home_directory.step @@ -0,0 +1,12 @@ +message "`cd` stands for **c**hange **d**irectory." + +message "A directory is the same thing as a folder." + +option "Windows" do + console "cd c:\\Sites" + message "`cd c:\\Sites` sets our Sites directory to our current directory." +end +option "Mac or Linux" do + console "cd ~" + message "`cd ~` sets our home directory to our current directory." +end diff --git a/sites/intro-to-rails/_working_effectively_and_efficiently.md b/sites/intro-to-rails/_working_effectively_and_efficiently.md new file mode 100644 index 000000000..f37b0dd36 --- /dev/null +++ b/sites/intro-to-rails/_working_effectively_and_efficiently.md @@ -0,0 +1,18 @@ +# Working Effectively and Efficiently + +We highly recommend you do the following: + +* Bring up your terminal and open 2 tabs: + * One is for regular terminal stuffs + * One will be for IRB (a.k.a. Rails console). We'll explain later. +* Open your browser fresh or hide any windows you already have open. + * Bring up one window with two tabs + * One for this content + * One for interacting with your app. +* Open your text editor and don't close it. +* Hide all extra applications. Turn off Twitter, Facebook, IM, and all other distractions. + +By minimizing the number of things you interact with, you reduce the +amount of time spent switching between them and the context lost as +you work through the lessons. Having 50 tabs open in your web +browser gets confusing. diff --git a/sites/intro-to-rails/add_the_project_to_a_git_repo.step b/sites/intro-to-rails/add_the_project_to_a_git_repo.step new file mode 100644 index 000000000..8b52a6464 --- /dev/null +++ b/sites/intro-to-rails/add_the_project_to_a_git_repo.step @@ -0,0 +1,70 @@ + +goals do + goal "Create a local git repository" + goal "Add all our files to the git repository" + + message "In order to publish our application, we need to add our application and any changes we make over time to a \"revision control system\". In our case we're going to use *git* because it is (relatively) easy and it is what our app server, *Heroku*, uses." +end + +steps do + + step do + console "git init" + + message "It doesn't look like anything really happened, but `git init` +initialized a new repository in a hidden directory called `.git`." + message "You can see this by typing `ls -a` (**l**i**s**t **a**ll files)." + end + + step do + console "git status" + + message "`git status` tells you everything git sees as modified, new, or missing." + message "You should see a ton of stuff under `Untracked files`." + end + + step do + console "git add ." + message "`git add .` tells git that you want to add the current directory (aka `.`) and everything under it to the repo." + tip "git add" do + message <<-MARKDOWN + With Git, there are usually many ways to do very similar things. + + * `git add foo.txt` adds a file named `foo.txt` + * `git add .` adds all new files and changed files, but *keeps* files that you've deleted + * `git add -A` adds everything, including deletions + + "Adding deletions" may sound weird, but if you think of a version control system as keeping + track of *changes*, it might make more sense. Most people use `git add .` but `git add -A` + can be safer. No matter what, `git status` is your friend. + MARKDOWN + end + end + + step do + console "git status" + + message "Now you should see a bunch of files listed under `Changes to be committed`." + end + + step do + console "git commit -m \"Added all the things\"" + message "`git commit` tells git to actually _do_ all things you've said you wanted to do." + message "This is done in two steps so you can group multiple changes together." + message "`-m \"Added all the things\"` is just a shortcut to say what your commit message is. You can skip that part and git will bring up an editor to fill out a more detailed message." + end + + step do + console "git status" + + message "Now you should see something like `nothing to commit, working directory clean` which means you've committed all of your changes." + end +end + +explanation do + message <<-MARKDOWN +By checking your application into git now, you're creating a record of your starting point. Whenever you make a change during today's workshop, we'll add it to git before moving on. This way, if anything ever breaks, or you make a change you don't like, you can use git as an all-powerful "undo" technique. But that only works when you remember to commit early and often! + MARKDOWN +end + +next_step "running_your_application_locally" diff --git a/sites/intro-to-rails/allow_people_to_vote.step b/sites/intro-to-rails/allow_people_to_vote.step new file mode 100644 index 000000000..851d1dc94 --- /dev/null +++ b/sites/intro-to-rails/allow_people_to_vote.step @@ -0,0 +1,85 @@ +goals { + message "Now we're going to add a button people can click to cast a vote." +} + +steps { + + step "Add a new controller action for voting" do + message "Edit `app/controllers/topics_controller.rb` and add this method at the end of the controller, above the `private` keyword:" + + source_code :ruby, <<-RUBY + def upvote + @topic = Topic.find(params[:id]) + @topic.votes.create + redirect_to(topics_path) + end + RUBY + + message <<-MARKDOWN + * `@topic = Topic.find(params[:id])` finds the topic in the database with that id and stores it in the variable `@topic`. + * `@topic.votes.create` creates a new vote for the current topic and saves it in the database. + * `redirect_to(topics_path)` tells the browser to go back to `topics_path` (the topics list). + MARKDOWN + end + + step "Add a new route for voting" do + message "Open `config/routes.rb` and find the section that looks like this:" + source_code :ruby, <<-RUBY + resources :topics + RUBY + + message "Replace that line so it looks like this:" + source_code :ruby, <<-RUBY + resources :topics do + member do + post 'upvote' + end + end + RUBY + + message <<-MARKDOWN + Verify that upvote route was added successfully by checking the output of `rails routes` or [http://localhost:3000/rails/info](http://localhost:3000/rails/info). You should see a line that looks like this: + + ``` + Prefix Verb URI Pattern Controller#Action + upvote_topic POST /topics/:id/upvote(.:format) topics#upvote + ``` + MARKDOWN + end + + step "Add the button to the view" do + + message "Edit `app/views/topics/index.html.erb` so that the bottom loop looks like this:" + + source_code :erb, <<-HTML + <% @topics.each do |topic| %> + + <%= topic.title %> + <%= topic.description %> + <%= pluralize(topic.votes.count, "vote") %> + <%= button_to '+1', upvote_topic_path(topic), method: :post %> + <%= link_to 'Show', topic %> + <%= link_to 'Edit', edit_topic_path(topic) %> + <%= link_to 'Destroy', topic, method: :delete, data: { confirm: 'Are you sure?' } %> + + <% end %> + HTML + + message <<-MARKDOWN + * `pluralize(topic.votes.count, "vote")` displays the number of votes the topic has, plus the word "vote" or "votes" accordingly. + * `button_to '+1'` creates an HTML button with the text "+1". + * `upvote_topic_path(topic)` creates the appropriate URL for the action we want to invoke. In this case, we want to upvote the current topic. + * `upvote_topic_path(topic)` would return `/topics/42/upvote` (if topic.id was 42) + * `method: :post` ensures we do the create action of CRUD, not the read action. + MARKDOWN + end + + step "Confirm your changes in the browser" do + message "Go back to and play." + message "Revel in the fact that you didn't have to restart the server to see these changes. Hawt, no?" + end +} + +insert 'consider_deploying_again' + +next_step "redirect_to_the_topics_list_after_creating_a_new_topic" diff --git a/sites/curriculum/clean_up_links_on_the_topics_list.step b/sites/intro-to-rails/clean_up_links_on_the_topics_list.step similarity index 84% rename from sites/curriculum/clean_up_links_on_the_topics_list.step rename to sites/intro-to-rails/clean_up_links_on_the_topics_list.step index 904f2b08e..811edf3f6 100644 --- a/sites/curriculum/clean_up_links_on_the_topics_list.step +++ b/sites/intro-to-rails/clean_up_links_on_the_topics_list.step @@ -21,7 +21,7 @@ steps { step "Change 'destroy' to 'delete'" do message "Change the line with the word 'Destroy' to this:" - source_code :erb, "<%= link_to 'Delete', topic, :confirm => 'Are you sure?', :method => :delete %>" + source_code :erb, "<%= link_to 'Delete', topic, method: :delete, data: { confirm: 'Are you sure?' } %>" end step "Confirm your changes" do @@ -35,8 +35,10 @@ explanation { message <<-MARKDOWN * The two links we removed were show and edit. We did this because the title now links to the show page and from the show page you can reach the edit page. - * The second change we made was to make the link text 'Delete' instead of destroy. + * The second change we made was to make the link text 'Delete' instead of 'Destroy'. MARKDOWN } -next_step "commit_and_push_to_heroku_yet_again" +insert 'consider_deploying_again' + +next_step "credits_and_next_steps" diff --git a/sites/intro-to-rails/creating_a_migration.step b/sites/intro-to-rails/creating_a_migration.step new file mode 100644 index 000000000..72d30f929 --- /dev/null +++ b/sites/intro-to-rails/creating_a_migration.step @@ -0,0 +1,44 @@ +goals { + + model_diagram header: 'Topics', fields: %w(id title description) + + message "The suggestotron has a list of topics that people can vote on. We'll store our topics in the database. In this step you'll do the following:" + + goal "Create a simple *Table* in the database for topics with a title and a description" + goal "Automatically generate the corresponding *Scaffold* in Rails (namely, the *Model*, the *View*, and the *Controller*)." + +} + +steps { + + step { + console "rails generate scaffold topic title:string description:text" + message <<-MARKDOWN +* `generate scaffold` tells Rails to create everything necessary to get up and running with topics. +* `topic` tells Rails the name of the new model. +* `title:string` says that topics have a title, which is a "string". +* `description:text` says that topics have a description which is a "text". (What's the difference between "string" and "text"? Basically "text" is for strings that might be very long.) + + MARKDOWN + message "If you want, take some time to poke around the files listed in this step. You can learn about them in the [Rails Architecture](rails_architecture) page." + link "rails_architecture" + } + + step { + console "rails db:migrate" + message "This tells Rails to update the database to include a table for our new model." + } +} + +explanation { + + message <<-MARKDOWN +Here, `rails db:migrate` is a command provided by the Rails framework. It uses the migration file we just created (`db/migrate/201xxxxxxxxxxx_create_topics.rb`) to change the database. Database migration files can be crucial to code collaboration. + + MARKDOWN + + tip "You can run `rails --help` to see a list of all the `rails` commands your app currently responds to, along with a short description of each one." +} + + +next_step "CRUD_with_scaffolding" diff --git a/sites/intro-to-rails/credits_and_next_steps.step b/sites/intro-to-rails/credits_and_next_steps.step new file mode 100644 index 000000000..d2639cdcd --- /dev/null +++ b/sites/intro-to-rails/credits_and_next_steps.step @@ -0,0 +1,43 @@ +message < Add Project Folder...` menu option: + +![Atom Project menu screenshot](img/atom_add_folder_to_project.png) + +Select your `suggestotron` folder from the file picker that opens. If everything works out Atom should show the directories of your app in a tree structure on the left: + +![Screenshot of Suggestotron project folder tree in Atom](img/atom_project_as_folder.png) + MARKDOWN + end + + message <<-MARKDOWN + You can see that rails new created a lot directories and + files. The ones we want to focus on today are: + MARKDOWN + + table border: "1", cellspacing: "0", cellpadding: "3", align: "center" do + tr { + th "File/Folder" + th "Purpose" + } + tr { + td "app/" + td "Contains the controllers, models, and views for your application. You will do most of your work here." + } + tr { + td "config/" + td "Configure your application's runtime rules, routes, database, and more." + } + tr { + td "db/" + td "Shows your current database schema, as well as the database migrations." + } + tr { + td "public/" + td "The only folder seen to the world as-is. If you put files in here, they will be served directly without any processing by Rails." + } + tr { + td "app/assets/" + td "This is where your images, JavaScript, stylesheets (CSS), and other static files should go. Modern Rails apps use something called the Assets Pipeline, which combines all the JavaScript and CSS files in this directory into a single file for speediness." + } + end + + message "There is a lot more that `rails new` created. Probably enough to fill a book, so we're going to ignore them for now." +end + +next_step "add_the_project_to_a_git_repo" diff --git a/sites/intro-to-rails/glossary.step b/sites/intro-to-rails/glossary.step new file mode 100644 index 000000000..4ee39d03f --- /dev/null +++ b/sites/intro-to-rails/glossary.step @@ -0,0 +1,24 @@ +h2 "If you are ever stuck trying to make sense of all this alphabet soup - these glossaries may be helpful!" + +span "Have a look at the " +a 'general Glossary', href: '#general-glossary' +span ", the " +a 'Command Line Glossary', href: '#command-line-glossary' +span ", and the " +a 'Ruby and Rails Glossary', href: '#ruby-and-rails-glossary' +span "." + +a name: 'general-glossary' +situation "General Glossary" do + insert '../installfest/general-glossary' +end + +a name: 'command-line-glossary' +situation "Command Line Glossary" do + insert '../installfest/command-line-glossary' +end + +a name: 'ruby-and-rails-glossary' +situation "Ruby and Rails Glossary" do + insert '../installfest/ruby-and-rails-glossary' +end diff --git a/sites/intro-to-rails/hooking_up_votes_and_topics.step b/sites/intro-to-rails/hooking_up_votes_and_topics.step new file mode 100644 index 000000000..42502a734 --- /dev/null +++ b/sites/intro-to-rails/hooking_up_votes_and_topics.step @@ -0,0 +1,127 @@ +goals { + + div(style: 'margin: 0 auto; width: 250px; height: 120px;') do + model_diagram header: 'Topics', fields: %w(id title description), style: "float: left;" + div(style: 'float: left; position: relative; width: 60px; height: 100px;') do + div(class: 'arrow-left', style: 'left: 0; top: 30px;') + div(class: 'horiz-line', style: 'left: 5px; top: 37px; width: 25px;') + div(class: 'vert-line', style: 'left: 30px; top: 38px; height: 25px;') + div(class: 'horiz-line', style: 'right: 0; top: 62px; width: 30px;') + end + model_diagram header: 'Votes', fields: %w(id topic_id), style: "float: left;" + end + + message "Because there is an explicit relationship between a topic and its + votes, we need to specify that. In this step, we'll explicitly + declare the relationship between votes and topics." +} + +steps { + + step "Teach the Topic model about Votes" do + message "Edit `app/models/topic.rb` so that it looks like this:" + + source_code :ruby, <<-RUBY +class Topic < ApplicationRecord + has_many :votes, dependent: :destroy +end + RUBY + end + + step "Teach the Vote model about Topics" do + message "Edit `app/models/vote.rb` so that it looks like this:" + source_code :ruby, <<-RUBY +class Vote < ApplicationRecord + belongs_to :topic +end + RUBY + end + + step "Play around with Topics and Votes in the Rails console" do + message "First, make sure you've made at least one topic on the site." + + console_with_message "Next, open a Rails console in a terminal window:", "rails c" + + result <<-CONSOLE + $ rails c + Loading development environment (Rails 5.0.0) + 2.3.0 :001 > + CONSOLE + + message "At the console, try the following things" + + console_with_message "See how many topics exist:", "Topic.count" + + console_with_message "Save the first topic into a variable:", "my_topic = Topic.first" + + tip "`my_topic` here could have been any variable name, but we'll stick with `my_topic` for consistency." + + console_with_message "Change the title of that topic to something else:", "my_topic.update_attributes(title: 'Edited in the console')" + + console_with_message "Add a vote to that topic:", "my_topic.votes.create" + + console_with_message "See how many votes that topic has:", "my_topic.votes.count" + + console_with_message "Remove a vote from that topic:", "my_topic.votes.first.destroy" + + message "Note that the things you can do to **Model classes** (like **Topic** and **Vote**), differ from the things you can do to **Model instances** (like my\\_topic, here). **my\\_topic.votes** is an **association**, and here behaves mostly like a model class." + + div do + half_width "Model class / association methods" do + ul class: 'no-style-type' do + li "Topic.first" + li "Topic.last" + li "Topic.all" + li "Topic.count" + li "Topic.find_by_id(5)" + li "Topic.destroy_all" + li "my_topic.votes.count" + li "my_topic.votes.create" + li "my_topic.votes.destroy_all" + end + end + + half_width "Model instance methods" do + ul class: 'no-style-type' do + li "my_topic.title" + li "my_topic.title = 'New title'" + li "my_topic.update_attributes(title: 'New title')" + li "my_topic.save" + li "my_topic.save!" + li "my_topic.destroy" + li "my_topic.votes.first.destroy" + end + end + end + + message <<-TEXT + An exhaustive list of things you can do to models and associations can be found in the Active Record Query Interface RailsGuide. + TEXT + end +} + +explanation { + + message <<-MARKDOWN + +`has_many` and `belongs_to`: + +* In Rails, relationships between models are called associations. +* Associations (usually) come in pairs. +* A topic will have many votes so we put `has_many :votes` in the + topic model. + * When you ask a topic for its votes, you get an array of votes + for that topic. +* A vote is for a particular topic, so we put `belongs_to :topic` + in the vote model. + * When you ask a vote for its topic, you get the topic for that + vote. + +It can still be important to clean up after yourself! `dependent: :destroy` + on `has_many :votes` means when a **Topic** gets destroyed, all + the **votes** that correspond to it will be destroyed, too. Without + `dependent :destroy`, those votes would live on the database forever. + MARKDOWN +} + +next_step "allow_people_to_vote" diff --git a/sites/intro-to-rails/img/Seattle_list_with_topic.png b/sites/intro-to-rails/img/Seattle_list_with_topic.png new file mode 100644 index 000000000..e693d8f27 Binary files /dev/null and b/sites/intro-to-rails/img/Seattle_list_with_topic.png differ diff --git a/sites/intro-to-rails/img/Seattle_topic_created.png b/sites/intro-to-rails/img/Seattle_topic_created.png new file mode 100644 index 000000000..77a8816f4 Binary files /dev/null and b/sites/intro-to-rails/img/Seattle_topic_created.png differ diff --git a/sites/intro-to-rails/img/Seattle_topic_list_page.png b/sites/intro-to-rails/img/Seattle_topic_list_page.png new file mode 100644 index 000000000..f4ef61124 Binary files /dev/null and b/sites/intro-to-rails/img/Seattle_topic_list_page.png differ diff --git a/sites/intro-to-rails/img/Start_page.png b/sites/intro-to-rails/img/Start_page.png new file mode 100644 index 000000000..d5fbf50f6 Binary files /dev/null and b/sites/intro-to-rails/img/Start_page.png differ diff --git a/sites/intro-to-rails/img/atom_add_folder_to_project.png b/sites/intro-to-rails/img/atom_add_folder_to_project.png new file mode 100644 index 000000000..154db6d95 Binary files /dev/null and b/sites/intro-to-rails/img/atom_add_folder_to_project.png differ diff --git a/sites/intro-to-rails/img/atom_project_as_folder.png b/sites/intro-to-rails/img/atom_project_as_folder.png new file mode 100644 index 000000000..0996dd84a Binary files /dev/null and b/sites/intro-to-rails/img/atom_project_as_folder.png differ diff --git a/sites/intro-to-rails/img/finished_app.png b/sites/intro-to-rails/img/finished_app.png new file mode 100644 index 000000000..c24d282e6 Binary files /dev/null and b/sites/intro-to-rails/img/finished_app.png differ diff --git a/sites/curriculum/img/mvc.png b/sites/intro-to-rails/img/mvc.png similarity index 100% rename from sites/curriculum/img/mvc.png rename to sites/intro-to-rails/img/mvc.png diff --git a/sites/intro-to-rails/img/rails4_rails_info_routing.png b/sites/intro-to-rails/img/rails4_rails_info_routing.png new file mode 100644 index 000000000..463b07d50 Binary files /dev/null and b/sites/intro-to-rails/img/rails4_rails_info_routing.png differ diff --git a/sites/curriculum/img/workflow.png b/sites/intro-to-rails/img/workflow.png similarity index 100% rename from sites/curriculum/img/workflow.png rename to sites/intro-to-rails/img/workflow.png diff --git a/sites/curriculum/curriculum.step b/sites/intro-to-rails/intro-to-rails.step similarity index 55% rename from sites/curriculum/curriculum.step rename to sites/intro-to-rails/intro-to-rails.step index e00fdcbc3..69f0a102d 100755 --- a/sites/curriculum/curriculum.step +++ b/sites/intro-to-rails/intro-to-rails.step @@ -12,7 +12,7 @@ allow users to: You've sketched up an initial screenshot of what you want it to look like: -![screenshot](img/finished_app.png) +![Browser window with topic titles that can be voted on, ordered by number of votes](img/finished_app.png) ### Meta-Goal @@ -20,33 +20,33 @@ When you have completed today's goal of getting the basic application online you should understand: * Basic Ruby syntax -* How to try your Ruby code (irb) +* How to try your Ruby code (IRB) * How to go from requirements to a new working Rails application * How to get your application online -* The basic tools a RoR developer uses (source control, editor, console, local server) +* The basic tools a RoR (Ruby on Rails) developer uses (source control, editor, console, local server) ### Schedule -* 1-ish hour of ruby -* 4-ish hours of rails, broken up in 1-ish hour steps +* 1-ish hour of Ruby +* 4-ish hours of Rails, broken up in 1-ish hour steps This is just a rough guideline, not a mandate. Some steps you'll go over and some you'll go under. It'll all work out by the end of the -day... unless I really screwed something up. :D +day. Probably. ### Requirements We're going to be working with: -* ruby 1.9.3 installed via rvm (mac or linux) or railsinstaller (windows) -* rails 3.2.x -* bundler -* sqlite -* the text editor of your choice +* Ruby 2.3 or greater installed via RVM (Mac or Linux) or RailsInstaller (Windows) +* Rails 5.0.x +* Bundler +* SQLite +* The text editor of your choice Everything should be set up the night before during our install-fest. Please ensure you have everything working _before_ you -show up for railsbridge on Saturday. +show up for RailsBridge on Saturday. You can verify that you have everything working by trying this out in your terminal: @@ -57,29 +57,16 @@ $ irb >> require "active_support" => true >> exit -$ +$ + + If you can do that, you are probably good to go. +MARKDOWN -### Working Effectively and Efficiently - -I highly recommend you do the following: - -* Bring up your terminal and open 2 tabs: - * One is for regular terminal stuffs - * One will be for irb (aka rails console). We'll explain later -* Open your browser fresh or hide any windows you already have open. - * Bring up one window with two tabs - * One for this content - * One for interacting with your app. -* Open your text editor and _do not ever close it_. We're not quitters. -* Hide all extra applications. Turn off twitter, IM, and all other distractions. - -By minimizing the number of things you interact with, you reduce the -amount of time spent switching between them and the context lost as -you work through the lessons. Having 50 tabs open in your web -browser gets confusing and wastes time. +insert 'working_effectively_and_efficiently' +message <<-MARKDOWN ### Format Each lesson will look like this: @@ -89,7 +76,7 @@ Each lesson will look like this:

Goal:

Description of the current step. -

Red because big goals are scary. +

Red because big goals are important.

Steps:

@@ -100,11 +87,11 @@ def code_to_write 1 + 1 end
-

Yellow because we've gotten it done, but we have no clue what's going on. +

Yellow because we've gotten it done, but we're not sure yet what's going on.

Explanation

-

Details of what the steps actually did... spell out the cause and effect. +

Details of what the steps actually did, explaining the cause and effect.

Green because we can tie everything together now.

diff --git a/sites/curriculum/make_the_topic_title_a_link.step b/sites/intro-to-rails/make_the_topic_title_a_link.step similarity index 88% rename from sites/curriculum/make_the_topic_title_a_link.step rename to sites/intro-to-rails/make_the_topic_title_a_link.step index 34ea97546..b27772f59 100644 --- a/sites/curriculum/make_the_topic_title_a_link.step +++ b/sites/intro-to-rails/make_the_topic_title_a_link.step @@ -1,7 +1,7 @@ goals { message "Your friends recommended two changes for the site:" goal "Don't show the description on the list page" - goal "Make the title a link and when its clicked show the description" + goal "Make the title a link and when it's clicked show the description" } steps { @@ -36,7 +36,7 @@ explanation { source_code :erb, "<%= topic.description %>" - message "This line was getting the description using .description and just printing it out." + message "This line was getting the description using `.description` and just printing it out." source_code :erb, "Description" @@ -53,7 +53,7 @@ explanation { message " Here's another use of `link_to` to create a link on the page. This `link_to` creates a link using the text from the topic title and goes - to the topic#show page." + to the topic show page." } next_step "clean_up_links_on_the_topics_list" diff --git a/sites/intro-to-rails/rails_architecture.step b/sites/intro-to-rails/rails_architecture.step new file mode 100644 index 000000000..e356d0df2 --- /dev/null +++ b/sites/intro-to-rails/rails_architecture.step @@ -0,0 +1,53 @@ +goals { + + goal "Create a database table for topics with a title and a description" + + message "In this step we'll learn a bit about Rails architecture. By the end of this step you should understand the following concepts:" + + ul { + li "Table" + li "Model" + li "View" + li "Controller" + } +} + +explanation { + + h2 "Rails architecture and its relation to the database" + + img(src: "img/mvc.png", alt: "MVC") + + message "Rails implements a very specific notion of the **Model/View/Controller** pattern, which guides how you structure your web applications." + + h3 "Model" + message <<-MARKDOWN +* For all the Models we create in RailsBridge, Model objects have a corresponding record in the database. The name of the table in the database is the plural version of the Model's class name. For example, if the Model is called 'Duck', it will automatically query or write to the 'ducks' table in the database. +* Methods internal to Rails make it easy to automatically write records to the database and query the database to get the records again later. +* The Model is a bridge between the database and your application's code. + MARKDOWN + + h3 "View" + message <<-MARKDOWN +* The View generates the HTML that will be displayed in the browser. +* View files are written in ERB, a templating language. It contains HTML with Ruby code embedded in it. The ruby variables in the view stand as placeholders for content that will be filled in when a user requests the page. +* (There are several other templating languages available, but in RailsBridge we always stick to ERB.) + MARKDOWN + + h3 "Controller" + message <<-MARKDOWN +* Controllers pass Ruby objects between the Models and the Views. +* Each url corresponds to a specific method in a Controller. +* After this step, when you visit any page in your application, that request will be handled by a method in a Controller. + MARKDOWN + + message <<-MARKDOWN +When Models, Views and Controllers are all put together, they follow a pattern: Given a URL, Rails will look up which Controller method (also called an "Action") to use. The Controller Action will use methods in a corresponding Model. The Model will need to read or write to the database, and return an object containing that data to the Controller. The Controller will take that object and pass it to the View. (Actions normally have a corresponding View file, and Rails will automatically find and use that file.) + +Models, Views and Controllers each have specific jobs. Separating responsibilities like this make it easier to develop, especially as it gets bigger. (When each file has a clear responsibility it's easier to fix problems and add new features.) + MARKDOWN + + message <<-MARKDOWN + If you want to learn more about Rails Architecture, you may want to watch this video explanation (3 min 30 sec) [MVC architecture *Youtube*](https://www.youtube.com/watch?v=eTdVkgF_Slo) + MARKDOWN +} diff --git a/sites/intro-to-rails/redirect_to_the_topics_list_after_creating_a_new_topic.step b/sites/intro-to-rails/redirect_to_the_topics_list_after_creating_a_new_topic.step new file mode 100644 index 000000000..8c60ed7c1 --- /dev/null +++ b/sites/intro-to-rails/redirect_to_the_topics_list_after_creating_a_new_topic.step @@ -0,0 +1,74 @@ +goals { + + message <<-MARKDOWN + When a user creates a new topic, or edits an existing topic, they are + currently shown a page with just that topic. For our voting app it makes + more sense that they would be taken back to the topic list. + + In this step we'll change the flow of our app so that the user is taken back to the topics list after they add a new topic (create) or + edit an existing topic (update). + MARKDOWN +} + +steps { + + step "Change the topics controller" do + + message "Open `app/controllers/topics_controller.rb` and look at the create method. " + + message "Find the line:" + + source_code :ruby, "format.html { redirect_to @topic, notice: 'Topic was successfully created.' }" + + + message 'and change `@topic` to `topics_path` like this:' + + source_code :ruby, "format.html { redirect_to topics_path, notice: 'Topic was successfully created.' }" + + message 'so that the file looks like this:' + + source_code :ruby, <<-RUBY +def create + @topic = Topic.new(topic_params) + + respond_to do |format| + if @topic.save + format.html { redirect_to topics_path, notice: 'Topic was successfully created.' } + format.json { render :show, status: :created, location: @topic } + else + format.html { render :new } + format.json { render json: @topic.errors, status: :unprocessable_entity } + end + end +end + RUBY + +message "In the same file, locate the update method. " + + message "Find the line:" + + source_code :ruby, "format.html { redirect_to @topic, notice: 'Topic was successfully updated.' }" + + + message 'and change `@topic` to `topics_path` like before:' + + source_code :ruby, "format.html { redirect_to topics_path, notice: 'Topic was successfully updated.' }" + end + + step "Confirm your changes" do + message "Look at ." + end +} + + +explanation { + + message <<-MARKDOWN + * `format.html { redirect_to topics_path, notice: 'Topic was successfully created.' }`: + * `format.html` means that the server should send HTML back to the browser + * `redirect_to topics_path` means show the **topics list page** when we're done creating or updating a topic + * `notice: 'Topic was successfully created/updated.'` puts the message into the flash so it will be displayed on the topics list + MARKDOWN +} + +next_step "make_the_topic_title_a_link" diff --git a/sites/intro-to-rails/ruby_language.step b/sites/intro-to-rails/ruby_language.step new file mode 100644 index 000000000..0d2754848 --- /dev/null +++ b/sites/intro-to-rails/ruby_language.step @@ -0,0 +1,160 @@ +goals do + goal "Be able to use the basic building blocks of Ruby code" + goal "Use IRB to run Ruby code" + goal "Do simple calculations" + goal "Use and understand variables" + goal "Use and understand arrays" + goal "Use loops and conditional statements" +end + +steps do + step do + message "Type this in the terminal to start the Interactive Ruby Shell, a program which lets you try out Ruby code:" + + console_without_message "irb" + + message "Yours might look different, but it should look something like this:" + + console_without_message "irb(main):001:0>" + end + + step do + console_with_message "Next try some simple math that's built into Ruby. Type these lines into IRB:", "3 + 3\n7 * 6" + end + + step do + message "**Variables** are names with values assigned to them." + + console_without_message "my_variable = 5" + + message "This assigns the value `5` to the name `my_variable`." + end + + step do + message "You can also do math with variables:" + console_without_message <<-RUBY +my_variable + 2 +my_variable * 3 + RUBY + end + + step do + message "Variables can also hold more than one value. This is called an **array**." + + console_without_message 'fruits = ["kiwi", "strawberry", "plum"]' + + message "Here we're using the variable `fruits` to hold a collection of fruit names." + end + + step do + console <<-RUBY +fruits = fruits + ["orange"] +fruits = fruits - ["kiwi"] + RUBY + + message "`+` and `-` are called operators. We can use them with the array of fruits just like we can use them with numbers." + + end + + step do + message "Everything in Ruby has a **class**. Type this into IRB:" + + console_without_message <<-RUBY +7.class +"kiwi".class +fruits.class +RUBY + + message "These are the three data types introduced so far: **Fixnum** (numbers), **String** (text), and **Array** (lists)." + end + + step do + message "Each class has different **methods** that can be used on **instances** of that class." + + console_without_message <<-RUBY +fruits.length +fruits.first + RUBY + + message "You can see all the methods available for an object:" + console_without_message <<-RUBY +fruits.methods + RUBY + + message "And **chain** methods together:" + console_without_message <<-RUBY +fruits.methods.sort + RUBY + end + + step do + message "Arrays have a method called **each** which iterates through the list running code on each item." + console_without_message <<-RUBY +fruits.each do |fruit| + puts fruit +end + RUBY + message "This takes the first item from the `fruits` array (`\"strawberry\"`), assigns it to the variable `fruit`, and runs the code between `do` and `end`. Then it does the same thing for each other item in the list. The code above should print a list of the fruits." + end + + step do + message "A **conditional** runs code only when a statement evaluates to true." + + console_without_message <<-RUBY +if my_variable > 1 + puts "YAY!" +end + RUBY + + message "This prints `YAY!` if the value stored in `my_variable` is greater than 1." + + message "Try changing the `>` in the conditional to a `<`." + + message "If you want to do something else when the statement evaluates to false, you can use an `else`:" + + console_without_message <<-RUBY +if my_variable > 1 + puts "YAY!" +else + puts "BOO!" +end + RUBY + end + + step do + message "You can also make your own methods:" + console_without_message <<-RUBY +def pluralize(word) + word + "s" +end +pluralize("kiwi") + RUBY + + message "Methods take **parameters**, which are the variables they work on. In this case, we made a method called pluralize that takes one parameter, a word." + + message "Methods can also return data. In this case, pluralize returns the word with an 's' added to the end of it. In Ruby, methods return whatever the last line of the method evaluates to." + end + + step do + message "Putting it all together, let's make a method that says your opinion of some fruits:" + message "**Don't try to type this all in!** Just paste it into irb and see what happens." + console_without_message <<-RUBY +def my_opinion(fruits) + fruits.each do |fruit| + if fruit == "pizza" + puts "pizza is the best!!!" + else + puts pluralize(fruit) + " are pretty good, I guess..." + end + end +end +my_opinion(["apple", "pizza", "orange"]) + RUBY + + message "Try changing this method to say what your favorite fruit is." + end +end + +important "Before you move on to the next step you must exit IRB by typing 'exit'" + +next_step "getting_started" diff --git a/sites/intro-to-rails/running_your_application_locally.step b/sites/intro-to-rails/running_your_application_locally.step new file mode 100644 index 000000000..1aadd1447 --- /dev/null +++ b/sites/intro-to-rails/running_your_application_locally.step @@ -0,0 +1,38 @@ +goals do + goal "Let's fire up the application locally" +end + + +steps do + step do + message "Make sure that you're in the `suggestotron` folder. You can type `pwd` (**p**rint **w**orking **d**irectory) in the terminal to see what folder you are in." + + console "rails server" + message "This will print some stuff and stay running forever, printing more stuff +every time you visit a page in your app." + end + step do + text "Point your web browser to " + url "http://localhost:3000/" + message "See your web app actually running!" + end + + step do + message "While the server is running, whatever you type in that terminal tab +will be ignored." + message "To get back to the terminal, you can stop the server by typing +`Control-c`." + result <<-STOPPING_RAILS_SERVER +^CExiting + STOPPING_RAILS_SERVER + end +end + +explanation do + message "`rails server` ran your application locally just like Heroku will be running it on their servers." + message "This provides a very simple means to see your changes before you commit and push them to Heroku." + message "`Control-c` is a way of closing or cancelling terminal programs. Since +`rails server` runs forever, you need to interrupt it with `Control-c`." +end + +next_step "creating_a_migration" diff --git a/sites/intro-to-rails/setting_the_default_page.step b/sites/intro-to-rails/setting_the_default_page.step new file mode 100644 index 000000000..a8f3db533 --- /dev/null +++ b/sites/intro-to-rails/setting_the_default_page.step @@ -0,0 +1,98 @@ +goals { + + message <<-MARKDOWN + Now that the structure is complete, let's make the flow work smoothly. + + Currently when you go to you see the "Welcome aboard" message. + + It would be easier to use our app if went directly to the topics list. + + In this step we'll make that happen and learn a bit about routes in Rails. + MARKDOWN +} + +steps { + + step "Add a root route" do + message "Open the file `config/routes.rb` in an editor (In the InstallFest yesterday, we suggested that you install and use **Atom** as your editor)." + + message "Look for the line `Rails.application.routes.draw` at the beginning of the file, and add the line `root 'topics#index'` after it. When you are done the start of the file should look like this:" + + source_code :ruby, <<-RUBY +Rails.application.routes.draw do + root 'topics#index' + RUBY + + em do + message "(Rails 3.x users should add `root to: 'topics#index'` and will need to remove their `public/index.html` file)." + end + end + + step "Confirm your changes" do + message "Go back to . You should be taken to the topics list automatically." + end +} + +explanation { + + message <<-MARKDOWN + * `root 'topics#index'` is a Rails route that says the default + address for your site is `topics#index`. `topics#index` is the topics + list page (the topics controller with the index action). + * Rails routes control how URLs (web addresses) get matched with + code on the server. Similar to how addresses match with houses and + apartments. + * The file `config/routes.rb` is like an address directory listing the + possible addresses and which code goes with each one + * `routes.rb` uses some shortcuts so it doesn't always show all the + possible URLs. To explore the URLs in more detail we can use the + terminal. + + At the terminal type `rails routes`. You should get something that + looks like this: + + ```` + $ rails routes + + Prefix Verb URI Pattern Controller#Action + topics GET /topics(.:format) topics#index + POST /topics(.:format) topics#create + new_topic GET /topics/new(.:format) topics#new + edit_topic GET /topics/:id/edit(.:format) topics#edit + topic GET /topics/:id(.:format) topics#show + PATCH /topics/:id(.:format) topics#update + PUT /topics/:id(.:format) topics#update + DELETE /topics/:id(.:format) topics#destroy + root GET / topics#index + ```` + This shows all the URLs your application responds to. The code that starts with colons are variables so :id means the id number of the record. The code in parenthesis is optional. + + You can also get this information on your site in development. Go to http://localhost:3000/rails/info and you'll see something like this: + + Screenshot of browser-based Rails routing info page + + You'll also see that table in whenever you try to access an invalid route (try http://localhost:3000/sandwich) + +### Exploring Routes (optional) + + Now you can have a look at the paths that are available in your app. + Let's try looking at one of the topics routes we just generated. + Open up your Rails console and play: + + $ rails console + >> app.topics_path + => "/topics" + >> app.topics_url + => "http://www.example.com/topics" + + `app` is a special object that represents your entire application. + You can ask it about its routes (as we just did), play with its + database connections, or make pseudo-web requests against it with + `get` or `post` (and lots more). + + MARKDOWN +} + +insert 'consider_deploying' + +next_step "voting_on_topics" diff --git a/sites/curriculum/voting_on_topics.step b/sites/intro-to-rails/voting_on_topics.step similarity index 85% rename from sites/curriculum/voting_on_topics.step rename to sites/intro-to-rails/voting_on_topics.step index e136e5fc5..d4776198b 100644 --- a/sites/curriculum/voting_on_topics.step +++ b/sites/intro-to-rails/voting_on_topics.step @@ -2,15 +2,15 @@ goals { goal "Create a model for votes" - img :alt => "Topics Table", :src => "img/votes_table.png" + model_diagram header: 'Votes', fields: %w(id topic_id) message "Every topic in suggestotron can be voted on. In order to count votes, we need to record votes. We'll add that table now." } steps { console <<-SHELL -rails generate resource vote topic_id:integer -rake db:migrate +rails generate model vote topic_id:integer +rails db:migrate SHELL } diff --git a/sites/javascript-snake-game/img/browser_console.png b/sites/javascript-snake-game/img/browser_console.png new file mode 100644 index 000000000..965ff66c6 Binary files /dev/null and b/sites/javascript-snake-game/img/browser_console.png differ diff --git a/sites/javascript-snake-game/javascript-snake-game.step b/sites/javascript-snake-game/javascript-snake-game.step new file mode 100644 index 000000000..ac318c35a --- /dev/null +++ b/sites/javascript-snake-game/javascript-snake-game.step @@ -0,0 +1,45 @@ +markdown <<-MARKDOWN + + ### Goal + + Today, we're going to recreate the classic game ~SNAKE!~ in the Javascript programming language. + By the end of this session, you should understand the basics of the JavaScript language and how you can use it in a web browser. + + The game you are going to create is going to work quite a bit like this: + +MARKDOWN + +canvas id: 'chunk-game', height: 600, width: 800 + +script src: 'js/chunk.js' + +script src: 'js/snake.js' + +markdown <<-MARKDOWN + + ### Meta-Goal + + When you have completed today's goal of creating a browser based game you should understand: + + * The basic syntax of JavaScript + * How to create, view and debug JavaScript + * How to use JavaScript with external libraries. + * How to incrementally build out a JavaScript application. + + ### Schedule + + * 2-ish hours of basic JavaScript syntax. + * 3-ish hours of creating and playing with a Javascript game and animations. + + This is just a rough guideline, not a mandate. Some steps you'll go + over and some you'll go under. It'll all work out by the end of the + day. Probably. + + Before you can start the tutorial, you'll need to download the tutorial + to your computer to edit the source code. + + Once you've downloaded the zip archive file, you will need to extract the contents and open index.html with your browser. If you have trouble extracting the files or opening them, get an instructor to help you out. + +MARKDOWN + +next_step 'lesson-1' \ No newline at end of file diff --git a/sites/javascript-snake-game/js-snake-game-tutorial.zip-manifest b/sites/javascript-snake-game/js-snake-game-tutorial.zip-manifest new file mode 100644 index 000000000..33a706ca8 --- /dev/null +++ b/sites/javascript-snake-game/js-snake-game-tutorial.zip-manifest @@ -0,0 +1,3 @@ +zip_content/index.html +zip_content/snake.js +js/chunk.js diff --git a/sites/javascript-snake-game/js/chunk.js b/sites/javascript-snake-game/js/chunk.js new file mode 100644 index 000000000..be6278b20 --- /dev/null +++ b/sites/javascript-snake-game/js/chunk.js @@ -0,0 +1,102 @@ +var CHUNK = { + canvasWidth: 800, + canvasHeight: 600, + pixelSize: 40, + KEY_MAPPING: { + 39: "right", + 40: "down", + 37: "left", + 38: "up" + }, + started: true, + attrs: {}, + gameHeight: function() { + return this.attrs.gameHeight || (this.attrs.gameHeight = this.canvasHeight / this.pixelSize); + }, + gameWidth: function() { + return this.attrs.gameWidth || (this.attrs.gameWidth = this.canvasWidth / this.pixelSize); + }, + canvas: function() { + if (CHUNK.context) { return CHUNK.context; } + var canvas = document.getElementById("chunk-game"); + CHUNK.context = canvas.getContext("2d"); + return CHUNK.context; + }, + executeNTimesPerSecond: function(tickCallback, gameSpeed) { + tickCallback(); + CHUNK.processID = setInterval(function() { + tickCallback(); + }, 1000 / gameSpeed); + }, + onArrowKey: function(callback) { + document.addEventListener('keydown', function(e) { + if (CHUNK.KEY_MAPPING[e.which]) { + e.preventDefault(); + callback(CHUNK.KEY_MAPPING[e.which]); + } + }); + }, + endGame: function() { + this.started = false + clearInterval(CHUNK.processID); + }, + draw: function(objects) { + if (this.started) { + CHUNK.clear(); + CHUNK.drawObjects(objects); + } + }, + clear: function() { + CHUNK.canvas().clearRect(0, 0, CHUNK.canvasWidth, CHUNK.canvasHeight); + }, + drawObjects: function(objects) { + var ui = this; + objects.forEach(function(object) { + object.pixels.forEach(function(pixel) { + ui.drawPixel(object.color, pixel); + }); + }); + }, + drawPixel: function(color, pixel) { + CHUNK.canvas().fillStyle = color; + var translatedPixel = CHUNK.translatePixel(pixel); + CHUNK.context.fillRect(translatedPixel.left, translatedPixel.top, CHUNK.pixelSize, CHUNK.pixelSize); + }, + translatePixel: function(pixel) { + return { left: pixel.left * CHUNK.pixelSize, + top: pixel.top * CHUNK.pixelSize } + }, + gameBoundaries: function() { + if (this.attrs.boundaries) { return this.attrs.boundaries; } + this.attrs.boundaries = []; + for (var top = -1; top <= CHUNK.gameHeight(); top++) { + this.attrs.boundaries.push({ top: top, left: -1}); + this.attrs.boundaries.push({ top: top, left: this.gameWidth() + 1}); + } + for (var left = -1; left <= CHUNK.gameWidth(); left++) { + this.attrs.boundaries.push({ top: -1, left: left}); + this.attrs.boundaries.push({ top: this.gameHeight() + 1, left: left }); + } + return this.attrs.boundaries; + }, + detectCollisionBetween: function(objectA, objectB) { + return objectA.some(function(pixelA) { + return objectB.some(function(pixelB) { + return pixelB.top === pixelA.top && pixelB.left === pixelA.left; + }); + }); + }, + randomLocation: function() { + return { + top: Math.floor(Math.random()*CHUNK.gameHeight()), + left: Math.floor(Math.random()*CHUNK.gameWidth()), + } + }, + flashMessage: function(message) { + var canvas = document.getElementById("chunk-game"); + var context = canvas.getContext('2d'); + context.font = '20pt Calibri'; + context.fillStyle = 'yellow'; + context.fillText(message, 275, 100); + } +} diff --git a/sites/javascript-snake-game/js/lesson-10.js b/sites/javascript-snake-game/js/lesson-10.js new file mode 100644 index 000000000..1d7ef10da --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-10.js @@ -0,0 +1,54 @@ +var drawSnake = function(snakeToDraw) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); +} + +var moveSegment = function(segment) { + switch(segment.direction) { + case "down": + return { top: segment.top + 1, left: segment.left }; + case "up": + return { top: segment.top - 1, left: segment.left }; + case "right": + return { top: segment.top, left: segment.left + 1 } + case "left": + return { top: segment.top, left: segment.left - 1 } + default: + return segment; + } +} + +var segmentFurtherForwardThan = function(index, snake) { + if (snake[index - 1] === undefined) { + return snake[index]; + } else { + return snake[index - 1]; + } +} + +var moveSnake = function(snake) { + return snake.map(function(oldSegment, segmentIndex) { + var newSegment = moveSegment(oldSegment); + newSegment.direction = segmentFurtherForwardThan(segmentIndex, snake).direction; + return newSegment; + }); +} + +var advanceGame = function() { + snake = moveSnake(snake); + if (CHUNK.detectCollisionBetween(snake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! you hit a wall!"); + } + drawSnake(snake); +} + +var changeDirection = function(direction) { + snake[0].direction = direction; +} + +var snake = [{ top: 1, left: 0, direction: "down" }, { top: 0, left: 0, direction: "down" }]; + +CHUNK.executeNTimesPerSecond(advanceGame, 1); +CHUNK.onArrowKey(changeDirection); diff --git a/sites/javascript-snake-game/js/lesson-11.js b/sites/javascript-snake-game/js/lesson-11.js new file mode 100644 index 000000000..d75d3da83 --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-11.js @@ -0,0 +1,53 @@ +var draw = function(snakeToDraw, apple) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableApple = { color: "red", pixels: [apple] }; + var drawableObjects = [drawableSnake, drawableApple]; + CHUNK.draw(drawableObjects); +} + +var moveSegment = function(segment) { + switch(segment.direction) { + case "down": + return { top: segment.top + 1, left: segment.left }; + case "up": + return { top: segment.top - 1, left: segment.left }; + case "right": + return { top: segment.top, left: segment.left + 1 } + case "left": + return { top: segment.top, left: segment.left - 1 } + default: + return segment; + } +} + +var segmentFurtherForwardThan = function(index, snake) { + return snake[index - 1] || snake[index]; +} + +var moveSnake = function(snake) { + return snake.map(function(oldSegment, segmentIndex) { + var newSegment = moveSegment(oldSegment); + newSegment.direction = segmentFurtherForwardThan(segmentIndex, snake).direction; + return newSegment; + }); +} + +var advanceGame = function() { + snake = moveSnake(snake); + if (CHUNK.detectCollisionBetween(snake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! you hit a wall!"); + } + draw(snake, apple); +} + +var changeDirection = function(direction) { + snake[0].direction = direction; +} + +var apple = { top: 8, left: 10 }; + +var snake = [{ top: 1, left: 0, direction: "down" }, { top: 0, left: 0, direction: "down" }]; + +CHUNK.executeNTimesPerSecond(advanceGame, 1); +CHUNK.onArrowKey(changeDirection); diff --git a/sites/javascript-snake-game/js/lesson-12.js b/sites/javascript-snake-game/js/lesson-12.js new file mode 100644 index 000000000..cdeea5a85 --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-12.js @@ -0,0 +1,63 @@ +var draw = function(snakeToDraw, apple) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableApple = { color: "red", pixels: [apple] }; + var drawableObjects = [drawableSnake, drawableApple]; + CHUNK.draw(drawableObjects); +} + +var moveSegment = function(segment) { + switch(segment.direction) { + case "down": + return { top: segment.top + 1, left: segment.left }; + case "up": + return { top: segment.top - 1, left: segment.left }; + case "right": + return { top: segment.top, left: segment.left + 1 } + case "left": + return { top: segment.top, left: segment.left - 1 } + default: + return segment; + } +} + +var segmentFurtherForwardThan = function(index, snake) { + return snake[index - 1] || snake[index]; +} + +var moveSnake = function(snake) { + return snake.map(function(oldSegment, segmentIndex) { + var newSegment = moveSegment(oldSegment); + newSegment.direction = segmentFurtherForwardThan(segmentIndex, snake).direction; + return newSegment; + }); +} + +var growSnake = function(snake) { + var indexOfLastSegment = snake.length - 1; + var lastSegment = snake[indexOfLastSegment]; + snake.push({ top: lastSegment.top, left: lastSegment.left }); + return snake; +} + +var advanceGame = function() { + snake = moveSnake(snake); + if (CHUNK.detectCollisionBetween([apple], snake)) { + snake = growSnake(snake); + apple = CHUNK.randomLocation(); + } + if (CHUNK.detectCollisionBetween(snake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! you hit a wall!"); + } + draw(snake, apple); +} + +var changeDirection = function(direction) { + snake[0].direction = direction; +} + +var apple = CHUNK.randomLocation(); +var snake = [{ top: 1, left: 0, direction: "down" }, { top: 0, left: 0, direction: "down" }]; + +CHUNK.executeNTimesPerSecond(advanceGame, 1); +CHUNK.onArrowKey(changeDirection); diff --git a/sites/javascript-snake-game/js/lesson-13.js b/sites/javascript-snake-game/js/lesson-13.js new file mode 100644 index 000000000..8b3134a5f --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-13.js @@ -0,0 +1,77 @@ +var draw = function(snakeToDraw, apple) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableApple = { color: "red", pixels: [apple] }; + var drawableObjects = [drawableSnake, drawableApple]; + CHUNK.draw(drawableObjects); +} + +var moveSegment = function(segment) { + switch(segment.direction) { + case "down": + return { top: segment.top + 1, left: segment.left }; + case "up": + return { top: segment.top - 1, left: segment.left }; + case "right": + return { top: segment.top, left: segment.left + 1 } + case "left": + return { top: segment.top, left: segment.left - 1 } + default: + return segment; + } +} + +var segmentFurtherForwardThan = function(index, snake) { + return snake[index - 1] || snake[index]; +} + +var moveSnake = function(snake) { + return snake.map(function(oldSegment, segmentIndex) { + var newSegment = moveSegment(oldSegment); + newSegment.direction = segmentFurtherForwardThan(segmentIndex, snake).direction; + return newSegment; + }); +} + +var growSnake = function(snake) { + var tipOfTailIndex = snake.length - 1; + var tipOfTail = snake[tipOfTailIndex]; + snake.push({ top: tipOfTail.top, left: tipOfTail.left }); + return snake; +} + +var ate = function(snake, otherThing) { + var head = snake[0]; + return CHUNK.detectCollisionBetween([head], otherThing); +} + +var advanceGame = function() { + var newSnake = moveSnake(snake); + + if (ate(newSnake, snake)) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! You ate yourself!"); + } + + if (ate(newSnake, [apple])) { + newSnake = growSnake(newSnake); + apple = CHUNK.randomLocation(); + } + + if (ate(newSnake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! you hit a wall!"); + } + + snake = newSnake; + draw(snake, apple); +} + +var changeDirection = function(direction) { + snake[0].direction = direction; +} + +var apple = CHUNK.randomLocation(); +var snake = [{ top: 1, left: 0, direction: "down" }, { top: 0, left: 0, direction: "down" }]; + +CHUNK.executeNTimesPerSecond(advanceGame, 1); +CHUNK.onArrowKey(changeDirection); diff --git a/sites/javascript-snake-game/js/lesson-2.js b/sites/javascript-snake-game/js/lesson-2.js new file mode 100644 index 000000000..1840eec22 --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-2.js @@ -0,0 +1,4 @@ +var snake = [{ top: 0, left: 0}]; +var drawableSnake = { color: "green", pixels: snake }; +var drawableObjects = [drawableSnake]; +CHUNK.draw(drawableObjects); diff --git a/sites/javascript-snake-game/js/lesson-3.js b/sites/javascript-snake-game/js/lesson-3.js new file mode 100644 index 000000000..1788fb86a --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-3.js @@ -0,0 +1,7 @@ +var drawSnake = function(snakeToDraw) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); +} +var snake = [{ top: 0, left: 0}]; +drawSnake(snake); diff --git a/sites/javascript-snake-game/js/lesson-4.js b/sites/javascript-snake-game/js/lesson-4.js new file mode 100644 index 000000000..a60931b6a --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-4.js @@ -0,0 +1,15 @@ +var drawSnake = function(snakeToDraw) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); +} + +var moveSnake = function(snake) { + var oldSegment = snake[0]; + var newSegment = { top: oldSegment.top + 1, left: oldSegment.left }; + var newSnake = [newSegment]; + return newSnake; +} + +var snake = [{ top: 0, left: 0}]; +drawSnake(snake); diff --git a/sites/javascript-snake-game/js/lesson-5.js b/sites/javascript-snake-game/js/lesson-5.js new file mode 100644 index 000000000..0b2c2ba93 --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-5.js @@ -0,0 +1,21 @@ +var drawSnake = function(snakeToDraw) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); +} + + +var moveSnake = function(snake) { + var oldSegment = snake[0]; + var newSegment = { top: oldSegment.top + 1, left: oldSegment.left }; + var newSnake = [newSegment]; + return newSnake; +} + +var advanceGame = function() { + snake = moveSnake(snake); + drawSnake(snake); +} + +var snake = [{ top: 0, left: 0}]; +CHUNK.executeNTimesPerSecond(advanceGame, 1); diff --git a/sites/javascript-snake-game/js/lesson-6.js b/sites/javascript-snake-game/js/lesson-6.js new file mode 100644 index 000000000..dbc3d9cce --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-6.js @@ -0,0 +1,35 @@ +var drawSnake = function(snakeToDraw) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); +} + +var moveSegment = function(segment) { + if (segment.direction === "down") { + return { top: segment.top + 1, left: segment.left } + } else if (segment.direction === "up") { + return { top: segment.top - 1, left: segment.left } + } else if (segment.direction === "right") { + return { top: segment.top, left: segment.left + 1 } + } else if (segment.direction === "left") { + return { top: segment.top, left: segment.left - 1 } + } + return segment; +} + +var moveSnake = function(snake) { + var oldSegment = snake[0]; + var newSegment = moveSegment(oldSegment); + newSegment.direction = oldSegment.direction; + var newSnake = [newSegment]; + return newSnake; +} + + +var advanceGame = function() { + snake = moveSnake(snake); + drawSnake(snake); +} + +var snake = [{ top: 0, left: 0, direction: "down" }]; +CHUNK.executeNTimesPerSecond(advanceGame, 1); diff --git a/sites/javascript-snake-game/js/lesson-7.js b/sites/javascript-snake-game/js/lesson-7.js new file mode 100644 index 000000000..ef1a0e578 --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-7.js @@ -0,0 +1,41 @@ +var drawSnake = function(snakeToDraw) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); +} + +var moveSegment = function(segment) { + switch(segment.direction) { + case "down": + return { top: segment.top + 1, left: segment.left }; + case "up": + return { top: segment.top - 1, left: segment.left }; + case "right": + return { top: segment.top, left: segment.left + 1 } + case "left": + return { top: segment.top, left: segment.left - 1 } + default: + return segment; + } +} + +var moveSnake = function(snake) { + var oldSegment = snake[0]; + var newSegment = moveSegment(oldSegment); + newSegment.direction = oldSegment.direction; + var newSnake = [newSegment]; + return newSnake; +} + +var advanceGame = function() { + snake = moveSnake(snake); + drawSnake(snake); +} + +var changeDirection = function(direction) { + snake[0].direction = direction; +} + +var snake = [{ top: 0, left: 0, direction: "down" }]; +CHUNK.executeNTimesPerSecond(advanceGame, 1); +CHUNK.onArrowKey(changeDirection); diff --git a/sites/javascript-snake-game/js/lesson-8.js b/sites/javascript-snake-game/js/lesson-8.js new file mode 100644 index 000000000..41466e510 --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-8.js @@ -0,0 +1,46 @@ +var drawSnake = function(snakeToDraw) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); +} + +var moveSegment = function(segment) { + switch(segment.direction) { + case "down": + return { top: segment.top + 1, left: segment.left }; + case "up": + return { top: segment.top - 1, left: segment.left }; + case "right": + return { top: segment.top, left: segment.left + 1 } + case "left": + return { top: segment.top, left: segment.left - 1 } + default: + return segment; + } +} + +var moveSnake = function(snake) { + var oldSegment = snake[0]; + var newSegment = moveSegment(oldSegment); + newSegment.direction = oldSegment.direction; + var newSnake = [newSegment]; + return newSnake; +} + +var advanceGame = function() { + snake = moveSnake(snake); + if (CHUNK.detectCollisionBetween(snake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! you hit a wall!"); + } + drawSnake(snake); +} + +var changeDirection = function(direction) { + snake[0].direction = direction; +} + +var snake = [{ top: 0, left: 0, direction: "down" }]; + +CHUNK.executeNTimesPerSecond(advanceGame, 1); +CHUNK.onArrowKey(changeDirection); diff --git a/sites/javascript-snake-game/js/lesson-9.js b/sites/javascript-snake-game/js/lesson-9.js new file mode 100644 index 000000000..d80607e46 --- /dev/null +++ b/sites/javascript-snake-game/js/lesson-9.js @@ -0,0 +1,49 @@ +var drawSnake = function(snakeToDraw) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); +} + +var moveSegment = function(segment) { + switch(segment.direction) { + case "down": + return { top: segment.top + 1, left: segment.left }; + case "up": + return { top: segment.top - 1, left: segment.left }; + case "right": + return { top: segment.top, left: segment.left + 1 } + case "left": + return { top: segment.top, left: segment.left - 1 } + default: + return segment; + } +} + +var moveSnake = function(snake) { + var newSnake = []; + snake.forEach(function(oldSegment) { + var newSegment = moveSegment(oldSegment); + newSegment.direction = oldSegment.direction; + newSnake.push(newSegment); + }); + + return newSnake; +} + +var advanceGame = function() { + snake = moveSnake(snake); + if (CHUNK.detectCollisionBetween(snake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! you hit a wall!"); + } + drawSnake(snake); +} + +var changeDirection = function(direction) { + snake[0].direction = direction; +} + +var snake = [{ top: 1, left: 0, direction: "down" }, { top: 0, left: 0, direction: "down" }]; + +CHUNK.executeNTimesPerSecond(advanceGame, 1); +CHUNK.onArrowKey(changeDirection); diff --git a/sites/javascript-snake-game/js/snake.js b/sites/javascript-snake-game/js/snake.js new file mode 100644 index 000000000..1af3ef07d --- /dev/null +++ b/sites/javascript-snake-game/js/snake.js @@ -0,0 +1,81 @@ +var draw = function(snakeToDraw, apple) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableApple = { color: "red", pixels: [apple] }; + var drawableObjects = [drawableSnake, drawableApple]; + CHUNK.draw(drawableObjects); +} + +var moveSegment = function(segment) { + switch(segment.direction) { + case "down": + return { top: segment.top + 1, left: segment.left }; + case "up": + return { top: segment.top - 1, left: segment.left }; + case "right": + return { top: segment.top, left: segment.left + 1 } + case "left": + return { top: segment.top, left: segment.left - 1 } + default: + return segment; + } +} + +var segmentFurtherForwardThan = function(index, snake) { + return snake[index - 1] || snake[index]; +} + +var moveSnake = function(snake) { + return snake.map(function(oldSegment, segmentIndex) { + var newSegment = moveSegment(oldSegment); + newSegment.direction = segmentFurtherForwardThan(segmentIndex, snake).direction; + return newSegment; + }); +} + +var growSnake = function(snake) { + var tipOfTailIndex = snake.length - 1; + var tipOfTail = snake[tipOfTailIndex]; + snake.push({ top: tipOfTail.top, left: tipOfTail.left }); + return snake; +} + + +// The last thing we need to do is check to see if the snake ran into itself. +var ate = function(snake, otherThing) { + var head = snake[0]; + return CHUNK.detectCollisionBetween([head], otherThing); +} + +var advanceGame = function() { + var newSnake = moveSnake(snake); + + if (ate(newSnake, snake)) { + CHUNK.endGame(); + return CHUNK.flashMessage("Whoops! You ate yourself!"); + } + // Now we just have to check if the newSnake ate it's previous self to see if + // there was a collision! + + if (ate(newSnake, [apple])) { + newSnake = growSnake(newSnake); + apple = CHUNK.randomLocation(); + } + + if (ate(newSnake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + return CHUNK.flashMessage("Whoops! you hit a wall!"); + } + + snake = newSnake; + draw(snake, apple); +} + +var changeDirection = function(direction) { + snake[0].direction = direction; +} + +var apple = CHUNK.randomLocation(); +var snake = [{ top: 1, left: 0, direction: "down" }, { top: 0, left: 0, direction: "down" }]; + +CHUNK.executeNTimesPerSecond(advanceGame, 1); +CHUNK.onArrowKey(changeDirection); diff --git a/sites/javascript-snake-game/lesson-1.step b/sites/javascript-snake-game/lesson-1.step new file mode 100644 index 000000000..18b53bc7b --- /dev/null +++ b/sites/javascript-snake-game/lesson-1.step @@ -0,0 +1,165 @@ +goals do + goal "Setup your programming environment" + goal "Be able to use the basic building blocks of JavaScript code" + goal "Do simple calculations" + goal "Use and understand variables" + goal "Use and understand arrays" + goal "Use loops and conditional statements" +end + +overview do + message <<-MARKDOWN + JavaScript is a fully featured programming language with variables, loops, + and conditionals. Just like Ruby, Java, Python, and PHP, you can use JavaScript + to do math, model large systems, and perform complex calculations – all in your browser! + In this lesson, we'll learn about the fundamentals of the JavaScript programming language. + MARKDOWN +end + +steps do + step "Launch your programming environment" do + message <<-MARKDOWN + + When programming, you'll generally want these tools on hand: + + * Your browser to see the code running (I recommend Chrome) + * A text editor to change the code (Railsbridge recommends Atom, but you can try a different editor) + * A javascript console so you can experiment and print out debugging + messages. This is built into your browser. + + Some helpful links for browser consoles: + + * How to launch dev tools in chrome. + * How to launch dev tools in internet explorer. + * How to launch dev tools in firefox. + * How to launch dev tools in safari. + + Once you have these tools available, we need to open the files we'll be working with: + + * Open game/snake.js in your text editor + * Open game/index.html in your browser + + + Now open up your browser's developer tools, and click over to the console tab. Type the following code: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + console.log('programming!'); + JAVASCRIPT + + message <<-MARKDOWN + Press enter. Your browser should now look like this: + image of browser console with console.log + + Congrats, you just wrote your first lines of JavaScript code! __console.log__ is an important function – + it allows you to print information to the browser's console. It's very helpful in debugging! You can also + use the __alert__ function to make a message pop up in the browser. Try it out! + MARKDOWN + end + + step do + message "Next try some simple math that's built into JavaScript. Type these lines into console:" + + source_code :javascript, <<-JAVASCRIPT + 3 + 3 + 7 * 6 + JAVASCRIPT + end + + step do + message "**Variables** are names with values assigned to them." + + source_code :javascript, <<-JAVASCRIPT + var myVariable = 5 + JAVASCRIPT + + message "This assigns the value `5` to the name `myVariable`." + end + + step do + message "You can also do math with variables:" + source_code :javascript, <<-JAVASCRIPT + myVariable + 2 + myVariable * 3 + JAVASCRIPT + end + + step do + message "Variables can also hold more than just numbers and text. Another type of data in Javascript is called an **array**." + + source_code :javascript, 'var fruits = ["kiwi", "strawberry", "plum"]' + message <<-MARKDOWN + Here we're using the variable `fruits` to hold a collection of fruit names. + An array, designated by the `[ ]` (square-brackets), is a list of data that can be referenced by its index. + The index is the position inside the array, each separated by a comma. Arrays use 0-based indices, so the first element in an array is at Index 0, the second is at index 1, and so on. + For example, in the array above, to get the string 'strawberry' in Javascript, you would type 'fruits[1]', which Javascript understands as: get the value at index 1 in the array fruits. + MARKDOWN + end + + step do + message "Arrays are a type of __object__ in JavaScript that are designated by the use of square brackets. Objects of all types in javascript often include helpful attributes!" + + source_code :javascript, <<-JAVASCRIPT + fruits.length + JAVASCRIPT + + message <<-MARKDOWN +Objects can also have __functions__, which can be helpful for altering objects and learning more about them. +Functions are __invoked__ with parentheses, which causes them to run. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + fruits.push("orange") + fruits.slice(1) + JAVASCRIPT + + message <<-MARKDOWN +The __push__ function allows us to add new items to an array. the slice function returns a new array with +with everything to the right of the __index__ we provided. Here, we passed the function the number 1, so +slice returned an array with everything after the first element in the array. (Note that the first element is assigned 0 as its index rather than 1.) + MARKDOWN + end + + step do + message "You can also make your own functions:" + + source_code :javascript, <<-JAVASCRIPT + var pluralize = function(word) { + return word + "s" + } + pluralize("kiwi") + JAVASCRIPT + + message "Functions take **parameters**, which are the variables they work on. In this case, we made a function called pluralize that takes one parameter, a word." + + message "Functions can also return data. In this case, pluralize returns the word with an 's' added to the end of it. In order for a function to return something, you have to use the __return__ keyword." + end + + + step do + message "Arrays have a function called **forEach** which iterates through the list running code on each item. It takes another function as a parameter." + source_code :javascript, <<-JAVASCRIPT + fruits.forEach(function(fruit) { + console.log(fruit) + }) + JAVASCRIPT + message "This takes the first item from the `fruits` array (`\"strawberry\"`), assigns it to the variable `fruit`, and runs the code between curly brackets. Then it does the same thing for each other item in the list. The code above should print a list of the fruits." + end + + step do + message "A **conditional** runs code only when a statement evaluates to true." + + source_code :javascript, <<-JAVASCRIPT + if(myVariable > 1) { + console.log('YAY') + } + JAVASCRIPT + + message "This prints `YAY!` if the value stored in `myVariable` is greater than 1." + + message "Try changing the `>` in the conditional to a `<`." + end + +end + +next_step "lesson-2" diff --git a/sites/javascript-snake-game/lesson-10.step b/sites/javascript-snake-game/lesson-10.step new file mode 100644 index 000000000..966eaf991 --- /dev/null +++ b/sites/javascript-snake-game/lesson-10.step @@ -0,0 +1,73 @@ +# Lesson 10 - Making the Tail Follow the Head + +markdown <<-MARKDOWN + To make the tail follow the head, we're going to assign the direction of the + segment further forward in snake to the current segment when it moves. This way + all segments follow the same path! + + Let's define a new function called `segmentFurtherForwardThan` which takes a + segment index and a snake and returns the segment closer to the head. Let's + start with: + + ```js + var segmentFurtherForwardThan = function(index, snake) { + return snake[index - 1] + } + ``` + + While this seems OK, in reality it has a nefarious bug! Open your console and type: + + ```js + segmentFurtherForwardThan(1, snake); + ``` + + This returns a segment object, which looks great! But what happens if we try + a index of 0? + + ```js + segmentFurtherForwardThan(0, snake); + ``` + + Instead of a segment, we get `undefined`! This means we can't get a + direction! Let's use an `if` statement to return the segment at the index if + there isn't a segment further forward. Change `segmentFurtherForwardThan` to: + + + ```js + var segmentFurtherForwardThan = function(index, snake) { + if (snake[index - 1] === undefined) { + return snake[index]; + } else { + return snake[index - 1]; + } + } + ``` + + Now when you run `segmentFurtherForwardThan(0, snake)` it will return the + snakes head instead of undefined. Excellent! Now we get to change moveSnake to + use `segmentFurtherForwardThan` when it assigns the segments direction. Change `moveSnake` to: + + ```js + var moveSnake = function(snake) { + return snake.map(function(oldSegment, segmentIndex) { + var newSegment = moveSegment(oldSegment); + newSegment.direction = segmentFurtherForwardThan(segmentIndex, snake).direction; + return newSegment; + }); + } + ``` + + Great! Now each segment will follow the segment ahead of it! +MARKDOWN + +js_expected_results 'lesson-10' + +markdown <<-MARKDOWN + ### Syntax Breakdown + + `array.map` and `array.forEach` pass 3 arguments to the function it is given: the + first is the element in the collection, the second is its index (or location in the collection) and + the third is the collection itself. +MARKDOWN + +next_step 'lesson-11' diff --git a/sites/javascript-snake-game/lesson-11.step b/sites/javascript-snake-game/lesson-11.step new file mode 100644 index 000000000..07a4515a8 --- /dev/null +++ b/sites/javascript-snake-game/lesson-11.step @@ -0,0 +1,46 @@ +# Lesson 11 - Making an Apple Appear on the Screen + +markdown <<-MARKDOWN + Let's make it so an apple appears on the screen! + + ```js + var draw = function(snakeToDraw, apple) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableApple = { color: "red", pixels: [apple] }; + var drawableObjects = [drawableSnake, drawableApple]; + CHUNK.draw(drawableObjects); + } + ``` + + We renamed the `drawSnake` function and let it take multiple arguments so it + can draw an apple. + + Next, let's make sure we pass the `apple` to the `draw` function, when we call it! + + ```js + var advanceGame = function() { + snake = moveSnake(snake); + if (CHUNK.detectCollisionBetween(snake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! you hit a wall!"); + } + draw(snake, apple); + } + ``` + + Finally, let's create an apple to be drawn! + + ```js + var apple = { top: 8, left: 10 }; + ``` +MARKDOWN + +js_expected_results 'lesson-11' + +markdown <<-MARKDOWN + ### Play Time! + + * Why do you think we put `[]` around the apple in the `draw` function? +MARKDOWN + +next_step 'lesson-12' diff --git a/sites/javascript-snake-game/lesson-12.step b/sites/javascript-snake-game/lesson-12.step new file mode 100644 index 000000000..f78637899 --- /dev/null +++ b/sites/javascript-snake-game/lesson-12.step @@ -0,0 +1,56 @@ +# Lesson 12 - Growing the snake when it eats an apple! + +markdown <<-MARKDOWN + Now that an apple appears on the screen, we can make eating it grow + the snake. + + To do that, we'll create a stationary copy of the snakes last segment. When + the snake moves next the copied segment will stay in place, causing it to + "grow" on the screen. + + ```js + var growSnake = function(snake) { + var indexOfLastSegment = snake.length - 1; + var lastSegment = snake[indexOfLastSegment]; + snake.push({ top: lastSegment.top, left: lastSegment.left }); + return snake; + } + ``` + + Now that we have a function to grow the snake, let's check for a collision + between the apple and the snake whenever the game advances. Let's change the + `advanceGame` function so it looks like this: + + ```js + var advanceGame = function() { + snake = moveSnake(snake); + if (CHUNK.detectCollisionBetween([apple], snake)) { + snake = growSnake(snake); + apple = CHUNK.randomLocation(); + } + if (CHUNK.detectCollisionBetween(snake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! you hit a wall!"); + } + draw(snake, apple); + } + ``` +MARKDOWN + +js_expected_results 'lesson-12' + +markdown <<-MARKDOWN + ### Syntax Breakdown + + `array.length` tells us how many elements are in a list. Arrays are zero + indexed, so we subtract one to get the index of the last element in the + list. + + ### Play Time! + + * When is the newly added segment given a direction to move in? + * Why do we put the `apple` variable inside of `[]` when we call + `detectCollision` +MARKDOWN + +next_step 'lesson-13' diff --git a/sites/javascript-snake-game/lesson-13.step b/sites/javascript-snake-game/lesson-13.step new file mode 100644 index 000000000..4c7670176 --- /dev/null +++ b/sites/javascript-snake-game/lesson-13.step @@ -0,0 +1,60 @@ +# Lesson 13 - Make the snake die when it eats itself! + +markdown <<-MARKDOWN + The last thing we need to do is check to see if the snake ran into itself. + + ```js + var ate = function(snake, otherThing) { + var head = snake[0]; + return CHUNK.detectCollisionBetween([head], otherThing); + } + ``` + + We've created an `ate` function that accepts a snake and the previous version of the snake. We can also think about passing the `ate` function other things, which we'll get into in a bit. In `ate` we also reuse the `detectCollisionBetween` to find out if there's a collision. + + Now we just have to check if the `newSnake` ate it's previous self to see if there was a collision! We do this by calling the `ate` function inside of `advanceGame`. + + ```js + var advanceGame = function() { + var newSnake = moveSnake(snake); + + if (ate(newSnake, snake)) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! You ate yourself!"); + } + + if (ate(newSnake, [apple])) { + newSnake = growSnake(newSnake); + apple = CHUNK.randomLocation(); + } + + if (ate(newSnake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! you hit a wall!"); + } + + snake = newSnake; + draw(snake, apple); + } + ``` + + Notice, we've abstracted out the `ate` function by reusing it. It can now determine if the snake ate itself, an apple or the wall. + + If your snake no longer moves after this step, make sure that you assigned `snake = newSnake` in the `advanceGame` function. +MARKDOWN + +js_expected_results 'lesson-13' + +markdown <<-MARKDOWN + ### Congratulations! You've built the snake game! + + If you want your friends and family to be able to play your game, you'll need + to put it online. Lesson 14 includes instructions on putting your game online. + + ### Helpful Links + + 1. Article on Abstraction from Princeton +MARKDOWN + +next_step 'lesson-14' diff --git a/sites/javascript-snake-game/lesson-14.step b/sites/javascript-snake-game/lesson-14.step new file mode 100644 index 000000000..c45903cde --- /dev/null +++ b/sites/javascript-snake-game/lesson-14.step @@ -0,0 +1,10 @@ +insert '../javascript-to-do-list/_deploying_your_site' + +message <<-MARKDOWN +# Credits + +This curriculum was originally written by +[Zassmin Montes de Oca](http://zassmin.com/) and +[Zee Spencer](http://www.zeespencer.com/), +and has been improved by all sorts of [lovely RailsBridge volunteers](https://github.com/railsbridge/docs/commits/master/sites/en/javascript-snake-game). +MARKDOWN diff --git a/sites/javascript-snake-game/lesson-2.step b/sites/javascript-snake-game/lesson-2.step new file mode 100644 index 000000000..ef1924d9f --- /dev/null +++ b/sites/javascript-snake-game/lesson-2.step @@ -0,0 +1,119 @@ +goals do + goal "Create your first snake" + goal "How to use a third party library as an 'engine' to power your game" +end + +steps do + + step "Now back to our snakes" do + + message "To get started, we want to create a variable that represents the snake. Add the following line to the beginning of game/snake.js:" + + source_code :javascript, <<-JAVASCRIPT + var snake = [{ top: 0, left: 0}]; + JAVASCRIPT + + markdown <<-MARKDOWN +

Now that we have this amazing snake we want to show it off, right? The + CHUNK game engine makes it easy to draw objects on the screen. Let's tell CHUNK + to 'draw' our snake!

+ MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + var drawableSnake = { color: "green", pixels: snake }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); + JAVASCRIPT + + markdown <<-MARKDOWN + CHUNK's 'draw' function expects to be given a collection of objects. This + means we must create an array and place the drawableSnake inside of it. Then we + can pass that array into 'CHUNK.draw' + + To see what your code does you'll need to save your 'snake.js' file and + refresh the browser window that has your index.html in it. We recommend doing + this early and often! + + Congratulations! You've drawn a (very short!) snake on the screen. + MARKDOWN + end + +js_expected_results 'lesson-2' + + step "What is CHUNK?" do + markdown <<-MARKDOWN + When programming, you'll frequently need to solve problems that have been + solved before. In many cases someone who solved the problem already has + packaged their code into a 'library'. + + Good libraries tend to: + + * Help you think about the problem you're solving more clearly + * Be well documented + * Solve a small set of closely related problems + + We'll be using the CHUNK library throughout this tutorial. CHUNK is an + example of a library whose job is to: + + * Draw chunky-pixels on the screen! + * Respond to user input + * Start and end a game + * Check for things running into each other + * Be reasonably approachable for novice developers + MARKDOWN + end + + step "Understanding the Code" do + message "The code you wrote used the CHUNK library to draw a 1px snake on the screen. Let's try to understand the code as a whole a bit better." + + source_code :javascript, <<-JAVASCRIPT + var snake = [{ top: 0, left: 0}]; + var drawableSnake = { color: "green", pixels: snake }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); + JAVASCRIPT + + markdown <<-MARKDOWN + The first line declares the variable 'snake' and assigns an array to it. Within that array is one item which is defined by a set of curly-braces. These curly-braces represent another Javascript data type called an Object which is also referred to as a 'hash map'. + + Data stored in an hash map object involves two parts, a key and a value. In our code, we have two pieces of data in the object, a 'top' value and a 'left' value. The key is used to get the data from the object. So in our example, if we need to know what the current 'left' value of snake is, we type 'snake.left' which will return a value of '0'. + + On the second line, we declare the variable 'drawableSnake' and assign an object to its value. The object has two pieces, the first has the key 'color' and the value '"green"', and the second has the key 'pixels' which has the value 'snake'. 'snake' is the variable we defined on the previous line, which means 'drawableSnake.pixels' will return us the object we created on the first line. + + The third line declares a variable 'drawableObjects', which we assign the value of an array whose only data is the 'drawableSnake' declared on the previous line. We do this because our CHUNK engine is looking for a list (an array) or objects to draw. + + Finally, we call the CHUNK library's built-in function 'draw' and pass it the argument 'drawableObjects' which was declared on the previous line. We'll explore functions and arguments in future lessons. + MARKDOWN + end + + step "Comments" do + markdown <<-MARKDOWN + Sometimes it's nice to leave a clarifying note to future readers of the code (this could very well be you!) in plain English. This is called a comment, and it is only intended for humans to read, the computer knows to ignore them. + Here's what comments look like in JavaScript: + + ```js + // this is a one line comment, here we're creating an array + var drawableObjects = [drawableSnake]; + /* + this is a multi line comment + here we're drawing the snake + */ + CHUNK.draw(drawableObjects); + ``` + MARKDOWN + end + + step "Play Time!" do + markdown <<-MARKDOWN + * Add comments above each line explaining what it does in plain old english. + * Change the color of the snake. + * Make the snake longer than just 1 segment! + * Draw something in addition to the snake. Perhaps an apple or a wall? Make + sure it's a different color! + MARKDOWN + end + +end + +next_step "lesson-3" + diff --git a/sites/javascript-snake-game/lesson-3.step b/sites/javascript-snake-game/lesson-3.step new file mode 100644 index 000000000..dee42854c --- /dev/null +++ b/sites/javascript-snake-game/lesson-3.step @@ -0,0 +1,97 @@ +# Lesson 3 - Drawing a Snake Multiple Times + +markdown <<-MARKDOWN + Now that we have a snake being drawn on the screen, We're going + to want to be able to draw it many times, for instance every time + it moves. + + To do this, we'll define a function which draws the snake + it's given. Add the following line to the beginning of + game/snake.js: + + ```js + var drawSnake = function(snakeToDraw) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); + } + ``` + + Functions are a way to group instructions so they're easy for humans to + understand or so the computer can use them over and over without us writing + the code out multiple times. + + Functions exist somewhat independently from the code around them; this means + that if the function needs variables from the rest of the code base they should + be passed in as arguments. The `drawSnake` function takes a single argument, + `snakeToDraw` which it then uses to create the `drawableSnake`. + + Now all we have to do is call drawSnake with a snake and it will be drawn on + the screen! Write the following under the drawSnake function. + + ```js + var snake = [{ top: 0, left: 0}]; + drawSnake(snake); + ``` + + Because Javascript interprets code from top to bottom we can't call + functions until after we've defined them. Make sure you put this code at the + end of the `snake.js` file. +MARKDOWN + +js_expected_results 'lesson-3' + +markdown <<-MARKDOWN + ### Play Time! + + * Open up the console and play around with making the + snake move by calling `drawSnake` with snakes that have + different `top` and `left` values for their segment. + * Try to access `drawableSnake` from outside of the `drawSnake` function. + Check the javascript console. + * Try to access `snake` inside of the `drawSnake` function. Check the + javascript console. + * Why do you suppose you could access one but not the other? Scope is how you + know what variables a function or piece of code has access too. + Understanding scope is hugely important when reading and writing code. + + ### A bit more about Functions + + Functions are where we'll be storing most of the logic as we develop our snake game. Let's take apart the function created above just to fully understand what is happening. + + ```js + var drawSnake = function(snakeToDraw) { + var drawableSnake = { color: "green", pixels: snakeToDraw }; + var drawableObjects = [drawableSnake]; + CHUNK.draw(drawableObjects); + } + var snake = [{ top: 0, left: 0}]; + drawSnake(snake); + ``` + + A function declaration is always going to be the word `function`, then parens `( )` then curly braces `{ }`. The parens are where you declare the arguments the function expects, and the curly braces are where you write out all of the logic that the function will perform. The simplest way to call a function is to write its name followed by parentheses: `drawSnake()`. But we have a function that expects an argument, `snakeToDraw`, so when we call the function in the code we do it by `drawSnake(snake)`. + + You may have noticed that the function `drawSnake` expects an argument of `snakeToDraw`, but when we call the function we're passing in the variable `snake`. The names are different - but the code works fine. What is actually happening is we're passing around the data that the variable `snake` represents (in this case, `[{ top: 0, left: 0}]`), which the function receives and names according to the argument inside its parens, `snakeToDraw`. Inside the function, the data is now available as the variable `snakeToDraw`, which we assign to the `pixels` key inside the `drawableSnake` variable. + + The function then takes `drawableSnake`, puts it inside an array named `drawableObjects`, and calls a function in the CHUNK library called `draw`, that expects the array we just created. + + + ### Syntax Breakdown + + `function() { }` says "Hey, between the curly braces is some + code we want to be able to execute." Functions can be stored inside + variables just like words and numbers and objects and lists. + + `(` and `)` in this case are a way to say that this function expects + a value to be given to it when it is called. These values are called + `arguments`, and can be used inside of the function body (the code between + the curly braces). + + ### Helpful Links + + 1. An article explaining functions + 1. Information on Objects +MARKDOWN + +next_step "lesson-4" diff --git a/sites/javascript-snake-game/lesson-4.step b/sites/javascript-snake-game/lesson-4.step new file mode 100644 index 000000000..df2659f7e --- /dev/null +++ b/sites/javascript-snake-game/lesson-4.step @@ -0,0 +1,60 @@ +# Lesson 4 - Moving a Single Segment Snake + +markdown <<-MARKDOWN + Now that we have a function to draw the snake, we want to move the snake. + Let's write a function which takes a snake and changes its segment's top and left + values. + + To make our lives easier, let's make some assumptions: + + * The snake is only 1 segment long + * The snake always moves down + + By making these assumptions it keeps the code clear and makes it easy to + take small steps. Add the following code to your snake.js file: + + ```js + var moveSnake = function(snake) { + var oldSegment = snake[0]; + var newSegment = { top: oldSegment.top + 1, left: oldSegment.left }; + var newSnake = [newSegment]; + return newSnake; + } + ``` + + Once we have this `moveSnake` function, we can call it and it will give us a + snake whose segment's location has changed. We can then call `drawSnake` with + that snake and see it move across the screen! + + Type the following lines into your javascript console a few times: + + ``` + snake = moveSnake(snake); + drawSnake(snake); + ``` +MARKDOWN + +js_expected_results 'lesson-4' + +markdown <<-MARKDOWN + ### Play Time! + + * Why does adding 1 to a segment's top value move it down? + * Can you make the snake move right instead of down? + * What happens to the newSnake variable? Can you use it outside of the + moveSnake function body (within the curly braces)? + * Why are we re-assigning snake the results of moveSnake? + + ### Syntax Breakdown + + `return value` - Tells the function to immediately respond with whatever + value it is given. In this case, the newSnake. + + `array[location]` -"Get the value at this location in the array." Arrays in + most programming languages are "zero-indexed" i.e. the first element is at 0, + the second is at 1, and so on. + + `+` adds the values on the left and right together. Surprise, eh? +MARKDOWN + +next_step 'lesson-5' diff --git a/sites/javascript-snake-game/lesson-5.step b/sites/javascript-snake-game/lesson-5.step new file mode 100644 index 000000000..3c54eb2a6 --- /dev/null +++ b/sites/javascript-snake-game/lesson-5.step @@ -0,0 +1,42 @@ +# Lesson 5 - Make CHUNK move the snake for us + +markdown <<-MARKDOWN + Hooray! We can both draw and move the snake! Of course, having our users + copy and paste lines of code into their javascript console isn't a wonderful + idea, so we're going to have CHUNK execute the move and draw commands for us. + + We'll start with an `advanceGame` function + + ```js + var advanceGame = function() { + snake = moveSnake(snake); + drawSnake(snake); + } + ``` + + To get this function running we'll use our CHUNK game engine to CALL the `executeNTimesPerSecond` function. It does exactly that, to do this `executeNTimesPerSecond` takes in two arguments. The first argument advances the game, and the second tells it how many times to move per-second. + + ```js + CHUNK.executeNTimesPerSecond(advanceGame, 1); + ``` + + Bam! Now the snake moves down until it runs off the game screen. + + Because functions are just another kind of value (like numbers, arrays, objects, strings, + etc.) we can pass them as arguments to other functions. + +MARKDOWN + +js_expected_results 'lesson-5' + +markdown <<-MARKDOWN + ### Play Time! + + * How can you increase the speed at which the snake moves? + + ### Helpful Links + + 1. Writing functions with more than one argument +MARKDOWN + +next_step "lesson-6" diff --git a/sites/javascript-snake-game/lesson-6.step b/sites/javascript-snake-game/lesson-6.step new file mode 100644 index 000000000..41e5c5163 --- /dev/null +++ b/sites/javascript-snake-game/lesson-6.step @@ -0,0 +1,95 @@ +# Lesson 6 - Snake can move in every direction + +markdown <<-MARKDOWN + Now that the snake is moving on it's own, let's make it so it can move in + different directions! + + First, we'll create a function that moves the segment based upon it's + direction (down, up, right, left): + + ```js + var moveSegment = function(segment) { + if (segment.direction === "down") { + return { top: segment.top + 1, left: segment.left } + } else if (segment.direction === "up") { + return { top: segment.top - 1, left: segment.left } + } else if (segment.direction === "right") { + return { top: segment.top, left: segment.left + 1 } + } else if (segment.direction === "left") { + return { top: segment.top, left: segment.left - 1 } + } + return segment; + } + ``` + + `if` statements choose what code to run. Here a series of `if` and `else if`s + to return a new object with coordinates offset from the passed in segment. + + When the passed in segment doesn't have a direction or it doesn't match the + directions in our `if`/`else if` then we return the original segment object. + + Our returned segment doesn't have a direction; which means the snake will + only move once! To keep the snake moving we need to assign every `newSegment` a + direction. + + Let's change our `moveSnake` function to give the `newSegment` the same + direction as the `oldSegment`. This will keep the snake moving in the same + direction. + + ```js + var moveSnake = function(snake) { + var oldSegment = snake[0]; + var newSegment = moveSegment(oldSegment); + newSegment.direction = oldSegment.direction; + var newSnake = [newSegment]; + return newSnake; + } + ``` + + Next add a direction to our initial snakes segments. + + ```js + var snake = [{ top: 0, left: 0, direction: "down" }]; + ``` + + Now our snake knows where to start moving! +MARKDOWN + +js_expected_results 'lesson-6' + +markdown <<-MARKDOWN + ### Switch statement + + A `switch` statement is another way to organize a seried of `if` and `else if`s. Here's an example: + + ```js + switch(color) { + case "red": + return { action: "stop" }; + case "orange": + return { action: "wait" }; + case "green": + return { action: "go" }; + default: + return { action: "unknown" }; + } + ``` + + Can you figure out how to replace our snake game code above with a `switch` statement? (Don't worry, you'll see how we do it in the next lesson) + + ### Play Time! + + * Use a switch statement instead of a series of ifs + * Make sure each direction works! + + ### Syntax Breakdown + + `===` tells the computer to check for EXACT equality. For example: "down" === "down" will return true but "down" === "DOWN" will return false. + + ### Helpful Links + + 1. switch statements + 1. === vs == +MARKDOWN + +next_step "lesson-7" diff --git a/sites/javascript-snake-game/lesson-7.step b/sites/javascript-snake-game/lesson-7.step new file mode 100644 index 000000000..ebd647ef5 --- /dev/null +++ b/sites/javascript-snake-game/lesson-7.step @@ -0,0 +1,32 @@ +# Lesson 7 - Changing the Snakes Movement Direction + +markdown <<-MARKDOWN + To change the direction of the snake, we need to change the direction of + its first segment. Add the following function to snake.js: + + ```js + var changeDirection = function(direction) { + snake[0].direction = direction; + } + ``` + + Next, we have CHUNK call changeDirection when an arrow key is pressed. + Add the following line at the end of snake.js: + + ```js + CHUNK.onArrowKey(changeDirection); + ``` + + This tells CHUNK to call the `changeDirection` function every time an arrow key + is pressed. + + Just like we pass our snake object into the `drawSnake` function, we can pass + the `changeDirection` function into the `onArrowKey` function. + + In JavaScript, we can pass functions into functions just like we can pass + arrays, strings, integers, and objects into functions +MARKDOWN + +js_expected_results 'lesson-7' + +next_step 'lesson-8' \ No newline at end of file diff --git a/sites/javascript-snake-game/lesson-8.step b/sites/javascript-snake-game/lesson-8.step new file mode 100644 index 000000000..bee225526 --- /dev/null +++ b/sites/javascript-snake-game/lesson-8.step @@ -0,0 +1,39 @@ +# Lesson 8 - Ending the Game When A Wall Is Hit + +markdown <<-MARKDOWN + Games aren't much fun if you can't lose them! Let's make it so when you hit + a wall. CHUNK comes with a function to detect collisions between things, so + we'll use that. + + Add an `if` statement to the `advanceGame` function like so: + + ```js + var advanceGame = function() { + snake = moveSnake(snake); + if (CHUNK.detectCollisionBetween(snake, CHUNK.gameBoundaries())) { + CHUNK.endGame(); + CHUNK.flashMessage("Whoops! you hit a wall!"); + } + drawSnake(snake); + } + ``` + + Now, when there is a collision between the snake and the border we can have + CHUNK end the game and let the player know the game is over. +MARKDOWN + +js_expected_results 'lesson-8' + +markdown <<-MARKDOWN + ### Syntax Breakdown + + `CHUNK.detectCollisionBetween` takes two collections of coordinates and + returns true if the collections share any pixels + + `CHUNK.endGame` tells chunk to stop calling the advanceGame function, + effectively ending the game. + + `CHUNK.flashMessage` displays text to the user. +MARKDOWN + +next_step 'lesson-9' diff --git a/sites/javascript-snake-game/lesson-9.step b/sites/javascript-snake-game/lesson-9.step new file mode 100644 index 000000000..4c0744b63 --- /dev/null +++ b/sites/javascript-snake-game/lesson-9.step @@ -0,0 +1,66 @@ +# Lesson 9 - Moving a Multiple Segment Snake + +markdown <<-MARKDOWN + Now that we can crash into a wall, lets have a snake longer than a single + segment! + + Currently, the `moveSnake` function only moves the first segment in the + snake. Let's make it to move every segment! Change `moveSnake` to the + following: + + ```js + var moveSnake = function(snake) { + var newSnake = []; + snake.forEach(function(oldSegment) { + var newSegment = moveSegment(oldSegment); + newSegment.direction = oldSegment.direction; + newSnake.push(newSegment); + }); + + return newSnake; + } + ``` + + `forEach` introduces `looping`. Looping allows us to do something multiple + times. Looping (for, while, forEach, etc) and conditionals (if, switch)` both + allow you to control the flow of the program. + + Now that we can move many segments across the screen, let's add a second + segment to our snake. Change the line that starts with `var snake = ...` to the + following: + + ```js + var snake = [{ top: 1, left: 0, direction: "down" }, { top: 0, left: 0, direction: "down" }]; + ``` +MARKDOWN + +js_expected_results 'lesson-9' + +markdown <<-MARKDOWN + You'll notice that if you play the game, the second segment always just + moves down. This is OK for now! + + ### Syntax Breakdown + + `array.forEach` allows us to call a function with each item in a collection. + This is a very common kind of loop and is very powerful. + + `array.push` adds the element it is given to the array it is called in. + + ### Play Time! + + * `array.map` + is another kind of loop that makes it easy to build a new collection from an + existing collection. Rewrite the `moveSnake` function to use `array.map` + instead of `array.forEach` + * The + `for` + and + `while` + loops are also quite common. They are more generally useful but harder to use + than the collection based `forEach` and `map` functions. How could you write + moveSnake with either `for` or `while`? +MARKDOWN + +next_step 'lesson-10' + diff --git a/sites/javascript-snake-game/zip_content/index.html b/sites/javascript-snake-game/zip_content/index.html new file mode 100644 index 000000000..d83e92eac --- /dev/null +++ b/sites/javascript-snake-game/zip_content/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/sites/javascript-snake-game/zip_content/snake.js b/sites/javascript-snake-game/zip_content/snake.js new file mode 100644 index 000000000..e69de29bb diff --git a/sites/javascript-to-do-list-with-react/AdvancedTodoList.zip-manifest b/sites/javascript-to-do-list-with-react/AdvancedTodoList.zip-manifest new file mode 100644 index 000000000..dee53707a --- /dev/null +++ b/sites/javascript-to-do-list-with-react/AdvancedTodoList.zip-manifest @@ -0,0 +1,6 @@ +zip_content/app.js +zip_content/debut_light.png +zip_content/index.html +zip_content/store.js +zip_content/styles.css +zip_content/console-polyfill.js diff --git a/sites/javascript-to-do-list-with-react/adding_an_item.step b/sites/javascript-to-do-list-with-react/adding_an_item.step new file mode 100644 index 000000000..bcaa0d08c --- /dev/null +++ b/sites/javascript-to-do-list-with-react/adding_an_item.step @@ -0,0 +1,183 @@ +goals do + goal "Allow a user to create a new item." + goal "Understand how to make an AJAX request when prompted by a user's action." +end + +overview do + message <<-MARKDOWN +JavaScript allows us to make our web page dynamic, and responsive to +the actions of our users. In this lesson, we'll allow our users to create +a new item for our list, and ask the server to save it to the database. + +To do so, we're going to use JavaScript's ability to perform a task when a +user has taken an action on the page. JavaScript refers to these actions as +__events__. React makes it easy to listen to these events, and cause the page +to change when necessary. + +Our code will take the following steps. + +1. When the user loads the page, our CreationForm component will start + listening for when the user submits the form at the top of the page. + +2. When a user submits the form (by pressing enter), we will prevent the page + from refreshing, which is the normal behavior for a form. + +3. The CreationForm component will then tell ListStore to add a new item. + +4. ListStore will make an AJAX request to our server, creating an item with the + description our user just provided. + +5. Once the request succeeds, we will update the items variable with our new item. + +6. Finally, we will tell the components to re-render themselves! + +We've already written the first two steps for you. Let's finish the rest! + MARKDOWN +end + + +steps do + step do + message <<-MARKDOWN +In the browser, try creating a new item. You should see an alert pop up with your item's value. Let's take +a look at the CreationForm component to see how that works. Open up index.html in your browser, and find the +component. It looks like this: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + render: function() { + return ( +
+ +
+ ) + }, + + handleSubmit: function(event) { + event.preventDefault() + var description = this.refs.description.getDOMNode().value + this.refs.description.getDOMNode().value = '' + alert('trying to create an item with description ' + description) + } + JAVASCRIPT + + message <<-MARKDOWN +When the form submits, the handleSubmit function runs! This function is preventing the page from refreshing +(using the preventDefault function) and using React to get the new description the user just wrote. See if you +can follow the code – how does the form know to run the handleSubmit function? + +Now, let's remove the alert, and instead tell the ListStore to add an item. Your code should look like this: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + handleSubmit: function(event) { + event.preventDefault() + var description = this.refs.description.getDOMNode().value + this.refs.description.getDOMNode().value = '' + ListStore.addItem(description) + } + JAVASCRIPT + end + + step do + message <<-MARKDOWN +Now, let's write the logic for the addItem function. We're going to ask the server to save +this item into the database. Open up store.js, and add the following code to the addItem +function. Replace 'YOUR-LIST-NAME-HERE' with your list's name. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + addItem: function(itemDescription) { + var creationRequest = $.ajax({ + type: 'POST', + url: "http://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items", + data: { description: itemDescription, completed: false } + }) + }, + JAVASCRIPT + + message <<-MARKDOWN +Try creating an item again. After you submit the form, look at the network tab. A +new request should have occurred to http://listalous.herokuapp.com/ ! + MARKDOWN + end + + step do + message <<-MARKDOWN +Finally, we need to tell the components to re-render themselves. After the creationRequest, +add the following code: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + creationRequest.done(function(itemDataFromServer) { + items.push(itemDataFromServer) + notifyComponents() + }) + JAVASCRIPT + + message <<-MARKDOWN +Try creating an item one more time. Once you hit enter, a new item should appear +on the page! If not, flag an instructor down to help you debug the problem. + MARKDOWN + end +end + +explanation do + + message "Here's what the bottom of store.js should now look like:" + + source_code :javascript, <<-JAVASCRIPT + ListStore = { + + getItems: function() { + return items + }, + + loadItems: function() { + var loadRequest = $.ajax({ + type: 'GET', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/" + }) + + loadRequest.done(function(dataFromServer) { + items = dataFromServer.items + notifyComponents() + }) + }, + + addItem: function(itemDescription) { + var creationRequest = $.ajax({ + type: 'POST', + url: "http://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items", + data: { description: itemDescription, completed: false } + }) + + creationRequest.done(function(itemDataFromServer) { + items.push(itemDataFromServer) + notifyComponents() + }) + }, + toggleCompleteness: function(itemId) {} + } + JAVASCRIPT + + message <<-MARKDOWN + +### The AJAX process + +You've just done something that many JavaScript developers do daily: Use JavaScript +to make a request to a server, and then update the page with the data with which +the server responds. This abstract process is repeated over and over again: + +1. Listen for a user action on the page. +2. Parse out the information the user is submitting. +3. Prevent the default action from occurring, if necessary. +4. Make a request to the server using AJAX. +5. When the request succeeds, parse the data the server sends back. +6. Update the page with the newly received data. + +This process is the basis of most modern web pages! + MARKDOWN +end + +next_step "marking_an_item_as_complete" diff --git a/sites/javascript-to-do-list-with-react/building_complex_applications_with_react.step b/sites/javascript-to-do-list-with-react/building_complex_applications_with_react.step new file mode 100644 index 000000000..5f8df950a --- /dev/null +++ b/sites/javascript-to-do-list-with-react/building_complex_applications_with_react.step @@ -0,0 +1,179 @@ +goals do + goal "Understand how modern web developers organize their code." + goal "Familiarize with Stores, JSX, and other features of a React application." +end + +overview do + message <<-MARKDOWN + When the web was in its infancy, JavaScript was often an afterthought. Web developers + were mostly concerned writing complex servers, and thought of JavaScript as a language to + simply make things move on their user's pages. Consequently, their JavaScript code + was often unorganized, untested, and hard to maintain. + + But as browsers became more advanced, JavaScript was suddenly asked to do much more – web + developers used JavaScript for complex animations, making requests to servers, even doing + complex calculations in the browser! The old approach of writing all your code in one place, + with all the logic jumbled together, quickly became untenable. In response to that, the + JavaScript community has started developing patterns to help themselves organize their code, + making it more readable. Most of these patterns separate code into two buckets: + + * __Views (or Components, or View Controllers)__ are in charge or rendering HTML elements, and + listening for user's actions like clicks and form submissions. + * __Models (or Stores)__ are responsible for the logic of your JavaScript application. + + There are many JavaScript libraries and frameworks that implement this structure, including + [AngularJS](https://angularjs.org/), [Ember.js](http://emberjs.com/), and [Backbone.js](http://backbonejs.org/). + They each have their own strengths and weaknesses. and we wouldn't presume to tell you + which one is "better." As you continue learning, you'll form your own opinions! + + For this tutorial, we'll be using [React](http://facebook.github.io/react/), a rendering library written by Facebook that has + grown in popularity over the last few years. To use React, you write your HTML as a series of __components__ + in a language called __JSX__. Don't worry, JSX looks a lot like HTML! These components handle + the rendering of your HTML elements, as well as listen for user actions. + + You'll be using React to make your view objects, and we'll use a simple JavaScript object as a model. We've + already built a lot of this structure for you! Let's walk through the code together. + MARKDOWN +end + + + + +steps do + step do + message <<-MARKDOWN +Open up store.js in your text editor. There, you should see a ListStore object defined like this: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + ListStore = { + getItems: function() { + return items + }, + loadItems: function() {}, + addItem: function(itemDescription) {}, + toggleCompleteness: function(itemId) {} + } + JAVASCRIPT + + message <<-MARKDOWN +This is where all the logic of our application will be performed! Whenever we need to execute a calcuation, or +make an AJAX request, our program will call a function on the ListStore object. For instance, when we want to load +all items from our server, you would write a line of code like this: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + ListStore.loadItems() + JAVASCRIPT + + message <<-MARKDOWN +In future lessons, we'll be writing the logic to make these functions work! Whenever the store changes – when an item +is added or marked as completed, for example – we'll use the notifyComponents function to update the user's page. + MARKDOWN + end + + step do + + message <<-MARKDOWN +Now that we've looked at the store, let's take a look at our components. Open index.html, and find the Item component. It should +look like this: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + var Item = React.createClass({ + render: function() { + var itemClass = this.props.completed ? 'item completed' : 'item' + return ( +
  • + {'\\u2714'} +
    {this.props.description}
    + {'\\u2718'} +
  • + ) + }, + }) + JAVASCRIPT + + message <<-MARKDOWN +This is a React component. It is responsible for rendering the HTML for our list's items! Every React component has +to have a render function, which are written in a language called JSX. It looks a like HTML, except you can write it +alongside your JavaScript code. Here's the HTML this component renders: + MARKDOWN + + source_code :HTML, <<-HTML +
  • + +
    A gallon of milk.
    + +
  • + HTML + + message <<-MARKDOWN +Take a look at the other components on the page. What do you think List component does, and how does it know to render +items? How about the CreationForm component? + MARKDOWN + end + + step do + message <<-MARKDOWN +Now you know what a React component looks like, but how does it work? Well, components can have __state__. +Whenever a component's state changes, it re-renders itself to the page! State can change in a handful +of ways. We've already written a function to make List's state change whenever the notifyComponents +function is called. This will be helpful later on! + +Components with state usually also implement a getInitialState function that tells the component what +to render immediately after the page loads. Our List component has one that looks like this: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + getInitialState: function() { + return ( + {items: [ + {description: 'a gallon of milk', completed: true, id: 1}, + {description: 'a stick of butter', completed: false, id: 2} + ]} + ) + }, + JAVASCRIPT + + message <<-MARKDOWN +Does that data look familiar? It's the default items that our list renders! Try changing these values. +Refresh your browser, and see what's changed! + +Before we move on, let's remove the default items altogether. Not everyone wants a gallon of milk! +Your code should look like this: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + getInitialState: function() { + return ( + {items: []} + ) + }, + JAVASCRIPT + end +end +explanation do + + message <<-MARKDOWN + +### Getting Comfortable with React. + +You've just looked at your first React application! There are a lot of moving parts, we know. +Don't worry if you don't understand how everything works just yet – as you keep playing with it, +it will become much clearer. Big picture, our web application has two parts: + +* A ListStore, which is responsible for fetching data from the server using AJAX, and keeps +track of all the items on our list. Whenever a new item has been added or updated, it will notify +all the components that it has changed. + +* A series of components that render our list as HTML. The List component updates whenever +the ListStore changes, and it renders a series of Item components. The CreationForm component +will be responsible for creating new items! + +Next, we'll write our first store function to load items when the user visits the page! + MARKDOWN +end + + +next_step 'loading_items' \ No newline at end of file diff --git a/sites/javascript-to-do-list-with-react/creating_a_list.step b/sites/javascript-to-do-list-with-react/creating_a_list.step new file mode 100644 index 000000000..f4816dd55 --- /dev/null +++ b/sites/javascript-to-do-list-with-react/creating_a_list.step @@ -0,0 +1,164 @@ +goals do + goal "Understand how the internet is made up of clients and servers." + goal "Write some basic requests using JavaScript, jQuery and your browser's console." + goal "Create a list and your first item." +end + +overview do + + message <<-MARKDOWN + +As you may already know, websites are built using HTML, CSS, and JavaScript. +These are the languages your browser understands, and it uses them to give +structure, style, and interactivity to your website. + +The problem, though, is that browsers don't remember anything. Every time you refresh +a page, your browser reruns all of your CSS, HTML, and JavaScript. A browser, by itself, +doesn't remember who your users are, or any of their information. + +But wait, we need our website to remember things! Specifically, we need our website +to remember all the items on our list, and whether we've completed them. That's where +a __server__ comes in. A server is a catch-all term for a web application that can be +reached over the internet. They are often attached to __databases__ that store information +about their users. If you make an __HTTP request__ with the correct __URL__ (like +http://www.google.com) and __HTTP method__ (like GET or POST), the server will +respond with helpful data. + +Servers are written in dozens of languages, including Java, PHP, and Ruby. +For this exercise, we've already built a server (using Ruby on Rails), and its +available at https://listalous.herokuapp.com/. If you want to learn more about how +to write a server, take a look at our Rails curriculum! + +Browsers, like Chrome and Internet Explorer, are in the business of making requests +to servers, and displaying the data they receive as a web page. Browsers are a +type of __client__, which consume data. There are other types of clients, too. +Your computer can be a client, using the curl command in the terminal. An iPhone +is another type of client, and servers can even communicate with each other. + +Every time you refresh a web page, your browser is making another request to +the server. On modern websites, browsers often make multiple requests to a server, +depending on what the user is doing. For instance, Twitter doesn't load your entire +feed at once – as you scroll down the page, it's making more and more requests! + +To accomplish that feat, websites can use JavaScript to make requests to servers, +updating the page without the user having to refresh. This type of +request is called an __AJAX__ request, which stands for __A__synchronous __J__avaScript +__a__nd __X__ML. It's a technique that is used by most major websites, to provide +a seamless experience to their users. + +In this lesson, we'll be making AJAX requests using our browser's console. + MARKDOWN +end + +steps do + + step do + message <<-MARKDOWN +Copy the following code into your browser's console, replacing 'YOUR-LIST-NAME-HERE' +with your name of choice. Note: it has to be unique! I'd suggest using your github username. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + $.ajax({ + type: 'POST', + url: "https://listalous.herokuapp.com/lists", + data: { name: 'YOUR-LIST-NAME-HERE' } + }) + JAVASCRIPT + + message <<-MARKDOWN +Now click over to your browser's network tab. It should look something like this. + +image of chrome's network tab. + +Find the request to listalous.herokuapp.com. Was it successful? If not, why +do you think it failed? Once you've successfully created a list, move on to the next step. + MARKDOWN + end + + step do + message <<-MARKDOWN +Now that we've created a list, let's create our list's first item. We're +going to use jQuery's AJAX function again to do so. Copy the following +code into your browser's console, replacing 'YOUR-LIST-NAME-HERE' with +your list's name, and 'DESCRIPTION-OF-YOUR-ITEM' with your item's description. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + $.ajax({ + type: 'POST', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items", + data: { description: 'DESCRIPTION-OF-YOUR-ITEM', completed: false } + }) + JAVASCRIPT + + message <<-MARKDOWN +Check the network tab again. Was your request successful? If it was, take a look at +the server's response. You'll notice that the item has an id attribute now. This is how +the server will uniquely identify your item in the future. + MARKDOWN + end + + step do + message <<-MARKDOWN +Finally, lets fetch our list from the server. Your list application will need to do this +every time someone refreshes the page, so that it can load previously created items. Copy +the following code into your browser's console, replacing 'YOUR-LIST-NAME-HERE' with your +list's name. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + $.ajax({ + type: 'GET', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/" + }) + JAVASCRIPT + + message <<-MARKDOWN +Check the network tab one more time. What data did the server send back? If your +request was successful, it should have returned all the data associated with your list: +your list's name, as well as all of your list's items. This is data will be basis +of your list application! + MARKDOWN + end +end + +explanation do + message <<-MARKDOWN + +That was a lot of copying and pasting! What did it all mean? + +### Adding a Server to the Equation. + +Remember, web pages have short memories. Whenever you refresh the page, all the HTML, +CSS, and JavaScript on that page has to reload and rerun. A common way for a website +to remember information about you (the fact that you have logged in before, your previous +purchases, etc.) is by getting a server involved. A server, attached to a database, can +persist information about your web site as users come and go. + +In this case, we have a server that can be reached over the internet at +https://listalous.herokuapp.com/. It's attached to a database that +stores all lists and items, that can fetched to make hundreds of independent to do lists. + +In step 1, we used jQuery's AJAX function to ask the server to make a +new list in its database. It only did so if the list's name was unique; +otherwise, how could the server tell your list apart from someone else's list? + +In step 2, we asked the server to create an item associated with your +list. The server responded with information regarding this item, including an +id, that we can use to identify that item later (if we want to mark it as complete, +or delete it.) + +In step 3, we asked the server to return all information associated with +our list, including all of the list's items. When we start building our list +application, we will use this data to load all items on our list every time we +refresh the page. + +We did all of this in the browser's console. In the next lesson, we'll learn +how modern web applications integrate AJAX requests into their applications. + + MARKDOWN +end + + +next_step "building_complex_applications_with_react" diff --git a/sites/javascript-to-do-list-with-react/deploying_your_site.step b/sites/javascript-to-do-list-with-react/deploying_your_site.step new file mode 100644 index 000000000..433ba891d --- /dev/null +++ b/sites/javascript-to-do-list-with-react/deploying_your_site.step @@ -0,0 +1,3 @@ +insert '../javascript-to-do-list/_deploying_your_site' + +next_step "next_steps" \ No newline at end of file diff --git a/sites/javascript-to-do-list-with-react/developer_tools.step b/sites/javascript-to-do-list-with-react/developer_tools.step new file mode 100644 index 000000000..f5c756c90 --- /dev/null +++ b/sites/javascript-to-do-list-with-react/developer_tools.step @@ -0,0 +1,3 @@ +insert '../frontend/_developer_tools' + +next_step 'creating_a_list' \ No newline at end of file diff --git a/sites/javascript-to-do-list-with-react/javascript-to-do-list-with-react.step b/sites/javascript-to-do-list-with-react/javascript-to-do-list-with-react.step new file mode 100644 index 000000000..84e4a6b9f --- /dev/null +++ b/sites/javascript-to-do-list-with-react/javascript-to-do-list-with-react.step @@ -0,0 +1,58 @@ +message <<-MARKDOWN +### Goal + +By the end of this session, you should understand how to write a JavaScript application +using the [React](http://facebook.github.io/react/) library. You're going to be building a personal to do list application. +You've decided that you'd like to: + +* Add an item to your list +* Mark an item as complete +* Load your list from multiple computers. + +You've sketched up an initial screenshot of what you want it to look like: + +![Browser window with list application running](../javascript-to-do-list/img/finished_app.png) + +### Meta-Goal + +When you have completed today's goal of getting the basic +application online you should understand: + +* How to use JavaScript to interact with a server. +* How to organize your JavaScript code into models and views. +* How to use the React library to render HTML elements. +* How to incrementally add features to your application. +* How to get your application online. + +### Schedule + +* 1-ish hours of the basics of clients and servers. +* 1-ish hours on organizing JavaScript using models and views. +* 3-ish hours of adding features to you application, broken up into 1 hour chunks. + +This is just a rough guideline, not a mandate. Some steps you'll go +over and some you'll go under. It'll all work out by the end of the +day. Probably. + +### Requirements + +When programming, you'll generally want these tools on hand: + + * An internet connection. We're going to be communicating with other websites using JavaScript. + * Your browser to see the code running (I recommend Chrome, but any will do!) + * A text editor to change the code (Railsbridge recommends Atom, but you can try a different editor) + * A javascript console so you can experiment and print out debugging + messages. This is built into your browser. + +Before you can start the tutorial, you'll need to download the tutorial +to your computer to edit the source code. + +Once you've downloaded it, open index.html with your browser. You should see a mock up of your list. If you do not, get an instructor to help you out. +MARKDOWN + +insert '../javascript-to-do-list/_lesson_format' + +insert '../javascript-to-do-list/_teachers_note' + +next_step "developer_tools" + diff --git a/sites/javascript-to-do-list-with-react/loading_items.step b/sites/javascript-to-do-list-with-react/loading_items.step new file mode 100644 index 000000000..e90a9dfdc --- /dev/null +++ b/sites/javascript-to-do-list-with-react/loading_items.step @@ -0,0 +1,131 @@ +goals do + goal "Make an AJAX request and update the page based on the server's response." + goal "Load your list's items every time a user visits the page." +end + +overview do + message <<-MARKDOWN + Now that we've learned about AJAX requests and React, let's tie everything together. + Here's what we want to happen. + + 1. Every time a user visits our site, we'll ask ListStore to load all items from the server. + 2. It will use jQuery's AJAX function to make a request to http://listalous.herokuapp.com/ and + get all the data about our list's items. + 3. We will then use our React Components to render all of our items! + MARKDOWN +end + +steps do + step do + message <<-MARKDOWN +First, we need to tell ListStore to call the loadItems function whenever a user visits +the page. Open up index.html, and add this line of code to the bottom of the page, before the end +of the script tag: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + ListStore.loadItems() + JAVASCRIPT + + message "the end of index.html should now look like this:" + + source_code :HTML, <<-HTML + React.render(, document.getElementById('form-container') ) + React.render(, document.getElementById('list-container') ) + ListStore.loadItems() + + + HTML + end + + step do + message <<-MARKDOWN +Now, we have to write the loadItems function! open up Store.js, and add the following code +to the loadItems function. Replace 'YOUR-LIST-NAME-HERE' with the name of the list you created. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + loadItems: function() { + var loadRequest = $.ajax({ + type: 'GET', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/" + }) + }, + + JAVASCRIPT + + message <<-MARKDOWN +Refresh the page, and click over to your browser's network tab. You should see +a new request there, that visits our server at https://listalous.herokuapp.com/. + MARKDOWN + end + + step do + message <<-MARKDOWN +Now that we've made the request, we need to update ListStore whenever the request +succeeds. Add the following lines of code to the bottom of the LoadItems function. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + loadRequest.done(function(dataFromServer) { + items = dataFromServer.items + notifyComponents() + }) + JAVASCRIPT + + message <<-MARKDOWN + Now refresh the page. Once the AJAX request succeeds, your site should now display + all the items you created last lesson! If not, flag an instructor down to help you + debug the problem. + MARKDOWN + end + +end + + +explanation do + message "Store.js should now look like this." + source_code :javascript, <<-JAVASCRIPT + ListStore = { + + getItems: function() { + return items + }, + + loadItems: function() { + var loadRequest = $.ajax({ + type: 'GET', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/" + }) + + loadRequest.done(function(dataFromServer) { + items = dataFromServer.items + notifyComponents() + }) + }, + addItem: function(itemDescription) {}, + toggleCompleteness: function(itemId) {} + + JAVASCRIPT + + message <<-MARKDOWN +Let's walk through what just happened: + + 1. When the user visited the page, we told the ListStore to load items. + 2. The ListStore used jQuery's AJAX function to make a request to our server. + 3. The server responded with all the items associated with our list. + 4. We saved the server's response in the items variable, and notified the components + that it was time to re-render themselves. + 5. The components heard the message, and updated! The List component used ListStore's getItems + function to get all the items data we had stored in the items variable. + +Your web page has made its first successful AJAX request! Now, your page will +load your list's items whenever you visit it. Once we host this page on the internet, +you will be able to see your list on any computer, tablet, or phone! + MARKDOWN +end + + + + +next_step "adding_an_item" diff --git a/sites/javascript-to-do-list-with-react/marking_an_item_as_complete.step b/sites/javascript-to-do-list-with-react/marking_an_item_as_complete.step new file mode 100644 index 000000000..91c9b29e6 --- /dev/null +++ b/sites/javascript-to-do-list-with-react/marking_an_item_as_complete.step @@ -0,0 +1,187 @@ +goals do + goal "Allow a user to mark an item as complete or incomplete." + goal "Understand how to listen for user events with React." +end + +overview do + message <<-MARKDOWN +In this lesson, we'll allow our users to mark items as complete and incomplete. As they +do, we will ask the server to update the item's status in its database. This process will look +a lot like the process for adding an item, with some additional complexity. + +First off, We'll have to write the event listener ourselves in the Item component. Don't worry, +we'll walk you through every step! + +Secondly, we need to pass the appropriate information to ListStore, so it can make the correct AJAX request. +To do so, we'll have to use __props__ to fetch the item's id value. + +Let's get started! + MARKDOWN +end + +steps do + step do + message "Let's add a click listener to our Item component's complete button. Find the line of code + that is creating the completion button, and add an onClick attribute to make React listen for your users' + clicks. Your code should look like this:" + + source_code :javascript, <<-JAVASCRIPT + {'\\u2714'} + JAVASCRIPT + + message "Now, let's write the handleComplete function." + + source_code :javascript, <<-JAVASCRIPT + handleComplete: function() { + alert('trying to complete item with an id of ' + this.props.id) + } + JAVASCRIPT + + message <<-MARKDOWN +Refresh the page, and try completing an item. What happens? How does the page know the item's id? + +Your Item component should now look like this: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + var Item = React.createClass({ + render: function() { + var itemClass = this.props.completed ? 'item completed' : 'item' + return ( +
  • + {'\\u2714'} +
    {this.props.description}
    + {'\\u2718'} +
  • + ) + }, + + handleComplete: function() { + alert('trying to update item with an id of ' + this.props.id) + } + }) + JAVASCRIPT + end + + step do + message "Now, we're going to tell ListStore to mark this item as complete/incomplete. Remove the alert from last step, and add the following code." + + source_code :javascript, <<-JAVASCRIPT + handleComplete: function() { + ListStore.toggleCompleteness(this.props.id) + } + JAVASCRIPT + end + + step do + message <<-MARKDOWN +Now, let's write the logic for updating an item! Open up store.js, and add the following code to +the toggleCompleteness function. replace 'YOUR-LIST-NAME-HERE' with your list's name. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + var item = findItemById(itemId) + var currentCompletedValue = item.completed + + var updateRequest = $.ajax({ + type: 'PUT', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items/" + itemId, + data: { completed: !currentCompletedValue } + }) + JAVASCRIPT + + message <<-MARKDOWN +We're using the pre-written findItemById method to fetch the correct item, and then checking +its current completed value. We then tell the server to toggle its completeness from true to false, +or false to true. Refresh the page and try marking an item as complete. Check the network tab to see if +a new request was made! + MARKDOWN + end + + step do + message <<-MARKDOWN +Finally, we'll update the specified item's completeness value in the items array, and +tell the components to re-render themselves. Add this code to the bottom of the toggleCompleteness +function: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + updateRequest.done(function(itemData) { + item.completed = itemData.completed + notifyComponents() + }) + JAVASCRIPT + + message <<-MARKDOWN +Mark an item as complete, and see it turn gray! If you mark a completed item as incomplete, +it should change colors, too. + MARKDOWN + end +end + + + +explanation do + + message "Here's what store.js should now look like:" + + source_code :javascript, <<-JAVASCRIPT + ListStore = { + + getItems: function() { + return items + }, + + loadItems: function() { + var loadRequest = $.ajax({ + type: 'GET', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/" + }) + + loadRequest.done(function(dataFromServer) { + items = dataFromServer.items + notifyComponents() + }) + }, + + addItem: function(itemDescription) { + var creationRequest = $.ajax({ + type: 'POST', + url: "http://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items", + data: { description: itemDescription, completed: false } + }) + + creationRequest.done(function(itemDataFromServer) { + items.push(itemDataFromServer) + notifyComponents() + }) + }, + + toggleCompleteness: function(itemId) { + var item = findItemById(itemId) + var currentCompletedValue = item.completed + + var updateRequest = $.ajax({ + type: 'PUT', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items/" + itemId, + data: { completed: !currentCompletedValue } + }) + + updateRequest.done(function(itemData) { + item.completed = itemData.completed + notifyComponents() + }) + } + } + JAVASCRIPT + + message <<-MARKDOWN +You've now written three ajax requests, making a modern, dynamic web page. Don't worry if you +didn't understand every line of code – JavaScript is complex stuff! + +There are many more features you could add (deleting items, sorting items, etc.) but in our next +lesson, we'll add the most important feature: the ability of users to actually use your site! + MARKDOWN +end + +next_step "deploying_your_site" diff --git a/sites/javascript-to-do-list-with-react/next_steps.step b/sites/javascript-to-do-list-with-react/next_steps.step new file mode 100644 index 000000000..5800a740b --- /dev/null +++ b/sites/javascript-to-do-list-with-react/next_steps.step @@ -0,0 +1,30 @@ +message < + + + + + + + + + + + + +
    +

    Your List App

    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/sites/javascript-to-do-list-with-react/zip_content/store.js b/sites/javascript-to-do-list-with-react/zip_content/store.js new file mode 100644 index 000000000..ac2b9103d --- /dev/null +++ b/sites/javascript-to-do-list-with-react/zip_content/store.js @@ -0,0 +1,22 @@ +var items = [] + +var notifyComponents = function() { + $(ListStore).trigger('storeHasChanged') +} + +var findItemById = function(id) { + return items.filter(function(item) { + return item.id === id + })[0] +}, + +ListStore = { + + getItems: function() { + return items + }, + + loadItems: function() {}, + addItem: function(itemDescription) {}, + toggleCompleteness: function(itemId) {} +} \ No newline at end of file diff --git a/sites/javascript-to-do-list-with-react/zip_content/styles.css b/sites/javascript-to-do-list-with-react/zip_content/styles.css new file mode 100644 index 000000000..23c97a756 --- /dev/null +++ b/sites/javascript-to-do-list-with-react/zip_content/styles.css @@ -0,0 +1,108 @@ +body { + background: url('https://github.com/debut_light.png') +} + +#container { + width: 600px; + font-family: 'Titillium Web', 'Arial', sans-serif; + font-size: 40px; + text-align: center; + margin: 40px auto; +} + +h1 { + margin: 20px; + color: #7d82b0; +} + +#app { + box-shadow: 0 0 5px #684F91; + font-size: 35px; + color: #080D3B; + background-color: white; +} + +form { + margin: 0; + padding: 0; +} + +#create { + width: 100%; + font-family: inherit; + font-size: inherit; + padding: 10px 30px; + border-left: 1px solid #7d82b0; + border-right: 1px solid #7d82b0; + border-bottom: 2px solid #7d82b0; + border-top: 15px solid #477187; + text-align: center; +} + + +#create::-webkit-input-placeholder { + color: #C3C3C4; + text-align: center; +} + +#create::-moz-placeholder { /* Firefox 19+ */ + color: #C3C3C4; + text-align: center; +} + +#create:focus::-webkit-input-placeholder { + color: transparent; + text-align: center; +} + +#create:focus::-moz-placeholder { /* Firefox 19+ */ + color: transparent; + text-align: center; +} + +#list { + list-style-type: none; + margin: 0; + padding: 0; + box-sizing: border-box; +} + +.item { + position: relative; + box-sizing: border-box; + padding: 10px; + list-style-type: none; + width: 100%; + border-left: 1px solid #7d82b0; + border-right: 1px solid #7d82b0; + border-bottom: 2px solid #7d82b0; + border-top: 0px solid #7d82b0; + +} + +.item .complete-button { + position: absolute; + left: 20px; + top: 10px; + cursor: pointer; +} + +.item .value { + margin: 0 50px; + padding: 0 15px; +} + +.item .delete-button { + position: absolute; + right: 20px; + top: 10px; + cursor: pointer; +} + +.item.completed { + color: #C3C3C4; +} + +#templates { + display: none +} \ No newline at end of file diff --git a/sites/javascript-to-do-list/IntermediateTodoList.zip-manifest b/sites/javascript-to-do-list/IntermediateTodoList.zip-manifest new file mode 100644 index 000000000..ce489a48e --- /dev/null +++ b/sites/javascript-to-do-list/IntermediateTodoList.zip-manifest @@ -0,0 +1,4 @@ +zip_content/app.js +zip_content/debut_light.png +zip_content/index.html +zip_content/styles.css diff --git a/sites/javascript-to-do-list/_deploying_your_site.md b/sites/javascript-to-do-list/_deploying_your_site.md new file mode 100644 index 000000000..608d627ed --- /dev/null +++ b/sites/javascript-to-do-list/_deploying_your_site.md @@ -0,0 +1,54 @@ +### Goal + +Hosting your project requires you to make the HTML, JavaScript and CSS files +available over the Internet. + +There are three hosting options, depending on how much of a challenge you are looking +for: + +* Use a static site hosting service +* Use git and GitHub Pages +* Roll your own with a hosting provider + +### Using a Static Site Hosting Service + +If you are brand new to hosting websites, you may want to use a static site +hosting service. These allow you to upload a zip file of html, css and +javascript and have a working website. An easy one is +[Forge](https://getforge.com/). + +1. Sign up for Forge +2. Zip your project directory +3. Drag it into the Forge website + +Now you have a fully functioning site hosted online! Share the link +with your friends and family and wow them with your skills! + +### Using Git and GitHub Pages + +If you like with git and github, take a stab at setting up your site +with [GitHub Pages](http://pages.github.com/). The easiest thing to do is: +cd . + +1. From the terminal, `cd` into your project directory +1. Turn it into a git repository by running `git init` +1. Checkout a branch called `gh-pages` +1. Commit all the files +1. Create a remote repository on github for the project. +1. Follow their instructions for adding the github remote to your existing repo +1. Follow the instructions on [GitHub Pages](http://pages.github.com) for + setting up a project site from scratch. +1. Make the `gh-pages` the default branch +1. Push it on up! + +### Rolling Your Own Hosting With a Cloud Provider + +If you secretly are an unix admin who floats in the cloud; consider +using Amazon +[S3](http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteHosting.html) or +[Rackspace Cloud +Files](http://www.rackspace.com/knowledge_center/article/use-cloud-files-to-serve-static-content-for-websites) + +Both of these services are designed to serve up static files without needing to +pay for a virtual server. This makes your monthly hosting bill for even large, +high traffic sites incredibly cheap. \ No newline at end of file diff --git a/sites/javascript-to-do-list/_lesson_format.md b/sites/javascript-to-do-list/_lesson_format.md new file mode 100644 index 000000000..1f1205287 --- /dev/null +++ b/sites/javascript-to-do-list/_lesson_format.md @@ -0,0 +1,22 @@ +### Format + +Each lesson will look like this: + +
    +

    Step Title

    +
    +

    Goal:

    +

    Description of the current step. +

    Red because big goals are scary. +

    +
    +

    Steps:

    +
    steps to take.
    +

    Yellow because we've gotten it done, but we have no clue what's going on. +

    +
    +

    Explanation

    +

    Details of what the steps actually did... spell out the cause and effect. +

    Green because we can tie everything together now. +

    +
    diff --git a/sites/javascript-to-do-list/_teachers_note.md b/sites/javascript-to-do-list/_teachers_note.md new file mode 100644 index 000000000..33b67771f --- /dev/null +++ b/sites/javascript-to-do-list/_teachers_note.md @@ -0,0 +1,5 @@ +### A note for teachers + +The backend for this curriculum is hosted at http://listalous.herokuapp.com/ + +If it isn't working, you might be able to deploy it somewhere else using the code from https://github.com/raorao/headless_todos diff --git a/sites/javascript-to-do-list/adding_an_item.step b/sites/javascript-to-do-list/adding_an_item.step new file mode 100644 index 000000000..36b0468aa --- /dev/null +++ b/sites/javascript-to-do-list/adding_an_item.step @@ -0,0 +1,143 @@ +goals do + goal "Allow a user to create a new item." + goal "Understand how to make an AJAX request when prompted by a user's action." +end + +overview do + message <<-MARKDOWN +JavaScript allows us to make our web page dynamic, and responsive to +the actions of our users. In this lesson, we'll allow our users to create +a new item for our list, and ask the server to save it to the database. + +To do so, we're going to use JavaScript's ability to perform a task when a +user has taken an action on the page. JavaScript refers to these actions as +__events__, and we can trigger code to run by using an __event listener__. We will +be using jQuery to accomplish this, as it provides a powerful and readable interface +to respond to user events. + +Our code will take the following steps. + +1. When the user loads the page, our code will start listening for when the user + submits the form at the top of the page. + +2. When a user submits the form (by pressing enter), we will prevent the page + from refreshing, which is the normal behavior for a form. + +3. We will make an AJAX request to our server, creating an item with the + description our user just provided. + +4. Once the request succeeds, we will add a new item to the page! + MARKDOWN +end + +steps do + step do + message "Add the following code the bottom of app.js." + + source_code :javascript, <<-JAVASCRIPT + $('#add-form').on('submit', function(event) { + var itemDescription = event.target.itemDescription.value + alert('trying to create a new item with a description ' + itemDescription) + }) + JAVASCRIPT + + message <<-MARKDOWN +Refresh the page and try creating an item. An alert should pop up when you try! What +happens after that? why? + MARKDOWN + end + + step do + message "Before the alert we wrote in the last step, add the following line of code." + + source_code :javascript, <<-JAVASCRIPT + event.preventDefault() + JAVASCRIPT + + message <<-MARKDOWN +Try creating an item again. What changed? Why did it change? + MARKDOWN + end + + step do + message <<-MARKDOWN +Now that we've successfully listened for a form submission and prevented the page from +refreshing, we're going to ask the server to save this item into the database. Remove +the alert that you wrote in step one, and replace it with the following code. replace +'YOUR-LIST-NAME-HERE' with your list's name. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + var creationRequest = $.ajax({ + type: 'POST', + url: "http://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items", + data: { description: itemDescription, completed: false } + }) + JAVASCRIPT + + message <<-MARKDOWN +Try creating an item again. After you submit the form, look at the network tab. A +new request should have occurred to http://listalous.herokuapp.com/ ! + MARKDOWN + end + + step do + message <<-MARKDOWN +Finally, we need to add the new item to the list. We'll use our addItemToPage function +again to do so. After the creationRequest, add the following code: + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + creationRequest.done(function(itemDataFromServer) { + addItemToPage(itemDataFromServer) + }) + JAVASCRIPT + + message <<-MARKDOWN + Try creating an item one more time. Once you hit enter, a new item should appear + on the page! If not, flag an instructor down to help you debug the problem. + MARKDOWN + end +end + +explanation do + + message "Here's what the bottom of app.js should now look like:" + + source_code :javascript, <<-JAVASCRIPT + $('#add-form').on('submit', function(event) { + event.preventDefault() + var itemDescription = event.target.itemDescription.value + var creationRequest = $.ajax({ + type: 'POST', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items", + data: { description: itemDescription, completed: false } + }) + + creationRequest.done(function(itemDataFromServer) { + addItemToPage(itemDataFromServer) + }) + }) + + JAVASCRIPT + + message <<-MARKDOWN + +### The AJAX process + +You've just done something that many JavaScript developers do daily: Use JavaScript +to make a request to a server, and then update the page with the data with which +the server responds. This abstract process is repeated over and over again: + +1. Add an event listener. +2. Parse out the information the user is submitting. +3. Prevent the default action from occurring, if necessary. +4. Make a request to the server using AJAX. +5. When the request succeeds, parse the data the server sends back. +6. Update the page with the newly received data. + +This process is the basis of most modern web pages! + MARKDOWN +end + +next_step "marking_an_item_as_complete" diff --git a/sites/javascript-to-do-list/creating_a_list.step b/sites/javascript-to-do-list/creating_a_list.step new file mode 100644 index 000000000..b2ecf9d27 --- /dev/null +++ b/sites/javascript-to-do-list/creating_a_list.step @@ -0,0 +1,165 @@ +goals do + goal "Understand how the internet is made up of clients and servers." + goal "Write some basic requests using JavaScript, jQuery and your browser's console." + goal "Create a list and your first item." +end + +overview do + + message <<-MARKDOWN + +So far, we've learned about JavaScript, HTML, and CSS. These are the languages +your browser understands, and it uses them to give structure, style, and interactivity +to your website. + +The problem, though, is that browsers don't remember anything. Every time you refresh +a page, your browser reruns all of your CSS, HTML, and JavaScript. A browser, by itself, +doesn't remember who your users are, or any of their information. + +But wait, we need our website to remember things! Specifically, we need our website +to remember all the items on our list, and whether we've completed them. That's where +a __server__ comes in. A server is a catch-all term for a web application that can be +reached over the internet. They are often attached to __databases__ that store information +about their users. If you make an __HTTP request__ with the correct __URL__ (like +http://www.google.com) and __HTTP method__ (like GET or POST), the server will +respond with helpful data. + +Servers are written in dozens of languages, including Java, PHP, and Ruby. +For this exercise, we've already built a server (using Ruby on Rails), and its +available at https://listalous.herokuapp.com/. If you want to learn more about how +to write a server, take a look at our Rails curriculum! + +Browsers, like Chrome and Internet Explorer, are in the business of making requests +to servers, and displaying the data they receive as a web page. Browsers are a +type of __client__, which consume data. There are other types of clients, too. +Your computer can be a client, using the curl command in the terminal. An iPhone +is another type of client, and servers can even communicate with each other. + +Every time you refresh a web page, your browser is making another request to +the server. On modern websites, browsers often make multiple requests to a server, +depending on what the user is doing. For instance, Twitter doesn't load your entire +feed at once – as you scroll down the page, it's making more and more requests! + +To accomplish that feat, websites can use JavaScript to make requests to servers, +updating the page without the user having to refresh. This type of +request is called an __AJAX__ request, which stands for __A__synchronous __J__avaScript +__a__nd __X__ML. It's a technique that is used by most major websites, to provide +a seamless experience to their users. + +In this lesson, we'll be making AJAX requests using our browser's console. + MARKDOWN +end + +steps do + + step do + message <<-MARKDOWN +Copy the following code into your browser's console, replacing 'YOUR-LIST-NAME-HERE' +with your name of choice. Note: it has to be unique! I'd suggest using your github username. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + $.ajax({ + type: 'POST', + url: "https://listalous.herokuapp.com/lists", + data: { name: 'YOUR-LIST-NAME-HERE' } + }) + JAVASCRIPT + + message <<-MARKDOWN +Now click over to your browser's network tab. Next to the 'filter' box, click on either the 'All' tab or 'XHR'. Below that, click on the 'Response' tab. It should look something like this: + +image of chrome's network tab. + +Find the request to listalous.herokuapp.com and click on it. Was it successful? If not, look in the console and read the error message. Why +do you think it failed? Once you've successfully created a list, move on to the next step. + MARKDOWN + end + + step do + message <<-MARKDOWN +Now that we've created a list, let's create our list's first item. We're +going to use jQuery's AJAX function again to do so. Copy the following +code into your browser's console, replacing 'YOUR-LIST-NAME-HERE' with +your list's name, and 'DESCRIPTION-OF-YOUR-ITEM' with your item's description. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + $.ajax({ + type: 'POST', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items", + data: { description: 'DESCRIPTION-OF-YOUR-ITEM', completed: false } + }) + JAVASCRIPT + + message <<-MARKDOWN +Check the network tab again. Was your request successful? If it was, take a look at +the server's response. You'll notice that the item has an id attribute now. This is how +the server will uniquely identify your item in the future. + MARKDOWN + end + + step do + message <<-MARKDOWN +Finally, lets fetch our list from the server. Your list application will need to do this +every time someone refreshes the page, so that it can load previously created items. Copy +the following code into your browser's console, replacing 'YOUR-LIST-NAME-HERE' with your +list's name. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + $.ajax({ + type: 'GET', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/" + }) + JAVASCRIPT + + message <<-MARKDOWN +Check the network tab one more time. What data did the server send back? If your +request was successful, it should have returned all the data associated with your list: +your list's name, as well as all of your list's items. This is data will be basis +of your list application! + MARKDOWN + end +end + +explanation do + message <<-MARKDOWN + +That was a lot of copying and pasting! What did it all mean? + +### Adding a Server to the Equation. + +Remember, web pages have short memories. Whenever you refresh the page, all the HTML, +CSS, and JavaScript on that page has to reload and rerun. A common way for a website +to remember information about you (the fact that you have logged in before, your previous +purchases, etc.) is by getting a server involved. A server, attached to a database, can +persist information about your web site as users come and go. + +In this case, we have a server that can be reached over the internet at +https://listalous.herokuapp.com/. It's attached to a database that +stores all lists and items, that can fetched to make hundreds of independent to do lists. + +In step 1, we used jQuery's AJAX function to ask the server to make a +new list in its database. It only did so if the list's name was unique; +otherwise, how could the server tell your list apart from someone else's list? + +In step 2, we asked the server to create an item associated with your +list. The server responded with information regarding this item, including an +id, that we can use to identify that item later (if we want to mark it as complete, +or delete it.) + +In step 3, we asked the server to return all information associated with +our list, including all of the list's items. When we start building our list +application, we will use this data to load all items on our list every time we +refresh the page. + +We did all of this in the browser's console. In the next lesson, we'll integrate +some of this code into our site, so it runs automatically whenever a user +visits your web site. + + MARKDOWN +end + + +next_step "loading_items" diff --git a/sites/javascript-to-do-list/deploying_your_site.step b/sites/javascript-to-do-list/deploying_your_site.step new file mode 100644 index 000000000..099c45876 --- /dev/null +++ b/sites/javascript-to-do-list/deploying_your_site.step @@ -0,0 +1,3 @@ +insert '_deploying_your_site' + +next_step "next_steps" \ No newline at end of file diff --git a/sites/javascript-to-do-list/developer_tools.step b/sites/javascript-to-do-list/developer_tools.step new file mode 100644 index 000000000..fbe7c35a5 --- /dev/null +++ b/sites/javascript-to-do-list/developer_tools.step @@ -0,0 +1,3 @@ +insert '../frontend/_developer_tools' + +next_step 'programming_with_javascript' \ No newline at end of file diff --git a/sites/javascript-to-do-list/img/browser_console.png b/sites/javascript-to-do-list/img/browser_console.png new file mode 100644 index 000000000..965ff66c6 Binary files /dev/null and b/sites/javascript-to-do-list/img/browser_console.png differ diff --git a/sites/javascript-to-do-list/img/finished_app.png b/sites/javascript-to-do-list/img/finished_app.png new file mode 100644 index 000000000..b3269d8b9 Binary files /dev/null and b/sites/javascript-to-do-list/img/finished_app.png differ diff --git a/sites/javascript-to-do-list/img/network_tab.png b/sites/javascript-to-do-list/img/network_tab.png new file mode 100644 index 000000000..57b6fc3b6 Binary files /dev/null and b/sites/javascript-to-do-list/img/network_tab.png differ diff --git a/sites/javascript-to-do-list/img/text_editor_html.png b/sites/javascript-to-do-list/img/text_editor_html.png new file mode 100644 index 000000000..98072b963 Binary files /dev/null and b/sites/javascript-to-do-list/img/text_editor_html.png differ diff --git a/sites/javascript-to-do-list/javascript-to-do-list.step b/sites/javascript-to-do-list/javascript-to-do-list.step new file mode 100644 index 000000000..1aff077ae --- /dev/null +++ b/sites/javascript-to-do-list/javascript-to-do-list.step @@ -0,0 +1,56 @@ +message <<-MARKDOWN +### Goal + +By the end of this session, you should understand how JavaScript is used +in a modern web application. You're going to be building a personal to do list application. +You've decided that you'd like to: + +* Add an item to your list +* Mark an item as complete +* Load your list from multiple computers. + +You've sketched up an initial screenshot of what you want it to look like: + +![Browser window with list application running](img/finished_app.png) + +### Meta-Goal + +When you have completed today's goal of getting the basic +application online you should understand: + +* How JavaScript, CSS, and HTML interact with the browser. +* How to use JavaScript to interact with a server. +* How to incrementally add features to your application. +* How to get your application online. + +### Schedule + +* 2-ish hours of JavaScript and the basics of clients and servers. +* 3-ish hours of adding features to you application, broken up into 1 hour chunks. + +This is just a rough guideline, not a mandate. Some steps you'll go +over and some you'll go under. It'll all work out by the end of the +day. Probably. + +### Requirements + +When programming, you'll generally want these tools on hand: + + * An internet connection. We're going to be communicating with other websites using JavaScript. + * Your browser to see the code running (I recommend Chrome, but any will do!) + * A text editor to change the code (Railsbridge recommends Atom, but you can try a different editor) + * A Javascript console so you can experiment and print out debugging + messages. This is built into your browser. + +Before you can start the tutorial, you'll need to download the tutorial +to your computer to edit the source code. + +Once you've downloaded it, open index.html with your browser. You should see a mock up of your list. If you do not, get an instructor to help you out. +MARKDOWN + +insert 'lesson_format' + +insert 'teachers_note' + +next_step "the_basics_of_a_website" + diff --git a/sites/javascript-to-do-list/loading_items.step b/sites/javascript-to-do-list/loading_items.step new file mode 100644 index 000000000..2a4218656 --- /dev/null +++ b/sites/javascript-to-do-list/loading_items.step @@ -0,0 +1,105 @@ +goals do + goal "Make an AJAX request and update the page based on the server's response." + goal "Load your list's items every time a user visits the page." +end + +overview do + message <<-MARKDOWN + Last lesson, we made AJAX requests in the browser's console to load all of + our list's items. Now, we'll tie that AJAX request to our web site! Here's what will happen. + + 1. Every time a user visits our site, the browser will run the JavaScript in app.js. + 2. It will use jQuery's AJAX method to make a request to http://listalous.herokuapp.com/ and + get all the data about our list's items. + 3. We will then use JavaScript to turn those items into HTML elements, and attach them to our list. + MARKDOWN +end + +steps do + step do + message <<-MARKDOWN +Open app.js in your text editor. Add the following code to the bottom of the file. +Replace 'YOUR-LIST-NAME-HERE' with the name of the list you created last lesson. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + var loadRequest = $.ajax({ + type: 'GET', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/" + }) + JAVASCRIPT + + message <<-MARKDOWN +Refresh the page, and click over to your browser's network tab. You should see +a new request there, that visits our server at https://listalous.herokuapp.com/. + MARKDOWN + end + + step do + message <<-MARKDOWN + Now that we've made the request, we need to update the page whenever the request + succeeds. Add the following lines of code to the bottom of app.js. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + loadRequest.done(function(dataFromServer) { + var itemsData = dataFromServer.items + + itemsData.forEach(function(itemData) { + addItemToPage(itemData) + }) + }) + JAVASCRIPT + + message <<-MARKDOWN + Now refresh the page. Once the AJAX request succeeds, your site should now display + all the items you created last lesson! If not, flag an instructor down to help you + debug the problem. + MARKDOWN + end + +end + + +explanation do + message "App.js should now look like this." + source_code :javascript, <<-JAVASCRIPT + var itemTemplate = $('#templates .item') + var list = $('#list') + + var addItemToPage = function(itemData) { + var item = itemTemplate.clone() + item.attr('data-id',itemData.id) + item.find('.description').text(itemData.description) + if(itemData.completed) { + item.addClass('completed') + } + list.append(item) + } + + var loadRequest = $.ajax({ + type: 'GET', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/" + }) + + loadRequest.done(function(dataFromServer) { + var itemsData = dataFromServer.items + + itemsData.forEach(function(itemData) { + addItemToPage(itemData) + }) + }) + + JAVASCRIPT + + message <<-MARKDOWN +Your web page has made its first successful AJAX request! Now your page will +load your list's items whenever you visit it. Once we host this page on the internet, +you will be able to see your list on any computer, tablet, or phone! + MARKDOWN +end + + + + +next_step "adding_an_item" diff --git a/sites/javascript-to-do-list/marking_an_item_as_complete.step b/sites/javascript-to-do-list/marking_an_item_as_complete.step new file mode 100644 index 000000000..075b6ef84 --- /dev/null +++ b/sites/javascript-to-do-list/marking_an_item_as_complete.step @@ -0,0 +1,144 @@ +goals do + goal "Allow a user to mark an item as complete or incomplete." + goal "Understand how to bind a complex event listener that requires event delegation." + goal "Understand how to parse complex information from the page." + goal "Understand how to make nuanced changes to the page." +end + +overview do + message <<-MARKDOWN +In this lesson, we'll allow our users to mark items as complete and incomplete. As they +do, we will ask the server to update the item's status in its database. This process will look +a lot like the process for adding an item, with some additional complexity. + +First off, the event listener is slightly more complex. Instead of binding a listener to +a single form (like we did in the last lesson), we'll be binding a listener to every check +mark in the list. jQuery's .on function allows you to do this, using __event delegation__. + +Secondly, it will be slightly harder to get the required information from the page when +the user clicks a check mark. We need to know which item they clicked, its id (so the server +can identify it), and whether it has been completed or not. + +And finally, we need to use jQuery to add and remove classes based on whether the item is completed. +Let's get started! + MARKDOWN +end + + +steps do + step do + message "Add the following code the bottom of app.js." + + source_code :javascript, <<-JAVASCRIPT + $('#list').on('click', '.complete-button', function(event) { + alert('trying to complete an item!') + }) + JAVASCRIPT + + message <<-MARKDOWN +Refresh the page and try completing an item. An alert should pop up! + MARKDOWN + end + + step do + message "Now, we're going to get all the information we need from the page. Remove the alert from last step, and add the following code." + + source_code :javascript, <<-JAVASCRIPT + var item = $(event.target).parent() + var isItemCompleted = item.hasClass('completed') + var itemId = item.attr('data-id') + alert('clicked item ' + itemId + ', which has completed currently set to ' + isItemCompleted) + JAVASCRIPT + + message <<-MARKDOWN +Try marking an item complete again. Does the alert reference the correct item and the correct +completed value? Mark a few other items complete. Does it still work? + MARKDOWN + end + + step do + message <<-MARKDOWN +Now that we have the necessary information, we'll make another request to the server. +This is a url and type we haven't used before! What do you think it does? + +Remove the alert we wrote in the last step, and replace it with the following code. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + var updateRequest = $.ajax({ + type: 'PUT', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items/" + itemId, + data: { completed: !isItemCompleted } + }) + + JAVASCRIPT + + message <<-MARKDOWN +Refresh the page and try marking an item as complete. Check the network tab to see if +a new request was made! + MARKDOWN + end + + step do + message <<-MARKDOWN +Finally, we'll update the item that has been marked as incomplete or complete. +Instead of creating a new item, we'll simple add or remove the class 'completed' +from the specified item (using jQuery's helpful addClass and removeClass functions). +This will cause the browser to render the item differently, based on the rules +written in styles.css. Add this line of code after the updateRequest. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + updateRequest.done(function(itemData) { + if (itemData.completed) { + item.addClass('completed') + } else { + item.removeClass('completed') + } + }) + JAVASCRIPT + + message <<-MARKDOWN +Mark an item as complete, and see it turn gray! If you mark a completed item as incomplete, +it should change colors, too. + MARKDOWN + end +end + +explanation do + + message "Here's what the bottom of app.js should now look like:" + + source_code :javascript, <<-JAVASCRIPT + $('#list').on('click', '.complete-button', function(event) { + var item = $(event.target).parent() + var isItemCompleted = item.hasClass('completed') + var itemId = item.attr('data-id') + + var updateRequest = $.ajax({ + type: 'PUT', + url: "https://listalous.herokuapp.com/lists/YOUR-LIST-NAME-HERE/items/" + itemId, + data: { completed: !isItemCompleted } + }) + + updateRequest.done(function(itemData) { + if (itemData.completed) { + item.addClass('completed') + } else { + item.removeClass('completed') + } + }) + }) + + JAVASCRIPT + + message <<-MARKDOWN +You've now written three ajax requests, making a modern, dynamic web page. Don't worry if you +didn't understand every line of code – JavaScript is complex stuff! + +There are many more features you could add (deleting items, sorting items, etc.) but in our next +lesson, we'll add the most important feature: the ability of users to actually use your site! + MARKDOWN +end + +next_step "deploying_your_site" diff --git a/sites/javascript-to-do-list/next_steps.step b/sites/javascript-to-do-list/next_steps.step new file mode 100644 index 000000000..02b8a53cd --- /dev/null +++ b/sites/javascript-to-do-list/next_steps.step @@ -0,0 +1,30 @@ +message < + +Congrats, you just wrote your first lines of JavaScript code! __console.log__ is an important function – +it allows you to print information to the browser's console. It's very helpful in debugging! You can also +use the __alert__ function to make a message pop up in the browser. Try it out! + MARKDOWN + end + + step do + message "Next try some simple math that's built into JavaScript. Type these lines into console:" + + source_code :javascript, <<-JAVASCRIPT + 3 + 3 + 7 * 6 + JAVASCRIPT + end + + step do + message "**Variables** are names with values assigned to them." + + source_code :javascript, <<-JAVASCRIPT + var myVariable = 5 + JAVASCRIPT + + message "This assigns the value `5` to the name `myVariable`." + end + + step do + message "You can also do math with variables:" + source_code :javascript, <<-JAVASCRIPT + myVariable + 2 + myVariable * 3 + JAVASCRIPT + end + + step do + message "Variables can also hold more than one value. This is called an **array**." + + source_code :javascript, 'var fruits = ["kiwi", "strawberry", "plum"]' + + message "Here we're using the variable `fruits` to hold a collection of fruit names." + end + + step do + message "Arrays are a type of __object__ in JavaScript. Objects often include helpful attributes!" + + source_code :javascript, <<-JAVASCRIPT + fruits.length + JAVASCRIPT + + message <<-MARKDOWN +Objects can also have __functions__, which can be helpful for altering objects and learning more about them. +Functions are __invoked__ with parentheses, which causes them to run. + MARKDOWN + + source_code :javascript, <<-JAVASCRIPT + fruits.push("orange") + fruits.slice(1) + JAVASCRIPT + + message <<-MARKDOWN +The __push__ function allows us to add new items to an array. The slice function returns a new array with +with everything to the right of the __index__ we provided. Here, we passed the function the number 1, so +slice returned an array with everything after the first element in the array. (Note that the first element is assigned 0 as its index rather than 1.) + MARKDOWN + end + + step do + message "You can also make your own functions:" + + source_code :javascript, <<-JAVASCRIPT + var pluralize = function(word) { + return word + "s" + } + pluralize("kiwi") + JAVASCRIPT + + message "Functions take **parameters**, which are the variables they work on. In this case, we made a function called pluralize that takes one parameter, a word." + + message "Functions can also return data. In this case, pluralize returns the word with an 's' added to the end of it. In order for a function to return something, you have to use the __return__ keyword." + end + + + step do + message "Arrays have a function called **forEach** which iterates through the list running code on each item. It takes another function as a parameter." + source_code :javascript, <<-JAVASCRIPT + fruits.forEach(function(fruit) { + console.log(fruit) + }) + JAVASCRIPT + message "This takes the first item from the `fruits` array (`\"strawberry\"`), assigns it to the variable `fruit`, and runs the code between curly brackets. Then it does the same thing for each other item in the list. The code above should print a list of the fruits." + end + + step do + message "A **conditional** runs code only when a statement evaluates to true." + + source_code :javascript, <<-JAVASCRIPT + if(myVariable > 1) { + console.log('YAY') + } + JAVASCRIPT + + message "This prints `YAY!` if the value stored in `myVariable` is greater than 1." + + message "Try changing the `>` in the conditional to a `<`." + end +end + + + +next_step "playing_with_jquery" diff --git a/sites/javascript-to-do-list/the_basics_of_a_website.step b/sites/javascript-to-do-list/the_basics_of_a_website.step new file mode 100644 index 000000000..fc115070b --- /dev/null +++ b/sites/javascript-to-do-list/the_basics_of_a_website.step @@ -0,0 +1,97 @@ +goals do + goal "Understand the purposes of HTML and CSS." + goal "Write some basic code in each language." +end + +overview do + + message <<-MARKDOWN + +## What makes a website? + +Modern websites are built using three languages: __HTML, CSS, and JavaScript__. + + * __HTML__ stands for __H__yper-__t__ext __M__arkup __L__anguage. It provides the structure for your website, using a system of nested __tags__. + + * __CSS__ stands for __C__ascading __S__tyle__s__heets. It's a language for creating rules that can select various elements on the page and change their visual properties. A CSS stylesheet is composed of __rules__ that dictate how your site is presented. + + * __JavaScript__ allows users to interact with your website, making it dynamic and customizable. It's a powerful language that can be used for extraordinarily complex tasks. + +In this lesson, we'll be reviewing HTML and CSS at a high level, and see how they interact. +You'll need to fire up your text editor for these steps. +MARKDOWN +end + +steps do + + step do + message < + +Take a look around. What are the different elements doing? Why are some lines indented deeper +than others? What are those strange series of numbers and ampersands? + +Finally, you should find this line of code in index.html: +MARKDOWN + source_code :html, <<-HTML +

    Your List App

    + HTML + message "and change the text whatever you want your list application to be called. Save your file. Go back to chrome and refresh the page. Your list application should have changed!" + end + + step do + message < + + +
      +
    + HTML + end + + +end + +next_step "developer_tools" \ No newline at end of file diff --git a/sites/javascript-to-do-list/zip_content/app.js b/sites/javascript-to-do-list/zip_content/app.js new file mode 100644 index 000000000..e69de29bb diff --git a/sites/javascript-to-do-list/zip_content/debut_light.png b/sites/javascript-to-do-list/zip_content/debut_light.png new file mode 100644 index 000000000..2f4febcb6 Binary files /dev/null and b/sites/javascript-to-do-list/zip_content/debut_light.png differ diff --git a/sites/javascript-to-do-list/zip_content/index.html b/sites/javascript-to-do-list/zip_content/index.html new file mode 100644 index 000000000..c3953e709 --- /dev/null +++ b/sites/javascript-to-do-list/zip_content/index.html @@ -0,0 +1,38 @@ + + + + + + + +
    +

    Your List App

    +
    +
    + +
    +
      +
    • + +
      A gallon of milk
      + +
    • +
    • + +
      A stick of butter
      + +
    • +
    +
    +
    + +
      +
    • + +
      A stick of butter
      + +
    • +
    + + + \ No newline at end of file diff --git a/sites/javascript-to-do-list/zip_content/styles.css b/sites/javascript-to-do-list/zip_content/styles.css new file mode 100644 index 000000000..0a8d26b84 --- /dev/null +++ b/sites/javascript-to-do-list/zip_content/styles.css @@ -0,0 +1,107 @@ +body { + background: url('https://github.com/debut_light.png') +} + +#container { + width: 600px; + font-family: 'Titillium Web', 'Arial', sans-serif; + font-size: 40px; + text-align: center; + margin: 40px auto; +} + +h1 { + margin: 20px; +} + +#app { + box-shadow: 0 0 5px #684F91; + font-size: 35px; + color: #080D3B; + background-color: white; +} + +form { + margin: 0; + padding: 0; +} + +#create { + width: 100%; + font-family: inherit; + font-size: inherit; + padding: 10px 30px; + border-left: 1px solid #7d82b0; + border-right: 1px solid #7d82b0; + border-bottom: 2px solid #7d82b0; + border-top: 15px solid #477187; + text-align: center; +} + + +#create::-webkit-input-placeholder { + color: #C3C3C4; + text-align: center; +} + +#create::-moz-placeholder { /* Firefox 19+ */ + color: #C3C3C4; + text-align: center; +} + +#create:focus::-webkit-input-placeholder { + color: transparent; + text-align: center; +} + +#create:focus::-moz-placeholder { /* Firefox 19+ */ + color: transparent; + text-align: center; +} + +#list { + list-style-type: none; + margin: 0; + padding: 0; + box-sizing: border-box; +} + +.item { + position: relative; + box-sizing: border-box; + padding: 10px; + list-style-type: none; + width: 100%; + border-left: 1px solid #7d82b0; + border-right: 1px solid #7d82b0; + border-bottom: 2px solid #7d82b0; + border-top: 0px solid #7d82b0; + +} + +.item .complete-button { + position: absolute; + left: 20px; + top: 10px; + cursor: pointer; +} + +.item .value { + margin: 0 50px; + padding: 0 15px; +} + +.item .delete-button { + position: absolute; + right: 20px; + top: 10px; + cursor: pointer; +} + +.item.completed { + color: #C3C3C4; +} + +#templates { + display: none +} \ No newline at end of file diff --git a/sites/job-board/add_a_navbar.step b/sites/job-board/add_a_navbar.step new file mode 100644 index 000000000..39f237c0c --- /dev/null +++ b/sites/job-board/add_a_navbar.step @@ -0,0 +1,123 @@ +message <<-MARKDOWN + # What we're going to do + + * Add top-level navigation + * Learn about CSS organization techniques + * Add some really, really ugly CSS to our app + + # Application layout + + We'll start with the application layout, which you can find in the views directory, at `app/views/layouts/application.html.erb`. +MARKDOWN + +discussion_box "What's an application layout?", <<-MARKDOWN + None of the view pages that we've been working with so far have had the necessary structure for a valid HTML page. No `` or `` or JavaScript or CSS tags. This application layout file is where all of that stuff exists! + + * How is this page related to the individual page content? Try comparing the source in the browser to this file, and see where the individual page's code starts showing up. + * If you add something (say, an h1) to this file, where will it show up? +MARKDOWN + +message <<-MARKDOWN + # Add the markup + Copying and pasting a nav bar onto every. single. page. of our application would be the worst. So instead of putting it into our individual pages, we'll add the nav bar HTML to this file! + + Add the following code **above** the line `<%= yield %>` +MARKDOWN + +source_code :erb, <<-ERB +
    +
    +
      +
    • <%= link_to "Home", jobs_path %>
    • +
    +
    +
    +
      +
    • <%= link_to "Add Job", new_job_path %>
    • +
    +
    +
    + ERB + +message <<-MARKDOWN + Additionally, wrap the `<%= yield %>` in a containing wrapper div so it looks like this: +MARKDOWN + +source_code :erb, <<-ERB +
    + <%= yield %> +
    +ERB + +message <<-MARKDOWN + Things to note: + + * We use Rails link helpers instead of typing in `Add Job`. The reason is that if the url of a page changes in the future, without the link helpers, we’d have to update every single place we link to that page. The link helpers abstract the actual url for us, so if we change a specific url, we update the address in only one place, in the routes file; and all the places we link to it stay the same. + * The `
    ` tag is HTML5. Aren't we cool!? + + So let's take a look at it. Refresh, and ... isn't that horrifying looking? Let's make it look like a nav, albeit a very ugly one. + + # Add the styles + + Open up the assets directory, and you should have a file here: app/assets/stylesheets/jobs.scss. This is a Rails-default created stylesheet, and isn't the best. [Smart CSS people](http://www.stubbornella.org/content/2013/09/12/rails-is-mucking-up-my-css-already/) have taught us that CSS should be organized into [reusable components](https://github.com/stubbornella/oocss/wiki), not organized based on where it is used. + + So let's delete that file and make a new one. + + Actually, we're going to make two new ones. Under `app/assets/stylesheets`, add `global.scss`: +MARKDOWN + +source_code :CSS, +<<-CSS + body { + margin: 0; + } + + .content { + margin: 10px auto; + width: 70%; + } + + .clearfix { + overflow: hidden; + } +CSS + +message <<-MARKDOWN + This is where we put styles that affect the whole app. + + Now, under `app/assets/stylesheets`, add `nav.scss`: +MARKDOWN + +source_code :CSS, +<<-CSS + header { + background: grey; + padding: 10px; + ul { + margin: 0; + padding: 0; + list-style-type: none; + } + a { + text-decoration: none; + font-weight: bold; + color: white; + } + } + + .left-nav { + float: left; + } + + .right-nav { + float: right; + } +CSS + +message <<-MARKDOWN + Refresh the page, and ... it's still totally horrific. This CSS is not going to win any awards. + + But it's usable, and we can keep working on that later. It turns out sometimes job descriptions have typos, and need to be updated. Let's make that possible! +MARKDOWN + +next_step "update_job_listings" diff --git a/sites/job-board/add_a_new_job_form.step b/sites/job-board/add_a_new_job_form.step new file mode 100644 index 000000000..17520d5a3 --- /dev/null +++ b/sites/job-board/add_a_new_job_form.step @@ -0,0 +1,83 @@ +message <<-MARKDOWN + # What we're going to do + + * Use Rails form helpers to create the HTML for the form + * Learn to use the Rails server output for great good + * Update the controller so that the form saves submissions to the database + * Learn about the beauty of the params hash + + # Side Note: Web forms are not perfectly straightfoward + + Web forms. They're like, the most basic thing about the internet, other than cat gifs, right? So they should be straightforward, right? WRONG! They are exciting and just complicated enough to bake your noodle a bit. But don't worry, Rails has strong opinions about forms, so we won't have to do _too_ much typing. + + # Setting up the page for the form + + Let's take a look at our handy routes, which can help us figure out what we need to do. We're going to follow the same pattern that we did for the main `/jobs` page. + + First, visit the routing page at , and find the route for `/jobs/new`. + + That's the one we want for our form. Let's visit that page and see what it says: . +MARKDOWN + +error_box "The action 'new' could not be found for JobsController" + +source_code_with_message "This looks familiar! Let's add a method to our jobs controller called `new`:", :ruby, +<<-RUBY + def new + end +RUBY + +message <<-MARKDOWN + Refresh , and we see that familiar "Template is missing" error. So, let's add that template. + + Under app/views/jobs, add a file called new.html.erb. This will be our form. Add some html to the template to keep things moving along: +MARKDOWN + +source_code :html, +<<-HTML +

    Add a job

    +HTML + +message <<-MARKDOWN + Refresh again: + + # Add a form! + + Rails has handy helpers for forms. We'll be using a form helper specifically made for use with a model. +MARKDOWN + +source_code_with_message "In the view file you were just editing (app/views/jobs/new), add the following code:", :erb, +<<-ERB + <%= form_for @job do |f| %> +
    + <%= f.label :title %> + <%= f.text_field :title %> +
    +
    + <%= f.label :description %> + <%= f.text_area :description, size: '60x6' %> +
    +
    + <%= f.submit %> +
    + <% end %> +ERB + +message "Save that file, and reload the page." + +error_box "First argument in form cannot contain nil or be empty" + +message "What do you think that means? What is the first argument? What is it supposed to be? In order for the method `form_for` to do its job, it has to know about the thing that it's building the form for. So we need to give it an object. We do that in the controller." + +source_code_with_message "Open up the jobs_controller, and update the new method:", :ruby, +<<-RUBY + def new + @job = Job.new + end +RUBY + +message "Now we should see our mostly unstyled form!" + +discussion_box "Form HTML", "What HTML did the form helpers produce? Using the web inspector, look through the form code and compare it to the file you've been working on in Atom." + +next_step "make_the_form_work" diff --git a/sites/job-board/add_more_things.step b/sites/job-board/add_more_things.step new file mode 100644 index 000000000..96322f257 --- /dev/null +++ b/sites/job-board/add_more_things.step @@ -0,0 +1,22 @@ +message <<-MARKDOWN + + ## Time to add more things! + + The time where we tell you what to type in has finally ended. You should do one or all of the things below! + + Working independently or in pairs is super fun at this point. + + * Add a show page for each listing and move the Edit and Delete links to there. + * Add validations — postings without titles or descriptions shouldn't be allowed, right? + * Add other useful fields to jobs + * Display jobs index in a table; make sortable with data_tables + * Add tags (time for a has_many, folks!!!) + * Make it look better + * Add a user model and auth with Devise + * Test it! Write a controller spec. + + ## Credits + + This curriculum was originally written by [Lillie Chilen](http://www.twitter.com/lilliealbert) + and has been improved by all sorts of [lovely RailsBridge volunteers](https://github.com/railsbridge/docs/commits/master/sites/en/job-board). +MARKDOWN diff --git a/sites/job-board/create_a_rails_app.step b/sites/job-board/create_a_rails_app.step new file mode 100644 index 000000000..4f03b75e3 --- /dev/null +++ b/sites/job-board/create_a_rails_app.step @@ -0,0 +1,75 @@ +message <<-MARKDOWN + # What we're going to do: + + * Make a new Rails app + * Add a few gems to make life easier + * Start the Rails server and see the default Rails page + * Learn a little about Bundler and dependency management + + # Get Set Up + + First, let's get into a directory for RailsBridge projects (either by finding your existing one & moving there or making a new folder). + + If you don't already have a RailsBridge folder, use the commands `mkdir` and `cd` to create one and move into it: +MARKDOWN + +console_without_message "mkdir railsbridge" +console_without_message "cd railsbridge" + +message <<-MARKDOWN + # Rails New!!! +MARKDOWN + +console "rails new job_board -T --skip-turbolinks" + +message <<-MARKDOWN + The `-T` in that command means that when you make new files using Rails generators, it doesn't automatically create test files using Test::Unit (the default Rails testing framework). + + Watch all the files that are created! Wow! + + # Open the project in Atom +MARKDOWN + +console_with_message "Move into the directory for your new Rails app:", "cd job_board" + +message <<-MARKDOWN + And open the project in Atom: + + * Open Atom + * Under Project, choose "Add Folder to Project" + + (You must have at least one window open, so if that option is greyed out, open a window with cmd+n (Mac) or ctl+n (PC)) +MARKDOWN + +discussion_box "Text Editor vs Command Line", "Review the differences between the command line and your text editor, even if everyone already knows!" + +message "# Let's Talk About Dependencies" + +message "When we created a new Rails app, it installed a bunch of stuff by default. The list of things Rails installed is in a file called `Gemfile`. If you want to add any additional third party code (aka **gems**), you can add more lines to the `Gemfile` and install them with `bundle`." + +console_with_message "Rails has already installed all the stuff we need, but you can always run bundle again to re-install gems, or install gems newly added to the Gemfile. In the command line, run the following command:", "bundle install" + +discussion_box "What does 'bundle' do?", <<-MARKDOWN + Bundler is the tool the Ruby community uses for dependency management. + + * What's dependency management? + * Why do we need it? + * Why do we even need gems? + * Is there a shorter method to use for `bundle install`? (Hint: yes!) +MARKDOWN + +message "# Look at your empty app" + +tip "Now is a good time to figure out how to have multiple tabs or windows of your terminal or command prompt. Starting and stopping the Rails server all day is tedious, so it's good to have one terminal tab or window for running commands, and a separate one for the server." + +console_with_message "Start the Rails server by running this command in the terminal:", "rails server" + +message <<-MARKDOWN + Now, let's check out our default home page + + In the browser, visit + + Yup, that's the default Rails home page! +MARKDOWN + +next_step "the_request_cycle" diff --git a/sites/job-board/crud_and_resourceful_routing.step b/sites/job-board/crud_and_resourceful_routing.step new file mode 100644 index 000000000..0c4678418 --- /dev/null +++ b/sites/job-board/crud_and_resourceful_routing.step @@ -0,0 +1,38 @@ +message <<-MARKDOWN + # CRUD + Why do we talk about building CRUD apps? What are some examples of CRUD apps that you use? + + Each action that CRUD describes maps to a SQL statement and an HTTP verb: + + Table mapping CRUD operations to SQL and HTTP verbs + + *Source: * + + Compare this to what we see at : + + !["Screenshot of Rails routes page"](img/rails-routes.png) +MARKDOWN + +discussion_box "CRUD", "Talk through the different uses for the HTTP verbs. What is their general purpose, and what are we specifically using them for??" + +message <<-MARKDOWN + To have a useful app, we need to be able to create job postings, see them all and their details, change them, and delete them. The way that Rails wants us to do this is by following RESTful routing conventions, which are perfectly encapsulated by the output of `resources :jobs` in our routes.rb file. + + In Rails we work with seven main verbs, which map to different CRUD actions and controller actions: + + Table mapping CRUD operations to REST verbs + + What does each of these do? Go through each method and review what they do. + + # Resourceful Routing + + You could make your URLs look like anything you wanted, like: `/all_the_jobs` or `/add_a_job` or `show_job`. This would totally work if you set up your controller methods and the route file correctly. + + But we generally avoid this because the RESTful routing is a best practice that Rails makes easy to follow. In RESTful routes, the paths are structured around a specific resource — in our case, jobs. Check out more about [RESTful](http://en.wikipedia.org/wiki/Representational_state_transfer) urls. + + Another example: if we were making a puppy app, I would expect that going to `/puppies` would show me an index of all the puppies, and `/puppies/1` would find the puppy with the ID of one and show me their individual page, and `/puppies/1/edit` would show me the form to update a given puppy. + + For more info on resource-based routing, check out the [Rails Guide on routing](http://guides.rubyonrails.org/routing.html#resource-routing-the-rails-default). +MARKDOWN + +next_step "listing_the_jobs" diff --git a/sites/job-board/delete_job_listings.step b/sites/job-board/delete_job_listings.step new file mode 100644 index 000000000..bf2f8208d --- /dev/null +++ b/sites/job-board/delete_job_listings.step @@ -0,0 +1,66 @@ +message <<-MARKDOWN + # What we're going to do + + * Remove postings + + Once a job is filled, we don't want a listing for it hanging out forever. Let's add a way to delete postings. + + # Postings be gone! + + If we look at our [handy Routes page](http://localhost:3000/rails/info/routes), we see this: +MARKDOWN + +source_code :html, "job_path DELETE /jobs/:id(.:format) jobs#destroy" + +message "So we need to send the DELETE http verb to the server, so we can get to the destroy method on the jobs controller (that we will make soon). It turns out rails link_to helpers accept specific verbs as an argument, so we can add this line below the edit posting link that we just added:" + +source_code :erb, "
    <%= link_to 'Delete Posting', job, method: :delete %>
    " + +message "Go to the index, and try to delete something." + +error_box "The action 'destroy' could not be found for JobsController" + +message "This is like my favorite error now! First, let's add the method:" + +source_code :ruby, +<<-RUBY + def destroy + end +RUBY + +message <<-MARKDOWN + This fixes the error. But just like updating, we still need to add the logic that actually deletes the job. + + See if you can figure out the right syntax for finding the job, deleting it, and then redirecting to a useful page. + + **Don't scroll down!!!** +MARKDOWN + +message <<-MARKDOWN + * here + * is + * even + * more + * strategic + * white + * space + * so + * the + * answer + * isn't + * immediately + * visible! + + Okay, here's the answer: +MARKDOWN + +source_code :ruby, +<<-RUBY + @job = Job.find(params[:id]) + @job.destroy + redirect_to jobs_path +RUBY + +message "Try it again, and... kablammo! We're destroying job listings left and right!" + +next_step "add_more_things" diff --git a/sites/job-board/img/crud_grid.jpg b/sites/job-board/img/crud_grid.jpg new file mode 100644 index 000000000..408bf0956 Binary files /dev/null and b/sites/job-board/img/crud_grid.jpg differ diff --git a/sites/job-board/img/crud_rails_methods.jpg b/sites/job-board/img/crud_rails_methods.jpg new file mode 100644 index 000000000..a37308843 Binary files /dev/null and b/sites/job-board/img/crud_rails_methods.jpg differ diff --git a/sites/job-board/img/rails-routes.png b/sites/job-board/img/rails-routes.png new file mode 100644 index 000000000..086b0eea4 Binary files /dev/null and b/sites/job-board/img/rails-routes.png differ diff --git a/sites/job-board/img/request-cycle.jpg b/sites/job-board/img/request-cycle.jpg new file mode 100644 index 000000000..53ac5ed5b Binary files /dev/null and b/sites/job-board/img/request-cycle.jpg differ diff --git a/sites/job-board/job-board.step b/sites/job-board/job-board.step new file mode 100644 index 000000000..f2c78c2b0 --- /dev/null +++ b/sites/job-board/job-board.step @@ -0,0 +1,30 @@ +message <<-MARKDOWN + We're going to build a job board in Rails using the tried-and-true method of following the errors that we make! + + Rails generators will help us avoid some tedious file creation, but provide a lot less magic than scaffolding (which is what is used in the Suggestotron to create *everything* for a Topic all at once). + + This means we'll a get a little less done today than we did when we built Suggestotron. But we're going to build an app in little pieces, so you can focus on understanding how the pieces fit together. +MARKDOWN + +tip "This is not a self-paced curriculum. You should use the discussion sections on each page to make sure everyone is together!" + +message <<-MARKDOWN + # Notable Things + + As you might have noticed, we're assuming you've already been to a RailsBridge workshop before or have otherwise already explored a Rails app, and are ready for deeper knowledge. + + We're also going to skip deploying to Heroku this time around, but you can definitely use the instructions from the Suggestotron curriculum to deploy your app to the internet! +MARKDOWN + +important "This curriculum is written for Rails 5. Things will get awkward / broken if you're using an earlier version of Rails, so if you skipped the Installfest, you need to upgrade to Rails 5 now." + +message <<-MARKDOWN + # Tips for everyone: + + * When adding code, it's awesome for students to walk through the code line by line and say out loud what is happening. (i.e., "The string is being stored in the instance variable" or "The method `snorgle` is being defined"). If you do it every time, you'll get really comfortable with the vocabulary of Rails! + * Error messages are your friend! Read them carefully, and practice understanding what Rails is telling you. Seeing an error and just diving back into your code is a natural reaction, but stop! Then read, think, and talk about what the error means before fixing it. +MARKDOWN + +insert '../intro-to-rails/working_effectively_and_efficiently' + +next_step "create_a_rails_app" diff --git a/sites/job-board/listing_the_jobs.step b/sites/job-board/listing_the_jobs.step new file mode 100644 index 000000000..1a6eeccd7 --- /dev/null +++ b/sites/job-board/listing_the_jobs.step @@ -0,0 +1,51 @@ +message <<-MARKDOWN + # What we're going to do + + * Show the jobs! + * Learn about ERB + + Going back to the jobs index (), we expect to see the new jobs. Let's actually build the job board part of this job board now! + + # Get all the jobs out of the database + + If we're going to show our jobs in view, first we have to get them out of the database and store them in an instance variable. Update the index method to look like this: +MARKDOWN + +source_code :ruby, +<<-RUBY + def index + @jobs = Job.all + end +RUBY + +message <<-MARKDOWN + Before we show the jobs, let's actually look at what that is doing. Go back to your Rails console and run `Job.all`. +MARKDOWN + +discussion_box "Rails Console", <<-MARKDOWN + The Rails console is super fun! It's giving us direct access to our local database. + + * Try running `Job.all.to_sql`. What does that do? + * Try selecting an individual Job record. + * Try updating that individual record from the console! +MARKDOWN + +message "# Show those jobs!" + +source_code_with_message "Add this to app/views/jobs/index.html.erb:", :erb, +<<-RUBY + <% @jobs.each do |job| %> +

    <%= job.title %>

    +

    <%= job.description %>

    + <% end %> +RUBY + +discussion_box "ERB", <<-MARKDOWN + What is this doing? Go through this line by line, having one person explain each line. + + Compare the ERB to the HTML that shows up on the page. ("Inspect Element" is your friend!) + + What's the difference between a line with `<% %>` brackets and `<%= %>` brackets? +MARKDOWN + +next_step "add_a_navbar" diff --git a/sites/job-board/make_a_jobs_home_page.step b/sites/job-board/make_a_jobs_home_page.step new file mode 100644 index 000000000..896107c53 --- /dev/null +++ b/sites/job-board/make_a_jobs_home_page.step @@ -0,0 +1,120 @@ +message <<-MARKDOWN + # What we're going to do: + + * Play follow-the-error to guide our development + * Add routes, a controller, and a view to make a home page + * Learn how to read the routing info at + + # Getting Started + + That Rails default page is boring. Let's add a page to list the open jobs at RailsBridgeCorp! + + To start, let's visit the page that we want to put our jobs on: . +MARKDOWN + +error_box "No route matches [GET] '/jobs'" + +message <<-MARKDOWN + That's pretty reasonable, since we currently don't have any routes at all right now. + + # Adding Routes + + So it's looking for a route, but can't find one. Let's add one! + + Open up the routes file. It's in the config directory, called `routes.rb`. If you're using Atom, you can open it using keyboard shortcuts: + + * Hitting cmd + p (on Mac) or ctl + p (on PC) + * typing in `route` + * hitting enter + + Magic! (Atom is using fuzzy search, so you can use the entire file path, or just part of the filename to go to files.) +MARKDOWN + +source_code_with_message "We're going to need a resource route, which will create EIGHT different routes for us. Add this to line two:", :ruby, "resources :jobs" + +message <<-MARKDOWN + Now, lets go look at what that made, by using the excellently helpful routes page: . +MARKDOWN + +discussion_box "How to read the routes page.", <<-MARKDOWN + In your browser, visit . Go over each of the columns and how to read it. + + * Helper + * HTTP Verb (don't worry about going to in-depth with these yet) + * Path + * Controller#Action + + Why does the Helper column sometimes not have anything? + + Can you find the line that will make `/jobs` a route? +MARKDOWN + +message <<-MARKDOWN + Since adding the line `resources :jobs` made a route matching `/jobs`, let's go visit that page again: +MARKDOWN + +error_box "uninitialized constant JobsController" + +message <<-MARKDOWN + Why does Rails now think it needs a JobsController? + + # Add a controller + + Time for a shortcut!!! Unlike the scaffold that we make in Suggestotron that makes about a million different files, we're just going to use a Rails generator to make the files we need. +MARKDOWN + +console "rails generate controller jobs" + +discussion_box "What did that command do?", <<-MARKDOWN + What files were made by that last command? Are they awesome? + + How will this change what error we see when we go to the jobs page? +MARKDOWN + +message "Now: refresh " + +error_box "The action 'index' could not be found for JobsController" + +message <<-MARKDOWN + What is telling Rails to look in JobsController? + + Let's go back to again and look at what controller and method (AKA action) the route /jobs is pointing to. + + It's looking for a method called index on the jobs controller! Since there isn't a controller method for this route, we'll need to add it. + + Open up your jobs controller (again, try to use keyboard shortcuts: cmd+p on Mac or ctl+p on PC to find the file at app/controllers/jobs_controller.rb). +MARKDOWN + +source_code_with_message "Add the index method to the controller:", :ruby, +<<-RUBY +class JobsController < ApplicationController + def index + end +end +RUBY + +message "And refresh " + +error_box "JobsController#index is missing a template for this request format and variant." + +message <<-MARKDOWN + What's a template? How does Rails decide to look for the template associated with JobsController's `index` action? + + Talk through what Rails is trying, and failing, to do, and how file names and method names are important here. + + # Add a view + + Within app/views/jobs, add a file called index.html.erb. + + Save that file, and refresh + + NO ERROR!?!?! How boring. Let's add some content so we can be more confident that this really a thing. +MARKDOWN + +source_code_with_message "Within index.html.erb, add this (or the name of your fictional megacorp):", :html, "

    RailsBridgeCorp Open Jobs

    " + +message <<-MARKDOWN + DONE! Well, except that we don't have any jobs. And even though we could hand-code a table of job titles here, that would be awfully tedious and we'd probably get sick of adding job postings every time someone came up with a new volunteer position. Let's empower people to add their own postings! +MARKDOWN + +next_step "store_jobs_in_the_database" diff --git a/sites/job-board/make_the_form_work.step b/sites/job-board/make_the_form_work.step new file mode 100644 index 000000000..747e99ad9 --- /dev/null +++ b/sites/job-board/make_the_form_work.step @@ -0,0 +1,132 @@ +message <<-MARKDOWN + # What we're going to do + + * Make the form actually work + * Learn how to read server output + * Review happy debugging techniques + + # Partytime: Use the form to Create Objects +MARKDOWN + +message "Now, try to submit your form." + +error_box "The action 'create' could not be found for JobsController" + +message <<-MARKDOWN + So, let's add it: + + (Don't forget to navigate to the jobs controller by using the search shortcut, cmd+p or ctl+p) +MARKDOWN + +source_code :ruby, +<<-RUBY + def create + end +RUBY + +message "Try to use your form again. Unfortunately, there won't be any output, not even an error page. But if you look closely at the output from your Rails server, you might find this:" + +error_box "No template found for JobsController#create, rendering head :no_content" + +<<-MARKDOWN + Familiar error, right? We don't have a template called create.html.erb. + + BUT!!! This time we're not going to make one. + + We actually want to go somewhere useful, right? Let's go back to the main jobs page (where we will eventually be listing our jobs). We need to tell the create method where to go, instead of letting it decide based on method name. +MARKDOWN + +source_code :ruby, +<<-RUBY + def create + redirect_to jobs_path + end +RUBY + +message <<-MARKDOWN + Okay, now go to again, and submit a new job. + + Hopefully it went to the right place! But did it do anything? + + Sadly, no. We just took our form contents and dropped them on the ground. +MARKDOWN + +discussion_box "Logging & Server Output", +<<-MARKDOWN + Arrange your screens so that you can see your server logs (in the terminal window) at the same time as your browser. Now, refresh the form page, and look at what happens in the server. You should see output like `Started GET "/jobs/new"`. + + As a group or in pairs, go over the output of the server, talking through the various pieces of information you see. +MARKDOWN + +message <<-MARKDOWN + # Saving form data! + + Head back to , and get your Rails server logs and your browser next to each other again. Submit the form again, this time looking for the section that looks something like: +MARKDOWN + +source_code :http, +<<-JSON + Parameters: { + "utf8"=>"✓", + "authenticity_token"=>"f48rtxanK9/MHu7TPvd6QzygGnrwv0P2/bxLllozw5U=", + "job"=>{ + "title"=>"Meta-organizer", + "description"=>"We need an somene to organize the organizers." + }, + "commit"=>"Create Job" + } +JSON + +message <<-MARKDOWN + This is the precious data that our form is sending, and right now we're just throwing it away. Let's not do that! Since we're using Rails 5 and all its great conventions, we're going to use Strong Parameters to limit what kind of data our form can submit to our app. +MARKDOWN + +source_code_with_message "Add this code to your jobs controller. (Notice that we're expanding the create method. Don't just copy and paste and end up with two create methods, folks.)", :ruby, +<<-RUBY + def create + Job.create(job_params) + redirect_to jobs_path + end + + private + + def job_params + params.require(:job).permit(:title, :description) + end +RUBY + +message <<-MARKDOWN + Walk through this code line by line with the class! (But don't get too hung up on how strong params works — suffice it to say that it limits the parameters that we'll allow in to the ones listed here.) +MARKDOWN + +console_with_message "Rails console time! Open up another tab or window in your terminal and type this:", "rails c" + +console_with_message "After that's loaded, let's see how many jobs we've saved to the database:", "Job.count" + +message <<-MARKDOWN + Now, submit a new job using the form. What's the count now? + + If the count is going up, yay!!! You got it! If not, time to debug! + + # Tips for Effective Debugging + + * Take a deep breath. Most of your time programming will be looking for bugs. + * Read the error message out loud. Even if you already think you know what it means. + * Check every assumption you can think of. You think that something is getting stored in a variable? WHO KNOWS? + * A good way to check your assumptions is to print out anything you can to the Rails server log. Add puts statements to your code! + * For example: If the jobs count isn't changing when we make jobs, make the jobs controller look like this. Now, it will print to the console the line "In the create method!!!!" and whatever is being returned from `Job.create(job_params)` +MARKDOWN + +source_code :ruby, +<<-RUBY + def create + p "In the create method!!!!!!" + job = Job.create(job_params) + p job + redirect_to jobs_path + end +RUBY + +message "* Think about how you would explain the problem to someone else, or, actually explain the problem to another student or a TA!" + +next_step "crud_and_resourceful_routing" diff --git a/sites/job-board/store_jobs_in_the_database.step b/sites/job-board/store_jobs_in_the_database.step new file mode 100644 index 000000000..c9aa51357 --- /dev/null +++ b/sites/job-board/store_jobs_in_the_database.step @@ -0,0 +1,85 @@ +message <<-MARKDOWN + # What we're going to do: + + * Review what a database is + * Create a table in the database for jobs + * Learn what a migration file is +MARKDOWN + +discussion_box "Databases & SQL", +<<-MARKDOWN + Review how relational databases are structured, how we communicate with them (hint: SQL!). +MARKDOWN + +message <<-MARKDOWN + # Make a jobs model & migration + + In order to make it possible for users to create jobs, we need: + + * A place to store the jobs (a table in the database) + * Rails to know what a job is (a model) + + We're going to use another Rails generator; this time to make our migration and model! +MARKDOWN + +console "rails g model job" + +message <<-MARKDOWN + (The g stands for *generate*, which has many more letters than is necessary to type.) + + That generated two files: a migration and models/job.rb. + + # Make that migration file useful + + Open up the migration file that you just made (cmd+p / ctl+p, type 'create jobs', and hit enter) and you'll see the following: +MARKDOWN + +source_code :ruby, <<-RUBY + class CreateJobs < ActiveRecord::Migration[5.0] + def change + create_table :jobs do |t| + + t.timestamps + end + end + end +RUBY + +message <<-MARKDOWN + Running this code will make a table in our database called jobs. Right now it just has the timestamps (`created_at` and `updated_at`). What else should a job have? Let's start with a title and description. + + Add the title and description so it looks like this: +MARKDOWN + +source_code :ruby, <<-RUBY + create_table :jobs do |t| + t.text :title + t.text :description + t.timestamps + end +RUBY + +message "Now we need to execute this file, so that the database schema gets updated." + +console "rails db:migrate" + +message <<-MARKDOWN + This tells Rails to run any migration files that haven't already been run at some point in the past. +MARKDOWN + +discussion_box "Why do we use migrations?", +<<-MARKDOWN + Talk about the pros and cons of using migrations to update the database, instead of just updating the schema directly. Also, discuss what the database schema is! + + (Pro-tip: *never* update the schema directly.) +MARKDOWN + +message <<-MARKDOWN + # Check out the model + + The migration we just ran updated the database, but that doesn't mean that we can talk to the database using Ruby yet. Look at the file `app/models/job.rb`. The `Job` class inherits from ApplicationRecord, so that we can talk to the database with Ruby instead of SQL! + + Okay, so we've got some place to store our jobs. But how can we make any? THROUGH THE MAGIC OF FORMS!!! +MARKDOWN + +next_step "add_a_new_job_form" diff --git a/sites/job-board/the_request_cycle.step b/sites/job-board/the_request_cycle.step new file mode 100644 index 000000000..ce3281348 --- /dev/null +++ b/sites/job-board/the_request_cycle.step @@ -0,0 +1,9 @@ +message "" + +discussion_box "What is this diagram?", <<-MARKDOWN + Talk through this diagram of the request cycle! + + If there's room, act out a request being made as a tiny play, in which a small object is the request and the students are the various pieces of code that the request travels through. +MARKDOWN + +next_step "make_a_jobs_home_page" diff --git a/sites/job-board/update_job_listings.step b/sites/job-board/update_job_listings.step new file mode 100644 index 000000000..52ceb2803 --- /dev/null +++ b/sites/job-board/update_job_listings.step @@ -0,0 +1,176 @@ +message <<-MARKDOWN + # What we're going to do + + * Create a form for editing job listings + * Learn about partials + * Revel in the joy of Rails form helpers + + # Add the edit page +MARKDOWN + +source_code_with_message "Say we want to edit the first job posting. If we look at , we see this line:", :html, "edit_job_path GET /jobs/:id/edit(.:format) jobs#edit" + +message "So, it looks like if we want to edit the job description, we should visit this URL: ." + +error_box "The action 'edit' could not be found for JobsController" + +source_code_with_message "We've seen this before, right? Let's add the controller action above the word `private`:", +<<-RUBY + def edit + end +RUBY + +source_code_with_message "Refresh, template is missing. Alright, let's add that edit view, under app/views/jobs/edit.html.erb", :erb, +<<-ERB +

    Edit Posting

    +ERB + +message <<-MARKDOWN + Okay, so that's awesome. Now we just have to add a form for editing. I wonder if it is any different from the create form? I guess we could copy and paste the other form? + + We could copy and paste from the other form, but we try to avoid that because duplicated code is hard to maintain. For example, if I want to add placeholder text for the inputs in the form, when the code is duplicated, I’ll need to update the code in each place the form was copied. (In large apps it’s easy to miss a place that would need to be updated.) The solution is to reuse rather than duplicate the code, and the way to reuse code in views is by using partials. +MARKDOWN + +discussion_box "Don't Repeat Yourself", +<<-MARKDOWN + * What are some reasons to DRY up our code? + * What are some strategies for DRYing up code throughout a Rails app? +MARKDOWN + +message <<-MARKDOWN + # Create a Partial + + Rails form helpers are designed beautifully for CRUD interfaces. So we're not gonna have to write very much code to make this form work for editing AND creating job postings. + + But first, a refactor: we're going to move the create form into a partial. + + (Refactoring is improving code while maintaining the behavior it produces.) + + Make a new file under jobs like so: `app/views/jobs/_form.html.erb`, and move the following code OUT of `app/views/jobs/new.html.erb` and into the `_form.html.erb` file: +MARKDOWN + +source_code :erb, +<<-ERB + <%= form_for @job do |f| %> +
    + <%= f.label :title %> + <%= f.text_field :title %> +
    +
    + <%= f.label :description %> + <%= f.text_area :description, size: '60x6' %> +
    +
    + <%= f.submit %> +
    + <% end %> +ERB + +source_code_with_message "Now, in `app/views/jobs/new.html.erb`, add the following line:", :erb, +<<-ERB + <%= render "form" %> +ERB + +message <<-MARKDOWN + Add a job posting, just to make sure that the form is working as expected. + + # Use the power of partials +MARKDOWN + +source_code_with_message "Now that we have a form partial, we can reuse it! In `app/views/jobs/edit.html.erb`, we can add the same line under the header:", :erb, +<<-ERB +<%= render "form" %> +ERB + +message "Refresh the page." + +error_box "First argument in form cannot contain nil or be empty" +source_code_with_message "It looks like we don't have a job ... because we haven't gotten our job out of the database! Let's go to the jobs\\_controller and fix that. In jobs\\_controller.rb, add the following", :ruby, +<<-RUBY + def edit + @job = Job.find(params[:id]) + end +RUBY + +discussion_box "Params", "What is `Job.find(params[:id])` doing? What is `params` again?" + +message <<-MARKDOWN + # Actually Update The Job + + So now the form works. Let's try to update that job posting. Change something about the job posting, and submit the form. +MARKDOWN + +error_box "The action 'update' could not be found for JobsController" + +source_code_with_message "So it looks like the form is finding the right route, but the method is missing from the controller. Let's add the update method--again, above the word `private`--to the file jobs_controller.rb", :ruby, +<<-RUBY + def update + end +RUBY + +source_code_with_message "Try it again, and ... template missing error! Similarly to create, we don't have a template to render for update. So let's just send them back to the jobs listing.", :ruby, +<<-RUBY + def update + redirect_to jobs_path + end +RUBY + +message <<-MARKDOWN + Try again, and ... no errors! But we're still not seeing our changes. +MARKDOWN + +discussion_box "What is this controller method missing?", <<-MARKDOWN + Who knows what we're missing? + + Take a look at the `create` method on the jobs controller and compare what we're doing in each. + + See if you can figure it out as a class. + + (Spoilers below, so don't keep scrolling!) +MARKDOWN + +message <<-MARKDOWN + * here + * is + * some + * strategic + * white + * space + * so + * the + * answer + * isn't + * immediately + * visible! +MARKDOWN + +source_code_with_message "Here's what the update method should actually look like:", :ruby, +<<-RUBY + def update + @job = Job.find(params[:id]) + @job.update(job_params) + redirect_to jobs_path + end +RUBY + +message <<-MARKDOWN + We needed to save our changes to the database so they can actually persist! If you didn't have the discussion before and work out the answer, go through this method line-by-line explaining precisely what the code is doing. + + ### Add a Link + + Our users probably aren't going to know they can hit `/jobs/:id/edit` to visit the edit form, so let's add a link so it's easy to find! + + In `app/views/jobs/index.html.erb`, just add this line with the `
    ` header in it ... don't copy and paste the whole thing! + +MARKDOWN + +source_code :erb, +<<-ERB + <% @jobs.each do |job| %> +

    <%= job.title %>

    +

    <%= job.description %>

    +
    <%= link_to "Edit Posting", edit_job_path(job)%>
    + <% end %> +ERB + +next_step "delete_job_listings" diff --git a/sites/learn-to-code/argv.md b/sites/learn-to-code/argv.md new file mode 100644 index 000000000..5d0411103 --- /dev/null +++ b/sites/learn-to-code/argv.md @@ -0,0 +1,54 @@ + + +# ARGV + +There is a magic array named `ARGV`. + +It contains the *command line arguments* to the program. + +If the user types: + + ruby hello.rb Alice Bob + +then ARGV contains: + + ["Alice", "Bob"] + +# Why ARGV? + +ARGV is a historical name. It means "Argument Vector" and has been around since the early 1970s. + +# Command-Line Hello + +Change `hello.rb` to contain: + + puts "Hello, " + ARGV[0] + +and run it a few times, e.g. + + ruby hello.rb Alice + +# LAB: Hello, Everyone! + +Change `hello.rb` to say hello to *every one* of its command line arguments. + +For instance: + + ruby hello.rb Alice Bob Charlie + Hello, Alice! + Hello, Bob! + Hello, Charlie! + +# LAB: Add + +Write a program named `add.rb` that adds all of its command line arguments together. + +e.g. + + ruby add.rb 1 2 3 + 6 + +Do you remember how to convert a string to an integer? + + + diff --git a/sites/learn-to-code/arrays.md b/sites/learn-to-code/arrays.md new file mode 100644 index 000000000..5dbc0eefc --- /dev/null +++ b/sites/learn-to-code/arrays.md @@ -0,0 +1,126 @@ + + +# Arrays + +* An ARRAY is a CONTAINER + * an object that contains other objects +* It's a list of objects + +# What makes an array an array + +* You can put any objects inside it +* In any order +* They stay in order +* Duplicates are fine + +# Creating an array + + ["apple", "banana", "cherry"] + +# Array Indexes + +* Every slot in the array has a serial number +* You can retrieve any item in an array by its INDEX +* An index is a number from 0 to infinity + * actually to the size of the array + +# Array Indexes Exercise + +Try this in IRB: + + fruits = ["apple", "banana", "cherry"] + fruits[1] + +Did you get the result you expected? + +Why or why not? + +# Start At Zero + +When counting, + +humans start at one, + +but **computers start at zero**. + +So the first item in an array is number zero, not number one. + +# The End + +Try this: + + fruits[99] + +Did you get the result you expected? + +Why or why not? + +# Array Methods + + fruits.last + fruits.first + fruits.reverse + fruits.shuffle + +# Turning an array into a string + + fruits.join + fruits.join(" and ") + +Note that `to_s` doesn't work right on arrays: + + fruits.to_s + puts fruits.to_s + +# Looping through an array + + fruits.each do |fruit| + puts fruit + end + +* `each` is like `while` for arrays +* `fruits.each do` means "for each item inside this array, do this" +* `|fruit|` means "put the current item into a variable named `fruit`" +* `puts fruit` means "print out the value of this variable" +* `end` means "we're done with the loop now" :-) + +# Lab: reverse fruit + +Given this array: + + fruits = ["apple", "banana", "cherry"] + +write a program that prints: + + yrrehc + ananab + elppa + +# Setting items in an array + +The `[]` operator works for assignment as well. + + fruits[0] = "Apricot" + fruits[1] = "Blueberry" + + puts fruits.first + +# Checking an array + +The `include?` method checks if an object is inside an array or not. + + fruits.include? "apple" + true + + fruits.include? "pizza" + false + +# LAB: enemies list refactoring + +I'd like you to **refactor** your old `hello.rb` program to use the `include?` method to check if someone is your enemy. + +# TODO: more array labs + + + + diff --git a/sites/learn-to-code/computers.md b/sites/learn-to-code/computers.md new file mode 100644 index 000000000..b8f90cb4a --- /dev/null +++ b/sites/learn-to-code/computers.md @@ -0,0 +1,106 @@ + + +# What is a computer? + +* A smart box + * desktop + * laptop + * cell phone + * smart phone + +# The Universal Machine + +* A computer can do anything + * (at least on the inside) +* This can be intimidating! +* But the basic rules are simple + +# The Terminal + +* the *TERMINAL* is a window into which you can talk directly to your computer +![Shall we play a game?](img/wargames-terminal.jpg) + +# In The Beginning Was The Command Line + +* the *TERMINAL* is a window into which you can talk directly to your computer +* aka *console* or *command line* or *command prompt* or *shell* or *prompt* + * contrast *CLI* (Command Line Interface) to *GUI* (Graphical User Interface) + +* ["In The Beginning Was The Command Line"](http://www.cryptonomicon.com/beginning.html) is an essay by Neil Stephenson describing the history of computers in an enjoyable and clever way + +# irb - the Interactive Ruby Browser + +## Exercise: Calculator + +* open a terminal +* type `irb` +* press the `return` key (also called `enter`) +* see the `>` prompt +* type `1 + 1` +* press the `return` key again +* see the `2` +* yay, a $1000 calculator! + +* Bonus: what other math can you do? + +* From now on, whenever you see text in the `code font`, try typing it into irb and see what happens! + +# Computer Anatomy: Hardware + +* CPU ("the brain") +* Memory +* Input/Output + * keyboard, mouse, touch screen, monitor + +# Computer Anatomy: Software + +* Operating System +* Libraries +* Applications +* Languages + +Every piece of software on your computer is a PROGRAM. + +# A Program Is Like A Recipe + +* a recipe is a collection of *ingredients* and *instructions* + +![Grandma's Cookie Recipe](img/cookie-recipe.gif) + +* a program is a collection of *data* and *code* + +When you are writing code, you are not baking cookies, you are writing a recipe for how to make cookies. + +(recipe from http://www.popcornpottery.com/rec.html) + +# Languages + +* every program is written in a LANGUAGE + * like Java or Python or C or Fortran + * even HTML and CSS and SQL are languages + * every computer language has a silly name +* different languages are useful in different areas, but there is a lot of overlap +* today we will learn the RUBY programming language + +# Ruby + +* Ruby was invented in the 1990s by a very nice Japanese man named Yukihiro Matsumoto (nicknamed Matz) +* The motto of Ruby is MINASWAN: + +> Matz is nice, and so we are nice. + +# Ruby is fun + +* Ruby was [designed to be fun](http://blog.crowdint.com/2013/06/11/matz-keynote-at-ruby-kaigi-2013.html) to write code in, while also being powerful and well-rounded. +* *Ruby is poetry* + * especially compared to other languages + +# Errors Are Awesome + +* Don't be afraid of errors +* Your computer is trying to help you fix your program + * It's just *really* bad at communicating +* It's not all gibberish +* Try to read it -- really try! -- and pull out the pearls from the pig slop + + diff --git a/sites/learn-to-code/extra.md b/sites/learn-to-code/extra.md new file mode 100644 index 000000000..0b38313a0 --- /dev/null +++ b/sites/learn-to-code/extra.md @@ -0,0 +1,17 @@ + + +# Extra Fun Ruby Stuff + +We've gone over the basics, but Ruby has lots of other language features. Here's a quick overview, with links to more info about them. + +[Iterators](http://codelikethis.com/lessons/ruby_blocks/iterators) are special loops that act on all the items in a collection. + +[Blocks](http://codelikethis.com/lessons/ruby_blocks) are like a cross between functions and methods. They allow Ruby to express many powerful algorithms in a compact writing style. + +[Symbols](http://codelikethis.com/lessons/ruby_basics/symbols) are like strings that start with a colon, and they're used all over the place. + +[Chaining](http://codelikethis.com/lessons/ruby_basics/chaining) is a compact way to express a chain of events. Properly executed, chaining turns Ruby from prose into poetry. + +[Methods](http://codelikethis.com/lessons/ruby_objects/objects#behavior) and [Classes](http://codelikethis.com/lessons/ruby_objects/classes) are the heart of [Object-Oriented Programming](https://en.wikipedia.org/wiki/Object-oriented_programming). They + +## ...and [lots more](http://codelikethis.com/lessons)... diff --git a/sites/learn-to-code/functions.md b/sites/learn-to-code/functions.md new file mode 100644 index 000000000..f6f5ff65d --- /dev/null +++ b/sites/learn-to-code/functions.md @@ -0,0 +1,58 @@ + + +# Functions + +* just like a VARIABLE is a name for a chunk of data +* a FUNCTION is a name for a chunk of code +* if you have some code you want to run again and again + * or just run once, but keep it organized + +# For example + +Here's a silly function: + + def add x, y + x + y + end + +* `def` means "define a function" +* `add` is the *name* of the function +* `x, y` are the *parameters* of the function +* `x + y` is the *body* of the function + * also the *return value* + +Lab: write a `multiply` method and use it to multiply 123 * 456 + +# Rant!!! + + def rant s + s.upcase.gsub(" ", "") + "!!!" + end + + puts rant "i like pizza" + +Lab: use "rant" to rant about something really important!!! + +# Capitalize Just The First Character + + def initial_cap s + s[0].upcase + s[1,s.length] + end + + puts initial_cap("smith") + puts initial_cap("deniro") + +Lab: capitalize a few things + +# Titleize + + def titleize string + string.split(' ').map(&:capitalize).join(' ') + end + +* The funny `&:` means "send this message" +* `map(&:capitalize)` means "send the message `capitalize` to every item in the array" + +# LAB: titleize your favorite movies + + diff --git a/sites/learn-to-code/hashes.md b/sites/learn-to-code/hashes.md new file mode 100644 index 000000000..14c753f44 --- /dev/null +++ b/sites/learn-to-code/hashes.md @@ -0,0 +1,14 @@ + + +# Hashes + +* `Hash` is a built-in type + * aka Map, Dictionary, Associative Array +* Like an array where the index can be *anything*, not just a number + +# TODO: + +* examples +* labs + + diff --git a/sites/learn-to-code/img/cookie-recipe.gif b/sites/learn-to-code/img/cookie-recipe.gif new file mode 100644 index 000000000..1a593848c Binary files /dev/null and b/sites/learn-to-code/img/cookie-recipe.gif differ diff --git a/sites/learn-to-code/img/dot.jpg b/sites/learn-to-code/img/dot.jpg new file mode 100644 index 000000000..e756c29fb Binary files /dev/null and b/sites/learn-to-code/img/dot.jpg differ diff --git a/sites/learn-to-code/img/fruit-banana-snack-banana.svg b/sites/learn-to-code/img/fruit-banana-snack-banana.svg new file mode 100644 index 000000000..061d03e1f --- /dev/null +++ b/sites/learn-to-code/img/fruit-banana-snack-banana.svg @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + "banana" + + + + fruit + + + + "BANANA" + + + + snack + + diff --git a/sites/learn-to-code/img/one-infinite-loop.jpg b/sites/learn-to-code/img/one-infinite-loop.jpg new file mode 100644 index 000000000..e635a0614 Binary files /dev/null and b/sites/learn-to-code/img/one-infinite-loop.jpg differ diff --git a/sites/learn-to-code/img/snack-apple.svg b/sites/learn-to-code/img/snack-apple.svg new file mode 100644 index 000000000..b64c4eaf9 --- /dev/null +++ b/sites/learn-to-code/img/snack-apple.svg @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + "Apple" + + + + snack + snack + + + diff --git a/sites/learn-to-code/img/snack-fruit.svg b/sites/learn-to-code/img/snack-fruit.svg new file mode 100644 index 000000000..788f7d147 --- /dev/null +++ b/sites/learn-to-code/img/snack-fruit.svg @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + "Apple" + + + + snack + snack + + + fruit + + + + diff --git a/sites/learn-to-code/img/spoon.jpg b/sites/learn-to-code/img/spoon.jpg new file mode 100644 index 000000000..a75005414 Binary files /dev/null and b/sites/learn-to-code/img/spoon.jpg differ diff --git a/sites/learn-to-code/img/truthiness.png b/sites/learn-to-code/img/truthiness.png new file mode 100644 index 000000000..ffabdecae Binary files /dev/null and b/sites/learn-to-code/img/truthiness.png differ diff --git a/sites/learn-to-code/img/warehouse.jpg b/sites/learn-to-code/img/warehouse.jpg new file mode 100644 index 000000000..b11e1f5f3 Binary files /dev/null and b/sites/learn-to-code/img/warehouse.jpg differ diff --git a/sites/learn-to-code/img/wargames-terminal.jpg b/sites/learn-to-code/img/wargames-terminal.jpg new file mode 100644 index 000000000..f82f86510 Binary files /dev/null and b/sites/learn-to-code/img/wargames-terminal.jpg differ diff --git a/sites/learn-to-code/input_and_output.md b/sites/learn-to-code/input_and_output.md new file mode 100644 index 000000000..27e8c85bd --- /dev/null +++ b/sites/learn-to-code/input_and_output.md @@ -0,0 +1,114 @@ + + +# Input and Output + +* Computers have many senses -- keyboard, mouse, network card, digital camera, etc. Collectively, these are called INPUT. + +* Computers can also express themselves in many ways -- text, graphics, sound, network, printers, etc. Collectively, these are called OUTPUT. + +* Input and Output together are called **I/O**. + +# Terminal I/O + +* In Ruby, + * `puts` means "print a line to the terminal" + * `gets` means "read a line from the terminal" + +* `gets` reads all the characters from the keyboard and puts them into a new string, until you press RETURN + +# LAB: Hello, friend! + +1. Open `hello.rb` in your text editor +2. Change it to contain the following code: + + puts "What is your name?" + name = gets + puts "Hello, " + name + "!" + +3. Save the file and switch back to the terminal +4. Run the program using `ruby hello.rb` +5. Type in your name and press the RETURN (or ENTER) key + +What happens? Is this what you expected? + +# Yikes! + +* Uh-oh! We've got trouble... what is that exclamation point doing way down there? + +* The first thing to do is DON'T PANIC! +* You are *totally* going to figure this out. +* And even if you don't, you haven't actually broken anything. +* In fact, it's really hard to break a computer, so just stay calm. + +# Breathe + +* In through the nose... +* Out through the mouth... +* In through the nose... +* Ahhhhhhhh. + +# Let's fix this + +* Have you figured out what the problem is? +* If not, I'll tell you on the next slide. +* Take a second and try to figure it out first. I'll wait. + +# The newline character + +* Here's a fun fact: +* In addition to letters, numbers, and punctuation, computers also store other keys inside strings +* Among these CONTROL CHARACTERS is the one that represents the RETURN KEY +* This character's name is NEWLINE +* Every time you use `gets`, Ruby reads *all* the characters, *including the newline*! + +# Strip it + +* Fortunately, there's an easy fix +* If you send the message `strip` to a string, it will remove all SPACES and NEWLINES from both ends + +# LAB: fixing Hello, Friend + +* Change the program to look like this: + + puts "What is your name?" + name = gets.strip + puts "Hello, " + name + "!" + +* Run it and make sure it works OK + +# LAB: Capitalization + +* What happens if you type your name in all lowercase? +* Make the program capitalize your name for you even if you forget. + +# LAB: Crazy Name + +* Now go crazy and make it do all sorts of silly things to your name! + +# LAB: Full Name + +* Write a program named `name.rb` that asks two things: + 1. Your first name + 2. Your last name +* Then it says hello to the user by her *full name*. +* Run the program by typing `ruby name.rb` on the command line. + +# CONGRATULATIONS! + +> You just wrote a program! + +You are now officially a coder. HIGH FIVE! + +# Lab: Name Length + +* Change `name.rb` so it also prints the number of characters in the user's name. +* For instance: + + What is your first name? + Alex + What is your last name? + Chaffee + Hello, Alex Chaffee! + Your name is 11 characters long. + + diff --git a/sites/learn-to-code/learn-to-code.md b/sites/learn-to-code/learn-to-code.md new file mode 100644 index 000000000..fc5c97251 --- /dev/null +++ b/sites/learn-to-code/learn-to-code.md @@ -0,0 +1,78 @@ + + +# Learn To Code + +You may have never programmed before. Now you will. + +# What is coding? + +* coding = programming + +* ...thoughts? + +# What is coding NOT? + +* coding is not mathematical + * some logic + * mostly just long todo lists +* coding is not lonely + * most coding happens in a team + * pair programming is awesome + + + +# What is coding? + +* coding is fun! +* coding is frustrating! +* coding is creative! +* coding is communication + * between you and a computer + * between you and other coders + +# What will we learn today? + +In this class, you will utilize Ruby to learn: + +* The command line and why we use it +* Learn about Strings, Arrays, Variables, Objects, Loops, Files +* Object concepts like Methods + +* How to run your Ruby code interactively (irb) or from a file +* Make a very simple website run on your own computer + +Follow along at http://docs.railsbridge.org/learn-to-code + +# Go at your own pace + +* we will **definitely not** get through all the materials today + +# What if I know some of that already? + +* Pair up and fill in each other's gaps +* Promote yourself to TA + +## And if you and your pair finish early... + +* Let us know! +* Start working on Ruby For Programmers lessons + * http://testfirst.org + * http://codelikethis.com/lessons +* Build a Rails website + * http://docs.railsbridge.org/ + +# Technical requirements + +* WIFI + * Ask for SSID and password +* a live Ruby installation + * e.g. http://railsinstaller.org -- click on the *BIG GREEN BUTTON* + * Ruby version 2.3 preferred, but 1.9+ is fine too + * run `ruby -v` to check +* a text editor + * we recommend [Atom](https://atom.io/), but you can try a [different editor](/installfest/editors) + +* If you do not have both of these things, RAISE YOUR HAND + * visit http://installfest.railsbridge.org for more instructions + + diff --git a/sites/learn-to-code/logic.md b/sites/learn-to-code/logic.md new file mode 100644 index 000000000..16769744e --- /dev/null +++ b/sites/learn-to-code/logic.md @@ -0,0 +1,111 @@ + + +# Truthiness + +Computers have a very strict idea of when things are *true* and *false*. + +![Truthiness](img/truthiness.png) + +(Unlike Stephen Colbert...) + +# True or False? + +Try the following in irb: + +* `1 < 2` +* `2 + 2 < 4` +* `2 + 2 <= 4` +* `2.even?` +* `4.odd?` +* `"apple".empty?` +* `"".empty?` + +# Conditions + +The magic word `if` is called a CONDITIONAL. + + if age < 18 then + puts "Sorry, adults only." + end + +# One-Line Condition + +Ruby has a compact way of putting an entire `if` expression on one line: + + puts "Sorry, adults only." if age < 18 + +Note that: + +* the action comes *first* in a one-line condition +* this sounds kind of natural + * "Go to bed if you're sleepy." + +# if... then... else... end + +The magic word `else` allows BRANCHING. + + if age >= 18 then + puts "allowed" + else + puts "denied" + end + +Like a fork in the road, the program chooses one path or the other. + +(In Ruby, `then` is optional, so we usually leave it off, but if it makes your code clearer, go ahead and use it.) + +# 2 + 2 = 4 + +Sadly, this expression: + + 2 + 2 = 4 + +causes a `SyntaxError`. You need to do + + 2 + 2 == 4 + +instead. Why? + +# The Tragedy of the Equal Sign + +* a single equal sign means ASSIGNMENT + * `name = "Alice"` -- "assign the variable 'name' to the value 'Alice'" +* two equal signs means COMPARISON + * `name == "Alice"` -- "does the variable 'name' contain the string 'Alice'?" + +> This is confusing, and you should feel confused. + +* (it's all FORTRAN's fault) + +# LAB: Good Friend, Bad Friend + +* Your `hello.rb` program should currently look something like this: + + puts "What is your name?" + name = gets.strip + puts "Hello, " + name + "!" + +* Now change `hello.rb` so that it doesn't always say hello! + * If the user's name is "Darth" then say "Go away!" + +# Conjunction Junction + +* You can make more complicated logical expressions using conjunctions like `and`, `or`, `not`: + * `X and Y` means "are both X and Y true?" + * `X or Y` means "is either X or Y (or both) true?" + * `not X` means "is X false?" (think about it) + +* For example: + + if age >= 18 or parent.gave_permission? then + puts "allowed" + else + puts "denied" + end + +# LAB: Enemies List + +* Change `hello.rb` so that it says "Go away!" if the user's name is any one of a number of evil names +* For instance, Voldemort, Satan, Lex Luthor... + + diff --git a/sites/learn-to-code/loops.md b/sites/learn-to-code/loops.md new file mode 100644 index 000000000..194c8b707 --- /dev/null +++ b/sites/learn-to-code/loops.md @@ -0,0 +1,232 @@ + + +# Loops + +Computers are like robots. They are good at doing things over and over and over and over again. + +A LOOP is when we ask a program to do something many times. + +# loop + +If you want something to keep going forever, use a special loop called `loop`. + + loop do + puts "Hello" + end + +To stop it, hold down the CONTROL key and press the C key. + +**Note well!** The lines between `do` and `end` are INDENTED. Indentation is very important to you and other humans. It lets our eyes follow the patterns and helps us quickly see what parts of the program go with each other. + +# One Infinite Loop + +![One Infinite Loop](img/one-infinite-loop.jpg) + +*Fun Fact:* The address of Apple HQ is + + 1 Infinite Loop + Cupertino, CA 95014 + +*Image from the Wikimedia Commons, licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license. Attribution: Joe Ravi* + +# LAB: Infinite Hello + +Let's change `hello.rb` so that it keeps saying hello over and over again. + + loop do + puts "What is your name?" + name = gets.strip + puts "Hello, " + name + "!" + end + +# LAB: Infinite Food + +Write a program called `food.rb` that + +1. asks the user for a food -- say, "pizza" +2. prints "Yum, I love pizza!" +3. asks again, and prints again, forever and ever + +Remember, CONTROL-C means "Stop everything!!!" + +# Who wants to loop forever? + +Next, we will change your `food.rb` program so that if the user types "return" -- meaning the string is empty -- then the program exits. + +Please try this yourself! But it's kind of tricky, so on the next slide I'll show you one solution. + +# Exiting a Loop with break + +The magic word `break` stops a loop immediately. + + loop do + puts "What is your name?" + name = gets.strip + break if name.empty? + puts "Hello, " + name + "!" + end + +The magic word `break` means "stop the current loop". It's less dangerous -- but still kind of weird, since it's not always clear which loop you mean. + +# LAB: Exiting a Loop + +Change your `food.rb` program so that if the user types "return" -- meaning the string is empty -- then the program exits. + +# LAB: Good Food, Bad Food + +* Change `food.rb` so that it doesn't love every food. +* If it's a food you like (let's say, pizza), make it print "Yum! I love pizza." +* If it's a food you like (let's say, cabbage), make it print "Yuck! I hate cabbage." + +# times + +Another loop in Ruby is called `times`, and it's a message you can send to a number. + +Try this in IRB: + + 3.times do + puts "Hip! Hip! Hooray!" + end + +Let's unpack this: + +* `3.times do` means what it sounds like: "do this three times" +* `end` means this is the end of what I want you to do +* and of course, `puts` means "show this on the terminal" + +# Counting in a loop + +Remember this poem? + + 1 potato + 2 potato + 3 potato + 4 + 5 potato + 6 potato + 7 potato + More + +We're going to examine a few different ways to code this. + +# `times` with a counter + +Try this: + + 4.times do |count| + puts count.to_s + " potato" + end + +`|count|` means + +> "the `count` variable points to the current value of the counter" + +# `times` output + + 0 potato + 1 potato + 2 potato + 3 potato + 4 + +Whoops! What's wrong? + +# `times` with a counter -- fixed + + 4.times do |count| + puts (count+1).to_s + " potato" + end + +# `loop` with a counter + + count = 1 + loop do + puts count.to_s + " potato" + count = count + 1 + end + +Whoops! Hit Control-C and join me on the next slide... + +# `loop` with a counter -- fixed + + count = 1 + loop do + puts count.to_s + " potato" + count = count + 1 + break if count > 4 + end + +# `while` + +The magic word `while` combines `loop` and `break`. + + count = 1 + while count <= 4 + puts count.to_s + " potato" + count = count + 1 + end + +* The `while` statement keeps checking the expression + + * if it's `true` then it loops back + * if it's `false` then it stops looping and goes on to the rest of the program + +This is fairly complicated, so let's stop here and make sure to understand everything that's happening in this little program. + +# `while` breakdown (pt.1) + + count = 1 + +creates a *variable* named `count` and sets its value to `1`. + + while count <= 4 + +starts a loop and immediately compares `count` to `4`. + +`1` is less than `4`, so the expression is `true`, so we continue with the loop. + +# `while` breakdown (pt.2) + + puts count.to_s + " potato" + +prints the current value of count (and the word "potato"). + + count = count + 1 + +*increments* the `count` variable... it was `1`, so now it's `2` + + end + +goes *back to the `while` line* and checks again + +# `while` breakdown (pt.2) + + while count <= 4 + +compares `count` to `4`. + +`2` is less than `4`, so the expression is `true`, so we continue with the loop. + +Eventually, `count` becomes `5`, and the `while` expression is `false`, and so we stop looping and go on. + +# LAB: One Potato + +Write a program called `potato.rb` that prints the entire potato poem, accurately. + +# Lab: Adder + +Write a program named `adder.rb` that keeps a *running total*. + +For example: + + ruby adder.rb + 1 + Total: 1 + 2 + Total: 3 + 4 + Total: 7 + -5 + Total: 2 + + diff --git a/sites/learn-to-code/methods.md b/sites/learn-to-code/methods.md new file mode 100644 index 000000000..0034323bf --- /dev/null +++ b/sites/learn-to-code/methods.md @@ -0,0 +1,7 @@ +# Methods vs. Functions + +* a FUNCTION is a named chunk of code with PARAMETERS and a RETURN VALUE +* a METHOD is a function that is *attached* to a specific object + * it has privileged access to that object's data +* in Ruby everything's an object, so the terms are mostly interchangeable + diff --git a/sites/learn-to-code/next_steps.md b/sites/learn-to-code/next_steps.md new file mode 100644 index 000000000..211bf927f --- /dev/null +++ b/sites/learn-to-code/next_steps.md @@ -0,0 +1,21 @@ +# Next Steps + +* [Try Ruby](http://tryruby.org/) at +* Sign up for Code School at + * Code School's "Ruby Bits" class +* Alex's online courses at [Code Like This](http://codelikethis.com) and [TestFirst.org](http://testfirst.org) +* Chris Pine's book "[Learn to Program](http://www.amazon.com/gp/product/1934356360/ref=as_li_ss_il?ie=UTF8&camp=1789&creative=390957&creativeASIN=1934356360&linkCode=as2&tag=alexchaffeeco-20)" book +* Attend or host a [RailsBridge Workshop](http://railsbridge.org) + +* _why's guide to Ruby (http://poignant.guide/) + +* +* + +# Thanks + +* to all the TAs! +* to all the students! +* to [Alex Chaffee](http://alexchaffee.com/), the original author of this curriculum, and +* to all the [RailsBridge volunteers](https://github.com/railsbridge/docs/commits/master/sites/en/learn-to-code) who have helped make it better! + diff --git a/sites/learn-to-code/nil.md b/sites/learn-to-code/nil.md new file mode 100644 index 000000000..d256fa794 --- /dev/null +++ b/sites/learn-to-code/nil.md @@ -0,0 +1,40 @@ + + +# Nil + +*nil* is a magic object + +# There Is No Spoon + +![](img/spoon.jpg) + +*nil* is the object that means "there is no object" + +# Experiment + + fruit = "apple" + fruit = nil + fruit.reverse + +*Read the error!* + +# Errors are good + +They tell you + +* you made a mistake +* what that mistake was +* (sometimes) how to fix it + +Interpret this error: + + fruit.reverse + NoMethodError: undefined method `reverse' for nil:NilClass + +# Fail Fast, Fail Often + +* Ruby has a "fail fast" philosophy +* Is this a good idea? +* Why or why not? + + diff --git a/sites/learn-to-code/numbers.md b/sites/learn-to-code/numbers.md new file mode 100644 index 000000000..9018485af --- /dev/null +++ b/sites/learn-to-code/numbers.md @@ -0,0 +1,156 @@ + + +# Numbers + +The following operations work on numbers: + + * + -- addition + * - -- subtraction + * * -- multiplication + * / -- division + * % -- modulus + * ** -- exponentiation + +# LAB: Playing With Numbers + +Answer the following questions using irb: + +* How many seconds are in an hour? +* How many minutes are in a week? +* How many seconds old are you? +* How many years old is someone who is 1 billion seconds old? + +# Order of operations + +Q: What is 1 plus 2 times 3? + +# Order of operations + +Q: What is 1 plus 2 times 3? + +A: *It depends!* + + * `(1 + 2) * 3` is 9 + * `1 + (2 * 3)` is 7 + +# Parentheses Are Free + +When in doubt, use parentheses! + +# Strings vs. Numbers + +Hmmm.... + + 1 + 2 + "1" + "2" + "1 + 2" + +# Strings plus Numbers + +Hmmm again... + + "1" + 2 + +Uh-oh! + + TypeError: can't convert Fixnum into String + +The problem is that Strings and Numbers are different TYPES, aka different CLASSES. + +Don't panic! The solution is easy. + +# Type Conversion + +Numbers know a message that converts them into strings. `to_s` means "to string". + + "1" + 2.to_s + +Likewise, strings know a message that converts them into numbers. + + 1 + "2".to_i + +`to_i` means "to integer". + +Try this in irb! + +# Advanced Number Theory (optional) + +# WTFixnum? + +The error said `can't convert Fixnum into String`. + +Q: What is a Fixnum? + +A: It's one type of number. + +# Math is hard + +There are many types of numbers! + +Each is useful in different situations. + +Without getting into too much detail, the two main number types in Ruby are: + +* `Fixnum` - for *integers* like 12 or -1023 +* `Float` - for *decimals* like 3.14 + +(Other number types include Complex, Rational, and Bignum.) + +# Number to Number + +You can convert from one type of number to another by sending a message: + +* `to_i` turns a Float into a Fixnum +* `to_f` turns a Fixnum into a Float + +Try this: + + 3.to_f + 3.14.to_i + +# String to Number + +`to_f` and `to_i` also work on Strings: + + "3.14".to_f + "3.14".to_i + +and `to_s` works on numbers: + + 3.14.to_s + +# Arithmetic + +Try this in irb: + + 1 + 2 + 3 - 4 + 5 * 6 + 7 / 8 + +Whoa! What just happened? + +# Integer Arithmetic + +7 and 8 are *Integers* + +so the result is an Integer + +7/8 is somewhere between 0 and 1 + +but there is no integer between 0 and 1 + +so the computer has to *round down* to 0 + +# Floating Point Arithmetic + + 7.0/8.0 + +7.0 and 8.0 are *Floats* + +so the result is a Float + +and `0.875` can fit in a float + +# Okay, that's enough math! + diff --git a/sites/learn-to-code/objects.md b/sites/learn-to-code/objects.md new file mode 100644 index 000000000..2075079cf --- /dev/null +++ b/sites/learn-to-code/objects.md @@ -0,0 +1,81 @@ + + +# Objects + +An OBJECT is a location in computer memory where you can store DATA (aka VALUES). + +There are many kinds of objects, including String, Number, Array, Hash, Time, ... + +(The different kinds of objects are called CLASSES or TYPES. Some day soon you will create your own classes but for now, we will use the built-in ones.) + +# Numbers + +A NUMBER is what it sounds like. + + 10 + -12 + 3.14 + +# Strings + +A STRING is an object that's a collection of characters, like a word or a sentence. + + "apple" + "banana" + "Cherry Pie" + +# Messages and Operators + +An object responds to MESSAGES. You send it messages using OPERATORS. + +The most powerful operator is DOT. + +On screen she looks like this... + + . + +# Dot up close + +...but here's what she looks like up close: + +![picture of Dot the Operator](img/dot.jpg) + +# Dot's job + +Dot can send any message she likes, by name, to any object. + + "apple".upcase + +The `upcase` message turns `"apple"` into `"APPLE"`. + +# Other Operators + +There are other operators, like PLUS (`+`) and TIMES (`*`), but they only send one message each. + +And remember, Dot is more powerful than any other operator! + + 2 + 7 + +is the same as + + 2.+ 7 + +Both send the message `+` to the object `2`. + +# Return Values + +Every time an object receives a message, it returns a response. + +The response is also called the VALUE or the RETURN VALUE. + +You can think of it as the answer to a question. + + 2 + 2 # Question: What is 2 + 2? + 4 # Answer: 4 + + "apple".upcase + # Q: What is the upcase of the string "apple"? + + "APPLE" + # A: the string "APPLE" + diff --git a/sites/learn-to-code/sinatra.md b/sites/learn-to-code/sinatra.md new file mode 100644 index 000000000..d7c672bdb --- /dev/null +++ b/sites/learn-to-code/sinatra.md @@ -0,0 +1,67 @@ + + +# Sinatra + +Sinatra is a Web Application Framework. It includes a Web Server and lets you write code to show when people request web pages. + +# Hi, Sinatra + +1. install Sinatra by running `gem install sinatra` on the command line + +2. create a file called `hi.rb` containing this: + + require 'sinatra' + + get '/hi' do + "Hi!" + end + +3. run `ruby hi.rb` + +Now open a Web Browser (like Firefox or Chrome or Safari or Internet Explorer) and enter the following URL into the address bar: + + http://localhost:4567/hi + +# Congratulations + +You just wrote a web server. + +No, really. + +# Hello, Whoever + +Change `hi.rb` to look like this: + + require 'sinatra' + + get '/hi/:who' do + "Hi " + params[:who] + "!" + end + +Now visit the following URL: + + http://localhost:4567/hi/alice + +# LAB: Yeller + +Make a route in your Sinatra application so that when someone requests this: + + /yell/ahoy + +they see this: + + AHOY!!! + +and when someone requests this: + + /yell/dinnertime + +they see this: + + DINNERTIME!!! + +# Detour: Deploying to Heroku + +* Railsbridge pages describing account setup & deploy steps + + diff --git a/sites/learn-to-code/strings.md b/sites/learn-to-code/strings.md new file mode 100644 index 000000000..3da28ac4e --- /dev/null +++ b/sites/learn-to-code/strings.md @@ -0,0 +1,45 @@ + + +# String Messages + +A string understands lots of messages. Here are a few: + + "banana".upcase + "Cherry".downcase + "titanic".capitalize + "elderberry".reverse + "fig".length + "Fig Newton".swapcase + "".empty? + "syzygy".length + +Try all of these out in irb! + +# String Operators + +A string knows DOT, but also understands several other operators: + + "blue" + "berry" + "yum" * 10 + "elderberry"[8] + +`+` `*` and `[]` are pronounced PLUS, TIMES, and SUB + +Try these out in irb! + +# Combining Messages and Operators + +You can combine messages and operators at will. + + "fig".upcase.reverse + "grape".reverse * 10 + "!!!" + +Definitely try these out in irb! It's pretty fun. + +# LAB: Playing With Strings + +* What is the reverse of "stressed"? +* How many characters long is your name? +* What does your name look like, repeated 1000 times? +* What is the tenth character of "Matz is nice"? (Trick question!) + diff --git a/sites/learn-to-code/the_command_line.md b/sites/learn-to-code/the_command_line.md new file mode 100644 index 000000000..6397782d9 --- /dev/null +++ b/sites/learn-to-code/the_command_line.md @@ -0,0 +1,106 @@ + + +# The Command Line + +* the TERMINAL is a window into which you can talk directly to your computer + * aka *console* or *command line* or *command prompt* +* very low level, based entirely on text and typing, not graphics and mousing +* when you type into the terminal, you are always issuing COMMANDS + * which is why it's called the Command Line + +# Opening the Terminal + +* to open your terminal: + * Mac OS: launch the "Terminal" application + * Windows with Railsinstaller: launch "Command Prompt with Ruby and Rails" +* **Important:** make your terminal *as tall as possible* + +# Directories + +* a DIRECTORY is a location on your hard disk + * also called a FOLDER +* directories can contain FILES +* directories can also contain other directories (called SUBDIRECTORIES) + +# The Current Directory + +* inside the Terminal, you are *always* inside a directory +* it is very important not to get lost! You must try to remember which directory you are in. +* If you forget, you can use a special command called `pwd` + +# Home Directory + +* when you open the Terminal you are in your HOME DIRECTORY +* usually you don't want to store files directly in here + +# Listing Directory Contents + +* when you type `ls` ("list") it shows the contents of the current directory + +# Making a directory + +* when you type `mkdir` ("make dir") it creates a new SUBDIRECTORY inside the current directory + + mkdir code + +# Changing directories + +* `cd` ("change dir") moves you into a different directory +* For example, `cd code` would move you into a directory named `code` +* If you ever get lost, type `cd` all on its own and press the return key. This will send you back to your home directory. + +# Basic Command Line Glossary + +* `pwd` ("print working dir") -- shows the name of the current directory +* `ls` ("list") -- shows the contents of the current directory +* `mkdir` ("make dir") -- creates a new SUBDIRECTORY inside the current directory +* `cd` ("change dir") -- move into a different directory +* `touch whatever.txt` -- creates an empty file named `whatever.txt` inside the current directory + +# LAB: make a subdirectory and then enter it + +1. open Terminal +2. make a new subdirectory using `mkdir code` +3. change into that directory using `cd code` +4. list its contents using `ls` (and note that it's empty) + +# Files + +* a file is a place on disk for storing stuff +* "stuff" here could be anything at all + * documents, pictures, sounds, applications... +* every file lives inside a directory + +# Text Editor + +* a text editor is a program that edits a text file +* a text editor is *like* a word processor +* but a text editor is **not** a word processor +* You probably have *Atom* + * or [another text editor](/installfest/editors) + * but **NOT** *TextEdit* or *Notepad* or *Microsoft Word* + +# Source File + +* source code is the essence of a program +* source files are text files that contain source code +* to RUN a program you type `ruby` and then the name of the source file + +* The Recipe Metaphor + * source file = recipe + * running = cooking + +# LAB: Hello, World + +1. Make sure you are in your `code` subdirectory using `pwd` +2. Create a file named `hello.rb` using `touch hello.rb` +3. Open `hello.rb` in your favorite text editor +4. Inside this file, put the following source code: + + puts "Hello, World!" + +5. Save the file +6. Go back to the terminal +7. Run this file using `ruby hello.rb` + +What happens? Is this what you expected? diff --git a/sites/learn-to-code/todo-learntocode.md b/sites/learn-to-code/todo-learntocode.md new file mode 100644 index 000000000..3a7bbb821 --- /dev/null +++ b/sites/learn-to-code/todo-learntocode.md @@ -0,0 +1,18 @@ +# todo: + +* diagram: memory (variables+objects) +* diagram: message passing + +* interpolation? +* more on arrays +* hashes +* more labs (for students who "get it" before the rest of the room and don't want to sit around bored (or help others)) +* functions +* more array labs +* methods +* classes +* File I/O +* testing +* symbols +* rand + diff --git a/sites/learn-to-code/variables.md b/sites/learn-to-code/variables.md new file mode 100644 index 000000000..63b40d6bf --- /dev/null +++ b/sites/learn-to-code/variables.md @@ -0,0 +1,114 @@ + + +# Variables + +A VARIABLE is a NAME for an object. You give an object a name using the ASSIGNMENT operator (it looks like an equal sign). + + color = "blue" + fruit = "berry" + +Anywhere you can use an object, you can use a variable instead. + + color + fruit + fruit.upcase + +# The Warehouse Metaphor + +![Warehouse from Raiders of the Lost Ark](img/warehouse.jpg) + +Think of memory as a giant warehouse. + +# The Warehouse Metaphor Explained + +If memory is a giant warehouse... + +...and *objects* are **boxes** in that warehouse + +...then a *value* is the **contents** of a box + +...and a *variable* is a **label** you stick on the outside of the box + +# Variables are documentation + +Which is clearer, this: + + 60 * 60 * 24 + +or this: + + seconds_per_minute = 60 + minutes_per_hour = 60 + hours_per_day = 24 + seconds_per_day = seconds_per_minute * minutes_per_hour * hours_per_day + +? + +# Lab: Play In IRB + +Let's spend a few minutes just playing around in IRB. Some things to try: + +* write a poem +* YELL THE POEM +* calculate 2 + 2 and more complicated things +* assign your best friend to a variable +* reverse your best friend's name +* get a new best friend and reverse her too + +# The Pointer Metaphor + + snack = "Apple" + +![snack-apple](img/snack-apple.svg) + +Think of a variable as **pointing** to an object. + +# Changing Variables + +You can assign and reassign variables at will. + + color = "blue" + fruit = "berry" + color + fruit + + color = "black" + color + fruit + +Changing a variable (using ASSIGNMENT) just changes the name of an object. It does *not* change the data inside the object. + +# Many pointers can point to the same thing + + fruit = "Apple" + snack = fruit + +![snack-fruit](img/snack-fruit.svg) + +After this both `snack` and `fruit`... + + * are *pointing* to the same *object* + * have the same *value* + +# Return values are new + +most messages return *new* values + + fruit = "banana" + snack = fruit.upcase + +![fruit-banana-snack-banana](img/fruit-banana-snack-banana.svg) + +`"banana"` and `"BANANA"` are two *different objects* in memory + +# Changing Values + +Most messages do not change the data inside the object. + + color.upcase + color + +But some messages do change the data! + + color.upcase! + color + +This can be dangerous so sometimes those messages end with a BANG (exclamation point). + diff --git a/sites/message-board/add_other_features_of_your_choosing.step b/sites/message-board/add_other_features_of_your_choosing.step new file mode 100644 index 000000000..836d3d56b --- /dev/null +++ b/sites/message-board/add_other_features_of_your_choosing.step @@ -0,0 +1,23 @@ +message <<-MARKDOWN +What other features do you want? + +Suggestions: + +* Profile pages for users (enter user’s name or details, have it display alongside posts). +* Post/Comment history for individual users (on their profile page?). +* Easy user profile pictures with [Gravatar](https://gravatar.com/). +* Add login options with [Omniauth](https://github.com/intridea/omniauth), including Twitter, Facebook, GitHub, Google, and more. + * Check out the Devise [documentation for integrating with Omniauth](https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview). +* Fiddle with the layout of the show page so it doesn't look bad. If it looks bad. It probably looks great. +* Perhaps fancier post markup with Markdown or something similar. +* Deploy to Heroku and send your message board to all your friends! + +## ...and that's that! Good luck! Make more things! Come back again! + +# Credits + +This curriculum was originally written by +[Lillie Chilen](http://www.twitter.com/lilliealbert) and +[Travis Grathwell](https://github.com/tjgrathwell), +and has been improved by all sorts of [lovely RailsBridge volunteers](https://github.com/railsbridge/docs/commits/master/sites/en/message-board). +MARKDOWN diff --git a/sites/message-board/add_pages_to_create_and_look_at_individual_posts.step b/sites/message-board/add_pages_to_create_and_look_at_individual_posts.step new file mode 100644 index 000000000..53ae6cdfe --- /dev/null +++ b/sites/message-board/add_pages_to_create_and_look_at_individual_posts.step @@ -0,0 +1,59 @@ +requirements do +message <<-MARKDOWN +* The user should be able to create a post with a title, author, date published, and content. The author should be the current user. +* The complete post should appear on its own page (aka its show page). +* If the user doesn't submit all required fields, they should see some error messaging, but shouldn't lose any of their work. +MARKDOWN + table do + tr do + td do + h2 'Create View', class: 'centered' + end + td do + h2 'Show View', class: 'centered' + end + end + tr do + td do + img class: 'noborder', src: 'img/create_post.png' + end + td do + img class: 'noborder', src: 'img/show_post.png' + end + end + end +end + +discussion do +message <<-MARKDOWN +* How do you create a resource? What parameters do you need to pass? +* Then: what did rails make for you when you used the resource generator? Find all the new files. Maybe even list them out on a whiteboard. +* How do you associate a user with a post? + * What did you have to put in the migration to create posts? + * How is this association communicated to the User and Post models? + * How will you associate the current user with a post when you create it? +* What's the difference between the `new` and `create` methods in the posts controller and how do these relate to the form to create a new post? +MARKDOWN +end + +tools_and_references do +message <<-MARKDOWN +* RailsGuides - Active Record Associations: . +* RailsGuides - Form Helpers, section 2.2: . +* RailsGuides - Routes - CRUD, Verbs, and Actions: . +* RailsGuides - Active Record Validations: . +* RailsGuides - Active Record Callbacks: . +MARKDOWN +end + +hints do +message <<-MARKDOWN +* Rails has some built in ways to associate one model with another. See the RailsGuides link above for hints on how to make a user the owner of a post! +* Don't hand code the form! You don't have to! Rails will help. See RailsGuide link above! +* Rails has a built-in way to note when something was stored in the database. Probably handy for showing the date / time a post was created. +* For now, we're going to use the user's email address when displaying a post's author. You can add names or other identifiers later! (Also, even though you're going to get the current user's email address from the User model, you'll still need a user parameter for your Post resource.) +* You need a `create` method to store your post data - scroll down a little bit in this section of the Getting Started guide for very helpful information and to see an example of a create method: . +MARKDOWN +end + +next_step "make_a_posts_index_page" diff --git a/sites/message-board/add_replying.step b/sites/message-board/add_replying.step new file mode 100644 index 000000000..0df64b867 --- /dev/null +++ b/sites/message-board/add_replying.step @@ -0,0 +1,42 @@ +requirements do +message <<-MARKDOWN +* The user should see a 'New Reply' link or button on a post's show page which takes them to a form. The form should include the title of the post being replied to. +* The user should be able to create a new reply to a post using the 'New Reply' form. +* The user should see all the replies to a post on the post’s show page. +* If the user doesn't submit all required fields, they should see some error messaging, but they shouldn't lose any of their work. +MARKDOWN + table do + tr do + td do + h2 'Create View', class: 'centered' + end + td do + h2 'Show View', class: 'centered' + end + end + tr do + td do + img class: 'noborder', src: 'img/create_reply.png' + end + td do + img class: 'noborder', src: 'img/show_replies.png' + end + end + end +end + +discussion do +message <<-MARKDOWN +* What is a nested resource? When is it appropriate and how does it help? +* How do the `RepliesController` and `PostsController` interact? +* What should happen to the routes file for this nesting business to work? +MARKDOWN +end + +tools_and_references do +message <<-MARKDOWN +* RailsGuides - Rails Routing from the Outside In http://guides.rubyonrails.org/routing.html +MARKDOWN +end + +next_step "inline_replying_on_a_post" diff --git a/sites/message-board/commands.md b/sites/message-board/commands.md new file mode 100644 index 000000000..b61bd3edd --- /dev/null +++ b/sites/message-board/commands.md @@ -0,0 +1,79 @@ +## Ruby + +Open an interactive Ruby terminal (type 'exit' to quit) + + irb + +Run a ruby program named FILENAME.rb + + ruby FILENAME.rb + +Installs a gem called GEMNAME + + gem install GEMNAME + +Installs gems listed in the `Gemfile` + + bundle install + +## Rails + +Create a new rails project called `NAME` + + rails new NAME + +Auto-generate routes (this can also be done manually) + + rails generate scaffold + +Create a new [Rails model] + + rails generate model MODELNAME + +Update the database to match what you have described in your code + + rails db:migrate + +Run the application locally (Ctrl-C to quit) + + rails server + +Start an interactive Ruby session that knows about your Rails models (type 'exit' to quit) + + rails console + +Print the routes for your application + + rails routes + +## Browser + +Go to the root page of your rails application + + http://localhost:3000 + +## Git + +Creates a new git repository in your current directory. + + git init + +Add the current directory, and all sub directories, to your git repository. + + git add . + +Tells you what you've added, deleted, and changed between your current directory and you local git repository. + + git status + +Prints the difference between FILENAME and what is in your local git repository. + + git diff FILENAME + +Commit the files you've added to the local repository. + + git commit -m "Describe what has changed, and why" . + +Push *committed* changes to the remote server. + + git push diff --git a/sites/message-board/create_a_new_rails_app_with_a_static_home_page.step b/sites/message-board/create_a_new_rails_app_with_a_static_home_page.step new file mode 100644 index 000000000..59e49917d --- /dev/null +++ b/sites/message-board/create_a_new_rails_app_with_a_static_home_page.step @@ -0,0 +1,35 @@ +requirements do +message <<-MARKDOWN +* You should have a new rails app with with a static home page that's under your control. Make sure the controller for this page is called **HomeController**. +* You, the developer, should explain to a teacher, TA, or fellow student how Rails knows to render the home view. +MARKDOWN + img class: 'noborder', src: 'img/static_home_page.png' +end + +discussion do +message <<-MARKDOWN +* In order to have a static home page, you will need a route, a controller, a view. Discuss! + * Seriously. If you don't discuss this stuff, things will be SO much harder. +* Generators! Rails has lots of them. Try exploring the output of `rails generate`. +* We’re not using `rails generate scaffold` in this curriculum. Because you will generate all your Models, Views and Controllers yourself, this will force you, to do more understanding-building brain work. The teacher/TAs can perhaps comment on what they would do in the real world and their thoughts on the excellence of this choice for learning-purposes. +* What do you need to add to your home controller (after you've made it) to have a static home page? +* How does the home/index.html.erb view file relate to the layouts/application.html.erb view file? +* What’s the significance of yield in the application view? +* What does the home controller do? +MARKDOWN +end + +tools_and_references do +message <<-MARKDOWN +* RailsGuides - Setting the Application Home Page: . +* RailsGuides - controllers overview: . +MARKDOWN +end + +hints do +message <<-MARKDOWN +* If you have no idea what to put in your home controller, maybe try looking back at a past-Railsbridge app? Or another rails app you have lying about? +MARKDOWN +end + +next_step "install_devise" diff --git a/sites/message-board/creating_a_new_controller.md b/sites/message-board/creating_a_new_controller.md new file mode 100644 index 000000000..b459ee313 --- /dev/null +++ b/sites/message-board/creating_a_new_controller.md @@ -0,0 +1,79 @@ +## Getting Started + +**1\.** Create your project + +**2\.** View your project in the browser. You should see a default page. + +## Create your Controller + +Controllers are classes that inherit from ApplicationController. + +Controllers in Rails have one or many **actions**. +Each action is defined as a method inside the controller. +Some standard actions are **index, create, new, edit, show, update,** and **destroy**. But you're not limited to using those names. + +We will start with index, which can be used to display the main page in your application. + +**3\.** Create your first controller called **HomeController**. + +Create a controller file in the app/controller folder. *The file should always be named with lower case letters and underscores.* + +``` +app/controllers/home_controller.rb +``` + +**4\.** Create your controller, with an empty index method. In the home_controller.rb file add: + +``` +class HomeController < ApplicationController + def index + end +end +``` + +## Create your View + +**5\.** Create your first view, to be associated with the `Home` controller and the `index` action. + +Create a new folder in app/views. *The folder name should always be lower case and the name of your controller.* + +``` +app/views/home +``` + +Create a file for your first action in the app/views/home folder. *The filename should always be lower case and the name of the action.* + +``` +app/views/home/index.html.rb +``` + +Each view created is associated with one controller action. You don't even really need to define a method in the associated controller: as long as the associated controller and route exist, Rails will pretend that the method exists, and is empty, and render as such. + +## Create your Route + +A **route** is a path that points to an action for a specific controller. This controller and action pair (often stylized as **controller#action**) also indicates which view should be used, via the file naming conventions mentioned above. + +**6\.** Edit routes.rb so that the base url points to your application entry point + +``` +root 'home#index' +``` + +NOTE: This says that your base url (i.e. root) should use the `index` action of the `home` controller. The view used will be the file in the app/views/home folder with name `index` (e.g. app/views/home/index.html.erb). + +Each subsequent route should be defined in a similar way: + +``` +get 'about' => 'home#about' +``` + +NOTE: This says that the base url with /about on the end will direct to the `about` action in the `home` controller. + +## Test your new Controller + +**7\.** Go back to your project in the browser and confirm that on the root path you now see the content from your index.html.erb file instead of the default Rails welcome page. + +**8\.** Add an `about` action to the Home controller and test that you can see it at the /about path in the web browser. + + + \ No newline at end of file diff --git a/sites/message-board/img/create_post.png b/sites/message-board/img/create_post.png new file mode 100644 index 000000000..34664b6bc Binary files /dev/null and b/sites/message-board/img/create_post.png differ diff --git a/sites/message-board/img/create_reply.png b/sites/message-board/img/create_reply.png new file mode 100644 index 000000000..9f8ebcd66 Binary files /dev/null and b/sites/message-board/img/create_reply.png differ diff --git a/sites/message-board/img/header.png b/sites/message-board/img/header.png new file mode 100644 index 000000000..cb2b50638 Binary files /dev/null and b/sites/message-board/img/header.png differ diff --git a/sites/message-board/img/inline_reply.png b/sites/message-board/img/inline_reply.png new file mode 100644 index 000000000..446da115e Binary files /dev/null and b/sites/message-board/img/inline_reply.png differ diff --git a/sites/message-board/img/post_index.png b/sites/message-board/img/post_index.png new file mode 100644 index 000000000..f0732dee9 Binary files /dev/null and b/sites/message-board/img/post_index.png differ diff --git a/sites/message-board/img/request-cycle.jpg b/sites/message-board/img/request-cycle.jpg new file mode 100644 index 000000000..53ac5ed5b Binary files /dev/null and b/sites/message-board/img/request-cycle.jpg differ diff --git a/sites/message-board/img/show_post.png b/sites/message-board/img/show_post.png new file mode 100644 index 000000000..41c83e72c Binary files /dev/null and b/sites/message-board/img/show_post.png differ diff --git a/sites/message-board/img/show_replies.png b/sites/message-board/img/show_replies.png new file mode 100644 index 000000000..fa6d23278 Binary files /dev/null and b/sites/message-board/img/show_replies.png differ diff --git a/sites/message-board/img/static_home_page.png b/sites/message-board/img/static_home_page.png new file mode 100644 index 000000000..dab159e02 Binary files /dev/null and b/sites/message-board/img/static_home_page.png differ diff --git a/sites/message-board/inline_replying_on_a_post.step b/sites/message-board/inline_replying_on_a_post.step new file mode 100644 index 000000000..f74d53888 --- /dev/null +++ b/sites/message-board/inline_replying_on_a_post.step @@ -0,0 +1,22 @@ +requirements do +message <<-MARKDOWN +* The user should see the 'New Reply' form at the bottom of a Post's show page. +* The user should be able to make replies using the inlined 'New Reply' form. +* Error handling (trying to reply without any content) should still work the same way as when replying was a separate page. +MARKDOWN +img class: 'noborder', src: 'img/inline_reply.png' +end + +discussion do +message <<-MARKDOWN +* What needs to happen in the posts controller to allow inline replying? What needs to happen in the view? +MARKDOWN +end + +tools_and_references do +message <<-MARKDOWN +* RailsGuides - Form Helpers, section 2.2: +MARKDOWN +end + +next_step 'add_other_features_of_your_choosing' \ No newline at end of file diff --git a/sites/message-board/install_devise.step b/sites/message-board/install_devise.step new file mode 100644 index 000000000..ee48163f1 --- /dev/null +++ b/sites/message-board/install_devise.step @@ -0,0 +1,45 @@ +requirements do +message <<-MARKDOWN +* The user should be able to create an account and log in. +* The user should see a prompt to log in if they aren't logged in. +* Once logged in, the user should see a static page with some kind of greeting. +* The page should display the logged-in user's email address and a link to log out. +MARKDOWN +end + +important do + h2 "Timebox It!" + p "The purpose of this section is to allow users to log in to your app and demonstrate how to add external libraries like Devise using the Gemfile. If you find yourself needing to write more than a couple of lines of code to make that happen, you may have gone too far. Talk to a TA!" +end + +discussion do +message <<-MARKDOWN +* What is Devise? +* What files did Devise add to your rails app? +* Someone Who Knows: explain how to read the output of the command `rails routes`. What does it give you? How is it helpful? +* How will you add the log out link? What's the syntax for a DELETE action? +* You're going to be editing your application layout to add error messaging. If you haven't already, discuss the relationship between the application layout and all the other views you'll be creating. +MARKDOWN +end + +tools_and_references do +message <<-MARKDOWN +* Devise: authentication magic! Learn more about it here: https://github.com/plataformatec/devise + * In the readme, scroll down to "Getting Started." You'll need to add it to your gem file. If you don't remember how, ask your neighbor! + * You probably don't want to just start typing commands from the Devise readme. You probably want to read through that whole section first. (i.e., don't skip bundle!) +MARKDOWN +end + +hints do +message <<-MARKDOWN +* Readme files are your best friends! Love them! +* The convention for naming models is to capitalize the first letter, like: User. +* When you run `rails generate devise:install`, you get five instructions for things to configure. 3 & 4 are good to do. +* The routes file goes through many common types of routes in the comments. This is also your friend. +* Devise has some magic that will help you with your logout link. Run `rails routes` and look for a route that helps you sign out. +* You'll probably want to show the current user's email address only if they are presently signed in, right? Devise has a helper for you. +* Optional: any time you generate a model in rails, you can use Rails Console to look at that model's methods and behavior interactively. +MARKDOWN +end + +next_step "make_it_pretty_with_bootstrap" diff --git a/sites/message-board/make_a_posts_index_page.step b/sites/message-board/make_a_posts_index_page.step new file mode 100644 index 000000000..017de5296 --- /dev/null +++ b/sites/message-board/make_a_posts_index_page.step @@ -0,0 +1,24 @@ +requirements do +message <<-MARKDOWN +* The user should be able to see each post’s name, author, and date published on the overview page (aka the index page). +* The name of the post should link to the post show page. +* The index page should include a “New Post” link or button. +MARKDOWN + img class: 'noborder', src: 'img/post_index.png' +end + +discussion do +message <<-MARKDOWN +* Maybe now would be a good time to discuss Active Record! Can anyone explain it? +MARKDOWN +end + +tools_and_references do +message <<-MARKDOWN +* RailsGuides - Listing All Posts: http://guides.rubyonrails.org/v4.0/getting_started.html#listing-all-posts + * Feel free to disregard the JSON stuff on this page, if you're so inclined. +* Bootstrap - Style that table! http://getbootstrap.com/css/#tables +MARKDOWN +end + +next_step "add_replying" \ No newline at end of file diff --git a/sites/message-board/make_it_pretty_with_bootstrap.step b/sites/message-board/make_it_pretty_with_bootstrap.step new file mode 100644 index 000000000..1b816f0c6 --- /dev/null +++ b/sites/message-board/make_it_pretty_with_bootstrap.step @@ -0,0 +1,58 @@ +requirements do + message <<-MARKDOWN +* The site should use Bootstrap for a superfun modern look. +* When logged in, the user's email address should appear in the upper-right corner of a navigation bar. +MARKDOWN + img class: 'noborder', src: 'img/header.png' +end + +important do + h2 "Timebox It!" + p "The purpose of this section is to make your app a bit prettier and demonstrate how to add CSS frameworks like Bootstrap using the Gemfile. Don't get snagged on the details of getting your CSS exactly right, unless that's valuable to you." +end + +discussion do +message <<-MARKDOWN +* Chrome developer tools / Firefox Firebug plugin — how to use ’em! +* What’s Bootstrap and (optional) why is everyone totally in love with it (or at least using it all over the place)? +* Maybe review the home/index.html.erb view file as it relates to the layouts/application.html.erb view file again, and where you might want to make some edits to take advantage of Bootstrap. +* Someone should explain how erb works — you'll be using it to show the user email address in right side +MARKDOWN +end + +tools_and_references do +message <<-MARKDOWN +* A gem for Bootstrap!! Here's where to get it https://github.com/twbs/bootstrap-sass +* Bootstrap docs: http://getbootstrap.com/css/ +* Here's a link to the navbar section: http://getbootstrap.com/components/#nav +* Reference for HTML and CSS: http://www.htmldog.com/ +MARKDOWN +end + +hints do +message <<-MARKDOWN +* Don't forget to read the readme! +* There are a couple of Bootstrappy ways to move text around — look for "Component alignment" in the docs. +* Devise has a helper method for accessing the current user - go back to the Devise readme to find it. +MARKDOWN +end + +h1 "Really Big Hint" +message "If you can't get the right HTML mojo for the Bootstrap nav bar, or want to move on quickly to the next page, here's some code you can drop into your layout (after installing Bootstrap!) that should work. The code should go immediately after your <body> tag." +pre <<-MARKDOWN + +MARKDOWN + +message "***You still have to add code to show the real logged in user and add a real log out button!***" + +next_step "add_pages_to_create_and_look_at_individual_posts" diff --git a/sites/message-board/message-board.step b/sites/message-board/message-board.step new file mode 100644 index 000000000..68544832d --- /dev/null +++ b/sites/message-board/message-board.step @@ -0,0 +1,42 @@ +message <<-MARKDOWN +## Assumptions made by this curriculum +* You’ve gone through the standard RailsBridge installfest and have successfully completed the Get a Sticker step. +* You’ve gone through the RailsBridge Suggestotron curriculum and the Job Board curriculum at least once before, or maybe a couple of times, or maybe you feel decently comfortable with Rails for some other reason. +* You want to learn more Rails!!! + +## Goals +* Make a thing! +* Understand what the models, views, and controllers of a Rails app do and how they work together. +* Get more comfortable using error messages as guideposts and not as scary things. + +## What’s Going to Happen? +* We’re going to build a message board system, where there are posts on the front page and you click through to see the original post plus discussion below. + +* We’ve divided this into challenges: + * Challenge 1: Create a new rails app with a static home page + * Challenge 2: Install Devise + * Challenge 3: Make it pretty with Bootstrap + * Challenge 4: Add pages to create and look at individual posts + * Challenge 5: Make a posts index page + * Challenge 6: Add replying + * Challenge 7: Inline replying on a post + * Challenge ∞: Other features of your choice + +* Each time you get your app into a functional state, before adding any more features, COMMIT TO GIT! The new features will probably break things, which is neat, but you’ll want to be able to roll back to a prior version if necessary. + +MAJORLY IMPORTANT NOTE: We called the sections challenges because they are challenging! This curriculum will be most fun as a collaboration — talk things through with your teacher, TAs, and other students. This is a very different style of curriculum than Suggestotron, so don’t be discouraged if you aren't quite sure what to do next. + +## How to use this curriculum +* Challenges are the big chunks of stuff to work on at a given time. These are gated by requirements — once you (or your group) has completed a set of requirements, go on to the next set. + +* Requirements in this curriculum are not entirely divorced from what a requirement is in the agile software development methodology — things that should be in the application as defined by whoever is designing/deciding things about the product. Except here, we’ve also thrown in some non-application requirements, like the ability to explain a concept to a peer. + +* Discussions are led by the teacher; this is the closest this curriculum gets to lecture-y teaching. + +* Hints are what they sound like, as are Tools & References. + + +* Teachers only!: there's a teaching guide at http://bit.ly/int-railsbridge-guide +MARKDOWN + +next_step "create_a_new_rails_app_with_a_static_home_page" diff --git a/sites/message-board/mvc_overview.md b/sites/message-board/mvc_overview.md new file mode 100644 index 000000000..17808b1ce --- /dev/null +++ b/sites/message-board/mvc_overview.md @@ -0,0 +1,24 @@ +## Explaining MVC and Records + +![MVC Overview](/intro-to-rails/img/mvc.png) + +Rails implements a very specific notion of the **Model/View/Controller** pattern, which guides how you structure your web applications. + +

    Model

    + +* saves data to the database +* accesses data from the database +* bridge between the database and objects + +

    View

    + +* display the data for human (or machine) consumption +* webpages are views + +

    Controller

    + +* acts as the glue between the models and the views +* combines data from multiple models +* summarizes and filters data + +In MVC, models, views, and controllers have very specific jobs. Separating responsibilities like this make it easy to maintain and extend rails applications. When responsibilities become muddied it gets much harder to debug issues and add new functionality. \ No newline at end of file diff --git a/sites/message-board/the_request_cycle.md b/sites/message-board/the_request_cycle.md new file mode 100644 index 000000000..432c3b9c5 --- /dev/null +++ b/sites/message-board/the_request_cycle.md @@ -0,0 +1,12 @@ +### How does typing in a URL result in a web page being rendered? Here's a rough overview. + + + +1. The user types in a URL, hoping for a cool website. +1. After the DNS gets resolved (a topic for another day), the request hits a web server, which asks Rails what it's got. +1. Rails goes to the routes file first, which takes the URL and calls a corresponding controller action. +1. The controller goes and gets whatever stuff it needs from the database using the relevant model. +1. With the data the controller got from the model, it uses the view to make some HTML. +1. Rails packages up the response and gives it to the web server. +1. The web server delivers the response to the browser to display a cool website to the user. + diff --git a/sites/organizers/1_wedding_planning.step b/sites/organizers/1_wedding_planning.step deleted file mode 100644 index 1fde06f3e..000000000 --- a/sites/organizers/1_wedding_planning.step +++ /dev/null @@ -1,28 +0,0 @@ -goals do - goal "Pre-workshop logistics, a.k.a wedding planning. It includes coordinating with the venue, planning food, talking with with volunteers and participants and making sure the invites get sent out on time, all in an effort to make the big day beautiful for the lucky...nevermind" -end - -steps do - - step do - message"Set the day." - - message "Contact the venue and make sure everyone's on board for the day you have in mind." - message "Post two events on [meetup](http://www.sfruby.info/events/calendar/), one for volunteers and one for participants. Make sure to set a limit on RSVPs that's appropriate for the venue." - message "Second, post it to the [RailsBridge calendar](http://workshops.railsbridge.org/list/) with links to the meetup events, where people should look to RSVP." - message "Promote it!" - end - - step do - message "Feed people!" - message "Plan pizza and beer for Friday's Installfest." - message "Plan bagels and coffee for Saturday Morning." - message "Plan lunch for Saturday." - end - - step do - message "Interact with participants. Make sure they download XCode. " - message "Interact with volunteers. Plan a teacher training." - message "A few days before the event, ask people to confirm that they're able to come and change their RSVP if they realize they're unable to come." - end -end diff --git a/sites/organizers/2_stage_direction.step b/sites/organizers/2_stage_direction.step deleted file mode 100644 index c4cad10cc..000000000 --- a/sites/organizers/2_stage_direction.step +++ /dev/null @@ -1,22 +0,0 @@ -goals do - goal "Stage Direction is the logistics role during the installfest, teacher training and workshop. This role is to make make sure there's a volunteer handing out extension cords, a volunteer with display adapters, a volunteer directing people to install instructions and teacher training, as appropriate." -end - -steps do - - step do - message "Teacher Training" - end - - step do - message "Installfest" - end - - step do - message "Workshop" - message "Sign people in." - message "Divide people into classes" - message "Get ready for lunch" - message "Remind people of breaks" - end -end diff --git a/sites/organizers/3_lounge_host.step b/sites/organizers/3_lounge_host.step deleted file mode 100644 index f2ee8ad45..000000000 --- a/sites/organizers/3_lounge_host.step +++ /dev/null @@ -1,18 +0,0 @@ -goals do - goal "The Lounge Host organizer role gives the somewhat technical intro and closing presentations, makes changes to the repository, and is a good person to make last minute changes if there's a bug in the curriculum" -end - -steps do - - step do - message "Intro Presentation" - end - - step do - message "Closing Presentation" - end - - step do - message "Make changes to the installfest or workshop content" - end -end diff --git a/sites/organizers/hosts.step b/sites/organizers/hosts.step deleted file mode 100644 index 39432ee0c..000000000 --- a/sites/organizers/hosts.step +++ /dev/null @@ -1,13 +0,0 @@ -goals do - goal "Thanks for welcoming RailsBridge into your space. Here are things to think about before hosting an event." -end - -steps do - - step do - message "Who from your organization will be available during the teacher training, installfest and workshop?" - message "Are you able to sponsor the event by providing food?" - message "How many people fit in your space?" - message "You'll have about 5 minutes in front of everyone. What do you want to say?" - end -end diff --git a/sites/organizers/organizers.step b/sites/organizers/organizers.step deleted file mode 100644 index 8fc6ff9e8..000000000 --- a/sites/organizers/organizers.step +++ /dev/null @@ -1,14 +0,0 @@ -message <<-MARKDOWN -### Organizers are super - -RailsBridge Workshops don't happen without organizers. The role of the organizers fall into roughly three buckets: - -* pre-workshop logistics planning -* day-of logistics, mostly pointing. -* presenting and talking - -It's easier to cover all the bases if you have at least two organizers for each workshop. - -### Hosts are super, too! - -MARKDOWN diff --git a/sites/ruby/arrays.step b/sites/ruby/arrays.step new file mode 100644 index 000000000..482da904d --- /dev/null +++ b/sites/ruby/arrays.step @@ -0,0 +1,102 @@ +goals do + goal "Make some arrays and do stuff with them" + goal "Retrieve data from arrays" +end + +step do + irb < 5 +15 >= 5 +10 == 12 + IRB +end + + +step do + message 'Notice we use a double equals sign to check if things are equal. It\'s a common mistake to use a single equals sign.' + irb <<-IRB +a = 'apple' +b = 'banana' +a == b +puts a + b +a = b +puts a + b +IRB + message "Surprise!" +end + +step do + message "For 'not equals', try these:" + irb <<-IRB +a = 'apple' +b = 'banana' +a != b +IRB + message "The exclamation point means **the opposite of**" + irb <<-IRB +!true +!false +!(a == b) +IRB + message "In `!(a == b)`, Ruby first evaluated `a == b`, then gave the opposite." + message "It also means **not true** . In conditionals, we'll see things like + + + if not sunny + puts \"Bring an umbrella!\" + +We can also say + + if sunny == false + puts \"Bring an umbrella!\" +but \"if not sunny\" is a little more natural sounding. It's also a little safer +- that double equals is easy to mistype as a single equals." +end + +step do + message "We can check more than one condition with `and` and `or` . `&&` and `||` (two pipes) is another notation for `and` and `or`." + message "We do something like this when we Google for 'microsoft and cambridge and not seattle'" +irb <<-IRB +# First let's define variables: +yes = true +no = false +# Now experiment. Boolean rule 1: AND means everything must be true. +# true and true are true +yes and yes +yes && yes +# true and false fail the test - AND means everything must be true +yes and no +no and yes +no and no +# Boolean rule 2: OR says at least one must be true. +yes or no +yes || no +yes or yes +IRB +end + + +step do + message 'By convention, methods in Ruby that return booleans end with a question mark.' + irb <<-IRB +'sandwich'.end_with?('h') +'sandwich'.end_with?('z') +[1,2,3].include?(2) +[1,2,3].include?(9) +'is my string'.empty? +''.empty? +'is this nil'.nil? +nil.nil? + IRB +end + + +explanation do + message "In code we ask a lot of questions. Boolean logic gives us tools to express the questions. " +end + +further_reading do + message "Some languages offer wiggle room about what evaluates to true or false. Ruby has very little. See [What's Truthy and Falsey in Ruby?](https://gist.github.com/jfarmer/2647362) for a more detailed walkthrough of booleans." + message "Ruby documentation for [true](http://ruby-doc.org/core-2.2.0/TrueClass.html) and [false](http://ruby-doc.org/core-2.2.0/FalseClass.html)" +end + +next_step "conditionals" diff --git a/sites/ruby/classes.step b/sites/ruby/classes.step new file mode 100644 index 000000000..96d7b1e53 --- /dev/null +++ b/sites/ruby/classes.step @@ -0,0 +1,43 @@ +goals do + goal "Define a new object" + goal "Create an instance of your object" + goal "Call methods on your object" +end + +step do + message 'Create a new file called circle.rb' + type_in_file 'circle.rb', <<-'CONTENTS' +class Circle + def initialize(radius) + @radius = radius + end + + def area + Math::PI * (@radius ** 2) + end + + def perimeter + 2 * Math::PI * @radius + end +end + +print "What is the radius of your circle? > " +radius = gets.to_i + +a_circle = Circle.new(radius) +puts "Your circle has an area of #{a_circle.area}" +puts "Your circle has a perimeter of #{a_circle.perimeter}" + CONTENTS + console 'ruby circle.rb' + message 'When prompted, type in a radius for your circle.' +end + +explanation do + message "Functions by themselves aren't always enough to keep your program organized. **Object-oriented programming** was developed to keep related data (attributes) and functions that work on that data (methods) together." + message "In Ruby, a new object is defined with the **class** keyword, followed by the name of your object (typically CamelCased). You finish the object definition later on with an **end**." + message "Most objects define a special method, **initialize**, that saves the initial data your object is created with (here, a radius) and performs any other required set-up." + message "You create an **instance** of your object with the **new** method. Arguments passed in to **new** are sent to your **initialize** method." + message "Data is stored on your object using **instance variables** that start with an `@` sign. Instance variables behave like normal variables, but are only visible from inside a specific instance of your object. If you want the data to be externally accessible, you have to write more methods." +end + +next_step 'how_to_write_a_program' \ No newline at end of file diff --git a/sites/ruby/command_line.step b/sites/ruby/command_line.step new file mode 100755 index 000000000..d691bb1fe --- /dev/null +++ b/sites/ruby/command_line.step @@ -0,0 +1,163 @@ +message "

    The command line is a text-based way to interact with your computer.

    + +

    In Windows and OS X we use graphical user interfaces (GUIs). GUIs let you operate your computer with pictures +and a mouse. We start programs by clicking an icon, look through files and folders in Finder or File Explorer, +or pick a command from a dropdown menu.

    + +

    In the command line, we type commands to run programs and navigate through files and folders. We will need this to run + Ruby progams from files. This lesson covers the commands we need for the workshop.

    " + +message "It\'s helpful to keep Finder (OS X), File Explorer (Windows), or +Nautilus (Ubuntu) open during this topic. We\'ll make files and move between +directories. A graphical file browser will help you see the results more +clearly." + +goals do + goal "Learn about the command line" + goal "Make a new directory" + goal "Browse some directories on your computer" + goal "Learn the commands `pwd`, `mkdir`, `ls`, `cd` and `man`" +end + +step do + message 'Open up a command line application on your computer.' + message "**On Macs:** In Finder, start Applications > Utilities > Terminal, or find the Terminal application through Spotlight (click the magnifying glass in the top right of the screen and start typing 'Terminal')" + message '**On Windows:** open up **Git Shell** from your desktop or All Programs menu. Do not use Power Shell! It doesn\'t support the ssh command that we need.' + message '**On Linux:** press Ctrl + Alt + T or Find Terminal under the Accessories category of your applications menu.' + + message 'We will call this "the console" or "the command line", no matter what program you\'re using. ' + message 'Consoles will either start in your home directory, the way OS X and Windows open up in your Desktop, or in the directory you ended in during your last console session. We\'ll talk more about the home directory in a few steps.' + +end + +step do + message 'The first word you type in the terminal is always the name of a program. + Many programs are included in OS X and Windows, like `ls` and `pwd`. Others + are programs you installed, like `ruby`, `irb`, or `vagrant`.' + console 'ls' + message 'Hit **RETURN** or **ENTER** to run the command.' + message '`ls` stands for **list**, and lists the contents of the current directory.' + message 'You are in your home directory. You may see directories like + \'Documents\' or \'Desktop\'. If you start up Finder, File Explorer, or + Nautilus, you\'ll see the same files and folders listed.' +end + +step do + console "pwd" + message '`pwd` means "print working directory". It shows you what **directory** you\'re currently in. Directories are also often called **folders**.' + message '`pwd` can tell you where you are if you get lost somewhere in your computer.' + message 'What directory are you in right now?' +end + +step do + console 'cd workspace' + message '`cd` means **c**hange **d**irectory. You use `cd` when you want to move from the current directory into some other directory.' + console 'pwd' + message "Notice how it changed? The working directory ends in '/workspace'." +end + +step do + console 'mkdir railsbridge_ruby' + message '`mkdir` means **m**ake **d**irectory. You use `mkdir` when you want to create a new directory.' + console 'ls' + message 'Now the directory "railsbridge_ruby" shows up in the list.' + message 'The command line is just one way of manipulating the files on your computer. Try to find the new directory you created in Finder or Windows Explorer.' + message 'If you get an error saying the directory already exists, maybe someone did these steps on your computer before. Don\'t fret.' +end + +step do + message "Let\'s go back to your home directory." + console 'cd ~' + message "`~` is the tilde key. On US keyboards, it's on the top left, next the + 1 key, and you have to press `SHIFT` along with it. + + On the command line, the tilde means your **home directory**, a directory owned by the account currently logged in to the computer. + Your home directory might be something like `/home/sparklepants` (Linux) or `/Users/saucyfrank` (Mac). + +What\'s your home directory?" + console 'pwd' + message "Remember that you can always get back to your home with `cd ~`." +end + +step do + message 'The dash is a special shortcut that brings you back to your last + location, like an "undo" for `cd`.' + console 'cd -' + message 'What command shows you your current directory?' + console 'pwd' + message 'You should be back in "railsbridge\_ruby\another\_directory".' + +end + + +step do + message "There are two tricks to save you some typing. The first one is command history." + message "Press the `up arrow` key." + result 'pwd' + message "Press `ENTER` to run the command." + message "Keep pressing the arrow key to replay commands. The `up arrow` moves backward through history, and the `down arrow` moves forward." +end + +step do + message "The `TAB` key will autocomplete a command." + console <<-LINES +cd ~ +ls +cd workspace +cd rai + LINES + message '... and hit `TAB`.' + result 'cd railsbridge_ruby' + message <<-LINES +Autocomplete will be as smart as it can. If you started with `cd`, it will list directories. + If it could be a command, it will suggest commands. When there are multiple matches, it will show you the options. + LINES + console 'p' + message '... and hit `TAB`. What happens? Hit `TAB` a second time.' + result 'Display all 203 possibilities? (y or n)' + message 'When autocomplete is stumped, it\'ll wait for another hint. It will ask when there are too many possibilities.' + console 'y' + message 'There are quite a few commands that begin with `p`! Autocomplete can remind you when you don\'t know what all the choices are.' + +end + +challenge do + message "Using the command line and your editor, create a new directory that contains a set of text files. These text files will be lists of the various things you want to learn." + + message "For example, you might want to learn various things about ruby and HTML. You could create a `ruby.txt` file and a `html.txt` file in the same directory. In each file, enter in a few questions you have about those topics." +end + +explanation do + message "The command line is an essential tool for computer programmers. While daunting at first, it offers great flexibility in moving around your computer and manipulating files." + message "There are many, many, many more commands available on the command line than what we've seen here, but these are enough to get you going." + message 'Command summary:' + + table class: 'bordered' do + tr do + td 'pwd' + td 'print working directory' + td 'print the full path to your current directory' + end + tr do + td 'ls' + td 'list directory' + td 'display the contents of the current directory' + end + tr do + td 'cd [directory]' + td 'change directory' + td 'make this directory the current directory' + end + tr do + td 'man [cmd]' + td 'manual' + td "show the manual for this command. press 'q' to quit." + end + end +end + +tip do + message "If your workshop is using a Virtual Machine (ask a TA!) now is the time to take a detour to [Using Virtual Machines](using_virtual_machines)" +end + +next_step 'irb' diff --git a/sites/ruby/conditionals.step b/sites/ruby/conditionals.step new file mode 100644 index 000000000..892de6454 --- /dev/null +++ b/sites/ruby/conditionals.step @@ -0,0 +1,100 @@ +goals do + goal 'Use `gets` to get input from the user of your program.' + goal 'Use a conditional statement to execute a branch of code only some of the time.' + goal 'if/elsif/else, while, unless' +end + + +step do + message "In this section, we'll modify the file we've created in previous steps which we called `conditionals_test.rb`. Verify that it has only the following contents." + + type_in_file 'conditionals_test.rb', <<-'CONTENTS' +print "How many apples do you have? > " +apple_count = gets.to_i +puts "You have #{apple_count} apples." + CONTENTS +end + +step do + message 'Continuing on from the end of conditionals_test.rb...' + type_in_file 'conditionals_test.rb', <<-'CONTENTS' +print "How many apples do you have? > " +apple_count = gets.to_i + +if apple_count > 5 + puts "Lots of apples!" +else + puts 'Not many apples...' +end + CONTENTS + console 'ruby conditionals_test.rb' + message 'Sometimes, we want our programs to make decisions for us. We call this concept **flow control** and it is found in most object oriented programming languages. The `if ... else ... end` construct is a way of selectively executing code based on values that exist in the program.' + message 'Try running the program with different values for `apple_count` to see each side of the conditional get executed. Notice how if you enter an apple count less than or equal to 5, `Not many apples...` is outputted. If you enter an apple count greater than 5, then `Lots of apples!` is outputted. The results of the expression `apple_count > 5` returns true or false, and with the use of the `if` statement, your program can start to make decisions as to what parts of the program to execute.' +end + + +step do + message 'What goes after the `if` is any expression that returns a **boolean**, (the values `true` or `false`). ' +end + + +step do + message "A **while** loop continues repeating until a certain statement is false. Here, the program continually asks us for numbers until we say the string 'stop'." + + message 'Create a new file called while_loop.rb' + type_in_file 'while_loop.rb', <<-'CONTENTS' +total = 0 +user_input = nil +while user_input != 'stop' + print 'Enter a number to add to the total. > ' + user_input = gets.chomp + total = total + user_input.to_i +end +puts "Your final total was #{total}!" + CONTENTS + console 'ruby while_loop.rb' + + message "It's easy for a while loop to get out of control! If your loop body doesn't do anything to make the **while** condition false, your loop will run forever. Programmers call this an **infinite loop**. If you get into an infinite loop, you can terminate the program by typing **ctrl+c**" +end + +challenge do + message "Let's extend the program you created in the numbers and arithmetic assignment to perform more mathematical operations." + + message "Using what you've learned about flow control, allow the user to select whether they would like two numbers to be added, subtracted, multiplied, or divided." + + message 'Here is a sample of how the program might run. The `>` has been used below to indicate that the user should input the value proceeding it.' + + source_code <<-'CONTENTS' +What is the first number? +> 2 +What is the second number? +> 4 +Would you like to add (1), subtract (2), multiply (3), or divide (4) these numbers? +> 3 +The product is 8 +CONTENTS + + message "As an optional challenge, can you modify this program to continuously ask for numbers?" + + message "Here is a sample of how the program might run." + + source_code <<-'CONTENTS' +What number do you want to include? +> 2 +What number do you want to include? +> 4 +What number do you want to include? +> 6 +What number do you want to include? +> DONE +Would you like to add (1), subtract (2), multiply (3), or divide (4) these numbers? +> 3 +The product is 48 +CONTENTS +end + +explanation do + message "Without some kind of conditional, your program would do the same thing every time. Conditionals let you choose to do different things depending on what data you have in hand." +end + +next_step 'nil' diff --git a/sites/ruby/datatypes.step b/sites/ruby/datatypes.step new file mode 100644 index 000000000..fb7cf9277 --- /dev/null +++ b/sites/ruby/datatypes.step @@ -0,0 +1,98 @@ +goals do + goal "Know the basic data types" + goal "Know why it matters" + message "Variables are easygoing about the objects they hold. You can assign a word, then assign a number, then a list of words." + message "But sometimes it matters what type of data we assign a variable. The fancy word for \"type of data\" is **class**." + message "Ruby has several built-in classes. In a later section, we'll explore how to make your own classes." +end + +step do + message "How can you find out what class something is?" + irb '"Letters and words".class' + result "String" + message "\"class\" is a **method**, a behavior you can ask an object to perform. " + message "To call a method on an object, type the object or variable name, then + a period, and finally the method name. " + message "Here's a preview of the datatypes we'll cover:" + irb <<-TRYME +'a'.class +5.class +3.14.class +true.class +:symbol.class +[ 1, 2, 3].class +{name: "value"}.class +nil.class +Class.class +TRYME +end + +step do + message "Different classes come with different methods. Browse the String + class' methods. (Ruby will put a ':' before the method name.)" + irb '"five".methods' + message "There are a lot of them. Can you predict what some of them do?" + message "Numbers have different methods." + irb "5.methods" + message "Notice that number methods include arithmetic operators." + message "Knowing what class an object is tells us what methods we can call." +end + +step do + message "Some datatypes are incompatible. For example, Ruby knows how to + concatenate two objects of the same class." + irb <<-TRYME +5 + 5 +"5" + "5" +TRYME + message "Ruby can guess how to convert some classes into a similar class." + irb "5 + 5.0" + message "... but it's totally confused by different objects." + irb <<-TRYME +5 + "5" +"5" + 5 +TRYME +end + +step do + message "When you have incompatible data types, you can use an object's + *conversion methods* to get a translated version of the object." + message "Conversion methods can be spotted by their name; they begin with + `to_`, e.g. `to_s` converts to a string, `to_i` to an integer, and `to_f` to + a float (decimal)." + irb <<-TRYME +5.to_s +5.to_s.class +5.to_f +5.to_f.class +5.67.to_i +"5".to_i +"5".to_i.class +TRYME + message "There isn't always a sensible conversion from one class to another." + irb <<-TRYME +"I am not a number".to_i +"five".to_i +TRYME + message "Conversion methods are used most often in calculations and in printing." + irb <<-TRYME +5 + "5".to_i +"5 plus" + 5.to_s +TRYME + message "It's common to include a `to_s` method when you make your own classes so it prints nicely." + message "To explore further, try converting the examples in step 1 to + different classes. To more easily see what conversion methods an object has, + call `methods.sort` on it." + irb "5.methods.sort" +end + +explanation do + message "Sometimes it's necessary to think about what type of data you're working with." + message "Use the `class` method to find out what class an object is." + message "Use the `methods` method to find out what methods an object has." + message "Use conversion methods to get a transformed version of an object." + message "A `TypeError` message is a hint to look more carefully at an object's class." + message "Let's look at the basic datatypes in more detail." +end + +next_step "strings" diff --git a/sites/ruby/functions.step b/sites/ruby/functions.step new file mode 100644 index 000000000..3a59daeac --- /dev/null +++ b/sites/ruby/functions.step @@ -0,0 +1,69 @@ + +message "A function is a step in a process. Think back to the last recipe you cooked. There were steps like 'mix', 'measure', and 'bake'." + +goals do + goal "Create a function" + goal "Call a function" +end + + +step do + message 'In your text editor, create a new file called area.rb' + type_in_file 'area.rb', <<-'CONTENTS' +def calculate_circle_area(radius) + Math::PI * (radius ** 2) +end + +print "What is the radius of your circle? > " +radius = gets.to_i + +puts "Your circle has an area of #{calculate_circle_area(radius)}" + CONTENTS + message "Save your work. " +end + +step do + message "Run the file." + console 'ruby area.rb' + message 'When prompted, type in a radius for your circle and hit return.' +end + +step do + message "We can write functions in IRB too." + + irb <<-CONTENTS + + def backwards( phrase ) + phrase.reverse + end +CONTENTS + + + message <<-CONTENTS +A function begins with `def`. It's followed by the name of the function, and then a list of what you plan to send in. + +Inside the function is the code that does the work. The function ends with a statement that evaluates to a function. What does it mean to evaluate a statement? When we type `puts "Two plus two is \#{2 + 2}", Ruby evaluates the statement inside the curly brackets. 2 + 2 evaluates to 4, and 3 < 9 evaluates to true. "Hello".reverse evaluates to "olleH". The function hands the value back by saying it. + +After you typed 'end' and hit return, irb typed `nil`. This is fine. Ruby always reports what it got when it ran your command, and functions result in `nil`. +CONTENTS + message "... and then **call** our function:" + + message "To call a function, first say the name of the function, then send in the values the function needs to do its job." + + irb "backwards 'Hello' " +end + + +step do + message "We can store the result in a variable." + irb "mirror = backwards 'Hello'" +end + + +explanation do + message "As your programs get more and more complicated, you'll want to group code into **functions** that can be called over and over again." + message "Pedantic Programmers might want to tell you that Ruby doesn't technically have **functions**, and everything in Ruby is really a **method**. It is appropriate to slap these people." +end + + +next_step 'classes' diff --git a/sites/ruby/hashes.step b/sites/ruby/hashes.step new file mode 100644 index 000000000..d1dfbd1f6 --- /dev/null +++ b/sites/ruby/hashes.step @@ -0,0 +1,50 @@ +goals do + goal "Make some hashes and get data back out of them" + goal "Understand why we might use a Hash" + goal "Understand the differences between Arrays and Hashes" +end + +step do + irb <<-IRB +states = {"CA" => "California", "DE" => "Delaware"} + IRB + message "Arrays are ideal when we want to store an ordered list of items. We can access elements in an array by their position, but sometimes the position doesn't really help us much. We may want to access items in the list based on a name, or **key**. A hash stores pairs of items, associating **keys** with **values**." + message 'The `{` character is typically called a \'curly brace\', and the `=>` is called a \'rocket\' or \'hashrocket\'' + irb <<-IRB +states.keys +states.values + IRB + message 'You can ask a hash for an array of just its keys or its values.' + irb <<-IRB +states['CA'] +states['DE'] + IRB + message 'With arrays, we accessed elements by their **index**, a number. With a hash, we access elements by their **key**.' +end + +step do + irb <<-IRB + bike_1 = {'make' => 'Trek', 'color' => 'Silver'} + bike_2 = {'make' => 'Cannondale', 'color' => 'Blue'} + bikes = [bike_1, bike_2] +IRB + message "Hashes are often used to store the properties of some object. Here, each hash stores the properties of a bike." + message "The *keys* are often the name of a property (here *make*, *color*) while tha *values* are the value of the property for a given object (here, *Trek*, *silver*)." + message "Consider how you might have had to store this data if you didn't have hash. What else might you want to store in a hash?" + irb <<-IRB + bikes[0]['make'] + bikes[1]['color'] +IRB + message 'When objects are nested deeply in arrays and hashes, you can access elements one after the other like this.' + message "For example, on the first line here, we are getting the first *bike* in the *bikes* array (`bikes[0]`) and then getting its *make* (`bikes[0]['make']`)" +end + +explanation do + message "Hashes are a fundamental way to group large amounts of related data. In other languages, hashes are called **dictionaries**, **maps**, or **associative arrays**." +end + +further_reading do + a "Ruby's documentation for Hash", href: 'http://ruby-doc.org/core-2.2.0/Hash.html' +end + +next_step 'loops' diff --git a/sites/ruby/how_to_write_a_program.step b/sites/ruby/how_to_write_a_program.step new file mode 100644 index 000000000..070bf4bc0 --- /dev/null +++ b/sites/ruby/how_to_write_a_program.step @@ -0,0 +1,240 @@ +goals do + goal "Break down problem statements into code" + goal "Design and arrange our code to serve a purpose" +end + + +explanation do + message "Every time we write a program, we translate abstract thoughts in our minds into workable, actual code. It helps to think out those thoughts and put them in a clear, plain, problem statement." + message "if we think out all the functionality of our program, we can write a description that we can *refactor* into a program, piece by piece, until it does what we want it to do." + message "Here, we're going to break down a problem statement into workable goals that we can then translate into our first program." +end + +step do + message "Today, we're going to write a Dice rolling simulator. It's a simple app, with three goals: **Simulate the rolling of a die**, **Roll a die with any number of sides**, and **Roll any number of dice.**" + + message "Lets start by making a file named **'roller.rb'**" +end +step do + message "lets start with the first goal, **Simulate the rolling of a die.**" + message "Ruby comes with the 'rand' method, which can give you a random number. It takes an integer as an argument, and will give you a random number." + + message "Note: rand is 0-indexed, like an array. Open up irb and type:" + + irb 'rand 2' + + message "Now, repeat that a few more times. You'll notice the numbers you get in response are either a 0 or a 1, never a 2. This is what 0-indexing means: the first number is always zero." + + message "The problem is, dice don't start at zero, they start at one. So we can just add one to the rand method to make it more dice-like." + + message "Open up 'roller.rb' and write this method into the file." + type_in_file 'roller.rb', <<-'CONTENTS' + def roll + rand(6) + 1 + end + puts roll + CONTENTS + + message "Run that file a few times, and you'll see that we have successfully simulated rolling a die." +end +step do + message 'Now on to the second goal, **Roll a die with any number of sides**.' + message 'We can take our existing roll method and add to it with a simple argument.' + + type_in_file 'roller.rb', <<-'CONTENTS' + def roll(sides) + rand(sides) + 1 + end + + puts roll(6) + CONTENTS + + message 'Now, our program can roll any number of die, so long as we pass in the number of sides to the roll method.' +end + +step do + message "We've got one more goal to knock down: **Roll any number of dice**." + message 'We can pass in another argument, but we still have to consider how to show this information to the user. For now, lets add the numbers together.' + message "We're going to need to increase the method somewhat. Make the changes to the method that you see below." + type_in_file 'roller.rb', <<-'CONTENTS' + def roll(sides, number=1) + roll_array = [] + number.times do + roll_value = rand(sides) + 1 + roll_array << roll_value + end + total = 0 + roll_array.each do |roll| + new_total = total + roll + total = new_total + end + total + end + + puts "We're rolling a six sided die!" + puts roll(6) + + puts "Now we're rolling two 20 sided die!" + puts roll(20, 2) + CONTENTS + + message "A lot is happening in this function, now! Here's a walkthrough of the changes we made: " + message "* We added an argument, 'number'. It has a default argument of '1', so that way if we don't pass it anything, it just uses the default. " + message "* We create an empty array to hold the dice we're about to roll, called 'roll_array'." + message "* We call the 'times' method on the number. This is like the 'each' method, which allows us to run the code inside of it as many times as the number. So, if it is 1, it will do it once. If it is 2, it will do it twice, and so on." + message "* every time we loop over this code, we make the roll (using the sides argument value) and then insert the result into the roll_array." + message "We assign 0 to the variable 'totals'. This is what we're going to use to hold the value of the combined rolls." + message "* We loop through each item in the roll_array, and add it to the 'totals' variable. The totals variable is saved outside of the loop, so you will effectively add each of the members together." + message "* We add the totals variable at the end so that the method knows what to return." + + message "And so, we have a program that works they way we want it to work... but how useful is it?" +end + +step do + message "The problem with our program currently is that it is too clunky. Whenever you write code, think about how you want it to be used - either by you, a user, or even another programmer that might want to adapt your work further down the line. We should strive to write code that is readable, and maintainable." + + message "Right now, our program is one big method, and that is not very good for readability or maintainability. Lets organize this a little more thoughtfully, and make a class that contains this behavior." + + type_in_file 'roller.rb', <<-'CONTENTS' + class Die + + def initialize(sides) + @sides = sides + end + + def roll(number=1) + roll_array = [] + number.times do + roll_value = rand(@sides) + 1 + roll_array << roll_value + end + total = 0 + roll_array.each do |roll| + new_total = total + roll + total = new_total + end + total + end + end + + puts "We're rolling a six sided die!" + puts Die.new(6).roll + + puts "Now we're rolling two 20 sided die!" + puts Die.new(20).roll(2) + CONTENTS + + message "Now, we can instantiate a Die object (passing in the number of sides as an argument) and then roll as a method called in it. This makes things a little easier to organize and break up this big method into smaller bits." + + message "Think for a moment about how we could break up that big method into smaller ones. What parts can we seperate now that we have the stability of a class?" +end +step do + message "One option for breaking it up would be to seperate out the method of rolling a single die. Right now, 'rand(sides) + 1' doesn't necessarily make sense in context. If someone was reading this code, it might not be obvious that it is actually generating a number for a rolled die. lets break that up into its own method:" + + type_in_file 'roller.rb', <<-'CONTENTS' + def generate_die_roll + rand(@sides) + 1 + end + end + CONTENTS + message "And now, our entire class looks like this:" + + type_in_file 'roller.rb', <<-'CONTENTS' + class Die + def initialize(sides) + @sides = sides + end + + def generate_die_roll + rand(@sides) + 1 + end + + def roll(number=1) + roll_array = [] + number.times do + roll_array << generate_die_roll + end + total = 0 + roll_array.each do |roll| + new_total = total + roll + total = new_total + end + total + end + end + + puts "We're rolling a six sided die!" + puts Die.new(6).roll + + puts "Now we're rolling two 20 sided die twice!" + puts Die.new(20).roll(2) + CONTENTS + + message "It is a small change, but now that line makes a little more sense when reviewing it. Making changes like this can be a big help to others, but an even bigger help to yourself. Programming is hard, so be sure to be kind to your memory while you do it!" +end + +step do + message "We're almost done. Why don't we make things easy for our soon-to-be users? We can imagine that there are certain dice that are popular in many games. Why not define some ready-made dice that people can use moving forward?" + + message "We're going to instantiate some dice in our class, and assign them to something called a 'Constant'. Constants are like variables, except they are unchanging - once they are assigned, they are assigned to that value - forever! They can't ever be changed. Note: You may have noticed that class names start with an uppercase - that is because class names are, in fact, constants!" + + message "We're going to assign some of these dice to constants to make it easier to use later." + + type_in_file 'roller.rb', <<-'CONTENTS' + SIX_SIDED_DIE = Die.new(6) + EIGHT_SIDED_DIE = Die.new(8) + TEN_SIDED_DIE = Die.new(10) + TWENTY_SIDED_DIE = Die.new(20) + CONTENTS + + message "So now, our file looks like this:" + type_in_file 'roller.rb', <<-'CONTENTS' + class Die + def initialize(sides) + @sides = sides + end + + def generate_die_roll + rand(@sides) + 1 + end + + def roll(number=1) + roll_array = [] + number.times do + roll_array << generate_die_roll + end + total = 0 + roll_array.each do |roll| + new_total = total + roll + total = new_total + end + total + end + end + + SIX_SIDED_DIE = Die.new(6) + EIGHT_SIDED_DIE = Die.new(8) + TEN_SIDED_DIE = Die.new(10) + TWENTY_SIDED_DIE = Die.new(20) + + puts "We're rolling a six sided die!" + puts SIX_SIDED_DIE.roll + + puts "Now we're rolling two 20 sided die twice!" + puts TWENTY_SIDED_DIE.roll(2) + CONTENTS + + message 'And now you have a program that works, is readable, and easy to use. Congratulations!' + + message 'Lets have some fun. launch irb and type the following:' + irb "require './roller.rb'" + + message "This loads the file into irb, which lets us play with it. Try using the dice. Roll a few! Use the constants to roll dice that you have already defined, or create some new dice and test the bounds of the system." +end + +explanation do + message "Consider the differences in the code between steps 4 and step 8. Coding is like being an architect, in that what you create is, at once, creative and load-bearing. The code at the end of step 4 worked perfectly, but it was hard to read, and a little bit of a pain to change. Imagine if we wanted to keep log of how the die rolled, or add other aspects or descriptions to the roll." + + message "The final version of the code gives us room to breathe, is easy to read and easy to modify. Consider what other changes might be made to make it even simpler - when it comes to writing code, it's generally better to split big methods into lots of smaller ones." +end + diff --git a/sites/ruby/input_and_output.step b/sites/ruby/input_and_output.step new file mode 100644 index 000000000..5cd3d62c1 --- /dev/null +++ b/sites/ruby/input_and_output.step @@ -0,0 +1,67 @@ +goals do +goal "Print to the screen with puts" +goal "Read user input with gets" +end + +message '' + +step do + irb <<-IRB +puts 'Hello World' + IRB + message '`puts` (**put** **s**tring) is a way of printing information to the user of your program.' + message 'Take some time to contemplate the output of `puts` in irb:' + result <<-RESULT +irb :006 > puts 'Hello World' +Hello World + => nil +irb :007 > + RESULT + message 'The method `puts` always has the **return value** of `nil`, which is what we see after the `=>` in the output. Printing \'Hello World\' to the screen is just a side-effect.' +end + + +step do + message "`print` is like `puts` but doesn't make a new line after printing." + + message 'Create a new file called input\_and\_output.rb' + type_in_file 'input_and_output.rb', <<-'CONTENTS' +print "All" +print " " +print "in" +print " " +print "a" +print " " +print "row" +CONTENTS + message "Save the file and exit out of irb." + console 'ruby input_and_output.rb' +end + + +step do + message 'Create a new file called conditionals_test.rb' + type_in_file 'conditionals_test.rb', <<-'CONTENTS' +print "How many apples do you have? > " +apple_count = gets.to_i +puts "You have #{apple_count} apples." + CONTENTS + message "Save the file and exit out of irb." + console 'ruby conditionals_test.rb' + message 'When prompted, type in a number of apples and press enter.' + message "`print` is like `puts` but doesn't make a new line after printing." + message "`gets`, or **get** **s**tring, pauses your program and waits for the user to type something and hit the enter key. It then returns the value back to your program and continues execution. Since the user could have typed anything ('banana', '19.2', '<` has been used below to indicate that the user should input the value proceeding it.' + + source_code <<-'CONTENTS' +What do you want to say? +> sally sells seashells +You said: sally sells seashells +CONTENTS +end +next_step "numbers_and_arithmetic" diff --git a/sites/ruby/irb.step b/sites/ruby/irb.step new file mode 100755 index 000000000..c565a3864 --- /dev/null +++ b/sites/ruby/irb.step @@ -0,0 +1,84 @@ +goals do + goal "Understand how and when to use irb" + goal "Understand how to start and stop irb" + goal "Understand the difference between irb and the command line" + goal "Execute some simple Ruby statements" +end + +step do + message "irb is the **i**nteractive **r**u**b**y interpreter. It's a program + that runs Ruby code as you type it in." + message "Start irb." + console "irb" + result "irb: line 001 >" + message "The prompt changed. This is irb's way of reminding you that it's + running, and telling you it's ready." + message "Hit **Enter** a few times." + result "irb: line 002 > +irb: line 003 > +irb: line 004 >" + +end + +step do + message "irb will run until you quit." + irb "exit" + message "You're back at the command line. Notice that the prompt no longer says irb." + message "`exit` is the guaranteed way to get out of irb. Depending on your + operating system, Control-C or Control-D on an empty line may also work." + message "Practice going in and out of irb a couple of times. How can you tell + when you're in irb? How can you tell when you're back at the command line?" +end + + +step do + message "irb is its own program with its own commands. Although we start it + from the command line, the commands to change directories and so on don't + work in irb." + message "Restart irb." + console "irb" + irb "pwd" + result <<-YIKES + NameError: undefined local variable or method `pwd' for main:Object + from (irb):1 + from /usr/bin/irb:12:in `
    ' + YIKES + message "This ferocious error message is irb saying \"Huh? What??\" because + only irb understands Ruby." +end + +step do + message "In irb, you can experiment with short pieces of code to figure out what they do." + irb <<-IRB +5 + 9 +109 / 17 +2 ** 8 +5 > 6 + IRB + message "What happened after each line? What do you think these statements do?" +end + +step do + message "Lets take a closer look at the output of irb:" + irb "1 + 2" + result <<-MESSAGE +1.9.3p125 :015 > 1 + 2 + => 3 +1.9.3p125 :016 > + MESSAGE + message "Here, `=> 3` is the **return value** of the **statement** `1 + 2`, + the result of running your code." + message "Every statement in Ruby has a **return value**: irb shows you that + value after you type a complete statement and press ENTER." +end + + +explanation do + message "irb is a place to experiment and find out how certain language + features work. It's not a good place to write long programs. It doesn't let + you save your work, and it's not a very friendly text editor." + message "When you write a full-fledged program, you'll save it into a text + file on your computer. We'll see this in a later step." +end + +next_step "running_programs_from_a_file" diff --git a/sites/ruby/loops.step b/sites/ruby/loops.step new file mode 100644 index 000000000..4d8c8ee02 --- /dev/null +++ b/sites/ruby/loops.step @@ -0,0 +1,88 @@ + message 'Loops are a way of doing something multiple times. In this loop, we printed each fruit to the screen in order.' + +goals do + goal "Use loops to do operations for every element in an array." + goal "Use `puts` to print strings to the screen." + goal 'Learn the two different syntaxes for creating blocks in Ruby.' +end + +step do +message "What do you predict this will do?" +irb "3.times { puts 'Hip, hip, hooray!'}" +message "The `times` method will do something as many times as you tell it." +message "The chunk of code between the brackets is called a `block`. It will get run each time." +end + +step do +message "We often want to do something to each item in a collection." +irb <<-CONTENTS +beatles = [ "John", "Paul", "George", "Ringo"] +beatles.each { |beatle| puts "Hi, my name is \#{beatle}"} +CONTENTS + +message 'The straight up-and-down `|` is called the **\'pipe character\'**. On a US keyboard, it\'s typically the shifted version of the `\` (backslash) key.' +message "The variable between the pipes is a **block variable**. It's a way to refer to each item in the collection." + message '`each` takes the first element in the array and sends it to the block, which temporarily stores it in the **block variable** and then runs the code after the pipes. It then goes back and does this again for each of the remaining items in the array.' + irb <<-'IRB' +ducks = ['Huey', 'Dewey', 'Louie'] +ducks.each { |duck| puts "#{duck} quacks!" } +ducks.each { |zombie| puts "#{zombie} quacks!" } + IRB + message 'It doesn\'t matter what you call your block variable: the previous two statements are exactly equivalent to Ruby. But you should try to name your variables something useful so the code makes sense to you later!' +end + +step do +irb "('a'..'z').each { |letter| puts letter }" +message "In ruby, we have a special construct called a **range**. A range gives us a shortcut in defining a list of objects that fall within starting and ending point. In the example above, we're defining a range of all the lowercase characters between and including 'a' and 'z'. When used with an `each` statement, a range will loop through each value within that starting point and ending point." + +irb "('a'...'z').each { |letter| puts letter }" + +message "How is this line different from the previous example? The three dots tells us to **omit** the ending point." +end + +step do + message "There's a second way to make a block in Ruby. Instead of { }, we surround the code with **`do`** and **`end`**." + irb <<-'IRB' + +total = 99 +colors = ['red', 'blue', 'green'] +colors.each do |color| + puts "#{total} colors of paint on the wall..." + puts "Take #{color} down, pass it around..." + total = total - 1 + puts "#{total} colors of paint on the wall!" +end + IRB + message " The `do ... end` syntax is typically used when a block needs to span multiple lines, while the `{ ... }` syntax is for a single line block." +end + +challenge do + message "Let's write a program that greets everyone in your group. Use an array to store everyone's name." + + message 'Here is a sample of how the program might run.' + + source_code <<-'CONTENTS' +Oh, hello, Sally Samsonite! +Oh, hello, Johnny Jameson! +Oh, hello, Beth Benitsky! +Oh, hello, Corinne Camelia! +CONTENTS + + message 'Your program should be written in a way that group members can be removed and added to your array without requiring a change to the rest of your program. Use what you now know about loops so that only one `puts` statement is in your program.' + + message "Let's write a program that adds up a set of numbers in an array. The numbers you can use as a test case are 4, 6, 5, 5, 10" + + message 'Here is a sample of how the program might run.' + + source_code <<-'CONTENTS' +The sum of all of the numbers is 30. +CONTENTS + + message "Your program should be written in a way that doesn't require massive changes when the array of numbers is modified." +end + +explanation do + message "As you build complex programs, you'll want to do something to many pieces of data without typing it all out. Loops help solve this problem." +end + +next_step "summary:_basics" diff --git a/sites/ruby/nil.step b/sites/ruby/nil.step new file mode 100644 index 000000000..eb7f32490 --- /dev/null +++ b/sites/ruby/nil.step @@ -0,0 +1,97 @@ +message <<-CONTENT +nil is a special Ruby data type that means "nothing". It's equivalent to null or None in other programming languages. + + +CONTENT + +goals do +goal "Know what nil is for" +end + +step do +message <<-CONTENT +How do you set a variable to empty? + +Some of the values we could use can be legitimate values, like: + +`temperature = 0` + +`today_is_friday = false` + +`empty_phrase = ""` + +Nil to the rescue. Nil means 'nothing'. It's used to show that a variable hasn't been assigned anything yet, or that a function didn't return a value. + + +CONTENT + +irb <<-IRB +# Where is the letter "a" in apple? +"apple".index("a") + +# Where is the letter "z"? +"apple".index("z") +IRB +end + +step do + message "You may have noticed this already:" + irb 'puts "What does puts evalute to?"' + message "Ruby returns what an expression evaluated to. `puts` puts a string on the screen, but always evaluates to nil." + +end + + + +step do + message "Here's an error message you will see often." +irb <<-IRB + word = "hello" + word.upcase + word = nil + word.upcase +IRB + result "NoMethodError: undefined method 'downcase' for nil:NilClass" + message "What happened?" + irb "word.class" + message "`word` was set to nil. Let's look at the alphabetized list of methods the Nil class has." + irb "word.methods.sort" + message "The error message told us Nil doesn't have a method called upcase." + +end + +step do + message "How can we avoid these errors? There are a few ways to tell if something is nil." + +irb <<-IRB +word = "something" +word.nil? +word = nil +word.nil? +# Remember, two equals signs asks if something is equal +word == nil +IRB + + message "What's going on here?" + source_code <<-CONTENT + name = nil + if name.nil? + puts "The name hasn't been set yet." + else + puts "Your name is \#{name}" + end +CONTENT +end + + +explanation do + +end + +further_reading do + message "[Nil Class documentation](http://ruby-doc.org/core-2.2.0/NilClass.html)" +end + + +next_step 'symbols' + diff --git a/sites/ruby/numbers_and_arithmetic.step b/sites/ruby/numbers_and_arithmetic.step new file mode 100644 index 000000000..710476372 --- /dev/null +++ b/sites/ruby/numbers_and_arithmetic.step @@ -0,0 +1,85 @@ +goals do + goal "Understand the difference between an integer and a float" + goal "Perform basic arithmetic operations" +end + +step do + message "In programming, we like to distinguish between different kinds of + numbers. Here, we'll talk about integers (whole numbers) and floats + (decimals)." + irb <<-TRYME +1.class +1.0.class +TRYME + message "You'll notice that `1.class` and `1.0.class` are actually 2 different + kinds of objects." + message "With that in mind, let's see how that plays into performing + arithmetic operations." +end + +step do + irb <<-TRYME +1 + 2 +1.0 + 2.0 +1 + 2.0 +TRYME + message "Here, you'll notice that performing an operation on 2 integers will + produce an integer and operating on 2 floats will produce a float, but mixing + an integer with a float would actually produce a float." +end + +step do + irb <<-TRYME + +2 - 1 +2.0 - 1.0 +2.0 - 1 +TRYME + message "Just like addition, we can perform subtraction on integers and floats interchangeably." +end + +step do + irb <<-TRYME + +2 * 3 +2.0 * 3 +2.0 * 3.0 +TRYME + message "Likewise, we can perform multiplication on floats and integers." +end + +step do + irb <<-TRYME + +4 / 2 +8 / 2 +6 / 4 +6 / 4.0 +TRYME + message "Division can be performed similarly. Notice the difference in the results of the last two lines. When dividing two integers, ruby will perform mathematical truncation, where the expected result 1.5 becomes 1" +end + +step do + irb <<-TRYME + +4 / 0 +TRYME + message "Uh oh! Notice how we did not get a result here. The ruby interpreter raised an error. These errors tell the programmer that they tried to do something that the interpreter does not like or cannot use. Each error has a classification and a message. In this case, we get a `ZeroDivisionError` with the message: divided by 0" +end + +challenge do + message "Write a calculator that performs addition. Your program should prompt the user to enter two numbers and output the sum." + + message 'Here is a sample of how the program might run. The `>` has been used below to indicate that the user should input the value proceeding it.' + + source_code <<-'CONTENTS' +What is the first number? +> 2 +What is the second number? +> 4 +The sum is 6 +CONTENTS + +end + +next_step "booleans" diff --git a/sites/ruby/overview:_building_blocks.step b/sites/ruby/overview:_building_blocks.step new file mode 100644 index 000000000..416a6fc0c --- /dev/null +++ b/sites/ruby/overview:_building_blocks.step @@ -0,0 +1,11 @@ + +goals do +goal "Learn about different kinds of data: what they\'re called, what you can do with them" +goal "Store data in a variable so you can use it later" +goal "Make decisions with conditionals" +goal "Hold collections of data in arrays and hashes" +goal "Loop over a collection" +end + + +next_step 'variables' \ No newline at end of file diff --git a/sites/ruby/overview:_organizing.step b/sites/ruby/overview:_organizing.step new file mode 100644 index 000000000..a1114872b --- /dev/null +++ b/sites/ruby/overview:_organizing.step @@ -0,0 +1,7 @@ +message "When your program gets longer than one screenful, it gets hard to keep track of. Most interesting problems have hundreds of lines of code. Let's look at tools that make it more manageable." +goals do + goal "Use functions to break up code into tasks" + goal "Use classes to group variables and functions that naturally go together" +end + +next_step 'functions' \ No newline at end of file diff --git a/sites/ruby/ruby.step b/sites/ruby/ruby.step new file mode 100755 index 000000000..b28742bb4 --- /dev/null +++ b/sites/ruby/ruby.step @@ -0,0 +1,76 @@ +message <<-MARKDOWN +### Goal + +To teach you Ruby we are going to explain the basic building blocks used in the Ruby language and allow you reinforce what you've learned through challenges. + +When you have completed this curriculum you should understand: + +* The command line and why we use it +* How to run your Ruby code interactively (irb) or from a file +* Simple types like numbers, strings, and booleans +* Data structures like arrays and hashes +* Object concepts like Classes + +### Requirements + +We're going to be working with: + +* Ruby +* A command line program +* A text editor of your choice + +Everything should be set up the night before during our +Installfest. Please +ensure you have everything working _before_ you show up for the workshop. + +You can verify that you have everything working by trying this out in your terminal: + +
    +$ irb
    +>> 1 + 2
    +=> 3
    +$
    +
    +
    + +If you can do that, you are probably good to go. + +### Format + +Each lesson will look like this: + +
    +

    Step Title

    +
    +

    Goal:

    +

    Description of the current step. +

    Red because big goals are scary. +

    +
    +

    Steps:

    +
    steps to take.
    +
    +
    +def code_to_write
    +  1 + 1
    +end
    +
    +

    Yellow because we've gotten it done, but we have no clue what's going on. +

    +
    +

    Challenges

    + +

    A list of activities to try to reinforce the concepts learned in the lesson

    + +

    Orange because it stimulates the brain by increasing its oxygen supply.

    +
    +
    +

    Explanation

    +

    Details of what the steps actually did... spell out the cause and effect. +

    Green because we can tie everything together now. +

    +
    +MARKDOWN + +next_step "what_is_ruby" + diff --git a/sites/ruby/running_programs_from_a_file.step b/sites/ruby/running_programs_from_a_file.step new file mode 100644 index 000000000..7903fc23c --- /dev/null +++ b/sites/ruby/running_programs_from_a_file.step @@ -0,0 +1,92 @@ +message <<-INTRO +irb isn't a good tool for writing long programs. Code is entered one line at a +time. You can't save your work, and you can't share it when you're done. + +Instead, we write code in a file and run it from the command line. + +For writing code, we use a program called a text editor. + +A text editor is a word processor that saves in plain text format. This is +unlike Word, which saves files in a special format that only Word can read. + +We may have recommended a particular text editor during the Installfest, such as +Atom. You can use any editor you like so long as it saves plain text. + +It'll helpful to keep your text editor running, since you'll be coming back to +it often. +INTRO + +goals do + goal "Use a text editor." + goal "Write and save a Ruby program into a file" + goal "Start and connect to the RailsBridge virtual machine, if you are using it for the workshop" + goal "Run Ruby code saved in a file" +end + +step do + message "Start your text editor." + message "In your text editor, create a new file called **my_program.rb**" + type_in_file "my_program.rb", <<-CONTENTS +puts 'This code is in a file!' +some_variable = 19 +puts "I stored a variable with the value #\{some_variable\}!" +CONTENTS +end + +step do + message <<-INFO +Save the file. + +If you're using the RailsBridge virtual machine, *save the file +in the same folder you created the virtual machine.* That folder is shared +between your laptop and the vm, like Google Drive. + +Did you notice we added `.rb` at the end of the name? It's standard practice to +name Ruby files this way, to help people and tools recognize the file contains +Ruby code. + +When you saved the file, your text editor may have added colors to the code. It +recognizes the file contains Ruby, and will mark up different kinds of words +with different colors. This is called "syntax highlighting", which makes it +easier to read code. +INFO +end + +step do + message "Let's look at the file." + message "Open your terminal, connect to the + VM, and `cd` to your workspace directory" + console "ls" + result "my_program.rb" + console "more my_program.rb" + result <<-CONTENTS +puts 'This code is in a file!' +some_variable = 19 +puts "I stored a variable with the value #\{some_variable\}!" +CONTENTS + message "**my_program.rb** was saved in plain text - you see exactly what you + typed. If this were a Word document, you would see a screenful of weird + characters and formatting, and you would get errors when you ran it." +end + + +step do + message "Now run the code. Stay in your workspace directory." + console "ruby my_program.rb" + result <<-CONTENTS +This code is in a file! +I stored a variable with the value 19! +CONTENTS + message "If you didn't see this message, troubleshoot with a TA. We will do + this for the rest of the tutorial, so it's important to ensure everything + works." +end + + +explanation do + message "Congratulations! You've run your first Ruby program!" + message "Ruby programs are written in files and run from the command line. As + your program becomes larger and larger, it may span many files!" +end + +next_step "summary:_tools" diff --git a/sites/ruby/strings.step b/sites/ruby/strings.step new file mode 100644 index 000000000..30ec3164a --- /dev/null +++ b/sites/ruby/strings.step @@ -0,0 +1,97 @@ +goals do + goal "Understand when to use strings" + goal "Learn how to combine strings" + goal "Learn about string interpolation" +end + +step do + irb <Recall that you can see what methods + are available for an object by calling `.methods` on it." + irb "'some string'.methods" +end + +explanation do + message "Strings are a key way to present information in your programs. Since a human will probably be reading the output of your program eventually, and humans speak words rather than numbers, you will often want to use strings." + message 'A not at all complete summary of methods on String:' + table class: 'bordered' do + tr do + td 'length' + td 'how long is this string (characters)' + end + tr do + td 'reverse' + td 'return same string, reversed' + end + tr do + td 'upcase' + td 'return same string, IN UPPER CASE' + end + tr do + td 'delete([another string])' + td 'delete all characters in the second string from the first' + end + tr do + td 'methods' + td 'get the names of all methods you can perform on string' + end + end +end + +further_reading do + a "Ruby's documentation for String", href: 'http://ruby-doc.org/core-2.2.0/String.html' +end + +next_step 'input_and_output' diff --git a/sites/ruby/summary:_basics.step b/sites/ruby/summary:_basics.step new file mode 100644 index 000000000..e564bee4d --- /dev/null +++ b/sites/ruby/summary:_basics.step @@ -0,0 +1,18 @@ +message "We\'ve seen built in data types, input and output, making decisions with conditionals as well as some looping. We've also identified two interesting data structures: the **array** and the **hash**." + +message "Challenge yourself to create programs for the following situations." + +message <<-CONTENT +
      +
    • Write a program that verifies whether someone can vote based on their supplied age.
    • +
    • Write a program that plays back the message a user supplied.
    • +
    • Write a program that adds up five user-supplied numbers.
    • +
    • Make a hash for the people at your table, where the key is their name and the value is their favorite color.
    • +
    • Make an array of the months in the year.
    • +
    +CONTENT + + +message "When programs get big, they get disorganized and hard to read. Next we'll look at how to keep things tidy with functions and classes." +next_step "overview:_organizing" + diff --git a/sites/ruby/summary:_tools.step b/sites/ruby/summary:_tools.step new file mode 100644 index 000000000..554a0a1b3 --- /dev/null +++ b/sites/ruby/summary:_tools.step @@ -0,0 +1,17 @@ + +message <<-SUMMARY +We\'ve explored the basic tools for writing and running Ruby programs. +
      +
    • How do you open the console on your laptop? +
    • How do you find out what directory you\'re in on the command line? How can you move to a different directory? +
    • How do you start the RailsBridge VM and connect to it? +
    • Where should you save files when you're using the RailsBridge VM? Can you show it in Finder or File Explorer? +
    • How do you run a Ruby program? +
    • How can you experiment with Ruby interactively? +
    • +
    +Let's dive into the ABCs of Ruby. + +SUMMARY + +next_step 'overview:_building_blocks' \ No newline at end of file diff --git a/sites/ruby/symbols.step b/sites/ruby/symbols.step new file mode 100644 index 000000000..3eba3993d --- /dev/null +++ b/sites/ruby/symbols.step @@ -0,0 +1,103 @@ + +message " +Symbols are, if not unique to Ruby, at least not found in most other languages. + +Symbols help Ruby use memory more efficiently. Every time you store a value in an object, Ruby sets aside a little piece of memory to hold it. If enough memory is tied up, we'll see an effect on performance. Perhaps you've seen your laptop run more and more slowly when lots of programs are open, or seen it speed up when you add more RAM. + +Symbols let Ruby variables point to the same object in several places instead of allocating a new copy. As a metaphor, think of how a rental car company lets several drivers use the same car instead of buying their own. + +Efficient memory use isn't a concern in simple programs, but it's considered good form . At this stage, focus on recognizing and declaring symbols. You'll see symbols regularly as you look at code. +" + + +goals do + goal "Know a symbol when you see it" + goal "Learn how to declare a symbol" + goal "Notice when symbols are used" +end + + + +step do + message "Symbols start with a colon. Let's look at one." + irb <<-CONTENTS + :north + :north.class + :north.methods + CONTENTS +end + + +step do + message "Here are more examples:" + irb <<-CONTENTS + :TUESDAY + :"symbols can have spaces if in quotes but it's a lot to type" + :'single quotes work too' +CONTENTS + message "Experiment with naming symbols. Do they use the same rules as variables?" +end + + +step do + message "Symbols are constants, like letters and numbers, so they can't be assigned a value." + irb <<-CONTENTS + :north = :south + CONTENTS + + message "Symbols print their names when they're converted to strings." + irb <<-CONTENTS + :Saturday.to_s + puts "Today is \#{:Saturday}" + CONTENTS + + message "Symbols are used in a similar way to enums or named constants in other languages - for unchanging ideas that you want to refer to the same way, no matter where you are in a program. Unlike enums and named constants, you never initialize them with a value." +end + +step do + message <<-CONTENTS +How do we know it's the same object? Ruby assigns an object_id number when it creates an object, like a serial number. These are the same object. + CONTENTS + irb <<-CONTENTS +:friday.object_id +:friday.object_id + CONTENTS + message "Now create two variables with the value 'Friday' and check their object_id. " + message "Ruby allocated a fresh chunk of memory each time, even though the strings says the same thing." + + message <<-CONTENTS +Ruby automatically treats strings, numbers, and some values as symbols. + +In irb, check the object ids of a word, your lucky number, true and false, and nil. Check the same value twice to prove it's the same object. ( Tip: to save typing, use the up-arrow key to scroll back through your last irb commands.) + CONTENTS +end + + +step do + message "Calendars are a good example of when to use symbols. The days and months repeat. In a one year calendar making 52 new Mondays takes some memory, but not too much. What if you wanted to make a ten year calendar, or one with no limits? This is a good place for symbols. + + days = [:Monday, :Tuesday, :Wednesday, :Thursday, :Friday, + :Saturday, :Sunday] + + months = [:January, :February, :March, :April, :May, :June, + :July, :August, :September, :October, :November, :December] + + today = :Friday + month = :May + +" +end + + +explanation do + message "Symbols are memory-efficient labels we re-use to keep track of all kinds of things in Ruby. You'll see them again soon in Hashes." + message "When you see a symbol, think about why the programmer chose to use one. Is the idea important here, or the contents?" +end + +further_reading do + message "[The Difference Between Ruby Symbols and Strings ](http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings)" + message "[The Ruby_Newbie Guide to Symbols](http://www.troubleshooters.com/codecorn/ruby/symbols.htm)" + message "[The Ruby documentation says](https://www.ruby-lang.org/en/documentation/ruby-from-other-languages): *If you\’re in doubt whether to use a Symbol or a String, consider what\’s more important: the identity of an object (i.e. a Hash key), or the contents.*" +end + +next_step 'working_with_collections' \ No newline at end of file diff --git a/sites/ruby/using_virtual_machines.step b/sites/ruby/using_virtual_machines.step new file mode 100644 index 000000000..ee788e453 --- /dev/null +++ b/sites/ruby/using_virtual_machines.step @@ -0,0 +1,114 @@ +message <<-INTRO +Some workshops ask OS X and Windows users to use a virtual machine. To +run Ruby, you need to start and connect to the virtual machine. +INTRO + +tip do + message "If students aren't using a virtual machine at your workshop, head back to the curriculum." +end + +goals do + goal "Define virtual machine, Vagrant, and VirtualBox" + goal "Find your workspace directory" + goal "Start and halt Vagrant" + goal "Connect to and exit Vagrant" +end + +message <<-CONTENTS +Most laptops don't come with Ruby installed. Getting 75+ people ready at a +workshop is a big logistical challenge. + +To get going quickly and to have things work the same for everyone, we're using +a virtual machine. Using a virtual machine adds some extra steps, which we'll +practice here. +CONTENTS + + +step do + message "First, let's define \"virtual machine.\"" + message "A virtual machine - **VM** for short - is a snapshot of all the + software on a computer. The snapshot is called an image. You downloaded and + saved the image file in the pre-workshop steps." + message "We've made an image of a Linux machine with Ruby and other tools + installed." + end + +step do + message <<-CONTENTS +By itself, a VM image is just a file like any other. It needs something to +run it. + +We're using VirtualBox. If a VM image is a fish, VirtualBox is the aquarium +it lives in. + +It knows how to translate the features of this Linux image into +something Windows or OSX understands, and display it on your monitor. It +knows how to translate your keyboard and mouse input to something the Linux +machine can use. + +If you're familiar with PC Anywhere, Remote Desktop, Parallels, or +VMware Fusion, VirtualBox is a bit like that. Essentially, you're accessing +another computer through your own computer. +CONTENTS +end + + +step do + message <<-CONTENTS +Vagrant is a tool for running a virtual machine. It will start and stop the +VM, and let you connect to it so you can use it. + +During the Installfest you made a folder called "workspace" and ran some vagrant +commands to set up the RailsBridge image. + +It created a configuration file named Vagrantfile. Vagrant needs the config +file in order to run the machine. +CONTENTS +end + +step do + message <<-CONTENTS +To run Ruby on our image, we have to start the VM and then connect to it. The +VM only needs to be started once. It will keep running until you halt it or +reboot your machine. +CONTENTS + console "vagrant up" + result <<-CONTENTS +Bringing machine 'default' up with 'virtualbox' provider... +[default] Clearing any previously set forwarded ports... +[default] Clearing any previously set network interfaces... +[default] Preparing network interfaces based on configuration... +[default] Forwarding ports... +[default] -- 22 => 2222 (adapter 1) +[default] -- 3000 => 3000 (adapter 1) +[default] Booting VM... +[default] Waiting for machine to boot. This may take a few minutes... +[default] Machine booted and ready! +[default] The guest additions on this VM do not match the installed version of +VirtualBox! In most cases this is fine, but in rare cases it can +prevent things such as shared folders from working properly. If you see +shared folder errors, please make sure the guest additions within the +virtual machine match the version of VirtualBox you have installed on +your host and reload your VM. + +Guest Additions Version: 4.2.0 +VirtualBox Version: 4.3 +[default] Mounting shared folders... +[default] -- /vagrant +[default] VM already provisioned. Run `vagrant provision` or use `--provision` to force it +CONTENTS + message "What happens if it's already running?" + console "vagrant up" + result <<-CONTENTS +Bringing machine 'default' up with 'virtualbox' provider... +[default] VirtualBox VM is already running. +CONTENTS +end + +further_reading do + message <<-CONTENTS +"What's virtualization? Why get that nerdy?" Lifehacker explains it all in [The +Beginner's Guide to Creating VMs with +VirtualBox](http://lifehacker.com/5204434/the-beginners-guide-to-creating-virtual-machines-with-virtualbox) +CONTENTS +end diff --git a/sites/ruby/variables.step b/sites/ruby/variables.step new file mode 100755 index 000000000..8f59e2b9b --- /dev/null +++ b/sites/ruby/variables.step @@ -0,0 +1,104 @@ +message "A variable is a way to name a piece of data so we can do things with +it. It can hold all kinds of things - words, numbers, the result of a function, +and even a function itself." + +goals do + goal "Understand what a variable is for" + goal "Store data in variables" + goal "Replace data in an existing variable" +end + +step do + message "Start up irb." + console "irb" +end + +step do + irb "my_variable = 5" + message "This creates a new variable called `my_variable` and stores the value 5 in it." + message "Let's confirm that. To see what value a variable holds, type its name." + irb "my_variable" + result "5" + + irb "another_variable = \"hi\"" + + message "This creates another variable and stores the value \"hi\" in it." + +end + +step do + irb "my_variable = 10" + message "`my_variable` has a new value." + irb "my_variable" + result "10" +end + +step do + irb <<-IRB +apples = 5 +bananas = 10 + 5 +fruits = 2 + apples + bananas +bananas = fruits - apples + IRB + message "Variables are assigned a value using a single equals sign (=)." + message "The right side of the equals sign is evaluated, and the result is + assigned to the variable named on the left side of the equals." + end + +step do + message "You've created a lot of variables. Let's see the list." + irb "local_variables" + result "[:bananas, :fruits, :apples, :another_variable, :my_variable]" + message "Here Ruby gives you a list of variables you've defined in irb so + far. This might be useful for when you want to see what variable names are + already in use." + end +step do + message <<-CONTENT + The opposite of a variable is `constant` - a value that doesn't change. + Numbers, strings, nil, true, and false are constants. + Check out what happens if you assign a value to a constant." + CONTENT + irb <<-IRB + true = false + 1 = 1 + "a" = 1 + IRB + message "(Notice in the last example we put the a in quotation marks to make it a string.)" +end + +step do + message "There are some rules about what you can name a variable." + message <<-VARIABLE_NAMES +Try making variables with the following kinds of names names in irb: + +* all letters (like 'folders') + +* all numbers (like '2000') + +* an underscore (like 'first_name') + +* a dash (like 'last-name') + +* a number anywhere (like 'y2k') + +* a number at the start (like '101dalmations') + +* a number at the end (like 'starwars2') + +Which worked? Which didn't? + VARIABLE_NAMES +end + +step do + message "Variables can hold the result of a function." + irb 'shouting = "hey, you!".upcase' +end + +explanation do + message "Variables allow you to store data so you can refer to it by name + later. The data you store in variables will persist as long as your program + keeps running and vanish when it stops." + end + +next_step "datatypes" diff --git a/sites/ruby/what_is_ruby.step b/sites/ruby/what_is_ruby.step new file mode 100755 index 000000000..8d18fef92 --- /dev/null +++ b/sites/ruby/what_is_ruby.step @@ -0,0 +1,23 @@ +goals do + goal "Discuss what a programming language is" + goal "Discuss some of the characteristics of the Ruby language" +end + +explanation do + message <<-CONTENTS + Every application on your computer was written by somebody, from the operating system to your web browser. Every program was written in some kind of programming language. + + Programming languages come in all shapes and sizes. Some are optimized for the speed at which the computer will execute them, and some are optimized for the speed at which a human can program in them. + + Ruby was invented in the mid-1990s by Yukihiro Matsumoto -- Matz for short. Matz\' goals were to make programming faster and easier. \'Ruby is designed to make programmers happy.\' + + Ruby\'s popularity soared with the invention of the **Ruby on Rails** web framework, but Ruby is useful for many more things than just websites. + CONTENTS +end + +further_reading do + message '[Wikipedia\'s article on Ruby](https://en.wikipedia.org/wiki/Ruby_\(programming_language\))' + message 'If you know another programming language, see [Ruby From Other Languages](https://www.ruby-lang.org/en/documentation/ruby-from-other-languages) for a comparison' +end + +next_step "command_line" diff --git a/sites/ruby/working_with_collections.step b/sites/ruby/working_with_collections.step new file mode 100644 index 000000000..bfbb27808 --- /dev/null +++ b/sites/ruby/working_with_collections.step @@ -0,0 +1,16 @@ +message <<-CONTENTS +We usually work with multiple things - print a list of groceries, add several numbers, etc. + +We often repeat the same action on each thing in a group - frost a dozen cupcakes. + +Ruby provides a couple of containers, plus ways to perform an action on each item in a collection. + +CONTENTS + +goals do + goal "Hold multiple things with arrays and hashes" + goal "Loop over all the members of a group" + goal "Do a function to each member of a group" +end + +next_step 'arrays' \ No newline at end of file diff --git a/sites/testing-rails-applications/additional_concepts.step b/sites/testing-rails-applications/additional_concepts.step new file mode 100644 index 000000000..d064404f2 --- /dev/null +++ b/sites/testing-rails-applications/additional_concepts.step @@ -0,0 +1,65 @@ +message <<-MARKDOWN + All right, you're almost at the end! Below are a few additional concepts of testing that may helpful in your future testing adventures. + + ### Doubles and stubs + Doubles are simpler objects that represent objects from your application. + + ```ruby + orange = double(:orange) + ``` + + If you instantiate that double in your test file, you have access to orange in your tests to test with. This is instead of creating an entire Orange model in ActiveRecord. If you need to create many different test objects with different properties, FactoryGirl is a great gem for that purpose and will allow persistence or in-memory object creation, depending on your testing situation. + + Stubs can be used to dictate what is returned when a method is called on a double. + + ```ruby + orange.stub(:name).and_return("Florida Orange") + ``` + + So, when you write a test that calls the title attribute of the orange double, you'll always get back the string Florida Orange. Got it? Good! + + ### Spies + With spies, we are not talking about espionage... at least, not in relation to testing :) Spies can be used to verify whether a method was called on an object a certain number of times. + For instance (assume you already have the orange double from above): + + ```ruby + orange = spy('orange') + orange.name + expect(orange).to receive(:name) + orange.name + expect(orange).to receive(:name).exactly(2).times + ``` + + Obviously, this is a simplified case. Instead of orange.name, you might have a complicated method that executes many functions internally and that's where spies can come in handy; they can check easily whether one specific method was called. Capiche? Ok, let's keep on trucking! + + ### Webmock + What if your app relies on third-party services or applications, known amongst friends as application programming interfaces or APIs? Well, it seems like APIs should also be tested but should our test suite really be dependent on someone else? NOPE! What if the API goes down? Or is slow? Welcome to the stage: Webmock! + Webmock is a gem that stubs out external HTTP requests. Once you include the gem, bundle install, and include the below code snippet in your spec helper file (which is included in every test file), you're good to go. +MARKDOWN + +source_code :ruby, <<-RUBY + require 'webmock/rspec' + WebMock.disable_net_connect!(allow_localhost: true) +RUBY + +message <<-MARKDOWN + Then, you can start stubbing out API requests in your spec helper file. Let's write an example for Bitly, a service that shortens long URLs. This may come in handy when you want to provide external links to info pages about the different types of oranges in your orange tree but the links are too long to display on a line. +MARKDOWN + +source_code :ruby, <<-RUBY + RSpec.configure do |config| + config.before(:each) do + stub_request(:get, /api.bitly.com.v3.shorten/). + with(headers: {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(status: 200, body: "stubbed response", headers: {}) + end + end +RUBY + +message <<-MARKDOWN + So, if you write any tests in your test files that call the Bitly API, then the response will be whatever you defined above. The test will prevent the actual API request from being made. Pretty cool, huh? + + Awesome, you are now equipped with a license to TEST! Go forth and create doubles, stubs, and spies in your app (at least one of each and have a TA verify). And, if you have time for a final challenge (optional), click below... +MARKDOWN + +next_step "final_challenge" diff --git a/sites/testing-rails-applications/final_challenge.step b/sites/testing-rails-applications/final_challenge.step new file mode 100644 index 000000000..5e1655b92 --- /dev/null +++ b/sites/testing-rails-applications/final_challenge.step @@ -0,0 +1,8 @@ +message <<-MARKDOWN + +Congratulations! Take a second and give yourself a pat on your back. You've come far, my young padawan. This is the final test (no pun intended haha!) :) + +# The final challenge +Essentially, you will clone this repo (https://github.com/akanshmurthy/railsbridge-testfest) that has broken tests and fix all the broken tests. The broken tests will consist of a variety of different types of tests, incorporating everything you have learned thus far. Good luck, keep calm, and test on! + +MARKDOWN diff --git a/sites/testing-rails-applications/img/rails-test-types.png b/sites/testing-rails-applications/img/rails-test-types.png new file mode 100644 index 000000000..3cd2b810d Binary files /dev/null and b/sites/testing-rails-applications/img/rails-test-types.png differ diff --git a/sites/testing-rails-applications/testing-rails-applications.step b/sites/testing-rails-applications/testing-rails-applications.step new file mode 100644 index 000000000..abad6177d --- /dev/null +++ b/sites/testing-rails-applications/testing-rails-applications.step @@ -0,0 +1,48 @@ +message <<-MARKDOWN +### Goal + +To teach you testing we are going to start with the basics and have you learn by doing through small challenges and then a final large. + +When you have completed this curriculum you should understand: + +* what tests are +* why they're used +* how they're used +* what types of tests exist +* what types of frameworks exist +* some additional concepts in testing such as doubles, stubs, spies, and Webmock +* how to write tests +* how to break tests +* how to fix tests + +### Requirements + +We're going to be working with: + +* Ruby on Rails +* A command line program +* A text editor of your choice + +MARKDOWN + +tip "This is not a self-paced curriculum. You should use the discussion sections on each page to make sure everyone is together!" + +message <<-MARKDOWN +### Notable things + + As you might have noticed, we're assuming you've already been to a RailsBridge workshop before or have otherwise already explored a Rails app, and are ready for deeper knowledge. + +MARKDOWN + +important "This curriculum is written for Rails 5. Things will get awkward / broken if you're using an earlier version of Rails, so if you skipped the Installfest, you need to upgrade to Rails 5 now." + +message <<-MARKDOWN +### Tips for everyone + + * When adding code, it's awesome for students to walk through the code line by line and say out loud what is happening. (i.e., "The string is being stored in the instance variable" or "The method `snorgle` is being defined"). If you do it every time, you'll get really comfortable with the vocabulary of Rails! + * Error messages are your friend! Read them carefully, and practice understanding what Rails is telling you. Seeing an error and just diving back into your code is a natural reaction, but stop! Then read, think, and talk about what the error means before fixing it. +MARKDOWN + +insert '../intro-to-rails/working_effectively_and_efficiently' + +next_step "what_are_tests" diff --git a/sites/testing-rails-applications/testing_frameworks.step b/sites/testing-rails-applications/testing_frameworks.step new file mode 100644 index 000000000..5e8710b8a --- /dev/null +++ b/sites/testing-rails-applications/testing_frameworks.step @@ -0,0 +1,133 @@ +message <<-MARKDOWN +# What's a testing framework? +A testing framework is an execution environment for automated tests. Testing frameworks help teams organize their test suites and in turn +help improve the efficiency of testing. + +# Types of testing frameworks +There are many testing frameworks that work great. Minitest is the default testing framework in Rails 5. However, we will be using the RSpec testing framework instead. + + +# RSpec +## How to set up RSpec in Rails + +First, create a new Rails app. + +
    +rails new testapp
    +
    +
    + +Then, add rspec-rails to both the :development and :test groups in the Gemfile: + +```ruby +group :development, :test do + gem 'rspec-rails', '~> 3.4' +end +``` + +Download and install by running: + +
    +bundle install
    +
    +
    + +Initialize the spec/ directory (where specs will reside) with: + +
    +rails generate rspec:install
    +
    +
    + +This command will add the spec helper, rails helper, and .rspec configuration files. + +Use the rspec command to run your specs: + +
    +bundle exec rspec
    +
    +
    + +By default, the above code will run all spec files in the spec directory. + +To run only a subset of these specs use the following command: + +
    +# Run only a specific folder name
    +bundle exec rspec spec/folder_name
    +
    +# Run only specs for a specific type of test such as the post controller
    +bundle exec rspec spec/controllers/post_controller_spec.rb
    +
    +
    + +## RSpec Basics + +```ruby +RSpec.describe Tree do + it "is able to age by 1 year increments" do + orange_tree = Tree.new + orange_tree.age + expect(orange_tree.age).to eq(1) + end +end +``` + +The 'describe' and 'it' methods come from rspec-core. The Tree class would be from your code. You can think of 'describe' as a header to describe which class you are testing and 'it' as a string/subheader that states what specifically you are testing in the Tree class. Note: you may or may not need the RSpec in front of the describe depending on your RSpec version. + +The last line of the example expresses an expected outcome. If orange_tree.age == 1, then the example passes. If not, it fails with a message like: + +
    +
    +expected: #< Tree @age=1 >
    +     got: #< Tree @age=0 >
    +
    +
    + +## Matchers +Remember our example in the RSpec Basics section above? The 'to eq' is a matcher! RSpec has many built-in matchers. You can think of them as ways to equate or check certain values or expressions to what you think or expect they would "match" to. In our example, we are saying that we expect orange_tree's age to equal an integer of 1. + +Check out the other built-in matchers! +https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + +# minitest +In case you were curious to see how tests are written in minitest. Minitest also allows you to write tests in 'expectation style' which is very similar to how RSpec tests are written. + +```ruby +class TestTree < Minitest::Test + def setup + @orange_tree = Tree.new + @orange_tree.age + end + + def test_age_by_one_year_increments + assert_equal 1, @orange_tree.age + end +end +``` + +To run the test enter the following in Terminal/Command Prompt: + +
    +
    +ruby tree_test.rb
    +
    +
    + +Once you run the test this is how the test looks like: +
    +
    +$ ruby tree_test.rb
    +Run options: --seed 30102
    +
    +#Running:
    +
    +.
    +Finished in 0.000980s, 1020.4082 runs/s, 1020.4082 assertions/s.
    +1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
    +
    +
    + +MARKDOWN + +next_step "types_of_tests" diff --git a/sites/testing-rails-applications/types_of_tests.step b/sites/testing-rails-applications/types_of_tests.step new file mode 100644 index 000000000..8cd8c7539 --- /dev/null +++ b/sites/testing-rails-applications/types_of_tests.step @@ -0,0 +1,148 @@ +message <<-MARKDOWN + Now, that you know what tests are, let's discuss the different types of tests! + + In your Rails app, you have models, views, and controllers. It should be no surprise that tests can be written for models, views, and controllers. + + As you learned in the previous section, tests are used to verify that your code is working as expected. So, a couple things we can test right off the bat are that a tree should have certain associations and validations. Let's start by writing some model tests also known as unit tests! This link might come in handy to remember singular vs. plural Rails conventions: https://alexander-clark.com/blog/rails-conventions-singular-or-plural/ +MARKDOWN + +steps do + step do + message "First, create a orange model. By creating the model, a spec file will also be added to the models folder of the spec folder. Type this in the terminal:" + + console_without_message "rails generate model Orange" + + message "Then, run the migration to actually create the oranges table." + + console_without_message "bundle exec rake db:migrate" + end + step do + message "Then, run rspec." + + console_without_message "bundle exec rspec" + + message "You will see a report with one pending test. When you generated your Orange model, RSpec also generated a matching spec file. Copy the test below into spec/models/orange_spec.rb and run 'bundle exec rspec' on the terminal again." + + + source_code :ruby, <<-RUBY + RSpec.describe Orange, :type => :model do + context 'ActiveRecord associations' do + it 'belongs to tree' do + expect(Orange.reflect_on_association(:tree).macro).to be (:belongs_to) + end + end + end + RUBY + + message "Run 'bundle exec rspec'. This test fails! Let's add an associaton to our model. Add a belongs_to association to orange.rb:" + + source_code :ruby, <<-RUBY + class Orange < ActiveRecord::Base + belongs_to :tree + end + RUBY + + message "Run 'bundle exec rspec' and now you should see one passing test! That's an example of an association test. Let's modify that test to fail. Then, run 'bundle exec rspec' and see what happens. Cool! Let's revert back to the passing test." + end + + step do + + message "Now let's write a has_many association test for the relationship between the Tree model and the Orange model! (hint: this doesn't exist yet so you'll have to create the model and migrate!)" + + message "On to controller tests! Just like the Orange model, you will create the OrangesController, which will also create spec files in the /spec/controllers folder of your app." + end + + step do + message "First, create the oranges controller and relevant spec files by typing this in the terminal:" + + console_without_message "rails generate controller Oranges" + end + step do + message "Then, run RSpec." + + console_without_message "bundle exec rspec" + + message "You should see a report with some passing tests but those are just the model tests you wrote. So, let's add some controller tests! Copy the below test, paste it into the oranges controller spec file, create the relevant view files and then run 'bundle exec rspec' on the terminal again." + + source_code :ruby, <<-RUBY + RSpec.describe OrangesController do + context '#index' do + it "renders the index view" do + get :index + expect(response).to render_template("index") + end + + it "renders html" do + process :index, method: :get + expect(response.content_type).to eq "text/html" + end + end + end + RUBY + + message "You should see two failing tests. Hint: You'll need to add a route, index action, and a view. Not sure where to start? Read the errors in your failing tests for a hint. Run 'bundle exec rspec' after each change until both tests pass." + + message "Note: as you write the controller tests, you may be prompted to install a missing gem called 'rails-controller-testing' to use the assert_template method. If prompted, please add it to your Gemfile and do a bundle install!" + end + + step do + message "Now, write another controller test for the new action (hint: you might need to look up what a mock is)." + end + + step do + message <<-MARKDOWN + Last but not least: view tests! Below is an short snippet of a possible Orange show HTML page you can create in your app: + MARKDOWN + + source_code :erb, <<-ERB +
    + Orange title: <%= @orange.name %> +
    +
    + Orange tree id: <%= @orange.tree_id %> +
    + ERB + + message <<-MARKDOWN + So, based on the orange you create, the show page should render HTML with the orange's name and tree id. Let's verify that with a few tests. + MARKDOWN + + message "First, create a views folder in the spec folder. Then, create an oranges folder in the views folder. Lastly, create an oranges view spec file in the oranges folder. Type these commands in the terminal:" + + console_without_message "mkdir spec/views" + console_without_message "mkdir spec/views/oranges" + console_without_message "cd spec/views/oranges" + console_without_message "touch show.html.erb_spec.rb" + end + step do + message "Change directory back to the root directory of your app. Then, run rspec." + + console_without_message "cd ../../.." + console_without_message "bundle exec rspec" + + message "You should see a report with some passing tests but those are just the model and controller tests you wrote. So, let's add some view tests!" + message "We're going to up the ante a bit here and NOT show you an example :) Google and StackOverflow are your friends here!" + end +end + +message <<-MARKDOWN + Once, you have written some passing view tests, take a deep breath and pat yourself on the back! Testing is hard. But, it's critical in making sure software is stable and functional. + + So, I fibbed a little bit. There are more types of tests than just MVC tests. One of these other important tests is called an integration test. As the name indicates, it tries to assess how well multiple components in an app interact and is written in Rails as a feature spec test. Typically, these tests simulate a user and a user's actions to test end-to-end functionality. + + Here's a diagram from Thoughtbot that may help with understanding how integration tests fit in: + +MARKDOWN + +img src: "img/rails-test-types.png", alt: "Thoughtbot's diagram of types of Rails tests" + +message <<-MARKDOWN + Capybara is a common framework used to write integration tests in Rails. It integrates nicely with RSpec such that you can use the same kind of test language to write integration tests. + + As an added bonus for this section, write an integration test :) + + Use the following link to get started: https://www.sitepoint.com/basics-capybara-improving-tests/ + +MARKDOWN + +next_step "additional_concepts" diff --git a/sites/testing-rails-applications/what_are_tests.step b/sites/testing-rails-applications/what_are_tests.step new file mode 100644 index 000000000..de61e16a6 --- /dev/null +++ b/sites/testing-rails-applications/what_are_tests.step @@ -0,0 +1,25 @@ +message <<-MARKDOWN + +Tests are not exams like the ones you have to take in school. Rather, tests are ways of testing code to see if it is performing the way we expect it to behave. Pretty easy, right? + +# Example + For example, let's say we wanted to create a program that models an orange tree because nature is awesome. Well, what defines an orange tree to us? Is it safe to say that we need at least two objects, an orange object and a tree object? Can our tree mature and bear more fruit at a certain age? Could our oranges ripen and fall from the tree? Sounds like the list can become lengthy, right? Let's stop here for a few minutes and see what we need to fulfill these user stories. + +# What We Need To Test? +Remember that we need to model an orange and a tree. So, we know that there are two objects that need to be created. + +We could say that the tree will not bear fruit until it matures at one year of age and then it will bear X number of oranges. We would need a test to test if the tree ages and a test to see if the tree has created X number of oranges once it has matured. + +How do we determine if an orange is ripe? Well, we can have an orange age and if it is at least 30 days old, then it is ripe and will fall from the tree. We would need to write a test to check for ripeness and a test to check if it falls at ripeness. + +# Why Is It Important That We Test? +You could think of tests as a requirement list. Every time a change is made we want to test to see if our code still meets those requirements. For example, as our program becomes increasingly complex and we want to have our tree to have a certain lifespan, would it make sense for our tree to continue to create oranges after exceeding that lifespan? + +A situation like our tree's lifespan is a prime example of why tests are important. As our program becomes more complex over time with new code added, the tests tell us that the new code negatively affected prior expectations of the behavior of the code. Tests can help keep the original behavior of the code and prevent new bugs from appearing. + +# Test Driven Development +Test-driven development (TDD) is a development technique where you must first write a test that fails before you write new functional code. TDD is a great way to develop your program! Add a test, run all tests, write code, run tests, and then refactor code! + +MARKDOWN + +next_step "testing_frameworks" diff --git a/sites/workshop/activities.md b/sites/workshop/activities.md new file mode 100644 index 000000000..7b31a627c --- /dev/null +++ b/sites/workshop/activities.md @@ -0,0 +1,139 @@ +These activities are mostly taken from [Sharon Bowman](http://bowperson.com/)'s excellent books: [The Ten-Minute Trainer](http://www.amazon.com/Ten-Minute-Trainer-Teach-Quick-Stick/dp/0787974420/) and [Training From the Back of the Room](http://www.amazon.com/Training-Back-Room-Aside-Learn/dp/0787996629/). For each activity, **print out instructions and put them up on a wall**. These are ideas and options: don't try and do them all! :) + +# Warm-up exercises (a week before the workshop) + +## What you know, and what’s your plan + +Send students an email, asking them two of these questions. + +* What are three things you already know about programming? (Small things or big things! Who does it? What can you make? Why do people write software?) +* What do you want to learn? +* What do you plan to do with what you learn? + +# Just before the workshop begins + +## Share your answers + +* Grab a pen and sticky notes. +* Write down your answers to the warm-up questions. +* Swap your answers with someone else. +* Swap again with someone new. +* Put the sticky notes up on the wall. + +## What's in it for me? + +* Grab a pen and sticky notes. +* Write down what you want to learn today. +* Find someone new and share your answer with them. +* Put the sticky notes up on the wall. +* Come back to them at the end of the day. + +## What does a developer look like? + +* Grab a pen and sticky notes. +* Write down answers to: "What does a developer look like?" +* Take a card from the table and find out the answer. +* Compare your earlier answers to the person you found on the internet. + +The cards are clues to finding developers (e.g. who wrote "Designing for Performance"). The answers are developers who are from groups of people that are underrepresented in tech. + +## What does an engineer look like? + +* Grab a pen and sticky notes. +* Write down answers to: "What does an engineer look like?" +* Look up #ILookLikeAnEngineer and find an awesome person there. +* Introduce them (and yourself!) to someone in the room. + +## What makes a good Web App? + +* Grab a pen and sticky notes. +* Write down at least three things that a good web app needs. + +## Find Your Match + +* Take an index card from the table. +* Find someone with the same card as you. +* Together, find out what the technical term means. +* One of the back of one of the cards, write the explanation down, and stick the cards up on the wall for other to read. + +The cards have tech terms on them, such as: HTML, CSS, JavaScript, front-end, Ruby, Rails, Model, View, Controller, MVC. Alternate version: cards have just acronyms: HTML, CSS, JS, MVC, DB, SQL. + +## Match It Up + +* With a partner, grab a deck of index cards. +* Match up cards with their definitions. +* Check your answers. + +The deck contains: + +* Ruby +* Rails +* Git +* Heroku +* Sublime Text +* Terminal / Command Prompt +* Browser + +These are in the opening presentation, and students have been using these things during the InstallFest. This activity helps them see how their learning is progressing. + +# During the workshop + +## Good, bad, change + +* Grab a pen and a card. +* Write down **one good thing**. +* Give your card to someone and take a card with one good thing on. +* Write down **one bad thing**. +* Give your card to someone and take a card with one bad thing on. +* Write down **one thing you would change**. + +This can be for the InstallFest or the workshop in general. + +## Pass That Card + +* Grab a pen and a card. +* Write down **a question you have**. +* Give your card to someone and take a card with a question on. +* Write down **an answer to the question on the card**. +* Give your card to someone and take a card with an answer on. +* Write down **agree or disagree**. + +## What Three Things + +* Grab a pen and sticky notes. +* Write down three things you've learned this morning, one per sticky note. +* Discuss them with a partner. + +## Take A Stand + +* Stand on a line from not so confident to very confident. +* Pair up with someone from the other end of the line and discuss any problems you've had, and get ideas for solutions. + +## Three things about Ruby or Rails + +* Grab a pen and sticky notes. +* Write down one thing that you can do now. +* Write down one thing you're still unsure about. +* Write down one thing you'd like to learn. + +## Three most important things + +* Write down the 3 most important things you learned so far. +* Discuss your list with 3 people. + +# Ending the workshop + +## Hey, buddy + +* Grab a pen and a card. +* Write down a SMART goal on the front of a card. +* Write down your name and email address on the back of a card. +* Swap cards with a buddy, and agree to email each other during the next week. + +A SMART goal is **Specific**, **Measurable**, **Attainable**, **Relevant**, **Time-bound**. + +## Valuable + +* Grab a pen and a card. +* On your own, write down the most valuable thing you've learned today. +* In groups of 3 or more, discuss your cards. diff --git a/sites/workshop/auth_logic.mw b/sites/workshop/auth_logic.mw deleted file mode 100644 index 91bd4b4a5..000000000 --- a/sites/workshop/auth_logic.mw +++ /dev/null @@ -1,252 +0,0 @@ -=== Authentication === -* Different options for auth -* Thinking about databases as "relational" -** Think about your table of topics - each has a unique numeric identifier (auto-increment primary key) -** We'll create another table that holds user information. Each row in that table also has an identifer -** Then we can set up a relationship between the tables: meeting has-a user, user (may) has-many meetings -** add a user id to the meeting table that we can use to cross-reference the user. -** in Rails, topic->user is called a "has_one" relationship, user->meeting is called a "has_many" relationship. -* So, users! name, email, ... -* TODO: move to install instructions: sudo gem install authlogic -* TODO: create a generator so folks don't have to do so much work for authentication (although the steps below could probably be made shorter by using generate scaffold) -* Now add the gem dependency in your config: (talk about what config/environment.rb ) - - # config/environment.rb - config.gem "authlogic" - - -* generate the user session - - script/generate session user_session - exists app/models/ - create app/models/user_session.rb - -** look at app/models/user_session.rb -* generate the user model - - script/generate model user email:string crypted_password:string password_salt:string persistence_token:string - exists app/models/ - exists test/unit/ - exists test/fixtures/ - create app/models/user.rb - create test/unit/user_test.rb - create test/fixtures/users.yml - exists db/migrate - create db/migrate/20090603045630_create_users.rb - -* edit migration to add :null => false - - class CreateUsers < ActiveRecord::Migration - def self.up - create_table :users do |t| - t.string :email, :null => false - t.string :crypted_password, :null => false - t.string :password_salt, :null => false - t.string :persistence_token, :null => false - t.timestamps - end - end - - def self.down - drop_table :users - end - end - -* edit the user model to add the plugin behavior - - class User < ActiveRecord::Base - acts_as_authentic - end - -*** One thing to note here is that this tries to take care of all the authentication grunt work, including validating your login, email, password, and token fields. -** generate sessions controller - -script/generate controller user_sessions - exists app/controllers/ - exists app/helpers/ - create app/views/user_sessions - exists test/functional/ - exists test/unit/helpers/ - create app/controllers/user_sessions_controller.rb - create test/functional/user_sessions_controller_test.rb - create app/helpers/user_sessions_helper.rb - create test/unit/helpers/user_sessions_helper_test.rb - -** create /views/users directory -** in that directory, create new.html.erb -
    - 

    Register

    - - <% form_for @user, :url => account_path do |f| %> - <%= f.error_messages %> - <%= f.label :email %>
    - <%= f.text_field :email %>
    - -
    - <%= f.label :password %>
    - <%= f.password_field :password %>
    - -
    - <%= f.label :password_confirmation %>
    - <%= f.password_field :password_confirmation %>
    - - <%= f.submit "Register" %> - <% end %> -
    -** edit config/routes.rb - - map.resource :user_session - map.root :controller => "user_sessions", :action => "new" # optional, this just sets the root route - - -** edit application controller - - class ApplicationController < ActionController::Base - helper :all # include all helpers, all the time - protect_from_forgery # See ActionController::RequestForgeryProtection for details - - filter_parameter_logging :password, :password_confirmation - helper_method :current_user_session, :current_user - - private - def current_user_session - return @current_user_session if defined?(@current_user_session) - @current_user_session = UserSession.find - end - - def current_user - return @current_user if defined?(@current_user) - @current_user = current_user_session && current_user_session.user - end - - def require_user - unless current_user - store_location - flash[:notice] = "You must be logged in to access this page" - redirect_to new_user_session_url - return false - end - end - - def require_no_user - if current_user - store_location - flash[:notice] = "You must be logged out to access this page" - redirect_to account_url - return false - end - end - - def redirect_back_or_default(default) - redirect_to(session[:return_to] || default) - session[:return_to] = nil - end - - def store_location - session[:return_to] = request.request_uri - end - - -end - -** user controller - - # config/routes.rb - map.resource :account, :controller => "users" - map.resources :users - -** Create your UsersController: - - script/generate controller users - -** add to user controller - - def new - @user = User.new - - respond_to do |format| - format.html # new.html.erb - format.xml { render :xml => @user } - end - end - - # POST /users - # POST /users.xml - def create - @user = User.new(params[:user]) - - respond_to do |format| - if @user.save - flash[:notice] = 'You successfully registered.' - format.html { redirect_to topics_path } - format.xml { render :xml => @user, :status => :created, :location => @user } - else - format.html { render :action => "new" } - format.xml { render :xml => @topic.errors, :status => :unprocessable_entity } - end - end - end - -** add to UserSessionsController - - class UserSessionsController < ApplicationController - before_filter :require_no_user, :only => [:new, :create] - before_filter :require_user, :only => :destroy - - def new - @user_session = UserSession.new - end - - def create - @user_session = UserSession.new(params[:user_session]) - if @user_session.save - flash[:notice] = "Login successful!" - redirect_back_or_default account_url - else - render :action => :new - end - end - - def destroy - current_user_session.destroy - flash[:notice] = "Logout successful!" - redirect_back_or_default new_user_session_url - end - end - -** add to topic controller - - before_filter :require_user, :only => [:new, :edit, :create, :update] - -** rake db:migrate -** go to: localhost:3000/account/new -*** and register, see that you are at topics_path and show cookies to show that the session is being tracked by a cookie -** add user_sessions view new.html.erb -
    -    

    Login

    - - <% form_for @user_session, :url => user_session_path do |f| %> - <%= f.error_messages %> - <%= f.label :email %>
    - <%= f.text_field :email %>
    -
    - <%= f.label :password %>
    - <%= f.password_field :password %>
    -
    - <%= f.check_box :remember_me %><%= f.label :remember_me %>
    -
    - <%= f.submit "Login" %> - <% end %> -
    -** add to topics layout - - <% if !current_user %> - <%= link_to "Register", new_account_path %> | - <%= link_to "Log In", new_user_session_path %> | - <% else %> - <%= link_to "Logout", user_session_path, :method => :delete, :confirm => "Are you sure you want to logout?" %> - <% end %> - -* make sure we save our progress -** git commit -a, commit message, save and close -** git push heroku master diff --git a/sites/workshop/command_prompt.mw b/sites/workshop/command_prompt.mw index b840b2b8f..29eccb110 100644 --- a/sites/workshop/command_prompt.mw +++ b/sites/workshop/command_prompt.mw @@ -4,7 +4,7 @@ There are several ways of opening a terminal window. You'll be using it often, === Windows 7 === * Click the Windows Icon. In the "Search programs and files" field, type: cmd and press the [enter] key -[[File:Win7 search programs.jpg]] +[[File:img/Win7_search_programs.jpg]] == Creating a terminal shortcut icon on the Taskbar == === Windows 7 === diff --git a/sites/workshop/current.deck.md b/sites/workshop/current.deck.md deleted file mode 100644 index 1b851e99f..000000000 --- a/sites/workshop/current.deck.md +++ /dev/null @@ -1,45 +0,0 @@ - -# Welcome to RailsBridge #38 -* OMG - - -# Thank you to our lovely sponsors -![EngineYard](img/ey_logo_rgb.png) - - -# Thank you to our lovely sponsors -![Blazing Cloud](img/blazingcloud.png) -![NIRD](img/nird.png) - - -# And thank you to Nick and Flannery! -![Nick](img/nicksieger.jpg) -![Flannery](img/flannery.jpg) - - -# Schedule -* 9:00-9:30 Introductions & Coffee -* 9:30-10:00 Workshop Intro -* 10:00-10:50 Session 1 -* 11:00-12:00 Session 2 -* 12:00-1:00 Lunch -* 1:00-2:15 Session 3 -* 2:30-3:30 Session 4 -* 3:30-4:00 Wrap up & final questions -* 4:00 Grab a drink (or 2 or 3?) at ? - - -# Who is here? -![Student Survey](img/student_survey.png) - - -# Who is here? -![Student Experience](img/student_experience.png) - - - -# 3 Groups -* one will go slowly through programming concepts -* two will focus on what is different about Ruby and Rails - - diff --git a/sites/workshop/curriculum.mw b/sites/workshop/curriculum.mw deleted file mode 100644 index 7075be4b1..000000000 --- a/sites/workshop/curriculum.mw +++ /dev/null @@ -1,693 +0,0 @@ -== Build and deploy a web application == -We will build a web application for people to create topics and vote on them. Ultimately, we'll have registration and authentication and a variety of other features. Today we'll focus on building the core features of topics and votes. - -Here is the UI design for the application: - -[[File:Suggestotron_topics_index.jpg]] - -=== Let's build a web application === -* Open the terminal or command line -* Create a folder for our programming stuff - - mkdir rubyonrails - -* Change into that directory - - cd rubyonrails - -* Create a new web application! -'''Mac and Linux users type:''' - - curl -O https://raw.github.com/gist/1143443/f4db3f9aa41ebb0726b98fe783e01f02e0f5ba09/workshop_template.txt - rails new suggestotron -m workshop_template.txt - -'''Windows users type:''' - - rails new suggestotron -Tm http://ram9.cc/863314.txt - - -IF you don't have curl installed then click on the link, download the txt file, save it locally and then instead of the above command do the following command: - - rails new suggestotron -m workshop_template.txt - - -Everything you need for your application is now in the suggestotron folder. If you decide you don't like it, just delete it and start over with the rails new command. - -The -T option (T for Test) is a shortcut to tell the rails command to not use Test::Unit because we will be using RSpec instead. -The -m option (m for teMplate) is a shortcut for a few commands (listed [http://gist.github.com/609269.txt here]). Mostly it sets up cucumber features and installs the gems you need to run them. - -Normally, you probably wouldn't use a template. Eventually though you might create your own template with your frequently used options. - -Next step: Go to your new folder: - - cd suggestotron - - -To see what the rails command created for you, type the following: - - ls - -(That's a lower-case L, for "list.") - -rails new created the following files and subdirectories within it: - -{| border="1" cellspacing="0" cellpadding="3" align="center" -! File/Folder -! Purpose -|- -| Gemfile -| This file lists the gems that bundler will install in your project. -|- -| Gemfile.lock -| This file lists the gems that were installed for your project. -|- -| README -| This is a brief instruction manual for your application. -|- -| Rakefile -| This file contains batch jobs that can be run from the terminal. -|- -| app/ -| Contains the controllers, models, and views for your application. You will do most of your work here. -|- -| config/ -| Configure your application’s runtime rules, routes, database, and more. -|- -| config.ru -| This file makes our app rack-friendly (.ru stands for rackup). -|- -| db/ -| Shows your current database schema, as well as the database migrations. -|- -| doc/ -| You would add documentation for your application here -|- -| features/ -| Added by the template. This is from cucumber, which is not part of core Rails -|- -| lib/ -| Extended modules for your application (not covered today) -|- -| log/ -| Application log files -|- -| public/ -| The only folder seen to the world as-is. This is where your images, JavaScript, stylesheets (CSS), and other static files go -|- -| script/ -| Scripts provided by Rails to do recurring tasks. We'll use some today -|- -| spec/ -| Unit tests, fixtures, and other test apparatus -|- -| tmp/ -| Temporary files -|- -| vendor/ -| A place for third-party code -|- -|} - -=== Set up your web application === -If you haven't already, change to the suggestotron directory by typing: - - cd suggestotron - - -Run the web app server: - - rails server - - -Windows 7 Users: -If you get an Invalid gemspec in c:/...mingw32.gemspec]: invalid date format in specification: "2011-08-20 00:00:00.000000000z" error: -# Open the .gemspec file for editing. The path/filename is reported in the error message. -# Remove the time portion 00:00:00.000000z of the date field in the .gemspec file. -# Save the changes and relaunch the rails server. - -Point your web browser to [http://localhost:3000 http://localhost:3000]. See your web app actually there! - -== Make it your own == -* Start Komodo Edit -* Project -> New Project... -* Select your new "suggestotron" directory -* Name the project file "suggestotron.komodoproject" - -If you don't see a list of files and folders to the left, then go to: -* View -> Tabs & Sidebars -> Projects - -==== Edit index page ==== -The page you are seeing at http://localhost:3000 is a static page, located at public/index.html. - -Make a change, save, and reload your browser to see it! - -=== Awesome! Let's ship it! === -From your new "suggestotron" directory, create a local git repository: - -
    -git init
    -
    - -This tells git that we want to start tracking the changes to this directory. - -=== A little note about gitignore === - -Whenever we commit a project, git tracks every file by default. There are, however, files that we'd rather leave untracked (or, ignored). -There is a hidden file in your app called "gitignore" that has a period as its first character in its filename to hide it from your normal directory view (this is why you didn't see it if you looked at your directory list earlier) -The entries in the "gitignore" file tell git what to avoid when tracking the changes of our project files. - -If you open the ".gitignore" file, you'll see this: -
    -.bundle
    -db/*.sqlite3
    -log/*.log
    -tmp/**/*
    -
    - -=== Making the first commit === - -After we've done that, we can proceed with our first commit by entering the following lines into the terminal: -
    -git add .
    -git commit -m "basic web application"
    -
    - -This saves a snapshot of the application to git. Let's check to make sure that everything was committed. - - - git status - git log - - -git status tells you whether you've made changes that haven't been saved in git. Currently it should show you something like this: - - # On branch master - nothing to commit (working directory clean) - - -git log shows you a history of all your commits. You should see one entry - the commit you just made. - -To exit git log, hit the letter 'q'. - -Once you've checked that, it's time to deploy to Heroku! -
    -heroku create
    -git push heroku master
    -
    - -Notice the URL that heroku reports after you typed "heroku create." Type this URL in your browser to see your application on the web. If you don't want to do that, the command heroku open does this for you by opening the browser. - -== A Closer Look at the Features == -* '''If you are using rails 3.1.0''' or greater and you get a postgresql error after pushing to heroku, then add the following to your Gemfile so that Rails can work with heroku: - -
    -group :production do
    -  gem 'therubyracer-heroku', '0.8.1.pre3'
    -  gem 'pg'
    -end
    -
    - -* Then in your terminal, type the following commands: - -
    -bundle install --without production
    -git add .
    -git commit -m "heroku postgresql workaround"
    -git push heroku master
    -
    - -* '''Windows Users! As of December 5, 2010''', there is a bug in cucumber that prevents the test from running properly. To fix this, open up '''features/support/env.rb''' and comment out line 19. (require 'cucumber/rails/capybara_javascript_emulation' # Lets you click links with onclick javascript handlers without using @culerity or @javascript) - -* First time project setup in Windows 7, execute these two commands: (this appears to eliminate the bug referenced above) If prompted, select "a" for overwrite "all". - -
         bundle install
    -     rails generate cucumber:install --rspec --capybara
    - - -In the /features directory are several files that end in .feature. These are cucumber features that describe the application we're building. - -You can find the first piece of the app that we'll build in features/1_topics.feature: -
    -Feature: Topics
    -  In order to see a list of potential topics for meetings
    -  people need to be able to create and edit them
    -
    -  Scenario: Getting to the new topic page
    -    When I go to the topics page
    -    And I follow "New Topic"
    -    Then I should see a "Create" button
    -
    - -It's got several "scenarios" in the file. You can run all the scenarios in topics.feature like this: - -rake cucumber FEATURE=features/1_topics.feature - -NOTE: -* For some OS X users, this may cause a test failure due to a Lib2XML conflict, which can be resolved by commenting out Line 19 in the features/support/env.rb file: # require 'cucumber/rails/capybara_javascript_emulation' -* if you get this error: "no such file to load -- cucumber/rails/active_record (LoadError)", please update your Gemfile, by replacing a line that mentions "cucumber-rails" with: - - gem 'cucumber-rails', :git => 'git://github.com/cucumber/cucumber-rails.git' - -Then re-run "bundle install" and "rails generate cucumber:install --rspec --capybara" (select "a" for overwrite all"). - -=====Once you've run it===== -Once you are able to run it, the first thing you'll notice is that it fails! That's '''good''', because we haven't written any code yet. - -These features are tests as well as documentation. The typical Rails workflow is: -# run the test -# watch it fail -# implement the feature -# run the test again to see if it passes. - -This is the workflow we'll be using today. - -1_topics.feature relies on some basic elements of a web app, which is what we'll build first. As we get features to pass, we'll look further at the features definitions to see what needs to be built. - -== First Feature: Adding topics == - -We can look through the features and screen shots and see how a topic is defined and how people expect to interact with it. We will use rails "scaffolding" to generate some pages and code. - -* topic will have a title and a description -* we will enable basic "CRUD" actions: -**'''C'''reate - enter a new topic -**'''R'''ead - see everyone's topics -**'''U'''pdate - change the topics you entered -**'''D'''elete - remove your topics from the system - -=== Scaffolding === - -* Run this command line script to generate a Topic model along with the application logic and views: - - rails generate scaffold topic title:string description:text - -* Holy generated files, Batman! - -* Open the migration file (yours will have a different number): - - db/migrate/20091014021209_create_topics.rb - -It contains Ruby code to set up the database table. Migrations can also be used for modifying tables (add/remove/rename columns) and even modifying data. - -* Now let's set up our development and test databases by running the migration file: - - rake db:migrate - - -* Run the cucumber feature again (the first scenario should pass, but the second will fail) - - rake cucumber FEATURE=features/1_topics.feature - - -* If you want to just see the passing scenario, type this instead: - - rake cucumber FEATURE=features/1_topics.feature:5:8 - -This command tells cucumber to only run lines 5 through 8 in your story, which is currently passing our tests. - -Now let's look at the feature we created by running the server locally. -* Start your server: - - rails server - - -* And point your browser to [http://localhost:3000/topics http://localhost:3000/topics] - -Congratulations! You have built a web application that works with a relational database. - -=== A Quick Look at Your Database === -You can access the database directly on your local machine. For this class we’re using SQLite, and we've installed SQLite Manager, a GUI tool that lets you inspect the database. To open it, open Firefox, then select "Tools -> SQLite Manager." - -Click the open folder icon [[File:Folder.jpg]]  or choose '''Database -> Connect Database''' to open suggestotron/db/development.sqlite3 - -Windows Users: You may have to change the file type selection field to "All Files" to be able to see files with the sqlite3 file extension. - -[[File:Sqllit.jpg]] - -In SQLite, you can choose “Browse & Search” to interactively explore the database or “Execute SQL” to type SQL commands. - -You can also access the database through the command line: - - rails dbconsole - - -{| border="1" cellspacing="0" cellpadding="5" align="center" -|+Common SQL Commands -! -! sqlite -! MySql -! PosgreSQL - -|- -| list tables in current db -| .tables -| show tables; -| \d -|- -| show SQL for table create list columns -| .schema -.schema topics -| show create table topics; -describe topics; -| \d topics -|- -| exit command line tool -| .quit -| exit -| \q -|- -| show all rows in table -| colspan="3" | select * from topics; - -|- -| show number of rows -| colspan="3" | select count(*) from topics; - -|- -| show matching record -| colspan="3" | select * from topic where title = "My Topic"; -|} - -=== Deploy Your Application === - -Don’t forget, "commit early, deploy often." Here's how: -
    -git add .
    -git commit -m "topic crud"
    -git push heroku master
    -heroku rake db:migrate
    -
    - -If you run into error, '''uninitialized constant Rake::DSL''' then add following to your Rakefile: -
    -require 'rake/dsl_definition'
    -require 'rake'
    -
    - -Congratulations! You have built '''and deployed''' a web application that works with a relational database. - - -Rails implements a very specific notion of the '''Model-View-Controller''' pattern, which guides how you build a web application. - -'''Model''' -* represents what is in the database -* ActiveRecord, ActiveModel - -'''View''' -* the model rendered as HTML -* ActionView, erb - -'''Controller''' -* receives HTTP actions (GET, POST, PUT, DELETE) -* decides what to do, such as rendering a view -* ActionController - -[[File:Mvc.jpg]] - -[[File:Mvc-stickman.png]] - -When you executed the rails generate scaffold command, Rails generated files that implement a model, some views, and a controller for topics. - -'''The Model''' -* create app/models/topic.rb -* create db/migrate/20090611073227_create_topics.rb - -'''Five Views''' -* create app/views/topics/_form.html.erb -* create app/views/topics/index.html.erb -* create app/views/topics/show.html.erb -* create app/views/topics/new.html.erb -* create app/views/topics/edit.html.erb - -'''The Controller''' -* create app/controllers/topics_controller.rb -* route map.resources :topics - -=== A closer look === -Rails allows you to easily invoke an interactive console with all of the Rails libraries and your application code loaded: -
    -    rails console
    -
    - -Let's add in some logging so that we can see what is happening in the database: -
    -ActiveRecord::Base.logger = Logger.new(STDOUT)
    -
    - -Let’s open the console and look at the model that is defined here: app/models/topic.rb -
    ->> t = Topic.new
    -=> #
    ->> t.title = "My topic"
    -=> "My topic"
    ->> t.description = "this is really cool"
    -=> "this is really cool"
    ->> t.save
    -
    - -Notice that the Topic class has title and description attributes which you did not need to explicitly declare in the class. This is handled by ActiveRecord which implements ORM (Object Relational Mapping) in Rails. - -== Routes == -Rails routes control how URLs map to code. We can use rake routes to list the relationships between routes and controllers. -
    -$ rake routes
    -    topics GET    /topics(.:format)          {:action=>"index", :controller=>"topics"}
    -    topics POST   /topics(.:format)          {:action=>"create", :controller=>"topics"}
    - new_topic GET    /topics/new(.:format)      {:action=>"new", :controller=>"topics"}
    -edit_topic GET    /topics/:id/edit(.:format) {:action=>"edit", :controller=>"topics"}
    -     topic GET    /topics/:id(.:format)      {:action=>"show", :controller=>"topics"}
    -     topic PUT    /topics/:id(.:format)      {:action=>"update", :controller=>"topics"}
    -     topic DELETE /topics/:id(.:format)      {:action=>"destroy", :controller=>"topics"}
    -
    -
    - -Each method in the controller will take an HTTP request, usually find some data in the database (via an ActiveRecord model) and render a view or redirect to another action. - -See [[Workshop_Diagrams]] to help trace the connections between HTTP, REST, routes, controllers, and databases. - -=== How to explore your routes === -Open up your rails console: - - rails console - -Now you can have a look at the paths that are available in your app. Let's try looking at one of the topics routes we just generated. - - app.topics_path - => "/topics" - app.topics_url - => "http://www.example.com/topics" - - -=== Remapping / === - -You can specify what Rails should route "/" to with the '''root''' method in routes.rb. You should put the root route near the end of the file, but before the final "end". - - root :to => 'topics#index' - -After adding the root route, your routes.rb file should look like this: - - Suggestotron::Application.routes.draw do - resources :topics - root :to => 'topics#index' - end - -You also need to delete the index.html file for the root route to work. - - git rm public/index.html - -== Next feature: creating a topic == - -Let's run the feature again and see what the next scenario is. - - rake cucumber FEATURE=features/1_topics.feature - -
    -  Scenario: Creating a topic
    -    Given I go to the topics page
    -    And I follow "New Topic"
    -    When I fill in "Title" with "Rails Fixtures"
    -    And I fill in "Description" with "Introduce how to add test data with fixtures."
    -    And I press "Create"
    -    Then I should see "Rails Fixtures"
    -    And I should be on the topics page
    -
    - -Run the server (rails server), look at the app ([http://localhost:3000/topics/ http://localhost:3000/topics]), and see how the scaffold template differs from the desired application as we've described it above using cucumber. What we want is the application to show the list of topics after the user creates a topic by completing the form. However, the scaffold displays instead a page showing only the individual topic the user just created. - -=== Controller: adjusting the flow of your application === -We looked at the scaffold-generated code a little earlier. To change the behavior and make the feature act as desired, we will look more closely now at the controller, which controls the general flow of your application; for instance, which page is displayed when the user clicks a link or a button. - -* Open app/controllers/topics_controller.rb and look at the new and create actions. - -* Notice that in create there is a redirect to a single topic, - format.html { redirect_to(@topic) } -We want to redirect instead to the list of topics: - format.html { redirect_to(topics_path) } # <-- note no @ - -* As of December 5, 2010, there is a bug in cucumuber that prevents the test from running properly. To fix this, open up features/support/env.rb and comment out line 19 (if you haven't done that already). - -* Now run your feature again with cucumber and it should pass - rake cucumber FEATURE=features/1_topics.feature - -=== Commit & Push === -Congratulations! This is definitely a checkpoint. Time to commit to git and push your changes out to Heroku. - - git add . - git commit -m "ZOMG first feature passes!!1!!11" - git push heroku master - - -(Run ‘heroku rake db:migrate --app ’ to bring over changes in the migration files under Feature 1.) - -== Next feature: the Topics page == - -Note that for this next set of feature scenarios we have a "Background" set of steps which will be executed before each scenario in the file. Since they depend on behavior that already works, they're rendered in green (pass). - -
    -  Scenario: Viewing a topic detail page
    -    When I go to the topics page
    -    And I follow "Rails Fixtures"
    -    Then I should see "Introduce how to add test data with fixtures."
    -    And I should not see "New Topic"
    -
    - -* Run the scenario and watch it fail: - rake cucumber FEATURE=features/2_topics_list_and_details.feature - -* In the last scenario we made it so the app redirected to "topics_path" after create. So, we expect that we will need to fix the error in that page. We can use rake routes to find the controller action, then we can see that the default view is rendered. The error is in app/views/topics/index.html.erb: - <%= link_to topic.title, topic %> - -* Run the scenario again and see that the "Clicking on the topic title" passes, but we still have one failure which can be addressed in the same file -* Open app/views/topics/index.html.erb and change "Destroy" to "Delete". - -=== Commit & Push === -Another checkpoint! Time to commit to git and push your changes out to Heroku. - - git add . - git commit -m "Topic titles are now links. for srs." - git push heroku master - - -* '''If you're getting an error''' when you go to your Heroku application, run one more command - -
    bundle exec rake assets:precompile
    - -Be sure to commit your changes and push your changes to Heroku - -== Next feature: allow voting on a topic == -
    -Feature: Votes
    -  In order to determine which talk to give
    -  people need to be able to vote for the ones they like
    -  
    -  Background: Make sure that we have a topic
    -    Given I go to the topics page
    -    And I follow "New Topic"
    -    And I fill in "Title" with "Rails Fixtures"
    -    And I fill in "Description" with "Introduce how to add test data with fixtures."
    -    And I press "Create"
    -    
    -  Scenario: viewing votes already cast
    -    When I go to the topics page
    -    Then I should see "0 votes"
    -    
    -  Scenario: voting on a topic
    -    When I go to the topics page
    -    And I follow "+1"
    -    Then I should see "1 vote"
    -
    - - rake cucumber FEATURE=features/3_votes.feature - -=== How will we build this feature? === - -* Each vote will be an object (row in database table) -* When someone votes on a topic, we'll create a new vote object and save it -* Each vote is associated with a specific topic - -=== Rails associations === - * Topic has_many :votes - * Vote belongs_to :topic - -[[File:Rails-associations.jpg]] - -=== Add votes === - -We will use the resource generation script to create a model and controller (no views): - - rails generate resource vote topic_id:integer - -And then we migrate - - rake db:migrate - -The script creates files with: -* model (including migration, unit, fixture) -* controller (and route) with no code - -Add code to to your models to create the associations: -*/app/models/topic.rb -
    - class Topic < ActiveRecord::Base
    -   has_many :votes
    - end
    -
    -*/app/models/vote.rb -
    - class Vote < ActiveRecord::Base
    -   belongs_to :topic
    - end
    -
    - -Check it out in rails console: - - >> t = Topic.new(:title => "My Topic") - => # - >> t.votes - => [] - >> t.votes.build - => # - >> t.votes - => [#] - >> t.save - => true - >> t.votes - => [#] - -Now you can use it in your view (/app/views/topics/index.html.erb): - - - <%= pluralize(topic.votes.length, "vote") %> - - -=== Allow people to vote === -It's good to do one bit at a time and let the test failures drive what you do next. - -Check rake routes for figuring out the path''TODO: diagram or screenshot of where this info is on the rake output'', and edit your view (/app/views/topics/index.html.erb): - - <%= link_to '+1', votes_path(:topic_id => topic.id), :method => :post %> - -Now we need to create the controller action. Open the Votes controller (/app/controllers/votes_controller.rb) to add the create action: -
    -class VotesController < ApplicationController
    -   def create
    -     topic = Topic.find(params[:topic_id])
    -     vote = topic.votes.build
    -
    -     if vote.save
    -       flash[:notice] = 'Vote was successfully created.'
    -     else
    -       flash[:notice] = 'Sorry we could not count your vote.'
    -     end
    -     redirect_to(topics_path)
    -   end
    - end
    -
    - -Add flash notice to app/views/layouts/application.html.erb -
    -

    <%= flash[:notice] %>

    -
    - -== What Next? == -=== Resources and Next Steps === - http://bit.ly/ruby-resources diff --git a/sites/workshop/diagrams.mw b/sites/workshop/diagrams.mw index 5ed0763b1..f32b9112e 100644 --- a/sites/workshop/diagrams.mw +++ b/sites/workshop/diagrams.mw @@ -2,6 +2,8 @@ * MVC: http://github.com/alexch/mission/raw/master/mvc.png +* MVC: https://www.railstutorial.org/book/toy_app#sec-exercises_mvc_in_action + * REST: @@ -34,14 +36,14 @@ - -
    shows the list of all topics
    +Read GET /topics/2 show shows only topic number 2
    +Read GET /topics/2/edit edit diff --git a/sites/workshop/hello_sinatra.rb b/sites/workshop/hello_sinatra.rb deleted file mode 100644 index 92d049985..000000000 --- a/sites/workshop/hello_sinatra.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'rubygems' -require 'sinatra' - -get '/' do - "Hello, bang bang!" -end - diff --git a/sites/workshop/img/Mvc-stickman.png b/sites/workshop/img/Mvc-stickman.png deleted file mode 100644 index 4d30e186b..000000000 Binary files a/sites/workshop/img/Mvc-stickman.png and /dev/null differ diff --git a/sites/workshop/img/Mvc.jpg b/sites/workshop/img/Mvc.jpg deleted file mode 100644 index d118b682a..000000000 Binary files a/sites/workshop/img/Mvc.jpg and /dev/null differ diff --git a/sites/workshop/img/Sqllit.jpg b/sites/workshop/img/Sqllit.jpg deleted file mode 100644 index 183b5123a..000000000 Binary files a/sites/workshop/img/Sqllit.jpg and /dev/null differ diff --git a/sites/workshop/img/Suggestotron_topics_index.jpg b/sites/workshop/img/Suggestotron_topics_index.jpg deleted file mode 100644 index b551cd32f..000000000 Binary files a/sites/workshop/img/Suggestotron_topics_index.jpg and /dev/null differ diff --git a/sites/installfest/Win7_search_programs.jpg b/sites/workshop/img/Win7_search_programs.jpg similarity index 100% rename from sites/installfest/Win7_search_programs.jpg rename to sites/workshop/img/Win7_search_programs.jpg diff --git a/sites/workshop/img/blazingcloud.png b/sites/workshop/img/blazingcloud.png deleted file mode 100644 index ebe92e501..000000000 Binary files a/sites/workshop/img/blazingcloud.png and /dev/null differ diff --git a/sites/workshop/img/finder.png b/sites/workshop/img/finder.png deleted file mode 100644 index 3300286df..000000000 Binary files a/sites/workshop/img/finder.png and /dev/null differ diff --git a/sites/workshop/img/firefox.png b/sites/workshop/img/firefox.png index e40f29d1d..36baae5eb 100644 Binary files a/sites/workshop/img/firefox.png and b/sites/workshop/img/firefox.png differ diff --git a/sites/workshop/img/flannery.jpg b/sites/workshop/img/flannery.jpg deleted file mode 100644 index 867fd5d5b..000000000 Binary files a/sites/workshop/img/flannery.jpg and /dev/null differ diff --git a/sites/workshop/img/lillie_cats.jpg b/sites/workshop/img/lillie_cats.jpg deleted file mode 100644 index b88bcbb0a..000000000 Binary files a/sites/workshop/img/lillie_cats.jpg and /dev/null differ diff --git a/sites/workshop/img/ms_office.png b/sites/workshop/img/ms_office.png deleted file mode 100644 index 42ab719c7..000000000 Binary files a/sites/workshop/img/ms_office.png and /dev/null differ diff --git a/sites/workshop/img/mvc_simple.png b/sites/workshop/img/mvc_simple.png deleted file mode 100644 index 220f14b8f..000000000 Binary files a/sites/workshop/img/mvc_simple.png and /dev/null differ diff --git a/sites/workshop/img/nicksieger.jpg b/sites/workshop/img/nicksieger.jpg deleted file mode 100644 index 996461493..000000000 Binary files a/sites/workshop/img/nicksieger.jpg and /dev/null differ diff --git a/sites/workshop/img/nird.png b/sites/workshop/img/nird.png deleted file mode 100644 index 53597399d..000000000 Binary files a/sites/workshop/img/nird.png and /dev/null differ diff --git a/sites/workshop/img/pivotal_labs_logo.png b/sites/workshop/img/pivotal_labs_logo.png deleted file mode 100644 index 4776918f5..000000000 Binary files a/sites/workshop/img/pivotal_labs_logo.png and /dev/null differ diff --git a/sites/workshop/img/pivotal_logo.png b/sites/workshop/img/pivotal_logo.png deleted file mode 100644 index 4776918f5..000000000 Binary files a/sites/workshop/img/pivotal_logo.png and /dev/null differ diff --git a/sites/workshop/img/quicktime.jpg b/sites/workshop/img/quicktime.jpg deleted file mode 100644 index c37fdc114..000000000 Binary files a/sites/workshop/img/quicktime.jpg and /dev/null differ diff --git a/sites/workshop/img/rails_logo_small.jpg b/sites/workshop/img/rails_logo_small.jpg deleted file mode 100644 index 28f8c576a..000000000 Binary files a/sites/workshop/img/rails_logo_small.jpg and /dev/null differ diff --git a/sites/workshop/img/rubygems.png b/sites/workshop/img/rubygems.png new file mode 100644 index 000000000..84547551a Binary files /dev/null and b/sites/workshop/img/rubygems.png differ diff --git a/sites/workshop/img/safari.png b/sites/workshop/img/safari.png deleted file mode 100644 index 50b552b9b..000000000 Binary files a/sites/workshop/img/safari.png and /dev/null differ diff --git a/sites/workshop/img/scribd.jpg b/sites/workshop/img/scribd.jpg deleted file mode 100644 index 4895c1667..000000000 Binary files a/sites/workshop/img/scribd.jpg and /dev/null differ diff --git a/sites/workshop/img/square_logo.jpg b/sites/workshop/img/square_logo.jpg deleted file mode 100644 index 8fb1b3cf8..000000000 Binary files a/sites/workshop/img/square_logo.jpg and /dev/null differ diff --git a/sites/workshop/img/student_experience.png b/sites/workshop/img/student_experience.png deleted file mode 100644 index d0227d314..000000000 Binary files a/sites/workshop/img/student_experience.png and /dev/null differ diff --git a/sites/workshop/img/unauthenticated_home-0.jpg b/sites/workshop/img/unauthenticated_home-0.jpg deleted file mode 100644 index 5017635fb..000000000 Binary files a/sites/workshop/img/unauthenticated_home-0.jpg and /dev/null differ diff --git a/sites/workshop/img/web-application.png b/sites/workshop/img/web-application.png deleted file mode 100644 index c213cabfc..000000000 Binary files a/sites/workshop/img/web-application.png and /dev/null differ diff --git a/sites/workshop/img/web_app_in_rails.jpg b/sites/workshop/img/web_app_in_rails.jpg deleted file mode 100644 index 7c05f6efb..000000000 Binary files a/sites/workshop/img/web_app_in_rails.jpg and /dev/null differ diff --git a/sites/workshop/img/wordpress.jpg b/sites/workshop/img/wordpress.jpg index ee5dcbd1a..c9a41fb23 100644 Binary files a/sites/workshop/img/wordpress.jpg and b/sites/workshop/img/wordpress.jpg differ diff --git a/sites/workshop/more_teacher_training.deck.md b/sites/workshop/more_teacher_training.deck.md new file mode 100644 index 000000000..4eb95088a --- /dev/null +++ b/sites/workshop/more_teacher_training.deck.md @@ -0,0 +1,251 @@ +# RailsBridge Teacher Training + +# What is this? + +You're probably at a teacher training for RailsBridge. + +This slide deck is a tool to facilitate conversations about teaching best practices and challenges, specifically for RailsBridge workshops. + +It helps to have a whiteboard or those giant sticky notes for the discussions if possible. + +### Discussion is key! +### Don't let the presenter(s) do all the talking! + +# Why RailsBridge? +We're making tech more diverse and more welcoming! + +### How? +* We throw super-welcoming, fun, free workshops. +* We provide hella networking opportunities for students and volunteers. +* We help our volunteers become more empathetic and better communicators. + +# Introductions +### Who are you? +* What's your name? +* Have you been to a RailsBridge before? How many? +* What do you do for a living? Care to share who you work for? +* What's your favorite structure in the Bay Area? +* Alternate silly questions: + * What's your favorite animal? + * If you could only eat one food for the rest of your life, what would it be? + +# What's a RailsBridge? +Raise your hand if you've been to a workshop before! + +### RailsBridge Fun Facts +* Founded in 2009 as a variety of initiatives, including Rails Mentors, Rails Bug Smashes, and the Open Workshop Project. +* The workshops project was led by Sarah Allen and Sarah Mei. +* Its goal: make the Rails community more diverse and more welcoming to newcomers. +* Workshops are happening all over the world! + +# How does a workshop work? +There are a few different RailsBridge curricula: + +* Intro to Rails (a.k.a. "Suggestotron") +* Job Board (no scaffolds) +* Message Board (doesn't tell you what to type) +* Intro to Ruby +* Front End (HTML, CSS, and a tiny bit of JavaScript). + +First, we get all the necessary technologies onto the students' computers (the Installfest). + +The next day we break into small groups and work through the curriculum. + +# Typical RailsBridge Schedule +* Friday, 6-10pm-ish: Installfest: installing software over pizza & beer (no formal presentations) + + _n.b.: the Front End workshop doesn't usually have an Installfest._ + +* Saturday's schedule, often: + * 9-9:30am: Check-in, coffee, bagels + * 9:30-10am: Opening presentation + * 10am-12:30pm: Class! + * 12:30-1:30pm: Lunch + * 1:30pm-4:30pm: Class! (with a break sometime mid-afternoon) + * 4:30-5:00pm: Closing presentation & retros + * 5:00pm-late: After-party + +# Is RailsBridge Open Source? + +### WHY YES, THANK YOU FOR ASKING! +### RAILSBRIDGE IS VERY OPEN SOURCE! + +All the materials you're using were created by volunteers, and are on GitHub for forking and editing and using! + +If you see something that could be better, make a pull request. Pull requests are the lifeblood of RailsBridge. + +If you don't know how to make one, we'll help! + +# How to make your class awesome +We've made three quasi-arbitrary categories of ways to make your class awesome: +### We want our students to feel: +* socially comfortable +* technically capable +* like you are approachable and can help solve their problems + +# Discussion: Social Comfort + +#### Imagine: +* You're trying to do something difficult +* You're in a group of strangers, many of whom know how to do it better than you +* You've tried before but got lost or bored or confused +* You don't feel like you can articulate your thoughts and questions +* You don't know the right names for anything + +#### When was the last time you felt this way? + +#### How can we help make this easier? +#### How can you help people feel socially comfortable? + + +# Social Comfort (Ideas) + +#### Introductions +* Include name, profession, why are you here / goals for today's class, and something silly. +* Don't rush, even if you have a big class. +* If someone joins the class late, ask them to introduce themselves. + +#### Icebreakers +* Name games! Admit up front that most people are bad at learning new names. +* Get people talking. The more comfortable they are at talking, the more likely they'll speak up when they don't understand something, or to answer someone else's question. + +# Social Comfort (More Ideas) +#### Try to suppress your (understandable) culturally-influenced sexism +* Don't hit on people. No sexual advances. None. Even at the after-party. +* Don't make sexist jokes. Or racist, classist, or ableist jokes. Call people out if they do. A simple "That's not funny" and moving on quickly with the conversation will often suffice. +* Don't make gender-based generalizations ("Women are better at X, because ...") +* Don't make references to people's bodies or state your opinion of them. +* Don't use slurs. + +# Discussion: Technical Capability +### How can you help people feel technically capable? +### What kinds of insecurities might your student have? +### How can you bolster their confidence? + +# Technical Capability (Ideas) +#### Explain that: +* Even professional developers are constantly learning new technologies, so being confused is normal. +* Initial code is often terrible: don't feel bad, just refactor! +* Mistakes == Learning! + +#### Dealing with technical concepts: +* Define technical terms! Several times! +* Assume anyone you're teaching has zero knowledge but infinite intelligence. +* Remember people's professional and code backgrounds (QA, DBA, C++, Java, JS) and relate where possible. If they are a cook, try a cooking analogy. + +# Technical Capability (More Ideas) +#### Encourage collaboration and interaction +* Explicitly encourage students to try to answer each other's questions. +* If a question is asked, ask if anyone in the class thinks they can explain. +* Be especially encouraging of the first few questions, to try to get things rolling. +* Good responses to questions: "I'm glad you asked!" or "I actually wondered that, too." or "Great question!" + +#### Be Super Positive, Always +* Students have diverse backgrounds. Appreciate this fact. +* If they aren't getting a concept, avoid anything that might shame them. +* Don't be surprised when someone hasn't heard of something before. +* Don't grab anyone's keyboard. Avoid taking over unless you think it's *really* necessary. Ask before you do. "Mind if I drive for a sec?" But really, don't. + +# Technical Capability (Even More Ideas) +#### Walk the Middle Path +* Don't go too deep for your class level, but also, don't gloss over things. +* When trying to be accurate, it's easy to go down a rabbit hole of specificity. Avoid. +* Work with the TAs to make sure no one goes down that rabbit hole. Accountability! +* Explain the big picture of a command *before* they type it in. + * i.e., before typing the command to deploy to Heroku, explain the difference between localhost and Heroku. + +# Discussion (Do you know what's up?) +#### How can you help people feel like you know what's going on? +#### What are things you can do to help the students trust you? +#### What are some things to avoid? + +# Know What's Up (Ideas) +#### Know what's going on +* Cover logistics at the beginning of class + * Planned breaks, lunch time + * Remind students that there is a closing presentation at the end + * Make sure they know where the bathroom is + * Encourage them to attend the after-party + +#### Establish a few ground rules +* Questions are always welcome, even if the student thinks it might be dumb. +* Explain that if someone has trouble (e.g., not getting the expected output), the TAs will help troubleshoot. +* If anyone wants to switch classes, tell them they should feel TOTALLY COMFORTABLE switching at any point. + +# Know What's Up (More Ideas) +#### Don't be afraid to: +* Call on people! By name! +* Correct people if they're wrong. Be polite and encouraging. For instance: + * "Well, this might work better and this is why." + * "Can you explain how you came to that conclusion?" + * "Does anyone have a different answer?" +* Ask yourself questions and answer them. + +# Know What's Up (Even More Ideas) +#### Pace yourself! +* Don't go too fast. You will probably go too fast. Check in occasionally to ensure everyone is still with you. +* You can say the same thing THREE TIMES and it will not be boring yet. +* When you ask a question, wait TEN WHOLE SECONDS before saying anything else. People need time to think. +* Don't let the most advanced students dictate the pacing or answer all the questions. + +# Discussion: Challenges +Talk about what problems you might anticipate, and what to do about them. + +#### Some issues: +* Student is in the wrong class level +* Student is disruptive +* Student is disengaged +* TA is not helping + +# What's a TA? + +* At RailsBridge, a TA is a volunteer who isn't leading the class. +* If you're volunteering at your first RailsBridge workshop, you should probably be a TA. +* Sometimes they are the technical experts (rather than the teacher), sometimes not. +* TAs often explain specific concepts for the class or teach a couple of sections to give the teacher a break from talking. + +# Discussion: TAs +#### TAs: How can you best utilize the AWESOME POWER that is a TA? + +# TAs (Some Ideas) +* TAs can ask questions to encourage students to speak up. +* Ask your TA to explain a concept; they may be more technically advanced than you! +* TAs can help people who get lost. +* Co-teaching is also an option if you feel like you can tag-team. There doesn't have to be a hierarchy. +* If someone falls behind, the TA can take them out of the room to do some 1-on-1, if there's another TA in the room. + +# Discussion: Comprehension +#### How can you tell if they understand the words you're saying? +#### What are good questions to ask to check comprehension? +#### What did your favorite teachers do to gauge understanding? + +# Student Comprehension (Some Ideas) +* Pay attention to body language. +* People ask questions most often when they are actively processing material. If they aren't, it might be that the material is too easy or hard. Try to figure out which it is! + +#### Calling on people +* Calling on people makes the class more interactive and engaging, and less lecture-y. +* Don't always ask questions to the whole class: call on individuals by name. +* Consider breaking the class into two teams and addressing questions to teams. +* Ask people what they expect a command to produce BEFORE you hit enter. +* Ask "How would you do \#\{this\}?" or "If I wanted to do \#\{that\}, what would I do?" + +# Installfest! +#### Keep in mind: +* There will be people with _all_ kinds of computers. +* Even though Windows is not an ideal Rails development environment, we're here to encourage people and meet them wherever they are right now. +* Do NOT say bad things about Windows, even if it's frustrating. +* If you're not sure about something, grab another volunteer. + +# Very Important, Very Practical Things +#### Where to find the curriculum: http://docs.railsbridge.org + +You need to read the curriculum through, beginning to end, before teaching it. + +First workshop? Be a TA! + +#### Where to submit pull requests: https://github.com/railsbridge/docs + +#### How to submit pull requests: http://railsbridge.github.io/bridge_troll/ + +We need your help! Thank you!!! diff --git a/sites/workshop/more_teacher_training_2016.deck.md b/sites/workshop/more_teacher_training_2016.deck.md new file mode 100644 index 000000000..6c48fd9b2 --- /dev/null +++ b/sites/workshop/more_teacher_training_2016.deck.md @@ -0,0 +1,422 @@ +# RailsBridge Teacher and TA Training + +# Why we are here? + +* Get to know each other and RailsBridge +* Know what will happen during a typical RailsBridge workshop +* Be empowered as teachers and TAs +* Practice teaching techniques (checks for understanding, scaffolding) + +### RailsBridge Teacher and TA Training + +# How is this going to happen? + +* Introductions +* Coverage of workshop logistics + * Schedule + * Student levels + * Available curricula + * Volunteer roles +* Facilitated conversations about soft skills, teaching best practices and challenges, specifically for RailsBridge workshops. + +# Introducing RailsBridge! + +* Founded in 2009 as a variety of initiatives, including Rails Mentors, Rails Bug Smashes, and the Open Workshop Project. +* The workshops project was led by Sarah Allen and Sarah Mei. +* Bridge Foundry +* Workshops not just Ruby on Rails, but also teaching Ruby, front-end and mobile development, Clojure, etc. + +# Introducing RailsBridge! + +### Current Level of Experience + +* Have held super-welcoming, fun, free workshops all over the world, over 200 in total! + +# Introducing RailsBridge! + +### Goals for All Workshops Ever + +* Make tech more diverse and welcoming! +* Provide extremely welcoming and useful high quality instruction to students. +* Provide hella networking opportunities for students and volunteers. +* Provide an opportunity for our volunteers to give back while they are also gaining valuable skills and connections. +* We throw super-welcoming, fun, free workshops. +* We help our volunteers become more empathetic and better communicators. +* Have fun! + +# Introductions + +* What is your name? What else would you like to share about yourself? Could be anything... work, hobbies, interests, education, favorite building or music, etc. +* What is your current level of experience with the workshop topic and with teaching? +* What would you like to get out of today's teacher training? Anything specific you want to learn or discuss? + +# Typical RailsBridge Schedule + +First, we get all the necessary technologies onto the students' computers aka the **Installfest**. + +The next day we break into small groups and work through the curriculum. + +# Installfest + +#### Friday 6-10pm-ish + +* Installfest: installing software over pizza & beer +* No formal presentations, a bit like a party, get to know your students and fellow volunteers +* Not necessary for Front-end workshops + +### Keep in mind: + +* There will be people with _all_ kinds of computers. +* Even though Windows is not an ideal Rails development environment, we're here to encourage people and meet them wherever they are right now. +* Do NOT say bad things about Windows, even if it's frustrating. +* If you're not sure about something, grab another volunteer. +* Do NOT just talk to other volunteers, interact with the students. + +### Questions about the Installfest? + +# Workshop + +### Saturday 9-4:30pm-ish plus After-party + +* 9-9:30am: Check-in, coffee, bagels +* 9:30-10am: Opening presentation, student sorting and classroom assignment, volunteer huddle +* 10am-12:30pm: Class! +* 12:30-1:30pm: Lunch +* 1:30pm-4:30pm: Class! +* 4:30-5:00pm: Closing presentation and retros +* 5:00pm-late: After-party + +# Workshop - Start with Introductions!!! + +* What is your name? Work, hobbies, interests, education, anything else you would like to share about yourself? +* What is your current level of experience with writing code/technology in general/the material we are covering in the workshop today? +* What would you like to get out of today's workshop? Anything specific you want to learn or discuss? + +# Introductions - Things to Look For + +* Make sure everyone participates, not just students. +* Look for students who are in the wrong level and find out if they want to move. +* Listen carefully to their goals so you can either realistically adjust them before class or meet them during class. +* Don't rush, even if you have a big class. Getting people talking +* If someone joins the class late, ask them to introduce themselves. +* Get people talking. The more comfortable they are at talking, the more likely they'll speak up when they don't understand something, or to answer someone else's question. + +# Workshop - Establish Ground Rules + +* Questions are always welcome, there are no silly questions. +* Explain that if someone has trouble (e.g., not getting the expected output), the TAs will help troubleshoot. +* If anyone wants to switch classes, tell them they should feel TOTALLY COMFORTABLE switching at any point. + +# Cover Classroom Logistics + +* Planned breaks, lunch time +* Remind students that there is a closing presentation and retrospective at the end +* Make sure they know where the bathroom is +* Encourage them to attend the after-party +* Students might not want to take breaks, but it is best that they do. + +# Student levels + + **Blue** - Totally New to Programming + + **Green** - Somewhat New to Programming + + **Gold** - Some Rails Experience + + **Orange** - Other Programming Experience + + **Purple** - Ready for the Next Challenge + +# Curricula + +#### Where to find the curriculum: http://docs.railsbridge.org + +### You need to read the curriculum through, beginning to end, before teaching or ta'ing it. + +**Rails** + +* Intro to Rails - Classic Suggestotron Curriculum - Blue/Green Levels + +* Job Board - Build a Simple Job Board - Green/Gold levels + +* Message Board - Orange/Purple Levels + +**FrontEnd** + +* Frontend Curriculum - HTML + CSS for beginners - Blue/Green levels + +* JavaScript Snake Game - Beginner Specific Curriculum - Blue/Green level + +* JavaScript To Do LIst - Gold/Orange Levels + +* JavaScript To Do List With React - Orange/Purple Levels + +#   + +## Seriously! You need to read the curriculum through, beginning to end, before teaching or ta'ing it. + + + +# What's a TA? + +### First workshop? Be a TA! + +* At RailsBridge, a TA is a volunteer who isn't leading the class. +* If you're volunteering at your first RailsBridge workshop, you should probably be a TA. +* Sometimes they are the technical experts (rather than the teacher), sometimes not. +* TAs often explain specific concepts for the class or teach a couple of sections to give the teacher a break from talking. +* TAs can ask questions to encourage students to speak up. +* Teachers can ask your TA to explain a concept; they may be more technically advanced than you! +* TAs can help people who get lost. +* Co-teaching is also an option if you feel like you can tag-team. There doesn't have to be a hierarchy. +* If someone falls behind, the TA can take them out of the room to do some 1-on-1, if there's another TA in the room. + + +# RailsBridge Teacher Training - Soft Skills Edition + +# How to make your class awesome + +We've made three quasi-arbitrary categories of ways to make your class awesome: + +##### We want our students to feel: + +* Like you are approachable and can help solve their problems +* Socially comfortable +* Technically capable + +#### Discussion is key! Don't let the presenter(s) do all the talking! + +# Social Comfort - Establishing trust with students aka "Do you know what's up?" + +#### Discussion points + +* How can you help people feel like you know what's going on? +* What are things you can do to help the students trust you? +* What are some things to avoid? + + +# Know What's Up: Ideas + +#### Don't be afraid to: +* Call on people! By name! +* Ask yourself questions and answer them. +* Be vulnerable yourself, discuss your learning process + +# Know What's Up: Even More Ideas +#### Pace yourself! +* Don't go too fast. You will probably go too fast. Check in occasionally to ensure everyone is still with you. +* You can say the same thing THREE TIMES and it will not be boring yet. +* When you ask a question, wait TEN WHOLE SECONDS before saying anything else. People need time to think. +* Don't let the most advanced students dictate the pacing or answer all the questions. +* Get people talking. The more comfortable they are at talking, the more likely they'll speak up when they don't understand something, or to answer someone else's question. +* Allow students time to write and play with code! + +# Social Comfort - Absolutes +* Try to be more aware of your (possible and understandable) culturally-influenced sexism so you don't express it during a class +* Don't hit on people. No sexual advances. None. Even at the after-party. +* DO NOT ask for student contact information. Feel free to share yours, in a non aggressive way, so students can take initiative to contact you. +* Don't make sexist jokes. Or racist, classist, or ableist jokes. Call people out if they do. A simple "That's not funny" and moving on quickly with the conversation will often suffice. +* Don't make gender-based generalizations ("Women are better at X, because ...") +* Don't make references to people's bodies or state your opinion of them. +* Don't use slurs. + +# Social Comfort: Making the students more comfortable with learning process + +#### Imagine: +* You're trying to do something difficult +* You're in a group of strangers, many of whom know how to do it better than you +* You've tried before but got lost or bored or confused +* You don't feel like you can articulate your thoughts and questions +* You don't know the right names for anything + +# Social Comfort: Comprehension + +#### Discussion points + +* How can you tell if they understand the words you're saying? +* What are good questions to ask to check comprehension? +* What did your favorite teachers do to gauge understanding? + +# Student Comprehension: Some Ideas +* Pay attention to body language. +* People ask questions most often when they are actively processing material. If they aren't, it might be that the material is too easy or hard. Try to figure out which it is! + +#### Calling on people +* Calling on people makes the class more interactive and engaging, and less lecture-y. +* Don't always ask questions to the whole class: call on individuals by name. +* Consider breaking the class into two teams and addressing questions to teams. +* Ask people what they expect a command to produce BEFORE you hit enter. +* Ask "How would you do \#\{this\}?" or "If I wanted to do \#\{that\}, what would I do?" + + +# Discussion: Technical Capability +### How can you help people feel technically capable? +### What kinds of insecurities might your student have? +### How can you bolster their confidence? + +# Technical Capability: Ideas +#### Explain that: +* Even professional developers are constantly learning new technologies, so being confused is normal. +* Initial code is often terrible: don't feel bad, just refactor! +* Mistakes == Learning! +* Growth mindset versus fixed mindset + +#### Dealing with technical concepts: +* Define technical terms! Several times! +* Assume anyone you're teaching has zero knowledge but infinite intelligence. +* Remember people's professional and code backgrounds (QA, DBA, C++, Java, JS) and relate where possible. If they are a cook, try a cooking analogy. + +# Technical Capability: More Ideas +#### Encourage collaboration and interaction +* Explicitly encourage students to try to answer each other's questions. +* If a question is asked, ask if anyone in the class thinks they can explain. +* Be especially encouraging of the first few questions, to try to get things rolling. +* Good responses to questions: "I'm glad you asked!" or "I actually wondered that, too." or "Great question!" + +#### Be Super Positive, Always +* Students have diverse backgrounds. Appreciate this fact. +* If they aren't getting a concept, avoid anything that might shame them. +* Don't be surprised when someone hasn't heard of something before. +* Don't grab anyone's keyboard. Avoid taking over unless you think it's *really* necessary. Ask before you do. "Mind if I drive for a sec?" But really, don't. + +# Technical Capability: Even More Ideas +#### Walk the Middle Path +* Don't go too deep for your class level, but also, don't gloss over things. +* When trying to be accurate, it's easy to go down a rabbit hole of specificity. Avoid. +* Work with the TAs to make sure no one goes down that rabbit hole. Accountability! +* Explain the big picture of a command *before* they type it in. + * i.e., before typing the command to deploy to Heroku, explain the difference between localhost and Heroku. + + +# Is RailsBridge Open Source? + +### WHY YES, THANK YOU FOR ASKING! +### RAILSBRIDGE IS VERY OPEN SOURCE! + +All the materials you're using were created by volunteers, and are on GitHub for forking and editing and using! + +If you see something that could be better, make a pull request. Pull requests are the lifeblood of RailsBridge. + +If you don't know how to make one, we'll help! + +#### Where to submit pull requests: https://github.com/railsbridge/docs + +#### How to submit pull requests: http://railsbridge.github.io/bridge_troll/ + +We need your help! Thank you!!! + + +# Very Important, Very Practical Things +#### Where to find the curriculum: http://docs.railsbridge.org + +You need to read the curriculum through, beginning to end, before teaching it. + +# Teaching Techniques +### Answering questions +* Correct people if they're wrong. Be polite and encouraging. For instance: + * "Well, this might work better and this is why." + * "Can you explain how you came to that conclusion?" + * "Does anyone have a different answer?" +* Don't be afraid to tell someone that you don't know +* Don't be afraid to not answer a question if it is out of scope + * Talk with the student during a break + +# Teaching Techniques +#### Checks for understanding +A tool for assessing how well the class understands a topic before moving on + +#### Reasons to check for understanding +* Helps guide your teaching pace +* You get more feedback about which students are more challenged with the material +* Feedback loop during the lesson + +# Avoid These Checks For Understanding +* Do you understand? +* Can I move on? +* Any questions? + +#### These checks tend to have a luke warm response +#### People may think they understand, but don't actually understand + +# Suggested Checks For Understanding +* Fist of five +* Calling on a person by name +* Have students pair on a problem, then explain it to the group +* Checking in with each student individually as they work on a problem +* Teach a concept to your neighbor + +# Discussion: Teach! +#### Incorporate at least one check for understanding and teach the following +* Arrays +* Hashes +* Scrum process +* OOP +* Anything you are passionate about + +# Scaffolding +* A lesson should build upon itself +* Teacher describes or illustrates a concept multiple ways +* Teacher gives a specific example that helps students complete an assignment +* I do, we do, you do + +### Not `rails g scaffold` + +# Discussion: Great learning experience +### Describe a great learning experience you have had +### Why do you think it was so successful? + +# General curriculum levels - detailed + +### **Blue** - Totally New to Programming + +* Student has little or no experience with the terminal or a graphical IDE +* Might be familiar with HTML or CSS, but not necessarily +* Unfamiliar with terms like methids, arrays, lists, hashes, or dictionaries + +### **Green** - Somewhat New to Programming + +* May have used the terminal a little - to change directories for example +* May have done an online programming tutorial or two +* Does not have alot of experience with Rails + +### **Gold** - Some Rails Experience + +* Comfortable using terminal, but not a power user +* Have general understanding of Rails app's strucutre, maybe a prior workshop or tutorial +* Knows how to define a method in Ruby +* Has decent handle on Ruby arrays and hashes + +# General curriculum levels - detailed continued + +### **Orange** - Other Programming Experience + +* Proficient in another language and understand general programming concepts like collections and scope +* New to Ruby on Rails +* Familiar with version control and basic web architecture + +### **Purple** - Ready for the Next Challenge + +* Exhausted fun of Suggestotron/Intro Rails curriculum +* Comfortable with terminal +* Want to problem solve instead of copying other's code +* Build an app without using scaffolds + +# Curriculum levels for JavaScript + +### **Blue** - No programming Experience + +* Totally new to JavaScript itself +* Made a webpage before, maybe at a RailsBridge Front End Workshop +* No experience with programming languages other than HTML and CSS + +### **Orange** - Programmer new to Javascript + +* Comfortable making a complex webpage +* Some experience in programming language like ActionScript, C, Java, Ruby or Python +* Has seen JavaScript, but didn't really understand how it worked + +### **Purple** - Some experience with JavaScript + +* Feel comfortable writing functions and objects in JavaScript +* Used jQuery before, has seen Ajax request, but doesn't understand all the moving parts +* Interested in learning how to organize JavaScript code using models and views diff --git a/sites/workshop/noobie-outline.txt b/sites/workshop/noobie-outline.txt index 12faef368..bef855eec 100644 --- a/sites/workshop/noobie-outline.txt +++ b/sites/workshop/noobie-outline.txt @@ -1,4 +1,4 @@ -RailsBridge Introductory Curriculum for Non-Programmers +RailsBridge Introductory Curriculum for Non-Programmers Target Audience: Students who have not exposure to the command line and/or programming (and may be fuzzy on how the file system works) @@ -14,12 +14,12 @@ File Directories: Use house / rooms / drawers metaphor for how directories store files and keep everything in your ‘house’ organized. Review ./ and ~/ references to self and root directories. Explain where home and root are located in relation to all directories. Explain difference between absolute paths (starting with a /) and relative paths. -.. is the relative path the the parent directory. +.. is the relative path the parent directory. Command Line Basics:: pwd, ls, cd, touch, mv, cp, rm, .. (parent directory) Text Editing: Explain differences between text editor vs. word processor. Have group open editor, create and save file with explanation of how and where files are stored. -Explain file extensions and file types. File types tell the editor how to color the special words in each language. +Explain file extensions and file types. File types tell the editor how to color the special words in each language. Explain difference between the buffer (in the editor’s memory) and the file (stored on disk). Emphasize the importance of saving the buffer to a file before trying to run it with ruby. Compilers, Interpreters and Programming Languages: Use metaphor of programming language as an agreed set of rules about syntax for writing source code that is sent to the interpreter and translated into byte code. @@ -44,10 +44,10 @@ Explain ‘method calls’. Explain to group the important of strings and review string operations. Have group work with strings in IRB. IRB Console: Have group open IRB console and create objects, method calls and variable assignments. -Tie in lecture above on Objects, Methods, Variables with exercises in IRB that allow students to see results line-by-line. +Tie in lecture above on Objects, Methods, Variables with exercises in IRB that allow students to see results line-by-line. Have group query classes for their methods in IRB. HTTP: Lead group exercise in using telnet to query Wikipedia, make GET request and view response in command line, then compare to view source of same page in browser. Sinatra Application -Lead group exercise in writing Sinatra application and run it locally. “Hello, Web!” -Extend to add “the time is Sun Mar 27 09:44:09 PDT 2011” using `date`. \ No newline at end of file +Lead group exercise in writing Sinatra application and run it locally. “Hello, Web!” +Extend to add “the time is Sun Mar 27 09:44:09 PDT 2011” using `date`. diff --git a/sites/workshop/nyc-resources.deck.md b/sites/workshop/nyc-resources.deck.md deleted file mode 100644 index 917fc0216..000000000 --- a/sites/workshop/nyc-resources.deck.md +++ /dev/null @@ -1,16 +0,0 @@ -!SLIDE - -## Additional Resources - -**github.com** for source code sharing and browsing - -**heroku.com** for easy site hosting - -**gemcutter.org** for all your gem needs - -**#nycrb** on IRC for realtime interaction with NYC ruby developers - -**nycruby.org** and **meetup.com/nycruby** for information about NYC ruby meetups and mailing lists - -**nyc-ruby-women@googlegroups.com** for continuing the experience after the workshop is over - diff --git a/sites/workshop/programming_intro.mw b/sites/workshop/programming_intro.mw deleted file mode 100644 index c6de009c7..000000000 --- a/sites/workshop/programming_intro.mw +++ /dev/null @@ -1,165 +0,0 @@ -An introduction to Programming in Ruby using irb -=== Getting started === -* irb is the Interactive RuBy shell. You can type Ruby code and it will be evaluated immediately. You can try some code that will seem familiar: - >> 1 + 1 -* try a few different calculations -* now try this one - >> "c" + "ode" -* why was this different? in Ruby everything is an object and each object has behaviors it knows how to do. Numbers know how to add. However "c" is not a number, so it behaves differently even when it gets the same message (+). Programmers call "c" or "ode" a "string." A string is series of letters that could be a word or a sentence. In Ruby the string behavior triggered by the (+) message is concatenation. -* Operators like + - * / are special since we want to use them like we did in grade school. -* Most messages (which are also called methods) are called by adding the message name after a . - >> "code".upcase - >> "code".reverse -* Another thing to notice about Ruby is that everything evaluates to something - >> 4 - >> 4 + 4 - >> (4+4).zero? -* Notice how parentheses can change behavior. Parentheses cause the stuff inside to be evaluated first, otherwise the '.' has the highest precedence. Remember "Please Excuse My Dear Aunt Sally?" The same rules apply, with . at the beginning. - >> "c" + "ode".upcase - >> ("c" + "ode").upcase - -=== Variables === -Variables can hold a value - >> a = 4 - >> a - >> b = 2 - >> a + b -In Ruby (unlike languages like C or Java), variables can be of any type and can change type - >> a = "cat" -But types do matter. For example, this is an error: - >> "cat" + 4 - TypeError: can't convert Fixnum into String - from (irb):4:in `+' - from (irb):4 -But this works: - >> "cat" + 4.to_s -Variables are useful when you start writing reusable code. -=== Defining Classes and Methods === -We define a new type of object by writing something called a "class." The behavior of the object is defined in blocks of code we call "methods." - -As an example we'll create a Calculator kind of object that knows how to describe itself in text. At first, that is all it will know how to do. - - class Calculator - def describe - "I am a really neat calculator!" - end - end - -save this in a file in your text editor called "calculator.rb" then start irb in the same directory - - >> load "calculator.rb" - >> c = Calculator.new - >> c.describe - -next we'll add a method that performs a calculation - - def add(a, b) - a + b - end - -then restart irb and try it: - - >> load "calculator.rb" - >> c = Calculator.new - >> c.add(1,2) - >> c.add(56,45) - -The same code can be reused with different inputs - -Now, why might we have a calculator object when Ruby can already do built in calculations. Suppose we wanted to keep track of when we did different calculations, how many each calculator had done. We might have two calculators and have each store the number of calculations it had performed. Each object can actually keep its own data with it, even though the code that defines each object is identical. We call each object an "instance" of the class and so we call this object data and "instance variable" - - class Calculator - def initialize - @num_calculations = 0 - end - - def how_many - @num_calculations - end - - def describe - "I am a really neat calculator!" - end - - def add(a, b) - @num_calculations = @num_calculations + 1 - a + b - end - - end - -=== Arrays === - >> my_fruits = ["kiwi", "strawberry", "plum"] - => ["kiwi", "strawberry", "plum"] - - >> my_fruits = my_fruits + ["lychee"] - => ["kiwi", "strawberry", "plum", "lychee"] - >> my_fruits = my_fruits - ["lychee"] - => ["kiwi", "strawberry", "plum"] - -=== Hashes === - >> states = {"CA" => "California", "DE" => "Delaware"} - => {"DE"=>"Delaware", "CA"=>"California"} - - >> states["DE"] - => "Delaware" - -=== Repeating yourself === - >> puts fruits[0] - kiwi - => nil - >> puts fruits[1] - strawberry - => nil - >> puts fruits[2] - plum - => nil - -Easier to write this: - >> fruits.each do |f| - * puts f - >> end - kiwi - strawberry - plum - -(Omit asterisk before 'puts f' and code below, it denotes start of new line and not part of the code) - -=== Conditions === - - if true - puts "you will see this" - end - - if false - puts "you won't see this" - end - - puts "you will see this" if true - - -==== Truth ==== - -Everything is true except for - -* false -* nil - -Therefore -* 0 is true -* "" is true - -==== Equality ==== - -Testing for equality is different from setting equality: - - a = 4 - puts "four" if a == 4 - -Another example: - - >> fruits.each do |f| - * puts f if f == "plum" - >> end - plum - diff --git a/sites/workshop/resources.md b/sites/workshop/resources.md new file mode 100644 index 000000000..110c3eb79 --- /dev/null +++ b/sites/workshop/resources.md @@ -0,0 +1,48 @@ +### Learning Ruby +* [TryRuby](http://tryruby.org/) a browser-based interactive tutorial in Ruby +* ["Learning to Program" by Chris Pine](http://www.pragprog.com/titles/ltp2/learn-to-program-2nd-edition) - a beginner's programming book with lots of Ruby exercises. (earlier version [online](http://pine.fm/LearnToProgram)) +* [Why's Poignant Guide](http://poignant.guide/book/chapter-1.html) +* [Hackety Hack](http://hackety-hack.com/) a fun way for beginners to learn Ruby. +* [Ruby Koans](http://rubykoans.com/) - a self-guided journey through topics in Ruby for beginners and experts alike +* [Test-First Teaching](http://testfirst.org) - click on 'Learn Ruby' +* [Ruby Warrior](https://github.com/ryanb/ruby-warrior) - write and refine some Ruby code to get your warrior to the top of a hazardous tower +* [Ruby Quiz](http://www.rubyquiz.com) - a guided tour through the world of possibility; use your Ruby to build simple apps, games, and solve problems +* [Why's Poignant Guide to Ruby](http://poignant.guide/book/chapter-1.html) - A whimsical comic book that teaches you Ruby. Legendary in the community. +* [Learn Ruby the Hard Way](http://ruby.learncodethehardway.org/) It's not actually hard. A great place to start if you're new to programming and want to learn with hands-on examples. +* [PDX tech workshop](http://github.com/caylee-hogg/pdx-ruby-course) This are the slides from the ruby/rails workshop organized in Portland, OR by [PDXtech](http://pdxtechworkshops.org/) + +### Learn about Rails + +* [Rails Guides](http://guides.rubyonrails.org/) - the official how-to articles for Rails +* [Rails API](http://api.rubyonrails.org/) - online documentation +* [Rails for Zombies](http://railsforzombies.org/) - a series of videos and browser-based Rails exercises +* [Rails Tutorial](http://ruby.railstutorial.org/) - a tutorial that leads you through writing a Rails messaging app + +### Watch screen casts + +* [RailsCasts](http://railscasts.com) (also available as [blog posts](http://asciicasts.com/)) + +### Classes & events in San Francisco + +* [RailsBridge Workshops organizing team](http://groups.google.com/group/railsbridge-workshops) +* [Women Who Code meetup](http://www.meetup.com/Women-Who-Code-SF/) (monthly hack nights & speakers) + +### Online + +* [RailsBridge](http://railsbridge.org) +* [DevChix](http://www.devchix.com) -blog and mailing list for women developers +* [Stack Overflow](http://stackoverflow.com/) -for answers to programming questions + +### Get experience + +* Just do it. Write and publish your own Rails app. +* Volunteer at the next workshop +* Volunteer on a RailsBridge Builders project +* Come to a hack session + +### Meetups and User Groups outside of San Francisco + +* Boulder Ruby (monthly events): http://boulderruby.org/ +* DeRailed - Denver Rails UG: http://groups.google.com/group/derailed +* Mountain.rb (Boulder, Colorado) http://mountainrb.com +* Chicago Ruby (beginners welcome!) http://www.meetup.com/ChicagoRuby/ diff --git a/sites/workshop/resources.mw b/sites/workshop/resources.mw deleted file mode 100644 index 9d6cb6510..000000000 --- a/sites/workshop/resources.mw +++ /dev/null @@ -1,63 +0,0 @@ -== Resources and Next Steps == -short link to this page: http://bit.ly/ruby-resources - -== Self-Organized Study Groups == -* [http://www.meetup.com/Women-Who-Code-SF/events/32515272/ WWCode-Rails study group] in San Francisco meets every Tuesday. Started on Sept 27, 2011. - * [http://groups.google.com/group/wwcode-rails/ WWCode-Rails Mailing List] - * Our devchix wiki page: [[WWCode Rails]] - -=== Learning Ruby === -* [http://tryruby.org/ TryRuby] a browser-based interactive tutorial in Ruby -* [http://www.pragprog.com/titles/ltp2/learn-to-program-2nd-edition"Learning to Program" by Chris Pine] - a beginner's programming book with lots of Ruby exercises. (earlier version [http://pine.fm/LearnToProgram online]) -* [http://mislav.uniqpath.com/poignant-guide/book/chapter-1.html Why's Poignant Guide] -* [http://hackety-hack.com/ Hackety Hack] a fun way for beginners to learn Ruby. -* [http://rubykoans.com/ Ruby Koans] - a self-guided journey through topics in Ruby for beginners and experts alike -* [http://testfirst.org Test-First Teaching] - click on 'Learn Ruby' -* [https://github.com/ryanb/ruby-warrior Ruby Warrior] - write and refine some Ruby code to get your warrior to the top of a hazardous tower -* [http://www.rubyquiz.com Ruby Quiz] - a guided tour through the world of possibility; use your Ruby to build simple apps, games, and solve problems -* [http://mislav.uniqpath.com/poignant-guide/book/chapter-2.html Why's Poignant Guide to Ruby] - A whimsical comic book that teaches you Ruby. Legendary in the community. -* [http://ruby.learncodethehardway.org/ Learn Ruby the Hard Way] It's not actually hard. A great place to start if you're new to programming and want to learn with hands-on examples. -* [http://github.com/caylee-hogg/pdx-ruby-course PDX tech workshop] This are the slides from the ruby/rails workshop organized in Portland, OR by [http://pdxtechworkshops.org/ PDXtech] - -=== Learn about Rails === - -* [http://guides.rubyonrails.org/ Rails Guides] - the official how-to articles for Rails -* [http://railsapi.com Rails API] - online documentation -* [http://railsforzombies.org/ Rails for Zombies] - a series of videos and browser-based Rails exercises -* [http://ruby.railstutorial.org/ Rails Tutorial] - a tutorial that leads you through writing a Rails messaging app - -=== Watch screen casts === - -* [http://railscasts.com RailsCasts] (also available as [http://asciicasts.com/ blog posts]) -* [http://peepcode.com PeepCode] -* [http://pivotallabs.com/talks Pivotal Labs talks] - -=== Classes & events in San Francisco === - -* [http://www.meetup.com/sfruby/ San Francisco Ruby Meetups (we'd love to have you when you're in the area!)] -* [http://groups.google.com/group/railsbridge-workshops RailsBridge Workshops organizing team] -* [http://www.meetup.com/Women-Who-Code-SF/ Women Who Code meetup] (monthly hack nights & speakers) - -=== Online === - -* [http://valobox.com Valobox] -use Promocode 'RailsBridge' to read free. -* [http://railsbridge.org RailsBridge] -* [http://www.devchix.com DevChix] -blog and mailing list for women developers -* [http://stackoverflow.com/ Stack Overflow] -for answers to programming questions -* [http://apprenticeship-patterns.labs.oreilly.com/ Apprenticeship Patterns] -advice for aspiring programmers - -=== Get experience === - -* Just do it. Write and publish your own Rails app. -* Volunteer at the next workshop -* Volunteer on a RailsBridge Builders project -* Come to a hack session - -=== Meetups and User Groups outside of San Francisco === - -* Boulder Ruby (monthly events): http://boulderruby.org/ -* DeRailed - Denver Rails UG: http://groups.google.com/group/derailed -* Mountain.rb (Boulder, Colorado) http://mountainrb.com -* Chicago Ruby (beginners welcome!) http://www.meetup.com/ChicagoRuby/ - -Check for Ruby and Rails meetups anywhere at [http://www.rubyinperson.com/ Ruby in Person] diff --git a/sites/workshop/ruby_for_beginners.deck.md b/sites/workshop/ruby_for_beginners.deck.md index 5a2b92ad3..1b014701c 100644 --- a/sites/workshop/ruby_for_beginners.deck.md +++ b/sites/workshop/ruby_for_beginners.deck.md @@ -1,221 +1,560 @@ !SLIDE subsection +# Ruby Programming for Beginners -# The Complete Beginner's Guide to Programming + +# What we'll cover -!SLIDE +* Programming: languages & applications +* What is Ruby? +* Ruby's common objects +* Ruby syntax basics +* Object oriented programming concepts +* Passing code to a Ruby interpreter -# What is a program? +!SLIDE +# Programming !SLIDE -# Operating Systems +# Operating System (OS) +Talk to a computer's `hardware`. -
    - - - - - + + + + + +
    !SLIDE -# Applications +# Application +The `software`. It sends `input` (`commands`, `data`, etc) to the operating system and receive `output` after the operating system has acted on it. ![](img/acrobat.jpg) -![](img/finder.png) ![](img/firefox.png) ![](img/itunes.png) -![](img/quicktime.jpg) -![](img/safari.png) -![](img/ms_office.png) ![](img/wordpress.jpg) -!SLIDE centereverything +!SLIDE +# Language +A set of code that can be used to create an application. + +* Ruby +* Python +* Perl +* Java +* C++ + +Many others. + +Q: How is a computer language similar to a human language, like English or Spanish? How is it different? + +# Library +A collection of reusable code to accomplish a generic activity. + +!SLIDE +# Framework +Collection of reusable code to facilitate development of a particular product or solution. + +!SLIDE +## Ruby vs. Rails + +### Ruby is a language + + +### Gems are Ruby libraries + + +### Rails is a framework + + + +!SLIDE +## Ruby vs. Rails +* Rails is written in Ruby language. +* Rails contains many Ruby gems. +* Rails is a framework. +* Rails' purpose is to build websites. + +The rest of this tutorial isn't about Rails. +You're learning how to do _any_ kind of programming with Ruby. + + +# Ruby Philosophy + -# Web Application In Rails -![](img/web_app_in_rails.jpg) +``` +I believe people want to express themselves when they program. + +They don't want to fight with the language. -!SLIDE incremental smbullets -# How do I write one? +Programming languages must feel natural to programmers. + +I tried to make people enjoy programming and concentrate on the fun and creative part of programming when they use Ruby. +``` + -- [Matz](http://linuxdevcenter.com/pub/a/linux/2001/11/29/ruby.html) (Yukihiro Matsumoto), Ruby creator + + +!SLIDE bullets +# Ruby is a scripting language + +* Scripting languages: + * Don't require a compiler. + * Have an interpreter _(more on that later...)_ + * Run "on the fly" + * Easy to change frequently + +Python, Perl, and JavaScript are scripting languages too. + +Java and C++ are examples of compiled languages. + + +!SLIDE centereverything -* Learn about customer's requirements -* Translate to "stories" -* Pick a story that seems doable -* Write code that does it -* Show your work to the customer, get feedback -* Based on feedback, adjust your stories -* When a story is done, go back to "pick a story" -* Repeat until app is finished! !SLIDE subsection -# Let's start writing code! +# Let's start coding! !SLIDE bullets -# "The Terminal" +# Open Your Terminal +You may also hear it called "command Line", "console", "shell", or "Bash" -* Windows: git bash ![](img/git_bash.png) +* Windows: `git bash` ![](img/git_bash.png) -* Mac OS X: Terminal ![](img/mac_terminal_sm.png) +* Mac OS X & Ubuntu: `Terminal` ![](img/mac_terminal_sm.png) + + +!SLIDE +# Prompt + +* Terminals show a line of text when you login & after a command finishes +* It's called the `prompt`, and customarily ends with a dollar sign + +Whenever instructions start with `"$ "`, type the rest of the line into terminal. + +Let's give the terminal a `command`, to open Interactive Ruby (IRB) + +```bash + $ irb +``` -* aka "The Shell" or "The Command Line" or "The Console" or "Bash" or "Shell" !SLIDE commandline -# irb: the Interactive Ruby Browser +# irb: Interactive Ruby + +IRB has its own prompt, which customarily ends with `>` + +``` + $ irb + > +``` + +You can use `Control-C` to exit IRB any time. +Or type `exit` on its own line. - $ irb +```ruby + > exit + $ +``` + +Now you're back to the terminal's prompt. + + Windows Users! Some people have experienced trouble with backspace, delete, and arrow keys working properly in irb - what a pain! If you run into this problem, use this command instead to launch irb. + + $ irb --noreadline !SLIDE ## Variables -### words that hold information +### A variable holds information. +* We give it a name so we can refer to it +* The info it holds can be changed - > my_variable = 5 - => 5 - > my_other_variable = "hi" - => "hi" +``` + $ irb + > my_variable = 5 + => 5 + > another_variable = "hi" + => "hi" + > my_variable = 10 + => 10 +``` -!SLIDE -## Many types of information +What is happening on the lines beginning with `=>` ? -* Strings -* Numbers -* Collections -* Dates -* Booleans (true/false) -* etc. +* Setting a variable equal to something is called "assignment." In the above examples, we are assigning my_variable to 5 and my_other_variable to "hi." +* What types of information can we hold in a variable? (see next slide for answers) !SLIDE -## Strings (text) +## Variable +### Variable Assignment + +Variables are assigned using a single equals sign (`=`). + +The *right* side of the equals sign is evaluated first, then the value is assigned to the variable named on the *left* side of the equals. + +```ruby + apples = 5 + bananas = 10 + 5 + fruits = 2 + apples + bananas + bananas = fruits - apples +``` + +What happened on each line? Is it what you expected? + + + +What could you do to see each's `return value` for confirmation? + + +!SLIDE !bullets +## Variable +### Variable Naming + +Create a variable whose name has: + +* all letters (like 'folders') + +* all numbers like '2000' + +* an underscore (like `first_name`) + +* a dash (like 'last-name') + +* a number anywhere (like `y2k`) + +* a number at the start (like '101dalmatians') + +* a number at the end (like 'starwars2') + +What did you learn? + + +!SLIDE bullets +# Common types of information +* Variables can hold many types of information, including: + * String + * Number + * Collections + * Booleans + +* Don't know what these are? Don't worry! We're about to find out! + +!SLIDE bullets +## String + +A string is text. It must be wrapped in a matched pair of quotation marks. + +```ruby + $ irb + > 'Single quotes work' + => "Single quotes work" + > "Double quotes work" + => "Double quotes work" + > "Start and end have to match' + "> +``` + +What is happening on the last two lines? How would you solve it? + + +### exercise +* Create variables called first_name, last_name, and favorite_color. +* Assign the variables to strings. +* Can you print out a sentence that reads "Hi, my name is (first name) (last name) and my favorite color is (favorite color)." with these variables? + * Hint: you can use a "+" to add strings together. + !SLIDE ## Numbers +* Numbers without decimal points are called **integers** and numbers with decimal points are called **floats**. +* Examples of integers: + * 0 + * -105 + * 898989898 + * 2 +* Examples of floats: + * 0.0 + * -105.56 + * 0.33 + * 0.00004 +* You can perform operations on both types of numbers with these characters: +, -, /, * + +### exercises +* Try dividing an integer by an integer. Try dividing an integer by a float. How are the results different? +* Create two integer variables called num1 and num2 and assign them your favorite numbers. +* Next, compute the sum, difference, quotient, and product of these two numbers and assign these values to variables called sum, difference, quotient, and product, respectively. !SLIDE -## Collections - -* Arrays -* Hashes +## Collection +### Collection types: Array, Hash + + * In the following slides, we will cover the following topics: + * Definition of an Array + * Array syntax + * Array indexing + * Array methods + * Definition of a hash + * Hash syntax + * Hash indexing !SLIDE -## Arrays +## Collection +### Array +An array is a list. -An Array is a list of objects. +Each array must be surrounded by `square braces` aka `square brackets`. A comma separates each `member`. - >> fruits = ["kiwi", "strawberry", "plum"] + @@@ Ruby + > fruits = ["kiwi", "strawberry", "plum"] => ["kiwi", "strawberry", "plum"] +### exercises +* Make your own array and name it grocery_list. +* Include at least 5 items from your grocery list in the array. + !SLIDE -# Array Indexing +## Collection +### Array +#### Indexing -Ruby starts counting at zero. +Members are stored in order. Each can be accessed by its `index`. Ruby starts counting at _zero_. - >> fruits[0] + @@@ Ruby + > fruits[0] => "kiwi" - >> fruits[2] + > fruits[1] + => "strawberry" + > fruits[2] => "plum" - >> fruits[3] - => nil + +### exercises +* Still have your grocery_list array? Good, because we're going to use it in this exercise. +* What is at index zero in your grocery_list array? How about index 5? Guess the answers and then use the syntax in the examples above (eg: fruits[0]) to see if your guesses were right. !SLIDE -# Array methods +## Collection +### Hash +In a `hash` we can refer to a member by a keyword instead of a number. Each member is a pair: + +* *Key*: address of the hash member + +* *Value*: variable contained by the member, and located by key name -* first, last -* push, pop -* shift, unshift +A hash may also be known as a `dictionary`, `associative array`, or `map`. - fruits.first #=> "kiwi" !SLIDE -# Hashes +## Collection +### Hash +#### Hash Syntax -* aka Dictionary or Map -* collection of key/value pairs +A hash is surrounded by `curly braces` aka `curly brackets`. A comma separates each member pair. A key uses `=>` (the `rocket`) to point to its value. + + @@@ Ruby + > states = {"CA" => "California", + "DE" => "Delaware"} + => {"CA"=>"California", "DE"=>"Delaware"} + +In real life, what lists do we make in key/value pairs? - >> states = {"CA" => "California", - "DE" => "Delaware"} - => {"CA"=>"California", "DE"=>"Delaware"} - >> states["CA"] - => "California" +### exercises +* Define a Hash variable called my_info which has the following keys: + * "first_name" + * "last_name" + * "hometown" + * "favorite_food" !SLIDE -## Operators +## Collection +### Hash +#### Hash Indexing -### doing stuff with variables +Member pairs can be accessed by their key. So each hash key has to be unique. + +Values don't have to be unique. + + @@@ Ruby + > states["CA"] + => "California" + +### exercises +* Add the key "good_food" to your my_info hash and give it the same value as your favorite_food key. What happens? +* Add a second "favorite_food" key to your my_info hash. What happens when you print out the has hash again? Why? - > my_variable + 2 - => 7 - > my_variable * 3 - => 15 - > my_fruits = my_fruits + ["lychee"] - => ["kiwi", "strawberry", "plum", "lychee"] - > my_fruits = my_fruits - ["lychee"] - => ["kiwi", "strawberry", "plum"] !SLIDE -# Loops +## Methods -### doing the same thing a bunch of times +### things that do stuff. -The hard way: +* "If objects (like strings, integers, and floats) are the nouns in the Ruby language, then methods are like the verbs." - Chris Pine's "Learn to Program" +* Methods are called (used) with a "." + * Example: 5.to_s (to_s is the method) +* As it turns out, 5 + 5 is really just a shortcut way of writing 5.+ 5. +* Each data type (string, integer, float) has a set of built in methods. You can see all of the string methods here: http://ruby-doc.org/core-1.9.3/String.html (there are tons - don't worry about memorizing them, just good to know where you can go to find out more) - >> puts fruits[0] - kiwi - => nil - >> puts fruits[1] - strawberry - => nil - >> puts fruits[2] - plum - => nil +### exercises +* Create a String variable called old_string and assign it the value "Ruby is cool" +* Use String methods to modify the old_string variable so that it is now "LOOC SI YBUR" and assign this to another variable called new_string. + * Hint: look at the string methods "upcase" and "reverse" !SLIDE -# Loops +## Boolean -### doing the same thing a bunch of times +A boolean is one of only two possible values: `true` or `false`. -The easy way: +``` + > 1 + 1 == 2 + => true + > 1 + 1 == 0 + => false +``` - >> fruits.each {|f| puts f} - kiwi - strawberry - plum - => ["kiwi", "strawberry", "plum"] +( `==` means "is equal to". _More on that later._) + +### exercises +* Create a variable named favorite_color and assign it to your favorite color. +* Create a variable named not_favorite_color and assign it to a different color. +* Test to see if these variables are equal. !SLIDE -# Loops (multi-line) +## Operators -The easy way, with "do...end" rather than "{...}" +### Do stuff with objects - >> fruits.each do |f| - ?> puts f - >> end - kiwi - strawberry - plum - => ["kiwi", "strawberry", "plum"] +``` + > my_variable + 2 + => 7 + > my_variable * 3 + => 15 +``` +``` + > my_fruits = fruits + ["lychee"] + => ["kiwi", "strawberry", "plum", "lychee"] + > my_fruits = my_fruits - ["plum"] + => ["kiwi", "strawberry", "lychee"] +``` + +### exercises + +* Create an array called "vegetables" than contains 3 vegetables you like and 1 vegetable you don't like. +* Using the vegetables array, create an array called "my_vegetables" that contains only the vegetables you like. +* Extra: can you use the first two arrays to create a third array called "your_vegetables" that only contains the vegetable you don't like? !SLIDE -## Conditionals +## Loop +### Does something repeatedly + +``` + > fruits.each do |fruit| + ?> puts fruit + > end + kiwi + strawberry + plum + => ["kiwi", "strawberry", "plum"] +``` +On the second line, what does `?>` indicate? + +### exercises +* Create an array of 4 places you would like to visit. +* Print out each of these places using a loop. + * Example: + +``` +"I would like to visit Barcelona" +"I would like to visit Antigua" +"I would like to visit Alaska" +"I would like to visit New Orleans" +``` -### doing something only if a condition is met +!SLIDE +## Conditional - >> fruits.each do |f| - ?> puts f if f == "plum" - >> end - plum - => ["kiwi", "strawberry", "plum"] +### Do something only if a condition is true + +``` + > fruits.each do |fruit| + ?> puts fruit if fruit == "plum" + > end + plum + => ["kiwi", "strawberry", "plum"] +``` + +### exercises +* Create an array called "group" that contains the names of some of the people in your Railsbridge group. Make sure you include your own name. +* Using your group array, create a conditional that prints "My Name is (your name)" for your name only. + +!SLIDE +# Running Your Code !SLIDE subsection -# Command-Line Programs +# Interpreter + +Ruby is an interpreted language. Its code can't run by the computer directly. It first must go through a Ruby interpreter. + +The most common interpreter is Matz's Ruby Interpreter ("MRI"). There are many others. + +There are various ways to run code through a Ruby interpreter. We were using IRB earlier and now we will use a file. + +!SLIDE +## Running code from a file +### Create the file + +* Why use a file? What's different from, say, irb? + +Note which folder your terminal is currently in, this is your `working directory` + +In your text editor, create a file named `my_program.rb` inside your working directory. + + @@@ Ruby + class Sample + def hello + puts "Hello World!" + end + end + + s = Sample.new + s.hello + +!SLIDE commandline +## Passing code from a file +### Run the saved code + +``` + $ ruby my_program.rb + Hello World! +``` + + +!SLIDE commandline +## Passing code from a file +### We can even load that file's code into IRB! + +```bash + $ irb + > load 'my_program.rb' + > second_time=Sample.new + > second_time.hello +``` + +When might it be useful to do this? + + +!SLIDE subsection +# Your Own Command Line Program !SLIDE bullets # Hello World -hello.rb: + +`hello.rb` @@@ Ruby puts "Hello, World!" @@ -223,29 +562,31 @@ hello.rb: !SLIDE bullets # Arguments (ARGV) -hello.rb: +`hello.rb` @@@ Ruby puts "Hello, #{ARGV.first}!" -terminal: +`terminal` - $ ruby hello.rb Alice - Hello, Alice! +``` +$ ruby hello.rb Alice +Hello, Alice! +``` !SLIDE bullets # Conditionals -hello.rb: +`hello.rb` @@@ Ruby if ARGV.empty? - puts "Hello, World!" + puts "Hello, World!" else - puts "Hello, #{ARGV.first}!" + puts "Hello, #{ARGV.first}!" end -terminal: +`terminal` $ ruby hello.rb Hello, World! @@ -253,43 +594,95 @@ terminal: Hello, Alice! !SLIDE -# Sinatra +# Object Oriented Programming (OOP) -hello_app.rb: +!SLIDE subsection +## Ruby is very object oriented +### Nearly everything in Ruby is an object. - @@@ Ruby - require 'rubygems' - require 'sinatra' - get '/' do - "Hello, bang bang!" - end +!SLIDE +## Class +Describes the generic characteristics of a single _type_ of object. -!SLIDE commandline -# Sinatra +What things of this type _are_. - $ gem install sinatra - $ ruby hello_app.rb - == Sinatra/1.2.6 has taken the stage on 4567 for development with backup from Thin - >> Thin web server (v1.2.7 codename No Hup) - >> Maximum connections set to 1024 - >> Listening on 0.0.0.0:4567, CTRL+C to stop +e.g. Dog, Vehicle, Baby -then open a browser to -!SLIDE bullets -# sinatra with rerun +!SLIDE +## Method +Defines _behavioral_ characteristic. + +What the things of the class's type _do_. + +e.g. Chase, Drive, Talk + + +!SLIDE +## Variable +Defines _attribute_ characteristic. + +What things of the class's type _have_. + +e.g. Breed, Model Year, Favorite Ice Cream + + +!SLIDE +## Instance +A specific incarnation of the class. + +e.g. Rin Tin Tin, garbage truck, the neighbor's kid + - gem install rerun - rerun hello_app.rb -...now it'll automatically reload when you edit a file. !SLIDE -## Web App Network Architecture -![](img/web-application.png) +# Let's Create Projects! !SLIDE -## Web App MVC Architecture -![](img/mvc_simple.png) +## Project 1: +### [Personal Chef Lab](http://tutorials.jumpstartlab.com/projects/ruby_in_100_minutes.html) +_(start at "4. Objects, Attributes, and Methods")_ + +Topics: + +* Commandline program. Practice in Ruby syntax and OOP concepts, and creating commandline programs. + +* Explore strings: concatenation, manipulation, interpolation, coercion. + +* Symbols, nil, basic math operators, blocks, method chaining, passing parameters to methods, iteration, branching, conditionals & conditional looping. + +!SLIDE +## Project 2: +### [Encryptor Lab](http://tutorials.jumpstartlab.com/projects/encryptor.html) + +Topics: + +* Commandline program. Reinforce skills learned in Personal Chef. + +* Explore how to manipulate arrays, do more elaborate strings manipulations, refactor code, take advantage of character mapping, and access the filesystem from within code. + + +!SLIDE +## Project 3: +### [Event Manager Lab](http://tutorials.jumpstartlab.com/projects/eventmanager.html) + +Topics: + +* Commandline program. Reusing others code & data, refactoring your own code & cleaning up data, writing custom code to solve requirements. + +* Gems, `initialize` method, parameters, file input/output, processing/sanitizing data, looping, conditional branching, using file-based data storage (CSV, XML, JSON), accessing an external API, nils, DRY principle, constants, sort_by, more string manipulations. + + +!SLIDE +## Project 4: +### Testing & More + +A follow-up to EventManager focusing more on Ruby object decomposition and working with Command Line Interfaces and program control flow. + +4. [Testing](http://tutorials.jumpstartlab.com/topics/internal_testing/rspec_and_bdd.html) Topics: TDD, BDD, Rspec +_(stop at "Exceptions")_ +4. [Event Reporter Lab](http://tutorials.jumpstartlab.com/projects/event_reporter.html) Topics: Object decomposition, working with Command Line Interfaces, and program control flow. Continues project created in Event Manager lab. +6. [Rspec](http://tutorials.jumpstartlab.com/topics/internal_testing/rspec_practices.html ) diff --git a/sites/workshop/selenium.mw b/sites/workshop/selenium.mw deleted file mode 100644 index 02c5c5dd8..000000000 --- a/sites/workshop/selenium.mw +++ /dev/null @@ -1,46 +0,0 @@ -== Notes == - -In some instances, you may have to '''specify''' which browser should be used for testing. - -In
    test/selenium/selenium_test_case.rb
    - -Add or edit the following line. - -
    config.selenium_browser_key = "[SUPPORTED BROWSER]"
    - -Actual error would look like the following. - -
    -  1) Error:
    -test_happy_path(HappyPathTest):
    -Selenium::CommandError: Failed to start new browser session: Browser not supported: *google-chrome
    -
    -Supported browsers include:
    -  *firefox
    -  *mock
    -  *firefoxproxy
    -  *pifirefox
    -  *chrome
    -  *iexploreproxy
    -  *iexplore
    -  *firefox3
    -  *safariproxy
    -  *googlechrome
    -  *konqueror
    -  *firefox2
    -  *safari
    -  *piiexplore
    -  *firefoxchrome
    -  *opera
    -  *iehta
    -  *custom
    -
    - -For windows users, there is a bind error with this error message: -WARN - Failed to start: SocketListener0@0.0.0.0:4444 - -To fix it, just add this line to config: (test/selenium/selenium_test_case.rb) -config.selenium_server_address = "127.0.0.1" - -Also, we figured it out by looking at this blog: -http://formatinternet.wordpress.com/2009/04/27/cucumber-selenium-webrat-and-windows/ diff --git a/sites/workshop/showoff-nyc.json b/sites/workshop/showoff-nyc.json deleted file mode 100644 index 940936419..000000000 --- a/sites/workshop/showoff-nyc.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "RailsBridge Ruby Workshop", - "description": "Slides and links to a full-day Ruby immersion workshop in New York", - "sections": [ - {"section":"welcome"}, - {"section":"nyc"}, - {"section":"ruby_for_programmers"}, - {"section":"suggestotron"} - ] -} diff --git a/sites/workshop/showoff.json b/sites/workshop/showoff.json deleted file mode 100644 index 4bda0af85..000000000 --- a/sites/workshop/showoff.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "RailsBridge Ruby Workshop", - "description": "Slides and links to a full-day Ruby immersion workshop", - "sections": [ - {"section":"000_setup.md"}, - {"section":"welcome"}, - {"section":"current"} - ] -} diff --git a/sites/workshop/shrink.js b/sites/workshop/shrink.js deleted file mode 100644 index 58544585a..000000000 --- a/sites/workshop/shrink.js +++ /dev/null @@ -1,49 +0,0 @@ -// TODO: make it bind to a good event, not a timeout -setTimeout(function() { -$(".content").bind("showoff:show", function (event) { - var content = $(event.target); - var slide = content.parent(".slide"); - var slideHeight = $(slide).innerHeight(); - var contentHeight = $(content).height(); - console.log("contentHeight", contentHeight, "slideHeight", slideHeight) - var fudge = 20; - var tooBig = (contentHeight + fudge > slideHeight); - if (tooBig) { - - // shrink text - var ratio = slideHeight / contentHeight - .15; // extra 15% for luck - var percent = "" + parseInt(ratio * 100) + "%"; - console.log("Shrinking by " + percent); - content.css("font-size", percent); - - // shrink images - content.find('img').each(function(i, element) { - var newHeight = parseInt($(element).height() * ratio); - var newWidth = parseInt($(element).width() * ratio); - - $(element).css('height', newHeight).css('width', newWidth); - }); - } - - // shrink pre (non-wrapping) text - // (do this after the page has shrunk, in case that fixed it already) - content.find('pre').each(function(i, element) { - var pre = $(element); - var code = $(pre.find("code")); - var codeWidth = code.width(); - var preWidth = pre.width(); - - var nominalWidth = preWidth - - parseInt(pre.css('padding-left')) - - parseInt(pre.css('padding-right')); - - if (codeWidth > nominalWidth) { - var ratio = nominalWidth / codeWidth - .035; // extra 3.5% for luck - var percent = "" + parseInt(ratio * 100) + "%"; - console.log("Shrinking code by " + percent); - code.css('font-size', percent); - pre.css('line-height', percent); - } - }); -}); -}, 500); diff --git a/sites/workshop/styles.css b/sites/workshop/styles.css deleted file mode 100644 index f9ce058c0..000000000 --- a/sites/workshop/styles.css +++ /dev/null @@ -1,23 +0,0 @@ -.centereverything { - text-align: center; -} - -.slide-hilite { - text-color: red; -} - -.big-text { - font-size: 150%; -} - -.table-code { - font-family: Bitstream Vera Sans Mono; -} -li { - padding: 8; -} - -td { - font-size: 32px; - padding: 8; -} diff --git a/sites/workshop/ta_cheat_sheet.md b/sites/workshop/ta_cheat_sheet.md new file mode 100644 index 000000000..120463dc1 --- /dev/null +++ b/sites/workshop/ta_cheat_sheet.md @@ -0,0 +1,38 @@ +First, thanks for volunteering your time to TA a workshop. Here we've tried to +capture tips to make your experience, and your student's, better. + +# First, Do No Harm + +The most important thing you can do as a TA is to make people +feel they are in a safe learning environment. The worst outcome +is for a student to leave frustrated, confused, or belittled and never wanting +to try programming again. + +# Troubleshoot Problems + +The most important job of a TA is to help students who are stuck on a step so +that the whole class does not have to wait for them. + +## Make yourself available for questions and troubleshooting + +Some amount of struggling is part of learning, but if someone looks frustrated, +offer a simple "Anything I can help with?" At the same time, try not to hover. + +# Help the Teacher Read the Room + +In general, teachers, especially new ones, tend to go too quickly. If you can +tell students are confused, try offering to repeat your own explanation +of a concept. Even just hearing it again can help and spark new questions. + +If you can tell someone is flying through the material (which often manifests +as impatiently checking their phone as they wait for others) casually mention they +are free to move up to the next level class if this class is moving too slowly. + +# Help with Logistics + +Volunteer to keep track of when it's time for breaks. This is super helpful! + +# Learn from Your Teacher + +Most teachers begin as TAs. Note what does and doesn't work about the class and +use that if you get the opportunity to teach. diff --git a/sites/workshop/teacher_cheat_sheet.md b/sites/workshop/teacher_cheat_sheet.md new file mode 100644 index 000000000..00ce3a932 --- /dev/null +++ b/sites/workshop/teacher_cheat_sheet.md @@ -0,0 +1,120 @@ +First, thanks for volunteering your time to teach a workshop. Here we've +tried to capture tips to make your experience, and your student's, better. + +# First, Do No Harm + +The most important thing you can do as a teacher is to make +people feel they are in a safe learning environment. The worst +outcome is for a student to leave frustrated, confused, or belittled and never +wanting to try programming again. + +# Plugging In, Setting Up + +## Teacher & TA Huddle + +It can be really helpful to talk with your TAs briefly before you begin and set +the tone that your classroom will be a collaborative experience, not just a +lecture. If any of them are up for it, you can have them teach a section or even +just explain or diagram a tricky concept like databases. But don't push people +if they aren't ready. + +## Laptop Setup +* Get your screen on the monitor or projector. This is never 100% foolproof! +* Zoom your browser and text editor fonts. +* Disable any potential popups like work email, etc + +## Try to Put Yourself in a Student's Shoes + +If you code all day for your job, it's easy to switch windows without stating +your intent, and use all sorts of timesaving shortcuts without even realizing +it. + +* Always annouce keyboard shortcuts and commands, along with +where to run them. Beginner students often get don't know +the difference between bash, IRB, the Rails console, etc. +* If you want, you can try +[KeyCastr](https://github.com/keycastr/keycastr) which will display shortcuts as you +type but be careful if you type passwords ;-) +* Be explicit: "Let's go back to our text editor and find +the topics.css file in the app/assets/stylesheets folder so we +can change the style." + +# How to Begin the Class + +Go around the room and have people introduce themselves. You can use +icebreaker techniques like having people name their favorite SF structure, +to use one example. + +It's also tremendously helpful if everyone states what they want to learn. +This can be really useful in higher levels to tailor the content to +what the students want. + +If it's clear that the class is generally at a lower level that the color, slow +down accordingly. It's also a good time to remind them that anyone is free to +change levels at any time, especially if it sounds like some students are +already at a higher level. + +# Roadmap + +* Many people like to know the destination before they begin the journey so +don't skip the "Objectives" part of each session +* The less advanced levels won't finish the entire curriculum so it's good to +tell them that upfront and remind them they can come back next time or try the +rest on their own + +# Pacing +* Hint: almost all new teachers go too fast +* Repeat, repeat, repeat +* Stop a lot so that the slowest person can catch up to the fastest +* If almost everyone is done, ask a TA to help any students who are stuck and +move on +* You can also use pauses while TAs are helping troubleshoot to ask questions +or suggest things to try. + +# Ask Questions! +* "What do you think will happen when I run this command?" +* Give generous pauses (count to 10!) after asking questions +* Don't be afraid to call on people by name +* Simply asking "Does everyone understand?" or "Any questions?" tends to +mask confusion. Be specific! + +# Whiteboards +If you have whiteboards, take advantage of them. Ask a TA to write +out new vocab there so it won't disappear as you move through slides. + +Many people are visual learners so diagrams can be very helpful too. + +# Breaks! +* Don't forget about the breaks. It's easy to get into flow +and forget. +* Use a timer on your phone or ask one of the TAs to be a Timekeeper +* Try to politely leave the room yourself. It can be easy to spend all the breaks +answering questions. Teaching is a ton of work. Give yourself a break too! + +# Wrapping Up + +* Set aside 10-15 minutes at the end for questions +* Hopefully you've got at least some students who are hooked by this whole +world of coding and want to learn more so share your favorite resources +* Ask the TAs and students to do the same +* Many students use online tutorials and can recommend good ones + +## Final Words + +* Remind them how much they accomplished +* Encourage students and volunteers to come to the after-party +* Encourage everyone to come back again +* Encourage them to stick around for the final presentation and retro + + + +# Prep + +The best prep is to read through the curriculum and do it yourself. + +# More Resources + +If you have time, you can read the full slide deck that this doc was adapted +from, as well as these general links about teaching. + +* [Teacher & TA Training Slide Deck](more_teacher_training) \ No newline at end of file diff --git a/sites/workshop/teacher_training.deck.md b/sites/workshop/teacher_training.deck.md deleted file mode 100644 index 72e2b8db5..000000000 --- a/sites/workshop/teacher_training.deck.md +++ /dev/null @@ -1,313 +0,0 @@ - -# Teacher Training - - -# What are we doing here? -* Over the next two hours, we will share advice about how to teach/TA a Railsbridge Open Workshop. -* This will be a conversation, not a lecture. - * You will share your experiences and advice -* (yes, this is a sneaky way of modeling how you should teach your class) - -## Make sure to go over the entire curriculum tonight -* to make sure there are no surprises -* if you can, do it with another teacher - - -# Who are you? - -* Who has taught before? -* Who has TA'd before? -* Why are you here? - - -# What are the goals of the Railsbridge Open Workshop? - -* Teaching Ruby on Rails? - * yes, but... -* Teaching programming? - * yes, but... -* Teaching that code is fun? - * Yes! -* increase number of female programmers and teachers -* show that programming is totally not scary - - -# What special challenges do we face as teachers of women? -* Madeline Kunin's research: women self-filter more than men - - -# Stress vs. Learning -* Excitement is good, but stress is bad for learning -* Before anyone touches a keyboard, set up a welcoming classroom environment. - -# How to set up a welcoming classroom environment? - -Discuss! - - -# How to set up a welcoming classroom environment? 1 - -* Smile `:-)` - * but don't be creepy `>;-{` -* make eye contact -* Tell people how long to expect the process to take -* Admit your ignorance -* Tell people it's ok to make mistakes -* Tell people to take breaks when you're frustrated - -# How to set up a welcoming classroom environment? 2 - -* Assume that anyone you're teaching to has 0 knowledge but infinite intelligence -* Check in a lot -* Use normal language over jargon -* Call newly arrived people in to circle or table -* Ask new people to introduce themselves - -# How to set up a welcoming classroom environment? 3 - -* Say up front that it'll be a collaborative learning environment - * Encourage students to answer each others questions -* Ask students "What do you want to get out of class?" - * Maybe write those on a board -* Keep track of students' backgrounds and call back during the class - * "Oh, you're a DBA, you'll like this part" -* Humor - -# How to set up a welcoming classroom environment? 4 - -* Students have diverse backgrounds - * Treat them with respect and humility - * Try to learn from your students' expertise -* Don't say "no" - * say "you're heading in the right direction" - * or "yes, and" -* Don't say "any questions?" - * say "What questions do you have?" -* ...? - - -# Introductions - -* Give people a chance to get to know each other in a casual way. -* Go around the table and ask people to share something about themselves - * name - * experience or profession - * why are you here? - * (better than "what do you want to get out of this?" for beginners) -* Encourage questions & discussion -* Tone: be approachable - - - -# Icebreakers -* Include something weird or meaningless - * What's your least favorite ice cream flavor? - * What one weird thing are you good at? - * Tell us one fact about yourself -- "keep it light" - * What's the first concert you went to? -* Pair up for 1 minute, then introduce your partner -* Remember the point is to get them confident at speaking out loud - - -# HOWTO Encourage Women in Linux - - -* Women have fewer opportunities for friendship or mentoring -* Don't take the keyboard away -* Do give directions and explain them clearly -* Don't make sexual advances towards women -* Don't criticize too much -* Do compliment - - -# How to introduce yourself to the class? -As a teacher or TA... - -* tell your story -* tell why you like programming and teaching. -* beware of bragging. -* tell why you care! Why are you getting up on saturday morning to come in here? - - -# What else to cover before starting class? -* Ground Rules -* Schedule -* Installation -* Logistics -* Web Resources - - -# Ground Rules - People may have missed these during the opening session, so briefly cover... - - * Questions are always welcome. Even dumb ones. - * What to expect from the day? - * Where is the bathroom? - * When is lunch? - * Where is the afterparty? - - -# Schedule - -* Everybody comes together for opening session -* Split into smaller groups for morning and afternoon teaching sessions - * This can be chaotic so make sure your students can find you -* Everyone back together for closing session - * Make sure your students know a closing session is coming so they don't run off - - -# What if a student has install trouble? -* Do **not** stop teaching to debug it -* Ask the student to pair up with her neighbor until the next break -* Ask a TA to take a look at it when they get a chance - - -# Web Resources -You should be aware of these, even if you don't tell the students all of them right away. - -* Diagrams: - * REST - * MVC - * git -* Workshop Github repo (slides and diagrams) - * - - -# General Teaching Advice - - -# How to work one-on-one with students? -* Never grab someone's keyboard - * This can be offputting and even scary -* Use your words! - * Instead, ask "Do you mind if I type?" or just "May I?" -* How else...? - - -# How should the teacher respond to the first few questions? -* Positively! - * i’m glad you said that - * what an interesting question - * great question - * i’ve wondered that myself - - -# Questions are good -* Get people comfortable asking questions -* Ask for questions (even dumb ones) - * Later in the workshop, anticipate questions if possible -* when a question is posed, let another student answer it first - * lay this expectation out explicitly at the beginning that they should try to answer each other’s questions - - -# Why might students *not* ask questions? - * Vocabulary (they don’t know what methods are, etc.) - * Insecurity - * ...? - - -# Pacing -* Slow down! -* if you’re excited/nervous, you will be talking too fast -* talk sssssslllllloooooowwwwwwllllllyyyyyyyy. -* Wait much longer than you feel is comfortable for questions/comments - * leave a space for them to ask questions - * Also make sure to wait for an answer after you ask a question - * Count to ten (silently) - * Chances are, someone else will fill the void - - -# Pacing (cont.) -* Ask about pacing. Ask for feedback. -* emphasize: this is a learning process -* get the students talking to each other - - -# What if a student feels the class is going x? -* Talk to her privately -* Suggest moving up from noob to post-noob -* Suggest field promotion to TA - - -# How to effectively use TA's? - -* have them debug/look at students’ computer screens -* pair them with the n00best/most shy students -* encourage TAs to answer questions - * TA's explanation might resonate with a student where yours didn't -* Treat student like customer - * What is underlying concern? - - -# Pair programming -- yea or nay? -* Pros? -* Cons? - - -# Install Fest -* If you go to the install fest, introduce yourself as a TA -* "I'll be here to help you tomorrow" - - -# More general teaching tips -* Don't think about what you're going to do in the class, think about what they're going to do. -* tell them what you’re going to tell them, tell them, and tell them what you told them -* ...? - - -# How do you check their understanding? - -* Some things that don't work: - * "Stop me if you don’t understand." (because they’ll be too shy) - * "Do you get it?" (because it’s too easy to say yes when the answer is no) - -* What works? - - -# How do you check their understanding? (more) - -* ask them a question about the material. but how to do this without putting them on the spot? -* ask questions about something that has already been built on top of. Spiral. -* keep an eye on body language/facial expressions - * "i see some confusion. let’s get a question." -* pairing off and discussing/explaining -* ask a less binary question to check understanding - * how would you do ___ - * if i want to do this, what would i do? - - -# How do you check their understanding? (more) -* teach basic concept, then ask them to apply it -* explain while loops, have them build one -* pair them off and debug -* ok to ask the same question twice with slightly different variables -* don’t let the most advanced student dictate the pace - - -# Teaching Programming - - -# How would you explain these concepts to a total beginner? - -* What is a variable? -* What is the difference between a string and a number? -* What is a comment? -* What is a function? - - -# More advice on teaching programming - -* Ruby sometimes makes things magical by doing things for you. But this magic obscures the underlying patterns for beginners. Don’t get into optional parameters. If something can be left out in a function call, DON’T leave it out. -* Before you have them do anything, first explain the big picture of what they’re about to do. - * E.G.: before typing in the command to deploy to heroku, explain the difference between localhost and heroku. -* Diagrams are helpful! Draw how all the pieces connect together. - - -# Now You Do It -* It's time to split up into smaller groups and go through all the materials -* Together with a partner (or two or three), do the whole workshop -* Make note of problems or questions so we can discuss them at the end - - -# See you next weekend! -And thanks for volunteering! diff --git a/sites/workshop/teaching_tips.md b/sites/workshop/teaching_tips.md new file mode 100644 index 000000000..8dcd35665 --- /dev/null +++ b/sites/workshop/teaching_tips.md @@ -0,0 +1,31 @@ +## General Teaching Advice + +When planning, don't think about what _you're_ going to do. Think about what _they're_ going to do. + + +## Metaphors and Stories for Teaching Programming: + +A program is like a recipe. There's two parts: a list of ingredients and a list of instructions. Think of code as the instructions and data as the ingredients. + +Also, to extend the metaphor, think about doubling a recipe: the instructions don't change even though the data are different this time. + +Computer memory is like a warehouse with lots of boxes. Each box has something inside (the data) and it also has a label (the variable name). + +The command line as Zork. You are "in" a room (a directory) and can either act on things in the room (commands) or move to a new room (cd). It's very important that you know where you are! Learn how to read the command line, and do an "ls" (or "dir") to look around. + +# Notes +* Have one volunteer be a runner to remind people when it's 10 minutes until the next break. (This worked really well.) +* Make sure all of the projectors have Mac adaptors +* Handouts with all of the code that people need to type +* Tell people at the beginning that the slides are online +* Have a volunteer leader, make sure they are distributed evenly +* Advertise that the Friday night setup is **required**, send notes out 1 week ahead of time, so people can get started ahead of time +* USB keys and/or DVDs with the big stuff like XCode +* Ask the venue about bike policy (can participants bring bikes inside?) and communicate that to participants ahead of time. +* Get a good night's sleep before the class, it is important to be well rested and fresh when teaching or TA'ing. +* Start with live coding - watch and type along, don't even use slides. make sure students & teachers irb prompts display line numbers, so we can say go to line 32 +* Go into IRB to practice basic concepts first, don't discuss what agile and a variable is in the abstract right away. Learning by doing first and talking later worked better. +* At the very beginning, show a designed version of app so people can recognize it as finished product like other web sites they see: screenshots are on Sarah Allen's site. +* Have TAs wear different badge colors so they can recognize each other. +* Discuss rubyisms where appropriate. use code to show elegance & readability of ruby vs other languages: each vs (for i; i = "lame"; i ++) +* Ask students to discuss what they learned, what they thought of it, at end of the day. As beginning of wrap-up session or just before it. diff --git a/sites/workshop/teaching_tips.mw b/sites/workshop/teaching_tips.mw deleted file mode 100644 index ba2b344e4..000000000 --- a/sites/workshop/teaching_tips.mw +++ /dev/null @@ -1,31 +0,0 @@ -== General Teaching Advice == - -When planning, don't think about what ''you're'' going to do. Think about what ''they're'' going to do. - - -== Metaphors and Stories for Teaching Programming: == - -A program is like a recipe. There's two parts: a list of ingredients and a list of instructions. Think of code as the instructions and data as the ingredients. - -Also, to extend the metaphor, think about doubling a recipe: the instructions don't change even though the data are different this time. - -Computer memory is like a warehouse with lots of boxes. Each box has something inside (the data) and it also has a label (the variable name). - -The command line as Zork. You are "in" a room (a directory) and can either act on things in the room (commands) or move to a new room (cd). It's very important that you know where you are! Learn how to read the command line, and do an "ls" (or "dir") to look around. - -= Notes = -* [[Workshop Advice]] -** Have one volunteer be a runner to remind people when it's 10 minutes until the next break. (This worked really well.) -** Make sure all of the projectors have Mac adaptors -** Handouts with all of the code that people need to type -** Tell people at the beginning that the slides are online -** Have a volunteer leader, make sure they are distributed evenly -** Advertise that the Friday night setup is '''required''', send notes out 1 week ahead of time, so people can get started ahead of time -** USB keys and/or DVDs with the big stuff like XCode -** Ask the venue about bike policy (can participants bring bikes inside?) and communicate that to participants ahead of time. -** Start with live coding - watch and type along, don't even use slides. make sure students & teachers irb prompts display line numbers, so we can say go to line 32 -** Go into IRB to practice basic concepts first, don't discuss what agile and a variable is in the abstract right away. Learning by doing first and talking later worked better. -** At the very beginning, show a designed version of app so people can recognize it as finished product like other web sites they see: screenshots are on Sarah Allen's site. -** Have TAs wear different badge colors so they can recognize each other. -** Discuss rubyisms where appropriate. use code to show elegance & readability of ruby vs other languages: each vs (for i; i = "lame"; i ++) -** Ask students to discuss what they learned, what they thought of it, at end of the day. As beginning of wrap-up session or just before it. diff --git a/sites/workshop/topics.mw b/sites/workshop/topics.md similarity index 60% rename from sites/workshop/topics.mw rename to sites/workshop/topics.md index f3c7ab4d6..164cbb3f5 100644 --- a/sites/workshop/topics.mw +++ b/sites/workshop/topics.md @@ -1,7 +1,7 @@ -=== Explain what they installed last night === +### Explain what they installed last night The Friday night install process is very cut-and-paste, and people new to Ruby don't understand any of the things that they did. Show them the components (git, github, ruby, rails, heroku) and explain what each is for and how they fit together in plain language and ''why'' each one is important. Diagrams can help. Don't assume beginners have any context: for example, git is not the only version-control software out there, it is just one way to do it. -=== Make sure they know basic *nix commands === +### Make sure they know basic *nix commands * Remember that some people don't yet know that when you're at the command line, you're actually sitting in a folder, and when you change a directory it's like moving into a folder with a GUI. This is NOT obvious if you've never used it before. * If they are learning this for the first time, give them a little challenge like: go into the Desktop folder, create a subfolder and then cd into it. Use pwd to prove you're in the right place. * Check in to make sure everyone knows at least the following unix commands: cd, ls, mkdir, irb, pwd. @@ -9,44 +9,41 @@ The Friday night install process is very cut-and-paste, and people new to Ruby d curl -O http://whatever.com/text.txt * (Someone who knows DOS should write the equivalent of this section for DOS) -=== Explain other foundational concepts. === +### Explain other foundational concepts. * How does a web server work? * What is versioning? Why would you need to use it? * What is the difference between running code at the command line and running code out of a file? * What is the difference between Ruby and Rails? -See [https://github.com/railsbridge/workshop/blob/master/basics/foundational_skills.md] for a slide set describing the basic skills required to be a programmer that don't involve writing code. +See [Foundational Skills](foundational_skills) for a slide set describing the basic skills required to be a programmer that don't involve writing code. -=== Slides === +### Slides -The Railsbridge Workshop slides are (mostly) in a GitHub repository. They're in a "lightweight markup" file format (Markdown and Showoff) so they're easy for you to read and edit. +The Railsbridge Workshop curriculum lives on http://docs.railsbridge.org, which is backed by the code in http://github.com/railsbridge/docs -* http://github.com/railsbridge/workshop - -To present the slides, check out the github repo on your own laptop and follow the [http://github.com/railsbridge/workshop/blob/master/README.md README] instructions. - -Here's a whiteboarded roadmap/flowchart of the slides: +Here's a whiteboarded roadmap/flowchart of the old slides: * http://www.flickr.com/photos/alexchaffee/5885335854 Contact Sarah Allen (sarah _at_ ultrasaurus _dot_ com) and send your github name if you want to be a collaborator. Or just fork the github project and send a pull request when you've made changes. -=== Ruby Language === -* For novices group: [[beginners guide to programming]] -* [[Programming intro|Intro to Ruby with IRB]] +### Ruby Language +* For novices group: [Ruby for Beginners](ruby_for_beginners) +* [Ruby for Programmers](ruby_for_programmers) -==== Key Concepts ==== +#### Key Concepts * everything is an object * everything returns something * open classes * blocks * symbols -==== Additional Notes for Programmers ==== + +#### Additional Notes for Programmers * http://gist.github.com/190567 * http://betterexplained.com/articles/starting-ruby-on-rails-what-i-wish-i-knew/ -=== Rails === +### Rails -* [[Rails 3 Curriculum|Building a Web Application with Rails]] (updated for Rails 3) -* [[Rails_3_Curriculum_Part_2 | Workshop 2 Curriculum]] -* [[ Workshop_in_Spanish | Rails 3 Curriculum - Spanish Workshop ]] +* Intro to Rails (Suggestotron) Curriculum +* Job Board Curriculum +* Message Board Curriculum diff --git a/sites/workshop/rest.deck.md b/sites/workshop/web_apps.deck.md similarity index 84% rename from sites/workshop/rest.deck.md rename to sites/workshop/web_apps.deck.md index 0ab3c3952..08d0b5bdb 100644 --- a/sites/workshop/rest.deck.md +++ b/sites/workshop/web_apps.deck.md @@ -1,3 +1,11 @@ +!SLIDE +## Web App Network Architecture +![](img/web-application.png) + +!SLIDE +## Web App MVC Architecture +![](img/mvc_simple.png) + !SLIDE # REST * Representational State Transfer diff --git a/sites/workshop/welcome.deck.md b/sites/workshop/welcome.deck.md index dc3b66b09..736f26a64 100644 --- a/sites/workshop/welcome.deck.md +++ b/sites/workshop/welcome.deck.md @@ -31,11 +31,6 @@ We want the community of software developers to reflect the diversity of our soc # What is Ruby on Rails? -!SLIDE image -# Web Application Plumbing - -![Web App](img/web-application.png) - !SLIDE center # Ruby vs. Rails @@ -69,9 +64,6 @@ We want the community of software developers to reflect the diversity of our soc * Test Driven Development (TDD) * Minimal code - maximal effect - - - # Agile development @@ -88,7 +80,7 @@ We want the community of software developers to reflect the diversity of our soc # Don't be shy! * Ask questions! -* TAs and teachers are here to +* TAs and teachers are here to help # Tools we'll be working with @@ -96,7 +88,7 @@ We want the community of software developers to reflect the diversity of our soc * **rake**: like make for Ruby. An easy way to run tasks. * **git**: source code control. * **database**: we'll use SQLite, but could be any relational database. -* **editor**: KomodoEdit (?) +* **editor**: Sublime Text 2 * **heroku**: free Rails hosting. @@ -110,6 +102,3 @@ We want the community of software developers to reflect the diversity of our soc * understanding the Ruby programming language * Rails * learn tools and patterns for building web apps in Ruby on Rails - ---- -Proceed to next slide deck: [Today's Workshop Info and Schedule](current) diff --git a/sites/workshop/workshop.md b/sites/workshop/workshop.md index 80799c002..72f39e8ff 100644 --- a/sites/workshop/workshop.md +++ b/sites/workshop/workshop.md @@ -1,19 +1,42 @@ -# Slide Decks: +# Materials for Teachers -## All-Hands Workshop Intro: -* [Welcome](welcome) -* [Current](current) -* [Closing](closing) - -## Breakout Sessions: * [Foundational Skills](foundational_skills) * [Ruby for Beginners](ruby_for_beginners) * [Ruby for Programmers](ruby_for_programmers) -* [REST](rest) -* [Suggestotron Curriculum](/curriculum) +* [Diagrams (Git, MVC, REST) ](diagrams) +* [Activities](activities) + +### Teacher Training +* [Teacher Training Deck](more_teacher_training) +* [Teacher Training Deck 2016](more_teacher_training_2016) +* [Teacher Cheat Sheet](teacher_cheat_sheet) - A TL;DR version of the deck +* [TA Cheat Sheet](ta_cheat_sheet) - A doc geared especially for first-time TAs + +# Materials for Organizers + +### Workshop Intro & Closing Presentation Slide Decks + +If you can edit HTML, this is the presentation for you. It's the prettiest: + +* [Welcome and Closing Reveal.js deck (zip file)](http://cl.ly/0T341w3X130q) -## Workshop Prep: -* [Teacher Training](teacher_training) +Download, then open up the index.html file in a text editor. Edit pages 0 +(dates, location, logo), 1 (sponsor logos), and 7 (after party location), and +you're good to go. (Run it locally for the presentation itself.) + +#### Or copy one of these other formats: + +Google Docs presentations + +* [Welcome (google doc)](https://docs.google.com/presentation/d/1VT8J6CTuN8ot_-0ZElLv49_-cxuNmXTp83DBonD1x5w/edit#slide=id.p) +* [Closing (google doc)](https://docs.google.com/presentation/d/19ik5tm_enCNRIM4zaY9rIoeRhDoMMfFUDgNXnd2lW6A/edit#slide=id.p) + +deck.rb + +* [Welcome](welcome) +* [Closing](closing) +Or make a presentation in the format of your choice. Powerpoint, Keynote, your own [reveal.js](http://lab.hakim.se/reveal-js/) deck — follow your heart! -See the [toc] link in the top right for a full table of contents. +# Other? +See the Table of Contents for a full list of materials. diff --git a/sites/workshop/workshop_todo.mw b/sites/workshop/workshop_todo.mw deleted file mode 100644 index bef6ebe4b..000000000 --- a/sites/workshop/workshop_todo.mw +++ /dev/null @@ -1,22 +0,0 @@ -(Note - these aren't all my personal to-dos - I just put a wiki signature on them so each one has a datestamp when I enter it.) -== TO DO == -* a mailing list for workshop participants, create a place for people -to follow-up where people will be able to ask questions of other folk -who have the same context and who they might know -* Plan a "hack session" in the next couple of weeks for -workshop participants/teachers/mentors -* Add link to flier, put the source files somewhere -* Talk to pragmatic programmers about getting copies of Chris Pine's book --[[User:Sarahmei|Sarahmei]] 01:43, 10 May 2009 (UTC) -* Choose a license: MIT or CC? -* Find a volunteer lead -* hand outs (online or paper?) -** outline, plus code that people need to type -** resources for after railscasts, peepcode and rails guides. MikeG maintains some pointers at http://railsircfaq.com/ as well. - -== Finished == -* Meetup.com - schedule actual meetup --[[User:Sarahmei|Sarahmei]] 01:36, 10 May 2009 (UTC) -* Follow up with Heather re: Addison-Wesley --[[User:Sarahmei|Sarahmei]] 02:55, 10 May 2009 (UTC) -* Email all meetup members --[[User:Sarahmei|Sarahmei]] 14:13, 11 May 2009 (UTC) -* Email women 2.0 folks --[[User:Sarahmei|Sarahmei]] 14:13, 11 May 2009 (UTC) -* Follow up with Elaine from sf-wow --[[User:Sarahmei|Sarahmei]] 04:34, 12 May 2009 (UTC) - diff --git a/spec/app_deck_spec.rb b/spec/app_deck_spec.rb new file mode 100644 index 000000000..2a375105f --- /dev/null +++ b/spec/app_deck_spec.rb @@ -0,0 +1,44 @@ +require "spec_helper" + +require_relative "../app" +require "rack/test" + +describe InstallFest do + include Rack::Test::Methods + + # todo: move to shared module + def get! *args + get *args + expect(last_response.status).to eq(200) + end + + def app + Rack::Builder.parse_file(File.join(File.dirname(__FILE__), '..', 'config.ru')).first + end + + describe "an app with slides" do + require "deck" + before do + here = File.expand_path(File.dirname(__FILE__)) + allow(Site).to receive(:sites_dir).and_return(File.join(here, 'sites')) + end + + it "renders a deck" do + get! "/meals/breakfast" + # rendered_breakfast = Deck::SlideDeck.new(:slides => Deck::Slide.split(@breakfast)).to_pretty # for some reason it's not rendering pretty from the app even though the app uses .to_pretty + expect(last_response.body).to include("Raisin Bran") + end + + # todo: include deck.js source right inside the HTML + it "serves up deck.js and other public assets" do + get! "/deck.js/core/deck.core.js" + expect(last_response.body).to include("Deck JS - deck.core") + + get! "/deck.js/jquery-1.7.2.min.js" + expect(last_response.body).to include("jQuery v1.7.2 jquery.com") + + get! "/assets/application.css" + expect(last_response.body).to include("/* Code ray css */") + end + end +end diff --git a/spec/app_spec.rb b/spec/app_spec.rb index 9624c27fa..910d7efed 100755 --- a/spec/app_spec.rb +++ b/spec/app_spec.rb @@ -1,89 +1,112 @@ -here = File.expand_path File.dirname(__FILE__) -require "#{here}/spec_helper" +require "spec_helper" -require "#{here}/../app" +require_relative "../app" require "rack/test" # todo: use a dummy set of sites instead of the real "installfest" and "curriculum" +# Here we need to use a trick to get ahold of the real Sinatra app instance +# because Sinatra uses dup on every call +class ::InstallFest + def dup *args + @@latest_instance = super + end + + def self.latest_instance + @@latest_instance + end +end + describe InstallFest do - include Rack::Test::Methods + include Rack::Test::Methods # see http://www.sinatrarb.com/testing.html def app - @app ||= InstallFest.new + InstallFest end # find the actual InstallFest app, discarding Rack middleware def true_app - true_app = app - until true_app.is_a? InstallFest - next_app = true_app.instance_variable_get(:@app) - break if next_app.nil? - true_app = next_app - end - true_app + InstallFest.latest_instance end def get! *args get *args - assert { last_response.status == 200 } + follow_redirect! while last_response.redirect? + expect(last_response.status).to eq(200) end - + it "is a sinatra app" do - assert { true_app.is_a? InstallFest } - assert { true_app.class.ancestors.include? Sinatra::Application } + get '/' + expect(true_app).to be_a(InstallFest) + expect(true_app.class.ancestors).to include(Sinatra::Application) end it "redirects / to the default site" do - get "/" - assert { last_response.redirect? } - follow_redirect! while last_response.redirect? - assert { last_request.path == "/installfest/" } + get! "/" + expect(last_request.path).to eq("/docs/") end it "redirects /site to /site/" do - get "/installfest" - follow_redirect! while last_response.redirect? - assert { last_request.path == "/installfest/" } + get! "/installfest" + expect(last_request.path).to eq("/installfest/") end it "redirects /site/page/ to /site/page" do - get "/installfest/foo/" - follow_redirect! while last_response.redirect? - assert { last_request.path == "/installfest/foo" } + get! "/installfest/linux/" + expect(last_request.path).to eq("/installfest/linux") end it "has a default site" do - assert { true_app.default_site == "installfest" } + expect(true_app.default_site).to eq("docs") end describe "settings" do # note: I'd rather pass settings into the constructor, but Sinatra uses that interface (for a downstream app) - it "accepts default_site via setter" do - true_app.default_site = "curriculum" - assert { true_app.default_site == "curriculum" } - end + before { get '/' } - it "accepts default_site via Sinatra set" do - pending "figure out Sinatra set" - InstallFest.set :default_site, "curriculum" - @app = InstallFest.new - assert { true_app.default_site == "curriculum" } - end + describe "learns the locale from" do + it "the locale parameter" do + true_app.params = {locale: 'es'} + expect(true_app.dynamic_locale).to eq('es') + end - it "can configure the sites_dir" do - foo_dir = dir "foo" - true_app.sites_dir = foo_dir - assert { true_app.sites_dir == foo_dir } + it "the l parameter" do + true_app.params = {l: 'es'} + expect(true_app.dynamic_locale).to eq('es') + end + + it "the subdomain" do + true_app.request = Rack::Request.new({"HTTP_HOST" => "es.example.com"}) + expect(true_app.dynamic_locale).to eq('es') + end + + it "the SITE_LOCALE environment var" do + begin + ENV["SITE_LOCALE"] = "es" + expect(true_app.dynamic_locale).to eq('es') + ensure + ENV["SITE_LOCALE"] = nil + end + end end end it "looks for a site named the same as the host" do - get "/", {}, {"HTTP_HOST" => "curriculum.example.com"} - assert { last_response.redirect? } + get "/", {}, {"HTTP_HOST" => "docs.example.com"} + expect(last_response).to be_redirect follow_redirect! while last_response.redirect? - assert { last_request.path == "/curriculum/" } + expect(last_request.path).to eq("/docs/") + end + + describe "in the 'es' locale" do + it "uses the same subdir as the sites_dir" do + get "/", locale: "es" + + es_dir = File.expand_path(File.join(__FILE__, "..", "..", "sites")) + expect(true_app.sites_dir).to eq(es_dir) + end + end describe "page headers" do @@ -96,62 +119,5 @@ def get! *args it "should contain the html5 doctype" do @body.should match(//i) end - - it "should render style tags without any attributes" do - @body.should match(/