advertisement

Print

Shoes Meets Merb: Driving a GUI App through Web Services in Ruby
Pages: 1, 2, 3, 4, 5, 6

Building a Shoes Frontend

Now we can get down to business and build the user interface for our pastebin. Documentation for Shoes is scant, but the best reference is the book Nobody Knows Shoes, by why the lucky stiff himself. The official API reference is available online as The Entirety of the Shoes Family.

The Shoes portion of the code is fairly simple, and should be easy to understand if you have experience programming for the Web, even if you have not used Shoes before. We will present it in its entirety, and then dissect and explain it. Because Shoes evals the code it is provided with, all of the code for a Shoes application should be in one file. Therefore, this code should follow the HttpToYaml code that we showed earlier:

  class ShoesPaste < Shoes
    include HttpToYaml

    url '/', :index
    url '/new', :new_paste
    url '/(\d+)', :show_paste

    def index
      pastes = get "/pastes" 

      stack :margin => 20 do    
        title "Shoes Pastebin" 

        pastes.sort_by{|p| p[:created_at]}.each do |paste|
          para link(paste[:title], :click => "/#{paste[:id]}")
        end

        para link('New paste', :click => "/new")
      end
    end

    def show_paste(id)
      paste = get "/pastes/#{id}" 
      stack :margin => 20 do
        title paste[:title]
        para paste[:text], :font => 'Monospace 12px'
        button "delete" do
          if confirm("Are you sure you want to delete this paste?")
            delete "/pastes/#{paste[:id]}" 
            visit "/" 
          end
        end
        view_all_link
      end
    end

    def new_paste
      stack :margin => 20 do
        title "New paste" 
        flow(:margin_top => 20) { caption "Title: "; @title = edit_line }
        @text = edit_box :margin_top => 20, :width => 400, :height => 200

        button("Paste!", :margin => 5) do
          paste = post '/pastes', :title => @title.text, :text => @text.text
          visit "/#{paste[:id]}" 
        end

        view_all_link
      end
    end

    protected

    def view_all_link
      para(link('View all pastes', :click => '/'), :margin_top => 20)
    end

  end

  Shoes.app :title => 'Shoes Pastebin', :width => 640, :height => 400

We inherit from Shoes to provide a clean environment in which to contain our application code. It is possible to contain the entire application in a block passed to the Shoes.app method, but it gets messy with larger applications. Our method allows us to keep our application code contained in one class.

After the initial include statement that pulls in our web service bridge, we set up the URLs for our application using the Shoes.url class method. This is a potential source of confusion because these URLs are completely different from those exposed by our Merb application. Because Shoes is modeled after the Web, it uses pseudo-URLs to reference different sections within an application.

  url '/', :index
  url '/new', :new_paste
  url '/(\d+)', :show_paste

Each url statement maps a regular expression to a method that is executed when a URL matching that regexp is "visited." (URLs can be triggered with a :click action on some elements, or with the visit method, as we will see later.) Any captures in the regular expression (such as (\d+)) will be passed as arguments to the method named.

We have three types of pages: the index, which shows a list of pastes, the "new paste" screen, and the screen to show an existing paste. Let's look at the index first, as it will introduce us to several aspects of the Shoes API.

  def index
    pastes = get "/pastes" 

    stack :margin => 20 do    
      title "Shoes Pastebin" 

      pastes.sort_by{|p| p[:created_at]}.each do |paste|
        para link(paste[:title], :click => "/#{paste[:id]}")
      end

      para link('New paste', :click => "/new")
    end
  end

The first line of the index method retrieves all of the current pastes from the /pastes URL on the server. As we saw before when experimenting with irb, this will be an array of hashes, with each hash containing attributes of one paste.

The stack method is unique to Shoes. For layout, Shoes uses the concept of stacks and flows, which are two types of boxes. Either type of box contains a series of elements. Stacks order their contents from top to bottom, while flows organize them from left to right (wrapping lines if necessary). We will use both stacks and flows to lay out different parts of our application. This method call also illustrates that most methods accept a hash of styles to fine-tune their positioning and look.

Within the stack, we first have a title, which is a simple text heading. Rather than HTML's unimaginative h1 through h6, Shoes uses creative heading names: banner, title, subtitle, tagline, caption, and inscription.

Next in the stack, we sort the pastes by their creation time, and create a paragraph (para) containing a link to that paste's show_paste method. (The "/#{paste[:id]}" URL will match the '/(\d+)' regexp.) Finally, we close out the stack by linking to the "new paste" URL.

When we run this with shoes /path/to/shoes_paste.rb, we see the following:

Now we probably want to be able to see some of the pasted code. So we will look at the show_paste function that lets us get at the pastes:

  def show_paste(id)
    paste = get "/pastes/#{id}" 
    stack :margin => 20 do
      title paste[:title]
      para paste[:text], :font => 'Monospace 12px'
      button "delete" do
        if confirm("Are you sure you want to delete this paste?")
          delete "/pastes/#{paste[:id]}" 
          visit "/" 
        end
      end
      view_all_link
    end
  end

  protected

  def view_all_link
    para(link('View all pastes', :click => '/'), :margin_top => 20)
  end

Because show_paste's mountpoint (its URL regular expression) has one capture, this function takes one argument: the ID of the paste. A URL of /123 will translate into a call to show_paste(123). Once we have that ID, we interpolate it into an actual URL on the Merb server, and retrieve the paste. The web service bridge does the translation from YAML to a hash, so we can work directly with the attributes of the paste.

Pages: 1, 2, 3, 4, 5, 6

Next Pagearrow