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.