Skip to content

Instantly share code, notes, and snippets.

@jlindsey
Created April 30, 2010 13:58
Show Gist options
  • Save jlindsey/385236 to your computer and use it in GitHub Desktop.
Save jlindsey/385236 to your computer and use it in GitHub Desktop.
Save partial model objects and junk
class CreateFormPartial < ActiveRecord::Migration
def self.up
create_table :form_partials do |t|
t.string :persistence_hash, :null => false
t.binary :data, :null => false
t.boolean :saved, :default => false
t.references :user, :null => false
t.timestamps
end
add_index :form_partials, [:persistence_hash, :user_id]
end
def self.down
drop_table :form_partials
end
end
##
# Used to store models and presenters in between multipage form pages.
# Can also store and retrieve any other object you want.
#
# @author Josh Lindsey
#
class FormPartial < ActiveRecord::Base
belongs_to :user
validates_presence_of :persistence_hash, :data, :user
validates_uniqueness_of :persistence_hash
class << self
##
# Store an object, bound to a single user for security.
#
# @param [Object] object The object to Marshal and store
# @param [User] user The user object (usually passed in with current_user)
# @param [Boolean] saved Set to true to override {clean_old_partials} date checking on this record
#
# @return [String] The persistence hash generated for this record, to {retrieve} it later
#
# @raises [InvalidRecord] On validation failure
#
# @author Josh Lindsey
#
def store(object, user, saved = false)
params = {}
params[:data] = Marshal.dump object
params[:persistence_hash] = generate_hash
params[:saved] = saved
params[:user] = user
model = self.create! params
params[:persistence_hash]
end
##
# Update an already stored object, given the hash and user it belongs to.
#
# @param [Object] object The updated object
# @param [User] user The user object
# @param [String] hash The persistence hash for the object
# @param [Boolean] saved Override {clean_old_partials} date checking
#
# @return [Boolean] True on success, false on failure
#
# @author Josh Lindsey
#
def update(object, user, hash, saved = nil)
params = {}
params[:data] = Marshal.dump object
params[:saved] = saved unless saved.nil?
model = self.find_by_persistence_hash_and_user_id(hash, user.id)
model.update_attributes params
end
##
# Shortcut method to {store} an object with saved set to true.
#
# @param [Object] object The object to save
# @param [User] The user object
#
# @return [String] The persistence hash generated by {store}
#
# @see {store} for actual logic
#
# @author Josh Lindsey
#
def save(object, user)
store(object, user, true)
end
##
# Retrieve a stored object given the user and hash it's associated with.
#
# @param [User] The user object
# @param [String] The persistence hash
#
# @return [Object] Whatever object was stored. *Note*: If that object was an ActiveRecord
# model, you will have to reestablish the database connection before calling save on it.
#
# @author Josh Lindsey
#
def retrieve(user, hash)
str = self.find_by_persistence_hash_and_user_id(hash, user.id).data
return if str.nil?
Marshal.load str
end
##
# Cleans out old partials (seriously). Should be run as a cron with script/runner or the like.
#
# @param [Date] date All records last updated before this date, and with saved == false are destroyed
#
# @author Josh Lindsey
#
def clean_old_partials(date = 1.day.ago)
partials = self.find(:all, :conditions => ['updated_at <= ? AND saved = 0', date])
partials.each { |partial| partial.destroy }
end
##
# Generate a random SHA1 hash for storing new records.
#
# @return [String] The hash generated
#
# @author Josh Lindsey
#
def generate_hash
Digest::SHA1.hexdigest rand.to_s
end
end
end
require 'test_helper'
class FormPartialTest < ActiveSupport::TestCase
should_validate_presence_of :persistence_hash, :data, :user
context "The FormPartial model" do
setup do
@stored_object = Child.make
@user = User.make
@token = FormPartial.store @stored_object, @user
end
should "store and retrieve objects" do
obj = FormPartial.retrieve(@user, @token)
assert_instance_of Child, obj
assert_equal @stored_object, obj
end
should "update existing objects" do
obj = FormPartial.retrieve(@user, @token)
obj.first_name = "Tom"
assert FormPartial.update(obj, @user, @token)
updated = FormPartial.retrieve(@user, @token)
assert_equal obj, updated
end
should "clean out its table" do
FormPartial.record_timestamps = false
assert_equal 1, FormPartial.count
FormPartial.store @stored_object, @user
FormPartial.last.update_attribute :updated_at, 5.days.ago
assert_equal 2, FormPartial.count
FormPartial.store @stored_object, @user
FormPartial.last.update_attributes :updated_at => 5.days.ago,
:saved => true
assert_equal 3, FormPartial.count
FormPartial.clean_old_partials
assert_equal 2, FormPartial.count
end
end
end
@jlindsey
Copy link
Author

This was made for an internal project, so obviously change the Child and User objects in the test to whatever you want to test it on.

The test is using Test::Unit, shoulda, and machinist (with my fork of random_data).

The documentation comments are in YARDOC syntax.

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