dsu 2.1.4 → 2.2.0.rc.1
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/.env.test +1 -0
- data/CHANGELOG.md +13 -1
- data/Gemfile.lock +2 -1
- data/README.md +13 -0
- data/lib/dsu/cli.rb +5 -0
- data/lib/dsu/models/entry_group.rb +14 -1
- data/lib/dsu/services/entry_group/browse_service.rb +100 -0
- data/lib/dsu/subcommands/browse.rb +48 -0
- data/lib/dsu/subcommands/delete.rb +1 -0
- data/lib/dsu/subcommands/edit.rb +1 -0
- data/lib/dsu/subcommands/list.rb +1 -1
- data/lib/dsu/subcommands/theme.rb +1 -0
- data/lib/dsu/support/command_hookable.rb +7 -7
- data/lib/dsu/support/entry_group_browsable.rb +101 -0
- data/lib/dsu/support/time_comparable.rb +1 -1
- data/lib/dsu/support/time_formatable.rb +5 -1
- data/lib/dsu/support/times_sortable.rb +2 -3
- data/lib/dsu/validators/time_validator.rb +1 -1
- data/lib/dsu/version.rb +1 -1
- data/lib/dsu/views/entry_group/shared/no_entries_to_display.rb +9 -4
- data/lib/dsu/views/entry_group/shared/no_entries_to_display_for_month_of.rb +32 -0
- data/lib/dsu/views/entry_group/shared/no_entries_to_display_for_week_of.rb +33 -0
- data/lib/dsu/views/entry_group/shared/no_entries_to_display_for_year_of.rb +33 -0
- data/lib/dsu.rb +17 -6
- data/lib/locales/en/commands.yml +15 -4
- data/lib/locales/en/miscellaneous.yml +5 -2
- data/lib/locales/en/subcommands.yml +33 -0
- data/lib/seed_data/themes/light.json +79 -0
- metadata +27 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbc51c0376177977ba4f3c3d06ad2cfe42150c2122eaa826e19ec9074cc52889
|
|
4
|
+
data.tar.gz: 7647b24eb4ba59f8bfc78457a68935c9e469a893597de2e3930ba4f80e2173ba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 774f2bbfaf2bb32e6e5653f6c3e917b7b63e19f5d223e9e29cb3cc28d62e076694a58780644de2101b254d672cd8a89fce35ae61183756ca2e72d193ced41d02
|
|
7
|
+
data.tar.gz: c4359560a4589bc360239dbc8b53cef1405f444fec5dffcfba7c50be0b95607a48c84aaee7dd7b68f6c7ef5b519b664431399c7db222fa798a165dc5b8a4a35a
|
data/.env.test
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DSU_ENV=test
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [2.2.0.rc.1] 2023-12-23
|
|
2
|
+
|
|
3
|
+
Enhancements
|
|
4
|
+
|
|
5
|
+
- Added `dsu browse` command to interactively page through DSU entries.
|
|
6
|
+
- Added "light" theme for terminals with light backgrounds, see `dsu theme list` or `dsu theme show light` for more information.
|
|
7
|
+
|
|
8
|
+
Changes
|
|
9
|
+
|
|
10
|
+
- Refactors to use activesupport Time#in_time_zone, including tests.
|
|
11
|
+
- Various code refactors to support the aforementioned change.
|
|
12
|
+
|
|
1
13
|
## [2.1.4] 2023-12-19
|
|
2
14
|
|
|
3
15
|
Changes
|
|
@@ -33,7 +45,6 @@ Bug fixes
|
|
|
33
45
|
|
|
34
46
|
- Fix bug that did not included I18n locale files in yanked version 2.1.0.
|
|
35
47
|
|
|
36
|
-
|
|
37
48
|
## [2.1.0] 2023-12-16
|
|
38
49
|
|
|
39
50
|
Enhancements
|
|
@@ -215,6 +226,7 @@ Changes
|
|
|
215
226
|
- `dsu config info` now displays the default configuration being used if no configuration file is being used.
|
|
216
227
|
|
|
217
228
|
Bug fixes
|
|
229
|
+
|
|
218
230
|
- Fix bug that fails to load/use configuration file options when a config file exists.
|
|
219
231
|
|
|
220
232
|
## [0.1.0.alpha.1] 2023-05-06
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
dsu (2.1
|
|
4
|
+
dsu (2.2.0.rc.1)
|
|
5
5
|
activemodel (>= 7.0.8, < 8.0)
|
|
6
6
|
activesupport (>= 7.0.8, < 8.0)
|
|
7
7
|
colorize (>= 0.8.1, < 1.0)
|
|
@@ -122,6 +122,7 @@ GEM
|
|
|
122
122
|
PLATFORMS
|
|
123
123
|
x86_64-darwin-19
|
|
124
124
|
x86_64-darwin-21
|
|
125
|
+
x86_64-linux
|
|
125
126
|
|
|
126
127
|
DEPENDENCIES
|
|
127
128
|
dotenv (~> 2.8, >= 2.8.1)
|
data/README.md
CHANGED
|
@@ -37,6 +37,7 @@ After installation (`gem install dsu`), the first thing you may want to do is ru
|
|
|
37
37
|
#=>
|
|
38
38
|
Commands:
|
|
39
39
|
dsu add|a [OPTIONS] DESCRIPTION # Adds a DSU entry...
|
|
40
|
+
dsu browse|b SUBCOMMAND # Browse DSU entries...
|
|
40
41
|
dsu config|c SUBCOMMAND # Manage configuration...
|
|
41
42
|
dsu delete|d SUBCOMMAND # Delete DSU entries...
|
|
42
43
|
dsu edit|e SUBCOMMAND # Edit DSU entries...
|
|
@@ -176,6 +177,18 @@ This can be accomplished MUCH easier by using the `yesterday` mnemonic. This wil
|
|
|
176
177
|
`$ dsu list dates --from yesterday --to -6`
|
|
177
178
|
`$ dsu l dd -f y -t -6`
|
|
178
179
|
|
|
180
|
+
## Browsing DSU Entries
|
|
181
|
+
You can browse DSU entries for the current week, month and year using any of the following commands. `dsu browse` somewhat similar to `dsu list` with added `week`, `month` and `year` convenience SUBCOMMANDs. `dsu browse` also pipes the output to the terminal, so you can conveniently scroll through the listed entries using your keyboard or mouse:
|
|
182
|
+
|
|
183
|
+
**NOTE:** Keyboard and/or mouse behavior while browsing (scrolling), is operating system dependent; `dsu browse` pipes its output to the terminal using `less` on nix systems, and `more` on Windows systems.
|
|
184
|
+
|
|
185
|
+
- `$ dsu browse week`
|
|
186
|
+
- `$ dsu b w` # Equivalent to the above, only using shortcuts
|
|
187
|
+
- `$ dsu browse month`
|
|
188
|
+
- `$ dsu b m` # Equivalent to the above, only using shortcuts
|
|
189
|
+
- `$ dsu browse year`
|
|
190
|
+
- `$ dsu b y` # Equivalent to the above, only using shortcuts
|
|
191
|
+
|
|
179
192
|
## Editing DSU Entries
|
|
180
193
|
|
|
181
194
|
You can edit DSU entry groups by date. `dsu` will allow you to edit a DSU entry group using the `dsu edit SUBCOMMAND` date (`n|today|t|tomorrow|y|yesterday|date DATE`) you specify. `dsu edit` will open your DSU entry group entries in your editor, where you'll be able to perform editing functions against one or all of the entries.
|
data/lib/dsu/cli.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require 'fileutils'
|
|
4
4
|
require 'time'
|
|
5
5
|
require_relative 'base_cli'
|
|
6
|
+
require_relative 'subcommands/browse'
|
|
6
7
|
require_relative 'subcommands/config'
|
|
7
8
|
require_relative 'subcommands/delete'
|
|
8
9
|
require_relative 'subcommands/edit'
|
|
@@ -13,6 +14,7 @@ module Dsu
|
|
|
13
14
|
# The `dsu` command.
|
|
14
15
|
class CLI < BaseCLI
|
|
15
16
|
map I18n.t('commands.add.key_mappings') => :add
|
|
17
|
+
map I18n.t('commands.browse.key_mappings') => :browse
|
|
16
18
|
map I18n.t('commands.config.key_mappings') => :config
|
|
17
19
|
map I18n.t('commands.delete.key_mappings') => :delete
|
|
18
20
|
map I18n.t('commands.edit.key_mappings') => :edit
|
|
@@ -43,6 +45,9 @@ module Dsu
|
|
|
43
45
|
view_entry_group(time: time)
|
|
44
46
|
end
|
|
45
47
|
|
|
48
|
+
desc I18n.t('commands.browse.desc'), I18n.t('commands.browse.usage')
|
|
49
|
+
subcommand :browse, Subcommands::Browse
|
|
50
|
+
|
|
46
51
|
desc I18n.t('commands.list.desc'), I18n.t('commands.list.usage')
|
|
47
52
|
subcommand :list, Subcommands::List
|
|
48
53
|
|
|
@@ -155,6 +155,18 @@ module Dsu
|
|
|
155
155
|
superclass.exist?(file_path: entries_path_for(time: time))
|
|
156
156
|
end
|
|
157
157
|
|
|
158
|
+
def entry_group_times(between: nil)
|
|
159
|
+
entry_files.filter_map do |file_path|
|
|
160
|
+
entry_file_name = File.basename(file_path)
|
|
161
|
+
next unless entry_file_name.match?(ENTRIES_FILE_NAME_REGEX)
|
|
162
|
+
|
|
163
|
+
time = File.basename(entry_file_name, '.*')
|
|
164
|
+
next if between && !Time.parse(time).between?(between.min, between.max)
|
|
165
|
+
|
|
166
|
+
time
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
158
170
|
def find(time:)
|
|
159
171
|
file_path = entries_path_for(time: time)
|
|
160
172
|
entry_group_hash = read!(file_path: file_path)
|
|
@@ -200,7 +212,8 @@ module Dsu
|
|
|
200
212
|
private
|
|
201
213
|
|
|
202
214
|
def ensure_local_time(time)
|
|
203
|
-
time
|
|
215
|
+
time ||= Time.now
|
|
216
|
+
time.in_time_zone
|
|
204
217
|
end
|
|
205
218
|
end
|
|
206
219
|
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../models/entry_group'
|
|
4
|
+
require_relative '../../support/time_formatable'
|
|
5
|
+
require_relative '../../support/times_sortable'
|
|
6
|
+
|
|
7
|
+
module Dsu
|
|
8
|
+
module Services
|
|
9
|
+
module EntryGroup
|
|
10
|
+
# This service is responsible for returning an array of
|
|
11
|
+
# sorted entry group dates as Time objects. The Time objects
|
|
12
|
+
# returned, and the sort order are determined by the options
|
|
13
|
+
# passed in.
|
|
14
|
+
class BrowseService
|
|
15
|
+
include Support::TimeFormatable
|
|
16
|
+
include Support::TimesSortable
|
|
17
|
+
|
|
18
|
+
def initialize(time:, options: {})
|
|
19
|
+
raise ArgumentError, 'Argument time is nil' if time.nil?
|
|
20
|
+
raise ArgumentError, 'Argument options is nil' if options.nil?
|
|
21
|
+
|
|
22
|
+
@time = time
|
|
23
|
+
@options = options
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def call
|
|
27
|
+
return [] if entry_group_times.empty?
|
|
28
|
+
|
|
29
|
+
times_sort(times: entry_group_times, entries_display_order: entries_display_order)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
attr_reader :time, :options
|
|
35
|
+
|
|
36
|
+
def entry_group_times
|
|
37
|
+
@entry_group_times ||= (min_time.to_i..max_time.to_i).step(1.day).each_with_object([]) do |time_step, times|
|
|
38
|
+
time = Time.at(time_step)
|
|
39
|
+
next unless include_all? || entry_group_count(time).positive?
|
|
40
|
+
|
|
41
|
+
times << time
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def entry_group_count(time)
|
|
46
|
+
entry_group = Models::EntryGroup.find_or_initialize(time: time)
|
|
47
|
+
entry_group.persisted? ? entry_group.entries.count : 0
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def min_time
|
|
51
|
+
@min_time ||= if week?
|
|
52
|
+
time.beginning_of_week
|
|
53
|
+
elsif month?
|
|
54
|
+
time.beginning_of_month
|
|
55
|
+
elsif year?
|
|
56
|
+
time.beginning_of_year
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def max_time
|
|
61
|
+
@max_time ||= if week?
|
|
62
|
+
time.end_of_week
|
|
63
|
+
elsif month?
|
|
64
|
+
time.end_of_month
|
|
65
|
+
elsif year?
|
|
66
|
+
time.end_of_year
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def entries_display_order
|
|
71
|
+
options[:entries_display_order] || default_entries_display_order
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def default_entries_display_order
|
|
75
|
+
:asc
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def week?
|
|
79
|
+
options.fetch(:browse, default_browse) == :week
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def month?
|
|
83
|
+
options[:browse] == :month
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def year?
|
|
87
|
+
options[:browse] == :year
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def default_browse
|
|
91
|
+
:week
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def include_all?
|
|
95
|
+
options.fetch(:include_all, false)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'io/console'
|
|
4
|
+
|
|
5
|
+
require_relative '../services/stdout_redirector_service'
|
|
6
|
+
require_relative '../support/command_options/dsu_times'
|
|
7
|
+
require_relative '../support/command_options/time_mnemonic'
|
|
8
|
+
require_relative '../support/entry_group_browsable'
|
|
9
|
+
require_relative '../support/time_formatable'
|
|
10
|
+
require_relative '../views/entry_group/shared/no_entries_to_display'
|
|
11
|
+
require_relative '../views/shared/error'
|
|
12
|
+
require_relative 'base_subcommand'
|
|
13
|
+
|
|
14
|
+
module Dsu
|
|
15
|
+
module Subcommands
|
|
16
|
+
class Browse < BaseSubcommand
|
|
17
|
+
include Support::EntryGroupBrowsable
|
|
18
|
+
include Support::CommandOptions::TimeMnemonic
|
|
19
|
+
include Support::TimeFormatable
|
|
20
|
+
|
|
21
|
+
# TODO: I18n.
|
|
22
|
+
map %w[w] => :week
|
|
23
|
+
map %w[m] => :month
|
|
24
|
+
map %w[y] => :year
|
|
25
|
+
|
|
26
|
+
class_option :include_all, default: nil, type: :boolean, aliases: '-a',
|
|
27
|
+
desc: I18n.t('options.include_all')
|
|
28
|
+
|
|
29
|
+
desc I18n.t('subcommands.browse.week.desc'), I18n.t('subcommands.browse.week.usage')
|
|
30
|
+
long_desc I18n.t('subcommands.browse.week.long_desc')
|
|
31
|
+
def week
|
|
32
|
+
browse_entry_groups time: Time.now, options: options.merge({ browse: :week })
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc I18n.t('subcommands.browse.month.desc'), I18n.t('subcommands.browse.month.usage')
|
|
36
|
+
long_desc I18n.t('subcommands.browse.month.long_desc')
|
|
37
|
+
def month
|
|
38
|
+
browse_entry_groups time: Time.now, options: options.merge({ browse: :month })
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
desc I18n.t('subcommands.browse.year.desc'), I18n.t('subcommands.browse.year.usage')
|
|
42
|
+
long_desc I18n.t('subcommands.browse.year.long_desc')
|
|
43
|
+
def year
|
|
44
|
+
browse_entry_groups time: Time.now, options: options.merge({ browse: :year })
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/dsu/subcommands/edit.rb
CHANGED
data/lib/dsu/subcommands/list.rb
CHANGED
|
@@ -79,7 +79,7 @@ module Dsu
|
|
|
79
79
|
# NOTE: special sort here, unlike the other commands where rules for
|
|
80
80
|
# displaying DSU entries are applied; this is more of a list command.
|
|
81
81
|
times = times_sort(times: times, entries_display_order: options[:entries_display_order])
|
|
82
|
-
view_entry_groups(times: times, options: options) do
|
|
82
|
+
view_entry_groups(times: times, options: options) do
|
|
83
83
|
if Services::EntryGroup::CounterService.new(times: times).call.zero?
|
|
84
84
|
Views::EntryGroup::Shared::NoEntriesToDisplay.new(times: times, options: options).render
|
|
85
85
|
end
|
|
@@ -30,6 +30,13 @@ module Dsu
|
|
|
30
30
|
puts
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def display_dsu_footer
|
|
34
|
+
puts apply_theme('_' * 35, theme_color: color_theme.dsu_footer)
|
|
35
|
+
# TODO: I18n.
|
|
36
|
+
footer = apply_theme("Theme: #{color_theme.theme_name}", theme_color: color_theme.dsu_footer)
|
|
37
|
+
puts footer
|
|
38
|
+
end
|
|
39
|
+
|
|
33
40
|
private
|
|
34
41
|
|
|
35
42
|
def suspend_header?(args, _options)
|
|
@@ -39,13 +46,6 @@ module Dsu
|
|
|
39
46
|
true if args[0] == 'theme' && %w[use delete].include?(args[1])
|
|
40
47
|
end
|
|
41
48
|
|
|
42
|
-
def display_dsu_footer
|
|
43
|
-
puts apply_theme('_' * 35, theme_color: color_theme.dsu_footer)
|
|
44
|
-
# TODO: I18n.
|
|
45
|
-
footer = apply_theme("Theme: #{color_theme.theme_name}", theme_color: color_theme.dsu_footer)
|
|
46
|
-
puts footer
|
|
47
|
-
end
|
|
48
|
-
|
|
49
49
|
def display_errors_if(stderror_string)
|
|
50
50
|
stderror_string = stderror_string.strip
|
|
51
51
|
return unless stderror_string.present?
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../models/configuration'
|
|
4
|
+
require_relative '../services/entry_group/browse_service'
|
|
5
|
+
require_relative '../services/entry_group/counter_service'
|
|
6
|
+
|
|
7
|
+
module Dsu
|
|
8
|
+
module Support
|
|
9
|
+
module EntryGroupBrowsable
|
|
10
|
+
def browse_entry_groups(time:, options: {})
|
|
11
|
+
raise ArgumentError, 'time must be a Time object' unless time.is_a?(Time)
|
|
12
|
+
raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
|
|
13
|
+
|
|
14
|
+
options = configuration.to_h.merge(options).with_indifferent_access
|
|
15
|
+
times = browse_service(time: time, options: options).call
|
|
16
|
+
if times.empty? || (options.fetch(:include_all, false) && no_entries_for?(times: times, options: options))
|
|
17
|
+
display_no_entries_to_display_message time: time, options: options
|
|
18
|
+
return
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
output = Services::StdoutRedirectorService.call do
|
|
22
|
+
self.class.display_dsu_header
|
|
23
|
+
header = browse_header_for(time: time, options: options)
|
|
24
|
+
Views::Shared::Info.new(messages: header).render
|
|
25
|
+
puts
|
|
26
|
+
view_entry_groups(times: times, options: options)
|
|
27
|
+
self.class.display_dsu_footer
|
|
28
|
+
end
|
|
29
|
+
output_with_pager output: output, options: options
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def no_entries_for?(times:, options:)
|
|
35
|
+
Services::EntryGroup::CounterService.new(times: times, options: options).call.zero?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def browse_header_for(time:, options:)
|
|
39
|
+
of, times = case options[:browse]
|
|
40
|
+
when :week
|
|
41
|
+
[
|
|
42
|
+
I18n.t('subcommands.browse.headers.week_of', week: time.beginning_of_week.to_date),
|
|
43
|
+
[time.beginning_of_week, time.end_of_week]
|
|
44
|
+
]
|
|
45
|
+
when :month
|
|
46
|
+
[
|
|
47
|
+
I18n.t('subcommands.browse.headers.month_of', month: I18n.l(time, format: '%B')),
|
|
48
|
+
[time.beginning_of_month, time.end_of_month]
|
|
49
|
+
]
|
|
50
|
+
when :year
|
|
51
|
+
[
|
|
52
|
+
I18n.t('subcommands.browse.headers.year_of', year: time.to_date.year),
|
|
53
|
+
[time.beginning_of_year, time.end_of_year]
|
|
54
|
+
]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
I18n.t('subcommands.browse.headers.browsing', of: of, from: times.min.to_date.to_s, to: times.max.to_date.to_s)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def output_with_pager(output:, options:)
|
|
61
|
+
if options[:pager] == :no_pager
|
|
62
|
+
puts output
|
|
63
|
+
return
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
pager_command = if RUBY_PLATFORM.match?(/win32|windows/i)
|
|
67
|
+
'more' # Windows command
|
|
68
|
+
else
|
|
69
|
+
'less' # Unix-like command
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
IO.popen(pager_command, 'w') do |pipe|
|
|
73
|
+
pipe.puts output
|
|
74
|
+
pipe.close_write
|
|
75
|
+
end
|
|
76
|
+
rescue Errno::ENOENT
|
|
77
|
+
message = "Operating system pager command (#{pager_command}) not found. Falling back to direct output."
|
|
78
|
+
Views::Shared::Error.new(messages: message).render
|
|
79
|
+
puts output
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def display_no_entries_to_display_message(time:, options:)
|
|
83
|
+
case options[:browse]
|
|
84
|
+
when :week
|
|
85
|
+
Views::EntryGroup::Shared::NoEntriesToDisplayForWeekOf.new(time: time, options: options).render
|
|
86
|
+
when :month
|
|
87
|
+
Views::EntryGroup::Shared::NoEntriesToDisplayForMonthOf.new(time: time, options: options).render
|
|
88
|
+
when :year
|
|
89
|
+
Views::EntryGroup::Shared::NoEntriesToDisplayForYearOf.new(time: time, options: options).render
|
|
90
|
+
else
|
|
91
|
+
raise NotImplementedError, 'Unhandled option; ' \
|
|
92
|
+
"expected :week, :month, or :year but received #{options[:browse]}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def browse_service(time:, options: {})
|
|
97
|
+
Services::EntryGroup::BrowseService.new(time: time, options: options)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -12,7 +12,7 @@ module Dsu
|
|
|
12
12
|
|
|
13
13
|
# TODO: I18n.
|
|
14
14
|
def formatted_time(time:)
|
|
15
|
-
time = time.
|
|
15
|
+
time = time.in_time_zone
|
|
16
16
|
|
|
17
17
|
today_yesterday_or_tomorrow = if time.today?
|
|
18
18
|
'Today'
|
|
@@ -39,6 +39,10 @@ module Dsu
|
|
|
39
39
|
time.strftime("%m#{separator}%d#{separator}%Y")
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
def dd_mm_yyyy(time:, separator: '/')
|
|
43
|
+
time.strftime("%d#{separator}%m#{separator}%Y")
|
|
44
|
+
end
|
|
45
|
+
|
|
42
46
|
def timezone_for(time:)
|
|
43
47
|
time.zone
|
|
44
48
|
end
|
|
@@ -23,10 +23,9 @@ module Dsu
|
|
|
23
23
|
# in ascending order. If the sort is descending, then in order to
|
|
24
24
|
# properly reverse the times array, it needs to first be sorted in
|
|
25
25
|
# ascending order before being reversed.
|
|
26
|
-
times.sort
|
|
27
|
-
times.reverse! if entries_display_order == :desc
|
|
26
|
+
return times.sort if entries_display_order == :asc
|
|
28
27
|
|
|
29
|
-
times
|
|
28
|
+
times.sort_by { |time| -time.to_i }
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
def times_for(times:)
|
data/lib/dsu/version.rb
CHANGED
|
@@ -23,10 +23,6 @@ module Dsu
|
|
|
23
23
|
|
|
24
24
|
# TODO: I18n.
|
|
25
25
|
def render
|
|
26
|
-
times.sort!
|
|
27
|
-
time_range = "#{formatted_time(time: times.first)} " \
|
|
28
|
-
"through #{formatted_time(time: times.last)}"
|
|
29
|
-
message = "(nothing to display for #{time_range})"
|
|
30
26
|
puts apply_theme(message, theme_color: color_theme.info)
|
|
31
27
|
end
|
|
32
28
|
|
|
@@ -34,6 +30,15 @@ module Dsu
|
|
|
34
30
|
|
|
35
31
|
attr_reader :times, :options
|
|
36
32
|
|
|
33
|
+
def message
|
|
34
|
+
"(nothing to display for #{time_range})"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def time_range
|
|
38
|
+
"#{formatted_time(time: times.min)} " \
|
|
39
|
+
"through #{formatted_time(time: times.max)}"
|
|
40
|
+
end
|
|
41
|
+
|
|
37
42
|
def color_theme
|
|
38
43
|
@color_theme ||= Models::ColorTheme.current_or_default
|
|
39
44
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'no_entries_to_display'
|
|
4
|
+
|
|
5
|
+
module Dsu
|
|
6
|
+
module Views
|
|
7
|
+
module EntryGroup
|
|
8
|
+
module Shared
|
|
9
|
+
class NoEntriesToDisplayForMonthOf < NoEntriesToDisplay
|
|
10
|
+
def initialize(time:, options: {})
|
|
11
|
+
super(times: [time.beginning_of_month, time.end_of_month], options: options)
|
|
12
|
+
|
|
13
|
+
@time = time
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
attr_reader :time
|
|
19
|
+
|
|
20
|
+
# TODO: I18n.
|
|
21
|
+
def message
|
|
22
|
+
"(nothing to display for the month of #{month_string}, #{time_range})"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def month_string
|
|
26
|
+
I18n.l(time, format: '%B')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'no_entries_to_display'
|
|
4
|
+
|
|
5
|
+
module Dsu
|
|
6
|
+
module Views
|
|
7
|
+
module EntryGroup
|
|
8
|
+
module Shared
|
|
9
|
+
class NoEntriesToDisplayForWeekOf < NoEntriesToDisplay
|
|
10
|
+
def initialize(time:, options: {})
|
|
11
|
+
super(times: [time.beginning_of_week, time.end_of_week], options: options)
|
|
12
|
+
|
|
13
|
+
@time = time
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
attr_reader :time
|
|
19
|
+
|
|
20
|
+
# TODO: I18n.
|
|
21
|
+
def message
|
|
22
|
+
"(nothing to display for week of #{week_of_string}, #{time_range})"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# TODO: I18n.
|
|
26
|
+
def week_of_string
|
|
27
|
+
time.to_date
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'no_entries_to_display'
|
|
4
|
+
|
|
5
|
+
module Dsu
|
|
6
|
+
module Views
|
|
7
|
+
module EntryGroup
|
|
8
|
+
module Shared
|
|
9
|
+
class NoEntriesToDisplayForYearOf < NoEntriesToDisplay
|
|
10
|
+
def initialize(time:, options: {})
|
|
11
|
+
super(times: [time.beginning_of_year, time.end_of_year], options: options)
|
|
12
|
+
|
|
13
|
+
@time = time
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
attr_reader :time
|
|
19
|
+
|
|
20
|
+
# TODO: I18n.
|
|
21
|
+
def message
|
|
22
|
+
"(nothing to display for the year of #{year_string}, #{time_range})"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# TODO: I18n.
|
|
26
|
+
def year_string
|
|
27
|
+
time.year
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/dsu.rb
CHANGED
|
@@ -24,11 +24,22 @@ Dir.glob("#{__dir__}/dsu/**/*.rb").each do |file|
|
|
|
24
24
|
require file
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
unless Dsu.env.test? || Dsu.env.development?
|
|
28
|
+
if Dsu::Migration::Service.run_migrations?
|
|
29
|
+
begin
|
|
30
|
+
Dsu::Migration::Service.new.call
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
puts I18n.t('migrations.error.failed', message: e.message)
|
|
33
|
+
exit 1
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
# TODO: Hack. Integrate this into the migration service
|
|
37
|
+
# so that this runs only if the migration version changes.
|
|
38
|
+
theme_file = 'light.json'
|
|
39
|
+
destination_theme_file_path = File.join(Dsu::Support::Fileable.themes_folder, theme_file)
|
|
40
|
+
unless File.exist?(destination_theme_file_path)
|
|
41
|
+
source_theme_file_path = File.join(Dsu::Support::Fileable.seed_data_folder, 'themes', theme_file)
|
|
42
|
+
FileUtils.cp(source_theme_file_path, destination_theme_file_path)
|
|
43
|
+
puts I18n.t('migrations.information.theme_copied', from: source_theme_file_path, to: destination_theme_file_path)
|
|
33
44
|
end
|
|
34
45
|
end
|
data/lib/locales/en/commands.yml
CHANGED
|
@@ -49,6 +49,10 @@ en:
|
|
|
49
49
|
DESCRIPTION
|
|
50
50
|
|
|
51
51
|
Must be be between 2 and 256 characters (inclusive) in length.
|
|
52
|
+
browse:
|
|
53
|
+
key_mappings: b
|
|
54
|
+
desc: browse|b SUBCOMMAND
|
|
55
|
+
usage: Browse DSU entries by the given SUBCOMMAND
|
|
52
56
|
config:
|
|
53
57
|
key_mappings: c
|
|
54
58
|
desc: config|c SUBCOMMAND
|
|
@@ -115,11 +119,18 @@ en:
|
|
|
115
119
|
date_option_description: |
|
|
116
120
|
DATE
|
|
117
121
|
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
In the format of: [d]d-|/[m]m-|/[-|/yyyy].
|
|
123
|
+
|
|
124
|
+
This may be any date string that can be parsed using ruby's `Time.parse`.
|
|
125
|
+
Consequently, you may use use '-' or '/' as date separators,
|
|
120
126
|
as well as omit the year if the date you want to display is the
|
|
121
|
-
current year (e.g. <month>/<day>, or 1/31).
|
|
122
|
-
|
|
127
|
+
current year (e.g. <month>/<day>, or 1/31). Leading zeroes are optional.
|
|
128
|
+
For example: `require 'time'; Time.parse('1/2') # etc.`
|
|
129
|
+
|
|
130
|
+
IMPORTANT: If you include the year as part of your date string, the format of
|
|
131
|
+
the date must be <day>/<month>/<year>, where <day> and <month> may include optional
|
|
132
|
+
leading zeroes, and <year> must be in the format of yyyy. For example
|
|
133
|
+
`require 'time'; Time.parse('31/1/2023') # etc.`
|
|
123
134
|
mnemonic_option_description: |
|
|
124
135
|
MNEMONIC
|
|
125
136
|
|
|
@@ -6,8 +6,6 @@ en:
|
|
|
6
6
|
error: "Error: %{message}"
|
|
7
7
|
from_option_invalid: Option -f, [--from=DATE|MNEMONIC] value is invalid ["%{from_option}"]
|
|
8
8
|
to_option_invalid: Option -t, [--to=DATE|MNEMONIC] value is invalid ["%{to_option}"]
|
|
9
|
-
migration:
|
|
10
|
-
error: "Error running migrations: %{message}"
|
|
11
9
|
headers:
|
|
12
10
|
entry:
|
|
13
11
|
could_not_be_added: "An error was encountered; the entry could not be added:"
|
|
@@ -21,3 +19,8 @@ en:
|
|
|
21
19
|
information:
|
|
22
20
|
dates:
|
|
23
21
|
through: "%{from} thru %{to}"
|
|
22
|
+
migrations:
|
|
23
|
+
error:
|
|
24
|
+
failed: "Error running migrations: %{message}"
|
|
25
|
+
information:
|
|
26
|
+
theme_copied: Theme copied from %{from} to %{to}.
|
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
# lib/dsu/subcommands
|
|
2
2
|
en:
|
|
3
3
|
subcommands:
|
|
4
|
+
browse:
|
|
5
|
+
week:
|
|
6
|
+
desc: week|w
|
|
7
|
+
usage: Browse DSU entries for the current week
|
|
8
|
+
long_desc: |
|
|
9
|
+
Browse DSU entries for the current week.
|
|
10
|
+
|
|
11
|
+
$ dsu browse w
|
|
12
|
+
|
|
13
|
+
$ dsu browse w
|
|
14
|
+
month:
|
|
15
|
+
desc: month|m
|
|
16
|
+
usage: Browse DSU entries for the current month
|
|
17
|
+
long_desc: |
|
|
18
|
+
Browse DSU entries for the current month.
|
|
19
|
+
|
|
20
|
+
$ dsu browse month
|
|
21
|
+
|
|
22
|
+
$ dsu browse m
|
|
23
|
+
year:
|
|
24
|
+
desc: year|y
|
|
25
|
+
usage: Browse DSU entries for the current year
|
|
26
|
+
long_desc: |
|
|
27
|
+
Browse DSU entries for the current year.
|
|
28
|
+
|
|
29
|
+
$ dsu browse year
|
|
30
|
+
|
|
31
|
+
$ dsu browse y
|
|
32
|
+
headers:
|
|
33
|
+
browsing: Browsing DSU entries for %{of} (%{from} thru %{to})
|
|
34
|
+
week_of: Week of %{week}
|
|
35
|
+
month_of: Month of %{month}
|
|
36
|
+
year_of: Year of %{year}
|
|
4
37
|
config:
|
|
5
38
|
delete:
|
|
6
39
|
desc: delete
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 20230613121411,
|
|
3
|
+
"description": "Theme for Light backgrounds",
|
|
4
|
+
"help": {
|
|
5
|
+
"color": "default",
|
|
6
|
+
"mode": "default",
|
|
7
|
+
"background": "default"
|
|
8
|
+
},
|
|
9
|
+
"dsu_header": {
|
|
10
|
+
"color": "white",
|
|
11
|
+
"mode": "bold",
|
|
12
|
+
"background": "black"
|
|
13
|
+
},
|
|
14
|
+
"dsu_footer": {
|
|
15
|
+
"color": "default",
|
|
16
|
+
"mode": "default",
|
|
17
|
+
"background": "default"
|
|
18
|
+
},
|
|
19
|
+
"header": {
|
|
20
|
+
"color": "default",
|
|
21
|
+
"mode": "bold",
|
|
22
|
+
"background": "default"
|
|
23
|
+
},
|
|
24
|
+
"subheader": {
|
|
25
|
+
"color": "default",
|
|
26
|
+
"mode": "underline",
|
|
27
|
+
"background": "default"
|
|
28
|
+
},
|
|
29
|
+
"body": {
|
|
30
|
+
"color": "default",
|
|
31
|
+
"mode": "default",
|
|
32
|
+
"background": "default"
|
|
33
|
+
},
|
|
34
|
+
"footer": {
|
|
35
|
+
"color": "light_default",
|
|
36
|
+
"mode": "default",
|
|
37
|
+
"background": "default"
|
|
38
|
+
},
|
|
39
|
+
"date": {
|
|
40
|
+
"color": "default",
|
|
41
|
+
"mode": "bold",
|
|
42
|
+
"background": "default"
|
|
43
|
+
},
|
|
44
|
+
"index": {
|
|
45
|
+
"color": "light_default",
|
|
46
|
+
"mode": "italic",
|
|
47
|
+
"background": "default"
|
|
48
|
+
},
|
|
49
|
+
"info": {
|
|
50
|
+
"color": "white",
|
|
51
|
+
"mode": "bold",
|
|
52
|
+
"background": "blue"
|
|
53
|
+
},
|
|
54
|
+
"success": {
|
|
55
|
+
"color": "white",
|
|
56
|
+
"mode": "bold",
|
|
57
|
+
"background": "green"
|
|
58
|
+
},
|
|
59
|
+
"warning": {
|
|
60
|
+
"color": "default",
|
|
61
|
+
"mode": "bold",
|
|
62
|
+
"background": "yellow"
|
|
63
|
+
},
|
|
64
|
+
"error": {
|
|
65
|
+
"color": "light_yellow",
|
|
66
|
+
"mode": "default",
|
|
67
|
+
"background": "red"
|
|
68
|
+
},
|
|
69
|
+
"prompt": {
|
|
70
|
+
"color": "default",
|
|
71
|
+
"mode": "bold",
|
|
72
|
+
"background": "default"
|
|
73
|
+
},
|
|
74
|
+
"prompt_options": {
|
|
75
|
+
"color": "default",
|
|
76
|
+
"mode": "bold",
|
|
77
|
+
"background": "default"
|
|
78
|
+
}
|
|
79
|
+
}
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dsu
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1
|
|
4
|
+
version: 2.2.0.rc.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gene M. Angelo, Jr.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-12-
|
|
11
|
+
date: 2023-12-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -145,6 +145,7 @@ executables:
|
|
|
145
145
|
extensions: []
|
|
146
146
|
extra_rdoc_files: []
|
|
147
147
|
files:
|
|
148
|
+
- ".env.test"
|
|
148
149
|
- ".reek.yml"
|
|
149
150
|
- ".rspec"
|
|
150
151
|
- ".rubocop.yml"
|
|
@@ -186,6 +187,7 @@ files:
|
|
|
186
187
|
- lib/dsu/services/color_theme/hydrator_service.rb
|
|
187
188
|
- lib/dsu/services/configuration/hydrator_service.rb
|
|
188
189
|
- lib/dsu/services/entry/hydrator_service.rb
|
|
190
|
+
- lib/dsu/services/entry_group/browse_service.rb
|
|
189
191
|
- lib/dsu/services/entry_group/counter_service.rb
|
|
190
192
|
- lib/dsu/services/entry_group/deleter_service.rb
|
|
191
193
|
- lib/dsu/services/entry_group/editor_service.rb
|
|
@@ -196,6 +198,7 @@ files:
|
|
|
196
198
|
- lib/dsu/services/temp_file/reader_service.rb
|
|
197
199
|
- lib/dsu/services/temp_file/writer_service.rb
|
|
198
200
|
- lib/dsu/subcommands/base_subcommand.rb
|
|
201
|
+
- lib/dsu/subcommands/browse.rb
|
|
199
202
|
- lib/dsu/subcommands/config.rb
|
|
200
203
|
- lib/dsu/subcommands/delete.rb
|
|
201
204
|
- lib/dsu/subcommands/edit.rb
|
|
@@ -210,6 +213,7 @@ files:
|
|
|
210
213
|
- lib/dsu/support/command_options/time_mnemonic.rb
|
|
211
214
|
- lib/dsu/support/command_options/time_mnemonics.rb
|
|
212
215
|
- lib/dsu/support/descriptable.rb
|
|
216
|
+
- lib/dsu/support/entry_group_browsable.rb
|
|
213
217
|
- lib/dsu/support/entry_group_viewable.rb
|
|
214
218
|
- lib/dsu/support/field_errors.rb
|
|
215
219
|
- lib/dsu/support/fileable.rb
|
|
@@ -229,6 +233,9 @@ files:
|
|
|
229
233
|
- lib/dsu/views/configuration/show.rb
|
|
230
234
|
- lib/dsu/views/entry_group/edit.rb
|
|
231
235
|
- lib/dsu/views/entry_group/shared/no_entries_to_display.rb
|
|
236
|
+
- lib/dsu/views/entry_group/shared/no_entries_to_display_for_month_of.rb
|
|
237
|
+
- lib/dsu/views/entry_group/shared/no_entries_to_display_for_week_of.rb
|
|
238
|
+
- lib/dsu/views/entry_group/shared/no_entries_to_display_for_year_of.rb
|
|
232
239
|
- lib/dsu/views/entry_group/show.rb
|
|
233
240
|
- lib/dsu/views/shared/error.rb
|
|
234
241
|
- lib/dsu/views/shared/info.rb
|
|
@@ -245,6 +252,7 @@ files:
|
|
|
245
252
|
- lib/seed_data/themes/cherry.json
|
|
246
253
|
- lib/seed_data/themes/default.json
|
|
247
254
|
- lib/seed_data/themes/lemon.json
|
|
255
|
+
- lib/seed_data/themes/light.json
|
|
248
256
|
- lib/seed_data/themes/matrix.json
|
|
249
257
|
- lib/seed_data/themes/whiteout.json
|
|
250
258
|
- sig/dsu.rbs
|
|
@@ -264,11 +272,26 @@ post_install_message: |
|
|
|
264
272
|
View the dsu README.md here: https://github.com/gangelo/dsu
|
|
265
273
|
View the dsu CHANGELOG.md: https://github.com/gangelo/dsu/blob/main/CHANGELOG.md
|
|
266
274
|
|
|
275
|
+
Dsu now has a browse command! Try it out by running `dsu browse help`.
|
|
276
|
+
|
|
277
|
+
Dsu now has a "light" theme for light background terminals! Try it out by running `dsu theme use light`.
|
|
278
|
+
|
|
267
279
|
Dsu now has a delete command! Try it out by running `dsu delete help`.
|
|
268
280
|
|
|
269
281
|
Try a dsu theme by running `dsu theme list` and then `dsu theme use THEME_NAME` where THEME_NAME is the name of the theme you want to try.
|
|
270
282
|
|
|
271
283
|
:)
|
|
284
|
+
|
|
285
|
+
Merry CHRISTmas, New Years and Happy holidays from dsu!
|
|
286
|
+
*
|
|
287
|
+
/*\
|
|
288
|
+
*/*|\*
|
|
289
|
+
/*/|\*\
|
|
290
|
+
*/**|\*\*
|
|
291
|
+
*//*|*\*\*\
|
|
292
|
+
|||
|
|
293
|
+
|||
|
|
294
|
+
|||
|
|
272
295
|
rdoc_options: []
|
|
273
296
|
require_paths:
|
|
274
297
|
- lib
|
|
@@ -279,9 +302,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
279
302
|
version: 3.0.1
|
|
280
303
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
281
304
|
requirements:
|
|
282
|
-
- - "
|
|
305
|
+
- - ">"
|
|
283
306
|
- !ruby/object:Gem::Version
|
|
284
|
-
version:
|
|
307
|
+
version: 1.3.1
|
|
285
308
|
requirements: []
|
|
286
309
|
rubygems_version: 3.2.15
|
|
287
310
|
signing_key:
|