representable 0.11.0 → 0.12.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.
- data/CHANGES.textile +7 -0
- data/README.rdoc +60 -36
- data/lib/representable.rb +46 -23
- data/lib/representable/binding.rb +47 -0
- data/lib/representable/bindings/json_bindings.rb +19 -21
- data/lib/representable/bindings/xml_bindings.rb +13 -18
- data/lib/representable/definition.rb +8 -7
- data/lib/representable/json.rb +12 -11
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +10 -10
- data/test/definition_test.rb +10 -3
- data/test/json_test.rb +88 -52
- data/test/representable_test.rb +55 -46
- data/test/test_helper.rb +13 -0
- data/test/xml_test.rb +103 -55
- metadata +4 -3
@@ -1,12 +1,8 @@
|
|
1
|
+
require 'representable/binding'
|
2
|
+
|
1
3
|
module Representable
|
2
4
|
module XML
|
3
|
-
class Binding
|
4
|
-
attr_reader :definition
|
5
|
-
|
6
|
-
def initialize(definition)
|
7
|
-
@definition = definition
|
8
|
-
end
|
9
|
-
|
5
|
+
class Binding < Representable::Binding
|
10
6
|
def read(xml)
|
11
7
|
value_from_node(xml)
|
12
8
|
end
|
@@ -66,12 +62,17 @@ module Representable
|
|
66
62
|
|
67
63
|
# Represents a tag with object binding.
|
68
64
|
class ObjectBinding < Binding
|
65
|
+
include Representable::Binding::Hooks # includes #create_object and #write_object.
|
66
|
+
include Representable::Binding::Extend
|
67
|
+
|
69
68
|
# Adds the ref's markup to +xml+.
|
70
|
-
def write(xml,
|
69
|
+
def write(xml, object)
|
71
70
|
if definition.array?
|
72
|
-
|
71
|
+
object.each do |item|
|
72
|
+
write_entity(xml, item)
|
73
|
+
end
|
73
74
|
else
|
74
|
-
write_entity(xml,
|
75
|
+
write_entity(xml, object)
|
75
76
|
end
|
76
77
|
end
|
77
78
|
|
@@ -79,18 +80,12 @@ module Representable
|
|
79
80
|
# Deserializes the ref's element from +xml+.
|
80
81
|
def value_from_node(xml)
|
81
82
|
collect_for(xml) do |node|
|
82
|
-
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def write_collection(xml, collection)
|
87
|
-
collection.each do |item|
|
88
|
-
write_entity(xml, item)
|
83
|
+
create_object.from_node(node)
|
89
84
|
end
|
90
85
|
end
|
91
86
|
|
92
87
|
def write_entity(xml, entity)
|
93
|
-
xml.add_child(entity.to_node)
|
88
|
+
xml.add_child(write_object(entity).to_node)
|
94
89
|
end
|
95
90
|
end
|
96
91
|
end
|
@@ -1,16 +1,17 @@
|
|
1
1
|
module Representable
|
2
2
|
# Created at class compile time. Keeps configuration options for one property.
|
3
3
|
class Definition
|
4
|
-
attr_reader :name, :sought_type, :from, :default
|
4
|
+
attr_reader :name, :sought_type, :from, :default, :representer_module
|
5
5
|
alias_method :getter, :name
|
6
6
|
|
7
7
|
def initialize(sym, options={})
|
8
|
-
@name
|
9
|
-
@array
|
10
|
-
@from
|
11
|
-
@sought_type
|
12
|
-
@default
|
13
|
-
@default
|
8
|
+
@name = sym.to_s
|
9
|
+
@array = options[:collection]
|
10
|
+
@from = (options[:from] || name).to_s
|
11
|
+
@sought_type = options[:class] || options[:class] || :text # TODO: deprecate :class in 1.0.
|
12
|
+
@default = options[:default]
|
13
|
+
@default ||= [] if array?
|
14
|
+
@representer_module = options[:extend] # DISCUSS: move to Representable::DCI?
|
14
15
|
|
15
16
|
# FIXME: move me to xml.
|
16
17
|
if options[:from].to_s =~ /^@/
|
data/lib/representable/json.rb
CHANGED
@@ -15,7 +15,7 @@ module Representable
|
|
15
15
|
def self.included(base)
|
16
16
|
base.class_eval do
|
17
17
|
include Representable # either in Hero or HeroRepresentation.
|
18
|
-
extend ClassMethods
|
18
|
+
extend ClassMethods # DISCUSS: do that only for classes?
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -23,30 +23,31 @@ module Representable
|
|
23
23
|
module ClassMethods
|
24
24
|
# Creates a new object from the passed JSON document.
|
25
25
|
def from_json(*args, &block)
|
26
|
-
|
26
|
+
create_represented(*args, &block).from_json(*args)
|
27
27
|
end
|
28
28
|
|
29
29
|
def from_hash(*args, &block)
|
30
|
-
|
30
|
+
create_represented(*args, &block).from_hash(*args)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
|
34
35
|
# Parses the body as JSON and delegates to #from_hash.
|
35
|
-
def from_json(data, *args
|
36
|
+
def from_json(data, *args)
|
36
37
|
data = ::JSON[data]
|
37
|
-
from_hash(data, *args
|
38
|
+
from_hash(data, *args)
|
38
39
|
end
|
39
40
|
|
40
|
-
def from_hash(data, options={}
|
41
|
+
def from_hash(data, options={})
|
41
42
|
if wrap = options[:wrap] || representation_wrap
|
42
43
|
data = data[wrap.to_s]
|
43
44
|
end
|
44
45
|
|
45
|
-
update_properties_from(data,
|
46
|
+
update_properties_from(data, options)
|
46
47
|
end
|
47
48
|
|
48
|
-
def to_hash(options={}
|
49
|
-
hash = create_representation_with({},
|
49
|
+
def to_hash(options={})
|
50
|
+
hash = create_representation_with({}, options)
|
50
51
|
|
51
52
|
return hash unless wrap = options[:wrap] || representation_wrap
|
52
53
|
|
@@ -54,8 +55,8 @@ module Representable
|
|
54
55
|
end
|
55
56
|
|
56
57
|
# Returns a JSON string representing this object.
|
57
|
-
def to_json(*args
|
58
|
-
to_hash(*args
|
58
|
+
def to_json(*args)
|
59
|
+
to_hash(*args).to_json
|
59
60
|
end
|
60
61
|
|
61
62
|
def binding_for_definition(definition)
|
data/lib/representable/xml.rb
CHANGED
@@ -27,33 +27,33 @@ module Representable
|
|
27
27
|
# Example:
|
28
28
|
# band.from_xml("<band><name>Nofx</name></band>")
|
29
29
|
def from_xml(*args, &block)
|
30
|
-
|
30
|
+
create_represented(*args, &block).from_xml(*args)
|
31
31
|
end
|
32
32
|
|
33
33
|
def from_node(*args, &block)
|
34
|
-
|
34
|
+
create_represented(*args, &block).from_node(*args)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
|
39
|
-
def from_xml(doc, *args
|
39
|
+
def from_xml(doc, *args)
|
40
40
|
node = Nokogiri::XML(doc).root
|
41
|
-
from_node(node, *args
|
41
|
+
from_node(node, *args)
|
42
42
|
end
|
43
43
|
|
44
|
-
def from_node(node, options={}
|
45
|
-
update_properties_from(node,
|
44
|
+
def from_node(node, options={})
|
45
|
+
update_properties_from(node, options)
|
46
46
|
end
|
47
47
|
|
48
48
|
# Returns a Nokogiri::XML object representing this object.
|
49
|
-
def to_node(options={}
|
49
|
+
def to_node(options={})
|
50
50
|
root_tag = options[:wrap] || representation_wrap
|
51
51
|
|
52
|
-
create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new),
|
52
|
+
create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new), options)
|
53
53
|
end
|
54
54
|
|
55
|
-
def to_xml(*args
|
56
|
-
to_node(*args
|
55
|
+
def to_xml(*args)
|
56
|
+
to_node(*args).to_s
|
57
57
|
end
|
58
58
|
|
59
59
|
def binding_for_definition(definition)
|
data/test/definition_test.rb
CHANGED
@@ -6,9 +6,16 @@ class DefinitionTest < MiniTest::Spec
|
|
6
6
|
@def = Representable::Definition.new(:songs)
|
7
7
|
end
|
8
8
|
|
9
|
+
describe "DCI" do
|
10
|
+
it "responds to #representer_module" do
|
11
|
+
assert_equal nil, Representable::Definition.new(:song).representer_module
|
12
|
+
assert_equal Hash, Representable::Definition.new(:song, :extend => Hash).representer_module
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
9
16
|
it "responds to #typed?" do
|
10
17
|
assert ! @def.typed?
|
11
|
-
assert Representable::Definition.new(:songs, :
|
18
|
+
assert Representable::Definition.new(:songs, :class => Hash).typed?
|
12
19
|
end
|
13
20
|
|
14
21
|
it "responds to #getter and returns string" do
|
@@ -68,9 +75,9 @@ class DefinitionTest < MiniTest::Spec
|
|
68
75
|
end
|
69
76
|
end
|
70
77
|
|
71
|
-
describe ":
|
78
|
+
describe ":class => Item" do
|
72
79
|
before do
|
73
|
-
@def = Representable::Definition.new(:songs, :
|
80
|
+
@def = Representable::Definition.new(:songs, :class => Hash)
|
74
81
|
end
|
75
82
|
|
76
83
|
it "responds to #sought_type" do
|
data/test/json_test.rb
CHANGED
@@ -10,8 +10,8 @@ module JsonTest
|
|
10
10
|
before do
|
11
11
|
@Band = Class.new do
|
12
12
|
include Representable::JSON
|
13
|
-
|
14
|
-
|
13
|
+
property :name
|
14
|
+
property :label
|
15
15
|
|
16
16
|
def initialize(name=nil)
|
17
17
|
self.name = name if name
|
@@ -24,18 +24,35 @@ module JsonTest
|
|
24
24
|
|
25
25
|
describe ".from_json" do
|
26
26
|
it "is delegated to #from_json" do
|
27
|
-
block = lambda {|
|
28
|
-
@Band.any_instance.expects(:from_json).with("{}", "
|
29
|
-
@Band.from_json("{}", "
|
27
|
+
block = lambda {|*args|}
|
28
|
+
@Band.any_instance.expects(:from_json).with("{document}", "options") # FIXME: how to NOT expect block?
|
29
|
+
@Band.from_json("{document}", "options", &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "yields new object and options to block" do
|
33
|
+
@Band.class_eval { attr_accessor :new_name }
|
34
|
+
@band = @Band.from_json({}, :new_name => "Diesel Boy") do |band, options|
|
35
|
+
band.new_name= options[:new_name]
|
36
|
+
end
|
37
|
+
assert_equal "Diesel Boy", @band.new_name
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
33
41
|
|
34
42
|
describe ".from_hash" do
|
35
|
-
it "is delegated to #from_hash" do
|
36
|
-
block = lambda {|
|
37
|
-
@Band.any_instance.expects(:from_hash).with("{}", "
|
38
|
-
@Band.from_hash("{}", "
|
43
|
+
it "is delegated to #from_hash not passing the block" do
|
44
|
+
block = lambda {|*args|}
|
45
|
+
@Band.any_instance.expects(:from_hash).with("{document}", "options") # FIXME: how to NOT expect block?
|
46
|
+
@Band.from_hash("{document}", "options", &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "yields new object and options to block" do
|
50
|
+
@Band.class_eval { attr_accessor :new_name }
|
51
|
+
@band = @Band.from_hash({}, :new_name => "Diesel Boy") do |band, options|
|
52
|
+
band.new_name= options[:new_name]
|
53
|
+
end
|
54
|
+
|
55
|
+
assert_equal "Diesel Boy", @band.new_name
|
39
56
|
end
|
40
57
|
end
|
41
58
|
|
@@ -50,14 +67,6 @@ module JsonTest
|
|
50
67
|
@band.from_json(@json)
|
51
68
|
assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
|
52
69
|
end
|
53
|
-
|
54
|
-
it "forwards block to #from_hash" do
|
55
|
-
@band.from_json(@json) do |name|
|
56
|
-
name == :name
|
57
|
-
end
|
58
|
-
|
59
|
-
assert_equal ["Nofx", nil], [@band.name, @band.label]
|
60
|
-
end
|
61
70
|
end
|
62
71
|
|
63
72
|
|
@@ -72,14 +81,6 @@ module JsonTest
|
|
72
81
|
assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
|
73
82
|
end
|
74
83
|
|
75
|
-
it "forwards block to #update_properties_from" do
|
76
|
-
@band.from_hash(@hash) do |name|
|
77
|
-
name == :name
|
78
|
-
end
|
79
|
-
|
80
|
-
assert_equal ["Nofx", nil], [@band.name, @band.label]
|
81
|
-
end
|
82
|
-
|
83
84
|
it "respects :wrap option" do
|
84
85
|
@band.from_hash({"band" => {"name" => "This Is A Standoff"}}, :wrap => :band)
|
85
86
|
assert_equal "This Is A Standoff", @band.name
|
@@ -99,17 +100,6 @@ module JsonTest
|
|
99
100
|
it "delegates to #to_hash and returns string" do
|
100
101
|
assert_equal "{\"name\":\"Rise Against\"}", @Band.new("Rise Against").to_json
|
101
102
|
end
|
102
|
-
|
103
|
-
it "forwards block to #to_hash" do
|
104
|
-
band = @Band.new
|
105
|
-
band.name = "The Guinea Pigs"
|
106
|
-
band.label = "n/a"
|
107
|
-
json = band.to_json do |name|
|
108
|
-
name == :name
|
109
|
-
end
|
110
|
-
|
111
|
-
assert_equal "{\"name\":\"The Guinea Pigs\"}", json
|
112
|
-
end
|
113
103
|
end
|
114
104
|
|
115
105
|
|
@@ -138,7 +128,7 @@ module JsonTest
|
|
138
128
|
|
139
129
|
describe "#binding_for_definition" do
|
140
130
|
it "returns ObjectBinding" do
|
141
|
-
assert_kind_of Json::ObjectBinding, @band.binding_for_definition(Def.new(:band, :
|
131
|
+
assert_kind_of Json::ObjectBinding, @band.binding_for_definition(Def.new(:band, :class => Hash))
|
142
132
|
end
|
143
133
|
|
144
134
|
it "returns TextBinding" do
|
@@ -153,13 +143,59 @@ module JsonTest
|
|
153
143
|
end
|
154
144
|
end
|
155
145
|
end
|
146
|
+
|
147
|
+
|
148
|
+
describe "DCI" do
|
149
|
+
module SongRepresenter
|
150
|
+
include Representable::JSON
|
151
|
+
property :name
|
152
|
+
end
|
153
|
+
|
154
|
+
module AlbumRepresenter
|
155
|
+
include Representable::JSON
|
156
|
+
property :best_song, :class => Song, :extend => SongRepresenter
|
157
|
+
collection :songs, :class => Song, :extend => SongRepresenter
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
it "allows adding the representer by using #extend" do
|
162
|
+
module BandRepresenter
|
163
|
+
include Representable::JSON
|
164
|
+
property :name
|
165
|
+
end
|
166
|
+
|
167
|
+
civ = Object.new
|
168
|
+
civ.instance_eval do
|
169
|
+
def name; "CIV"; end
|
170
|
+
end
|
171
|
+
|
172
|
+
civ.extend(BandRepresenter)
|
173
|
+
assert_equal "{\"name\":\"CIV\"}", civ.to_json
|
174
|
+
end
|
175
|
+
|
176
|
+
it "extends contained models when serializing" do
|
177
|
+
@album = Album.new(Song.new("I Hate My Brain"), Song.new("Mr. Charisma"))
|
178
|
+
@album.extend(AlbumRepresenter)
|
179
|
+
|
180
|
+
assert_equal "{\"best_song\":{\"name\":\"Mr. Charisma\"},\"songs\":[{\"name\":\"I Hate My Brain\"},{\"name\":\"Mr. Charisma\"}]}", @album.to_json
|
181
|
+
end
|
182
|
+
|
183
|
+
it "extends contained models when deserializing" do
|
184
|
+
#@album = Album.new(Song.new("I Hate My Brain"), Song.new("Mr. Charisma"))
|
185
|
+
@album = Album.new
|
186
|
+
@album.extend(AlbumRepresenter)
|
187
|
+
|
188
|
+
@album.from_json("{\"best_song\":{\"name\":\"Mr. Charisma\"},\"songs\":[{\"name\":\"I Hate My Brain\"},{\"name\":\"Mr. Charisma\"}]}")
|
189
|
+
assert_equal "Mr. Charisma", @album.best_song.name
|
190
|
+
end
|
191
|
+
end
|
156
192
|
end
|
157
193
|
|
158
194
|
class PropertyTest < MiniTest::Spec
|
159
|
-
describe "
|
195
|
+
describe "property :name" do
|
160
196
|
class Band
|
161
197
|
include Representable::JSON
|
162
|
-
|
198
|
+
property :name
|
163
199
|
end
|
164
200
|
|
165
201
|
it "#from_json creates correct accessors" do
|
@@ -175,15 +211,15 @@ module JsonTest
|
|
175
211
|
end
|
176
212
|
end
|
177
213
|
|
178
|
-
describe ":
|
214
|
+
describe ":class => Item" do
|
179
215
|
class Label
|
180
216
|
include Representable::JSON
|
181
|
-
|
217
|
+
property :name
|
182
218
|
end
|
183
219
|
|
184
220
|
class Album
|
185
221
|
include Representable::JSON
|
186
|
-
|
222
|
+
property :label, :class => Label
|
187
223
|
end
|
188
224
|
|
189
225
|
it "#from_json creates one Item instance" do
|
@@ -198,11 +234,11 @@ module JsonTest
|
|
198
234
|
assert_equal '{"label":{"name":"Fat Wreck"}}', album.to_json
|
199
235
|
end
|
200
236
|
|
201
|
-
describe ":different_name, :
|
237
|
+
describe ":different_name, :class => Label" do
|
202
238
|
before do
|
203
239
|
@Album = Class.new do
|
204
240
|
include Representable::JSON
|
205
|
-
|
241
|
+
property :seller, :class => Label
|
206
242
|
end
|
207
243
|
end
|
208
244
|
|
@@ -218,7 +254,7 @@ module JsonTest
|
|
218
254
|
describe ":from => :songName" do
|
219
255
|
class Song
|
220
256
|
include Representable::JSON
|
221
|
-
|
257
|
+
property :name, :from => :songName
|
222
258
|
end
|
223
259
|
|
224
260
|
it "respects :from in #from_json" do
|
@@ -236,7 +272,7 @@ module JsonTest
|
|
236
272
|
before do
|
237
273
|
@Album = Class.new do
|
238
274
|
include Representable::JSON
|
239
|
-
|
275
|
+
property :name, :default => "30 Years Live"
|
240
276
|
end
|
241
277
|
end
|
242
278
|
|
@@ -279,10 +315,10 @@ end
|
|
279
315
|
|
280
316
|
|
281
317
|
class CollectionTest < MiniTest::Spec
|
282
|
-
describe "
|
318
|
+
describe "collection :name" do
|
283
319
|
class CD
|
284
320
|
include Representable::JSON
|
285
|
-
|
321
|
+
collection :songs
|
286
322
|
end
|
287
323
|
|
288
324
|
it "#from_json creates correct accessors" do
|
@@ -298,10 +334,10 @@ end
|
|
298
334
|
end
|
299
335
|
end
|
300
336
|
|
301
|
-
describe "
|
337
|
+
describe "collection :name, :class => Band" do
|
302
338
|
class Band
|
303
339
|
include Representable::JSON
|
304
|
-
|
340
|
+
property :name
|
305
341
|
|
306
342
|
def initialize(name="")
|
307
343
|
self.name = name
|
@@ -310,7 +346,7 @@ end
|
|
310
346
|
|
311
347
|
class Compilation
|
312
348
|
include Representable::JSON
|
313
|
-
|
349
|
+
collection :bands, :class => Band
|
314
350
|
end
|
315
351
|
|
316
352
|
describe "#from_json" do
|
@@ -339,7 +375,7 @@ end
|
|
339
375
|
describe ":from => :songList" do
|
340
376
|
class Songs
|
341
377
|
include Representable::JSON
|
342
|
-
|
378
|
+
collection :tracks, :from => :songList
|
343
379
|
end
|
344
380
|
|
345
381
|
it "respects :from in #from_json" do
|