'Publishing' an object and associations using serialized fields
This is one I'm pretty proud of. It's certainly not perfect, and we'll see how future proof it is, but I think with the problem I was trying to solve, it came out as a fairly elegant solution.On our project, we had, ironically, a Project model that had all kinds of has_many associations on it. We needed a user to be able to "publish" their project and it would essentially take a snapshot of the project object and all association data at that time, and be able to server it in JSON for API calls.
I made a PublishedVersions polymorphic model with a publishable_id and publishable_type, and a serialized `details` field so it can just hold a JSON hash in the right format.
class PublishedVersion < ActiveRecord::Base
serialize :details
belongs_to :publishable, polymorphic: true
end
Then, when the user publishes an project, in whatever action that form goes to (:publish or :update, probably), we run
object_details = JSON.parse render_to_string(partial: 'api/projects/detail.json.jbuilder')
to render the proper project detail response as JSON.
After that, we persist that response right into the details field of the PublishedVersion object.
PublishedVersion.create(publishable_type: ‘Project’, publishable_id: project.id, details: published_details)
Our Project class gets a
has_many :published_versions, as: :publishable
relation. This way, there is a history of published versions which user can view as snapshots. If they really screw up the work-in-progress version of Project, they could revert to a previous version potentially, although this kind of functionality would be better handled by using something like paper_trail.
(The reverting process would have to be built as a separate method or class even, and it would have to find a published version by timestamp or id, and create or update all of the instances and associations. Nonetheless, the data would be in the database, so it's completely doable.)
For API calls to get the published version, since it’s already in JSON format, we don’t even need to render a jbuilder, we can simply run:
render json: object.published_versions.last.details
I made a helper method for this, #most_recent_published_version, that calls the same code.