calendar-assistant 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|