Empact / roxml
- Source
- Commits
- Network (12)
- Issues (7)
- Downloads (13)
- Wiki (1)
- Graphs
-
Branch:
master
Pledgie Donations
Once activated, we'll place the following badge in your repository's detail box:
ROXML is a module for binding Ruby classes to XML. It supports custom mapping and bidirectional marshalling between Ruby and XML using annotation-style class methods, via Nokogiri or LibXML. — Read more
-
Could you avoid modifying the behavior of Nokogiri ?
3 comments Created about 1 month ago by nfoThe commit http://github.com/Empact/roxml/commit/9f0d49613f3337a7232d9d5cbebb44ffef34b10a monkey-patches Nokogiri::XML::Node.search, by adding "./" to every given xpath.
- module NodeExtensions
- def search(xpath)
super("./#{xpath}")- end
- end
Is this really necessary ? It simply breaks any other code in the same project that will use Nokogiri. For example, it breaks Mechanize in this case: getting all the fields in an HTML form. The patch forbids the search of child nodes in the tag.
Comments
-
For reference, the Product class in my ONIX library: http://github.com/yob/onix/blob/master/lib/onix/product.rb, lines 31-37.
The ONIX spec requires all dates to be formatted as YYYYMMDD.
I handle the to_xml conversion from a Date class to a string by defining a lambda method and passing it to the attribute using the :to_xml option.
For marshalling xml strings into Date objects, I was using ":as => Date", but that would raise an exception when I tried to parse a file that containing invalid dates ( like "00000000"). To define my own XML->Ruby process, I have to pass a block into the attribute definition. This works, but has lead to some duplication.
It would be awesome if I could pass a lambda object into :as, just like I can with :to_xml.
Comments
-
Comments
-
Rails class reloading leads to attribute redefinition errors
3 comments Created 8 months ago by EmpactReported by gorism:
I feel bad for continuing to pester you with questions, but I ran into another issue. This time it has to do with the buildup of accessors for a given class (that happens to include ROXML). Here is the class in all it's glory:
class BatchRequest
include ROXMLxml_name 'batch_request' xml_reader :batch_identifier xml_reader :batch_file, :cdata => true end
Note that this class is defined within a Controller class file, so it's not in a stand-alone .rb file.
Now, if I start the Rails application and submit a request to the Controller (which ends up loading and initializing the BatchRequest class, obviously), it works the first time. The second time, I get the following error:
Accessor batch_identifier is already defined as XML accessor in class BatchRequestIf I restart Mongrel, it works again the first time, fails the next time. Obviously something is sticking around between invocations. Also note that I can execute an Rspec test built to test the Controller class, with multiple "examples" within a single Rspec file testing the controller. That works fine.
I've tried a number of things:
1. Rolled back to the 2.4.3 version of ROXML (I'm currently using 2.5.1)
2. Forced using of REXML, just in case libxml was doing something odd (I'm currently at libxml-ruby 1.1.1)
3. Tried a different version of libxml-rubyI pursued all of these things, because I could have sworn I wasn't getting this behavior before, but the code hasn't changed.
Maybe I don't understand something about Ruby classloading (or perhaps Mongrel classloading), but this behavior is confusing.
Comments
We should probably simply be more open to redefinition.
More comments from gorism:
Disabling the offending code (in xml_attr()) didn't work. I didn't think it would, but what the heck. That resulted in duplicate data getting returned, because each time the class re-loads it keeps adding the same element definitions to the @roxml_attr array.
So, next I tried running mongrel in production mode, where classes don't get reloaded between requests. Magically fixed!
So the issue lies in the fact that the @roxml_attr array is hanging around between class loads. The issue is that @roxml_attr is a class instance variable. It's getting manipulated at class load time of my class(es) that have mixed in ROXML. Each time the classes get loaded (which is every request in development mode), that class instance variable is being referred to without having been re-initialized.
So it seems like there are a couple of possibilities here. The first is to re-initialize this value on class load. Seems the most obvious and straightforward to me. After all, if the class is reloading, you probably want the definition of elements/attributes to be reset. The second is to freeze the definition after initial load. That one doesn't feel right, because you would end up with weird behavior in development mode, where developers expert their changes to be immediately visible.
For now I'm pursuing the "initialize on class load" thing. I can solve it simply (but in a brute-force way) by simply setting "@roxml_attr = []" at the class level of my Ruby classes that have included ROXML. It's obviously more desirable to do this as part of ROXML. Perhaps an explicit method call that is done prior to any "xml_accessor" or "xml_reader" calls? That's sort of fragile, and I'd prefer for it to be something done behind the scenes. But I experimented for a while and couldn't get such a method to execute in the context of my class, rather than the ROXML module. Initializing @roxml_attrs in the context of the ROXML module does no good, because of scoping.
More comments from gorism:
OK, different idea.
How about ignoring the duplicate element/attribute declarations? You can add duplicate accessors all you want to a Ruby class, and the interpreter will use the last one defined.
So, change
xml_attrto:
def xml_attr(sym, type_and_or_opts = nil, opts = nil, &block) returning Definition.new(sym, *[type_and_or_opts, opts].compact, &block) do |attr| if !roxml_attrs.map(&:accessor).include? attr.accessor @roxml_attrs << attr end end endOr something like that.
-
Inlining multiple attributes in a call can lead to unexpected results
0 comments Created 2 months ago by EmpactI tried inlining some of the examples and specs didn't pass. The attrs shouldn't be order or declaration dependent, but they seem to be.
Comments
-
Ordering is not preserved between input and output
0 comments Created 2 months ago by EmpactIdeally, ordering should either be in the order found or in the order attributes are declared
Comments
-
Hi, I'm consuming a WebService that gives me some weird XML.
I have something like
<root_node> <some_node> <books><id>5</id><name>Book 5</name></books> <books><id>9</id><name>Book 9</name></books> </some_node> </root_node>I'd like to have an array of "books", but it seems impossible (as far as I can tell) because the "books" node name is already a plural.
I know, I should be singular in the first place, but I don't control this, it comes from a major player in the tourism industry.I've tried many things, including specifying an explicit :from, … but it doesn't work.
If I create a copy of the XML and change "books" into a singular node, it's working great.Any idea on how I can do this ?
Comments
I don't believe I've tested this case. If you'd write a failing test case and push it out, I'd find it very helfull, but in the mean time, have you taken a look at the :from option? When you explicitly specify what node you'd like to query from, roxml should always honor that. In short: xml_reader :books, :as => SomeIdAndNameObject, :from => 'books', :in => 'some_node'
I can't give any guarantees, but that's a likely fix.
Hi, thanks for your answer. I've tried what you suggest and it doesn't work.
Here is the exact code I ran :
require 'rubygems' require 'roxml' require 'pp' xml = %( <myxml> <store> <books><id>1</id><title>first book</title></books> <books><id>2</id><title>second book</title></books> <books><id>3</id><title>third book</title></books> </store> </myxml> ) class Book include ROXML xml_reader :id, :as => Integer xml_reader :title end class Store include ROXML xml_reader :books, :from => 'books', :in => 'store', :as => [Book] end class MyXml include ROXML xml_reader :store, :as => Store end pp MyXml.from_xml(xml).store.booksAbout the failing test, I'm still in the learning process of writing tests and I'm not familiar with the way you organized your specs. I you tell me which spec file I should write my test in, I'd try to make one and send you a pull request.
Cheers





Yeah we should be using a separate shim class to provide the common interface we need - just got lazy. You're welcome to take a crack at it (the specs provide good coverage and it's a reasonably isolated problem), otherwise I'll get around to it sometime in the near future.
+1... One of those fun things that unexpectedly break unrelated aspects, making it quite hard to diagnose. That ROXML was a dependency for another project we're using makes it even harder to figure out.
@Empact - you mind going into some more detail on how you'd implement that shim class? I'm keen on fixing this, so I'm willing to dive into it, but I'm running into some limitations on my current capability, ha. :) Just unfamiliar with some of the fine points regarding opening/inheriting/redefining existing classes here.