icalPal 3.3.0 → 3.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd12b179a212d0582bd2740c8697f994f75d71fab58ab9ca28c8330e59e33b08
4
- data.tar.gz: ab00df9c7f7b5fce00f8a247f346f5049f019d5336973eaec5e17d1970e773da
3
+ metadata.gz: f2549d35908833fd3fbb48d98187e3ee5618fb3c610dcb03659e8f41b67ac91d
4
+ data.tar.gz: b5999da17435b5eefbc1f19fe8822c45c5ffec0741f5cb58d7f64c313d700ca2
5
5
  SHA512:
6
- metadata.gz: 5b2a5690bcf64c79fff82138dfeb27063dcc79d65ca29082e387cc396c7787d8beeec4beaf5ab56492bfb1739e1dcabfb2012ebceb34b899b90a118a8cf522b5
7
- data.tar.gz: d65423b35e47f5287cb44406f618d8cb90825163d1d416e3dc2b1921483fa67ad6e62873c692fd25173a1485d8507a4f70c276a66e2a626cf0d4573e1e5571c8
6
+ metadata.gz: 64f542aa97dd5b66c28001b0f70ef289a447bc89b5272ed3a4916930a048ea2c2baed4c75b7fe71310611827b0e6e8b0a0dd13665807dba91816e72a6647b0d9
7
+ data.tar.gz: e229b5a9a3261c1f145f1692d4abeb26e653e2e8c7d086d1d0f3a900fe1dfe13a8c68477ddbb22538262d27d1f7fd9cdb7ad302693815e4cb721955c5c379e30
data/README.md CHANGED
@@ -4,11 +4,27 @@
4
4
 
5
5
  ## Description
6
6
 
7
- icalPal is a command-line tool to query a macOS Calendar and Reminders
7
+ icalPal is a command-line tool to query macOS Calendar and Reminders
8
8
  databases for accounts, calendars, events, and tasks. It can be run
9
9
  on any system with [Ruby](https://www.ruby-lang.org/) and access to a
10
10
  Calendar or Reminders database.
11
11
 
12
+ <!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
13
+
14
+ **Table of Contents**
15
+
16
+ - [Installation](#installation)
17
+ - [Features](#features)
18
+ - [Compatability with icalBuddy](#compatability-with-icalbuddy)
19
+ - [Additional commands](#additional-commands)
20
+ - [Additional options](#additional-options)
21
+ - [Usage](#usage)
22
+ - [Output formats](#output-formats)
23
+ - [History](#history)
24
+
25
+ <!-- markdown-toc end -->
26
+
27
+
12
28
  ## Installation
13
29
 
14
30
  As a system-wide Ruby gem:
@@ -23,20 +39,14 @@ or in your home diretory:
23
39
  gem install --user-install icalPal
24
40
  ```
25
41
 
26
- As a Homebrew formula:
27
-
28
- ```
29
- brew tap ajrosen/icalPal
30
- brew install icalPal
31
- ```
32
-
33
42
  ## Features
34
43
 
35
- ### Compatability with [icalBuddy](https://github.com/ali-rantakari/icalBuddy)
44
+ ### Compatability with icalBuddy
36
45
 
37
- icalPal tries to be compatible with icalBuddy for command-line options
38
- and for output. There are a some important differences to be aware
39
- of.
46
+ icalPal tries to be compatible with
47
+ [icalBuddy](https://github.com/ali-rantakari/icalBuddy) for
48
+ command-line options and for output. There are a some important
49
+ differences to be aware of.
40
50
 
41
51
  * Options require two hyphens, except for single-letter options that require one hyphen
42
52
  * *eventsFrom* is not supported. Instead there is *--from*, *--to*, and *--days*
@@ -50,7 +60,8 @@ of.
50
60
 
51
61
  ```icalPal accounts```
52
62
 
53
- Shows a list of enabled Calendar accounts. Internally they are known as *Stores*; you can run ```icalPal stores``` instead.
63
+ Shows a list of enabled Calendar accounts. Internally they are known
64
+ as *Stores*; you can run ```icalPal stores``` instead.
54
65
 
55
66
  ```icalPal datedTasks```
56
67
 
@@ -62,7 +73,7 @@ Shows only reminders that have a due date.
62
73
  * The ```-c``` part is optional, but you cannot abbreviate the command if you leave it off.
63
74
  * Use ```-o``` to print the output in different formats. CSV or JSON are intertesting choices.
64
75
  * Copy your Calendar database file and use ```--db``` on it.
65
- * ```--it``` and ```--et``` will filter by Calendar *type*. Types are **Local**, **Exchange**, **CalDAV**, **MobileMe**, **Subscribed**, and **Birthdays**
76
+ * ```--it``` and ```--et``` will filter by Calendar *type*. Types are **Local**, **Exchange**, **CalDAV**, **MobileMe**, **Subscribed**, **Birthdays**, and **Reminders**
66
77
  * ```--il``` and ```-el``` will filter by Reminder list
67
78
  * ```--ia``` includes *only* all-day events (opposite of ```--ea```)
68
79
  * ```--aep``` is like ```--iep```, but *adds* to the default property list instead of replacing it.
@@ -70,7 +81,9 @@ Shows only reminders that have a due date.
70
81
  * ```--color``` uses a wider color palette. Calendar colors are what you have chosen in the Calendar app. Not supported in all terminals, but looks great in [iTerm2](https://iterm2.com/).
71
82
  * ```--match``` lets you filter the results of any command to items where a *FIELD* matches a regular expression. Eg., ```--match notes=zoom.us``` to show only Zoom meeetings
72
83
 
73
- Because icalPal is written in Ruby, and not a native Mac application, you can run it just about anywhere. It's been tested with the version of Ruby (2.6.10) included with macOS.
84
+ Because icalPal is written in Ruby, and not a native Mac application,
85
+ you can run it just about anywhere. It's been tested with the version
86
+ of Ruby (2.6.10) included with macOS.
74
87
 
75
88
  ## Usage
76
89
 
@@ -93,10 +106,12 @@ COMMAND must be one of the following:
93
106
  Global options:
94
107
  ```
95
108
  -c, --cmd=COMMAND Command to run
96
- --db=DB Use DB file instead of Calendar (default: /Users/ajr/Library/Calendars/Calendar.sqlitedb)
109
+ --db=DB Use DB file instead of Calendar
110
+ (default: ["/Users/user/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb", "/Users/user/Library/Calendars/Calendar.sqlitedb"]
97
111
  For the tasks commands this should be a directory containing .sqlite files
98
- (default: /Users/ajr/Library/Group Containers/group.com.apple.reminders/Container_v1/Stores)
99
- --cf=FILE Set config file path (default: /Users/ajr/.icalpal)
112
+ (default: /Users/user/Library/Group Containers/group.com.apple.reminders/Container_v1/Stores)
113
+ --cf=FILE Set config file path (default: /Users/user/.icalpal)
114
+ --norc Ignore ICALPAL and ICALPAL_CONFIG environment variables
100
115
  -o, --output=FORMAT Print as FORMAT (default: default)
101
116
  [ansi, csv, default, hash, html, json, md, rdoc, remind, toc, xml, yaml]
102
117
  ```
@@ -200,7 +215,9 @@ Environment variables:
200
215
  ```
201
216
  ICALPAL Additional arguments
202
217
  ICALPAL_CONFIG Additional arguments from a file
203
- (default: /Users/ajr/.icalpal)
218
+ (default: /Users/user/.icalpal)
219
+
220
+ Do not quote or escape values. Options set in ICALPAL override ICALPAL_CONFIG. Options on the command line override ICALPAL.
204
221
  ```
205
222
 
206
223
  ## Output formats
@@ -212,7 +229,8 @@ CSV, Hash, JSON, XML, and YAML print all fields for all items in their
212
229
  respective formats. From that you can analyze the results any way you
213
230
  like.
214
231
 
215
- [Remind](https://dianne.skoll.ca/projects/remind/) format uses a minimal implementation built in icalPal.
232
+ [Remind](https://dianne.skoll.ca/projects/remind/) format uses a
233
+ minimal implementation built into icalPal.
216
234
 
217
235
  Other formats such as ANSI, HTML, Markdown, RDoc, and TOC, use Ruby's
218
236
  [RDoc::Markup](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup.html)
@@ -221,7 +239,8 @@ framework to build and render the items.
221
239
  Each item to be printed is a new
222
240
  [RDoc::Markup::Document](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Document.html).
223
241
 
224
- When using one of the <em>separate by</em> options, a section header is added first. The section contains:
242
+ When using one of the <em>separate by</em> options, a section header
243
+ is added first. The section contains:
225
244
 
226
245
  * [RDoc::Markup::BlankLine](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/BlankLine.html)
227
246
  (unless this is the first section)
@@ -248,9 +267,10 @@ information about the item and property to the default formatter.
248
267
  ## History
249
268
 
250
269
  I used icalBuddy for many years. It's great for scripting,
251
- automation, and as a desktop widget for apps like
252
- [GeekTool](https://www.tynsoe.org/geektool/) and
253
- [Übersicht](https://tracesof.net/uebersicht/).
270
+ automation, and as a widget for apps like
271
+ [Übersicht](https://tracesof.net/uebersicht/),
272
+ [GeekTool](https://www.tynsoe.org/geektool/), and
273
+ [SketchyBar](https://felixkratz.github.io/SketchyBar/).
254
274
 
255
275
  As with many applications, I started to run into some limitations in
256
276
  icalBuddy. The biggest being that active development ended in 2014.
@@ -259,7 +279,7 @@ Lawton](https://github.com/jimlawton) that it even compiles anymore.
259
279
 
260
280
  Instead of trying to understand and extend the existing code, I chose
261
281
  to start anew using my language of choice: Ruby. Using Ruby meant
262
- there is *much* less code; about 1,800 lines vs. 7,000. It also means
282
+ there is *much* less code; less than 2,000 lines vs. 7,000. It also means
263
283
  icalPal is multi-platform.
264
284
 
265
285
  I won't pretend to understand **why** you would want to run this on
data/bin/icalPal CHANGED
@@ -10,6 +10,7 @@ begin
10
10
  require 'yaml'
11
11
 
12
12
  require_relative '../lib/icalpal'
13
+ require_relative '../lib/defaults'
13
14
  require_relative '../lib/options'
14
15
  require_relative '../lib/utils'
15
16
  rescue LoadError => e
@@ -29,14 +30,14 @@ end
29
30
  # All kids love log!
30
31
  $log = Logger.new(STDERR, { level: $defaults[:common][:debug] })
31
32
  $log.formatter = proc do |s, t, _p, m| # Severity, time, progname, msg
32
- ($log.level.positive?)? "#{s}: #{m}\n" :
33
- format("[%-5<sev>s] %<time>s [%<caller>s] - %<message>s\n",
34
- {
35
- sev: s,
36
- time: t.strftime('%H:%M:%S.%L'),
37
- caller: caller(4, 1)[0].split('/')[-1],
38
- message: m
39
- })
33
+ format("[%-5<sev>s] %<time>s [%<file>s:%<line>5s] - %<message>s\n",
34
+ {
35
+ sev: s,
36
+ time: t.strftime('%H:%M:%S.%L'),
37
+ file: caller(4, 1)[0].split('/')[-1].split(':')[0],
38
+ line: caller(4, 1)[0].split('/')[-1].split(':')[1],
39
+ message: m
40
+ })
40
41
  end
41
42
 
42
43
  $opts = ICalPal::Options.new.parse_options
@@ -57,9 +58,8 @@ $log.info("Options: #{$opts}")
57
58
  # @param item[Object]
58
59
 
59
60
  def add(item)
60
- $log.info("Adding #{item.inspect} #{item['UUID']} (#{item['title']})") if item['UUID']
61
+ $log.debug("Adding #{item.dump} #{item['UUID']} (#{item['title']})") if item['UUID']
61
62
 
62
- item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if ICalPal::Event === item && item['sdate']
63
63
  $items.push(item)
64
64
  end
65
65
 
@@ -128,108 +128,58 @@ unless success
128
128
  end
129
129
 
130
130
  $log.info("Loaded #{$rows.count} #{klass} rows")
131
+ $log.info("Window is #{$opts[:from]} to #{$opts[:to]}")
131
132
 
132
133
 
133
134
  ##################################################
134
135
  # Process the data
135
136
 
136
137
  # Add rows
137
- $rows.each_with_index do |row, i|
138
- $log.debug("Row #{i}: #{row['ROWID']}:#{row['UUID']} - #{row['account']}/#{row['calendar']}/#{row['title']}")
139
-
138
+ $rows.each do |row|
140
139
  # --es/--is
141
- if $opts[:es].any? row['account']
142
- $log.debug(':es')
143
- next
144
- end
145
-
146
- unless $opts[:is].empty? || ($opts[:is].any? row['account'])
147
- $log.debug(':is')
148
- next
149
- end
140
+ next if $opts[:es].any? row['account']
141
+ next unless $opts[:is].empty? || ($opts[:is].any? row['account'])
150
142
 
151
143
  # --ec/--ic
152
144
  unless klass == ICalPal::Store || !row['calendar']
153
- if $opts[:ec].any? row['calendar']
154
- $log.debug(':ec')
155
- next
156
- end
157
-
158
- unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
159
- $log.debug(':ic')
160
- next
161
- end
145
+ next if $opts[:ec].any? row['calendar']
146
+ next unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
162
147
  end
163
148
 
164
149
  # Instantiate an item
165
150
  item = klass.new(row)
166
151
 
167
152
  # --et/--it
168
- if $opts[:et].any? item['type']
169
- $log.debug(':et')
170
- next
171
- end
172
-
173
- unless $opts[:it].empty? || ($opts[:it].any? item['type'])
174
- $log.debug(':it')
175
- next
176
- end
153
+ next if $opts[:et].any? item['type']
154
+ next unless $opts[:it].empty? || ($opts[:it].any? item['type'])
177
155
 
178
156
  # --el/--il
179
- if $opts[:el].any? item['list_name']
180
- $log.debug(':el')
181
- next
182
- end
183
-
184
- unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
185
- $log.debug(':il')
186
- next
187
- end
157
+ next if $opts[:el].any? item['list_name']
158
+ next unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
188
159
 
189
160
  # --match
190
161
  if $opts[:match]
191
162
  r = $opts[:match].split('=')
192
163
 
193
164
  if item[r[0]].to_s.respond_to?(:match)
194
- unless item[r[0]].to_s.match(Regexp.new(r[1].to_s, Regexp::IGNORECASE))
195
- $log.debug(':match')
196
- next
197
- end
165
+ next unless item[r[0]].to_s =~ Regexp.new(r[1], Regexp::IGNORECASE)
198
166
  end
199
167
  end
200
168
 
201
169
  if ICalPal::Event === item
202
170
  # Check for all-day and cancelled events
203
- if $opts[:ea] && item['all_day'].positive?
204
- $log.debug(':ea')
205
- next
206
- end
207
-
208
- if $opts[:ia] && !item['all_day'].positive?
209
- $log.debug(':ia')
210
- next
211
- end
212
-
213
- if item['status'] == :canceled
214
- $log.debug(':canceled')
215
- next
216
- end
171
+ next if $opts[:ea] && item['all_day'].positive?
172
+ next if $opts[:ia] && !item['all_day'].positive?
173
+ next if item['status'] == :canceled
217
174
 
218
175
  (item['has_recurrences'].positive?)?
219
- item.recurring.each { |i| add(i) } :
220
- item.non_recurring.each { |i| add(i) }
176
+ item.recurring.each { |j| add(j) } :
177
+ item.non_recurring.each { |j| add(j) }
221
178
  else
222
179
  # Check for dated reminders
223
180
  if ICalPal::Reminder === item
224
- if $opts[:dated] == 1 && item['due_date'].positive?
225
- $log.debug(':undated')
226
- next
227
- end
228
-
229
- if $opts[:dated] == 2 && item['due_date'].zero?
230
- $log.debug(':dated')
231
- next
232
- end
181
+ next if $opts[:dated] == 1 && item['due_date'].positive?
182
+ next if $opts[:dated] == 2 && item['due_date'].zero?
233
183
  end
234
184
 
235
185
  add(item)
@@ -248,11 +198,10 @@ end
248
198
 
249
199
  # Sort the rows
250
200
  begin
251
- $log.info("Sorting/uniqing #{$items.count} items by #{[ $opts[:sep], $opts[:sort], 'sdate' ]}, reverse #{$opts[:reverse].inspect}")
201
+ $log.info("Sorting #{$items.count} items by #{[ $opts[:sep], $opts[:sort], 'sdate' ]}, reverse #{$opts[:reverse].inspect}")
252
202
 
253
203
  $items.sort_by! { |i| [ i[$opts[:sep]], i[$opts[:sort]], i['sdate'] ] }
254
204
  $items.reverse! if $opts[:reverse]
255
- $items.uniq!
256
205
  rescue Exception => e
257
206
  $log.info("Sorting failed: #{e}\n")
258
207
  end
@@ -298,7 +247,7 @@ unless mu
298
247
  when 'json' then items.map { |i| i.self }.to_json
299
248
  when 'xml'
300
249
  xml = items.map { |i| "<#{$opts[:cmd].chomp('s')}>#{i.to_xml}</#{$opts[:cmd].chomp('s')}>" }
301
- "<#{$opts[:cmd]}>\n#{xml.join('')}</#{$opts[:cmd]}>"
250
+ "<#{$opts[:cmd]}>\n#{xml.join}</#{$opts[:cmd]}>"
302
251
  when 'yaml' then items.map { |i| i.self }.to_yaml
303
252
  when 'remind' then items.map { |i|
304
253
  "REM #{i['sdate'].strftime('%F AT %R')} " +
@@ -363,14 +312,6 @@ items.each_with_index do |i, j|
363
312
  # First property, value only
364
313
  props << RDoc::Markup::Heading.new(2, value.to_s)
365
314
  end
366
-
367
- # unless k.positive?
368
- # # First property, value only
369
- # props << RDoc::Markup::Heading.new(2, value.to_s)
370
- # else
371
- # props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
372
- # props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless (i['placeholder'])
373
- # end
374
315
  end
375
316
 
376
317
  # Print it
data/bin/icalpal CHANGED
@@ -10,6 +10,7 @@ begin
10
10
  require 'yaml'
11
11
 
12
12
  require_relative '../lib/icalpal'
13
+ require_relative '../lib/defaults'
13
14
  require_relative '../lib/options'
14
15
  require_relative '../lib/utils'
15
16
  rescue LoadError => e
@@ -29,14 +30,14 @@ end
29
30
  # All kids love log!
30
31
  $log = Logger.new(STDERR, { level: $defaults[:common][:debug] })
31
32
  $log.formatter = proc do |s, t, _p, m| # Severity, time, progname, msg
32
- ($log.level.positive?)? "#{s}: #{m}\n" :
33
- format("[%-5<sev>s] %<time>s [%<caller>s] - %<message>s\n",
34
- {
35
- sev: s,
36
- time: t.strftime('%H:%M:%S.%L'),
37
- caller: caller(4, 1)[0].split('/')[-1],
38
- message: m
39
- })
33
+ format("[%-5<sev>s] %<time>s [%<file>s:%<line>5s] - %<message>s\n",
34
+ {
35
+ sev: s,
36
+ time: t.strftime('%H:%M:%S.%L'),
37
+ file: caller(4, 1)[0].split('/')[-1].split(':')[0],
38
+ line: caller(4, 1)[0].split('/')[-1].split(':')[1],
39
+ message: m
40
+ })
40
41
  end
41
42
 
42
43
  $opts = ICalPal::Options.new.parse_options
@@ -57,9 +58,8 @@ $log.info("Options: #{$opts}")
57
58
  # @param item[Object]
58
59
 
59
60
  def add(item)
60
- $log.info("Adding #{item.inspect} #{item['UUID']} (#{item['title']})") if item['UUID']
61
+ $log.debug("Adding #{item.dump} #{item['UUID']} (#{item['title']})") if item['UUID']
61
62
 
62
- item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if ICalPal::Event === item && item['sdate']
63
63
  $items.push(item)
64
64
  end
65
65
 
@@ -128,108 +128,58 @@ unless success
128
128
  end
129
129
 
130
130
  $log.info("Loaded #{$rows.count} #{klass} rows")
131
+ $log.info("Window is #{$opts[:from]} to #{$opts[:to]}")
131
132
 
132
133
 
133
134
  ##################################################
134
135
  # Process the data
135
136
 
136
137
  # Add rows
137
- $rows.each_with_index do |row, i|
138
- $log.debug("Row #{i}: #{row['ROWID']}:#{row['UUID']} - #{row['account']}/#{row['calendar']}/#{row['title']}")
139
-
138
+ $rows.each do |row|
140
139
  # --es/--is
141
- if $opts[:es].any? row['account']
142
- $log.debug(':es')
143
- next
144
- end
145
-
146
- unless $opts[:is].empty? || ($opts[:is].any? row['account'])
147
- $log.debug(':is')
148
- next
149
- end
140
+ next if $opts[:es].any? row['account']
141
+ next unless $opts[:is].empty? || ($opts[:is].any? row['account'])
150
142
 
151
143
  # --ec/--ic
152
144
  unless klass == ICalPal::Store || !row['calendar']
153
- if $opts[:ec].any? row['calendar']
154
- $log.debug(':ec')
155
- next
156
- end
157
-
158
- unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
159
- $log.debug(':ic')
160
- next
161
- end
145
+ next if $opts[:ec].any? row['calendar']
146
+ next unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
162
147
  end
163
148
 
164
149
  # Instantiate an item
165
150
  item = klass.new(row)
166
151
 
167
152
  # --et/--it
168
- if $opts[:et].any? item['type']
169
- $log.debug(':et')
170
- next
171
- end
172
-
173
- unless $opts[:it].empty? || ($opts[:it].any? item['type'])
174
- $log.debug(':it')
175
- next
176
- end
153
+ next if $opts[:et].any? item['type']
154
+ next unless $opts[:it].empty? || ($opts[:it].any? item['type'])
177
155
 
178
156
  # --el/--il
179
- if $opts[:el].any? item['list_name']
180
- $log.debug(':el')
181
- next
182
- end
183
-
184
- unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
185
- $log.debug(':il')
186
- next
187
- end
157
+ next if $opts[:el].any? item['list_name']
158
+ next unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
188
159
 
189
160
  # --match
190
161
  if $opts[:match]
191
162
  r = $opts[:match].split('=')
192
163
 
193
164
  if item[r[0]].to_s.respond_to?(:match)
194
- unless item[r[0]].to_s.match(Regexp.new(r[1].to_s, Regexp::IGNORECASE))
195
- $log.debug(':match')
196
- next
197
- end
165
+ next unless item[r[0]].to_s =~ Regexp.new(r[1], Regexp::IGNORECASE)
198
166
  end
199
167
  end
200
168
 
201
169
  if ICalPal::Event === item
202
170
  # Check for all-day and cancelled events
203
- if $opts[:ea] && item['all_day'].positive?
204
- $log.debug(':ea')
205
- next
206
- end
207
-
208
- if $opts[:ia] && !item['all_day'].positive?
209
- $log.debug(':ia')
210
- next
211
- end
212
-
213
- if item['status'] == :canceled
214
- $log.debug(':canceled')
215
- next
216
- end
171
+ next if $opts[:ea] && item['all_day'].positive?
172
+ next if $opts[:ia] && !item['all_day'].positive?
173
+ next if item['status'] == :canceled
217
174
 
218
175
  (item['has_recurrences'].positive?)?
219
- item.recurring.each { |i| add(i) } :
220
- item.non_recurring.each { |i| add(i) }
176
+ item.recurring.each { |j| add(j) } :
177
+ item.non_recurring.each { |j| add(j) }
221
178
  else
222
179
  # Check for dated reminders
223
180
  if ICalPal::Reminder === item
224
- if $opts[:dated] == 1 && item['due_date'].positive?
225
- $log.debug(':undated')
226
- next
227
- end
228
-
229
- if $opts[:dated] == 2 && item['due_date'].zero?
230
- $log.debug(':dated')
231
- next
232
- end
181
+ next if $opts[:dated] == 1 && item['due_date'].positive?
182
+ next if $opts[:dated] == 2 && item['due_date'].zero?
233
183
  end
234
184
 
235
185
  add(item)
@@ -248,11 +198,10 @@ end
248
198
 
249
199
  # Sort the rows
250
200
  begin
251
- $log.info("Sorting/uniqing #{$items.count} items by #{[ $opts[:sep], $opts[:sort], 'sdate' ]}, reverse #{$opts[:reverse].inspect}")
201
+ $log.info("Sorting #{$items.count} items by #{[ $opts[:sep], $opts[:sort], 'sdate' ]}, reverse #{$opts[:reverse].inspect}")
252
202
 
253
203
  $items.sort_by! { |i| [ i[$opts[:sep]], i[$opts[:sort]], i['sdate'] ] }
254
204
  $items.reverse! if $opts[:reverse]
255
- $items.uniq!
256
205
  rescue Exception => e
257
206
  $log.info("Sorting failed: #{e}\n")
258
207
  end
@@ -298,7 +247,7 @@ unless mu
298
247
  when 'json' then items.map { |i| i.self }.to_json
299
248
  when 'xml'
300
249
  xml = items.map { |i| "<#{$opts[:cmd].chomp('s')}>#{i.to_xml}</#{$opts[:cmd].chomp('s')}>" }
301
- "<#{$opts[:cmd]}>\n#{xml.join('')}</#{$opts[:cmd]}>"
250
+ "<#{$opts[:cmd]}>\n#{xml.join}</#{$opts[:cmd]}>"
302
251
  when 'yaml' then items.map { |i| i.self }.to_yaml
303
252
  when 'remind' then items.map { |i|
304
253
  "REM #{i['sdate'].strftime('%F AT %R')} " +
@@ -363,14 +312,6 @@ items.each_with_index do |i, j|
363
312
  # First property, value only
364
313
  props << RDoc::Markup::Heading.new(2, value.to_s)
365
314
  end
366
-
367
- # unless k.positive?
368
- # # First property, value only
369
- # props << RDoc::Markup::Heading.new(2, value.to_s)
370
- # else
371
- # props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
372
- # props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless (i['placeholder'])
373
- # end
374
315
  end
375
316
 
376
317
  # Print it
data/icalPal.gemspec CHANGED
@@ -16,33 +16,35 @@ EOF
16
16
  s.licenses = [ 'GPL-3.0-or-later' ]
17
17
 
18
18
  s.metadata = {
19
- 'bug_tracker_uri' => "https://github.com/ajrosen/#{s.name}/issues"
19
+ 'bug_tracker_uri' => "https://github.com/ajrosen/#{s.name}/issues",
20
+ 'rubygems_mfa_required' => 'true'
20
21
  }
21
22
 
22
23
  s.files = Dir["#{s.name}.gemspec", 'bin/*', 'lib/*.rb']
23
24
  s.executables = [ "#{s.name}" ]
24
25
  s.extra_rdoc_files = [ 'README.md' ]
25
26
 
26
- s.add_dependency 'nokogiri-plist', '~> 0.5.0'
27
- s.add_dependency 'sqlite3', '~> 2.6.0' unless s.rubygems_version == `/usr/bin/gem --version`.strip
28
-
29
27
  # The macOS and Homebrew versions of rubygems have incompatible
30
28
  # requirements for sqlite3.
31
- #
29
+ #
32
30
  # macOS comes with version 1.3.13, so it does not need to be added
33
31
  # as a dependency, but it cannot install anything newer:
34
- #
32
+ #
35
33
  # requires Ruby version >= 3.0, < 3.4.dev. The current ruby version is 2.6.10.
36
- #
34
+ #
37
35
  # Homebrew's Ruby formula does not come with sqlite3, so it does
38
36
  # need to be added as a dependency, but it cannot install version
39
37
  # 1.3.13:
40
- #
38
+ #
41
39
  # error: call to undeclared function
42
- #
40
+ #
43
41
  # So we must call add_dependency, but iff we are not building with
44
42
  # macOS' Ruby installation.
45
43
 
44
+ s.add_dependency 'nokogiri-plist', '~> 0.5.0'
45
+ s.add_dependency 'sqlite3', '~> 2.6.0' unless s.rubygems_version == `/usr/bin/gem --version`.strip
46
+ s.add_dependency 'timezone', '>= 0.99', '~> 1.3.0'
47
+
46
48
  s.bindir = 'bin'
47
49
  s.required_ruby_version = '>= 2.6.0'
48
50