Zipping a directory recursively & skip container directory - ruby-on-rails

Consider, we have following directory structure:
Location:
/Users/me/Desktop/directory_to_zip/
dir1 dir2 somefile.txt
now, If I use rubyzip to zip the contents of directory_to_zip using the following code:
directory = '/Users/me/Desktop/directory_to_zip/'
zipfile_name = '/Users/me/Desktop/recursive_directory.zip'
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
Dir[File.join(directory, '**', '**')].each do |file|
zipfile.add(file.sub(directory, ''), file)
end
end
This will create a zip file named recursive_directory.zip, which will contain a container directory called directory_to_zip & inside directory_to_zip, will I find my files(dir1 dir2 somefile.txt)
HOw do I skip creation of directory_to_zip inside recursive_directory.zip, so that the zip file just contains the contents of directory_to_zip & not the directory itself.

Okay, I solved this on my own. If you are in same boat, here is here how I did it:
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
Dir.chdir directory
Dir.glob("**/*").reject {|fn| File.directory?(fn) }.each do |file|
puts "Adding #{file}"
zipfile.add(file.sub(directory + '/', ''), file)
end
end
This works exactly I want. the limitation here here is that it doesn't handle empty directories. Hopefully, it would help someone.

Related

No such file or directory # rb_sysopen -

I want to resolve this error.
No such file or directory # rb_sysopen - zipUpTest/1051687701.jpg
The following is the source code.
Zip::File.open_buffer(obj) do |zip|
zip.each do |entry|
ext = File.extname(entry.name)
file_name = File.basename(entry.name)
next if ext.blank? || file_name.count(".") > 1
dir = File.join(dir_name, File.dirname(entry.name))
FileUtils.mkpath(dir.to_s)
zip.extract(entry, entry.name) {true}
file_name.force_encoding("UTF-8")
file_new_name = "#{dir_name}/#{file_name}"
File.rename(entry.name, file_new_name)
#input_dir << file_new_name
end
end
end
Zip::OutputStream.open(zip_file.path) do |zip_data|
#input_dir.each do |file|
zip_data.put_next_entry(file)
zip_data.write(File.read(file.to_s))
end
end
That means there is no file, how to deal with this problem?
You have to ensure the file zipUpTest/1051687701.jpg exists relative to where the process was run, not where the program is saved.
For example, let's say your program is /home/taizo/program. If you're in /home/taizo and run ruby program then it will look for /home/taizo/zipUpTest/1051687701.jpg. If you're in /tmp and run ruby /home/taizo/program the program will look for /tmp/zipUpTest/1051687701.jpg.

delete/remove temp zip file in ruby on rails

i am creating temp zip file in rails tmp directory as follows:-
zipfile_name = Rails.root.join("tmp/abc.zip")
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
Dir[File.join(data, '**',)].each do |file|
zipfile.add(file.sub(dir+"/", ''), file)
end
end
zip_data = File.read(zipfile_name) # sending zip file.
zipfile_name.try(:unlink)` # try to remove or unlink the zip but not working.
temp zipfile_name was abc.zip20140816-8931-1yl3g60 and i want to delete it after sending but it's not found.
File.delete(zipfile_name)
Try file delete command it should work out.

Download all files to a zip within Heroku

I've been knocking my head around with Heroku, while trying to download a zip file with all my receipt files data.
The files are stored on amazon s3 and it all works fine on my development machine..
I thought it had to do with Tempfile, and abandoned that previous solution, since heroku has some strict policies with their filesystem, so i used the tmp folder, but the problem doesn't seem to be there. I already tried to load directly from s3 (using openUri) to the zip file, but it doesn't seem to work either on Heroku.
What might be wrong with my code for Heroku not loading the files to the zip?
Here is my model method :
def zip_receipts(search_hash=nil)
require 'zip/zip'
require 'zip/zipfilesystem'
t=File.open("#{Rails.root}/tmp/#{Digest::MD5.hexdigest(rand(12).to_s)}_#{Process.pid}",'w')
# t = Tempfile.new(Digest::MD5.hexdigest(rand(12).to_s))
# Give the path of the temp file to the zip outputstream, it won't try to open it as an archive.
Zip::ZipOutputStream.open(t.path) do |zos|
logger.debug("search hash Zip: #{search_hash.inspect}")
self.feed(search_hash).each do |receipt|
begin
require 'open-uri'
require 'tempfile'
#configures filename
filen = File.basename(receipt.receipt_file_file_name)
ext= File.extname(filen)
filen_noext = File.basename(receipt.receipt_file_file_name, '.*')
filen=filen_noext+SecureRandom.hex(10)+ext
logger.info("Info Zip - Filename: #{filen}")
# Create a new entry on the zip file
zos.put_next_entry(filen)
# logger.info("Info Zip - Added entry: #{zos.inspect}")
# Add the contents of the file, reading directly from amazon
tfilepath= "#{Rails.root}/tmp/#{File.basename(filen,ext)}_#{Process.pid}"
open(tfilepath,"wb") do |file|
file << open(receipt.authenticated_url(:original),:ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE).read
end
zos.print IO.binread tfilepath
# logger.info("Info Zip - Extracted from amazon: #{zos.inspect}")
rescue Exception => e
logger.info("exception #{e}")
end # closes the exception begin
end #closes receipts cycle
end #closes zip file stream cycle
# The temp file will be deleted some time...
t.close
#returns the path for send file controller to act
t.path
end
My controller:
def download_all
#user = User.find_by_id(params[:user_id])
filepath = #user.zip_receipts
# Send it using the right mime type, with a download window and some nice file name.
send_file(filepath,type: 'application/zip', disposition: 'attachment',filename:"MyReceipts.zip")
end
And I write also my view and routes, so that it might serve anyone else trying to implement a download all feature
routes.rb
resources :users do
post 'download_all'
end
my view
<%= link_to "Download receipts", user_download_all_path(user_id:user.id), method: :post %>
The problem seemed to be with the search hash, and the sql query, and not the code itself. For some reason, the receipts get listed, but aren't downloaded. So it is an all different issue
In the end i have this code for the model
def zip_receipts(search_hash=nil)
require 'zip/zip'
require 'zip/zipfilesystem'
t=File.open("#{Rails.root}/tmp/MyReceipts.zip_#{Process.pid}","w")
# t = Tempfile.new(Digest::MD5.hexdigest(rand(12).to_s))
#"#{Rails.root}/tmp/RecibosOnline#{SecureRandom.hex(10)}.zip"
puts "Zip- Receipts About to enter"
# Give the path of the temp file to the zip outputstream, it won't try to open it as an archive.
Zip::ZipOutputStream.open(t.path) do |zos|
self.feed(search_hash).each do |receipt|
begin
require 'open-uri'
require 'tempfile'
filen = File.basename(receipt.receipt_file_file_name)
ext= File.extname(filen)
filen_noext = File.basename(receipt.receipt_file_file_name, '.*')
filen=filen_noext+SecureRandom.hex(10)+ext
# puts "Info Zip - Filename: #{filen}"
# Create a new entry on the zip file
zos.put_next_entry(filen)
zos.print open(receipt.authenticated_url(:original),:ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE).read
rescue Exception => e
puts "exception #{e}"
end # closes the exception begin
end #closes receipts cycle
end #closes zip file stream cycle
# The temp file will be deleted some time...
t.close
#returns the path for send file controller to act
t.path
end

rails directory

I have rails project and want to load all files from directory. I started it like ""rails c but it says that
No such file or directory
Here is example of my code:
class ParserPlugin < ApplicationController
def self.loadPlugins
a = File.new("1234")
Dir.foreach("/plugins") do |f|
if f != '.' and f != '..'
load("/plugins" + f.to_s)
end
end
end
def self.createplugin
##plugins << self.new
end
end
File structure is
/home/andrii/LV-081/app/controllers/admin/plugins
Can you give me any advice?
You read files from /plugins directory, not from plugins subdirectory of app/controllers/admin. Try this:
Dir.foreach(Rails.root.join("app", "controllers", "admin", "plugins")) do |f|
if f != '.' and f != '..'
load(Rails.root.join("app", "controllers", "admin", "plugins", f.to_s))
end
Your file is probably located here: /app/controllers/ParserPlugin.rb
The directory you're trying to print is here: /app/controllers/admin/plugins
By saying this:
Dir.foreach("/plugins")
Rails thinks there is a /plugins folder in the current working directory (/app/controllers/plugins)
I would try logging something like Dir.getwd (get current working directory) to see where Rails thinks you are, and then access your files from there.
You should try using Rails.root though: Dir.foreach(Rails.root.join('app', 'controllers', 'admin', 'plugins'))

What is the best way to get an empty temporary directory in Ruby on Rails?

What is the best way to get a temporary directory with nothing in it using Ruby on Rails? I need the API to be cross-platform compatible. The stdlib tmpdir won't work.
The Dir object has a method mktmpdir which creates a temporary directory:
require 'tmpdir' # Not needed if you are using rails.
Dir.mktmpdir do |dir|
puts "My new temp dir: #{dir}"
end
The temporary directory will be removed after execution of the block.
The Dir#tmpdir function in the Ruby core (not stdlib that you linked to) should be cross-platform.
To use this function you need to require 'tmpdir'.
A general aprox I'm using now:
def in_tmpdir
path = File.expand_path "#{Dir.tmpdir}/#{Time.now.to_i}#{rand(1000)}/"
FileUtils.mkdir_p path
yield path
ensure
FileUtils.rm_rf( path ) if File.exists?( path )
end
So in your code you can:
in_tmpdir do |tmpdir|
puts "My tmp dir: #{tmpdir}"
# work with files in the dir
end
The temporary dir will be removed automatically when your method will finish.
Ruby has Dir#mktmpdir, so just use that.
require 'tempfile'
Dir.mktmpdir('prefix_unique_to_your_program') do |dir|
### your work here ###
end
See http://www.ruby-doc.org/stdlib-1.9.3/libdoc/tmpdir/rdoc/Dir.html
Or build your own using Tempfile tempfile that is process and thread unique, so just use that to build a quick Tempdir.
require 'tempfile'
Tempfile.open('prefix_unique_to_your_program') do |tmp|
tmp_dir = tmp.path + "_dir"
begin
FileUtils.mkdir_p(tmp_dir)
### your work here ###
ensure
FileUtils.rm_rf(tmp_dir)
end
end
See http://www.ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/Tempfile.html for optional suffix/prefix options.
require 'tmpdir' # not needed if you are loading Rails
tmp_dir = File.join(Dir::tmpdir, "my_app_#{Time.now.to_i}_#{rand(100)}")
Dir.mkdir(tmp_dir)
Works for me.
You can use Dir.mktmpdir.
Using a block will get rid of the temporary directory when it closes.
Dir.mktmpdir do |dir|
File.open("#{dir}/foo", 'w') { |f| f.write('foo') }
end
Or if you need multiple temp directories to exist at the same time, for example
context 'when there are duplicate tasks' do
it 'raises an DuplicateTask error' do
begin
tmp_dir1 = Dir.mktmpdir('foo')
tmp_dir2 = Dir.mktmpdir('bar')
File.new("#{tmp_dir1}/task_name", 'w+')
File.new("#{tmp_dir2}/task_name", 'w+')
expect { subject.filepath('task_name') }.to raise_error(TaskFinder::DuplicateTask)
ensure
FileUtils.remove_entry tmp_dir1
FileUtils.remove_entry tmp_dir2
end
end
end
Dir.mktmpdir creates a temporary directory under Dir.tmpdir (you'll need to require 'tmpdir' to see what that evaluates to).
If you want to use your own path, Dir.mktmpdir takes an optional second argument tmpdir if non-nil value is given. E.g.
Dir.mktmpdir(nil, "/var/tmp") { |dir| "dir is '/var/tmp/d...'" }
I started to tackle this by hijacking Tempfile, see below.
It should clean itself up as Tempfile does, but doesn't always yet..
It's yet to delete files in the tempdir.
Anyway I share this here, might be useful as a starting point.
require 'tempfile'
class Tempdir < Tempfile
require 'tmpdir'
def initialize(basename, tmpdir = Dir::tmpdir)
super
p = self.path
File.delete(p)
Dir.mkdir(p)
end
def unlink # copied from tempfile.rb
# keep this order for thread safeness
begin
Dir.unlink(#tmpname) if File.exist?(#tmpname)
##cleanlist.delete(#tmpname)
#data = #tmpname = nil
ObjectSpace.undefine_finalizer(self)
rescue Errno::EACCES
# may not be able to unlink on Windows; just ignore
end
end
end
This can be used the same way as Tempfile, eg:
Tempdir.new('foo')
All methods on Tempfile , and in turn, File should work.
Just briefly tested it, so no guarantees.
Update: gem install files, then
require "files"
dir = Files do
file "hello.txt", "stuff"
end
See below for more examples.
Here's another solution, inspired by a few other answers. This one is suitable for inclusion in a test (e.g. rspec or spec_helper.rb). It makes a temporary dir based on the name of the including file, stores it in an instance variable so it persists for the duration of the test (but is not shared between tests), and deletes it on exit (or optionally doesn't, if you want to check its contents after the test run).
def temp_dir options = {:remove => true}
#temp_dir ||= begin
require 'tmpdir'
require 'fileutils'
called_from = File.basename caller.first.split(':').first, ".rb"
path = File.join(Dir::tmpdir, "#{called_from}_#{Time.now.to_i}_#{rand(1000)}")
Dir.mkdir(path)
at_exit {FileUtils.rm_rf(path) if File.exists?(path)} if options[:remove]
File.new path
end
end
(You could also use Dir.mktmpdir (which has been around since Ruby 1.8.7) instead of Dir.mkdir but I find the API of that method confusing, not to mention the naming algorithm.)
Usage example (and another useful test method):
def write name, contents = "contents of #{name}"
path = "#{temp_dir}/#{name}"
File.open(path, "w") do |f|
f.write contents
end
File.new path
end
describe "#write" do
before do
#hello = write "hello.txt"
#goodbye = write "goodbye.txt", "farewell"
end
it "uses temp_dir" do
File.dirname(#hello).should == temp_dir
File.dirname(#goodbye).should == temp_dir
end
it "writes a default value" do
File.read(#hello).should == "contents of hello.txt"
end
it "writes a given value" do
# since write returns a File instance, we can call read on it
#goodbye.read.should == "farewell"
end
end
Update: I've used this code to kickstart a gem I'm calling files which intends to make it super-easy to create directories and files for temporary (e.g. unit test) use. See https://github.com/alexch/files and https://rubygems.org/gems/files . For example:
require "files"
files = Files do # creates a temporary directory inside Dir.tmpdir
file "hello.txt" # creates file "hello.txt" containing "contents of hello.txt"
dir "web" do # creates directory "web"
file "snippet.html", # creates file "web/snippet.html"...
"<h1>Fix this!</h1>" # ...containing "<h1>Fix this!</h1>"
dir "img" do # creates directory "web/img"
file File.new("data/hello.png") # containing a copy of hello.png
file "hi.png", File.new("data/hello.png") # and a copy of hello.png named hi.png
end
end
end # returns a string with the path to the directory
Check out the Ruby STemp library: http://ruby-stemp.rubyforge.org/rdoc/
If you do something like this:
dirname = STemp.mkdtemp("#{Dir.tmpdir}/directory-name-template-XXXXXXXX")
dirname will be a string that points to a directory that's guaranteed not to exist previously. You get to define what you want the directory name to start with. The X's get replaced with random characters.
EDIT: someone mentioned this didn't work for them on 1.9, so YMMV.

Resources