How to generate xml_builder ruby code from an XML file - ruby

I've got an xml file. How could I generate xml_builder ruby code out of that file?
Notice - I'm sort of going backwards here (instead of generating xml, I'm generating ruby code).
Pretty formatting isn't a big deal - I can always run it through a formatter later.

It's sort of impossible, not unlike if you asked "how to generate Ruby script that outputs number 3", for the answer could be:
puts 3
or
puts 2+1
or
puts [1,2,3].count
etc.
So, one answer to your question would be:
xml = File.read('your.xml')
puts "puts <<EOF\n#{xml}\nEOF"
Anyway, if you would just want to generate Builder based script which just generates your XML node-for-node, I guess it would be easiest using XSLT. That's a language constructed exactly for such purposes - transforming XMLs.

Here's what I eventually came up with:
#!/usr/bin/env ruby
require "rexml/document"
filename = ARGV[0]
if filename
f = File.read(filename)
else
raise "Couldn't read file: `#{filename}'"
end
doc = REXML::Document.new(f)
def self.output_hash(attributes={})
count = attributes.size
str = ""
index = 0
attributes.each do |key, value|
if index == 0
str << " "
end
str << "#{key.inspect} => "
str << "#{value.inspect}"
if index + 1 < count
str << ", "
end
index += 1
end
str
end
def self.make_xml_builder(doc, str = "")
doc.each do |element|
if element.respond_to?(:name)
str << "xml.#{element.name}"
str << "#{output_hash(element.attributes)}"
if element.length > 0
str << " do \n"
make_xml_builder(element, str)
str << "end\n"
else
str << "\n"
end
elsif element.class == REXML::Text
string = element.to_s
string.gsub!("\n", "")
string.gsub!("\t", "")
if !string.empty?
str << "xml.text!(#{string.inspect})\n"
end
end
end
str
end
puts make_xml_builder(doc)
After generating that, I then formatted it in Emacs.

Related

parsing numbers in a string

I'm trying to get my code to pass this test. If you've ever played magic the gathering, this might look familiar to you.
Test.assert_equals(can_cast("11RB","10B","1R"), true)
Test.assert_equals(can_cast("13BBRR","10BR","2R","B"), true)
But I can't seem to parse the correct numbers out of the elements correctly. Does anyone see a flaw in my code that's keeping me from passing these test?
def can_cast(hand, *spell_cost)
colored_mana_hand = Array.new
colored_mana_cost_aggregate = Array.new
colored_mana_spent = Array.new
colorless_mana_hand_array = []
colorless_mana_hand = 0
colorless_mana_cost_array = []
colorless_mana_cost_aggregate_array = []
colorless_mana_cost_aggregate = 0
hand.split("").each do |i|
if i.to_i != 0 # extracting existing colorless mana from hand
colorless_mana_hand_array << i
else
colored_mana_hand << i
end
end
colorless_mana_hand = colorless_mana_hand_array.join.to_i
spell_cost.each do |i| # extracting existing colorless mana from cost
i.split("").each do |j|
if j.to_i != 0
colorless_mana_cost_array << j
else
colored_mana_cost_aggregate << j
end
end
colorless_mana_cost_aggregate_array << colorless_mana_cost_array.join
colorless_mana_cost_array.clear
end
colorless_mana_cost_aggregate_array.each do |i|
colorless_mana_cost_aggregate += i.to_i
end
colored_mana_cost_aggregate.each do |i| # pay colored mana first
if colored_mana_hand.include?(i)
colored_mana_spent << i
colored_mana_hand.rotate(colored_mana_hand.index(i)).shift
end
end
(colored_mana_spent.sort == colored_mana_cost_aggregate.sort) && (colored_mana_hand.length + colorless_mana_hand) >= colorless_mana_cost_aggregate
end
This is a funny way to pull numbers out of the string. It would probably be easier to use scan, here is how you can use it:
# extracting existing colorless
colorless_mana_hand_array = hand.scan(/\d/).join.to_i
This will extract the digits from the string into an array, join them, and then convert to an integer.
Does anyone see a flaw in my code that's keeping me from passing these test?
Yes, Indeed.
You split your spell_cost strings via:
i.split("").each do |j|
# ...
end
The code will yield each single character to the block. For "10BR" this gives:
"10BR".split("").each do |j|
p j: j
end
Output:
{:j=>"1"}
{:j=>"0"}
{:j=>"B"}
{:j=>"R"}
Furthermore you have this check:
if j.to_i != 0
colorless_mana_cost_array << j
else
colored_mana_cost_aggregate << j
end
For the above input, you get
colorless_mana_cost_array #=> ["1"]
colored_mana_cost_aggregate #=> ["0", "B", "R"]
Because "0".to_i != 0 evaluate to false, just like "B" and "R".
If I understand your code correctly, "0" should go into the colorless_mana_cost_array.

How to make sure REXML::Formatters::Pretty uses \t instead of white-space for indentation

It seems to me that there's no way of making sure REXML::Formatters::Pretty can use \t instead of white-space for the indentation strategy in the XML Tree. The only thing I can do is to define how many white spaces are used per indentation level.
Am I wrong?
Not sure why REXML library does not provide you with this option since it could definitely support it internally but you can just roll your own formatter:
module REXML
module Formatters
class Prettier < Pretty
attr_accessor :style
def initialize(indentation = 2, indent_style =" ", ie_hack=false)
#style = indent_style
super(indentation,ie_hack)
end
protected
def write_element(node, output)
output << style*#level
output << "<#{node.expanded_name}"
node.attributes.each_attribute do |attr|
output << " "
attr.write( output )
end unless node.attributes.empty?
if node.children.empty?
if #ie_hack
output << " "
end
output << "/"
else
output << ">"
# If compact and all children are text, and if the formatted output
# is less than the specified width, then try to print everything on
# one line
skip = false
if compact
if node.children.inject(true) {|s,c| s & c.kind_of?(Text)}
string = ""
old_level = #level
#level = 0
node.children.each { |child| write( child, string ) }
#level = old_level
if string.length < #width
output << string
skip = true
end
end
end
unless skip
output << "\n"
#level += #indentation
node.children.each { |child|
next if child.kind_of?(Text) and child.to_s.strip.length == 0
write( child, output )
output << "\n"
}
#level -= #indentation
output << style*#level
end
output << "</#{node.expanded_name}"
end
output << ">"
end
def write_text( node, output )
s = node.to_s()
s.gsub!(/\s/,' ')
s.squeeze!(" ")
s = wrap(s, #width - #level)
s = indent_text(s, #level, style, true)
output << (style*#level + s)
end
def write_comment( node, output)
output << style * #level
Default.instance_method(:write_comment).bind(self).call(node,output)
end
def write_cdata( node, output)
output << style * #level
Default.instance_method(:write_cdata).bind(self).call(node,output)
end
end
end
end
Now you can specify your own indentation level and a indent style e.g.
require "rexml/document"
include REXML
string = <<EOF
<mydoc>
<someelement attribute="nanoo">Text, text, text</someelement>
</mydoc>
EOF
doc = Document.new string
f = Formatters::Prettier(2,"h")
f.write(doc,$stdout)
#<mydoc>
#hh<someelement attribute='nanoo'>
#hhhhText, text, text
#hh</someelement>
#</mydoc>
I used "h" to show how the indentation works as \t will not show up in $stdout but in you case this would be
f = Formatters::Prettier(1,"\t")

Creating A Caesar Cipher in Ruby, getting an error

I am attempting to create a Caesar Cipher in Ruby for my computer science class. My friend was able to create part of the code:
def cipher(word, n)
new_word = ""
word.each_char do |i|
n.times do
if(i == "z")
i = "a"
next
elsif(i == "Z")
i = "A"
next
end
i.next!
i == "%" ? i = " " : ""
end
new_word += i
end
puts new_word
end
cipher("phrase", 5)
Where the last line is where you would put the phrase you want to scramble, and the number is how much you want to scramble it by. One of the requirements is that we use gets.chomp to specify a phrase and amount to scramble by without editing the .rb file itself. So I came up with this:
puts "What would you like to scramble?"
word = gets.chomp
puts "How much would you like to scramble that?"
n = gets.chomp
def cipher(word, n)
new_word = ""
word.each_char do |i|
n.times do
if(i == "z")
i = "a"
next
elsif(i == "Z")
i = "A"
next
end
i.next!
i == "%" ? i = " " : ""
end
new_word += i
end
puts new_word
end
cipher(word, n)
And I get the following error outputed when run in Terminal:
some.rb:10:in `block in cipher': undefined method `times' for "5":String (NoMethodError)
from some.rb:9:in `each_char'
from some.rb:9:in `cipher'
from some.rb:26:in `<main>'
If someone could help me figure out what I'm doing wrong, that would help me out a lot.
gets.chomp returns a string
word = gets.chomp
So word is a string, as expected, but then you call gets.chomp again, this time to get number of scrabbles that should be applied to the string. So n is a string as well.
n = gets.chomp
When you call the times method on n it's not defined, because it only makes sense on integers. The solution is to convert n to an integer. This should work:
n = gets.chomp.to_i
Update
Documentation on the to_i method on String instances: http://ruby-doc.org/core-2.0.0/String.html#method-i-to_i
Call .to_i on n.
You need to convert that string you got from the user's input into a number before you can run .times on it. .to_i does this for you.
Example:
http://progzoo.net/wiki/Ruby:Convert_a_String_to_a_Number
Did this a while ago, the requirements were only lowercase ASCII alphabet letters, hope you get the general idea to do it your way:
def encrypt(msg, key)
msg.downcase.split("").each_with_index do |char, i|
next if msg[i] == " "
msg[i] = (msg[i].ord + key) > 122 ? (((msg[i].ord + key) % 123) + 97).chr : (msg[i].ord + key).chr
end
msg
end
def decrypt(msg, key)
msg.downcase.split("").each_with_index do |char, i|
next if msg[i] == " "
msg[i] = (msg[i].ord - key) < 97 ? (123 - (97 - (msg[i].ord - key))).chr : (msg[i].ord - key).chr
end
msg
end
gets.chomp return a string, you must convert it to a number in order to call .times method. Change this line n = gets.chomp by n = gets.chomp.to_i

Ruby Array Loop

I am currently trying to create a ruby algorithm to execute the following:
l = Array.new
Given array is text in the form of an array and has three manifests each titled Section No. 1, Section No. 2, Section No. 3 respectively.
Put the entire text in one string by looping through the array(l) and adding each line to the one big string each time.
Split the string using the split method and the key word "Section No." This will create an array with each element being one section of the text.
Loop through this new array to create files for each element.
So far I have the following:
a = l.join ''
b = Array.new
b = a.split ("Section No.")`
How would I go writing the easiest method to the third part?
Should only be about 2-3 lines.
Output would be the creation of three files each named after the manifest titles.
"Complex Version"
file_name = "Section"
section_number = "1"
new_text = File.open(file_name + section_number, 'w')
i = 0
n= 1
while i < l.length
if (l[i]!= "SECTION") and (l[i+1]!= "No")
new_text.puts l[i]
i = i + 1
else
new_text.close
section_number = (section_number.to_i +1).to_s
new_text = File.open(file_name + section_number, "w")
new_text.puts(l[i])
new_text.puts(l[i+1])
i=i+2
end
end
b.each_with_index(1) do |text, index|
File.write "section_#{index}.txt", text
end
To answer your most basic question, you could probably get away with:
sections.each_with_index do |section, index|
File.open("section_#{index}.txt", 'w') { |file| file.print section }
end
Here's an alternate solution:
input_string = "This should be your manifest string"
starting_string = "Section No."
copy_input_string = input_string.clone
sections = []
while(copy_input_string.length > 0)
index_of_next_start = copy_input_string.index(starting_string, starting_string.length) || copy_input_string.length
sections.push(copy_input_string.slice!(0...index_of_next_start))
end
sections.each_with_index do |section, index|
File.open("section_#{index}.txt", 'w') { |file| file.print section }
end
create string s by putting a space between each string in l
s = l.join ' '
split on 'Section No.' - note that 'Section No.' no longer appears in a
a = s.split('Section No.')
throw away the part before the first section
a = a[1..-1]
create the files
a.each do |section|
File.open('Section' + section.strip[0], 'w') do |file_handle|
file_handle.puts section
end
end

Ruby: Method return without a block

I am trying to make this code return when called without a block. The uncommented lines at the bottom is what I'm trying to get to return. The first uncommented line should return in tut, second line converted to english and the last should be in english. And why is the line " puts eng " returning up and down and not in sentence form? Thanks for any and all help.
Here's my code:
class Tut
##consonants = ["b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"]
def is_tut? string
if string =~ /^(([b-df-hj-np-z]ut)|([aeiou\s])|[[:punct:]])+$/i
yield
else
false
end
end
def self.to_tut string
string.each_char do |c|
c += "ut" if ##consonants.find { |i| i == c.downcase }
yield c if block_given?
end
end
def self.to_english string
array = string.split //
array.each do |c|
if ##consonants.find { |i| i == c.downcase }
array.shift
array.shift
end
yield c if block_given?
end
end
end
#Tut.to_tut( "Wow! Look at this get converted to Tut!" ) { |c| print c }
# should output : Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!
#puts
#puts
tut = Tut.to_tut( "Wow! Look at this get converted to Tut!" )
puts "from return: #{tut}"
puts
#Tut.to_tut( "Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!" ) { |c| print c }
#should outout : Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!
#puts
#puts
tut = Tut.to_tut( "Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!" )
puts "from return: #{tut}"
#puts
#tut_string = ""
#Tut.to_tut( "I'm in tut but I want to be in english." ) { |c| tut_string += c }
#puts tut_string
# should output : I'mut inut tututut bututut I wutanuttut tuto bute inut enutgutlutisuthut.
puts
#Tut.to_english( tut_string ){ |c| print c }
# should output : I'm in tut but I want to be in english.
lan = Tut.to_english( tut )
puts lan
(Opening note: You normally don't want to modify an Enumerable object while iterating over it, since that makes it much harder to read the code and debug it.)
Your to_tut doesn't retain your modifications because the "c" block variable is a copy of the string slice, instead of a reference to part of the string (if it were a ref, you'd be able to use << to append; "+=" still wouldn't work because it reassigns rather than changing the ref). That's just how each_char works, since a String doesn't contain references.
If you wanted to modify the string in place, you'd probably have to count backwards and then insert the 'ut' by index via string#[]= . But that's way complicated so I'll present a couple alternates.
Working to_tut #1:
def self.to_tut string
string.chars.map do |c|
yield c if block_given?
# this must be the last expression the block
if ##consonants.find { |i| i == c.downcase }
c + 'ut'
else
c
end
end.join
end
Working to_tut #2 - this is probably the most ruby-ish way to do it:
def self.to_tut string
string.gsub(/[#{##consonants.join}]/i) {|match|
yield match if block_given?
# this must be the last expression in the block
match + 'ut'
}
end
Your to_english doesn't work because array.shift always removes the first element of the array. Instead, you want to track the current index, and remove 2 chars starting from index+1.
Working to_english:
def self.to_english2 string
array = string.split //
array.each_with_index do |c, idx|
if ##consonants.find { |i| i == c.downcase }
array.slice!(idx+1, 2)
end
yield c if block_given?
end
array.join
end
Regarding why your "puts lan" returns one char per line - it's because your to_english returns an array. You'll want to call join to convert it.
The methods to_tut and to_english are giving you wrong answers when used without a block. This happens because ruby always returns the last value evaluated in your method. In your code that will be the result of the string.each_char for to_tut or array.each for to_english. In both cases, the result contains the original input, which is consequently returned and printed.
As to the puts eng, it prints the array returned by array.each of to_english.

Resources