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.
- checksums.yaml +15 -0
- data/CHANGELOG.md +5 -1
- data/README.md +74 -53
- data/lib/happymapper/anonymous_mapper.rb +114 -0
- data/lib/happymapper/attribute.rb +20 -2
- data/lib/happymapper/element.rb +52 -2
- data/lib/happymapper/item.rb +89 -182
- data/lib/happymapper/supported_types.rb +140 -0
- data/lib/happymapper/text_node.rb +6 -1
- data/lib/happymapper/version.rb +3 -0
- data/lib/happymapper.rb +42 -22
- data/spec/attribute_default_value_spec.rb +50 -0
- data/spec/fixtures/default_namespace_combi.xml +2 -1
- data/spec/happymapper/attribute_spec.rb +12 -0
- data/spec/happymapper/element_spec.rb +9 -0
- data/spec/{happymapper_item_spec.rb → happymapper/item_spec.rb} +5 -5
- data/spec/happymapper/text_node_spec.rb +9 -0
- data/spec/happymapper_parse_spec.rb +87 -0
- data/spec/happymapper_spec.rb +9 -3
- data/spec/ignay_spec.rb +22 -22
- data/spec/inheritance_spec.rb +61 -0
- data/spec/parse_with_object_to_update_spec.rb +111 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/to_xml_spec.rb +200 -0
- data/spec/to_xml_with_namespaces_spec.rb +196 -0
- data/spec/wilcard_tag_name_spec.rb +96 -0
- data/spec/wrap_spec.rb +82 -0
- data/spec/xpath_spec.rb +60 -59
- metadata +34 -33
- data/TODO +0 -0
- data/spec/happymapper_attribute_spec.rb +0 -21
- data/spec/happymapper_element_spec.rb +0 -21
- data/spec/happymapper_generic_base_spec.rb +0 -92
- data/spec/happymapper_text_node_spec.rb +0 -21
- data/spec/happymapper_to_xml_namespaces_spec.rb +0 -196
- data/spec/happymapper_to_xml_spec.rb +0 -203
- data/spec/happymapper_wrap_spec.rb +0 -69
- 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
|
-
##
|
305
|
+
## Shared Functionality
|
282
306
|
|
283
|
-
### Inheritance
|
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
|
-
|
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
|
3
|
-
|
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
|
data/lib/happymapper/element.rb
CHANGED
@@ -1,3 +1,53 @@
|
|
1
1
|
module HappyMapper
|
2
|
-
class Element < Item
|
3
|
-
|
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
|