Excluding fields from Rails' #to_json/#to_xml

By Morten Møller Riis

February 17 2011 14:00 CET

Rails makes it very easy to allow RESTful API access to your application and presenting the user with JSON or XML.

Actually, if you build your app following the REST conventions it is ready out of the box.

One detail, however, is that Rails as default returns JSON/XML output by using the #to_json or #to_xml methods.

Imagine the following scenario. We have a model called Person which has the attributes name, age, is_a_troll.

Now in our application we use some respond_to :html, :json, :xml and respond_with(person) magic to present the object to the user.

However, we do not want to show the is_a_troll attribute by default. In our HTML view this is pretty easy to avoid. However, we might not want to make dedicated xml and json views or use the block method for respond_with since this, in a large app, can become pretty cumbersome.

What I have done instead is to write a small extension for ActiveRecord.

config/initializers/active_record_extension.rb

               require 'active_record_extensions'
               ActiveRecord::Base.send(:include, ActiveRecordExtensions::Base)
             

lib/active_record_extension.rb

              module ActiveRecordExtensions
                module Base
                  module ClassMethods
                    def json(options={})
                      @to_json_options = options
                    end
                    
                    def json_options
                      @to_json_options
                    end
                    
                    def xml(options={})
                      @to_xml_options = options
                    end
                    
                    def xml_options
                      @to_xml_options
                    end
                  end
                  
                  def self.included(base)
                    base.extend(ClassMethods)
                    base.class_eval do
                      alias_method_chain :to_json, :config        
                      alias_method_chain :to_xml, :config
                    end
                  end
                  
                  def to_json_with_config(options = {})
                    options.merge!(self.class.json_options || {})
                    to_json_without_config(options)      
                  end
                  
                  def to_xml_with_config(options = {}, &block)
                    options.merge!(self.class.xml_options || {})
                    to_xml_without_config(options, &block)
                  end
                  
                end
              end
             

This enables you to do the following in your model eg.

               class Person < ActiveRecord::Base
                 json :except => [:is_a_troll]
                 xml :except => [:is_a_troll]
               end
             

The options set via the #json and #xml methods will be appended to the #to_json and #to_xml calls made by respond_with.

And that is all there is to it. I would love to see such a feature make it into Rails at some point and am contemplating submitting a patch.

If you have any ideas or critique of this approach – please do not hesitate to leave a comment!