representable 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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