xing-vpim 0.658.1

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.
Files changed (68) hide show
  1. data/CHANGES +504 -0
  2. data/COPYING +58 -0
  3. data/README +182 -0
  4. data/bin/reminder +203 -0
  5. data/bin/rrule +71 -0
  6. data/lib/atom.rb +728 -0
  7. data/lib/atom/pub.rb +206 -0
  8. data/lib/atom/version.rb +9 -0
  9. data/lib/atom/xml/parser.rb +305 -0
  10. data/lib/plist.rb +22 -0
  11. data/lib/plist/generator.rb +224 -0
  12. data/lib/plist/parser.rb +225 -0
  13. data/lib/vpim.rb +13 -0
  14. data/lib/vpim/address.rb +219 -0
  15. data/lib/vpim/attachment.rb +102 -0
  16. data/lib/vpim/date.rb +222 -0
  17. data/lib/vpim/dirinfo.rb +277 -0
  18. data/lib/vpim/duration.rb +119 -0
  19. data/lib/vpim/enumerator.rb +32 -0
  20. data/lib/vpim/field.rb +614 -0
  21. data/lib/vpim/icalendar.rb +381 -0
  22. data/lib/vpim/maker/vcard.rb +16 -0
  23. data/lib/vpim/property/base.rb +193 -0
  24. data/lib/vpim/property/common.rb +315 -0
  25. data/lib/vpim/property/location.rb +49 -0
  26. data/lib/vpim/property/priority.rb +43 -0
  27. data/lib/vpim/property/recurrence.rb +69 -0
  28. data/lib/vpim/property/resources.rb +24 -0
  29. data/lib/vpim/repo.rb +181 -0
  30. data/lib/vpim/rfc2425.rb +367 -0
  31. data/lib/vpim/rrule.rb +591 -0
  32. data/lib/vpim/time.rb +40 -0
  33. data/lib/vpim/vcard.rb +1429 -0
  34. data/lib/vpim/version.rb +18 -0
  35. data/lib/vpim/vevent.rb +188 -0
  36. data/lib/vpim/view.rb +90 -0
  37. data/lib/vpim/vjournal.rb +58 -0
  38. data/lib/vpim/vpim.rb +65 -0
  39. data/lib/vpim/vtodo.rb +103 -0
  40. data/samples/README.mutt +93 -0
  41. data/samples/ab-query.rb +57 -0
  42. data/samples/cmd-itip.rb +156 -0
  43. data/samples/ex_cpvcard.rb +55 -0
  44. data/samples/ex_get_vcard_photo.rb +22 -0
  45. data/samples/ex_mkv21vcard.rb +34 -0
  46. data/samples/ex_mkvcard.rb +64 -0
  47. data/samples/ex_mkyourown.rb +29 -0
  48. data/samples/ics-dump.rb +210 -0
  49. data/samples/ics-to-rss.rb +84 -0
  50. data/samples/mutt-aliases-to-vcf.rb +45 -0
  51. data/samples/osx-wrappers.rb +86 -0
  52. data/samples/reminder.rb +203 -0
  53. data/samples/rrule.rb +71 -0
  54. data/samples/tabbed-file-to-vcf.rb +390 -0
  55. data/samples/vcf-dump.rb +86 -0
  56. data/samples/vcf-lines.rb +61 -0
  57. data/samples/vcf-to-ics.rb +22 -0
  58. data/samples/vcf-to-mutt.rb +121 -0
  59. data/test/test_all.rb +17 -0
  60. data/test/test_date.rb +120 -0
  61. data/test/test_dur.rb +41 -0
  62. data/test/test_field.rb +156 -0
  63. data/test/test_ical.rb +415 -0
  64. data/test/test_repo.rb +158 -0
  65. data/test/test_rrule.rb +1030 -0
  66. data/test/test_vcard.rb +973 -0
  67. data/test/test_view.rb +79 -0
  68. metadata +132 -0
data/lib/atom/pub.rb ADDED
@@ -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