calendar-assistant 0.6.0 → 0.7.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 +152 -67
- data/Rakefile +13 -3
- data/bin/calendar-assistant +3 -1
- data/lib/calendar_assistant.rb +0 -1
- data/lib/calendar_assistant/calendar_assistant.rb +16 -14
- data/lib/calendar_assistant/cli.rb +9 -233
- data/lib/calendar_assistant/cli/authorizer.rb +88 -0
- data/lib/calendar_assistant/cli/commands.rb +284 -0
- data/lib/calendar_assistant/cli/config.rb +51 -0
- data/lib/calendar_assistant/cli/event_presenter.rb +71 -0
- data/lib/calendar_assistant/cli/event_set_presenter.rb +69 -0
- data/lib/calendar_assistant/cli/helpers.rb +54 -0
- data/lib/calendar_assistant/cli/linter_event_presenter.rb +56 -0
- data/lib/calendar_assistant/cli/linter_event_set_presenter.rb +23 -0
- data/lib/calendar_assistant/cli/printer.rb +90 -0
- data/lib/calendar_assistant/config.rb +21 -45
- data/lib/calendar_assistant/event.rb +16 -12
- data/lib/calendar_assistant/event_repository.rb +18 -9
- data/lib/calendar_assistant/event_repository_factory.rb +2 -2
- data/lib/calendar_assistant/version.rb +1 -1
- metadata +78 -38
- data/lib/calendar_assistant/authorizer.rb +0 -86
- data/lib/calendar_assistant/cli_helpers.rb +0 -228
@@ -0,0 +1,51 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
module CLI
|
3
|
+
class Config < CalendarAssistant::Config
|
4
|
+
class TomlParseFailure < CalendarAssistant::BaseException;
|
5
|
+
end
|
6
|
+
class NoConfigFileToPersist < CalendarAssistant::BaseException;
|
7
|
+
end
|
8
|
+
|
9
|
+
CONFIG_FILE_PATH = File.join (ENV['CA_HOME'] || ENV["HOME"]), ".calendar-assistant"
|
10
|
+
attr_reader :config_file_path
|
11
|
+
|
12
|
+
def initialize options: {},
|
13
|
+
config_file_path: CONFIG_FILE_PATH,
|
14
|
+
defaults: DEFAULT_SETTINGS
|
15
|
+
|
16
|
+
|
17
|
+
@config_file_path = config_file_path
|
18
|
+
|
19
|
+
user_config = if File.exist? config_file_path
|
20
|
+
begin
|
21
|
+
FileUtils.chmod 0600, config_file_path
|
22
|
+
TOML.load_file config_file_path
|
23
|
+
rescue Exception => e
|
24
|
+
raise TomlParseFailure, "could not parse #{config_file_path}: #{e}"
|
25
|
+
end
|
26
|
+
else
|
27
|
+
Hash.new
|
28
|
+
end
|
29
|
+
super(options: options, defaults: defaults, user_config: user_config)
|
30
|
+
end
|
31
|
+
|
32
|
+
def profile_name
|
33
|
+
super.tap do |token|
|
34
|
+
persist!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def persist!
|
39
|
+
if config_file_path.nil?
|
40
|
+
raise NoConfigFileToPersist, "Cannot persist config when there's no config file"
|
41
|
+
end
|
42
|
+
|
43
|
+
content = TOML::Generator.new(user_config).body
|
44
|
+
|
45
|
+
File.open(config_file_path, "w") do |f|
|
46
|
+
f.write content
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
module CLI
|
3
|
+
class EventPresenter < SimpleDelegator
|
4
|
+
EMOJI_WARN = "⚠"
|
5
|
+
|
6
|
+
def description
|
7
|
+
s = formatted_event_date
|
8
|
+
s += rainbow.wrap(sprintf(" | %s", view_summary)).bold
|
9
|
+
s += event_attributes unless private?
|
10
|
+
s = rainbow.wrap(Rainbow.uncolor(s)).faint.strike if declined?
|
11
|
+
s
|
12
|
+
end
|
13
|
+
|
14
|
+
def view_summary
|
15
|
+
return "(private)" if private? && (summary.nil? || summary.blank?)
|
16
|
+
return "(no title)" if summary.nil? || summary.blank?
|
17
|
+
summary
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def event_attributes
|
23
|
+
attributes = []
|
24
|
+
|
25
|
+
attributes << "recurring" if recurring?
|
26
|
+
attributes << "not-busy" unless busy?
|
27
|
+
attributes << "self" if self?
|
28
|
+
attributes << "1:1" if one_on_one?
|
29
|
+
attributes << "awaiting" if awaiting?
|
30
|
+
attributes << "tentative" if tentative?
|
31
|
+
attributes << rainbow.wrap(sprintf(" %s abandoned %s ", EMOJI_WARN, EMOJI_WARN)).red.bold.inverse if abandoned?
|
32
|
+
|
33
|
+
attributes << visibility if explicitly_visible?
|
34
|
+
|
35
|
+
attributes.empty? ? "" : rainbow.wrap(sprintf(" (%s)", attributes.to_a.sort.join(", "))).italic
|
36
|
+
end
|
37
|
+
|
38
|
+
def rainbow
|
39
|
+
@rainbow ||= Rainbow.global
|
40
|
+
end
|
41
|
+
|
42
|
+
def formatted_event_date
|
43
|
+
date = sprintf("%-25.25s", event_date)
|
44
|
+
|
45
|
+
date_ansi_codes = []
|
46
|
+
date_ansi_codes << :bright if current?
|
47
|
+
date_ansi_codes << :faint if past?
|
48
|
+
|
49
|
+
date_ansi_codes.inject(rainbow.wrap(date)) {|text, ansi| text.send ansi}
|
50
|
+
end
|
51
|
+
|
52
|
+
def event_date
|
53
|
+
if all_day?
|
54
|
+
start_date = __getobj__.start_date
|
55
|
+
end_date = __getobj__.end_date
|
56
|
+
if (end_date - start_date) <= 1
|
57
|
+
start.to_s
|
58
|
+
else
|
59
|
+
sprintf("%s - %s", start_date, end_date - 1.day)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
if start_date == end_date
|
63
|
+
sprintf("%s - %s", start.date_time.strftime("%Y-%m-%d %H:%M"), __getobj__.end.date_time.strftime("%H:%M"))
|
64
|
+
else
|
65
|
+
sprintf("%s - %s", start.date_time.strftime("%Y-%m-%d %H:%M"), __getobj__.end.date_time.strftime("%Y-%m-%d %H:%M"))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
module CLI
|
3
|
+
class EventSetPresenter < SimpleDelegator
|
4
|
+
def initialize(obj, config:, event_presenter_class: CLI::EventPresenter)
|
5
|
+
super(obj)
|
6
|
+
@config = config
|
7
|
+
@event_presenter_class = event_presenter_class
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
[
|
12
|
+
title,
|
13
|
+
description
|
14
|
+
].join("\n")
|
15
|
+
end
|
16
|
+
|
17
|
+
def title
|
18
|
+
rainbow.wrap("#{event_repository.calendar.id} (all times in #{event_repository.calendar.time_zone})\n").italic
|
19
|
+
end
|
20
|
+
|
21
|
+
def description
|
22
|
+
out = StringIO.new
|
23
|
+
|
24
|
+
if __getobj__.is_a?(EventSet::Hash)
|
25
|
+
events.each do |key, value|
|
26
|
+
out.puts rainbow.wrap(key.to_s.capitalize + ":").bold.italic
|
27
|
+
out.puts self.class.new(__getobj__.new(value), config: @config, event_presenter_class: @event_presenter_class).description
|
28
|
+
end
|
29
|
+
return out.string
|
30
|
+
end
|
31
|
+
|
32
|
+
_events = Array(events)
|
33
|
+
|
34
|
+
return "No events in this time range.\n" if _events.empty?
|
35
|
+
|
36
|
+
display_events = _events.select do |event|
|
37
|
+
!@config.setting(CalendarAssistant::Config::Keys::Options::COMMITMENTS) || event.commitment?
|
38
|
+
end
|
39
|
+
|
40
|
+
printed_now = false
|
41
|
+
|
42
|
+
display_events.each_with_object([]) do |event, out|
|
43
|
+
printed_now = now! event, printed_now, out: out, presenter_class: @event_presenter_class
|
44
|
+
out << @event_presenter_class.new(event).description
|
45
|
+
pp event if @config.debug?
|
46
|
+
end.join("\n")
|
47
|
+
end
|
48
|
+
|
49
|
+
def now!(event, printed_now, out:, presenter_class: CLI::EventPresenter)
|
50
|
+
return true if printed_now
|
51
|
+
return false if event.start_date != Date.today
|
52
|
+
|
53
|
+
if event.start_time > Time.now
|
54
|
+
out << presenter_class.new(CalendarAssistant::CLI::Helpers.now).description
|
55
|
+
|
56
|
+
return true
|
57
|
+
end
|
58
|
+
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def rainbow
|
65
|
+
@rainbow ||= Rainbow.global
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
class CalendarAssistant
|
3
|
+
module CLI
|
4
|
+
module Helpers
|
5
|
+
class ChronicParseException < CalendarAssistant::BaseException;
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.parse_datespec userspec
|
9
|
+
start_userspec, end_userspec = userspec.split(/ ?\.\.\.? ?/)
|
10
|
+
|
11
|
+
if end_userspec.nil?
|
12
|
+
time = Chronic.parse(userspec) || raise(ChronicParseException, "could not parse '#{userspec}'")
|
13
|
+
return time.beginning_of_day..time.end_of_day
|
14
|
+
end
|
15
|
+
|
16
|
+
start_time = Chronic.parse(start_userspec) || raise(ChronicParseException, "could not parse '#{start_userspec}'")
|
17
|
+
end_time = Chronic.parse(end_userspec) || raise(ChronicParseException, "could not parse '#{end_userspec}'")
|
18
|
+
|
19
|
+
if start_time.to_date == end_time.to_date
|
20
|
+
start_time..end_time
|
21
|
+
else
|
22
|
+
start_time.beginning_of_day..end_time.end_of_day
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.now
|
27
|
+
CalendarAssistant::Event.new(
|
28
|
+
Google::Apis::CalendarV3::Event.new(start: Google::Apis::CalendarV3::EventDateTime.new(date_time: Time.now),
|
29
|
+
end: Google::Apis::CalendarV3::EventDateTime.new(date_time: Time.now),
|
30
|
+
summary: Rainbow(" now ").inverse.faint)
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.find_av_uri ca, timespec
|
35
|
+
time = Chronic.parse timespec
|
36
|
+
range = time..(time + 5.minutes)
|
37
|
+
event_set = ca.find_events range
|
38
|
+
|
39
|
+
[CalendarAssistant::Event::Response::ACCEPTED,
|
40
|
+
CalendarAssistant::Event::Response::TENTATIVE,
|
41
|
+
CalendarAssistant::Event::Response::NEEDS_ACTION,
|
42
|
+
].each do |response|
|
43
|
+
event_set.events.reverse.select do |event|
|
44
|
+
event.response_status == response
|
45
|
+
end.each do |event|
|
46
|
+
return [event_set.new(event), event.av_uri] if event.av_uri
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
event_set.new(nil)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
module CLI
|
3
|
+
class LinterEventPresenter < EventPresenter
|
4
|
+
EMOJI_ACCEPTED = "👍"
|
5
|
+
EMOJI_DECLINED = "👎"
|
6
|
+
EMOJI_NEEDS_ACTION = "🤷"
|
7
|
+
SUMMARY_THRESHOLD = 5
|
8
|
+
|
9
|
+
def description
|
10
|
+
s = formatted_event_date
|
11
|
+
date_length = s.length
|
12
|
+
s += rainbow.wrap(sprintf(" | %s", view_summary)).bold
|
13
|
+
s += event_attributes unless private?
|
14
|
+
s = rainbow.wrap(Rainbow.uncolor(s)).faint.strike if declined?
|
15
|
+
s += "\n #{' ' * (date_length + 2)}attendees: #{attendees}"
|
16
|
+
s
|
17
|
+
end
|
18
|
+
|
19
|
+
def attendees
|
20
|
+
if required_other_attendees .length > SUMMARY_THRESHOLD
|
21
|
+
summary_attendee_list
|
22
|
+
else
|
23
|
+
detailed_attendee_list
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def detailed_attendee_list
|
30
|
+
required_other_attendees.map do |attendee|
|
31
|
+
sprintf "%s %s", response_emoji(attendee.response_status), attendee.email || "<no email>"
|
32
|
+
end.join(", ")
|
33
|
+
end
|
34
|
+
|
35
|
+
def summary_attendee_list
|
36
|
+
summary = required_other_attendees.group_by do |attendee|
|
37
|
+
response_emoji(attendee.response_status)
|
38
|
+
end
|
39
|
+
|
40
|
+
summary.sort.map do |emoji, attendees|
|
41
|
+
"#{emoji} - #{attendees.count}"
|
42
|
+
end.join(", ")
|
43
|
+
end
|
44
|
+
|
45
|
+
def required_other_attendees
|
46
|
+
@required_other_attendees ||= (other_human_attendees || []).select {|a| !a.optional }
|
47
|
+
end
|
48
|
+
|
49
|
+
def response_emoji(response_status)
|
50
|
+
return EMOJI_ACCEPTED if response_status == CalendarAssistant::Event::Response::ACCEPTED
|
51
|
+
return EMOJI_DECLINED if response_status == CalendarAssistant::Event::Response::DECLINED
|
52
|
+
return EMOJI_NEEDS_ACTION
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
module CLI
|
3
|
+
class LinterEventSetPresenter < EventSetPresenter
|
4
|
+
def initialize(obj, config:, event_presenter_class: CLI::LinterEventPresenter)
|
5
|
+
super(obj, config: config, event_presenter_class: event_presenter_class)
|
6
|
+
end
|
7
|
+
|
8
|
+
def title
|
9
|
+
rainbow.wrap(<<~OUT)
|
10
|
+
#{event_repository.calendar.id}
|
11
|
+
- looking for events that need attention
|
12
|
+
- all times in #{event_repository.calendar.time_zone}
|
13
|
+
OUT
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def rainbow
|
19
|
+
@rainbow ||= Rainbow.global
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class CalendarAssistant
|
2
|
+
module CLI
|
3
|
+
class Printer
|
4
|
+
|
5
|
+
attr_reader :io
|
6
|
+
|
7
|
+
def initialize io = STDOUT
|
8
|
+
@io = io
|
9
|
+
end
|
10
|
+
|
11
|
+
def launch url
|
12
|
+
Launchy.open url
|
13
|
+
end
|
14
|
+
|
15
|
+
def puts *args
|
16
|
+
io.puts(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def prompt query, default = nil
|
20
|
+
loop do
|
21
|
+
message = query
|
22
|
+
message += " [#{default}]" if default
|
23
|
+
message += ": "
|
24
|
+
print Rainbow(message).bold
|
25
|
+
answer = STDIN.gets.chomp.strip
|
26
|
+
if answer.empty?
|
27
|
+
return default if default
|
28
|
+
puts Rainbow("Please provide an answer.").red
|
29
|
+
else
|
30
|
+
return answer
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def print_events ca, event_set, omit_title: false, presenter_class: CLI::EventSetPresenter
|
36
|
+
puts presenter_class.new(event_set, config: ca.config).to_s
|
37
|
+
puts
|
38
|
+
end
|
39
|
+
|
40
|
+
def print_available_blocks ca, event_set, omit_title: false
|
41
|
+
ers = ca.config.attendees.map {|calendar_id| ca.event_repository calendar_id}
|
42
|
+
time_zones = ers.map {|er| er.calendar.time_zone}.uniq
|
43
|
+
|
44
|
+
unless omit_title
|
45
|
+
puts Rainbow(ers.map {|er| er.calendar.id}.join(", ")).italic
|
46
|
+
puts Rainbow(sprintf("- looking for blocks at least %s long",
|
47
|
+
ChronicDuration.output(
|
48
|
+
ChronicDuration.parse(
|
49
|
+
ca.config.setting(Config::Keys::Settings::MEETING_LENGTH))))).italic
|
50
|
+
time_zones.each do |time_zone|
|
51
|
+
puts Rainbow(sprintf("- between %s and %s in %s",
|
52
|
+
ca.config.setting(Config::Keys::Settings::START_OF_DAY),
|
53
|
+
ca.config.setting(Config::Keys::Settings::END_OF_DAY),
|
54
|
+
time_zone,
|
55
|
+
)).italic
|
56
|
+
end
|
57
|
+
puts
|
58
|
+
end
|
59
|
+
|
60
|
+
if event_set.is_a?(EventSet::Hash)
|
61
|
+
event_set.events.each do |key, value|
|
62
|
+
puts(sprintf(Rainbow("Availability on %s:\n").bold,
|
63
|
+
key.strftime("%A, %B %-d")))
|
64
|
+
print_available_blocks ca, event_set.new(value), omit_title: true
|
65
|
+
puts
|
66
|
+
end
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
events = Array(event_set.events)
|
71
|
+
if events.empty?
|
72
|
+
puts " (No available blocks in this time range.)"
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
events.each do |event|
|
77
|
+
line = []
|
78
|
+
time_zones.each do |time_zone|
|
79
|
+
line << sprintf("%s - %s",
|
80
|
+
event.start_time.in_time_zone(time_zone).strftime("%l:%M%P"),
|
81
|
+
event.end_time.in_time_zone(time_zone).strftime("%l:%M%P %Z"))
|
82
|
+
end
|
83
|
+
line.uniq!
|
84
|
+
puts " • " + line.join(" / ") + Rainbow(" (" + event.duration + ")").italic
|
85
|
+
pp event if ca.config.debug?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -2,13 +2,10 @@ class CalendarAssistant
|
|
2
2
|
class Config
|
3
3
|
autoload :TokenStore, "calendar_assistant/config/token_store"
|
4
4
|
|
5
|
-
class
|
6
|
-
|
7
|
-
class
|
8
|
-
|
9
|
-
|
10
|
-
CONFIG_FILE_PATH = File.join ENV["HOME"], ".calendar-assistant"
|
11
|
-
DEFAULT_CALENDAR_ID = "primary"
|
5
|
+
class NoTokensAuthorized < CalendarAssistant::BaseException;
|
6
|
+
end
|
7
|
+
class AccessingHashAsScalar < CalendarAssistant::BaseException;
|
8
|
+
end
|
12
9
|
|
13
10
|
module Keys
|
14
11
|
TOKENS = "tokens"
|
@@ -23,6 +20,7 @@ class CalendarAssistant
|
|
23
20
|
MEETING_LENGTH = "meeting-length" # ChronicDuration
|
24
21
|
START_OF_DAY = "start-of-day" # BusinessTime
|
25
22
|
END_OF_DAY = "end-of-day" # BusinessTime
|
23
|
+
LOCATION_ICONS = "location-icons" # Location Icons
|
26
24
|
end
|
27
25
|
|
28
26
|
#
|
@@ -35,45 +33,30 @@ class CalendarAssistant
|
|
35
33
|
ATTENDEES = "attendees" # array of calendar ids (comma-delimited)
|
36
34
|
LOCAL_STORE = "local-store" # filename
|
37
35
|
DEBUG = "debug" # bool
|
36
|
+
FORMATTING = "formatting" # Rainbow
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
40
|
+
DEFAULT_CALENDAR_ID = "primary"
|
41
|
+
|
41
42
|
DEFAULT_SETTINGS = {
|
43
|
+
Keys::Settings::LOCATION_ICONS => ["🗺 ", "🌎"], # Location Icons
|
42
44
|
Keys::Settings::MEETING_LENGTH => "30m", # ChronicDuration
|
43
45
|
Keys::Settings::START_OF_DAY => "9am", # BusinessTime
|
44
46
|
Keys::Settings::END_OF_DAY => "6pm", # BusinessTime
|
45
47
|
Keys::Options::ATTENDEES => [DEFAULT_CALENDAR_ID], # array of calendar ids
|
48
|
+
Keys::Options::FORMATTING => true, # Rainbow
|
46
49
|
}
|
47
50
|
|
48
|
-
attr_reader :
|
51
|
+
attr_reader :user_config, :options, :defaults
|
49
52
|
|
50
53
|
def initialize options: {},
|
51
|
-
|
52
|
-
config_io: nil,
|
54
|
+
user_config: {},
|
53
55
|
defaults: DEFAULT_SETTINGS
|
54
|
-
if config_io.nil?
|
55
|
-
@config_file_path = config_file_path
|
56
|
-
end
|
57
|
-
|
58
|
-
@user_config = if config_io
|
59
|
-
begin
|
60
|
-
TOML.load config_io.read
|
61
|
-
rescue Exception => e
|
62
|
-
raise TomlParseFailure, "could not parse IO stream: #{e}"
|
63
|
-
end
|
64
|
-
elsif File.exist? config_file_path
|
65
|
-
begin
|
66
|
-
FileUtils.chmod 0600, config_file_path
|
67
|
-
TOML.load_file config_file_path
|
68
|
-
rescue Exception => e
|
69
|
-
raise TomlParseFailure, "could not parse #{config_file_path}: #{e}"
|
70
|
-
end
|
71
|
-
else
|
72
|
-
Hash.new
|
73
|
-
end
|
74
56
|
|
75
57
|
@defaults = defaults
|
76
58
|
@options = options
|
59
|
+
@user_config = user_config
|
77
60
|
end
|
78
61
|
|
79
62
|
def in_env &block
|
@@ -101,11 +84,10 @@ class CalendarAssistant
|
|
101
84
|
# finally we'll grab the first configured token and set that as the default
|
102
85
|
token_names = tokens.keys
|
103
86
|
if token_names.empty?
|
104
|
-
raise NoTokensAuthorized, "Please run `calendar-assistant help authorize` for help."
|
87
|
+
raise CalendarAssistant::Config::NoTokensAuthorized, "Please run `calendar-assistant help authorize` for help."
|
105
88
|
end
|
106
89
|
token_names.first.tap do |new_default|
|
107
90
|
Config.set_in_hash user_config, [Keys::SETTINGS, Keys::Settings::PROFILE], new_default
|
108
|
-
persist!
|
109
91
|
end
|
110
92
|
end
|
111
93
|
|
@@ -113,7 +95,7 @@ class CalendarAssistant
|
|
113
95
|
rval = Config.find_in_hash(user_config, keypath)
|
114
96
|
|
115
97
|
if rval.is_a?(Hash)
|
116
|
-
raise AccessingHashAsScalar, "keypath #{keypath} is not a scalar"
|
98
|
+
raise CalendarAssistant::Config::AccessingHashAsScalar, "keypath #{keypath} is not a scalar"
|
117
99
|
end
|
118
100
|
|
119
101
|
rval
|
@@ -133,6 +115,8 @@ class CalendarAssistant
|
|
133
115
|
Config.find_in_hash(defaults, setting_name)
|
134
116
|
end
|
135
117
|
|
118
|
+
alias_method :[], :setting
|
119
|
+
|
136
120
|
def settings
|
137
121
|
setting_names = CalendarAssistant::Config::Keys::Settings.constants.map do |k|
|
138
122
|
CalendarAssistant::Config::Keys::Settings.const_get k
|
@@ -152,18 +136,6 @@ class CalendarAssistant
|
|
152
136
|
CalendarAssistant::Config::TokenStore.new self
|
153
137
|
end
|
154
138
|
|
155
|
-
def persist!
|
156
|
-
if config_file_path.nil?
|
157
|
-
raise NoConfigFileToPersist, "Cannot persist config when initialized with an IO"
|
158
|
-
end
|
159
|
-
|
160
|
-
content = TOML::Generator.new(user_config).body
|
161
|
-
|
162
|
-
File.open(config_file_path, "w") do |f|
|
163
|
-
f.write content
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
139
|
#
|
168
140
|
# helper method for Keys::Options::ATTENDEES
|
169
141
|
#
|
@@ -179,6 +151,10 @@ class CalendarAssistant
|
|
179
151
|
setting(Keys::Options::DEBUG)
|
180
152
|
end
|
181
153
|
|
154
|
+
def persist!
|
155
|
+
#noop
|
156
|
+
end
|
157
|
+
|
182
158
|
private
|
183
159
|
|
184
160
|
def self.find_in_hash hash, keypath
|