edit xml file.

D

Dan Thomas

I'm new to Ruby, and this may be considered advanced. I'll do my best
to describe it. I inherited some code that uses erb to display a web
page form, like this:

get "/" do
$script = File.read("public/script2.xml")

@xml = Nokogiri::XML($script)

erb :index
end

index.erb (attached) seems to get that xml file passed into it. It has
embedded
Ruby code that seems to loop through the xml and assign a bunch of

variables and such.

The user inputs something on the form, and I am supposed to then edit
the xml with that new data. For example a node in the xml has a
attribute of value = "sound1.mp3". On the form the user enters

"sound2.mp3", and now the xml has to get modified to say, value =
"sound2.mp3".

I actually got it all working, except that I am modifying the xml
explicitly (in a post block), using REXML like this:


newSound = params[:newsound]

# open the xml
File.open("public/script2.xml", "r") do |aFile|
config = REXML::Document.new(aFile)
rootVar = config.root.elements['object']


#drill down to the sound node, assign the new value to it
rootVar.elements['property[2]'].elements['array'].elements['object'].elements['property[2]'].elements['array'].elements['object'].elements['property[2]'].attributes["value"]

= newSound

# reopen the file for writing, which will erase it, and copy all the
data into it
formatter = REXML::Formatters::Default.new
File.open('public/script2.xml', 'w') do |result|

formatter.write(config, result)
end
end
# re-read in the xml file, and then load index again
$script_post = File.read("public/script2.xml")
@xml = Nokogiri::XML($script_post)
erb:index



The part I need to fix for my boss is: the part where I'm drilling down
to assign the new value should be somehow connected to index.erb, which
is doing all kinds of looping and variable assignments related to the
xml. I don't know what it is called or where to start. Hell I don't
even know how to explain it. Sorry for the long post, any help, search
words to look-up, or examples greatly appreciated.

Attachments:
http://www.ruby-forum.com/attachment/5764/index.erb
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

I actually got it all working, except that I am modifying the xml
explicitly (in a post block), using REXML like this:
Good job! I know that can be frustrating to try to figure out :) Though, you
should know that Nokogiri is widely considered the best Ruby XML library
there is, it is fastest, most reliable, most consistent, and has a pretty
nice interface. Even Martin Fowler uses it for his own site. So, if you wind
up refactoring that code, you might consider using Nokogiri over REXML.

newSound = params[:newsound]

# open the xml
File.open("public/script2.xml", "r") do |aFile|
config = REXML::Document.new(aFile)
rootVar = config.root.elements['object']


#drill down to the sound node, assign the new value to it

rootVar.elements['property[2]'].elements['array'].elements['object'].elements['property[2]'].elements['array'].elements['object'].elements['property[2]'].attributes["value"]
= newSound

This looks fragile to me. I don't understand xpaths, I always use CSS
selectors, but it looks like you are telling it what to set, based on its
position within the document. You'll want to be careful to check if an
element is added or removed, or if the order is changed, will that break
this query? It seems like a Law of Demeter violation. You might put an id on
the sound node so you don't have to drill down to it, but can just say to
get the one with the given id, and edit it that way. Then it would probably
be more robust, easier to read, and faster.

# reopen the file for writing, which will erase it, and copy all the
data into it
formatter = REXML::Formatters::Default.new
File.open('public/script2.xml', 'w') do |result|

formatter.write(config, result)
end
end
# re-read in the xml file, and then load index again
$script_post = File.read("public/script2.xml")
@xml = Nokogiri::XML($script_post)
erb:index
I guess I typically store my data in a database, but if the XML file is
working, then that seems like a fine solution. Sounds like its in the spirit
of YAGNI, so I suppose the XPers would be proud :)

The part I need to fix for my boss is: the part where I'm drilling down
to assign the new value should be somehow connected to index.erb, which
is doing all kinds of looping and variable assignments related to the
xml. I don't know what it is called or where to start. Hell I don't
even know how to explain it. Sorry for the long post, any help, search
words to look-up, or examples greatly appreciated.
Hmm, sorry, its not very clear to me what you want to do differently. I
don't think it is a good idea to modify your template (the erb file). I got
the impression you were storing your data in an XML file on your file
system, which seems fine, and seems like your solution is congruent with.

I'm a little confused by the use of global variables (the ones that begin
with the dollar signs), I was kind of thinking maybe the guy before you read
in the xml to there, then stored that in memory. And then you could just
edit the Nokogiri document, and save that to a file without having to do
lots of writing and reading and redoing the same work. But since its inside
the root get request, I'm less sure. I suppose I'd need to see more code,
that seems nonstandard.

Regarding the looping and variable assignments, you are right, that probably
shouldn't be in the template, it could probably be refactored to use helpers
(methods that handle responsibilities, you call them from your template so
the template doesn't have to do the work right there inside it) and partials
(other erb templates that you can render into the one you are working on),
If you decide you want to do that, the Sinatra code below should talk about
both of those.


I think the best I can do is give you links to where you can find more info
on some of the tools you are using.

* It looks like you are using the Sinatra web framework
http://www.sinatrarb.com/
Very simple and elegant, you could probably learn enough about it to
understand everything in your script with just a few hours
There is a Sinatra book, which is comprehensive
http://sinatra-book.gittr.com/
I personally got a lot out of the Sinatra Peepcode
http://peepcode.com/products/sinatra though it goes beyond what you are
using it for

* For XML, you have Nokogiri http://nokogiri.org/
Check out the tutorials section to see how to use it to modify your xml

* For templating, you are using ERB
http://ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
It's really straightforward, anything with <%= ... %> will treat the
inside as Ruby, convert it to a string, and stick it in the document
Anything with <% ... %> will treat the inside as Ruby, but won't have
output (ie a control structure like a loop)
Here are a couple of simple examples
https://github.com/JoshCheek/JoshsRubyKickstart/blob/master/cheatsheets/erb-embedded_ruby.rb

* I also see you are using andand http://andand.rubyforge.org/
It can be summarized as:
a.andand(b)

is equivalent to
a && a.b

which doesn't seem that impressive, until you realize a could be anything,
including a really long or expensive method call, where you would otherwise
have to store the result in a temporary variable.

There is also a fascinating, though probably not particularly relevant for
you video by the author of andand, about how it works, and how he perceives
Ruby vs purely functional languages, what he sees as issues, and why he
wrote andand http://www.infoq.com/presentations/braithwaite-rewrite-ruby
 
D

Dan Thomas

Josh,

Thank you for that unbelievably helpful and in-depth reply. The "editing
xml" section of Nokogiri, is helpful, and I will experiment with that.

So now that I am a little more clear I can ask better questions. Are you
saying that in my post block, I should do something like:




#this is defined globally
$script = File.read("public/script.xml")






post "/" do
@xml = Nokogiri::XML($script)

# read the nokogiri tutorial to learn how to search for the
# thing that i want to change, and modify it,
# i don't know how to do this yet
@xml.blah
# assume that worked and the Nokogiri object got modified

# Save it back to the xml file.
# I saw something like this, looks like it may save over the
# original xml?
@xml.to_xml

# now that i've changed the xml, diplay the page again
erb :index
end


OR,

in an index_post erb file, should I be trying to change values sorta
like this:


<% current code %>
<% animation_url
=dialogue.search("property[name=animationUrl]").first.andand["value"]%>

<% # my additional code suggestion. change the value based on user input
dialogue.search("property[name=animationUrl]").first.andand["value"]
= "some new value entered by user"%>

and then somehow write this back to the xml?

Again, thanks a ton. Hope this makes sense.
 
D

Dan Thomas

Josh,

having re-read, I'm understanding your comments more. Can't seem to
delete my last post.

The 'violation of the law of Demeter' is actually what I'm trying to
fix. I thought that maybe I should be trying to modify data in the erb
file, but it sounds like you're saying just use Nogogiri, search for the
id I want to change, make the change, and save it back to the xml file.
Sounds good to me.

Are there any examples of searching a Nogogiri object using CSS
selectors, and making a change? Sorry, I'm out of my element here, and
appreciate your help. Thanks
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

Josh,

having re-read, I'm understanding your comments more. Can't seem to
delete my last post.
The forum is a front-end for a newsgroup / mailing list. So, for example, I
am typing this from gmail. Since email isn't mutable, you can't remove posts
once they are made.

The 'violation of the law of Demeter' is actually what I'm trying to
fix. I thought that maybe I should be trying to modify data in the erb
file, but it sounds like you're saying just use Nogogiri, search for the
id I want to change, make the change, and save it back to the xml file.
Sounds good to me.
I had started to write a response, but my code wasn't working like I wanted,
and I had to go to class before I had a chance to look into it :/

If I understand your code correctly, you are storing your values in an XML
file, and your sinatra app just loads it in to find the values, displays
them to the user with a form to submit updates, receives the updates, and
changes the XML accordingly. If that is the case, the perpetuation of your
data happens in the XML, and if you want to change the data, that is where
you should be changing it. In otherwords, the XML file takes the place of
your database (coincidentally, the class I'm in as I'm writing this is XML
databases!)

Are there any examples of searching a Nogogiri object using CSS
selectors, and making a change? Sorry, I'm out of my element here, and
appreciate your help. Thanks
I don't actually know much about using Nokogiri with XML, I usually use it
with HTML, and choose YAML when selecting a format to store my data in. But
I see "Notably, you can even use CSS queries in an XML document!" on
http://nokogiri.org/tutorials/searching_a_xml_html_document.html and it has
an example.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top