simplify SimplyVersioned option storage and add callbacks

SimplyVersioned plugin needs callbacks for the addition of the
submission_versions index table. Whenever a submission version is
saved, we will want to add an entry to the submission_versions table
in addition to the versions table. This patch provides hooks for
those callbacks.

Test Plan:
- Versioned anything should still work

Refs CNVS-2802

Change-Id: I02a862c5894a98ece8a586330f264680751ab52f
Reviewed-on: https://gerrit.instructure.com/19520
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Jacob Fugal <jacob@instructure.com>
Reviewed-by: Duane Johnson <duane@instructure.com>
Product-Review: Jacob Fugal <jacob@instructure.com>
This commit is contained in:
Duane Johnson 2013-04-11 12:01:34 -06:00 committed by Jacob Fugal
parent bd5c66011a
commit 9c55ed33f9
7 changed files with 67 additions and 58 deletions

View File

@ -49,13 +49,7 @@ class LearningOutcomeResult < ActiveRecord::Base
nil nil
end end
end end
def changes_worth_versioning?
!(self.changes.keys - [
"updated_at",
]).empty?
end
def save_to_version(attempt) def save_to_version(attempt)
current_version = self.versions.current.try(:model) current_version = self.versions.current.try(:model)
if current_version.try(:attempt) && attempt < current_version.attempt if current_version.try(:attempt) && attempt < current_version.attempt

View File

@ -140,8 +140,18 @@ class Submission < ActiveRecord::Base
has_a_broadcast_policy has_a_broadcast_policy
simply_versioned :explicit => true simply_versioned :explicit => true, :when => lambda{ |model|
model.turnitin_data_changed? || (model.changes.keys - [
"updated_at",
"processed",
"process_attempts",
"changed_since_publish",
"grade_matches_current_submission",
"published_score",
"published_grade"
]).present?
}
set_policy do set_policy do
given {|user| user && user.id == self.user_id } given {|user| user && user.id == self.user_id }
can :read and can :comment and can :make_group_comment and can :submit can :read and can :comment and can :make_group_comment and can :submit
@ -1064,19 +1074,11 @@ class Submission < ActiveRecord::Base
def turnitin_data_changed! def turnitin_data_changed!
@turnitin_data_changed = true @turnitin_data_changed = true
end end
def changes_worth_versioning? def turnitin_data_changed?
!(self.changes.keys - [ @turnitin_data_changed
"updated_at",
"processed",
"process_attempts",
"changed_since_publish",
"grade_matches_current_submission",
"published_score",
"published_grade"
]).empty? || @turnitin_data_changed
end end
def get_web_snapshot def get_web_snapshot
# This should always be called in the context of a delayed job # This should always be called in the context of a delayed job
return unless CutyCapt.enabled? return unless CutyCapt.enabled?

View File

@ -67,9 +67,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../common')
end end
def create_wiki_page(title, hfs, edit_roles) def create_wiki_page(title, hfs, edit_roles)
p = @course.wiki.wiki_pages.create(:title => title, :hide_from_students => hfs, :editing_roles => edit_roles, :notify_of_update => true) @course.wiki.wiki_pages.create(:title => title, :hide_from_students => hfs, :editing_roles => edit_roles, :notify_of_update => true)
p.save!
p
end end
def select_all_wiki def select_all_wiki
@ -129,4 +127,4 @@ require File.expand_path(File.dirname(__FILE__) + '/../common')
wait_for_ajax_requests wait_for_ajax_requests
get "/courses/#{@course.id}/wiki" #can't just wait for the dom, for some reason it stays in edit mode get "/courses/#{@course.id}/wiki" #can't just wait for the dom, for some reason it stays in edit mode
wait_for_ajax_requests wait_for_ajax_requests
end end

View File

@ -99,7 +99,7 @@ describe "Wiki pages and Tiny WYSIWYG editor" do
keep_trying_until { f("#page_history").should be_displayed } keep_trying_until { f("#page_history").should be_displayed }
f('#page_history').click f('#page_history').click
ff('a[title]').length.should == 3 ff('a[title]').length.should == 2
end end
@ -167,7 +167,7 @@ describe "Wiki pages and Tiny WYSIWYG editor" do
f('.history').click f('.history').click
wait_for_ajax_requests wait_for_ajax_requests
ff('a[title]').length.should == 3 ff('a[title]').length.should == 2
end end
end end
end end

View File

@ -16,6 +16,17 @@ module SoftwareHeretics
super( "Keys: #{keys.join( "," )} are not known by SimplyVersioned" ) super( "Keys: #{keys.join( "," )} are not known by SimplyVersioned" )
end end
end end
DEFAULTS = {
:keep => nil,
:automatic => true,
:exclude => [],
:explicit => false,
# callbacks
:when => nil,
:on_create => nil,
:on_update => nil
}
module ClassMethods module ClassMethods
@ -24,11 +35,22 @@ module SoftwareHeretics
# the +versions+ association. # the +versions+ association.
# #
# Options: # Options:
# +limit+ - specifies the number of old versions to keep (default = nil, never delete old versions) # +keep+ - specifies the number of old versions to keep (default = nil, never delete old versions)
# +automatic+ - controls whether versions are created automatically (default = true, save versions) # +automatic+ - controls whether versions are created automatically (default = true, save versions)
# +exclude+ - specify columns that will not be saved (default = [], save all columns) # +exclude+ - specify columns that will not be saved (default = [], save all columns)
# +explicit+ - INSTRUCTURE: explicit versioning keeps the last version up to date, but doesn't #
# automatically create new versions (default = false) # Additional INSTRUCTURE options:
# +explicit+ - explicit versioning keeps the last version up to date,
# but doesn't automatically create new versions (default = false)
# +when+ - callback to indicate whether an instance needs a version
# saved or not. if present, the model is passed to the
# callback which should return true or false, true indicating
# a version should be saved. if absent, versions are saved if
# any attribute other than updated_at is changed.
# +on_create+ - callback to allow additional changes to a new version
# that's about to be saved.
# +on_update+ - callback to allow additional changes to an updated (see
# +explicit+ parameter) version that's about to be saved.
# #
# To save the record without creating a version either set +versioning_enabled+ to false # To save the record without creating a version either set +versioning_enabled+ to false
# on the model before calling save or, alternatively, use +without_versioning+ and save # on the model before calling save or, alternatively, use +without_versioning+ and save
@ -36,15 +58,11 @@ module SoftwareHeretics
# #
def simply_versioned( options = {} ) def simply_versioned( options = {} )
bad_keys = options.keys - [:keep,:automatic,:exclude,:explicit] bad_keys = options.keys - SimplyVersioned::DEFAULTS.keys
raise SimplyVersioned::BadOptions.new( bad_keys ) unless bad_keys.empty? raise SimplyVersioned::BadOptions.new( bad_keys ) unless bad_keys.empty?
options.reverse_merge!( { options.reverse_merge!(DEFAULTS)
:keep => nil, options[:exclude] = Array( options[ :exclude ] ).map( &:to_s )
:automatic => true,
:exclude => [],
:explicit => false
})
has_many :versions, :order => 'number DESC', :as => :versionable, :dependent => :destroy, :extend => VersionsProxyMethods has_many :versions, :order => 'number DESC', :as => :versionable, :dependent => :destroy, :extend => VersionsProxyMethods
# INSTRUCTURE: Added to allow quick access to the most recent version # INSTRUCTURE: Added to allow quick access to the most recent version
@ -53,17 +71,8 @@ module SoftwareHeretics
before_save :check_if_changes_are_worth_versioning before_save :check_if_changes_are_worth_versioning
after_save :simply_versioned_create_version after_save :simply_versioned_create_version
cattr_accessor :simply_versioned_keep_limit cattr_accessor :simply_versioned_options
self.simply_versioned_keep_limit = options[:keep] self.simply_versioned_options = options
cattr_accessor :simply_versioned_save_by_default
self.simply_versioned_save_by_default = options[:automatic]
cattr_accessor :simply_versioned_excluded_columns
self.simply_versioned_excluded_columns = Array( options[ :exclude ] ).map( &:to_s )
cattr_accessor :simply_versioned_explicitly_versioned
self.simply_versioned_explicitly_versioned = options[:explicit]
class_eval do class_eval do
def versioning_enabled=( enabled ) def versioning_enabled=( enabled )
@ -73,7 +82,7 @@ module SoftwareHeretics
def versioning_enabled? def versioning_enabled?
enabled = self.instance_variable_get( :@simply_versioned_enabled ) enabled = self.instance_variable_get( :@simply_versioned_enabled )
if enabled.nil? if enabled.nil?
enabled = self.instance_variable_set( :@simply_versioned_enabled, self.simply_versioned_save_by_default ) enabled = self.instance_variable_set( :@simply_versioned_enabled, self.simply_versioned_options[:automatic] )
end end
enabled enabled
end end
@ -170,8 +179,9 @@ module SoftwareHeretics
# on the before_create to see if the changes are worth # on the before_create to see if the changes are worth
# creating a new version for # creating a new version for
def check_if_changes_are_worth_versioning def check_if_changes_are_worth_versioning
@changes_are_worth_versioning = !self.respond_to?(:changes_worth_versioning?) || self.changes_worth_versioning? @changes_are_worth_versioning = simply_versioned_options[:when] ?
@changes_are_worth_versioning = false if self.changes.keys == ["updated_at"] simply_versioned_options[:when].call(self) :
(self.changes.keys - ["updated_at"]).present?
true true
end end
@ -180,12 +190,17 @@ module SoftwareHeretics
# INSTRUCTURE # INSTRUCTURE
if @versioning_explicitly_enabled || @changes_are_worth_versioning if @versioning_explicitly_enabled || @changes_are_worth_versioning
@changes_are_worth_versioning = nil @changes_are_worth_versioning = nil
if simply_versioned_explicitly_versioned && !@simply_versioned_explicit_enabled && versioned? if simply_versioned_options[:explicit] && !@simply_versioned_explicit_enabled && versioned?
version = self.versions.current version = self.versions.current
version.yaml = self.attributes.except( *simply_versioned_excluded_columns ).to_yaml version.yaml = self.attributes.except( *simply_versioned_options[:exclude] ).to_yaml
simply_versioned_options[:on_update].try(:call, self, version)
version.save version.save
elsif self.versions.create( :yaml => self.attributes.except( *simply_versioned_excluded_columns ).to_yaml ) else
self.versions.clean_old_versions( simply_versioned_keep_limit.to_i ) if simply_versioned_keep_limit version = self.versions.create( :yaml => self.attributes.except( *simply_versioned_options[:exclude] ).to_yaml )
if version
simply_versioned_options[:on_create].try(:call, self, version)
self.versions.clean_old_versions( simply_versioned_options[:keep].to_i ) if simply_versioned_options[:keep]
end
end end
end end
end end

View File

@ -283,7 +283,7 @@ class SimplyVersionedTest < FixturedTestCase
end end
def test_should_exclude_columns def test_should_exclude_columns
assert_equal ["trouble"], Saproling.simply_versioned_excluded_columns assert_equal ["trouble"], Saproling.simply_versioned_options[:exclude]
sylvia = Saproling.create!( :name => 'Sylvia', :trouble => "big" ) sylvia = Saproling.create!( :name => 'Sylvia', :trouble => "big" )
@ -296,7 +296,7 @@ class SimplyVersionedTest < FixturedTestCase
klass.module_eval <<-DEFN klass.module_eval <<-DEFN
simply_versioned :exclude => :some_column simply_versioned :exclude => :some_column
DEFN DEFN
assert_equal ['some_column'], klass.simply_versioned_excluded_columns assert_equal ['some_column'], klass.simply_versioned_options[:exclude]
end end
def test_should_report_version_number def test_should_report_version_number

View File

@ -3,7 +3,7 @@ TEST_FOLDER = File.dirname( __FILE__ )
$:.unshift( File.join( TEST_FOLDER, '..', 'lib' ) ) $:.unshift( File.join( TEST_FOLDER, '..', 'lib' ) )
HOST_APP_FOLDER = File.expand_path( ENV['HOST_APP'] || File.join( TEST_FOLDER, '..', '..', '..' ) ) HOST_APP_FOLDER = File.expand_path( ENV['HOST_APP'] || File.join( TEST_FOLDER, '..', '..', '..', '..' ) )
puts "Host application: #{HOST_APP_FOLDER}" puts "Host application: #{HOST_APP_FOLDER}"
require 'test/unit' require 'test/unit'