anchor.diff:  -module(chttpd_view).
   -include_lib("couch/include/couch_db.hrl").
   
  --export([handle_view_req/3,handle_temp_view_req/2]).
  -
  --export([get_stale_type/1, get_reduce_type/1, parse_view_params/3]).
  --export([make_view_fold_fun/6, finish_view_fold/3, view_row_obj/3]).
  --export([view_group_etag/2, view_group_etag/3, make_reduce_fold_funs/5]).
  --export([design_doc_view/5, parse_bool_param/1, extract_view_type/3]).
  +-export([handle_view_req/3, handle_temp_view_req/2, get_reduce_type/1,
  +    parse_view_params/3, view_group_etag/2, view_group_etag/3,
  +    parse_bool_param/1, extract_view_type/3]).
   
   -import(chttpd,
       [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,send_chunk/2,
  
positive.code:     % they will change the Etag. For more granular Etags we'd need to keep
      % track of the last Db seq that caused an index change.
      chttpd:make_etag({Sig, CurrentSeq, Extra}).
  
  % the view row has an error
  view_row_obj(_Db, {{Key, error}, Value}, _IncludeDocs) ->
      {[{key, Key}, {error, Value}]};
  % include docs in the view output
  view_row_obj(Db, {{Key, DocId}, {Props}}, true) ->
      Rev = case couch_util:get_value(<<"_rev">>, Props) of
      undefined ->
          nil;
      Rev0 ->
          couch_doc:parse_rev(Rev0)
      end,
      view_row_with_doc(Db, {{Key, DocId}, {Props}}, Rev);
  view_row_obj(Db, {{Key, DocId}, Value}, true) ->
      view_row_with_doc(Db, {{Key, DocId}, Value}, nil);
  % the normal case for rendering a view row
  view_row_obj(_Db, {{Key, DocId}, Value}, _IncludeDocs) ->
      {[{id, DocId}, {key, Key}, {value, Value}]}.
  
  view_row_with_doc(Db, {{Key, DocId}, Value}, Rev) ->
      ?LOG_DEBUG("Include Doc: ~p ~p", [DocId, Rev]),
      case (catch chttpd_db:couch_doc_open(Db, DocId, Rev, [])) of
        {{not_found, missing}, _RevId} ->
            {[{id, DocId}, {key, Key}, {value, Value}, {error, missing}]};
        {not_found, missing} ->
            {[{id, DocId}, {key, Key}, {value, Value}, {error, missing}]};
        {not_found, deleted} ->
            {[{id, DocId}, {key, Key}, {value, Value}]};
        Doc ->
          JsonDoc = couch_doc:to_json_obj(Doc, []),
          {[{id, DocId}, {key, Key}, {value, Value}, {doc, JsonDoc}]}
      end.
  
  finish_view_fold(Req, TotalRows, FoldResult) ->
      case FoldResult of
      {ok, {_, _, undefined, _, Offset}} ->
          % nothing found in the view, nothing has been returned
          % send empty view
          NewOffset = case Offset of
              nil -> TotalRows;
              _ -> Offset
          end,
          send_json(Req, 200, {[
              {total_rows, TotalRows},
              {offset, NewOffset},
              {rows, []}
          ]});
      {ok, {_, _, Resp, _, _}} ->
          % end the view
          send_chunk(Resp, "\r\n]}"),
          end_json_response(Resp);
      Error ->
          throw(Error)
      end.
  
  finish_reduce_fold(Req, Resp) ->
      case Resp of
      undefined ->
          send_json(Req, 200, {[
              {rows, []}
          ]});
      Resp ->
          send_chunk(Resp, "\r\n]}"),
          end_json_response(Resp)
      end.
  
  parse_bool_param("true") -> true;
  parse_bool_param("false") -> false;
  
negative.code: handle_view_req(Req, _Db, _DDoc) ->
      send_method_not_allowed(Req, "GET,POST,HEAD").
  
  handle_temp_view_req(Req, _Db) ->
      Msg = <<"Temporary views are not supported by Cloudant">>,
      chttpd:send_error(Req, 403, Msg).
  
  reverse_key_default(?MIN_STR) -> ?MAX_STR;
  reverse_key_default(?MAX_STR) -> ?MIN_STR;
  reverse_key_default(Key) -> Key.
  
  get_reduce_type(Req) ->
      list_to_atom(chttpd:qs_value(Req, "reduce", "true")).
  