diff --git a/data/toc.yml b/data/toc.yml index d1d5440..c5a1e4f 100644 --- a/data/toc.yml +++ b/data/toc.yml @@ -41,7 +41,10 @@ - listing_names - using_post - redirect -- sessions +- path: sessions + children: + - cookies + - sinatra_sessions - validations - resources - path: exercises diff --git a/source/12-sessions.md b/source/12-sessions.md index 3154caa..c22b3f1 100644 --- a/source/12-sessions.md +++ b/source/12-sessions.md @@ -29,146 +29,6 @@ Basically. However, of course there are ways to identify your users. We all know that we can sign in to a web application, and it would recognize who we are. Right? -Often web applications use cookies for this. - -A cookie is a little piece of information that a web application can send along -with a response, and that will be stored by the browser. From then on, when the -browser makes another request to the same application it will include the cookie -to the request, sending it back to the application. - -For example, an HTTP response that sets a cookie for a user's prefered visual -theme could look like this: - -``` -HTTP/1.0 200 OK -Content-type: text/html -Set-Cookie: theme=light -``` - -From now on, the browser would then include the cookie to subsequent requests: - -``` -GET /blog.html HTTP/1.1 -Host: rubymonstas.org -Cookie: theme=light -``` - -This way the application could apply the "light" theme to the blog, because the -user has selected it in some previous request: Cookies are a way to persist -(keep) state (data) across multiple requests. - -Building on top of cookies, even though HTTP itself does not have a concept of a -"session" (or conversation), Sinatra (just as basically any web application -tool or framework) supports sessions. A session is a way for a web application -to set a cookie that persists arbitrary data across multiple requests. - -Let's have a look how this works. - -Say we'd like to pass a message from our `post` route to the next request, saying -"Successfully stored the name [name]". After successfully storing the name to -the file, we'd like to pass a message to the `GET` request that the browser is -going to be redirected to. - -We'd only want to display this message once right after the redirection from -the `post` route: when we'd reload the page it should be gone. This is called -transient state, and a session is a great place to keep it. - -First, we need to enable the `:sessions` feature in Sinatra. You can do that -by adding this line above of your routes: - -```ruby -enable :sessions -``` - -Now, we want to store the message in our `post` route: - -```ruby -post "/hello" do - name = params[:name] - store_name("names.txt", name) - session[:message] = "Successfully stored the name #{name}." - redirect "/hello" -end -``` - -Ok, cool. - -This will add a `Set-Cookie` header to the reponse which has a long, messy looking, encrypted -string as a value. - -In my browser it looks like this: -``` -Set-Cookie: rack.session=BAh7CUkiD3Nlc3Npb25faWQGOgZFVEkiRWI4OTdhMDJlNDBkMDFlNjcxNWUw%0AZGI1ZWU5MzQ0YTQyMjAzYjFiZTE2YzYxNzgwMWQxYjI3NzhiOWNhYTQ4YzUG%0AOwBGSSIJY3NyZgY7AEZJIiU2ZjdjN2Y0ZmM0MTdmMGJkNjBkNmY5MmQ1NDEx%0ANGQ4ZgY7AEZJIg10cmFja2luZwY7AEZ7B0kiFEhUVFBfVVNFUl9BR0VOVAY7%0AAFRJIi03NGNlNDIxYTczNjMwZDY3MWViNTlkYzIzN2YyN2M5NGU3ZWU4NTRm%0ABjsARkkiGUhUVFBfQUNDRVBUX0xBTkdVQUdFBjsAVEkiLTA3NjBhNDRjMzU0%0AODIxMzJjZjIyNDQyYTBkODhjMDhiYjg1NTYyNTAGOwBGSSIIZm9vBjsARkki%0ACGJhcgY7AFQ%3D%0A; path=/; HttpOnly -``` - -Wow. Ok, the name of the cookie gives us a hint that this is a session, and it -is managed by Rack, which is what Sinatra uses under the hood to persist the -session. - -Luckily we do not need to understand how exactly it does this. All we need to -know is that we can now use this data in the next request (the `GET` request) -like so: - -```ruby -get "/hello" do - @message = session.delete(:message) - @names = read_names - @name = params["name"] - erb :hello -end -``` - -I.e. we delete the key `:message` from our session (which is something very -similar to a Ruby hash). Deleting it will return the value that was stored on -this key, and we assign it to the instance variable `@message`, which makes it -available to our template. - -That means we can now use the message in our view like so: - -```erb -<% if @message %> -

<%= @message %>

-<% end %> -``` - -Let's try it out. Restart your application, and go to http://localhost:4567. -If you enter a name, and click submit you should then see something like this: - - - -When you reload the page the message should be gone. - -How does this work? - -In our `post` route we store the message to the session hash. This has is -something that Sinatra provides to us as developers. When we enable this -feature then Sinatra will, after every request, store this hash to a cookie -with the name `rack.session`, in the encrypted form that you saw above. - -We say the hash is being serialized, -which is a fancy way of saying that it is turned into some kind of format that -can be stored in text form. Sinatra (actually, Rack, under the hood) then also -encrypts and signs this data, so it is safe to send it over the internet (in -case we keep any sensitive data in it). - -Ok, so the `post` route includes the `Set-Cookie` header with this session -cookie into its response, and sends it to the browser. The browser will, from -now on, pass this cookie back the our application as part of every subsequent -request. - -When our browser is now redirected to `GET` the same URL again, it passes the -cookie, and Sinatra will, because we have the `:sessions` feature enabled, -*deserialize* (i.e. decrypt and read) the session data, and put it into the -hash that is returned by the method `session`, so we can work with it. - -In our `get` route we now simply always delete the key `:message` from the -session hash. Should anything be in there it will be assigned to the `@message` -instance variable, and the view will display it. If nothing's stored on the -session key, for example when we reload the page, then deleting the key will -simply return nil, and nothing will be displayed in the view. - -Does that make sense? - -Awesome :) +Often web applications use cookies for this. diff --git a/source/12-sessions/01-cookies.md b/source/12-sessions/01-cookies.md new file mode 100644 index 0000000..4ec832b --- /dev/null +++ b/source/12-sessions/01-cookies.md @@ -0,0 +1,28 @@ +# Cookies + +A cookie is a little piece of information that a web application can send along +with a response, and that will be stored by the browser. From then on, when the +browser makes another request to the same application it will include the cookie +to the request, sending it back to the application. + +For example, an HTTP response that sets a cookie for a user's prefered visual +theme could look like this: + +``` +HTTP/1.0 200 OK +Content-type: text/html +Set-Cookie: theme=light +``` + +From now on, the browser would then include the cookie to subsequent requests: + +``` +GET /blog.html HTTP/1.1 +Host: rubymonstas.org +Cookie: theme=light +``` + +This way the application could apply the "light" theme to the blog, because the +user has selected it in some previous request: Cookies are a way to persist +(keep) state (data) across multiple requests. + diff --git a/source/12-sessions/02-sinatra_sessions.md b/source/12-sessions/02-sinatra_sessions.md new file mode 100644 index 0000000..e7eb15f --- /dev/null +++ b/source/12-sessions/02-sinatra_sessions.md @@ -0,0 +1,118 @@ +# Sessions in Sinatra + +Building on top of cookies, even though HTTP itself does not have a concept of a +"session" (or conversation), Sinatra (just as basically any web application +tool or framework) supports sessions. + +A session is a way for a web application to set a cookie that persists +arbitrary data across multiple requests. + +Let's have a look how this works. + +Say we'd like to pass a message from our `post` route to the next request, saying +*"Successfully stored the name [name]"*. I.e. after storing the name to the +file, we'd like to pass a message to the `GET` request that the browser is +going to be redirected to. + +We'd only want to display this message once right after the redirection from +the `post` route: when we'd reload the page it should be gone. This is called +transient state, and a session is a great place to keep it. + +First, we need to enable the `:sessions` feature in Sinatra. You can do that +by adding this line above of your routes: + +```ruby +enable :sessions +``` + +Now, we want to store the message in our `post` route: + +```ruby +post "/hello" do + name = params[:name] + store_name("names.txt", name) + session[:message] = "Successfully stored the name #{name}." + redirect "/hello" +end +``` + +Ok, cool. + +This will add a `Set-Cookie` header to the reponse which has a long, messy looking, encrypted +string as a value. + +In my browser it looks like this: + +``` +Set-Cookie: rack.session=BAh7CUkiD3Nlc3Npb25faWQGOgZFVEkiRWI4OTdhMDJlNDBkMDFlNjcxNWUw%0AZGI1ZWU5MzQ0YTQyMjAzYjFiZTE2YzYxNzgwMWQxYjI3NzhiOWNhYTQ4YzUG%0AOwBGSSIJY3NyZgY7AEZJIiU2ZjdjN2Y0ZmM0MTdmMGJkNjBkNmY5MmQ1NDEx%0ANGQ4ZgY7AEZJIg10cmFja2luZwY7AEZ7B0kiFEhUVFBfVVNFUl9BR0VOVAY7%0AAFRJIi03NGNlNDIxYTczNjMwZDY3MWViNTlkYzIzN2YyN2M5NGU3ZWU4NTRm%0ABjsARkkiGUhUVFBfQUNDRVBUX0xBTkdVQUdFBjsAVEkiLTA3NjBhNDRjMzU0%0AODIxMzJjZjIyNDQyYTBkODhjMDhiYjg1NTYyNTAGOwBGSSIIZm9vBjsARkki%0ACGJhcgY7AFQ%3D%0A; path=/; HttpOnly +``` + +Wow. Ok, the name of the cookie gives us a hint that this is a session, and it +is managed by Rack, which is what Sinatra uses under the hood to persist the +session. + +Luckily we do not need to understand how exactly it does this. All we need to +know is that we can now use this data in the next request (the `GET` request) +like so: + +```ruby +get "/hello" do + @message = session.delete(:message) + @names = read_names + erb :hello +end +``` + +I.e. we delete the key `:message` from our session (which is something very +similar to a Ruby hash). Deleting it will return the value that was stored on +this key, and we assign it to the instance variable `@message`, which makes it +available to our template. + +That means we can now use the message in our view like so: + +```erb +<% if @message %> +

<%= @message %>

+<% end %> +``` + +Let's try it out. Restart your application, and go to http://localhost:4567. +If you enter a name, and click submit you should then see something like this: + + + +When you reload the page the message should be gone. + +How does this work? + +In our `post` route we store the message to the session hash. This has is +something that Sinatra provides to us as developers. When we enable this +feature then Sinatra will, after every request, store this hash to a cookie +with the name `rack.session`, in the encrypted form that you saw above. + +We say the hash is being serialized, +which is a fancy way of saying that it is turned into some kind of format that +can be stored in text form. Sinatra (actually, Rack, under the hood) then also +encrypts and signs this data, so it is safe to send it over the internet (in +case we keep any sensitive data in it). + +Ok, so the `post` route includes the `Set-Cookie` header with this session +cookie into its response, and sends it to the browser. The browser will, from +now on, pass this cookie back the our application as part of every subsequent +request. + +When our browser is now redirected to `GET` the same URL again, it passes the +cookie, and Sinatra will, because we have the `:sessions` feature enabled, +*deserialize* (i.e. decrypt and read) the session data, and put it into the +hash that is returned by the method `session`, so we can work with it. + +In our `get` route we now simply always delete the key `:message` from the +session hash. Should anything be in there it will be assigned to the `@message` +instance variable, and the view will display it. If nothing's stored on the +session key, for example when we reload the page, then deleting the key will +simply return nil, and nothing will be displayed in the view. + +Does that make sense? + +Awesome :) + diff --git a/source/13-validations.md b/source/13-validations.md index b51948c..dab2e9c 100644 --- a/source/13-validations.md +++ b/source/13-validations.md @@ -162,8 +162,8 @@ We can fix this by changing our view like so: Awesome. -With this completed you have now walked through an important pattern, which -also is used in Rails applications by default: +With this completed you have now walked through an important pattern for web +applications, which also is used in Rails applications by default: * There is an HTML form which is being retrieved via a `GET` request. * This form posts to another route, which validates the submitted data. diff --git a/source/14-resources.md b/source/14-resources.md index ccbac1d..bddb0c6 100644 --- a/source/14-resources.md +++ b/source/14-resources.md @@ -1,9 +1,12 @@ # Resources -In HTTP a "resource" is something that is identified by a URL. In Rails it is -something slightly different: a collection of 7 routes that belong together in -the sense that they allow listing, viewing, and managing a collection of -things. +In HTTP a "resource" is something that is identified by a URL. + +In Rails it is something slightly different: In Rails a resource a collection +of 7 routes that belong together in the sense that they allow listing, viewing, +and managing a collection of things. It is, essentially, a convention of +grouping routes, which has been encoded in Rails, but also is being used in +other applications, and generally a great practice. Let's have a look at an example. diff --git a/source/assets/images/12-sessions_1.png b/source/assets/images/12-sessions_1.png index 1d77237..a5f3fc9 100644 Binary files a/source/assets/images/12-sessions_1.png and b/source/assets/images/12-sessions_1.png differ diff --git a/source/assets/stylesheets/_style.scss b/source/assets/stylesheets/_style.scss index 9375da1..181c5d9 100644 --- a/source/assets/stylesheets/_style.scss +++ b/source/assets/stylesheets/_style.scss @@ -90,18 +90,16 @@ code:before, code:after { table { margin-top: 1em; width: 100%; - - tr { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - } + border-collapse: collapse; th { + border: 1px solid #ddd; text-align: left; } td, th { - padding: 0.5em 1em 0.5em 0; + border: 1px solid #ddd; + padding: 0.5em 1em 0.5em 1em; } } diff --git a/source/layouts/layout.erb b/source/layouts/layout.erb index d57ee16..14822f8 100644 --- a/source/layouts/layout.erb +++ b/source/layouts/layout.erb @@ -34,6 +34,7 @@