representable 2.4.0.rc3 → 2.4.0.rc4

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 (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,21 @@
1
+ require 'test_helper'
2
+
3
+ class GetterSetterTest < BaseTest
4
+ representer! do
5
+ property :name, # key under :name.
6
+ :getter => lambda { |args| "#{args[:welcome]} #{song_name}" },
7
+ :setter => lambda { |val, args| self.song_name = "#{args[:welcome]} #{val}" }
8
+ end
9
+
10
+ subject { Struct.new(:song_name).new("Mony Mony").extend(representer) }
11
+
12
+ it "uses :getter when rendering" do
13
+ subject.instance_eval { def name; raise; end }
14
+ subject.to_hash(:welcome => "Hi").must_equal({"name" => "Hi Mony Mony"})
15
+ end
16
+
17
+ it "uses :setter when parsing" do
18
+ subject.instance_eval { def name=(*); raise; end; self }
19
+ subject.from_hash({"name" => "Eyes Without A Face"}, :welcome => "Hello").song_name.must_equal "Hello Eyes Without A Face"
20
+ end
21
+ end
@@ -0,0 +1,87 @@
1
+ require 'test_helper'
2
+
3
+ class HashBindingTest < MiniTest::Spec
4
+ module SongRepresenter
5
+ include Representable::JSON
6
+ property :name
7
+ end
8
+
9
+ class SongWithRepresenter < ::Song
10
+ include Representable
11
+ include SongRepresenter
12
+ end
13
+
14
+
15
+ describe "PropertyBinding" do
16
+ describe "#read" do
17
+ before do
18
+ @property = Representable::Hash::Binding.new(Representable::Definition.new(:song))
19
+ end
20
+
21
+ it "returns fragment if present" do
22
+ assert_equal "Stick The Flag Up Your Goddamn Ass, You Sonofabitch", @property.read({"song" => "Stick The Flag Up Your Goddamn Ass, You Sonofabitch"}, "song")
23
+ assert_equal "", @property.read({"song" => ""}, "song")
24
+ assert_equal nil, @property.read({"song" => nil}, "song")
25
+ end
26
+
27
+ it "returns FRAGMENT_NOT_FOUND if not in document" do
28
+ assert_equal Representable::Binding::FragmentNotFound, @property.read({}, "song")
29
+ end
30
+
31
+ end
32
+ end
33
+
34
+
35
+ describe "CollectionBinding" do
36
+ describe "with plain text items" do
37
+ before do
38
+ @property = Representable::Hash::Binding::Collection.new(Representable::Definition.new(:songs, :collection => true))
39
+ end
40
+
41
+ it "extracts with #read" do
42
+ assert_equal ["The Gargoyle", "Bronx"], @property.read({"songs" => ["The Gargoyle", "Bronx"]}, "songs")
43
+ end
44
+
45
+ it "inserts with #write" do
46
+ doc = {}
47
+ assert_equal(["The Gargoyle", "Bronx"], @property.write(doc, ["The Gargoyle", "Bronx"], "songs"))
48
+ assert_equal({"songs"=>["The Gargoyle", "Bronx"]}, doc)
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+
55
+
56
+ describe "HashBinding" do
57
+ describe "with plain text items" do
58
+ before do
59
+ @property = Representable::Hash::Binding.new(Representable::Definition.new(:songs, :hash => true))
60
+ end
61
+
62
+ it "extracts with #read" do
63
+ assert_equal({"first" => "The Gargoyle", "second" => "Bronx"} , @property.read({"songs" => {"first" => "The Gargoyle", "second" => "Bronx"}}, "songs"))
64
+ end
65
+
66
+ it "inserts with #write" do
67
+ doc = {}
68
+ assert_equal({"first" => "The Gargoyle", "second" => "Bronx"}, @property.write(doc, {"first" => "The Gargoyle", "second" => "Bronx"}, "songs"))
69
+ assert_equal({"songs"=>{"first" => "The Gargoyle", "second" => "Bronx"}}, doc)
70
+ end
71
+ end
72
+
73
+ describe "with objects" do
74
+ before do
75
+ @property = Representable::Hash::Binding.new(Representable::Definition.new(:songs, :hash => true, :class => Song, :extend => SongRepresenter))
76
+ end
77
+
78
+ it "doesn't change the represented hash in #write" do
79
+ song = Song.new("Better Than That")
80
+ hash = {"first" => song}
81
+ @property.write({}, hash, "song")
82
+ assert_equal({"first" => song}, hash)
83
+ end
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,160 @@
1
+ require 'test_helper'
2
+
3
+ class HashWithScalarPropertyTest < MiniTest::Spec
4
+ Album = Struct.new(:title)
5
+
6
+ representer! do
7
+ property :title
8
+ end
9
+
10
+ let (:album) { Album.new("Liar") }
11
+
12
+ describe "#to_hash" do
13
+ it "renders plain property" do
14
+ album.extend(representer).to_hash.must_equal("title" => "Liar")
15
+ end
16
+ end
17
+
18
+
19
+ describe "#from_hash" do
20
+ it "parses plain property" do
21
+ album.extend(representer).from_hash("title" => "This Song Is Recycled").title.must_equal "This Song Is Recycled"
22
+ end
23
+
24
+ # Fixes issue #115
25
+ it "allows nil value in the incoming document and corresponding nil value for the represented" do
26
+ album = Album.new
27
+ album.title.must_equal nil
28
+ album.extend(representer).from_hash("title" => nil)
29
+ end
30
+ end
31
+ end
32
+
33
+
34
+ class HashWithTypedPropertyTest < MiniTest::Spec
35
+ Album = Struct.new(:best_song)
36
+
37
+ representer! do
38
+ property :best_song, :class => Song do
39
+ property :name
40
+ end
41
+ end
42
+
43
+ let (:album) { Album.new(Song.new("Liar")) }
44
+
45
+ describe "#to_hash" do
46
+ it "renders embedded typed property" do
47
+ album.extend(representer).to_hash.must_equal("best_song" => {"name" => "Liar"})
48
+ end
49
+ end
50
+
51
+ describe "#from_hash" do
52
+ it "parses embedded typed property" do
53
+ album.extend(representer).from_hash("best_song" => {"name" => "Go With Me"})
54
+ album.best_song.name.must_equal "Go With Me"
55
+ end
56
+
57
+ # nested nil removes nested object.
58
+ it do
59
+ album = Album.new(Song.new("Pre-medicated Murder"))
60
+ album.extend(representer).from_hash("best_song" => nil)
61
+ album.best_song.must_equal nil
62
+ end
63
+
64
+ # nested blank hash creates blank object when not populated.
65
+ it do
66
+ album = Album.new#(Song.new("Pre-medicated Murder"))
67
+ album.extend(representer).from_hash("best_song" => {})
68
+ album.best_song.name.must_equal nil
69
+ end
70
+
71
+ # Fixes issue #115
72
+ it "allows nil value in the incoming document and corresponding nil value for the represented" do
73
+ album = Album.new
74
+ album.extend(representer).from_hash("best_song" => nil)
75
+ album.best_song.must_equal nil
76
+ end
77
+ end
78
+ end
79
+
80
+ # TODO: move to AsTest.
81
+ class HashWithTypedPropertyAndAs < MiniTest::Spec
82
+ representer! do
83
+ property :song, :class => Song, :as => :hit do
84
+ property :name
85
+ end
86
+ end
87
+
88
+ let (:album) { OpenStruct.new(:song => Song.new("Liar")).extend(representer) }
89
+
90
+ it { album.to_hash.must_equal("hit" => {"name" => "Liar"}) }
91
+ it { album.from_hash("hit" => {"name" => "Go With Me"}).must_equal OpenStruct.new(:song => Song.new("Go With Me")) }
92
+ end
93
+ # # describe "FIXME COMBINE WITH ABOVE with :extend and :as" do
94
+ # # hash_song = Module.new do
95
+ # # include Representable::XML
96
+ # # self.representation_wrap = :song
97
+ # # property :name
98
+ # # end
99
+
100
+ # # let (:hash_album) { Module.new do
101
+ # # include Representable::XML
102
+ # # self.representation_wrap = :album
103
+ # # property :song, :extend => hash_song, :class => Song, :as => :hit
104
+ # # end }
105
+
106
+ # # let (:album) { OpenStruct.new(:song => Song.new("Liar")).extend(hash_album) }
107
+
108
+ # # it { album.to_xml.must_equal_xml("<album><hit><name>Liar</name></hit></album>") }
109
+ # # #it { album.from_hash("hit" => {"name" => "Go With Me"}).must_equal OpenStruct.new(:song => Song.new("Go With Me")) }
110
+ # # end
111
+ # end
112
+
113
+
114
+
115
+ class HashWithTypedCollectionTest < MiniTest::Spec
116
+ Album = Struct.new(:songs)
117
+
118
+ representer! do
119
+ collection :songs, class: Song do
120
+ property :name
121
+ property :track
122
+ end
123
+ end
124
+
125
+ let (:album) { Album.new([Song.new("Liar", 1), Song.new("What I Know", 2)]) }
126
+
127
+ describe "#to_hash" do
128
+ it "renders collection of typed property" do
129
+ album.extend(representer).to_hash.must_equal("songs" => [{"name" => "Liar", "track" => 1}, {"name" => "What I Know", "track" => 2}])
130
+ end
131
+ end
132
+
133
+ describe "#from_hash" do
134
+ it "parses collection of typed property" do
135
+ album.extend(representer).from_hash("songs" => [{"name" => "One Shot Deal", "track" => 4},
136
+ {"name" => "Three Way Dance", "track" => 5}]).must_equal Album.new([Song.new("One Shot Deal", 4), Song.new("Three Way Dance", 5)])
137
+ end
138
+ end
139
+ end
140
+
141
+ class HashWithScalarCollectionTest < MiniTest::Spec
142
+ Album = Struct.new(:songs)
143
+ representer! { collection :songs }
144
+
145
+ let (:album) { Album.new(["Jackhammer", "Terrible Man"]) }
146
+
147
+
148
+ describe "#to_hash" do
149
+ it "renders a block style list per default" do
150
+ album.extend(representer).to_hash.must_equal("songs" => ["Jackhammer", "Terrible Man"])
151
+ end
152
+ end
153
+
154
+
155
+ describe "#from_hash" do
156
+ it "parses a block style list" do
157
+ album.extend(representer).from_hash("songs" => ["Off Key Melody", "Sinking"]).must_equal Album.new(["Off Key Melody", "Sinking"])
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,62 @@
1
+ require "test_helper"
2
+
3
+ class HeritageTest < Minitest::Spec
4
+ module Hello
5
+ def hello
6
+ puts "Hello!"
7
+ end
8
+ end
9
+ module Ciao
10
+ def ciao
11
+ puts "Ciao!"
12
+ end
13
+ end
14
+
15
+
16
+ class A < Representable::Decorator
17
+ include Representable::Hash
18
+
19
+ feature Hello
20
+
21
+ property :id do
22
+ end
23
+ end
24
+
25
+ class B < A
26
+ feature Ciao # does NOT extend id, of course.
27
+
28
+ property :id, inherit: true do
29
+
30
+ end
31
+ end
32
+
33
+ class C < A
34
+ property :id do end # overwrite old :id.
35
+ end
36
+
37
+ it do
38
+ # puts A.heritage.inspect
39
+ # puts B.heritage.inspect
40
+
41
+ puts B.representable_attrs.get(:id)[:extend].(nil).new(nil).hello
42
+ puts B.representable_attrs.get(:id)[:extend].(nil).new(nil).ciao
43
+
44
+ # feature Hello must be "inherited" from A and included in new C properties, too.
45
+ puts C.representable_attrs.get(:id)[:extend].(nil).new(nil).hello
46
+ end
47
+
48
+ module M
49
+ include Representable
50
+ feature Hello
51
+ end
52
+
53
+ module N
54
+ include Representable
55
+ include M
56
+ feature Ciao
57
+ end
58
+
59
+ it do
60
+ Object.new.extend(N).hello
61
+ end
62
+ end
@@ -0,0 +1,79 @@
1
+ require 'test_helper'
2
+
3
+ class IfTest < MiniTest::Spec
4
+ let (:band_class) { Class.new do
5
+ include Representable::Hash
6
+ attr_accessor :fame
7
+ self
8
+ end }
9
+
10
+ it "respects property when condition true" do
11
+ band_class.class_eval { property :fame, :if => lambda { |*| true } }
12
+ band = band_class.new
13
+ band.from_hash({"fame"=>"oh yes"})
14
+ assert_equal "oh yes", band.fame
15
+ end
16
+
17
+ it "ignores property when condition false" do
18
+ band_class.class_eval { property :fame, :if => lambda { |*| false } }
19
+ band = band_class.new
20
+ band.from_hash({"fame"=>"oh yes"})
21
+ assert_equal nil, band.fame
22
+ end
23
+
24
+ it "ignores property when :exclude'ed even when condition is true" do
25
+ band_class.class_eval { property :fame, :if => lambda { |*| true } }
26
+ band = band_class.new
27
+ band.from_hash({"fame"=>"oh yes"}, {:exclude => [:fame]})
28
+ assert_equal nil, band.fame
29
+ end
30
+
31
+ it "executes block in instance context" do
32
+ band_class.class_eval { property :fame, :if => lambda { |*| groupies }; attr_accessor :groupies }
33
+ band = band_class.new
34
+ band.groupies = true
35
+ band.from_hash({"fame"=>"oh yes"})
36
+ assert_equal "oh yes", band.fame
37
+ end
38
+
39
+ describe "executing :if lambda in represented instance context" do
40
+ representer! do
41
+ property :label, :if => lambda { |*| signed_contract }
42
+ end
43
+
44
+ subject { OpenStruct.new(:signed_contract => false, :label => "Fat") }
45
+
46
+ it "skips when false" do
47
+ subject.extend(representer).to_hash.must_equal({})
48
+ end
49
+
50
+ it "represents when true" do
51
+ subject.signed_contract= true
52
+ subject.extend(representer).to_hash.must_equal({"label"=>"Fat"})
53
+ end
54
+
55
+ it "works with decorator" do
56
+ rpr = representer
57
+ Class.new(Representable::Decorator) do
58
+ include Representable::Hash
59
+ include rpr
60
+ end.new(subject).to_hash.must_equal({})
61
+ end
62
+ end
63
+
64
+
65
+ describe "propagating user options to the block" do
66
+ representer! do
67
+ property :name, :if => lambda { |opts| opts[:include_name] }
68
+ end
69
+ subject { OpenStruct.new(:name => "Outbound").extend(representer) }
70
+
71
+ it "works without specifying options" do
72
+ subject.to_hash.must_equal({})
73
+ end
74
+
75
+ it "passes user options to block" do
76
+ subject.to_hash(:include_name => true).must_equal({"name" => "Outbound"})
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,88 @@
1
+ require "test_helper"
2
+
3
+ class IncludeExcludeTest < Minitest::Spec
4
+ Song = Struct.new(:title, :artist, :id)
5
+ Artist = Struct.new(:name, :id, :songs)
6
+
7
+ representer!(decorator: true) do
8
+ property :title
9
+ property :artist, class: Artist do
10
+ property :name
11
+ property :id
12
+
13
+ collection :songs, class: Song do
14
+ property :title
15
+ property :id
16
+ end
17
+ end
18
+ end
19
+
20
+ let (:song) { Song.new("Listless", Artist.new("7yearsbadluck", 1 )) }
21
+ let (:decorator) { representer.new(song) }
22
+
23
+ describe "#from_hash" do
24
+ it "accepts :exclude option" do
25
+ decorator.from_hash({"title"=>"Don't Smile In Trouble", "artist"=>{"id"=>2}}, exclude: [:title])
26
+
27
+ song.title.must_equal "Listless"
28
+ song.artist.must_equal Artist.new(nil, 2)
29
+ end
30
+
31
+ it "accepts :include option" do
32
+ decorator.from_hash({"title"=>"Don't Smile In Trouble", "artist"=>{"id"=>2}}, include: [:title])
33
+
34
+ song.title.must_equal "Don't Smile In Trouble"
35
+ song.artist.must_equal Artist.new("7yearsbadluck", 1)
36
+ end
37
+
38
+ it "accepts nested :exclude/:include option" do
39
+ decorator.from_hash({"title"=>"Don't Smile In Trouble", "artist"=>{"name"=>"Foo", "id"=>2, "songs"=>[{"id"=>1, "title"=>"Listless"}]}},
40
+ exclude: [:title],
41
+ artist: {
42
+ exclude: [:id],
43
+ songs: { include: [:title] }
44
+ }
45
+ )
46
+
47
+ song.title.must_equal "Listless"
48
+ song.artist.must_equal Artist.new("Foo", nil, [Song.new("Listless", nil, nil)])
49
+ end
50
+ end
51
+
52
+ describe "#to_hash" do
53
+ it "accepts :exclude option" do
54
+ decorator.to_hash(exclude: [:title]).must_equal({"artist"=>{"name"=>"7yearsbadluck", "id"=>1}})
55
+ end
56
+
57
+ it "accepts :include option" do
58
+ decorator.to_hash(include: [:title]).must_equal({"title"=>"Listless"})
59
+ end
60
+
61
+ it "accepts nested :exclude/:include option" do
62
+ decorator = representer.new(Song.new("Listless", Artist.new("7yearsbadluck", 1, [Song.new("C.O.A.B.I.E.T.L.")])))
63
+
64
+ decorator.to_hash(
65
+ exclude: [:title],
66
+ artist: {
67
+ exclude: [:id],
68
+ songs: { include: [:title] }
69
+ }
70
+ ).must_equal({"artist"=>{"name"=>"7yearsbadluck", "songs"=>[{"title"=>"C.O.A.B.I.E.T.L."}]}})
71
+ end
72
+ end
73
+
74
+ it "xdoes not propagate private options to nested objects" do
75
+ Cover = Struct.new(:title, :original)
76
+
77
+ cover_rpr = Module.new do
78
+ include Representable::Hash
79
+ property :title
80
+ property :original, extend: self
81
+ end
82
+
83
+ # FIXME: we should test all representable-options (:include, :exclude, ?)
84
+
85
+ Cover.new("Roxanne", Cover.new("Roxanne (Don't Put On The Red Light)")).extend(cover_rpr).
86
+ to_hash(:include => [:original]).must_equal({"original"=>{"title"=>"Roxanne (Don't Put On The Red Light)"}})
87
+ end
88
+ end