Introducing acts_as_api

Veröffentlich am 30.07.2010 von Christian Bäuerlein

As you may have noticed we are happily developing here at flinc using Ruby on Rails.

Rails makes it very easy to respond to clients in different formats like HTML, XML or JSON.

But only if you want to send the complete XML or JSON representation of a record.

If you want to exclude special fields or add some additional ones to the response it can get messy.

When we thought about a solution for this problem we had these requirements:

  • It should allow API responses with XML and JSON.
  • Keep it DRY. We didn’t want to create a template or builder for every response format.
  • Use the default XML, JSON renderers of the Rails application. (In case we want to exchange the renderer engines we will do this consistently for the complete app.)
  • Do it the Rails way. :)

The library acts_as_api is our approach to solve this problem in Rails 3 (at the moment it doesn’t support earlier versions).

Let’s do it:

When you installed the gem and added the dependency to the Gemfile of your Rails project your ActiveRecord models and your controllers are enriched with a couple new methods.

Prepare the models

This example assumes that you have a model called User. The User has probably a lot of attributes (password, email, created_at and so on) but you only want to expose first_name and last_name.

class User < ActiveRecord::Base

  # let this model act as api!
  acts_as_api

  # define the accessible attributes/methods for the api response
  api_accessible :first_name, :last_name

end

Now the model knows that it can “act as api” and you determined the attributes you want to render. That was easy right?

Prepare the controller

Your controllers knows a new method too: render_for_api. Just replace the old render with it if you want to render XML or JSON. You can pass any parameter you would also pass to the render method.

def show
  @user = User.find(params[:id])
  respond_to do |format|
    format.html
    format.xml  { render_for_api :x ml =>  @user }
    format.json { render_for_api :json => @user }
  end
end

That’s it!

Now no matter how many attributes your User model has, your API response will only render the attributes specified in api_accessible. An XML response would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<user>
    <first-name>Bruce</first-name>
    <last-name>Wayne</last-name>
</user>

And JSON like that:

{ "user": { "first_name": "Bruce", "last_name": "Wayne" } }

But wait! There’s more…

Besides excluding attributes you can also easily include virtual attributes. Let’s say you want to combine the first_name and last_name attributes into a new one called full_name.

Just create a method in the model and make it accessible for the API:

class User < ActiveRecord::Base

  # let this model act as api!
  acts_as_api

  # define the accessible attributes/methods for the api response
  api_accessible :full_name

  def full_name
    '' << first_name << ' ' << last_name
  end

end

And the result will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<user>
    <full-name>Bruce Wayne</full-name>
</user>

You also can rename the nodes/fields in the response by simply passing a hash instead of a symbol:

class User < ActiveRecord::Base   

  # let this model act as api!
  acts_as_api
  # define the accessible attributes/methods for the api response
  api_accessible :full_name, :but_call_me => :first_name

  def full_name
    '' << first_name << ' ' << last_name
  end

end

Now the first_name attribute is rendered as a node/field called but_call_me:

<?xml version="1.0" encoding="UTF-8"?>
<user>
    <full-name>Bruce Wayne</full-name>
    <but-call-me>Bruce</but-call-me>
</user>

And there is even more…

acts_as_api offers you even more options to customize XML/JSON requests the way you want. For example you can…

  • Include attributes and all other kinds of methods of your model
  • Include child associations (if they also act_as_api this will be considered)
  • Call methods of a parent association
  • Rename attributes, methods, associations
  • Create your own hierarchies

Take a look at the readme on github or our example Rails app to see what is possible.

Yep, it’s open source!

The acts_as_api gem is hosted at rubygems.org so you should be able to install it using ruby gems:

sudo gem install acts_as_api

The code is hosted at github and we also provide an example application to play around with.

The app uses SQLite as database so you can just clone it and start messing around. ;)

To dive into the single methods you can take a look at the documentation.

If you face any problems, bugs or you just want to help us enhancing the code please let us know in the comments or create an issue on github.

Let us know what you’re thinking!


flinc Unternehmenslösung

flinc bietet Unternehmen ein Echtzeit-Mitfahrnetzwerk für ihre Mitarbeiter. Mit flinc sind Sie nachhaltig und umweltschonend unterwegs. Sie verbessern nachweisbar die CO2-Bilanz Ihres Unternehmens und helfen, den Straßenverkehr zu entlasten und Staus zu vermeiden.

Vorteile der flinc-Unternehmenslösung für Unternehmen:

  • CO2-Bilanz verbessern
  • Parkraumkosten senken
  • Mitarbeiterkommunikation verbessern
  • Flexibilität verbessern

Unternehmen wie BASF, Procter & Gamble, WABCO, Marc O'Polo, Max Bögl und Vaude setzen unsere Lösung bereits ein.

Mehr Informationen zur Unternehmenslösung

7 thoughts on “Introducing acts_as_api

  1. Pingback: Twitted by flowdi

  2. One minor idea for the api: I like the way authlogic handles its configuration. They accept the whole configuration in a block, this way it can’t get scattered in the model.

    In their example, it looks like this:
    acts_as_authentic do |c|
    c.my_config_option = my_value
    end # the configuration block is optional

    For your gem, this could become:
    acts_as_api do |api|
    api.accessor :first_name, :last_name
    api.writer :password
    api.reader :id
    end
    Names are based on standard ruby class-methods attr_(reader|writer|accessor).

    Good work!

  3. sorry, the form butchered my formatting. Think of it as beeing neatly indented with 2 spaces in the blocks :-D

  4. Good post and this fill someone in on helped me alot in my college assignement. Say thank you you on your information.

  5. Hello Christian,

    Great plugin. However I am having some trouble in generating the same output as to_json. I have a posts collection in @posts that I am trying to render as json. However, it would put it like

    [{"post":"first_post's_json"},{"post":"second_post's_json"}]

    The plugin generates the json as

    {“posts”:[{first_post's_json}, {second_post's_json}]

    How do I fix this?

    Thanks,
    Prateek

    • Hi Prateek,

      at the moment your desired scheme is only possible if you wrap the desired into another hash (see the sub_node example on github https://github.com/fabrik42/acts_as_api)

      If you have further questions or feature requests please feel free to contact me, open a issue on github or fork my project.

      Best,
      Christian

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>