representable 1.8.5 → 2.0.0.rc1
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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGES.md +36 -0
- data/Gemfile +3 -3
- data/README.md +62 -2
- data/Rakefile +1 -1
- data/lib/representable.rb +32 -109
- data/lib/representable/TODO.getting_serious +7 -1
- data/lib/representable/autoload.rb +10 -0
- data/lib/representable/binding.rb +20 -16
- data/lib/representable/bindings/xml_bindings.rb +0 -1
- data/lib/representable/coercion.rb +23 -31
- data/lib/representable/config.rb +45 -46
- data/lib/representable/declarative.rb +78 -0
- data/lib/representable/decorator.rb +39 -10
- data/lib/representable/definition.rb +40 -33
- data/lib/representable/deserializer.rb +2 -0
- data/lib/representable/for_collection.rb +25 -0
- data/lib/representable/hash.rb +4 -8
- data/lib/representable/hash/collection.rb +2 -9
- data/lib/representable/hash_methods.rb +0 -7
- data/lib/representable/inheritable.rb +50 -0
- data/lib/representable/json.rb +3 -9
- data/lib/representable/json/collection.rb +1 -3
- data/lib/representable/json/hash.rb +4 -9
- data/lib/representable/mapper.rb +8 -5
- data/lib/representable/parse_strategies.rb +1 -0
- data/lib/representable/pipeline.rb +14 -0
- data/lib/representable/represent.rb +6 -0
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +3 -18
- data/lib/representable/xml/collection.rb +2 -4
- data/lib/representable/xml/hash.rb +2 -10
- data/lib/representable/yaml.rb +1 -20
- data/representable.gemspec +2 -2
- data/test/class_test.rb +5 -10
- data/test/coercion_test.rb +31 -92
- data/test/config/inherit_test.rb +128 -0
- data/test/config_test.rb +114 -80
- data/test/definition_test.rb +107 -64
- data/test/features_test.rb +41 -0
- data/test/filter_test.rb +59 -0
- data/test/for_collection_test.rb +74 -0
- data/test/inherit_test.rb +44 -3
- data/test/inheritable_test.rb +97 -0
- data/test/inline_test.rb +0 -18
- data/test/instance_test.rb +0 -19
- data/test/json_test.rb +9 -44
- data/test/lonely_test.rb +1 -0
- data/test/parse_strategy_test.rb +30 -0
- data/test/represent_test.rb +88 -0
- data/test/representable_test.rb +3 -50
- data/test/schema_test.rb +123 -0
- data/test/test_helper.rb +1 -1
- data/test/xml_test.rb +34 -38
- metadata +25 -15
- data/lib/representable/decorator/coercion.rb +0 -4
- data/lib/representable/readable_writeable.rb +0 -29
- data/test/inheritance_test.rb +0 -22
data/test/definition_test.rb
CHANGED
@@ -3,8 +3,45 @@ require 'test_helper'
|
|
3
3
|
class DefinitionTest < MiniTest::Spec
|
4
4
|
Definition = Representable::Definition
|
5
5
|
|
6
|
-
|
6
|
+
# TODO: test that we DON'T clone options, that must happen in
|
7
|
+
describe "#initialize" do
|
8
|
+
it do
|
9
|
+
opts = nil
|
10
|
+
|
11
|
+
# new yields the defaultized options HASH.
|
12
|
+
definition = Definition.new(:song, :extend => Module) do |options|
|
13
|
+
options[:awesome] = true
|
14
|
+
options[:parse_filter] << 1
|
15
|
+
|
16
|
+
# default variables
|
17
|
+
options[:as].must_equal "song"
|
18
|
+
options[:extend].must_equal Module
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
definition[:awesome].must_equal true
|
23
|
+
definition[:parse_filter].instance_variable_get(:@value).must_equal Representable::Pipeline[1]
|
24
|
+
definition[:render_filter].instance_variable_get(:@value).must_equal Representable::Pipeline[]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#[]" do
|
7
29
|
let (:definition) { Definition.new(:song) }
|
30
|
+
# default is nil.
|
31
|
+
it { definition[:bla].must_equal nil }
|
32
|
+
end
|
33
|
+
|
34
|
+
# merge!
|
35
|
+
describe "#merge!" do
|
36
|
+
let (:definition) { Definition.new(:song, :whatever => true) }
|
37
|
+
|
38
|
+
# merges new options.
|
39
|
+
it { definition.merge!(:something => true)[:something].must_equal true }
|
40
|
+
# doesn't override original options.
|
41
|
+
it { definition.merge!({:something => true})[:whatever].must_equal true }
|
42
|
+
# override original when passed in #merge!.
|
43
|
+
it { definition.merge!({:whatever => false})[:whatever].must_equal false }
|
44
|
+
|
8
45
|
|
9
46
|
it "runs macros" do
|
10
47
|
definition[:setter].must_equal nil
|
@@ -12,12 +49,46 @@ class DefinitionTest < MiniTest::Spec
|
|
12
49
|
definition[:setter].must_respond_to :evaluate
|
13
50
|
end
|
14
51
|
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
52
|
+
# with block
|
53
|
+
it do
|
54
|
+
definition = Definition.new(:song, :extend => Module).merge!({:something => true}) do |options|
|
55
|
+
options[:awesome] = true
|
56
|
+
options[:render_filter] << 1
|
57
|
+
|
58
|
+
# default variables
|
59
|
+
# options[:as].must_equal "song"
|
60
|
+
# options[:extend].must_equal Module
|
61
|
+
end
|
62
|
+
|
63
|
+
definition[:awesome].must_equal true
|
64
|
+
definition[:something].must_equal true
|
65
|
+
definition[:render_filter].instance_variable_get(:@value).must_equal Representable::Pipeline[1]
|
66
|
+
definition[:parse_filter].instance_variable_get(:@value).must_equal Representable::Pipeline[]
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "with :parse_filter" do
|
70
|
+
let (:definition) { Definition.new(:title, :parse_filter => 1) }
|
71
|
+
|
72
|
+
# merges :parse_filter and :render_filter.
|
73
|
+
it do
|
74
|
+
merged = definition.merge!(:parse_filter => 2)[:parse_filter]
|
75
|
+
|
76
|
+
merged.instance_variable_get(:@value).must_be_kind_of Representable::Pipeline
|
77
|
+
merged.instance_variable_get(:@value).size.must_equal 2
|
78
|
+
end
|
79
|
+
|
80
|
+
# :parse_filter can also be array.
|
81
|
+
it { definition.merge!(:parse_filter => [2, 3])[:parse_filter].instance_variable_get(:@value).size.must_equal 3 }
|
82
|
+
end
|
83
|
+
|
84
|
+
# does not change arguments
|
85
|
+
it do
|
86
|
+
Definition.new(:title).merge!(options = {:whatever => 1})
|
87
|
+
options.must_equal(:whatever => 1)
|
88
|
+
end
|
19
89
|
end
|
20
90
|
|
91
|
+
|
21
92
|
describe "generic API" do
|
22
93
|
before do
|
23
94
|
@def = Representable::Definition.new(:songs)
|
@@ -25,7 +96,7 @@ class DefinitionTest < MiniTest::Spec
|
|
25
96
|
|
26
97
|
it "responds to #representer_module" do
|
27
98
|
assert_equal nil, Representable::Definition.new(:song).representer_module
|
28
|
-
assert_equal Hash, Representable::Definition.new(:song, :extend => Hash).representer_module
|
99
|
+
assert_equal Hash, Representable::Definition.new(:song, :extend => Hash).representer_module
|
29
100
|
end
|
30
101
|
|
31
102
|
describe "#typed?" do
|
@@ -68,15 +139,39 @@ class DefinitionTest < MiniTest::Spec
|
|
68
139
|
assert_equal :"songs=", @def.setter
|
69
140
|
end
|
70
141
|
|
142
|
+
|
71
143
|
describe "#clone" do
|
144
|
+
subject { Representable::Definition.new(:title, :volume => 9, :clonable => Uber::Options::Value.new(1)) }
|
145
|
+
|
146
|
+
it { subject.clone.must_be_kind_of Representable::Definition }
|
147
|
+
it { subject.clone[:clonable].evaluate(nil).must_equal 1 }
|
148
|
+
|
72
149
|
it "clones @options" do
|
73
150
|
@def.merge!(:volume => 9)
|
151
|
+
|
74
152
|
cloned = @def.clone
|
75
153
|
cloned.merge!(:volume => 8)
|
76
154
|
|
77
155
|
assert_equal @def[:volume], 9
|
78
156
|
assert_equal cloned[:volume], 8
|
79
157
|
end
|
158
|
+
|
159
|
+
# pipeline gets cloned properly
|
160
|
+
describe "pipeline cloning" do
|
161
|
+
subject { Definition.new(:title, :render_filter => 1) }
|
162
|
+
|
163
|
+
it ("yy")do
|
164
|
+
cloned = subject.clone
|
165
|
+
|
166
|
+
cloned.merge!(:render_filter => 2)
|
167
|
+
|
168
|
+
subject.instance_variable_get(:@options)[:render_filter].must_equal [1]
|
169
|
+
cloned.instance_variable_get(:@options)[:render_filter].must_equal [1,2]
|
170
|
+
|
171
|
+
subject[:render_filter].instance_variable_get(:@value).must_equal [1]
|
172
|
+
cloned[:render_filter].instance_variable_get(:@value).must_equal [1,2]
|
173
|
+
end
|
174
|
+
end
|
80
175
|
end
|
81
176
|
end
|
82
177
|
|
@@ -91,26 +186,26 @@ class DefinitionTest < MiniTest::Spec
|
|
91
186
|
end
|
92
187
|
|
93
188
|
|
94
|
-
describe "#
|
95
|
-
# default if
|
189
|
+
describe "#skipable_empty_value?" do
|
190
|
+
# default if skipable_empty_value?
|
96
191
|
before do
|
97
192
|
@def = Representable::Definition.new(:song, :render_nil => true)
|
98
193
|
end
|
99
194
|
|
100
195
|
it "returns false when not nil" do
|
101
|
-
assert_equal false, @def.
|
196
|
+
assert_equal false, @def.skipable_empty_value?("Disconnect, Disconnect")
|
102
197
|
end
|
103
198
|
|
104
199
|
it "returns false when nil and :render_nil => true" do
|
105
|
-
assert_equal false, @def.
|
200
|
+
assert_equal false, @def.skipable_empty_value?(nil)
|
106
201
|
end
|
107
202
|
|
108
203
|
it "returns true when nil and :render_nil => false" do
|
109
|
-
assert_equal true, Representable::Definition.new(:song).
|
204
|
+
assert_equal true, Representable::Definition.new(:song).skipable_empty_value?(nil)
|
110
205
|
end
|
111
206
|
|
112
207
|
it "returns false when not nil and :render_nil => false" do
|
113
|
-
assert_equal false, Representable::Definition.new(:song).
|
208
|
+
assert_equal false, Representable::Definition.new(:song).skipable_empty_value?("Fatal Flu")
|
114
209
|
end
|
115
210
|
end
|
116
211
|
|
@@ -149,54 +244,6 @@ class DefinitionTest < MiniTest::Spec
|
|
149
244
|
end
|
150
245
|
|
151
246
|
|
152
|
-
describe "#writeable?" do
|
153
|
-
|
154
|
-
it "returns true when :writeable is not given" do
|
155
|
-
@def = Representable::Definition.new(:song)
|
156
|
-
assert_equal true, @def.writeable?
|
157
|
-
end
|
158
|
-
|
159
|
-
it "returns true when :writeable => true" do
|
160
|
-
@def = Representable::Definition.new(:song, :writeable => true)
|
161
|
-
assert_equal true, @def.writeable?
|
162
|
-
end
|
163
|
-
|
164
|
-
it "returns false when :writeable => false" do
|
165
|
-
@def = Representable::Definition.new(:song, :writeable => false)
|
166
|
-
assert_equal false, @def.writeable?
|
167
|
-
end
|
168
|
-
|
169
|
-
it "returns nil when :writeable is nil" do
|
170
|
-
@def = Representable::Definition.new(:song, :writeable => nil)
|
171
|
-
assert_equal nil, @def.writeable?
|
172
|
-
end
|
173
|
-
|
174
|
-
end
|
175
|
-
|
176
|
-
describe "#readable?" do
|
177
|
-
|
178
|
-
it "returns true when :readable is not given" do
|
179
|
-
@def = Representable::Definition.new(:song)
|
180
|
-
assert_equal true, @def.readable?
|
181
|
-
end
|
182
|
-
|
183
|
-
it "returns true when :readable => true" do
|
184
|
-
@def = Representable::Definition.new(:song, :readable => true)
|
185
|
-
assert_equal true, @def.readable?
|
186
|
-
end
|
187
|
-
|
188
|
-
it "returns false when :readable => false" do
|
189
|
-
@def = Representable::Definition.new(:song, :readable => false)
|
190
|
-
assert_equal false, @def.readable?
|
191
|
-
end
|
192
|
-
|
193
|
-
it "returns nil when :readable is nil" do
|
194
|
-
@def = Representable::Definition.new(:song, :readable => nil)
|
195
|
-
assert_equal nil, @def.readable?
|
196
|
-
end
|
197
|
-
|
198
|
-
end
|
199
|
-
|
200
247
|
describe "#binding" do
|
201
248
|
it "returns true when :binding is set" do
|
202
249
|
assert Representable::Definition.new(:songs, :binding => Object)[:binding]
|
@@ -223,10 +270,6 @@ class DefinitionTest < MiniTest::Spec
|
|
223
270
|
it "responds to #array?" do
|
224
271
|
assert @def.array?
|
225
272
|
end
|
226
|
-
|
227
|
-
it "responds to #default" do
|
228
|
-
assert_equal nil, @def.send(:default)
|
229
|
-
end
|
230
273
|
end
|
231
274
|
|
232
275
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FeaturesTest < MiniTest::Spec
|
4
|
+
module Title
|
5
|
+
def title; "Is It A Lie"; end
|
6
|
+
end
|
7
|
+
module Length
|
8
|
+
def length; "2:31"; end
|
9
|
+
end
|
10
|
+
|
11
|
+
definition = lambda {
|
12
|
+
feature Title
|
13
|
+
feature Length
|
14
|
+
|
15
|
+
# exec_context: :decorator, so the readers are called on the Decorator instance (which gets readers from features!).
|
16
|
+
property :title, exec_context: :decorator
|
17
|
+
property :length, exec_context: :decorator
|
18
|
+
property :details do
|
19
|
+
property :title, exec_context: :decorator
|
20
|
+
end
|
21
|
+
}
|
22
|
+
|
23
|
+
let (:song) { OpenStruct.new(:details => Object.new) }
|
24
|
+
|
25
|
+
describe "Module" do
|
26
|
+
representer! do
|
27
|
+
instance_exec &definition
|
28
|
+
end
|
29
|
+
|
30
|
+
it { song.extend(representer).to_hash.must_equal({"title"=>"Is It A Lie", "length"=>"2:31", "details"=>{"title"=>"Is It A Lie"}}) }
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
describe "Decorator" do
|
35
|
+
representer!(:decorator => true) do
|
36
|
+
instance_exec &definition
|
37
|
+
end
|
38
|
+
|
39
|
+
it { representer.new(song).to_hash.must_equal({"title"=>"Is It A Lie", "length"=>"2:31", "details"=>{"title"=>"Is It A Lie"}}) }
|
40
|
+
end
|
41
|
+
end
|
data/test/filter_test.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FilterPipelineTest < MiniTest::Spec
|
4
|
+
let (:block1) { lambda { |value, *| "1: #{value}" } }
|
5
|
+
let (:block2) { lambda { |value, *| "2: #{value}" } }
|
6
|
+
|
7
|
+
subject { Representable::Pipeline[block1, block2] }
|
8
|
+
|
9
|
+
it { subject.call(Object, "Horowitz").must_equal "2: 1: Horowitz" }
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
class FilterTest < MiniTest::Spec
|
14
|
+
representer! do
|
15
|
+
property :title
|
16
|
+
property :track,
|
17
|
+
:parse_filter => lambda { |val, doc, options| "#{val.downcase},#{doc},#{options}" },
|
18
|
+
:render_filter => lambda { |val, doc, options| "#{val.upcase},#{doc},#{options}" }
|
19
|
+
end
|
20
|
+
|
21
|
+
# gets doc and options.
|
22
|
+
it {
|
23
|
+
song = OpenStruct.new.extend(representer).from_hash("title" => "VULCAN EARS", "track" => "Nine")
|
24
|
+
song.title.must_equal "VULCAN EARS"
|
25
|
+
song.track.must_equal "nine,{\"title\"=>\"VULCAN EARS\", \"track\"=>\"Nine\"},{}"
|
26
|
+
}
|
27
|
+
|
28
|
+
it { OpenStruct.new("title" => "vulcan ears", "track" => "Nine").extend(representer).to_hash.must_equal( {"title"=>"vulcan ears", "track"=>"NINE,{\"title\"=>\"vulcan ears\"},{}"}) }
|
29
|
+
|
30
|
+
|
31
|
+
describe "#parse_filter" do
|
32
|
+
representer! do
|
33
|
+
property :track,
|
34
|
+
:parse_filter => [
|
35
|
+
lambda { |val, doc, options| "#{val}-1" },
|
36
|
+
lambda { |val, doc, options| "#{val}-2" }],
|
37
|
+
:render_filter => [
|
38
|
+
lambda { |val, doc, options| "#{val}-1" },
|
39
|
+
lambda { |val, doc, options| "#{val}-2" }]
|
40
|
+
|
41
|
+
# definition[:parse_filter].instance_variable_get(:@value) << lambda { |val, doc, options| "#{val}-1" }
|
42
|
+
# property :track, :parse_filter => lambda { |val, doc, options| "#{val}-2" }, :inherit => true
|
43
|
+
end
|
44
|
+
|
45
|
+
# order matters.
|
46
|
+
it { OpenStruct.new.extend(representer).from_hash("track" => "Nine").track.must_equal "Nine-1-2" }
|
47
|
+
it { OpenStruct.new("track" => "Nine").extend(representer).to_hash.must_equal({"track"=>"Nine-1-2"}) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
class RenderFilterTest < MiniTest::Spec
|
53
|
+
representer! do
|
54
|
+
property :track, :render_filter => [lambda { |val, doc, options| "#{val}-1" } ]
|
55
|
+
property :track, :render_filter => [lambda { |val, doc, options| "#{val}-2" } ], :inherit => true
|
56
|
+
end
|
57
|
+
|
58
|
+
it { OpenStruct.new("track" => "Nine").extend(representer).to_hash.must_equal({"track"=>"Nine-1-2"}) }
|
59
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ForCollectionTest < MiniTest::Spec
|
4
|
+
module SongRepresenter
|
5
|
+
include Representable::JSON
|
6
|
+
|
7
|
+
property :name
|
8
|
+
end
|
9
|
+
|
10
|
+
let (:songs) { [Song.new("Days Go By"), Song.new("Can't Take Them All")] }
|
11
|
+
let (:json) { "[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]" }
|
12
|
+
|
13
|
+
|
14
|
+
# Module.for_collection
|
15
|
+
# Decorator.for_collection
|
16
|
+
for_formats(
|
17
|
+
:hash => [Representable::Hash, out=[{"name" => "Days Go By"}, {"name"=>"Can't Take Them All"}], out],
|
18
|
+
:json => [Representable::JSON, out="[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]", out],
|
19
|
+
# :xml => [Representable::XML, out="<a><song></song><song></song></a>", out]
|
20
|
+
) do |format, mod, output, input|
|
21
|
+
|
22
|
+
describe "Module::for_collection [#{format}]" do
|
23
|
+
let (:format) { format }
|
24
|
+
|
25
|
+
let (:representer) {
|
26
|
+
Module.new do
|
27
|
+
include mod
|
28
|
+
property :name#, :as => :title
|
29
|
+
|
30
|
+
collection_representer :class => Song
|
31
|
+
|
32
|
+
# self.representation_wrap = :songs if format == :xml
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
it { render(songs.extend(representer.for_collection)).must_equal_document output }
|
37
|
+
it { render(representer.for_collection.prepare(songs)).must_equal_document output }
|
38
|
+
# parsing needs the class set, at least
|
39
|
+
it { parse([].extend(representer.for_collection), input).must_equal songs }
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "Module::for_collection without configuration [#{format}]" do
|
43
|
+
let (:format) { format }
|
44
|
+
|
45
|
+
let (:representer) {
|
46
|
+
Module.new do
|
47
|
+
include mod
|
48
|
+
property :name
|
49
|
+
end
|
50
|
+
}
|
51
|
+
|
52
|
+
# rendering works out of the box, no config necessary
|
53
|
+
it { render(songs.extend(representer.for_collection)).must_equal_document output }
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
describe "Decorator::for_collection [#{format}]" do
|
58
|
+
let (:format) { format }
|
59
|
+
let (:representer) {
|
60
|
+
Class.new(Representable::Decorator) do
|
61
|
+
include mod
|
62
|
+
property :name
|
63
|
+
|
64
|
+
collection_representer :class => Song
|
65
|
+
end
|
66
|
+
}
|
67
|
+
|
68
|
+
it { render(representer.for_collection.new(songs)).must_equal_document output }
|
69
|
+
it { parse(representer.for_collection.new([]), input).must_equal songs }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# with module including module
|
74
|
+
end
|
data/test/inherit_test.rb
CHANGED
@@ -40,6 +40,7 @@ class InheritTest < MiniTest::Spec
|
|
40
40
|
representer! do
|
41
41
|
include SongRepresenter
|
42
42
|
|
43
|
+
puts "passing block"
|
43
44
|
property :name, :inherit => true do # inherit as: title
|
44
45
|
property :string, :as => :s
|
45
46
|
property :length
|
@@ -86,11 +87,51 @@ class InheritTest < MiniTest::Spec
|
|
86
87
|
it "replaces inherited property" do
|
87
88
|
representer.representable_attrs.size.must_equal 2
|
88
89
|
|
89
|
-
definition = representer.representable_attrs
|
90
|
-
definition.keys.size.must_equal 2
|
90
|
+
definition = representer.representable_attrs.get(:track) # TODO: find a better way to assert Definition identity.
|
91
|
+
# definition.keys.size.must_equal 2
|
91
92
|
definition[:representable]. must_equal true
|
92
93
|
definition[:as].evaluate(nil).must_equal "track" # was "no".
|
93
94
|
end
|
94
95
|
end
|
95
96
|
|
96
|
-
|
97
|
+
|
98
|
+
# decorator
|
99
|
+
describe ":inherit with decorator" do
|
100
|
+
representer!(:decorator => true) do
|
101
|
+
property :hit do
|
102
|
+
property :title
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
let (:inheriting) {
|
107
|
+
class InheritingDecorator < representer
|
108
|
+
property :hit, :inherit => true do
|
109
|
+
property :length
|
110
|
+
end
|
111
|
+
self
|
112
|
+
end
|
113
|
+
}
|
114
|
+
|
115
|
+
it { inheriting.new(OpenStruct.new(:hit => OpenStruct.new(:title => "Hole In Your Soul", :length => "2:59"))).to_hash.must_equal(
|
116
|
+
{"hit"=>{"title"=>"Hole In Your Soul", "length"=>"2:59"}}) }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
# class InheritancingTest < MiniTest::Spec
|
122
|
+
# class SongDecorator < Representable::Decorator
|
123
|
+
# include Representable::Hash
|
124
|
+
# property :album do
|
125
|
+
# # does have Hash.
|
126
|
+
# property :title
|
127
|
+
# end
|
128
|
+
# end
|
129
|
+
|
130
|
+
# class JsonSongDecorator < SongDecorator
|
131
|
+
# include Representable::XML
|
132
|
+
# end
|
133
|
+
|
134
|
+
# it do
|
135
|
+
# puts JsonSongDecorator.new(OpenStruct.new(:album => OpenStruct.new(:title => "Erotic Cakes", :tracks => nil))).to_xml
|
136
|
+
# end
|
137
|
+
# end
|