There can be a business or security requirement that certain fields on a model should never be exposed in an API. One approach to this is to tell the developers not to ever put the field in the API output, but I prefer to protect them at the model level because that's where the requirement actually is.

The goal of this is to have a simple way to specify fields that should be prevented from being exposed. This is one approach and I would love feedback both on the implementation and on the approach. Ideas and criticism are very welcome. :)

*-----  please check out my questions at the bottom ------*

### Implementation - Extend ActiveRecord::Base

    # Provide a system for specifying private attributes that shouldn't be exposed
    module ActiveRecord
      class Base
        class << self

          # Instead of setting the instance variable when this is called, we only check
          # if it's defined. It's only set when attr_private is called. This allows us
          # to know if the model has ever set any private attributes or not
          def private_attributes
            instance_variable_defined?('@private_attributes') ? @private_attributes : []
          end

          def is_private_attribute?(name)
            private_attributes.include?(name.to_sym)
          end

          protected

          # Set the @private_attributes variable with an array of attribute symbols
          def attr_private(*args)
            (@private_attributes ||= []).push(*args.collect { |a| a.to_sym }).uniq!
          end

          # Specify public attributes, which conversely privatizes the other attributes
          # If private attributes have previously been declared, attr_public can override
          # the setting. If it is the first time, make all the attributes private unless
          # they are in the args.
          def attr_public(*args)
            if instance_variable_defined?('@private_attributes') && !@private_attributes.empty?
              @private_attributes.delete_if { |n| args.include?(n.to_sym) }
            else
              attr_private(*attribute_names.reject { |n| args.include?(n.to_sym) })
            end
          end
        end

        # Run the to_xml options through a filter
        def to_xml(options={})
          super(secure_private_options(options))
        end

        # Run the serializable_hash options through a filter
        def serializable_hash(options={})
          super(secure_private_options(options))
        end

        protected

        # Filter the options to make sure private attributes aren't included
        def secure_private_options(options={})
          (options[:except] ||= []).push(*self.class.private_attributes)
          options[:only].delete_if { |n| self.class.is_private_attribute?(n) } if options.has_key?(:only)
          options[:methods].delete_if { |n| self.class.is_private_attribute?(n) } if options.has_key?(:methods)
          options
        end
      end
    end

### Example of use in a third party library - RABL

    # Modify Rabl's builder to check if a method is private before exposing it
    module Rabl
      class Builder
        protected
        # Don't output the
        def attribute(name, options={})
          unless @_object.class.respond_to?(:is_private_attribute?) && @_object.class.is_private_attribute?(name)
            @_result[options[:as] || name] = data_object_attribute(name) if @_object && @_object.respond_to?(name)
          end
        end
      end
    end

## Example of general use on a model instance

    # An example of usage.
    # Fields: category_id, name, description, supplier
    #
    # Let's assume we never want supplier to be exposed.
    #
    class Product < ActiveRecord::Base
    attr_private :supplier
    end

    Product.first.to_json
    #=> { "category_id": 1, "name": "Bucky Balls", "description": "Awesome Magnets" }

    Product.is_private_attribute?(:supplier)
    #=> true

    Product.is_private_attribute?(:category_id)
    #=> false

    Product.private_attributes
    #=> [ :supplier ]


    # You can also use attr_public, which takes the attributes_names and makes all of them private except the items listed in the attr_public arguments

## Additional notes

 * You could support role based access control by turning the underlying instance variable into a hash keyed to the role instead of a single array.
 * The attributes could still be exposed by having them called directly, which allows for usage in cases where you actually WANT them exposed (like perhaps an admin panel).


## Questions

1) As you see in the "secure_private_options" method, I check the passed in methods to make sure they aren't on the blacklist. This is fine for using attr_private to declare methods that shouldn't called, but I can't figure out how to set it so that attr_public can automatically include all getter attribute methods as well as just the attributes. For example, if I have a custom method called "full_name" that I want to protect, it would be nice if attr_public would automatically protect it.

2) Is there something in ActiveRecord or rails that provides for this functionality already?

3) What use cases am I not thinking about? 

## Gist

A gist of this code can be found here: https://gist.github.com/3806392