Giter VIP home page Giter VIP logo

curriculum's People

Contributors

amandakievet avatar astasu avatar brandonbloom avatar bridgethillyer avatar cndreisbach avatar danielcompton avatar davidchambers avatar dependabot[bot] avatar dijonkitchen avatar iantruslove avatar jellea avatar joberding avatar joshhead avatar ku1ik avatar pernicat avatar rubygeek avatar saskali avatar sduckett avatar seancorfield avatar socksy avatar tkhquang avatar yokolet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

curriculum's Issues

Code example for leap-year incorrect

(defn leap-year?
  "ever four years, except those divisible by 100, but including those 
  divisible by 400"
  [year]
  (and (zero? (mod year 4))
       (not (zero? (mod year 100)))) ;;this line makes function work properly
       (or (zero? (mod year 400)))
           ;(not (zero? (mod year 100)))) ;; this line was included here in original solution
)

(leap-year? 1900) ;false
(leap-year? 2000) ;true

Unable to deploy to Heroku

Locally, everything is running just fine, but I keep getting an application error when I run heroku open as instructed in "Putting Your Application Online"

Things I've tried already:

  • In project.clj, updated Lein with :min-lein-version "2.0.0" as instructed on heroku dev center
  • In web.clj requirements, added [hiccup.form :as form] and [ring.adapter.jetty :as ring]
  • In project.clj, added :main ^:skip-aot global-growth.web
  • In web.clj, added (defn -main [] (ring/run-jetty #'main-routes {:port 8080 :join? false})

There was also talk of this being an issue with Procfile (there isn't one?), but I'm not sure what's going on. Any ideas? Let me know if you'd like me to copy in any of my heroku logs.

Related: Is there recommended documentation on clojure app deployment via github.io?

Thanks for all your help in advance! ๐Ÿ˜„

heroku keys:add missing for WIndows 7 & 8 install instructions

I needed to add

heroku keys:add %USERPROFILE%/.ssh/id_rsa.pub

to the instructions to get the student up and running. Otherwise Heroku didn't know who they were. heroku keys:add prolly works for most situations.

Happy to add this if it's helpful. Looks like both windows installs are missing them.

Heroku min-lein-version

After completing the project and following the deploy instructions for global-growth, Heroku's lein complains that it doesn't know how to lein with-profile production trampoline run. This was fixed for me by added :min-lein-version "2.0.0" to project.clj to force Heroku to use lein 2+ instead of 1.7. (The site worked great locally.)

Would love to hear if anyone else can replicate this. I'm using Nitrous.io so possible it's just me.

If confirmed this should likely be added to the clojurebridge lein template but could also be included in (or before) the deploy instructions. Happy to do the latter if needed.

println and LightTable

Because we are using LightTable, I find myself never using println in any examples. At a traditional REPL, we'd see the output of println, but in LightTable, println output shows in the console, which is hidden by default, and we see the results of calling a function inline.

Is this ok? Using println seems to be a big part of my day when developing, but I've just started using LightTable full time, and it might not be that important.

I'd love to see input from @bridgethillyer, @flyingmachine, @seancorfield, and @7maples on this.

Unable to `git push heroku master` until I add an SSH key

I ran through these instructions on my new MacBook, and git push heroku master failed until I added my new SSH public key to my Heroku account.

If it's possible to push to Heroku via HTTPS or there's some other way to avoid SSH key setup, that would be ideal.

Otherwise, we probably need to add some additional instructions to create and SSH key and add to Heroku. On the upside, the incremental effort to create and configure a GitHub account will be less.

Image links needs /curriculum/

Images in RevealJS presentation need /curriculum/ in its URL. For example, /img/instarepl.png doesn't exists, but /curriculum/img/intarepl.png does.
For this reason, the images don't show up on slides.

Need to decide on editor for curriculum

As I see it, there's four real choices: Nightcode, Catnip, Light Table, and Cursive Clojure. Here's the current knowledge about all four:

Nightcode

Simple Clojure IDE written in Clojure. Reasonably user-friendly.

  • Crossplatform? Yes; it's a Java .jar file that should run everywhere. It's not quite as nice as having a normal OS X or Windows executable.
  • Includes Leiningen? Yes, but doesn't expose all the tasks. If we make deployment a Leiningen task, we'll need lein installed separately.
  • Students can continue to use this afterwards for further Clojure projects.
  • Clinton's take: Not bad.

Catnip

Web-based editor, runs in the browser. Provided as a lein plugin.

  • Crossplatform? Yes; it's web-based.
  • Includes Leiningen? No. It's a lein task, so all Leiningen interaction would be at the command-line.
  • Pretty good, but doesn't stand up to very large projects well. Students would likely migrate to another editor if they kept using Clojure.
  • Clinton's take: The simplest solution.

Light Table

Next-generation Clojure (+ Javascript and Python) editor/IDE.

  • Crossplatform? Yes. Doesn't work with OS X 10.6 and earlier, though.
  • Includes Leiningen? Yes, but like Nightcode, you'd still need to install it locally, so it isn't a perfect integration.
  • Students could continue to use this afterward for further projects.
  • Still in beta.
  • Clinton's take: Still not reliable enough to use. Recommend using something else.

Cursive Clojure

Plugin for IntelliJ. A true IDE.

  • Crossplatform? Yes. Pretty heavy-weight, though; you wouldn't want to run it on an underpowered machine.
  • Includes Leiningen? Yes. Exposes it very well. Command-line not needed.
  • Students could continue to use this afterward for further projects easily. IntelliJ is well-known and loved among devs.
  • Still in beta.
  • Clinton's take: I use this professionally. It's the most complicated, but you don't have to know much to get started. I think this or Nightcode are the best options.

Create cheatsheet for students

Create a cheatsheet with key syntax, function names, etc. that students are using in the exercises. A simple one-pager that could be printed out and used by students to help guide them during the exercises.

Clarify usage of keywords in explanation of maps

In data_structures.md, the explanation of maps begins with an example using keywords:

{:first "Sally", :last "Brown"}
{:a 1, :b "two"}
{}

However, in the "Introduction to Programming in Clojure" in intro.md, the definition for keywords does not prepare students for this usage:

Keywords are the strangest of the basic value types, because they don't have a real world analog like numbers, strings, and booleans do. You can think of them as a special type of string, one that's used for labels. They are easiest to understand when we cover maps later, as they are most commonly used as keys in maps.

There should be example code added to the keywords definition in intro.md, so that students will be able to recognize keywords when they appear, and the maps definition in data_structures.md should be further clarified to point out which parts of the example represent keys, and which values, and also more explicitly define what role keywords play in this context.

To demonstrate: looking at the current maps example, me-a-year-ago wouldn't be sure whether :first "Sally" is a key, if :first is a key, if "Sally" is a key, or if all are valid as keys. If I wanted to retrieve the value "Brown", would I need to provide :first "Sally" in its entirety? Or, should I provide :last as the key, because the comma is slightly unclear? If I provided :first "Sally" as a key, would :last "Brown" be returned? How would I get rid of the :last label in that case? What's the difference between a "key" and a "keyword," anyway?

The curriculum currently attempts to demonstrate this using examples ("Let's look at some functions we can use with maps"), but this isn't entirely clear.

Update OS X setup with workaround for 10.6.8

Whatever was keeping Light Table from working on OS X 10.6.8 seems to have been resolved. So if you do the following, LT should be able to run:

"remov[e] key "LSMinimumSystemVersion" from Info.plist"

[comment] Exercise: Basic arithmetic

I am a person of metric system. In metric system world, people's hight is measured by "cm". (1m = 100cm)
Talking people's hight in meter sounds awkward to me.

Just a comment.

Switch "Infix vs. prefix notation" slide to a table

Currently, the "Infix vs. prefix notation" slide in Module 1 looks like this:

Infix: 1 + 2 * 3 / 4 - 5
Prefix: (- (+ 1 (/ (* 2 3) 4)) 5)
Infix: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
Prefix: (+ 1 2 3 4 5 6 7 8 9)

If you look at the rendered slide, though, that's pretty difficult to read.

Perhaps we should replace this with a table?

More typos on web.md

Typos on web.md

Line 8: Missing "the"
HTTP is the protocol for sending requests to ---[the]--- server and for the server to send responses to those requests.

Line 108: Should be "URLs" not "URLS"
The web browser gets to the right server with an address called a URL. Take a look at the following URL---[s]---:

Line 113: Extra "s"
If one web application---[s]--- handles all of that, it needs to track who takes care of what.

Suggested re-write of app.md

Please consider the following restructuring of this lesson. Some of the specific explanations may need revision, but I think overall it flows well and allows the student to build a working app as well as dig deeper into the functional components within the REPL for greater understanding. Please note it does assume some small changes to the current shell-project version of the global-growth app.

Making Your Own Web Application

The World Bank provides a data collection of world development indicators, showing the current state of global development, as well as a web API to provide access to this data. A web API is a way to provide access for one program to call another program over HTTP. In this case, the World Bank Indicators API provides access to their set of data.

We will use the World Bank Indicators API to explore some of the world development indicators for different countries. We will sort and compare certain indicators. This is a task that Clojure is well suited for.

Getting Started

  1. Launch Light Table
  2. Click File > Open folder, select the global-growth folder, and click the Upload button

You should now see global-growth/ at the top left of the Light Table screen. Click this to expand the directory structure, and then click core.clj to load the file in the main window.

This file has been pre-populated with 3 sections:

1. Namespace, dependencies, and constants

As seen in previous lessons, a namespace allows you to define new functions and data structures without worrying about whether the name you'd like is already taken.

Dependencies are just code libraries that others have written which you can incorporate in your own project. Click on project.clj from the directory structure in Light Table to view this file. Take a look at the :dependencies section, which includes entries like clj-http, a library that allows you to communicate over HTTP, and cheshire, another library that reads and understands the JSON format.

Click the core.clj tab at the top to go back to our main file. Notice the :require section, which is where the libraries loaded by project.clj are included and given aliases (shorter names) to be referenced later in our program when we need functionality that they provide.

2. Support functions

(defn remove-aggregate-countries
  "Remove all countries that aren't actually countries, but are aggregates."
   [countries]
   (remove (fn [country]
             (= (get-in country [:region :value]) "Aggregates")) countries))

(defn get-country-ids
  "Get set of country ids so we can filter out aggregate values."
  []
  (let [countries (remove-aggregate-countries (:results (get-api "/countries" {})))]
    (set (map :iso2Code countries))))

(def country-ids (get-country-ids))

These functions serve to build up our list of countries from data returned by the API to be used by our application. Like many well-written functions in Clojure, you can often get a good idea about what they are doing even if you don't understand exactly what each part means.

3. Web layout, controls, and routes

This is the part of the application that controls how your app will appear in the browser, and what content will be in each control (such as a drop-down menu). It also handles basic text and number formatting and what page to load based on the URL requested.

In the next section, we will add the code necessary to download, filter, and sort the data based on the selections made by the user on the web page. Before we get to that, however, let's see what happens if we run our application in its current state.

Enter this at the command line:

cd global_growth
lein ring server

BOOM! You should see some ominous looking output similar to this:

Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: get-api in this context, compiling:(global_growth/core.clj:23:57)
    at clojure.lang.Compiler.analyze(Compiler.java:6380)
    at clojure.lang.Compiler.analyze(Compiler.java:6322)
     ...

The informative part of this error is Unable to resolve symbol: get-api in this context on line 23, character 57. If you click on the core.clj file in Light Table, you will see a line number and character count in the bottom right corner. Placing the cursor on line 23, character 57 (or whatever number is in your error message) will show you the problem, which is the call to the get-api function. Since we have not yet written that function, the program cannot run. Let's do that now.

Calling the API

Our get-api function will take two parameters to hold the path in the API we are calling (such as "/countries") and any additional query parameters that need to be added to the API URL. You can put all of the above pieces into the let part of the function. You need: base-path, query-params, response, metadata, and results. Then return a map with keys :metadata and :results, each of which has that corresponding value.

Add the following code to your core.clj file right after the parse-json line near the top of the file.

;; WORLD BANK API CALLS
(defn get-api
  "Returns map representing API response."
  [path qp]
  (let [base-path (str base-uri path)
         query-params (merge qp {:format "json" :per_page 10000})
         response (parse-json (:body (client/get base-path {:query-params query-params})))
         metadata (first response)
         results (second response)]
  {:metadata metadata
   :results results}))

Define a var to hold the results of calling get-api for the countries endpoint.

(def countries (get-api "/countries" {}))

Notice that when we call get-api we are including the path and enabling the passing in of query-params to the API. Save your changes to core.clj.

Let's take a moment now to look at what is going on here. In order to really understand how the get-api function works, let's fire up the REPL and run through a few exercises.

REPL EXERCISE

Click the first line of the file (anywhere on the line with ns-global-growth.core) and then press cmd-shift-enter to load the project and any dependencies into the REPL session (you will see the activity in the lower left corner of the Light Table window while this is taking place).
Once everything is loaded up, press Ctrl-Space to bring up the search command box. Type "insta" and press enter when the "Instarepl: Open a Clojure instarepl" choice is highlighted.

You are now ready to execute functions.

As mentioned previously, we use the clj-http library to enable making calls over the internet with HTTP. In the Instarepl tab, type in the following line:

(require '[clj-http.client :as client])

Now let's walk through the components of the get-api function to see what each one does.
Go ahead and set the address that our application will use to reach the World Bank API by entering:

(def base-uri "http://api.worldbank.org")

Now whenever we need the address for the World Bank API, we can use base-uri instead of the longer URL.

Each type of data available through the World Bank API has an API endpoint. The endpoint has a specific path added to the base URL. http://api.worldbank.org/countries is the endpoint that lists all of the countries with data available. Create a var to refer to that URL by combining the base-uri and "/countries" by entering:

(def base-path (str base-uri "/countries"))

Now make a call using the get function (courtesy of the clj-http library) to the full API endpoint: http://api.worldbank.org/countries, which should result in a bunch of light blue text with country names and other values appearing just to the right of what you typed within Light Table.

(client/get base-path)

There, you've done it! You have successfully used Clojure to ask the World Bank for information and received back a response with the raw data. The other lines in the get-api function simply set more specific parameters for your query and assign values to vars for use elsewhere in the application.

This line,

query-params (merge qp {:format "json" :per_page 10000})

just means "please send me results in the JSON format, 10,000 values at a time."
The other two lines,

(def metadata (first response))
(def results (second response))

are used to assign the metadata, such as latitude and longitude for each country, and the actual indicator results, such as income level, for each country, respectively.

Filter the results

Click back to the core.clj tab, and we will continue building out our application. To add the filtering functions, place the cursor after the supporting function section, right after the following line:

(def country-ids (get-country-ids))

On the next line create a function called get-value-map where the two keys that we want to look up are parameters that can be passed to the function. Put the values into a map with "into {}"

;; FILTER THE RESULTS
(defn get-value-map
  "Returns relation of two keys from API response"
  [path query-params key1 key2]
  (let [response (get-api path query-params)]
    (into {} (for [item (:results response)]
                  [(key1 item) (key2 item)]))))

Now create another function called get-indicator-map.

(defn get-indicator-map []
  "Gets map of indicators.
  /topics/16/indicators:   All urban development"
  (get-value-map "/topics/16/indicators" {} :name :id))

Define a var to hold the results of calling get-indicator-map.

(def indicator-map (get-indicator-map))

Finally, create a function called get-indicator-all. The indicator name needs to be a parameter to the function. So should the data and two keys. We make these parameters so we will be able to get other indicators and pull out different pieces from the results.

(defn get-indicator-all
  "Returns indicator for a specified year for all countries"
  [indicator year key1 key2]
  (get-value-map (str "/countries"
                      "/all"
                      "/indicators"
                      "/" indicator)
                 {:date (str year)}
                  key1
                  key2))

REPL EXERCISE

To get a better grasp on what we have just accomplished, click back to the Instarepl tab and place the cursor on a new line after everything we typed in the previous exercise.

Go ahead and add in the cheshire library, which reads and understands the JSON format. Then we need a function to read (parse) the json we will receive back from the World Bank API. Passing the second argument as true tells the function to return Clojure keywords (i.e. :incomeLevel) back for the map keys.

(require '[cheshire.core :as json])

(defn parse-json [str]
  (json/parse-string str true))

Next, copy over the whole get-api function from core.clj, including the countries definition line, and paste in the Instarepl window on the line after the parse-json function.

We now have what we need (the ability to download and process the raw World Bank data) available in the Instarepl to allow us to walk through the components of the filtering functions.

Let's go through the raw data and pull out a couple values associated with keys in the results. You can use the for function to do this. We want to pull out the values for the "name" and "longitude" items from the results section. Those will have been turned into Clojure keywords by the cheshire json library, so you can refer to them as :name and :longitude. Add the following lines in the Instarepl:

(for [item (:results countries)]
      [(:name item) (:longitude item)])

Behold! A list of countries and their longitudes should appear alongside your function. Our function has taken the raw data set (countries) and returned only the specific information that we asked for, in this case country name and longitude. Look back at the get-value-map in core.clj to see how we are using similar logic to load up all of the indicator names and their values. To see this in action, copy the full get-value-map function from core.clj and paste it into the Instarepl.

"Hey, nothing happened!" That is because we simply added the function for use inside the Instarepl, but have not actually told it to run. Now let's put it to use. On the next line in the Instarepl, type:

(get-value-map "/topics/16/indicators" {} :name :id)

Voila! The names and id's of all the world's development indicators (all 16 of them) should appear next to your function.

You should be able to see now how the core.clj function get-indicator-all uses this logic to build a map of all the indicators and their values for each country. Go ahead and copy the full get-indicator-all function to the Instarepl so we can use it in the next exercise.

Sort the data

After the filtering section, create a new function called sorted-indicator-map.

;; SORT THE DATA
(defn sorted-indicator-map
  "Sort the map of indicator numeric values"
  [inds]
  (take list-size
        (sort-by val >
                 (into {} (for [[k v] inds
                                :when (and v (country-ids (:id k)))]
                            [(:value k) (read-string v)])))))

Save your changes to core.clj.

REPL EXERCISE

To help you understand what is going on here, click back to the Instarepl tab and place the cursor on a new line after everything we typed in the previous exercise.

Enter the following line:

(def inds (get-indicator-all "EP.PMP.SGAS.CD" "2012" :country :value))

Since EP.PMP.SGAS.CD is the indicator name for gas pump price, you will see a list containing value pairs that describe each country with keys :id and :value, followed by a value with the actual pump price.

For the next step, you will need to first copy over the "Supporting Functions" to the Instarepl, which we talked about earlier in this lesson. These are remove-aggregate-countries and get-country-ids, as well as the line where we define country-id. Go ahead and do that.

Now enter:

(for [[k v] inds
      :when (and v (country-ids (:id k)))]
    [(:value k) (read-string v)])

What this function does is take the list of countries (after the raw data has been processed by the supporting functions to remove aggregate countries and build a list of country names with matching id's), then creates a new list of gas pump price by country name. Next, load this list into a map by typing:

(into {} (for [[k v] inds
              :when (and v (country-ids (:id k)))]
            [(:value k) (read-string v)]))

Notice how instead of the previous ["name" price] ["name" price] ["name" price] format, the map of countries and prices looks like ```{"name" price, "name" price, "name", price ... }.

Lastly, sort the list by price (value), and return the top 10.

(take 10 
  (sort-by val >
      (into {} (for [[k v] inds
                    :when (and v (country-ids (:id k)))]
                  [(:value k) (read-string v)]))))

That is really all sorted-indicator-map is doing. Make sense?

Running the app

Now go back to your command line in the global_growth directory and enter:

lein ring server

After several seconds, you should see a couple lines of INFO output followed by:

Started server on port 3000

To view the running application, open your web browser and go to http://localhost:3000

Congratulations, you have created your first Clojure web application!

Opening file in Light Table from command line?

A few times at the SF workshop, the curriculum/slides/TAs guided students to cd into a certain directory--leading to the natural question, "How do I open this file/directory in Light Table from the command line?"

We should either have the Light Table equivalent of subl added to the installfest, or rewrite the sections that refer to cding into directories in the curriculum.

Clarify `associative?` vector function definition

In data_structures.md, the definition of the associative? function for vectors states,

It lets us know whether we can look up values in this collection using keys. We can with a vector; each item in the vector has a key starting with zero and going up that points to it. In our vector [:a :b :c], the key 0 points to :a, the key 1 points to :b, and the key 2 points to :c.

However, at this point in the curriculum, we have not yet described what a map is, nor have we defined what a key is, so this definition is problematic.

Proposed clarifications for web.md

For your consideration... old text in {{ }}, suggested text in << >>.

Introduction to Web Applications

How does the web work?

A web application is software that runs in a web browser (like Chrome, Firefox, Internet Explorer, Safari). It communicates with a server over the Internet that can do central processing and data storage. The browser is the client, or user interface, to the web app where users interact with it. The server does the heavy lifting.

The way that the client and server communicate with each other is through {{ a protocol called HTTP }} << the Hypertext Transfer Protocol, or HTTP >>. A protocol is a set of rules for communication. Think of the airport ground crew who use wands to communicate with the pilots taxiing the planes around the tarmac. They have a limited set of signals that mean specific things which guide what the pilot does. There are a set of guidelines for when and how the pilot will receive and respond to those signals. HTTP is the protocol for sending requests to the server and for the server to send responses to those requests.

A web app accepts an HTTP request from the client and gives it a response. Think of the server as just like the Clojure functions we have written. They take the input of a request and give the output of a response. In fact, we can write that function:

(defn app
  [request]

  {:status 200})

This function accepts 'request' as a parameter, then returns a map with a status of 200. In HTTP, a status of 200 means everything worked fine.

What is HTML?

When you bring up something in your web browser you want more than a status code, though. You want to see words and images and dancing gifs (ok, maybe not that). You want it to be easy to read and pleasant to look at. {{ HTML }} << The Hypertext Markup Language, or HTML, >> is what makes web pages look that way. It is a markup language, which is different from a programming language. It is a way to put tags around words that suggest to the web browser the way you want them to look.

Go to the ClojureBridge web site. Then in your web browser, view the source.

  • In Chrome, go to View - Developer - View Source
  • In Firefox, go to Tools - Web Developer - Page Source
  • In Safari, go to Safari menu - Preferences - Advanced - Check "Show Develop menu in menu bar." Then go to Develop - Show Page Source
  • In Internet Explorer, go to View - Source

Look at the parts that have angle brackets around them like <this>. Those are HTML tags. Search for <h1>. That is a tag that tells the browser to display the text until the close tag, </h1>, as a level one header. Look at the web page and see how that text displays.

Making the simplest web application

Let's make the simplest possible web application. It will say hello in the browser.

First, run lein new basic-app in the console << to create a new project >>.

{{ Create the file src/basic_app.clj and make it look like this: }}
<< Create a new file in the basic-app/src folder called basic_app.clj with the following contents: >>

(ns basic-app)

(defn app

  "Return a status."
  ;; A request comes in the handler.
  [request]

  ;; The handler returns a response map.
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body "Hello, world."})

{{ We need some supporting pieces to run this. We need the project.clj for Leiningen. }}
<< Now open up basic-app/project.clj and change that file so that it reads: >>

(defproject basic-app "0.1.0-SNAPSHOT"

  ;; We require ring.
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [ring "1.2.1"]]

  ;; We use the lein-ring plugin to start ring.
  :plugins [[lein-ring "0.8.10"]]

  ;; We tell Ring what our handler function is and
  ;; what port to start on.
  :ring {:handler basic-app/app
         :port 3001})

There are multiple dependencies in this file: Ring and Lein-ring. Ring is a Clojure library {{ need to talk about libraries in First Program section }} << , or set of functionality written by someone else (so you don't have to do it all from scratch) >> that handles HTTP requests and responses. Lein-ring allows Leiningen to start ring << and run your web app >>.

To run the web app, type the following at the command line:

lein ring server

Making it look nicer with HTML

Now let's do the same thing but with HTML so that the text displays with formatting.

(ns basic-app
  (:require [clojure.pprint :refer [pprint]]))

(defn app

  "Return the request as HTML."
  ;; A request comes in the handler.
  [request]

  ;; The handler returns a response map.
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body (str "<h1>Hello World</h1><pre>"
              (with-out-str (pprint request))
              "</pre>")})

<< Requiring clojure.pprint, or "pretty printer" allows you to display text in a more readable format on the page.

The change shown below tells the browser to display "Hello World" as a level one heading (big and bold), then calls the function with-out-str which means "capture" the "pretty printed" web request that was sent to the server, and display it within a <pre> (preformatted text) block in the browser.

:body (str "<h1>Hello World</h1><pre>"
            (with-out-str (pprint request))
            "</pre>")})

Refresh your browser to see the formatted page. You should see "Hello World" at the top in big, bold text, followed by a list of easy to read request related key-value pairs. >>

URLs and Routes

The web browser gets to the right server with an address called a {{ URL }} << Uniform Resource Locator, or URL >>. Take a look at the following URLs:

After the "http://", "www.google.com" identifies the server. Then the part after that, "/advanced_search" is the path to the resource {{ (or program?) }} << , program, or page >> on that server that will handle this request. In a single web application, you will almost certainly have many actions. For example, you may want to order a book, look up all the books you ordered, or check the status of an order. If one web application handles all of that, it needs to track who takes care of what.

The process of coordinating which path goes to what action is called routing. The Clojure library that does this is called Compojure. Here is an example of routing with Compojure.

(ns basic-app
  (:use compojure.core)
  (:require [clojure.pprint :refer [pprint]]
            [compojure.route :as route]))


(defn index-page []
  "index content")

(defn person-page [person]
  "person content")

(defn add-debt-page []
  "add debt content")

(defn add-debt-post [xfer]
  "add debt post content")

(defroutes app

  ;verb  route             parameters        handler
  (GET   "/"               []                (index-page))
  (GET   "/debts/:person"  [person]          (person-page person))
  (GET   "/add-debt"       []                (add-debt-page))
  (POST  "/add-debt"       [from to amount]
         (add-debt-post {:from from,
                         :to to,
                         :amount amount}))

  (route/resources "/")
  (route/not-found "Page not found"))

The verbs - GET, POST - are part of the http request and say what you want to do with this URL. GET is just fetching some data. POST is usually doing some processing on that data. The route is the path part of the URL. The handler is the Clojure function that is going to handle this particular request. The routes table maps that combination of verb and route to the handler function.

<< As an example from above, if a user just types in our server name (i.e. basic-app.com), this is the same as "/", meaning this is a request for our site's index page, and the request should be routed to our index-page handler which probably does something like return a response containing the contents of our home page to be displayed in the user's browser. Similarly, a request for "/debts/:person" is sent to our person-page handler which returns the correct page for the specified person. >>

One more thing about URLs. They can contain query strings at the end. These are a way to pass additional information to web applications.

Here you see the URL for Twitter search: https://twitter.com/search. The part after the ? is the query string: q=marmots&lang=en. What am I searching for? Marmots. What language do I want results for? English.

JSON

A brief word about {{ JSON }} << JavaScript Object Notation, or JSON >>. JSON is a format for providing data. Sometimes, instead of a web page you see in your browser, you want to provide a list of information from a web application. Having a format that everyone agrees on makes it easier to deal with the data. Here's an example that describes a book:

{"book": {
    "title": "The Fault in our Stars",
    "author": "John Green",
    "characters": [
        {"name": "Hazel Grace Lancaster", "age": 16},
        {"name": "Augustus Waters", "age": 17}
       ]
}}

<< A handler on the server might query a database and receive a list of book details in the format shown above, which it will process and then return as a formatted HTML response to the user's browser. This allows the database to just store key-value pairs (i.e. "author": "John Green") and enables the handler to decide how to format the display depending on the browser or device type. >>

What it would look like with all edits accepted (so you can copy/paste if desired:

Introduction to Web Applications

How does the web work?

A web application is software that runs in a web browser (like Chrome, Firefox, Internet Explorer, Safari). It communicates with a server over the Internet that can do central processing and data storage. The browser is the client, or user interface, to the web app where users interact with it. The server does the heavy lifting.

The way that the client and server communicate with each other is through the Hypertext Transfer Protocol, or HTTP. A protocol is a set of rules for communication. Think of the airport ground crew who use wands to communicate with the pilots taxiing the planes around the tarmac. They have a limited set of signals that mean specific things which guide what the pilot does. There are a set of guidelines for when and how the pilot will receive and respond to those signals. HTTP is the protocol for sending requests to the server and for the server to send responses to those requests.

A web app accepts an HTTP request from the client and gives it a response. Think of the server as just like the Clojure functions we have written. They take the input of a request and give the output of a response. In fact, we can write that function:

(defn app
  [request]

  {:status 200})

This function accepts 'request' as a parameter, then returns a map with a status of 200. In HTTP, a status of 200 means everything worked fine.

What is HTML?

When you bring up something in your web browser you want more than a status code, though. You want to see words and images and dancing gifs (ok, maybe not that). You want it to be easy to read and pleasant to look at. The Hypertext Markup Language, or HTML, is what makes web pages look that way. It is a markup language, which is different from a programming language. It is a way to put tags around words that suggest to the web browser the way you want them to look.

Go to the ClojureBridge web site. Then in your web browser, view the source.

  • In Chrome, go to View - Developer - View Source
  • In Firefox, go to Tools - Web Developer - Page Source
  • In Safari, go to Safari menu - Preferences - Advanced - Check "Show Develop menu in menu bar." Then go to Develop - Show Page Source
  • In Internet Explorer, go to View - Source

Look at the parts that have angle brackets around them like <this>. Those are HTML tags. Search for <h1>. That is a tag that tells the browser to display the text until the close tag, </h1>, as a level one header. Look at the web page and see how that text displays.

Making the simplest web application

Let's make the simplest possible web application. It will say hello in the browser.

First, run lein new basic-app in the console to create a new project.

Create a new file in the basic-app/src folder called basic_app.clj with the following contents:

(ns basic-app)

(defn app

  "Return a status."
  ;; A request comes in the handler.
  [request]

  ;; The handler returns a response map.
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body "Hello, world."})

Now open up basic-app/project.clj and change that file so that it reads:

(defproject basic-app "0.1.0-SNAPSHOT"

  ;; We require ring.
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [ring "1.2.1"]]

  ;; We use the lein-ring plugin to start ring.
  :plugins [[lein-ring "0.8.10"]]

  ;; We tell Ring what our handler function is and
  ;; what port to start on.
  :ring {:handler basic-app/app
         :port 3001})

There are multiple dependencies in this file: Ring and Lein-ring. Ring is a Clojure library, or set of functionality written by someone else (so you don't have to do it all from scratch) that handles HTTP requests and responses. Lein-ring allows Leiningen to start ring and run your web app.

To run the web app, type the following at the command line:

lein ring server

Making it look nicer with HTML

Now let's do the same thing but with HTML so that the text displays with formatting.

(ns basic-app
  (:require [clojure.pprint :refer [pprint]]))

(defn app

  "Return the request as HTML."
  ;; A request comes in the handler.
  [request]

  ;; The handler returns a response map.
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body (str "<h1>Hello World</h1><pre>"
              (with-out-str (pprint request))
              "</pre>")})

Requiring clojure.pprint, or "pretty printer" allows you to display text in a more readable format on the page.

The change shown below tells the browser to display "Hello World" as a level one heading (big and bold), then calls the function with-out-str which means "capture" the "pretty printed" web request that was sent to the server, and display it within a <pre> (preformatted text) block in the browser.

:body (str "<h1>Hello World</h1><pre>"
            (with-out-str (pprint request))
            "</pre>")})

Refresh your browser to see the formatted page. You should see "Hello World" at the top in big, bold text, followed by a list of easy to read request related key-value pairs.

URLs and Routes

The web browser gets to the right server with an address called a Uniform Resource Locator, or URL. Take a look at the following URLs:

After the "http://", "www.google.com" identifies the server. Then the part after that, "/advanced_search" is the path to the resource, program, or page on that server that will handle this request. In a single web application, you will almost certainly have many actions. For example, you may want to order a book, look up all the books you ordered, or check the status of an order. If one web application handles all of that, it needs to track who takes care of what.

The process of coordinating which path goes to what action is called routing. The Clojure library that does this is called Compojure. Here is an example of routing with Compojure.

(ns basic-app
  (:use compojure.core)
  (:require [clojure.pprint :refer [pprint]]
            [compojure.route :as route]))


(defn index-page []
  "index content")

(defn person-page [person]
  "person content")

(defn add-debt-page []
  "add debt content")

(defn add-debt-post [xfer]
  "add debt post content")

(defroutes app

  ;verb  route             parameters        handler
  (GET   "/"               []                (index-page))
  (GET   "/debts/:person"  [person]          (person-page person))
  (GET   "/add-debt"       []                (add-debt-page))
  (POST  "/add-debt"       [from to amount]
         (add-debt-post {:from from,
                         :to to,
                         :amount amount}))

  (route/resources "/")
  (route/not-found "Page not found"))

The verbs - GET, POST - are part of the http request and say what you want to do with this URL. GET is just fetching some data. POST is usually doing some processing on that data. The route is the path part of the URL. The handler is the Clojure function that is going to handle this particular request. The routes table maps that combination of verb and route to the handler function.

As an example from above, if a user just types in our server name (i.e. basic-app.com), this is the same as "/", meaning this is a request for our site's index page, and the request should be routed to our index-page handler which probably does something like return a response containing the contents of our home page to be displayed in the user's browser. Similarly, a request for "/debts/:person" is sent to our person-page handler which returns the correct page for the specified person.

One more thing about URLs. They can contain query strings at the end. These are a way to pass additional information to web applications.

Here you see the URL for Twitter search: https://twitter.com/search. The part after the ? is the query string: q=marmots&lang=en. What am I searching for? Marmots. What language do I want results for? English.

JSON

A brief word about JavaScript Object Notation, or JSON. JSON is a format for providing data. Sometimes, instead of a web page you see in your browser, you want to provide a list of information from a web application. Having a format that everyone agrees on makes it easier to deal with the data. Here's an example that describes a book:

{"book": {
    "title": "The Fault in our Stars",
    "author": "John Green",
    "characters": [
        {"name": "Hazel Grace Lancaster", "age": 16},
        {"name": "Augustus Waters", "age": 17}
       ]
}}

A handler on the server might query a database and receive a list of book details in the format shown above, which it will process and then return as a formatted HTML response to the user's browser. This allows the database to just store key-value pairs (i.e. "author": "John Green") and enables the handler to decide how to format the display depending on the browser or device type.

clarify namespace workflow

outline/first-program.md:

A namespace exists in the file src/global_growth/core.clj. Open it, and find this line:

(ns global-growth.core)

This is followed by instructions on requiring dependencies.

Confusingly, the aforementioned file already requires the dependencies, so no changes are necessary. We could either:

  • update the template to match the documentation; or
  • update the documentation to match the template.

Which approach is preferable? I'll put together a pull request once we've decided. :)

Functions section average exercise note

In EXERCISE: Find the average in the functions section, it asks to pass a vector into the function, and I don't know that that has been done up to that point in the curriculum. So that might need some description.

Remove Heroku setup from installfest instructions

Since we no longer deploy a web app, the Heroku setup can be removed (or, at least, marked as optional) from the installfest instructions. Note: there are multiple versions of the instructions, so need to take them out from multiple places.

Add setup instructions for installing git.

At least on Mac OS 10.8, git is not installed by default. The student needs to install it as part of setup. Probably the easiest path to done is downloading from git-scm.org.

Add a link to what a BigInt is?

In the "Arithmetic" section, there's currently a reference to BigInts:

(* 8 1/4)     ;=> 2   ;; this produces 2N which means 2 is a BigInt.

BigInts are pretty far out of scope for a beginner-level workshop. Any feedback as to how we want to adjust this line?

Add Troubleshooting section

As we encounter issues with environment setup, etc. in workshops, it would be great to have a section of the curriculum that has fixes for problems we've seen before.

Include Issue #75

Reword initial description of vectors

At the beginning of the "Vectors" section of data_structures.md, there's an initial description of vectors:

Vectors are sequential collections of data. You could say they are lists of data, but we also have another collection called a list. If you have programmed in another language, these might have been called arrays in that language.

...this needs rephrasing. Suggestions?

Edit curriculum

When "first program" and "Make your own web application" sections are done, review entire curriculum and take out parts that:

  1. Are not necessary to a basic introduction to clojure
  2. Are not in the list of concepts to be covered to support building the app
  3. other criteria for cutting?

Update CONTRIBUTING.md with information about updating slides

When folks update the curriculum, the slides usually end up falling out of date, potentially requiring someone to manually go back through and correct them before each workshop.

We should probably have a section in CONTRIBUTING.md that addresses this--possibly just a section that says tl;dr "If you update the curriculum, your PR must also address updates in the slides"?

Clarification of vector definition

The definition of vectors could use some clarification:

Vectors are sequential collections of data. You could say they are lists of data, but
we also have another collection called a list. If you have programmed in another
language, these might have been called arrays in that language.

This doesn't clearly define what vectors are; it mostly defines what they are by comparison (not "lists," but "arrays"). It would be worthwhile to rewrite this definition for students who have never programmed before.

Windows 7 setup feedback

Keith worked through the Windows 7 setup instructions. It took about 40 minutes altogether. It took a while for the JDK to download. Here is where he had some hiccups:

  • At the end of "Get Setup with Heroku" section, he had trouble with the directions to get his user directory. Maybe change this:
    "you will need to type the name of your user directory, plus .ssh\id_rsa "
    to
    "you will need to type the name of your user directory - the whole thing from "C:" onward - plus .ssh\id_rsa "
    Maybe bold the "Look at the following example:" text
  • The "Enter passphrase" part of creating the ssh key prompted him to ask what to do. So maybe add text:
    "When it asks to 'Enter passphrase' just hit Enter, then Enter again." (or suggest they enter a passphrase that they can remember... conflicted on that one)
  • Had to go through the ssh key part a few times. Maybe add:
    "Be very careful to type everything exactly."
  • After the Heroku install, he had to close the command prompt then reopen it.
  • (+ 1 1) - he wanted to know if there were spaces in there. Maybe add additional spaces to make it clear - (+ 1 1)
  • Add a blank line between:
    git push heroku master

heroku open

I can make these changes. I thought the narrative around the issues he had was interesting perspective, though, on the kinds things that trip up students.

for is missing

One of the concepts needed for the app is for. Daniel kindly wrote something up, but I don't know where it should go. It should go in data structures, but that section is very full. Also, I can't quite picture where it goes there. Thoughts @cndreisbach ?

Diagrams for data structures

In the "Data Structures" section, there's a diagram for lists:

linked-list

It would be ideal if we had diagrams like this for all of the data structures.

Incorrect function name in first-project.md, apij.clj causes boomness

'parse-json' should be 'json/parse-string'

'parse-json' doesn't seem to exist according to https://github.com/dakrone/cheshire

(defn get-api
"Returns map representing API response."
[path qp](let [base-path %28str base-uri path%29
query-params %28merge qp {:format "json" :per_page 10000})
response (parse-json (:body (client/get base-path {:query-params query-params}))) <---
metadata (first response)
results (second response)]
{:metadata metadata

:results results}))

Fix to lein repl problems for windows 7 student

Student with WIndows 7 kept getting errors when she ran lein repl that had this in them:

Address family not supported by protocol family: connect

lein couldn't download its stuff. Found the resolution here:
http://stackoverflow.com/a/21383865

A program called Relevant Knowledge was somehow blocking the traffic. Possibly spyware. Anyways, uninstalling it (with her permission- she hadn't heard of it) fixed the problem. Documenting for posterity- not sure whether a troubleshooting section would be helpful for future groups. Happy to write something up if there's a place that makes sense.

Add a description of how `sort-by` works

curriculum/outline/functions.md has fairly long descriptions of how map and reduce work, then closes the section on "Functions that take other functions" with a reference to sort-by. However, it doesn't explain how sort-by works, other than with a very short code example demonstrating usage.

It would probably be useful for students to see another example of the sort-by algorithm working step-by-step, even if only to preserve parallelism in the text.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.