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 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