Building Backbone.js Apps With Ruby, Sinatra, MongoDB and Haml

优质
小牛编辑
113浏览
2023-12-01

Introduction

In this chapter we’re going to explore writing Backbone.js applications with a Ruby back-end. To assist with this, we’re going to use Sinatra - a DSL (domain specific language) for rapidly creating web applications in Ruby. Similar to the section on writing an application with Node.js, our server-side language (Ruby) will be used to power an API whilst Backbone.js will be the client consuming it.

What Is Sinatra?

In the past, you’ve likely come across or used Ruby on Rails (RoR) - a popular web application framework for the Ruby programming language that helps organize applications using the MVC pattern. Sinatra is a much smaller, more light-weight alternative to it.

Whilst a very basic Rails application may require a more strict project structure (such as requiring the use of controllers, views and routing etc.), Sinatra doesn’t require as many of these dependencies, sacrificing the helpers needed to connect to databases, tools to create forms or any of the other utilities Rails comes with out of the box.

What Sinatra does have is a minimal set of features most useful for tying specific URLs and RESTful HTTP actions to blocks of Ruby code and returning this code’s output as a response. Sinatra is particularly useful for getting projects up and running quickly where we don’t have a need for the extra pieces RoR provides.

For those who are familiar with more Rails, you probably know that it requires a separate routes file to define how an application should be responding to requests. These are then piped into the relevant models and controllers as needed.

Sinatra takes a more straight-forward approach, providing us with the most simple path to handling routing. By declaring get,post, put or delete actions, we can inform Sinatra to add a new route, which we can then have respond to requests.

The framework is particularly useful for writing APIs, widgets and small-scale applications that can power the backend of a client-heavy application. As mentioned, we will be using it to power our API.

Getting Started With Sinatra

Let’s review how to write and run a very basic Sinatra application. As most programming languages and frameworks typically start with some variation of Hello World, we’ll start with a similar example.

Note: Before beginning this section, I recommend installing Sinatra on your system. A guide to doing this can be found in the prerequisites section lower down in the article.

Routes

As mentioned, Sinatra allows us to define new routes using HTTP actions. Semantically, a route follows quite a simple structure:

<a HTTP action> <the desired route> do
   # some behaviour
end

A tiny route that outputs a Hello World-like message when we attempt to get the root could thus be written as follows:

require 'sinatra'

get '/' do
   "Hello World! Is it me you're looking for?"
end

To run this snippet, we can can simply save it to a local ’.rb’ file and execute it as follows:

ruby -rubygems example.rb

If we now navigated to http://localhost:4567 in our browser we could now see the application running successfully.

The HTTP verbs we commonly work with when writing RESTful web services are: get, post, delete and put. As we now know, all Sinatra routes are basically HTTP actions (get etc.) that are paired with a URL-matching pattern. We associate a pair of an action and route with code we would like sent back to the browser (executed)if the route is reached. Sinatra doesn’t enforce much in the way of architectural structure, instead relying on simplicity to supporting writing powerful APIs.

Here’s an example of a skeleton service we could put together supporting four common HTTP actions:

get '/items' do
  # list all items available
end

get '/item/:id' do
  # get a single item
end

post '/item' do
  # create a new item
end

put '/item/:id' do
  # update an existing item
end

delete '/item/:id' do
  # delete an item
end

Sinatra’s routing is both easy for beginners to get started with but is also flexible enough for those wishing to define more complex routes. As you probably noticed in the above example, routes can include named parameters (e.g /item/:id). We can actually access the content of these routes using the params hash as follows:

get '/item/:id' do
  # this matches "GET /item/10" and "GET /item/11"
  # params[:id] is "10" or "11"
  "You reached #{params[:id]}"
end

Sinatra also supports route matching via splats, wildcards and regular expressions. For more information on this I recommend reading the official docs. Let’s now take a look at handlers.

Sinatra includes convenient handler methods for tasks such as redirection, halting and passing.

Redirection

A simple route supporting redirection which returns a 302 response can be written as follows:

get '/items' do
      redirect '/items/welcome'
end

And if we wish to pass additional parameters such as arguments we can do so like this: redirect http://site.com/, Oops! I think we have a problem!

Halting

To immediately stop a request (halting) we can use halt. Heres an example of halting a request where we specify the message body:

halt "who goes there!?"

Passing

Passing is the concept of deferring processing of a block to the next matching route. We do this using pass. In the following example if a parameter isnt the username we expect (rick-astley) we simply pass it on:

get '/members/:username' do
 pass unless params[:username] == 'rick-astley'
 'Never gonna give you up, never gonna let you down'
end

get '/members/*' do
 'Welcome!'
end

There are also handler methods that can assist with sessions (specifically, cookie-based session handling). To use Sinatra’s session handling, first enable it in your application with:

enable :sessions

You can then use the session handling capabilities as follows:

get '/items' do
  session['visitCounter'] ||= 0;
  session['visitCounter'] += 1;
  "This page has been accessed #{session['visitCounter']} times"
end

Note: By default enable:sessions will store all data in cookies. If this is not desired, you can not call this and instead use some Rack middleware instead. For more on this see here.

This only touches the surface of what can be done using routes and handlers, but is sufficient for us to write the Sinatra-powered API service we require in the practical section of this chapter.

Templating And HAML

Let’s now discuss templating.Out of the box, we can begin using templates in our Sinatra applications with ERB. ERB is included with Ruby and allows Ruby code to be added to any plain text document for the purpose of generating information or flow control. In the following example using an ERB template, note that views are by default located in the views directory of our application.

get '/items' do
  erb :default
  # renders views/default.erb
end

A useful Sinatra convention worth noting is how layouts are handled. Layouts automatically search for a views/layout template which is rendered before any other views are loaded. With ERB, our views/layout.erb file could look as follows:

<html>
  <head></head>
  <body>
    <%= data %>
  </body>
</html>

Haml is a popular alternative to ERB which offers an abstract syntax for writing application templates. It has been said to be:

  • Straight-forward to learn
  • Very easy to read and use for visually expressing a hierarchy of DOM elements
  • Popular with web designers as it builds on top of CSS syntax
  • Well documented with a large community backing it
  • Almost as fast as ERB

For the purpose of comparison, below we can see an ERB template compared to its Haml equivalent.

ERB

<div class="todo" id="content">
  <h2 class="entry_title"><%= h @todo.title %></h2>
  <div class="entry_link"><%= link_to('link', @todo.link) %></div>
</div>

Haml

.todo#content
  %h2.entry_title= @todo.title
  .entry_link= link_to('link', @todo.link)

One of the first things we notice is that the Haml snippet looks significantly more like CSS than it does traditional markup. It’s much easier to read and we no longer need to be concerned with divs, spans, closing tags or other semantic rules that usually mean more keystrokes. The approach taken to making whitespace a part of the syntax also means it can be much easier to compare changes between multiple documents (especially if you’re doing a diff).

In the list of Haml features, we briefly mentioned web designers. As developers, we regularly need to communicate and work with designers, but we always have to remember that at the end of the day, they are not programmers. They’re usually more concerned with the look and the feel of an application, but if we want them to write mark-up as a part of the templates or skins they create, Haml is a simpler option that has worked well for teams at a number of companies.

%h1 This is some h1 text
%h2 This is some h2 text.

%p Now we have a line containing a single instance variable: @content
%p= @content

%p Embedding Ruby code in the middle of a line can be done using ==.
%p== Here is an example: #{@foobar}

%p We can also add attributes using {}
%p{:style => "color:green"} We just made this paragraph green!

%p You'll want to apply classes and ids to your DOM, too.
%p.foo This has the foo class
%p.bar This has the bar class
%p#foobar This has the foobar id
%p.foo#foobar Or you can combine them!

%p Nesting can be done like this
%p
  Or even like this

Note: Haml is whitespace sensitive and will not correctly work if it isn’t indented by an even number of spaces. This is due to whitespace being used for nesting in place of the classic HTML markup approach of closing tags.

最后更新:

类似资料

  • Sinatra:一个优雅地包装了Web开发的DSL 你相信用100行代码可以实现一个博客吗?用Sinatra框架就可以做到。下面就是这样子的一个Web应用: Reprise - A Minimalistic Blog 这个应用的代码:点这里查看 一个简单的hello world的web应用要写多少代码?用Sinatra只需5行代码: require 'rubygems' require 'sina

  • sinatra-webdav 是基于 Sinatra 的 WebDAV 实现。 安装: $ git clone git@github.com:fork/sinatra-webdav.git$ cd sinatra-webdav$ bundle install

  • Sinatra::Synchrony是 Sinatra的一个小扩展,它动态提升了Sinatra网络应用的并发性。由于EventMachine和EM-Synchrony的支持,当你有很多 传输和低速IO请求时(如向外部APIs发送的HTTP请求),它增加了你的应用每个过程可服务的客户的数量。由于它内部使用Fibers处理堵塞的 IO,所以没有回调的需要。这意味着我们可以像写一个正常的Sinatra网

  • 问题内容: 我有一个Sinatra应用程序,并且在我的大多数控制器中,json都会传入并在params对象中自动获取。但是,除非我用before方法来拉取request.body参数,否则我将获得一个根本不获取参数的后期操作.body参数将它们解析为JSON并将它们合并到params哈希中。 这是控制器以及filter方法: 我看到的输出基本上是,控制器操作中的参数实际上正确地位于其中。但是,如果

相关阅读