Created
April 30, 2010 13:58
-
-
Save jlindsey/385236 to your computer and use it in GitHub Desktop.
Save partial model objects and junk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This was made for an internal project, so obviously change the
Child
andUser
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.