Skip to content
ewernli edited this page Sep 29, 2012 · 18 revisions

Deadline: October, 3, 2012, 12:00

Sinatra 101

You are asked to implement a simple graphical web-based user-interface (view) using the User and Item model you implemented in the last week’s homework.

It is mandatory to use the sinatra framework, that is "a Domain-Specific Language (DSL) for quickly creating web applications in Ruby with minimal effort".

HTTP defines several methods (also called verbs) to perfom actions on URLs. The main ones are GET and POST respectively to fetch and send data to URL.

Sinatra maps verbs and URL with ruby code via routes:

     get '/' do
        "<h1>Hello World</h1>"
     end

Usually, the ruby code does not produce HTML as above, but instead forwards to a view (also called template):

	get '/' do
 	  haml :hello_world
	end

The above code will renders the file hello_world.haml, that simply contains text Hello World.

To pass values to a template for rendering, use the options hash idiom:

    get '/' do
      haml :print_time, :locals => { :time => Time.now }
    end

The above code passes the current time into a paramter time that can be used in the template print_time.haml and formatted accordingly, e.g.

Time: #{time.strftime("%H:%M:%S")}

Sinatra provides the "C" in the MVC pattern: the routes are the controller(s). The template engine is the "V" in MVC. The controller manipulates the model, while the view simply renders it.

You should try to postpone rendering issues (like formatting dates) as late as possible to keep the separation between controllers and views.

You can go through Sinatra's README for more examples of routes, parameter passing, and template engines.

You can use the template engine of your choice, but we recommender either haml or erb.

Structure

We ask you to structure your project as follows

/trade
	/app 
	    app.rb
	    /public
	    	static-file
	   	/models
	   	   /module
	              class-in-module.rb
   		/views
   		/controllers
                main.rb
                authentication.rb
    /test

Pay attention to name the main file app.rb.

You might want to serve certain static files like images, CSS, etc. To do so, you can use

		set :public_folder, 'app/public'

in the app file to exclude a folder from matching other routes. Files in this folder will be served directly.

No database

Persistency using database is outside the scope of the ESE lecture, so we simply ask to keep the model in memory in a way similar to the exercise from last week.

You can load test data on startup using a configure block in the app file:

	configure :development do
 	  University::Student.named( 'John' ).save
 	  ...
	end

See Configuration in Sinatra's doc.

Method save add the student to a class variable students:

      class Student

          # class variable
          @@students = []

          ...

          def self.all
            @@students
          end

          def self.by_name name
            @@students.detect {|student| student.name == name }
          end

          # add the instance to the list of students
          def save
            @@students << self # or @@students.push(self)
          end
          
          ...

Login

Sinatra is a very simple framework. It does not by iteself support authentication.

We sketch now how you can easily add support for authentication using Sinatra's sessions.

  • First, you need to enable sessions in the main application file
		enable :sessions
  • Add a login page that will check the username and password; if the login succeeds it sets the parameter name in the session, e.g.
		session[:name] = username
  • Add a logout page that resets the parameter in the session to nil, e.g.
		session[:name] = nil
  • For the other pages, check the value of session[:name]. If it is nil, redirect to the login page, e.g.
		redirect '/login' unless session[:name] 

Redirect after Post

The Post/Redirect/Get pattern (also called Get after Post) is a web development pattern that prevents some duplicate form submissions, and make sure the browser's back button works in a predictable way.

	get "/"
	   haml :list_students, :locals => 
	   		{ :students => University::Student.all }
	end
	
	post "/add"
	   ... extract parameters from request ...
	   student = ... create student ...
	   student.save
	   redirect '/'
	end

With this pattern, GET methods should be idempotent; they should not update the model. Only POST methods should update the model.

It is not mandatory to enforce this pattern, but we encourage you to do it.

#Study the Code

Nothing is better than code, no?

For the impatient, you can jump to the code of ese2012-sinatra-basics, a simple web UI for the University::Student model of last week.

For a smoother introduction and a more comprehensive example, you can read the following blog serie of yours faithful zombiecalypse: installation, basics, using a model, REST, and sessions. This tutorial covers additional useful topics:

  • HTML forms
  • Helper methods between the view and the controller
  • Dealing with relative paths between files
  • Authentication with password hash
  • Identity and equality of entities with identifiers
  • RESTful web applications

It is not mandatory to go through it to implement the requirments below, though.

Requirements (4 points)

The web application must support the following requirements.

  • User authentication -- If the user is not authenticated, he/she is redirect to the login page. Authentication suceeds if the password = the username.

  • Show all active items -- The default page lists all active items in the system. For each item there is

    • the name
    • the price
    • a link to the owner
    • a link or button to buy the item
  • List items of a user -- The page lists all items owned by the user and indicates whether they are active (to sell) or inactive (not to sell).

  • Buy an item -- From the default page, a user can buy an item. If the transaction suceeds, the page is updated to show the new state of the system; otherwise an exception is raised.

  • Test data -- Please prepare test data that are loaded on startup. The must be a user ese that we can use to log in the system and test the other features.

Questions (1 point)

Answer the following questions in a file answers.txt that you will deliver with the sources:

  1. Explain how a typical HTTP request is processed with the framework (the request lifecycle)
  2. Why should GET requests be indempotent?
  3. Where should you format data for rendering?

Git (1 point)

As in the last excercise you have to use the github infrastructure to provide your solutions for this exercise. There is a lot of documentation available on the net about git, check out the links we provide in the resources section.

We ask you to create a repository ese2011-web-ui where you will commit your sources on the master branch. Everybody has by default read-only access to the repository. However in a way for us to commit remarks in your repository, add us as collaborator (ewernli, jokr, zombiecalypse).

As the final deliverable of these exercises we expect one of the author to publish the solutions to the repository {git account}/ese2012-web-ui by Wednesday, 3. October 2012, 12:00. Make sure that you include all code of Task 2 and that it runs correctly if we use

	cd ese2012-web-ui
	ruby app/app.rb

Include the answers to Task 3 in a file called answers.txt. Then send a mail to [email protected] with the following subject line: "Exercise 2: Web UI", give the names of the authors, and the git repository to pull the code from.