nokogiri-happymapper 0.6.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +64 -5
  3. data/README.md +296 -192
  4. data/lib/happymapper/anonymous_mapper.rb +46 -43
  5. data/lib/happymapper/attribute.rb +7 -5
  6. data/lib/happymapper/element.rb +19 -22
  7. data/lib/happymapper/item.rb +19 -21
  8. data/lib/happymapper/supported_types.rb +20 -28
  9. data/lib/happymapper/text_node.rb +4 -3
  10. data/lib/happymapper/version.rb +3 -1
  11. data/lib/happymapper.rb +336 -362
  12. data/lib/nokogiri-happymapper.rb +4 -0
  13. metadata +124 -105
  14. data/spec/attribute_default_value_spec.rb +0 -50
  15. data/spec/attributes_spec.rb +0 -36
  16. data/spec/fixtures/address.xml +0 -9
  17. data/spec/fixtures/ambigous_items.xml +0 -22
  18. data/spec/fixtures/analytics.xml +0 -61
  19. data/spec/fixtures/analytics_profile.xml +0 -127
  20. data/spec/fixtures/atom.xml +0 -19
  21. data/spec/fixtures/commit.xml +0 -52
  22. data/spec/fixtures/current_weather.xml +0 -89
  23. data/spec/fixtures/current_weather_missing_elements.xml +0 -18
  24. data/spec/fixtures/default_namespace_combi.xml +0 -6
  25. data/spec/fixtures/dictionary.xml +0 -20
  26. data/spec/fixtures/family_tree.xml +0 -21
  27. data/spec/fixtures/inagy.xml +0 -85
  28. data/spec/fixtures/lastfm.xml +0 -355
  29. data/spec/fixtures/multiple_namespaces.xml +0 -170
  30. data/spec/fixtures/multiple_primitives.xml +0 -5
  31. data/spec/fixtures/optional_attributes.xml +0 -6
  32. data/spec/fixtures/pita.xml +0 -133
  33. data/spec/fixtures/posts.xml +0 -23
  34. data/spec/fixtures/product_default_namespace.xml +0 -18
  35. data/spec/fixtures/product_no_namespace.xml +0 -10
  36. data/spec/fixtures/product_single_namespace.xml +0 -10
  37. data/spec/fixtures/quarters.xml +0 -19
  38. data/spec/fixtures/radar.xml +0 -21
  39. data/spec/fixtures/set_config_options.xml +0 -3
  40. data/spec/fixtures/statuses.xml +0 -422
  41. data/spec/fixtures/subclass_namespace.xml +0 -50
  42. data/spec/fixtures/unformatted_address.xml +0 -1
  43. data/spec/fixtures/wrapper.xml +0 -11
  44. data/spec/happymapper/attribute_spec.rb +0 -12
  45. data/spec/happymapper/element_spec.rb +0 -9
  46. data/spec/happymapper/item_spec.rb +0 -136
  47. data/spec/happymapper/text_node_spec.rb +0 -9
  48. data/spec/happymapper_parse_spec.rb +0 -113
  49. data/spec/happymapper_spec.rb +0 -1129
  50. data/spec/has_many_empty_array_spec.rb +0 -43
  51. data/spec/ignay_spec.rb +0 -95
  52. data/spec/inheritance_spec.rb +0 -107
  53. data/spec/mixed_namespaces_spec.rb +0 -61
  54. data/spec/parse_with_object_to_update_spec.rb +0 -111
  55. data/spec/spec_helper.rb +0 -7
  56. data/spec/to_xml_spec.rb +0 -201
  57. data/spec/to_xml_with_namespaces_spec.rb +0 -232
  58. data/spec/wilcard_tag_name_spec.rb +0 -96
  59. data/spec/wrap_spec.rb +0 -82
  60. data/spec/xpath_spec.rb +0 -89
data/README.md CHANGED
@@ -1,11 +1,14 @@
1
- HappyMapper
2
- ===========
1
+ # HappyMapper
3
2
 
4
- Happymapper allows you to parse XML data and convert it quickly and easily into ruby data structures.
3
+ Happymapper allows you to parse XML data and convert it quickly and easily into
4
+ ruby data structures.
5
5
 
6
6
  This project is a fork of the great work done first by
7
7
  [jnunemaker](https://github.com/jnunemaker/happymapper).
8
8
 
9
+ [![Gem Version](https://badge.fury.io/rb/nokogiri-happymapper.svg)](https://badge.fury.io/rb/nokogiri-happymapper)
10
+ [![Maintainability](https://api.codeclimate.com/v1/badges/491015f82bd2a45fd9d3/maintainability)](https://codeclimate.com/github/mvz/happymapper/maintainability)
11
+
9
12
  ## Major Differences
10
13
 
11
14
  * [Nokogiri](http://nokogiri.org/) support
@@ -13,40 +16,49 @@ This project is a fork of the great work done first by
13
16
  * Raw XML content parsing
14
17
  * `#to_xml` support utilizing the same HappyMapper tags
15
18
  * Numerous fixes for namespaces when using composition of classes
16
- * Fixes for instances of XML where a namespace is defined but no elements with that namespace are found
19
+ * Fixes for instances of XML where a namespace is defined but no elements
20
+ with that namespace are found
17
21
 
18
22
  ## Installation
19
23
 
20
- ### [Rubygems](https://rubyygems.org/gems/nokogiri-happymapper)
24
+ Install via rubygems:
21
25
 
22
26
  $ gem install nokogiri-happymapper
23
27
 
24
- ### [Bundler](http://gembundler.com/)
25
- Add the `nokogiri-happymapper` gem to your project's `Gemfile`.
28
+ Or add the `nokogiri-happymapper` gem to your project's `Gemfile`.
29
+
30
+ gem 'nokogiri-happymapper', require: 'happymapper'
31
+
32
+ You can now also require `nokogiri-happymapper` directly.
26
33
 
27
- gem 'nokogiri-happymapper', :require => 'happymapper'
34
+ gem 'nokogiri-happymapper'
28
35
 
29
- Run the bundler command to install the gem:
36
+ Run Bundler to install the gem:
30
37
 
31
38
  $ bundle install
32
39
 
33
- # Examples
40
+ ## Examples
34
41
 
35
- Let's start with a simple example to get our feet wet. Here we have a simple example of XML that defines some address information:
42
+ Let's start with a simple example to get our feet wet. Here we have a simple
43
+ example of XML that defines some address information:
36
44
 
37
- <address>
38
- <street>Milchstrasse</street>
39
- <housenumber>23</housenumber>
40
- <postcode>26131</postcode>
41
- <city>Oldenburg</city>
42
- <country code="de">Germany</country>
43
- </address>
45
+ ```xml
46
+ <address>
47
+ <street>Milchstrasse</street>
48
+ <housenumber>23</housenumber>
49
+ <postcode>26131</postcode>
50
+ <city>Oldenburg</city>
51
+ <country code="de">Germany</country>
52
+ </address>
53
+ ```
44
54
 
45
- Happymapper provides support for simple, zero configuration parsing as well as the ability to model the XML content in classes.
55
+ Happymapper provides support for simple, zero configuration parsing as well as
56
+ the ability to model the XML content in classes.
46
57
 
47
- ## HappyMapper.parse(XML)
58
+ ### HappyMapper.parse(XML)
48
59
 
49
- With no classes or configuration you can parse the example XML with little effort:
60
+ With no classes or configuration you can parse the example XML with little
61
+ effort:
50
62
 
51
63
  ```ruby
52
64
  address = HappyMapper.parse(ADDRESS_XML_DATA)
@@ -60,11 +72,14 @@ address.country.content # => Germany
60
72
 
61
73
  It is important to be aware that this no configuration parsing is limited in capacity:
62
74
 
63
- * All element names are converted to accessor methods with [underscorized](http://rubydoc.info/gems/activesupport/ActiveSupport/Inflector:underscore) names
75
+ * All element names are converted to accessor methods with
76
+ [underscorized](http://rubydoc.info/gems/activesupport/ActiveSupport/Inflector:underscore)
77
+ names
64
78
  * 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.
79
+ * Determining if there is just one or multiple child elements is hard, so it
80
+ assumes it is one until it finds another with the same name.
66
81
 
67
- ## Address.parse(XML)
82
+ ### Address.parse(XML)
68
83
 
69
84
  Happymapper will let you easily model this information as a class:
70
85
 
@@ -75,88 +90,115 @@ class Address
75
90
  include HappyMapper
76
91
 
77
92
  tag 'address'
78
- element :street, String, :tag => 'street'
79
- element :postcode, String, :tag => 'postcode'
80
- element :housenumber, Integer, :tag => 'housenumber'
81
- element :city, String, :tag => 'city'
82
- element :country, String, :tag => 'country'
93
+ element :street, String, tag: 'street'
94
+ element :postcode, String, tag: 'postcode'
95
+ element :housenumber, Integer, tag: 'housenumber'
96
+ element :city, String, tag: 'city'
97
+ element :country, String, tag: 'country'
83
98
  end
84
99
  ```
85
100
 
86
- To make a class HappyMapper compatible you simply `include HappyMapper` within the class definition. This takes care of all the work of defining all the speciality methods and magic you need to get running. As you can see we immediately start using these methods.
101
+ To make a class HappyMapper compatible you simply `include HappyMapper` within
102
+ the class definition. This takes care of all the work of defining all the
103
+ speciality methods and magic you need to get running. As you can see we
104
+ immediately start using these methods.
87
105
 
88
106
  * `tag` matches the name of the XML tag name 'address'.
89
107
 
90
- * `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'`).
108
+ * `element` defines accessor methods for the specified symbol
109
+ (e.g. `:street`,`:housenumber`) that will return the class type
110
+ (e.g. `String`,`Integer`) of the XML tag specified
111
+ (e.g. `tag: 'street'`, `tag: 'housenumber'`).
91
112
 
92
- 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:
113
+ When you define an element with an accessor with the same name as the tag, this
114
+ is the case for all the examples above, you can omit the `:tag`. These two
115
+ element declaration are equivalent to each other:
93
116
 
94
117
  ```ruby
95
- element :street, String, :tag => 'street'
118
+ element :street, String, tag: 'street'
96
119
  element :street, String
97
120
  ```
98
121
 
99
- Including the additional tag element is not going to hurt anything and in some cases will make it absolutely clear how these elements map to the XML. However, once you know this rule, it is hard not to want to save yourself the keystrokes.
122
+ Including the additional tag element is not going to hurt anything and in some
123
+ cases will make it absolutely clear how these elements map to the XML. However,
124
+ once you know this rule, it is hard not to want to save yourself the
125
+ keystrokes.
100
126
 
101
127
  Instead of `element` you may also use `has_one`:
102
128
 
103
129
  ```ruby
104
- element :street, String, :tag => 'street'
130
+ element :street, String, tag: 'street'
105
131
  element :street, String
106
132
  has_one :street, String
107
133
  ```
108
134
 
109
135
  These three statements are equivalent to each other.
110
136
 
111
- ## Parsing
137
+ ### Parsing
112
138
 
113
- With the mapping of the address XML articulated in our Address class it is time to parse the data:
139
+ With the mapping of the address XML articulated in our Address class it is time
140
+ to parse the data:
114
141
 
115
142
  ```ruby
116
- address = Address.parse(ADDRESS_XML_DATA, :single => true)
143
+ address = Address.parse(ADDRESS_XML_DATA, single: true)
117
144
  puts address.street
118
145
  ```
119
146
 
120
- 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.
147
+ Assuming that the constant `ADDRESS_XML_DATA` contains a string representation
148
+ of the address XML data this is fairly straight-forward save for the `parse`
149
+ method.
121
150
 
122
- The `parse` method, like `tag` and `element` are all added when you included HappyMapper in the class. Parse is a wonderful, magical place that converts all these declarations that you have made into the data structure you are about to know and love.
151
+ The `parse` method, like `tag` and `element` are all added when you included
152
+ HappyMapper in the class. Parse is a wonderful, magical place that converts all
153
+ these declarations that you have made into the data structure you are about to
154
+ know and love.
123
155
 
124
- But what about the `:single => true`? Right, that is because by default when your object is all done parsing it will be an array. In this case an array with one element, but an array none the less. So the following are equivalent to each other:
156
+ But what about the `single: true`? Right, that is because by default when
157
+ your object is all done parsing it will be an array. In this case an array with
158
+ one element, but an array none the less. So the following are equivalent to
159
+ each other:
125
160
 
126
161
  ```ruby
127
162
  address = Address.parse(ADDRESS_XML_DATA).first
128
- address = Address.parse(ADDRESS_XML_DATA, :single => true)
163
+ address = Address.parse(ADDRESS_XML_DATA, single: true)
129
164
  ```
130
165
 
131
- The first one returns an array and we return the first instance, the second will do that work for us inside of parse.
166
+ The first one returns an array and we return the first instance, the second
167
+ will do that work for us inside of parse.
132
168
 
133
- ## Multiple Elements Mapping
169
+ ### Multiple Elements Mapping
134
170
 
135
- What if our address XML was a little different, perhaps we allowed multiple streets:
171
+ What if our address XML was a little different, perhaps we allowed multiple
172
+ streets:
136
173
 
137
- <address>
138
- <street>Milchstrasse</street>
139
- <street>Another Street</street>
140
- <housenumber>23</housenumber>
141
- <postcode>26131</postcode>
142
- <city>Oldenburg</city>
143
- <country code="de">Germany</country>
144
- </address>
174
+ ```xml
175
+ <address>
176
+ <street>Milchstrasse</street>
177
+ <street>Another Street</street>
178
+ <housenumber>23</housenumber>
179
+ <postcode>26131</postcode>
180
+ <city>Oldenburg</city>
181
+ <country code="de">Germany</country>
182
+ </address>
183
+ ```
145
184
 
146
- Similar to `element` or `has_one`, the declaration for when you have multiple elements you simply use:
185
+ Similar to `element` or `has_one`, the declaration for when you have multiple
186
+ elements you simply use:
147
187
 
148
188
  ```ruby
149
- has_many :streets, String, :tag => 'street'
189
+ has_many :streets, String, tag: 'street'
150
190
  ```
151
191
 
152
192
  Your resulting `streets` method will now return an array.
153
193
 
154
194
  ```ruby
155
- address = Address.parse(ADDRESS_XML_DATA, :single => true)
195
+ address = Address.parse(ADDRESS_XML_DATA, single: true)
156
196
  puts address.streets.join('\n')
157
197
  ```
158
198
 
159
- 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.
199
+ Imagine that you have to write `streets.join('\n')` for the rest of eternity
200
+ throughout your code. It would be a nightmare and one that you could avoid by
201
+ creating your own convenience method.
160
202
 
161
203
  ```ruby
162
204
  require 'happymapper'
@@ -172,52 +214,60 @@ class Address
172
214
  @streets.join('\n')
173
215
  end
174
216
 
175
- element :postcode, String, :tag => 'postcode'
176
- element :housenumber, String, :tag => 'housenumber'
177
- element :city, String, :tag => 'city'
178
- element :country, String, :tag => 'country'
217
+ element :postcode, String, tag: 'postcode'
218
+ element :housenumber, String, tag: 'housenumber'
219
+ element :city, String, tag: 'city'
220
+ element :country, String, tag: 'country'
179
221
  end
180
222
  ```
181
223
 
182
- Now when we call the method `streets` we get a single value, but we still have the instance variable `@streets` if we ever need to the values as an array.
224
+ Now when we call the method `streets` we get a single value, but we still have
225
+ the instance variable `@streets` if we ever need to the values as an array.
183
226
 
184
227
 
185
- ## Attribute Mapping
228
+ ### Attribute Mapping
186
229
 
187
- <address location='home'>
188
- <street>Milchstrasse</street>
189
- <street>Another Street</street>
190
- <housenumber>23</housenumber>
191
- <postcode>26131</postcode>
192
- <city>Oldenburg</city>
193
- <country code="de">Germany</country>
194
- </address>
230
+ ```xml
231
+ <address location='home'>
232
+ <street>Milchstrasse</street>
233
+ <street>Another Street</street>
234
+ <housenumber>23</housenumber>
235
+ <postcode>26131</postcode>
236
+ <city>Oldenburg</city>
237
+ <country code="de">Germany</country>
238
+ </address>
239
+ ```
195
240
 
196
241
  Attributes are absolutely the same as `element` or `has_many`
197
242
 
198
243
  ```ruby
199
- attribute :location, String, :tag => 'location
244
+ attribute :location, String, tag: 'location
200
245
  ```
201
246
 
202
- Again, you can omit the tag if the attribute accessor symbol matches the name of the attribute.
247
+ Again, you can omit the tag if the attribute accessor symbol matches the name
248
+ of the attribute.
203
249
 
250
+ #### Attributes On Empty Child Elements
204
251
 
205
- ### Attributes On Empty Child Elements
206
-
207
- <feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
208
- <id>tag:all-the-episodes.heroku.com,2005:/tv_shows</id>
209
- <link rel="alternate" type="text/html" href="http://all-the-episodes.heroku.com"/>
210
- <link rel="self" type="application/atom+xml" href="http://all-the-episodes.heroku.com/tv_shows.atom"/>
211
- <title>TV Shows</title>
212
- <updated>2011-07-10T06:52:27Z</updated>
213
- </feed>
252
+ ```xml
253
+ <feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
254
+ <id>tag:all-the-episodes.heroku.com,2005:/tv_shows</id>
255
+ <link rel="alternate" type="text/html" href="http://all-the-episodes.heroku.com"/>
256
+ <link rel="self" type="application/atom+xml"
257
+ href="http://all-the-episodes.heroku.com/tv_shows.atom"/>
258
+ <title>TV Shows</title>
259
+ <updated>2011-07-10T06:52:27Z</updated>
260
+ </feed>
261
+ ```
214
262
 
215
- In this case you would need to map an element to a new `Link` class just to access `<link>`s attributes, except that there is an alternate syntax. Instead of
263
+ In this case you would need to map an element to a new `Link` class just to
264
+ access `<link>`s attributes, except that there is an alternate syntax. Instead
265
+ of
216
266
 
217
267
  ```ruby
218
268
  class Feed
219
269
  # ....
220
- has_many :links, Link, :tag => 'link', :xpath => '.'
270
+ has_many :links, Link, tag: 'link', xpath: '.'
221
271
  end
222
272
 
223
273
  class Link
@@ -232,17 +282,25 @@ end
232
282
  You can drop the `Link` class and simply replace the `has_many` on `Feed` with
233
283
 
234
284
  ```ruby
235
- element :link, String, :single => false, :attributes => { :rel => String, :type => String, :href => String }
285
+ element :link, String, single: false, attributes: { rel: String, type: String, href: String }
236
286
  ```
237
287
 
238
- As there is no content, the type given for `:link` (`String` above) is irrelevant, but `nil` won't work and other types may try to perform typecasting and fail. You can omit the :single => false for elements that only occur once within their parent.
288
+ As there is no content, the type given for `:link` (`String` above) is
289
+ irrelevant, but `nil` won't work and other types may try to perform typecasting
290
+ and fail. You can omit the single: false for elements that only occur once
291
+ within their parent.
239
292
 
240
- This syntax is most appropriate for elements that (a) have attributes but no content and (b) only occur at only one level of the heirarchy. If `<feed>` contained another element that also contained a `<link>` (as atom feeds generally do) it would be DRY-er to use the first syntax, i.e. with a separate `Link` class.
293
+ This syntax is most appropriate for elements that (a) have attributes but no
294
+ content and (b) only occur at only one level of the heirarchy. If `<feed>`
295
+ contained another element that also contained a `<link>` (as atom feeds
296
+ generally do) it would be DRY-er to use the first syntax, i.e. with a separate
297
+ `Link` class.
241
298
 
242
299
 
243
- ## Class composition (and Text Node)
300
+ ### Class composition (and Text Node)
244
301
 
245
- Our address has a country and that country element has a code. Up until this point we neglected it as we declared a `country` as being a `String`.
302
+ Our address has a country and that country element has a code. Up until this
303
+ point we neglected it as we declared a `country` as being a `String`.
246
304
 
247
305
  <address location='home'>
248
306
  <street>Milchstrasse</street>
@@ -253,7 +311,8 @@ Our address has a country and that country element has a code. Up until this poi
253
311
  <country code="de">Germany</country>
254
312
  </address>
255
313
 
256
- Well if we only going to parse country, on it's own, we would likely create a class mapping for it.
314
+ Well if we only going to parse country, on it's own, we would likely create a
315
+ class mapping for it.
257
316
 
258
317
  ```ruby
259
318
  class Country
@@ -270,7 +329,8 @@ We are utilizing an `attribute` declaration and a new declaration called `conten
270
329
 
271
330
  * `content` is used when you want the text contained within the element
272
331
 
273
- Awesome, now if we were to redeclare our `Address` class we would use our new `Country` class.
332
+ Awesome, now if we were to redeclare our `Address` class we would use our new
333
+ `Country` class.
274
334
 
275
335
  ```ruby
276
336
  class Address
@@ -278,64 +338,77 @@ class Address
278
338
 
279
339
  tag 'address'
280
340
 
281
- has_many :streets, String, :tag => 'street'
341
+ has_many :streets, String, tag: 'street'
282
342
 
283
343
  def streets
284
344
  @streets.join('\n')
285
345
  end
286
346
 
287
- element :postcode, String, :tag => 'postcode'
288
- element :housenumber, String, :tag => 'housenumber'
289
- element :city, String, :tag => 'city'
290
- element :country, Country, :tag => 'country'
347
+ element :postcode, String, tag: 'postcode'
348
+ element :housenumber, String, tag: 'housenumber'
349
+ element :city, String, tag: 'city'
350
+ element :country, Country, tag: 'country'
291
351
  end
292
352
  ```
293
353
 
294
- 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.
354
+ Instead of `String`, `Boolean`, or `Integer` we say that it is a `Country` and
355
+ HappyMapper takes care of the details of continuing the XML mapping through the
356
+ country element.
295
357
 
296
358
  ```ruby
297
- address = Address.parse(ADDRESS_XML_DATA, :single => true)
359
+ address = Address.parse(ADDRESS_XML_DATA, single: true)
298
360
  puts address.country.code
299
361
  ```
300
362
 
301
- 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.
302
-
303
- ## Custom XPATH
304
-
305
- ### Has One, Has Many
306
-
307
- Getting to elements deep down within your XML can be a little more work if you did not have xpath support. Consider the following example:
308
-
309
- <media>
310
- <gallery>
311
- <title href="htttp://fishlovers.org/friends">Friends Who Like Fish</title>
312
- <picture>
313
- <name>Burtie Sanchez</name>
314
- <img>burtie01.png</img>
315
- </picture>
316
- </gallery>
317
- <picture>
318
- <name>Unsorted Photo</name>
319
- <img>bestfriends.png</img>
320
- </picture>
321
- </media>
363
+ A quick note, in the above example we used the constant `Country`. We could
364
+ have used `'Country'`. The nice part of using the latter declaration, enclosed
365
+ in quotes, is that you do not have to define your class before this class. So
366
+ Country and Address can live in separate files and as long as both constants
367
+ are available when it comes time to parse you are golden.
368
+
369
+ ### Custom XPATH
370
+
371
+ #### Has One, Has Many
372
+
373
+ Getting to elements deep down within your XML can be a little more work if you
374
+ did not have xpath support. Consider the following example:
375
+
376
+ ```xml
377
+ <media>
378
+ <gallery>
379
+ <title href="htttp://fishlovers.org/friends">Friends Who Like Fish</title>
380
+ <picture>
381
+ <name>Burtie Sanchez</name>
382
+ <img>burtie01.png</img>
383
+ </picture>
384
+ </gallery>
385
+ <picture>
386
+ <name>Unsorted Photo</name>
387
+ <img>bestfriends.png</img>
388
+ </picture>
389
+ </media>
390
+ ```
322
391
 
323
- You may want to map the sub-elements contained buried in the 'gallery' as top level items in the media. Traditionally you could use class composition to accomplish this task, however, using the xpath attribute you have the ability to shortcut some of that work.
392
+ You may want to map the sub-elements contained buried in the 'gallery' as top
393
+ level items in the media. Traditionally you could use class composition to
394
+ accomplish this task, however, using the xpath attribute you have the ability
395
+ to shortcut some of that work.
324
396
 
325
397
  ```ruby
326
398
  class Media
327
399
  include HappyMapper
328
400
 
329
- has_one :title, String, :xpath => 'gallery/title'
330
- has_one :link, String, :xpath => 'gallery/title/@href'
401
+ has_one :title, String, xpath: 'gallery/title'
402
+ has_one :link, String, xpath: 'gallery/title/@href'
331
403
  end
332
404
  ```
333
405
 
334
- ## Shared Functionality
406
+ ### Shared Functionality
335
407
 
336
- ### Inheritance Approach
408
+ #### Inheritance Approach
337
409
 
338
- While mapping XML to objects you may arrive at a point where you have two or more very similar structures.
410
+ While mapping XML to objects you may arrive at a point where you have two or
411
+ more very similar structures.
339
412
 
340
413
  ```ruby
341
414
  class Article
@@ -361,7 +434,9 @@ class Gallery
361
434
  end
362
435
  ```
363
436
 
364
- In this example there are definitely two similarities between our two pieces of content. So much so that you might be included to create an inheritance structure to save yourself some keystrokes.
437
+ In this example there are definitely two similarities between our two pieces of
438
+ content. So much so that you might be included to create an inheritance
439
+ structure to save yourself some keystrokes.
365
440
 
366
441
  ```ruby
367
442
  class Content
@@ -385,7 +460,7 @@ class Gallery < Content
385
460
  end
386
461
  ```
387
462
 
388
- ### Module Mixins Approach
463
+ #### Module Mixins Approach
389
464
 
390
465
  You can also solve the above problem through mixins.
391
466
 
@@ -417,25 +492,31 @@ class Gallery
417
492
  end
418
493
  ```
419
494
 
420
- Here, when we include `Content` in both of these classes the module method `#included` is called and our class is given as a parameter. So we take that opportunity to do some surgery and define our happymapper elements as well as any other methods that may rely on those instance variables that come along in the package.
421
-
422
-
423
- ## Filtering with XPATH (non-greedy)
424
-
425
- I ran into a case where I wanted to capture all the pictures that were directly under media, but not the ones contained within a gallery.
426
-
427
- <media>
428
- <gallery>
429
- <picture>
430
- <name>Burtie Sanchez</name>
431
- <img>burtie01.png</img>
432
- </picture>
433
- </gallery>
434
- <picture>
435
- <name>Unsorted Photo</name>
436
- <img>bestfriends.png</img>
437
- </picture>
438
- </media>
495
+ Here, when we include `Content` in both of these classes the module method
496
+ `#included` is called and our class is given as a parameter. So we take that
497
+ opportunity to do some surgery and define our happymapper elements as well as
498
+ any other methods that may rely on those instance variables that come along in
499
+ the package.
500
+
501
+ ### Filtering with XPATH (non-greedy)
502
+
503
+ I ran into a case where I wanted to capture all the pictures that were directly
504
+ under media, but not the ones contained within a gallery.
505
+
506
+ ```xml
507
+ <media>
508
+ <gallery>
509
+ <picture>
510
+ <name>Burtie Sanchez</name>
511
+ <img>burtie01.png</img>
512
+ </picture>
513
+ </gallery>
514
+ <picture>
515
+ <name>Unsorted Photo</name>
516
+ <img>bestfriends.png</img>
517
+ </picture>
518
+ </media>
519
+ ```
439
520
 
440
521
  The following `Media` class is where I started:
441
522
 
@@ -445,15 +526,15 @@ require 'happymapper'
445
526
  class Media
446
527
  include HappyMapper
447
528
 
448
- has_many :galleries, Gallery, :tag => 'gallery'
449
- has_many :pictures, Picture, :tag => 'picture'
529
+ has_many :galleries, Gallery, tag: 'gallery'
530
+ has_many :pictures, Picture, tag: 'picture'
450
531
  end
451
532
  ```
452
533
 
453
534
  However when I parsed the media xml the number of pictures returned to me was 2, not 1.
454
535
 
455
536
  ```ruby
456
- pictures = Media.parse(MEDIA_XML,:single => true).pictures
537
+ pictures = Media.parse(MEDIA_XML,single: true).pictures
457
538
  pictures.length.should == 1 # => Failed Expectation
458
539
  ```
459
540
 
@@ -466,16 +547,16 @@ To limit an element from being greedy and only finding elements at the
466
547
  level of the current node you can specify an XPATH.
467
548
 
468
549
  ```ruby
469
- has_many :pictures, Picture, :tag => 'picture', :xpath => '.'
550
+ has_many :pictures, Picture, tag: 'picture', xpath: '.'
470
551
  ```
471
552
 
472
553
  `.` states that we are only interested in pictures that can be found directly
473
554
  under the current node. So when we parse again we will have only our one element.
474
555
 
556
+ ### Namespaces
475
557
 
476
- ## Namespaces
477
-
478
- Obviously your XML and these trivial examples are easy to map and parse because they lack the treacherous namespaces that befall most XML files.
558
+ Obviously your XML and these trivial examples are easy to map and parse because
559
+ they lack the treacherous namespaces that befall most XML files.
479
560
 
480
561
  Perhaps our `address` XML is really swarming with namespaces:
481
562
 
@@ -488,7 +569,10 @@ Perhaps our `address` XML is really swarming with namespaces:
488
569
  <prefix:country code="de">Germany</prefix:country>
489
570
  </prefix:address>
490
571
 
491
- Here again is our address example with a made up namespace called `prefix` that comes direct to use from unicornland, a very magical place indeed. Well we are going to have to do some work on our class definition and that simply adding this one liner to the `Address` class:
572
+ Here again is our address example with a made up namespace called `prefix` that
573
+ comes direct to use from unicornland, a very magical place indeed. Well we are
574
+ going to have to do some work on our class definition and that simply adding
575
+ this one liner to the `Address` class:
492
576
 
493
577
  ```ruby
494
578
  class Address
@@ -500,90 +584,110 @@ class Address
500
584
  end
501
585
  ```
502
586
 
503
- Of course, if that is too easy for you, you can append a `:namespace => 'prefix` to every one of the elements that you defined.
587
+ Of course, if that is too easy for you, you can append a `namespace: 'prefix`
588
+ to every one of the elements that you defined.
504
589
 
505
590
  ```ruby
506
- has_many :street, String, :tag => 'street', :namespace => 'prefix'
507
- element :postcode, String, :tag => 'postcode', :namespace => 'prefix'
508
- element :housenumber, String, :tag => 'housenumber', :namespace => 'prefix'
509
- element :city, String, :tag => 'city', :namespace => 'prefix'
510
- element :country, Country, :tag => 'country', :namespace => 'prefix'
591
+ has_many :street, String, tag: 'street', namespace: 'prefix'
592
+ element :postcode, String, tag: 'postcode', namespace: 'prefix'
593
+ element :housenumber, String, tag: 'housenumber', namespace: 'prefix'
594
+ element :city, String, tag: 'city', namespace: 'prefix'
595
+ element :country, Country, tag: 'country', namespace: 'prefix'
511
596
  ```
512
597
 
513
- 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'`.
598
+ I definitely recommend the former, as it saves you a whole hell of lot of
599
+ typing. However, there are times when appending a namespace to an element
600
+ declaration is important and that is when it has a different namespace than
601
+ `namespace 'prefix'`.
514
602
 
515
603
  Imagine that our `country` actually belonged to a completely different namespace.
516
604
 
517
- <prefix:address location='home' xmlns:prefix="http://www.unicornland.com/prefix"
518
- xmlns:different="http://www.trollcountry.com/different">
519
- <prefix:street>Milchstrasse</prefix:street>
520
- <prefix:street>Another Street</prefix:street>
521
- <prefix:housenumber>23</prefix:housenumber>
522
- <prefix:postcode>26131</prefix:postcode>
523
- <prefix:city>Oldenburg</prefix:city>
524
- <different:country code="de">Germany</different:country>
525
- </prefix:address>
605
+ ```xml
606
+ <prefix:address location='home'
607
+ xmlns:prefix="http://www.unicornland.com/prefix"
608
+ xmlns:different="http://www.trollcountry.com/different">
609
+ <prefix:street>Milchstrasse</prefix:street>
610
+ <prefix:street>Another Street</prefix:street>
611
+ <prefix:housenumber>23</prefix:housenumber>
612
+ <prefix:postcode>26131</prefix:postcode>
613
+ <prefix:city>Oldenburg</prefix:city>
614
+ <different:country code="de">Germany</different:country>
615
+ </prefix:address>
616
+ ```
526
617
 
527
618
  Well we would need to specify that namespace:
528
619
 
529
620
  ```ruby
530
- element :country, Country, :tag => 'country', :namespace => 'different'
621
+ element :country, Country, tag: 'country', namespace: 'different'
531
622
  ```
532
623
 
533
624
  With that we should be able to parse as we once did.
534
625
 
535
- ## Large Datasets (in_groups_of)
626
+ ### Large Datasets (`:in_groups_of`)
536
627
 
537
- When dealing with large sets of XML that simply cannot or should not be placed into memory the objects can be handled in groups through the `:in_groups_of` parameter.
628
+ When dealing with large sets of XML that simply cannot or should not be placed
629
+ into memory the objects can be handled in groups through the `:in_groups_of`
630
+ parameter.
538
631
 
539
632
  ```ruby
540
- Address.parse(LARGE_ADDRESS_XML_DATA,:in_groups_of => 5) do |group|
633
+ Address.parse(LARGE_ADDRESS_XML_DATA,in_groups_of: 5) do |group|
541
634
  puts address.streets
542
635
  end
543
636
  ```
544
637
 
545
- This trivial block will parse the large set of XML data and in groups of 5 addresses at a time display the streets.
638
+ This trivial block will parse the large set of XML data and in groups of 5
639
+ addresses at a time display the streets.
546
640
 
547
- ## Saving to XML
641
+ ### Saving to XML
548
642
 
549
- Saving a class to XML is as easy as calling `#to_xml`. The end result will be the current state of your object represented as xml. Let's cover some details that are sometimes necessary and features present to make your life easier.
643
+ Saving a class to XML is as easy as calling `#to_xml`. The end result will be
644
+ the current state of your object represented as xml. Let's cover some details
645
+ that are sometimes necessary and features present to make your life easier.
550
646
 
551
647
 
552
- ### :on_save
648
+ #### :on_save
553
649
 
554
- 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:
650
+ When you are saving data to xml it is often important to change or manipulate
651
+ data to a particular format. For example, a time object:
555
652
 
556
653
  ```ruby
557
- has_one :published_time, Time, :on_save => lambda {|time| time.strftime("%H:%M:%S") if time }
654
+ has_one :published_time, Time, on_save: lambda {|time| time.strftime("%H:%M:%S") if time }
558
655
  ```
559
656
 
560
- Here we add the options `:on_save` and specify a lambda which will be executed on the method call to `:published_time`.
657
+ Here we add the options `:on_save` and specify a lambda which will be executed
658
+ on the method call to `:published_time`.
561
659
 
562
- ### :state_when_nil
660
+ #### :state_when_nil
563
661
 
564
- 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.
662
+ When an element contains a nil value, or perhaps the result of the :on_save
663
+ lambda correctly results in a nil value you will be happy that the element will
664
+ not appear in the resulting XML. However, there are time when you will want to
665
+ see that element and that's when `:state_when_nil` is there for you.
565
666
 
566
667
  ```ruby
567
- has_one :favorite_color, String, :state_when_nil => true
668
+ has_one :favorite_color, String, state_when_nil: true
568
669
  ```
569
670
 
570
- The resulting XML will include the 'favorite_color' element even if the favorite color has not been specified.
671
+ The resulting XML will include the 'favorite_color' element even if the
672
+ favorite color has not been specified.
571
673
 
572
- ### :read_only
674
+ #### :read_only
573
675
 
574
676
  When an element, attribute, or text node is a value that you have no interest in
575
677
  saving to XML, you can ensure that takes place by stating that it is `read only`.
576
678
 
577
679
  ```ruby
578
- has_one :modified, Boolean, :read_only => true
579
- attribute :temporary, Boolean, :read_only => true
680
+ has_one :modified, Boolean, read_only: true
681
+ attribute :temporary, Boolean, read_only: true
580
682
  ```
581
683
 
582
684
  This is useful if perhaps the incoming XML is different than the out-going XML.
583
685
 
584
- ### namespaces
686
+ #### namespaces
585
687
 
586
- Parsing the XML to objects only required you to simply specify the prefix of the namespace you wanted to parse, when you persist to xml you will need to define your namespaces so that they are correctly captured.
688
+ Parsing the XML to objects only required you to simply specify the prefix of
689
+ the namespace you wanted to parse, when you persist to xml you will need to
690
+ define your namespaces so that they are correctly captured.
587
691
 
588
692
  ```ruby
589
693
  class Address
@@ -599,7 +703,7 @@ class Address
599
703
  element :postcode, String
600
704
  element :housenumber, String
601
705
  element :city, String
602
- element :country, Country, :tag => 'country', :namespace => 'different'
706
+ element :country, Country, tag: 'country', namespace: 'different'
603
707
 
604
708
  end
605
- ```
709
+ ```