objectify-xml 0.2.1 → 0.2.2

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/History.txt CHANGED
@@ -1,3 +1,8 @@
1
+ === 0.2.2 / 2009-03-02
2
+
3
+ * Improved type lookup to look in the object's scope before the general scope.
4
+ * Added full documentation
5
+
1
6
  === 0.2.1 / 2009-03-02
2
7
 
3
8
  * Minor update to support the latest version of Nokogiri (1.2.1)
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.1'
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
@@ -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)
@@ -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
- begin
115
- type = type_name.constantize
116
- rescue
117
- # Try to search the current object's namespace explicitly
118
- sections = self.name.split(/::/)
119
- while sections.length > 1
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 }="
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: objectify-xml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - pangloss