Coder Social home page Coder Social logo

rack-intro's Introduction

Inspecting The Web With Rack

Objectives

  1. Explain the mechanics of Rack
  2. Create a basic web app
  3. Set up an HTTP web server using a config.ru file and the rackup command

Why Rack?

How does a web server work?

We open a browser (or, these days, an "app") and it uses HTTP to connect to a remote server. Servers are just code. But somehow when you say /search?item=shoes&size=13M it knows how to find some more code that knows how to search (in this case, for shoes of size 13M).

All web servers have a core architecture in common. By looking at it, we can build a mental model for how all web servers work. In the same way, we can explain how all cars work by:

Explosions made by gasoline and fire make an inside wheel go round and that inside wheel makes the outside wheels go round"

In the same way, we can say that all web servers:

They look at an HTTP request and look at the HTTP verb and path and then run some conditional logic to find out which stuff to send back

In Ruby, this idea of "a common foundation for all web-server like things" is captured in a gem called Rack. Rails "rides on top of" Rack. Sinatra "rides on top of" Rack. In fact, the idea of a base, common web-server library was such a good idea, other languages like Python and Node.JS implemented their own "base" web server.

Before we get to the complexity of things built on top of Rack, let's get a simple server working on Rack.

Setting up Rack

To work with Rack, we need to create a thing that responds to a single method: #call. Often, this will be a new class. However, new instances of the Proc class are run by calling call on them, so one could simply use a Proc.

All this method needs to do is return an Array with three elements:

  • An HTTP Status code where 200 is used for OK
  • A Hash with a "Content-Type" key that returns the value (for HTML-based documents) of text/html
  • Something that responds to each which contains the multiple lines of a document of the "Content-Type"'s type (here, Strings that look like HTML: "<p>Like this!</p>". The most common "each-able" thing is an Array

Here's a sample that returns HTML:

[200, {"Content-Type" => "text/html"}, ["Hello <em>World</em>!"]]
[200, {"Content-Type" => "plain/text"}, ["Hello World!"]]

In real life, we tend to read HTML content from a file...

html_from_file = File.open("my_html.html").readlines
[200, {"Content-Type" => "plain/text"}, html_from_file]

Creating a Rack-Based Web Server

Using this, let's create a basic web app. Follow along with the below instructions.

Let's create a file called first.ru. Files that are used by Rack end with .ru instead of .rb because they're normally loaded with a command called rackup. It's a way to say "Hey this is a server definition" to anyone casually using ls in the directory.

require 'rack'

# Instances of Proc automatically have a call method that runs the block that
# they're initialized with.
my_server = Proc.new do
  [200, { 'Content-Type' => 'text/html' }, ['<em>Hello</em>']]
end

run my_server

Run this code by executing, from the command line:

$ rackup first.ru

Rack will print out something like:

[2019-10-28 12:04:12] INFO  WEBrick 1.4.2
[2019-10-28 12:04:12] INFO  ruby 2.6.3 (2019-04-16) [x86_64-darwin17]
[2019-10-28 12:04:12] INFO  WEBrick::HTTPServer#start: pid=5567 port=9292

Note: If you're using the Learn IDE, you won't be able to get to your website with localhost. Instead, you'll see a line that looks something like this - Starting server on 159.203.101.28:30001. To see the webpage, just go to 159.203.101.28:30001 in your web browser. Anywhere these instructions tell you to go to localhost, replace that with this IP address instead!

Let's deconstruct this URL a little bit though. The URL is http://localhost:9292/. The protocol is http. That makes sense, but the domain is localhost:9292. What's going on there? localhost is normally where a server like google.com goes. In this case, since you are running the server on your computer, localhost is the server name of your own computer. Nobody else can get that URL though. That's good for right now. This allows you to play around with writing websites without the security concerns of opening it up to the entire web. The last part of that URL is the :9292 section. This the "port number" of your server. Don't worry too much about this, but you may want to run multiple servers on one computer and having different ports allows them to be running simultaneously without conflicting.

The resource that you are requesting is /. This is effectively like saying the home or default. If you're doing local development, you should be able to go to http://localhost:9292/ and see Hello printed out by your web server!

Feel free to change first.ru to add changes to your web server. If you make changes to first.ru you'll have to shut down the server (Control-C) and re-start it to see the changes.

Interestingly, we can swap out the simple Proc for a class. So long as it responds to #call, Rack will let us make a substitution.

Create a new file called second.ru and fill it out like:

require 'rack'

# Something that responds to call, that's what Rack demands
class MyServer
  def call(env)
    return [ 200, {'Content-Type' => 'text/html'}, pretty_response ]
  end

  def pretty_response
    (Time.now.to_i % 2).zero? ?  ["<em>Hello</em>"] : ["<strong>Hello</strong>"]
  end
end

run MyServer.new

Start it up with: rackup second.ru. Visit the web page and hit refresh several times. When the present time, as an Integer, is even, the output is emphatic; when odd, it's strong.

It's not too far of a step from this "conditional" logic to realize that if a web server knew what was after the / in the URL path, it could make decisions, run logic, etc... and change what's displayed in response to what's in the URL. That's basically what web servers do all day long. Rails, Sinatra, any web programming framework is a way to organize the code that fills out that third Array element in our Rack responses. Amazing!

We could make things look a bit more like a web server by taking our server code out of the rackup file and put it into a class file. We could create:

# ./my_server.rb

class MyServer
  def call(env)
    return [ 200, {'Content-Type' => 'text/html'}, pretty_response ]
  end

  def pretty_response
    (Time.now.to_i % 2).zero? ?  ["<em>Hello</em>"] : ["<strong>Hello</strong>"]
  end
end

And create a new rackup file: third.ru

require_relative './my_server'
run MyServer.new

Now we're honoring the way most Rack applications are built: the server setup code lives in the rackup file and our application logic lives in a class that's referenced by the run command in the rackup file.

rack-intro's People

Contributors

annjohn avatar aviflombaum avatar curiositypaths avatar dependabot[bot] avatar ihollander avatar ipc103 avatar jmburges avatar lizbur10 avatar maxwellbenton avatar morgvanny avatar pletcher avatar victhevenot avatar

Stargazers

 avatar

Watchers

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

rack-intro's Issues

Re-write to use simpler rack application

The introduction of Rack::Response is a poor choice for beginners and can create buffer issues. Explaining a 3 part array is way better.

Plus, the test and the example are bad.

Readme no longer contains information regarding application.rb

It looks like the tests are asking to do something that is no longer described in the readme file. Looking at issue #13 it seems that there was some verbiage removed from the lesson which would give more information about how to pass this particular lab. There is no information given related to how to use Rack::Response or what is happening at all in the application.rb file, but the test still expects us to update that file to pass.

Consider updating tests to reflect new readme, or else re-insert information regarding Rack::Response that was removed from line 30 of this commit.

Learn IDE special instructions

My computer got the "This site cannot be reached" error message even with the Learn IDE workaround that was suggested of inputting the IP address directly.

Missing gem: rack-test

The repo is missing the rack-test gem. Students will need to run gem install rack-test in order for learn commands to execute.

Review second.ru example

I needed to remove the square brackets from the return value of pretty_response for it to work correctly - bug? I know the return response of call should be an array, so I was thinking the double array was causing the error:

Error:
[2021-03-11 22:56:39] ERROR Rack::Lint::LintError: Body yielded non-string value ["<strong>Hello</strong>"]

Original:

  def pretty_response
    (Time.now.to_i % 2).zero? ?  ["<em>Hello</em>"] : ["<strong>Hello</strong>"]
  end

Proposed:

  def pretty_response
    (Time.now.to_i % 2).zero? ?  "<em>Hello</em>" : "<strong>Hello</strong>"
  end

It is not explicit about the application file

The paragraph from the README: "We first create a Rack::Response object, then add some text "Hello, World" to the body, and complete the response with the #finish method. By default, Rack sets our status codes and headers. Don't worry about the env input. This holds all of the request info in it and we will use it later!" as well as the paragraph following the code, do not explicitly say where you've put this in the repo.

I recommend adding "...create a Rack::Response object, written in the application.rb file, then add..." as a simple fix to prevent wasted time with confusion or looking for it.

AWS Cloud9 Different Instructions

I just started utilizing the AWS Cloud9 and it appears that Cloud9 will only listen to port 8080 , 8081 and 8082. I had to utilize this command in the terminal: rackup -p 8080 -o 0.0.0.0 and was able to see the preview of the page only then.

Not sure if this is worth mentioning in the instructions or not. Thanks!

env variable

Do students already know what the env argument in the call method? If not, should this be touched on?

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.