dsu 2.4.2 → 2.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +8 -8
- data/README.md +14 -5
- data/lib/dsu/base_cli.rb +0 -2
- data/lib/dsu/cli.rb +5 -1
- data/lib/dsu/crud/json_file.rb +30 -16
- data/lib/dsu/env.rb +20 -1
- data/lib/dsu/models/color_theme.rb +1 -1
- data/lib/dsu/models/configuration.rb +2 -0
- data/lib/dsu/models/entry_group.rb +1 -1
- data/lib/dsu/models/migration_version.rb +2 -0
- data/lib/dsu/presenters/base_presenter_ex.rb +1 -1
- data/lib/dsu/presenters/configuration_presenter.rb +6 -1
- data/lib/dsu/presenters/entry_group/list/date_presenter.rb +77 -0
- data/lib/dsu/presenters/entry_group/list/dates_presenter.rb +60 -0
- data/lib/dsu/presenters/entry_group/list/messages.rb +15 -0
- data/lib/dsu/presenters/entry_group/list/nothing_to_list.rb +15 -0
- data/lib/dsu/services/entry_group/editor_service.rb +1 -1
- data/lib/dsu/subcommands/list.rb +13 -10
- data/lib/dsu/support/command_hookable.rb +5 -0
- data/lib/dsu/support/entry_group_browsable.rb +4 -1
- data/lib/dsu/version.rb +1 -1
- data/lib/dsu/views/entry_group/list.rb +23 -0
- data/lib/dsu/views/entry_group/show.rb +5 -1
- data/lib/locales/en/services.yml +2 -2
- metadata +35 -30
- data/lib/dsu/crud/raw_json_file.rb +0 -51
- data/lib/dsu/support/entry_group_viewable.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c8163f812eaded6f88a8fd25e501ebca51e820f7d93b06ab349a9fb4facecde
|
4
|
+
data.tar.gz: e18ed74fa803102a3cc957d0b9ce420831ed4054d23d431338af2681a012d236
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b69dce6d25a7bba32473a8cfbe1c82130ffd19f37a34b8a2c1514f9397add35611dbd8d08da642b03f2eb0c5eab42a2f0b35d20335ed0f9c3f98f19ceeaef9be
|
7
|
+
data.tar.gz: 353119cd18d19d9d4f77afcf437a35f1ca262aa9da5cdc91d0477a7d7d373490bac75a2d91d21ebd7dca76930bb3fbde53e4e8fa53693e4364239c8942020cd9
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## [2.4.3] 2024-01-07
|
2
|
+
|
3
|
+
Changes
|
4
|
+
|
5
|
+
- Code refactors, nothing discernable to the end user.
|
6
|
+
|
7
|
+
Bug fixes
|
8
|
+
|
9
|
+
- Fixed bug that failed to display the correct editor if the editor session failed to open. For example, if the editor session failed to open and the EDITOR environment variable was used, the editor in the configuration file would be displayed instead of the EDITOR environment variable value.
|
10
|
+
|
1
11
|
## [2.4.2] 2024-01-06
|
2
12
|
|
3
13
|
Changes
|
data/Gemfile.lock
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dsu (2.4.
|
4
|
+
dsu (2.4.3)
|
5
5
|
activemodel (>= 7.0.8, < 8.0)
|
6
6
|
activesupport (>= 7.0.8, < 8.0)
|
7
|
-
colorize (>=
|
7
|
+
colorize (>= 1.1, < 2.0)
|
8
8
|
os (>= 1.1, < 2.0)
|
9
9
|
thor (>= 1.2, < 2.0)
|
10
10
|
thor_nested_subcommand (>= 1.0, < 2.0)
|
@@ -29,7 +29,7 @@ GEM
|
|
29
29
|
bigdecimal (3.1.5)
|
30
30
|
byebug (11.1.3)
|
31
31
|
coderay (1.1.3)
|
32
|
-
colorize (
|
32
|
+
colorize (1.1.0)
|
33
33
|
concurrent-ruby (1.2.2)
|
34
34
|
connection_pool (2.4.1)
|
35
35
|
diff-lcs (1.5.0)
|
@@ -93,7 +93,7 @@ GEM
|
|
93
93
|
parser (~> 3.2.0)
|
94
94
|
rainbow (>= 2.0, < 4.0)
|
95
95
|
rexml (~> 3.1)
|
96
|
-
regexp_parser (2.
|
96
|
+
regexp_parser (2.9.0)
|
97
97
|
rexml (3.2.6)
|
98
98
|
rspec (3.12.0)
|
99
99
|
rspec-core (~> 3.12.0)
|
@@ -123,9 +123,9 @@ GEM
|
|
123
123
|
parser (>= 3.2.1.0)
|
124
124
|
rubocop-capybara (2.20.0)
|
125
125
|
rubocop (~> 1.41)
|
126
|
-
rubocop-factory_bot (2.25.
|
127
|
-
rubocop (~> 1.
|
128
|
-
rubocop-performance (1.20.
|
126
|
+
rubocop-factory_bot (2.25.1)
|
127
|
+
rubocop (~> 1.41)
|
128
|
+
rubocop-performance (1.20.2)
|
129
129
|
rubocop (>= 1.48.1, < 2.0)
|
130
130
|
rubocop-ast (>= 1.30.0, < 2.0)
|
131
131
|
rubocop-rspec (2.26.1)
|
@@ -141,7 +141,7 @@ GEM
|
|
141
141
|
simplecov-html (0.12.3)
|
142
142
|
simplecov_json_formatter (0.1.4)
|
143
143
|
thor (1.3.0)
|
144
|
-
thor_nested_subcommand (1.0.
|
144
|
+
thor_nested_subcommand (1.0.6)
|
145
145
|
tzinfo (2.0.6)
|
146
146
|
concurrent-ruby (~> 1.0)
|
147
147
|
unicode-display_width (2.5.0)
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# `dsu`
|
2
2
|
|
3
3
|
[![Ruby](https://github.com/gangelo/dsu/actions/workflows/ruby.yml/badge.svg)](https://github.com/gangelo/dsu/actions/workflows/ruby.yml)
|
4
|
-
[![GitHub version](http://badge.fury.io/gh/gangelo%2Fdsu.svg?refresh=
|
5
|
-
[![Gem Version](https://badge.fury.io/rb/dsu.svg?refresh=
|
4
|
+
[![GitHub version](http://badge.fury.io/gh/gangelo%2Fdsu.svg?refresh=9)](https://badge.fury.io/gh/gangelo%2Fdsu)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/dsu.svg?refresh=9)](https://badge.fury.io/rb/dsu)
|
6
6
|
[![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/dsu/)
|
7
7
|
[![Report Issues](https://img.shields.io/badge/report-issues-red.svg)](https://github.com/gangelo/dsu/issues)
|
8
8
|
[![License](http://img.shields.io/badge/license-MIT-yellowgreen.svg)](#license)
|
@@ -12,6 +12,9 @@
|
|
12
12
|
- `dsu` uses _no_ network connections whatsoever.
|
13
13
|
- `dsu` stores all of its data _locally_, in .json files.
|
14
14
|
- `dsu` is a simple (but powerful) command-line tool for users who _love_ to work within the terminal.
|
15
|
+
- `dsu` versioning follows the [semantic versioning standard](https://semver.org/) (MAJOR.MINOR.PATCH).
|
16
|
+
- See the [CHANGELOG.md](https://github.com/gangelo/dsu/blob/main/CHANGELOG.md) before upgrading to a MAJOR `dsu` version.
|
17
|
+
- See the [Exporting DSU entries](https://github.com/gangelo/dsu/wiki/Exporting-DSU-entries) wiki on how to export (backup) your data.
|
15
18
|
|
16
19
|
# Installation
|
17
20
|
```shell
|
@@ -22,9 +25,15 @@ gem install dsu
|
|
22
25
|
[https://github.com/gangelo/dsu/wiki](https://github.com/gangelo/dsu/wiki)
|
23
26
|
|
24
27
|
# Examples
|
25
|
-
|
28
|
+
The [dsu wiki](https://github.com/gangelo/dsu/wiki) is repleat with practical examples on how to use `dsu`.
|
29
|
+
|
30
|
+
It you're interested in how _I personally_ use `dsu` every day, I blog about it [here](https://genemangelojr.blogspot.com/2024/01/the-dsu-ruby-gem-workflow-how-to-use-it.html).
|
26
31
|
|
27
32
|
# Supported ruby versions
|
28
|
-
|
33
|
+
`dsu` _should_ work with any ruby version `['>= 3.0.1', '< 4.0']`; however, `dsu` is currently tested against the following ruby versions:
|
34
|
+
- 3.0.1
|
35
|
+
- 3.0.6
|
36
|
+
- 3.1.4
|
37
|
+
- 3.2.2
|
29
38
|
|
30
|
-
Copyright (c) 2023-2024 Erik Gene Angelo. See [LICENSE](https://github.com/gangelo/dsu/LICENSE.txt) for details.
|
39
|
+
Copyright (c) 2023-2024 Erik Gene Angelo. See [LICENSE](https://github.com/gangelo/dsu/blob/main/LICENSE.txt) for details.
|
data/lib/dsu/base_cli.rb
CHANGED
@@ -10,7 +10,6 @@ require_relative 'services/stdout_redirector_service'
|
|
10
10
|
require_relative 'support/color_themable'
|
11
11
|
require_relative 'support/command_help_colorizeable'
|
12
12
|
require_relative 'support/command_hookable'
|
13
|
-
require_relative 'support/entry_group_viewable'
|
14
13
|
require_relative 'support/times_sortable'
|
15
14
|
require_relative 'version'
|
16
15
|
require_relative 'views/entry_group/show'
|
@@ -20,7 +19,6 @@ module Dsu
|
|
20
19
|
include Support::ColorThemable
|
21
20
|
include Support::CommandHelpColorizable
|
22
21
|
include Support::CommandHookable
|
23
|
-
include Support::EntryGroupViewable
|
24
22
|
include Support::TimesSortable
|
25
23
|
|
26
24
|
class_option :debug, type: :boolean, default: false
|
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 'presenters/entry_group/list/date_presenter'
|
6
7
|
require_relative 'subcommands/browse'
|
7
8
|
require_relative 'subcommands/config'
|
8
9
|
require_relative 'subcommands/delete'
|
@@ -11,6 +12,7 @@ require_relative 'subcommands/export'
|
|
11
12
|
require_relative 'subcommands/import'
|
12
13
|
require_relative 'subcommands/list'
|
13
14
|
require_relative 'subcommands/theme'
|
15
|
+
require_relative 'views/entry_group/list'
|
14
16
|
|
15
17
|
module Dsu
|
16
18
|
# The `dsu` command.
|
@@ -46,7 +48,9 @@ module Dsu
|
|
46
48
|
end
|
47
49
|
entry = Models::Entry.new(description: description)
|
48
50
|
CommandServices::AddEntryService.new(entry: entry, time: time).call
|
49
|
-
|
51
|
+
presenter = Presenters::EntryGroup::List::DatePresenter.new(times: [time], options: options)
|
52
|
+
# TODO: Refactor View::EntryGroup::Show to accept a presenter and use it here
|
53
|
+
Views::EntryGroup::List.new(presenter: presenter).render
|
50
54
|
end
|
51
55
|
|
52
56
|
desc I18n.t('commands.browse.desc'), I18n.t('commands.browse.usage')
|
data/lib/dsu/crud/json_file.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'active_model'
|
4
4
|
require 'json'
|
5
|
-
require_relative 'raw_json_file'
|
6
5
|
|
7
6
|
module Dsu
|
8
7
|
module Crud
|
@@ -23,12 +22,12 @@ module Dsu
|
|
23
22
|
self.class.delete!(file_path: file_path)
|
24
23
|
end
|
25
24
|
|
26
|
-
def
|
27
|
-
self.class.
|
25
|
+
def file_exist?
|
26
|
+
self.class.file_exist?(file_path: file_path)
|
28
27
|
end
|
29
28
|
|
30
29
|
def persisted?
|
31
|
-
|
30
|
+
file_exist?
|
32
31
|
end
|
33
32
|
|
34
33
|
# Override this method to reload data from the file
|
@@ -67,16 +66,20 @@ module Dsu
|
|
67
66
|
alias save! write!
|
68
67
|
|
69
68
|
class << self
|
70
|
-
def
|
71
|
-
|
69
|
+
def file_exist?(file_path:)
|
70
|
+
File.exist?(file_path)
|
72
71
|
end
|
73
72
|
|
74
73
|
def delete(file_path:)
|
75
|
-
|
74
|
+
return false unless file_exist?(file_path: file_path)
|
75
|
+
|
76
|
+
File.delete(file_path)
|
77
|
+
|
78
|
+
true
|
76
79
|
end
|
77
80
|
|
78
81
|
def delete!(file_path:)
|
79
|
-
|
82
|
+
raise file_does_not_exist_message(file_path: file_path) unless delete(file_path: file_path)
|
80
83
|
end
|
81
84
|
|
82
85
|
def parse(json)
|
@@ -86,54 +89,65 @@ module Dsu
|
|
86
89
|
end
|
87
90
|
|
88
91
|
def read(file_path:)
|
89
|
-
|
92
|
+
json = File.read(file_path) if file_exist?(file_path: file_path)
|
93
|
+
hash = parse(json)
|
90
94
|
return yield hash if hash && block_given?
|
91
95
|
|
92
96
|
hash
|
93
97
|
end
|
94
98
|
|
95
99
|
def read!(file_path:)
|
96
|
-
|
100
|
+
raise file_does_not_exist_message(file_path: file_path) unless file_exist?(file_path: file_path)
|
101
|
+
|
102
|
+
hash = read(file_path: file_path)
|
97
103
|
return yield hash if hash && block_given?
|
98
104
|
|
99
105
|
hash
|
100
106
|
end
|
101
107
|
|
102
108
|
def write(file_data:, file_path:)
|
103
|
-
|
109
|
+
raise ArgumentError, 'file_data is nil' if file_data.nil?
|
110
|
+
raise ArgumentError, "file_data is the wrong object type:\"#{file_data}\"" unless file_data.is_a?(Hash)
|
111
|
+
|
112
|
+
file_data = JSON.pretty_generate(file_data)
|
113
|
+
File.write(file_path, file_data)
|
104
114
|
end
|
105
115
|
|
106
116
|
def write!(file_data:, file_path:)
|
107
117
|
write(file_data: file_data, file_path: file_path)
|
108
118
|
end
|
119
|
+
|
120
|
+
def file_does_not_exist_message(file_path:)
|
121
|
+
"File \"#{file_path}\" does not exist"
|
122
|
+
end
|
109
123
|
end
|
110
124
|
|
111
125
|
private
|
112
126
|
|
127
|
+
attr_writer :file_path, :version
|
128
|
+
|
113
129
|
def read
|
114
|
-
hash = self.class.
|
130
|
+
hash = self.class.read(file_path: file_path)
|
115
131
|
return yield hash if block_given?
|
116
132
|
|
117
133
|
hash
|
118
134
|
end
|
119
135
|
|
120
136
|
def read!
|
121
|
-
hash = self.class.
|
137
|
+
hash = self.class.read!(file_path: file_path)
|
122
138
|
return yield hash if hash && block_given?
|
123
139
|
|
124
140
|
hash
|
125
141
|
end
|
126
142
|
|
127
143
|
def read_version
|
128
|
-
return 0 unless
|
144
|
+
return 0 unless file_exist?
|
129
145
|
|
130
146
|
hash = read
|
131
147
|
return 0 if hash.nil?
|
132
148
|
|
133
149
|
hash.fetch(:version, 0).to_i
|
134
150
|
end
|
135
|
-
|
136
|
-
attr_writer :file_path, :version
|
137
151
|
end
|
138
152
|
end
|
139
153
|
end
|
data/lib/dsu/env.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Dsu
|
4
4
|
class << self
|
5
|
-
def env
|
5
|
+
def env # rubocop:disable Metrics/MethodLength
|
6
6
|
@env ||= Struct.new(:env) do
|
7
7
|
def test?
|
8
8
|
env.fetch('DSU_ENV', nil) == 'test'
|
@@ -19,6 +19,25 @@ module Dsu
|
|
19
19
|
def production?
|
20
20
|
env.fetch('DSU_ENV', 'production') == 'production'
|
21
21
|
end
|
22
|
+
|
23
|
+
def screen_shot_mode?
|
24
|
+
development? && (env.fetch('SCREEN_SHOT_USERNAME', '').present? ||
|
25
|
+
env.fetch('SCREEN_SHOT_HOSTNAME', '').present?)
|
26
|
+
end
|
27
|
+
|
28
|
+
def screen_shot_prompt
|
29
|
+
username = screen_shot_username
|
30
|
+
hostname = screen_shot_hostname
|
31
|
+
"#{username}@#{hostname}:~ $"
|
32
|
+
end
|
33
|
+
|
34
|
+
def screen_shot_username
|
35
|
+
env.fetch('SCREEN_SHOT_USERNAME', 'username')
|
36
|
+
end
|
37
|
+
|
38
|
+
def screen_shot_hostname
|
39
|
+
env.fetch('SCREEN_SHOT_HOSTNAME', 'hostname')
|
40
|
+
end
|
22
41
|
end.new(ENV)
|
23
42
|
end
|
24
43
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../env'
|
3
4
|
require_relative '../support/color_themable'
|
4
5
|
require_relative 'base_presenter'
|
5
6
|
|
@@ -28,7 +29,11 @@ module Dsu
|
|
28
29
|
private
|
29
30
|
|
30
31
|
def config_path
|
31
|
-
@config_path ||=
|
32
|
+
@config_path ||= if Dsu.env.screen_shot_mode?
|
33
|
+
"/Users/#{Dsu.env.screen_shot_username}/.dsu"
|
34
|
+
else
|
35
|
+
config.file_path
|
36
|
+
end
|
32
37
|
end
|
33
38
|
|
34
39
|
def formatted_config_entry_with_index(config_entry, index:, theme_color:)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../../models/configuration'
|
4
|
+
require_relative '../../../models/entry_group'
|
5
|
+
require_relative '../../../views/entry_group/shared/no_entries_to_display'
|
6
|
+
require_relative '../../base_presenter_ex'
|
7
|
+
require_relative 'messages'
|
8
|
+
require_relative 'nothing_to_list'
|
9
|
+
|
10
|
+
module Dsu
|
11
|
+
module Presenters
|
12
|
+
module EntryGroup
|
13
|
+
module List
|
14
|
+
class DatePresenter < BasePresenterEx
|
15
|
+
include Messages
|
16
|
+
include NothingToList
|
17
|
+
|
18
|
+
def initialize(times:, options: {})
|
19
|
+
raise ArgumentError, 'times must be an Array' unless times.is_a?(Array)
|
20
|
+
raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
|
21
|
+
|
22
|
+
super(options: options)
|
23
|
+
|
24
|
+
@times = times
|
25
|
+
end
|
26
|
+
|
27
|
+
def render
|
28
|
+
return if nothing_to_list?
|
29
|
+
|
30
|
+
entry_groups.each do |entry_group|
|
31
|
+
Views::EntryGroup::Show.new(entry_group: entry_group).render
|
32
|
+
puts
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def display_nothing_to_list_message
|
37
|
+
# This presenter will ALWAYS have something to list (display) since the first
|
38
|
+
# and last (if different) entry groups will always be displayed.
|
39
|
+
raise 'display_nothing_to_list_message called when there are entries to display'
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :times
|
45
|
+
|
46
|
+
def entry_groups
|
47
|
+
@entry_groups ||= begin
|
48
|
+
options = configuration.to_h.merge(self.options).with_indifferent_access
|
49
|
+
|
50
|
+
times.filter_map do |time|
|
51
|
+
view_options = options.dup
|
52
|
+
# Always show the first and last entry groups.
|
53
|
+
view_options[:include_all] = true if times_min_max.include?(time)
|
54
|
+
|
55
|
+
next unless show_entry_group?(time: time, options: view_options)
|
56
|
+
|
57
|
+
Models::EntryGroup.find_or_initialize(time: time)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def times_min_max
|
63
|
+
@times_min_max ||= times.minmax
|
64
|
+
end
|
65
|
+
|
66
|
+
def configuration
|
67
|
+
@configuration ||= Models::Configuration.new
|
68
|
+
end
|
69
|
+
|
70
|
+
def show_entry_group?(time:, options:)
|
71
|
+
Models::EntryGroup.exist?(time: time) || options[:include_all]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../../models/entry_group'
|
4
|
+
require_relative '../../../views/entry_group/shared/no_entries_to_display'
|
5
|
+
require_relative '../../base_presenter_ex'
|
6
|
+
require_relative 'messages'
|
7
|
+
require_relative 'nothing_to_list'
|
8
|
+
|
9
|
+
module Dsu
|
10
|
+
module Presenters
|
11
|
+
module EntryGroup
|
12
|
+
module List
|
13
|
+
class DatesPresenter < BasePresenterEx
|
14
|
+
include Messages
|
15
|
+
include NothingToList
|
16
|
+
|
17
|
+
def initialize(times:, options: {})
|
18
|
+
raise ArgumentError, 'times must be an Array' unless times.is_a?(Array)
|
19
|
+
raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
|
20
|
+
|
21
|
+
super(options: options)
|
22
|
+
|
23
|
+
@times = times
|
24
|
+
end
|
25
|
+
|
26
|
+
def render
|
27
|
+
return if nothing_to_list?
|
28
|
+
|
29
|
+
entry_groups.each do |entry_group|
|
30
|
+
Views::EntryGroup::Show.new(entry_group: entry_group).render
|
31
|
+
puts
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def display_nothing_to_list_message
|
36
|
+
raise 'display_nothing_to_list_message called when there are entries to display' unless nothing_to_list?
|
37
|
+
|
38
|
+
Views::EntryGroup::Shared::NoEntriesToDisplay.new(times: times, options: options).render
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :times
|
44
|
+
|
45
|
+
def entry_groups
|
46
|
+
@entry_groups ||= times.filter_map do |time|
|
47
|
+
next unless show_entry_group?(time: time, options: options)
|
48
|
+
|
49
|
+
Models::EntryGroup.find_or_initialize(time: time)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def show_entry_group?(time:, options:)
|
54
|
+
Models::EntryGroup.exist?(time: time) || options[:include_all]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -65,7 +65,7 @@ module Dsu
|
|
65
65
|
process_entry_group!(entry_group_with_edits)
|
66
66
|
else
|
67
67
|
message_array = I18n.t('services.editor_service.errors.temp_file_error',
|
68
|
-
editor: configuration.editor,
|
68
|
+
editor: ENV.fetch('EDITOR', configuration.editor),
|
69
69
|
status: $CHILD_STATUS).split("\n")
|
70
70
|
puts apply_theme(message_array, theme_color: color_theme.error)
|
71
71
|
end
|
data/lib/dsu/subcommands/list.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../presenters/entry_group/list/date_presenter'
|
4
|
+
require_relative '../presenters/entry_group/list/dates_presenter'
|
3
5
|
require_relative '../services/entry_group/counter_service'
|
4
6
|
require_relative '../support/command_options/dsu_times'
|
5
7
|
require_relative '../support/command_options/time_mnemonic'
|
6
8
|
require_relative '../support/time_formatable'
|
7
|
-
require_relative '../views/entry_group/
|
9
|
+
require_relative '../views/entry_group/list'
|
8
10
|
require_relative '../views/shared/error'
|
9
11
|
require_relative 'base_subcommand'
|
10
12
|
|
@@ -25,7 +27,8 @@ module Dsu
|
|
25
27
|
def today
|
26
28
|
time = Time.now
|
27
29
|
times = sorted_dsu_times_for(times: [time.yesterday, time])
|
28
|
-
|
30
|
+
presenter = Presenters::EntryGroup::List::DatePresenter.new(times: times, options: options)
|
31
|
+
Views::EntryGroup::List.new(presenter: presenter).render
|
29
32
|
end
|
30
33
|
|
31
34
|
desc I18n.t('subcommands.list.tomorrow.desc'), I18n.t('subcommands.list.tomorrow.usage')
|
@@ -33,7 +36,8 @@ module Dsu
|
|
33
36
|
def tomorrow
|
34
37
|
time = Time.now
|
35
38
|
times = sorted_dsu_times_for(times: [time, time.tomorrow])
|
36
|
-
|
39
|
+
presenter = Presenters::EntryGroup::List::DatePresenter.new(times: times, options: options)
|
40
|
+
Views::EntryGroup::List.new(presenter: presenter).render
|
37
41
|
end
|
38
42
|
|
39
43
|
desc I18n.t('subcommands.list.yesterday.desc'), I18n.t('subcommands.list.yesterday.usage')
|
@@ -41,7 +45,8 @@ module Dsu
|
|
41
45
|
def yesterday
|
42
46
|
time = Time.now
|
43
47
|
times = sorted_dsu_times_for(times: [time.yesterday, time.yesterday.yesterday])
|
44
|
-
|
48
|
+
presenter = Presenters::EntryGroup::List::DatePresenter.new(times: times, options: options)
|
49
|
+
Views::EntryGroup::List.new(presenter: presenter).render
|
45
50
|
end
|
46
51
|
|
47
52
|
desc I18n.t('subcommands.list.date.desc'), I18n.t('subcommands.list.date.usage')
|
@@ -55,7 +60,8 @@ module Dsu
|
|
55
60
|
Time.parse(date_or_mnemonic)
|
56
61
|
end
|
57
62
|
times = sorted_dsu_times_for(times: [time, time.yesterday])
|
58
|
-
|
63
|
+
presenter = Presenters::EntryGroup::List::DatePresenter.new(times: times, options: options)
|
64
|
+
Views::EntryGroup::List.new(presenter: presenter).render
|
59
65
|
rescue ArgumentError => e
|
60
66
|
Views::Shared::Error.new(messages: e.message).render
|
61
67
|
end
|
@@ -79,11 +85,8 @@ module Dsu
|
|
79
85
|
# NOTE: special sort here, unlike the other commands where rules for
|
80
86
|
# displaying DSU entries are applied; this is more of a list command.
|
81
87
|
times = times_sort(times: times, entries_display_order: options[:entries_display_order])
|
82
|
-
|
83
|
-
|
84
|
-
Views::EntryGroup::Shared::NoEntriesToDisplay.new(times: times, options: options).render
|
85
|
-
end
|
86
|
-
end
|
88
|
+
presenter = Presenters::EntryGroup::List::DatesPresenter.new(times: times, options: options)
|
89
|
+
Views::EntryGroup::List.new(presenter: presenter).render
|
87
90
|
rescue ArgumentError => e
|
88
91
|
Views::Shared::Error.new(messages: e.message).render
|
89
92
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../env'
|
3
4
|
require_relative '../models/color_theme'
|
4
5
|
require_relative '../services/stderr_redirector_service'
|
5
6
|
require_relative '../views/shared/error'
|
@@ -26,6 +27,10 @@ module Dsu
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def display_dsu_header
|
30
|
+
if Dsu.env.screen_shot_mode?
|
31
|
+
puts apply_theme('Running screen shot mode!', theme_color: color_theme.warning)
|
32
|
+
puts "#{Dsu.env.screen_shot_prompt} dsu #{ARGV.join(' ')}"
|
33
|
+
end
|
29
34
|
end
|
30
35
|
|
31
36
|
def display_dsu_footer
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../models/configuration'
|
4
|
+
require_relative '../presenters/entry_group/list/dates_presenter'
|
4
5
|
require_relative '../services/entry_group/browse_service'
|
5
6
|
require_relative '../services/entry_group/counter_service'
|
7
|
+
require_relative '../views/entry_group/list'
|
6
8
|
|
7
9
|
module Dsu
|
8
10
|
module Support
|
@@ -23,7 +25,8 @@ module Dsu
|
|
23
25
|
header = browse_header_for(time: time, options: options)
|
24
26
|
Views::Shared::Info.new(messages: header).render
|
25
27
|
puts
|
26
|
-
|
28
|
+
presenter = Presenters::EntryGroup::List::DatesPresenter.new(times: times, options: options)
|
29
|
+
Views::EntryGroup::List.new(presenter: presenter).render
|
27
30
|
self.class.display_dsu_footer
|
28
31
|
end
|
29
32
|
output_with_pager output: output, options: options
|
data/lib/dsu/version.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dsu
|
4
|
+
module Views
|
5
|
+
module EntryGroup
|
6
|
+
class List
|
7
|
+
def initialize(presenter:)
|
8
|
+
@presenter = presenter
|
9
|
+
end
|
10
|
+
|
11
|
+
def render
|
12
|
+
return presenter.display_nothing_to_list_message if presenter.nothing_to_list?
|
13
|
+
|
14
|
+
presenter.render
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :presenter
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -36,7 +36,11 @@ module Dsu
|
|
36
36
|
puts presenter.formatted_time
|
37
37
|
|
38
38
|
entry_group.validate!
|
39
|
-
|
39
|
+
if entry_group.entries.empty?
|
40
|
+
puts presenter.no_entries_available
|
41
|
+
|
42
|
+
return
|
43
|
+
end
|
40
44
|
|
41
45
|
entry_group.entries.each_with_index do |entry, index|
|
42
46
|
entry_presenter = entry.presenter
|
data/lib/locales/en/services.yml
CHANGED
@@ -4,7 +4,7 @@ en:
|
|
4
4
|
errors:
|
5
5
|
temp_file_error: |
|
6
6
|
Failed to open temporary file in editor '%{editor}'; the system error returned was: '%{status}'.
|
7
|
-
|
8
|
-
Run
|
7
|
+
Check your EDITOR environment variable and the dsu editor configuration option (run `dsu config info`).
|
8
|
+
Run `dsu help config` for more information.
|
9
9
|
messages:
|
10
10
|
editing: "Editing entry group %{formatted_time}..."
|
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.4.
|
4
|
+
version: 2.4.3
|
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: 2024-01-
|
11
|
+
date: 2024-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -56,20 +56,20 @@ dependencies:
|
|
56
56
|
requirements:
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version:
|
59
|
+
version: '1.1'
|
60
60
|
- - "<"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: '
|
62
|
+
version: '2.0'
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - ">="
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
69
|
+
version: '1.1'
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version: '
|
72
|
+
version: '2.0'
|
73
73
|
- !ruby/object:Gem::Dependency
|
74
74
|
name: os
|
75
75
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,14 +130,13 @@ dependencies:
|
|
130
130
|
- - "<"
|
131
131
|
- !ruby/object:Gem::Version
|
132
132
|
version: '2.0'
|
133
|
-
description:
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
brings a dash of simplicity and fun, fun, fun to your daily agile routine!\n"
|
133
|
+
description: |2
|
134
|
+
dsu is ruby gem that enables anyone practicing Agile methodology to record, keep track of, and manage their daily standup (DSU) activities.
|
135
|
+
|
136
|
+
* dsu uses no network connections whatsoever.
|
137
|
+
* dsu stores all of its data locally, in .json files.
|
138
|
+
* dsu is a simple (but powerful) command-line tool for users who love to work within the terminal.
|
139
|
+
* dsu versioning follows the semantic versioning standard (MAJOR.MINOR.PATCH).
|
141
140
|
email:
|
142
141
|
- public.gma@gmail.com
|
143
142
|
executables:
|
@@ -169,7 +168,6 @@ files:
|
|
169
168
|
- lib/dsu/cli.rb
|
170
169
|
- lib/dsu/command_services/add_entry_service.rb
|
171
170
|
- lib/dsu/crud/json_file.rb
|
172
|
-
- lib/dsu/crud/raw_json_file.rb
|
173
171
|
- lib/dsu/env.rb
|
174
172
|
- lib/dsu/migration/service.rb
|
175
173
|
- lib/dsu/migration/version.rb
|
@@ -183,6 +181,10 @@ files:
|
|
183
181
|
- lib/dsu/presenters/color_theme_presenter.rb
|
184
182
|
- lib/dsu/presenters/color_theme_show_presenter.rb
|
185
183
|
- lib/dsu/presenters/configuration_presenter.rb
|
184
|
+
- lib/dsu/presenters/entry_group/list/date_presenter.rb
|
185
|
+
- lib/dsu/presenters/entry_group/list/dates_presenter.rb
|
186
|
+
- lib/dsu/presenters/entry_group/list/messages.rb
|
187
|
+
- lib/dsu/presenters/entry_group/list/nothing_to_list.rb
|
186
188
|
- lib/dsu/presenters/entry_group_presenter.rb
|
187
189
|
- lib/dsu/presenters/entry_presenter.rb
|
188
190
|
- lib/dsu/presenters/export/all_presenter.rb
|
@@ -229,7 +231,6 @@ files:
|
|
229
231
|
- lib/dsu/support/command_options/time_mnemonics.rb
|
230
232
|
- lib/dsu/support/descriptable.rb
|
231
233
|
- lib/dsu/support/entry_group_browsable.rb
|
232
|
-
- lib/dsu/support/entry_group_viewable.rb
|
233
234
|
- lib/dsu/support/field_errors.rb
|
234
235
|
- lib/dsu/support/fileable.rb
|
235
236
|
- lib/dsu/support/presentable.rb
|
@@ -247,6 +248,7 @@ files:
|
|
247
248
|
- lib/dsu/views/color_theme/show.rb
|
248
249
|
- lib/dsu/views/configuration/show.rb
|
249
250
|
- lib/dsu/views/entry_group/edit.rb
|
251
|
+
- lib/dsu/views/entry_group/list.rb
|
250
252
|
- lib/dsu/views/entry_group/shared/no_entries_to_display.rb
|
251
253
|
- lib/dsu/views/entry_group/shared/no_entries_to_display_for_month_of.rb
|
252
254
|
- lib/dsu/views/entry_group/shared/no_entries_to_display_for_week_of.rb
|
@@ -290,25 +292,28 @@ post_install_message: |
|
|
290
292
|
View the dsu README.md here: https://github.com/gangelo/dsu
|
291
293
|
View the dsu CHANGELOG.md: https://github.com/gangelo/dsu/blob/main/CHANGELOG.md
|
292
294
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
295
|
+
*
|
296
|
+
***
|
297
|
+
*******
|
298
|
+
*********
|
299
|
+
***********************
|
300
|
+
*****************
|
301
|
+
*************
|
302
|
+
******* *******
|
303
|
+
***** *****
|
304
|
+
*** ***
|
305
|
+
** **
|
298
306
|
|
299
|
-
|
307
|
+
Using dsu? dsu is made available free of charge. Please consider giving dsu a STAR on GitHub as well as sharing dsu with your fellow developers on social media.
|
300
308
|
|
301
|
-
|
309
|
+
Knowing that dsu is being used and appreciated is a great motivator to continue developing and improving dsu.
|
302
310
|
|
303
|
-
|
311
|
+
>>> Star it on github: https://github.com/gangelo/dsu
|
312
|
+
>>> Share on social media: https://rubygems.org/gems/dsu
|
304
313
|
|
305
|
-
|
306
|
-
* * . * . *
|
307
|
-
* * * * * *
|
308
|
-
* * * * *
|
309
|
-
* * *
|
314
|
+
Thank you!
|
310
315
|
|
311
|
-
|
316
|
+
<3 Gene
|
312
317
|
rdoc_options: []
|
313
318
|
require_paths:
|
314
319
|
- lib
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'fileutils'
|
4
|
-
|
5
|
-
module Dsu
|
6
|
-
module Crud
|
7
|
-
module RawJsonFile
|
8
|
-
class << self
|
9
|
-
def delete(file_path:)
|
10
|
-
return false unless exist?(file_path: file_path)
|
11
|
-
|
12
|
-
File.delete(file_path)
|
13
|
-
|
14
|
-
true
|
15
|
-
end
|
16
|
-
|
17
|
-
def delete!(file_path:)
|
18
|
-
raise file_does_not_exist_message(file_path: file_path) unless delete(file_path: file_path)
|
19
|
-
end
|
20
|
-
|
21
|
-
def exist?(file_path:)
|
22
|
-
File.exist?(file_path)
|
23
|
-
end
|
24
|
-
|
25
|
-
def read(file_path:)
|
26
|
-
File.read(file_path) if exist?(file_path: file_path)
|
27
|
-
end
|
28
|
-
|
29
|
-
def read!(file_path:)
|
30
|
-
raise file_does_not_exist_message(file_path: file_path) unless exist?(file_path: file_path)
|
31
|
-
|
32
|
-
read(file_path: file_path)
|
33
|
-
end
|
34
|
-
|
35
|
-
def write(file_data:, file_path:)
|
36
|
-
raise ArgumentError, 'file_data is nil' if file_data.nil?
|
37
|
-
raise ArgumentError, "file_data is the wrong object type:\"#{file_data}\"" unless file_data.is_a?(Hash)
|
38
|
-
|
39
|
-
file_data = JSON.pretty_generate(file_data)
|
40
|
-
File.write(file_path, file_data)
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def file_does_not_exist_message(file_path:)
|
46
|
-
"File \"#{file_path}\" does not exist"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Dsu
|
4
|
-
module Support
|
5
|
-
module EntryGroupViewable
|
6
|
-
def view_entry_groups(times:, options: {})
|
7
|
-
raise ArgumentError, 'times must be an Array' unless times.is_a?(Array)
|
8
|
-
raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
|
9
|
-
|
10
|
-
total_viewable_entry_groups = 0
|
11
|
-
|
12
|
-
times.each do |time|
|
13
|
-
view_entry_group(time: time, options: options) do
|
14
|
-
total_viewable_entry_groups += 1
|
15
|
-
puts
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
total_unviewable_entry_groups = times.size - total_viewable_entry_groups
|
20
|
-
yield total_viewable_entry_groups, total_unviewable_entry_groups if block_given?
|
21
|
-
end
|
22
|
-
|
23
|
-
def view_entry_group(time:, options: {})
|
24
|
-
raise ArgumentError, 'time must be a Time object' unless time.is_a?(Time)
|
25
|
-
raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
|
26
|
-
|
27
|
-
return unless show_entry_group?(time: time, options: options)
|
28
|
-
|
29
|
-
entry_group = Models::EntryGroup.find_or_initialize(time: time)
|
30
|
-
Views::EntryGroup::Show.new(entry_group: entry_group).render
|
31
|
-
|
32
|
-
yield if block_given?
|
33
|
-
end
|
34
|
-
|
35
|
-
# This method will unconditionally display the FIRST and LAST entry groups
|
36
|
-
# associated with the times provided by the <times> argument. All other
|
37
|
-
# entry groups will be conditionally displayed based on the :include_all
|
38
|
-
# value in the <options> argument.
|
39
|
-
def view_list_for(times:, options:)
|
40
|
-
configuration = Models::Configuration.new unless defined?(configuration) && configuration
|
41
|
-
options = configuration.to_h.merge(options).with_indifferent_access
|
42
|
-
times_first_and_last = [times.first, times.last]
|
43
|
-
times.each do |time|
|
44
|
-
view_options = options.dup
|
45
|
-
view_options[:include_all] = true if times_first_and_last.include?(time)
|
46
|
-
view_entry_group(time: time, options: view_options) do
|
47
|
-
puts
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def show_entry_group?(time:, options:)
|
55
|
-
Models::EntryGroup.exist?(time: time) || options[:include_all]
|
56
|
-
end
|
57
|
-
|
58
|
-
module_function :view_entry_group, :view_entry_groups, :view_list_for, :show_entry_group?
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|