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.
@@ -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