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