Created
July 7, 2016 01:43
-
-
Save atelic/b4eeafd3c689392f3a8dd1cf616c2f6e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'sinatra' | |
require "sinatra/namespace" | |
require 'mongoid' | |
# DB Setup | |
Mongoid.load! "mongoid.config" | |
# Models | |
class Book | |
include Mongoid::Document | |
field :title, type: String | |
field :author, type: String | |
field :isbn, type: String | |
validates :title, presence: true | |
validates :author, presence: true | |
validates :isbn, presence: true | |
index({ title: 'text' }) | |
index({ isbn: 1 }, { unique: true, name: "isbn_index" }) | |
scope :title, -> (title) { where(title: /^#{title}/) } | |
scope :isbn, -> (isbn) { where(isbn: isbn) } | |
scope :author, -> (author) { where(author: author) } | |
end | |
# Serializers | |
class BookSerializer | |
def initialize(book) | |
@book = book | |
end | |
def as_json(*) | |
data = { | |
id: @book.id.to_s, | |
title: @book.title, | |
author: @book.author, | |
isbn: @book.isbn | |
} | |
data[:errors] = @book.errors if @book.errors.any? | |
data | |
end | |
end | |
# Routes | |
get '/' do | |
'Welcome to BookList!' | |
end | |
# API | |
namespace '/api/v1' do | |
before do | |
content_type 'application/json' | |
end | |
helpers do | |
def base_url | |
@base_url ||= "#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}" | |
end | |
def json_params | |
begin | |
JSON.parse(request.body.read) | |
rescue | |
halt 400, { message: 'Invalid JSON' }.to_json | |
end | |
end | |
# Using a method to access the book can save us | |
# from a lot of repetitions and can be used | |
# anywhere in the endpoints during the same | |
# request | |
def book | |
@book ||= Book.where(id: params[:id]).first | |
end | |
# Since we used this code in both show and update | |
# extracting it to a method make it easier and | |
# less redundant | |
def halt_if_not_found! | |
halt(404, { message: 'Book Not Found'}.to_json) unless book | |
end | |
def serialize(book) | |
BookSerializer.new(book).to_json | |
end | |
end | |
get '/books' do | |
books = Book.all | |
[:title, :isbn, :author].each do |filter| | |
books = books.send(filter, params[filter]) if params[filter] | |
end | |
books.map { |book| BookSerializer.new(book) }.to_json | |
end | |
get '/books/:id' do |id| | |
halt_if_not_found! | |
serialize(book) | |
end | |
# We switched from an if...else statement | |
# to using a guard clause which is much easier | |
# to read and makes the flow more logical | |
post '/books' do | |
book = Book.new(json_params) | |
halt 422, serialize(book) unless book.save | |
response.headers['Location'] = "#{base_url}/api/v1/books/#{book.id}" | |
status 201 | |
end | |
# Just like for the create endpoint, | |
# we switched to a guard clause style to | |
# check if the book is not found or if | |
# the data is not valid | |
patch '/books/:id' do |id| | |
halt_if_not_found! | |
halt 422, serialize(book) unless book.update_attributes(json_params) | |
serialize(book) | |
end | |
delete '/books/:id' do |id| | |
book.destroy if book | |
status 204 | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment