representable 0.0.1.alpha1 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +58 -153
- data/lib/representable.rb +18 -42
- data/lib/representable/bindings/json_bindings.rb +69 -0
- data/lib/representable/bindings/xml_bindings.rb +152 -0
- data/lib/representable/definition.rb +1 -12
- data/lib/representable/json.rb +66 -0
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +32 -38
- data/representable.gemspec +3 -2
- data/test/bindings_test.rb +110 -0
- data/test/json_test.rb +130 -0
- data/test/{roxml_test.rb → representable_test.rb} +28 -9
- data/test/test_helper.rb +2 -0
- data/test/xml_test.rb +192 -0
- metadata +32 -105
- data/History.txt +0 -354
- data/TODO +0 -37
- data/VERSION +0 -1
- data/examples/amazon.rb +0 -35
- data/examples/current_weather.rb +0 -27
- data/examples/dashed_elements.rb +0 -20
- data/examples/library.rb +0 -40
- data/examples/posts.rb +0 -27
- data/examples/rails.rb +0 -70
- data/examples/twitter.rb +0 -37
- data/examples/xml/active_record.xml +0 -70
- data/examples/xml/amazon.xml +0 -133
- data/examples/xml/current_weather.xml +0 -89
- data/examples/xml/dashed_elements.xml +0 -52
- data/examples/xml/posts.xml +0 -23
- data/examples/xml/twitter.xml +0 -422
- data/lib/representable/references.rb +0 -153
- data/spec/definition_spec.rb +0 -495
- data/spec/examples/active_record_spec.rb +0 -41
- data/spec/examples/amazon_spec.rb +0 -54
- data/spec/examples/current_weather_spec.rb +0 -37
- data/spec/examples/dashed_elements_spec.rb +0 -20
- data/spec/examples/library_spec.rb +0 -46
- data/spec/examples/post_spec.rb +0 -24
- data/spec/examples/twitter_spec.rb +0 -32
- data/spec/roxml_integration_test.rb +0 -289
- data/spec/roxml_spec.rb +0 -372
- data/spec/shared_specs.rb +0 -15
- data/spec/spec_helper.rb +0 -5
- data/spec/support/libxml.rb +0 -3
- data/spec/support/nokogiri.rb +0 -3
- data/spec/xml/array_spec.rb +0 -36
- data/spec/xml/attributes_spec.rb +0 -71
- data/spec/xml/encoding_spec.rb +0 -53
- data/spec/xml/namespace_spec.rb +0 -270
- data/spec/xml/namespaces_spec.rb +0 -67
- data/spec/xml/object_spec.rb +0 -82
- data/spec/xml/parser_spec.rb +0 -21
- data/spec/xml/text_spec.rb +0 -71
- data/test/fixtures/book_malformed.xml +0 -5
- data/test/fixtures/book_pair.xml +0 -8
- data/test/fixtures/book_text_with_attribute.xml +0 -5
- data/test/fixtures/book_valid.xml +0 -5
- data/test/fixtures/book_with_authors.xml +0 -7
- data/test/fixtures/book_with_contributions.xml +0 -9
- data/test/fixtures/book_with_contributors.xml +0 -7
- data/test/fixtures/book_with_contributors_attrs.xml +0 -7
- data/test/fixtures/book_with_default_namespace.xml +0 -9
- data/test/fixtures/book_with_depth.xml +0 -6
- data/test/fixtures/book_with_octal_pages.xml +0 -4
- data/test/fixtures/book_with_publisher.xml +0 -7
- data/test/fixtures/book_with_wrapped_attr.xml +0 -3
- data/test/fixtures/dictionary_of_attr_name_clashes.xml +0 -8
- data/test/fixtures/dictionary_of_attrs.xml +0 -6
- data/test/fixtures/dictionary_of_guarded_names.xml +0 -6
- data/test/fixtures/dictionary_of_mixeds.xml +0 -4
- data/test/fixtures/dictionary_of_name_clashes.xml +0 -10
- data/test/fixtures/dictionary_of_names.xml +0 -4
- data/test/fixtures/dictionary_of_texts.xml +0 -10
- data/test/fixtures/library.xml +0 -30
- data/test/fixtures/library_uppercase.xml +0 -30
- data/test/fixtures/muffins.xml +0 -3
- data/test/fixtures/nameless_ageless_youth.xml +0 -2
- data/test/fixtures/node_with_attr_name_conflicts.xml +0 -1
- data/test/fixtures/node_with_name_conflicts.xml +0 -4
- data/test/fixtures/numerology.xml +0 -4
- data/test/fixtures/person.xml +0 -1
- data/test/fixtures/person_with_guarded_mothers.xml +0 -13
- data/test/fixtures/person_with_mothers.xml +0 -10
- data/test/mocks/dictionaries.rb +0 -57
- data/test/mocks/mocks.rb +0 -279
- data/test/support/fixtures.rb +0 -11
- data/test/unit/definition_test.rb +0 -235
- data/test/unit/deprecations_test.rb +0 -24
- data/test/unit/to_xml_test.rb +0 -81
- data/test/unit/xml_attribute_test.rb +0 -39
- data/test/unit/xml_block_test.rb +0 -81
- data/test/unit/xml_bool_test.rb +0 -122
- data/test/unit/xml_convention_test.rb +0 -150
- data/test/unit/xml_hash_test.rb +0 -115
- data/test/unit/xml_initialize_test.rb +0 -49
- data/test/unit/xml_name_test.rb +0 -141
- data/test/unit/xml_namespace_test.rb +0 -31
- data/test/unit/xml_object_test.rb +0 -206
- data/test/unit/xml_required_test.rb +0 -94
- data/test/unit/xml_text_test.rb +0 -71
- data/website/index.html +0 -98
data/README.rdoc
CHANGED
@@ -1,186 +1,91 @@
|
|
1
|
-
|
1
|
+
= Representable
|
2
2
|
|
3
|
-
|
3
|
+
<em>Maps representation documents from and to Ruby objects.</em>
|
4
4
|
|
5
|
-
http://rdoc.info/projects/Empact/roxml
|
6
|
-
http://empact.github.com/roxml/
|
7
|
-
http://rubyforge.org/projects/roxml/
|
8
5
|
|
9
|
-
|
6
|
+
== Introduction
|
10
7
|
|
11
|
-
|
8
|
+
_Representable_ maps fragments in documents to attributes in Ruby objects and back. It allows parsing representations and gives an object-oriented interface to the document. But that's only half of it! Representable can also render documents from an object instance.
|
12
9
|
|
13
|
-
|
10
|
+
This is especially helpful when implementing REST services and clients and keeps your representation knowledge in one place.
|
14
11
|
|
15
|
-
|
12
|
+
== Example
|
16
13
|
|
17
|
-
|
14
|
+
When writing a REST service you'd have to parse and extract data from an incoming representation document manually. Given the following XML document which represents an order.
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
<order>
|
17
|
+
<id>1</id>
|
18
|
+
<item>
|
19
|
+
<name>Chocolate Cookie</name>
|
20
|
+
</item>
|
21
|
+
<item>
|
22
|
+
<name>Vanilla Muffin</name>
|
23
|
+
</item>
|
24
|
+
</order>
|
22
25
|
|
23
|
-
|
24
|
-
include ROXML
|
26
|
+
Now, parsing manually starts simple but gets complex soon. In the end, you have a nested hash data structure. Try this with Representable.
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
28
|
+
class Order
|
29
|
+
include Representable::XML
|
30
|
+
|
31
|
+
representable_property :id
|
32
|
+
representable_collection :items, :tag => :item, :as => Item
|
30
33
|
end
|
31
|
-
|
32
|
-
class
|
33
|
-
include
|
34
|
-
|
35
|
-
|
36
|
-
xml_accessor :books, :as => [Book] # by default roxml searches for books for in <book> child nodes, then, if none are present, in ./books/book children
|
37
|
-
end
|
38
|
-
|
39
|
-
To create a library and put a number of books in it we could run the following code:
|
40
|
-
|
41
|
-
book = Book.new
|
42
|
-
book.isbn = "0201710897"
|
43
|
-
book.title = "The PickAxe"
|
44
|
-
book.description = "Best Ruby book out there!"
|
45
|
-
book.author = "David Thomas, Andrew Hunt, Dave Thomas"
|
46
|
-
|
47
|
-
lib = Library.new
|
48
|
-
lib.name = "Favorite Books"
|
49
|
-
lib.books = [book]
|
50
|
-
|
51
|
-
To save this information to an XML file:
|
52
|
-
|
53
|
-
doc = Nokogiri::XML::Document.new
|
54
|
-
doc.root = lib.to_xml
|
55
|
-
open("library.xml", 'w') do |file|
|
56
|
-
file << doc.serialize
|
57
|
-
end
|
58
|
-
|
59
|
-
or
|
60
|
-
|
61
|
-
doc = LibXML::XML::Document.new
|
62
|
-
doc.root = lib.to_xml
|
63
|
-
doc.save("library.xml")
|
64
|
-
|
65
|
-
To later populate the library object from the XML file:
|
66
|
-
|
67
|
-
lib = Library.from_xml(File.read("library.xml"))
|
68
|
-
|
69
|
-
Similarly, to do a one-to-one mapping between XML objects, such as book and publisher,
|
70
|
-
you would add a reference to another ROXML class. For example:
|
71
|
-
|
72
|
-
<book isbn="0974514055">
|
73
|
-
<title>Programming Ruby - 2nd Edition</title>
|
74
|
-
<description>Second edition of the great book.</description>
|
75
|
-
<publisher>
|
76
|
-
<name>Pragmatic Bookshelf</name>
|
77
|
-
</publisher>
|
78
|
-
</book>
|
79
|
-
|
80
|
-
can be mapped using the following code:
|
81
|
-
|
82
|
-
class Publisher
|
83
|
-
include ROXML
|
84
|
-
|
85
|
-
xml_accessor :name
|
86
|
-
|
87
|
-
# other important functionality
|
34
|
+
|
35
|
+
class Item
|
36
|
+
include Representable::XML
|
37
|
+
|
38
|
+
representable_property :name
|
88
39
|
end
|
40
|
+
|
41
|
+
== Consuming Representations
|
89
42
|
|
90
|
-
|
91
|
-
include ROXML
|
43
|
+
Representable gives you easy access for consuming incoming representation documents. It maps the appropriate content to objects' attributes.
|
92
44
|
|
93
|
-
|
94
|
-
|
45
|
+
@order = Order.from_xml(xml)
|
46
|
+
@order.id # => "1"
|
47
|
+
@order.items # => [<item>, <item>]
|
48
|
+
@order.items.first.name # => "Chocolate Cookie"
|
95
49
|
|
96
|
-
# or, alternatively, if no class is needed to hang functionality on:
|
97
|
-
# xml_reader :publisher, :from => 'name', :in => 'publisher'
|
98
|
-
end
|
99
|
-
|
100
|
-
Note: In the above example, _xml_name_ annotation tells ROXML to set the element
|
101
|
-
name to "book" for mapping to XML. The default is XML element name is the class name in lowercase; "bookwithpublisher"
|
102
|
-
in this case.
|
103
|
-
|
104
|
-
=== Namespace Support
|
105
|
-
|
106
|
-
Namespaced nodes are supported via the xml_namespace and xml_namespaces declarations and the :from and :namespace attr options. See spec/xml/namespace_spec.rb for usage.
|
107
|
-
|
108
|
-
Note that ROXML does not currently support outputting namespaced nodes. This is planned for a future version.
|
109
50
|
|
110
|
-
==
|
51
|
+
== Extendability
|
111
52
|
|
112
|
-
|
113
|
-
In such a case, you can extend any object with a block to manipulate it's value at parse time. For example:
|
53
|
+
The cool thing with object-oriented representations is: you can treat them just like any other object in Ruby and extend them dynamically - which is impossible with plain hashes.
|
114
54
|
|
115
|
-
class
|
116
|
-
|
117
|
-
|
118
|
-
|
55
|
+
class Order
|
56
|
+
# ...
|
57
|
+
|
58
|
+
def sort_items
|
59
|
+
items.sort! do |a,b|
|
60
|
+
a.name <=> b.name
|
61
|
+
end
|
62
|
+
end
|
119
63
|
end
|
120
64
|
|
121
|
-
The result of the block above is stored, rather than the actual value parsed from the document.
|
122
65
|
|
123
|
-
==
|
66
|
+
== Generating Representations
|
124
67
|
|
125
|
-
|
126
|
-
in file, string, or path form, and with optional initialization_args following.
|
68
|
+
Usually you have two places that share knowledge about a representation and its syntax and semantics: the parser <em>and</em> the renderer. With Representable, this is kept in one place.
|
127
69
|
|
128
|
-
|
129
|
-
|
130
|
-
|
70
|
+
@order.to_xml # => "<order><id>1</id>...</order>"
|
71
|
+
|
72
|
+
You can also render a representation from a newly created object.
|
131
73
|
|
132
|
-
|
133
|
-
requires more than one variable in concert.
|
74
|
+
Order.new(:id => 2, :items => [candy]).to_xml
|
134
75
|
|
135
|
-
E.g.:
|
136
76
|
|
137
|
-
|
138
|
-
include ROXML
|
77
|
+
Representable makes working with representations extremely easy and testable.
|
139
78
|
|
140
|
-
|
141
|
-
xml_reader :value, :from => :content
|
79
|
+
== More
|
142
80
|
|
143
|
-
|
144
|
-
to_metric
|
145
|
-
end
|
81
|
+
Representable was written with REST representations in mind. However, it is a generic module for working with documents. If you do consider using it for a REST project, check out the {Roar framework}[http://github.com/apotonick/roar], which comes with representers, built-in hypermedia support and more. It internally uses Representable and streamlines the process for building hypermedia-driven REST applications.
|
146
82
|
|
147
|
-
|
148
|
-
def after_parse
|
149
|
-
# xml attributes of self are already valid
|
150
|
-
to_metric
|
151
|
-
end
|
83
|
+
Representable comes with parser and renderer for XML and JSON and can easily be extended (Warning: internal API still changing).
|
152
84
|
|
153
|
-
def to_metric
|
154
|
-
# translate units & value into metric, for example
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
One important use of this approach is to make ROXML object which may or may not include an xml backing,
|
159
|
-
which may be used via _new_ construction as well as _from_xml_ construction.
|
160
|
-
|
161
|
-
== Selecting a parser
|
162
|
-
|
163
|
-
By default, ROXML will use Nokogiri if it is available, followed by LibXML. If you'd like to
|
164
|
-
explicitly require one or the other, you may do the following:
|
165
|
-
|
166
|
-
module ROXML
|
167
|
-
XML_PARSER = 'nokogiri' # or 'libxml'
|
168
|
-
end
|
169
|
-
require 'roxml'
|
170
|
-
|
171
|
-
For more information on available annotations, see ROXML::ClassMethods::Declarations
|
172
|
-
|
173
|
-
== Note on Patches/Pull Requests
|
174
|
-
|
175
|
-
* Fork the project.
|
176
|
-
* Make your feature addition or bug fix.
|
177
|
-
* Add specs for it. This is important so I don't break it in a
|
178
|
-
future version unintentionally.
|
179
|
-
* Commit, do not mess with rakefile, version, or history.
|
180
|
-
(if you want to have your own version, that is fine but
|
181
|
-
bump version in a commit by itself I can ignore when I pull)
|
182
|
-
* Send me a pull request. Bonus points for topic branches.
|
183
85
|
|
184
86
|
== Copyright
|
185
87
|
|
186
|
-
|
88
|
+
Representable is a simplified fork of the ROXML gem. Big thanks to Ben Woosley for his work.
|
89
|
+
|
90
|
+
Copyright (c) 2011 Nick Sutterer <apotonick@gmail.com>
|
91
|
+
Copyright (c) 2004-2009 Ben Woosley, Zak Mandhro and Anders Engstrom.
|
data/lib/representable.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'active_support'
|
2
2
|
require 'active_support/core_ext/module/delegation'
|
3
|
-
require 'active_support/core_ext/array/extract_options'
|
4
|
-
require 'active_support/core_ext/string/starts_ends_with'
|
5
3
|
require 'active_support/core_ext/string/inflections.rb'
|
6
4
|
require 'active_support/core_ext/hash/reverse_merge.rb'
|
7
5
|
|
@@ -10,32 +8,19 @@ require 'hooks/inheritable_attribute'
|
|
10
8
|
|
11
9
|
require 'representable/definition'
|
12
10
|
require 'representable/nokogiri_extensions'
|
13
|
-
require 'representable/references'
|
14
11
|
|
15
|
-
require 'representable/xml' # TODO: do that dynamically.
|
12
|
+
#require 'representable/xml' # TODO: do that dynamically.
|
16
13
|
|
17
14
|
module Representable
|
18
|
-
VERSION = '3.1.5'
|
19
|
-
|
20
|
-
|
21
15
|
def self.included(base)
|
22
16
|
base.class_eval do
|
23
17
|
extend ClassMethods::Accessors, ClassMethods::Declarations
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
attr_accessor :roxml_references
|
28
18
|
|
29
19
|
extend Hooks::InheritableAttribute
|
30
20
|
inheritable_attr :representable_attrs
|
31
21
|
self.representable_attrs = []
|
32
22
|
|
33
23
|
inheritable_attr :explicit_representation_name # FIXME: move to Accessors.
|
34
|
-
|
35
|
-
|
36
|
-
extend Xml::Declarations # DISCUSS: do that dynamically?
|
37
|
-
extend Xml::ClassMethods # DISCUSS: do that dynamically?
|
38
|
-
include Xml::InstanceMethods # DISCUSS: do that dynamically?
|
39
24
|
end
|
40
25
|
end
|
41
26
|
|
@@ -204,39 +189,30 @@ module Representable
|
|
204
189
|
# [:cdata] true for values which should be input from or output as cdata elements
|
205
190
|
# [:to_xml] this proc is applied to the attributes value outputting the instance via #to_xml
|
206
191
|
#
|
207
|
-
def
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
representable_attrs << attr
|
212
|
-
end
|
213
|
-
end
|
192
|
+
def representable_property(*args) # TODO: make it accept 1-n props.
|
193
|
+
attr = representable_attr(*args)
|
194
|
+
add_reader(attr)
|
195
|
+
attr_writer(attr.accessor)
|
214
196
|
end
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
197
|
+
|
198
|
+
def representable_collection(name, options={})
|
199
|
+
options[:as] = [options[:as]].compact
|
200
|
+
representable_property(name, options)
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
def representable_attr(name, options={})
|
205
|
+
definition_class.new(name, options).tap do |attr|
|
206
|
+
representable_attrs << attr
|
224
207
|
end
|
225
208
|
end
|
226
|
-
|
227
|
-
|
228
|
-
#
|
229
|
-
# Note that while xml_accessor does create a setter for this attribute,
|
230
|
-
# you can use the :frozen option to prevent its value from being
|
231
|
-
# modified indirectly via methods.
|
232
|
-
def representable_accessor(*syms, &block)
|
209
|
+
|
210
|
+
def representable_reader(*syms, &block)
|
233
211
|
representable_attr(*syms, &block).each do |attr|
|
234
212
|
add_reader(attr)
|
235
|
-
attr_writer(attr.accessor)
|
236
213
|
end
|
237
214
|
end
|
238
|
-
|
239
|
-
private
|
215
|
+
|
240
216
|
def add_reader(attr)
|
241
217
|
define_method(attr.accessor) do
|
242
218
|
instance_variable_get(attr.instance_variable_name)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Representable
|
2
|
+
module JSON
|
3
|
+
class Binding
|
4
|
+
attr_reader :definition
|
5
|
+
delegate :required?, :array?, :accessor, :wrapper, :name, :to => :definition
|
6
|
+
|
7
|
+
def initialize(definition)
|
8
|
+
@definition = definition
|
9
|
+
end
|
10
|
+
|
11
|
+
def value_in(hash)
|
12
|
+
value_from_hash(hash) or default
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def default
|
17
|
+
""
|
18
|
+
end
|
19
|
+
|
20
|
+
def collect_for(hash)
|
21
|
+
nodes = hash[name.to_s] or return
|
22
|
+
nodes = [nodes] unless nodes.is_a?(Array)
|
23
|
+
|
24
|
+
vals = nodes.collect { |node| yield node }
|
25
|
+
|
26
|
+
array? ? vals : vals.first
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Represents plain key-value.
|
31
|
+
class TextBinding < Binding
|
32
|
+
def update_json(hash, value)
|
33
|
+
hash[name] = value
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def value_from_hash(hash)
|
38
|
+
collect_for(hash) do |value|
|
39
|
+
value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Represents a tag with object binding.
|
45
|
+
class ObjectBinding < Binding
|
46
|
+
delegate :sought_type, :to => :definition
|
47
|
+
|
48
|
+
def update_json(hash, value)
|
49
|
+
if array?
|
50
|
+
hash.merge! ({accessor => value.collect {|v| v.to_json(:wrap => false)}}) # hier name=> wech.
|
51
|
+
else
|
52
|
+
hash.merge! value.to_json
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def default
|
58
|
+
[]
|
59
|
+
end
|
60
|
+
|
61
|
+
def value_from_hash(xml)
|
62
|
+
collect_for(xml) do |node|
|
63
|
+
sought_type.from_json(node, :wrap => false) # hier name=> wech.
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module Representable
|
2
|
+
module XML
|
3
|
+
class Binding
|
4
|
+
attr_reader :definition
|
5
|
+
delegate :required?, :array?, :accessor, :wrapper, :name, :to => :definition
|
6
|
+
|
7
|
+
def initialize(definition)
|
8
|
+
@definition = definition
|
9
|
+
end
|
10
|
+
|
11
|
+
def value_in(xml)
|
12
|
+
xml = Nokogiri::XML::Node.from(xml) or return default
|
13
|
+
|
14
|
+
value_from_node(xml) or default
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def default
|
19
|
+
""
|
20
|
+
end
|
21
|
+
|
22
|
+
def xpath
|
23
|
+
name
|
24
|
+
end
|
25
|
+
|
26
|
+
def wrap(xml, opts = {:always_create => false})
|
27
|
+
wrap_with = @auto_vals ? auto_wrapper : wrapper
|
28
|
+
|
29
|
+
return xml if !wrap_with || xml.name == wrap_with
|
30
|
+
if !opts[:always_create] && (child = xml.children.find {|c| c.name == wrap_with })
|
31
|
+
return child
|
32
|
+
end
|
33
|
+
xml.add_node(wrap_with.to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
def collect_for(xml)
|
37
|
+
nodes = xml.search("./#{xpath}")
|
38
|
+
vals = nodes.collect { |node| yield node }
|
39
|
+
|
40
|
+
array? ? vals : vals.first
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# Represents a tag attribute.
|
46
|
+
class AttributeBinding < Binding
|
47
|
+
def update_xml(xml, values)
|
48
|
+
wrap(xml).tap do |xml|
|
49
|
+
xml[name] = values.to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def value_from_node(xml)
|
55
|
+
xml[name]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# Represents text content in a tag. # FIXME: is this tested???
|
61
|
+
class TextBinding < Binding
|
62
|
+
delegate :cdata?, :content?, :name?, :to => :definition
|
63
|
+
|
64
|
+
# Updates the text in the given _xml_ block to
|
65
|
+
# the _value_ provided.
|
66
|
+
def update_xml(xml, value)
|
67
|
+
wrap(xml).tap do |xml|
|
68
|
+
if content?
|
69
|
+
add(xml, value)
|
70
|
+
elsif name?
|
71
|
+
xml.name = value
|
72
|
+
elsif array?
|
73
|
+
value.each do |v|
|
74
|
+
add(xml.add_node(name), v)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
add(xml.add_node(name), value)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def value_from_node(xml)
|
84
|
+
collect_for(xml) do |node|
|
85
|
+
node.content
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def add(dest, value)
|
90
|
+
if cdata?
|
91
|
+
dest.add_child(Nokogiri::XML::CDATA.new(dest.document, content))
|
92
|
+
else
|
93
|
+
dest.content = value.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
class NamespaceBinding < Binding
|
100
|
+
private
|
101
|
+
def value_from_node(xml)
|
102
|
+
xml.namespace.prefix
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Represents a tag with object binding.
|
107
|
+
class ObjectBinding < Binding
|
108
|
+
delegate :sought_type, :to => :definition
|
109
|
+
|
110
|
+
# Adds the ref's markup to +xml+.
|
111
|
+
def update_xml(xml, value)
|
112
|
+
wrap(xml).tap do |xml|
|
113
|
+
if array?
|
114
|
+
update_xml_for_collection(xml, value)
|
115
|
+
else
|
116
|
+
update_xml_for_entity(xml, value)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
def default
|
123
|
+
[]
|
124
|
+
end
|
125
|
+
|
126
|
+
def serialize(object)
|
127
|
+
object.to_xml
|
128
|
+
end
|
129
|
+
|
130
|
+
def deserialize(node_class, xml)
|
131
|
+
node_class.from_xml(xml)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Deserializes the ref's element from +xml+.
|
135
|
+
def value_from_node(xml)
|
136
|
+
collect_for(xml) do |node|
|
137
|
+
deserialize(sought_type, node)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def update_xml_for_collection(xml, collection)
|
142
|
+
collection.each do |item|
|
143
|
+
update_xml_for_entity(xml, item)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def update_xml_for_entity(xml, entity)
|
148
|
+
xml.add_child(serialize(entity))
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|