Ruby Tempfile - Modify file name? - ruby

I'm using Tempfile to store a generated PDF before uploading to a new destination.
pdf_file = WickedPdf.new.pdf_from_string(msgbody)
tempfile = Tempfile.new(['Bob', '.pdf'], Rails.root.join('public','pdf-test'))
tempfile.binmode
tempfile.write pdf_file
tempfile.close
While this works fine, the resulting file names, eg- bob20140331-19260-1g6rzr1.pdf are not user friendly.
I understand that Tempfile creates a unique name and why, but I ultimately need to change the name to make it more intuitive/easier to digest for my users.
Is there a recommended way to do so? Even if its to simply remove the middle (19260)? Thanks for your time and assistance.

A Tempfile is used to create a temporary file with a unique file name, which will be cleaned up by the garbage collector or when the ruby interpreter exits.
Tempfiles behave like File objects, but I am not sure if you can rename files and if you can, if the automatic cleanup described above will still work. Additionally you might break the constraint of unique file names if you change the temporary file name manually.
I suggest creating an ordinary file and specify the entire name by yourself (the succ method can be helpful to prevent name clashes).
Another solution might be setting the file name during or after the upload process, you mentioned.

Note sure if there is one with Tempfile, but can you not rename the file after creation time via FileUtils module? That way you could achieve that the file that was created still has a valid and user-friendly name.

Related

Removing and recreating file does not change its creation timestamp on NTFS [duplicate]

Trying this I have found a strange problem:
Delete an old file.
Create a new file and name it the same as the old file.
Then the "created time" property of the new file is set to the "created time" of the old file. Why does that happen? And how?
It's due to file system tunnelling, as explained by Raymond Chen:
"Why does tunneling exist at all?
When you use a program to edit an existing file, then save it, you expect the original creation timestamp to be preserved, since you're editing a file, not creating a new one. But internally, many programs save a file by performing a combination of save, delete, and rename operations (such as the ones listed in the linked article), and without tunneling, the creation time of the file would seem to change even though from the end user's point of view, no file got created.
As another example of the importance of tunneling, consider that file "File with long name.txt", whose short name is say "FILEWI~1.TXT". You load this file into a program that is not long-filename-aware and save it. It deletes the old "FILEWI~1.TXT" and creates a new one with the same name. Without tunnelling, the associated long name of the file would be lost. Instead of a friendly long name, the file name got corrupted into this thing with squiggly marks. Not good."
The NT file system does not delete a file when you think it does. When you delete a file on an NT file system the operating system simply marks the disk space that file occupied as available. The file will not be truly deleted until another file is written to that location on disk.
As to why Windows would think it was the same exact file I believe this is due to the fact that the file was empty when you deleted it so creating a new file in the same location with the same name and the same (empty) contents makes Windows think it is in fact the same file. I would consider this to be a bug.
As a side note, the fact that Windows handles file deletes in this way is the very reason that you are able to use file-recovery utilities to recover deleted files.
You can update creation time with this command manually:
powershell (ls YourFile.txt).CreationTime = Get-Date
... and the same in a batch file:
call powershell "(ls YourFile.txt).CreationTime = Get-Date"

Ruby Tempfile vs File

I want to know the difference between Tempfile and File.
I found that :
require 'open-uri'
open('c:/boot.ini'){|file|
puts file.class #File
}
open('http://coderlee.cnblogs.com'){|file|
puts file.class #Tempfile
}
and when I save the stream to a remote storage server,the Tempfile will cause an error,It seems that the reason is the encoding is not ASCII-8BIT why?
In the first case, you are loading a file from your file system. This create a File object, using the file name (it has one).
In the second case, you are opening a stream toward a remote file. There is no associated file on your file system, yet you need one if you want to make any operation on it. Thus, Ruby creates a Tempfile for you with a unique filename that you don't even need to know (as the resource does not have a name itself). It then behave exactly like a File object.
The encoding of the document you retrieved is controlled by the server. If you want to retrieve the document in a different encoding, you need to change the encoding on the server.

Ruby - Delete the last character in a file?

Seems like it must be easy, but I just can't figure it out. How do you delete the very last character of a file using Ruby IO?
I took a look at the answer for deleting the last line of a file with Ruby but didn't fully understand it, and there must be a simpler way.
Any help?
There is File.truncate:
truncate(file_name, integer) → 0
Truncates the file file_name to be at most integer bytes long. Not available on all platforms.
So you can say things like:
File.truncate(file_name, File.size(file_name) - 1)
That should truncate the file with a single system call to adjust the file's size in the file system without copying anything.
Note that not available on all platforms caveat though. File.truncate should be available on anything unixy (such as Linux or OSX), I can't say anything useful about Windows support.
I assume you are referring to a text file. The usual way of changing such is to read it, make the changes, then write a new file:
text = File.read(in_fname)
File.write(out_fname, text[0..-2])
Insert the name of the file you are reading from for in_fname and the name of the file you are writing to for 'out_fname'. They can be the same file, but if that's the intent it's safer to write to a temporary file, copy the temporary file to the original file then delete the temporary file. That way, if something goes wrong before the operations are completed, you will probably still have either the original or temporary file. text[0..-2] is a string comprised of all characters read except for the last one. You could alternatively do this:
File.write(out_fname, File.read(in_fname, File.stat(in_fname).size-1))

Ruby - How to prevent wiping your hard drive when using delete file and directory commands in your code

I'm writing some code that at run time may create or delete directories within the project path. I haven't really used ruby for file processing so i'm really uneasy about having code that, with a few mistypes weeks down the line, could result in wiping other directories outside of my project path.
Is there anyway to make it impossible for the program to delete files outside of its own path regardless of whats typed in destructive calls?
Pathname is a wrapper class for almost any file operations.
require "pathname"
path= Pathname.new("/home/johannes")
path.directory? # => true
path.children # => [#<Pathname:.bash_history>, #<Pathname:Documents>, #<Pathname:Desktop>]
path.children.each do |p|
p.delete if p.file?
end
Pathname#children does not contain . or .. so you don't accidently walk up the tree instead of down. If you still don't trust in the code, you can even check if on path is contained in another
Pathname.new("test") <=> Pathname.new("test/123") # => -1
You might want to create a wrapper method around your favourite delete method (or, perhaps, around whole class, because not only deleting files is potentially destructive file operation), which would expand all the submitted paths and check whether they begin with your "sandbox" path). You can also try to redefine delete method, if you are willing to cripple it through whole application.
And maybe the cleanest solution of them all would be to create a new user on your system and run your program as him.
On a POSIX system, you can use Dir.chroot to change the root that your application sees. Then ALL actions, not just delete ones, will be limited to the project directory. This does mean that external commands will be unavailable unless you make them part of your project directory as well.
This is the standard 'sandboxing' method used in Unix based systems. It can be difficult to setup (eliminating all external dependancies is sometimes hard), but affords significant protection when configured properly.
You could generate an Array of filenames in your project directory using
my_files = Dir["/bla/bla/your/directory/**/*"]
and then simply check if the filename passed to your "delete" function exist in your my_files array.
I'm sure there is a more elegant solution, but this could work ^_^
You could use File.expand_path and File.dirname on the input, and check that against __FILE__. So something like this might work:
File.delete(path) if File.dirname(File.expand_path(path)).include? File.dirname(File.expand_path(__FILE__))
I've got automated tests that routinely create and wipe out directories. I've taken two approaches:
Use /tmp as much as possible. The 'tmpdir' standard library module will create temporary directories which will be destroyed when your program exits. Or,
When the code creates a directory that it will later be deleting, it drops a marker file into the directory. When it comes time to delete the directory, if the marker file is not found, the code refuses to delete the directory. A marker file might be called ".ok_to_delete", for example.

Why Windows sets new created file's "created time" property to old time?

Trying this I have found a strange problem:
Delete an old file.
Create a new file and name it the same as the old file.
Then the "created time" property of the new file is set to the "created time" of the old file. Why does that happen? And how?
It's due to file system tunnelling, as explained by Raymond Chen:
"Why does tunneling exist at all?
When you use a program to edit an existing file, then save it, you expect the original creation timestamp to be preserved, since you're editing a file, not creating a new one. But internally, many programs save a file by performing a combination of save, delete, and rename operations (such as the ones listed in the linked article), and without tunneling, the creation time of the file would seem to change even though from the end user's point of view, no file got created.
As another example of the importance of tunneling, consider that file "File with long name.txt", whose short name is say "FILEWI~1.TXT". You load this file into a program that is not long-filename-aware and save it. It deletes the old "FILEWI~1.TXT" and creates a new one with the same name. Without tunnelling, the associated long name of the file would be lost. Instead of a friendly long name, the file name got corrupted into this thing with squiggly marks. Not good."
The NT file system does not delete a file when you think it does. When you delete a file on an NT file system the operating system simply marks the disk space that file occupied as available. The file will not be truly deleted until another file is written to that location on disk.
As to why Windows would think it was the same exact file I believe this is due to the fact that the file was empty when you deleted it so creating a new file in the same location with the same name and the same (empty) contents makes Windows think it is in fact the same file. I would consider this to be a bug.
As a side note, the fact that Windows handles file deletes in this way is the very reason that you are able to use file-recovery utilities to recover deleted files.
You can update creation time with this command manually:
powershell (ls YourFile.txt).CreationTime = Get-Date
... and the same in a batch file:
call powershell "(ls YourFile.txt).CreationTime = Get-Date"

Resources