Ruby win32ole - Can't replace text in word document - ruby

I'm trying to replace text in a document like so:
require 'win32ole'
def replace_doc(doc, find, repl)
begin
word = WIN32OLE.new('Word.Application')
word.Visible = true
doc = word.Documents.Open(doc)
word.Selection.HomeKey(unit=6)
finder = word.Selection.Find
finder.Text = "[#{find}]"
while word.Selection.Find.Execute
word.Selection.TypeText(text=repl)
end
doc.SaveAs(doc)
doc.Close
rescue Exception => e
puts e.message
puts "Unable to edit file."
end
end
def main()
puts "File: "
doc = gets.chomp()
puts "Find: "
find = gets.chomp()
puts "Replace with: "
repl = gets.chomp()
replace_doc(doc, find, repl)
end
main()
I'm running Ruby 2.0 on Windows XP. The WINWORD.exe process starts (I see it in task manager), and no exception is raised. However, when I go to the document, none of the text I expect to be replaced -- is. What is going on? I've copied the code (except for a few things) from here.

It's hard to say without the actual word document and input data you're using, but I suspect that the square brackets in finder.Text are your issue. As your program is now, entering foo for the find text would search for [foo] in your word document, not plain foo. Note that in the post you linked. There are actual square brackets in the example word document (it contains [date] etc.)

Related

Search for a .yml file in a folder

I have developed a system where a specific user has access to search for a specific file. The problem is that this file is in a folder so I am having a hard time coding it.
My method must be able to find my keyword input in the file name.
For example, If I search for Bob, I will get all the files where Bob are included in the filename
def search_user(search)
keyword = File.readlines('test3.yml')
matches = keyword.select { |username| username[/#{search}/] }
if File.read("test3.yml").include?(search)
puts "_____________________________________________"
puts ("Search results for student: " + search + ":") #
puts
puts matches
puts "_____________________________________________"
else #If not it will give the user feedback that its not there
puts "_____________________________________________"
puts
puts ("Sorry, we couldnt find #{search} in the system.")
puts "_____________________________________________"
end
end

Ruby: gets.chomp with default value

Is there some simple way how to ask for a user input in Ruby WHILE providing a default value?
Consider this code in bash:
function ask_q {
local PROMPT="$1"
local DEF_V="$2"
read -e -p "$PROMPT" -i "$DEF_V" REPLY
echo $REPLY
}
TEST=$(ask_q "Are you hungry?" "Yes")
echo "Answer was \"$TEST\"."
Can you achieve similar behaviour with Ruby's gets.chomp?
function ask_q(prompt, default="")
puts prompt
reply = gets.chomp() # ???
return reply
def
reply = ask_q("Are you hungry?", "Yes")
I understand I can sort replicate the functionality in Ruby this way ...
def ask_q(prompt, default="")
default_msg = (default.to_s.empty?) ? "" : "[default: \"#{default}\"]"
puts "${prompt} ${default}"
reply = gets.chomp()
reply = (default.to_s.empty?) ? default : reply
return reply
end
... but it does not seem very pretty. I also need to show the default value manually and the user needs to retype it in the prompt line, if he wants to use modified version of it (say yes! instead of yes).
I'm starting with Ruby now, so there may be a lot of syntax mistakes and I also may be missing something obvious ... Also, I googled a lot but surprisingly found no clue.
TL; DR
To make the question clearer, this is what you should see in terminal and what I am able to achieve in bash (and not in Ruby, so far):
### Terminal output of `reply=ask_q("Are you hungry?" "Yes")`
$ Are you hungry?
$ Yes # default editable value
### Terminal output of `reply=ask_q("What do you want to eat?")`
$ What do you want to eat?
$ # blank line waiting for user input, since there is no second parameter
And the actual situation: I am building bootstrap script for my web apps. I need to provide users with existing configuration data, that they can change if needed.
### Terminal output of `reply=ask_q("Define name of database." "CURR_DB_NAME")`
I don't think it's that fancy functionality, that would require switch to GUI app world.
And as I've said before, this is quite easily achievable in bash. Problem is, that other things are pure pain (associative arrays, no return values from functions, passing parameters, ...). I guess I just need to decide what sucks the least in my case ...
You need to do one of two things:
1) Create a gui program.
2) Use curses.
Personally, I think it's a waste of time to spend any time learning curses. Curses has even been removed from the Ruby Standard Library.
A GUI program:
Here is what a gui app looks like using the Tkinter GUI Framework:
def ask_q(prompt, default="")
require 'tk'
root = TkRoot.new
root.title = "Your Info"
#Display the prompt:
TkLabel.new(root) do
text "#{prompt}: "
pack("side" => "left")
end
#Create a textbox that displays the default value:
results_var = TkVariable.new
results_var.value = default
TkEntry.new(root) do
textvariable results_var
pack("side" => "left")
end
user_input = nil
#Create a button for the user to click to send the input to your program:
TkButton.new(root) do
text "OK"
command(Proc.new do
user_input = results_var.value
root.destroy
end)
pack("side" => "right", "padx"=> "50", "pady"=> "10")
end
Tk.mainloop
user_input
end
puts ask_q("What is your name", "Petr Cibulka")
Calling a function in a bash script from ruby:
.../bash_programs/ask_q.sh:
#!/usr/bin/env bash
function ask_q {
local QUESTION="$1"
local DEFAULT_ANSWER="$2"
local PROMPT="$QUESTION"
read -p "$PROMPT $DEFAULT_ANSWER" USERS_ANSWER #I left out the -i stuff, because it doesn't work for my version of bash
echo $USERS_ANSWER
}
ruby_prog.rb:
answer = %x{
source ../bash_programs/ask_q.sh; #When ask_q.sh is not in a directory in your $PATH, this allows the file to be seen.
ask_q 'Are you Hungry?' 'Yes' #Now you can call functions defined inside ask_q.sh
}
p answer.chomp #=> "Maybe"
Using curses:
require 'rbcurse/core/util/app'
def help_text
<<-eos
Enter as much help text
here as you want
eos
end
user_answer = "error"
App.new do #Ctrl+Q to terminate curses, or F10(some terminals don't process function keys)
#form.help_manager.help_text = help_text() #User can hit F1 to get help text (some terminals do not process function keys)
question = "Are You Hungry?"
default_answer = "Yes"
row_position = 1
column_position = 10
text_field = Field.new(#form).
name("textfield1").
label(question).
text(default_answer).
display_length(20).
bgcolor(:white).
color(:black).
row(row_position).
col(column_position)
text_field.cursor_end
text_field.bind_key(13, 'return') do
user_answer = text_field.text
throw :close
end
end
puts user_answer

Toggling true/false: editing a file in ruby

I have some code that tries to change 'false' to 'true' in a ruby file, but it only works once while the script is running.
toggleto = true
text = File.read(filename)
text.gsub!("#{!toggleto}", "#{toggleto}")
File.open(filename, 'w+') {|file| file.write(text); file.close}
As far as I know, as long as I close a file, i should be able to read it it afterwards with what I previously wrote and thus change it back and forth no matter how many times.
Larger Context:
def toggleAutoAction
require "#{#require_path}/options"
filename = "#{#require_path}/options.rb"
writeToggle(filename, !OPTIONS[:auto])
0
end
def writeToggle(filename, toggleto)
text = File.read(filename)
text.gsub!(":auto => #{!toggleto}", ":auto => #{toggleto}")
File.open(filename, 'w+') {|file| file.write(text); file.close}
end
def exitOrMenu
puts "Are you done? (y/n)"
prompt
if gets.chomp == 'n'
whichAction
else
exit
end
end
def whichAction
if action == 5
toggleAutoAction
else
puts "Sorry, that isn't an option...returning"
return 1
end
exitOrMenu
end
The problem lays within this method:
def toggleAutoAction
require "#{#require_path}/options" # here
filename = "#{#require_path}/options.rb"
writeToggle(filename, !OPTIONS[:auto])
0
end
Ruby will not load the options.rb a second time (i.e. with the exact same path name), hence your !OPTIONS[:auto] will only be evaluated once (otherwise you would get a constant-already-defined-warning, provided OPTIONS is defined in options.rb). See Kernel#require docs.
You could, of course, do crazy stuff like
eval File.read("#{#require_path}/options.rb")
but I would not recommend that (performance wise).
As noted above, reading/writing from/to YAML files is less painful ;-)

How do handle control flow better and nil objects in ruby

I have this script that is a part of a bigger one. I have tree diffrent XML files that looks a litle diffrent from each other and I need some type of control structure to handle nil-object and xpath expressions better
The script that I have right now, outputs nil objects:
require 'open-uri'
require 'rexml/document'
include REXML
#urls = Array.new()
#urls << "http://testnavet.skolverket.se/SusaNavExport/EmilObjectExporter?id=186956355&strId=info.uh.kau.KTADY1&EMILVersion=1.1"
#urls << "http://testnavet.skolverket.se/SusaNavExport/EmilObjectExporter?id=184594606&strId=info.uh.gu.GS5&EMILVersion=1.1"
#urls << "http://testnavet.skolverket.se/SusaNavExport/EmilObjectExporter?id=185978100&strId=info.uh.su.ARO720&EMILVersion=1.1"
#urls.each do |url|
doc = REXML::Document.new(open(url).read)
doc.elements.each("/educationInfo/extensionInfo/nya:textualDescription/nya:textualDescriptionPhrase | /ns:educationInfo/ns:extensionInfo/gu:guInfoExtensions/gu:guSubject/gu:descriptions/gu:description | //*[name()='ct:text']"){
|e| m = e.text
m.gsub!(/<.+?>/, "")
puts "Description: " + m
puts ""
}
end
OUTPUT:
Description: bestrykning, kalandrering, tryckning, kemiteknik
Description: Vill du jobba med internationella och globala frågor med...
Description: The study of globalisation is becoming ever more
important for our understanding of today´s world and the School of
Global Studies is a unique environment for research.
Description:
Description:
Description: Kursen behandlar identifieringen och beskrivningen av
sjukliga förändringar i mänskliga skelett. Kursen ger en
ämneshistorisk bakgrund och skelettförändringars förhållanden till
moderna kliniska data diskuteras.
See this post on how to skip over entries when using a block in ruby. The method each() on doc.elements is being called with a block (which is you code containing gsub and puts calls). The "next" keyword will let you stop executing the block for the current element and move on to the next one.
doc.elements.each("/educationInfo/extensionInfo/nya:textualDescription/nya:textualDescriptionPhrase | /ns:educationInfo/ns:extensionInfo/gu:guInfoExtensions/gu:guSubject/gu:descriptions/gu:description | //*[name()='ct:text']"){
|e| m = e.text
m.gsub!(//, "")
next if m.empty?
puts "Description: " + m
puts ""
}
We know that "m" is a string (and not nil) when using the "next" keyword because we just called gsub! on it, which did not throw an error when executing that line. That means the blank Descriptions are caused by empty strings, not nil objects.

How to get long filename from ARGV

I want to make a tool that takes some filenames as parameters, but when I use this code:
ARGV.each do|a|
puts "Argument: #{a}"
end
and I use drag and drop or "send to" in Windows, I get the short filename.
So a file like "C:\Ruby193\bin\test\New Text Document.txt" becomes
C:\Ruby193\bin\test\NEWTEX~1.TXT as the argument.
There is no problem when I run the script from the commandline, with the longfilenames as parameters.
How do i get the long filename when i use drag and drop or send to?
http://www.varioustopics.com/ruby/518646-rre-ruby-cygwin-and-paths.html
require 'find'
require 'fileutils'
require 'Win32API'
def get_long_win32_filename(short_name)
max_path = 1024
long_name = " " * max_path
lfn_size = Win32API.new("kernel32", "GetLongPathName", ['P','P','L'],'L').call(short_name, long_name, max_path)
return (1..max_path).include?(lfn_size) ? long_name[0..lfn_size-1] : short_name
end
ARGV.each do|a|
puts a
puts get_long_win32_filename(a)
end
I don't know if it is possible to change the argument you recieve on a drag and drop, but you could use the Win32 getLongPathName() function, using the Ruby Win32 bindings
--edit--
Including #peter's solution formatted for readability:
require 'find'
require 'fileutils'
require 'Win32API'
def get_long_win32_filename(short_name)
max_path = 1024
long_name = " " * max_path
lfn_size = Win32API.new("kernel32",
"GetLongPathName", ['P','P','L'],'L').call(short_name, long_name, max_path)
return (1..max_path).include?(lfn_size) ? long_name[0..lfn_size-1] : short_name
end
ARGV.each do|a|
puts a
puts get_long_win32_filename(a)
end
I learned a lot trying to figure this out!
However, #peter beat me to it with a much simpler solution.
Here is mine, in case someone finds it useful. file_get_long_name.rb
I got the idea from: an archived vb-world.net article and converted it to ruby.
require 'win32ole'
def get_long_filename(shortpath, fso = WIN32OLE.new("Scripting.FileSystemObject"))
path = case
when fso.FolderExists(shortpath)
fso.GetFolder(fso.GetAbsolutePathName(shortpath))
when fso.FileExists(shortpath)
fso.GetFile(fso.GetAbsolutePathName(shortpath))
else
return nil
end
parts = path.Path.split(/\\/)
working = fso.GetDrive(parts.shift).RootFolder
longpath = working.Path
parts.each do |part|
temppath = fso.BuildPath(longpath, part)
working = fso.GetFolder(longpath)
if fso.FolderExists(temppath)
working.SubFolders.each do |sub|
longpath = fso.BuildPath(longpath, sub.Name) if part== sub.ShortName || part == sub.Name
end
elsif fso.FileExists(temppath)
working.Files.each do |sub|
longpath = fso.BuildPath(longpath, sub.Name) if part== sub.ShortName || part == sub.Name
end
end
end
longpath
end
fso = WIN32OLE.new("Scripting.FileSystemObject")
short = "C:\\DOCUME~1\\jamal\\Desktop\\NEWTEX~1.TXT"
long = get_long_filename(short, fso)
p long
# ==> "C:\\Documents and Settings\\jamal\\Desktop\\New Text Document.txt"
I found the reason my script receaved short filenames, i had done a registry patch to enable the drag and drop on ruby scripts and schortcuts as follows
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\rbfile\ShellEx\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
[HKEY_CLASSES_ROOT\rbwfile\ShellEx\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
[HKEY_CLASSES_ROOT\RubyFile\ShellEx\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
[HKEY_CLASSES_ROOT\RubyWFile\ShellEx\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
But it had to be the following for LONG filenames
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\rbfile\ShellEx\DropHandler]
#="{60254CA5-953B-11CF-8C96-00AA00B8708C}"
[HKEY_CLASSES_ROOT\rbwfile\ShellEx\DropHandler]
#="{60254CA5-953B-11CF-8C96-00AA00B8708C}"
[HKEY_CLASSES_ROOT\RubyFile\ShellEx\DropHandler]
#="{60254CA5-953B-11CF-8C96-00AA00B8708C}"
[HKEY_CLASSES_ROOT\RubyWFile\ShellEx\DropHandler]
#="{60254CA5-953B-11CF-8C96-00AA00B8708C}"

Resources