representable 1.0.1 → 1.1.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 +10 -0
- data/README.rdoc +34 -0
- data/lib/representable.rb +5 -0
- data/lib/representable/binding.rb +12 -15
- data/lib/representable/bindings/json_bindings.rb +54 -34
- data/lib/representable/bindings/xml_bindings.rb +96 -59
- data/lib/representable/definition.rb +30 -30
- data/lib/representable/json.rb +3 -2
- data/lib/representable/json/collection.rb +38 -0
- data/lib/representable/json/hash.rb +38 -0
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +4 -3
- data/lib/representable/xml/collection.rb +38 -0
- data/lib/representable/xml/hash.rb +38 -0
- data/test/definition_test.rb +23 -24
- data/test/json_bindings_test.rb +119 -0
- data/test/json_test.rb +127 -3
- data/test/polymorphic_test.rb +45 -0
- data/test/representable_test.rb +0 -1
- data/test/test_helper.rb +4 -0
- data/test/xml_bindings_test.rb +153 -0
- data/test/xml_test.rb +10 -5
- metadata +109 -73
- data/test/bindings_test.rb +0 -16
data/lib/representable/json.rb
CHANGED
@@ -9,8 +9,9 @@ module Representable
|
|
9
9
|
# things might work as expected.
|
10
10
|
module JSON
|
11
11
|
def self.binding_for_definition(definition)
|
12
|
-
return
|
13
|
-
|
12
|
+
return CollectionBinding.new(definition) if definition.array?
|
13
|
+
return HashBinding.new(definition) if definition.hash?
|
14
|
+
PropertyBinding.new(definition)
|
14
15
|
end
|
15
16
|
|
16
17
|
def self.included(base)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Representable::JSON
|
2
|
+
module Collection
|
3
|
+
include Representable::JSON
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
include Representable
|
8
|
+
extend ClassMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def items(options)
|
15
|
+
collection :_self, options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def create_representation_with(doc, options, format)
|
21
|
+
bin = representable_bindings_for(format).first
|
22
|
+
bin.serialize_for(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_properties_from(doc, options, format)
|
26
|
+
bin = representable_bindings_for(format).first
|
27
|
+
value = bin.deserialize_from(doc)
|
28
|
+
replace(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
# FIXME: refactor Definition so we can simply add options in #items to existing definition.
|
32
|
+
def representable_attrs
|
33
|
+
attrs = super
|
34
|
+
attrs << Definition.new(:_self, :collection => true) if attrs.size == 0
|
35
|
+
attrs
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Representable::JSON
|
2
|
+
module Hash
|
3
|
+
include Representable::JSON
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
include Representable
|
8
|
+
extend ClassMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def values(options)
|
15
|
+
hash :_self, options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def create_representation_with(doc, options, format)
|
21
|
+
bin = representable_bindings_for(format).first
|
22
|
+
bin.serialize_for(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_properties_from(doc, options, format)
|
26
|
+
bin = representable_bindings_for(format).first
|
27
|
+
value = bin.deserialize_from(doc)
|
28
|
+
replace(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
# FIXME: refactor Definition so we can simply add options in #items to existing definition.
|
32
|
+
def representable_attrs
|
33
|
+
attrs = super
|
34
|
+
attrs << Definition.new(:_self, :hash => true) if attrs.size == 0
|
35
|
+
attrs
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/representable/xml.rb
CHANGED
@@ -5,9 +5,10 @@ require 'nokogiri'
|
|
5
5
|
module Representable
|
6
6
|
module XML
|
7
7
|
def self.binding_for_definition(definition)
|
8
|
-
return
|
9
|
-
return
|
10
|
-
|
8
|
+
return CollectionBinding.new(definition) if definition.array?
|
9
|
+
return HashBinding.new(definition) if definition.hash?
|
10
|
+
return AttributeBinding.new(definition) if definition.attribute
|
11
|
+
PropertyBinding.new(definition)
|
11
12
|
end
|
12
13
|
|
13
14
|
def self.included(base)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Representable::XML
|
2
|
+
module Collection
|
3
|
+
include Representable::XML
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
include Representable
|
8
|
+
extend ClassMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def items(options)
|
15
|
+
collection :_self, options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def create_representation_with(doc, options, format)
|
21
|
+
bin = representable_bindings_for(format).first
|
22
|
+
bin.serialize_for(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_properties_from(doc, options, format)
|
26
|
+
bin = representable_bindings_for(format).first
|
27
|
+
value = bin.deserialize_from(doc)
|
28
|
+
replace(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
# FIXME: refactor Definition so we can simply add options in #items to existing definition.
|
32
|
+
def representable_attrs
|
33
|
+
attrs = super
|
34
|
+
attrs << Definition.new(:_self, :collection => true) if attrs.size == 0
|
35
|
+
attrs
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Representable::XML
|
2
|
+
module Hash
|
3
|
+
include Representable::XML
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
include Representable
|
8
|
+
extend ClassMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def values(options)
|
15
|
+
hash :_self, options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def create_representation_with(doc, options, format)
|
21
|
+
bin = representable_bindings_for(format).first
|
22
|
+
bin.serialize_for(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_properties_from(doc, options, format)
|
26
|
+
bin = representable_bindings_for(format).first
|
27
|
+
value = bin.deserialize_from(doc)
|
28
|
+
replace(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
# FIXME: refactor Definition so we can simply add options in #items to existing definition.
|
32
|
+
def representable_attrs
|
33
|
+
attrs = super
|
34
|
+
attrs << Definition.new(:_self, :hash => true) if attrs.size == 0
|
35
|
+
attrs
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/test/definition_test.rb
CHANGED
@@ -13,9 +13,18 @@ class DefinitionTest < MiniTest::Spec
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
describe "#typed?" do
|
17
|
+
it "is false per default" do
|
18
|
+
assert ! @def.typed?
|
19
|
+
end
|
20
|
+
|
21
|
+
it "is true when :class is present" do
|
22
|
+
assert Representable::Definition.new(:songs, :class => Hash).typed?
|
23
|
+
end
|
24
|
+
|
25
|
+
it "is true when :extend is present, only" do
|
26
|
+
assert Representable::Definition.new(:songs, :extend => Hash).typed?
|
27
|
+
end
|
19
28
|
end
|
20
29
|
|
21
30
|
it "responds to #getter and returns string" do
|
@@ -26,10 +35,6 @@ class DefinitionTest < MiniTest::Spec
|
|
26
35
|
assert_equal "songs", @def.name
|
27
36
|
end
|
28
37
|
|
29
|
-
it "responds to #instance_variable_name" do
|
30
|
-
assert_equal :"@songs", @def.instance_variable_name
|
31
|
-
end
|
32
|
-
|
33
38
|
it "responds to #setter" do
|
34
39
|
assert_equal :"songs=", @def.setter
|
35
40
|
end
|
@@ -39,23 +44,6 @@ class DefinitionTest < MiniTest::Spec
|
|
39
44
|
end
|
40
45
|
end
|
41
46
|
|
42
|
-
|
43
|
-
describe "#apply" do
|
44
|
-
it "works with a single item" do
|
45
|
-
@d = Representable::Definition.new(:song)
|
46
|
-
assert_equal 2, @d.apply(1) { |v| v+1 }
|
47
|
-
end
|
48
|
-
|
49
|
-
it "works with collection" do
|
50
|
-
@d = Representable::Definition.new(:song, :collection => true)
|
51
|
-
assert_equal [2,3,4], @d.apply([1,2,3]) { |v| v+1 }
|
52
|
-
end
|
53
|
-
|
54
|
-
it "skips with collection and nil" do
|
55
|
-
@d = Representable::Definition.new(:song, :collection => true)
|
56
|
-
assert_equal nil, @d.apply(nil) { |v| v+1 }
|
57
|
-
end
|
58
|
-
end
|
59
47
|
|
60
48
|
describe ":collection => true" do
|
61
49
|
before do
|
@@ -96,4 +84,15 @@ class DefinitionTest < MiniTest::Spec
|
|
96
84
|
assert_equal "Atheist Peace", @def.default
|
97
85
|
end
|
98
86
|
end
|
87
|
+
|
88
|
+
describe ":hash => true" do
|
89
|
+
before do
|
90
|
+
@def = Representable::Definition.new(:songs, :hash => true)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "responds to #hash?" do
|
94
|
+
assert @def.hash?
|
95
|
+
assert ! Representable::Definition.new(:songs).hash?
|
96
|
+
end
|
97
|
+
end
|
99
98
|
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'representable/json'
|
3
|
+
|
4
|
+
class JSONBindingTest < MiniTest::Spec
|
5
|
+
module SongRepresenter
|
6
|
+
include Representable::JSON
|
7
|
+
property :name
|
8
|
+
end
|
9
|
+
|
10
|
+
class SongWithRepresenter < ::Song
|
11
|
+
include Representable
|
12
|
+
include SongRepresenter
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
describe "PropertyBinding" do
|
17
|
+
describe "with plain text" do
|
18
|
+
before do
|
19
|
+
@property = Representable::JSON::PropertyBinding.new(Representable::Definition.new(:song))
|
20
|
+
end
|
21
|
+
|
22
|
+
it "extracts with #read" do
|
23
|
+
assert_equal "Thinning the Herd", @property.read("song" => "Thinning the Herd")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "inserts with #write" do
|
27
|
+
doc = {}
|
28
|
+
assert_equal("Thinning the Herd", @property.write(doc,"Thinning the Herd"))
|
29
|
+
assert_equal({"song"=>"Thinning the Herd"}, doc)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "with an object" do
|
34
|
+
before do
|
35
|
+
@property = Representable::JSON::PropertyBinding.new(Representable::Definition.new(:song, :class => SongWithRepresenter))
|
36
|
+
@doc = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
it "extracts with #read" do
|
40
|
+
assert_equal SongWithRepresenter.new("Thinning the Herd"), @property.read("song" => {"name" => "Thinning the Herd"})
|
41
|
+
end
|
42
|
+
|
43
|
+
it "inserts with #write" do
|
44
|
+
assert_equal({"name"=>"Thinning the Herd"}, @property.write(@doc, SongWithRepresenter.new("Thinning the Herd")))
|
45
|
+
assert_equal({"song" => {"name"=>"Thinning the Herd"}}, @doc)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "with an object and :extend" do
|
50
|
+
before do
|
51
|
+
@property = Representable::JSON::PropertyBinding.new(Representable::Definition.new(:song, :class => Song, :extend => SongRepresenter))
|
52
|
+
@doc = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
it "extracts with #read" do
|
56
|
+
assert_equal Song.new("Thinning the Herd"), @property.read("song" => {"name" => "Thinning the Herd"})
|
57
|
+
end
|
58
|
+
|
59
|
+
it "inserts with #write" do
|
60
|
+
assert_equal({"name"=>"Thinning the Herd"}, @property.write(@doc, Song.new("Thinning the Herd")))
|
61
|
+
assert_equal({"song" => {"name"=>"Thinning the Herd"}}, @doc)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
describe "CollectionBinding" do
|
68
|
+
describe "with plain text items" do
|
69
|
+
before do
|
70
|
+
@property = Representable::JSON::CollectionBinding.new(Representable::Definition.new(:songs, :collection => true))
|
71
|
+
end
|
72
|
+
|
73
|
+
it "extracts with #read" do
|
74
|
+
assert_equal ["The Gargoyle", "Bronx"], @property.read("songs" => ["The Gargoyle", "Bronx"])
|
75
|
+
end
|
76
|
+
|
77
|
+
it "inserts with #write" do
|
78
|
+
doc = {}
|
79
|
+
assert_equal(["The Gargoyle", "Bronx"], @property.write(doc, ["The Gargoyle", "Bronx"]))
|
80
|
+
assert_equal({"songs"=>["The Gargoyle", "Bronx"]}, doc)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
describe "HashBinding" do
|
89
|
+
describe "with plain text items" do
|
90
|
+
before do
|
91
|
+
@property = Representable::JSON::HashBinding.new(Representable::Definition.new(:songs, :hash => true))
|
92
|
+
end
|
93
|
+
|
94
|
+
it "extracts with #read" do
|
95
|
+
assert_equal({"first" => "The Gargoyle", "second" => "Bronx"} , @property.read("songs" => {"first" => "The Gargoyle", "second" => "Bronx"}))
|
96
|
+
end
|
97
|
+
|
98
|
+
it "inserts with #write" do
|
99
|
+
doc = {}
|
100
|
+
assert_equal({"first" => "The Gargoyle", "second" => "Bronx"}, @property.write(doc, {"first" => "The Gargoyle", "second" => "Bronx"}))
|
101
|
+
assert_equal({"songs"=>{"first" => "The Gargoyle", "second" => "Bronx"}}, doc)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "with objects" do
|
106
|
+
before do
|
107
|
+
@property = Representable::JSON::HashBinding.new(Representable::Definition.new(:songs, :hash => true, :class => Song, :extend => SongRepresenter))
|
108
|
+
end
|
109
|
+
|
110
|
+
it "doesn't change the represented hash in #write" do
|
111
|
+
song = Song.new("Better Than That")
|
112
|
+
hash = {"first" => song}
|
113
|
+
@property.write({}, hash)
|
114
|
+
assert_equal({"first" => song}, hash)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
data/test/json_test.rb
CHANGED
@@ -133,7 +133,15 @@ module JsonTest
|
|
133
133
|
end
|
134
134
|
|
135
135
|
it "returns TextBinding" do
|
136
|
-
assert_kind_of Json::
|
136
|
+
assert_kind_of Json::PropertyBinding, Json.binding_for_definition(Def.new(:band))
|
137
|
+
end
|
138
|
+
|
139
|
+
it "returns HashBinding" do
|
140
|
+
assert_kind_of Json::HashBinding, Json.binding_for_definition(Def.new(:band, :hash => true))
|
141
|
+
end
|
142
|
+
|
143
|
+
it "returns CollectionBinding" do
|
144
|
+
assert_kind_of Json::CollectionBinding, Json.binding_for_definition(Def.new(:band, :collection => true))
|
137
145
|
end
|
138
146
|
end
|
139
147
|
|
@@ -155,7 +163,7 @@ module JsonTest
|
|
155
163
|
module AlbumRepresenter
|
156
164
|
include Representable::JSON
|
157
165
|
property :best_song, :class => Song, :extend => SongRepresenter
|
158
|
-
collection :songs, :class => Song, :extend => SongRepresenter
|
166
|
+
collection :songs, :class => Song, :extend => [SongRepresenter]
|
159
167
|
end
|
160
168
|
|
161
169
|
|
@@ -194,7 +202,8 @@ module JsonTest
|
|
194
202
|
end
|
195
203
|
end
|
196
204
|
end
|
197
|
-
|
205
|
+
|
206
|
+
|
198
207
|
class PropertyTest < MiniTest::Spec
|
199
208
|
describe "property :name" do
|
200
209
|
class Band
|
@@ -405,4 +414,119 @@ end
|
|
405
414
|
end
|
406
415
|
end
|
407
416
|
end
|
417
|
+
|
418
|
+
class HashTest < MiniTest::Spec
|
419
|
+
describe "hash :songs" do
|
420
|
+
before do
|
421
|
+
representer = Module.new do
|
422
|
+
include Representable::JSON
|
423
|
+
hash :songs
|
424
|
+
end
|
425
|
+
|
426
|
+
class SongList
|
427
|
+
attr_accessor :songs
|
428
|
+
end
|
429
|
+
|
430
|
+
@list = SongList.new.extend(representer)
|
431
|
+
end
|
432
|
+
|
433
|
+
it "renders with #to_json" do
|
434
|
+
@list.songs = {:one => "65", :two => "Emo Boy"}
|
435
|
+
assert_equal "{\"songs\":{\"one\":\"65\",\"two\":\"Emo Boy\"}}", @list.to_json
|
436
|
+
end
|
437
|
+
|
438
|
+
it "parses with #from_json" do
|
439
|
+
assert_equal({"one" => "65", "two" => ["Emo Boy"]}, @list.from_json("{\"songs\":{\"one\":\"65\",\"two\":[\"Emo Boy\"]}}").songs)
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
end
|
444
|
+
|
445
|
+
|
446
|
+
require 'representable/json/collection'
|
447
|
+
class CollectionRepresenterTest < MiniTest::Spec
|
448
|
+
module SongRepresenter
|
449
|
+
include Representable::JSON
|
450
|
+
property :name
|
451
|
+
end
|
452
|
+
|
453
|
+
describe "JSON::Collection" do
|
454
|
+
describe "with contained objects" do
|
455
|
+
before do
|
456
|
+
@songs_representer = Module.new do
|
457
|
+
include Representable::JSON::Collection
|
458
|
+
items :class => Song, :extend => SongRepresenter
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
it "renders objects with #to_json" do
|
463
|
+
assert_equal "[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]", [Song.new("Days Go By"), Song.new("Can't Take Them All")].extend(@songs_representer).to_json
|
464
|
+
end
|
465
|
+
|
466
|
+
it "returns objects array from #from_json" do
|
467
|
+
assert_equal [Song.new("Days Go By"), Song.new("Can't Take Them All")], [].extend(@songs_representer).from_json("[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]")
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
describe "with contained text" do
|
472
|
+
before do
|
473
|
+
@songs_representer = Module.new do
|
474
|
+
include Representable::JSON::Collection
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
it "renders contained items #to_json" do
|
479
|
+
assert_equal "[\"Days Go By\",\"Can't Take Them All\"]", ["Days Go By", "Can't Take Them All"].extend(@songs_representer).to_json
|
480
|
+
end
|
481
|
+
|
482
|
+
it "returns objects array from #from_json" do
|
483
|
+
assert_equal ["Days Go By", "Can't Take Them All"], [].extend(@songs_representer).from_json("[\"Days Go By\",\"Can't Take Them All\"]")
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
|
490
|
+
require 'representable/json/hash'
|
491
|
+
class HashRepresenterTest < MiniTest::Spec
|
492
|
+
module SongRepresenter
|
493
|
+
include Representable::JSON
|
494
|
+
property :name
|
495
|
+
end
|
496
|
+
|
497
|
+
describe "JSON::Hash" do
|
498
|
+
describe "with contained objects" do
|
499
|
+
before do
|
500
|
+
@songs_representer = Module.new do
|
501
|
+
include Representable::JSON::Hash
|
502
|
+
values :class => Song, :extend => SongRepresenter
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
it "renders objects with #to_json" do
|
507
|
+
assert_equal "{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json
|
508
|
+
end
|
509
|
+
|
510
|
+
it "returns objects array from #from_json" do
|
511
|
+
assert_equal({"one" => Song.new("Days Go By"), "two" => Song.new("Can't Take Them All")}, {}.extend(@songs_representer).from_json("{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}"))
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
describe "with contained text" do
|
516
|
+
before do
|
517
|
+
@songs_representer = Module.new do
|
518
|
+
include Representable::JSON::Collection
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
it "renders contained items #to_json" do
|
523
|
+
assert_equal "[\"Days Go By\",\"Can't Take Them All\"]", ["Days Go By", "Can't Take Them All"].extend(@songs_representer).to_json
|
524
|
+
end
|
525
|
+
|
526
|
+
it "returns objects array from #from_json" do
|
527
|
+
assert_equal ["Days Go By", "Can't Take Them All"], [].extend(@songs_representer).from_json("[\"Days Go By\",\"Can't Take Them All\"]")
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
531
|
+
end
|
408
532
|
end
|