after opening a file check if the file was modified on close - ruby

I am wondering if I am doing it the correct way.
def checkout
clone = system( "svn export #{file} tmp/" )
open_file = system( "start tmp/#{#file}" )
end
Now, I am able to open the file I want with the default editor but how to record if the file was modified before close.
Should I create a Process and do Process.wait or something?
Thanks for your help

Use File::mtime method for the same.
Returns the modification time for the named file as a Time object.
file_time_before_opening = File.mtime('your file/path')
# do file operation as you like
file_time_after_closing = File.mtime('your file/path')
# now compare file_time_before_opening and file_time_after_closing to know
# if it is modified or not.

If you mean you are using start in Windows, use /wait or /w option to make it wait until the editor termination.
Use IO::read to check file content modification. (before, after the editor execution).
before = IO.read('tmp/#{#file}', {mode: 'rb'})
system("start /wait tmp/#{#file}")
after = IO.read('tmp/#{#file}', {mode: 'rb'})
# Check the file content modification.
if before != after:
# File changed!
If you're editing a huge file, IO::read will consume memroy accordingly. Use File::mtime as Arup Rakshit suggested, if there's such huge file in your repository. (cons: false positive alarm for save without modification)

Related

Open Editor From Command Line and Fetch Input

I'm currently working on a feature for CocoaPods, a Ruby gem. There's an existing command that accepts a number of options. I'd like to add an extra option that enables the user to enter a custom message by opening the default text editor and, when the user saves and quits the editor, the message is fed to the command that was executed.
What I want to replicate is how you can add -m to the git commit command to have you enter a commit message. I have little experience with creating command line tools so any help is much appreciated.
The goal is to execute a specific command command --message, open the editor, have the user enter a custom message, and execute the command with the custom message being one of the arguments stored in argv.
The common workflow is:
the caller application creates a temporary file;
determines the default editor (for Debian-based it would be /usr/bin/editor, for other linuces — the content of shell variable $EDITOR, etc);
runs a shell command in a subshell with Kernel#system (not with backticks!);
waits for it to exit;
determines the exit code, and skips following if it is not 0;
reads the content of temporary file, created in step 1 and removes this file.
In ruby that would be like:
▶ f = Tempfile.new 'cocoapods'
#⇒ #<File:/tmp/am/cocoapods20151120-6901-u2lubx>
-rw------- 1 am am 0 nov 20 15:03 /tmp/am/cocoapods20151120-6901-u2lubx
▶ path = f.path
#⇒ "/tmp/am/cocoapods20151120-6901-u2lubx"
▶ f.puts 'This content is already presented in file'
#⇒ nil
▶ f.close # HERE MUST BE ENSURE BLOCK, BUT FOR THE SAKE OF AN EXAMPLE...
#⇒ nil
▶ system "editor #{path}"
#⇒ Vim: Warning: Output is not to a terminal
If you are testing this in console, just type anything, followed by Esc:wq. In real life there will be normal vim (or what the default editor is) opened.
▶ File.read path
#⇒ "GGGGGGGGGThis content is already presented in file\n"
All together:
#!/usr/bin/env ruby
require 'tempfile'
f = Tempfile.new 'cocoapods'
path = f.path
f.puts 'This content is already presented in file'
f.close # HERE MUST BE ENSURE BLOCK, BUT FOR THE SAKE OF AN EXAMPLE...
system "editor #{path}"
puts File.read path

File not being created in Ruby script

I am trying to open a non existent file and write to it, however when I run the script, no file is being created.
Here is the line of code
File.open("valid_policies.txt", 'a+').write(policy_number.to_s + "\n")
Instead of using .write try this instead:
File.open("valid_policies.txt", 'a+') {|f| f.write(policy_number.to_s + "\n") }
You're using:
File.open("valid_policies.txt", 'a+').write(policy_number.to_s + "\n")
That's a non-block form of open which doesn't automatically close the file. That means the data is most likely not being written to the file but is sitting in the IO buffer waiting to be flushed/synced. You could add a close but that only propagates non-idiomatic code.
Instead you can use:
File.write("valid_policies.txt", policy_number.to_s + "\n")
File.write automatically creates then writes to the file then closes it. It will overwrite existing files though.
If you aren't sure whether the file exists and want to create it if it doesn't, or append to it, then you use File.open with the a mode instead of a+. From the mode documentation:
"a" Write-only, each write call appends data at end of file.
Creates a new file for writing if file does not exist.
Using a+ will work but it unnecessarily opens the file for reading also. Don't do that unless you're sure that's what you have to do.
If I needed to append I'd use:
File.open('valid_policies.txt', 'a') do |fa|
fa.puts policy_number
end
That's idiomatic Ruby. puts will automatically "stringify" policy_number if it has a to_s method, which it should have since you're already calling it, and it'll also automatically add the trailing "\n" if it doesn't exist at the end of the string. Also, using the block form of open will automatically close the file when the block exists, which is smart house-keeping.

Changing information in a CSV file

I'm trying to write a ruby script that will read through a CSV file and prepend information to certain cells (for instance adding a path to a file). I am able to open and mutate the text just fine, but am having issues writing back to the CSV without overriding everything. This is a sample of what I have so far:
CSV.foreach(path) { |row|
text = row[0].to_s
new_text = "test:#{text}"
}
I would like to add something within that block that would then write new_textback to the same reference cell(row) in the file. The only way I have to found to write to a file is
CSV.open(path, "wb") { |row|
row << new_text
}
But I think that is bad practice since you are reopening the file within the file block already. Is there a better way I could do this?
EX: I have a CSV file that looks something like:
file,destination
test.txt,A101
and need it to be:
file,destination
path/test.txt,id:A101
Hope that makes sense. Thanks in advance!
Depending on the size if the file, you might consider loading the contents of the file into a local variable and then manipulating that, overwriting the original file.
lines = CSV.read(path)
File.open(path, "wb") do |file|
lines.each do |line|
text = line[0].to_s
line[0] = "test:#{text}" # Replace this with your editing logic
file.write CSV.generate_line(line)
end
end
Alternately, if the file is big, you could write each modified line to a new file along the way and then replace the old file with the new one at the end.
Given that you don't appear to be doing anything that draws on CSV capabilities, I'd recommend using Ruby's "in-place" option variable $-i.
Some of the stats software I use wants just the data, and can't deal with a header line. Here's a script I wrote a while back to (appear to) strip the first line out of one or more data files specified on the command-line.
#! /usr/bin/env ruby -w
#
# User supplies the name of one or more files to be "stripped"
# on the command-line.
#
# This script ignores the first line of each file.
# Subsequent lines of the file are copied to the new version.
#
# The operation saves each original input file with a suffix of
# ".orig" and then operates in-place on the specified files.
$-i = ".orig" # specify backup suffix
oldfilename = ""
ARGF.each do |line|
if ARGF.filename == oldfilename # If it's an old file
puts line # copy lines through.
else # If it's a new file remember it
oldfilename = ARGF.filename # but don't copy the first line.
end
end
Obviously you'd want to change the puts line pass-through to whatever edit operations you want to perform.
I like this solution because even if you screw it up, you've preserved your original file as its original name with .orig (or whatever suffix you choose) appended.

Reopening closed file: Lua

I have a file called backup.lua, which the program should write to every so often in order to backup its status, in case of a failure.
The problem is that the program writes the backup.lua file completely fine first-time round, but any other times it refuses to write to the file.
I tried removing the file while the program was still open but Windows told me that the file was in use by 'CrysisWarsDedicatedServer.exe', which is the program. I have told the host Lua function to close the backup.lua file, so why isn't it letting me modify the file at will after it has been closed?
I can't find anything on the internet (Google actually tried to correct my search) and the secondary programmer on the project doesn't know either.
So I'm wondering if any of you folks know what we are doing wrong here?
Host function code:
function ServerBackup(todo)
local write, read;
if todo=="write" then
write = true;
else
read = true;
end
if (write) then
local source = io.open(Root().."Mods/Infinity/System/Read/backup.lua", "w");
System.Log(TeamInstantAction:GetTeamScore(2).." for 2, and for 1: "..TeamInstantAction:GetTeamScore(1))
System.LogAlways("[System] Backing up serverdata to file 'backup.lua'");
source:write("--[[ The server is dependent on this file; editing it will lead to serious problems.If there is a problem with this file, please re-write it by accessing the backup system ingame.--]]");
source:write("Backup = {};Backup.Time = '"..os.date("%H:%M").."';Backup.Date = '"..os.date("%d/%m/%Y").."';");
source:write(XFormat("TeamInstantAction:SetTeamScore(2, %d);TeamInstantAction:SetTeamScore(1, %d);TeamInstantAction:UpdateScores();",TeamInstantAction:GetTeamScore(2), TeamInstantAction:GetTeamScore(1) ));
source:close();
for i,player in pairs(g_gameRules.game:GetPlayers() or {}) do
if (IsModerator(player)) then
CMPlayer(player, "[!backup] Completed server backup.");
end
end
end
--local source = io.open(Root().."Mods/Infinity/System/Read/backup.lua", "r"); Can the file be open here and by the Lua scriptloader too?
if (read) then
System.LogAlways("[System] Restoring serverdata from file 'backup.lua'");
--source:close();
Backup = {};
Script.LoadScript(Root().."Mods/Infinity/System/Read/backup.lua");
if not Backup or #Backup < 1 then
System.LogAlways("[System] Error restoring serverdata from file 'backup.lua'");
end
end
end
Thanks all :).
Edit:
Although the file is now written to the disk fine, the system fails to read the dumped file.
So, now the problem is that the "LoadScript" function isn't doing what you expect:
Because I'm psychic, i have divined that you're writing a Crysis plugin, and are attempting to use it's LoadScript API call.
(Please don't assume everyone here would guess this, or be bothered to look for it. It's vital information that must form part of your questions)
The script you're writing attempts to set Backup - but your script, as written - does not separate lines with newline characters. As the first line is a comment, the entire script will be ignored.
Basicallty the script you've written looks like this, which is all treated as a comment.
--[[ comment ]]--Backup="Hello!"
You need to write a "\n" after the comment (and, I'd recommend in other places too) to make it like this. In fact, you don't really need block comments at all.
-- comment
Backup="Hello!"

How to create a file in Ruby

I'm trying to create a new file and things don't seem to be working as I expect them too. Here's what I've tried:
File.new "out.txt"
File.open "out.txt"
File.new "out.txt","w"
File.open "out.txt","w"
According to everything I've read online all of those should work but every single one of them gives me this:
ERRNO::ENOENT: No such file or directory - out.txt
This happens from IRB as well as a Ruby script. What am I missing?
Use:
File.open("out.txt", [your-option-string]) {|f| f.write("write your stuff here") }
where your options are:
r - Read only. The file must exist.
w - Create an empty file for writing.
a - Append to a file.The file is created if it does not exist.
r+ - Open a file for update both reading and writing. The file must exist.
w+ - Create an empty file for both reading and writing.
a+ - Open a file for reading and appending. The file is created if it does not exist.
In your case, 'w' is preferable.
OR you could have:
out_file = File.new("out.txt", "w")
#...
out_file.puts("write your stuff here")
#...
out_file.close
Try
File.open("out.txt", "w") do |f|
f.write(data_you_want_to_write)
end
without using the
File.new "out.txt"
Try using "w+" as the write mode instead of just "w":
File.open("out.txt", "w+") { |file| file.write("boo!") }
OK, now I feel stupid. The first two definitely do not work but the second two do. Not sure how I convinced my self that I had tried them. Sorry for wasting everyone's time.
In case this helps anyone else, this can occur when you are trying to make a new file in a directory that does not exist.
If the objective is just to create a file, the most direct way I see is:
FileUtils.touch "foobar.txt"
The directory doesn't exist. Make sure it exists as open won't create those dirs for you.
I ran into this myself a while back.
File.new and File.open default to read mode ('r') as a safety mechanism, to avoid possibly overwriting a file. We have to explicitly tell Ruby to use write mode ('w' is the most common way) if we're going to output to the file.
If the text to be output is a string, rather than write:
File.open('foo.txt', 'w') { |fo| fo.puts "bar" }
or worse:
fo = File.open('foo.txt', 'w')
fo.puts "bar"
fo.close
Use the more succinct write:
File.write('foo.txt', 'bar')
write has modes allowed so we can use 'w', 'a', 'r+' if necessary.
open with a block is useful if you have to compute the output in an iterative loop and want to leave the file open as you do so. write is useful if you are going to output the content in one blast then close the file.
See the documentation for more information.
data = 'data you want inside the file'.
You can use File.write('name of file here', data)
You can also use constants instead of strings to specify the mode you want. The benefit is if you make a typo in a constant name, your program will raise an runtime exception.
The constants are File::RDONLY or File::WRONLY or File::CREAT. You can also combine them if you like.
Full description of file open modes on ruby-doc.org

Resources