icalPal 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a530f29b36a30e180b8bf2a82c3107a2bfa40a12fc62e1fd5431696c9fc860a0
4
+ data.tar.gz: 95caaf61238e643ef328dd5dfafaa00b4a14c50e4565ca5498ddf660e24245f3
5
+ SHA512:
6
+ metadata.gz: 8624c9eeea269874b876205cb556cff492bcd39d9196a428a1f95a3995ca997545ded47749841c8e113a6c4862405d85f053e99f9fc03ddaa877a3b543189eb2
7
+ data.tar.gz: 8870801cc536557e2cb8c4eb1e6a7831ae0c73ced12ff41be3a4a0e19ea482a934b514b9eee6228e09d2b82e2bdc7b51db7c7e1707c3fd600a6bc81bb5054bdd
data/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # icalPal
2
+
3
+ ## Description
4
+
5
+ icalPal is a command-line tool to query a macOS Calendar database for
6
+ accounts, calendars, and events. It can be run on any system with
7
+ [Ruby](https://www.ruby-lang.org/) and access to a Calendar database
8
+ file.
9
+
10
+ ## Installation
11
+
12
+ <code>gem install icalPal</code>
13
+
14
+
15
+ ## Usage
16
+
17
+ ical: Usage: ical [options] [-c] COMMAND
18
+
19
+ COMMAND must be one of the following:
20
+
21
+ events Print events
22
+ calendars Print calendars
23
+ accounts Print accounts
24
+
25
+ eventsToday Print events occurring today
26
+ eventsToday+NUM Print events occurring between today and NUM days into the future
27
+ eventsNow Print events occurring at present time
28
+
29
+ Global options:
30
+
31
+ -c, --cmd=COMMAND Command to run
32
+ --db=DB Use DB file instead of Calendar
33
+ --cf=FILE Set config file path (default: $HOME/.icalPal)
34
+ -o, --output=FORMAT Print as FORMAT (default: default)
35
+ [ansi, csv, default, hash, html, json, md, rdoc, toc, yaml]
36
+
37
+ Including/excluding calendars:
38
+
39
+ --is=ACCOUNTS List of accounts to include
40
+ --es=ACCOUNTS List of accounts to exclude
41
+
42
+ --it=TYPES List of calendar types to include
43
+ --et=TYPES List of calendar types to exclude
44
+ [Local, Exchange, CalDAV, MobileMe, Subscribed, Birthdays]
45
+
46
+ --ic=CALENDARS List of calendars to include
47
+ --ec=CALENDARS List of calendars to exclude
48
+
49
+ Choosing dates:
50
+
51
+ --from=DATE List events starting on or after DATE
52
+ --to=DATE List events starting on or before DATE
53
+ DATE can be yesterday, today, tomorrow, +N, -N, or anything accepted by DateTime.parse()
54
+ See https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-c-parse
55
+
56
+ -n Include only events from now on
57
+ --days=N Show N days of events, including start date
58
+ --sed Show empty dates with --sd
59
+ --ia Include only all-day events
60
+ --ea Exclude all-day events
61
+
62
+ Choose properties to include in the output:
63
+
64
+ --iep=PROPERTIES List of properties to include
65
+ --eep=PROPERTIES List of properties to exclude
66
+ --aep=PROPERTIES List of properties to include in addition to the default list
67
+
68
+ --uid Show event UIDs
69
+ --eed Exclude end datetimes
70
+
71
+ --nc No calendar names
72
+ --npn No property names
73
+ --nrd No relative dates
74
+
75
+ Properties are listed in the order specified
76
+
77
+ Use 'all' for PROPERTIES to include all available properties (except any listed in --eep)
78
+ Use 'list' for PROPERTIES to list all available properties and exit
79
+
80
+ Formatting the output:
81
+
82
+ --li=N Show at most N items (default: 0 for no limit)
83
+
84
+ --sc Separate by calendar
85
+ --sd Separate by date
86
+ --sep=PROPERTY Separate by PROPERTY
87
+
88
+ --sort=PROPERTY Sort by PROPERTY
89
+ -r, --reverse Sort in reverse
90
+
91
+ --ps=SEPARATORS List of property separators
92
+ --ss=SEPARATOR Set section separator
93
+
94
+ --df=FORMAT Set date format
95
+ --tf=FORMAT Set time format
96
+ See https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-i-strftime for details
97
+
98
+ -b, --ab=STRING Use STRING for bullets
99
+ --nnr=SEPARATOR Set replacement for newlines within notes
100
+
101
+ -f Format output using standard ANSI colors
102
+ --color Format output using a larger color palette
103
+
104
+ Help:
105
+
106
+ -h, --help Show this message
107
+ -V, -v, --version Show version and exit (1.0)
108
+ -d, --debug=LEVEL Set the logging level (default: warn)
109
+ [debug, info, warn, error, fatal]
110
+
111
+ Environment variables:
112
+
113
+ ICALPAL Additional arguments
114
+ ICALPAL_CONFIG Additional arguments from a file
115
+ (default: $HOME/.icalPal)
116
+
117
+
118
+ ## History
119
+
120
+ If you've found this page it's likely you've heard of [icalBuddy](https://github.com/ali-rantakari/icalBuddy):
121
+
122
+ > Command-line utility for printing events and tasks from the OS X calendar database.
123
+
124
+ I have used icalBuddy for many years. It's great for scripting,
125
+ automation, and as a desktop widget for apps like
126
+ [GeekTool](https://www.tynsoe.org/geektool/) and
127
+ [Übersicht](https://tracesof.net/uebersicht/).
128
+
129
+ As with many applications, I started to run into some limitations in
130
+ icalBuddy. The biggest being that active development ended over 8
131
+ years ago. It's only thanks to the efforts of [Jim
132
+ Lawton](https://github.com/jimlawton) that it even compiles anymore.
133
+
134
+ Instead of trying to understand and extend the existing code, I chose
135
+ to start anew using my language of choice.
136
+
137
+ - Output in CSV, JSON, HTML, Markdown, and [more](#label-Output+formats)
138
+ - Enhanced color option[#label-Usage]
139
+ - Show and filter by Account
140
+ - Show and filter by Calendar type
141
+ - Select a different Calendar database
142
+ - Multi-platform
143
+ - Much less code (1200 lines vs. 7000)
144
+
145
+ I won't pretend to understand **why** you would want this on Linux or
146
+ Windows. But since icalPal is written in Ruby and gets its data
147
+ directly from the Calendar database file instead of an API, you *can*.
148
+
149
+ ## Output formats
150
+
151
+ icalPal supports several output formats. The +default+ format tries
152
+ to mimic icalBuddy as much as possible.
153
+
154
+ CSV, Hash, JSON, and YAML print all fields for all items in their
155
+ respective formats. From that you can analyze the results any way you like.
156
+
157
+ All other formats, ANSI, HTML, Markdown, RDoc, and TOC, use Ruby's
158
+ [RDoc::Markup](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup.html)
159
+ framework to build and render the items.
160
+
161
+ Each item to be printed is a new
162
+ [RDoc::Markup::Document](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Document.html).
163
+
164
+ When using one of the <em>separate by</em> options, a section header is added first. The section contains:
165
+
166
+ * [RDoc::Markup::BlankLine](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/BlankLine.html)
167
+ (unless this is the first section)
168
+ * RDoc::Markup::Heading (level 1)
169
+ * [RDoc::Markup::Rule](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Rule.html)
170
+
171
+ The rest of the document is a series of
172
+ [RDoc::Markup::List](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/List.html)
173
+ objects, one for each of the item's properties:
174
+
175
+ * [RDoc::Markup::List](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/List.html)
176
+ * RDoc::Markup::Heading (level 2)
177
+ * [RDoc::Markup::BlankLine](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/BlankLine.html)
178
+ * [RDoc::Markup::ListItem](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/ListItem.html)
179
+ * [RDoc::Markup::Paragraph](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Paragraph.html)
180
+
181
+ The document will also include a number of
182
+ [RDoc::Markup::Verbatim](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup/Verbatim.html)
183
+ items. The are not included in the output, but are used to pass
184
+ information about the item and property to the default formatter.
data/bin/icalPal ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env ruby
2
+ # *-*- mode: enh-ruby -*-*
3
+
4
+ require 'logger'
5
+
6
+ require 'csv'
7
+ require 'json'
8
+ require 'rdoc'
9
+ require 'sqlite3'
10
+ require 'yaml'
11
+
12
+ require_relative '../lib/icalPal'
13
+ require_relative '../lib/options'
14
+
15
+
16
+ ##################################################
17
+ # Load options
18
+
19
+ # All kids love log!
20
+ $log = Logger.new(STDERR, { datetime_format: '%H:%M:%S.%L', level: $defaults[:common][:debug] })
21
+ $log.formatter = proc do |s, t, p, m| # Severity, time, progname, msg
22
+ ($log.level.positive?)? "#{s}: #{m}\n" :
23
+ "[%-5s] %s [%s] - %s\n" %
24
+ [ s, t.strftime($log.datetime_format), caller(4, 1)[0].split('/')[-1], m ]
25
+ end
26
+
27
+ $opts = ICalPal::Options.new.parse_options
28
+
29
+ $rows = [] # Rows from the database
30
+ $items = [] # Items to be printed
31
+
32
+
33
+ ##################################################
34
+ # All kids love log!
35
+
36
+ $log.info("Options: #{$opts}")
37
+
38
+
39
+ ##################################################
40
+ # Add an item to the list
41
+ #
42
+ # @param item[Object]
43
+ def add(item)
44
+ $log.debug("Adding #{item['UUID']} (#{item['title']})") if item['UUID']
45
+ item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if item['sdate']
46
+ $items.push(item)
47
+ end
48
+
49
+
50
+ ##################################################
51
+ # Load the data
52
+
53
+ # What are we getting?
54
+ klass = ICalPal::($opts[:cmd])
55
+ q = klass::QUERY
56
+
57
+ $log.debug(q.gsub(/\n/, ' '))
58
+
59
+ # Get it
60
+ stmt = $db.prepare(q)
61
+ abort(stmt.columns.sort.join(' ')) if $opts[:props].any? 'list'
62
+ $opts[:props] = stmt.columns - $opts[:eep] if $opts[:props].any? 'all'
63
+
64
+ # Iterate the SQLite3::ResultSet once
65
+ stmt.execute.each_with_index { |i, j| $rows[j] = i }
66
+ stmt.close
67
+
68
+ $log.info("Loaded #{$rows.count} rows from #{$db.filename}")
69
+ $db.close
70
+
71
+
72
+ ##################################################
73
+ # Process the data
74
+
75
+ # Add rows
76
+ $rows.each do |row|
77
+ # --es/--is
78
+ next if $opts[:es].any? row['account']
79
+ next unless $opts[:is].empty? or $opts[:is].any? row['account']
80
+
81
+ # --ec/--ic
82
+ next if $opts[:ec].any? row['calendar']
83
+ next unless $opts[:ic].empty? or $opts[:ic].any? row['calendar']
84
+
85
+ item = klass.new(row)
86
+
87
+ # --et/--it
88
+ next if $opts[:et].any? item['type']
89
+ next unless $opts[:it].empty? or $opts[:it].any? item['type']
90
+
91
+ unless ICalPal::Event === item
92
+ # Always add non-event items
93
+ add(item)
94
+ else
95
+ # Check for all-day and cancelled events
96
+ next if $opts[:ea] && item['all_day'].positive?
97
+ next if $opts[:ia] && !item['all_day'].positive?
98
+ next if item['status'] == :canceled
99
+
100
+ (item['has_recurrences'].positive?)?
101
+ item.recurring.each { |i| add(i) } :
102
+ item.non_recurring.each { |i| add(i) }
103
+ end
104
+ end
105
+
106
+ # Add placeholders for empty days
107
+ if $opts[:sed] && $opts[:sd] && klass == ICalPal::Event
108
+ days = $items.collect { |i| i['sday'] }.uniq.sort
109
+
110
+ $opts[:days].times do |n|
111
+ day = $opts[:from] + n
112
+ $items.push(klass.new(day)) unless days.any? { |i| i == day }
113
+ end
114
+ end
115
+
116
+ # Sort the rows
117
+ begin
118
+ $items.sort_by! { |i| [ i[$opts[:sep]], i[$opts[:sort]], i['sdate'] ] }
119
+ $items.reverse! if $opts[:reverse]
120
+ rescue ArgumentError => e
121
+ $log.warn("Sorting failed, results may be unexpected\n")
122
+ end
123
+
124
+ # Configure formatting
125
+ mu = case $opts[:output]
126
+ when 'ansi' then RDoc::Markup::ToAnsi.new
127
+ when 'csv' then proc { |f| f.respond_to?(:gsub)? f.gsub(/\n/, '\n') : f }
128
+ when 'default' then RDoc::Markup::ToICalPal.new($opts)
129
+ when 'html' then
130
+ rdoc = RDoc::Options.new
131
+ rdoc.pipe = true
132
+ rdoc.output_decoration = false
133
+ RDoc::Markup::ToHtml.new(rdoc)
134
+ when 'md' then RDoc::Markup::ToMarkdown.new
135
+ when 'rdoc' then RDoc::Markup::ToRdoc.new
136
+ when 'toc' then RDoc::Markup::ToTableOfContents.new
137
+ end
138
+
139
+
140
+ ##################################################
141
+ # Print the data
142
+
143
+ section = nil if $opts[:sep]
144
+
145
+ $items.each_with_index do |i, j|
146
+ # --li
147
+ break if $opts[:li].positive? && j >= $opts[:li]
148
+
149
+ case $opts[:output]
150
+ when 'csv' then
151
+ values = i.values.map { |v| v.to_s }
152
+ puts CSV.generate(write_converters: mu) { |k| k << i.keys } unless j.positive?
153
+ puts CSV.generate(write_converters: mu) { |k| k << values }
154
+ when 'hash' then p i.self
155
+ when 'json' then puts i.self.to_json
156
+ when 'yaml' then puts i.self.to_yaml
157
+ else
158
+ doc = RDoc::Markup::Document.new
159
+
160
+ # Sections
161
+ if $opts[:sep] && section != i[$opts[:sep]]
162
+ $log.debug("Section: #{i[$opts[:sep]]}")
163
+
164
+ v = RDoc::Markup::Verbatim.new
165
+ v.format = { item: i, prop: $opts[:sep] }
166
+ doc << v
167
+
168
+ doc << RDoc::Markup::BlankLine.new if j.positive?
169
+ doc << RDoc::Markup::Heading.new(1, i[$opts[:sep]].to_s)
170
+ doc << RDoc::Markup::Rule.new(0)
171
+
172
+ section = i[$opts[:sep]]
173
+ end
174
+
175
+ # Item
176
+ props = RDoc::Markup::List.new(:BULLET)
177
+
178
+ # Properties
179
+ $opts[:props].each_with_index do |prop, k|
180
+ next unless i[prop]
181
+ next if Array === i[prop] && !i[prop][0]
182
+
183
+ $log.debug("#{prop}: #{i[prop]}")
184
+
185
+ v = RDoc::Markup::Verbatim.new
186
+ v.format = { item: i, prop: prop }
187
+ props << v
188
+
189
+ unless k.positive?
190
+ # First property, value only
191
+ props << RDoc::Markup::Heading.new(2, i[prop].to_s)
192
+ else
193
+ props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
194
+ props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(i[prop]))
195
+ end
196
+ end
197
+
198
+ # Print it
199
+ unless props.empty?
200
+ doc << props
201
+ puts doc.accept(mu)
202
+ end
203
+ end
204
+ end
data/icalPal.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "icalPal"
3
+ s.version = "1.0.0"
4
+ s.summary = "Command-line tool to query the macOS Calendar"
5
+ s.description = <<-EOF
6
+ Inspired by icalBuddy and maintains close compatability. Includes
7
+ many additional features for querying, filtering, and formatting.
8
+ EOF
9
+
10
+ s.authors = "Andy Rosen"
11
+ s.email = "ajr@corp.mlfs.org"
12
+ s.homepage = "https://github.com/ajrosen/#{s.name}"
13
+ s.licenses = [ "GPL-3.0+" ]
14
+
15
+ s.files = Dir[ "#{s.name}.gemspec", "bin/*", "lib/*.rb" ]
16
+ s.executables = [ "#{s.name}" ]
17
+ s.extra_rdoc_files = [ "README.md" ]
18
+
19
+ s.bindir = 'bin'
20
+ s.required_ruby_version = '>= 2.6.0'
21
+ end
data/lib/EventKit.rb ADDED
@@ -0,0 +1,44 @@
1
+ # Constants from EventKit[https://developer.apple.com/documentation/eventkit/]
2
+ class EventKit
3
+ EKEntityType = [
4
+ 'event',
5
+ 'reminder',
6
+ ]
7
+
8
+ EKEventAvailability = {
9
+ notSupported: -1,
10
+ busy: 0,
11
+ free: 1,
12
+ tentative: 2,
13
+ unavailable: 3,
14
+ }
15
+
16
+ EKEventStatus = {
17
+ none: 0,
18
+ confirmed: 1,
19
+ tentative: 2,
20
+ canceled: 3,
21
+ }
22
+
23
+ EKRecurrenceFrequency = [
24
+ 'daily',
25
+ 'weekly',
26
+ 'monthly',
27
+ 'yearly',
28
+ ]
29
+
30
+ # EKSourceType (with color)
31
+ EKSourceType = [
32
+ { name: 'Local', color: '#FFFFFF' }, # White
33
+ { name: 'Exchange', color: '#00FFFF' }, # Cyan
34
+ { name: 'CalDAV', color: '#00FF00' }, # Green
35
+ { name: 'MobileMe', color: '#FFFF00' }, # Yellow
36
+ { name: 'Subscribed', color: '#FF0000' }, # Red
37
+ { name: 'Birthdays', color: '#FF00FF' }, # Magenta
38
+ ]
39
+
40
+ EKSpan = [
41
+ 'this',
42
+ 'future',
43
+ ]
44
+ end
data/lib/ToICalPal.rb ADDED
@@ -0,0 +1,177 @@
1
+ ##################################################
2
+ # Render an RDoc::Markup::Document, closely mimicking
3
+ # icalBuddy[https://github.com/ali-rantakari/icalBuddy]
4
+
5
+ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
6
+ # Standard
7
+ # ANSI[https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.416-199303-I!!PDF-E&type=items]
8
+ # colors
9
+ ANSI = {
10
+ 'black': 30, '#000000': '38;5;0',
11
+ 'red': 31, '#ff0000': '38;5;1',
12
+ 'green': 32, '#00ff00': '38;5;2',
13
+ 'yellow': 33, '#ffff00': '38;5;3',
14
+ 'blue': 34, '#0000ff': '38;5;4',
15
+ 'magenta': 35, '#ff00ff': '38;5;5',
16
+ 'cyan': 36, '#00ffff': '38;5;6',
17
+ 'white': 37, '#ffffff': '38;5;255',
18
+ 'default': 39, 'custom': nil,
19
+ }
20
+
21
+ # Increased intensity
22
+ BOLD = '%c[1m' % 27.chr
23
+
24
+ # Default rendition
25
+ NORM = '%c[0m' % 27.chr
26
+
27
+ # Properties for which we don't include labels
28
+ NO_LABEL = [ 'title', 'datetime' ]
29
+
30
+ # Properties that are always colorized
31
+ COLOR_LABEL = [ 'title', 'calendar' ]
32
+
33
+ # Default color for labels
34
+ LABEL_COLOR = [ 'cyan', '#00ffff' ]
35
+
36
+ # Color for datetime value
37
+ DATE_COLOR = [ 'yellow', '#ffff00' ]
38
+
39
+ # @param opts [Hash] Used for conditional formatting
40
+ # @option opts [String] :bullet Bullet
41
+ # @option opts [Boolean] :nc No calendar names
42
+ # @option opts [Boolean] :npn No property names
43
+ # @option opts [Integer] :palette (nil) 8 for \-f, 24 for \--color
44
+ # @option opts [Array<String>] :ps List of property separators
45
+ # @option opts [String] :ss Section separator
46
+ def initialize(opts)
47
+ @opts = opts
48
+ end
49
+
50
+ # Start a new document
51
+ def start_accepting
52
+ @res = []
53
+ @ps = 0
54
+ end
55
+
56
+ # Close the document
57
+ def end_accepting
58
+ @res.join
59
+ end
60
+
61
+ # Add a bullet for the first property of an item
62
+ #
63
+ # @param arg [Array] Ignored
64
+ def accept_list_start(arg)
65
+ begin
66
+ return if @item['placeholder']
67
+ rescue
68
+ end
69
+
70
+ @res << "#{@opts[:bullet]} "
71
+ end
72
+
73
+ # Add a property name
74
+ #
75
+ # @param arg [RDoc::Markup::ListItem]
76
+ # @option arg [String] .label Contains the property name
77
+ def accept_list_item_start(arg)
78
+ @res << @opts[:ps][@ps] || ' ' unless @item['placeholder']
79
+ @res << colorize(*LABEL_COLOR, arg.label) << ": " unless @opts[:npn] || NO_LABEL.any?(arg.label)
80
+
81
+ @ps += 1 unless @ps == @opts[:ps].count - 1
82
+ end
83
+
84
+ # Add a blank line
85
+ #
86
+ # @param arg [Array] Ignored
87
+ def accept_blank_line(*arg)
88
+ @res << "\n"
89
+ end
90
+
91
+ # Add either a section header or the first property of an item
92
+ #
93
+ # @param h [RDoc::Markup::Heading]
94
+ # @option h [Integer] :level 1 for a section header
95
+ # @option h [Integer] :level 2 for a property name
96
+ # @option h [String] :text The header's text
97
+ def accept_heading(h)
98
+ h.text = colorize(@item['symbolic_color_name'], @item['color'], h.text) if (h.level == 2) || COLOR_LABEL.any?(@prop)
99
+ @res << h.text
100
+
101
+ case h.level
102
+ when 1 then
103
+ @res << ":"
104
+ when 2 then
105
+ if @prop == 'title' && @item['calendar']
106
+ @res << bold(" (#{@item['calendar']})") unless @opts[:nc] || @item['title'] == @item['calendar']
107
+ end
108
+ end
109
+ end
110
+
111
+ # Add the property value
112
+ #
113
+ # @param p [RDoc::Markup::Paragraph]
114
+ # @option p [Array<String>] :parts The property's text
115
+ def accept_paragraph(p)
116
+ t = p.parts.join('; ').gsub(/\n/, "\n ")
117
+ t = colorize(*DATE_COLOR, t) if @prop == 'datetime'
118
+ @res << t
119
+ end
120
+
121
+ # Add a section separator
122
+ #
123
+ # @param weight Ignored
124
+ def accept_rule(weight)
125
+ @res << @opts[:ss]
126
+ accept_blank_line
127
+ end
128
+
129
+ # Don't add anything to the document, just save the item and
130
+ # property name for later
131
+ #
132
+ # @param h [RDoc::Markup::Verbatim]
133
+ # @option h [String] :parts Ignored
134
+ # @option h [{item, prop => ICalPal::Event, String}] :format
135
+ def accept_verbatim(h)
136
+ @item = h.format[:item]
137
+ @prop = h.format[:prop]
138
+ end
139
+
140
+ # @param str [String]
141
+ # @return [String] str with increased intensity[#BOLD]
142
+ def bold(str)
143
+ return str unless @opts[:palette]
144
+ BOLD + str + NORM
145
+ end
146
+
147
+ # @param c8 [String] Color used for \-f
148
+ # @param c24 [String] Color used for \--color
149
+ # @return [String] str in color, depending on opts[#]
150
+ def colorize(c8, c24, str)
151
+ return str unless c8 && c24 && @opts[:palette]
152
+
153
+ case @opts[:palette]
154
+ when 8 then # Default colour table
155
+ c = ANSI[c8.downcase.to_sym]
156
+ c ||= ANSI[c24[0..6].downcase.to_sym]
157
+ c ||= ANSI[:white]
158
+
159
+ when 24 then # Direct colour in RGB space
160
+ rgb = c24[1..].split(/(\h\h)(\h\h)(\h\h)/)
161
+ rgb.map! { |i| i.to_i(16) }
162
+ c = [ 38, 2, rgb[1..] ].join(';')
163
+ end
164
+
165
+ sprintf('%c[%sm%s%c[%sm', 27.chr, c, str, 27.chr, ANSI[:default])
166
+ end
167
+
168
+ # @!visibility private
169
+
170
+ # @param a [Array] Ignored
171
+ def accept_list_end(a)
172
+ end
173
+
174
+ # @param a [Array] Ignored
175
+ def accept_list_item_end(a)
176
+ end
177
+ end
data/lib/calendar.rb ADDED
@@ -0,0 +1,24 @@
1
+ module ICalPal
2
+ # Class representing items from the <tt>Calendar</tt> table
3
+ class Calendar
4
+ include ICalPal
5
+
6
+ QUERY = <<~SQL
7
+ SELECT DISTINCT
8
+
9
+ Store.name AS account,
10
+ Calendar.title AS calendar,
11
+ *
12
+
13
+ FROM #{self.name.split('::').last}
14
+
15
+ JOIN Store ON store_id = Store.rowid
16
+
17
+ WHERE Store.disabled IS NOT 1
18
+ AND Store.display_order IS NOT -1
19
+ AND (Calendar.display_order IS NOT -1 OR external_rep IS NOT NULL)
20
+ AND Calendar.flags IS NOT 519
21
+ SQL
22
+
23
+ end
24
+ end