atom 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|