require 'ftools'
module ScribdFu
module Paperclip
module ClassMethods
# Adds validations to the current model to check that the attachment at
# +attribute+ is scribdable. If +attribute+ is nil, then all attachments
# that have been marked as scribdable are validated.
#
# Note that all calls to has_scribdable_attachment should be made before
# calling validates_attachment_scribdability with a nil parameter;
# otherwise, only those that have been created already will be validated.
def validates_attachment_scribdability(attribute = nil)
attributes = attribute.nil? ? scribd_attributes : [attribute]
attributes.each do |attribute|
validates_presence_of "#{attribute}_scribd_id",
"#{attribute}_scribd_access_key",
"#{attribute}_content_type"
validates_attachment_content_type attribute,
:content_type => ScribdFu::CONTENT_TYPES
end
end
# Adds the given +attribute+ to the list of attributes that are uploaded
# to scribd.
#
# Note that a scribd attribute should not be added if it is not a
# Paperclip attachment attribute.
def add_scribd_attribute(attribute)
write_inheritable_attribute :scribd_attributes, [] if scribd_attributes.nil?
scribd_attributes << attribute
setup_scribd_attribute(attribute)
end
def scribd_attributes
read_inheritable_attribute :scribd_attributes
end
private
# Sets up methods needed for the given +attribute+ to be scribdable.
def setup_scribd_attribute(attribute)
define_method("#{attribute}_scribd_id=") do |id|
write_attribute "#{attribute}_scribd_id", id.to_s.strip
end
define_method("#{attribute}_scribd_access_key=") do |key|
write_attribute "#{attribute}_scribd_access_key", key.to_s.strip
end
end
end
module InstanceMethods
def self.included(base)
base.extend ClassMethods
end
# Checks whether the given attribute is scribdable. This boils down to a
# check to ensure that the contents of the attribute are of a content type
# that scribd can understand.
def scribdable?(attribute)
ScribdFu::CONTENT_TYPES.include?(self["#{attribute}_content_type"])
end
# Destroys all scribd documents for this record. This is called
# +before_destroy+, as set up by ScribdFu::ClassMethods#extended.
def destroy_scribd_documents
self.class.scribd_attributes.each do |attribute|
document = scribd_document_for(self["#{attribute}_scribd_id"])
unless document.nil?
if document.destroy
logger.info "[Scribd_fu] #{Time.now.rfc2822}: Removing Object #{id}##{attribute} successful"
else
logger.info "[Scribd_fu] #{Time.now.rfc2822}: Removing Object #{id}##{attribute} failed!"
end
end
end
end
# Uploads all scribdable attributes to scribd for processing. This is
# called +before_validation+, as set up by
# ScribdFu::ClassMethods#extended.
def upload_to_scribd
self.class.scribd_attributes.each do |attribute|
scribd_id = self["#{attribute}_scribd_id"]
if scribdable?(attribute) and scribd_id.blank?
with_file_path_for(attribute) do |filename|
if resource = scribd_login.upload(:file => filename,
:access => access_level)
self.send("#{attribute}_scribd_id=", resource.doc_id)
self.send("#{attribute}_scribd_access_key=", resource.access_key)
logger.info "[Scribd_fu] #{Time.now.rfc2822}: Object " +
"#{id}##{attribute} successfully uploaded " +
"for conversion to iPaper."
else
logger.info "[Scribd_fu] #{Time.now.rfc2822}: Object " +
"#{id}##{attribute} upload failed!"
false # cancel the save
end
end
end
end
end
# Returns a URL for a thumbnail for the specified +attribute+ attachment.
#
# If Scribd does not provide a thumbnail URL, then Paperclip's thumbnail
# is fallen back on by returning the value of
# attribute.url(:thumb).
#
# Sample use in a view:
# <%= image_tag(@attachment.thumbnail_url, :alt => @attachment.name) %>
def thumbnail_url(attribute)
doc = scribd_document_for(attribute)
(doc && doc.thumbnail_url) or self.send(attribute).url(:thumb)
end
# Returns the actual image data of a thumbnail for the specified
# +attribute+ attachment.
#
# If Scribd does not have a thumbnail for this file, then
# Paperclip's thumbnanil is fallen back on by returning the file from
# attribute.to_file(:thumb).
#
# Sample use in a controller:
# render :inline => @attachment.thumbnail_file,
# :content_type => 'image/jpeg'
def thumbnail_file(attribute)
doc = scribd_document_for(attribute)
if doc && doc.thumbnail_url
open(doc.thumbnail_url).read
else
send(attribute).to_file(:thumb).open { |f| f.read }
end
rescue Errno::ENOENT, NoMethodError # file not found or nil thumb file
nil
end
# Responds true if the conversion is complete for the given +attribute+ --
# note that this gives no indication as to whether the conversion had an
# error or was succesful, just that the conversion completed. See
# conversion_successful? for that information.
#
# Note also that this method still returns false if the model does not
# refer to a valid document. scribd_attributes_valid? should be used to
# determine the validity of the document.
def conversion_complete?(attribute)
doc = scribd_document_for(attribute)
doc && doc.conversion_status != 'PROCESSING'
end
# Responds true if the document for the given +attribute+ has been
# converted successfully. This *will* respond false if the conversion has
# failed.
#
# Note that this method still returns false if the model does not refer to a
# valid document. scribd_attributes_valid? should be used to
# determine the validity of the document.
def conversion_successful?(attribute)
doc = scribd_document_for(attribute)
doc && doc.conversion_status =~ /^DISPLAYABLE|DONE$/
end
# Responds true if there was a conversion error while converting the given
# +attribute+ to iPaper.
#
# Note that this method still returns false if the model does not refer to a
# valid document. scribd_attributes_valid? should be used to
# determine the validity of the document.
def conversion_error?(attribute)
doc = scribd_document_for(attribute)
doc && doc.conversion_status == 'ERROR'
end
# Responds the Scribd::Document associated with the given +attribute+, or
# nil if it does not exist.
def scribd_document_for(attribute)
scribd_documents[attribute] ||= scribd_login.find_document(self["#{attribute}_scribd_id"])
rescue Scribd::ResponseError # at minimum, the document was not found
nil
end
private
def scribd_documents
@scribd_documents ||= HashWithIndifferentAccess.new
end
# Returns the full filename for the given attribute. If the file is
# stored on S3, this is a full S3 URI, while it is a full path to the
# local file if the file is stored locally.
def full_filename_for(attribute)
filename = attachment_for(attribute).path
end
# Yields the correct path to the file for the attachment in
# +attribute+, either the local filename or the S3 URL.
#
# This method creates a temporary file of the correct filename for the
# attachment in +attribute+ if necessary, so as to be able to give
# scribd the right filename. The file is destroyed when the passed block
# ends.
def with_file_path_for(attribute, &block) # :yields: full_file_path
attachment = attachment_for(attribute)
if attachment.respond_to?(:s3)
yield attachment.url
elsif File.exists?(attachment.path)
yield attachment.path
else # file hasn't been saved, use a tempfile
temp_rename = File.join(Dir.tmpdir, attachment.original_filename)
File.copy(attachment.to_file.path, temp_rename)
yield temp_rename
end
ensure
temp_rename && File.unlink(temp_rename) # always delete this
end
end
end
end