roxml 2.4.3 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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,43 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require example('active_record')
3
+
4
+ describe ROXML, "under ActiveRecord" do
5
+ before do
6
+ @route = Route.from_xml(xml_for('active_record'))
7
+ end
8
+
9
+ after(:all) do
10
+ db = File.dirname(__FILE__) + "/../../examples/active_record/active_record.sqlite3"
11
+ if File.exists?(db)
12
+ FileUtils.rm(db)
13
+ end
14
+ end
15
+
16
+ it "should be parsed" do
17
+ @route.should_not == nil
18
+ @route.should be_an_instance_of(Route)
19
+ end
20
+
21
+ describe "xml attributes" do
22
+ it "should extract xml attributes" do
23
+ @route.totalHg.should == "640"
24
+ @route.lonlatx.should == "357865"
25
+ @route.lonlaty.should == "271635"
26
+ @route.grcenter.should == "SH 71635 57865"
27
+ @route.totalMins.should == "235.75000000000003"
28
+ @route.totalDist.should == "11185.321521477119"
29
+ end
30
+ end
31
+
32
+ describe "xml sub-objects" do
33
+ it "should extract xml sub-objects" do
34
+ @route.should have(6).waypoints
35
+ @route.waypoints.each {|waypoint| waypoint.should be_an_instance_of(Waypoint)}
36
+ end
37
+ it "should be usable as a ActiveRecord object" do
38
+ Waypoint.count.should == 0
39
+ @route.save!
40
+ Waypoint.count.should == 6
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,372 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe ROXML, "#from_xml" do
4
+ describe "from_xml call", :shared => true do
5
+ it "should fetch values" do
6
+ book = BookWithContributors.from_xml(@path)
7
+ book.title.should == "Programming Ruby - 2nd Edition"
8
+ book.contributors.map(&:name).should == ["David Thomas","Andrew Hunt","Chad Fowler"]
9
+ end
10
+ end
11
+
12
+ context "called with PathName" do
13
+ before do
14
+ @path = Pathname.new(fixture_path(:book_with_contributors))
15
+ end
16
+ it_should_behave_like "from_xml call"
17
+ end
18
+
19
+ context "called with File" do
20
+ before do
21
+ @path = File.new(fixture_path(:book_with_contributors))
22
+ end
23
+ it_should_behave_like "from_xml call"
24
+ end
25
+
26
+ context "called with URI" do
27
+ before do
28
+ require 'uri'
29
+ @path = URI.parse("file://#{File.expand_path(File.expand_path(fixture_path(:book_with_contributors)))}")
30
+ end
31
+ it_should_behave_like "from_xml call"
32
+ end
33
+ end
34
+
35
+ describe ROXML, "#xml" do
36
+ class DescriptionReadonly
37
+ include ROXML
38
+
39
+ xml_reader :writable, :from => :content
40
+ xml_reader :readonly, :from => :content, :frozen => true
41
+ end
42
+
43
+ class Contributor
44
+ include ROXML
45
+
46
+ xml_reader :role, :from => :attr
47
+ xml_reader :name
48
+ end
49
+
50
+ class BookWithContributions
51
+ include ROXML
52
+
53
+ xml_name :book
54
+ xml_reader :isbn, :from => :attr
55
+ xml_reader :title
56
+ xml_reader :description, DescriptionReadonly
57
+ xml_reader :contributions, [Contributor], :from => 'contributor', :in => "contributions"
58
+ end
59
+
60
+ class BookWithContributionsReadonly
61
+ include ROXML
62
+
63
+ xml_name :book
64
+ xml_reader :isbn, :from => :attr, :frozen => true
65
+ xml_reader :title, :frozen => true
66
+ xml_reader :description, DescriptionReadonly, :frozen => true
67
+ xml_reader :contributions, [Contributor], :from => 'contributor', :in => "contributions", :frozen => true
68
+ end
69
+
70
+ before do
71
+ @writable = BookWithContributions.from_xml(fixture(:book_with_contributions))
72
+ @readonly = BookWithContributionsReadonly.from_xml(fixture(:book_with_contributions))
73
+ end
74
+
75
+ it "should raise on duplicate accessor name" do
76
+ proc do
77
+ Class.new do
78
+ include ROXML
79
+
80
+ xml_reader :id
81
+ xml_accessor :id
82
+ end
83
+ end.should raise_error(RuntimeError)
84
+ end
85
+
86
+ class OctalInteger
87
+ def self.from_xml(val)
88
+ new(Integer(val.content))
89
+ end
90
+
91
+ def initialize(value)
92
+ @val = value
93
+ end
94
+
95
+ def ==(other)
96
+ @val == other
97
+ end
98
+
99
+ def to_xml
100
+ sprintf("%#o", @val)
101
+ end
102
+ end
103
+
104
+ describe "overriding output" do
105
+ class BookWithOctalPages
106
+ include ROXML
107
+
108
+ xml_accessor :pages_with_as, :as => Integer, :to_xml => proc {|val| sprintf("%#o", val) }, :required => true
109
+ xml_accessor :pages_with_type, OctalInteger, :required => true
110
+ end
111
+
112
+ # to_xml_test :book_with_octal_pages
113
+
114
+ describe "with :to_xml option" do
115
+ it "should output with to_xml filtering"
116
+ end
117
+
118
+ describe "with #to_xml on the object" do
119
+ it "should output with to_xml filtering"
120
+ end
121
+ end
122
+
123
+ describe "overriding input" do
124
+ before do
125
+ @book_with_octal_pages_xml = %{
126
+ <book>
127
+ <pages>0357</pages>
128
+ </book>
129
+ }
130
+
131
+ @expected_pages = 239
132
+ end
133
+
134
+ describe "with :as block shorthand" do
135
+ class BookWithOctalPagesBlockShorthand
136
+ include ROXML
137
+
138
+ xml_accessor :pages, :as => Integer, :required => true
139
+ end
140
+
141
+ it "should apply filtering on input" do
142
+ book = BookWithOctalPagesBlockShorthand.from_xml(@book_with_octal_pages_xml)
143
+ book.pages.should == @expected_pages
144
+ end
145
+ end
146
+
147
+ describe "with #from_xml defined on the object" do
148
+ class BookWithOctalPagesType
149
+ include ROXML
150
+
151
+ xml_accessor :pages, OctalInteger, :required => true
152
+ end
153
+
154
+ it "should apply filtering on input" do
155
+ book = BookWithOctalPagesType.from_xml(@book_with_octal_pages_xml)
156
+ book.pages.should == @expected_pages
157
+ end
158
+ end
159
+ end
160
+
161
+ describe "attribute reference" do
162
+ before do
163
+ @frozen = @readonly.isbn
164
+ @unfrozen = @writable.isbn
165
+ end
166
+
167
+ it_should_behave_like "freezable xml reference"
168
+ end
169
+
170
+ describe "text reference" do
171
+ before do
172
+ @frozen = @readonly.title
173
+ @unfrozen = @writable.title
174
+ end
175
+
176
+ it_should_behave_like "freezable xml reference"
177
+ end
178
+
179
+ describe "object reference" do
180
+ before do
181
+ @frozen = @readonly.description
182
+ @unfrozen = @writable.description
183
+ end
184
+
185
+ it_should_behave_like "freezable xml reference"
186
+
187
+ describe "indirect reference via an object" do
188
+ it "does not inherit the frozen status from its parent" do
189
+ @frozen.writable.frozen?.should be_false
190
+ @frozen.readonly.frozen?.should be_true
191
+
192
+ @unfrozen.writable.frozen?.should be_false
193
+ @unfrozen.readonly.frozen?.should be_true
194
+ end
195
+ end
196
+ end
197
+
198
+ describe "array reference" do
199
+ before do
200
+ @frozen = @readonly.contributions
201
+ @unfrozen = @writable.contributions
202
+ end
203
+
204
+ it_should_behave_like "freezable xml reference"
205
+
206
+ it "should apply :frozen to the constituent elements" do
207
+ @frozen.all?(&:frozen?).should be_true
208
+ @unfrozen.any?(&:frozen?).should be_false
209
+ end
210
+
211
+ context "no elements are present in root, no :in is specified" do
212
+ class BookWithContributors
213
+ include ROXML
214
+
215
+ xml_name :book
216
+ xml_reader :isbn, :from => :attr
217
+ xml_reader :title
218
+ xml_reader :description
219
+ xml_reader :contributors, [Contributor]
220
+ end
221
+
222
+ it "should look for elements :in the plural of name" do
223
+ book = BookWithContributors.from_xml(%{
224
+ <book isbn="0974514055">
225
+ <contributors>
226
+ <contributor role="author"><name>David Thomas</name></contributor>
227
+ <contributor role="supporting author"><name>Andrew Hunt</name></contributor>
228
+ <contributor role="supporting author"><name>Chad Fowler</name></contributor>
229
+ </contributors>
230
+ </book>
231
+ })
232
+ book.contributors.map(&:name).sort.should == ["David Thomas","Andrew Hunt","Chad Fowler"].sort
233
+ end
234
+ end
235
+ end
236
+
237
+ describe "hash reference" do
238
+ class DictionaryOfGuardedNames
239
+ include ROXML
240
+
241
+ xml_name :dictionary
242
+ xml_reader :definitions, {:key => :name,
243
+ :value => :content}, :in => :definitions
244
+ end
245
+
246
+ class DictionaryOfGuardedNamesReadonly
247
+ include ROXML
248
+
249
+ xml_name :dictionary
250
+ xml_reader :definitions, {:key => :name,
251
+ :value => :content}, :in => :definitions, :frozen => true
252
+ end
253
+
254
+ before do
255
+ @frozen = DictionaryOfGuardedNamesReadonly.from_xml(fixture(:dictionary_of_guarded_names)).definitions
256
+ @unfrozen = DictionaryOfGuardedNames.from_xml(fixture(:dictionary_of_guarded_names)).definitions
257
+ end
258
+
259
+ it_should_behave_like "freezable xml reference"
260
+
261
+ it "should have frozen keys, as with all hashes" do
262
+ @frozen.keys.all?(&:frozen?).should be_true
263
+ @unfrozen.keys.all?(&:frozen?).should be_true
264
+ end
265
+
266
+ it "should apply :frozen to the constituent values" do
267
+ @frozen.values.all?(&:frozen?).should be_true
268
+ @unfrozen.values.any?(&:frozen?).should be_false
269
+ end
270
+ end
271
+ end
272
+
273
+ describe ROXML, "inheritance" do
274
+ class Book
275
+ include ROXML
276
+
277
+ xml_accessor :isbn, :attr => 'ISBN'
278
+ xml_reader :title
279
+ xml_reader :description, :as => :cdata
280
+ xml_reader :author
281
+ xml_accessor :pages, :text => 'pagecount', :as => Integer
282
+ end
283
+
284
+ class Measurement
285
+ include ROXML
286
+
287
+ xml_reader :units, :from => :attr
288
+ xml_reader :value, :from => :content, :as => Float
289
+
290
+ def initialize(value = 0, units = 'pixels')
291
+ @value = Float(value)
292
+ @units = units.to_s
293
+ normalize_hundredths
294
+ end
295
+
296
+ def to_s
297
+ "#{value} #{units}"
298
+ end
299
+
300
+ def ==(other)
301
+ other.units == @units && other.value == @value
302
+ end
303
+
304
+ private
305
+ def after_parse
306
+ normalize_hundredths
307
+ end
308
+
309
+ def normalize_hundredths
310
+ if @units.starts_with? 'hundredths-'
311
+ @value /= 100
312
+ @units = @units.split('hundredths-')[1]
313
+ end
314
+ end
315
+ end
316
+
317
+ class InheritedBookWithDepth < Book
318
+ xml_reader :depth, Measurement
319
+ end
320
+
321
+ before do
322
+ @book_xml = %{
323
+ <book ISBN="0201710897">
324
+ <title>The PickAxe</title>
325
+ <description><![CDATA[Probably the best Ruby book out there]]></description>
326
+ <author>David Thomas, Andrew Hunt, Dave Thomas</author>
327
+ <depth units="hundredths-meters">1130</depth>
328
+ <publisher>Pragmattic Programmers</publisher>
329
+ </book>
330
+ }
331
+
332
+ @parent = Book.from_xml(@book_xml)
333
+ @child = InheritedBookWithDepth.from_xml(@book_xml)
334
+ end
335
+
336
+ describe "parent" do
337
+ it "should include its attributes" do
338
+ @child.isbn.should == "0201710897"
339
+ @child.title.should == "The PickAxe"
340
+ @child.description.should == "Probably the best Ruby book out there"
341
+ @child.author.should == 'David Thomas, Andrew Hunt, Dave Thomas'
342
+ @child.pages.should == nil
343
+ end
344
+
345
+ it "should not include its child's attributes" do
346
+ @parent.should_not respond_to(:depth)
347
+ end
348
+ end
349
+
350
+ describe "child" do
351
+ it "should include its parent's attributes" do
352
+ @child.isbn.should == @parent.isbn
353
+ @child.title.should == @parent.title
354
+ @child.description.should == @parent.description
355
+ @child.author.should == @parent.author
356
+ @child.pages.should == @parent.pages
357
+ end
358
+
359
+ it "should include its attributes" do
360
+ @child.depth.to_s.should == '11.3 meters'
361
+ end
362
+
363
+ it "should include parent's attributes added after declaration" do
364
+ Book.class_eval do
365
+ xml_reader :publisher, :require => true
366
+ end
367
+
368
+ book = InheritedBookWithDepth.from_xml(@book_xml)
369
+ book.publisher.should == "Pragmattic Programmers"
370
+ end
371
+ end
372
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe "freezable xml reference", :shared => true do
4
+ describe "with :frozen option" do
5
+ it "should be frozen" do
6
+ @frozen.frozen?.should be_true
7
+ end
8
+ end
9
+
10
+ describe "without :frozen option" do
11
+ it "should not be frozen" do
12
+ @unfrozen.frozen?.should be_false
13
+ end
14
+ end
15
+ end
@@ -4,13 +4,30 @@ module ROXML
4
4
  SILENCE_XML_NAME_WARNING = true
5
5
  end
6
6
 
7
- DIR = Pathname.new(__FILE__).dirname
8
- require DIR.join('../lib/roxml').expand_path
7
+ DIR = Pathname.new(__FILE__ + '../..').expand_path.dirname
8
+ LOAD_PATH = DIR.join('lib').to_s
9
+ $LOAD_PATH.unshift(LOAD_PATH) unless
10
+ $LOAD_PATH.include?(LOAD_PATH) || $LOAD_PATH.include?(File.expand_path(LOAD_PATH))
11
+ require 'roxml'
12
+
13
+ require File.join(File.dirname(__FILE__), 'shared_specs')
9
14
 
10
15
  def example(name)
11
- DIR.join("../examples/#{name}.rb").expand_path
16
+ DIR.join("examples/#{name}.rb")
17
+ end
18
+
19
+ def fixture(name)
20
+ File.read(fixture_path(name))
21
+ end
22
+
23
+ def xml_fixture(name)
24
+ ROXML::XML::Parser.parse_file(fixture_path(name)).root
25
+ end
26
+
27
+ def fixture_path(name)
28
+ "test/fixtures/#{name}.xml"
12
29
  end
13
30
 
14
31
  def xml_for(name)
15
- DIR.join("../examples/xml/#{name}.xml").expand_path
32
+ DIR.join("examples/xml/#{name}.xml")
16
33
  end