vpim 0.17 → 0.323

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/vpim/date.rb~ DELETED
@@ -1,198 +0,0 @@
1
- =begin
2
- $Id: date.rb,v 1.8 2004/11/17 05:06:27 sam Exp $
3
-
4
- Copyright (C) 2005 Sam Roberts
5
-
6
- This library is free software; you can redistribute it and/or modify it
7
- under the same terms as the ruby language itself, see the file COPYING for
8
- details.
9
- =end
10
-
11
- require 'date'
12
-
13
- # Extensions to the standard library Date.
14
- class Date
15
-
16
- TIME_START = Date.new(1970, 1, 1)
17
- SECS_PER_DAY = 24 * 60 * 60
18
-
19
- # Converts this object to a Time object, or throws an ArgumentError if
20
- # conversion is not possible because it is before the start of epoch.
21
- def to_time
22
- raise ArgumentError, 'date is before the start of system time' if self < TIME_START
23
- days = self - TIME_START
24
-
25
- Time.at((days * SECS_PER_DAY).to_i)
26
- end
27
-
28
- # If wday responds to to_str, convert it to the wday number by searching for
29
- # a wday that matches, using as many characters as are in wday to do the
30
- # comparison. wday must be 2 or more characters long in order to be a unique
31
- # match, other than that, "mo", "Mon", and "MonDay" are all valid strings
32
- # for wday 1.
33
- #
34
- # This method can be called on a valid wday, and it will return it. Perhaps
35
- # it should be called by default inside the Date#new*() methods so that
36
- # non-integer wday arguments can be used? Perhaps a similar method should
37
- # exist for months? But with months, we all know January is 1, who can
38
- # remember where Date chooses to start its wday count!
39
- #
40
- # Examples:
41
- # Date.bywday(2004, 2, Date.str2wday('TU')) => the first Tuesday in
42
- # February
43
- # Date.bywday(2004, 2, Date.str2wday(2)) => the same day, but notice
44
- # that a valid wday integer can be passed right through.
45
- #
46
- def Date.str2wday(wdaystr)
47
- return wdaystr unless wdaystr.respond_to? :to_str
48
-
49
- str = wdaystr.to_str.upcase
50
- if str.length < 2
51
- raise ArgumentError, 'wday #{wday} is not long enough to be a unique weekday name'
52
- end
53
-
54
- wday = Date::DAYNAMES.map { |n| n.slice(0, str.length).upcase }.index(str)
55
-
56
- return wday if wday
57
-
58
- raise ArgumentError, 'wday #{wdaystr} was not a recognizable weekday name'
59
- end
60
-
61
-
62
- # Create a new Date object for the date specified by year +year+, month
63
- # +mon+, and day-of-the-week +wday+.
64
- #
65
- # The nth, +n+, occurrence of +wday+ within the period will be generated
66
- # (+n+ defaults to 1). If +n+ is positive, the nth occurence from the
67
- # beginning of the period will be returned, if negative, the nth occurrence
68
- # from the end of the period will be returned.
69
- #
70
- # The period is a year, unless +month+ is non-nil, in which case it is just
71
- # that month.
72
- #
73
- # Examples:
74
- # - Date.bywday(2004, nil, 1, 9) => the ninth Sunday of 2004
75
- # - Date.bywday(2004, nil, 1) => the first Sunday of 2004
76
- # - Date.bywday(2004, nil, 1, -2) => the second last Sunday of 2004
77
- # - Date.bywday(2004, 12, 1) => the first sunday in the 12th month of 2004
78
- # - Date.bywday(2004, 2, 2, -1) => last Tuesday in the 2nd month in 2004
79
- # - Date.bywday(2004, -2, 3, -2) => second last Wednesday in the second last month of 2004
80
- #
81
- # Compare this to Date.new, which allows a Date to be created by
82
- # day-of-the-month, mday, to Date.new2, which allows a Date to be created by
83
- # day-of-the-year, yday, and to Date.neww, which allows a Date to be created
84
- # by day-of-the-week, but within a specific week.
85
- def Date.bywday(year, mon, wday, n = 1, sg=Date::ITALY)
86
- # Normalize mon to 1-12.
87
- if mon
88
- if mon > 12 || mon == 0 || mon < -12
89
- raise ArgumentError, "mon #{mon} must be 1-12 or negative 1-12"
90
- end
91
- if mon < 0
92
- mon = 13 + mon
93
- end
94
- end
95
- if wday < 0 || wday > 6
96
- raise ArgumentError, 'wday must be in range 0-6, or a weekday name'
97
- end
98
-
99
- # Determine direction of indexing.
100
- inc = n <=> 0
101
- if inc == 0
102
- raise ArgumentError, 'n must be greater or less than zero'
103
- end
104
-
105
- # if !mon, n is index into year, but direction of search is determined by
106
- # sign of n
107
- d = Date.new(year, mon ? mon : inc, inc, sg)
108
-
109
- while d.wday != wday
110
- d += inc
111
- end
112
-
113
- # Now we have found the first/last day with the correct wday, search
114
- # for nth occurrence, by jumping by n.abs-1 weeks forward or backward.
115
- d += 7 * (n.abs - 1) * inc
116
-
117
- if d.year != year
118
- raise ArgumentError, 'n is out of bounds of year'
119
- end
120
- if mon && d.mon != mon
121
- raise ArgumentError, 'n is out of bounds of month'
122
- end
123
- d
124
- end
125
- end
126
-
127
- # DateGen generates arrays of dates matching simple criteria.
128
- class DateGen
129
- # Generate an array of dates on +wday+ (the day-of-week,
130
- # 0-6, where 0 is Sunday).
131
- #
132
- # If +n+ is specified, only the nth occurrence of +wday+ within the period
133
- # will be generated. If +n+ is positive, the nth occurence from the
134
- # beginning of the period will be returned, if negative, the nth occurrence
135
- # from the end of the period will be returned.
136
- #
137
- # The period is a year, unless +month+ is non-nil, in which case it is just
138
- # that month.
139
- #
140
- # Examples:
141
- # - DateGen.bywday(2004, nil, 1, 9) => the ninth Sunday in 2004
142
- # - DateGen.bywday(2004, nil, 1) => all Sundays in 2004
143
- # - DateGen.bywday(2004, nil, 1, -2) => second last Sunday in 2004
144
- # - DateGen.bywday(2004, 12, 1) => all sundays in December 2004
145
- # - DateGen.bywday(2004, 2, 2, -1) => last Tuesday in February in 2004
146
- # - DateGen.bywday(2004, -2, 3, -2) => second last Wednesday in November of 2004
147
- #
148
- # Compare to Date.bywday(), which allows a single Date to be created with
149
- # similar criteria.
150
- def DateGen.bywday(year, month, wday, n = nil)
151
- seed = Date.bywday(year, month, wday, n ? n : 1)
152
-
153
- dates = [ seed ]
154
-
155
- return dates if n
156
-
157
- succ = seed.clone
158
-
159
- # Collect all matches until we're out of the year (or month, if specified)
160
- loop do
161
- succ += 7
162
-
163
- break if succ.year != year
164
- break if month && succ.month != seed.month
165
-
166
- dates.push succ
167
- end
168
- dates.sort!
169
- dates
170
- end
171
-
172
- # Generate an array of dates on +mday+ (the day-of-month, 1-31). For months
173
- # in which the +mday+ is not present, no date will be generated.
174
- #
175
- # The period is a year, unless +month+ is non-nil, in which case it is just
176
- # that month.
177
- #
178
- # Compare to Date.new(), which allows a single Date to be created with
179
- # similar criteria.
180
- def DateGen.bymonthday(year, month, mday)
181
- months = month ? [ month ] : 1..12
182
- dates = [ ]
183
-
184
- months.each do |m|
185
- begin
186
- dates << Date.new(year, m, mday)
187
- rescue ArgumentError
188
- # Don't generate dates for invalid combinations (Feb 29, when it's not
189
- # a leap year, for example).
190
- #
191
- # TODO - should we raise when month is out of range, or mday can never
192
- # be in range (32)?
193
- end
194
- end
195
- dates
196
- end
197
- end
198
-
data/lib/vpim/dirinfo.rb~ DELETED
@@ -1,242 +0,0 @@
1
- =begin
2
- $Id: dirinfo.rb,v 1.19 2004/11/17 05:06:27 sam Exp $
3
-
4
- Copyright (C) 2005 Sam Roberts
5
-
6
- This library is free software; you can redistribute it and/or modify it
7
- under the same terms as the ruby language itself, see the file COPYING for
8
- details.
9
- =end
10
-
11
- require 'vpim/enumerator'
12
- require 'vpim/field'
13
- require 'vpim/rfc2425'
14
- require 'vpim/vpim'
15
-
16
- module Vpim
17
- # An RFC 2425 directory info object.
18
- #
19
- # A directory information object is a sequence of fields. The basic
20
- # structure of the object, and the way in which it is broken into fields
21
- # is common to all profiles of the directory info type.
22
- #
23
- # A vCard, for example, is a specialization of a directory info object.
24
- #
25
- # [RFC2425] the directory information framework (ftp://ftp.ietf.org/rfc/rfc2425.txt)
26
- class DirectoryInfo
27
- include Enumerable
28
-
29
- private_class_method :new
30
-
31
- # Initialize a DirectoryInfo object from +fields+. If +profile+ is
32
- # specified, check the BEGIN/END fields.
33
- def initialize(fields, profile = nil) #:nodoc:
34
- if fields.detect { |f| ! f.kind_of? DirectoryInfo::Field }
35
- raise ArgumentError, 'fields must be an array of DirectoryInfo::Field objects'
36
- end
37
-
38
- @string = nil # this is used as a flag to indicate that recoding will be necessary
39
- @fields = fields
40
-
41
- check_begin_end(profile) if profile
42
- end
43
-
44
- # Decode +card+ into a DirectoryInfo object.
45
- #
46
- # +card+ may either be a something that is convertible to a string using
47
- # #to_str or an array of objects that can be joined into a string using
48
- # #join("\n"), or an IO object (which will be read to end-of-file).
49
- #
50
- # The lines in the string may be delimited using IETF (CRLF) or Unix (LF) conventions.
51
- #
52
- # A DirectoryInfo is mutable, you can add new fields to it, see
53
- # Vpim::DirectoryInfo::Field#create() for how to create a new Field.
54
- #
55
- # TODO: I don't believe this is ever used, maybe I can remove it.
56
- def DirectoryInfo.decode(card) #:nodoc:
57
- if card.respond_to? :to_str
58
- string = card.to_str
59
- elsif card.kind_of? Array
60
- string = card.join("\n")
61
- elsif card.kind_of? IO
62
- string = card.read(nil)
63
- else
64
- raise ArgumentError, "DirectoryInfo cannot be created from a #{card.type}"
65
- end
66
-
67
- fields = Vpim.decode(string)
68
-
69
- new(fields)
70
- end
71
-
72
- # Create a new DirectoryInfo object. The +fields+ are an optional array of
73
- # DirectoryInfo::Field objects to add to the new object, between the
74
- # BEGIN/END. If the +profile+ string is not nil, then it is the name of
75
- # the directory info profile, and the BEGIN:+profile+/END:+profile+ fields
76
- # will be added.
77
- #
78
- # A DirectoryInfo is mutable, you can add new fields to it using #push(),
79
- # and see Field#create().
80
- def DirectoryInfo.create(fields = [], profile = nil)
81
-
82
- if profile
83
- p = profile.to_str
84
- f = [ Field.create('BEGIN', p) ]
85
- f.concat fields
86
- f.push Field.create('END', p)
87
- fields = f
88
- end
89
-
90
- new(fields, profile)
91
- end
92
-
93
- # The first field named +name+, or nil if no
94
- # match is found.
95
- def field(name)
96
- enum_by_name(name).each { |f| return f }
97
- nil
98
- end
99
-
100
- # The value of the first field named +name+, or nil if no
101
- # match is found.
102
- def [](name)
103
- enum_by_name(name).each { |f| return f.value if f.value != ''}
104
- enum_by_name(name).each { |f| return f.value }
105
- nil
106
- end
107
-
108
- # An array of all the values of fields named +name+, converted to text
109
- # (using Field#to_text()).
110
- #
111
- # TODO - call this #texts(), as in the plural?
112
- def text(name)
113
- enum_by_name(name).map { |f| f.to_text }
114
- end
115
-
116
- # Array of all the Field#group()s.
117
- def groups
118
- @fields.collect { |f| f.group } .compact.uniq
119
- end
120
-
121
- # All fields, frozen.
122
- def fields #:nodoc:
123
- @fields.dup.freeze
124
- end
125
-
126
- # Yields for each Field for which +cond+.call(field) is true. The
127
- # (default) +cond+ of nil is considered true for all fields, so
128
- # this acts like a normal #each() when called with no arguments.
129
- def each(cond = nil) # :yields: Field
130
- @fields.each do |field|
131
- if(cond == nil || cond.call(field))
132
- yield field
133
- end
134
- end
135
- self
136
- end
137
-
138
- # Returns an Enumerator for each Field for which #name?(+name+) is true.
139
- #
140
- # An Enumerator supports all the methods of Enumerable, so it allows iteration,
141
- # collection, mapping, etc.
142
- #
143
- # Examples:
144
- #
145
- # Print all the nicknames in a card:
146
- #
147
- # card.enum_by_name('nickname') { |f| puts f.value }
148
- #
149
- # Get an Array of the preferred email addresses in the card:
150
- #
151
- # card.enum_by_name('email').collect { |f| f.pref? ? f.value : nil }.compact
152
- # FIXME - make sure this works!
153
- def enum_by_name(name)
154
- Enumerator.new(self, Proc.new { |field| field.name?(name) })
155
- end
156
-
157
- # Returns an Enumerator for each Field for which #group?(+group+) is true.
158
- #
159
- # For example, to print all the fields, sorted by group, you could do:
160
- #
161
- # card.groups.sort.each do |group|
162
- # card.enum_by_group(group).each do |field|
163
- # puts "#{group} -> #{field.name}"
164
- # end
165
- # end
166
- #
167
- # or to get an array of all the fields in group 'agroup', you could do:
168
- #
169
- # card.enum_by_group('agroup').to_a
170
- def enum_by_group(group)
171
- Enumerator.new(self, Proc.new { |field| field.group?(group) })
172
- end
173
-
174
- # Returns an Enumerator for each Field for which +cond+.call(field) is true.
175
- def enum_by_cond(cond)
176
- Enumerator.new(self, cond )
177
- end
178
-
179
- # Append +field+ to the fields. Note that it won't be literally appended
180
- # to the fields, it will be inserted before the closing END field.
181
- def push(field)
182
- @fields[-1,0] = field
183
- self
184
- end
185
-
186
- alias << push
187
-
188
- # Push +field+ onto the fields, unless there is already a field
189
- # with this name.
190
- def push_unique(field)
191
- push(field) unless @fields.detect { |f| f.name? field.name }
192
- self
193
- end
194
-
195
- # Append +field+ to the end of all the fields. This isn't usually what you
196
- # want to do, usually a DirectoryInfo's first and last fields are a
197
- # BEGIN/END pair, see #push().
198
- def push_end(field)
199
- @fields << field
200
- self
201
- end
202
-
203
- def delete(field)
204
- end
205
-
206
- # The string encoding of the DirectoryInfo. See Field#encode for information
207
- # about the width parameter.
208
- def encode(width=nil)
209
- unless @string
210
- @string = @fields.collect { |f| f.encode(width) } . join ""
211
- end
212
- @string
213
- end
214
-
215
- alias to_s encode
216
-
217
- # Check that the DirectoryInfo object is correctly delimited by a BEGIN
218
- # and END, that their profile values match, and if +profile+ is specified, that
219
- # they are the specified profile.
220
- def check_begin_end(profile=nil) #:nodoc:
221
- unless @fields.first
222
- raise "No fields to check"
223
- end
224
- unless @fields.first.name? "begin"
225
- raise "Needs BEGIN, found: #{@fields.first.encode nil}"
226
- end
227
- unless @fields.last.name? "end"
228
- raise "Needs END, found: #{@fields.last.encode nil}"
229
- end
230
- unless @fields.last.value? @fields.first.value
231
- raise "BEGIN/END mismatch: (#{@fields.first.value.downcase} != #{@fields.last.value.downcase}"
232
- end
233
- if profile
234
- if ! @fields.first.value? profile
235
- raise "Mismatched profile"
236
- end
237
- end
238
- true
239
- end
240
- end
241
- end
242
-
@@ -1,121 +0,0 @@
1
- =begin
2
- $Id: duration.rb,v 1.4 2004/11/17 05:06:27 sam Exp $
3
-
4
- Copyright (C) 2005 Sam Roberts
5
-
6
- This library is free software; you can redistribute it and/or modify it
7
- under the same terms as the ruby language itself, see the file COPYING for
8
- details.
9
- =end
10
-
11
- module Vpim
12
- class Duration
13
- SECS_HOUR = 60 * 60
14
- SECS_DAY = 24 * SECS_HOUR
15
- MINS_HOUR = 60
16
-
17
- # Initialize from a number of seconds.
18
- def initialize(secs)
19
- @secs = secs
20
- end
21
-
22
- def Duration.secs(secs)
23
- Duration.new(secs)
24
- end
25
-
26
- def Duration.mins(mins)
27
- Duration.new(mins * 60)
28
- end
29
-
30
- def Duration.hours(hours)
31
- Duration.new(hours * SECS_HOUR)
32
- end
33
-
34
- def Duration.days(days)
35
- Duration.new(days * SECS_DAY)
36
- end
37
-
38
- def secs
39
- @secs
40
- end
41
-
42
- def mins
43
- (@secs/60).to_i
44
- end
45
-
46
- def hours
47
- (@secs/SECS_HOUR).to_i
48
- end
49
-
50
- def days
51
- (@secs/SECS_DAY).to_i
52
- end
53
-
54
- def weeks
55
- (days/7).to_i
56
- end
57
-
58
- def by_hours
59
- [ hours, mins % MINS_HOUR, secs % 60]
60
- end
61
-
62
- def by_days
63
- [ days, hours % 24, mins % MINS_HOUR, secs % 60]
64
- end
65
-
66
- def to_a
67
- by_days
68
- end
69
-
70
- def to_s
71
- Duration.as_str(self.to_a)
72
- end
73
-
74
- def Duration.as_str(arr)
75
- s = ""
76
- case arr.length
77
- when 4
78
- if arr[0] > 0
79
- s << "#{arr[0]} days"
80
- end
81
- if arr[1] > 0
82
- if s.length > 0
83
- s << ', '
84
- end
85
- s << "#{arr[1]} hours"
86
- end
87
- if arr[2] > 0
88
- if s.length > 0
89
- s << ', '
90
- end
91
- s << "#{arr[2]} mins"
92
- end
93
- if arr[3] > 0
94
- if s.length > 0
95
- s << ', '
96
- end
97
- s << "#{arr[3]} secs"
98
- end
99
- when 3
100
- if arr[0] > 0
101
- s << "#{arr[0]} hours"
102
- end
103
- if arr[1] > 0
104
- if s.length > 0
105
- s << ', '
106
- end
107
- s << "#{arr[1]} mins"
108
- end
109
- if arr[2] > 0
110
- if s.length > 0
111
- s << ', '
112
- end
113
- s << "#{arr[2]} secs"
114
- end
115
- end
116
-
117
- s
118
- end
119
- end
120
- end
121
-
@@ -1,29 +0,0 @@
1
- =begin
2
- $Id: enumerator.rb,v 1.2 2004/11/17 05:06:27 sam Exp $
3
-
4
- Copyright (C) 2005 Sam Roberts
5
-
6
- This library is free software; you can redistribute it and/or modify it
7
- under the same terms as the ruby language itself, see the file COPYING for
8
- details.
9
- =end
10
-
11
- module Vpim
12
- # This is a way for an object to have multiple ways of being enumerated via
13
- # argument to it's #each() method. An Enumerator mixes in Enumerable, so the
14
- # standard APIS such as Enumerable#map(), Enumerable#to_a(), and
15
- # Enumerable#find_all() can be used on it.
16
- class Enumerator
17
- include Enumerable
18
-
19
- def initialize(obj, *args)
20
- @obj = obj
21
- @args = args
22
- end
23
-
24
- def each(&block)
25
- @obj.each(*@args, &block)
26
- end
27
- end
28
- end
29
-