ruby xml builder send text - ruby

I am trying to run though a program and send string literal value of variable to xml markup using xml.text! var_name. This var_name is having xml tag opening and closing, when i use the below code it covert < and > symbol into escape chars like < > respectively.
is it possible to achive my desired effect using builder
file = File.new("output.xml", "wb")
x = Builder::XmlMarkup.new(:target => file, :indent => 2)
x.instruct!
var_name += "<tag1>value"
var_name += "</tag1>"
xml.text! var_name
actual output is:
<tag1>value</tag1>
desired output:
<tag1>value</tag1>

Is xml a typo? It should be x.text!, right? Did you try to use x << var_name instead of x.text! var_name? This method appends text without escaping markup

Related

How to use variable in Nokogiri contains

I have an XML file like:
<main>
<key>xxx</key>
<key>yyy</b>
<key>zzz</key>
</main>
To check whether the XML has a node with text "yyy", I can use:
xml = Nokogiri::XML(file)
if xml.at('main/key:contains("yyy")')
#code will go here
end
But if the text value is a variable how can I use the same statement?
Note: I have tried below solution and it doesn't work:
var = "yyy"
if xml.at("main/key:contains(#{var})")
"main/key:contains(#{var})"
interpolates to to
"main/key:contains(yyy)"
Note the absence of quotes. You want this:
"main/key:contains(\"#{var}\")"
or more prettily
%Q{main/key:contains("#{var}")}
and some clever escaping would also help if you are not sure about the content of var.

Parse a string with multiple XML-like tags using Ruby

I have a string which looks like the following:
string = " <SET-TOPIC>INITIATE</SET-TOPIC>
<SETPROFILE>
<PROFILE-KEY>predicates_live</PROFILE-KEY>
<PROFILE-VALUE>yes</PROFILE-VALUE>
</SETPROFILE>
<think>
<set><name>first_time_initiate</name>yes</set>
</think>
<SETPROFILE>
<PROFILE-KEY>first_time_initiate</PROFILE-KEY>
<PROFILE-VALUE>YES</PROFILE-VALUE>
</SETPROFILE>"
My objective is to be able to read out each top level that is in caps with the parse. I use a case statement to evaluate what is the top level key, such as <SETPROFILE> but there can be lots of different values, and then run a method that does different things with the contnts of the tag.
What this means is I need to be able to know very easily:
top_level_keys = ['SET-TOPIC', 'SET-PROFILE', 'SET-PROFILE']
when I pass in the key know the full value
parsed[0].value = {:PROFILE-KEY => predicates_live, :PROFILE-VALUE => yes}
parsed[0].key = ['SET-TOPIC']
I currently parse the whole string as follows:
doc = Nokogiri::XML::DocumentFragment.parse(string)
parsed = doc.search('*').each_with_object({}){ |n, h|
h[n.name] = n.text
}
As a result, I only parse and know of the second tag. The values from the first tag do not show up in the parsed variable.
I have control over what the tags are, if that helps.
But I need to be able to parse and know the contents of both tag as a result of the parse because I need to apply a method for each instance of the node.
Note: the string also contains just regular text, both before, in between, and after the XML-like tags.
It depends on what you are going to achieve. The problem is that you are overriding hash keys by new values. The easiest way to collect values is to store them in array:
parsed = doc.search('*').each_with_object({}) do |n, h|
# h[n.name] = n.text :: removed because it overrides values
(h[n.name] ||= []) << n.text
end

Use embedded string as variable name

I have a YAML file that uses the encoding __firstname__ as a placeholder which signifies that an existing method firstname should be used, rather than the literal string in a subsequent process.
I am trying to understand the most ruby way to to do this. Basically, I need to extract the part between the underscores and send it to an object. Here is pseudocode:
variable = '__firstname__'
if variable is prefixed and suffixed with underscores
result = object.send(variable.removeunderscores)
else
result = variable
end
puts result
I was about to write this procedurally like this, but this is the type of thing that I think ruby can less clunkily if only I knew the language better.
What is a clean why to write this?
There's nothing wrong with verbose code if it's clear to read IMO.
I'd do something like this using String#start_with? and String#end_with?:
variable = '__firstname__'
if variable.start_with?("__") && variable.end_with?("__")
result = object.send(variable[2...-2])
else
result = variable
end

Replacing scan by gsub in Ruby: how to allow code in gsub block?

I am parsing a Wiki text from an XML dump, for a string named 'section' which includes templates in double braces, including some arguments, which I want to reorganize.
This has an example named TextTerm:
section="Sample of a text with a first template {{TextTerm|arg1a|arg2a|arg3a...}} and then a second {{TextTerm|arg1b|arg2b|arg3b...}} etc."
I can use scan and a regex to get each template and work on it on a loop using:
section.scan(/\{\{(TextTerm)\|(.*?)\|(.*?)\}\}/i).each { |item| puts "1=" + item[1] # arg1a etc.}
And, I have been able to extract the database of the first argument of the template.
Now I also want to replace the name of the template "NewTextTerm" and reorganize its arguments by placing the second argument in place of the first.
Can I do it in the same loop? For example by changing scan by a gsub(rgexp){ block}:
section.gsub!(/\{\{(TextTerm)\|(.*?)\|(.*?)\}\}/) { |item| '{{NewTextTerm|\2|\1}}'}
I get:
"Sample of a text with a first template {{NewTextTerm|\\2|\\1}} and then a second {{NewTextTerm|\\2|\\1}} etc."
meaning that the arguments of the regexp are not recognized. Even if it worked, I would like to have some place within the gsub block to work on the arguments. For example, I can't have a puts in the gsub block similar to the scan().each block but only a string to be substituted.
Any ideas are welcome.
PS: Some editing: braces and "section= added", code is complete.
When you have the replacement as a string argument, you can use '\1', etc. like this:
string.gsub!(regex, '...\1...\2...')
When you have the replacement as a block, you can use "#$1", etc. like this:
string.gsub!(regex){"...#$1...#$2..."}
You are mixing the uses. Stick to either one.
Yes, changing the quote by a double quote isn't enough, #$1 is the answer. Here is the complete code:
section="Sample of a text with a first template {{TextTerm|arg1a|arg2a|arg3a...}} and then a second {{TextTerm|arg1b|arg2b|arg3b...}} etc."
section.gsub(/\{\{(TextTerm)\|(.*?)\|(.*?)\}\}/) { |item| "{{New#$1|#$3|#$2}}"}
"Sample of a text with a first template {{NewTextTerm|arg2a|arg3a...|arg1a}} and then a second {{NewTextTerm|arg2b|arg3b...|arg1b}} etc."
Thus, it works. Thanks.
But now I have to replace the string, by a "function" returning the changed string:
def stringreturn(arg1,arg2,arg3) strr = "{{New"+arg1 + arg3 +arg2 + "}}"; return strr ; end
and
section.gsub(/\{\{(TextTerm)\|(.*?)\|(.*?)\}\}/) { |item| stringreturn("#$1","|#$2","|#$3") }
will return:
"Sample of a text with a first template {{NewTextTerm|arg2a|arg3a...|arg1a}} and then a second {{NewTextTerm|arg2b|arg3b...|arg1b}} etc."
Thanks to all!
There is probably a better way to manipulate arguments in MediaWiki templates using Ruby.

Replacing part of the content in one file with data from another file

I have an html file myfile.html, which includes a script with a line like this:
var json = '[{"name":"Hydrogen","number":"1","symbol":"H","weight":"1.00794"},{"name":"Helium","number":2,"symbol":"He","weight":4.002602},{"name":"Lithium","number":3,"symbol":"Li","weight":6.941},{"name":"Beryllium","number":4,"symbol":"Be","weight":9.012182},{"name":"Boron","number":5,"symbol":"B","weight":10.811},{"name":"Carbon","number":6,"symbol":"C","weight":12.0107}]';
The string within single quotes that is assigned to variable json will actually vary. I would like to replace this string with the entire contents of another file myjson.json.
I tried with the code here:
Find and replace in a file in Ruby
and here:
search and replace with ruby regex
doing this:
replace = File.read("myjson.json")
changefile = File.read("myfile.html")
changefile.sub( %r{var json = '[^<]+';}, replace )
but its not working. I'm not sure if its the regex I'm doing incorrectly, or if its something more.
UPDATE
After reading the reply below, my first attempt was:
replace = File.read("myjson.json")
changefile = File.read("myfile.html")
changefile.sub!(%r{var json = '.+'}, replace)
puts changefile
This did the find correctly, but removed all of the var json = '' and replaced it with myjson.json - I want to keep var json = and only replace the contents between the two single quotes after. So then I tried:
replace = File.read("myjson.json")
changefile = File.read("myfile.html")
changefile.sub!(%r{var json = '.+'}, "var json = 'replace'")
puts changefile
But that just replaced it with var json = 'replace'
I want to use the original var json = to find the location, but I don't want it to be removed.
So I did something I know is dumb and wrong, but it worked:
replace = File.read("myjson.json")
changefile = File.read("myfile.html")
changefile.sub!(%r{var json = '.+'}, "var json = 'thanksforthehelptinman'")
changefile.sub!(%r{thanksforthehelptinman}, replace)
puts changefile
Thanks for the help!
The regex isn't right because [ and ] are reserved in regex. You need to escape them:
%r{var json = '\[.+\]'}
I can't be more exact because I don't know what's in your JSON file, but that should get you into the ballpark.
Also, unless you assign changefile.sub to something, the substitution will be thrown away. You can do one of these two things:
changefile = changefile.sub(%r{var json = '\[.+\]'}, json)
or mutate changefile:
changefile.sub!(%r{var json = '\[.+\]'}, json)

Resources