Ruby tk -- how do you get it working?

Tim F. wrote:

One small point I meant to bring up earlier - when I’ve written scripts
like this in the past, I always have the script move the files to a
temporary location rather than deleting them directly. That way I can
inspect everything and delete them by hand if everything checks out
ok…

Maybe that’s overly paranoid but having a script delete something
important by mistake really sucks… :slight_smile:

Delete 1500 files by hand?

7stud – wrote:

Tim F. wrote:

One small point I meant to bring up earlier - when I’ve written scripts
like this in the past, I always have the script move the files to a
temporary location rather than deleting them directly. That way I can
inspect everything and delete them by hand if everything checks out
ok…

Maybe that’s overly paranoid but having a script delete something
important by mistake really sucks… :slight_smile:

Delete 1500 files by hand?

Ah. I think I get what you mean now. Instead of deleting the files,
you would have the program collect all the files in a new directory.
Then after running the program if the original ruby install works
correctly, you know you can safely run another program to delete all the
files in the new directory.

One problem I see with that is: if your original ruby install failed to
work correctly, you won’t have any idea how to put everything back
together again. In order to know how to put all the files back in their
previous locations, you would have to do something like create a file in
the new directory which lists the original path of the each file and the
new path. Then you could create a program that reads that file and
moves all the files back to their previous directories. Or is there an
easier way?

7stud – wrote:

Delete 1500 files by hand?

Ah. I think I get what you mean now. Instead of deleting the files,
you would have the program collect all the files in a new directory.
Then after running the program if the original ruby install works
correctly, you know you can safely run another program to delete all the
files in the new directory.

Exactly … stage the deletes somewhere like ${HOME}/temp_delete and, if
everything looks ok do a simple rm -rf …

One problem I see with that is: if your original ruby install failed to
work correctly, you won’t have any idea how to put everything back
together again. In order to know how to put all the files back in their
previous locations, you would have to do something like create a file in
the new directory which lists the original path of the each file and the
new path. Then you could create a program that reads that file and
moves all the files back to their previous directories. Or is there an
easier way?

There is an easier way :slight_smile: Preserve the path underneath the temp root…
something like:

def delete_to_stage(file)
destdir = File.join(TEMPROOT, File.dirname(file))
FileUtils::mkdir_p(destdir)
FileUtils::mv(file, destdir)
end

Then you know exactly where things go back if need be…

Cheers,
Tim

In case anyone else would like to use it, the following is what I used
to uninstall all the files installed by the mac one click installer.
Look how easy it was!

The program produces output while running that identifies each file name
as either a dir or a file. If the file name is a dir, it is skipped. If
the file name is a file, it is deleted. For files that are deleted, the
dir containing the file is examined to determine if it is now empty. If
the dir is empty, it is deleted. Here is some sample output:

/users/me/2testing/dir2 is a dir
—Skipping to next file name.
/users/me/2testing/dir2/aaa.txt is a file
—Deleted file: /users/me/2testing/dir2/aaa.txt
examining dir: /users/me/2testing/dir2
—Dir isn’t empty.
/users/me/2testing/dir2/test1.txt is a file
—Deleted file: /users/me/2testing/dir2/test1.txt
examining dir: /users/me/2testing/dir2
—Dir isn’t empty.
/users/me/2testing/dir2/sub_dir is a dir
—Skipping to next file name.
/users/me/2testing/dir2/sub_dir/bbb.txt is a file
—Deleted file: /users/me/2testing/dir2/sub_dir/bbb.txt
examining dir: /users/me/2testing/dir2/sub_dir
—Dir isn’t empty.
/users/me/2testing/dir2/sub_dir/test2.txt is a file
—Deleted file: /users/me/2testing/dir2/sub_dir/test2.txt
examining dir: /users/me/2testing/dir2/sub_dir
—Deleted dir: /users/me/2testing/dir2/sub_dir
examining dir: /users/me/2testing/dir2
—Deleted dir: /users/me/2testing/dir2

The program also writes every file and directory that it deletes to a
file named:

deleted_files_Mac_One_Click_Installer.txt

so that there is a permanent record of what the program did. The output
to the file looks like this:

Deleted Directories:
/users/me/2testing/dir2/sub_dir
/users/me/2testing/dir2

Deleted Files:
/users/me/2testing/dir2/aaa.txt
/users/me/2testing/dir2/test1.txt
/users/me/2testing/dir2/sub_dir/bbb.txt
/users/me/2testing/dir2/sub_dir/test2.txt

Here is the program (I also wrote some comments after the code):

=begin
If you go here:

/Library/Receipts/

and click on:

Ruby O.-Click Installer for OSX Tiger.pkg

the installer will launch. If you then click on File>Show Files
in the Apple menubar, you will see a list of the files installed
by the installer. To copy all the file names, click anywhere in
the window and hit A and C. You can then paste the
file names into a .txt file by hitting P.

You can also get that list of files here:

/Library/Receipts/Ruby O.-Click Installer for OSX
Tiger.pkg/Contents/Resources$

with this command:

/Library…/Resources$ lsbom -s Ruby\ One-Click\ Installer\ for\ OSX\
Tiger.bom
=end

def get_master_file_and_path_to_remove

puts
puts <<STR
To delete the files under the directory /usr/local, you will most
likely need ‘root permissions’. Therefore, you need to run this
program using the command:

$ sudo ruby this_program_name.rb
password:

If you didn’t run this program using that command, then exit
the program now. Would you like to exit now(‘y’ or ‘n’):
STR

answer = gets.strip
if [‘y’, ‘Y’, ‘yes’].include?(answer)
puts
puts “Program terminated.”
exit(1)
end

puts
puts <<STR
Enter the path to a file that contains the file names you
want to delete(if the path has spaces in it, escape the
spaces with a ‘\’). You will be given the opportunity
to exit the program before any actual deleting takes place:
STR

#File containing all the file names to delete:
delete_list = gets.strip

puts
puts <<STR
Enter the path that should be removed from the front of
the filenames(or hit return for none):
STR

remove_path = gets.strip
if remove_path.length == 0
remove_path = nil
end

#Show examples of new file names:
samples = []
count = 0
fname = nil
IO.foreach(delete_list) do |line|
fname = extract_fname(line, remove_path) #extract_fname() defined
below

if fname
  samples << fname
  count += 1
  break if count > 5
end

end

puts

#Confirm whether these are the file names that should be deleted:
puts <<STR
Do the following look like the names of the files you want to
delete(y or n)?\n
STR
puts samples

answer = gets.strip
puts
if ![‘y’, ‘Y’, ‘yes’].include?(answer)
puts
puts “Program terminated.”
exit(1)
end

puts <<STR
Are you sure you want to delete all the files? If you answer yes,
then this program will scour your hard drive for all the file names
contained in the file you entered. The program will then delete
those files as well as any directories that become empty as a result
of deleting a file. This program will NOT delete the directory
/usr/local or any higher directories.

The names of all the files and directories that were deleted will
be written to a file called:

‘deleted_files_Mac_One_Click_Installer.txt’.

Enter ‘y’(for yes) if you want to begin deleting the files now.
Otherwise enter, ‘n’(for no) if you want to exit the program:
STR

answer = gets.strip
puts
if ![‘y’, ‘Y’, ‘yes’].include?(answer):
puts
puts “Program Terminated”
exit(1)
end

return delete_list, remove_path
end

def extract_fname(line_from_file, path_to_remove=nil)

#remove any leading and trailing whitespace:
fname = line_from_file.strip

if fname.length == 0 #then blank line in file
return nil

elsif not path_to_remove
return fname

else #then chop the specified path off the start of fname
if fname == path_to_remove #
return nil
end

len = path_to_remove.length
if fname[0, len] == path_to_remove  #then fname starts with the path
  fname = fname[len..-1] #remove leading path from fname
  return fname

else  #fname doesn't start with the specified path
  raise ArgumentError,
       "Cannot remove path:\n\t%s from file name:\n\t%s" %
        [path_to_remove, fname]
end

end
end

def delete_file(file_name)
#If the file name corresponds to a file, delete the file.
#If the file name is a dir, take no action.
#If the file name doesn’t correspond to an existing file or dir,
#raise a RuntimeError.

if File.directory?(file_name)
printf("%s is a dir\n", file_name)
puts “—Skipping to next file name.”
return false
elsif File.file?(file_name)
printf("%s is a file\n", file_name)
File.delete(file_name)
printf("—Deleted file: %s\n", file_name)
return true
else
raise RuntimeError, “No such file or directory - %s” % file_name
end
end

def delete_empty_dirs(curr_dir, stop_dir)
#Check whether the current directory that used to contain
#the deleted file is empty and therefore should be deleted.
#as well. If the current diretory is deleted, then recursively
#check the parent directories to see if they are now
#empty and need deleting.
#–stop_dir is the furthest up the file hierarchy
#that this method will delete empty dirs.

deleted_dirs = []
while curr_dir != stop_dir
printf(“examining dir: %s\n”, curr_dir)

files_in_dir = Dir.entries(curr_dir)
#Empty dirs on a mac contain the dirs ".", ".." and possibly a file
#called ".DS_Store".  A .DS_Store file is created if you ever
#navigated to the dir using Finder. The .DS_Store file contains view
#options, icon position info, etc. Ruby will delete a dir if the dir
#contains only "." and "..", but ruby won't delete a dir if the dir
#contains the file .DS_Store. Hence the following:
empty_dir_on_mac = [".", "..", ".DS_Store"]
extra_files = files_in_dir - empty_dir_on_mac

if extra_files.length == 0  #then dir needs deleting
  if files_in_dir.include?(".DS_Store")
    full_path = File.join(curr_dir, ".DS_Store")
    File.delete(full_path)
  end

  Dir.delete(curr_dir)
  printf("---Deleted dir: %s\n", curr_dir)
  deleted_dirs << curr_dir

else
  printf("---Dir isn't empty.\n")
  break  #no need to check parent dirs
end

#Get parent dir for the next loop to examine:
curr_dir = File.dirname(curr_dir)

end

return deleted_dirs
end

def log_results(files_deleted, dirs_deleted)
File.open(‘deleted_files_Mac_One_Click_Installer.txt’, ‘w’) do |file|
file.puts(“Deleted Directories:”)
file.puts(dirs_deleted)

file.puts

file.puts("Deleted Files:")
file.puts(files_deleted)

end
end

#------MAIN PROGRAM-----------

delete_list, path_to_remove = get_master_file_and_path_to_remove()

deleted_files = []
deleted_dirs = []

IO.foreach(delete_list) do |a_line|

#Extract a file name from the line:
fname = extract_fname(a_line, path_to_remove)
next if !fname

#If fname is the name of a file, delete it:
if delete_file(fname)
deleted_files << fname
else #then fname is a dir
next #A dir doesn’t become empty unless you delete a file contained
#therein, so skip the code below which checks for empty dirs.
end

#Delete any directories that are now empty as the result
#of deleting a file:
top_dir = ‘/users/autie/2testing’ #don’t check directories above this
one
dir_name = File.dirname(fname)
deleted = delete_empty_dirs(dir_name, top_dir)
deleted_dirs.concat(deleted)

end

#Write the names of all the deleted files and dirs
#to a file:
log_results(deleted_files, deleted_dirs)

===============


===============

Comments on testing

For testing, I used this hierarchy:
/
–users
-----me
-------2testing
-----------dir2
---------------aaa.txt
---------------test1.txt
---------------sub_dir
-------------------test2.txt
-------------------bbb.txt

This was my list of files to delete:

./users/me/2testing/dir2
./users/me/2testing/dir2/aaa.txt
./users/me/2testing/dir2/test1.txt

./users/me/2testing/dir2/sub_dir
./users/me/2testing/dir2/sub_dir/bbb.txt
./users/me/2testing/dir2/sub_dir/test2.txt

During testing, I used the directory /users/me/2testing as the top
directory in the code (instead of /usr/local). After running the
program and entering the file name with the files to delete and ‘.’ as
the path to remove, I was left with this hierarchy:

/
–me
----2testing

After getting that to work correctly, I changed the top directory in the
code to /usr/local, and then I ran the program for real. When prompted,
I entered a file name containing all the files that the mac one click
installer installed, and I entered ‘.’ as the path to remove.

After the program finished deleting all the files, the pre-installed
ruby, which is version 1.8.2 for me, worked without me doing anything
else, and ruby tk started working again.

7stud – wrote:

One thing
I discovered: join() doesn’t work in this case. You are trying to join
something like:

/users/me/Test + /users/me/2testing/dir2/test2.txt

and join just puts a ‘/’ between them producing:

/users/me/Test //users/me/2testing/dir2/test2.txt

I’m not seeing that here…

Tim@macbook: ~ => irb

d1 = “/Users/me/Test”
=> “/Users/me/Test”

d2 = “/Users/me/2testing/dir2/test2.txt”
=> “/Users/me/2testing/dir2/test2.txt”

File.join(d1, d2)
=> “/Users/me/Test/Users/me/2testing/dir2/test2.txt”

I am on the latest ruby though (1.8.6-p111) so behavior might have
changed since 1.8.2…

Cheers,
Tim

mmm, that thread is soo long… and doesn’t seem to solve the firs
issue… does it?
anyway…

For me, it started to work when I did the following:

Are you building Ruby from source? If so, try going to the ext/tk
directory and running

ruby extconf.rb

If that seems to work, continue with make and make install.
If it does not seem to be finding the Tcl/Tk libraries, try reading the
README.tcltklib file in that directory.

it gave me an error message saying to recompile with --enable-pthread
option. which I did… solved my ruby tk trouble.

so, what you need to de is :

  • get ruby source
  • ./configure --enable-pthread
  • make
  • make test
  • sudo make install

Tim F. wrote:

def delete_to_stage(file)
destdir = File.join(TEMPROOT, File.dirname(file))
FileUtils::mkdir_p(destdir)
FileUtils::mv(file, destdir)
end

Thanks. I was just playing around with mkdir_p(I was actually using the
alias mkpath). I tested it out on my test directory structure(from
above), moving the files from those directories into another
directory–keeping their paths intact–and it worked nicely. One thing
I discovered: join() doesn’t work in this case. You are trying to join
something like:

/users/me/Test + /users/me/2testing/dir2/test2.txt

and join just puts a ‘/’ between them producing:

/users/me/Test //users/me/2testing/dir2/test2.txt