nokogiri-happymapper 0.5.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG.md +5 -1
  3. data/README.md +74 -53
  4. data/lib/happymapper/anonymous_mapper.rb +114 -0
  5. data/lib/happymapper/attribute.rb +20 -2
  6. data/lib/happymapper/element.rb +52 -2
  7. data/lib/happymapper/item.rb +89 -182
  8. data/lib/happymapper/supported_types.rb +140 -0
  9. data/lib/happymapper/text_node.rb +6 -1
  10. data/lib/happymapper/version.rb +3 -0
  11. data/lib/happymapper.rb +42 -22
  12. data/spec/attribute_default_value_spec.rb +50 -0
  13. data/spec/fixtures/default_namespace_combi.xml +2 -1
  14. data/spec/happymapper/attribute_spec.rb +12 -0
  15. data/spec/happymapper/element_spec.rb +9 -0
  16. data/spec/{happymapper_item_spec.rb → happymapper/item_spec.rb} +5 -5
  17. data/spec/happymapper/text_node_spec.rb +9 -0
  18. data/spec/happymapper_parse_spec.rb +87 -0
  19. data/spec/happymapper_spec.rb +9 -3
  20. data/spec/ignay_spec.rb +22 -22
  21. data/spec/inheritance_spec.rb +61 -0
  22. data/spec/parse_with_object_to_update_spec.rb +111 -0
  23. data/spec/spec_helper.rb +1 -1
  24. data/spec/to_xml_spec.rb +200 -0
  25. data/spec/to_xml_with_namespaces_spec.rb +196 -0
  26. data/spec/wilcard_tag_name_spec.rb +96 -0
  27. data/spec/wrap_spec.rb +82 -0
  28. data/spec/xpath_spec.rb +60 -59
  29. metadata +34 -33
  30. data/TODO +0 -0
  31. data/spec/happymapper_attribute_spec.rb +0 -21
  32. data/spec/happymapper_element_spec.rb +0 -21
  33. data/spec/happymapper_generic_base_spec.rb +0 -92
  34. data/spec/happymapper_text_node_spec.rb +0 -21
  35. data/spec/happymapper_to_xml_namespaces_spec.rb +0 -196
  36. data/spec/happymapper_to_xml_spec.rb +0 -203
  37. data/spec/happymapper_wrap_spec.rb +0 -69
  38. data/spec/parse_instance_spec.rb +0 -129
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OTI1ZDRjNDk4NTE1ZjAxZTA0ZjE0YzhhM2JkYTk3YmZjOWZkYjE3NA==
5
+ data.tar.gz: !binary |-
6
+ NjQyMjEyMDBmYTU5NGZmNzM2NGY4NzJkM2Q1ZDA5YmU0NzY5NDU2OA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NjY1MTkxOTI0NjM1NDI2YTdjOGUwNzFhNjkzMWNiMzg5OGQ4ZTcyZGQ3NzZm
10
+ YmJlNzg0MjNlYjk4MDE5N2E0M2IyNjM5MGE5MmE3M2I3MzE2MTM4MmEwZTNi
11
+ OTMxMzkxMTViMGI3Mzc5MmY2ZGJjOGY5YTYyNjY1OTk3YTIwZDU=
12
+ data.tar.gz: !binary |-
13
+ ZjBmZjEwMzQ4NWRmYWJmYzFiMDRkZDYwYzU5N2VjY2U5ZTEwODU1ZmRiYjVm
14
+ Njc5OTIyNjA3NmJhMDNmOWNhM2RjOTI5NDZhNmEzODQ5ZDc0MzMxYTM0MWE0
15
+ MTQzMjYzMDY0Njg4ZGE3NWFjNTU1YmY3NDAwY2ExNDhmMDdhMDc=
data/CHANGELOG.md CHANGED
@@ -1,10 +1,14 @@
1
+ ## 0.5.6 / 2012-10-29
2
+
3
+ * Add possibility to give a configuration block to Nokogiri when parsing (DieboldInc).
4
+
1
5
  ## 0.5.5 / 2012-09-30
2
6
 
3
7
  * Fix for Boolean attributes to ensure that they parse correctly (zrob)
4
8
 
5
9
  ## 0.5.4/ 2012-09-25
6
10
 
7
- * #wrap method allows you to better model xml content that is buried deep
11
+ * the #wrap method allows you to better model xml content that is buried deep
8
12
  within the xml. This implementation addresses issues with calling #to_xml
9
13
  with content that was parsed from an xpath. (zrob)
10
14
 
data/README.md CHANGED
@@ -13,7 +13,7 @@ This project is a fork of the great work done first by
13
13
  * Raw XML content parsing
14
14
  * [burtlo](http://github.com/burtlo/happymapper)'s `#to_xml` support utilizing the same HappyMapper tags
15
15
  * Fixes for [namespaces when using composition of classes](https://github.com/burtlo/happymapper/commit/fd1e898c70f7289d2d2618d629b56f2f6623785c)
16
- * Fixes for instances of XML where a [namespace is defined but no elements with that namespace are found](https://github.com/burtlo/happymapper/commit/9614221a80ff3bda18ff859aa751dff29cf52fd3).
16
+ * Fixes for instances of XML where a [namespace is defined but no elements with that namespace are found](https://github.com/burtlo/happymapper/commit/9614221a80ff3bda18ff859aa751dff29cf52fd3).
17
17
 
18
18
  ## Installation
19
19
 
@@ -42,6 +42,30 @@ Let's start with a simple example to get our feet wet. Here we have a simple exa
42
42
  <country code="de">Germany</country>
43
43
  </address>
44
44
 
45
+ Happymapper provides support for simple, zero configuration parsing as well as the ability to model the XML content in classes.
46
+
47
+ ## HappyMapper.parse(XML)
48
+
49
+ With no classes or configuration you can parse the example XML with little effort:
50
+
51
+ ```ruby
52
+ address = HappyMapper.parse(ADDRESS_XML_DATA)
53
+ address.street # => Milchstrasse
54
+ address.housenumber # => 23
55
+ address.postcode # => 26131
56
+ address.city # => Oldenburg
57
+ address.country.code # => de
58
+ address.country.content # => Germany
59
+ ```
60
+
61
+ It is important to be aware that this no configuration parsing is limited in capacity:
62
+
63
+ * All element names are converted to accessor methods with [underscorized](http://rubydoc.info/gems/activesupport/ActiveSupport/Inflector:underscore) names
64
+ * All value fields are left as String types
65
+ * Determining if there is just one or multiple child elements is hard, so it assumes it is one until it finds another with the same name.
66
+
67
+ ## Address.parse(XML)
68
+
45
69
  Happymapper will let you easily model this information as a class:
46
70
 
47
71
  require 'happymapper'
@@ -62,7 +86,7 @@ To make a class HappyMapper compatible you simply `include HappyMapper` within t
62
86
  * `tag` matches the name of the XML tag name 'address'.
63
87
 
64
88
  * `element` defines accessor methods for the specified symbol (e.g. `:street`,`:housenumber`) that will return the class type (e.g. `String`,`Integer`) of the XML tag specified (e.g. `:tag => 'street'`, `:tag => 'housenumber'`).
65
-
89
+
66
90
  When you define an element with an accessor with the same name as the tag, this is the case for all the examples above, you can omit the `:tag`. These two element declaration are equivalent to each other:
67
91
 
68
92
  element :street, String, :tag => 'street'
@@ -83,7 +107,7 @@ These three statements are equivalent to each other.
83
107
  With the mapping of the address XML articulated in our Address class it is time to parse the data:
84
108
 
85
109
  address = Address.parse(ADDRESS_XML_DATA, :single => true)
86
- puts address.street
110
+ puts address.street
87
111
 
88
112
  Assuming that the constant `ADDRESS_XML_DATA` contains a string representation of the address XML data this is fairly straight-forward save for the `parse` method.
89
113
 
@@ -117,7 +141,7 @@ Your resulting `streets` method will now return an array.
117
141
 
118
142
  address = Address.parse(ADDRESS_XML_DATA, :single => true)
119
143
  puts address.streets.join('\n')
120
-
144
+
121
145
  Imagine that you have to write `streets.join('\n')` for the rest of eternity throughout your code. It would be a nightmare and one that you could avoid by creating your own convenience method.
122
146
 
123
147
  require 'happymapper'
@@ -126,13 +150,13 @@ Imagine that you have to write `streets.join('\n')` for the rest of eternity thr
126
150
  include HappyMapper
127
151
 
128
152
  tag 'address'
129
-
153
+
130
154
  has_many :streets, String
131
-
155
+
132
156
  def streets
133
157
  @streets.join('\n')
134
158
  end
135
-
159
+
136
160
  element :postcode, String, :tag => 'postcode'
137
161
  element :housenumber, String, :tag => 'housenumber'
138
162
  element :city, String, :tag => 'city'
@@ -156,7 +180,7 @@ Now when we call the method `streets` we get a single value, but we still have t
156
180
  Attributes are absolutely the same as `element` or `has_many`
157
181
 
158
182
  attribute :location, String, :tag => 'location
159
-
183
+
160
184
  Again, you can omit the tag if the attribute accessor symbol matches the name of the attribute.
161
185
 
162
186
 
@@ -211,9 +235,9 @@ Well if we only going to parse country, on it's own, we would likely create a cl
211
235
 
212
236
  class Country
213
237
  include HappyMapper
214
-
238
+
215
239
  tag 'country'
216
-
240
+
217
241
  attribute :code, String
218
242
  content :name, String
219
243
  end
@@ -228,24 +252,24 @@ Awesome, now if we were to redeclare our `Address` class we would use our new `C
228
252
  include HappyMapper
229
253
 
230
254
  tag 'address'
231
-
255
+
232
256
  has_many :streets, String, :tag => 'street'
233
-
257
+
234
258
  def streets
235
259
  @streets.join('\n')
236
260
  end
237
-
261
+
238
262
  element :postcode, String, :tag => 'postcode'
239
263
  element :housenumber, String, :tag => 'housenumber'
240
264
  element :city, String, :tag => 'city'
241
265
  element :country, Country, :tag => 'country'
242
266
  end
243
-
267
+
244
268
  Instead of `String`, `Boolean`, or `Integer` we say that it is a `Country` and HappyMapper takes care of the details of continuing the XML mapping through the country element.
245
269
 
246
270
  address = Address.parse(ADDRESS_XML_DATA, :single => true)
247
271
  puts address.country.code
248
-
272
+
249
273
  A quick note, in the above example we used the constant `Country`. We could have used `'Country'`. The nice part of using the latter declaration, enclosed in quotes, is that you do not have to define your class before this class. So Country and Address can live in separate files and as long as both constants are available when it comes time to parse you are golden.
250
274
 
251
275
  ## Custom XPATH
@@ -258,12 +282,12 @@ Getting to elements deep down within your XML can be a little more work if you d
258
282
  <gallery>
259
283
  <title href="htttp://fishlovers.org/friends">Friends Who Like Fish</title>
260
284
  <picture>
261
- <name>Burtie Sanchez</name>
285
+ <name>Burtie Sanchez</name>
262
286
  <img>burtie01.png</img>
263
287
  </picture>
264
288
  </gallery>
265
289
  <picture>
266
- <name>Unsorted Photo</name>
290
+ <name>Unsorted Photo</name>
267
291
  <img>bestfriends.png</img>
268
292
  </picture>
269
293
  </media>
@@ -272,36 +296,36 @@ You may want to map the sub-elements contained buried in the 'gallery' as top le
272
296
 
273
297
  class Media
274
298
  include HappyMapper
275
-
299
+
276
300
  has_one :title, String, :xpath => 'gallery/title'
277
301
  has_one :link, String, :xpath => 'gallery/title/@href'
278
302
  end
279
303
 
280
304
 
281
- ## Subclasses
305
+ ## Shared Functionality
282
306
 
283
- ### Inheritance (it doesn't work!)
307
+ ### Inheritance Approach
284
308
 
285
309
  While mapping XML to objects you may arrive at a point where you have two or more very similar structures.
286
310
 
287
311
  class Article
288
312
  include HappyMapper
289
-
313
+
290
314
  has_one :title, String
291
315
  has_one :author, String
292
316
  has_one :published, Time
293
-
317
+
294
318
  has_one :entry, String
295
-
319
+
296
320
  end
297
321
 
298
322
  class Gallery
299
323
  include HappyMapper
300
-
324
+
301
325
  has_one :title, String
302
326
  has_one :author, String
303
327
  has_one :published, Time
304
-
328
+
305
329
  has_many :photos, String
306
330
 
307
331
  end
@@ -310,29 +334,27 @@ In this example there are definitely two similarities between our two pieces of
310
334
 
311
335
  class Content
312
336
  include HappyMapper
313
-
337
+
314
338
  has_one :title, String
315
339
  has_one :author, String
316
340
  has_one :published, Time
317
-
318
341
  end
319
342
 
320
343
  class Article < Content
321
344
  include HappyMapper
322
-
345
+
323
346
  has_one :entry, String
324
347
  end
325
-
348
+
326
349
  class Gallery < Content
327
350
  include HappyMapper
328
-
351
+
329
352
  has_many :photos, String
330
353
  end
331
-
332
- However, *this does not work*. And the reason is because each one of these element declarations are method calls that are defining elements on the class itself. So it is not passed down through inheritance.
333
354
 
334
- You can however, use some module mixin power to save you those keystrokes and impress your friends.
355
+ ### Module Mixins Approache
335
356
 
357
+ You can also solve the above problem through mixins.
336
358
 
337
359
  module Content
338
360
  def self.included(content)
@@ -340,23 +362,22 @@ You can however, use some module mixin power to save you those keystrokes and im
340
362
  content.has_one :author, String
341
363
  content.has_one :published, Time
342
364
  end
343
-
365
+
344
366
  def published_time
345
367
  @published.strftime("%H:%M:%S")
346
368
  end
347
-
348
369
  end
349
370
 
350
371
  class Article
351
372
  include HappyMapper
352
-
373
+
353
374
  include Content
354
375
  has_one :entry, String
355
376
  end
356
377
 
357
378
  class Gallery
358
379
  include HappyMapper
359
-
380
+
360
381
  include Content
361
382
  has_many :photos, String
362
383
  end
@@ -372,23 +393,23 @@ I ran into a case where I wanted to capture all the pictures that were directly
372
393
  <media>
373
394
  <gallery>
374
395
  <picture>
375
- <name>Burtie Sanchez</name>
396
+ <name>Burtie Sanchez</name>
376
397
  <img>burtie01.png</img>
377
398
  </picture>
378
399
  </gallery>
379
400
  <picture>
380
- <name>Unsorted Photo</name>
401
+ <name>Unsorted Photo</name>
381
402
  <img>bestfriends.png</img>
382
403
  </picture>
383
404
  </media>
384
-
405
+
385
406
  The following `Media` class is where I started:
386
407
 
387
408
  require 'happymapper'
388
409
 
389
410
  class Media
390
411
  include HappyMapper
391
-
412
+
392
413
  has_many :galleries, Gallery, :tag => 'gallery'
393
414
  has_many :pictures, Picture, :tag => 'picture'
394
415
  end
@@ -424,20 +445,20 @@ Here again is our address example with a made up namespace called `prefix` that
424
445
 
425
446
  class Address
426
447
  include HappyMapper
427
-
448
+
428
449
  tag 'address'
429
450
  namespace 'prefix'
430
451
  # ... rest of the code ...
431
452
  end
432
-
433
- Of course, if that is too easy for you, you can append a `:namespace => 'prefix` to every one of the elements that you defined.
453
+
454
+ Of course, if that is too easy for you, you can append a `:namespace => 'prefix` to every one of the elements that you defined.
434
455
 
435
456
  has_many :street, String, :tag => 'street', :namespace => 'prefix'
436
457
  element :postcode, String, :tag => 'postcode', :namespace => 'prefix'
437
458
  element :housenumber, String, :tag => 'housenumber', :namespace => 'prefix'
438
459
  element :city, String, :tag => 'city', :namespace => 'prefix'
439
460
  element :country, Country, :tag => 'country', :namespace => 'prefix'
440
-
461
+
441
462
  I definitely recommend the former, as it saves you a whole hell of lot of typing. However, there are times when appending a namespace to an element declaration is important and that is when it has a different namespace then `namespsace 'prefix'`.
442
463
 
443
464
  Imagine that our `country` actually belonged to a completely different namespace.
@@ -455,7 +476,7 @@ Imagine that our `country` actually belonged to a completely different namespace
455
476
  Well we would need to specify that namespace:
456
477
 
457
478
  element :country, Country, :tag => 'country', :namespace => 'different'
458
-
479
+
459
480
  With that we should be able to parse as we once did.
460
481
 
461
482
  ## Large Datasets (in_groups_of)
@@ -478,7 +499,7 @@ Saving a class to XML is as easy as calling `#to_xml`. The end result will be t
478
499
  When you are saving data to xml it is often important to change or manipulate data to a particular format. For example, a time object:
479
500
 
480
501
  has_one :published_time, Time, :on_save => lambda {|time| time.strftime("%H:%M:%S") if time }
481
-
502
+
482
503
  Here we add the options `:on_save` and specify a lambda which will be executed on the method call to `:published_time`.
483
504
 
484
505
  ### :state_when_nil
@@ -486,7 +507,7 @@ Here we add the options `:on_save` and specify a lambda which will be executed o
486
507
  When an element contains a nil value, or perhaps the result of the :on_save lambda correctly results in a nil value you will be happy that the element will not appear in the resulting XML. However, there are time when you will want to see that element and that's when `:state_when_nil` is there for you.
487
508
 
488
509
  has_one :favorite_color, String, :state_when_nil => true
489
-
510
+
490
511
  The resulting XML will include the 'favorite_color' element even if the favorite color has not been specified.
491
512
 
492
513
  ### :read_only
@@ -496,7 +517,7 @@ saving to XML, you can ensure that takes place by stating that it is `read only`
496
517
 
497
518
  has_one :modified, Boolean, :read_only => true
498
519
  attribute :temporary, Boolean, :read_only => true
499
-
520
+
500
521
  This is useful if perhaps the incoming XML is different than the out-going XML.
501
522
 
502
523
  ### namespaces
@@ -505,17 +526,17 @@ While parsing the XML only required you to simply specify the prefix of the name
505
526
 
506
527
  class Address
507
528
  include HappyMapper
508
-
529
+
509
530
  register_namespace 'prefix', 'http://www.unicornland.com/prefix'
510
531
  register_namespace 'different', 'http://www.trollcountry.com/different'
511
-
532
+
512
533
  tag 'address'
513
534
  namespace 'prefix'
514
-
535
+
515
536
  has_many :street, String
516
537
  element :postcode, String
517
538
  element :housenumber, String
518
539
  element :city, String
519
540
  element :country, Country, :tag => 'country', :namespace => 'different'
520
-
541
+
521
542
  end
@@ -0,0 +1,114 @@
1
+ module HappyMapper
2
+ module AnonymousMapper
3
+
4
+ def parse(xml_content)
5
+
6
+ # TODO: this should be able to handle all the types of functionality that parse is able
7
+ # to handle which includes the text, xml document, node, fragment, etc.
8
+ xml = Nokogiri::XML(xml_content)
9
+
10
+ happymapper_class = create_happymapper_class_with_element(xml.root)
11
+
12
+ # With all the elements and attributes defined on the class it is time
13
+ # for the class to actually use the normal HappyMapper powers to parse
14
+ # the content. At this point this code is utilizing all of the existing
15
+ # code implemented for parsing.
16
+ happymapper_class.parse(xml_content, :single => true)
17
+
18
+ end
19
+
20
+ private
21
+
22
+ #
23
+ # Borrowed from Active Support to convert unruly element names into a format
24
+ # known and loved by Rubyists.
25
+ #
26
+ def underscore(camel_cased_word)
27
+ word = camel_cased_word.to_s.dup
28
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
29
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
30
+ word.tr!("-", "_")
31
+ word.downcase!
32
+ word
33
+ end
34
+
35
+ #
36
+ # Used internally when parsing to create a class that is capable of
37
+ # parsing the content. The name of the class is of course not likely
38
+ # going to match the content it will be able to parse so the tag
39
+ # value is set to the one provided.
40
+ #
41
+ def create_happymapper_class_with_tag(tag_name)
42
+ happymapper_class = Class.new
43
+ happymapper_class.class_eval do
44
+ include HappyMapper
45
+ tag tag_name
46
+ end
47
+ happymapper_class
48
+ end
49
+
50
+ #
51
+ # Used internally to create and define the necessary happymapper
52
+ # elements.
53
+ #
54
+ def create_happymapper_class_with_element(element)
55
+ happymapper_class = create_happymapper_class_with_tag(element.name)
56
+
57
+ happymapper_class.namespace element.namespace.prefix if element.namespace
58
+
59
+ element.namespaces.each do |prefix,namespace|
60
+ happymapper_class.register_namespace prefix, namespace
61
+ end
62
+
63
+ element.attributes.each do |name,attribute|
64
+ define_attribute_on_class(happymapper_class,attribute)
65
+ end
66
+
67
+ element.children.each do |element|
68
+ define_element_on_class(happymapper_class,element)
69
+ end
70
+
71
+ happymapper_class
72
+ end
73
+
74
+
75
+ #
76
+ # Define a HappyMapper element on the provided class based on
77
+ # the element provided.
78
+ #
79
+ def define_element_on_class(class_instance,element)
80
+
81
+ # When a text element has been provided create the necessary
82
+ # HappyMapper content attribute if the text happens to content
83
+ # some content.
84
+
85
+ if element.text? and element.content.strip != ""
86
+ class_instance.content :content, String
87
+ end
88
+
89
+ # When the element has children elements, that are not text
90
+ # elements, then we want to recursively define a new HappyMapper
91
+ # class that will have elements and attributes.
92
+
93
+ element_type = if !element.elements.reject {|e| e.text? }.empty? or !element.attributes.empty?
94
+ create_happymapper_class_with_element(element)
95
+ else
96
+ String
97
+ end
98
+
99
+ method = class_instance.elements.find {|e| e.name == element.name } ? :has_many : :has_one
100
+
101
+ class_instance.send(method,underscore(element.name),element_type)
102
+ end
103
+
104
+ #
105
+ # Define a HappyMapper attribute on the provided class based on
106
+ # the attribute provided.
107
+ #
108
+ def define_attribute_on_class(class_instance,attribute)
109
+ class_instance.attribute underscore(attribute.name), String
110
+ end
111
+
112
+ end
113
+
114
+ end
@@ -1,3 +1,21 @@
1
1
  module HappyMapper
2
- class Attribute < Item; end
3
- end
2
+ class Attribute < Item
3
+ attr_accessor :default
4
+
5
+ # @see Item#initialize
6
+ # Additional options:
7
+ # :default => Object The default value for this
8
+ def initialize(name, type, o={})
9
+ super
10
+ self.default = o[:default]
11
+ end
12
+
13
+ def find(node, namespace, xpath_options)
14
+ if options[:xpath]
15
+ yield(node.xpath(options[:xpath],xpath_options))
16
+ else
17
+ yield(node[tag])
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,53 @@
1
1
  module HappyMapper
2
- class Element < Item; end
3
- end
2
+ class Element < Item
3
+
4
+ def find(node, namespace, xpath_options)
5
+ if self.namespace
6
+ # from the class definition
7
+ namespace = self.namespace
8
+ elsif options[:namespace]
9
+ namespace = options[:namespace]
10
+ end
11
+
12
+ if options[:single]
13
+ if options[:xpath]
14
+ result = node.xpath(options[:xpath], xpath_options)
15
+ else
16
+ result = node.xpath(xpath(namespace), xpath_options)
17
+ end
18
+
19
+ if result
20
+ value = yield(result.first)
21
+ handle_attributes_option(result, value, xpath_options)
22
+ value
23
+ end
24
+ else
25
+ target_path = options[:xpath] ? options[:xpath] : xpath(namespace)
26
+ node.xpath(target_path, xpath_options).collect do |result|
27
+ value = yield(result)
28
+ handle_attributes_option(result, value, xpath_options)
29
+ value
30
+ end
31
+ end
32
+ end
33
+
34
+ def handle_attributes_option(result, value, xpath_options)
35
+ if options[:attributes].is_a?(Hash)
36
+ result = result.first unless result.respond_to?(:attribute_nodes)
37
+
38
+ result.attribute_nodes.each do |xml_attribute|
39
+ if attribute_options = options[:attributes][xml_attribute.name.to_sym]
40
+ attribute_value = Attribute.new(xml_attribute.name.to_sym, *attribute_options).from_xml_node(result, namespace, xpath_options)
41
+
42
+ result.instance_eval <<-EOV
43
+ def value.#{xml_attribute.name}
44
+ #{attribute_value.inspect}
45
+ end
46
+ EOV
47
+ end # if attributes_options
48
+ end # attribute_nodes.each
49
+ end # if options[:attributes]
50
+ end # def handle...
51
+
52
+ end
53
+ end