representable 1.2.7 → 1.2.8
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 +5 -0
- data/lib/representable.rb +26 -17
- data/lib/representable/binding.rb +8 -8
- data/lib/representable/bindings/hash_bindings.rb +11 -5
- data/lib/representable/bindings/xml_bindings.rb +16 -8
- data/lib/representable/bindings/yaml_bindings.rb +12 -4
- data/lib/representable/definition.rb +1 -1
- data/lib/representable/feature/readable_writeable.rb +30 -0
- data/lib/representable/hash.rb +4 -12
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +2 -12
- data/lib/representable/yaml.rb +2 -12
- data/test/Gemfile +3 -0
- data/test/coercion_test.rb +25 -18
- data/test/definition_test.rb +50 -0
- data/test/example.rb +159 -0
- data/test/hash_test.rb +19 -0
- data/test/json_test.rb +8 -8
- data/test/representable_test.rb +55 -20
- data/test/xml_test.rb +6 -6
- metadata +5 -2
data/CHANGES.textile
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
h2. 1.2.8
|
2
|
+
|
3
|
+
* Reverting all the bullshit from 1.2.7 making it even better. @Binding@s now wrap their @Definition@ instance adopting its API. Moved the binding_for_definition mechanics to the respecting @Binding@ subclass.
|
4
|
+
* Added :readable and :writeable to #property: while @:readable => true@ renders the property into the document @:writeable => true@ allows updating the property's value when consuming a representation. Both default to @true@.
|
5
|
+
|
1
6
|
h2. 1.2.7
|
2
7
|
|
3
8
|
* Moving @Format.binding_for_definition@ to @Format#{format}_binding_for_definition@, making it an instance method in its own "namespace". This allows mixing in multiple representer engines into a user's representer module.
|
data/lib/representable.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'representable/deprecations'
|
2
2
|
require 'representable/definition'
|
3
|
+
require 'representable/feature/readable_writeable'
|
3
4
|
|
4
5
|
# Representable can be used in two ways.
|
5
6
|
#
|
@@ -35,15 +36,14 @@ module Representable
|
|
35
36
|
extend ClassMethods::Declarations
|
36
37
|
|
37
38
|
include Deprecations
|
39
|
+
include Feature::ReadableWriteable
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
43
|
# Reads values from +doc+ and sets properties accordingly.
|
42
44
|
def update_properties_from(doc, options, format)
|
43
45
|
representable_bindings_for(format).each do |bin|
|
44
|
-
|
45
|
-
|
46
|
-
uncompile_fragment(bin, doc)
|
46
|
+
deserialize_property(bin, doc, options)
|
47
47
|
end
|
48
48
|
self
|
49
49
|
end
|
@@ -52,13 +52,21 @@ private
|
|
52
52
|
# Compiles the document going through all properties.
|
53
53
|
def create_representation_with(doc, options, format)
|
54
54
|
representable_bindings_for(format).each do |bin|
|
55
|
-
|
56
|
-
|
57
|
-
compile_fragment(bin, doc)
|
55
|
+
serialize_property(bin, doc, options)
|
58
56
|
end
|
59
57
|
doc
|
60
58
|
end
|
61
|
-
|
59
|
+
|
60
|
+
def serialize_property(binding, doc, options)
|
61
|
+
return if skip_property?(binding, options)
|
62
|
+
compile_fragment(binding, doc)
|
63
|
+
end
|
64
|
+
|
65
|
+
def deserialize_property(binding, doc, options)
|
66
|
+
return if skip_property?(binding, options)
|
67
|
+
uncompile_fragment(binding, doc)
|
68
|
+
end
|
69
|
+
|
62
70
|
# Checks and returns if the property should be included.
|
63
71
|
def skip_property?(binding, options)
|
64
72
|
return true if skip_excluded_property?(binding, options) # no need for further evaluation when :exclude'ed
|
@@ -68,19 +76,19 @@ private
|
|
68
76
|
|
69
77
|
def skip_excluded_property?(binding, options)
|
70
78
|
return unless props = options[:exclude] || options[:include]
|
71
|
-
res = props.include?(binding.
|
79
|
+
res = props.include?(binding.name.to_sym)
|
72
80
|
options[:include] ? !res : res
|
73
81
|
end
|
74
82
|
|
75
83
|
def skip_conditional_property?(binding)
|
76
|
-
return unless condition = binding.
|
84
|
+
return unless condition = binding.options[:if]
|
77
85
|
not instance_exec(&condition)
|
78
86
|
end
|
79
87
|
|
80
88
|
# Retrieve value and write fragment to the doc.
|
81
89
|
def compile_fragment(bin, doc)
|
82
|
-
value = send(bin.
|
83
|
-
value = bin.
|
90
|
+
value = send(bin.getter)
|
91
|
+
value = bin.default_for(value)
|
84
92
|
|
85
93
|
write_fragment_for(bin, value, doc)
|
86
94
|
end
|
@@ -90,15 +98,15 @@ private
|
|
90
98
|
value = read_fragment_for(bin, doc)
|
91
99
|
|
92
100
|
if value == Binding::FragmentNotFound
|
93
|
-
return unless bin.
|
94
|
-
value = bin.
|
101
|
+
return unless bin.has_default?
|
102
|
+
value = bin.default
|
95
103
|
end
|
96
104
|
|
97
|
-
send(bin.
|
105
|
+
send(bin.setter, value)
|
98
106
|
end
|
99
107
|
|
100
108
|
def write_fragment_for(bin, value, doc) # DISCUSS: move to Binding?
|
101
|
-
return if bin.
|
109
|
+
return if bin.skipable_nil_value?(value)
|
102
110
|
bin.write(doc, value)
|
103
111
|
end
|
104
112
|
|
@@ -111,7 +119,7 @@ private
|
|
111
119
|
end
|
112
120
|
|
113
121
|
def representable_bindings_for(format)
|
114
|
-
representable_attrs.map {|attr|
|
122
|
+
representable_attrs.map {|attr| format.build_for(attr) }
|
115
123
|
end
|
116
124
|
|
117
125
|
# Returns the wrapper for the representation. Mostly used in XML.
|
@@ -119,7 +127,6 @@ private
|
|
119
127
|
representable_attrs.wrap_for(self.class.name)
|
120
128
|
end
|
121
129
|
|
122
|
-
|
123
130
|
module ClassInclusions
|
124
131
|
def included(base)
|
125
132
|
super
|
@@ -163,6 +170,8 @@ private
|
|
163
170
|
# property :name, :class => Name
|
164
171
|
# property :name, :default => "Mike"
|
165
172
|
# property :name, :render_nil => true
|
173
|
+
# property :name, :readable => false
|
174
|
+
# property :name, :writeable => false
|
166
175
|
def property(name, options={})
|
167
176
|
representable_attrs << definition_class.new(name, options)
|
168
177
|
end
|
@@ -1,13 +1,13 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
1
3
|
module Representable
|
2
|
-
|
4
|
+
# The Binding wraps the Definition instance for this property and provides methods to read/write fragments.
|
5
|
+
class Binding < SimpleDelegator
|
3
6
|
class FragmentNotFound
|
4
7
|
end
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(definition)
|
10
|
-
@definition = definition
|
9
|
+
def definition # TODO: remove in 1.4.
|
10
|
+
raise "Binding#definition is no longer supported as all Definition methods are now delegated automatically."
|
11
11
|
end
|
12
12
|
|
13
13
|
# Main entry point for rendering/parsing a property object.
|
@@ -33,7 +33,7 @@ module Representable
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def extend_for(object)
|
36
|
-
if mod =
|
36
|
+
if mod = representer_module
|
37
37
|
object.extend(*mod)
|
38
38
|
end
|
39
39
|
|
@@ -56,7 +56,7 @@ module Representable
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def create_object
|
59
|
-
|
59
|
+
sought_type.new
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -16,20 +16,26 @@ module Representable
|
|
16
16
|
|
17
17
|
|
18
18
|
class PropertyBinding < Representable::Binding
|
19
|
-
def
|
19
|
+
def self.build_for(definition)
|
20
|
+
return CollectionBinding.new(definition) if definition.array?
|
21
|
+
return HashBinding.new(definition) if definition.hash?
|
22
|
+
new(definition)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(*args) # FIXME. make generic.
|
20
26
|
super
|
21
|
-
extend ObjectBinding if
|
27
|
+
extend ObjectBinding if typed?
|
22
28
|
end
|
23
29
|
|
24
30
|
def read(hash)
|
25
|
-
return FragmentNotFound unless hash.has_key?(
|
31
|
+
return FragmentNotFound unless hash.has_key?(from) # DISCUSS: put it all in #read for performance. not really sure if i like returning that special thing.
|
26
32
|
|
27
|
-
fragment = hash[
|
33
|
+
fragment = hash[from]
|
28
34
|
deserialize_from(fragment)
|
29
35
|
end
|
30
36
|
|
31
37
|
def write(hash, value)
|
32
|
-
hash[
|
38
|
+
hash[from] = serialize_for(value)
|
33
39
|
end
|
34
40
|
|
35
41
|
def serialize_for(value)
|
@@ -24,15 +24,23 @@ module Representable
|
|
24
24
|
|
25
25
|
|
26
26
|
class PropertyBinding < Binding
|
27
|
-
def
|
27
|
+
def self.build_for(definition)
|
28
|
+
return CollectionBinding.new(definition) if definition.array?
|
29
|
+
return HashBinding.new(definition) if definition.hash? and not definition.options[:use_attributes] # FIXME: hate this.
|
30
|
+
return AttributeHashBinding.new(definition) if definition.hash? and definition.options[:use_attributes]
|
31
|
+
return AttributeBinding.new(definition) if definition.attribute
|
32
|
+
new(definition)
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(*args)
|
28
36
|
super
|
29
|
-
extend ObjectBinding if
|
37
|
+
extend ObjectBinding if typed? # FIXME.
|
30
38
|
end
|
31
39
|
|
32
40
|
def write(parent, value)
|
33
41
|
wrap_node = parent
|
34
42
|
|
35
|
-
if wrap =
|
43
|
+
if wrap = options[:wrap]
|
36
44
|
parent << wrap_node = node_for(parent, wrap)
|
37
45
|
end
|
38
46
|
|
@@ -41,7 +49,7 @@ module Representable
|
|
41
49
|
|
42
50
|
def read(node)
|
43
51
|
selector = "./#{xpath}"
|
44
|
-
selector = "./#{
|
52
|
+
selector = "./#{options[:wrap]}/#{xpath}" if options[:wrap]
|
45
53
|
nodes = node.search(selector)
|
46
54
|
|
47
55
|
return FragmentNotFound if nodes.size == 0 # TODO: write dedicated test!
|
@@ -52,7 +60,7 @@ module Representable
|
|
52
60
|
# Creates wrapped node for the property.
|
53
61
|
def serialize_for(value, parent)
|
54
62
|
#def serialize_for(value, parent, tag_name=definition.from)
|
55
|
-
node = node_for(parent,
|
63
|
+
node = node_for(parent, from)
|
56
64
|
serialize_node(node, value)
|
57
65
|
end
|
58
66
|
|
@@ -72,7 +80,7 @@ module Representable
|
|
72
80
|
|
73
81
|
private
|
74
82
|
def xpath
|
75
|
-
|
83
|
+
from
|
76
84
|
end
|
77
85
|
|
78
86
|
def node_for(parent, name)
|
@@ -138,11 +146,11 @@ module Representable
|
|
138
146
|
# Represents a tag attribute. Currently this only works on the top-level tag.
|
139
147
|
class AttributeBinding < PropertyBinding
|
140
148
|
def read(node)
|
141
|
-
deserialize(node[
|
149
|
+
deserialize(node[from])
|
142
150
|
end
|
143
151
|
|
144
152
|
def serialize_for(value, parent)
|
145
|
-
parent[
|
153
|
+
parent[from] = serialize(value.to_s)
|
146
154
|
end
|
147
155
|
|
148
156
|
def write(parent, value)
|
@@ -19,13 +19,21 @@ module Representable
|
|
19
19
|
end
|
20
20
|
|
21
21
|
class PropertyBinding < Representable::Hash::PropertyBinding
|
22
|
-
def
|
22
|
+
def self.build_for(definition)
|
23
|
+
return CollectionBinding.new(definition) if definition.array?
|
24
|
+
#return HashBinding.new(definition) if definition.hash? and not definition.options[:use_attributes] # FIXME: hate this.
|
25
|
+
#return AttributeHashBinding.new(definition) if definition.hash? and definition.options[:use_attributes]
|
26
|
+
#return AttributeBinding.new(definition) if definition.attribute
|
27
|
+
new(definition)
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(*args) # FIXME. make generic.
|
23
31
|
super
|
24
|
-
extend ObjectBinding if
|
32
|
+
extend ObjectBinding if typed?
|
25
33
|
end
|
26
34
|
|
27
35
|
def write(map, value)
|
28
|
-
map.children << Psych::Nodes::Scalar.new(
|
36
|
+
map.children << Psych::Nodes::Scalar.new(from)
|
29
37
|
map.children << serialize_for(value) # FIXME: should be serialize.
|
30
38
|
end
|
31
39
|
|
@@ -42,7 +50,7 @@ module Representable
|
|
42
50
|
class CollectionBinding < PropertyBinding
|
43
51
|
def serialize_for(value)
|
44
52
|
Psych::Nodes::Sequence.new.tap do |seq|
|
45
|
-
seq.style = Psych::Nodes::Sequence::FLOW if
|
53
|
+
seq.style = Psych::Nodes::Sequence::FLOW if options[:style] == :flow
|
46
54
|
value.each { |obj| seq.children << super(obj) }
|
47
55
|
end
|
48
56
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Representable
|
2
|
+
module Feature
|
3
|
+
module ReadableWriteable
|
4
|
+
def deserialize_property(binding, doc, options)
|
5
|
+
return unless binding.writeable?
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def serialize_property(binding, doc, options)
|
10
|
+
return unless binding.readable?
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO: i hate monkey-patching Definition here since it globally adds this options. However, for now this should be ok :-)
|
17
|
+
class Definition
|
18
|
+
# Checks and returns if the property is writeable
|
19
|
+
def writeable?
|
20
|
+
return options[:writeable] if options.has_key?(:writeable)
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
# Checks and returns if the property is readable
|
25
|
+
def readable?
|
26
|
+
return options[:readable] if options.has_key?(:readable)
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/representable/hash.rb
CHANGED
@@ -22,28 +22,20 @@ module Representable
|
|
22
22
|
end
|
23
23
|
|
24
24
|
|
25
|
-
def from_hash(data, options={},
|
25
|
+
def from_hash(data, options={}, binding_builder=PropertyBinding)
|
26
26
|
if wrap = options[:wrap] || representation_wrap
|
27
27
|
data = data[wrap.to_s]
|
28
28
|
end
|
29
29
|
|
30
|
-
update_properties_from(data, options,
|
30
|
+
update_properties_from(data, options, binding_builder)
|
31
31
|
end
|
32
32
|
|
33
|
-
def to_hash(options={},
|
34
|
-
hash = create_representation_with({}, options,
|
33
|
+
def to_hash(options={}, binding_builder=PropertyBinding)
|
34
|
+
hash = create_representation_with({}, options, binding_builder)
|
35
35
|
|
36
36
|
return hash unless wrap = options[:wrap] || representation_wrap
|
37
37
|
|
38
38
|
{wrap => hash}
|
39
39
|
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def hash_binding_for_definition(definition)
|
44
|
-
return Representable::Hash::CollectionBinding.new(definition) if definition.array?
|
45
|
-
return Representable::Hash::HashBinding.new(definition) if definition.hash?
|
46
|
-
Representable::Hash::PropertyBinding.new(definition)
|
47
|
-
end
|
48
40
|
end
|
49
41
|
end
|
data/lib/representable/xml.rb
CHANGED
@@ -37,28 +37,18 @@ module Representable
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def from_node(node, options={})
|
40
|
-
update_properties_from(node, options,
|
40
|
+
update_properties_from(node, options, PropertyBinding)
|
41
41
|
end
|
42
42
|
|
43
43
|
# Returns a Nokogiri::XML object representing this object.
|
44
44
|
def to_node(options={})
|
45
45
|
root_tag = options[:wrap] || representation_wrap
|
46
46
|
|
47
|
-
create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new), options,
|
47
|
+
create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new), options, PropertyBinding)
|
48
48
|
end
|
49
49
|
|
50
50
|
def to_xml(*args)
|
51
51
|
to_node(*args).to_s
|
52
52
|
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def xml_binding_for_definition(definition)
|
57
|
-
return CollectionBinding.new(definition) if definition.array?
|
58
|
-
return HashBinding.new(definition) if definition.hash? and not definition.options[:use_attributes] # FIXME: hate this.
|
59
|
-
return AttributeHashBinding.new(definition) if definition.hash? and definition.options[:use_attributes]
|
60
|
-
return AttributeBinding.new(definition) if definition.attribute
|
61
|
-
PropertyBinding.new(definition)
|
62
|
-
end
|
63
53
|
end
|
64
54
|
end
|
data/lib/representable/yaml.rb
CHANGED
@@ -30,7 +30,7 @@ module Representable
|
|
30
30
|
|
31
31
|
def from_yaml(doc, options={})
|
32
32
|
hash = Psych.load(doc)
|
33
|
-
from_hash(hash, options,
|
33
|
+
from_hash(hash, options, PropertyBinding)
|
34
34
|
end
|
35
35
|
|
36
36
|
# Returns a Nokogiri::XML object representing this object.
|
@@ -38,7 +38,7 @@ module Representable
|
|
38
38
|
#root_tag = options[:wrap] || representation_wrap
|
39
39
|
|
40
40
|
Psych::Nodes::Mapping.new.tap do |map|
|
41
|
-
create_representation_with(map, options,
|
41
|
+
create_representation_with(map, options, PropertyBinding)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -49,15 +49,5 @@ module Representable
|
|
49
49
|
doc.children << to_ast(*args)
|
50
50
|
stream.to_yaml
|
51
51
|
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def yaml_binding_for_definition(definition)
|
56
|
-
return CollectionBinding.new(definition) if definition.array?
|
57
|
-
#return HashBinding.new(definition) if definition.hash? and not definition.options[:use_attributes] # FIXME: hate this.
|
58
|
-
#return AttributeHashBinding.new(definition) if definition.hash? and definition.options[:use_attributes]
|
59
|
-
#return AttributeBinding.new(definition) if definition.attribute
|
60
|
-
PropertyBinding.new(definition)
|
61
|
-
end
|
62
52
|
end
|
63
53
|
end
|
data/test/Gemfile
ADDED
data/test/coercion_test.rb
CHANGED
@@ -10,34 +10,41 @@ class VirtusCoercionTest < MiniTest::Spec
|
|
10
10
|
module SongRepresenter
|
11
11
|
include Representable::JSON
|
12
12
|
include Representable::Coercion
|
13
|
-
property :composed_at,
|
13
|
+
property :composed_at, :type => DateTime
|
14
|
+
property :track, :type => Integer
|
14
15
|
end
|
15
16
|
|
16
17
|
it "coerces properties in #from_json" do
|
17
|
-
song = Song.new.extend(SongRepresenter).from_json("{\"composed_at\":\"November 18th, 1983\"}")
|
18
|
+
song = Song.new.extend(SongRepresenter).from_json("{\"composed_at\":\"November 18th, 1983\",\"track\":\"18\"}")
|
18
19
|
assert_kind_of DateTime, song.composed_at
|
20
|
+
assert_equal 18, song.track
|
19
21
|
assert_equal DateTime.parse("Fri, 18 Nov 1983 00:00:00 +0000"), song.composed_at
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
25
|
|
24
|
-
class
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
describe "on class level" do
|
27
|
+
class ImmigrantSong
|
28
|
+
attr_accessor :track
|
29
|
+
include Representable::JSON
|
30
|
+
include Virtus
|
31
|
+
include Representable::Coercion
|
32
|
+
|
33
|
+
property :composed_at, :type => DateTime, :default => "May 12th, 2012"
|
34
|
+
property :track, :type => Integer
|
35
|
+
end
|
28
36
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
assert_equal DateTime.parse("Mon, 12 May 2012 00:00:00 +0000"), song.composed_at
|
37
|
+
it "coerces into the provided type" do
|
38
|
+
song = ImmigrantSong.new.from_json("{\"composed_at\":\"November 18th, 1983\",\"track\":\"18\"}")
|
39
|
+
assert_equal DateTime.parse("Fri, 18 Nov 1983 00:00:00 +0000"), song.composed_at
|
40
|
+
assert_equal 18, song.track
|
41
|
+
end
|
42
|
+
|
43
|
+
it "respects the :default options" do
|
44
|
+
song = ImmigrantSong.new.from_json("{}")
|
45
|
+
assert_kind_of DateTime, song.composed_at
|
46
|
+
assert_equal DateTime.parse("Mon, 12 May 2012 00:00:00 +0000"), song.composed_at
|
47
|
+
end
|
41
48
|
end
|
42
49
|
|
43
50
|
end
|
data/test/definition_test.rb
CHANGED
@@ -128,6 +128,56 @@ class DefinitionTest < MiniTest::Spec
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
+
|
132
|
+
describe "#writeable?" do
|
133
|
+
|
134
|
+
it "returns true when :writeable is not given" do
|
135
|
+
@def = Representable::Definition.new(:song)
|
136
|
+
assert_equal true, @def.writeable?
|
137
|
+
end
|
138
|
+
|
139
|
+
it "returns true when :writeable => true" do
|
140
|
+
@def = Representable::Definition.new(:song, :writeable => true)
|
141
|
+
assert_equal true, @def.writeable?
|
142
|
+
end
|
143
|
+
|
144
|
+
it "returns false when :writeable => false" do
|
145
|
+
@def = Representable::Definition.new(:song, :writeable => false)
|
146
|
+
assert_equal false, @def.writeable?
|
147
|
+
end
|
148
|
+
|
149
|
+
it "returns nil when :writeable is nil" do
|
150
|
+
@def = Representable::Definition.new(:song, :writeable => nil)
|
151
|
+
assert_equal nil, @def.writeable?
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "#readable?" do
|
157
|
+
|
158
|
+
it "returns true when :readable is not given" do
|
159
|
+
@def = Representable::Definition.new(:song)
|
160
|
+
assert_equal true, @def.readable?
|
161
|
+
end
|
162
|
+
|
163
|
+
it "returns true when :readable => true" do
|
164
|
+
@def = Representable::Definition.new(:song, :readable => true)
|
165
|
+
assert_equal true, @def.readable?
|
166
|
+
end
|
167
|
+
|
168
|
+
it "returns false when :readable => false" do
|
169
|
+
@def = Representable::Definition.new(:song, :readable => false)
|
170
|
+
assert_equal false, @def.readable?
|
171
|
+
end
|
172
|
+
|
173
|
+
it "returns nil when :readable is nil" do
|
174
|
+
@def = Representable::Definition.new(:song, :readable => nil)
|
175
|
+
assert_equal nil, @def.readable?
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
|
131
181
|
describe ":collection => true" do
|
132
182
|
before do
|
133
183
|
@def = Representable::Definition.new(:songs, :collection => true, :tag => :song)
|
data/test/example.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require 'representable/yaml'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
class Song < OpenStruct
|
8
|
+
end
|
9
|
+
|
10
|
+
song = Song.new(:title => "Fallout", :track => 1)
|
11
|
+
|
12
|
+
require 'representable/json'
|
13
|
+
module SongRepresenter
|
14
|
+
include Representable::JSON
|
15
|
+
|
16
|
+
property :title
|
17
|
+
property :track
|
18
|
+
end
|
19
|
+
|
20
|
+
puts song.extend(SongRepresenter).to_json
|
21
|
+
|
22
|
+
rox = Song.new.extend(SongRepresenter).from_json(%{ {"title":"Roxanne"} })
|
23
|
+
puts rox.inspect
|
24
|
+
|
25
|
+
module SongRepresenter
|
26
|
+
include Representable::JSON
|
27
|
+
|
28
|
+
self.representation_wrap= :hit
|
29
|
+
|
30
|
+
property :title
|
31
|
+
property :track
|
32
|
+
end
|
33
|
+
|
34
|
+
puts song.extend(SongRepresenter).to_json
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
######### collections
|
39
|
+
|
40
|
+
module SongRepresenter
|
41
|
+
include Representable::JSON
|
42
|
+
|
43
|
+
self.representation_wrap= false
|
44
|
+
end
|
45
|
+
|
46
|
+
module SongRepresenter
|
47
|
+
include Representable::JSON
|
48
|
+
|
49
|
+
property :title
|
50
|
+
property :track
|
51
|
+
collection :composers
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
song = Song.new(:title => "Fallout", :composers => ["Steward Copeland", "Sting"])
|
56
|
+
puts song.extend(SongRepresenter).to_json
|
57
|
+
|
58
|
+
|
59
|
+
######### nesting types
|
60
|
+
|
61
|
+
class Album < OpenStruct
|
62
|
+
end
|
63
|
+
|
64
|
+
module AlbumRepresenter
|
65
|
+
include Representable::JSON
|
66
|
+
|
67
|
+
property :name
|
68
|
+
property :song, :extend => SongRepresenter, :class => Song
|
69
|
+
end
|
70
|
+
|
71
|
+
album = Album.new(:name => "The Police", :song => song)
|
72
|
+
puts album.extend(AlbumRepresenter).to_json
|
73
|
+
|
74
|
+
|
75
|
+
module AlbumRepresenter
|
76
|
+
include Representable::JSON
|
77
|
+
|
78
|
+
property :name
|
79
|
+
collection :songs, :extend => SongRepresenter, :class => Song
|
80
|
+
end
|
81
|
+
|
82
|
+
album = Album.new(:name => "The Police", :songs => [song, Song.new(:title => "Synchronicity")])
|
83
|
+
puts album.extend(AlbumRepresenter).to_json
|
84
|
+
|
85
|
+
|
86
|
+
SongRepresenter.module_eval do
|
87
|
+
@representable_attrs = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
######### inheritance
|
92
|
+
module SongRepresenter
|
93
|
+
include Representable::JSON
|
94
|
+
|
95
|
+
property :title
|
96
|
+
property :track
|
97
|
+
end
|
98
|
+
|
99
|
+
module CoverSongRepresenter
|
100
|
+
include Representable::JSON
|
101
|
+
include SongRepresenter
|
102
|
+
|
103
|
+
property :covered_by
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
song = Song.new(:title => "Truth Hits Everybody", :covered_by => "No Use For A Name")
|
108
|
+
puts song.extend(CoverSongRepresenter).to_json
|
109
|
+
|
110
|
+
|
111
|
+
### XML
|
112
|
+
require 'representable/xml'
|
113
|
+
module SongRepresenter
|
114
|
+
include Representable::XML
|
115
|
+
|
116
|
+
property :title
|
117
|
+
property :track
|
118
|
+
collection :composers
|
119
|
+
end
|
120
|
+
song = Song.new(:title => "Fallout", :composers => ["Steward Copeland", "Sting"])
|
121
|
+
puts song.extend(SongRepresenter).to_xml
|
122
|
+
|
123
|
+
|
124
|
+
SongRepresenter.module_eval do
|
125
|
+
@representable_attrs = nil
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
### YAML
|
130
|
+
require 'representable/yaml'
|
131
|
+
module SongRepresenter
|
132
|
+
include Representable::YAML
|
133
|
+
|
134
|
+
property :title
|
135
|
+
property :track
|
136
|
+
collection :composers
|
137
|
+
end
|
138
|
+
puts song.extend(SongRepresenter).to_yaml
|
139
|
+
|
140
|
+
|
141
|
+
SongRepresenter.module_eval do
|
142
|
+
@representable_attrs = nil
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
### YAML
|
147
|
+
module SongRepresenter
|
148
|
+
include Representable::YAML
|
149
|
+
|
150
|
+
property :title
|
151
|
+
property :track
|
152
|
+
collection :composers, :style => :flow
|
153
|
+
end
|
154
|
+
puts song.extend(SongRepresenter).to_yaml
|
155
|
+
|
156
|
+
|
157
|
+
######### custom methods in representer (using helpers)
|
158
|
+
######### r/w, conditions
|
159
|
+
#########
|
data/test/hash_test.rb
CHANGED
@@ -114,4 +114,23 @@ class YamlTest < MiniTest::Spec
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
117
|
+
|
118
|
+
|
119
|
+
class DefinitionTest < MiniTest::Spec
|
120
|
+
it "what" do
|
121
|
+
class Representable::Hash::Binding < SimpleDelegator
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
definition = Representable::Definition.new(:name)
|
126
|
+
wrapped = Representable::Hash::Binding.new(definition)
|
127
|
+
|
128
|
+
wrapped.name.must_equal "name"
|
129
|
+
wrapped.hash?.must_equal nil
|
130
|
+
wrapped.array?.must_equal nil
|
131
|
+
wrapped.options.must_equal({})
|
132
|
+
|
133
|
+
#wrapped.
|
134
|
+
end
|
135
|
+
end
|
117
136
|
end
|
data/test/json_test.rb
CHANGED
@@ -126,28 +126,28 @@ module JsonTest
|
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
|
-
describe "#
|
129
|
+
describe "#build_for" do
|
130
130
|
it "returns ObjectBinding" do
|
131
|
-
assert_kind_of Representable::Hash::ObjectBinding,
|
131
|
+
assert_kind_of Representable::Hash::ObjectBinding, Representable::Hash::PropertyBinding.build_for(Def.new(:band, :class => Hash))
|
132
132
|
end
|
133
133
|
|
134
134
|
it "returns TextBinding" do
|
135
|
-
assert_kind_of Representable::Hash::PropertyBinding,
|
135
|
+
assert_kind_of Representable::Hash::PropertyBinding, Representable::Hash::PropertyBinding.build_for(Def.new(:band))
|
136
136
|
end
|
137
137
|
|
138
138
|
it "returns HashBinding" do
|
139
|
-
assert_kind_of Representable::Hash::HashBinding,
|
139
|
+
assert_kind_of Representable::Hash::HashBinding, Representable::Hash::PropertyBinding.build_for(Def.new(:band, :hash => true))
|
140
140
|
end
|
141
141
|
|
142
142
|
it "returns CollectionBinding" do
|
143
|
-
assert_kind_of Representable::Hash::CollectionBinding,
|
143
|
+
assert_kind_of Representable::Hash::CollectionBinding, Representable::Hash::PropertyBinding.build_for(Def.new(:band, :collection => true))
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
147
|
-
describe "#
|
147
|
+
describe "#representable_bindings_for" do
|
148
148
|
it "returns bindings for each property" do
|
149
|
-
assert_equal 2, @band.send(:representable_bindings_for,
|
150
|
-
assert_equal "name", @band.send(:representable_bindings_for,
|
149
|
+
assert_equal 2, @band.send(:representable_bindings_for, Representable::JSON::PropertyBinding).size
|
150
|
+
assert_equal "name", @band.send(:representable_bindings_for, Representable::JSON::PropertyBinding).first.name
|
151
151
|
end
|
152
152
|
end
|
153
153
|
end
|
data/test/representable_test.rb
CHANGED
@@ -98,6 +98,25 @@ class RepresentableTest < MiniTest::Spec
|
|
98
98
|
|
99
99
|
|
100
100
|
describe "Representable" do
|
101
|
+
describe "inheritance" do
|
102
|
+
class CoverSong < OpenStruct
|
103
|
+
end
|
104
|
+
module SongRepresenter
|
105
|
+
include Representable::Hash
|
106
|
+
property :name
|
107
|
+
end
|
108
|
+
module CoverSongRepresenter
|
109
|
+
include Representable::Hash
|
110
|
+
include SongRepresenter
|
111
|
+
property :by
|
112
|
+
end
|
113
|
+
|
114
|
+
it "merges properties from all ancestors" do
|
115
|
+
props = {"name"=>"The Brews", "by"=>"Nofx"}
|
116
|
+
assert_equal(props, CoverSong.new(props).extend(CoverSongRepresenter).to_hash)
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
101
120
|
it "allows mixing in multiple representers" do
|
102
121
|
require 'representable/json'
|
103
122
|
require 'representable/xml'
|
@@ -218,39 +237,46 @@ class RepresentableTest < MiniTest::Spec
|
|
218
237
|
end
|
219
238
|
|
220
239
|
it "copies values from document to object" do
|
221
|
-
@band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {},
|
240
|
+
@band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {}, Representable::Hash::PropertyBinding)
|
222
241
|
assert_equal "No One's Choice", @band.name
|
223
242
|
assert_equal 2, @band.groupies
|
224
243
|
end
|
225
244
|
|
226
245
|
it "accepts :exclude option" do
|
227
|
-
@band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:exclude => [:groupies]},
|
246
|
+
@band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:exclude => [:groupies]}, Representable::Hash::PropertyBinding)
|
228
247
|
assert_equal "No One's Choice", @band.name
|
229
248
|
assert_equal nil, @band.groupies
|
230
249
|
end
|
231
250
|
|
232
251
|
it "still accepts deprecated :except option" do # FIXME: remove :except option.
|
233
|
-
assert_equal @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:except => [:groupies]},
|
252
|
+
assert_equal @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:except => [:groupies]}, Representable::Hash::PropertyBinding), @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:exclude => [:groupies]}, Representable::Hash::PropertyBinding)
|
234
253
|
end
|
235
254
|
|
236
255
|
it "accepts :include option" do
|
237
|
-
@band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:include => [:groupies]},
|
256
|
+
@band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:include => [:groupies]}, Representable::Hash::PropertyBinding)
|
238
257
|
assert_equal 2, @band.groupies
|
239
258
|
assert_equal nil, @band.name
|
240
259
|
end
|
260
|
+
|
261
|
+
it "ignores non-writeable properties" do
|
262
|
+
@band = Class.new(Band) { property :name; collection :founders, :writeable => false; attr_accessor :founders }.new
|
263
|
+
@band.update_properties_from({"name" => "Iron Maiden", "groupies" => 2, "founders" => [{ "name" => "Steve Harris" }] }, {}, Representable::Hash::PropertyBinding)
|
264
|
+
assert_equal "Iron Maiden", @band.name
|
265
|
+
assert_equal nil, @band.founders
|
266
|
+
end
|
241
267
|
|
242
268
|
it "always returns self" do
|
243
|
-
assert_equal @band, @band.update_properties_from({"name"=>"Nofx"}, {},
|
269
|
+
assert_equal @band, @band.update_properties_from({"name"=>"Nofx"}, {}, Representable::Hash::PropertyBinding)
|
244
270
|
end
|
245
271
|
|
246
272
|
it "includes false attributes" do
|
247
|
-
@band.update_properties_from({"groupies"=>false}, {},
|
273
|
+
@band.update_properties_from({"groupies"=>false}, {}, Representable::Hash::PropertyBinding)
|
248
274
|
assert_equal false, @band.groupies
|
249
275
|
end
|
250
276
|
|
251
277
|
it "ignores (no-default) properties not present in the incoming document" do
|
252
|
-
{ Representable::JSON => [{},
|
253
|
-
Representable::XML => [xml(%{<band/>}),
|
278
|
+
{ Representable::JSON => [{}, Representable::Hash::PropertyBinding],
|
279
|
+
Representable::XML => [xml(%{<band/>}), Representable::XML::PropertyBinding]
|
254
280
|
}.each do |format, config|
|
255
281
|
nested_repr = Module.new do # this module is never applied.
|
256
282
|
include format
|
@@ -277,31 +303,40 @@ class RepresentableTest < MiniTest::Spec
|
|
277
303
|
end
|
278
304
|
|
279
305
|
it "compiles document from properties in object" do
|
280
|
-
assert_equal({"name"=>"No One's Choice", "groupies"=>2}, @band.send(:create_representation_with, {}, {},
|
306
|
+
assert_equal({"name"=>"No One's Choice", "groupies"=>2}, @band.send(:create_representation_with, {}, {}, Representable::Hash::PropertyBinding))
|
281
307
|
end
|
282
308
|
|
283
309
|
it "accepts :exclude option" do
|
284
|
-
hash = @band.send(:create_representation_with, {}, {:exclude => [:groupies]},
|
310
|
+
hash = @band.send(:create_representation_with, {}, {:exclude => [:groupies]}, Representable::Hash::PropertyBinding)
|
285
311
|
assert_equal({"name"=>"No One's Choice"}, hash)
|
286
312
|
end
|
287
313
|
|
288
314
|
it "still accepts deprecated :except option" do # FIXME: remove :except option.
|
289
|
-
assert_equal @band.send(:create_representation_with, {}, {:except => [:groupies]},
|
315
|
+
assert_equal @band.send(:create_representation_with, {}, {:except => [:groupies]}, Representable::Hash::PropertyBinding), @band.send(:create_representation_with, {}, {:exclude => [:groupies]}, Representable::Hash::PropertyBinding)
|
290
316
|
end
|
291
317
|
|
292
318
|
it "accepts :include option" do
|
293
|
-
hash = @band.send(:create_representation_with, {}, {:include => [:groupies]},
|
319
|
+
hash = @band.send(:create_representation_with, {}, {:include => [:groupies]}, Representable::Hash::PropertyBinding)
|
294
320
|
assert_equal({"groupies"=>2}, hash)
|
295
321
|
end
|
296
322
|
|
323
|
+
it "ignores non-readable properties" do
|
324
|
+
@band = Class.new(Band) { property :name; collection :founder_ids, :readable => false; attr_accessor :founder_ids }.new
|
325
|
+
@band.name = "Iron Maiden"
|
326
|
+
@band.founder_ids = [1,2,3]
|
327
|
+
|
328
|
+
hash = @band.send(:create_representation_with, {}, {}, Representable::Hash::PropertyBinding)
|
329
|
+
assert_equal({"name" => "Iron Maiden"}, hash)
|
330
|
+
end
|
331
|
+
|
297
332
|
it "does not write nil attributes" do
|
298
333
|
@band.groupies = nil
|
299
|
-
assert_equal({"name"=>"No One's Choice"}, @band.send(:create_representation_with, {}, {},
|
334
|
+
assert_equal({"name"=>"No One's Choice"}, @band.send(:create_representation_with, {}, {}, Representable::Hash::PropertyBinding))
|
300
335
|
end
|
301
336
|
|
302
337
|
it "writes false attributes" do
|
303
338
|
@band.groupies = false
|
304
|
-
assert_equal({"name"=>"No One's Choice","groupies"=>false}, @band.send(:create_representation_with, {}, {},
|
339
|
+
assert_equal({"name"=>"No One's Choice","groupies"=>false}, @band.send(:create_representation_with, {}, {}, Representable::Hash::PropertyBinding))
|
305
340
|
end
|
306
341
|
|
307
342
|
describe "when :render_nil is true" do
|
@@ -314,7 +349,7 @@ class RepresentableTest < MiniTest::Spec
|
|
314
349
|
|
315
350
|
@band.extend(mod) # FIXME: use clean object.
|
316
351
|
@band.groupies = nil
|
317
|
-
hash = @band.send(:create_representation_with, {}, {},
|
352
|
+
hash = @band.send(:create_representation_with, {}, {}, Representable::Hash::PropertyBinding)
|
318
353
|
assert_equal({"name"=>"No One's Choice", "groupies" => nil}, hash)
|
319
354
|
end
|
320
355
|
|
@@ -327,7 +362,7 @@ class RepresentableTest < MiniTest::Spec
|
|
327
362
|
|
328
363
|
@band.extend(mod) # FIXME: use clean object.
|
329
364
|
@band.groupies = nil
|
330
|
-
hash = @band.send(:create_representation_with, {}, {},
|
365
|
+
hash = @band.send(:create_representation_with, {}, {}, Representable::Hash::PropertyBinding)
|
331
366
|
assert_equal({"name"=>"No One's Choice", "groupies" => nil}, hash)
|
332
367
|
end
|
333
368
|
end
|
@@ -341,21 +376,21 @@ class RepresentableTest < MiniTest::Spec
|
|
341
376
|
it "respects property when condition true" do
|
342
377
|
@pop.class_eval { property :fame, :if => lambda { true } }
|
343
378
|
band = @pop.new
|
344
|
-
band.update_properties_from({"fame"=>"oh yes"}, {},
|
379
|
+
band.update_properties_from({"fame"=>"oh yes"}, {}, Representable::Hash::PropertyBinding)
|
345
380
|
assert_equal "oh yes", band.fame
|
346
381
|
end
|
347
382
|
|
348
383
|
it "ignores property when condition false" do
|
349
384
|
@pop.class_eval { property :fame, :if => lambda { false } }
|
350
385
|
band = @pop.new
|
351
|
-
band.update_properties_from({"fame"=>"oh yes"}, {},
|
386
|
+
band.update_properties_from({"fame"=>"oh yes"}, {}, Representable::Hash::PropertyBinding)
|
352
387
|
assert_equal nil, band.fame
|
353
388
|
end
|
354
389
|
|
355
390
|
it "ignores property when :exclude'ed even when condition is true" do
|
356
391
|
@pop.class_eval { property :fame, :if => lambda { true } }
|
357
392
|
band = @pop.new
|
358
|
-
band.update_properties_from({"fame"=>"oh yes"}, {:exclude => [:fame]},
|
393
|
+
band.update_properties_from({"fame"=>"oh yes"}, {:exclude => [:fame]}, Representable::Hash::PropertyBinding)
|
359
394
|
assert_equal nil, band.fame
|
360
395
|
end
|
361
396
|
|
@@ -364,7 +399,7 @@ class RepresentableTest < MiniTest::Spec
|
|
364
399
|
@pop.class_eval { property :fame, :if => lambda { groupies } }
|
365
400
|
band = @pop.new
|
366
401
|
band.groupies = true
|
367
|
-
band.update_properties_from({"fame"=>"oh yes"}, {},
|
402
|
+
band.update_properties_from({"fame"=>"oh yes"}, {}, Representable::Hash::PropertyBinding)
|
368
403
|
assert_equal "oh yes", band.fame
|
369
404
|
end
|
370
405
|
end
|
data/test/xml_test.rb
CHANGED
@@ -124,22 +124,22 @@ class XmlTest < MiniTest::Spec
|
|
124
124
|
end
|
125
125
|
|
126
126
|
|
127
|
-
describe "XML#
|
127
|
+
describe "XML::Binding#build_for" do
|
128
128
|
it "returns AttributeBinding" do
|
129
|
-
assert_kind_of XML::AttributeBinding,
|
129
|
+
assert_kind_of XML::AttributeBinding, XML::PropertyBinding.build_for(Def.new(:band, :from => "band", :attribute => true))
|
130
130
|
end
|
131
131
|
|
132
132
|
it "returns PropertyBinding" do
|
133
|
-
assert_kind_of XML::PropertyBinding,
|
134
|
-
assert_kind_of XML::PropertyBinding,
|
133
|
+
assert_kind_of XML::PropertyBinding, XML::PropertyBinding.build_for(Def.new(:band, :class => Hash))
|
134
|
+
assert_kind_of XML::PropertyBinding, XML::PropertyBinding.build_for(Def.new(:band, :from => :content))
|
135
135
|
end
|
136
136
|
|
137
137
|
it "returns CollectionBinding" do
|
138
|
-
assert_kind_of XML::CollectionBinding,
|
138
|
+
assert_kind_of XML::CollectionBinding, XML::PropertyBinding.build_for(Def.new(:band, :collection => :true))
|
139
139
|
end
|
140
140
|
|
141
141
|
it "returns HashBinding" do
|
142
|
-
assert_kind_of XML::HashBinding,
|
142
|
+
assert_kind_of XML::HashBinding, XML::PropertyBinding.build_for(Def.new(:band, :hash => :true))
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
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.8
|
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-
|
12
|
+
date: 2012-11-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
@@ -164,6 +164,7 @@ files:
|
|
164
164
|
- lib/representable/coercion.rb
|
165
165
|
- lib/representable/definition.rb
|
166
166
|
- lib/representable/deprecations.rb
|
167
|
+
- lib/representable/feature/readable_writeable.rb
|
167
168
|
- lib/representable/hash.rb
|
168
169
|
- lib/representable/hash_methods.rb
|
169
170
|
- lib/representable/json.rb
|
@@ -175,8 +176,10 @@ files:
|
|
175
176
|
- lib/representable/xml/hash.rb
|
176
177
|
- lib/representable/yaml.rb
|
177
178
|
- representable.gemspec
|
179
|
+
- test/Gemfile
|
178
180
|
- test/coercion_test.rb
|
179
181
|
- test/definition_test.rb
|
182
|
+
- test/example.rb
|
180
183
|
- test/hash_bindings_test.rb
|
181
184
|
- test/hash_test.rb
|
182
185
|
- test/json_test.rb
|