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.
data/bin/icalpal CHANGED
@@ -11,6 +11,7 @@ begin
11
11
 
12
12
  require_relative '../lib/icalpal'
13
13
  require_relative '../lib/options'
14
+ require_relative '../lib/utils'
14
15
  rescue LoadError => e
15
16
  dep = e.message[/-- (.*)/, 1]
16
17
 
@@ -27,10 +28,15 @@ end
27
28
 
28
29
  # All kids love log!
29
30
  $log = Logger.new(STDERR, { level: $defaults[:common][:debug] })
30
- $log.formatter = proc do |s, t, p, m| # Severity, time, progname, msg
31
+ $log.formatter = proc do |s, t, _p, m| # Severity, time, progname, msg
31
32
  ($log.level.positive?)? "#{s}: #{m}\n" :
32
- "[%-5s] %s [%s] - %s\n" %
33
- [ s, t.strftime('%H:%M:%S.%L'), caller(4, 1)[0].split('/')[-1], m ]
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
+ })
34
40
  end
35
41
 
36
42
  $opts = ICalPal::Options.new.parse_options
@@ -53,7 +59,7 @@ $log.info("Options: #{$opts}")
53
59
  def add(item)
54
60
  $log.info("Adding #{item.inspect} #{item['UUID']} (#{item['title']})") if item['UUID']
55
61
 
56
- item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if item === ICalPal::Event && item['sdate']
62
+ item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if ICalPal::Event === item && item['sdate']
57
63
  $items.push(item)
58
64
  end
59
65
 
@@ -62,22 +68,63 @@ end
62
68
  # Load the data
63
69
 
64
70
  # What are we getting?
65
- klass = ICalPal::($opts[:cmd])
71
+ klass = ICalPal.call($opts[:cmd])
72
+ success = false
66
73
 
67
74
  # Get it
68
75
  $opts[:db].each do |db|
69
- $log.debug("Trying #{db}")
76
+ $log.debug("Trying #{db}")
70
77
 
71
- if klass == ICalPal::Reminder then
78
+ if klass == ICalPal::Reminder
79
+ begin
72
80
  # Load all .sqlite files
73
81
  $log.debug("Loading *.sqlite in #{db}")
74
- Dir.glob("#{db}/*.sqlite").each { |d| $rows += ICalPal.load_data(d, klass::QUERY) }
75
- else
76
- # Load database
77
- begin
78
- $rows += ICalPal.load_data(db, klass::QUERY)
82
+ Dir.glob("#{db}/*.sqlite").each do |d|
83
+ $rows += ICalPal.load_data(d, klass::QUERY)
84
+ success = true
85
+
86
+ rescue SQLite3::CantOpenException
87
+ # Non-fatal exception, try the next one
79
88
  end
89
+ end
90
+ else
91
+ # Load database
92
+ begin
93
+ $rows += ICalPal.load_data(db, klass::QUERY)
94
+ success = true
95
+
96
+ rescue SQLite3::CantOpenException
97
+ # Non-fatal exception, try the next one
98
+ end
99
+ end
100
+ end
101
+
102
+ # Make sure we opened at least one database
103
+ unless success
104
+ $log.fatal('Could not open database')
105
+
106
+ # SQLite3 does not return useful error messages. If any databases
107
+ # failed because of EPERM (operation not permitted), our parent
108
+ # process might need Full Disk Access, and we should suggest that.
109
+ eperm = 0
110
+
111
+ $opts[:db].each do |db|
112
+ # Use a real open to get a useful error
113
+ File.open(db).close
114
+ rescue Exception => e
115
+ $log.fatal("#{e.class}: #{db}")
116
+
117
+ eperm = 1 if e.instance_of?(Errno::EPERM)
80
118
  end
119
+
120
+ if eperm.positive?
121
+ $stderr.puts
122
+ $stderr.puts "Does #{ancestor} have Full Disk Access in System Settings?"
123
+ $stderr.puts
124
+ $stderr.puts "Try running: open 'x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles'"
125
+ end
126
+
127
+ abort
81
128
  end
82
129
 
83
130
  $log.info("Loaded #{$rows.count} #{klass} rows")
@@ -91,25 +138,25 @@ $rows.each_with_index do |row, i|
91
138
  $log.debug("Row #{i}: #{row['ROWID']}:#{row['UUID']} - #{row['account']}/#{row['calendar']}/#{row['title']}")
92
139
 
93
140
  # --es/--is
94
- if $opts[:es].any? row['account'] then
95
- $log.debug(":es")
141
+ if $opts[:es].any? row['account']
142
+ $log.debug(':es')
96
143
  next
97
144
  end
98
145
 
99
- unless $opts[:is].empty? or $opts[:is].any? row['account']
100
- $log.debug(":is");
146
+ unless $opts[:is].empty? || ($opts[:is].any? row['account'])
147
+ $log.debug(':is')
101
148
  next
102
149
  end
103
150
 
104
151
  # --ec/--ic
105
- unless klass == ICalPal::Store or !row['calendar']
106
- if $opts[:ec].any? row['calendar'] then
107
- $log.debug(":ec")
152
+ unless klass == ICalPal::Store || !row['calendar']
153
+ if $opts[:ec].any? row['calendar']
154
+ $log.debug(':ec')
108
155
  next
109
156
  end
110
157
 
111
- unless $opts[:ic].empty? or $opts[:ic].any? row['calendar']
112
- $log.debug(":ic")
158
+ unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
159
+ $log.debug(':ic')
113
160
  next
114
161
  end
115
162
  end
@@ -118,24 +165,24 @@ $rows.each_with_index do |row, i|
118
165
  item = klass.new(row)
119
166
 
120
167
  # --et/--it
121
- if $opts[:et].any? item['type'] then
122
- $log.debug(":et")
168
+ if $opts[:et].any? item['type']
169
+ $log.debug(':et')
123
170
  next
124
171
  end
125
172
 
126
- unless $opts[:it].empty? or $opts[:it].any? item['type']
127
- $log.debug(":it")
173
+ unless $opts[:it].empty? || ($opts[:it].any? item['type'])
174
+ $log.debug(':it')
128
175
  next
129
176
  end
130
177
 
131
178
  # --el/--il
132
- if $opts[:el].any? item['list_name'] then
133
- $log.debug(":el")
179
+ if $opts[:el].any? item['list_name']
180
+ $log.debug(':el')
134
181
  next
135
182
  end
136
183
 
137
- unless $opts[:il].empty? or $opts[:il].any? item['list_name']
138
- $log.debug(":il")
184
+ unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
185
+ $log.debug(':il')
139
186
  next
140
187
  end
141
188
 
@@ -144,8 +191,8 @@ $rows.each_with_index do |row, i|
144
191
  r = $opts[:match].split('=')
145
192
 
146
193
  if item[r[0]].to_s.respond_to?(:match)
147
- unless item[r[0]].to_s.match(Regexp.new(r[1].to_s, Regexp::IGNORECASE)) then
148
- $log.debug(":match")
194
+ unless item[r[0]].to_s.match(Regexp.new(r[1].to_s, Regexp::IGNORECASE))
195
+ $log.debug(':match')
149
196
  next
150
197
  end
151
198
  end
@@ -153,18 +200,18 @@ $rows.each_with_index do |row, i|
153
200
 
154
201
  if ICalPal::Event === item
155
202
  # Check for all-day and cancelled events
156
- if $opts[:ea] && item['all_day'].positive? then
157
- $log.debug(":ea")
203
+ if $opts[:ea] && item['all_day'].positive?
204
+ $log.debug(':ea')
158
205
  next
159
206
  end
160
207
 
161
- if $opts[:ia] && !item['all_day'].positive? then
162
- $log.debug(":ia")
208
+ if $opts[:ia] && !item['all_day'].positive?
209
+ $log.debug(':ia')
163
210
  next
164
211
  end
165
212
 
166
- if item['status'] == :canceled then
167
- $log.debug(":canceled")
213
+ if item['status'] == :canceled
214
+ $log.debug(':canceled')
168
215
  next
169
216
  end
170
217
 
@@ -173,14 +220,14 @@ $rows.each_with_index do |row, i|
173
220
  item.non_recurring.each { |i| add(i) }
174
221
  else
175
222
  # Check for dated reminders
176
- if ICalPal::Reminder === item then
177
- if $opts[:dated] == 1 and item['due_date'] > 0 then
178
- $log.debug(":undated")
223
+ if ICalPal::Reminder === item
224
+ if $opts[:dated] == 1 && item['due_date'].positive?
225
+ $log.debug(':undated')
179
226
  next
180
227
  end
181
228
 
182
- if $opts[:dated] == 2 and item['due_date'] == 0 then
183
- $log.debug(":dated")
229
+ if $opts[:dated] == 2 && item['due_date'].zero?
230
+ $log.debug(':dated')
184
231
  next
185
232
  end
186
233
  end
@@ -216,7 +263,7 @@ $log.debug("#{$items.count} items remain")
216
263
  mu = case $opts[:output]
217
264
  when 'ansi' then RDoc::Markup::ToAnsi.new
218
265
  when 'default' then RDoc::Markup::ToICalPal.new($opts)
219
- when 'html' then
266
+ when 'html'
220
267
  rdoc = RDoc::Options.new
221
268
  rdoc.pipe = true
222
269
  rdoc.output_decoration = false
@@ -236,7 +283,7 @@ unless mu
236
283
  $log.debug("Output in #{$opts[:output]} format")
237
284
 
238
285
  puts case $opts[:output]
239
- when 'csv' then
286
+ when 'csv'
240
287
  # Get all headers
241
288
  headers = []
242
289
  items.each { |i| headers += i.keys }
@@ -249,15 +296,15 @@ unless mu
249
296
  table
250
297
  when 'hash' then items.map { |i| i.self }
251
298
  when 'json' then items.map { |i| i.self }.to_json
252
- when 'xml' then
253
- xml = items.map { |i| "<#{$opts[:cmd].chomp("s")}>#{i.to_xml}</#{$opts[:cmd].chomp("s")}>" }
254
- "<#{$opts[:cmd]}>\n#{xml.join("")}</#{$opts[:cmd]}>"
299
+ when 'xml'
300
+ xml = items.map { |i| "<#{$opts[:cmd].chomp('s')}>#{i.to_xml}</#{$opts[:cmd].chomp('s')}>" }
301
+ "<#{$opts[:cmd]}>\n#{xml.join('')}</#{$opts[:cmd]}>"
255
302
  when 'yaml' then items.map { |i| i.self }.to_yaml
256
303
  when 'remind' then items.map { |i|
257
- "REM #{i['sdate'].strftime('%F AT %R')} " +
258
- "DURATION #{((i['edate'] - i['sdate']).to_f * 1440).to_i } " +
259
- "MSG #{i['title']}"
260
- }.join("\n")
304
+ "REM #{i['sdate'].strftime('%F AT %R')} " +
305
+ "DURATION #{((i['edate'] - i['sdate']).to_f * 1440).to_i} " +
306
+ "MSG #{i['title']}"
307
+ }.join("\n")
261
308
  else abort "No formatter for #{$opts[:output]}"
262
309
  end
263
310
 
@@ -302,20 +349,28 @@ items.each_with_index do |i, j|
302
349
 
303
350
  next unless value
304
351
  next if Array === value && !value[0]
305
- next if String === value && value.length == 0
352
+ next if String === value && value.empty?
306
353
 
307
354
  $log.debug("#{prop}: #{value}")
308
355
 
309
356
  # Use Raw to save the property
310
357
  props << RDoc::Markup::Raw.new(prop)
311
358
 
312
- unless k.positive?
359
+ if k.positive?
360
+ props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
361
+ props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless (i['placeholder'])
362
+ else
313
363
  # First property, value only
314
364
  props << RDoc::Markup::Heading.new(2, value.to_s)
315
- else
316
- props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
317
- props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless(i['placeholder'])
318
365
  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
319
374
  end
320
375
 
321
376
  # Print it
data/icalPal.gemspec CHANGED
@@ -1,27 +1,55 @@
1
- require './lib/version'
1
+ require_relative 'lib/version'
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = ICalPal::NAME
5
5
  s.version = ICalPal::VERSION
6
6
 
7
- s.summary = "Command-line tool to query the macOS Calendar"
7
+ s.summary = 'Command-line tool to query the macOS Calendar'
8
8
  s.description = <<-EOF
9
9
  Inspired by icalBuddy and maintains close compatability. Includes
10
10
  many additional features for querying, filtering, and formatting.
11
11
  EOF
12
12
 
13
- s.authors = "Andy Rosen"
14
- s.email = "ajr@corp.mlfs.org"
13
+ s.authors = 'Andy Rosen'
14
+ s.email = 'ajr@corp.mlfs.org'
15
15
  s.homepage = "https://github.com/ajrosen/#{s.name}"
16
- s.licenses = [ "GPL-3.0-or-later" ]
16
+ s.licenses = [ 'GPL-3.0-or-later' ]
17
17
 
18
- s.files = Dir[ "#{s.name}.gemspec", "bin/*", "lib/*.rb" ]
18
+ s.metadata = {
19
+ 'bug_tracker_uri' => "https://github.com/ajrosen/#{s.name}/issues"
20
+ }
21
+
22
+ s.files = Dir["#{s.name}.gemspec", 'bin/*', 'lib/*.rb']
19
23
  s.executables = [ "#{s.name}" ]
20
- s.extra_rdoc_files = [ "README.md" ]
24
+ s.extra_rdoc_files = [ 'README.md' ]
25
+
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
21
28
 
22
- s.add_runtime_dependency "sqlite3", "~> 1"
23
- s.add_runtime_dependency "nokogiri-plist", "~> 0.5.0"
29
+ # The macOS and Homebrew versions of rubygems have incompatible
30
+ # requirements for sqlite3.
31
+ #
32
+ # macOS comes with version 1.3.13, so it does not need to be added
33
+ # as a dependency, but it cannot install anything newer:
34
+ #
35
+ # requires Ruby version >= 3.0, < 3.4.dev. The current ruby version is 2.6.10.
36
+ #
37
+ # Homebrew's Ruby formula does not come with sqlite3, so it does
38
+ # need to be added as a dependency, but it cannot install version
39
+ # 1.3.13:
40
+ #
41
+ # error: call to undeclared function
42
+ #
43
+ # So we must call add_dependency, but iff we are not building with
44
+ # macOS' Ruby installation.
24
45
 
25
46
  s.bindir = 'bin'
26
47
  s.required_ruby_version = '>= 2.6.0'
48
+
49
+ s.post_install_message = <<-EOF
50
+
51
+ Note: #{ICalPal::NAME} requires "Full Disk Access" in System Settings to access your calendar.
52
+ Make sure the program that runs #{ICalPal::NAME}, not #{ICalPal::NAME} itself, has these permissions.
53
+
54
+ EOF
27
55
  end
data/lib/EventKit.rb CHANGED
@@ -1,9 +1,12 @@
1
+ # rubocop: disable Naming/ConstantName
2
+
1
3
  # Constants from EventKit[https://developer.apple.com/documentation/eventkit/]
4
+
2
5
  class EventKit
3
- EKEntityType = [
4
- 'event',
5
- 'reminder',
6
- ]
6
+ EKEntityType = %w[
7
+ event
8
+ reminder
9
+ ].freeze
7
10
 
8
11
  EKEventAvailability = {
9
12
  notSupported: -1,
@@ -11,28 +14,28 @@ class EventKit
11
14
  free: 1,
12
15
  tentative: 2,
13
16
  unavailable: 3,
14
- }
17
+ }.freeze
15
18
 
16
19
  EKEventStatus = {
17
20
  none: 0,
18
21
  confirmed: 1,
19
22
  tentative: 2,
20
23
  canceled: 3,
21
- }
24
+ }.freeze
22
25
 
23
- EKRecurrenceFrequency = [
24
- 'daily',
25
- 'weekly',
26
- 'monthly',
27
- 'yearly',
28
- ]
26
+ EKRecurrenceFrequency = %w[
27
+ daily
28
+ weekly
29
+ monthly
30
+ yearly
31
+ ].freeze
29
32
 
30
33
  EKReminderProperty = [
31
34
  'none', # 0
32
35
  'high', nil, nil, nil, # 1
33
36
  'medium', nil, nil, nil, # 5
34
37
  'low', # 9
35
- ]
38
+ ].freeze
36
39
 
37
40
  # EKSourceType (with color)
38
41
  EKSourceType = [
@@ -43,10 +46,13 @@ class EventKit
43
46
  { name: 'Subscribed', color: '#FF0000' }, # Red
44
47
  { name: 'Birthdays', color: '#FF00FF' }, # Magenta
45
48
  { name: 'Reminders', color: '#066FF3' }, # Blue
46
- ]
49
+ ].freeze
50
+
51
+ EKSpan = %w[
52
+ this
53
+ future
54
+ ].freeze
47
55
 
48
- EKSpan = [
49
- 'this',
50
- 'future',
51
- ]
52
56
  end
57
+
58
+ # rubocop: enable Naming/ConstantName
data/lib/ToICalPal.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  ##################################################
2
2
  # Render an RDoc::Markup::Document, closely mimicking
3
3
  # icalBuddy[https://github.com/ali-rantakari/icalBuddy]
4
-
5
4
  class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
6
5
  # Standard
7
6
  # ANSI[https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.416-199303-I!!PDF-E&type=items]
@@ -26,25 +25,25 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
26
25
  'pink': '38;2;255;45;85',
27
26
  'purple': '38;2;204;115;225',
28
27
  'rose': '38;2;217;166;159',
29
- }
28
+ }.freeze
30
29
 
31
30
  # Increased intensity
32
- BOLD = '%c[1m' % 27.chr
31
+ BOLD = format('%c[1m', 27.chr)
33
32
 
34
33
  # Default rendition
35
- NORM = '%c[0m' % 27.chr
34
+ NORM = format('%c[0m', 27.chr)
36
35
 
37
36
  # Properties for which we don't include labels
38
- NO_LABEL = [ 'title', 'datetime' ]
37
+ NO_LABEL = %w[ title datetime ].freeze
39
38
 
40
39
  # Properties that are always colorized
41
- COLOR_LABEL = [ 'title', 'calendar' ]
40
+ COLOR_LABEL = %w[ title calendar ].freeze
42
41
 
43
42
  # Default color for labels
44
- LABEL_COLOR = [ 'cyan', '#00ffff' ]
43
+ LABEL_COLOR = [ 'cyan', '#00ffff' ].freeze
45
44
 
46
45
  # Color for datetime value
47
- DATE_COLOR = [ 'yellow', '#ffff00' ]
46
+ DATE_COLOR = [ 'yellow', '#ffff00' ].freeze
48
47
 
49
48
  # @param opts [Hash] Used for conditional formatting
50
49
  # @option opts [String] :bullet Bullet
@@ -56,6 +55,7 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
56
55
  # @option opts [Array<String>] :ps List of property separators
57
56
  # @option opts [String] :ss Section separator
58
57
  def initialize(opts)
58
+ super(opts)
59
59
  @opts = opts
60
60
  end
61
61
 
@@ -72,15 +72,15 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
72
72
 
73
73
  # Add a bullet for the first property of an item
74
74
  #
75
- # @param arg [Array] Ignored
76
- def accept_list_start(arg)
75
+ # @param _arg [Array] Ignored
76
+ def accept_list_start(_arg)
77
77
  begin
78
78
  return if @item['placeholder']
79
79
  rescue
80
80
  end
81
81
 
82
82
  begin
83
- if (@item['due_date'] + ICalPal::ITIME).between?(ICalPal::ITIME + 1, $now.to_i) then
83
+ if (@item['due_date'] + ICalPal::ITIME).between?(ICalPal::ITIME + 1, $now.to_i)
84
84
  @res << "#{@opts[:ab]} " unless @opts[:nb]
85
85
  return
86
86
  end
@@ -96,15 +96,15 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
96
96
  # @option arg [String] .label Contains the property name
97
97
  def accept_list_item_start(arg)
98
98
  @res << @opts[:ps][@ps] || ' ' unless @item['placeholder']
99
- @res << colorize(*LABEL_COLOR, arg.label) << ": " unless @opts[:npn] || NO_LABEL.any?(arg.label)
99
+ @res << colorize(*LABEL_COLOR, arg.label) << ': ' unless @opts[:npn] || NO_LABEL.any?(arg.label)
100
100
 
101
101
  @ps += 1 unless @ps == @opts[:ps].count - 1
102
102
  end
103
103
 
104
104
  # Add a blank line
105
105
  #
106
- # @param arg [Array] Ignored
107
- def accept_blank_line(*arg)
106
+ # @param *_arg [Array] Ignored
107
+ def accept_blank_line(*_arg)
108
108
  @res << "\n"
109
109
  end
110
110
 
@@ -119,9 +119,9 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
119
119
  @res << h.text
120
120
 
121
121
  case h.level
122
- when 1 then
123
- @res << ":"
124
- when 2 then
122
+ when 1
123
+ @res << ':'
124
+ when 2
125
125
  if @prop == 'title' && @item['calendar']
126
126
  @res << bold(" (#{@item['calendar']})") unless @opts[:nc] || @item['title'] == @item['calendar']
127
127
  end
@@ -140,8 +140,8 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
140
140
 
141
141
  # Add a section separator
142
142
  #
143
- # @param weight Ignored
144
- def accept_rule(weight)
143
+ # @param _weight Ignored
144
+ def accept_rule(_weight)
145
145
  @res << @opts[:ss]
146
146
  accept_blank_line
147
147
  end
@@ -167,6 +167,7 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
167
167
  # @return [String] str with increased intensity[#BOLD]
168
168
  def bold(str)
169
169
  return str unless @opts[:palette]
170
+
170
171
  BOLD + str + NORM
171
172
  end
172
173
 
@@ -177,27 +178,28 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
177
178
  return str unless c8 && c24 && @opts[:palette]
178
179
 
179
180
  case @opts[:palette]
180
- when 8 then # Default colour table
181
+ when 8 # Default colour table
181
182
  c = ANSI[c8.downcase.to_sym]
182
183
  c ||= ANSI[c24[0..6].downcase.to_sym]
183
184
  c ||= ANSI[:white]
184
185
 
185
- when 24 then # Direct colour in RGB space
186
+ when 24 # Direct colour in RGB space
186
187
  rgb = c24[1..].split(/(\h\h)(\h\h)(\h\h)/)
187
188
  rgb.map! { |i| i.to_i(16) }
188
189
  c = [ 38, 2, rgb[1..] ].join(';')
189
190
  end
190
191
 
191
- sprintf('%c[%sm%s%c[%sm', 27.chr, c, str, 27.chr, ANSI[:default])
192
+ # esc c str esc ansi
193
+ format('%<esc>c[%<color>sm%<string>s%<esc>c[%<ansi_default>sm',
194
+ { esc: 27.chr, color: c, string: str, ansi_default: ANSI[:default] })
192
195
  end
193
196
 
194
197
  # @!visibility private
195
198
 
196
199
  # @param a [Array] Ignored
197
- def accept_list_end(a)
198
- end
200
+ def accept_list_end(a) end
199
201
 
200
202
  # @param a [Array] Ignored
201
- def accept_list_item_end(a)
202
- end
203
+ def accept_list_item_end(a) end
204
+
203
205
  end
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: [ 'title', 'notes', 'due', 'priority' ],
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: [ 'title', 'notes', 'due', 'priority' ],
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: [ 'title', 'notes', 'due', 'priority' ],
55
+ iep: %w[ title notes due priority ],
56
56
  sort: 'prio',
57
57
  },
58
58
  stores: {
59
- iep: [ 'account', 'type' ],
59
+ iep: %w[ account type ],
60
60
  sort: 'account',
61
61
  },
62
62
  calendars: {
63
- iep: [ 'calendar', 'type', 'UUID' ],
63
+ iep: %w[ calendar type UUID ],
64
64
  sort: 'calendar',
65
65
  },
66
66
  events: {
@@ -69,10 +69,11 @@ $defaults = {
69
69
  eed: false,
70
70
  eep: [],
71
71
  from: $today,
72
- iep: [ 'title', 'location', 'notes', 'url', 'attendees', 'datetime' ],
72
+ iep: %w[ title location notes url attendees datetime ],
73
73
  n: false,
74
74
  nnr: "\n ",
75
75
  nrd: false,
76
+ now: false,
76
77
  ps: [ "\n " ],
77
78
  sa: false,
78
79
  sed: false,