Using RABL (or other API builder) to handle model creation/update?

406 Views Asked by At

I'm using RABL right now to generate JSON responses of an API in Rails, but I'm finding that while RABL is super handy for mapping models to responses, to create a consistent API I'm having to to duplicate that mapping logic in the update and create functions of my controller.

As a simple example, if I just want to change the attribute names in the response to a POST request, I can do this in RABL:

create.rabl

object @car
attributes car_id: :id, badly_named_legacy_column_that_means_color: :color

But if I want the client to be able to use these same "cleaned up" attributes in the JSON POST/PUT request itself (i.e. be able to send { "id": 1, "color": "red" } instead of { "car_id": 1, "badly_named_legacy_column_that_means_color": "red" }), I have to manually do this mapping again in the controller:

cars_controller.rb

def create
  params[:car_id] = params.delete(:id)
  params[:badly_named_legacy_column_that_means_color] = params.delete(:color)
  @car = Car.create(params)
end

Now there are two places that I need to map car_id to badly_named_legacy_column_that_means_color. Not very DRY.

So far I haven't come across any way to handle this using RABL. Is there one that I'm missing? I also realize this might be outside the scope of RABL, which bills itself specifically as a templating system, so maybe is there another API builder that would allow me to do this? I love the idea of mapping messy database columns to a clean API but having to specify this mapping in both the view and the controller isn't very DRY. Any thoughts appreciated.

1

There are 1 best solutions below

5
On BEST ANSWER

Update

The original answer is all about Ruby/Rails => JSON, the question is JSON => Ruby/Rails. This answer about associating columns should explain an approach:

alias_attribute :new_column_name, :column_name_in_db

Then you can just reference new_column_name in the RABL and Rails will handle the association on the create/update.


You should be able to call render from the create method and render any view. You could customize a response with a create specific template or reuse the generic show template. The trick is to re-use the object rabl template (app/views/car/car.rabl in this case), for example:

  # POST /cars
  def create
    @car = Car.new(params)

    if @car.save
      render action: 'show'
    else
      respond_with @car
    end
  end

Where app/views/cars/car.rabl is

attributes :id, ...

and app/views/cars/show.rabl is

object @car

extends "cars/car"