objectify-xml 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/lib/objectify_xml.rb +14 -1
- data/lib/objectify_xml/atom.rb +32 -0
- data/lib/objectify_xml/document_parser.rb +15 -0
- data/lib/objectify_xml/dsl.rb +50 -13
- data/lib/objectify_xml/element_parser.rb +5 -0
- metadata +1 -1
data/History.txt
CHANGED
data/lib/objectify_xml.rb
CHANGED
@@ -7,8 +7,10 @@ require File.join(File.dirname(__FILE__), 'objectify_xml/document_parser')
|
|
7
7
|
require File.join(File.dirname(__FILE__), 'objectify_xml/element_parser')
|
8
8
|
|
9
9
|
module Objectify
|
10
|
+
# Base class inherited by the DocumentParser and ElementParser. Not intended
|
11
|
+
# for independent use.
|
10
12
|
class Xml
|
11
|
-
VERSION = '0.2.
|
13
|
+
VERSION = '0.2.2'
|
12
14
|
|
13
15
|
# When child nodes are created, they are given the name of the node
|
14
16
|
# that created them which is available here.
|
@@ -24,6 +26,10 @@ module Objectify
|
|
24
26
|
target.extend Dsl
|
25
27
|
end
|
26
28
|
|
29
|
+
# Returns the first Nokogiri::XML::Element if any in the document.
|
30
|
+
#
|
31
|
+
# The xml attribute may be a string, a File object or a Nokogiri::XML
|
32
|
+
# object.
|
27
33
|
def self.first_element(xml)
|
28
34
|
return if xml.nil?
|
29
35
|
if xml.is_a?(String) or xml.is_a?(File)
|
@@ -45,6 +51,8 @@ module Objectify
|
|
45
51
|
primary_xml_element(xml) if xml
|
46
52
|
end
|
47
53
|
|
54
|
+
# A short but informative indication of data type and which and how many
|
55
|
+
# elements are present.
|
48
56
|
def inspect
|
49
57
|
begin
|
50
58
|
attrs = (attributes || {}).map do |k,v|
|
@@ -62,6 +70,10 @@ module Objectify
|
|
62
70
|
end
|
63
71
|
end
|
64
72
|
|
73
|
+
# require 'pp'
|
74
|
+
#
|
75
|
+
# A more detailed recursive dump of the data in and associated to the
|
76
|
+
# object.
|
65
77
|
def pretty_print(q)
|
66
78
|
begin
|
67
79
|
q.object_group(self) do
|
@@ -82,6 +94,7 @@ module Objectify
|
|
82
94
|
|
83
95
|
protected
|
84
96
|
|
97
|
+
# Attempts to recognize and typecast xml element text values.
|
85
98
|
def xml_text_to_value(value)
|
86
99
|
value = value.strip
|
87
100
|
case value
|
data/lib/objectify_xml/atom.rb
CHANGED
@@ -1,25 +1,43 @@
|
|
1
1
|
module Objectify
|
2
|
+
# Given as an example of usage, but fully functional. Must be required
|
3
|
+
# separately: require 'objectify/atom'
|
2
4
|
module Atom
|
5
|
+
# attributes :rel, :type, :href
|
3
6
|
class Link < ElementParser
|
4
7
|
attributes :rel, :type, :href
|
5
8
|
end
|
6
9
|
|
7
10
|
|
11
|
+
# attributes :scheme, :term
|
8
12
|
class Category < ElementParser
|
9
13
|
attributes :scheme, :term
|
10
14
|
end
|
11
15
|
|
12
16
|
|
17
|
+
# attributes :type, :xml_lang, :xml_base, :src, :inner_html
|
13
18
|
class Content < ElementParser
|
14
19
|
attributes :type, :xml_lang, :xml_base, :src, :inner_html
|
15
20
|
end
|
16
21
|
|
17
22
|
|
23
|
+
# attributes :version, :uri, :inner_text
|
18
24
|
class Generator < ElementParser
|
19
25
|
attributes :version, :uri, :inner_text
|
20
26
|
end
|
21
27
|
|
22
28
|
|
29
|
+
# The root object of an Atom feed.
|
30
|
+
#
|
31
|
+
# attributes :id,
|
32
|
+
# :published,
|
33
|
+
# :updated,
|
34
|
+
# :title,
|
35
|
+
# :subtitle,
|
36
|
+
# :rights,
|
37
|
+
# :icon
|
38
|
+
# has_many :links, :Link, 'link'
|
39
|
+
# has_many :entries, :Entry, 'entry'
|
40
|
+
# has_one :generator, Generator, 'generator'
|
23
41
|
class Feed < DocumentParser
|
24
42
|
attributes :id,
|
25
43
|
:published,
|
@@ -34,6 +52,18 @@ module Objectify
|
|
34
52
|
end
|
35
53
|
|
36
54
|
|
55
|
+
# The feed has a collection of entries.
|
56
|
+
#
|
57
|
+
# attributes :id,
|
58
|
+
# :published,
|
59
|
+
# :updated,
|
60
|
+
# :title,
|
61
|
+
# :summary
|
62
|
+
# has_many :links, Link, 'link'
|
63
|
+
# has_one :category, Category, 'category'
|
64
|
+
# has_many :contents, Content, 'content'
|
65
|
+
# has_one :author, :Author, 'author'
|
66
|
+
# has_many :contributors, :Contributor, 'contributor'
|
37
67
|
class Entry < DocumentParser
|
38
68
|
attributes :id,
|
39
69
|
:published,
|
@@ -48,11 +78,13 @@ module Objectify
|
|
48
78
|
end
|
49
79
|
|
50
80
|
|
81
|
+
# attributes :name, :uri, :email
|
51
82
|
class Author < DocumentParser
|
52
83
|
attributes :name, :uri, :email
|
53
84
|
end
|
54
85
|
|
55
86
|
|
87
|
+
# attributes :name
|
56
88
|
class Contributor < DocumentParser
|
57
89
|
attributes :name
|
58
90
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module Objectify
|
2
|
+
#
|
2
3
|
class DocumentParser < Xml
|
3
4
|
# The entry point to the parser, normally called by initialize after the
|
4
5
|
# initialization is complete.
|
@@ -8,24 +9,32 @@ module Objectify
|
|
8
9
|
|
9
10
|
private
|
10
11
|
|
12
|
+
# Returns the given element's name as it would appear in the xml document,
|
13
|
+
# including namespace if present.
|
11
14
|
def qualified_name(x)
|
12
15
|
qn = x.name
|
13
16
|
qn = "#{ x.namespace }:#{ x.name }" if x.namespace
|
14
17
|
qn
|
15
18
|
end
|
16
19
|
|
20
|
+
# Returns the type of the element's association if one is defined, otherwise nil.
|
17
21
|
def attribute_type(x)
|
18
22
|
self.class.attribute_type qualified_name(x)
|
19
23
|
end
|
20
24
|
|
25
|
+
# Returns boolean to indicate if the children of the element should be
|
26
|
+
# treated as part of the current object's data.
|
21
27
|
def flatten?(x)
|
22
28
|
self.class.flatten?(qualified_name(x))
|
23
29
|
end
|
24
30
|
|
31
|
+
# Returns boolean to indicate if the given element is defined as being one
|
32
|
+
# member of a collection.
|
25
33
|
def collection?(x)
|
26
34
|
self.class.collection?(qualified_name(x))
|
27
35
|
end
|
28
36
|
|
37
|
+
# Returns boolean to indicate if the given element's namespace is supported.
|
29
38
|
def namespace?(x)
|
30
39
|
if x.namespace
|
31
40
|
self.class.namespace?(x.namespace)
|
@@ -34,10 +43,13 @@ module Objectify
|
|
34
43
|
end
|
35
44
|
end
|
36
45
|
|
46
|
+
# Returns the attribute name representing the given element. If the element
|
47
|
+
# is not defined, returns nil.
|
37
48
|
def attribute(x)
|
38
49
|
self.class.find_attribute(qualified_name(x), x.namespace, x.name)
|
39
50
|
end
|
40
51
|
|
52
|
+
# Parses the given xml element and all of its siblings.
|
41
53
|
def parse_xml(xml)
|
42
54
|
while xml
|
43
55
|
read_xml_element(xml)
|
@@ -45,6 +57,8 @@ module Objectify
|
|
45
57
|
end
|
46
58
|
end
|
47
59
|
|
60
|
+
# Processes the given element and stores the resultant value if it is
|
61
|
+
# a defined attribute or association.
|
48
62
|
def read_xml_element(x)
|
49
63
|
return if x.is_a? Nokogiri::XML::Text
|
50
64
|
return unless namespace?(x)
|
@@ -57,6 +71,7 @@ module Objectify
|
|
57
71
|
end
|
58
72
|
end
|
59
73
|
|
74
|
+
# Stores the value of the given attribute or association if it is defined.
|
60
75
|
def set_attribute(x)
|
61
76
|
if attr_name = attribute(x)
|
62
77
|
if collection?(x)
|
data/lib/objectify_xml/dsl.rb
CHANGED
@@ -5,6 +5,8 @@ module Objectify
|
|
5
5
|
target.init
|
6
6
|
end
|
7
7
|
|
8
|
+
# Initializes required metadata variables when the module is included in a
|
9
|
+
# class or a class is inherited.
|
8
10
|
def init
|
9
11
|
parent = ancestors[1]
|
10
12
|
unless /^Objectify::(Xml|ElementParser|DocumentParser)$/ =~ parent.name
|
@@ -24,21 +26,37 @@ module Objectify
|
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
29
|
+
# Define a typed association attribute. The type should be derived from
|
30
|
+
# either DocumentParser or ElementParser.
|
31
|
+
#
|
32
|
+
# If the type has not yet been defined at the point that the declaration
|
33
|
+
# is made, a String or Symbol may be used and will be looked up from
|
34
|
+
# the scope that the declaration is made in.
|
27
35
|
def has_one(name, type, qualified_name)
|
28
36
|
set_type(qualified_name, type)
|
29
37
|
attribute name, qualified_name
|
30
38
|
end
|
31
39
|
|
40
|
+
# Define an attribute as a collection of typed associations. The type
|
41
|
+
# should be derived from either DocumentParser or ElementParser.
|
42
|
+
#
|
43
|
+
# If the type has not yet been defined at the point that the declaration
|
44
|
+
# is made, a String or Symbol may be used and will be instantiated from
|
45
|
+
# the scope that the declaration is made in.
|
32
46
|
def has_many(name, type, qualified_name)
|
33
47
|
set_type(qualified_name, type)
|
34
48
|
attribute name, qualified_name, true
|
35
49
|
end
|
36
50
|
|
51
|
+
# Define a list of simple attributes.
|
37
52
|
def attributes(*names)
|
38
53
|
names.each { |n| attribute n }
|
39
54
|
@attributes + @qualified_attributes.keys
|
40
55
|
end
|
41
56
|
|
57
|
+
# Define a simple attribute or collection of simple attributes. This
|
58
|
+
# method can be used if the desired attribute name is different from the
|
59
|
+
# xml element name by specifying a qualified name.
|
42
60
|
def attribute(name, qualified_name = nil, collection = false)
|
43
61
|
name = name.to_s.underscore
|
44
62
|
@qualified_attributes[qualified_name] = name if qualified_name
|
@@ -55,6 +73,8 @@ module Objectify
|
|
55
73
|
name
|
56
74
|
end
|
57
75
|
|
76
|
+
# Used internally to find the name of an attribute based on the name of
|
77
|
+
# the xml element.
|
58
78
|
def find_attribute(qualified_name, namespace, name)
|
59
79
|
if qname = @qualified_attributes[qualified_name]
|
60
80
|
return qname
|
@@ -76,18 +96,23 @@ module Objectify
|
|
76
96
|
end
|
77
97
|
end
|
78
98
|
|
99
|
+
# Specify that an element should be recursed into while still associating
|
100
|
+
# its child elements with the current object.
|
79
101
|
def flatten(qualified_name)
|
80
102
|
@flatten << qualified_name.to_s
|
81
103
|
end
|
82
104
|
|
105
|
+
# Used internally to determine whether an element has been flattened.
|
83
106
|
def flatten?(qualified_name)
|
84
107
|
@flatten.include? qualified_name
|
85
108
|
end
|
86
109
|
|
110
|
+
# Used internally to determine whether a namespace is supported.
|
87
111
|
def namespace?(namespace)
|
88
112
|
@namespaces.keys.include? namespace
|
89
113
|
end
|
90
114
|
|
115
|
+
# Specify a list of supported namespaces.
|
91
116
|
def namespaces(*namespaces)
|
92
117
|
namespaces.each do |ns|
|
93
118
|
namespace ns
|
@@ -95,38 +120,45 @@ module Objectify
|
|
95
120
|
@namespaces
|
96
121
|
end
|
97
122
|
|
123
|
+
# Specify the url of the default namespace.
|
98
124
|
def default_namespace(url)
|
99
125
|
@namespaces[''] = url
|
100
126
|
end
|
101
127
|
|
128
|
+
# Specify a namespace with its url.
|
102
129
|
def namespace(name = nil, url = nil)
|
103
130
|
@namespaces[name.to_s] = url
|
104
131
|
end
|
105
132
|
|
133
|
+
# Return the url (if any) associated with a namespace.
|
106
134
|
def find_namespace(name = '')
|
107
135
|
@namespaces[name]
|
108
136
|
end
|
109
137
|
|
138
|
+
# Returns the attribute type associated to an element name if any.
|
139
|
+
#
|
140
|
+
# Searches up the defining object's namespace to the root namespace for
|
141
|
+
# the definition of the constant.
|
110
142
|
def attribute_type(qualified_name)
|
111
143
|
type = @types[qualified_name]
|
112
144
|
if type and not type.is_a? Class
|
113
145
|
type_name = type.to_s
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
sections
|
119
|
-
|
146
|
+
type = nil
|
147
|
+
# Try to search the current object's namespace explicitly
|
148
|
+
sections = self.name.split(/::/)
|
149
|
+
while sections.length > 1
|
150
|
+
sections.pop
|
151
|
+
begin
|
152
|
+
sections.push(type_name)
|
153
|
+
type = sections.join('::').constantize
|
154
|
+
break
|
155
|
+
rescue
|
120
156
|
sections.pop
|
121
|
-
begin
|
122
|
-
sections.push(type_name)
|
123
|
-
type = sections.join('::').constantize
|
124
|
-
break
|
125
|
-
rescue
|
126
|
-
sections.pop
|
127
|
-
end
|
128
157
|
end
|
129
158
|
end
|
159
|
+
if type.nil?
|
160
|
+
type = type_name.constantize rescue nil
|
161
|
+
end
|
130
162
|
if type.nil?
|
131
163
|
raise "Unable to instantiate the constant '#{ type_name }'."
|
132
164
|
end
|
@@ -135,14 +167,19 @@ module Objectify
|
|
135
167
|
type
|
136
168
|
end
|
137
169
|
|
170
|
+
# Sets the associated type of an attribute. Generally used only internally
|
171
|
+
# via has_one or has_many.
|
138
172
|
def set_type(qualified_name, type)
|
139
173
|
@types[qualified_name] = type
|
140
174
|
end
|
141
175
|
|
176
|
+
# Used internally to determine whether an element is represented by a
|
177
|
+
# collection.
|
142
178
|
def collection?(qualified_name)
|
143
179
|
@collections.include?(qualified_name)
|
144
180
|
end
|
145
181
|
|
182
|
+
# Return the object's metadata for testing and debugging purposes.
|
146
183
|
def metadata
|
147
184
|
{ :attributes => @attributes,
|
148
185
|
:qualified_attributes => @qualified_attributes,
|
@@ -1,5 +1,10 @@
|
|
1
1
|
module Objectify
|
2
2
|
class ElementParser < Xml
|
3
|
+
# Stores the xml element's attribute values, inner_text and inner_html if
|
4
|
+
# they are defined.
|
5
|
+
#
|
6
|
+
# Attributes can be defined either with the attribute dsl method or by
|
7
|
+
# defining a name= method in the object.
|
3
8
|
def primary_xml_element(xml)
|
4
9
|
xml.attributes.keys.each do |name|
|
5
10
|
method = "#{ name }="
|