atom 0.1 → 0.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/lib/atom.rb +2 -0
- data/lib/xmlmapping.rb +128 -92
- metadata +2 -2
data/lib/atom.rb
CHANGED
@@ -133,6 +133,8 @@ module Atom
|
|
133
133
|
has_many :category, :name => 'category', :type => Category
|
134
134
|
has_one :content, :type => Content
|
135
135
|
has_one :source, :type => Source
|
136
|
+
|
137
|
+
has_many :extended_elements, :name => :any, :namespace => :any, :type => :raw
|
136
138
|
end
|
137
139
|
|
138
140
|
class Feed
|
data/lib/xmlmapping.rb
CHANGED
@@ -26,131 +26,167 @@ require 'rexml/document'
|
|
26
26
|
module XMLMapping
|
27
27
|
def self.included(mod)
|
28
28
|
mod.extend(ClassMethods)
|
29
|
-
|
30
|
-
mod.instance_variable_set("@
|
31
|
-
mod.instance_variable_set("@
|
29
|
+
|
30
|
+
mod.instance_variable_set("@raw_mappings", {})
|
31
|
+
mod.instance_variable_set("@mappings", { :element => {}, :attribute => {}, :text => {}, :namespace => nil})
|
32
32
|
end
|
33
33
|
|
34
|
+
|
34
35
|
def initialize(input)
|
35
|
-
|
36
|
-
root = REXML::Document.new(input).root
|
37
|
-
elsif input.respond_to?(:node_type) && input.node_type == :document
|
38
|
-
root = input.root
|
39
|
-
elsif input.respond_to?(:node_type) && input.node_type == :element
|
40
|
-
root = input
|
41
|
-
else
|
42
|
-
raise "Invalid input: #{input}"
|
43
|
-
end
|
36
|
+
root = parse(input)
|
44
37
|
|
45
|
-
|
46
|
-
|
47
|
-
elements = self.class.elements
|
48
|
-
text_attribute = self.class.text_attribute
|
38
|
+
mappings = self.class.mappings
|
39
|
+
raw_mappings = self.class.raw_mappings
|
49
40
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
41
|
+
# initialize :many attributes
|
42
|
+
raw_mappings.values.select { |mapping| mapping[:cardinality] == :many }.each { |m|
|
43
|
+
instance_variable_set("@#{m[:attribute]}", [])
|
44
|
+
}
|
54
45
|
|
55
|
-
|
56
|
-
|
57
|
-
|
46
|
+
root.each_element { |e|
|
47
|
+
process(e, mappings[:element])
|
48
|
+
}
|
49
|
+
|
50
|
+
root.attributes.each_attribute { |a|
|
51
|
+
process(a, mappings[:attribute])
|
52
|
+
}
|
58
53
|
|
54
|
+
mappings[:text].values.each { |mapping|
|
55
|
+
name = mapping[:attribute]
|
56
|
+
value = extract_value(root, mapping)
|
59
57
|
instance_variable_set("@#{name}", value)
|
58
|
+
}
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
private
|
64
|
+
def process(e, mappings)
|
65
|
+
mapping = find_mapping(mappings, e.namespace, e.name)
|
66
|
+
|
67
|
+
if !mapping.nil?
|
68
|
+
value = extract_value(e, mapping)
|
69
|
+
|
70
|
+
attribute = mapping[:attribute]
|
71
|
+
previous = instance_variable_get("@#{attribute}")
|
72
|
+
case mapping[:cardinality]
|
73
|
+
when :one
|
74
|
+
raise "Found more than one #{e.name}" if !previous.nil?
|
75
|
+
instance_variable_set("@#{attribute}", value)
|
76
|
+
when :many
|
77
|
+
previous << value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def find_mapping(mappings, namespace, name)
|
83
|
+
mappings.values_at([namespace, name], [namespace, :any], [:any, :any] ).compact.first
|
84
|
+
end
|
85
|
+
|
86
|
+
def extract_value(node, mapping)
|
87
|
+
if mapping.has_key? :type
|
88
|
+
type = mapping[:type]
|
89
|
+
if type == :raw
|
90
|
+
value = node
|
91
|
+
else
|
92
|
+
value = mapping[:type].new(node)
|
93
|
+
end
|
94
|
+
elsif node.node_type == :element
|
95
|
+
value = node.texts.map { |t| t.value }.to_s
|
96
|
+
elsif node.node_type == :attribute
|
97
|
+
value = node.value
|
60
98
|
else
|
61
|
-
|
62
|
-
elements.select { |k, v|
|
63
|
-
v[:cardinality] == :many
|
64
|
-
}.each { |k, v|
|
65
|
-
instance_variable_set("@#{v[:attribute]}", [])
|
66
|
-
}
|
67
|
-
|
68
|
-
root.each_element { |e|
|
69
|
-
if e.namespace == namespace && elements.has_key?(e.name.to_sym)
|
70
|
-
options = elements[e.name.to_sym]
|
71
|
-
|
72
|
-
if options.has_key? :type
|
73
|
-
value = options[:type].new(e)
|
74
|
-
else
|
75
|
-
value = e.text
|
76
|
-
end
|
77
|
-
|
78
|
-
if options.has_key? :transform
|
79
|
-
value = options[:transform].call(value)
|
80
|
-
end
|
81
|
-
|
82
|
-
attribute = options[:attribute]
|
83
|
-
|
84
|
-
existing = instance_variable_get("@#{attribute}")
|
85
|
-
case options[:cardinality]
|
86
|
-
when :one
|
87
|
-
raise "Found more than one #{e.name}" if !existing.nil?
|
88
|
-
instance_variable_set("@#{attribute}", value)
|
89
|
-
when :many
|
90
|
-
existing << value
|
91
|
-
end
|
92
|
-
end
|
93
|
-
}
|
99
|
+
raise "Unexpected node: #{node.inspect}"
|
94
100
|
end
|
95
101
|
|
96
|
-
|
97
|
-
|
98
|
-
|
102
|
+
if mapping.has_key? :transform
|
103
|
+
value = mapping[:transform].call(value)
|
104
|
+
end
|
99
105
|
|
100
|
-
|
101
|
-
|
102
|
-
value = options[:transform].call(value)
|
103
|
-
end
|
106
|
+
value
|
107
|
+
end
|
104
108
|
|
105
|
-
|
106
|
-
|
107
|
-
|
109
|
+
def parse(input)
|
110
|
+
if input.respond_to? :to_str
|
111
|
+
root = REXML::Document.new(input).root
|
112
|
+
elsif input.respond_to?(:node_type) && input.node_type == :document
|
113
|
+
root = input.root
|
114
|
+
elsif input.respond_to?(:node_type) && input.node_type == :element
|
115
|
+
root = input
|
116
|
+
else
|
117
|
+
raise "Invalid input: #{input}"
|
118
|
+
end
|
119
|
+
|
120
|
+
root
|
108
121
|
end
|
109
122
|
|
110
123
|
module ClassMethods
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
124
|
+
def raw_mappings
|
125
|
+
@raw_mappings || superclass.raw_mappings
|
126
|
+
end
|
127
|
+
|
128
|
+
def mappings
|
129
|
+
@mappings || superclass.mappings
|
130
|
+
end
|
115
131
|
|
116
132
|
def namespace(namespace)
|
117
|
-
|
133
|
+
initialize_vars
|
134
|
+
@mappings[:namespace] = namespace
|
118
135
|
end
|
119
136
|
|
120
137
|
def has_one(attribute, options = {})
|
121
|
-
attr attribute
|
122
138
|
options[:cardinality] = :one
|
123
|
-
|
124
|
-
|
125
|
-
name = attribute
|
126
|
-
if options.has_key? :name
|
127
|
-
name = options[:name].to_sym
|
128
|
-
end
|
129
|
-
|
130
|
-
@elements[name] = options
|
139
|
+
add(attribute, :element, options)
|
131
140
|
end
|
132
141
|
|
133
142
|
def has_many(attribute, options = {})
|
134
|
-
attr attribute
|
135
143
|
options[:cardinality] = :many
|
136
|
-
|
137
|
-
|
138
|
-
name = attribute
|
139
|
-
if options.has_key? :name
|
140
|
-
name = options[:name].to_sym
|
141
|
-
end
|
142
|
-
|
143
|
-
@elements[name] = options
|
144
|
+
add(attribute, :element, options)
|
144
145
|
end
|
145
146
|
|
146
147
|
def has_attribute(attribute, options = {})
|
147
|
-
|
148
|
-
@attributes[attribute] = options
|
148
|
+
add(attribute, :attribute, options)
|
149
149
|
end
|
150
150
|
|
151
151
|
def text(attribute, options = {})
|
152
|
+
options[:namespace] = :any
|
153
|
+
add(attribute, :text, options)
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
def add(attribute, xml_type, mapping)
|
152
158
|
attr attribute
|
153
|
-
|
159
|
+
|
160
|
+
initialize_vars
|
161
|
+
|
162
|
+
mapping[:namespace] ||= mappings[:namespace]
|
163
|
+
mapping[:cardinality] ||= :one
|
164
|
+
mapping[:name] ||= attribute.to_s
|
165
|
+
mapping[:attribute] = attribute
|
166
|
+
|
167
|
+
qualified_name = [mapping[:namespace], mapping[:name]]
|
168
|
+
mappings = @mappings[xml_type][qualified_name] = mapping
|
169
|
+
|
170
|
+
@raw_mappings[attribute] = mapping
|
171
|
+
end
|
172
|
+
|
173
|
+
def initialize_vars
|
174
|
+
@mappings ||= deep_clone(superclass.mappings)
|
175
|
+
@raw_mappings ||= deep_clone(superclass.raw_mappings)
|
176
|
+
end
|
177
|
+
|
178
|
+
def deep_clone(obj)
|
179
|
+
case obj
|
180
|
+
when Hash
|
181
|
+
obj.entries.inject({}) { |hash, entry|
|
182
|
+
hash[entry[0]] = deep_clone(entry[1])
|
183
|
+
hash
|
184
|
+
}
|
185
|
+
when Array
|
186
|
+
obj.map { |v| deep_clone[v] }
|
187
|
+
else
|
188
|
+
obj
|
189
|
+
end
|
154
190
|
end
|
155
191
|
end
|
156
192
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: atom
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "0.
|
7
|
-
date: 2006-04-
|
6
|
+
version: "0.2"
|
7
|
+
date: 2006-04-08 00:00:00 -07:00
|
8
8
|
summary: Ruby library for working with the Atom syndication format
|
9
9
|
require_paths:
|
10
10
|
- lib
|