Skip to content

Instantly share code, notes, and snippets.

@theotherzach
Created April 3, 2014 20:24
Show Gist options
  • Save theotherzach/9962199 to your computer and use it in GitHub Desktop.
Save theotherzach/9962199 to your computer and use it in GitHub Desktop.

The problem: Active Record objects have built in serialization (to_json), but I want to use vanilla objects wherever possible.

My solution:

class Report
  attr_reader :data, :group_reports
  def initialize(data)
    @data = data
    @group_reports = (data.weekly_group_reports + [data.weekly_used_report]).map { |group_report|
      GroupReport.build(group_report, self)
    }
  end

#Not shown: a bunch of public methods

  def to_hash
    attributes.each_with_object({}) { |e,h|
      h[e] = send(e)
    }.merge({ group_reports: group_reports.map(&:to_hash) })
  end

 
  # When public_methods is used with the false flag, an
  # array of the instance's methods WITHOUT the ancestors'
  # are returned.
  # Note that we're subtracting methods that we don't want
  # to serialize. We don't need to blacklist :attributes since we're
  # able to leave that as a private method.
  private
  def attributes
    public_methods(false) - [:data, :to_hash, :group_reports]
  end
end

.

  class GroupReport
    attr_reader :data, :back
    def initialize(data, report)
      @data = data
      @back = report.per_retail_unit_new
    end

    def to_hash
      attributes.each_with_object({}) { |e,h|
        h[e] = send(e)
      }
    end


   # public_methods(false) doesn't work here since we're using a strategy pattern.
   # If an instance of GroupReportUsed was sent public_methods(false), it would 
   # only return [:franchise_name]. Luckily, we're able to manually blacklist the
   # next highest known ancestor. 
    def attributes
      public_methods - Object.methods - [:attributes, :data, :to_hash]
    end

    def franchise_name
      data.franchise_name
    end

    def front
      data.average_gross
    end
  end

  class GroupReportUsed < GroupReport
    def initialize(data, report)
      @data = data
      @back = report.per_retail_unit_used
    end

    def franchise_name
      "Used"
    end

  end

I chose to expose this functionality via creating a hash rather than implementing the interface that Active Model likes. The end result looks like: render json: report.to_hash

That might be inefficient on larger objects since I'm creating an intermediary hash, but I find this to be significantly faster than relying on the built in Active Record serialization. That's not the point of the exercise, but it was a nice bonus.

Not sure if this is useful for anybody else, but I thought I'd share.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment