Write a script in Ruby to rename all *.htm files to *.html - ruby-on-rails

I need to write a script in Ruby to rename all *.htm files to *.html in a given
directory.
I've been given a script with some pieces missing.
I need to "METHOD" with the appropriate method name and "REGEX" with an appropriate
regular expression to match all the files that end in .htm.
Dir.METHOD("*.htm").each do |html_file|
FileUtils.METHOD html_file, "#{html_file.METHOD(/REGEX/,'.html')}"
end
Does anyone know what I should replace "METHOD" and "REGEX" with?

Dir.glob("*.htm") do |html_file|
FileUtils.mv(html_file, "#{File.basename(html_file, ".htm")}.html")
end

Dir.glob("*.htm").each do |html_file|
FileUtils.mv html_file, "#{html_file.sub(/.htm/,'.html')}"
end

Here's how I did it though it did not use a FileUtils method and I skipped ahead and did string manipulation before it was discussed in the lessons.
Dir.glob("*.htm") {|old_filename| #Save file names w/ .htm to old_
tmp_filename = old_filename.slice(0..-5) #Remove (.htm) the file extension
new_filename = tmp_filename + '.html' #Append the .html extension
puts new_filename #Display renamed file names
}

Related

create file in different directories with ruby

I'm new to ruby. Actually I'm trying to create an empty file "myfile.txt" in each of the following directories:
../../../../../TESTS/Test_A/myTest_A/
../../../../../TESTS/Test_B/myTest_B/
../../../../../TESTS/Test_C/myTest_C/
../../../../../TESTS/Test_D/myTest_D/
As you can see, the name of the Top directory is "TEST" and than after this, every directory have a different name but starts with "Test_", and than each "Test_*" directory contains only one directory and there my file should go in. I'm trying something like this:
require 'pathname'
def create_myFile
pn = Pathname.new('../../../../../TESTS/Test_*/**')
myFile = File.new("#{pn}/myFile.txt", "w+")
end
create_myFile
It doesn't work. Any suggestions?
Pathname does not accept wildcards. Dir#[] does:
Dir['../../../../../TESTS/Test_*/**'].each do |dir|
File.new File.join(dir, 'myFile.txt'), 'w+'
end

Using Tempfile to create a zip file in rails

I want to create a temporary .zip in rails. For creating zip file I am using rubyzip gem.
Currently I am doing this:
zfname = Tempfile.new(['somename','.zip'], Rails.root.to_s + '/tmp/')
Zip::ZipFile.open(zfname.path, Zip::ZipFile::CREATE) do |zipfile|
zipfile.add(file, basepath + file)
end
This generates following error:
Zip::ZipError: Zip end of central directory signature not found
Is it possible to use Tempfile for zip? If yes, what is wrong here?
In my rails app when I needed to send zip files to user, I just stored them in a buffer and used 'send data' method in controller.
I was trying to use 'Tempfile' initially but it had an added task of removing zip files after sending it to user which is a pain.
Is this something you are looking for?
Zip::OutputStream.write_buffer do |stream|
file_paths.each_with_index do |file_path, index|
# rename the pdf
stream.put_next_entry("#{name}-#{index + 1}.pdf")
# add pdf to zip
stream.write IO.read(file_path)
end
end
So it doesn't actually look like you need or want a Tempfile. You really want a random path in the Rails.root/tmp directory. Try something like this:
zfpath = Rails.root.join('tmp', "somename-#{SecureRandom.hex(8)}.zip"
Zip::ZipFile.open(zfpath, Zip::ZipFile::CREATE) do |zipfile|
zipfile.add(file, basepath + file)
end
Update:
While it's far more complex, you can find a discussion of how to do this with a Tempfile here - http://thinkingeek.com/2013/11/15/create-temporary-zip-file-send-response-rails/ .

Sanitising a download URL with params being passed to send_file

I have numerous files in a directory app/assets/downloadables/ and I want authenticated users to be able to download arbitrary files by name in params within that directory or any subdirectories.
How can I sanitise the input to send_file to prevent users from accessing arbitrary files on the server?
At the moment I'm looking at doing something like this, but I'm not confident that it's safe:
DOWNLOADS_ROOT = File.join(Rails.root, "app", "assets", "downloadables")
# e.g. request.path = "/downloads/subdir/file1.pdf"
file = request.path.gsub(/^\/downloads\//,'').gsub('..','').split('/')
# send file /app/assets/downloadables/subdir/file1.pdf
send_file File.join(DOWNLOADS_ROOT, file)
Would this sufficiently protect against app-wide arbitrary file access or are there improvements or a different approach that would be better?
I found an answer to this here: http://makandracards.com/makandra/1083-sanitize-user-generated-filenames-and-only-send-files-inside-a-given-directory
This file needed to be created per the link:
app/controllers/shared/send_file_inside_trait.rb
module ApplicationController::SendFileInsideTrait
as_trait do
private
def send_file_inside(allowed_path, filename, options = {})
path = File.expand_path(File.join(allowed_path, filename))
if path.match Regexp.new('^' + Regexp.escape(allowed_path))
send_file path, options
else
raise 'Disallowed file requested'
end
end
end
end
To be used as follows in controllers:
send_file_inside File.join(Rails.root, 'app', 'assets', 'downloadables'), request.path.gsub(/^\/downloads\//,'').split('/')
The magic happens where the calculated path of allowed_path + file_name is entered into expand_path which will strip out any special directory browsing strings and just return an absolute path. That resolved path is then compared against the allowed_path to ensure that the file being requested resides within the allowed path and/or sub-directories.
Note that this solution requires the Modularity gem v2 https://github.com/makandra/modularity

Carrierwave & Zipfiles: Using an extracted file as a version

Something I'm not getting about the version process...
I have a zip file with a file inside, and I want to upload the file as a "version" of the zip:
Uploader:
version :specificFile do
process :extract_file
end
def extract_file
file = nil
Zip::ZipFile.open(current_path) do |zip_file|
file = zip_file.select{|f| f.name.match(/specificFile/)}.first
zip_file.extract(file, "tmp/" + file.name.gsub("/", "-")){ true }
end
File.open("tmp/" + file.name.gsub("/", "-"))
end
Usage:
=link_to "Specific File", instance.uploader.specificFile.url
Only this just nets me two copies of the zip. Clearly, there's something I'm missing about how version / process works, and I haven't been able to find documentation that actually explains the magic.
So how do I do this, and what am I missing?
This provided the "why", although it took a bit to understand:
How do you create a new file in a CarrierWave process?
To rephrase, when you go to create a version, carrierwave makes a copy of the file and then passes the process the file path. When the process exits, carrierwave will upload the contents of that path - not the file the process returns, which is what I thought was going on.
Working code:
version :specificFile do
process :extract_file
def full_filename (for_file = model.logo.file)
"SpecificFile.ext"
end
end
def extract_plist
file = nil
Zip::ZipFile.open(current_path) do |zip_file|
file = zip_file.select{|f| f.name.match(/specificFile/)}.first
zip_file.extract(file, "tmp/" + file.name.gsub("/", "-")){ true }
end
File.delete(current_path)
FileUtils.cp("tmp/" + file.name.gsub("/", "-"), current_path)
end
So, to make what I want to happen, happen, I:
Tell carrierwave to use a particular filename. I'm using a hardcoded value but you should be able to use whatever you want.
Overwrite the contents of current_path with the contents you want under the version name. In my case, I can't just overwrite the zip while I'm "in it" (I think), so I make a copy of the file I care about and overwrite the zip via File and FileUtils.
PS - It would be nice to avoid the duplication of the zip, but it doesn't look like you can tell carrierwave to skip the duplication.

Ruby Net::FTP, extract filename from ftp.list()

I'm using the following code to try and get all files from ftp using Ruby.
files = ftp.list()
files.each do |file|
ftp.gettextfile(file)
end
The problem is ftp.list returns a whole line of information, not just the filename e.g.
-rw-r--r-- 1 ftp ftp 0 May 31 11:18 brett.txt
How do I extract the filname from this string?
Many thanks
You can use the nlst public method like this
files = ftp.nlst("*.zip")|ftp.nlst("*.txt")|ftp.nlst("*.xml")
#optionally exclude falsely matched files
exclude = /\.old|temp/
#exclude files with 'old' or 'temp' in the name
files = files.reject{ |e| exclude.match e } #remove files matching the exclude regex
files.each do |file|
#do something with each file here
end
If you want to process the output of ftp.list you may find net-ftp-list useful.
However, list appears to be useful, as you can pass in a matching pattern, which it doesn't appear that nlst supports. I just did a quick-and-dirty hack to make list output work:
ftp.list("*.zip") do |zipfile|
zipfile = zipfile.split(/\s+/).last
# ... do something with the file
end

Resources