davideisinger.com

My personal website
Log | Files | Refs | README

index.md (3655B)


      1 ---
      2 title: "Simple APIs using SerializeWithOptions"
      3 date: 2009-07-09T00:00:00+00:00
      4 draft: false
      5 canonical_url: https://www.viget.com/articles/simple-apis-using-serializewithoptions/
      6 ---
      7 
      8 While we were creating the [SpeakerRate
      9 API](http://speakerrate.com/api), we noticed that ActiveRecord's
     10 serialization system, while expressive, requires entirely too much
     11 repetition. As an example, keeping a speaker's email address out of an
     12 API response is simple enough:
     13 
     14 ```ruby
     15 @speaker.to_xml(:except => :email)
     16 ```
     17 
     18 But if we want to include speaker information in a talk response, we
     19 have to exclude the email attribute again:
     20 
     21 ```ruby
     22 @talk.to_xml(:include => { :speakers => { :except => :email } })
     23 ```
     24 
     25 Then imagine that a talk has a set of additional directives, and the API
     26 responses for events and series include lists of talks, and you can see
     27 how our implementation quickly turned into dozens of lines of repetitive
     28 code strewn across several controllers. We figured there had to be a
     29 better way, so when we couldn't find one, we created [SerializeWithOptions](https://github.com/vigetlabs/serialize_with_options). 
     30 
     31 At its core, SerializeWithOptions is a simple DSL for describing how to
     32 turn an ActiveRecord object into XML or JSON. To use it, put
     33 a `serialize_with_options` block in your model, like so:
     34 
     35 ```ruby
     36 class Speaker < ActiveRecord::Base
     37   # ...
     38   serialize_with_options do
     39     methods :average_rating, :avatar_url
     40     except :email, :claim_code
     41     includes :talks
     42   end
     43   # ...
     44 end
     45 
     46 class Talk < ActiveRecord::Base
     47   # ...
     48   serialize_with_options do
     49     methods :average_rating
     50     except :creator_id
     51     includes :speakers, :event, :series
     52   end
     53   # ...
     54 end
     55 ```
     56 
     57 With this configuration in place, calling `@speaker.to_xml` is the same
     58 as calling:
     59 
     60 ```ruby
     61 @speaker.to_xml(
     62   :methods => [:average_rating, :avatar:url],
     63   :except => [:email, :claim_code],
     64   :include => {
     65     :talks => {
     66       :methods => :average_rating,
     67       :except => :creator_id
     68     }
     69   }
     70 )
     71 ```
     72 
     73 Once you've defined your serialization options, your controllers will
     74 end up looking like this:
     75 
     76 ```ruby
     77 def show
     78   @post = Post.find(params[:id]) respond_to do |format|
     79     format.html
     80     format.xml { render :xml => @post }
     81     format.json { render :json => @post }
     82   end
     83 end
     84 ```
     85 
     86 Source code and installation instructions are available on GitHub. We
     87 hope this can help you DRY up your app's API, or, if it doesn't have
     88 one, remove your last excuse.
     89 
     90 **UPDATE 6/14:** We've added a few new features to SerializeWithOptions
     91 to handle some real-world scenarios we've encountered. You can now
     92 specify multiple `serialize_with_options` blocks:
     93 
     94 ```ruby
     95 class Speaker < ActiveRecord::Base
     96   # ...
     97   serialize_with_options do
     98     methods :average_rating, :avatar_url
     99     except :email, :claim_code
    100     includes :talks
    101   end
    102 
    103   serialize_with_options :with_email do
    104     methods :average_rating, :avatar_url
    105     except :claim_code
    106     includes :talks
    107   end
    108   # ...
    109 end
    110 ```
    111 
    112 You can now call `@speaker.to_xml` and get the default options, or
    113 `@speaker.to_xml(:with_email)` for the second set. When pulling in
    114 nested models, SerializeWithOptions will use configuration blocks with
    115 the same name if available, otherwise it will use the default.
    116 
    117 Additionally, you can now pass a hash to `:includes` to set a custom
    118 configuration for included models
    119 
    120 ```ruby
    121 class Speaker < ActiveRecord::Base
    122   # ...
    123   serialize_with_options do
    124     methods :average_rating, :avatar_url
    125     except :email, :claim_code
    126     includes :talks => { :include => :comments }
    127   end
    128   # ...
    129 end
    130 ```
    131 
    132 Use this method if you want to nest multiple levels of models or
    133 overwrite other settings.