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