icalPal 3.1.1 → 3.2.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 +90 -56
- data/icalPal.gemspec +9 -9
- data/lib/EventKit.rb +24 -18
- data/lib/ToICalPal.rb +28 -26
- data/lib/calendar.rb +1 -2
- data/lib/defaults.rb +7 -7
- data/lib/event.rb +54 -53
- data/lib/icalPal.rb +35 -23
- data/lib/options.rb +39 -35
- data/lib/rdt.rb +7 -5
- data/lib/reminder.rb +22 -22
- data/lib/store.rb +1 -1
- data/lib/version.rb +2 -2
- metadata +9 -13
- data/bin/icalpal +0 -327
data/lib/calendar.rb
CHANGED
@@ -3,7 +3,7 @@ module ICalPal
|
|
3
3
|
class Calendar
|
4
4
|
include ICalPal
|
5
5
|
|
6
|
-
QUERY = <<~SQL
|
6
|
+
QUERY = <<~SQL.freeze
|
7
7
|
SELECT DISTINCT
|
8
8
|
|
9
9
|
Store.name AS account,
|
@@ -16,7 +16,6 @@ JOIN Store ON store_id = Store.rowid
|
|
16
16
|
|
17
17
|
WHERE Store.disabled IS NOT 1
|
18
18
|
AND Store.display_order IS NOT -1
|
19
|
-
AND (Calendar.display_order IS NOT -1 OR external_rep IS NOT NULL)
|
20
19
|
AND Calendar.flags IS NOT 519
|
21
20
|
SQL
|
22
21
|
|
data/lib/defaults.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Does anybody really know what time it is?
|
2
2
|
$now = ICalPal::RDT.now
|
3
|
-
$today = ICalPal::RDT.new(*$now.to_a[0..2] + [0, 0, 0, $now.zone])
|
3
|
+
$today = ICalPal::RDT.new(*$now.to_a[0..2] + [ 0, 0, 0, $now.zone ])
|
4
4
|
|
5
5
|
# Defaults
|
6
6
|
$defaults = {
|
@@ -40,27 +40,27 @@ $defaults = {
|
|
40
40
|
tasks: {
|
41
41
|
dated: 0,
|
42
42
|
db: [ ICalPal::Reminder::DB_PATH ],
|
43
|
-
iep: [
|
43
|
+
iep: %w[ title notes due priority ],
|
44
44
|
sort: 'prio',
|
45
45
|
},
|
46
46
|
undatedTasks: {
|
47
47
|
dated: 1,
|
48
48
|
db: [ ICalPal::Reminder::DB_PATH ],
|
49
|
-
iep: [
|
49
|
+
iep: %w[ title notes due priority ],
|
50
50
|
sort: 'prio',
|
51
51
|
},
|
52
52
|
datedTasks: {
|
53
53
|
dated: 2,
|
54
54
|
db: [ ICalPal::Reminder::DB_PATH ],
|
55
|
-
iep: [
|
55
|
+
iep: %w[ title notes due priority ],
|
56
56
|
sort: 'prio',
|
57
57
|
},
|
58
58
|
stores: {
|
59
|
-
iep: [
|
59
|
+
iep: %w[ account type ],
|
60
60
|
sort: 'account',
|
61
61
|
},
|
62
62
|
calendars: {
|
63
|
-
iep: [
|
63
|
+
iep: %w[ calendar type UUID ],
|
64
64
|
sort: 'calendar',
|
65
65
|
},
|
66
66
|
events: {
|
@@ -69,7 +69,7 @@ $defaults = {
|
|
69
69
|
eed: false,
|
70
70
|
eep: [],
|
71
71
|
from: $today,
|
72
|
-
iep: [
|
72
|
+
iep: %w[ title location notes url attendees datetime ],
|
73
73
|
n: false,
|
74
74
|
nnr: "\n ",
|
75
75
|
nrd: false,
|
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,16 +118,16 @@ 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
133
|
events.push(clone) if in_window?(self['sdate'])
|
@@ -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
|
@@ -285,26 +288,24 @@ module ICalPal
|
|
285
288
|
# @param e [RDT] Event end
|
286
289
|
# @return [Boolean]
|
287
290
|
def in_window?(s, e = s)
|
288
|
-
if $opts[:n]
|
289
|
-
if ($now >= s && $now < e)
|
291
|
+
if $opts[:n]
|
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,8 +104,8 @@ 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
|
@@ -118,26 +119,26 @@ module ICalPal
|
|
118
119
|
def xmlify(key, value)
|
119
120
|
case value
|
120
121
|
# Nil
|
121
|
-
when NilClass then
|
122
|
+
when NilClass then "<#{key}/>"
|
122
123
|
|
123
124
|
# String, Integer
|
124
|
-
when String then
|
125
|
-
when Integer then
|
125
|
+
when String then "<#{key}>#{value}</#{key}>"
|
126
|
+
when Integer then "<#{key}>#{value}</#{key}>"
|
126
127
|
|
127
128
|
# Array
|
128
|
-
when Array
|
129
|
+
when Array
|
129
130
|
# Treat empty arrays as nil values
|
130
|
-
|
131
|
+
xmlify(key, nil) if value[0].nil?
|
131
132
|
|
132
|
-
retval =
|
133
|
+
retval = ''
|
133
134
|
value.each { |x| retval += xmlify("#{key}0", x) }
|
134
|
-
|
135
|
+
"<#{key}>#{retval}</#{key}>"
|
135
136
|
|
136
137
|
# RDT
|
137
|
-
when ICalPal::RDT then
|
138
|
+
when ICalPal::RDT then "<#{key}>#{value}</#{key}>"
|
138
139
|
|
139
140
|
# Unknown
|
140
|
-
else
|
141
|
+
else "<#{key}>#{value}</#{key}>"
|
141
142
|
end
|
142
143
|
end
|
143
144
|
|
@@ -150,7 +151,7 @@ module ICalPal
|
|
150
151
|
def self.nth(n, dow, m)
|
151
152
|
# Get the number of days in the month
|
152
153
|
a = [ ICalPal::RDT.new(m.year, m.month, 1) ] # First of this month
|
153
|
-
a[1] = (a[0] >> 1) - 1
|
154
|
+
a[1] = (a[0] >> 1) - 1 # First of next month, minus 1 day
|
154
155
|
|
155
156
|
# Reverse it if going backwards
|
156
157
|
a.reverse! if n.negative?
|
@@ -164,17 +165,28 @@ module ICalPal
|
|
164
165
|
end
|
165
166
|
|
166
167
|
# Epoch + 31 years
|
167
|
-
ITIME =
|
168
|
+
ITIME = 978_307_200
|
168
169
|
|
169
170
|
# Days of the week abbreviations used in recurrence rules
|
170
171
|
#
|
171
172
|
# <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 }
|
173
|
+
DOW = { 'SU': 0, 'MO': 1, 'TU': 2, 'WE': 3, 'TH': 4, 'FR': 5, 'SA': 6 }.freeze
|
173
174
|
|
174
175
|
# @!group Accessors
|
175
|
-
def [](k)
|
176
|
-
|
177
|
-
|
178
|
-
|
176
|
+
def [](k)
|
177
|
+
@self[k]
|
178
|
+
end
|
179
|
+
|
180
|
+
def []=(k, v)
|
181
|
+
@self[k] = v
|
182
|
+
end
|
183
|
+
|
184
|
+
def keys
|
185
|
+
@self.keys
|
186
|
+
end
|
187
|
+
|
188
|
+
def values
|
189
|
+
@self.values
|
190
|
+
end
|
179
191
|
# @!endgroup
|
180
192
|
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
|
|
@@ -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
|