representable 0.9.3 → 0.10.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 CHANGED
@@ -1,3 +1,10 @@
1
+ h2. 0.10.0
2
+
3
+ * Wrapping can now be set through @Representable.representation_wrap=@. Possible values are:
4
+ * @false@: No wrapping. In XML context, this is undefined behaviour. Default in JSON.
5
+ * @String@: Wrap with provided string.
6
+ * @true@: compute wrapper from class name.
7
+
1
8
  h2. 0.9.3
2
9
 
3
10
  * Removed the @:as => [..]@ syntax in favor of @:array => true@.
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = Representable
2
2
 
3
- <em>Maps representation documents from and to Ruby objects.</em>
3
+ <em>Maps documents to Ruby objects and back.</em>
4
4
 
5
5
 
6
6
  == Introduction
@@ -9,8 +9,18 @@ _Representable_ maps fragments in documents to attributes in Ruby objects and ba
9
9
 
10
10
  This is especially helpful when implementing REST services and clients and keeps your representation knowledge in one place.
11
11
 
12
+
13
+ == Features
14
+
15
+ * Bidirectional - rendering and parsing
16
+ * OOP documents
17
+ * Support for JSON and XML
18
+
19
+
12
20
  == Example
13
21
 
22
+
23
+
14
24
  When writing a REST service you'd have to parse and extract data from an incoming representation document manually. Given the following XML document which represents an order.
15
25
 
16
26
  <order>
data/lib/representable.rb CHANGED
@@ -1,18 +1,17 @@
1
1
  require 'hooks/inheritable_attribute'
2
2
  require 'representable/definition'
3
- require 'representable/nokogiri_extensions'
4
-
5
3
 
6
4
  module Representable
7
5
  def self.included(base)
8
6
  base.class_eval do
9
- extend ClassMethods::Accessors, ClassMethods::Declarations
7
+ extend ClassMethods::Declarations
8
+ extend ClassMethods::Accessors
10
9
 
11
10
  extend Hooks::InheritableAttribute
12
11
  inheritable_attr :representable_attrs
13
12
  self.representable_attrs = []
14
13
 
15
- inheritable_attr :explicit_representation_name # FIXME: move to Accessors.
14
+ inheritable_attr :representable_wrap
16
15
  end
17
16
  end
18
17
 
@@ -85,12 +84,20 @@ private
85
84
  end
86
85
 
87
86
  module Accessors
88
- def representation_name=(name)
89
- self.explicit_representation_name = name
87
+ def representation_wrap=(name)
88
+ self.representable_wrap = name
89
+ end
90
+
91
+ # Returns the wrapper for the representation. Mostly used in XML.
92
+ def representation_wrap
93
+ return unless representable_wrap
94
+ return infer_representation_name if representable_wrap === true
95
+ representable_wrap
90
96
  end
91
97
 
92
- def representation_name
93
- explicit_representation_name or name.split('::').last.
98
+ private
99
+ def infer_representation_name
100
+ name.split('::').last.
94
101
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
95
102
  gsub(/([a-z\d])([A-Z])/,'\1_\2').
96
103
  downcase
@@ -44,9 +44,9 @@ module Representable
44
44
  class ObjectBinding < Binding
45
45
  def write(hash, value)
46
46
  if definition.array?
47
- hash.merge!({definition.from => value.collect {|v| v.to_hash(:wrap => false)}})
47
+ hash[definition.from] = value.collect {|v| v.to_hash(:wrap => false)}
48
48
  else
49
- hash.merge! value.to_hash
49
+ hash[definition.from] = value.to_hash(:wrap => false)
50
50
  end
51
51
  end
52
52
 
@@ -8,8 +8,6 @@ module Representable
8
8
  end
9
9
 
10
10
  def read(xml)
11
- xml = Nokogiri::XML::Node.from(xml) or return default
12
-
13
11
  value_from_node(xml) or default
14
12
  end
15
13
 
@@ -22,16 +20,6 @@ module Representable
22
20
  definition.from
23
21
  end
24
22
 
25
- def wrap(xml, opts = {:always_create => false})
26
- wrap_with = @auto_vals ? auto_wrapper : definition.wrapper
27
-
28
- return xml if !wrap_with || xml.name == wrap_with
29
- if !opts[:always_create] && (child = xml.children.find {|c| c.name == wrap_with })
30
- return child
31
- end
32
- xml.add_node(wrap_with.to_s)
33
- end
34
-
35
23
  def collect_for(xml)
36
24
  nodes = xml.search("./#{xpath}")
37
25
  vals = nodes.collect { |node| yield node }
@@ -44,9 +32,7 @@ module Representable
44
32
  # Represents a tag attribute.
45
33
  class AttributeBinding < Binding
46
34
  def write(xml, values)
47
- wrap(xml).tap do |xml|
48
- xml[definition.from] = values.to_s
49
- end
35
+ xml[definition.from] = values.to_s
50
36
  end
51
37
 
52
38
  private
@@ -58,21 +44,13 @@ module Representable
58
44
 
59
45
  # Represents text content in a tag. # FIXME: is this tested???
60
46
  class TextBinding < Binding
61
- # Updates the text in the given _xml_ block to
62
- # the _value_ provided.
63
47
  def write(xml, value)
64
- wrap(xml).tap do |xml|
65
- if definition.content?
66
- add(xml, value)
67
- elsif definition.name?
68
- xml.name = value
69
- elsif definition.array?
70
- value.each do |v|
71
- add(xml.add_node(definition.from), v)
72
- end
73
- else
74
- add(xml.add_node(definition.from), value)
48
+ if definition.array?
49
+ value.each do |v|
50
+ add(xml, definition.from, v)
75
51
  end
52
+ else
53
+ add(xml, definition.from, value)
76
54
  end
77
55
  end
78
56
 
@@ -82,9 +60,10 @@ module Representable
82
60
  node.content
83
61
  end
84
62
  end
85
-
86
- def add(dest, value)
87
- dest.content = value.to_s
63
+
64
+ def add(xml, name, value)
65
+ child = xml.add_child Nokogiri::XML::Node.new(name, xml.document)
66
+ child.content = value
88
67
  end
89
68
  end
90
69
 
@@ -93,12 +72,10 @@ module Representable
93
72
  class ObjectBinding < Binding
94
73
  # Adds the ref's markup to +xml+.
95
74
  def write(xml, value)
96
- wrap(xml).tap do |xml|
97
- if definition.array?
98
- write_collection(xml, value)
99
- else
100
- write_entity(xml, value)
101
- end
75
+ if definition.array?
76
+ write_collection(xml, value)
77
+ else
78
+ write_entity(xml, value)
102
79
  end
103
80
  end
104
81
 
@@ -107,18 +84,10 @@ module Representable
107
84
  []
108
85
  end
109
86
 
110
- def serialize(object)
111
- object.to_xml
112
- end
113
-
114
- def deserialize(node_class, xml)
115
- node_class.from_xml(xml)
116
- end
117
-
118
87
  # Deserializes the ref's element from +xml+.
119
88
  def value_from_node(xml)
120
89
  collect_for(xml) do |node|
121
- deserialize(definition.sought_type, node)
90
+ definition.sought_type.from_node(node)
122
91
  end
123
92
  end
124
93
 
@@ -129,7 +98,7 @@ module Representable
129
98
  end
130
99
 
131
100
  def write_entity(xml, entity)
132
- xml.add_child(serialize(entity))
101
+ xml.add_child(entity.to_node)
133
102
  end
134
103
  end
135
104
  end
@@ -1,6 +1,7 @@
1
1
  module Representable
2
- class Definition # :nodoc:
3
- attr_reader :name, :sought_type, :wrapper, :from
2
+ # Created at class compile time. Keeps configuration options for one property.
3
+ class Definition
4
+ attr_reader :name, :sought_type, :from
4
5
  alias_method :getter, :name
5
6
 
6
7
  def initialize(sym, options={})
@@ -28,23 +29,10 @@ module Representable
28
29
  sought_type.is_a?(Class)
29
30
  end
30
31
 
31
- def name?
32
- @name == '*'
33
- end
34
-
35
- def content?
36
- @name == '.'
37
- end
38
-
39
32
  def array?
40
33
  @array
41
34
  end
42
35
 
43
- def cdata? # FIXME: move to XML!
44
- @cdata
45
- end
46
-
47
-
48
36
  # Applies the block to +value+ which might also be a collection.
49
37
  def apply(value)
50
38
  return value unless value # DISCUSS: is that ok here?
@@ -24,33 +24,26 @@ module Representable
24
24
  (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
25
25
  end
26
26
 
27
- # Creates a new Ruby object from XML using mapping information declared in the class.
28
- #
29
- # Example:
30
- # book = Book.from_xml("<book><name>Beyond Java</name></book>")
31
- # DISCUSS: assumes shitty wrapping like :article => {:name => ...}
32
- def from_json(data, *args, &block)
33
- create_from_json.tap do |object|
34
- object.from_json(data, *args, &block)
35
- end
27
+ # Creates a new object from the passed JSON document.
28
+ def from_json(*args, &block)
29
+ new.from_json(*args, &block)
36
30
  end
37
31
 
38
- def from_hash(data)
39
- create_from_json.tap do |object|
40
- object.update_properties_from(data)
41
- end
42
- end
43
-
44
- private
45
- def create_from_json(*args)
46
- new(*args)
32
+ def from_hash(*args, &block)
33
+ new.from_hash(*args, &block)
47
34
  end
48
35
  end
49
36
 
50
- def from_json(data, options={}, &block)
37
+ # Parses the body as JSON and delegates to #from_hash.
38
+ def from_json(data, *args, &block)
51
39
  data = ::JSON[data]
52
- data = data[self.class.representation_name.to_s] unless options[:wrap] == false
53
- data ||= {} # FIXME: should we fail here? generate a warning?
40
+ from_hash(data, *args, &block)
41
+ end
42
+
43
+ def from_hash(data, options={}, &block)
44
+ if wrap = options[:wrap] || self.class.representation_wrap
45
+ data = data[wrap.to_s]
46
+ end
54
47
 
55
48
  update_properties_from(data, &block)
56
49
  end
@@ -58,8 +51,9 @@ module Representable
58
51
  def to_hash(options={})
59
52
  hash = create_representation_with({})
60
53
 
61
- # DISCUSS: where to wrap?
62
- options[:wrap] == false ? hash : {self.class.representation_name => hash}
54
+ return hash unless wrap = options[:wrap] || self.class.representation_wrap
55
+
56
+ {wrap => hash}
63
57
  end
64
58
 
65
59
  # Returns a JSON string representing this object.
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "0.9.3"
2
+ VERSION = "0.10.0"
3
3
  end
@@ -1,5 +1,4 @@
1
1
  require 'representable'
2
- require 'representable/nokogiri_extensions'
3
2
  require 'representable/bindings/xml_bindings'
4
3
 
5
4
  module Representable
@@ -13,20 +12,12 @@ module Representable
13
12
  base.class_eval do
14
13
  include Representable
15
14
  extend ClassMethods
15
+ self.representation_wrap = true # let representable compute it.
16
16
  end
17
17
  end
18
18
 
19
19
 
20
- class Definition < Representable::Definition
21
- # FIXME: extract xml-specific from Definition.
22
- end
23
-
24
-
25
20
  module ClassMethods
26
- def definition_class
27
- Definition
28
- end
29
-
30
21
  def binding_for_definition(definition)
31
22
  (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
32
23
  end
@@ -38,29 +29,34 @@ module Representable
38
29
  #
39
30
  # Example:
40
31
  # band.from_xml("<band><name>Nofx</name></band>")
41
- def from_xml(doc, *args, &block)
42
- create_from_xml(*args).tap do |object|
43
- object.from_xml(doc, *args, &block)
44
- end
32
+ def from_xml(*args, &block)
33
+ new.from_xml(*args, &block)
45
34
  end
46
35
 
47
- private
48
- def create_from_xml(*args)
49
- new(*args)
36
+ def from_node(*args, &block)
37
+ new.from_node(*args, &block)
50
38
  end
51
39
  end
52
40
 
41
+
53
42
  def from_xml(doc, *args, &block)
54
- xml = Nokogiri::XML::Node.from(doc)
55
- update_properties_from(xml, &block)
43
+ node = Nokogiri::XML(doc).root
44
+ from_node(node, *args, &block)
56
45
  end
57
46
 
47
+ def from_node(node, options={}, &block)
48
+ update_properties_from(node, &block)
49
+ end
58
50
 
59
51
  # Returns a Nokogiri::XML object representing this object.
60
- def to_xml(params={})
61
- root_tag = params[:name] || self.class.representation_name
52
+ def to_node(options={}, &block)
53
+ root_tag = options[:wrap] || self.class.representation_wrap
62
54
 
63
- create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new))
55
+ create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new), &block)
56
+ end
57
+
58
+ def to_xml(*args, &block)
59
+ to_node(*args, &block).to_s
64
60
  end
65
61
  end
66
62
  end
@@ -13,7 +13,7 @@ class BindingsTest < MiniTest::Spec
13
13
 
14
14
 
15
15
  describe "TextRef#read" do
16
- def parse_xml(xml); Nokogiri::XML::Node.from(xml); end
16
+ def parse_xml(xml); Nokogiri::XML(xml).root; end
17
17
 
18
18
  before do
19
19
  @ref = Representable::XML::TextBinding.new(Representable::Definition.new(:song))
data/test/json_test.rb CHANGED
@@ -9,7 +9,6 @@ module JsonTest
9
9
  describe "JSON module" do
10
10
  before do
11
11
  @Band = Class.new(Band) do
12
- self.representation_name= :band
13
12
  representable_property :label
14
13
  end
15
14
  end
@@ -42,35 +41,50 @@ module JsonTest
42
41
  end
43
42
 
44
43
  it "accepts json string" do
45
- @band.from_json({band: {name: "Nofx", label: "NOFX"}}.to_json)
44
+ @band.from_json({name: "Nofx", label: "NOFX"}.to_json)
46
45
  assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
47
46
  end
48
47
 
49
48
  it "forwards block to #update_properties_from" do
50
- @band.from_json({band: {name: "Nofx", label: "NOFX"}}.to_json) do |binding|
49
+ @band.from_json({name: "Nofx", label: "NOFX"}.to_json) do |binding|
51
50
  binding.definition.name == "name"
52
51
  end
53
52
 
54
53
  assert_equal ["Nofx", nil], [@band.name, @band.label]
55
54
  end
56
55
 
57
- it "accepts wrapped properties" do
58
- band = Band.new
59
- band.from_json({:band => {:name => "This Is A Standoff"}}.to_json)
60
- assert_equal "This Is A Standoff", band.name
56
+
57
+ it "doesn't use wrap per default" do
58
+ @band.from_json({:name => "This Is A Standoff"}.to_json)
59
+ assert_equal "This Is A Standoff", @band.name
60
+ end
61
+
62
+ it "respects :wrap option" do
63
+ @band.from_json({:band => {:name => "This Is A Standoff"}}.to_json, :wrap => :band)
64
+ assert_equal "This Is A Standoff", @band.name
65
+ end
66
+
67
+ it "respects :wrap option over representation_wrap" do
68
+ @Band.class_eval do
69
+ self.representation_wrap = :group
70
+ end
71
+ @band.from_json({:band => {:name => "This Is A Standoff"}}.to_json, :wrap => :band)
72
+ assert_equal "This Is A Standoff", @band.name
61
73
  end
62
74
 
63
- it "accepts unwrapped properties using the :wrap option" do # DISCUSS: should be default.
64
- band = Band.new
65
- band.from_json({name: "This Is A Standoff"}.to_json, :wrap => false)
66
- assert_equal "This Is A Standoff", band.name
75
+ it "respects representation_wrap" do
76
+ @Band.class_eval do
77
+ self.representation_wrap = :group
78
+ end
79
+ @band.from_json({:group => {:name => "This Is A Standoff"}}.to_json)
80
+ assert_equal "This Is A Standoff", @band.name
67
81
  end
68
82
  end
69
83
 
70
84
 
71
85
  describe ".from_json" do
72
86
  it "delegates to #from_json after object conception" do
73
- band = @Band.from_json({band: {name: "Nofx", label: "NOFX"}}.to_json) do |binding| binding.definition.name == "name" end
87
+ band = @Band.from_json({name: "Nofx", label: "NOFX"}.to_json) do |binding| binding.definition.name == "name" end
74
88
  assert_equal ["Nofx", nil], [band.name, band.label]
75
89
  end
76
90
 
@@ -95,12 +109,43 @@ module JsonTest
95
109
  end
96
110
  end
97
111
 
112
+
98
113
  describe ".from_hash" do
99
114
  it "accepts unwrapped hash with string keys" do
100
115
  band = Band.from_hash("name" => "Bombshell Rocks")
101
116
  assert_equal "Bombshell Rocks", band.name
102
117
  end
103
118
  end
119
+
120
+
121
+ describe "#to_json" do
122
+ before do
123
+ @band = @Band.new
124
+ @band.label = "Fat"
125
+ end
126
+
127
+ it "doesn't wrap per default" do
128
+ assert_equal "{\"label\":\"Fat\"}", @band.to_json
129
+ end
130
+
131
+ it "respects :wrap option" do
132
+ assert_equal "{\"band\":{\"label\":\"Fat\"}}", @band.to_json(:wrap => :band)
133
+ end
134
+
135
+ it "respects :wrap option over representation_wrap" do
136
+ @Band.class_eval do
137
+ self.representation_wrap = :group
138
+ end
139
+ assert_equal "{\"band\":{\"label\":\"Fat\"}}", @band.to_json(:wrap => :band)
140
+ end
141
+
142
+ it "respects representation_wrap" do
143
+ @Band.class_eval do
144
+ self.representation_wrap = :group
145
+ end
146
+ assert_equal "{\"group\":{\"label\":\"Fat\"}}", @band.to_json
147
+ end
148
+ end
104
149
  end
105
150
  end
106
151
 
@@ -112,7 +157,7 @@ module JsonTest
112
157
  end
113
158
 
114
159
  it "#from_json creates correct accessors" do
115
- band = Band.from_json({:band => {:name => "Bombshell Rocks"}}.to_json)
160
+ band = Band.from_json({:name => "Bombshell Rocks"}.to_json)
116
161
  assert_equal "Bombshell Rocks", band.name
117
162
  end
118
163
 
@@ -120,7 +165,7 @@ module JsonTest
120
165
  band = Band.new
121
166
  band.name = "Cigar"
122
167
 
123
- assert_equal '{"band":{"name":"Cigar"}}', band.to_json
168
+ assert_equal '{"name":"Cigar"}', band.to_json
124
169
  end
125
170
  end
126
171
 
@@ -136,7 +181,7 @@ module JsonTest
136
181
  end
137
182
 
138
183
  it "#from_json creates one Item instance" do
139
- album = Album.from_json('{"album":{"label":{"name":"Fat Wreck"}}}')
184
+ album = Album.from_json('{"label":{"name":"Fat Wreck"}}')
140
185
  assert_equal "Fat Wreck", album.label.name
141
186
  end
142
187
 
@@ -144,7 +189,23 @@ module JsonTest
144
189
  label = Label.new; label.name = "Fat Wreck"
145
190
  album = Album.new; album.label = label
146
191
 
147
- assert_equal '{"album":{"label":{"name":"Fat Wreck"}}}', album.to_json
192
+ assert_equal '{"label":{"name":"Fat Wreck"}}', album.to_json
193
+ end
194
+
195
+ describe ":different_name, :as => Label" do
196
+ before do
197
+ @Album = Class.new do
198
+ include Representable::JSON
199
+ representable_property :seller, :as => Label
200
+ end
201
+ end
202
+
203
+ it "#to_xml respects the different name" do
204
+ label = Label.new; label.name = "Fat Wreck"
205
+ album = @Album.new; album.seller = label
206
+
207
+ assert_equal "{\"seller\":{\"name\":\"Fat Wreck\"}}", album.to_json(:wrap => false)
208
+ end
148
209
  end
149
210
  end
150
211
 
@@ -155,13 +216,13 @@ module JsonTest
155
216
  end
156
217
 
157
218
  it "respects :from in #from_json" do
158
- song = Song.from_json({:song => {:songName => "Run To The Hills"}}.to_json)
219
+ song = Song.from_json({:songName => "Run To The Hills"}.to_json)
159
220
  assert_equal "Run To The Hills", song.name
160
221
  end
161
222
 
162
223
  it "respects :from in #to_json" do
163
224
  song = Song.new; song.name = "Run To The Hills"
164
- assert_equal '{"song":{"songName":"Run To The Hills"}}', song.to_json
225
+ assert_equal '{"songName":"Run To The Hills"}', song.to_json
165
226
  end
166
227
  end
167
228
  end
@@ -175,7 +236,7 @@ module JsonTest
175
236
  end
176
237
 
177
238
  it "#from_json creates correct accessors" do
178
- cd = CD.from_json({:cd => {:songs => ["Out in the cold", "Microphone"]}}.to_json)
239
+ cd = CD.from_json({:songs => ["Out in the cold", "Microphone"]}.to_json)
179
240
  assert_equal ["Out in the cold", "Microphone"], cd.songs
180
241
  end
181
242
 
@@ -183,7 +244,7 @@ module JsonTest
183
244
  cd = CD.new
184
245
  cd.songs = ["Out in the cold", "Microphone"]
185
246
 
186
- assert_equal '{"cd":{"songs":["Out in the cold","Microphone"]}}', cd.to_json
247
+ assert_equal '{"songs":["Out in the cold","Microphone"]}', cd.to_json
187
248
  end
188
249
  end
189
250
 
@@ -204,9 +265,9 @@ module JsonTest
204
265
 
205
266
  describe "#from_json" do
206
267
  it "pushes collection items to array" do
207
- cd = Compilation.from_json({:compilation => {:bands => [
268
+ cd = Compilation.from_json({:bands => [
208
269
  {:name => "Cobra Skulls"},
209
- {:name => "Diesel Boy"}]}}.to_json)
270
+ {:name => "Diesel Boy"}]}.to_json)
210
271
  assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
211
272
  end
212
273
 
@@ -220,7 +281,7 @@ module JsonTest
220
281
  cd = Compilation.new
221
282
  cd.bands = [Band.new("Diesel Boy"), Band.new("Bad Religion")]
222
283
 
223
- assert_equal '{"compilation":{"bands":[{"name":"Diesel Boy"},{"name":"Bad Religion"}]}}', cd.to_json
284
+ assert_equal '{"bands":[{"name":"Diesel Boy"},{"name":"Bad Religion"}]}', cd.to_json
224
285
  end
225
286
  end
226
287
 
@@ -232,7 +293,7 @@ module JsonTest
232
293
  end
233
294
 
234
295
  it "respects :from in #from_json" do
235
- songs = Songs.from_json({:songs => {:songList => ["Out in the cold", "Microphone"]}}.to_json)
296
+ songs = Songs.from_json({:songList => ["Out in the cold", "Microphone"]}.to_json)
236
297
  assert_equal ["Out in the cold", "Microphone"], songs.tracks
237
298
  end
238
299
 
@@ -240,7 +301,7 @@ module JsonTest
240
301
  songs = Songs.new
241
302
  songs.tracks = ["Out in the cold", "Microphone"]
242
303
 
243
- assert_equal '{"songs":{"songList":["Out in the cold","Microphone"]}}', songs.to_json
304
+ assert_equal '{"songList":["Out in the cold","Microphone"]}', songs.to_json
244
305
  end
245
306
  end
246
307
  end
@@ -58,7 +58,7 @@ class RepresentableTest < MiniTest::Spec
58
58
  end
59
59
 
60
60
 
61
- describe "#representation_name" do
61
+ describe "#representation_wrap" do
62
62
  class SoundSystem
63
63
  include Representable
64
64
  end
@@ -70,14 +70,24 @@ class RepresentableTest < MiniTest::Spec
70
70
  class SoftcoreBand < HardcoreBand
71
71
  end
72
72
 
73
- it "provides the root 'tag' name" do
74
- assert_equal "sound_system", SoundSystem.representation_name
73
+ it "returns false per default" do
74
+ assert_equal nil, SoundSystem.representation_wrap
75
+ end
76
+
77
+
78
+ it "infers a printable class name if set to true" do
79
+ HardcoreBand.representation_wrap = true
80
+ assert_equal "hardcore_band", HardcoreBand.send(:representation_wrap)
81
+ end
82
+
83
+ it "can be set explicitely" do
84
+ HardcoreBand.representation_wrap = "breach"
85
+ assert_equal "breach", HardcoreBand.representation_wrap
75
86
  end
76
87
 
77
88
  it "inherits correctly" do
78
- HardcoreBand.representation_name = "breach"
79
- assert_equal "breach", HardcoreBand.representation_name
80
- assert_equal "breach", SoftcoreBand.representation_name
89
+ HardcoreBand.representation_wrap = "breach"
90
+ assert_equal "breach", SoftcoreBand.representation_wrap
81
91
  end
82
92
  end
83
93
 
data/test/xml_test.rb CHANGED
@@ -10,9 +10,12 @@ class Band
10
10
  end
11
11
  end
12
12
 
13
- class Label
14
- def to_xml
15
- "<label>Fat Wreck</label>"
13
+ class Album
14
+ include Representable::XML
15
+ representable_property :band, :as => Band
16
+
17
+ def initialize(band=nil)
18
+ band and self.band = band
16
19
  end
17
20
  end
18
21
 
@@ -25,48 +28,65 @@ class XmlTest < MiniTest::Spec
25
28
  before do
26
29
  @Band = Class.new do
27
30
  include Representable::XML
28
- self.representation_name= :band
31
+ self.representation_wrap = :band
29
32
  representable_property :name
30
33
  representable_property :label
31
34
  end
32
35
  end
33
-
34
- class Band
35
- include Representable::XML
36
- representable_property :href, :from => "@href"
37
- representable_property :title, :from => "@title"
36
+
37
+
38
+ describe ".from_xml" do
39
+ it "is delegated to #from_xml" do
40
+ block = lambda {|bind|}
41
+ @Band.any_instance.expects(:from_xml).with("{}", "yo") # FIXME: how to expect block?
42
+ @Band.from_xml("{}", "yo", &block)
43
+ end
38
44
  end
39
45
 
40
- it "#definition_class returns Definition class" do
41
- assert_equal XML::Definition, Band.definition_class
46
+
47
+ describe ".from_node" do
48
+ it "is delegated to #from_node" do
49
+ block = lambda {|bind|}
50
+ @Band.any_instance.expects(:from_node).with("{}", "yo") # FIXME: how to expect block?
51
+ @Band.from_node("{}", "yo", &block)
52
+ end
42
53
  end
43
-
44
- describe "#binding_for_definition" do
45
- it "returns AttributeBinding" do
46
- assert_kind_of XML::AttributeBinding, Band.binding_for_definition(Def.new(:band, :from => "@band"))
54
+
55
+
56
+ describe "#from_xml" do
57
+ before do
58
+ @band = @Band.new
59
+ @xml = %{<band><name>Nofx</name><label>NOFX</label></band>}
47
60
  end
48
61
 
49
- it "returns ObjectBinding" do
50
- assert_kind_of XML::ObjectBinding, Band.binding_for_definition(Def.new(:band, :as => Hash))
62
+ it "parses XML and assigns properties" do
63
+ @band.from_xml(@xml)
64
+ assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
51
65
  end
52
66
 
53
- it "returns TextBinding" do
54
- assert_kind_of XML::TextBinding, Band.binding_for_definition(Def.new(:band, :from => :content))
67
+ it "forwards block to #from_node" do
68
+ @band.from_xml(@xml) do |binding|
69
+ binding.definition.name == "name"
70
+ end
71
+
72
+ assert_equal ["Nofx", nil], [@band.name, @band.label]
55
73
  end
56
74
  end
57
75
 
58
- describe "#from_xml" do
76
+
77
+ describe "#from_node" do
59
78
  before do
60
79
  @band = @Band.new
80
+ @xml = Nokogiri::XML(%{<band><name>Nofx</name><label>NOFX</label></band>}).root
61
81
  end
62
82
 
63
- it "accepts xml string" do
64
- @band.from_xml(%{<band><name>Nofx</name><label>NOFX</label></band>})
83
+ it "receives Nokogiri node and assigns properties" do
84
+ @band.from_node(@xml)
65
85
  assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
66
86
  end
67
87
 
68
88
  it "forwards block to #update_properties_from" do
69
- @band.from_xml(%{<band><name>Nofx</name><label>NOFX</label></band>}) do |binding|
89
+ @band.from_node(@xml) do |binding|
70
90
  binding.definition.name == "name"
71
91
  end
72
92
 
@@ -74,17 +94,61 @@ class XmlTest < MiniTest::Spec
74
94
  end
75
95
  end
76
96
 
77
- describe ".from_xml" do
78
- it "passes all args to #from_xml" do
79
- block = lambda {|bind|}
80
- @Band.any_instance.expects(:from_xml).with("{}", "yo") # FIXME: how to expect block?
81
- @Band.from_xml("{}", "yo", &block)
97
+
98
+ describe "#to_xml" do
99
+ it "delegates to #to_node and returns string" do
100
+ assert_xml_equal "<band><name>Rise Against</name></band>", Band.new("Rise Against").to_xml
101
+ end
102
+
103
+ it "forwards block to #to_node" do
104
+ band = @Band.new
105
+ band.name = "The Guinea Pigs"
106
+ band.label = "n/a"
107
+ xml = band.to_xml do |binding|
108
+ binding.definition.name == "name"
109
+ end
110
+
111
+ assert_xml_equal "<band><name>The Guinea Pigs</name></band>", xml
82
112
  end
83
113
  end
84
114
 
115
+
116
+ describe "#to_node" do
117
+ it "returns Nokogiri node" do
118
+ node = Band.new("Rise Against").to_node
119
+ assert_kind_of Nokogiri::XML::Element, node
120
+ end
121
+
122
+ it "wraps with infered class name per default" do
123
+ node = Band.new("Rise Against").to_node
124
+ assert_xml_equal "<band><name>Rise Against</name></band>", node.to_s
125
+ end
126
+
127
+ it "respects #representation_wrap=" do
128
+ klass = Class.new(Band)
129
+ klass.representation_wrap = :group
130
+ assert_xml_equal "<group><name>Rise Against</name></group>", klass.new("Rise Against").to_node.to_s
131
+ end
132
+ end
133
+
134
+
135
+ describe "#binding_for_definition" do
136
+ it "returns AttributeBinding" do
137
+ assert_kind_of XML::AttributeBinding, @Band.binding_for_definition(Def.new(:band, :from => "@band"))
138
+ end
139
+
140
+ it "returns ObjectBinding" do
141
+ assert_kind_of XML::ObjectBinding, @Band.binding_for_definition(Def.new(:band, :as => Hash))
142
+ end
143
+
144
+ it "returns TextBinding" do
145
+ assert_kind_of XML::TextBinding, @Band.binding_for_definition(Def.new(:band, :from => :content))
146
+ end
147
+ end
85
148
  end
86
149
  end
87
150
 
151
+
88
152
  class AttributesTest < MiniTest::Spec
89
153
  describe ":from => @rel" do
90
154
  class Link
@@ -105,19 +169,13 @@ class AttributesTest < MiniTest::Spec
105
169
  link = Link.new
106
170
  link.href = "http://apotomo.de/"
107
171
 
108
- assert_xml_equal %{<link href="http://apotomo.de/">}, link.to_xml.to_s
172
+ assert_xml_equal %{<link href="http://apotomo.de/">}, link.to_xml
109
173
  end
110
174
  end
111
175
  end
112
176
 
113
177
  class TypedPropertyTest < MiniTest::Spec
114
178
  describe ":as => Item" do
115
- class Album
116
- include Representable::XML
117
- representable_property :band, :as => Band
118
- representable_property :label, :as => Label
119
- end
120
-
121
179
  it "#from_xml creates one Item instance" do
122
180
  album = Album.from_xml(%{
123
181
  <album>
@@ -129,25 +187,25 @@ class TypedPropertyTest < MiniTest::Spec
129
187
 
130
188
  describe "#to_xml" do
131
189
  it "doesn't escape xml from Band#to_xml" do
132
- band = Band.new
133
- band.name = "Bad Religion"
134
- album = Album.new
135
- album.band = band
190
+ band = Band.new("Bad Religion")
191
+ album = Album.new(band)
136
192
 
137
193
  assert_xml_equal %{<album>
138
194
  <band>
139
195
  <name>Bad Religion</name>
140
196
  </band>
141
- </album>}, album.to_xml.to_s
197
+ </album>}, album.to_xml
142
198
  end
143
199
 
144
- it "doesn't escape and wrap string from Label#to_xml" do
145
- album = Album.new
146
- album.label = Label.new
200
+ it "doesn't escape and wrap string from Band#to_node" do
201
+ band = Band.new("Bad Religion")
202
+ band.instance_eval do
203
+ def to_node
204
+ "<band>Baaaad Religion</band>"
205
+ end
206
+ end
147
207
 
148
- assert_xml_equal %{<album>
149
- <label>Fat Wreck</label>
150
- </album>}, album.to_xml.to_s
208
+ assert_xml_equal %{<album><band>Baaaad Religion</band></album>}, Album.new(band).to_xml
151
209
  end
152
210
  end
153
211
  end
@@ -155,19 +213,12 @@ end
155
213
 
156
214
 
157
215
  class CollectionTest < MiniTest::Spec
158
- describe ":as => [Band], :tag => :band" do
216
+ describe ":as => Band, :from => :band, :collection => true" do
159
217
  class Compilation
160
218
  include Representable::XML
161
219
  representable_collection :bands, :as => Band, :from => :band
162
220
  end
163
221
 
164
- describe "#representable_collection" do
165
- it "declares a collection" do
166
- assert Compilation.representable_attrs.first.array?
167
- end
168
- end
169
-
170
-
171
222
  describe "#from_xml" do
172
223
  it "pushes collection items to array" do
173
224
  cd = Compilation.from_xml(%{
@@ -195,12 +246,12 @@ class CollectionTest < MiniTest::Spec
195
246
  assert_xml_equal %{<compilation>
196
247
  <band><name>Diesel Boy</name></band>
197
248
  <band><name>Bad Religion</name></band>
198
- </compilation>}, cd.to_xml.to_s
249
+ </compilation>}, cd.to_xml
199
250
  end
200
251
  end
201
252
 
202
253
 
203
- describe ":as => []" do
254
+ describe ":from" do
204
255
  class Album
205
256
  include Representable::XML
206
257
  representable_collection :songs, :from => :song
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 9
8
- - 3
9
- version: 0.9.3
7
+ - 10
8
+ - 0
9
+ version: 0.10.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Nick Sutterer
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-11-27 00:00:00 +01:00
17
+ date: 2011-11-29 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -133,7 +133,6 @@ files:
133
133
  - lib/representable/bindings/xml_bindings.rb
134
134
  - lib/representable/definition.rb
135
135
  - lib/representable/json.rb
136
- - lib/representable/nokogiri_extensions.rb
137
136
  - lib/representable/version.rb
138
137
  - lib/representable/xml.rb
139
138
  - representable.gemspec
@@ -1,19 +0,0 @@
1
- require 'nokogiri'
2
-
3
- Nokogiri::XML::Node.class_eval do
4
- def add_node(name)
5
- add_child Nokogiri::XML::Node.new(name, document)
6
- end
7
-
8
- # FIXME: remove switch. where is #from used with nodes?
9
- def self.from(data)
10
- case data
11
- when Nokogiri::XML::Node
12
- data
13
- when Nokogiri::XML::Document
14
- data.root
15
- else
16
- Nokogiri::XML(data).root
17
- end
18
- end
19
- end