0

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.

Ryan Mitchell
  • 1,410
  • 1
  • 14
  • 23

1 Answers1

1

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"
Community
  • 1
  • 1
phillbaker
  • 1,518
  • 11
  • 22
  • Sorry if I wasn't clear, but this isn't what I'm asking - I'm not concerned with rendering the RESPONSE to the post, that I know how to do. I tweaked the question to draw more attention to the important part: that doing it the way I'm doing above results in TWO places I have to map DB column names to API property names, and I'd prefer to just do the mapping once. Ideally I could leverage the knowledge in the RABL template, but I'm open to other approaches. – Ryan Mitchell Dec 13 '13 at 18:25
  • Would putting the mapping in `car.rabl` work? You can just do the mapping once there and then reference that file from the different actions. It's basically a partial for the object. – phillbaker Dec 13 '13 at 19:13
  • The issue isn't duplicating the mapping in multiple RABL templates. It's duplicating the mapping in the template and the controller - having to perform database_column -> api_property in the template, and then to perform api_property -> database_column in the create action of the controller. – Ryan Mitchell Dec 13 '13 at 19:21
  • You're totally right. My answer is all about Ruby/Rails => JSON, your question is JSON => Ruby/Rails. This answer might help: http://stackoverflow.com/a/4017071/2284646. Or you can extract a class that does this mapping and centralizing the logic there. You're right, RABL is an output generator, not a parser. I'll update my answer. – phillbaker Dec 13 '13 at 20:00
  • Aliased attributes are an interesting idea - and if that isn't fine-grained enough it sounds like I'll need to suck it up and do it myself. Thanks for the help! – Ryan Mitchell Dec 15 '13 at 20:16