lib/carte/server.rb

Summary

Maintainability
A
2 hrs
Test Coverage
require 'sinatra/base'
require 'sinatra/namespace'
require 'mongoid'
require 'mongoid_auto_increment_id'
require 'will_paginate_mongoid'
require 'mongoid-simple-tags'
require 'mongoid/geospatial'
require 'redcarpet'
require 'carte/server/validators'
require 'carte/server/models'

module Carte
  class Server < Sinatra::Base
    use Rack::Deflater
    register Sinatra::Namespace
    include Carte::Server::Models

    configure do
      set :protection, :except => :path_traversal
      set :views, File.join(File.dirname(__FILE__), 'server/views')
      set :public_folder, 'public'
      set :default, JSON.parse(File.read(File.join(File.dirname(__FILE__), 'shared/default.json')))
      set :carte, {}
    end

    helpers do
      def config
        @config ||= settings.default.update(settings.carte)
      end

      def json_data
        request.body.rewind
        JSON.parse(request.body.read)
      end

      def search(params)
        order = (params[:order] && %w(asc desc random).include?(params[:order])) ? params[:order] : 'desc'
        sort = (params[:sort] && %w(title created_at updated_at).include?(params[:sort])) ? params[:sort] : 'updated_at'
        if order == 'random'
          cards = Card.random
        else
          cards = Card.send(order, sort)
        end
        conditions = []
        if title = params[:title]
          conditions << {title: /#{title}/i}
        end
        if content = params[:content]
          conditions << {content: /#{content}/i}
        end
        if conditions.size > 0
          cards = cards.any_of(conditions)
        end
        if params[:tags]
          tags = params[:tags].split(',')
          cards = cards.tagged_with_all(tags)
        end
        cards = cards.paginate(per_page: 9, page: params[:page])
      end

      def markdown2html(markdown)
        renderer = Redcarpet::Render::HTML.new(filter_html:true)
        html = Redcarpet::Markdown.new(renderer, autolink: true).render(markdown)
        parse_card_link(html)
      end

      def parse_card_link(html)
        html.gsub(/\[\[(.+?)\]\]/) do |match|
          title = $1.dup
          if title.match(/<("[^"]*"|'[^']*'|[^'">])*>/)
            match
          else
        %Q(<a href="http://#{request.host}/#/#{URI.escape(title)}">#{title}</a>)
          end
        end
      end
    end

    get '/cards.xml' do
      @cards = search(params)
      builder :cards
    end
    
    get '/cards.json' do
      cards = search(params)
      {
        cards: cards.map {|card| card.as_json(only: %w(title content tags)).update(version: card.version) },
        pagination: {current_page: cards.current_page, total_pages: cards.total_pages, total_entries: cards.total_entries}
      }.to_json
    end
    
    get '/cards/:title.json' do
      context = (params[:context] && %w(title created_at updated_at).include?(params[:context])) ? params[:context] : 'created_at'
      card = Card.where(title: params[:title]).first
      halt 404 if card.nil?
      {
        card: card.as_json(only: %w(title content version tags)).update(
          version: card.version,
          lefts: card.lefts(4, context).as_json(only: %w(title content version tags)),
          rights: card.rights(4, context).as_json(only: %w(title content version tags))
        )
      }.to_json
    end

    post '/cards.json' do
      card = Card.new(json_data.slice('title', 'content', 'tags'))
      if card.save
        status 201
        {}.to_json
      else
        status 400
        {card: {errors: card.errors}}.to_json
      end
    end
 
    put '/cards/:title.json' do
      card = Card.where(title: params[:title]).first
      halt 404 if card.nil?
      card.histories.create!
      if card.update_attributes(json_data.slice('new_title', 'content', 'tags').compact)
        status 201
        {}.to_json
      else
        status 400
        {card: {errors: card.errors}}.to_json
      end
    end
    
    #delete '/cards/:title.json' do
    #  card = Card.where(title: params[:title]).first
    #  halt 404 if card.nil?
    #  card.destroy
    #end
    
    get '/cards/:title/history.json' do
      card = Card.where(title: params[:title]).first
      halt 404 if card.nil?
      {
        history: [
          card.as_json(only: %w(title content version tags)).update(version: card.version),
          card.histories.desc(:version).as_json(only: %w(title content version tags))
        ].flatten
      }.to_json
    end

    get '/tags.json' do
      {tags: Card.all_tags}.to_json
    end

    put '/tags/:name.json' do
      # TODO: existence and length validation
      if json_data['new_name']
        cards = Card.collection.where(tags: params[:name])
    cards.update( 
          {'$push' => {tags: json_data['new_name']}},
          {:multi => true}
        )
    cards.update( 
          {'$pull' => {tags: params[:name]}},
          {:multi => true}
        )
      end
      status 201
      {}.to_json
    end

    error(404) do
      {}.to_json
    end
  end
end