class CouchrestAttachment

Converts from Tinkit attachment format to closer to the metal couchrest/CouchDB attachment format. The reason this is needed is because CouchDB cannot support custom metadata for attachments. So custom metadata is held in the CouchrestAttachment document. This document will also hold the attachments and its built in metadata (such as content-type and modified times

Attachment structure:
attachments =>{ attachment_1 => { 'data1' => raw attachment data1,
                                  'md1' => combined attachment metadata1 },
                attachment_2 => { 'data2' => raw attachment data2,
                                  'md2' => combined attachment metadata2 }
}

Constants

AttachmentID

changing this will result in existing persisted data being lost (unless the persisted data is updated as well)

CouchDBAttachParams

CouchDB attachment metadata parameters supported by CouchrestAttachment

Public Class Methods

add_attachment_package(doc_id, attach_class, attachments) click to toggle source
# File lib/glue_envs/couchrest/couchrest_attachment_handler.rb, line 131
def self.add_attachment_package(doc_id, attach_class, attachments)
  raise "No class definition provided for attachments" unless attach_class
  raise "No id found for the document" unless doc_id #bufs_node._model_metadata[:_id]
  raise "No attachments provided for attaching" unless attachments
  att_doc_id = self.uniq_att_doc_id(doc_id)
  att_doc = self.get(att_doc_id)
  rtn = if att_doc
    self.update_attachment_package(att_doc, attachments)
  else
    self.create_attachment_package(att_doc_id, attach_class, attachments)
  end
  return rtn
end
create_attachment_package(att_doc_id, attach_class, attachments) click to toggle source

Create an attachment for a particular BUFS document

# File lib/glue_envs/couchrest/couchrest_attachment_handler.rb, line 146
def self.create_attachment_package(att_doc_id, attach_class, attachments)
  raise "No class definition provided for attachments" unless attach_class
  raise "No id found for the document" unless att_doc_id 
  raise "No attachments provided for attaching" unless attachments
  
  sorted_attachments = CouchrestAttachmentHelpers.sort_attachment_data(attachments)
  custom_metadata_doc_params = {'_id' => att_doc_id, 'md_attachments' => sorted_attachments['cust_md_by_name']}
  att_doc = attach_class.new(custom_metadata_doc_params)
  att_doc.save
 
  sorted_attachments['att_md_by_name'].each do |att_name, params|
    esc_att_name = TkEscape.escape(att_name)
    att_doc.put_attachment(esc_att_name, sorted_attachments['data_by_name'][esc_att_name],params)
  end
  
  #returns the updated document from the database
  return self.get(att_doc_id)
end
get_attachments(att_doc) click to toggle source

retrieves document attachments for a particular document

# File lib/glue_envs/couchrest/couchrest_attachment_handler.rb, line 222
def self.get_attachments(att_doc)
  return nil unless att_doc
  custom_md = att_doc['md_attachments']
  esc_couch_md = att_doc['_attachments']
  md_no_att = custom_md && !esc_couch_md
  att_no_md = esc_couch_md && !custom_md
  raise "DB Record corrupted? Attachment metadata exists,"\
        " but no attachments for #{att_doc['_id'].inspect}" if md_no_att 
  raise "DB Record corrupted? Attachments exist, but no metadata"\
        " is associated with it for #{att_doc['_id'].inspect}" if att_no_md
  couch_md = CouchrestAttachmentHelpers.unescape_names_in_attachments(esc_couch_md)
  if custom_md.keys.sort != couch_md.keys.sort
    raise "data integrity error, attachment metadata inconsistency\n"\
           "in memory: #{custom_md.inspect} \n persisted: #{couch_md.inspect}"
  end
  (attachment_data = custom_md.dup).merge(couch_md) {|k,v_custom, v_couch| v_custom.merge(v_couch)}
end
uniq_att_doc_id(doc_id) click to toggle source

create the attachment document id to be used

# File lib/glue_envs/couchrest/couchrest_attachment_handler.rb, line 127
def self.uniq_att_doc_id(doc_id)
  uniq_id = doc_id + AttachmentID if doc_id#bufs_node.class.attachment_base_id
end
update_attachment_package(att_doc, new_attachments) click to toggle source

Update the attachment data for a particular BUFS document

Important Note: Currently existing data is only updated if new data has been modified more recently than the existing data.
# File lib/glue_envs/couchrest/couchrest_attachment_handler.rb, line 173
def self.update_attachment_package(att_doc, new_attachments)
  existing_attachments = att_doc.get_attachments
  most_recent_attachment = {}
  if existing_attachments
    new_attachments.each do |new_att_name, new_data|
    esc_new_att_name = TkEscape.escape(new_att_name)
      working_doc = att_doc.class.get(att_doc['_id'])
      if existing_attachments.keys.include? esc_new_att_name
        #filename already exists as an attachment
        fresh_attachment =self.find_most_recent_attachment(existing_attachments[esc_new_att_name], new_attachments[new_att_name]['md'])
        most_recent_attachment[esc_new_att_name] = fresh_attachment
        
        #re-add the if statement to prevent old from overwriting newer files
        ###if most_recent_attachment[esc_new_att_name] != existing_attachments[esc_new_att_name]
          #update that file and metadata
          sorted_attachments = CouchrestAttachmentHelpers.sort_attachment_data(esc_new_att_name => new_data)
          #update doc
          working_doc['md_attachments'] = working_doc['md_attachments'].merge(sorted_attachments['cust_md_by_name'])
          #update attachments
          working_doc.save
          #Add Couch attachment data
          att_data = sorted_attachments['data_by_name'][esc_new_att_name]
          att_md =  sorted_attachments['att_md_by_name'][esc_new_att_name]
          working_doc.put_attachment(esc_new_att_name, att_data,att_md)
        ###else
          #do anything here?
          #puts "Warning, didn't update the attachment because current attachment is older than present one"
        ###end
      else #filename does not exist in attachment
        #puts "Attachment Name not found in Attachment Document, adding #{esc_new_att_name}"
        sorted_attachments = CouchrestAttachmentHelpers.sort_attachment_data(esc_new_att_name => new_data)
        #update doc
        working_doc['md_attachments'] = working_doc['md_attachments'].merge(sorted_attachments['cust_md_by_name'])
        #update attachments
        working_doc.save
        #Add Couch attachment data
        att_data = sorted_attachments['data_by_name'][esc_new_att_name]
        att_md =  sorted_attachments['att_md_by_name'][esc_new_att_name]
        working_doc.put_attachment(esc_new_att_name, att_data,att_md)
        #working_doc does not have attachment
        
      end
      
    end
  end
  return att_doc.class.get(att_doc['_id'])
end

Private Class Methods

find_most_recent_attachment(attachment_data1, attachment_data2) click to toggle source
# File lib/glue_envs/couchrest/couchrest_attachment_handler.rb, line 260
def self.find_most_recent_attachment(attachment_data1, attachment_data2)
  #"Finding most recent attachment"
  most_recent_attachment_data = nil
  if attachment_data1 && attachment_data2
    if attachment_data1['file_modified'] >= attachment_data2['file_modified']
      most_recent_attachment_data = attachment_data1
    else
      most_recent_attachment_data = attachment_data2
    end
  else
    most_recent_attachment_data = attachment_data1 || attachment_data2
  end
  most_recent_attachment_data
end

Public Instance Methods

get_attachments() click to toggle source

retrieves document attachments for this document

# File lib/glue_envs/couchrest/couchrest_attachment_handler.rb, line 241
def get_attachments
  self.class.get_attachments(self) 
end
remove_attachment(attachment_names) click to toggle source
# File lib/glue_envs/couchrest/couchrest_attachment_handler.rb, line 245
def remove_attachment(attachment_names)
  attachment_names = [attachment_names].flatten
  attachment_names.each do |att_name|
    att_name = TkEscape.escape(att_name)
    self['md_attachments'].delete(att_name)
    self['_attachments'].delete(att_name)
  end
  resp = self.save
  atts = self.class.get_attachments(self)
  raise "Remove Attachment Operation Failed with response: #{resp.inspect}" unless resp == true
  self
end