Hidden Model Attributes

on Rails, ActiveRecord, Hidden Attributes

Sometimes we have some attributes of model that should not to be available for client (via json format, javascript variables...), such as: password, login count, secret token... When we do render json: @visitor, Rails will return to client like this:

{
  id: 1,
  secret_token: "iHj-Psm45Ymge-7kZNyx",
  name: "Test Visitor",
  visit_count: "19"
}

This is not what we want because we want secret_token and visit_count invisible to client.

Solution

One of below ways

  1. Add except option. This solution is good for attributes that not always need to be hidden, not good otherwise because it need to be applied everywhere
    render json: @user.as_json(except: [:secret_token, :visit_count])

  2. Override serializable_hash instance method (rails use this method to get a hash that will be used to convert to formats)

#app/models/visitor.rb

HIDDEN_FIELDS = [:secret_token, :visit_count]

def serializable_hash(options={})
  options[:except] = Array(options[:except])

  if options[:force_except]
    options[:except].concat Array(options[:force_except])
  else
    options[:except].concat HIDDEN_FIELDS
  end
  super(options)
end

The idea is simple, just override it to add our HIDDEN_FIELDS to except option like we did in #1. With this overriding, we will not need to specific except anytimes we render object, just use render json: @visitor as normal.

I recommend to use ActiveSupport::Concern for this overriding, e.g: create module Utils::HiddenFields like this:

#app/models/concerns/hidden_fields.rb
Module Utils::HiddenFields
  extend ActiveSupport::Concern

  def serializable_hash(options={})
    options[:except] = Array(options[:except])

    if options[:force_except]
      options[:except].concat Array(options[:force_except])
    else
      # Don't hide anything of HIDDEN_FIELDS is not defined
      options[:except].concat(HIDDEN_FIELDS || [])
    end
    super(options)
  end
end

and include it in models that need this feature

#app/models/visitor.rb

class Visitor < ActiveRecord::Base
  HIDDEN_FIELDS = [:secret_token, :visit_count]

  include Utils::HiddenFields
end

A full stack developer with Ruby on Rails as the main framework. Eager to learn and share.