class Unival::App

Constants

Inv
SUPPORTED_METHODS

Public Instance Methods

call(env) click to toggle source
# File lib/unival/app.rb, line 8
def call(env)
  req = Rack::Request.new(env)
  
  # Only support POST, PUT and PATCH
  if !SUPPORTED_METHODS.include?(env['REQUEST_METHOD'])
    e = {error: 'Unsupported HTTP method %s' % env['REQUEST_METHOD']}
    return [406, {'Content-Type' => 'application/json', 'Allow' => 'POST,PUT,PATCH'}, [JSON.dump(e)]]
  end
  
  params = JSON.load(env['rack.input'].read)
  
  query_params = extract_query_or_route_params_from(req)
  
  model_module_name = query_params.delete('model')
  raise Inv, "No model class given (by default passed as the `model' query-string param)" if model_module_name.to_s.empty?
  
  model_module = Kernel.const_get(model_module_name)
  raise Inv, "Invalid model or model not permitted" unless model_accessible?(model_module)
  
  model = if req.post?
    raise Inv, "The model module does not support .new" unless model_module.respond_to?(:new)
    model_module.new
  else
    model_id = query_params.delete('id')
    raise Inv, "No model ID to find given (by default passed as the `id' query-string param)" unless model_id
    raise Inv, "The model module does not support .find" unless model_module.respond_to?(:find)
    model_module.find(model_id)
  end
  
  # Instead of scanning for instance_methods, check the object itself.
  raise Inv, "The model (#{model.class}) does not support `#valid?'" unless model.respond_to?(:valid?)
  
  model_data = filter_model_params(model_module, params)
  
  # Instead of scanning for instance_methods, check the object itself.
  raise Inv, "The model does not support `#valid?'" unless model.respond_to?(:valid?)
  
  # Despite what you might think, attributes= ONLY updates the attributes given in the argument.
  model.attributes = model_data
  
  is_create = req.post?
  if model.valid?
    d = {model: model_module.to_s, is_create: is_create, valid: true, errors: nil}
    [200, {'Content-Type' => 'application/json'}, [JSON.dump(d)]]
  else
    model_errors = replace_with_translation_keys(model.errors.as_json)
    d = {model: model_module.to_s, is_create: is_create, valid: false, errors: model_errors}
    [409, {'Content-Type' => 'application/json'}, [JSON.dump(d)]]
  end
rescue Exception => e
  if e.to_s =~ /NotFound/
    d = {error: "Model not found: #{e}"}
    [404, {'Content-Type' => 'application/json'}, [JSON.dump(d)]]
  elsif e.is_a?(Inv)
    d = {error: e.message}
    [400, {'Content-Type' => 'application/json'}, [JSON.dump(d)]]
  else
    raise e # Something we can't handle internally, raise it up the stack for eventual exception capture in middleware
  end
end
extract_query_or_route_params_from(rack_request) click to toggle source

Extract params like :format, :id and :model

# File lib/unival/app.rb, line 91
def extract_query_or_route_params_from(rack_request)
  Rack::Utils.parse_nested_query(rack_request.query_string)
end
filter_model_params(model_module, params) click to toggle source

Can be used to do optional parameter filtering. If you want to use strong parameters, this is the place to apply them.

# File lib/unival/app.rb, line 86
def filter_model_params(model_module, params)
  params
end
model_accessible?(model_module) click to toggle source

Tells whether it is permitted to validate a given Module object as an ActiveModel. You might want to restrict this further. The default permits everything.

# File lib/unival/app.rb, line 71
def model_accessible?(model_module)
  true
end
replace_with_translation_keys(model_errors) click to toggle source

Replaces the literal strings in the model errors (furnished as a json-able Hash with arbitrary nesting) with the I18n keys. Only gets performed if the translation introspection module is present on the I18n backend currently in use.

# File lib/unival/app.rb, line 79
def replace_with_translation_keys(model_errors)
  return model_errors if internationalized?
  deep_translation_replace(model_errors)
end