representable 0.9.2 → 0.9.3
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 +4 -0
- data/lib/representable.rb +14 -158
- data/lib/representable/bindings/json_bindings.rb +1 -1
- data/lib/representable/definition.rb +7 -22
- data/lib/representable/json.rb +4 -0
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +1 -0
- data/test/definition_test.rb +4 -17
- metadata +3 -3
data/CHANGES.textile
CHANGED
data/lib/representable.rb
CHANGED
@@ -51,172 +51,28 @@ private
|
|
51
51
|
representable_attrs.map {|attr| binding_for_definition(attr) }
|
52
52
|
end
|
53
53
|
|
54
|
-
# Declares a
|
55
|
-
# or a typed collection of nodes. This method does not add a corresponding accessor
|
56
|
-
# to the object. For that behavior see the similar methods: .xml_reader and .xml_accessor.
|
54
|
+
# Declares a represented document node, which is usually a XML tag or a JSON key.
|
57
55
|
#
|
58
|
-
#
|
59
|
-
# [sym] Symbol representing the name of the accessor.
|
56
|
+
# Examples:
|
60
57
|
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
|
65
|
-
# xml_reader :bob
|
66
|
-
# xml_accessor :pony, :from => :attr
|
67
|
-
#
|
68
|
-
# are equivalent to:
|
69
|
-
#
|
70
|
-
# xml_reader :bob, :from => 'bob'
|
71
|
-
# xml_accessor :pony, :from => '@pony'
|
72
|
-
#
|
73
|
-
# == Options
|
74
|
-
# === :as
|
75
|
-
# ==== Basic Types
|
76
|
-
# Allows you to specify one of several basic types to return the value as. For example
|
77
|
-
#
|
78
|
-
# xml_reader :count, :as => Integer
|
79
|
-
#
|
80
|
-
# is equivalent to:
|
81
|
-
#
|
82
|
-
# xml_reader(:count) {|val| Integer(val) unless val.empty? }
|
83
|
-
#
|
84
|
-
# Such block shorthands for Integer, Float, Fixnum, BigDecimal, Date, Time, and DateTime
|
85
|
-
# are currently available, but only for non-Hash declarations.
|
86
|
-
#
|
87
|
-
# To reference many elements, put the desired type in a literal array. e.g.:
|
88
|
-
#
|
89
|
-
# xml_reader :counts, :as => [Integer]
|
90
|
-
#
|
91
|
-
# Even an array of text nodes can be specified with :as => []
|
92
|
-
#
|
93
|
-
# xml_reader :quotes, :as => []
|
94
|
-
#
|
95
|
-
# === Other ROXML Class
|
96
|
-
# Declares an accessor that represents another ROXML class as child XML element
|
97
|
-
# (one-to-one or composition) or array of child elements (one-to-many or
|
98
|
-
# aggregation) of this type. Default is one-to-one. For one-to-many, simply pass the class
|
99
|
-
# as the only element in an array.
|
100
|
-
#
|
101
|
-
# Composition example:
|
102
|
-
# <book>
|
103
|
-
# <publisher>
|
104
|
-
# <name>Pragmatic Bookshelf</name>
|
105
|
-
# </publisher>
|
106
|
-
# </book>
|
107
|
-
#
|
108
|
-
# Can be mapped using the following code:
|
109
|
-
# class Book
|
110
|
-
# xml_reader :publisher, :as => Publisher
|
111
|
-
# end
|
112
|
-
#
|
113
|
-
# Aggregation example:
|
114
|
-
# <library>
|
115
|
-
# <books>
|
116
|
-
# <book/>
|
117
|
-
# <book/>
|
118
|
-
# </books>
|
119
|
-
# </library>
|
120
|
-
#
|
121
|
-
# Can be mapped using the following code:
|
122
|
-
# class Library
|
123
|
-
# xml_reader :books, :as => [Book], :in => "books"
|
124
|
-
# end
|
125
|
-
#
|
126
|
-
# If you don't have the <books> tag to wrap around the list of <book> tags:
|
127
|
-
# <library>
|
128
|
-
# <name>Ruby books</name>
|
129
|
-
# <book/>
|
130
|
-
# <book/>
|
131
|
-
# </library>
|
132
|
-
#
|
133
|
-
# You can skip the wrapper argument:
|
134
|
-
# xml_reader :books, :as => [Book]
|
135
|
-
#
|
136
|
-
# === :from
|
137
|
-
# The name by which the xml value will be found, either an attribute or tag name in XML.
|
138
|
-
# Default is sym, or the singular form of sym, in the case of arrays and hashes.
|
139
|
-
#
|
140
|
-
# This value may also include XPath notation.
|
141
|
-
#
|
142
|
-
# ==== :from => :content
|
143
|
-
# When :from is set to :content, this refers to the content of the current node,
|
144
|
-
# rather than a sub-node. It is equivalent to :from => '.'
|
145
|
-
#
|
146
|
-
# Example:
|
147
|
-
# class Contributor
|
148
|
-
# xml_reader :name, :from => :content
|
149
|
-
# xml_reader :role, :from => :attr
|
150
|
-
# end
|
151
|
-
#
|
152
|
-
# To map:
|
153
|
-
# <contributor role="editor">James Wick</contributor>
|
154
|
-
#
|
155
|
-
# ==== :from => :attr
|
156
|
-
# When :from is set to :attr, this refers to the content of an attribute,
|
157
|
-
# rather than a sub-node. It is equivalent to :from => '@attribute_name'
|
158
|
-
#
|
159
|
-
# Example:
|
160
|
-
# class Book
|
161
|
-
# xml_reader :isbn, :from => "@ISBN"
|
162
|
-
# xml_accessor :title, :from => :attr # :from defaults to '@title'
|
163
|
-
# end
|
164
|
-
#
|
165
|
-
# To map:
|
166
|
-
# <book ISBN="0974514055" title="Programming Ruby: the pragmatic programmers' guide" />
|
167
|
-
#
|
168
|
-
# ==== :from => :text
|
169
|
-
# The default source, if none is specified, this means the accessor
|
170
|
-
# represents a text node from XML. This is documented for completeness
|
171
|
-
# only. You should just leave this option off when you want the default behavior,
|
172
|
-
# as in the examples below.
|
173
|
-
#
|
174
|
-
# :text is equivalent to :from => accessor_name, and you should specify the
|
175
|
-
# actual node name (and, optionally, a namespace) if it differs, as in the case of :author below.
|
176
|
-
#
|
177
|
-
# Example:
|
178
|
-
# class Book
|
179
|
-
# xml_reader :author, :from => 'Author'
|
180
|
-
# xml_accessor :description, :cdata => true
|
181
|
-
# xml_reader :title
|
182
|
-
# end
|
183
|
-
#
|
184
|
-
# To map:
|
185
|
-
# <book>
|
186
|
-
# <title>Programming Ruby: the pragmatic programmers' guide</title>
|
187
|
-
# <description><![CDATA[Probably the best Ruby book out there]]></description>
|
188
|
-
# <Author>David Thomas</Author>
|
189
|
-
# </book>
|
190
|
-
#
|
191
|
-
# Likewise, a number of :text node values can be collected in an array like so:
|
192
|
-
#
|
193
|
-
# Example:
|
194
|
-
# class Library
|
195
|
-
# xml_reader :books, :as => []
|
196
|
-
# end
|
197
|
-
#
|
198
|
-
# To map:
|
199
|
-
# <library>
|
200
|
-
# <book>To kill a mockingbird</book>
|
201
|
-
# <book>House of Leaves</book>
|
202
|
-
# <book>Gödel, Escher, Bach</book>
|
203
|
-
# </library>
|
204
|
-
#
|
205
|
-
# === Other Options
|
206
|
-
# [:in] An optional name of a wrapping tag for this XML accessor.
|
207
|
-
# This can include other xpath values, which will be joined with :from with a '/'
|
208
|
-
# [:required] If true, throws RequiredElementMissing when the element isn't present
|
209
|
-
# [:cdata] true for values which should be input from or output as cdata elements
|
210
|
-
# [:to_xml] this proc is applied to the attributes value outputting the instance via #to_xml
|
211
|
-
#
|
212
|
-
def representable_property(*args) # TODO: make it accept 1-n props.
|
58
|
+
# representable_property :name
|
59
|
+
# representable_property :name, :from => :title
|
60
|
+
# representable_property :name, :as => Name
|
61
|
+
def representable_property(*args)
|
213
62
|
attr = add_representable_property(*args)
|
214
63
|
attr_reader(attr.getter)
|
215
64
|
attr_writer(attr.getter)
|
216
65
|
end
|
217
66
|
|
67
|
+
# Declares a represented document node collection.
|
68
|
+
#
|
69
|
+
# Examples:
|
70
|
+
#
|
71
|
+
# representable_collection :products
|
72
|
+
# representable_collection :products, :from => :item
|
73
|
+
# representable_collection :products, :as => Product
|
218
74
|
def representable_collection(name, options={})
|
219
|
-
options[:
|
75
|
+
options[:collection] = true
|
220
76
|
representable_property(name, options)
|
221
77
|
end
|
222
78
|
|
@@ -44,7 +44,7 @@ module Representable
|
|
44
44
|
class ObjectBinding < Binding
|
45
45
|
def write(hash, value)
|
46
46
|
if definition.array?
|
47
|
-
hash.merge!
|
47
|
+
hash.merge!({definition.from => value.collect {|v| v.to_hash(:wrap => false)}})
|
48
48
|
else
|
49
49
|
hash.merge! value.to_hash
|
50
50
|
end
|
@@ -3,24 +3,16 @@ module Representable
|
|
3
3
|
attr_reader :name, :sought_type, :wrapper, :from
|
4
4
|
alias_method :getter, :name
|
5
5
|
|
6
|
-
def initialize(sym,
|
7
|
-
@name
|
8
|
-
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@sought_type = extract_type(opts[:as])
|
6
|
+
def initialize(sym, options={})
|
7
|
+
@name = sym.to_s
|
8
|
+
@array = options[:collection]
|
9
|
+
@from = (options[:from] || name).to_s
|
10
|
+
@sought_type = options[:as] || :text
|
12
11
|
|
13
12
|
# FIXME: move me to xml.
|
14
|
-
if
|
15
|
-
opts[:from] = '.'
|
16
|
-
elsif opts[:from] == :name
|
17
|
-
opts[:from] = '*'
|
18
|
-
elsif opts[:from] == :attr
|
19
|
-
@sought_type = :attr
|
20
|
-
opts[:from] = nil
|
21
|
-
elsif opts[:from].to_s =~ /^@/
|
13
|
+
if options[:from].to_s =~ /^@/
|
22
14
|
@sought_type = :attr
|
23
|
-
|
15
|
+
options[:from].sub!('@', '')
|
24
16
|
end
|
25
17
|
end
|
26
18
|
|
@@ -67,12 +59,5 @@ module Representable
|
|
67
59
|
|
68
60
|
value
|
69
61
|
end
|
70
|
-
|
71
|
-
private
|
72
|
-
def extract_type(as)
|
73
|
-
as = as.first if as.is_a?(Array) # TODO: move to ArrayDefinition.
|
74
|
-
|
75
|
-
as || :text
|
76
|
-
end
|
77
62
|
end
|
78
63
|
end
|
data/lib/representable/json.rb
CHANGED
@@ -2,6 +2,10 @@ require 'json'
|
|
2
2
|
require 'representable/bindings/json_bindings'
|
3
3
|
|
4
4
|
module Representable
|
5
|
+
# Brings #to_xml, #to_hash, #from_xml and #from_hash to your object.
|
6
|
+
#
|
7
|
+
# Note: The authorative methods are #to_hash and #from_hash, if you override #to_json instead,
|
8
|
+
# things might work as expected.
|
5
9
|
module JSON
|
6
10
|
BINDING_FOR_TYPE = { # TODO: refactor #representable_accessor for better extendability.
|
7
11
|
:text => TextBinding,
|
data/lib/representable/xml.rb
CHANGED
data/test/definition_test.rb
CHANGED
@@ -9,7 +9,6 @@ class DefinitionTest < MiniTest::Spec
|
|
9
9
|
it "responds to #typed?" do
|
10
10
|
assert ! @def.typed?
|
11
11
|
assert Representable::Definition.new(:songs, :as => Hash).typed?
|
12
|
-
assert Representable::Definition.new(:songs, :as => [Hash]).typed?
|
13
12
|
end
|
14
13
|
|
15
14
|
it "responds to #getter and returns string" do
|
@@ -41,19 +40,19 @@ class DefinitionTest < MiniTest::Spec
|
|
41
40
|
end
|
42
41
|
|
43
42
|
it "works with collection" do
|
44
|
-
@d = Representable::Definition.new(:song, :
|
43
|
+
@d = Representable::Definition.new(:song, :collection => true)
|
45
44
|
assert_equal [2,3,4], @d.apply([1,2,3]) { |v| v+1 }
|
46
45
|
end
|
47
46
|
|
48
47
|
it "skips with collection and nil" do
|
49
|
-
@d = Representable::Definition.new(:song, :
|
48
|
+
@d = Representable::Definition.new(:song, :collection => true)
|
50
49
|
assert_equal nil, @d.apply(nil) { |v| v+1 }
|
51
50
|
end
|
52
51
|
end
|
53
52
|
|
54
|
-
describe ":
|
53
|
+
describe ":collection => true" do
|
55
54
|
before do
|
56
|
-
@def = Representable::Definition.new(:songs, :
|
55
|
+
@def = Representable::Definition.new(:songs, :collection => true, :tag => :song)
|
57
56
|
end
|
58
57
|
|
59
58
|
it "responds to #array?" do
|
@@ -64,18 +63,6 @@ class DefinitionTest < MiniTest::Spec
|
|
64
63
|
assert_equal :text, @def.sought_type
|
65
64
|
end
|
66
65
|
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
66
|
|
80
67
|
describe ":as => Item" do
|
81
68
|
before do
|
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
|
+
- 3
|
9
|
+
version: 0.9.3
|
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-11-
|
17
|
+
date: 2011-11-27 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|