representable 1.2.4 → 1.2.5
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 +4 -0
- data/README.rdoc +46 -2
- data/gemfiles/Gemfile.mongoid-2.4 +1 -0
- data/lib/representable/bindings/yaml_bindings.rb +69 -0
- data/lib/representable/version.rb +1 -1
- data/lib/representable/yaml.rb +67 -0
- data/test/json_test.rb +1 -1
- data/test/test_helper.rb +11 -6
- data/test/xml_test.rb +1 -1
- data/test/yaml_test.rb +166 -0
- metadata +5 -2
data/CHANGES.textile
CHANGED
data/README.rdoc
CHANGED
@@ -14,7 +14,7 @@ This keeps your representation knowledge in one place when implementing REST ser
|
|
14
14
|
|
15
15
|
* Bidirectional - rendering and parsing
|
16
16
|
* OOP access to documents
|
17
|
-
* Support for JSON and
|
17
|
+
* Support for JSON, XML and YAML.
|
18
18
|
* Coercion support with virtus[https://github.com/solnic/virtus]
|
19
19
|
|
20
20
|
|
@@ -279,7 +279,7 @@ There's no need to specify a representer for the +origin+ property since the +Lo
|
|
279
279
|
|
280
280
|
== XML support
|
281
281
|
|
282
|
-
Representable allows declaring a document's syntax and structure while having different formats. Currently, it ships with JSON and
|
282
|
+
Representable allows declaring a document's syntax and structure while having different formats. Currently, it ships with JSON, XML and YAML bindings.
|
283
283
|
|
284
284
|
class Hero
|
285
285
|
include Representable::XML
|
@@ -338,6 +338,50 @@ Note that +:wrap+ defines the container tag name.
|
|
338
338
|
</songs>
|
339
339
|
</album>
|
340
340
|
|
341
|
+
|
342
|
+
== YAML Support
|
343
|
+
|
344
|
+
Representers also come in handy if you need to render or parse YAML. The YAML module works exactly like the others.
|
345
|
+
|
346
|
+
module HotBandsRepresenter
|
347
|
+
include Representable::YAML
|
348
|
+
|
349
|
+
property :for
|
350
|
+
collection :names
|
351
|
+
end
|
352
|
+
|
353
|
+
Now, just call #to_yaml to render or #from_yaml to parse.
|
354
|
+
|
355
|
+
HotBands.new(:for => "Nick", :names => ["Bad Religion", "Van Halen", "Mozart"]).
|
356
|
+
extend(HotBandsRepresenter).
|
357
|
+
to_yaml
|
358
|
+
|
359
|
+
#=> ---
|
360
|
+
for: Nick
|
361
|
+
names:
|
362
|
+
- Bad Religion
|
363
|
+
- Van Halen
|
364
|
+
- Mozart
|
365
|
+
|
366
|
+
=== Nested Objects
|
367
|
+
|
368
|
+
The YAML parser does handle nested objects just like JSON and XML does it.
|
369
|
+
|
370
|
+
=== Flow Style Lists
|
371
|
+
|
372
|
+
If you want flow style (aka inline style) lists, use the :style option. See http://www.yaml.org/spec/1.2/spec.html#id2790088 for more infos on flow sequences.
|
373
|
+
|
374
|
+
module HotBandsRepresenter
|
375
|
+
include Representable::YAML
|
376
|
+
|
377
|
+
collection :names, :style => :flow
|
378
|
+
end
|
379
|
+
|
380
|
+
#=> ---
|
381
|
+
names: [Bad Religion, Van Halen, Mozart]
|
382
|
+
|
383
|
+
Need anything else for YAML? Let me know.
|
384
|
+
|
341
385
|
== Coercion
|
342
386
|
|
343
387
|
If you fancy coercion when parsing a document you can use the Coercion module which uses virtus[https://github.com/solnic/virtus] for type conversion.
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'representable/binding'
|
2
|
+
|
3
|
+
module Representable
|
4
|
+
module YAML
|
5
|
+
module ObjectBinding
|
6
|
+
include Binding::Object
|
7
|
+
|
8
|
+
def serialize_method
|
9
|
+
:to_ast
|
10
|
+
end
|
11
|
+
|
12
|
+
def deserialize_method
|
13
|
+
:from_hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def write_scalar(value)
|
17
|
+
value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class YAMLBinding < Representable::Binding
|
22
|
+
def initialize(definition) # FIXME. make generic.
|
23
|
+
super
|
24
|
+
extend ObjectBinding if definition.typed?
|
25
|
+
end
|
26
|
+
|
27
|
+
def read(hash)
|
28
|
+
return FragmentNotFound unless hash.has_key?(definition.from) # DISCUSS: put it all in #read for performance. not really sure if i like returning that special thing.
|
29
|
+
|
30
|
+
fragment = hash[definition.from]
|
31
|
+
deserialize_from(fragment)
|
32
|
+
end
|
33
|
+
|
34
|
+
def write(map, value)
|
35
|
+
map.children << Psych::Nodes::Scalar.new(definition.from)
|
36
|
+
map.children << serialize_for(value) # FIXME: should be serialize.
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
class PropertyBinding < YAMLBinding
|
42
|
+
def serialize_for(value)
|
43
|
+
write_scalar serialize(value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def deserialize_from(fragment)
|
47
|
+
deserialize(fragment)
|
48
|
+
end
|
49
|
+
|
50
|
+
def write_scalar(value)
|
51
|
+
Psych::Nodes::Scalar.new(value.to_s)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
class CollectionBinding < PropertyBinding
|
57
|
+
def serialize_for(value)
|
58
|
+
Psych::Nodes::Sequence.new.tap do |seq|
|
59
|
+
seq.style = Psych::Nodes::Sequence::FLOW if definition.options[:style] == :flow
|
60
|
+
value.each { |obj| seq.children << super(obj) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def deserialize_from(fragment)
|
65
|
+
fragment.collect { |item_fragment| deserialize(item_fragment) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'representable'
|
2
|
+
require 'representable/bindings/yaml_bindings'
|
3
|
+
|
4
|
+
module Representable
|
5
|
+
module YAML
|
6
|
+
def self.binding_for_definition(definition)
|
7
|
+
return CollectionBinding.new(definition) if definition.array?
|
8
|
+
#return HashBinding.new(definition) if definition.hash? and not definition.options[:use_attributes] # FIXME: hate this.
|
9
|
+
#return AttributeHashBinding.new(definition) if definition.hash? and definition.options[:use_attributes]
|
10
|
+
#return AttributeBinding.new(definition) if definition.attribute
|
11
|
+
PropertyBinding.new(definition)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
base.class_eval do
|
16
|
+
include Representable
|
17
|
+
extend ClassMethods
|
18
|
+
#self.representation_wrap = true # let representable compute it.
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
# Creates a new Ruby object from XML using mapping information declared in the class.
|
25
|
+
#
|
26
|
+
# Accepts a block yielding the currently iterated Definition. If the block returns false
|
27
|
+
# the property is skipped.
|
28
|
+
#
|
29
|
+
# Example:
|
30
|
+
# band.from_xml("<band><name>Nofx</name></band>")
|
31
|
+
def from_xml(*args, &block)
|
32
|
+
create_represented(*args, &block).from_xml(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def from_node(*args, &block)
|
36
|
+
create_represented(*args, &block).from_node(*args)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def from_yaml(doc, *args)
|
42
|
+
hash = Psych.load(doc)
|
43
|
+
from_hash(hash, *args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def from_hash(hash, options={})
|
47
|
+
update_properties_from(hash, options, YAML)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a Nokogiri::XML object representing this object.
|
51
|
+
def to_ast(options={})
|
52
|
+
#root_tag = options[:wrap] || representation_wrap
|
53
|
+
|
54
|
+
Psych::Nodes::Mapping.new.tap do |map|
|
55
|
+
create_representation_with(map, options, YAML)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_yaml(*args)
|
60
|
+
stream = Psych::Nodes::Stream.new
|
61
|
+
stream.children << doc = Psych::Nodes::Document.new
|
62
|
+
|
63
|
+
doc.children << to_ast(*args)
|
64
|
+
stream.to_yaml
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/test/json_test.rb
CHANGED
@@ -185,7 +185,7 @@ module JsonTest
|
|
185
185
|
end
|
186
186
|
|
187
187
|
it "extends contained models when serializing" do
|
188
|
-
@album = Album.new(Song.new("I Hate My Brain"), Song.new("Mr. Charisma"))
|
188
|
+
@album = Album.new([Song.new("I Hate My Brain"), mr=Song.new("Mr. Charisma")], mr)
|
189
189
|
@album.extend(AlbumRepresenter)
|
190
190
|
|
191
191
|
assert_json "{\"best_song\":{\"name\":\"Mr. Charisma\"},\"songs\":[{\"name\":\"I Hate My Brain\"},{\"name\":\"Mr. Charisma\"}]}", @album.to_json
|
data/test/test_helper.rb
CHANGED
@@ -13,20 +13,25 @@ require 'mocha'
|
|
13
13
|
|
14
14
|
class Album
|
15
15
|
attr_accessor :songs, :best_song
|
16
|
-
def initialize(
|
16
|
+
def initialize(songs=nil, best_song=nil)
|
17
17
|
@songs = songs
|
18
|
-
@best_song =
|
18
|
+
@best_song = best_song
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
songs == other.songs and best_song == other.best_song
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
22
26
|
class Song
|
23
|
-
attr_accessor :name
|
24
|
-
def initialize(name=nil)
|
25
|
-
@name
|
27
|
+
attr_accessor :name, :track
|
28
|
+
def initialize(name=nil, track=nil)
|
29
|
+
@name = name
|
30
|
+
@track = track
|
26
31
|
end
|
27
32
|
|
28
33
|
def ==(other)
|
29
|
-
name == other.name
|
34
|
+
name == other.name and track == other.track
|
30
35
|
end
|
31
36
|
end
|
32
37
|
|
data/test/xml_test.rb
CHANGED
@@ -178,7 +178,7 @@ class XmlTest < MiniTest::Spec
|
|
178
178
|
end
|
179
179
|
|
180
180
|
it "extends contained models when serializing" do
|
181
|
-
@album = Album.new(Song.new("I Hate My Brain"), Song.new("Mr. Charisma"))
|
181
|
+
@album = Album.new([Song.new("I Hate My Brain"), mr=Song.new("Mr. Charisma")], mr)
|
182
182
|
@album.extend(AlbumRepresenter)
|
183
183
|
|
184
184
|
assert_xml_equal "<album>
|
data/test/yaml_test.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'representable/yaml'
|
3
|
+
|
4
|
+
class YamlTest < MiniTest::Spec
|
5
|
+
def self.yaml_representer(&block)
|
6
|
+
Module.new do
|
7
|
+
include Representable::YAML
|
8
|
+
instance_exec &block
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def yaml_representer(&block)
|
13
|
+
self.class.yaml_representer(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
describe "property" do
|
18
|
+
let (:yaml) { yaml_representer do property :best_song end }
|
19
|
+
|
20
|
+
let (:album) { Album.new.tap do |album|
|
21
|
+
album.best_song = "Liar"
|
22
|
+
end }
|
23
|
+
|
24
|
+
describe "#to_yaml" do
|
25
|
+
it "renders plain property" do
|
26
|
+
album.extend(yaml).to_yaml.must_equal(
|
27
|
+
"---
|
28
|
+
best_song: Liar
|
29
|
+
")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "always renders values into strings" do
|
33
|
+
Album.new.tap { |a| a.best_song = 8675309 }.extend(yaml).to_yaml.must_equal(
|
34
|
+
"---
|
35
|
+
best_song: 8675309
|
36
|
+
"
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
describe "#from_yaml" do
|
43
|
+
it "parses plain property" do
|
44
|
+
album.extend(yaml).from_yaml(
|
45
|
+
"---
|
46
|
+
best_song: This Song Is Recycled
|
47
|
+
").best_song.must_equal "This Song Is Recycled"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
describe "with :class and :extend" do
|
53
|
+
yaml_song = yaml_representer do property :name end
|
54
|
+
let (:yaml_album) { Module.new do
|
55
|
+
include Representable::YAML
|
56
|
+
property :best_song, :extend => yaml_song, :class => Song
|
57
|
+
end }
|
58
|
+
|
59
|
+
let (:album) { Album.new.tap do |album|
|
60
|
+
album.best_song = Song.new("Liar")
|
61
|
+
end }
|
62
|
+
|
63
|
+
|
64
|
+
describe "#to_yaml" do
|
65
|
+
it "renders embedded typed property" do
|
66
|
+
album.extend(yaml_album).to_yaml.must_equal "---
|
67
|
+
best_song:
|
68
|
+
name: Liar
|
69
|
+
"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#from_yaml" do
|
74
|
+
it "parses embedded typed property" do
|
75
|
+
album.extend(yaml_album).from_yaml("---
|
76
|
+
best_song:
|
77
|
+
name: Go With Me
|
78
|
+
").must_equal Album.new(nil,Song.new("Go With Me"))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
describe "collection" do
|
86
|
+
let (:yaml) { yaml_representer do collection :songs end }
|
87
|
+
|
88
|
+
let (:album) { Album.new.tap do |album|
|
89
|
+
album.songs = ["Jackhammer", "Terrible Man"]
|
90
|
+
end }
|
91
|
+
|
92
|
+
|
93
|
+
describe "#to_yaml" do
|
94
|
+
it "renders a block style list per default" do
|
95
|
+
album.extend(yaml).to_yaml.must_equal "---
|
96
|
+
songs:
|
97
|
+
- Jackhammer
|
98
|
+
- Terrible Man
|
99
|
+
"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "renders a flow style list when :style => :flow set" do
|
103
|
+
yaml = yaml_representer { collection :songs, :style => :flow }
|
104
|
+
album.extend(yaml).to_yaml.must_equal "---
|
105
|
+
songs: [Jackhammer, Terrible Man]
|
106
|
+
"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
describe "#from_yaml" do
|
112
|
+
it "parses a block style list" do
|
113
|
+
album.extend(yaml).from_yaml("---
|
114
|
+
songs:
|
115
|
+
- Off Key Melody
|
116
|
+
- Sinking").must_equal Album.new(["Off Key Melody", "Sinking"])
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
it "parses a flow style list" do
|
121
|
+
album.extend(yaml).from_yaml("---
|
122
|
+
songs: [Off Key Melody, Sinking]").must_equal Album.new(["Off Key Melody", "Sinking"])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
describe "with :class and :extend" do
|
128
|
+
yaml_song = yaml_representer do
|
129
|
+
property :name
|
130
|
+
property :track
|
131
|
+
end
|
132
|
+
let (:yaml_album) { Module.new do
|
133
|
+
include Representable::YAML
|
134
|
+
collection :songs, :extend => yaml_song, :class => Song
|
135
|
+
end }
|
136
|
+
|
137
|
+
let (:album) { Album.new.tap do |album|
|
138
|
+
album.songs = [Song.new("Liar", 1), Song.new("What I Know", 2)]
|
139
|
+
end }
|
140
|
+
|
141
|
+
|
142
|
+
describe "#to_yaml" do
|
143
|
+
it "renders collection of typed property" do
|
144
|
+
album.extend(yaml_album).to_yaml.must_equal "---
|
145
|
+
songs:
|
146
|
+
- name: Liar
|
147
|
+
track: 1
|
148
|
+
- name: What I Know
|
149
|
+
track: 2
|
150
|
+
"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "#from_yaml" do
|
155
|
+
it "parses collection of typed property" do
|
156
|
+
album.extend(yaml_album).from_yaml("---
|
157
|
+
songs:
|
158
|
+
- name: One Shot Deal
|
159
|
+
track: 4
|
160
|
+
- name: Three Way Dance
|
161
|
+
track: 5").must_equal Album.new([Song.new("One Shot Deal", 4), Song.new("Three Way Dance", 5)])
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: representable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
@@ -160,6 +160,7 @@ files:
|
|
160
160
|
- lib/representable/binding.rb
|
161
161
|
- lib/representable/bindings/json_bindings.rb
|
162
162
|
- lib/representable/bindings/xml_bindings.rb
|
163
|
+
- lib/representable/bindings/yaml_bindings.rb
|
163
164
|
- lib/representable/coercion.rb
|
164
165
|
- lib/representable/definition.rb
|
165
166
|
- lib/representable/deprecations.rb
|
@@ -171,6 +172,7 @@ files:
|
|
171
172
|
- lib/representable/xml.rb
|
172
173
|
- lib/representable/xml/collection.rb
|
173
174
|
- lib/representable/xml/hash.rb
|
175
|
+
- lib/representable/yaml.rb
|
174
176
|
- representable.gemspec
|
175
177
|
- test/coercion_test.rb
|
176
178
|
- test/definition_test.rb
|
@@ -183,6 +185,7 @@ files:
|
|
183
185
|
- test/test_helper_test.rb
|
184
186
|
- test/xml_bindings_test.rb
|
185
187
|
- test/xml_test.rb
|
188
|
+
- test/yaml_test.rb
|
186
189
|
homepage: http://representable.apotomo.de
|
187
190
|
licenses: []
|
188
191
|
post_install_message:
|