icalPal 3.1.1 → 3.3.0
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.
- checksums.yaml +4 -4
- data/README.md +19 -19
- data/bin/icalPal +111 -56
- data/bin/icalpal +111 -56
- data/icalPal.gemspec +37 -9
- data/lib/EventKit.rb +24 -18
- data/lib/ToICalPal.rb +28 -26
- data/lib/calendar.rb +1 -2
- data/lib/defaults.rb +8 -7
- data/lib/event.rb +56 -55
- data/lib/icalPal.rb +26 -46
- data/lib/options.rb +40 -36
- data/lib/rdt.rb +7 -5
- data/lib/reminder.rb +22 -22
- data/lib/store.rb +1 -1
- data/lib/utils.rb +45 -0
- data/lib/version.rb +2 -2
- metadata +18 -13
data/lib/event.rb
CHANGED
@@ -22,13 +22,13 @@ module ICalPal
|
|
22
22
|
# @param k [String] Key/property name
|
23
23
|
def [](k)
|
24
24
|
case k
|
25
|
-
when 'age'
|
25
|
+
when 'age' # pseudo-property
|
26
26
|
@self['sdate'].year - @self['edate'].year
|
27
27
|
|
28
|
-
when 'availability'
|
29
|
-
EventKit::EKEventAvailability.select { |
|
28
|
+
when 'availability' # Integer -> String
|
29
|
+
EventKit::EKEventAvailability.select { |_k, v| v == @self['availability'] }.keys
|
30
30
|
|
31
|
-
when 'datetime'
|
31
|
+
when 'datetime' # date[ at time[ - time]]
|
32
32
|
unless $opts[:sd] || $opts[:days] == 1
|
33
33
|
t = @self['sdate'].to_s
|
34
34
|
t += ' at ' unless @self['all_day'].positive?
|
@@ -41,22 +41,22 @@ module ICalPal
|
|
41
41
|
end
|
42
42
|
t
|
43
43
|
|
44
|
-
when 'location'
|
45
|
-
@self['location']? [ @self['location'], @self['address'] ].join(' ').chop : nil
|
44
|
+
when 'location' # location[ address]
|
45
|
+
(@self['location'])? [ @self['location'], @self['address'] ].join(' ').chop : nil
|
46
46
|
|
47
|
-
when 'notes'
|
48
|
-
@self['notes']? @self['notes'].strip.gsub(/\n/, $opts[:nnr]) : nil
|
47
|
+
when 'notes' # \n -> :nnr
|
48
|
+
(@self['notes'])? @self['notes'].strip.gsub(/\n/, $opts[:nnr]) : nil
|
49
49
|
|
50
|
-
when 'sday'
|
50
|
+
when 'sday' # pseudo-property
|
51
51
|
ICalPal::RDT.new(*@self['sdate'].to_a[0..2])
|
52
52
|
|
53
|
-
when 'status'
|
54
|
-
EventKit::EKEventStatus.select { |
|
53
|
+
when 'status' # Integer -> String
|
54
|
+
EventKit::EKEventStatus.select { |_k, v| v == @self['status'] }.keys[0]
|
55
55
|
|
56
|
-
when 'title'
|
57
|
-
@self['title'] + ((@self['calendar'] == 'Birthdays')? " (age #{self['age']})" :
|
56
|
+
when 'title' # title[ (age N)]
|
57
|
+
@self['title'] + ((@self['calendar'] == 'Birthdays')? " (age #{self['age']})" : '')
|
58
58
|
|
59
|
-
when 'uid'
|
59
|
+
when 'uid' # for icalBuddy
|
60
60
|
@self['UUID']
|
61
61
|
|
62
62
|
else @self[k]
|
@@ -72,20 +72,22 @@ module ICalPal
|
|
72
72
|
def initialize(obj)
|
73
73
|
# Placeholder for days with no events
|
74
74
|
return @self = {
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
75
|
+
$opts[:sep] => obj,
|
76
|
+
'sdate' => obj,
|
77
|
+
'placeholder' => true,
|
78
|
+
'title' => 'Nothing.',
|
79
|
+
} if DateTime === obj
|
80
80
|
|
81
81
|
@self = {}
|
82
|
-
obj.
|
82
|
+
obj.each_key { |k| @self[k] = obj[k] }
|
83
83
|
|
84
84
|
# Convert JSON arrays to Arrays
|
85
85
|
@self['attendees'] = JSON.parse(obj['attendees'])
|
86
|
+
# rubocop: disable Lint/UselessAssignment
|
86
87
|
@self['xdate'] = JSON.parse(obj['xdate']).map do |k|
|
87
88
|
k = RDT.new(*Time.at(k + ITIME).to_a.reverse[4..]) if k
|
88
89
|
end
|
90
|
+
# rubocop: enable Lint/UselessAssignment
|
89
91
|
|
90
92
|
# Convert iCal dates to normal dates
|
91
93
|
obj.keys.select { |i| i.end_with? '_date' }.each do |k|
|
@@ -94,7 +96,7 @@ module ICalPal
|
|
94
96
|
end
|
95
97
|
|
96
98
|
if @self['start_tz'] == '_float'
|
97
|
-
tzoffset = Time.zone_offset($now.zone
|
99
|
+
tzoffset = Time.zone_offset($now.zone)
|
98
100
|
|
99
101
|
@self['sdate'] = RDT.new(*(@self['sdate'].to_time - tzoffset).to_a.reverse[4..], $now.zone)
|
100
102
|
@self['edate'] = RDT.new(*(@self['edate'].to_time - tzoffset).to_a.reverse[4..], $now.zone)
|
@@ -116,19 +118,19 @@ module ICalPal
|
|
116
118
|
def non_recurring
|
117
119
|
events = []
|
118
120
|
|
119
|
-
nDays = (self['duration'] /
|
121
|
+
nDays = (self['duration'] / 86_400).to_i
|
120
122
|
|
121
123
|
# Sanity checks
|
122
|
-
return events if nDays >
|
124
|
+
return events if nDays > 100_000
|
123
125
|
|
124
126
|
# Repeat for multi-day events
|
125
127
|
(nDays + 1).times do |i|
|
126
128
|
break if self['sdate'] > $opts[:to]
|
127
129
|
|
128
|
-
$log.debug("multi-day event #{i + 1}") if (i
|
130
|
+
$log.debug("multi-day event #{i + 1}") if (i.positive?)
|
129
131
|
|
130
132
|
self['daynum'] = i + 1
|
131
|
-
events.push(clone) if in_window?(self['sdate'])
|
133
|
+
events.push(clone) if in_window?(self['sdate'], self['edate'])
|
132
134
|
|
133
135
|
self['sdate'] += 1
|
134
136
|
self['edate'] += 1
|
@@ -145,9 +147,9 @@ module ICalPal
|
|
145
147
|
stop = [ $opts[:to], (self['rdate'] || $opts[:to]) ].min
|
146
148
|
|
147
149
|
# See if event ends before we start
|
148
|
-
if stop < $opts[:from]
|
150
|
+
if stop < $opts[:from]
|
149
151
|
$log.debug("#{stop} < #{$opts[:from]}")
|
150
|
-
return(
|
152
|
+
return([])
|
151
153
|
end
|
152
154
|
|
153
155
|
# Get changes to series
|
@@ -159,11 +161,12 @@ module ICalPal
|
|
159
161
|
|
160
162
|
while self['sdate'] <= stop
|
161
163
|
# count
|
162
|
-
break if self['count'].positive?
|
164
|
+
break if self['count'].positive? && count > self['count']
|
165
|
+
|
163
166
|
count += 1
|
164
167
|
|
165
168
|
# Handle specifier or clone self
|
166
|
-
if self['specifier']
|
169
|
+
if self['specifier'] && self['specifier'].length.positive?
|
167
170
|
occurrences = get_occurrences(changes)
|
168
171
|
else
|
169
172
|
occurrences = [ clone ]
|
@@ -173,18 +176,20 @@ module ICalPal
|
|
173
176
|
occurrences.each do |occurrence|
|
174
177
|
changes.each do |change|
|
175
178
|
next if change['orig_date'] == self['sdate'].to_i - ITIME
|
179
|
+
|
176
180
|
events.push(occurrence) if in_window?(occurrence['sdate'], occurrence['edate'])
|
177
181
|
end
|
178
182
|
end
|
179
183
|
|
180
184
|
break if self['specifier']
|
185
|
+
|
181
186
|
apply_frequency!
|
182
187
|
end
|
183
188
|
|
184
189
|
# Remove exceptions
|
185
190
|
events.delete_if { |event| event['xdate'].any?(event['sdate']) }
|
186
191
|
|
187
|
-
|
192
|
+
events
|
188
193
|
end
|
189
194
|
|
190
195
|
private
|
@@ -192,15 +197,15 @@ module ICalPal
|
|
192
197
|
# @!visibility public
|
193
198
|
|
194
199
|
# @return a deep clone of self
|
195
|
-
def clone
|
200
|
+
def clone
|
196
201
|
Marshal.load(Marshal.dump(self))
|
197
202
|
end
|
198
203
|
|
199
204
|
# Get next occurences of a recurring event from a specifier
|
200
205
|
#
|
201
|
-
# @param
|
206
|
+
# @param _changes [Array] Recurrence changes for the event
|
202
207
|
# @return [Array<IcalPal::Event>]
|
203
|
-
def get_occurrences(
|
208
|
+
def get_occurrences(_changes)
|
204
209
|
occurrences = []
|
205
210
|
|
206
211
|
dow = DOW.keys
|
@@ -208,7 +213,7 @@ module ICalPal
|
|
208
213
|
moy = 1..12
|
209
214
|
nth = nil
|
210
215
|
|
211
|
-
specifier = self['specifier']? self['specifier'] : []
|
216
|
+
specifier = (self['specifier'])? self['specifier'] : []
|
212
217
|
|
213
218
|
# Deconstruct specifier
|
214
219
|
specifier.split(';').each do |k|
|
@@ -226,7 +231,7 @@ module ICalPal
|
|
226
231
|
|
227
232
|
# Build array of DOWs
|
228
233
|
dows = [ nil ]
|
229
|
-
dow.each { |d| dows.push(DOW[d[-2
|
234
|
+
dow.each { |d| dows.push(DOW[d[-2..].to_sym]) }
|
230
235
|
|
231
236
|
# Months of the year (O)
|
232
237
|
moy.each do |m|
|
@@ -249,16 +254,14 @@ module ICalPal
|
|
249
254
|
self['sdate'] = ICalPal.nth(nth, dows, nsdate)
|
250
255
|
self['edate'] = ICalPal.nth(nth, dows, nedate)
|
251
256
|
occurrences.push(clone)
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
occurrences.push(clone)
|
257
|
-
end
|
257
|
+
elsif dows[0]
|
258
|
+
self['sdate'] = RDT.new(nsdate.year, m.to_i, nsdate.wday)
|
259
|
+
self['edate'] = RDT.new(nedate.year, m.to_i, nedate.wday)
|
260
|
+
occurrences.push(clone)
|
258
261
|
end
|
259
262
|
end
|
260
263
|
|
261
|
-
|
264
|
+
occurrences
|
262
265
|
end
|
263
266
|
|
264
267
|
# Apply frequency and interval
|
@@ -284,27 +287,25 @@ module ICalPal
|
|
284
287
|
# @param s [RDT] Event start
|
285
288
|
# @param e [RDT] Event end
|
286
289
|
# @return [Boolean]
|
287
|
-
def in_window?(s, e
|
288
|
-
if $opts[:
|
289
|
-
if ($now >= s && $now < e)
|
290
|
+
def in_window?(s, e)
|
291
|
+
if $opts[:now]
|
292
|
+
if ($now >= s && $now < e)
|
290
293
|
$log.debug("now: #{s} to #{e} vs. #{$now}")
|
291
|
-
|
294
|
+
true
|
292
295
|
else
|
293
296
|
$log.debug("not now: #{s} to #{e} vs. #{$now}")
|
294
|
-
|
297
|
+
false
|
295
298
|
end
|
299
|
+
elsif ([ s, e ].max >= $opts[:from] && s < $opts[:to])
|
300
|
+
$log.debug("in window: #{s} to #{e} vs. #{$opts[:from]} to #{$opts[:to]}")
|
301
|
+
true
|
296
302
|
else
|
297
|
-
|
298
|
-
|
299
|
-
return(true)
|
300
|
-
else
|
301
|
-
$log.debug("not in window: #{s} to #{e} vs. #{$opts[:from]} to #{$opts[:to]}")
|
302
|
-
return(false)
|
303
|
-
end
|
303
|
+
$log.debug("not in window: #{s} to #{e} vs. #{$opts[:from]} to #{$opts[:to]}")
|
304
|
+
false
|
304
305
|
end
|
305
306
|
end
|
306
307
|
|
307
|
-
QUERY = <<~SQL
|
308
|
+
QUERY = <<~SQL.freeze
|
308
309
|
SELECT DISTINCT
|
309
310
|
|
310
311
|
Store.name AS account,
|
data/lib/icalPal.rb
CHANGED
@@ -9,7 +9,6 @@ require_relative 'store'
|
|
9
9
|
# Encapsulate the _Store_ (accounts), _Calendar_ and _CalendarItem_
|
10
10
|
# tables of a Calendar database, and the _Reminder_ table of a
|
11
11
|
# Reminders database
|
12
|
-
|
13
12
|
module ICalPal
|
14
13
|
attr_reader :self
|
15
14
|
|
@@ -57,19 +56,21 @@ module ICalPal
|
|
57
56
|
|
58
57
|
rescue SQLite3::BusyException => e
|
59
58
|
$log.error("Non-fatal error closing database #{db.filename}")
|
59
|
+
raise e
|
60
60
|
|
61
61
|
rescue SQLite3::CantOpenException => e
|
62
62
|
$log.debug("Can't open #{db_file}")
|
63
|
+
raise e
|
63
64
|
|
64
65
|
rescue SQLite3::SQLException => e
|
65
66
|
$log.info("#{db_file}: #{e}")
|
67
|
+
raise e
|
66
68
|
|
67
69
|
rescue SQLite3::Exception => e
|
68
70
|
abort("#{db_file}: #{e}")
|
69
|
-
|
70
71
|
end
|
71
72
|
|
72
|
-
|
73
|
+
rows
|
73
74
|
end
|
74
75
|
|
75
76
|
# @param obj [ICalPal] A +Store+ or +Calendar+
|
@@ -93,9 +94,9 @@ module ICalPal
|
|
93
94
|
# @return [CSV::Row] The +Store+, +Calendar+, or +CalendarItem+ as a CSV::Row
|
94
95
|
def to_csv(headers)
|
95
96
|
values = []
|
96
|
-
headers.each { |h| values.push(@self[h].respond_to?(:gsub)? @self[h].gsub(/\n/, '\n') : @self[h]) }
|
97
|
+
headers.each { |h| values.push((@self[h].respond_to?(:gsub))? @self[h].gsub(/\n/, '\n') : @self[h]) }
|
97
98
|
|
98
|
-
CSV::Row
|
99
|
+
CSV::Row.new(headers, values)
|
99
100
|
end
|
100
101
|
|
101
102
|
# Convert +self+ to XML
|
@@ -103,44 +104,12 @@ module ICalPal
|
|
103
104
|
# @return [String] All fields in a simple XML format: <field>value</field>.
|
104
105
|
# Fields with empty values return <field/>.
|
105
106
|
def to_xml
|
106
|
-
retval =
|
107
|
-
@self.
|
107
|
+
retval = ''
|
108
|
+
@self.each_key { |k| retval += xmlify(k, @self[k]) }
|
108
109
|
|
109
110
|
retval
|
110
111
|
end
|
111
112
|
|
112
|
-
# Convert a key/value pair to XML. The value should be +nil+, +String+,
|
113
|
-
# +Integer+, +Array+, or +ICalPal::RDT+
|
114
|
-
#
|
115
|
-
# @param key The key
|
116
|
-
# @param value The value
|
117
|
-
# @return [String] The key/value pair in a simple XML format
|
118
|
-
def xmlify(key, value)
|
119
|
-
case value
|
120
|
-
# Nil
|
121
|
-
when NilClass then return("<#{key}/>")
|
122
|
-
|
123
|
-
# String, Integer
|
124
|
-
when String then return("<#{key}>#{value}</#{key}>")
|
125
|
-
when Integer then return("<#{key}>#{value}</#{key}>")
|
126
|
-
|
127
|
-
# Array
|
128
|
-
when Array then
|
129
|
-
# Treat empty arrays as nil values
|
130
|
-
return(xmlify(key, nil)) if value[0] == nil
|
131
|
-
|
132
|
-
retval = ""
|
133
|
-
value.each { |x| retval += xmlify("#{key}0", x) }
|
134
|
-
return("<#{key}>#{retval}</#{key}>")
|
135
|
-
|
136
|
-
# RDT
|
137
|
-
when ICalPal::RDT then return("<#{key}>#{value.to_s}</#{key}>")
|
138
|
-
|
139
|
-
# Unknown
|
140
|
-
else return("<#{key}>#{value.to_s}</#{key}>")
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
113
|
# Get the +n+'th +dow+ in month +m+
|
145
114
|
#
|
146
115
|
# @param n [Integer] Integer between -4 and +4
|
@@ -150,7 +119,7 @@ module ICalPal
|
|
150
119
|
def self.nth(n, dow, m)
|
151
120
|
# Get the number of days in the month
|
152
121
|
a = [ ICalPal::RDT.new(m.year, m.month, 1) ] # First of this month
|
153
|
-
a[1] = (a[0] >> 1) - 1
|
122
|
+
a[1] = (a[0] >> 1) - 1 # First of next month, minus 1 day
|
154
123
|
|
155
124
|
# Reverse it if going backwards
|
156
125
|
a.reverse! if n.negative?
|
@@ -164,17 +133,28 @@ module ICalPal
|
|
164
133
|
end
|
165
134
|
|
166
135
|
# Epoch + 31 years
|
167
|
-
ITIME =
|
136
|
+
ITIME = 978_307_200
|
168
137
|
|
169
138
|
# Days of the week abbreviations used in recurrence rules
|
170
139
|
#
|
171
140
|
# <tt><i>SU, MO, TU, WE, TH, FR, SA</i></tt>
|
172
|
-
DOW = { 'SU': 0, 'MO': 1, 'TU': 2, 'WE': 3, 'TH': 4, 'FR': 5, 'SA': 6 }
|
141
|
+
DOW = { 'SU': 0, 'MO': 1, 'TU': 2, 'WE': 3, 'TH': 4, 'FR': 5, 'SA': 6 }.freeze
|
173
142
|
|
174
143
|
# @!group Accessors
|
175
|
-
def [](k)
|
176
|
-
|
177
|
-
|
178
|
-
|
144
|
+
def [](k)
|
145
|
+
@self[k]
|
146
|
+
end
|
147
|
+
|
148
|
+
def []=(k, v)
|
149
|
+
@self[k] = v
|
150
|
+
end
|
151
|
+
|
152
|
+
def keys
|
153
|
+
@self.keys
|
154
|
+
end
|
155
|
+
|
156
|
+
def values
|
157
|
+
@self.values
|
158
|
+
end
|
179
159
|
# @!endgroup
|
180
160
|
end
|
data/lib/options.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# rubocop: disable Style/FormatString, Style/FormatStringToken
|
2
|
+
|
1
3
|
require 'optparse'
|
2
4
|
|
3
5
|
require_relative 'defaults'
|
@@ -18,12 +20,13 @@ module ICalPal
|
|
18
20
|
#
|
19
21
|
# Options can be abbreviated as long as they are unique.
|
20
22
|
class Options
|
23
|
+
|
21
24
|
# Define the OptionParser
|
22
25
|
def initialize
|
23
26
|
# prologue
|
24
27
|
@op = OptionParser.new
|
25
28
|
@op.summary_width = 23
|
26
|
-
@op.banner +=
|
29
|
+
@op.banner += ' [-c] COMMAND'
|
27
30
|
@op.version = ICalPal::VERSION
|
28
31
|
|
29
32
|
@op.accept(ICalPal::RDT) { |s| ICalPal::RDT.conv(s) }
|
@@ -31,30 +34,30 @@ module ICalPal
|
|
31
34
|
# head
|
32
35
|
@op.on_head("\nCOMMAND must be one of the following:\n\n")
|
33
36
|
|
34
|
-
@op.on(
|
35
|
-
@op.on(
|
36
|
-
@op.on(
|
37
|
-
@op.on(
|
37
|
+
@op.on('%s%s %sPrint events' % pad('events'))
|
38
|
+
@op.on('%s%s %sPrint tasks' % pad('tasks'))
|
39
|
+
@op.on('%s%s %sPrint calendars' % pad('calendars'))
|
40
|
+
@op.on('%s%s %sPrint accounts' % pad('accounts'))
|
38
41
|
|
39
42
|
@op.separator('')
|
40
|
-
@op.on(
|
41
|
-
@op.on(
|
42
|
-
@op.on(
|
43
|
-
@op.on(
|
44
|
-
@op.on(
|
43
|
+
@op.on('%s%s %sPrint events occurring today' % pad('eventsToday'))
|
44
|
+
@op.on('%s%s %sPrint events occurring between today and NUM days into the future' % pad('eventsToday+NUM'))
|
45
|
+
@op.on('%s%s %sPrint events occurring at present time' % pad('eventsNow'))
|
46
|
+
@op.on('%s%s %sPrint tasks with a due date' % pad('datedTasks'))
|
47
|
+
@op.on('%s%s %sPrint tasks with no due date' % pad('undatedTasks'))
|
45
48
|
|
46
49
|
# global
|
47
50
|
@op.separator("\nGlobal options:\n\n")
|
48
51
|
|
49
52
|
@op.on('-c=COMMAND', '--cmd=COMMAND', COMMANDS, 'Command to run')
|
50
|
-
@op.on('--db=DB',
|
53
|
+
@op.on('--db=DB', 'Use DB file instead of Calendar',
|
51
54
|
"(default: #{$defaults[:common][:db]}",
|
52
55
|
'For the tasks commands this should be a directory containing .sqlite files',
|
53
56
|
"(default: #{$defaults[:tasks][:db]})")
|
54
57
|
@op.on('--cf=FILE', "Set config file path (default: #{$defaults[:common][:cf]})")
|
55
58
|
@op.on('-o', '--output=FORMAT', OUTFORMATS,
|
56
|
-
|
57
|
-
|
59
|
+
"Print as FORMAT (default: #{$defaults[:common][:output]})", "[#{OUTFORMATS.join(', ')}]")
|
60
|
+
|
58
61
|
# include/exclude
|
59
62
|
@op.separator("\nIncluding/excluding accounts, calendars, items:\n\n")
|
60
63
|
|
@@ -64,7 +67,7 @@ module ICalPal
|
|
64
67
|
@op.separator('')
|
65
68
|
@op.on('--it=TYPES', Array, 'List of calendar types to include')
|
66
69
|
@op.on('--et=TYPES', Array, 'List of calendar types to exclude',
|
67
|
-
|
70
|
+
"[#{EventKit::EKSourceType.map { |i| i[:name] }.join(', ')}]")
|
68
71
|
|
69
72
|
@op.separator('')
|
70
73
|
@op.on('--ic=CALENDARS', Array, 'List of calendars to include')
|
@@ -82,12 +85,12 @@ module ICalPal
|
|
82
85
|
|
83
86
|
@op.on('--from=DATE', ICalPal::RDT, 'List events starting on or after DATE')
|
84
87
|
@op.on('--to=DATE', ICalPal::RDT, 'List events starting on or before DATE',
|
85
|
-
|
86
|
-
|
88
|
+
'DATE can be yesterday, today, tomorrow, +N, -N, or anything accepted by DateTime.parse()',
|
89
|
+
'See https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-c-parse')
|
87
90
|
@op.separator('')
|
88
91
|
@op.on('-n', 'Include only events from now on')
|
89
92
|
@op.on('--days=N', OptionParser::DecimalInteger,
|
90
|
-
|
93
|
+
'Show N days of events, including start date')
|
91
94
|
@op.on('--sed', 'Show empty dates with --sd')
|
92
95
|
@op.on('--ia', 'Include only all-day events')
|
93
96
|
@op.on('--ea', 'Exclude all-day events')
|
@@ -102,7 +105,7 @@ module ICalPal
|
|
102
105
|
@op.on('--itp=PROPERTIES', Array, 'List of task properties to include')
|
103
106
|
@op.on('--etp=PROPERTIES', Array, 'List of task properties to exclude')
|
104
107
|
@op.on('--atp=PROPERTIES', Array, 'List of task properties to include in addition to the default list',
|
105
|
-
|
108
|
+
'Included for backwards compatability, these are aliases for --iep, --eep, and --aep')
|
106
109
|
@op.separator('')
|
107
110
|
|
108
111
|
@op.on('--uid', 'Show event UIDs')
|
@@ -114,12 +117,10 @@ module ICalPal
|
|
114
117
|
@op.on('--nrd', 'No relative dates')
|
115
118
|
|
116
119
|
@op.separator('')
|
117
|
-
@op.separator(@op.summary_indent
|
120
|
+
@op.separator("#{@op.summary_indent}Properties are listed in the order specified")
|
118
121
|
@op.separator('')
|
119
|
-
@op.separator(@op.summary_indent
|
120
|
-
|
121
|
-
@op.separator(@op.summary_indent +
|
122
|
-
"Use 'list' for PROPERTIES to list all available properties and exit")
|
122
|
+
@op.separator("#{@op.summary_indent}Use 'all' for PROPERTIES to include all available properties (except any listed in --eep)")
|
123
|
+
@op.separator("#{@op.summary_indent}Use 'list' for PROPERTIES to list all available properties and exit")
|
123
124
|
|
124
125
|
# formatting
|
125
126
|
@op.separator("\nFormatting the output:\n\n")
|
@@ -144,7 +145,7 @@ module ICalPal
|
|
144
145
|
@op.separator('')
|
145
146
|
@op.on('--df=FORMAT', String, 'Set date format')
|
146
147
|
@op.on('--tf=FORMAT', String, 'Set time format',
|
147
|
-
|
148
|
+
'See https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-i-strftime for details')
|
148
149
|
|
149
150
|
@op.separator('')
|
150
151
|
@op.on('-b', '--bullet=STRING', String, 'Use STRING for bullets')
|
@@ -160,16 +161,16 @@ module ICalPal
|
|
160
161
|
@op.separator("\nHelp:\n\n")
|
161
162
|
|
162
163
|
@op.on('-h', '--help', 'Show this message') { @op.abort(@op.help) }
|
163
|
-
@op.on('-V', '-v', '--version', "Show version and exit (#{@op.version})") { @op.abort(@op.version)
|
164
|
+
@op.on('-V', '-v', '--version', "Show version and exit (#{@op.version})") { @op.abort(@op.version) }
|
164
165
|
@op.on('-d', '--debug=LEVEL', /#{Regexp.union(Logger::SEV_LABEL[0..-2]).source}/i,
|
165
|
-
|
166
|
-
|
166
|
+
"Set the logging level (default: #{Logger::SEV_LABEL[$defaults[:common][:debug]].downcase})",
|
167
|
+
"[#{Logger::SEV_LABEL[0..-2].join(', ').downcase}]")
|
167
168
|
|
168
169
|
# environment variables
|
169
170
|
@op.on_tail("\nEnvironment variables:\n\n")
|
170
171
|
|
171
|
-
@op.on_tail(
|
172
|
-
@op.on_tail(
|
172
|
+
@op.on_tail('%s%s %sAdditional arguments' % pad('ICALPAL'))
|
173
|
+
@op.on_tail('%s%s %sAdditional arguments from a file' % pad('ICALPAL_CONFIG'))
|
173
174
|
@op.on_tail("%s%s %s(default: #{$defaults[:common][:cf]})" % pad(''))
|
174
175
|
end
|
175
176
|
|
@@ -196,7 +197,7 @@ module ICalPal
|
|
196
197
|
|
197
198
|
# Parse eventsNow and eventsToday commands
|
198
199
|
cli[:cmd].match('events(Now|Today)(\+[0-9]+)?') do |m|
|
199
|
-
cli[:
|
200
|
+
cli[:now] = true if m[1] == 'Now'
|
200
201
|
cli[:days] = (m[1] == 'Today')? m[2].to_i + 1 : 1
|
201
202
|
|
202
203
|
cli[:from] = $today
|
@@ -218,8 +219,8 @@ module ICalPal
|
|
218
219
|
.merge(cli)
|
219
220
|
|
220
221
|
# datedTasks and undatedTasks
|
221
|
-
opts[:cmd] =
|
222
|
-
opts[:cmd] =
|
222
|
+
opts[:cmd] = 'tasks' if opts[:cmd] == 'datedTasks'
|
223
|
+
opts[:cmd] = 'tasks' if opts[:cmd] == 'undatedTasks'
|
223
224
|
|
224
225
|
# Make sure opts[:db] and opts[:tasks] are Arrays
|
225
226
|
opts[:db] = [ opts[:db] ] unless opts[:db].is_a?(Array)
|
@@ -248,7 +249,7 @@ module ICalPal
|
|
248
249
|
end
|
249
250
|
|
250
251
|
# Sorting
|
251
|
-
opts[:sort] = 'due_date' if opts[:std]
|
252
|
+
opts[:sort] = 'due_date' if opts[:std] || opts[:stda]
|
252
253
|
opts[:reverse] = true if opts[:std]
|
253
254
|
|
254
255
|
# Colors
|
@@ -276,10 +277,10 @@ module ICalPal
|
|
276
277
|
end
|
277
278
|
|
278
279
|
# Commands that can be run
|
279
|
-
COMMANDS = %w
|
280
|
+
COMMANDS = %w[events eventsToday eventsNow tasks datedTasks undatedTasks calendars accounts stores].freeze
|
280
281
|
|
281
282
|
# Supported output formats
|
282
|
-
OUTFORMATS = %w
|
283
|
+
OUTFORMATS = %w[ansi csv default hash html json md rdoc remind toc xml yaml].freeze
|
283
284
|
|
284
285
|
private
|
285
286
|
|
@@ -288,7 +289,10 @@ module ICalPal
|
|
288
289
|
# @param t [String] Text on the left side
|
289
290
|
# @return [String] Text indented by summary_indent, and padded according to summary_width
|
290
291
|
def pad(t)
|
291
|
-
[ @op.summary_indent, t,
|
292
|
+
[ @op.summary_indent, t, ' ' * (@op.summary_width - t.length) ]
|
292
293
|
end
|
294
|
+
|
293
295
|
end
|
294
296
|
end
|
297
|
+
|
298
|
+
# rubocop: enable Style/FormatString, Style/FormatStringToken
|
data/lib/rdt.rb
CHANGED
@@ -3,6 +3,7 @@ require 'date'
|
|
3
3
|
module ICalPal
|
4
4
|
# Child class of DateTime that adds support for relative dates (<em><b>R</b>elative<b>D</b>ate<b>T</b>ime</em>).
|
5
5
|
class RDT < DateTime
|
6
|
+
|
6
7
|
# Convert a String to an RDT
|
7
8
|
#
|
8
9
|
# @param str [String] can be +yesterday+, +today+, +tomorrow+,
|
@@ -14,8 +15,8 @@ module ICalPal
|
|
14
15
|
when 'yesterday' then $today - 1
|
15
16
|
when 'today' then $today
|
16
17
|
when 'tomorrow' then $today + 1
|
17
|
-
when /^\+([0-9]+)/ then $today +
|
18
|
-
when
|
18
|
+
when /^\+([0-9]+)/ then $today + Regexp.last_match(1).to_i
|
19
|
+
when /^-([0-9]+)/ then $today - Regexp.last_match(1).to_i
|
19
20
|
else parse(str)
|
20
21
|
end
|
21
22
|
end
|
@@ -40,7 +41,7 @@ module ICalPal
|
|
40
41
|
end
|
41
42
|
|
42
43
|
alias inspect to_s
|
43
|
-
|
44
|
+
|
44
45
|
# @see Time.to_a
|
45
46
|
#
|
46
47
|
# @return [Array] Self as an array
|
@@ -56,8 +57,9 @@ module ICalPal
|
|
56
57
|
# @see ICalPal::RDT.to_s
|
57
58
|
#
|
58
59
|
# @return [Boolean]
|
59
|
-
def ==(
|
60
|
-
self.to_s ==
|
60
|
+
def ==(other)
|
61
|
+
self.to_s == other.to_s
|
61
62
|
end
|
63
|
+
|
62
64
|
end
|
63
65
|
end
|