Gsub and multiple-replacement

So I have a file that I am replicating per user.
I'm copying the file,

Profile_path='/path/to/file/'
Profile_file='myfile.txt'
copy(Profile_path + Profile_file, Profile_path + username + '.txt')

This obviously goes off without a hitch. Now, I need to find and replace
three specific values within the file itself.

So within the file we have:

Record for {username} with {email} from {country}

I've read through a ton of material and am looking for the best method.
Here are a few caveats:
I don't need a temp file.
I am unconcerned with overwriting the username.txt file with itself.
I have to replace three values within the file.

Basically, I want to do something like:

text = File.read(Profile_path+username+'.txt')
File.open(Profile_path+username+'.txt','w+){|f| f <<
text.gsub(regex_to_find,
text_to_put_in_place)}

But what is the best way to do this for three variables?

···

--
Posted via http://www.ruby-forum.com/.

Here are a few caveats:
I don't need a temp file.

Yes, you do.

I am unconcerned with overwriting the username.txt file with itself.

Ok, you still need a tempfile.

Now, if you say you are unconcerned with losing all the data in your
file, then you don't need a tempfile.

The bottom line is this: you don't replace anything in a file. Instead,
you read in the original file, and write out the altered lines to a new
file. When you are done, you can delete the original file and rename
the new file to the old file name.

···

--
Posted via http://www.ruby-forum.com/\.

How about using ERB and a template?

http://ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html

Load the template, substitute the values in your template with what's
user specific, and write the template out to the path you want?

That way, you don't have to copy, don't need a tempfile, and can scale
up to any amount of users. :wink:

···

On Tue, May 10, 2011 at 11:16 PM, Greg Hacke <greghacke@gmail.com> wrote:

This obviously goes off without a hitch. Now, I need to find and replace
three specific values within the file itself.

So within the file we have:

Record for {username} with {email} from {country}

I've read through a ton of material and am looking for the best method.
Here are a few caveats:
I don't need a temp file.
I am unconcerned with overwriting the username.txt file with itself.
I have to replace three values within the file.

--
Phillip Gawlowski

Though the folk I have met,
(Ah, how soon!) they forget
When I've moved on to some other place,
There may be one or two,
When I've played and passed through,
Who'll remember my song or my face.

require 'stringio'

str =<<'ENDOFSTRING'
Record for WendyQ with girlpower@google.com from Germany
Record for bohH with bogH@yahoo.com from USA
ENDOFSTRING

file = StringIO.new(str)
file.each do |line|
  words = line.split(' ')
  words[2] = '***'
  words[4] = '***'
  words[6] = '***'

  puts words.join(' ')
end

--output:--
Record for *** with *** from ***
Record for *** with *** from ***

···

--
Posted via http://www.ruby-forum.com/.

7stud -- wrote in post #997885:

Yeah, what is so hard about posting a sample of the original file, as
well as the modified file that you want to end up with, and leaving out
all your confusing descriptions?

OK, here's the inbound:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
<plist version="1.0">
<dict>
  <key>PayloadContent</key>
  <array>
    <dict>
      <key>EmailAddress</key>
      <string>{username}</string>
      <key>Host</key>
      <string>exchange.domain.com</string>
      <key>MailNumberOfPastDaysToSync</key>
      <integer>3</integer>
      <key>Password</key>
      <string>{password}</string>
      <key>PayloadDescription</key>
      <string>Configures device for use with Microsoft Exchange
ActiveSync services.</string>
      <key>PayloadDisplayName</key>
      <string>Exchange</string>
      <key>PayloadIdentifier</key>
      <string>com.domain.enterprise.eas</string>
      <key>PayloadOrganization</key>
      <string>TipsHouse</string>
      <key>PayloadType</key>
      <string>com.apple.eas.account</string>
      <key>PayloadUUID</key>
      <string>FE477C13-BF4F-4EA0-BE09-3968EC40C952</string>
      <key>PayloadVersion</key>
      <integer>1</integer>
      <key>SSL</key>
      <true/>
      <key>UserName</key>
      <string>domain\{userroot}</string>
    </dict>
  </array>
  <key>PayloadDescription</key>
  <string>Profile Test</string>
  <key>PayloadDisplayName</key>
  <string>TipsHouse</string>
  <key>PayloadIdentifier</key>
  <string>com.domain.enterprise</string>
  <key>PayloadOrganization</key>
  <string>Domain</string>
  <key>PayloadRemovalDisallowed</key>
  <false/>
  <key>PayloadType</key>
  <string>Configuration</string>
  <key>PayloadUUID</key>
  <string>A6B7D66D-1179-4E85-A005-4DAACD4EDF0F</string>
  <key>PayloadVersion</key>
  <integer>1</integer>
</dict>
</plist>

And the outbound replaces elements in only the array area (in this case,
just the EAS but other options may be included - LDAP, IMAP, etc.)
<dict>
      <key>EmailAddress</key>
      <string>greghacke@domain.com</string>
      <key>Host</key>
      <string>exchange.domain.com</string>
      <key>MailNumberOfPastDaysToSync</key>
      <integer>3</integer>
      <key>Password</key>
      <string>MyPassword</string>
      <key>PayloadDescription</key>
      <string>Configures device for use with Microsoft Exchange
ActiveSync services.</string>
      <key>PayloadDisplayName</key>
      <string>Exchange</string>
      <key>PayloadIdentifier</key>
      <string>com.domain.enterprise.eas</string>
      <key>PayloadOrganization</key>
      <string>TipsHouse</string>
      <key>PayloadType</key>
      <string>com.apple.eas.account</string>
      <key>PayloadUUID</key>
      <string>FE477C13-BF4F-4EA0-BE09-3968EC40C952</string>
      <key>PayloadVersion</key>
      <integer>1</integer>
      <key>SSL</key>
      <true/>
      <key>UserName</key>
      <string>domain\greghacke</string>
    </dict>

Now, the initial file is provided to me may be replaced as needed. I
would like to avoid converting it to ERb as I cannot gaurentee anyone
else will do the work to ensure it stays updated and correct.

I've dug around - really - and found two examples for single element
replacement.

My belief is that there are a plethora of options execute this. I would
like to do something like:
File.copy(master,user_file)
File.open('/tmp/temp_file.txt', 'w+') do | new_file |
     new_file.puts(File.open(user_file, 'r') do | original_file |
     original_file.read.gsub(/\{username\}/, username)
     end)
end
FileUtils.mv("/tmp/replaceable2.txt", user_file)

  text= File.read user_file
  File.open(user_file, 'w+'){|f| f << text.gsub(/\{username\}/,
username)}

···

--
Posted via http://www.ruby-forum.com/\.

You can also read the file line by line, and use a hash of replacements
with gsub:

replacements = {
  '{username}' => 'BOB',
  '{password}' => 'BOB-PASSWORD',
  '{userroot}' => 'BOB-USERROOT'
}

pattern = '{.*?}'

File.open('out.xml') do |outfile|
  IO.foreach('in.xml') do |line|
    outfile.print gsub(/#{pattern}/, replacements)
  end
end

···

--
Posted via http://www.ruby-forum.com/.

And here is a xml parsing solution:

require 'rexml/document'
include REXML

xml =<<ENDOFXML
#your xml here
ENDOFXML

replacements = {
  '{username}' => 'BOB',
  '{password}' => 'BOB-PASSWORD',
  '{userroot}' => 'BOB-USERROOT'
}

pattern = '{.*?}'
doc = REXML::Document.new(xml)

XPath.each(doc, '//string') do |element|
  text = element.text
  element.text = text.gsub(/#{pattern}/, replacements)
end

puts doc.to_s

Note that you are using an unfortunate character in the string:

   domain\{userroot}

A backslash has a special meaning in ruby strings. You need to change
that to a forward slash(or escape it).

···

--
Posted via http://www.ruby-forum.com/.

7stud -- wrote in post #997852:

OK, I think it would be better to be a bit more specific...

User logs into site. Credentials are verified via LDAP.

At login, I am copying a .mobileconfig template file (xml/plist) and
saving it as {username}.profile.mobileconfig
This file contains placeholders for:
  {username} = bob@domain.tld
  {userroot} = bob
  {password} = mypassword

I need to read it, modify it, save it and replace it. At that point, the
user will get the index and be afforded to download this file.

Thoughts?

···

--
Posted via http://www.ruby-forum.com/.

You can use the block form of gsub to do only one pass:

variables = {"username" => "Bob", "password" => "pwd"}

file_contents = File.read("master_file").read
file_contents.gsub!(/\{(.*?)\}/) {|m| variables[$1]}

#and then write the file contents to another file, delete and rename

Jesus.

···

On Wed, May 11, 2011 at 3:45 PM, Greg Hacke <greghacke@gmail.com> wrote:

7stud -- wrote in post #997885:

Yeah, what is so hard about posting a sample of the original file, as
well as the modified file that you want to end up with, and leaving out
all your confusing descriptions?

OK, here's the inbound:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>EmailAddress</key>
<string>{username}</string>
<key>Host</key>
<string>exchange.domain.com</string>
<key>MailNumberOfPastDaysToSync</key>
<integer>3</integer>
<key>Password</key>
<string>{password}</string>
<key>PayloadDescription</key>
<string>Configures device for use with Microsoft Exchange
ActiveSync services.</string>
<key>PayloadDisplayName</key>
<string>Exchange</string>
<key>PayloadIdentifier</key>
<string>com.domain.enterprise.eas</string>
<key>PayloadOrganization</key>
<string>TipsHouse</string>
<key>PayloadType</key>
<string>com.apple.eas.account</string>
<key>PayloadUUID</key>
<string>FE477C13-BF4F-4EA0-BE09-3968EC40C952</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>SSL</key>
<true/>
<key>UserName</key>
<string>domain\{userroot}</string>
</dict>
</array>
<key>PayloadDescription</key>
<string>Profile Test</string>
<key>PayloadDisplayName</key>
<string>TipsHouse</string>
<key>PayloadIdentifier</key>
<string>com.domain.enterprise</string>
<key>PayloadOrganization</key>
<string>Domain</string>
<key>PayloadRemovalDisallowed</key>
<false/>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>A6B7D66D-1179-4E85-A005-4DAACD4EDF0F</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>

And the outbound replaces elements in only the array area (in this case,
just the EAS but other options may be included - LDAP, IMAP, etc.)
<dict>
<key>EmailAddress</key>
<string>greghacke@domain.com</string>
<key>Host</key>
<string>exchange.domain.com</string>
<key>MailNumberOfPastDaysToSync</key>
<integer>3</integer>
<key>Password</key>
<string>MyPassword</string>
<key>PayloadDescription</key>
<string>Configures device for use with Microsoft Exchange
ActiveSync services.</string>
<key>PayloadDisplayName</key>
<string>Exchange</string>
<key>PayloadIdentifier</key>
<string>com.domain.enterprise.eas</string>
<key>PayloadOrganization</key>
<string>TipsHouse</string>
<key>PayloadType</key>
<string>com.apple.eas.account</string>
<key>PayloadUUID</key>
<string>FE477C13-BF4F-4EA0-BE09-3968EC40C952</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>SSL</key>
<true/>
<key>UserName</key>
<string>domain\greghacke</string>
</dict>

Now, the initial file is provided to me may be replaced as needed. I
would like to avoid converting it to ERb as I cannot gaurentee anyone
else will do the work to ensure it stays updated and correct.

I've dug around - really - and found two examples for single element
replacement.

My belief is that there are a plethora of options execute this. I would
like to do something like:
File.copy(master,user_file)
File.open('/tmp/temp_file.txt', 'w+') do | new_file |
new_file.puts(File.open(user_file, 'r') do | original_file |
original_file.read.gsub(/\{username\}/, username)
end)
end
FileUtils.mv("/tmp/replaceable2.txt", user_file)

text= File.read user_file
File.open(user_file, 'w+'){|f| f << text.gsub(/\{username\}/,
username)}

Greg Hacke wrote in post #997877:

7stud -- wrote in post #997852:

OK, I think it would be better to be a bit more specific...

User logs into site. Credentials are verified via LDAP.

At login, I am copying a .mobileconfig template file (xml/plist) and
saving it as {username}.profile.mobileconfig
This file contains placeholders for:
  {username} = bob@domain.tld
  {userroot} = bob
  {password} = mypassword

I need to read it, modify it, save it and replace it. At that point, the
user will get the index and be afforded to download this file.

Thoughts?

Yeah, what is so hard about posting a sample of the original file, as
well as the modified file that you want to end up with, and leaving out
all your confusing analysis?

···

--
Posted via http://www.ruby-forum.com/\.

How big is the file? Is it acceptable to have it all in memory?
Do you have control over the format of the placeholders? If so, I'd go
with Philip's suggestion of using ERB. Just change {username} to <%=
username %> and evaluate the template with a binding that contains a
variable username. For example:

username = "Bob"
password = "pwd"
expanded_file = ERB.new(File.read("template.erb"), nil, "%<>").result(binding)

template.erb:

The user is <%= username %>
The password is <%= password %>

Jesus.

···

On Wed, May 11, 2011 at 2:49 AM, Greg Hacke <greghacke@gmail.com> wrote:

7stud -- wrote in post #997852:

OK, I think it would be better to be a bit more specific...

User logs into site. Credentials are verified via LDAP.

At login, I am copying a .mobileconfig template file (xml/plist) and
saving it as {username}.profile.mobileconfig
This file contains placeholders for:
{username} = bob@domain.tld
{userroot} = bob
{password} = mypassword

I need to read it, modify it, save it and replace it. At that point, the
user will get the index and be afforded to download this file.

Thoughts?