dsu 2.1.4 → 2.2.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|