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