representable 2.4.0.rc3 → 2.4.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +7 -2
  3. data/Rakefile +6 -0
  4. data/lib/representable.rb +21 -17
  5. data/lib/representable/binding.rb +3 -2
  6. data/lib/representable/definition.rb +1 -1
  7. data/lib/representable/deprecations.rb +31 -5
  8. data/lib/representable/deserializer.rb +24 -34
  9. data/lib/representable/hash/collection.rb +9 -2
  10. data/lib/representable/hash_methods.rb +2 -2
  11. data/lib/representable/parse_strategies.rb +6 -7
  12. data/lib/representable/pipeline.rb +12 -1
  13. data/lib/representable/pipeline_factories.rb +9 -2
  14. data/lib/representable/serializer.rb +3 -3
  15. data/lib/representable/version.rb +1 -1
  16. data/test-with-deprecations/as_test.rb +65 -0
  17. data/test-with-deprecations/benchmarking.rb +83 -0
  18. data/test-with-deprecations/binding_test.rb +46 -0
  19. data/test-with-deprecations/blaaaaaaaa_test.rb +69 -0
  20. data/test-with-deprecations/cached_test.rb +147 -0
  21. data/test-with-deprecations/class_test.rb +119 -0
  22. data/test-with-deprecations/coercion_test.rb +52 -0
  23. data/test-with-deprecations/config/inherit_test.rb +135 -0
  24. data/test-with-deprecations/config_test.rb +122 -0
  25. data/test-with-deprecations/decorator_scope_test.rb +28 -0
  26. data/test-with-deprecations/decorator_test.rb +96 -0
  27. data/test-with-deprecations/default_test.rb +34 -0
  28. data/test-with-deprecations/defaults_options_test.rb +93 -0
  29. data/test-with-deprecations/definition_test.rb +264 -0
  30. data/test-with-deprecations/example.rb +310 -0
  31. data/test-with-deprecations/examples/object.rb +31 -0
  32. data/test-with-deprecations/exec_context_test.rb +93 -0
  33. data/test-with-deprecations/features_test.rb +70 -0
  34. data/test-with-deprecations/filter_test.rb +57 -0
  35. data/test-with-deprecations/for_collection_test.rb +74 -0
  36. data/test-with-deprecations/generic_test.rb +116 -0
  37. data/test-with-deprecations/getter_setter_test.rb +21 -0
  38. data/test-with-deprecations/hash_bindings_test.rb +87 -0
  39. data/test-with-deprecations/hash_test.rb +160 -0
  40. data/test-with-deprecations/heritage_test.rb +62 -0
  41. data/test-with-deprecations/if_test.rb +79 -0
  42. data/test-with-deprecations/include_exclude_test.rb +88 -0
  43. data/test-with-deprecations/inherit_test.rb +159 -0
  44. data/test-with-deprecations/inline_test.rb +272 -0
  45. data/test-with-deprecations/instance_test.rb +266 -0
  46. data/test-with-deprecations/is_representable_test.rb +77 -0
  47. data/test-with-deprecations/json_test.rb +355 -0
  48. data/test-with-deprecations/lonely_test.rb +239 -0
  49. data/test-with-deprecations/mongoid_test.rb +31 -0
  50. data/test-with-deprecations/nested_test.rb +115 -0
  51. data/test-with-deprecations/object_test.rb +60 -0
  52. data/{test/---deserialize-pipeline_test.rb → test-with-deprecations/parse_pipeline_test.rb} +29 -2
  53. data/test-with-deprecations/parse_strategy_test.rb +279 -0
  54. data/{test → test-with-deprecations}/pass_options_test.rb +0 -0
  55. data/test-with-deprecations/pipeline_test.rb +277 -0
  56. data/test-with-deprecations/populator_test.rb +105 -0
  57. data/test-with-deprecations/prepare_test.rb +67 -0
  58. data/test-with-deprecations/private_options_test.rb +18 -0
  59. data/test-with-deprecations/reader_writer_test.rb +19 -0
  60. data/test-with-deprecations/realistic_benchmark.rb +115 -0
  61. data/test-with-deprecations/render_nil_test.rb +21 -0
  62. data/test-with-deprecations/represent_test.rb +88 -0
  63. data/test-with-deprecations/representable_test.rb +511 -0
  64. data/test-with-deprecations/schema_test.rb +148 -0
  65. data/test-with-deprecations/serialize_deserialize_test.rb +33 -0
  66. data/test-with-deprecations/skip_test.rb +81 -0
  67. data/test-with-deprecations/stringify_hash_test.rb +41 -0
  68. data/test-with-deprecations/test_helper.rb +135 -0
  69. data/test-with-deprecations/test_helper_test.rb +25 -0
  70. data/test-with-deprecations/uncategorized_test.rb +67 -0
  71. data/test-with-deprecations/user_options_test.rb +15 -0
  72. data/test-with-deprecations/wrap_test.rb +152 -0
  73. data/test-with-deprecations/xml_bindings_test.rb +62 -0
  74. data/test-with-deprecations/xml_test.rb +503 -0
  75. data/test-with-deprecations/yaml_test.rb +162 -0
  76. data/test/as_test.rb +3 -3
  77. data/test/cached_test.rb +2 -2
  78. data/test/class_test.rb +5 -5
  79. data/test/exec_context_test.rb +2 -2
  80. data/test/filter_test.rb +1 -1
  81. data/test/getter_setter_test.rb +4 -4
  82. data/test/if_test.rb +2 -2
  83. data/test/include_exclude_test.rb +88 -0
  84. data/test/instance_test.rb +15 -15
  85. data/test/lonely_test.rb +18 -2
  86. data/test/object_test.rb +4 -4
  87. data/test/parse_pipeline_test.rb +64 -0
  88. data/test/parse_strategy_test.rb +3 -3
  89. data/test/pipeline_test.rb +8 -12
  90. data/test/prepare_test.rb +2 -3
  91. data/test/reader_writer_test.rb +3 -3
  92. data/test/representable_test.rb +12 -48
  93. data/test/serialize_deserialize_test.rb +9 -9
  94. data/test/skip_test.rb +11 -11
  95. data/test/test_helper.rb +2 -0
  96. data/test/uncategorized_test.rb +10 -10
  97. data/test/user_options_test.rb +15 -0
  98. data/test/wrap_test.rb +1 -1
  99. metadata +65 -4
@@ -0,0 +1,67 @@
1
+ require "test_helper"
2
+
3
+ class StopWhenIncomingObjectFragmentIsNilTest < MiniTest::Spec
4
+ Album = Struct.new(:id, :songs)
5
+ Song = Struct.new(:title)
6
+
7
+ representer!(decorator: true) do
8
+ property :id
9
+ collection :songs, class: Song, parse_pipeline: ->(input, options) { # TODO: test if :doc is set for parsing. test if options are ok and contain :user_options!
10
+ Representable::Pipeline[*parse_functions.insert(3, Representable::StopOnNil)]
11
+ } do
12
+ property :title
13
+ end
14
+ end
15
+
16
+ it do
17
+ album = Album.new
18
+ representer.new(album).from_hash({"id"=>1, "songs"=>[{"title"=>"Walkie Talkie"}]}).songs.must_equal [Song.new("Walkie Talkie")]
19
+ end
20
+
21
+ it do
22
+ album = Album.new(2, ["original"])
23
+ representer.new(album).from_hash({"id"=>1, "songs"=>nil}).songs.must_equal ["original"]
24
+ end
25
+
26
+ end
27
+
28
+ class RenderPipelineOptionTest < MiniTest::Spec
29
+ Album = Struct.new(:id, :songs)
30
+ NilToNA = ->(input, options) { input.nil? ? "n/a" : input }
31
+
32
+ representer!(decorator: true) do
33
+ property :id, render_pipeline: ->(input, options) do
34
+ Representable::Pipeline[*render_functions.insert(2, options[:options][:user_options][:function])]
35
+ end
36
+ end
37
+
38
+ it { representer.new(Album.new).to_hash(function: NilToNA).must_equal({"id"=>"n/a"}) }
39
+ it { representer.new(Album.new(1)).to_hash(function: NilToNA).must_equal({"id"=>1}) }
40
+
41
+ it "is cached" do
42
+ decorator = representer.new(Album.new)
43
+ decorator.to_hash(function: NilToNA).must_equal({"id"=>"n/a"})
44
+ decorator.to_hash(function: nil).must_equal({"id"=>"n/a"}) # non-sense function is not applied.
45
+ end
46
+
47
+ end
48
+
49
+ class ParsePipelineOptionTest < MiniTest::Spec
50
+ Album = Struct.new(:id, :songs)
51
+ NilToNA = ->(input, options) { input.nil? ? "n/a" : input }
52
+
53
+ representer!(decorator: true) do
54
+ property :id, parse_pipeline: ->(input, options) do
55
+ Representable::Pipeline[*parse_functions.insert(3, options[:options][:user_options][:function])].extend(Representable::Pipeline::Debug)
56
+ end
57
+ end
58
+
59
+ it { representer.new(Album.new).from_hash({"id"=>nil}, function: NilToNA).id.must_equal "n/a" }
60
+ it { representer.new(Album.new(1)).to_hash(function: NilToNA).must_equal({"id"=>1}) }
61
+
62
+ it "is cached" do
63
+ decorator = representer.new(Album.new)
64
+ decorator.from_hash({"id"=>nil}, function: NilToNA).id.must_equal "n/a"
65
+ decorator.from_hash({"id"=>nil}, function: "nonsense").id.must_equal "n/a" # non-sense function is not applied.
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ require "test_helper"
2
+
3
+ class UserOptionsTest < Minitest::Spec
4
+ Song = Struct.new(:title)
5
+
6
+ representer! do
7
+ property :title, if: ->(options) { options[:visible] }
8
+ end
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(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"}, visible: true).title.must_equal "Business Conduct" }
14
+ end
15
+ # Representable.deprecations=false
@@ -0,0 +1,152 @@
1
+ require "test_helper"
2
+
3
+ class WrapTest < MiniTest::Spec
4
+ class HardcoreBand
5
+ include Representable::Hash
6
+ end
7
+
8
+ class SoftcoreBand < HardcoreBand
9
+ end
10
+
11
+ let (:band) { HardcoreBand.new }
12
+
13
+ it "returns false per default" do
14
+ assert_equal nil, SoftcoreBand.new.send(:representation_wrap)
15
+ end
16
+
17
+ it "infers a printable class name if set to true" do
18
+ HardcoreBand.representation_wrap = true
19
+ assert_equal "hardcore_band", band.send(:representation_wrap)
20
+ end
21
+
22
+ it "can be set explicitely" do
23
+ HardcoreBand.representation_wrap = "breach"
24
+ assert_equal "breach", band.send(:representation_wrap)
25
+ end
26
+
27
+ for_formats(
28
+ :hash => [Representable::Hash, {"Blink182"=>{"genre"=>"Pop"}}, {"Blink182"=>{"genre"=>"Poppunk"}}],
29
+ :json => [Representable::JSON, "{\"Blink182\":{\"genre\":\"Pop\"}}", "{\"Blink182\":{\"genre\":\"Poppunk\"}}"],
30
+ :xml => [Representable::XML, "<Blink182><genre>Pop</genre></Blink182>", "<Blink182><genre>Poppunk</genre></Blink182>"],
31
+ # :yaml => [Representable::YAML, "---\nBlink182:\n"], # TODO: fix YAML.
32
+ ) do |format, mod, output, input|
33
+
34
+ describe "[#{format}] dynamic wrap" do
35
+ let (:band) { representer.prepare(Struct.new(:name, :genre).new("Blink", "Pop")) }
36
+ let (:format) { format }
37
+
38
+ representer!(:module => mod) do
39
+ self.representation_wrap = lambda { |args| "#{name}#{args[:number]}" }
40
+ property :genre
41
+ end
42
+
43
+ it { render(band, {:number => 182}).must_equal_document(output) }
44
+
45
+ it { parse(band, input, {:number => 182}).genre.must_equal "Poppunk" } # TODO: better test. also, xml parses _any_ wrap.
46
+ end
47
+ end
48
+ end
49
+
50
+
51
+ class HashDisableWrapTest < MiniTest::Spec
52
+ Band = Struct.new(:name, :label)
53
+ Album = Struct.new(:band)
54
+ Label = Struct.new(:name)
55
+
56
+ class BandDecorator < Representable::Decorator
57
+ include Representable::Hash
58
+
59
+ self.representation_wrap = :bands
60
+ property :name
61
+
62
+ property :label do # this should have a wrap!
63
+ self.representation_wrap = :important
64
+ property :name
65
+ end
66
+ end
67
+
68
+ let (:band) { BandDecorator.prepare(Band.new("Social Distortion")) }
69
+
70
+ # direct, local api.
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"})
75
+ end
76
+
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"
81
+ end
82
+
83
+
84
+ class AlbumDecorator < Representable::Decorator
85
+ include Representable::Hash
86
+
87
+ self.representation_wrap = :albums
88
+
89
+ property :band, decorator: BandDecorator, wrap: false, class: Band
90
+ end
91
+
92
+
93
+ let (:album) { AlbumDecorator.prepare(Album.new(Band.new("Social Distortion", Label.new("Epitaph")))) }
94
+
95
+ # band has wrap turned off per property definition, however, label still has wrap.
96
+ it "renders" do
97
+ album.to_hash.must_equal({"albums" => {"band" => {"name"=>"Social Distortion", "label"=>{"important"=>{"name"=>"Epitaph"}}}}})
98
+ end
99
+
100
+ it "parses" do
101
+ album.from_hash({"albums" => {"band" => {"name"=>"Rvivr"}}}).band.name.must_equal "Rvivr"
102
+ end
103
+ end
104
+
105
+
106
+ class XMLDisableWrapTest < MiniTest::Spec
107
+ Band = Struct.new(:name, :label)
108
+ Album = Struct.new(:band)
109
+ Label = Struct.new(:name)
110
+
111
+ class BandDecorator < Representable::Decorator
112
+ include Representable::XML
113
+
114
+ self.representation_wrap = :bands # when nested, it uses this.
115
+ property :name
116
+
117
+ # property :label do # this should have a wrap!
118
+ # self.representation_wrap = :important
119
+ # property :name
120
+ # end
121
+ end
122
+
123
+ let (:band) { BandDecorator.prepare(Band.new("Social Distortion")) }
124
+
125
+ it do
126
+ band.to_xml.must_equal_xml "<bands><name>Social Distortion</name></bands>"
127
+ band.to_xml(wrap: "combo").must_equal_xml "<combo><name>Social Distortion</name></combo>"
128
+ end
129
+
130
+
131
+ class AlbumDecorator < Representable::Decorator
132
+ include Representable::XML
133
+
134
+ self.representation_wrap = :albums
135
+
136
+ property :band, decorator: BandDecorator, wrap: "po", class: Band
137
+ end
138
+
139
+
140
+ let (:album) { AlbumDecorator.prepare(Album.new(Band.new("Social Distortion", Label.new("Epitaph")))) }
141
+
142
+ # band has wrap turned of per property definition, however, label still has wrap.
143
+ it "rendersxx" do
144
+ skip
145
+ album.to_xml.must_equal({"albums" => {"band" => {"name"=>"Social Distortion", "label"=>{"important"=>{"name"=>"Epitaph"}}}}})
146
+ end
147
+
148
+ # it "parses" do
149
+ # album.from_hash({"albums" => {"band" => {"name"=>"Rvivr"}}}).band.name.must_equal "Rvivr"
150
+ # end
151
+ end
152
+
@@ -0,0 +1,62 @@
1
+ require 'test_helper'
2
+ require 'representable/json' # FIXME.
3
+ require 'representable/xml/collection'
4
+ require 'representable/xml/hash'
5
+
6
+ require 'representable/xml'
7
+
8
+ class XMLBindingTest < MiniTest::Spec
9
+ module SongRepresenter
10
+ include Representable::XML
11
+ property :name
12
+ self.representation_wrap = :song
13
+ end
14
+
15
+ class SongWithRepresenter < ::Song
16
+ include Representable
17
+ include SongRepresenter
18
+ self.representation_wrap = :song
19
+ end
20
+
21
+ before do
22
+ @doc = Nokogiri::XML::Document.new
23
+ @song = SongWithRepresenter.new("Thinning the Herd")
24
+ end
25
+
26
+
27
+
28
+
29
+ describe "AttributeBinding" do
30
+ describe "with plain text items" do
31
+ before do
32
+ @property = Representable::XML::Binding::Attribute.new(Representable::Definition.new(:name, :attribute => true))
33
+ end
34
+
35
+ it "extracts with #read" do
36
+ assert_equal "The Gargoyle", @property.read(Nokogiri::XML("<song name=\"The Gargoyle\" />").root, "name")
37
+ end
38
+
39
+ it "inserts with #write" do
40
+ parent = Nokogiri::XML::Node.new("song", @doc)
41
+ @property.write(parent, "The Gargoyle", "name")
42
+ assert_xml_equal("<song name=\"The Gargoyle\" />", parent.to_s)
43
+ end
44
+ end
45
+ end
46
+
47
+ describe "ContentBinding" do
48
+ before do
49
+ @property = Representable::XML::Binding::Content.new(Representable::Definition.new(:name, :content => true))
50
+ end
51
+
52
+ it "extracts with #read" do
53
+ assert_equal "The Gargoyle", @property.read(Nokogiri::XML("<song>The Gargoyle</song>").root, "song")
54
+ end
55
+
56
+ it "inserts with #write" do
57
+ parent = Nokogiri::XML::Node.new("song", @doc)
58
+ @property.write(parent, "The Gargoyle", "song")
59
+ assert_xml_equal("<song>The Gargoyle</song>", parent.to_s)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,503 @@
1
+ require 'test_helper'
2
+ require 'representable/xml'
3
+
4
+ class Band
5
+ include Representable::XML
6
+ property :name
7
+ attr_accessor :name
8
+
9
+ def initialize(name=nil)
10
+ name and self.name = name
11
+ end
12
+ end
13
+
14
+ class Album
15
+ attr_accessor :songs
16
+ end
17
+
18
+
19
+ class XmlTest < MiniTest::Spec
20
+ XML = Representable::XML
21
+ Def = Representable::Definition
22
+
23
+ describe "Xml module" do
24
+ before do
25
+ @Band = Class.new do
26
+ include Representable::XML
27
+ self.representation_wrap = :band
28
+ property :name
29
+ property :label
30
+ attr_accessor :name, :label
31
+ end
32
+
33
+ @band = @Band.new
34
+ end
35
+
36
+
37
+ describe "#from_xml" do
38
+ before do
39
+ @band = @Band.new
40
+ @xml = %{<band><name>Nofx</name><label>NOFX</label></band>}
41
+ end
42
+
43
+ it "parses XML and assigns properties" do
44
+ @band.from_xml(@xml)
45
+ assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
46
+ end
47
+ end
48
+
49
+ describe "#from_xml with remove_namespaces! and xmlns present" do
50
+ before do
51
+ @Band.remove_namespaces!
52
+ @band = @Band.new
53
+ @xml = %{<band xmlns="exists"><name>Nofx</name><label>NOFX</label></band>}
54
+ end
55
+
56
+ it "parses with xmlns present" do
57
+ @band.from_xml(@xml)
58
+ assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
59
+ end
60
+ end
61
+
62
+ describe "#from_node" do
63
+ before do
64
+ @band = @Band.new
65
+ @xml = Nokogiri::XML(%{<band><name>Nofx</name><label>NOFX</label></band>}).root
66
+ end
67
+
68
+ it "receives Nokogiri node and assigns properties" do
69
+ @band.from_node(@xml)
70
+ assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
71
+ end
72
+ end
73
+
74
+
75
+ describe "#to_xml" do
76
+ it "delegates to #to_node and returns string" do
77
+ assert_xml_equal "<band><name>Rise Against</name></band>", Band.new("Rise Against").to_xml
78
+ end
79
+ end
80
+
81
+
82
+ describe "#to_node" do
83
+ it "returns Nokogiri node" do
84
+ node = Band.new("Rise Against").to_node
85
+ assert_kind_of Nokogiri::XML::Element, node
86
+ end
87
+
88
+ it "wraps with infered class name per default" do
89
+ node = Band.new("Rise Against").to_node
90
+ assert_xml_equal "<band><name>Rise Against</name></band>", node.to_s
91
+ end
92
+
93
+ it "respects #representation_wrap=" do
94
+ klass = Class.new(Band) do
95
+ include Representable
96
+ property :name
97
+ end
98
+
99
+ klass.representation_wrap = :group
100
+ assert_xml_equal "<group><name>Rise Against</name></group>", klass.new("Rise Against").to_node.to_s
101
+ end
102
+ end
103
+
104
+
105
+ describe "XML::Binding#build_for" do
106
+ it "returns AttributeBinding" do
107
+ assert_kind_of XML::Binding::Attribute, XML::Binding.build_for(Def.new(:band, :as => "band", :attribute => true))
108
+ end
109
+
110
+ it "returns Binding" do
111
+ assert_kind_of XML::Binding, XML::Binding.build_for(Def.new(:band, :class => Hash))
112
+ assert_kind_of XML::Binding, XML::Binding.build_for(Def.new(:band, :as => :content))
113
+ end
114
+
115
+ it "returns CollectionBinding" do
116
+ assert_kind_of XML::Binding::Collection, XML::Binding.build_for(Def.new(:band, :collection => :true))
117
+ end
118
+
119
+ it "returns HashBinding" do
120
+ assert_kind_of XML::Binding::Hash, XML::Binding.build_for(Def.new(:band, :hash => :true))
121
+ end
122
+ end
123
+
124
+
125
+ describe "DCI" do
126
+ module SongRepresenter
127
+ include Representable::XML
128
+ property :name
129
+ representation_wrap = :song
130
+ end
131
+
132
+ module AlbumRepresenter
133
+ include Representable::XML
134
+ property :best_song, :class => Song, :extend => SongRepresenter
135
+ collection :songs, :class => Song, :as => :song, :extend => SongRepresenter
136
+ representation_wrap = :album
137
+ end
138
+
139
+
140
+ it "allows adding the representer by using #extend" do
141
+ module BandRepresenter
142
+ include Representable::XML
143
+ property :name
144
+ end
145
+
146
+ civ = Object.new
147
+ civ.instance_eval do
148
+ def name; "CIV"; end
149
+ def name=(v)
150
+ @name = v
151
+ end
152
+ end
153
+
154
+ civ.extend(BandRepresenter)
155
+ assert_xml_equal "<object><name>CIV</name></object>", civ.to_xml
156
+ end
157
+
158
+ it "extends contained models when serializing" do
159
+ @album = Album.new([Song.new("I Hate My Brain"), mr=Song.new("Mr. Charisma")], mr)
160
+ @album.extend(AlbumRepresenter)
161
+
162
+ assert_xml_equal "<album>
163
+ <song><name>Mr. Charisma</name></song>
164
+ <song><name>I Hate My Brain</name></song>
165
+ <song><name>Mr. Charisma</name></song>
166
+ </album>", @album.to_xml
167
+ end
168
+
169
+ it "extends contained models when deserializing" do
170
+ @album = Album.new
171
+ @album.extend(AlbumRepresenter)
172
+
173
+ @album.from_xml("<album><best_song><name>Mr. Charisma</name></best_song><song><name>I Hate My Brain</name></song><song><name>Mr. Charisma</name></song></album>")
174
+ assert_equal "Mr. Charisma", @album.best_song.name
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+
181
+ class AttributesTest < MiniTest::Spec
182
+ describe ":as => rel, :attribute => true" do
183
+ class Link
184
+ include Representable::XML
185
+ property :href, :as => "href", :attribute => true
186
+ property :title, :as => "title", :attribute => true
187
+ attr_accessor :href, :title
188
+ end
189
+
190
+ it "#from_xml creates correct accessors" do
191
+ link = Link.new.from_xml(%{
192
+ <a href="http://apotomo.de" title="Home, sweet home" />
193
+ })
194
+ assert_equal "http://apotomo.de", link.href
195
+ assert_equal "Home, sweet home", link.title
196
+ end
197
+
198
+ it "#to_xml serializes correctly" do
199
+ link = Link.new
200
+ link.href = "http://apotomo.de/"
201
+
202
+ assert_xml_equal %{<link href="http://apotomo.de/">}, link.to_xml
203
+ end
204
+ end
205
+ end
206
+
207
+
208
+ class CDataBand
209
+ class CData < Representable::XML::Binding
210
+ def serialize_node(parent, value)
211
+ parent << Nokogiri::XML::CDATA.new(parent, represented.name)
212
+ end
213
+ end
214
+
215
+ include Representable::XML
216
+ property :name, :binding => lambda { |*args| CData.new(*args) }#getter: lambda { |opt| Nokogiri::XML::CDATA.new(opt[:doc], name) }
217
+ attr_accessor :name
218
+
219
+ def initialize(name=nil)
220
+ name and self.name = name
221
+ end
222
+ end
223
+
224
+ class TypedPropertyTest < MiniTest::Spec
225
+ module AlbumRepresenter
226
+ include Representable::XML
227
+ property :band, :class => Band
228
+ end
229
+
230
+ class Album
231
+ attr_accessor :band
232
+ def initialize(band=nil)
233
+ @band = band
234
+ end
235
+ end
236
+
237
+ # TODO:property :group, :class => Band
238
+ # :class
239
+ # where to mixin DCI?
240
+ describe ":class => Item" do
241
+ it "#from_xml creates one Item instance" do
242
+ album = Album.new.extend(AlbumRepresenter).from_xml(%{
243
+ <album>
244
+ <band><name>Bad Religion</name></band>
245
+ </album>
246
+ })
247
+ assert_equal "Bad Religion", album.band.name
248
+ end
249
+
250
+ describe "#to_xml" do
251
+ it "doesn't escape xml from Band#to_xml" do
252
+ band = Band.new("Bad Religion")
253
+ album = Album.new(band).extend(AlbumRepresenter)
254
+
255
+ assert_xml_equal %{<album>
256
+ <band>
257
+ <name>Bad Religion</name>
258
+ </band>
259
+ </album>}, album.to_xml
260
+ end
261
+
262
+ it "doesn't escape and wrap string from Band#to_node" do
263
+ band = Band.new("Bad Religion")
264
+ band.instance_eval do
265
+ def to_node(*)
266
+ "<band>Baaaad Religion</band>"
267
+ end
268
+ end
269
+
270
+ assert_xml_equal %{<album><band>Baaaad Religion</band></album>}, Album.new(band).extend(AlbumRepresenter).to_xml
271
+ end
272
+ end
273
+
274
+ describe "#to_xml with CDATA" do
275
+ it "wraps Band name in CDATA#to_xml" do
276
+ band = CDataBand.new("Bad Religion")
277
+ album = Album.new(band).extend(AlbumRepresenter)
278
+
279
+ assert_xml_equal %{<album>
280
+ <c_data_band>
281
+ <name><![CDATA[Bad Religion]]></name>
282
+ </c_data_band>
283
+ </album>}, album.to_xml
284
+ end
285
+ end
286
+ end
287
+ end
288
+
289
+
290
+ class CollectionTest < MiniTest::Spec
291
+ describe ":class => Band, :as => :band, :collection => true" do
292
+ class Compilation
293
+ include Representable::XML
294
+ collection :bands, :class => Band, :as => :band
295
+ attr_accessor :bands
296
+ end
297
+
298
+ describe "#from_xml" do
299
+ it "pushes collection items to array" do
300
+ cd = Compilation.new.from_xml(%{
301
+ <compilation>
302
+ <band><name>Diesel Boy</name></band>
303
+ <band><name>Cobra Skulls</name></band>
304
+ </compilation>
305
+ })
306
+ assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
307
+ end
308
+ end
309
+
310
+ it "responds to #to_xml" do
311
+ cd = Compilation.new
312
+ cd.bands = [Band.new("Diesel Boy"), Band.new("Bad Religion")]
313
+
314
+ assert_xml_equal %{<compilation>
315
+ <band><name>Diesel Boy</name></band>
316
+ <band><name>Bad Religion</name></band>
317
+ </compilation>}, cd.to_xml
318
+ end
319
+ end
320
+
321
+
322
+ describe ":as" do
323
+ let(:xml_doc) {
324
+ Module.new do
325
+ include Representable::XML
326
+ collection :songs, :as => :song
327
+ end }
328
+
329
+ it "collects untyped items" do
330
+ album = Album.new.extend(xml_doc).from_xml(%{
331
+ <album>
332
+ <song>Two Kevins</song>
333
+ <song>Wright and Rong</song>
334
+ <song>Laundry Basket</song>
335
+ </album>
336
+ })
337
+ assert_equal ["Laundry Basket", "Two Kevins", "Wright and Rong"].sort, album.songs.sort
338
+ end
339
+ end
340
+
341
+
342
+ describe ":wrap" do
343
+ let (:album) { Album.new.extend(xml_doc) }
344
+ let (:xml_doc) {
345
+ Module.new do
346
+ include Representable::XML
347
+ collection :songs, :as => :song, :wrap => :songs
348
+ end }
349
+
350
+ describe "#from_xml" do
351
+ it "finds items in wrapped collection" do
352
+ album.from_xml(%{
353
+ <album>
354
+ <songs>
355
+ <song>Two Kevins</song>
356
+ <song>Wright and Rong</song>
357
+ <song>Laundry Basket</song>
358
+ </songs>
359
+ </album>
360
+ })
361
+ assert_equal ["Laundry Basket", "Two Kevins", "Wright and Rong"].sort, album.songs.sort
362
+ end
363
+ end
364
+
365
+ describe "#to_xml" do
366
+ it "wraps items" do
367
+ album.songs = ["Laundry Basket", "Two Kevins", "Wright and Rong"]
368
+ assert_xml_equal %{
369
+ <album>
370
+ <songs>
371
+ <song>Laundry Basket</song>
372
+ <song>Two Kevins</song>
373
+ <song>Wright and Rong</song>
374
+ </songs>
375
+ </album>
376
+ }, album.to_xml
377
+ end
378
+ end
379
+ end
380
+
381
+ require 'representable/xml/hash'
382
+ class LonelyRepresenterTest < MiniTest::Spec
383
+ # TODO: where is the XML::Hash test?
384
+ module SongRepresenter
385
+ include Representable::XML
386
+ property :name
387
+ self.representation_wrap = :song
388
+ end
389
+
390
+ let (:decorator) { rpr = representer; Class.new(Representable::Decorator) { include Representable::XML; include rpr } }
391
+
392
+ describe "XML::Collection" do
393
+ describe "with contained objects" do
394
+ representer!(:module => Representable::XML::Collection) do
395
+ items :class => Song, :extend => SongRepresenter
396
+ self.representation_wrap= :songs
397
+ end
398
+
399
+ let (:songs) { [Song.new("Days Go By"), Song.new("Can't Take Them All")] }
400
+ let (:xml_doc) { "<songs><song><name>Days Go By</name></song><song><name>Can't Take Them All</name></song></songs>" }
401
+
402
+ it "renders array" do
403
+ songs.extend(representer).to_xml.must_equal_xml xml_doc
404
+ end
405
+
406
+ it "renders array with decorator" do
407
+ decorator.new(songs).to_xml.must_equal_xml xml_doc
408
+ end
409
+
410
+ it "parses array" do
411
+ [].extend(representer).from_xml(xml_doc).must_equal songs
412
+ end
413
+
414
+ it "parses array with decorator" do
415
+ decorator.new([]).from_xml(xml_doc).must_equal songs
416
+ end
417
+ end
418
+ end
419
+
420
+ describe "XML::AttributeHash" do # TODO: move to HashTest.
421
+ representer!(:module => Representable::XML::AttributeHash) do
422
+ self.representation_wrap= :songs
423
+ end
424
+
425
+ let (:songs) { {"one" => "Graveyards", "two" => "Can't Take Them All"} }
426
+ let (:xml_doc) { "<favs one=\"Graveyards\" two=\"Can't Take Them All\" />" }
427
+
428
+ describe "#to_xml" do
429
+ it "renders hash" do
430
+ songs.extend(representer).to_xml.must_equal_xml xml_doc
431
+ end
432
+
433
+ it "respects :exclude" do
434
+ assert_xml_equal "<favs two=\"Can't Take Them All\" />", songs.extend(representer).to_xml(:exclude => [:one])
435
+ end
436
+
437
+ it "respects :include" do
438
+ assert_xml_equal "<favs two=\"Can't Take Them All\" />", songs.extend(representer).to_xml(:include => [:two])
439
+ end
440
+
441
+ it "renders hash with decorator" do
442
+ decorator.new(songs).to_xml.must_equal_xml xml_doc
443
+ end
444
+ end
445
+
446
+ describe "#from_json" do
447
+ it "returns hash" do
448
+ {}.extend(representer).from_xml(xml_doc).must_equal songs
449
+ end
450
+
451
+ it "respects :exclude" do
452
+ assert_equal({"two" => "Can't Take Them All"}, {}.extend(representer).from_xml(xml_doc, :exclude => [:one]))
453
+ end
454
+
455
+ it "respects :include" do
456
+ assert_equal({"one" => "Graveyards"}, {}.extend(representer).from_xml(xml_doc, :include => [:one]))
457
+ end
458
+
459
+ it "parses hash with decorator" do
460
+ decorator.new({}).from_xml(xml_doc).must_equal songs
461
+ end
462
+ end
463
+ end
464
+ end
465
+ end
466
+
467
+ class XmlHashTest < MiniTest::Spec
468
+ # scalar, no object
469
+ describe "plain text" do
470
+ representer!(module: Representable::XML) do
471
+ hash :songs
472
+ end
473
+
474
+ let (:doc) { "<open_struct><first>The Gargoyle</first><second>Bronx</second></open_struct>" }
475
+
476
+ # to_xml
477
+ it { OpenStruct.new(songs: {"first" => "The Gargoyle", "second" => "Bronx"}).extend(representer).to_xml.must_equal_xml(doc) }
478
+ # FIXME: this NEVER worked!
479
+ # it { OpenStruct.new.extend(representer).from_xml(doc).songs.must_equal({"first" => "The Gargoyle", "second" => "Bronx"}) }
480
+ end
481
+
482
+ describe "with objects" do
483
+ representer!(module: Representable::XML) do
484
+ hash :songs, class: OpenStruct do
485
+ property :title
486
+ end
487
+ end
488
+
489
+ let (:doc) { "<open_struct>
490
+ <open_struct>
491
+ <title>The Gargoyle</title>
492
+ </open_struct>
493
+ <open_struct>
494
+ <title>Bronx</title>
495
+ </open_struct>
496
+ </open_struct>" }
497
+
498
+ # to_xml
499
+ it { OpenStruct.new(songs: {"first" => OpenStruct.new(title: "The Gargoyle"), "second" => OpenStruct.new(title: "Bronx")}).extend(representer).to_xml.must_equal_xml(doc) }
500
+ # FIXME: this NEVER worked!
501
+ # it { OpenStruct.new.extend(representer).from_xml(doc).songs.must_equal({"first" => "The Gargoyle", "second" => "Bronx"}) }
502
+ end
503
+ end