Edit xml file

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.

On Wed, Jan 19, 2011 at 6:54 PM, Dan T. [email protected]
wrote:

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 :slight_smile: 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 F. uses it for his own site. So, if you
wind
up refactoring that code, you might consider using Nokogiri over REXML.

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.

$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 :slight_smile:

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 Ruby.rewrite(Ruby)

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.

On Thu, Jan 20, 2011 at 2:02 AM, Dan T. [email protected]
wrote:

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 :confused:

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
Searching a XML/HTML document - Nokogiri and it
has
an example.

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 modifying data? Sorry, I’m out of my element here, I
don’t even know what a css selector is. Appreciate your help. Thanks