lib/active_metadata/base.rb in active_metadata-0.2.3 vs lib/active_metadata/base.rb in active_metadata-0.2.4

- old
+ new

@@ -17,11 +17,14 @@ end module Config def acts_as_metadata *args + before_update :manage_concurrency after_save :save_history + attr_accessor :conflicts + attr_accessor :active_metadata_timestamp class_variable_set("@@metadata_id_from", args.empty? ? nil : args[0][:metadata_id_from]) include ActiveMetadata::Base::InstanceMethods include ActiveMetadata::Persistence @@ -32,63 +35,91 @@ module InstanceMethods include ActiveMetadata::Helpers + def attributes=(attributes) + attributes.delete(:active_metadata_timestamp) unless attributes[:active_metadata_timestamp].nil? + super + end + def self.included(klass) [:notes, :attachments, :history].each do |item| klass.send(:define_method, "#{item.to_s}_cache_key".to_sym) do |field| "#{Rails.env}/active_metadata/#{item.to_s}/#{self.class}/#{metadata_id}/#{field}/" end end end def metadata_id - metadata_id_from = self.class.class_variable_get("@@metadata_id_from") - return self.id if metadata_id_from.nil? - receiver = self - metadata_id_from.each do |item| - receiver = receiver.send item - end - receiver.id + metadata_root.id end + def active_metadata_timestamp + metadata_root.active_metadata_timestamp || nil + end + def current_user_id if User.respond_to?(:current) && !User.current.nil? User.current.id else nil end end - # Resolve concurrency using the passed timestamps and the active_metadata histories - # To be called from controller before updating the model. Params that contains a conflict are removed from the params list. - # Returns 2 values containing the the deleted params with the related history: - # [{:key => [passed_value,history]}] + def metadata_root + metadata_id_from = self.class.class_variable_get("@@metadata_id_from") + return self if metadata_id_from.nil? + receiver = self + metadata_id_from.each do |item| + receiver = receiver.send item + end + receiver + end + + # Resolve concurrency using the provided timestamps and the active_metadata histories. + # Conflicts are stored into @conflicts instance variable # - # first result is the WARNINGS array: conflict appears on a field not touched by the user that submit the value - # first result is the FATALS Array : if the conflict appears on a field modified by the user that submit the value - # an empty array is returned by default - def manage_concurrency(params, timestamp) - warnings = [] - fatals = [] + # Timestamp used for versioning can be passed both as : + # ==== + # * @object.active_metadata_timestamp = .... + # * @object.update_attributes :active_metadata_timestamp => ... + # + # Check is skipped if no timestamp exists. A check is made also for parents defined via acts_as_metadata method + # + # Conflicts + # ==== + # * @conflicts[:warnings] contains any conflict that "apply cleanly" or where the submit value has not been modified by the user that is saving + # the data + # + # * @conflicts[:fatals] contains any conflict that "do not apply cleanly" or where the passed value does not match the last history value and was modified + # by the user that is submittig the data + # + def manage_concurrency + return if active_metadata_timestamp.nil? #if no timestamp no way to check concurrency so just skip + self.conflicts = { :warnings => [], :fatals => [] } + # scan params - params.each do |key, val| + self.attributes.each do |key, val| # ensure the query order histories = history_for key.to_sym, "created_at DESC" latest_history = histories.first + timestamp = self.active_metadata_timestamp # if form timestamp is subsequent the history last change go on # if history does not exists yet go on next if latest_history.nil? || timestamp > latest_history.created_at #if the timestamp is previous of the last history change if timestamp < latest_history.created_at - #remove the key from the update process - params.delete key + begin + self[key.to_sym] = self.changes[key][0] + rescue + end # there is a conflict so ensure the actual value if any change exists + # We have a conflict. # Check if the actual submission has been modified histories.each do |h| # Looking for the value that was loaded by the user. First history with a ts that is younger than the form ts next if timestamp > h.created_at @@ -98,20 +129,19 @@ if self.column_for_attribute(key).type == :boolean b_val = to_bool(h.value) end if [h.value, b_val].include? val - warnings << {key => [val, h]} + self.conflicts[:warnings] << {key.to_sym => [val, h]} else - fatals << {key => [val, h]} + self.conflicts[:fatals] << {key.to_sym => [val, h]} end end end end - return warnings, fatals end end # InstanceMethods end