representable 0.0.1.alpha1 → 0.0.1
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/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
|