roxml 2.4.3 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/History.txt +54 -0
  2. data/Manifest.txt +9 -6
  3. data/README.rdoc +24 -17
  4. data/Rakefile +2 -1
  5. data/TODO +30 -31
  6. data/examples/active_record.rb +69 -0
  7. data/examples/amazon.rb +1 -1
  8. data/examples/current_weather.rb +1 -1
  9. data/examples/posts.rb +8 -8
  10. data/examples/twitter.rb +2 -2
  11. data/examples/xml/active_record.xml +70 -0
  12. data/lib/roxml.rb +174 -174
  13. data/lib/roxml/definition.rb +165 -89
  14. data/lib/roxml/extensions/deprecation.rb +5 -0
  15. data/lib/roxml/extensions/string/conversions.rb +2 -3
  16. data/lib/roxml/hash_definition.rb +26 -25
  17. data/lib/roxml/xml.rb +15 -6
  18. data/lib/roxml/xml/parsers/libxml.rb +9 -6
  19. data/lib/roxml/xml/parsers/rexml.rb +1 -1
  20. data/lib/roxml/xml/references.rb +14 -17
  21. data/roxml.gemspec +8 -5
  22. data/spec/definition_spec.rb +563 -0
  23. data/spec/examples/active_record_spec.rb +43 -0
  24. data/spec/roxml_spec.rb +372 -0
  25. data/spec/shared_specs.rb +15 -0
  26. data/spec/spec_helper.rb +21 -4
  27. data/spec/string_spec.rb +15 -0
  28. data/spec/xml/parser_spec.rb +22 -0
  29. data/test/fixtures/book_valid.xml +1 -1
  30. data/test/fixtures/person_with_guarded_mothers.xml +3 -3
  31. data/test/mocks/mocks.rb +57 -45
  32. data/test/unit/definition_test.rb +161 -12
  33. data/test/unit/deprecations_test.rb +97 -0
  34. data/test/unit/to_xml_test.rb +30 -1
  35. data/test/unit/xml_bool_test.rb +15 -3
  36. data/test/unit/xml_construct_test.rb +6 -6
  37. data/test/unit/xml_hash_test.rb +18 -0
  38. data/test/unit/xml_initialize_test.rb +6 -3
  39. data/test/unit/xml_object_test.rb +66 -5
  40. data/test/unit/xml_text_test.rb +3 -0
  41. metadata +23 -15
  42. data/test/unit/array_test.rb +0 -16
  43. data/test/unit/freeze_test.rb +0 -71
  44. data/test/unit/inheritance_test.rb +0 -63
  45. data/test/unit/overriden_output_test.rb +0 -33
  46. data/test/unit/roxml_test.rb +0 -60
  47. data/test/unit/string_test.rb +0 -11
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe String do
4
+ describe "#to_latin" do
5
+ it "should be accessible" do
6
+ "".should respond_to(:to_latin)
7
+ end
8
+ end
9
+
10
+ describe "#to_utf" do
11
+ it "should be accessible" do
12
+ "".should respond_to(:to_utf)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe ROXML::XML::Parser do
4
+ before do
5
+ # quiet the error handler
6
+ ROXML::XML::Error.reset_handler
7
+ end
8
+
9
+ it "should raise on malformed xml" do
10
+ proc { Book.from_xml(fixture(:book_malformed)) }.should raise_error(ROXML::XML::Error)
11
+ end
12
+
13
+ it "should escape invalid characters on output to text node" do
14
+ ROXML::XML::Node.new("entities", " < > ' \" & ").to_s.should == "<entities> &lt; &gt; ' \" &amp; </entities>"
15
+ end
16
+
17
+ it "should esape invalid characters for attribute name" do
18
+ node = ROXML::XML::Node.new("attr_holder")
19
+ node.attributes["entities"] = "\"'<>&"
20
+ node.to_s.should == %{<attr_holder entities="&quot;'&lt;&gt;&amp;"/>}
21
+ end
22
+ end
@@ -1,5 +1,5 @@
1
1
  <book ISBN="0201710897">
2
2
  <title>The PickAxe</title>
3
3
  <description><![CDATA[Probably the best Ruby book out there]]></description>
4
- <author>David Thomas, Andrew Hunt, Dave Thomas</author>
4
+ <author>David Thomas, Andrew Hunt &amp; Dave Thomas</author>
5
5
  <pagecount>357</pagecount></book>
@@ -1,11 +1,11 @@
1
1
  <person>
2
- <name>Ben Franklin</name>
2
+ <name>Ben &quot;Benji&quot; Franklin</name>
3
3
  <mother>
4
4
  <person>
5
- <name>Abiah Folger</name>
5
+ <name>Abiah &apos;Abby&apos; Folger</name>
6
6
  <mother>
7
7
  <person>
8
- <name>Madeup Mother</name>
8
+ <name>Madeup Mother &lt; the third &gt;</name>
9
9
  </person>
10
10
  </mother>
11
11
  </person>
@@ -23,23 +23,23 @@ end
23
23
  class Contributor
24
24
  include ROXML
25
25
 
26
- xml_reader :role, :attr
26
+ xml_reader :role, :from => :attr
27
27
  xml_reader :name
28
28
  end
29
29
 
30
30
  class WriteableContributor
31
31
  include ROXML
32
32
 
33
- xml_accessor :role, :attr
33
+ xml_accessor :role, :from => :attr
34
34
  xml_accessor :name
35
35
  end
36
36
 
37
37
  class Book
38
38
  include ROXML
39
39
 
40
- xml_accessor :isbn, :attr => 'ISBN'
40
+ xml_accessor :isbn, :from => '@ISBN'
41
41
  xml_reader :title
42
- xml_reader :description, :as => :cdata
42
+ xml_reader :description, :cdata => true
43
43
  xml_reader :author
44
44
  xml_accessor :pages, :text => 'pagecount', :as => Integer
45
45
  end
@@ -47,7 +47,7 @@ end
47
47
  class BookWithRequired
48
48
  include ROXML
49
49
 
50
- xml_accessor :isbn, :attr => 'ISBN', :required => true
50
+ xml_accessor :isbn, :from => '@ISBN', :required => true
51
51
  xml_reader :title, :required => true
52
52
  xml_reader :contributors, [Contributor], :in => 'contributor_array', :required => true
53
53
  xml_reader :contributor_hash, {:attrs => ['role', 'name']},
@@ -57,33 +57,26 @@ end
57
57
  class BookWithAttrFrom
58
58
  include ROXML
59
59
 
60
- xml_accessor :isbn, :attr, :from => 'ISBN'
60
+ xml_accessor :isbn, :from => '@ISBN'
61
61
  end
62
62
 
63
63
  class BookWithWrappedAttr
64
64
  include ROXML
65
65
 
66
66
  xml_name :book
67
- xml_accessor :isbn, :attr => 'ISBN', :in => 'ids'
67
+ xml_accessor :isbn, :from => '@ISBN', :in => 'ids'
68
68
  end
69
69
 
70
70
  class Measurement
71
71
  include ROXML
72
72
 
73
- xml_reader :units, :attr
74
- xml_reader :value, :content
73
+ xml_reader :units, :from => :attr
74
+ xml_reader :value, :from => :content, :as => Float
75
75
 
76
- def xml_initialize
77
- initialize(value, units)
78
- end
79
-
80
- def initialize(value, units = 'pixels')
76
+ def initialize(value = 0, units = 'pixels')
81
77
  @value = Float(value)
82
78
  @units = units.to_s
83
- if @units.starts_with? 'hundredths-'
84
- @value /= 100
85
- @units = @units.split('hundredths-')[1]
86
- end
79
+ normalize_hundredths
87
80
  end
88
81
 
89
82
  def to_s
@@ -93,14 +86,26 @@ class Measurement
93
86
  def ==(other)
94
87
  other.units == @units && other.value == @value
95
88
  end
89
+
90
+ private
91
+ def after_parse
92
+ normalize_hundredths
93
+ end
94
+
95
+ def normalize_hundredths
96
+ if @units.starts_with? 'hundredths-'
97
+ @value /= 100
98
+ @units = @units.split('hundredths-')[1]
99
+ end
100
+ end
96
101
  end
97
102
 
98
103
  class BookWithDepth
99
104
  include ROXML
100
105
 
101
- xml_reader :isbn, :attr => 'ISBN'
106
+ xml_reader :isbn, :from => '@ISBN'
102
107
  xml_reader :title
103
- xml_reader :description, :as => :cdata
108
+ xml_reader :description, :cdata => true
104
109
  xml_reader :author
105
110
  xml_reader :depth, Measurement
106
111
  end
@@ -108,27 +113,27 @@ end
108
113
  class Author
109
114
  include ROXML
110
115
 
111
- xml_reader :role, :attr
112
- xml_reader :text, :content
116
+ xml_reader :role, :from => :attr
117
+ xml_reader :text, :from => :content
113
118
  end
114
119
 
115
120
  class BookWithAuthors
116
121
  include ROXML
117
122
 
118
123
  xml_name :book
119
- xml_reader :isbn, :attr, :from => 'ISBN'
124
+ xml_reader :isbn, :from => '@ISBN'
120
125
  xml_reader :title
121
- xml_reader :description, :as => :cdata
122
- xml_reader :authors, :as => :array
126
+ xml_reader :description, :cdata => true
127
+ xml_reader :authors, []
123
128
  end
124
129
 
125
130
  class BookWithAuthorTextAttribute
126
131
  include ROXML
127
132
 
128
133
  xml_name :book
129
- xml_reader :isbn, :attr, :from => 'ISBN'
134
+ xml_reader :isbn, :from => '@ISBN'
130
135
  xml_reader :title
131
- xml_reader :description, :as => :cdata
136
+ xml_reader :description, :cdata => true
132
137
  xml_reader :author, Author
133
138
  end
134
139
 
@@ -136,7 +141,7 @@ class BookWithContributions
136
141
  include ROXML
137
142
 
138
143
  xml_name :book
139
- xml_reader :isbn, :attr
144
+ xml_reader :isbn, :from => :attr
140
145
  xml_reader :title
141
146
  xml_reader :description
142
147
  xml_reader :contributions, [Contributor], :from => 'contributor', :in => "contributions"
@@ -146,29 +151,29 @@ class BookWithContributors
146
151
  include ROXML
147
152
 
148
153
  xml_name :book
149
- xml_reader :isbn, :attr
154
+ xml_reader :isbn, :from => :attr
150
155
  xml_reader :title
151
156
  xml_reader :description
152
- xml_reader :contributors, Contributor, :as => :array
157
+ xml_reader :contributors, [Contributor]
153
158
  end
154
159
 
155
160
  class WriteableBookWithContributors
156
161
  include ROXML
157
162
 
158
163
  xml_name :book
159
- xml_accessor :isbn, :attr
164
+ xml_accessor :isbn, :from => :attr
160
165
  xml_accessor :title
161
166
  xml_accessor :description
162
- xml_accessor :contributors, Contributor, :as => :array
167
+ xml_accessor :contributors, [Contributor]
163
168
  end
164
169
 
165
170
  class NamelessBook
166
171
  include ROXML
167
172
 
168
- xml_reader :isbn, :attr
173
+ xml_reader :isbn, :from => :attr
169
174
  xml_reader :title
170
175
  xml_reader :description
171
- xml_reader :contributors, Contributor, :as => :array
176
+ xml_reader :contributors, [Contributor]
172
177
  end
173
178
 
174
179
  class Publisher
@@ -181,7 +186,7 @@ class BookWithPublisher
181
186
  include ROXML
182
187
 
183
188
  xml_reader :book
184
- xml_reader :isbn, :attr
189
+ xml_reader :isbn, :from => :attr
185
190
  xml_reader :title
186
191
  xml_reader :description
187
192
  xml_reader :publisher, Publisher
@@ -190,7 +195,7 @@ end
190
195
  class BookPair
191
196
  include ROXML
192
197
 
193
- xml_reader :isbn, :attr
198
+ xml_reader :isbn, :from => :attr
194
199
  xml_reader :title
195
200
  xml_reader :description
196
201
  xml_reader :author
@@ -201,7 +206,7 @@ class Library
201
206
  include ROXML
202
207
 
203
208
  xml_reader :name
204
- xml_reader :books, BookWithContributions, :as => :array
209
+ xml_reader :books, [BookWithContributions]
205
210
  end
206
211
 
207
212
  class UppercaseLibrary
@@ -215,8 +220,8 @@ end
215
220
  class LibraryWithBooksOfUnderivableName
216
221
  include ROXML
217
222
 
218
- xml_reference true, :name
219
- xml_reader :novels, NamelessBook, :as => :array
223
+ xml_accessor :name
224
+ xml_reader :novels, [NamelessBook]
220
225
  end
221
226
 
222
227
  class NodeWithNameConflicts
@@ -231,15 +236,22 @@ class NodeWithAttrNameConflicts
231
236
  include ROXML
232
237
 
233
238
  xml_name :node
234
- xml_reader :content, :attr => :content
235
- xml_reader :name, :attr => :name
239
+ xml_reader :content, :from => '@content'
240
+ xml_reader :name, :from => '@name'
236
241
  end
237
242
 
238
243
  class Person
239
244
  include ROXML
240
245
 
241
- xml_reader :age, :attr, :else => 21
242
- xml_accessor :name, :content, :else => 'Unknown'
246
+ xml_accessor :age, :from => :attr, :else => 21
247
+ xml_accessor :name, :from => :content, :else => 'Unknown'
248
+
249
+ def self.blank
250
+ returning new do |instance|
251
+ instance.age = 21
252
+ instance.name = 'Unknown'
253
+ end
254
+ end
243
255
  end
244
256
 
245
257
  class PersonWithMother
@@ -261,7 +273,7 @@ end
261
273
  class PersonWithMotherOrMissing
262
274
  include ROXML
263
275
 
264
- xml_reader :age, :attr, :else => 21
276
+ xml_reader :age, :from => :attr, :else => 21
265
277
  xml_reader :name, :else => 'Anonymous'
266
- xml_reader :mother, PersonWithMotherOrMissing, :else => Person.new
278
+ xml_reader :mother, PersonWithMotherOrMissing, :else => Person.blank
267
279
  end
@@ -14,6 +14,12 @@ class TestDefinition < Test::Unit::TestCase
14
14
  assert_equal :text, opts.type
15
15
  end
16
16
 
17
+ def test_empty_array_means_as_array_for_text
18
+ opts = ROXML::Definition.new(:authors, [])
19
+ assert opts.array?
20
+ assert_equal :text, opts.type
21
+ end
22
+
17
23
  def test_attr_in_array_means_as_array_for_attr
18
24
  opts = ROXML::Definition.new(:authors, [:attr])
19
25
  assert opts.array?
@@ -26,22 +32,31 @@ class TestDefinition < Test::Unit::TestCase
26
32
  assert_equal Hash, opts.type
27
33
  end
28
34
 
29
- def test_content_is_a_recognized_type
30
- assert ROXML::Definition.new(:author, :content).content?
35
+ def test_literal_as_array_is_deprecated
36
+ assert_deprecated do
37
+ assert ROXML::Definition.new(:authors, :as => :array).array?
38
+ end
39
+ end
40
+
41
+ def test_block_shorthand_in_array_means_array
42
+ opts = ROXML::Definition.new(:intarray, :as => [Integer])
43
+ assert opts.array?
44
+ assert_equal :text, opts.type
45
+ assert 1, opts.blocks.size
31
46
  end
32
47
 
33
48
  def test_required
34
- assert !ROXML::Definition.new(:author, :content).required?
35
- assert ROXML::Definition.new(:author, :content, :required => true).required?
36
- assert !ROXML::Definition.new(:author, :content, :required => false).required?
49
+ assert !ROXML::Definition.new(:author).required?
50
+ assert ROXML::Definition.new(:author, :required => true).required?
51
+ assert !ROXML::Definition.new(:author, :required => false).required?
37
52
  end
38
53
 
39
54
  def test_required_conflicts_with_else
40
55
  assert_raise ArgumentError do
41
- ROXML::Definition.new(:author, :content, :required => true, :else => 'Johnny')
56
+ ROXML::Definition.new(:author, :required => true, :else => 'Johnny')
42
57
  end
43
58
  assert_nothing_raised do
44
- ROXML::Definition.new(:author, :content, :required => false, :else => 'Johnny')
59
+ ROXML::Definition.new(:author, :required => false, :else => 'Johnny')
45
60
  end
46
61
  end
47
62
 
@@ -65,19 +80,24 @@ class TestDefinition < Test::Unit::TestCase
65
80
  def test_hash_with_attr_key_and_content_val
66
81
  opts = ROXML::Definition.new(:attributes, {:key => {:attr => :name},
67
82
  :value => :content})
68
- assert_hash(opts, :attr => 'name', :content => '')
83
+ assert_hash(opts, :attr => 'name', :text => '.')
69
84
  end
70
85
 
71
86
  def test_hash_with_options
72
87
  opts = ROXML::Definition.new(:definitions, {:attrs => [:dt, :dd]},
73
- :in => :definitions)
88
+ :in => :definitions, :from => 'definition')
74
89
  assert_hash(opts, :attr => 'dt', :attr => 'dd')
90
+ assert_equal 'definition', opts.hash.wrapper
75
91
  end
76
92
 
77
93
  def test_no_block_shorthand_means_no_block
78
94
  assert ROXML::Definition.new(:count).blocks.empty?
79
- assert ROXML::Definition.new(:count, :as => :intager).blocks.empty?
80
- assert ROXML::Definition.new(:count, :as => :foat).blocks.empty?
95
+ assert_deprecated do
96
+ assert ROXML::Definition.new(:count, :as => :intager).blocks.empty?
97
+ end
98
+ assert_deprecated do
99
+ assert ROXML::Definition.new(:count, :as => :foat).blocks.empty?
100
+ end
81
101
  end
82
102
 
83
103
  def test_block_integer_shorthand
@@ -88,9 +108,23 @@ class TestDefinition < Test::Unit::TestCase
88
108
  assert_equal 3.1, ROXML::Definition.new(:count, :as => Float).blocks.first['3.1']
89
109
  end
90
110
 
111
+ def test_from_attr_is_supported
112
+ opts = ROXML::Definition.new(:count, :from => :attr)
113
+ assert_equal "count", opts.name
114
+ assert_equal :attr, opts.type
115
+ end
116
+
117
+ def test_from_at_name_is_supported
118
+ opts = ROXML::Definition.new(:count, :from => "@COUNT")
119
+ assert_equal "COUNT", opts.name
120
+ assert_equal :attr, opts.type
121
+ end
122
+
91
123
  def test_multiple_shorthands_raises
92
124
  assert_raise ArgumentError do
93
- ROXML::Definition.new(:count, :as => [Float, Integer])
125
+ assert_deprecated do
126
+ ROXML::Definition.new(:count, :as => [Float, Integer])
127
+ end
94
128
  end
95
129
  end
96
130
 
@@ -157,4 +191,119 @@ class TestDefinition < Test::Unit::TestCase
157
191
  assert_equal "manufacturer", opts.name
158
192
  assert_equal "./", opts.wrapper
159
193
  end
194
+
195
+ def test_cdata_is_specifiable
196
+ assert ROXML::Definition.new(:manufacturer, :cdata => true).cdata?
197
+ end
198
+
199
+ def test_as_cdata_is_deprecated
200
+ assert_deprecated do
201
+ assert ROXML::Definition.new(:manufacturer, :as => :cdata).cdata?
202
+ end
203
+ assert_deprecated do
204
+ assert ROXML::Definition.new(:manufacturer, :as => [Integer, :cdata]).cdata?
205
+ end
206
+ end
207
+
208
+ def test_as_supports_generic_roxml_types
209
+ assert_equal RoxmlObject, ROXML::Definition.new(:type, :as => RoxmlObject).type
210
+ assert_deprecated do
211
+ assert_equal RoxmlObject, ROXML::Definition.new(:type, RoxmlObject).type
212
+ end
213
+ end
214
+
215
+ def test_as_supports_generic_roxml_types_in_arrays
216
+ assert_equal RoxmlObject, ROXML::Definition.new(:types, :as => [RoxmlObject]).type
217
+ assert_deprecated do
218
+ assert_equal RoxmlObject, ROXML::Definition.new(:types, [RoxmlObject]).type
219
+ end
220
+ end
221
+
222
+ def test_default_works
223
+ opts = ROXML::Definition.new(:missing, :else => true)
224
+ assert_equal true, opts.to_ref(RoxmlObject.new).value_in(ROXML::XML::Parser.parse('<xml></xml>'))
225
+ end
226
+
227
+ def test_default_works_for_arrays
228
+ opts = ROXML::Definition.new(:missing, :as => [])
229
+ assert_equal [], opts.to_ref(RoxmlObject.new).value_in(ROXML::XML::Parser.parse('<xml></xml>'))
230
+ assert_deprecated do
231
+ opts = ROXML::Definition.new(:missing, [])
232
+ assert_equal [], opts.to_ref(RoxmlObject.new).value_in(ROXML::XML::Parser.parse('<xml></xml>'))
233
+ end
234
+ assert_deprecated do
235
+ opts = ROXML::Definition.new(:missing, :as => :array)
236
+ assert_equal [], opts.to_ref(RoxmlObject.new).value_in(ROXML::XML::Parser.parse('<xml></xml>'))
237
+ end
238
+ end
239
+
240
+ def test_default_works_for_recursive_objects
241
+ opts = ROXML::Definition.new(:missing, :as => RecursiveObject, :else => false)
242
+ assert_equal false, opts.to_ref(RoxmlObject.new).value_in(ROXML::XML::Parser.parse('<xml></xml>'))
243
+ end
244
+
245
+ def test_content_is_accepted_as_from
246
+ assert ROXML::Definition.new(:author, :from => :content).content?
247
+ assert ROXML::Definition.new(:author, :from => '.').content?
248
+ end
249
+
250
+ def test_content_is_a_recognized_type
251
+ assert_deprecated do
252
+ opts = ROXML::Definition.new(:author, :content)
253
+ assert opts.content?
254
+ assert_equal '.', opts.name
255
+ assert_equal :text, opts.type
256
+ end
257
+ end
258
+
259
+ def test_content_symbol_as_target_is_translated_to_string
260
+ assert_deprecated do
261
+ opts = ROXML::Definition.new(:content, :attr => :content)
262
+ assert_equal 'content', opts.name
263
+ assert_equal :attr, opts.type
264
+ end
265
+ end
266
+
267
+ def test_attr_is_accepted_as_from
268
+ assert_equal :attr, ROXML::Definition.new(:author, :from => :attr).type
269
+ assert_equal :attr, ROXML::Definition.new(:author, :from => '@author').type
270
+ end
271
+
272
+ def test_attr_is_a_recognized_type
273
+ assert_deprecated do
274
+ opts = ROXML::Definition.new(:author, :attr)
275
+ assert_equal 'author', opts.name
276
+ assert_equal :attr, opts.type
277
+ end
278
+ end
279
+
280
+ def test_name_ending_with_on_warns_of_coming_date_default
281
+ assert_deprecated do
282
+ assert_equal :text, ROXML::Definition.new(:created_at).type
283
+ end
284
+ end
285
+
286
+ def test_name_ending_with_at_warns_of_coming_datetime_default
287
+ assert_deprecated do
288
+ assert_equal :text, ROXML::Definition.new(:created_on).type
289
+ end
290
+ end
291
+ end
292
+
293
+ class RecursiveObject
294
+ include ROXML
295
+
296
+ xml_reader :next, RecursiveObject, :else => true
297
+ end
298
+
299
+ class RoxmlObject
300
+ include ROXML
301
+ end
302
+
303
+ class HashDefinitionTest < Test::Unit::TestCase
304
+ def test_content_detected_as_from
305
+ opts = ROXML::Definition.new(:hash, {:key => :content, :value => :name})
306
+ assert_equal '.', opts.hash.key.name
307
+ assert_equal :text, opts.hash.key.type
308
+ end
160
309
  end