-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathremaining-time-to-spend-with-my-children.1h.rb
executable file
·245 lines (198 loc) · 7.33 KB
/
remaining-time-to-spend-with-my-children.1h.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#!/usr/bin/env ruby
require 'erb'
require 'time'
module BitBar
class INIFile
Error = Class.new(StandardError)
INIFileNotFound = Class.new(Error)
SectionNotFound = Class.new(Error)
def self.load(file = "#{ENV['HOME']}/.bitbarrc", base: {})
raise INIFileNotFound if !File.exist?(file)
parse(open(file, 'r:UTF-8') { |f| f.read })
end
def self.parse(source)
# XXX: This implementation isn't correct, but will work in most cases.
# (Probably `StringScanner` will make code correct and clean.)
sections = {}
section = nil
source.each_line do |line|
if line =~ /^ *;/
# comment
next
end
if line =~ /^\[(.+)\]$/
section = sections[$1.to_sym] = {}
next
end
next unless section
if line =~ /(.+?)=(.+)$/
name = $1.strip.to_sym
value = $2.strip
section[name] = value[/^"(.*)"$/, 1] || value[/^'(.*)'$/, 1] || value
next
end
end
new(sections: sections)
end
def initialize(sections:)
@sections = sections
end
def fetch(name, *options)
@sections.fetch(name.to_sym, *options)
rescue KeyError
raise SectionNotFound
end
end
module RemainingTimeToSpendWithMyChildren
ConfigurationError = Class.new(StandardError)
class Children < Struct.new(:label,
:birthday,
:independence_day,
:hours_a_day_during_infant,
:hours_a_day_during_elementary,
:hours_a_day_during_junior_high_school,
:hours_a_day_during_high_school,
:hours_a_day_during_college_or_later)
def self.from(children, options:)
children.map do |child|
base_keys = %i[
label
birthday
independence_day
]
option_keys = %i[
hours_a_day_during_infant
hours_a_day_during_elementary
hours_a_day_during_junior_high_school
hours_a_day_during_high_school
hours_a_day_during_college_or_later
]
Children.new(
*base_keys.map {|key| child.fetch(key) },
*option_keys.map {|key| options.fetch(key) },
)
end
end
# FIXME: Should consider "早生まれ".
def remaining_hours
hours = 0
today = Date.today
i = Date.new(birthday.year + 6, 4, 1)
t = (i - today).to_i
hours += t * hours_a_day_during_infant if t > 0
e = Date.new(birthday.year + 12, 4, 1)
t = (e - [i, today].max).to_i
hours += t * hours_a_day_during_elementary if t > 0
j = Date.new(birthday.year + 15, 4, 1)
t = (j - [e, today].max).to_i
hours += t * hours_a_day_during_junior_high_school if t > 0
h = Date.new(birthday.year + 18, 4, 1)
t = (h - [j, today].max).to_i
hours += t * hours_a_day_during_high_school if t > 0
t = (independence_day - [h, today].max).to_i
hours += t * hours_a_day_during_college_or_later if t > 0
hours
end
def remaining_days
(independence_day - Date.today).to_i
end
end
class View
TEMPLATE = <<-EOT.gsub(/^ */, '')
<%= @children[0].label %><%= @children[0].remaining_days %> days | font=courier color=<%= @text_color %>
---
<% @children.each do |children| -%>
<%= children.label %> <%= children.remaining_days %> days (<%= children.remaining_hours %> hours) | font=courier color=<%= @text_color %>
<% end -%>
EOT
def initialize(children:, text_color:)
@children = children
@text_color = text_color
end
def render
puts ERB.new(TEMPLATE, nil, '-').result(binding)
end
end
class App
DEFAULT_CONFIG = { text_color: 'black' }.freeze
def initialize(config = {})
@config = cast_config(DEFAULT_CONFIG.merge(config))
end
def run
children = Children.from(@config.fetch(:children), options: @config)
text_color = @config.fetch(:text_color)
View.new(children: children, text_color: text_color).render
end
private
def cast_config(config)
config[:child_identifiers] = config.fetch(:child_identifiers).split(',')
config[:children] = config[:child_identifiers].map do |identifier|
label = :"#{identifier}_label"
birthday = :"#{identifier}_birthday"
independence_day = :"#{identifier}_independence_day"
{
label: config.fetch(label),
birthday: Date.parse(config.fetch(birthday)),
independence_day: Date.parse(config.fetch(independence_day))
}
end
%i[
hours_a_day_during_infant
hours_a_day_during_elementary
hours_a_day_during_junior_high_school
hours_a_day_during_high_school
hours_a_day_during_college_or_later
].each { |key| config[key] = config.fetch(key, 24).to_i }
config
rescue KeyError => e
raise ConfigurationError, "Required key missing - #{e.message}"
rescue ArgumentError
raise ConfigurationError, 'Date format might be invalid.'
end
end
end
end
if __FILE__ == $0
begin
config = BitBar::INIFile
.load
.fetch(:'remaining_time_to_spend_with_my_children', {})
if File.exists?("#{ENV['HOME']}/.bitbarrc.local")
local_config = BitBar::INIFile
.load("#{ENV['HOME']}/.bitbarrc.local")
.fetch(:'remaining_time_to_spend_with_my_children', {})
config.merge!(local_config)
end
raise BitBar::INIFile::Error if config.empty?
BitBar::RemainingTimeToSpendWithMyChildren::App.new(config).run
rescue BitBar::INIFile::Error
puts <<-EOM.gsub(/^ */, '')
⚠️
---
To setup, create or edit your ~/.bitbarrc file with a new section, like:
|
[remaining_time_to_spend_with_my_children] | font=courier color=black
;# Required | font=courier
child_identifiers = child0,child1 | font=courier
child0_label = ":girl:" | font=courier
child0_birthday = "2017-04-05+09:00" | font=courier
child0_independence_day = "2035-04-01+09:00" | font=courier
child1_label = ":boy:" | font=courier
child1_birthday = "2019-04-05+09:00" | font=courier
child1_independence_day = "2037-04-01+09:00" | font=courier
|
;# Optional | font=courier
hours_a_day_during_infant = 5 | font=courier
hours_a_day_during_elementary = 5 | font=courier
hours_a_day_during_junior_high_school = 3 | font=courier
hours_a_day_during_high_school = 2 | font=courier
hours_a_day_during_college_or_later = 2 | font=courier
EOM
rescue BitBar::RemainingTimeToSpendWithMyChildren::ConfigurationError => e
puts <<-EOM.gsub(/^ */, '')
⚠️
---
#{e.message}
EOM
end
end