representable 0.9.0 → 0.9.1
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/.travis.yml +3 -0
- data/CHANGES.textile +5 -0
- data/lib/representable.rb +34 -19
- data/lib/representable/bindings/json_bindings.rb +6 -9
- data/lib/representable/bindings/xml_bindings.rb +9 -9
- data/lib/representable/definition.rb +5 -5
- data/lib/representable/json.rb +33 -39
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +23 -33
- data/representable.gemspec +1 -1
- data/test/bindings_test.rb +3 -90
- data/test/definition_test.rb +89 -0
- data/test/json_test.rb +65 -12
- data/test/representable_test.rb +47 -1
- data/test/xml_test.rb +31 -7
- metadata +11 -8
data/.travis.yml
ADDED
data/CHANGES.textile
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
h2. 0.9.1
|
2
|
+
|
3
|
+
* Extracted common serialization into @Representable#create_representation_with@ and deserialization into @#update_properties_from@.
|
4
|
+
* Both serialization and deserialization now accept a block to make them skip elements while iterating the property definitions.
|
5
|
+
|
1
6
|
h2. 0.9.0
|
2
7
|
|
3
8
|
h3. Changes
|
data/lib/representable.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'hooks/inheritable_attribute'
|
2
|
-
|
3
2
|
require 'representable/definition'
|
4
3
|
require 'representable/nokogiri_extensions'
|
5
4
|
|
@@ -17,13 +16,41 @@ module Representable
|
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
19
|
+
# Reads values from +doc+ and sets properties accordingly.
|
20
|
+
def update_properties_from(doc)
|
21
|
+
self.class.representable_bindings.each do |ref|
|
22
|
+
next if block_given? and not yield ref # skip if block is false. # DISCUSS: will we keep that?
|
23
|
+
|
24
|
+
value = ref.read(doc)
|
25
|
+
send(ref.definition.setter, value)
|
26
|
+
end
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
# Compiles the document going through all properties.
|
32
|
+
def create_representation_with(doc)
|
33
|
+
self.class.representable_bindings.each do |ref|
|
34
|
+
next if block_given? and not yield ref # skip if block is false. # DISCUSS: will we keep that?
|
35
|
+
|
36
|
+
value = public_send(ref.definition.getter) # DISCUSS: eventually move back to Ref.
|
37
|
+
ref.write(doc, value) if value
|
38
|
+
end
|
39
|
+
doc
|
40
|
+
end
|
41
|
+
|
42
|
+
|
20
43
|
module ClassMethods # :nodoc:
|
21
44
|
module Declarations
|
22
45
|
def definition_class
|
23
46
|
Definition
|
24
47
|
end
|
25
48
|
|
26
|
-
|
49
|
+
# Returns bindings for all properties.
|
50
|
+
def representable_bindings
|
51
|
+
representable_attrs.map {|attr| binding_for_definition(attr) }
|
52
|
+
end
|
53
|
+
|
27
54
|
# Declares a reference to a certain xml element, whether an attribute, a node,
|
28
55
|
# or a typed collection of nodes. This method does not add a corresponding accessor
|
29
56
|
# to the object. For that behavior see the similar methods: .xml_reader and .xml_accessor.
|
@@ -183,9 +210,9 @@ module Representable
|
|
183
210
|
# [:to_xml] this proc is applied to the attributes value outputting the instance via #to_xml
|
184
211
|
#
|
185
212
|
def representable_property(*args) # TODO: make it accept 1-n props.
|
186
|
-
attr =
|
187
|
-
|
188
|
-
attr_writer(attr.
|
213
|
+
attr = add_representable_property(*args)
|
214
|
+
attr_reader(attr.getter)
|
215
|
+
attr_writer(attr.getter)
|
189
216
|
end
|
190
217
|
|
191
218
|
def representable_collection(name, options={})
|
@@ -194,23 +221,11 @@ module Representable
|
|
194
221
|
end
|
195
222
|
|
196
223
|
private
|
197
|
-
def
|
198
|
-
definition_class.new(
|
224
|
+
def add_representable_property(*args)
|
225
|
+
definition_class.new(*args).tap do |attr|
|
199
226
|
representable_attrs << attr
|
200
227
|
end
|
201
228
|
end
|
202
|
-
|
203
|
-
def representable_reader(*syms, &block)
|
204
|
-
representable_attr(*syms, &block).each do |attr|
|
205
|
-
add_reader(attr)
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
def add_reader(attr)
|
210
|
-
define_method(attr.accessor) do
|
211
|
-
instance_variable_get(attr.instance_variable_name)
|
212
|
-
end
|
213
|
-
end
|
214
229
|
end
|
215
230
|
|
216
231
|
module Accessors
|
@@ -2,13 +2,12 @@ module Representable
|
|
2
2
|
module JSON
|
3
3
|
class Binding
|
4
4
|
attr_reader :definition
|
5
|
-
#delegate :required?, :array?, :accessor, :from, :to => :definition
|
6
5
|
|
7
6
|
def initialize(definition)
|
8
7
|
@definition = definition
|
9
8
|
end
|
10
9
|
|
11
|
-
def
|
10
|
+
def read(hash)
|
12
11
|
value_from_hash(hash) or default
|
13
12
|
end
|
14
13
|
|
@@ -29,7 +28,7 @@ module Representable
|
|
29
28
|
|
30
29
|
# Represents plain key-value.
|
31
30
|
class TextBinding < Binding
|
32
|
-
def
|
31
|
+
def write(hash, value)
|
33
32
|
hash[definition.from] = value
|
34
33
|
end
|
35
34
|
|
@@ -43,9 +42,7 @@ module Representable
|
|
43
42
|
|
44
43
|
# Represents a tag with object binding.
|
45
44
|
class ObjectBinding < Binding
|
46
|
-
|
47
|
-
|
48
|
-
def update_json(hash, value)
|
45
|
+
def write(hash, value)
|
49
46
|
if definition.array?
|
50
47
|
hash.merge! ({definition.from => value.collect {|v| v.to_hash(:wrap => false)}}) # hier name=> wech.
|
51
48
|
else
|
@@ -58,9 +55,9 @@ module Representable
|
|
58
55
|
[]
|
59
56
|
end
|
60
57
|
|
61
|
-
def value_from_hash(
|
62
|
-
collect_for(
|
63
|
-
definition.sought_type.
|
58
|
+
def value_from_hash(hash)
|
59
|
+
collect_for(hash) do |node|
|
60
|
+
definition.sought_type.from_hash(node) # call #from_hash as it's already deserialized.
|
64
61
|
end
|
65
62
|
end
|
66
63
|
|
@@ -7,7 +7,7 @@ module Representable
|
|
7
7
|
@definition = definition
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def read(xml)
|
11
11
|
xml = Nokogiri::XML::Node.from(xml) or return default
|
12
12
|
|
13
13
|
value_from_node(xml) or default
|
@@ -43,7 +43,7 @@ module Representable
|
|
43
43
|
|
44
44
|
# Represents a tag attribute.
|
45
45
|
class AttributeBinding < Binding
|
46
|
-
def
|
46
|
+
def write(xml, values)
|
47
47
|
wrap(xml).tap do |xml|
|
48
48
|
xml[definition.from] = values.to_s
|
49
49
|
end
|
@@ -60,7 +60,7 @@ module Representable
|
|
60
60
|
class TextBinding < Binding
|
61
61
|
# Updates the text in the given _xml_ block to
|
62
62
|
# the _value_ provided.
|
63
|
-
def
|
63
|
+
def write(xml, value)
|
64
64
|
wrap(xml).tap do |xml|
|
65
65
|
if definition.content?
|
66
66
|
add(xml, value)
|
@@ -92,12 +92,12 @@ module Representable
|
|
92
92
|
# Represents a tag with object binding.
|
93
93
|
class ObjectBinding < Binding
|
94
94
|
# Adds the ref's markup to +xml+.
|
95
|
-
def
|
95
|
+
def write(xml, value)
|
96
96
|
wrap(xml).tap do |xml|
|
97
97
|
if definition.array?
|
98
|
-
|
98
|
+
write_collection(xml, value)
|
99
99
|
else
|
100
|
-
|
100
|
+
write_entity(xml, value)
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
@@ -122,13 +122,13 @@ module Representable
|
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
125
|
-
def
|
125
|
+
def write_collection(xml, collection)
|
126
126
|
collection.each do |item|
|
127
|
-
|
127
|
+
write_entity(xml, item)
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
-
def
|
131
|
+
def write_entity(xml, entity)
|
132
132
|
xml.add_child(serialize(entity))
|
133
133
|
end
|
134
134
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module Representable
|
2
2
|
class Definition # :nodoc:
|
3
|
-
attr_reader :name, :sought_type, :wrapper, :
|
4
|
-
|
3
|
+
attr_reader :name, :sought_type, :wrapper, :from
|
4
|
+
alias_method :getter, :name
|
5
5
|
|
6
6
|
def initialize(sym, opts={})
|
7
|
-
@
|
7
|
+
@name = sym.to_s
|
8
8
|
|
9
9
|
@array = true if opts[:as].is_a?(Array) # DISCUSS: move to ArrayDefinition.
|
10
10
|
@from = (opts[:from] || name).to_s
|
@@ -25,11 +25,11 @@ module Representable
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def instance_variable_name
|
28
|
-
:"@#{
|
28
|
+
:"@#{name}"
|
29
29
|
end
|
30
30
|
|
31
31
|
def setter
|
32
|
-
:"#{
|
32
|
+
:"#{name}="
|
33
33
|
end
|
34
34
|
|
35
35
|
def typed?
|
data/lib/representable/json.rb
CHANGED
@@ -6,39 +6,34 @@ module Representable
|
|
6
6
|
BINDING_FOR_TYPE = { # TODO: refactor #representable_accessor for better extendability.
|
7
7
|
:text => TextBinding,
|
8
8
|
}
|
9
|
-
def self.binding_for_definition(definition)
|
10
|
-
(BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
|
11
|
-
end
|
12
9
|
|
13
10
|
def self.included(base)
|
14
11
|
base.class_eval do
|
15
12
|
include Representable
|
16
|
-
|
13
|
+
extend ClassMethods
|
17
14
|
end
|
18
|
-
base.extend ClassMethods # DISCUSS: do that dynamically?
|
19
15
|
end
|
20
16
|
|
17
|
+
|
21
18
|
module ClassMethods
|
19
|
+
def binding_for_definition(definition)
|
20
|
+
(BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
|
21
|
+
end
|
22
|
+
|
22
23
|
# Creates a new Ruby object from XML using mapping information declared in the class.
|
23
24
|
#
|
24
25
|
# Example:
|
25
26
|
# book = Book.from_xml("<book><name>Beyond Java</name></book>")
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
create_from_json.tap do |
|
35
|
-
|
36
|
-
|
37
|
-
refs.each do |ref|
|
38
|
-
value = ref.value_in(data)
|
39
|
-
|
40
|
-
inst.send(ref.definition.setter, value)
|
41
|
-
end
|
27
|
+
# DISCUSS: assumes shitty wrapping like :article => {:name => ...}
|
28
|
+
def from_json(data, options={}, &block)
|
29
|
+
create_from_json.tap do |object|
|
30
|
+
object.from_json(data, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def from_hash(data)
|
35
|
+
create_from_json.tap do |object|
|
36
|
+
object.update_properties_from(data)
|
42
37
|
end
|
43
38
|
end
|
44
39
|
|
@@ -48,25 +43,24 @@ module Representable
|
|
48
43
|
end
|
49
44
|
end
|
50
45
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
refs.each do |ref|
|
57
|
-
value = public_send(ref.definition.accessor) # DISCUSS: eventually move back to Ref.
|
58
|
-
ref.update_json(attrs, value) if value
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# DISCUSS: where to wrap?
|
63
|
-
options[:wrap] == false ? hash : {self.class.representation_name => hash}
|
64
|
-
end
|
46
|
+
def from_json(data, options={}, &block)
|
47
|
+
data = ::JSON[data]
|
48
|
+
data = data[self.class.representation_name.to_s] unless options[:wrap] == false
|
49
|
+
data ||= {} # FIXME: should we fail here? generate a warning?
|
65
50
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
51
|
+
update_properties_from(data, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_hash(options={})
|
55
|
+
hash = create_representation_with({})
|
56
|
+
|
57
|
+
# DISCUSS: where to wrap?
|
58
|
+
options[:wrap] == false ? hash : {self.class.representation_name => hash}
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns a JSON string representing this object.
|
62
|
+
def to_json(options={})
|
63
|
+
to_hash(options).to_json
|
70
64
|
end
|
71
65
|
end
|
72
66
|
end
|
data/lib/representable/xml.rb
CHANGED
@@ -8,42 +8,41 @@ module Representable
|
|
8
8
|
:text => TextBinding,
|
9
9
|
}
|
10
10
|
|
11
|
-
def self.binding_for_definition(definition)
|
12
|
-
(BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
|
13
|
-
end
|
14
|
-
|
15
11
|
def self.included(base)
|
16
12
|
base.class_eval do
|
17
13
|
include Representable
|
18
|
-
include InstanceMethods
|
19
14
|
extend ClassMethods
|
15
|
+
alias_method :from_xml, :update_properties_from
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
19
|
+
|
23
20
|
class Definition < Representable::Definition
|
24
21
|
# FIXME: extract xml-specific from Definition.
|
25
22
|
end
|
26
23
|
|
27
|
-
module ClassMethods
|
28
|
-
def definition_class
|
29
|
-
Definition
|
30
|
-
end
|
31
24
|
|
25
|
+
module ClassMethods
|
26
|
+
def definition_class
|
27
|
+
Definition
|
28
|
+
end
|
29
|
+
|
30
|
+
def binding_for_definition(definition)
|
31
|
+
(BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
|
32
|
+
end
|
33
|
+
|
32
34
|
# Creates a new Ruby object from XML using mapping information declared in the class.
|
33
35
|
#
|
36
|
+
# Accepts a block yielding the currently iterated Definition. If the block returns false
|
37
|
+
# the property is skipped.
|
38
|
+
#
|
34
39
|
# Example:
|
35
|
-
#
|
40
|
+
# band.from_xml("<band><name>Nofx</name></band>")
|
36
41
|
def from_xml(data, *args)
|
37
42
|
xml = Nokogiri::XML::Node.from(data)
|
38
43
|
|
39
|
-
create_from_xml(*args).tap do |
|
40
|
-
|
41
|
-
|
42
|
-
refs.each do |ref|
|
43
|
-
value = ref.value_in(xml)
|
44
|
-
|
45
|
-
inst.send(ref.definition.setter, value)
|
46
|
-
end
|
44
|
+
create_from_xml(*args).tap do |object|
|
45
|
+
object.update_properties_from(xml)
|
47
46
|
end
|
48
47
|
end
|
49
48
|
|
@@ -53,20 +52,11 @@ module Representable
|
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
Nokogiri::XML::Node.new(params[:name].to_s, Nokogiri::XML::Document.new).tap do |root|
|
62
|
-
refs = self.class.representable_attrs.map {|attr| XML.binding_for_definition(attr) }
|
63
|
-
|
64
|
-
refs.each do |ref|
|
65
|
-
value = public_send(ref.definition.accessor) # DISCUSS: eventually move back to Ref.
|
66
|
-
ref.update_xml(root, value) if value
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
55
|
+
# Returns a Nokogiri::XML object representing this object.
|
56
|
+
def to_xml(params={})
|
57
|
+
root_tag = params[:name] || self.class.representation_name
|
58
|
+
|
59
|
+
create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new))
|
70
60
|
end
|
71
|
-
end
|
61
|
+
end
|
72
62
|
end
|
data/representable.gemspec
CHANGED
data/test/bindings_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class BindingsTest < MiniTest::Spec
|
4
4
|
describe "ObjectRef with []" do
|
5
5
|
before do
|
6
6
|
@ref = Representable::XML::ObjectBinding.new(Representable::Definition.new(:songs, :as => [Hash]))
|
@@ -12,7 +12,7 @@ class ReferenceTest < MiniTest::Spec
|
|
12
12
|
end
|
13
13
|
|
14
14
|
|
15
|
-
describe "TextRef#
|
15
|
+
describe "TextRef#read" do
|
16
16
|
def parse_xml(xml); Nokogiri::XML::Node.from(xml); end
|
17
17
|
|
18
18
|
before do
|
@@ -20,95 +20,8 @@ class ReferenceTest < MiniTest::Spec
|
|
20
20
|
end
|
21
21
|
|
22
22
|
it "returns found value" do
|
23
|
-
assert_equal "Unkoil", @ref.
|
23
|
+
assert_equal "Unkoil", @ref.read(parse_xml("<a><song>Unkoil</song></a>"))
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
class DefinitionTest < MiniTest::Spec
|
29
|
-
describe "generic API" do
|
30
|
-
before do
|
31
|
-
@def = Representable::Definition.new(:songs)
|
32
|
-
end
|
33
|
-
|
34
|
-
it "responds to #typed?" do
|
35
|
-
assert ! @def.typed?
|
36
|
-
assert Representable::Definition.new(:songs, :as => Hash).typed?
|
37
|
-
assert Representable::Definition.new(:songs, :as => [Hash]).typed?
|
38
|
-
end
|
39
|
-
|
40
|
-
it "responds to #accessor" do
|
41
|
-
assert_equal "songs", @def.accessor
|
42
|
-
end
|
43
|
-
|
44
|
-
it "responds to #name" do
|
45
|
-
assert_equal "songs", @def.name
|
46
|
-
end
|
47
|
-
|
48
|
-
it "responds to #instance_variable_name" do
|
49
|
-
assert_equal :"@songs", @def.instance_variable_name
|
50
|
-
end
|
51
|
-
|
52
|
-
it "responds to #setter" do
|
53
|
-
assert_equal :"songs=", @def.setter
|
54
|
-
end
|
55
|
-
|
56
|
-
it "responds to #sought_type" do
|
57
|
-
assert_equal :text, @def.sought_type
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
|
62
|
-
describe "#apply" do
|
63
|
-
it "works with a single item" do
|
64
|
-
@d = Representable::Definition.new(:song)
|
65
|
-
assert_equal 2, @d.apply(1) { |v| v+1 }
|
66
|
-
end
|
67
|
-
|
68
|
-
it "works with collection" do
|
69
|
-
@d = Representable::Definition.new(:song, :as => [])
|
70
|
-
assert_equal [2,3,4], @d.apply([1,2,3]) { |v| v+1 }
|
71
|
-
end
|
72
|
-
|
73
|
-
it "skips with collection and nil" do
|
74
|
-
@d = Representable::Definition.new(:song, :as => [])
|
75
|
-
assert_equal nil, @d.apply(nil) { |v| v+1 }
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
describe ":as => []" do
|
80
|
-
before do
|
81
|
-
@def = Representable::Definition.new(:songs, :as => [], :tag => :song)
|
82
|
-
end
|
83
|
-
|
84
|
-
it "responds to #array?" do
|
85
|
-
assert @def.array?
|
86
|
-
end
|
87
|
-
|
88
|
-
it "responds to #sought_type" do
|
89
|
-
assert_equal :text, @def.sought_type
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
|
94
|
-
describe ":as => [Item]" do
|
95
|
-
before do
|
96
|
-
@def = Representable::Definition.new(:songs, :as => [Hash])
|
97
|
-
end
|
98
|
-
|
99
|
-
it "responds to #sought_type" do
|
100
|
-
assert_equal Hash, @def.sought_type
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
|
105
|
-
describe ":as => Item" do
|
106
|
-
before do
|
107
|
-
@def = Representable::Definition.new(:songs, :as => Hash)
|
108
|
-
end
|
109
|
-
|
110
|
-
it "responds to #sought_type" do
|
111
|
-
assert_equal Hash, @def.sought_type
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class DefinitionTest < MiniTest::Spec
|
4
|
+
describe "generic API" do
|
5
|
+
before do
|
6
|
+
@def = Representable::Definition.new(:songs)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "responds to #typed?" do
|
10
|
+
assert ! @def.typed?
|
11
|
+
assert Representable::Definition.new(:songs, :as => Hash).typed?
|
12
|
+
assert Representable::Definition.new(:songs, :as => [Hash]).typed?
|
13
|
+
end
|
14
|
+
|
15
|
+
it "responds to #getter and returns string" do
|
16
|
+
assert_equal "songs", @def.getter
|
17
|
+
end
|
18
|
+
|
19
|
+
it "responds to #name" do
|
20
|
+
assert_equal "songs", @def.name
|
21
|
+
end
|
22
|
+
|
23
|
+
it "responds to #instance_variable_name" do
|
24
|
+
assert_equal :"@songs", @def.instance_variable_name
|
25
|
+
end
|
26
|
+
|
27
|
+
it "responds to #setter" do
|
28
|
+
assert_equal :"songs=", @def.setter
|
29
|
+
end
|
30
|
+
|
31
|
+
it "responds to #sought_type" do
|
32
|
+
assert_equal :text, @def.sought_type
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
describe "#apply" do
|
38
|
+
it "works with a single item" do
|
39
|
+
@d = Representable::Definition.new(:song)
|
40
|
+
assert_equal 2, @d.apply(1) { |v| v+1 }
|
41
|
+
end
|
42
|
+
|
43
|
+
it "works with collection" do
|
44
|
+
@d = Representable::Definition.new(:song, :as => [])
|
45
|
+
assert_equal [2,3,4], @d.apply([1,2,3]) { |v| v+1 }
|
46
|
+
end
|
47
|
+
|
48
|
+
it "skips with collection and nil" do
|
49
|
+
@d = Representable::Definition.new(:song, :as => [])
|
50
|
+
assert_equal nil, @d.apply(nil) { |v| v+1 }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe ":as => []" do
|
55
|
+
before do
|
56
|
+
@def = Representable::Definition.new(:songs, :as => [], :tag => :song)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "responds to #array?" do
|
60
|
+
assert @def.array?
|
61
|
+
end
|
62
|
+
|
63
|
+
it "responds to #sought_type" do
|
64
|
+
assert_equal :text, @def.sought_type
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
describe ":as => [Item]" do
|
70
|
+
before do
|
71
|
+
@def = Representable::Definition.new(:songs, :as => [Hash])
|
72
|
+
end
|
73
|
+
|
74
|
+
it "responds to #sought_type" do
|
75
|
+
assert_equal Hash, @def.sought_type
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
describe ":as => Item" do
|
81
|
+
before do
|
82
|
+
@def = Representable::Definition.new(:songs, :as => Hash)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "responds to #sought_type" do
|
86
|
+
assert_equal Hash, @def.sought_type
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/test/json_test.rb
CHANGED
@@ -7,21 +7,74 @@ module JsonTest
|
|
7
7
|
Def = Representable::Definition
|
8
8
|
|
9
9
|
describe "JSON module" do
|
10
|
+
before do
|
11
|
+
@Band = Class.new(Band) do
|
12
|
+
self.representation_name= :band
|
13
|
+
representable_property :label
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Band
|
18
|
+
include Representable::JSON
|
19
|
+
representable_property :name
|
20
|
+
end
|
21
|
+
|
10
22
|
describe "#binding_for_definition" do
|
11
23
|
it "returns ObjectBinding" do
|
12
|
-
assert_kind_of Json::ObjectBinding,
|
24
|
+
assert_kind_of Json::ObjectBinding, Band.binding_for_definition(Def.new(:band, :as => Hash))
|
13
25
|
end
|
14
26
|
|
15
27
|
it "returns TextBinding" do
|
16
|
-
assert_kind_of Json::TextBinding,
|
28
|
+
assert_kind_of Json::TextBinding, Band.binding_for_definition(Def.new(:band))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#representable_bindings" do
|
33
|
+
it "returns bindings for each property" do
|
34
|
+
assert_equal 1, Band.representable_bindings.size
|
35
|
+
assert_equal "name", Band.representable_bindings.first.definition.name
|
17
36
|
end
|
18
37
|
end
|
19
38
|
|
20
39
|
describe "#from_json" do
|
21
|
-
|
22
|
-
|
40
|
+
before do
|
41
|
+
@band = @Band.new
|
42
|
+
end
|
43
|
+
|
44
|
+
it "accepts json string" do
|
45
|
+
@band.from_json({band: {name: "Nofx", label: "NOFX"}}.to_json)
|
46
|
+
assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
|
23
47
|
end
|
24
48
|
|
49
|
+
it "forwards block to #update_properties_from" do
|
50
|
+
@band.from_json({band: {name: "Nofx", label: "NOFX"}}.to_json) do |binding|
|
51
|
+
binding.definition.name == "name"
|
52
|
+
end
|
53
|
+
|
54
|
+
assert_equal ["Nofx", nil], [@band.name, @band.label]
|
55
|
+
end
|
56
|
+
|
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
|
61
|
+
end
|
62
|
+
|
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
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
describe ".from_json" do
|
72
|
+
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
|
74
|
+
assert_equal ["Nofx", nil], [band.name, band.label]
|
75
|
+
end
|
76
|
+
|
77
|
+
# TODO: move following tests to #from_json test.
|
25
78
|
it "raises error with emtpy string" do
|
26
79
|
assert_raises JSON::ParserError do
|
27
80
|
Band.from_json("")
|
@@ -33,10 +86,15 @@ module JsonTest
|
|
33
86
|
end
|
34
87
|
|
35
88
|
it "generates warning with inappropriate hash in debugging mode" do
|
36
|
-
|
37
89
|
end
|
38
90
|
end
|
39
91
|
|
92
|
+
describe ".from_hash" do
|
93
|
+
it "accepts unwrapped hash with string keys" do
|
94
|
+
band = Band.from_hash("name" => "Bombshell Rocks")
|
95
|
+
assert_equal "Bombshell Rocks", band.name
|
96
|
+
end
|
97
|
+
end
|
40
98
|
end
|
41
99
|
end
|
42
100
|
|
@@ -52,11 +110,6 @@ module JsonTest
|
|
52
110
|
assert_equal "Bombshell Rocks", band.name
|
53
111
|
end
|
54
112
|
|
55
|
-
it "#from_json accepts hash, too" do
|
56
|
-
band = Band.from_json({"band" => {"name" => "Bombshell Rocks"}})
|
57
|
-
assert_equal "Bombshell Rocks", band.name
|
58
|
-
end
|
59
|
-
|
60
113
|
it "#to_json serializes correctly" do
|
61
114
|
band = Band.new
|
62
115
|
band.name = "Cigar"
|
@@ -77,8 +130,8 @@ module JsonTest
|
|
77
130
|
end
|
78
131
|
|
79
132
|
it "#from_json creates one Item instance" do
|
80
|
-
album = Album.from_json({:
|
81
|
-
assert_equal "
|
133
|
+
album = Album.from_json('{"album":{"label":{"name":"Fat Wreck"}}}')
|
134
|
+
assert_equal "Fat Wreck", album.label.name
|
82
135
|
end
|
83
136
|
|
84
137
|
it "#to_json serializes" do
|
data/test/representable_test.rb
CHANGED
@@ -25,7 +25,6 @@ class RepresentableTest < MiniTest::Spec
|
|
25
25
|
end
|
26
26
|
|
27
27
|
|
28
|
-
|
29
28
|
describe "#representable_property" do
|
30
29
|
it "creates accessors for the attribute" do
|
31
30
|
@band = PunkBand.new
|
@@ -87,6 +86,53 @@ class RepresentableTest < MiniTest::Spec
|
|
87
86
|
it "returns Definition class" do
|
88
87
|
assert_equal Representable::Definition, Band.definition_class
|
89
88
|
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
require 'representable/json' # DISCUSS: i don't like the JSON requirement here, what about some generic test module?
|
93
|
+
class PopBand
|
94
|
+
include Representable::JSON
|
95
|
+
representable_property :name
|
96
|
+
representable_property :groupies
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#update_properties_from" do
|
100
|
+
it "copies values from document to object" do
|
101
|
+
band = PopBand.new
|
102
|
+
band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2})
|
103
|
+
assert_equal "No One's Choice", band.name
|
104
|
+
assert_equal 2, band.groupies
|
105
|
+
end
|
106
|
+
|
107
|
+
it "skips elements when block returns false" do
|
108
|
+
band = PopBand.new
|
109
|
+
band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}) do |binding|
|
110
|
+
binding.definition.name == "name"
|
111
|
+
end
|
112
|
+
assert_equal "No One's Choice", band.name
|
113
|
+
assert_equal nil, band.groupies
|
114
|
+
end
|
115
|
+
|
116
|
+
it "always returns self" do
|
117
|
+
band = PopBand.new
|
118
|
+
assert_equal band, band.update_properties_from({"name"=>"Nofx"})
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "#create_representation_with" do
|
123
|
+
before do
|
124
|
+
@band = PopBand.new
|
125
|
+
@band.name = "No One's Choice"
|
126
|
+
@band.groupies = 2
|
127
|
+
end
|
128
|
+
|
129
|
+
it "compiles document from properties in object" do
|
130
|
+
assert_equal({"name"=>"No One's Choice", "groupies"=>2}, @band.send(:create_representation_with, {}))
|
131
|
+
end
|
90
132
|
|
133
|
+
it "skips elements when block returns false" do
|
134
|
+
assert_equal({"name"=>"No One's Choice"}, @band.send(:create_representation_with, {}) do |binding| binding.definition.name == "name" end)
|
135
|
+
end
|
91
136
|
end
|
137
|
+
|
92
138
|
end
|
data/test/xml_test.rb
CHANGED
@@ -22,6 +22,15 @@ class XmlTest < MiniTest::Spec
|
|
22
22
|
Def = Representable::Definition
|
23
23
|
|
24
24
|
describe "Xml module" do
|
25
|
+
before do
|
26
|
+
@Band = Class.new do
|
27
|
+
include Representable::XML
|
28
|
+
self.representation_name= :band
|
29
|
+
representable_property :name
|
30
|
+
representable_property :label
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
25
34
|
class Band
|
26
35
|
include Representable::XML
|
27
36
|
representable_property :href, :from => "@href"
|
@@ -34,19 +43,34 @@ class XmlTest < MiniTest::Spec
|
|
34
43
|
|
35
44
|
describe "#binding_for_definition" do
|
36
45
|
it "returns AttributeBinding" do
|
37
|
-
assert_kind_of XML::AttributeBinding,
|
46
|
+
assert_kind_of XML::AttributeBinding, Band.binding_for_definition(Def.new(:band, :from => "@band"))
|
38
47
|
end
|
39
48
|
|
40
49
|
it "returns ObjectBinding" do
|
41
|
-
assert_kind_of XML::ObjectBinding,
|
50
|
+
assert_kind_of XML::ObjectBinding, Band.binding_for_definition(Def.new(:band, :as => Hash))
|
42
51
|
end
|
43
52
|
|
44
|
-
#it "returns NamespaceBinding" do
|
45
|
-
# assert_kind_of Xml::AttributeBinding, Xml.binding_for_definition(Def.new(:band, :from => "@band"))
|
46
|
-
#end
|
47
|
-
|
48
53
|
it "returns TextBinding" do
|
49
|
-
assert_kind_of XML::TextBinding,
|
54
|
+
assert_kind_of XML::TextBinding, Band.binding_for_definition(Def.new(:band, :from => :content))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#from_xml" do
|
59
|
+
before do
|
60
|
+
@band = @Band.new
|
61
|
+
end
|
62
|
+
|
63
|
+
it "accepts xml string" do
|
64
|
+
@band.from_xml(%{<band><name>Nofx</name><label>NOFX</label></band>})
|
65
|
+
assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
|
66
|
+
end
|
67
|
+
|
68
|
+
it "forwards block to #update_properties_from" do
|
69
|
+
@band.from_xml(%{<band><name>Nofx</name><label>NOFX</label></band>}) do |binding|
|
70
|
+
binding.definition.name == "name"
|
71
|
+
end
|
72
|
+
|
73
|
+
assert_equal ["Nofx", nil], [@band.name, @band.label]
|
50
74
|
end
|
51
75
|
end
|
52
76
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 9
|
8
|
-
-
|
9
|
-
version: 0.9.
|
8
|
+
- 1
|
9
|
+
version: 0.9.1
|
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-
|
17
|
+
date: 2011-11-22 00:00:00 -02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -70,7 +70,7 @@ dependencies:
|
|
70
70
|
type: :development
|
71
71
|
version_requirements: *id004
|
72
72
|
- !ruby/object:Gem::Dependency
|
73
|
-
name:
|
73
|
+
name: test_xml
|
74
74
|
prerelease: false
|
75
75
|
requirement: &id005 !ruby/object:Gem::Requirement
|
76
76
|
none: false
|
@@ -83,16 +83,17 @@ dependencies:
|
|
83
83
|
type: :development
|
84
84
|
version_requirements: *id005
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
|
-
name:
|
86
|
+
name: minitest
|
87
87
|
prerelease: false
|
88
88
|
requirement: &id006 !ruby/object:Gem::Requirement
|
89
89
|
none: false
|
90
90
|
requirements:
|
91
|
-
- -
|
91
|
+
- - ~>
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
segments:
|
94
|
-
-
|
95
|
-
|
94
|
+
- 2
|
95
|
+
- 8
|
96
|
+
version: "2.8"
|
96
97
|
type: :development
|
97
98
|
version_requirements: *id006
|
98
99
|
description: Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties and compositions.
|
@@ -108,6 +109,7 @@ files:
|
|
108
109
|
- .gitignore
|
109
110
|
- .gitmodules
|
110
111
|
- .rspec
|
112
|
+
- .travis.yml
|
111
113
|
- CHANGES.textile
|
112
114
|
- Gemfile
|
113
115
|
- LICENSE
|
@@ -123,6 +125,7 @@ files:
|
|
123
125
|
- lib/representable/xml.rb
|
124
126
|
- representable.gemspec
|
125
127
|
- test/bindings_test.rb
|
128
|
+
- test/definition_test.rb
|
126
129
|
- test/json_test.rb
|
127
130
|
- test/representable_test.rb
|
128
131
|
- test/test_helper.rb
|