vpim 0.17 → 0.323

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