representable 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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, value)
69
+ def write(xml, object)
71
70
  if definition.array?
72
- write_collection(xml, value)
71
+ object.each do |item|
72
+ write_entity(xml, item)
73
+ end
73
74
  else
74
- write_entity(xml, value)
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
- definition.sought_type.from_node(node)
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 = sym.to_s
9
- @array = options[:collection]
10
- @from = (options[:from] || name).to_s
11
- @sought_type = options[:as] || :text
12
- @default = options[:default]
13
- @default ||= [] if array?
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 =~ /^@/
@@ -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
- new.from_json(*args, &block)
26
+ create_represented(*args, &block).from_json(*args)
27
27
  end
28
28
 
29
29
  def from_hash(*args, &block)
30
- new.from_hash(*args, &block)
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, &block)
36
+ def from_json(data, *args)
36
37
  data = ::JSON[data]
37
- from_hash(data, *args, &block)
38
+ from_hash(data, *args)
38
39
  end
39
40
 
40
- def from_hash(data, options={}, &block)
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, &block)
46
+ update_properties_from(data, options)
46
47
  end
47
48
 
48
- def to_hash(options={}, &block)
49
- hash = create_representation_with({}, &block)
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, &block)
58
- to_hash(*args, &block).to_json
58
+ def to_json(*args)
59
+ to_hash(*args).to_json
59
60
  end
60
61
 
61
62
  def binding_for_definition(definition)
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "0.11.0"
2
+ VERSION = "0.12.0"
3
3
  end
@@ -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
- new.from_xml(*args, &block)
30
+ create_represented(*args, &block).from_xml(*args)
31
31
  end
32
32
 
33
33
  def from_node(*args, &block)
34
- new.from_node(*args, &block)
34
+ create_represented(*args, &block).from_node(*args)
35
35
  end
36
36
  end
37
37
 
38
38
 
39
- def from_xml(doc, *args, &block)
39
+ def from_xml(doc, *args)
40
40
  node = Nokogiri::XML(doc).root
41
- from_node(node, *args, &block)
41
+ from_node(node, *args)
42
42
  end
43
43
 
44
- def from_node(node, options={}, &block)
45
- update_properties_from(node, &block)
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={}, &block)
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), &block)
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, &block)
56
- to_node(*args, &block).to_s
55
+ def to_xml(*args)
56
+ to_node(*args).to_s
57
57
  end
58
58
 
59
59
  def binding_for_definition(definition)
@@ -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, :as => Hash).typed?
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 ":as => Item" do
78
+ describe ":class => Item" do
72
79
  before do
73
- @def = Representable::Definition.new(:songs, :as => Hash)
80
+ @def = Representable::Definition.new(:songs, :class => Hash)
74
81
  end
75
82
 
76
83
  it "responds to #sought_type" do
@@ -10,8 +10,8 @@ module JsonTest
10
10
  before do
11
11
  @Band = Class.new do
12
12
  include Representable::JSON
13
- representable_property :name
14
- representable_property :label
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 {|bind|}
28
- @Band.any_instance.expects(:from_json).with("{}", "yo") # FIXME: how to expect block?
29
- @Band.from_json("{}", "yo", &block)
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 {|bind|}
37
- @Band.any_instance.expects(:from_hash).with("{}", "yo") # FIXME: how to expect block?
38
- @Band.from_hash("{}", "yo", &block)
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, :as => Hash))
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 "representable_property :name" do
195
+ describe "property :name" do
160
196
  class Band
161
197
  include Representable::JSON
162
- representable_property :name
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 ":as => Item" do
214
+ describe ":class => Item" do
179
215
  class Label
180
216
  include Representable::JSON
181
- representable_property :name
217
+ property :name
182
218
  end
183
219
 
184
220
  class Album
185
221
  include Representable::JSON
186
- representable_property :label, :as => Label
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, :as => Label" do
237
+ describe ":different_name, :class => Label" do
202
238
  before do
203
239
  @Album = Class.new do
204
240
  include Representable::JSON
205
- representable_property :seller, :as => Label
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
- representable_property :name, :from => :songName
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
- representable_property :name, :default => "30 Years Live"
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 "representable_collection :name" do
318
+ describe "collection :name" do
283
319
  class CD
284
320
  include Representable::JSON
285
- representable_collection :songs
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 "representable_collection :name, :as => Band" do
337
+ describe "collection :name, :class => Band" do
302
338
  class Band
303
339
  include Representable::JSON
304
- representable_property :name
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
- representable_collection :bands, :as => Band
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
- representable_collection :tracks, :from => :songList
378
+ collection :tracks, :from => :songList
343
379
  end
344
380
 
345
381
  it "respects :from in #from_json" do