In this exercise I had Posts with many image attachments attached with attachment_fu and rmagick. I also had a bunch of old data that i wanted to import in using a rake task and finally all image attachments needed to be watermarked.
First my assumptions:
- “A Post can have many Image attachments”
- You have the attachment_fu plugin installed already
Here is how it’s done.
Set constants for your image queue path, the directory for completed imports, and the watermark image path.
Add to bottom of /config/configuration/environment.rb:
def logger
RAILS_DEFAULT_LOGGER
end
# CONSTANTS ####################3
IMAGES_QUEUE_PATH = "#{RAILS_ROOT}/images_import/queue/"
IMAGES_DONE_PATH = "#{RAILS_ROOT}/images_import/done/"
WATERMARK_IMAGE = "#{RAILS_ROOT}/config/watermark.png"
Configure your /models/image.rb as follows:
class Image :image,
:storage => :file_system,
#:max_size => 500.kilobytes,
#:resize_to => '320x200>',
:thumbnails => { :thumb => '320x320>' }
validates_as_attachment
after_save :watermark_image
def watermark_image
unless self.thumbnail?
image_to_be_watermarked = Magick::Image.read("#{RAILS_ROOT}/public/#{self.public_filename}").first
watermark = Magick::Image.read(WATERMARK_IMAGE).first
watermarked_image = image_to_be_watermarked.composite(watermark, Magick::SouthEastGravity, Magick::OverCompositeOp)
watermarked_image.write("#{RAILS_ROOT}/public/#{self.public_filename}")
end
end
end
Setup /models/post.rb as follows:
class Post :destroy
def image_attributes=(image_attributes)
image_attributes.each do |attributes|
if attributes[:id].blank?
images.build(attributes)
else
image = images.detect { |pic| pic.id == attributes[:id].to_i }
logger.debug("image #{attributes[:id].to_i}, is_default as: #{attributes[:is_default].to_i}")
image.attributes = attributes
end
end
end
end
Create /models/local_file.rb and populate it with this:
require 'tempfile'
class LocalFile
# The filename, *not* including the path, of the "uploaded" file
attr_reader :original_filename
# The content type of the "uploaded" file
attr_reader :content_type
def initialize(path)
raise "#{path} file does not exist" unless File.exist?(path)
content_type ||= @@image_mime_types[File.extname(path).downcase]
raise "Unrecognized MIME type for #{path}" unless content_type
@content_type = content_type
@original_filename = File.basename(path)
@tempfile = Tempfile.new(@original_filename)
FileUtils.copy_file(path, @tempfile.path)
end
def path #:nodoc:
@tempfile.path
end
alias local_path path
def method_missing(method_name, *args, &block) #:nodoc:
@tempfile.send(method_name, *args, &block)
end
@@image_mime_types ||= { ".gif" => "image/gif", ".ief" => "image/ief", ".jpe" => "image/jpeg", ".jpeg" => "image/jpeg", ".jpg" => "image/jpeg", ".pbm" => "image/x-portable-bitmap", ".pgm" => "image/x-portable-graymap", ".png" => "image/png", ".pnm" => "image/x-portable-anymap", ".ppm" => "image/x-portable-pixmap", ".ras" => "image/cmu-raster", ".rgb" => "image/x-rgb", ".tif" => "image/tiff", ".tiff" => "image/tiff", ".xbm" => "image/x-xbitmap", ".xpm" => "image/x-xpixmap", ".xwd" => "image/x-xwindowdump" }.freeze
end
Now we create a new rake file and task to do our importing.
Create /libs/tasks/import.rake
desc "Imports images/post content from set constant path"
task :do_gallery_import => :environment do
require 'logger'
ignore = ['.','..','body.txt','title.txt']
directory = Dir.new(IMAGES_QUEUE_PATH)
directory.each do |image_dir|
unless ignore.include?(image_dir)
gallery_path = IMAGES_QUEUE_PATH + image_dir
logger.debug("Importing: " + gallery_path)
if !create_new_post(gallery_path).nil?
logger.debug("Import Complete.")
#Move the completed import dir
move_done(image_dir)
end
end
end
end
#Creates a new post by reading the title.txt, body.txt and all images
def create_new_post(directory)
p = Post.new
p.title = File.new(directory + "/title.txt").read.chomp
p.body = File.new(directory + "/body.txt").read.chomp
get_pics_in_directory(directory).each do |image|
p.image_attributes=[{"uploaded_data"=>LocalFile.new(image)}]
end
p.save
end
#Returns a list of all images in a "working" directory
def get_pics_in_directory(directory)
ignore = ['.','..','body.txt','title.txt']
file_list = []
image_directory = Dir.new(directory)
image_directory.each do |pic|
unless ignore.include?(pic)
file_list << directory + "/#{pic.to_s}"
end
end
return file_list
end
def move_done(imported_directory)
gallery_path = IMAGES_QUEUE_PATH + imported_directory
gallery_done_path = IMAGES_DONE_PATH + imported_directory
system "mv #{gallery_path} #{gallery_done_path}"
logger.debug("Moved #{gallery_path} to #{gallery_done_path}")
end
Create the following directories:
/images_import/done/
/images_import/queue/