Skip to content

Commit

Permalink
Check for validity of models on the server, and add an example.
Browse files Browse the repository at this point in the history
  • Loading branch information
rstacruz committed Sep 11, 2011
1 parent 0d416f8 commit 3b679e2
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 3 deletions.
40 changes: 40 additions & 0 deletions examples/restapi/app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
$:.unshift File.expand_path('../../lib', __FILE__)

require 'sinatra/base'
require 'sequel'
require 'sinatra/backbone'

DB = Sequel.connect("sqlite::memory:")
DB.create_table :books do
primary_key :id
String :title
String :author
end

class Book < Sequel::Model
def to_hash
{ :id => id, :title => title, :author => author }
end

def validate
errors.add :author, "can't be empty" if author.to_s.size == 0
end
end

class App < Sinatra::Base
enable :raise_errors, :logging
enable :show_exceptions if development?

register Sinatra::RestAPI

rest_create("/book") { Book.new }
rest_resource("/book/:id") { |id| Book[id] }

set :root, File.expand_path('../', __FILE__)
set :views, File.expand_path('../', __FILE__)
set :public, File.expand_path('../public', __FILE__)

get '/' do
erb :home
end
end
3 changes: 3 additions & 0 deletions examples/restapi/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require './app'
App.set :run, false
run App
24 changes: 24 additions & 0 deletions examples/restapi/home.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title></title>
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/1.6.2/jquery.min.js'></script>
<script src='http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.1.7/underscore-min.js'></script>
<script src='http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js'></script>
<style>
body { font-family: sans-serif; font-size: 13px; line-height: 1.5; background: #d0d0da; }
#messages { width: 400px; margin: 20px auto; background: white; padding: 20px; border: solid 10px #c0c0ca; }
h3 { border-top: dotted 1px #ccc; padding: 20px 20px 0 20px; margin: 20px -20px 0 -20px; color: #46a; }
h3:first-child { border-top: 0; margin-top: 0; padding-top: 0; }
dl { overflow: hidden; }
dt { float: left; width: 120px; margin-right: 10px; text-align: right; color: #aaa; }
h3 { font-family: palatino; font-size: 1.3em; }
</style>
</head>
<body>
<div id="messages">
</div>
<script src='app.js'></script>
</body>
</html>
105 changes: 105 additions & 0 deletions examples/restapi/public/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Here is our Backbone model!
Book = Backbone.Model.extend({
urlRoot: '/book'
});

$(function() {
do_create();
});

function do_create() {
echo("<h3>Creating a book:</h3>");

var book = new Book;
book.set({ title: "Darkly Dreaming Dexter", author: "Jeff Lindsay" });
book.save({}, {
error: onerror,
success: function() {
print_book(book);
echo("<h3>Retrieving the same book:</h3>");
do_retrieve(book);
}
});
}

function do_retrieve(_book) {
var book = new Book({ id: _book.id });
book.fetch({
error: onerror,
success: function() {
print_book(book);
do_edit_error(book);
}
});
}

function do_edit_error(book) {
echo("<h3>Editing book with an error:</h3>");
console.log("(You should see an HTTP error right about here:)");
book.set({ author: '' });
book.save({}, {
success: onerror,
error: function() {
console.log("(...yep.)");
echo("...yes, it occured.");
do_edit(book);
}
});
}

function do_edit(book) {
echo("<h3>Editing book:</h3>");
book.set({ author: 'Anne Rice', title: 'The Claiming of Sleeping Beauty' });
book.save({}, {
error: onerror,
success: function() {
print_book(book);
do_delete(book);
}
});
}

function do_delete(book) {
echo("<h3>Deleting book:</h3>");
book.destroy({
error: onerror,
success: function() {
echo("Success.");
do_verify_delete(book.id);
}
});
}

function do_verify_delete(id) {
echo("<h3>Checking if book "+id+" still exists:</h3>");
console.log("(You should see an HTTP error right about here:)");
var book = new Book({ id: id });
book.fetch({
success: onerror,
error: function() {
console.log("(...yep.)");
echo("No, it doesn't.");
do_success();
}
});
}

function do_success() {
echo("<h3>Success!</h3>");
}

function print_book(book) {
echo("<dl><dt>Title:</dt><dd>"+book.get('title')+"</dd></dl>");
echo("<dl><dt>Author:</dt><dd>"+book.get('author')+"</dd></dl>");
echo("<dl><dt>ID:</dt><dd>"+book.get('id')+"</dd></dl>");
}

// Helper functions
function echo(html) {
$("#messages").append(html);
};

function onerror() {
echo("<p class='error'>Oops... an error occured.</p>");
};

2 changes: 1 addition & 1 deletion lib/sinatra/backbone.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Sinatra
module Backbone
def self.version
"0.1.0.rc1"
"0.1.0.rc2"
end

end
Expand Down
30 changes: 28 additions & 2 deletions lib/sinatra/restapi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@
#
# ### RestAPI example
# Here's a simple example of how to use Backbone models with RestAPI.
# Also see the [example application][ex] included in the gem.
#
# [ex]: https://github.com/rstacruz/sinatra-backbone/tree/master/examples/restapi
#
# #### Model setup
# Let's say you have a `Book` model in your application. Let's use [Sequel][sq]
# for this example, but feel free to use any other ORM.
# for this example, but feel free to use any other ORM that is
# ActiveModel-compatible.
#
# You will need to define `to_hash` in your model.
#
# db = Sequel.connect(...)
#
Expand All @@ -29,6 +35,9 @@
#
# class Book < Sequel::Model
# # ...
# def to_hash
# { :title => title, :author => author, :id => id }
# end
# end
#
# [sq]: http://sequel.rubyforge.org
Expand Down Expand Up @@ -64,12 +73,21 @@
# book.set({ title: "Darkly Dreaming Dexter", author: "Jeff Lindsay" });
# book.save();
#
# // In Ruby, equivalent to:
# // book = Book.new
# // book.title = "Darkly Dreaming Dexter"
# // book.author = "Jeff Lindsay"
# // book.save
#
# Or you may retrieve new items. Note that in this example, since we defined
# `urlRoot()` but not `url()`, the model URL with default to `/[urlRoot]/[id]`.
#
# book = new Book({ id: 1 });
# book.fetch();
#
# // In Ruby, equivalent to:
# // Book.find(:id => 1)
#
# Deletes will work just like how you would expect it:
#
# book.destroy();
Expand All @@ -94,7 +112,9 @@ def self.registered(app)
# your record. For instance, for an attrib like `title`, it wil lbe
# calling `object.title = "hello"`.
#
# * `object.save` will be called.
# * if `object.valid?` returns false, it returns an error 400.
#
# * `object.save` will then be called.
#
# * `object`'s contents will then be returned to the client as JSON.
#
Expand All @@ -111,6 +131,9 @@ def rest_create(path, options={}, &blk)
post path do
@object = yield
rest_params.each { |k, v| @object.send :"#{k}=", v }

return 400, @object.errors.to_json unless @object.valid?

@object.save
rest_respond @object.to_hash
end
Expand Down Expand Up @@ -170,6 +193,9 @@ def rest_edit(path, options={}, &blk)
callback = Proc.new { |*args|
@object = yield(*args) or pass
rest_params.each { |k, v| @object.send :"#{k}=", v unless k == 'id' }

return 400, @object.errors.to_json unless @object.valid?

@object.save
rest_respond @object
}
Expand Down
12 changes: 12 additions & 0 deletions test/app_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ class Book < Sequel::Model
def to_hash
{ :name => name, :author => author }
end

def validate
super
errors.add(:author, "can't be empty") if author.to_s.size == 0
end
end

class AppTest < UnitTest
class App < Sinatra::Base
register Sinatra::RestAPI
disable :show_exceptions
enable :raise_errors
rest_create("/book") { Book.new }
rest_resource("/book/:id") { |id| Book[id] }
end
def app() App; end
Expand All @@ -41,6 +47,12 @@ def app() App; end
assert json_response['author'] == @book.author
end

test "validation fail" do
hash = { :name => "The Claiming of Sleeping Beauty" }
post "/book", :model => hash.to_json
p last_response
end

test "should 404" do
get "/book/823978"

Expand Down

0 comments on commit 3b679e2

Please sign in to comment.