return errors as json, improve handling of nested params
[arvados.git] / app / controllers / application_controller.rb
1 class ApplicationController < ActionController::Base
2   protect_from_forgery
3   before_filter :uncamelcase_params_hash_keys
4   before_filter :find_object_by_uuid, :except => :index
5
6   unless Rails.application.config.consider_all_requests_local
7     rescue_from Exception,
8     :with => :render_error
9     rescue_from ActiveRecord::RecordNotFound,
10     :with => :render_not_found
11     rescue_from ActionController::RoutingError,
12     :with => :render_not_found
13     rescue_from ActionController::UnknownController,
14     :with => :render_not_found
15     rescue_from ActionController::UnknownAction,
16     :with => :render_not_found
17   end
18
19   def render_error
20     render json: { errors: @object.errors.full_messages }, status: 422
21   end
22
23   def render_not_found
24     render json: { errors: ["Path not found"] }, status: 401
25   end
26
27   def index
28     @objects ||= model_class.all
29     render_list
30   end
31
32   def show
33     render json: @object
34   end
35
36   def create
37     @attrs = params[resource_name]
38     if @attrs.nil?
39       raise "no #{resource_name} (or #{resource_name.camelcase(:lower)}) provided with request #{params.inspect}"
40     end
41     if @attrs.class == String
42       @attrs = uncamelcase_hash_keys(JSON.parse @attrs)
43     end
44     @object = model_class.new @attrs
45     @object.save
46     show
47   end
48
49   def update
50     @attrs = params[resource_name]
51     if @attrs.is_a? String
52       @attrs = uncamelcase_hash_keys(JSON.parse @attrs)
53     end
54     @object.update_attributes @attrs
55     show
56   end
57
58   protected
59
60   def model_class
61     controller_name.classify.constantize
62   end
63
64   def resource_name             # params[] key used by client
65     controller_name.singularize
66   end
67
68   def find_object_by_uuid
69     logger.info params.inspect
70     if params[:id] and params[:id].match /\D/
71       params[:uuid] = params.delete :id
72     end
73     @object = model_class.where('uuid=?', params[:uuid]).first
74   end
75
76   def self.accept_attribute_as_json(attr, force_class=nil)
77     before_filter lambda { accept_attribute_as_json attr, force_class }
78   end
79   def accept_attribute_as_json(attr, force_class)
80     if params[resource_name].is_a? Hash
81       if params[resource_name][attr].is_a? String
82         params[resource_name][attr] = JSON.parse params[resource_name][attr]
83         if force_class and !params[resource_name][attr].is_a? force_class
84           raise TypeError.new("#{resource_name}[#{attr.to_s}] must be a #{force_class.to_s}")
85         end
86       end
87     end
88   end
89
90   def uncamelcase_params_hash_keys
91     self.params = uncamelcase_hash_keys(params)
92   end
93
94   def uncamelcase_hash_keys(h, max_depth=-1)
95     if h.is_a? Hash and max_depth != 0
96       nh = Hash.new
97       h.each do |k,v|
98         if k.class == String
99           nk = k.underscore
100         elsif k.class == Symbol
101           nk = k.to_s.underscore.to_sym
102         else
103           nk = k
104         end
105         nh[nk] = uncamelcase_hash_keys(v, max_depth-1)
106       end
107       h.replace(nh)
108     end
109     h
110   end
111
112   def render_list
113     @object_list = {
114       :kind  => "orvos##{resource_name}List",
115       :etag => "",
116       :self_link => "",
117       :next_page_token => "",
118       :next_link => "",
119       :items => @objects.map { |x| x }
120     }
121     render json: @object_list
122   end
123 end