representable 3.0.0 → 3.2.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 (103) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +23 -0
  3. data/CHANGES.md +33 -0
  4. data/Gemfile +4 -12
  5. data/LICENSE +1 -1
  6. data/README.md +12 -13
  7. data/Rakefile +1 -6
  8. data/TODO +1 -3
  9. data/TODO-4.0.md +72 -0
  10. data/lib/representable/binding.rb +32 -12
  11. data/lib/representable/cached.rb +1 -1
  12. data/lib/representable/coercion.rb +8 -6
  13. data/lib/representable/config.rb +13 -3
  14. data/lib/representable/debug.rb +23 -15
  15. data/lib/representable/declarative.rb +12 -7
  16. data/lib/representable/decorator.rb +1 -1
  17. data/lib/representable/definition.rb +7 -3
  18. data/lib/representable/deserializer.rb +5 -4
  19. data/lib/representable/for_collection.rb +1 -1
  20. data/lib/representable/hash/allow_symbols.rb +9 -11
  21. data/lib/representable/hash/binding.rb +1 -0
  22. data/lib/representable/hash/collection.rb +4 -2
  23. data/lib/representable/hash.rb +9 -2
  24. data/lib/representable/hash_methods.rb +3 -2
  25. data/lib/representable/insert.rb +1 -1
  26. data/lib/representable/json/collection.rb +3 -0
  27. data/lib/representable/json.rb +8 -7
  28. data/lib/representable/object/binding.rb +5 -1
  29. data/lib/representable/object.rb +1 -1
  30. data/lib/representable/option.rb +19 -0
  31. data/lib/representable/pipeline.rb +3 -2
  32. data/lib/representable/pipeline_factories.rb +4 -2
  33. data/lib/representable/populator.rb +1 -1
  34. data/lib/representable/represent.rb +1 -0
  35. data/lib/representable/serializer.rb +3 -2
  36. data/lib/representable/version.rb +1 -1
  37. data/lib/representable/xml/binding.rb +19 -13
  38. data/lib/representable/xml/namespace.rb +122 -0
  39. data/lib/representable/xml.rb +12 -10
  40. data/lib/representable/yaml/binding.rb +1 -0
  41. data/lib/representable/yaml.rb +6 -2
  42. data/lib/representable.rb +19 -25
  43. data/representable.gemspec +8 -9
  44. data/test/as_test.rb +7 -7
  45. data/test/binding_test.rb +14 -14
  46. data/test/cached_test.rb +59 -49
  47. data/test/class_test.rb +9 -9
  48. data/test/coercion_test.rb +33 -22
  49. data/test/config/inherit_test.rb +14 -14
  50. data/test/config_test.rb +20 -20
  51. data/test/decorator_scope_test.rb +4 -4
  52. data/test/decorator_test.rb +33 -20
  53. data/test/default_test.rb +8 -8
  54. data/test/defaults_options_test.rb +3 -3
  55. data/test/definition_test.rb +38 -40
  56. data/test/{example.rb → examples/example.rb} +0 -1
  57. data/test/examples/object.rb +1 -5
  58. data/test/exec_context_test.rb +8 -8
  59. data/test/features_test.rb +6 -6
  60. data/test/filter_test.rb +8 -8
  61. data/test/for_collection_test.rb +10 -10
  62. data/test/generic_test.rb +13 -13
  63. data/test/getter_setter_test.rb +5 -5
  64. data/test/hash_bindings_test.rb +1 -1
  65. data/test/hash_test.rb +45 -23
  66. data/test/heritage_test.rb +16 -13
  67. data/test/if_test.rb +9 -9
  68. data/test/include_exclude_test.rb +14 -14
  69. data/test/inherit_test.rb +18 -18
  70. data/test/inline_test.rb +24 -24
  71. data/test/instance_test.rb +31 -31
  72. data/test/is_representable_test.rb +10 -10
  73. data/test/json_test.rb +29 -7
  74. data/test/lonely_test.rb +31 -31
  75. data/test/nested_test.rb +13 -13
  76. data/test/object_test.rb +9 -9
  77. data/test/option_test.rb +36 -0
  78. data/test/parse_pipeline_test.rb +3 -5
  79. data/test/pipeline_test.rb +50 -50
  80. data/test/populator_test.rb +18 -18
  81. data/test/prepare_test.rb +4 -4
  82. data/test/private_options_test.rb +2 -2
  83. data/test/reader_writer_test.rb +2 -2
  84. data/test/render_nil_test.rb +2 -2
  85. data/test/represent_test.rb +14 -14
  86. data/test/representable_test.rb +34 -36
  87. data/test/schema_test.rb +8 -11
  88. data/test/serialize_deserialize_test.rb +2 -2
  89. data/test/skip_test.rb +14 -14
  90. data/test/stringify_hash_test.rb +3 -3
  91. data/test/test_helper.rb +26 -14
  92. data/test/uncategorized_test.rb +10 -10
  93. data/test/user_options_test.rb +4 -4
  94. data/test/wrap_test.rb +19 -19
  95. data/test/xml_bindings_test.rb +0 -4
  96. data/test/xml_namespace_test.rb +186 -0
  97. data/test/xml_test.rb +103 -43
  98. data/test/yaml_test.rb +51 -26
  99. metadata +42 -35
  100. data/.travis.yml +0 -7
  101. data/lib/representable/TODO.getting_serious +0 -11
  102. data/lib/representable/autoload.rb +0 -10
  103. data/test/mongoid_test.rb +0 -31
data/test/test_helper.rb CHANGED
@@ -1,16 +1,20 @@
1
+ require 'pry-byebug'
1
2
  require 'representable'
2
3
 
3
- require "json"
4
- require "psych"
5
-
6
- require 'representable/json'
7
- require 'representable/xml'
8
- require 'representable/yaml'
9
4
  require 'minitest/autorun'
10
5
  require 'test_xml/mini_test'
11
6
 
12
7
  require "representable/debug"
8
+ require 'minitest/assertions'
13
9
 
10
+ module MiniTest::Assertions
11
+ def assert_equal_xml(text, subject)
12
+ assert_equal (text.gsub("\n", "").gsub(/(\s\s+)/, "")), subject.gsub("\n", "").gsub(/(\s\s+)/, "")
13
+ end
14
+ end
15
+ String.infect_an_assertion :assert_equal_xml, :must_xml
16
+
17
+ # TODO: delete all that in 4.0:
14
18
  class Album
15
19
  attr_accessor :songs, :best_song
16
20
  def initialize(songs=nil, best_song=nil)
@@ -45,7 +49,7 @@ module AssertJson
45
49
  module Assertions
46
50
  def assert_json(expected, actual, msg=nil)
47
51
  msg = message(msg, "") { diff expected, actual }
48
- assert(expected.split("").sort == actual.split("").sort, msg)
52
+ assert_equal(expected.split("").sort, actual.split("").sort, msg)
49
53
  end
50
54
  end
51
55
  end
@@ -83,7 +87,7 @@ MiniTest::Spec.class_eval do
83
87
  end
84
88
 
85
89
  def self.representer!(options={}, &block)
86
- fmt = options # we need that so the 2nd call to ::let (within a ::describe) remembers the right format.
90
+ fmt = options # we need that so the 2nd call to ::let(within a ::describe) remembers the right format.
87
91
 
88
92
  name = options[:name] || :representer
89
93
  format = options[:module] || Representable::Hash
@@ -101,6 +105,8 @@ MiniTest::Spec.class_eval do
101
105
  mod
102
106
  end
103
107
 
108
+ undef :inject_representer if method_defined? :inject_representer
109
+
104
110
  def inject_representer(mod, options)
105
111
  return unless options[:inject]
106
112
 
@@ -116,7 +122,7 @@ MiniTest::Spec.class_eval do
116
122
  def representer_for(modules=[Representable::Hash], &block)
117
123
  Module.new do
118
124
  extend TestMethods
119
- include *modules
125
+ include(*modules)
120
126
  module_exec(&block)
121
127
  end
122
128
  end
@@ -127,9 +133,15 @@ MiniTest::Spec.class_eval do
127
133
  end
128
134
 
129
135
  class BaseTest < MiniTest::Spec
130
- let (:new_album) { OpenStruct.new.extend(representer) }
131
- let (:album) { OpenStruct.new(:songs => ["Fuck Armageddon"]).extend(representer) }
132
- let (:song) { OpenStruct.new(:title => "Resist Stance") }
133
- let (:song_representer) { Module.new do include Representable::Hash; property :title end }
136
+ let(:new_album) { OpenStruct.new.extend(representer) }
137
+ let(:album) { OpenStruct.new(:songs => ["Fuck Armageddon"]).extend(representer) }
138
+ let(:song) { OpenStruct.new(:title => "Resist Stance") }
139
+ let(:song_representer) { Module.new do include Representable::Hash; property :title end }
134
140
 
135
- end
141
+ end
142
+
143
+ Band = Struct.new(:id, :name) do
144
+ def [](*attrs)
145
+ attrs.collect { |attr| send(attr) }
146
+ end
147
+ end
@@ -15,12 +15,12 @@ class StopWhenIncomingObjectFragmentIsNilTest < MiniTest::Spec
15
15
 
16
16
  it do
17
17
  album = Album.new
18
- representer.new(album).from_hash({"id"=>1, "songs"=>[{"title"=>"Walkie Talkie"}]}).songs.must_equal [Song.new("Walkie Talkie")]
18
+ _(representer.new(album).from_hash({"id"=>1, "songs"=>[{"title"=>"Walkie Talkie"}]}).songs).must_equal [Song.new("Walkie Talkie")]
19
19
  end
20
20
 
21
21
  it do
22
22
  album = Album.new(2, ["original"])
23
- representer.new(album).from_hash({"id"=>1, "songs"=>nil}).songs.must_equal ["original"]
23
+ _(representer.new(album).from_hash({"id"=>1, "songs"=>nil}).songs).must_equal ["original"]
24
24
  end
25
25
 
26
26
  end
@@ -35,13 +35,13 @@ class RenderPipelineOptionTest < MiniTest::Spec
35
35
  end
36
36
  end
37
37
 
38
- it { representer.new(Album.new).to_hash(user_options: {function: NilToNA}).must_equal({"id"=>"n/a"}) }
39
- it { representer.new(Album.new(1)).to_hash(user_options: {function: NilToNA}).must_equal({"id"=>1}) }
38
+ it { _(representer.new(Album.new).to_hash(user_options: {function: NilToNA})).must_equal({"id"=>"n/a"}) }
39
+ it { _(representer.new(Album.new(1)).to_hash(user_options: {function: NilToNA})).must_equal({"id"=>1}) }
40
40
 
41
41
  it "is cached" do
42
42
  decorator = representer.new(Album.new)
43
- decorator.to_hash(user_options: {function: NilToNA}).must_equal({"id"=>"n/a"})
44
- decorator.to_hash(user_options: {function: nil}).must_equal({"id"=>"n/a"}) # non-sense function is not applied.
43
+ _(decorator.to_hash(user_options: {function: NilToNA})).must_equal({"id"=>"n/a"})
44
+ _(decorator.to_hash(user_options: {function: nil})).must_equal({"id"=>"n/a"}) # non-sense function is not applied.
45
45
  end
46
46
 
47
47
  end
@@ -56,12 +56,12 @@ class ParsePipelineOptionTest < MiniTest::Spec
56
56
  end
57
57
  end
58
58
 
59
- it { representer.new(Album.new).from_hash({"id"=>nil}, user_options: {function: NilToNA}).id.must_equal "n/a" }
60
- it { representer.new(Album.new(1)).to_hash(user_options: {function: NilToNA}).must_equal({"id"=>1}) }
59
+ it { _(representer.new(Album.new).from_hash({"id"=>nil}, user_options: {function: NilToNA}).id).must_equal "n/a" }
60
+ it { _(representer.new(Album.new(1)).to_hash(user_options: {function: NilToNA})).must_equal({"id"=>1}) }
61
61
 
62
62
  it "is cached" do
63
63
  decorator = representer.new(Album.new)
64
- decorator.from_hash({"id"=>nil}, user_options: {function: NilToNA}).id.must_equal "n/a"
65
- decorator.from_hash({"id"=>nil}, user_options: {function: "nonsense"}).id.must_equal "n/a" # non-sense function is not applied.
64
+ _(decorator.from_hash({"id"=>nil}, user_options: {function: NilToNA}).id).must_equal "n/a"
65
+ _(decorator.from_hash({"id"=>nil}, user_options: {function: "nonsense"}).id).must_equal "n/a" # non-sense function is not applied.
66
66
  end
67
67
  end
@@ -7,9 +7,9 @@ class UserOptionsTest < Minitest::Spec
7
7
  property :title, if: ->(options) { options[:user_options][:visible] }
8
8
  end
9
9
 
10
- it { Song.new("Run With It").extend(representer).to_hash.must_equal({}) }
11
- it { Song.new("Run With It").extend(representer).to_hash(user_options: {visible: true}).must_equal({"title"=>"Run With It"}) }
12
- it { Song.new("Run With It").extend(representer).from_hash("title"=>"Business Conduct").title.must_equal "Run With It" }
13
- it { Song.new("Run With It").extend(representer).from_hash({"title"=>"Business Conduct"}, user_options: {visible: true}).title.must_equal "Business Conduct" }
10
+ it { _(Song.new("Run With It").extend(representer).to_hash).must_equal({}) }
11
+ it { _(Song.new("Run With It").extend(representer).to_hash(user_options: {visible: true})).must_equal({"title"=>"Run With It"}) }
12
+ it { _(Song.new("Run With It").extend(representer).from_hash("title"=>"Business Conduct").title).must_equal "Run With It" }
13
+ it { _(Song.new("Run With It").extend(representer).from_hash({"title"=>"Business Conduct"}, user_options: {visible: true}).title).must_equal "Business Conduct" }
14
14
  end
15
15
  # Representable.deprecations=false
data/test/wrap_test.rb CHANGED
@@ -8,10 +8,10 @@ class WrapTest < MiniTest::Spec
8
8
  class SoftcoreBand < HardcoreBand
9
9
  end
10
10
 
11
- let (:band) { HardcoreBand.new }
11
+ let(:band) { HardcoreBand.new }
12
12
 
13
13
  it "returns false per default" do
14
- assert_equal nil, SoftcoreBand.new.send(:representation_wrap)
14
+ assert_nil SoftcoreBand.new.send(:representation_wrap)
15
15
  end
16
16
 
17
17
  it "infers a printable class name if set to true" do
@@ -32,17 +32,17 @@ class WrapTest < MiniTest::Spec
32
32
  ) do |format, mod, output, input|
33
33
 
34
34
  describe "[#{format}] dynamic wrap" do
35
- let (:band) { representer.prepare(Struct.new(:name, :genre).new("Blink", "Pop")) }
36
- let (:format) { format }
35
+ let(:band) { representer.prepare(Struct.new(:name, :genre).new("Blink", "Pop")) }
36
+ let(:format) { format }
37
37
 
38
38
  representer!(:module => mod) do
39
- self.representation_wrap = lambda { |args| "#{name}#{args[:number]}" }
39
+ self.representation_wrap = lambda { |number:, **| "#{name}#{number}" }
40
40
  property :genre
41
41
  end
42
42
 
43
43
  it { render(band, {:number => 182}).must_equal_document(output) }
44
44
 
45
- it { parse(band, input, {:number => 182}).genre.must_equal "Poppunk" } # TODO: better test. also, xml parses _any_ wrap.
45
+ it { _(parse(band, input, {:number => 182}).genre).must_equal "Poppunk" } # TODO: better test. also, xml parses _any_ wrap.
46
46
  end
47
47
  end
48
48
  end
@@ -65,19 +65,19 @@ class HashDisableWrapTest < MiniTest::Spec
65
65
  end
66
66
  end
67
67
 
68
- let (:band) { BandDecorator.prepare(Band.new("Social Distortion")) }
68
+ let(:band) { BandDecorator.prepare(Band.new("Social Distortion")) }
69
69
 
70
70
  # direct, local api.
71
71
  it do
72
- band.to_hash.must_equal({"bands" => {"name"=>"Social Distortion"}})
73
- band.to_hash(wrap: false).must_equal({"name"=>"Social Distortion"})
74
- band.to_hash(wrap: :band).must_equal(:band=>{"name"=>"Social Distortion"})
72
+ _(band.to_hash).must_equal({"bands" => {"name"=>"Social Distortion"}})
73
+ _(band.to_hash(wrap: false)).must_equal({"name"=>"Social Distortion"})
74
+ _(band.to_hash(wrap: :band)).must_equal(:band=>{"name"=>"Social Distortion"})
75
75
  end
76
76
 
77
77
  it do
78
- band.from_hash({"bands" => {"name"=>"Social Distortion"}}).name.must_equal "Social Distortion"
79
- band.from_hash({"name"=>"Social Distortion"}, wrap: false).name.must_equal "Social Distortion"
80
- band.from_hash({band: {"name"=>"Social Distortion"}}, wrap: :band).name.must_equal "Social Distortion"
78
+ _(band.from_hash({"bands" => {"name"=>"Social Distortion"}}).name).must_equal "Social Distortion"
79
+ _(band.from_hash({"name"=>"Social Distortion"}, wrap: false).name).must_equal "Social Distortion"
80
+ _(band.from_hash({band: {"name"=>"Social Distortion"}}, wrap: :band).name).must_equal "Social Distortion"
81
81
  end
82
82
 
83
83
 
@@ -90,15 +90,15 @@ class HashDisableWrapTest < MiniTest::Spec
90
90
  end
91
91
 
92
92
 
93
- let (:album) { AlbumDecorator.prepare(Album.new(Band.new("Social Distortion", Label.new("Epitaph")))) }
93
+ let(:album) { AlbumDecorator.prepare(Album.new(Band.new("Social Distortion", Label.new("Epitaph")))) }
94
94
 
95
95
  # band has wrap turned off per property definition, however, label still has wrap.
96
96
  it "renders" do
97
- album.to_hash.must_equal({"albums" => {"band" => {"name"=>"Social Distortion", "label"=>{"important"=>{"name"=>"Epitaph"}}}}})
97
+ _(album.to_hash).must_equal({"albums" => {"band" => {"name"=>"Social Distortion", "label"=>{"important"=>{"name"=>"Epitaph"}}}}})
98
98
  end
99
99
 
100
100
  it "parses" do
101
- album.from_hash({"albums" => {"band" => {"name"=>"Rvivr"}}}).band.name.must_equal "Rvivr"
101
+ _(album.from_hash({"albums" => {"band" => {"name"=>"Rvivr"}}}).band.name).must_equal "Rvivr"
102
102
  end
103
103
  end
104
104
 
@@ -120,7 +120,7 @@ class XMLDisableWrapTest < MiniTest::Spec
120
120
  # end
121
121
  end
122
122
 
123
- let (:band) { BandDecorator.prepare(Band.new("Social Distortion")) }
123
+ let(:band) { BandDecorator.prepare(Band.new("Social Distortion")) }
124
124
 
125
125
  it do
126
126
  band.to_xml.must_equal_xml "<bands><name>Social Distortion</name></bands>"
@@ -137,12 +137,12 @@ class XMLDisableWrapTest < MiniTest::Spec
137
137
  end
138
138
 
139
139
 
140
- let (:album) { AlbumDecorator.prepare(Album.new(Band.new("Social Distortion", Label.new("Epitaph")))) }
140
+ let(:album) { AlbumDecorator.prepare(Album.new(Band.new("Social Distortion", Label.new("Epitaph")))) }
141
141
 
142
142
  # band has wrap turned of per property definition, however, label still has wrap.
143
143
  it "rendersxx" do
144
144
  skip
145
- album.to_xml.must_equal({"albums" => {"band" => {"name"=>"Social Distortion", "label"=>{"important"=>{"name"=>"Epitaph"}}}}})
145
+ _(album.to_xml).must_equal({"albums" => {"band" => {"name"=>"Social Distortion", "label"=>{"important"=>{"name"=>"Epitaph"}}}}})
146
146
  end
147
147
 
148
148
  # it "parses" do
@@ -1,10 +1,6 @@
1
1
  require 'test_helper'
2
- require 'representable/json' # FIXME.
3
- require 'representable/xml/collection'
4
2
  require 'representable/xml/hash'
5
3
 
6
- require 'representable/xml'
7
-
8
4
  class XMLBindingTest < MiniTest::Spec
9
5
  module SongRepresenter
10
6
  include Representable::XML
@@ -0,0 +1,186 @@
1
+ require "test_helper"
2
+
3
+ # <lib:library
4
+ # xmlns:lib="http://eric.van-der-vlist.com/ns/library"
5
+ # xmlns:hr="http://eric.van-der-vlist.com/ns/person">
6
+ # <lib:book id="b0836217462" available="true">
7
+ # <lib:isbn>0836217462</lib:isbn>
8
+ # <lib:title xml:lang="en">Being a Dog Is a Full-Time Job</lib:title>
9
+ # <hr:author id="CMS">
10
+ # <hr:name>Charles M Schulz</hr:name>
11
+ # <hr:born>1922-11-26</hr:born>
12
+ # <hr:dead>2000-02-12</hr:dead>
13
+ # </hr:author>
14
+ # <lib:character id="PP">
15
+ # <hr:name>Peppermint Patty</hr:name>
16
+ # <hr:born>1966-08-22</hr:born>
17
+ # <lib:qualification>bold, brash and tomboyish</lib:qualification>
18
+ # </lib:character>
19
+ # <lib:character id="Snoopy">
20
+ # <hr:name>Snoopy</hr:name>
21
+ # <hr:born>1950-10-04</hr:born>
22
+ # <lib:qualification>extroverted beagle</lib:qualification>
23
+ # </lib:character>
24
+ # <lib:character id="Schroeder">
25
+ # <hr:name>Schroeder</hr:name>
26
+ # <hr:born>1951-05-30</hr:born>
27
+ # <lib:qualification>brought classical music to the Peanuts strip
28
+ # </lib:qualification>
29
+ # </lib:character>
30
+ # <lib:character id="Lucy">
31
+ # <hr:name>Lucy</hr:name>
32
+ # <hr:born>1952-03-03</hr:born>
33
+ # <lib:qualification>bossy, crabby and selfish</lib:qualification>
34
+ # </lib:character>
35
+ # </lib:book>
36
+ # </lib:library>
37
+
38
+ # Theoretically, every property (scalar) needs/should have a namespace.
39
+ # property :name, namespace: "http://eric.van-der-vlist.com/ns/person"
40
+ # # This is implicit if contained:
41
+ # class Person < Decorator
42
+ # namespace: "http://eric.van-der-vlist.com/ns/person"
43
+ # property :name #, namespace: "http://eric.van-der-vlist.com/ns/person"
44
+ # end
45
+ # class Library
46
+ # namespace "http://eric.van-der-vlist.com/ns/library"
47
+
48
+ # namespace_def lib: "http://eric.van-der-vlist.com/ns/library"
49
+ # namespace_def person: "http://eric.van-der-vlist.com/ns/person"
50
+ # # namespace_def person: Person
51
+
52
+ # property :person, decorator: Person
53
+ # end
54
+ class NamespaceXMLTest < Minitest::Spec
55
+ module Model
56
+ Library = Struct.new(:book)
57
+ Book = Struct.new(:id, :isbn)
58
+ end
59
+
60
+ let (:book) { Model::Book.new(1, 666) }
61
+
62
+ #:simple-class
63
+ class Library < Representable::Decorator
64
+ feature Representable::XML
65
+ feature Representable::XML::Namespace
66
+
67
+ namespace "http://eric.van-der-vlist.com/ns/library"
68
+
69
+ property :book do
70
+ namespace "http://eric.van-der-vlist.com/ns/library"
71
+
72
+ property :id, attribute: true
73
+ property :isbn
74
+ end
75
+ end
76
+ #:simple-class end
77
+
78
+
79
+ # default namespace for library
80
+ it "what" do
81
+ Library.new(Model::Library.new(book)).to_xml.must_xml(
82
+
83
+ #:simple-xml
84
+ %{<library xmlns="http://eric.van-der-vlist.com/ns/library">
85
+ <book id="1">
86
+ <isbn>666</isbn>
87
+ </book>
88
+ </library>}
89
+ #:simple-xml end
90
+ )
91
+ end
92
+ end
93
+
94
+ class Namespace2XMLTest < Minitest::Spec
95
+ module Model
96
+ Library = Struct.new(:book)
97
+ Book = Struct.new(:id, :isbn, :author, :character)
98
+ Character = Struct.new(:name, :born, :qualification)
99
+ end
100
+
101
+ let (:book) { Model::Book.new(1, 666, Model::Character.new("Fowler"), [Model::Character.new("Frau Java", 1991, "typed")]) }
102
+
103
+ #:map-class
104
+ class Library < Representable::Decorator
105
+ feature Representable::XML
106
+ feature Representable::XML::Namespace
107
+
108
+ namespace "http://eric.van-der-vlist.com/ns/library"
109
+ namespace_def lib: "http://eric.van-der-vlist.com/ns/library"
110
+ namespace_def hr: "http://eric.van-der-vlist.com/ns/person"
111
+
112
+ property :book, class: Model::Book do
113
+ namespace "http://eric.van-der-vlist.com/ns/library"
114
+
115
+ property :id, attribute: true
116
+ property :isbn
117
+
118
+ property :author, class: Model::Character do
119
+ namespace "http://eric.van-der-vlist.com/ns/person"
120
+
121
+ property :name
122
+ property :born
123
+ end
124
+
125
+ collection :character, class: Model::Character do
126
+ namespace "http://eric.van-der-vlist.com/ns/library"
127
+
128
+ property :qualification
129
+
130
+ property :name, namespace: "http://eric.van-der-vlist.com/ns/person"
131
+ property :born, namespace: "http://eric.van-der-vlist.com/ns/person"
132
+ end
133
+ end
134
+ end
135
+ #:map-class end
136
+
137
+ it "renders" do
138
+ Library.new(Model::Library.new(book)).to_xml.must_xml(
139
+ #:map-xml
140
+ %{<lib:library xmlns:lib=\"http://eric.van-der-vlist.com/ns/library\" xmlns:hr=\"http://eric.van-der-vlist.com/ns/person\">
141
+ <lib:book id=\"1\">
142
+ <lib:isbn>666</lib:isbn>
143
+ <hr:author>
144
+ <hr:name>Fowler</hr:name>
145
+ </hr:author>
146
+ <lib:character>
147
+ <lib:qualification>typed</lib:qualification>
148
+ <hr:name>Frau Java</hr:name>
149
+ <hr:born>1991</hr:born>
150
+ </lib:character>
151
+ </lib:book>
152
+ </lib:library>}
153
+ #:map-xml end
154
+ )
155
+ end
156
+
157
+ it "parses" do
158
+ lib = Model::Library.new
159
+ #:parse-call
160
+ Library.new(lib).from_xml(%{<lib:library
161
+ xmlns:lib="http://eric.van-der-vlist.com/ns/library"
162
+ xmlns:hr="http://eric.van-der-vlist.com/ns/person">
163
+ <lib:book id="1">
164
+ <lib:isbn>666</lib:isbn>
165
+ <hr:author>
166
+ <hr:name>Fowler</hr:name>
167
+ </hr:author>
168
+ <lib:character>
169
+ <lib:qualification>typed</lib:qualification>
170
+ <hr:name>Frau Java</hr:name>
171
+ <bogus:name>Mr. Ruby</hr:name>
172
+ <name>Dr. Elixir</hr:name>
173
+ <hr:born>1991</hr:born>
174
+ </lib:character>
175
+ </lib:book>
176
+ </lib:library>}
177
+ #:parse-call end
178
+ )
179
+
180
+ _(lib.book.inspect).must_equal %{#<struct Namespace2XMLTest::Model::Book id=\"1\", isbn=\"666\", author=#<struct Namespace2XMLTest::Model::Character name=\"Fowler\", born=nil, qualification=nil>, character=[#<struct Namespace2XMLTest::Model::Character name=\"Frau Java\", born=\"1991\", qualification=\"typed\">]>}
181
+
182
+ #:parse-res
183
+ lib.book.character[0].name #=> "Frau Java"
184
+ #:parse-res end
185
+ end
186
+ end