vpim 0.619 → 0.658

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,206 @@
1
+ # Copyright (c) 2008 The Kaphan Foundation
2
+ #
3
+ # Possession of a copy of this file grants no permission or license
4
+ # to use, modify, or create derivate works.
5
+ # Please contact info@peerworks.org for further information.
6
+ #
7
+
8
+ require 'atom'
9
+ require 'atom/xml/parser'
10
+ require 'atom/version'
11
+ require 'xml/libxml'
12
+ require 'uri'
13
+ require 'net/http'
14
+
15
+ module Atom
16
+ module Pub
17
+ class NotSupported < StandardError; end
18
+ class ProtocolError < StandardError
19
+ attr_reader :response
20
+ def initialize(response)
21
+ @response = response
22
+ end
23
+ end
24
+
25
+ class Service
26
+ include Atom::Xml::Parseable
27
+ namespace Atom::Pub::NAMESPACE
28
+ elements :workspaces
29
+ loadable! do |reader, message, severity, base, line|
30
+ if severity == XML::Reader::SEVERITY_ERROR
31
+ raise ParseError, "#{message} at #{line}"
32
+ end
33
+ end
34
+
35
+ def initialize(xml = nil)
36
+ @workspaces = []
37
+
38
+ if xml
39
+ begin
40
+ if next_node_is?(xml, 'service', Atom::Pub::NAMESPACE)
41
+ xml.read
42
+ parse(xml)
43
+ else
44
+ raise ArgumentError, "XML document was missing atom:service"
45
+ end
46
+ ensure
47
+ xml.close
48
+ end
49
+ end
50
+
51
+ yield(self) if block_given?
52
+ end
53
+ end
54
+
55
+ class Categories < DelegateClass(Array)
56
+ include Atom::Xml::Parseable
57
+ elements :categories, :class => Atom::Category
58
+
59
+ def initialize(o)
60
+ super([])
61
+ o.read
62
+ parse(o)
63
+ end
64
+
65
+ remove_method :categories
66
+ def categories; self; end
67
+ end
68
+
69
+ class Workspace
70
+ include Atom::Xml::Parseable
71
+ element :title, :class => Content, :namespace => Atom::NAMESPACE
72
+ elements :collections
73
+
74
+ def initialize(o = nil)
75
+ @collections = []
76
+
77
+ case o
78
+ when XML::Reader
79
+ o.read
80
+ parse(o)
81
+ when Hash
82
+ o.each do |k, v|
83
+ self.send("#{k}=".to_sym, v)
84
+ end
85
+ end
86
+
87
+ yield(self) if block_given?
88
+ end
89
+ end
90
+
91
+ class Collection
92
+ include Atom::Xml::Parseable
93
+ attribute :href
94
+ element :title, :class => Content, :namespace => Atom::NAMESPACE
95
+ element :categories, :class => Categories
96
+ elements :accepts, :content_only => true
97
+
98
+ def initialize(o = nil)
99
+ @accepts = []
100
+ case o
101
+ when XML::Reader
102
+ # do it once to get the attributes
103
+ parse(o, :once => true)
104
+ # now step into the element and the sub tree
105
+ o.read
106
+ parse(o)
107
+ when Hash
108
+ o.each do |k, v|
109
+ self.send("#{k}=", v)
110
+ end
111
+ end
112
+
113
+ yield(self) if block_given?
114
+ end
115
+
116
+ def feed
117
+ if href
118
+ Atom::Feed.load_feed(URI.parse(href))
119
+ end
120
+ end
121
+
122
+ def publish(entry)
123
+ uri = URI.parse(href)
124
+ response = nil
125
+ Net::HTTP.start(uri.host, uri.port) do |http|
126
+ response = http.post(uri.path, entry.to_xml.to_s, headers)
127
+ end
128
+
129
+ case response
130
+ when Net::HTTPCreated
131
+ published = begin
132
+ Atom::Entry.load_entry(response.body)
133
+ rescue Atom::ParseError
134
+ entry
135
+ end
136
+
137
+ if response['Location']
138
+ if published.edit_link
139
+ published.edit_link.href = response['Location']
140
+ else
141
+ published.links << Atom::Link.new(:rel => 'edit', :href => response['Location'])
142
+ end
143
+ end
144
+
145
+ published
146
+ else
147
+ raise Atom::Pub::ProtocolError, response
148
+ end
149
+ end
150
+
151
+ private
152
+ def headers
153
+ {'Accept' => 'application/atom+xml',
154
+ 'Content-Type' => 'application/atom+xml;type=entry',
155
+ 'User-Agent' => "rAtom #{Atom::VERSION::STRING}"
156
+ }
157
+ end
158
+ end
159
+ end
160
+
161
+ class Entry
162
+ def save!
163
+ if edit = edit_link
164
+ uri = URI.parse(edit.href)
165
+ response = nil
166
+ Net::HTTP.start(uri.host, uri.port) do |http|
167
+ response = http.put(uri.path, self.to_xml, headers)
168
+ end
169
+
170
+ case response
171
+ when Net::HTTPSuccess
172
+ else
173
+ raise Atom::Pub::ProtocolError, response
174
+ end
175
+ else
176
+ raise Atom::Pub::NotSupported, "Entry does not have an edit link"
177
+ end
178
+ end
179
+
180
+ def destroy!
181
+ if edit = edit_link
182
+ uri = URI.parse(edit.href)
183
+ response = nil
184
+ Net::HTTP.start(uri.host, uri.port) do |http|
185
+ response = http.delete(uri.path, {'Accept' => 'application/atom+xml', 'User-Agent' => "rAtom #{Atom::VERSION::STRING}"})
186
+ end
187
+
188
+ case response
189
+ when Net::HTTPSuccess
190
+ else
191
+ raise Atom::Pub::ProtocolError, response
192
+ end
193
+ else
194
+ raise Atom::Pub::NotSupported, "Entry does not have an edit link"
195
+ end
196
+ end
197
+
198
+ private
199
+ def headers
200
+ {'Accept' => 'application/atom+xml',
201
+ 'Content-Type' => 'application/atom+xml;type=entry',
202
+ 'User-Agent' => "rAtom #{Atom::VERSION::STRING}"
203
+ }
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,9 @@
1
+ module Atom #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 3
5
+ TINY = 5
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,305 @@
1
+ # Copyright (c) 2008 The Kaphan Foundation
2
+ #
3
+ # Possession of a copy of this file grants no permission or license
4
+ # to use, modify, or create derivate works.
5
+ # Please contact info@peerworks.org for further information.
6
+ #
7
+
8
+ require 'net/http'
9
+ require 'time'
10
+
11
+ # Just a couple methods form transforming strings
12
+ unless defined?(ActiveSupport)
13
+ class String # :nodoc:
14
+ def singularize
15
+ if self =~ /ies$/
16
+ self.sub(/ies$/, 'y')
17
+ else
18
+ self.sub(/s$/, '')
19
+ end
20
+ end
21
+
22
+ def demodulize
23
+ self.sub(/.*::/, '')
24
+ end
25
+
26
+ def constantize
27
+ Object.module_eval("::#{self}", __FILE__, __LINE__)
28
+ end
29
+ end
30
+ end
31
+
32
+ module Atom
33
+ module Xml # :nodoc:
34
+ class NamespaceMap
35
+ def initialize
36
+ @i = 0
37
+ @map = {}
38
+ end
39
+
40
+ def get(ns)
41
+ if ns == Atom::NAMESPACE
42
+ @map[ns] = "atom"
43
+ elsif ns == Atom::Pub::NAMESPACE
44
+ @map[ns] = "app"
45
+ else
46
+ @map[ns] or @map[ns] = "ns#{@i += 1}"
47
+ end
48
+ end
49
+
50
+ def each(&block)
51
+ @map.each(&block)
52
+ end
53
+ end
54
+
55
+ module Parseable # :nodoc:
56
+ def parse(xml, options = {})
57
+ starting_depth = xml.depth
58
+ loop do
59
+ case xml.node_type
60
+ when XML::Reader::TYPE_ELEMENT
61
+ if element_specs.include?(xml.local_name) && [Atom::NAMESPACE, Atom::Pub::NAMESPACE].include?(xml.namespace_uri)
62
+ element_specs[xml.local_name].parse(self, xml)
63
+ elsif attributes.any?
64
+ while (xml.move_to_next_attribute == 1)
65
+ if attributes.include?(xml.name)
66
+ # Support attribute names with namespace prefixes
67
+ self.send("#{xml.name.sub(/:/, '_')}=", xml.value)
68
+ elsif self.respond_to?(:simple_extensions)
69
+ self[xml.namespace_uri, xml.local_name].as_attribute = true
70
+ self[xml.namespace_uri, xml.local_name] << xml.value
71
+ end
72
+ end
73
+ elsif self.respond_to?(:simple_extensions)
74
+ self[xml.namespace_uri, xml.local_name] << xml.read_string
75
+ end
76
+ end
77
+ break unless !options[:once] && xml.next == 1 && xml.depth >= starting_depth
78
+ end
79
+ end
80
+
81
+ def next_node_is?(xml, element, ns = nil)
82
+ xml.next == 1 && current_node_is?(xml, element, ns)
83
+ end
84
+
85
+ def current_node_is?(xml, element, ns = nil)
86
+ xml.node_type == XML::Reader::TYPE_ELEMENT && xml.local_name == element && (ns.nil? || ns == xml.namespace_uri)
87
+ end
88
+
89
+ def Parseable.included(o)
90
+ o.class_eval do
91
+ def o.ordered_element_specs; @ordered_element_specs ||= []; end
92
+ def o.element_specs; @element_specs ||= {}; end
93
+ def o.attributes; @attributes ||= []; end
94
+ def element_specs; self.class.element_specs; end
95
+ def ordered_element_specs; self.class.ordered_element_specs; end
96
+ def attributes; self.class.attributes; end
97
+ def o.namespace(ns = @namespace); @namespace = ns; end
98
+ end
99
+ o.send(:extend, DeclarationMethods)
100
+ end
101
+
102
+ def ==(o)
103
+ if self.object_id == o.object_id
104
+ true
105
+ elsif o.instance_of?(self.class)
106
+ self.class.element_specs.values.all? do |spec|
107
+ self.send(spec.attribute) == o.send(spec.attribute)
108
+ end
109
+ else
110
+ false
111
+ end
112
+ end
113
+
114
+ def to_xml(nodeonly = false, root_name = self.class.name.demodulize.downcase, namespace = nil, namespace_map = nil)
115
+ namespace_map = NamespaceMap.new if namespace_map.nil?
116
+ node = XML::Node.new(root_name)
117
+ node['xmlns'] = self.class.namespace unless nodeonly || !self.class.respond_to?(:namespace)
118
+
119
+ self.class.ordered_element_specs.each do |spec|
120
+ if spec.single?
121
+ if attribute = self.send(spec.attribute)
122
+ if attribute.respond_to?(:to_xml)
123
+ node << attribute.to_xml(true, spec.name, spec.options[:namespace], namespace_map)
124
+ else
125
+ n = XML::Node.new(spec.name)
126
+ n['xmlns'] = spec.options[:namespace]
127
+ n << (attribute.is_a?(Time)? attribute.xmlschema : attribute.to_s)
128
+ node << n
129
+ end
130
+ end
131
+ else
132
+ self.send(spec.attribute).each do |attribute|
133
+ if attribute.respond_to?(:to_xml)
134
+ node << attribute.to_xml(true, spec.name.singularize, nil, namespace_map)
135
+ else
136
+ n = XML::Node.new(spec.name.singularize)
137
+ n['xmlns'] = spec.options[:namespace]
138
+ n << attribute.to_s
139
+ node << n
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ self.class.attributes.each do |attribute|
146
+ if value = self.send("#{attribute.sub(/:/, '_')}")
147
+ if value != 0
148
+ node[attribute] = value.to_s
149
+ end
150
+ end
151
+ end
152
+
153
+ if self.respond_to?(:simple_extensions) && self.simple_extensions
154
+ self.simple_extensions.each do |name, value_array|
155
+ if name =~ /\{(.*),(.*)\}/
156
+ value_array.each do |value|
157
+ if value_array.as_attribute
158
+ node["#{namespace_map.get($1)}:#{$2}"] = value
159
+ else
160
+ ext = XML::Node.new("#{namespace_map.get($1)}:#{$2}")
161
+ ext << value
162
+ node << ext
163
+ end
164
+ end
165
+ else
166
+ STDERR.print "Couldn't split #{name}"
167
+ end
168
+ end
169
+ end
170
+
171
+ unless nodeonly
172
+ namespace_map.each do |ns, prefix|
173
+ node["xmlns:#{prefix}"] = ns
174
+ end
175
+
176
+ doc = XML::Document.new
177
+ doc.root = node
178
+ doc.to_s
179
+ else
180
+ node
181
+ end
182
+ end
183
+
184
+ module DeclarationMethods # :nodoc:
185
+ def element(*names)
186
+ options = {:type => :single}
187
+ options.merge!(names.pop) if names.last.is_a?(Hash)
188
+
189
+ names.each do |name|
190
+ attr_accessor name
191
+ self.ordered_element_specs << self.element_specs[name.to_s] = ParseSpec.new(name, options)
192
+ end
193
+ end
194
+
195
+ def elements(*names)
196
+ options = {:type => :collection}
197
+ options.merge!(names.pop) if names.last.is_a?(Hash)
198
+
199
+ names.each do |name|
200
+ attr_accessor name
201
+ self.ordered_element_specs << self.element_specs[name.to_s.singularize] = ParseSpec.new(name, options)
202
+ end
203
+ end
204
+
205
+ def attribute(*names)
206
+ names.each do |name|
207
+ attr_accessor name.to_s.sub(/:/, '_').to_sym
208
+ self.attributes << name.to_s
209
+ end
210
+ end
211
+
212
+ def loadable!(&error_handler)
213
+ class_name = self.name
214
+ (class << self; self; end).instance_eval do
215
+ define_method "load_#{class_name.demodulize.downcase}" do |o|
216
+ xml = nil
217
+ case o
218
+ when String
219
+ xml = XML::Reader.new(o)
220
+ when IO
221
+ xml = XML::Reader.new(o.read)
222
+ when URI
223
+ raise ArgumentError, "#{class_name}.load only handles http URIs" if o.scheme != 'http'
224
+ xml = XML::Reader.new(Net::HTTP.get_response(o).body)
225
+ else
226
+ raise ArgumentError, "#{class_name}.load needs String, URI or IO, got #{o.class.name}"
227
+ end
228
+
229
+ if error_handler
230
+ xml.set_error_handler(&error_handler)
231
+ else
232
+ xml.set_error_handler do |reader, message, severity, base, line|
233
+ if severity == XML::Reader::SEVERITY_ERROR
234
+ raise ParseError, "#{message} at #{line}"
235
+ end
236
+ end
237
+ end
238
+
239
+ o = self.new(xml)
240
+ xml.close
241
+ o
242
+ end
243
+ end
244
+ end
245
+
246
+ def parse(xml)
247
+ new(xml)
248
+ end
249
+ end
250
+
251
+ # Contains the specification for how an element should be parsed.
252
+ #
253
+ # This should not need to be constructed directly, instead use the
254
+ # element and elements macros in the declaration of the class.
255
+ #
256
+ # See Parseable.
257
+ #
258
+ class ParseSpec # :nodoc:
259
+ attr_reader :name, :options, :attribute
260
+
261
+ def initialize(name, options = {})
262
+ @name = name.to_s
263
+ @attribute = name.to_s.sub(/:/, '_')
264
+ @options = options
265
+ end
266
+
267
+ # Parses a chunk of XML according the specification.
268
+ # The data extracted will be assigned to the target object.
269
+ #
270
+ def parse(target, xml)
271
+ case options[:type]
272
+ when :single
273
+ target.send("#{@attribute}=".to_sym, build(target, xml))
274
+ when :collection
275
+ target.send("#{@attribute}") << build(target, xml)
276
+ end
277
+ end
278
+
279
+ def single?
280
+ options[:type] == :single
281
+ end
282
+
283
+ private
284
+ # Create a member
285
+ def build(target, xml)
286
+ if options[:class].is_a?(Class)
287
+ if options[:content_only]
288
+ options[:class].parse(xml.read_string)
289
+ else
290
+ options[:class].parse(xml)
291
+ end
292
+ elsif options[:type] == :single
293
+ xml.read_string
294
+ elsif options[:content_only]
295
+ xml.read_string
296
+ else
297
+ target_class = target.class.name
298
+ target_class = target_class.sub(/#{target_class.demodulize}$/, name.singularize.capitalize)
299
+ target_class.constantize.parse(xml)
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end