representable 0.0.1.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/.gitignore +7 -0
  2. data/.gitmodules +3 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +3 -0
  5. data/History.txt +354 -0
  6. data/LICENSE +20 -0
  7. data/README.rdoc +186 -0
  8. data/Rakefile +10 -0
  9. data/TODO +37 -0
  10. data/VERSION +1 -0
  11. data/examples/amazon.rb +35 -0
  12. data/examples/current_weather.rb +27 -0
  13. data/examples/dashed_elements.rb +20 -0
  14. data/examples/library.rb +40 -0
  15. data/examples/posts.rb +27 -0
  16. data/examples/rails.rb +70 -0
  17. data/examples/twitter.rb +37 -0
  18. data/examples/xml/active_record.xml +70 -0
  19. data/examples/xml/amazon.xml +133 -0
  20. data/examples/xml/current_weather.xml +89 -0
  21. data/examples/xml/dashed_elements.xml +52 -0
  22. data/examples/xml/posts.xml +23 -0
  23. data/examples/xml/twitter.xml +422 -0
  24. data/lib/representable.rb +257 -0
  25. data/lib/representable/definition.rb +109 -0
  26. data/lib/representable/nokogiri_extensions.rb +19 -0
  27. data/lib/representable/references.rb +153 -0
  28. data/lib/representable/version.rb +3 -0
  29. data/lib/representable/xml.rb +79 -0
  30. data/representable.gemspec +29 -0
  31. data/spec/definition_spec.rb +495 -0
  32. data/spec/examples/active_record_spec.rb +41 -0
  33. data/spec/examples/amazon_spec.rb +54 -0
  34. data/spec/examples/current_weather_spec.rb +37 -0
  35. data/spec/examples/dashed_elements_spec.rb +20 -0
  36. data/spec/examples/library_spec.rb +46 -0
  37. data/spec/examples/post_spec.rb +24 -0
  38. data/spec/examples/twitter_spec.rb +32 -0
  39. data/spec/roxml_integration_test.rb +289 -0
  40. data/spec/roxml_spec.rb +372 -0
  41. data/spec/shared_specs.rb +15 -0
  42. data/spec/spec_helper.rb +5 -0
  43. data/spec/support/libxml.rb +3 -0
  44. data/spec/support/nokogiri.rb +3 -0
  45. data/spec/xml/array_spec.rb +36 -0
  46. data/spec/xml/attributes_spec.rb +71 -0
  47. data/spec/xml/encoding_spec.rb +53 -0
  48. data/spec/xml/namespace_spec.rb +270 -0
  49. data/spec/xml/namespaces_spec.rb +67 -0
  50. data/spec/xml/object_spec.rb +82 -0
  51. data/spec/xml/parser_spec.rb +21 -0
  52. data/spec/xml/text_spec.rb +71 -0
  53. data/test/fixtures/book_malformed.xml +5 -0
  54. data/test/fixtures/book_pair.xml +8 -0
  55. data/test/fixtures/book_text_with_attribute.xml +5 -0
  56. data/test/fixtures/book_valid.xml +5 -0
  57. data/test/fixtures/book_with_authors.xml +7 -0
  58. data/test/fixtures/book_with_contributions.xml +9 -0
  59. data/test/fixtures/book_with_contributors.xml +7 -0
  60. data/test/fixtures/book_with_contributors_attrs.xml +7 -0
  61. data/test/fixtures/book_with_default_namespace.xml +9 -0
  62. data/test/fixtures/book_with_depth.xml +6 -0
  63. data/test/fixtures/book_with_octal_pages.xml +4 -0
  64. data/test/fixtures/book_with_publisher.xml +7 -0
  65. data/test/fixtures/book_with_wrapped_attr.xml +3 -0
  66. data/test/fixtures/dictionary_of_attr_name_clashes.xml +8 -0
  67. data/test/fixtures/dictionary_of_attrs.xml +6 -0
  68. data/test/fixtures/dictionary_of_guarded_names.xml +6 -0
  69. data/test/fixtures/dictionary_of_mixeds.xml +4 -0
  70. data/test/fixtures/dictionary_of_name_clashes.xml +10 -0
  71. data/test/fixtures/dictionary_of_names.xml +4 -0
  72. data/test/fixtures/dictionary_of_texts.xml +10 -0
  73. data/test/fixtures/library.xml +30 -0
  74. data/test/fixtures/library_uppercase.xml +30 -0
  75. data/test/fixtures/muffins.xml +3 -0
  76. data/test/fixtures/nameless_ageless_youth.xml +2 -0
  77. data/test/fixtures/node_with_attr_name_conflicts.xml +1 -0
  78. data/test/fixtures/node_with_name_conflicts.xml +4 -0
  79. data/test/fixtures/numerology.xml +4 -0
  80. data/test/fixtures/person.xml +1 -0
  81. data/test/fixtures/person_with_guarded_mothers.xml +13 -0
  82. data/test/fixtures/person_with_mothers.xml +10 -0
  83. data/test/mocks/dictionaries.rb +57 -0
  84. data/test/mocks/mocks.rb +279 -0
  85. data/test/roxml_test.rb +58 -0
  86. data/test/support/fixtures.rb +11 -0
  87. data/test/test_helper.rb +6 -0
  88. data/test/unit/definition_test.rb +235 -0
  89. data/test/unit/deprecations_test.rb +24 -0
  90. data/test/unit/to_xml_test.rb +81 -0
  91. data/test/unit/xml_attribute_test.rb +39 -0
  92. data/test/unit/xml_block_test.rb +81 -0
  93. data/test/unit/xml_bool_test.rb +122 -0
  94. data/test/unit/xml_convention_test.rb +150 -0
  95. data/test/unit/xml_hash_test.rb +115 -0
  96. data/test/unit/xml_initialize_test.rb +49 -0
  97. data/test/unit/xml_name_test.rb +141 -0
  98. data/test/unit/xml_namespace_test.rb +31 -0
  99. data/test/unit/xml_object_test.rb +206 -0
  100. data/test/unit/xml_required_test.rb +94 -0
  101. data/test/unit/xml_text_test.rb +71 -0
  102. data/website/index.html +98 -0
  103. metadata +248 -0
@@ -0,0 +1,3 @@
1
+ module Representable
2
+ VERSION = "0.0.1.alpha1"
3
+ end
@@ -0,0 +1,79 @@
1
+ module Representable
2
+ module Xml
3
+ module Declarations
4
+ # Sets the name of the XML element that represents this class. Use this
5
+ # to override the default lowercase class name.
6
+ #
7
+ # Example:
8
+ # class BookWithPublisher
9
+ # xml_name :book
10
+ # end
11
+ #
12
+ # Without the xml_name annotation, the XML mapped tag would have been "bookwithpublisher".
13
+ def xml_name(name)
14
+ self.explicit_representation_name = name
15
+ end
16
+
17
+ def xml_accessor(*args) # TODO: remove me, just for back-compat.
18
+ representable_accessor(*args)
19
+ end
20
+
21
+ end
22
+
23
+ module ClassMethods
24
+ #
25
+ # Creates a new Ruby object from XML using mapping information
26
+ # annotated in the class.
27
+ #
28
+ # The input data is either an XML::Node, String, Pathname, or File representing
29
+ # the XML document.
30
+ #
31
+ # Example
32
+ # book = Book.from_xml(File.read("book.xml"))
33
+ # or
34
+ # book = Book.from_xml("<book><name>Beyond Java</name></book>")
35
+ #
36
+ # _initialization_args_ passed into from_xml will be passed into
37
+ # the object's .new, prior to populating the xml_attrs.
38
+ #
39
+ # After the instatiation and xml population
40
+ #
41
+ # See also: xml_initialize
42
+ #
43
+ def from_xml(data, *args)
44
+ xml = Nokogiri::XML::Node.from(data)
45
+
46
+ create_from_xml(*args).tap do |inst|
47
+ refs = representable_attrs.map {|attr| attr.to_ref }
48
+
49
+ refs.each do |ref|
50
+ value = ref.value_in(xml)
51
+
52
+ inst.send(ref.definition.setter, value)
53
+ end
54
+ end
55
+ end
56
+
57
+ private
58
+ def create_from_xml(*args)
59
+ new(*args)
60
+ end
61
+ end
62
+
63
+ module InstanceMethods # :nodoc:
64
+ # Returns an XML object representing this object
65
+ def to_xml(params={})
66
+ params.reverse_merge!(:name => self.class.representation_name)
67
+
68
+ Nokogiri::XML::Node.new(params[:name].to_s, Nokogiri::XML::Document.new).tap do |root|
69
+ refs = self.class.representable_attrs.map {|attr| attr.to_ref }
70
+
71
+ refs.each do |ref|
72
+ value = public_send(ref.accessor) # DISCUSS: eventually move back to Ref.
73
+ ref.update_xml(root, value) if value
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end # Xml
79
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+
5
+ require 'representable/version'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "representable"
9
+ s.version = Representable::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = ["Nick Sutterer"]
12
+ s.email = ["apotonick@gmail.com"]
13
+ s.homepage = "http://representable.apotomo.de"
14
+ s.summary = %q{Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties and compositions.}
15
+ s.description = %q{CMaps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties and compositions.}
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency "activesupport", "~> 3.0.0"
23
+ s.add_dependency "hooks"
24
+ s.add_dependency "nokogiri"
25
+ s.add_dependency "i18n"
26
+
27
+ s.add_development_dependency "rspec"
28
+ s.add_development_dependency "test_xml"
29
+ end
@@ -0,0 +1,495 @@
1
+ # encoding: utf-8
2
+ require_relative './spec_helper'
3
+
4
+ describe ROXML::Definition do
5
+ describe "#name_explicit?" do
6
+ it "should indicate whether from option is present" do
7
+ ROXML::Definition.new(:element, :from => 'somewhere').name_explicit?.should be_true
8
+ ROXML::Definition.new(:element).name_explicit?.should be_false
9
+ end
10
+
11
+ it "should not consider name proxies as explicit" do
12
+ ROXML::Definition.new(:element, :from => :attr).name_explicit?.should be_false
13
+ ROXML::Definition.new(:element, :from => :content).name_explicit?.should be_false
14
+ end
15
+ end
16
+
17
+ shared_examples_for "DateTime reference" do
18
+ it "should return nil on empty string" do
19
+ @subject.blocks.first.call(" ").should be_nil
20
+ end
21
+
22
+ it "should return a time version of the string" do
23
+ @subject.blocks.first.call("12:05pm, September 3rd, 1970").to_s == "1970-09-03T12:05:00+00:00"
24
+ end
25
+
26
+ context "when passed an array of values" do
27
+ it "should timify all of them" do
28
+ @subject.blocks.first.call(["12:05pm, September 3rd, 1970", "3:00pm, May 22, 1700"]).map(&:to_s).should == ["1970-09-03T12:05:00+00:00", "1700-05-22T15:00:00+00:00"]
29
+ end
30
+ end
31
+ end
32
+
33
+ shared_examples_for "Date reference" do
34
+ it "should return nil on empty string" do
35
+ @subject.blocks.first.call(" ").should be_nil
36
+ end
37
+
38
+ it "should return a time version of the string" do
39
+ @subject.blocks.first.call("September 3rd, 1970").to_s == "1970-09-03"
40
+ end
41
+
42
+ context "when passed an array of values" do
43
+ it "should timify all of them" do
44
+ @subject.blocks.first.call(["September 3rd, 1970", "1776-07-04"]).map(&:to_s).should == ["1970-09-03", "1776-07-04"]
45
+ end
46
+ end
47
+ end
48
+
49
+ it "should unescape xml entities" do
50
+ ROXML::Definition.new(:questions, :as => []).to_ref(RoxmlObject.new).value_in(%{
51
+ <xml>
52
+ <question>&quot;Wickard &amp; Filburn&quot; &gt;</question>
53
+ <question> &lt; McCulloch &amp; Maryland?</question>
54
+ </xml>
55
+ }).should == ["\"Wickard & Filburn\" >", " < McCulloch & Maryland?"]
56
+ end
57
+
58
+ it "should unescape utf characters in xml" do
59
+ ROXML::Definition.new(:questions, :as => []).to_ref(RoxmlObject.new).value_in(%{
60
+ <xml>
61
+ <question>ROXML\342\204\242</question>
62
+ </xml>
63
+ }).should == ["ROXML™"]
64
+ end
65
+
66
+ describe "attr name" do
67
+ context "when ending with '_at'" do
68
+ context "and without an :as argument" do
69
+ before(:all) do
70
+ @subject = ROXML::Definition.new(:time_at)
71
+ end
72
+ it_should_behave_like "DateTime reference"
73
+ end
74
+ end
75
+
76
+ context "when ending with '_on'" do
77
+ context "and without an :as argument" do
78
+ before(:all) do
79
+ @subject = ROXML::Definition.new(:created_on)
80
+ end
81
+ it_should_behave_like "Date reference"
82
+ end
83
+ end
84
+ end
85
+
86
+ describe ":as" do
87
+ describe "=> []" do
88
+ it "should means array of texts" do
89
+ opts = ROXML::Definition.new(:authors, :as => [])
90
+ opts.array?.should be_true
91
+ opts.sought_type.should == :text
92
+ end
93
+ end
94
+
95
+ describe "=> RoxmlClass" do
96
+ class RoxmlClass
97
+ include ROXML
98
+ end
99
+
100
+ it "should store type" do
101
+ opts = ROXML::Definition.new(:name, :as => RoxmlClass)
102
+ opts.sought_type.should == RoxmlClass
103
+ end
104
+ end
105
+
106
+ describe "=> NonRoxmlClassWithFromXmlDefined" do
107
+ class OctalInteger
108
+ def self.from_xml(val)
109
+ new(Integer(val.content))
110
+ end
111
+ end
112
+
113
+ it "should accept type" do
114
+ opts = ROXML::Definition.new(:name, :as => OctalInteger)
115
+ opts.sought_type.should == OctalInteger
116
+ end
117
+ end
118
+
119
+ describe "=> NonRoxmlClass" do
120
+ it "should fail with a warning" do
121
+ proc { ROXML::Definition.new(:authors, :as => Module) }.should raise_error(ArgumentError)
122
+ end
123
+ end
124
+
125
+ describe "=> [NonRoxmlClass]" do
126
+ it "should raise" do
127
+ proc { ROXML::Definition.new(:authors, :as => [Module]) }.should raise_error(ArgumentError)
128
+ end
129
+ end
130
+
131
+ describe "=> {}" do
132
+ shared_examples_for "hash options declaration" do
133
+ it "should represent a hash" do
134
+ @opts.hash?.should be_true
135
+ end
136
+
137
+ it "should have hash definition" do
138
+ {@opts.hash.key.sought_type => @opts.hash.key.name}.should == @hash_args[:key]
139
+ {@opts.hash.value.sought_type => @opts.hash.value.name}.should == @hash_args[:value]
140
+ end
141
+
142
+ it "should not represent an array" do
143
+ @opts.array?.should be_false
144
+ end
145
+ end
146
+
147
+ describe "hash with attr key and text val" do
148
+ before do
149
+ @opts = ROXML::Definition.new(:attributes, :as => {:key => '@name',
150
+ :value => 'value'})
151
+ @hash_args = {:key => {:attr => 'name'},
152
+ :value => {:text => 'value'}}
153
+ end
154
+
155
+ it_should_behave_like "hash options declaration"
156
+ end
157
+
158
+ describe "hash with String class for type" do
159
+ before do
160
+ @opts = ROXML::Definition.new(:attributes, :as => {:key => 'name',
161
+ :value => 'value'})
162
+ @hash_args = {:key => {:text => 'name'}, :value => {:text => 'value'}}
163
+ end
164
+
165
+ it_should_behave_like "hash options declaration"
166
+ end
167
+
168
+ describe "hash with attr key and content val" do
169
+ before do
170
+ @opts = ROXML::Definition.new(:attributes, :as => {:key => '@name',
171
+ :value => :content})
172
+ @hash_args = {:key => {:attr => 'name'}, :value => {:text => '.'}}
173
+ end
174
+
175
+ it_should_behave_like "hash options declaration"
176
+ end
177
+
178
+ describe "hash with names as keys and content vals" do
179
+ before do
180
+ @opts = ROXML::Definition.new(:attributes, :as => {:key => :name,
181
+ :value => :content})
182
+ @hash_args = {:key => {:text => '*'}, :value => {:text => '.'}}
183
+ end
184
+
185
+ it_should_behave_like "hash options declaration"
186
+ end
187
+ end
188
+
189
+ describe "for block shorthand" do
190
+ describe "in literal array" do
191
+ before do
192
+ @opts = ROXML::Definition.new(:intarray, :as => [Integer])
193
+ end
194
+
195
+ it "should be detected as array reference" do
196
+ @opts.array?.should be_true
197
+ end
198
+
199
+ it "should be normal otherwise" do
200
+ @opts.sought_type.should == :text
201
+ @opts.blocks.size.should == 1
202
+ end
203
+ end
204
+
205
+ it "should have no blocks without a shorthand" do
206
+ ROXML::Definition.new(:count).blocks.should be_empty
207
+ end
208
+
209
+ it "should raise on unknown :as" do
210
+ proc { ROXML::Definition.new(:count, :as => :bogus) }.should raise_error(ArgumentError)
211
+ proc { ROXML::Definition.new(:count, :as => :foat) }.should raise_error(ArgumentError)
212
+ end
213
+
214
+ shared_examples_for "block shorthand type declaration" do
215
+ it "should translate nil to nil" do
216
+ @definition.blocks.first.call(nil).should be_nil
217
+ end
218
+
219
+ it "should translate empty strings to nil" do
220
+ @definition.blocks.first.call("").should be_nil
221
+ @definition.blocks.first.call(" ").should be_nil
222
+ end
223
+ end
224
+
225
+ describe "Integer" do
226
+ before do
227
+ @definition = ROXML::Definition.new(:intvalue, :as => Integer)
228
+ end
229
+
230
+ it_should_behave_like "block shorthand type declaration"
231
+
232
+ it "should translate text to integers" do
233
+ @definition.blocks.first['3'].should == 3
234
+ @definition.blocks.first['792'].should == 792
235
+ end
236
+
237
+ it "should raise on non-integer values" do
238
+ proc { @definition.blocks.first['08'] }.should raise_error(ArgumentError)
239
+ proc { @definition.blocks.first['793.12'] }.should raise_error(ArgumentError)
240
+ proc { @definition.blocks.first['junk 11'] }.should raise_error(ArgumentError)
241
+ proc { @definition.blocks.first['11sttf'] }.should raise_error(ArgumentError)
242
+ end
243
+
244
+ context "when passed an array" do
245
+ it "should translate the array elements to integer" do
246
+ @definition.blocks.first.call(["792", "12", "328"]).should == [792, 12, 328]
247
+ end
248
+ end
249
+ end
250
+
251
+ describe "Float" do
252
+ before do
253
+ @definition = ROXML::Definition.new(:floatvalue, :as => Float)
254
+ end
255
+
256
+ it_should_behave_like "block shorthand type declaration"
257
+
258
+ it "should translate text to float" do
259
+ @definition.blocks.first['3'].should == 3.0
260
+ @definition.blocks.first['12.7'].should == 12.7
261
+ end
262
+
263
+ it "should raise on non-float values" do
264
+ proc { @definition.blocks.first['junk 11.3'] }.should raise_error(ArgumentError)
265
+ proc { @definition.blocks.first['11.1sttf'] }.should raise_error(ArgumentError)
266
+ end
267
+
268
+ context "when passed an array" do
269
+ it "should translate the array elements to integer" do
270
+ @definition.blocks.first.call(["792.13", "240", "3.14"]).should == [792.13, 240.0, 3.14]
271
+ end
272
+ end
273
+ end
274
+
275
+ describe "BigDecimal" do
276
+ before do
277
+ @definition = ROXML::Definition.new(:decimalvalue, :as => BigDecimal)
278
+ end
279
+
280
+ it_should_behave_like "block shorthand type declaration"
281
+
282
+ it "should translate text to decimal numbers" do
283
+ @definition.blocks.first['3'].should == BigDecimal.new("3.0")
284
+ @definition.blocks.first['0.3'].should == BigDecimal.new("0.3")
285
+ end
286
+
287
+ it "should extract what it can, and fall back to 0" do
288
+ @definition.blocks.first['junk 11'].should eql(BigDecimal.new("0"))
289
+ @definition.blocks.first['11sttf'].should eql(BigDecimal.new("11.0"))
290
+ end
291
+
292
+ context "when passed an array" do
293
+ it "should translate the array elements to integer" do
294
+ @definition.blocks.first.call(["12.1", "328.2"]).should == [BigDecimal.new("12.1"), BigDecimal.new("328.2")]
295
+ end
296
+ end
297
+ end
298
+
299
+ describe "Fixnum" do
300
+ before do
301
+ @definition = ROXML::Definition.new(:fixnumvalue, :as => Fixnum)
302
+ end
303
+
304
+ it_should_behave_like "block shorthand type declaration"
305
+
306
+ it "should translate text to integers" do
307
+ @definition.blocks.first['3'].should == 3
308
+ @definition.blocks.first['792'].should == 792
309
+ @definition.blocks.first['08'].should == 8
310
+ @definition.blocks.first['279.23'].should == 279
311
+ end
312
+
313
+ it "should extract whatever is possible and fall back to 0" do
314
+ @definition.blocks.first['junk 11'].should eql(0)
315
+ @definition.blocks.first['.?sttf'].should eql(0)
316
+ @definition.blocks.first['11sttf'].should eql(11)
317
+ end
318
+
319
+ context "when passed an array" do
320
+ it "should translate the array elements to integer" do
321
+ @definition.blocks.first.call(["792", "12", "328"]).should == [792, 12, 328]
322
+ end
323
+ end
324
+ end
325
+
326
+ describe ":bool" do
327
+ it "should boolify individual values" do
328
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("1").should be_true
329
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("True").should be_true
330
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("Yes").should be_true
331
+ end
332
+
333
+ context "when an array is passed in" do
334
+ it "should boolify arrays of values" do
335
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("0").should be_false
336
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("false").should be_false
337
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("nO").should be_false
338
+ end
339
+ end
340
+
341
+ context "when no value is detected" do
342
+ it "should return nil" do
343
+ ROXML::Definition.new(:floatvalue, :as => :bool).blocks.first.call("junk").should be_nil
344
+ end
345
+
346
+ context "when a literal block is available" do
347
+ it "should pass the value itself to the block"
348
+ end
349
+ end
350
+ end
351
+
352
+ describe "Time" do
353
+ it "should return nil on empty string" do
354
+ ROXML::Definition.new(:floatvalue, :as => Time).blocks.first.call(" ").should be_nil
355
+ end
356
+
357
+ it "should return a time version of the string" do
358
+ ROXML::Definition.new(:datevalue, :as => Time).blocks.first.call("12:31am").min.should == 31
359
+ end
360
+
361
+ context "when passed an array of values" do
362
+ it "should timify all of them" do
363
+ ROXML::Definition.new(:datevalue, :as => Time).blocks.first.call(["12:31am", "3:00pm", "11:59pm"]).map(&:min).should == [31, 0, 59]
364
+ end
365
+ end
366
+ end
367
+
368
+ describe "Date" do
369
+ before do
370
+ @subject = ROXML::Definition.new(:datevalue, :as => Date)
371
+ end
372
+ it_should_behave_like "Date reference"
373
+ end
374
+
375
+ describe "DateTime" do
376
+ before do
377
+ @subject = ROXML::Definition.new(:datevalue, :as => DateTime)
378
+ end
379
+ it_should_behave_like "DateTime reference"
380
+ end
381
+
382
+ it "should prohibit multiple shorthands" do
383
+ proc { ROXML::Definition.new(:count, :as => [Float, Integer]) }.should raise_error(ArgumentError)
384
+ end
385
+
386
+ it "should stack block shorthands with explicit blocks" do
387
+ ROXML::Definition.new(:count, :as => Integer) {|val| val.to_i }.blocks.size.should == 2
388
+ ROXML::Definition.new(:count, :as => Float) {|val| val.object_id }.blocks.size.should == 2
389
+ end
390
+ end
391
+ end
392
+
393
+ describe ":from" do
394
+ shared_examples_for "attribute reference" do
395
+ it "should be interpreted as :attr" do
396
+ @opts.sought_type.should == :attr
397
+ end
398
+
399
+ it "should strip '@' from name" do
400
+ @opts.name.should == 'attr_name'
401
+ end
402
+
403
+ it "should unescape xml entities" do
404
+ @opts.to_ref(RoxmlObject.new).value_in(%{
405
+ <question attr_name="&quot;Wickard &amp; Filburn&quot; &gt; / &lt; McCulloch &amp; Marryland?" />
406
+ }).should == "\"Wickard & Filburn\" > / < McCulloch & Marryland?"
407
+ end
408
+ end
409
+
410
+ context ":attr" do
411
+ before do
412
+ @opts = ROXML::Definition.new(:attr_name, :from => :attr)
413
+ end
414
+
415
+ it_should_behave_like "attribute reference"
416
+ end
417
+
418
+ context "@attribute_name" do
419
+ before do
420
+ @opts = ROXML::Definition.new(:attr_name, :from => '@attr_name')
421
+ end
422
+
423
+ it_should_behave_like "attribute reference"
424
+ end
425
+
426
+ describe ":content" do
427
+ it "should be recognized" do
428
+ ROXML::Definition.new(:author).content?.should be_false
429
+ ROXML::Definition.new(:author, :from => :content).content?.should == true
430
+ end
431
+
432
+ it "should be equivalent to :from => '.'" do
433
+ ROXML::Definition.new(:author, :from => '.').content?.should == true
434
+ end
435
+ end
436
+ end
437
+
438
+ describe ":in" do
439
+ context "as xpath" do
440
+ it "should pass through as wrapper" do
441
+ ROXML::Definition.new(:manufacturer, :in => './').wrapper.should == './'
442
+ end
443
+ end
444
+
445
+ context "as xpath" do
446
+ it "should pass through as wrapper" do
447
+ ROXML::Definition.new(:manufacturer, :in => 'wrapper').wrapper.should == 'wrapper'
448
+ end
449
+ end
450
+ end
451
+
452
+ describe "options" do
453
+
454
+ shared_examples_for "boolean option" do
455
+ it "should be recognized" do
456
+ ROXML::Definition.new(:author, :from => :content, @option => true).respond_to?(:"#{@option}?")
457
+ ROXML::Definition.new(:author, :from => :content, @option => true).send(:"#{@option}?").should be_true
458
+ ROXML::Definition.new(:author, :from => :content, @option => false).send(:"#{@option}?").should be_false
459
+ end
460
+
461
+ it "should default to false" do
462
+ ROXML::Definition.new(:author, :from => :content).send(:"#{@option}?").should be_false
463
+ end
464
+ end
465
+
466
+ describe ":required" do
467
+ before do
468
+ @option = :required
469
+ end
470
+
471
+ it_should_behave_like "boolean option"
472
+
473
+ it "should not be allowed together with :else" do
474
+ proc { ROXML::Definition.new(:author, :from => :content, :required => true, :else => 'Johnny') }.should raise_error(ArgumentError)
475
+ proc { ROXML::Definition.new(:author, :from => :content, :required => false, :else => 'Johnny') }.should_not raise_error
476
+ end
477
+ end
478
+
479
+ describe ":frozen" do
480
+ before do
481
+ @option = :frozen
482
+ end
483
+
484
+ it_should_behave_like "boolean option"
485
+ end
486
+
487
+ describe ":cdata" do
488
+ before do
489
+ @option = :cdata
490
+ end
491
+
492
+ it_should_behave_like "boolean option"
493
+ end
494
+ end
495
+ end