dsu 2.4.3 → 3.0.0.alpha.0
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/.rubocop.yml +12 -0
- data/CHANGELOG.md +181 -204
- data/Gemfile.lock +13 -13
- data/README.md +7 -8
- data/Rakefile +6 -0
- data/current_project.bak +4 -0
- data/lib/dsu/cli.rb +24 -6
- data/lib/dsu/crud/json_file.rb +3 -0
- data/lib/dsu/migration/version.rb +1 -1
- data/lib/dsu/models/color_theme.rb +7 -58
- data/lib/dsu/models/configuration.rb +18 -3
- data/lib/dsu/models/entry_group.rb +0 -7
- data/lib/dsu/models/migration_version.rb +0 -1
- data/lib/dsu/models/project.rb +295 -0
- data/lib/dsu/presenters/base_presenter_ex.rb +1 -12
- data/lib/dsu/presenters/export/all_presenter.rb +14 -19
- data/lib/dsu/presenters/export/dates_presenter.rb +17 -20
- data/lib/dsu/presenters/import/all_presenter.rb +20 -25
- data/lib/dsu/presenters/import/dates_presenter.rb +25 -27
- data/lib/dsu/presenters/import/import_entry.rb +22 -0
- data/lib/dsu/presenters/import/import_file.rb +9 -1
- data/lib/dsu/presenters/project/create_presenter.rb +44 -0
- data/lib/dsu/presenters/project/delete_by_number_presenter.rb +54 -0
- data/lib/dsu/presenters/project/delete_presenter.rb +53 -0
- data/lib/dsu/presenters/project/list_presenter.rb +24 -0
- data/lib/dsu/presenters/project/rename_by_number_presenter.rb +63 -0
- data/lib/dsu/presenters/project/rename_presenter.rb +57 -0
- data/lib/dsu/presenters/project/use_by_number_presenter.rb +53 -0
- data/lib/dsu/presenters/project/use_presenter.rb +52 -0
- data/lib/dsu/services/entry_group/exporter_service.rb +22 -5
- data/lib/dsu/services/entry_group/importer_service.rb +41 -8
- data/lib/dsu/services/project/hydrator_service.rb +40 -0
- data/lib/dsu/services/project/rename_service.rb +70 -0
- data/lib/dsu/subcommands/export.rb +4 -2
- data/lib/dsu/subcommands/import.rb +7 -3
- data/lib/dsu/subcommands/project.rb +149 -0
- data/lib/dsu/support/ask.rb +10 -3
- data/lib/dsu/support/color_themable.rb +1 -1
- data/lib/dsu/support/command_hookable.rb +7 -2
- data/lib/dsu/support/descriptable.rb +5 -21
- data/lib/dsu/support/fileable.rb +39 -1
- data/lib/dsu/support/project_file_system.rb +121 -0
- data/lib/dsu/support/short_string.rb +24 -0
- data/lib/dsu/support/time_comparable.rb +2 -0
- data/lib/dsu/support/transform_project_name.rb +24 -0
- data/lib/dsu/validators/project_name_validator.rb +58 -0
- data/lib/dsu/version.rb +1 -1
- data/lib/dsu/views/base_list_view.rb +41 -0
- data/lib/dsu/views/export.rb +60 -6
- data/lib/dsu/views/import.rb +83 -7
- data/lib/dsu/views/import_dates.rb +17 -0
- data/lib/dsu/views/project/create.rb +87 -0
- data/lib/dsu/views/project/delete.rb +96 -0
- data/lib/dsu/views/project/delete_by_number.rb +19 -0
- data/lib/dsu/views/project/list.rb +115 -0
- data/lib/dsu/views/project/rename.rb +98 -0
- data/lib/dsu/views/project/rename_by_number.rb +21 -0
- data/lib/dsu/views/project/use.rb +97 -0
- data/lib/dsu/views/project/use_by_number.rb +19 -0
- data/lib/dsu.rb +2 -10
- data/lib/locales/en/active_record.yml +9 -0
- data/lib/locales/en/commands.yml +9 -3
- data/lib/locales/en/miscellaneous.yml +4 -0
- data/lib/locales/en/services.yml +4 -0
- data/lib/locales/en/subcommands.yml +247 -15
- data/project.bak +0 -0
- metadata +34 -9
- data/lib/dsu/presenters/export/messages.rb +0 -32
- data/lib/dsu/presenters/export/nothing_to_export.rb +0 -13
- data/lib/dsu/presenters/export/service_callable.rb +0 -20
- data/lib/dsu/presenters/import/messages.rb +0 -42
- data/lib/dsu/presenters/import/service_callable.rb +0 -21
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../support/ask'
|
4
|
+
require_relative '../../support/color_themable'
|
5
|
+
require_relative '../../models/color_theme'
|
6
|
+
|
7
|
+
module Dsu
|
8
|
+
module Views
|
9
|
+
module Project
|
10
|
+
class Create
|
11
|
+
include Support::Ask
|
12
|
+
include Support::ColorThemable
|
13
|
+
|
14
|
+
def initialize(presenter:, options: {})
|
15
|
+
@presenter = presenter
|
16
|
+
@options = options&.dup || {}
|
17
|
+
@color_theme = Models::ColorTheme.find(theme_name: theme_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def render
|
21
|
+
return display_project_errors if presenter.project_errors?
|
22
|
+
return display_project_already_exists if presenter.project_already_exists?
|
23
|
+
|
24
|
+
response = display_project_create_prompt
|
25
|
+
if presenter.respond response: response
|
26
|
+
display_project_created_message
|
27
|
+
else
|
28
|
+
display_project_cancelled_message
|
29
|
+
end
|
30
|
+
rescue StandardError => e
|
31
|
+
puts apply_theme(e.message, theme_color: color_theme.error)
|
32
|
+
puts apply_theme(e.backtrace_locations.join("\n"), theme_color: color_theme.error) if Dsu.env.local?
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :presenter, :color_theme, :options
|
38
|
+
|
39
|
+
def project_name
|
40
|
+
presenter.project_name
|
41
|
+
end
|
42
|
+
|
43
|
+
def display_project_cancelled_message
|
44
|
+
message = I18n.t('subcommands.project.messages.cancelled', project_name: project_name)
|
45
|
+
puts apply_theme(message, theme_color: color_theme.info)
|
46
|
+
end
|
47
|
+
|
48
|
+
def display_project_create_prompt
|
49
|
+
response = ask_while(prompt_with_options(prompt: create_prompt,
|
50
|
+
options: create_prompt_options), options: options) do |input|
|
51
|
+
message = I18n.t('information.input.try_again', options: create_prompt_options.join(','))
|
52
|
+
puts apply_theme(message, theme_color: color_theme.info) unless create_prompt_options.include?(input)
|
53
|
+
create_prompt_options.include?(input)
|
54
|
+
end
|
55
|
+
response == create_prompt_options.first
|
56
|
+
end
|
57
|
+
|
58
|
+
def display_project_created_message
|
59
|
+
message = I18n.t('subcommands.project.create.messages.created', project_name: project_name)
|
60
|
+
puts apply_theme(message, theme_color: color_theme.success)
|
61
|
+
end
|
62
|
+
|
63
|
+
def display_project_errors
|
64
|
+
errors = presenter.project_errors.join("\n")
|
65
|
+
puts apply_theme(errors, theme_color: color_theme.error)
|
66
|
+
end
|
67
|
+
|
68
|
+
def display_project_already_exists
|
69
|
+
message = I18n.t('subcommands.project.messages.already_exists', project_name: project_name)
|
70
|
+
puts apply_theme(message, theme_color: color_theme.error)
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_prompt
|
74
|
+
I18n.t('subcommands.project.create.prompts.create_confirm', project_name: project_name)
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_prompt_options
|
78
|
+
I18n.t('subcommands.project.create.prompts.create_options')
|
79
|
+
end
|
80
|
+
|
81
|
+
def theme_name
|
82
|
+
@theme_name ||= options.fetch(:theme_name, Models::Configuration.new.theme_name)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../env'
|
4
|
+
require_relative '../../models/color_theme'
|
5
|
+
require_relative '../../support/ask'
|
6
|
+
require_relative '../../support/color_themable'
|
7
|
+
|
8
|
+
module Dsu
|
9
|
+
module Views
|
10
|
+
module Project
|
11
|
+
class Delete
|
12
|
+
include Support::Ask
|
13
|
+
include Support::ColorThemable
|
14
|
+
|
15
|
+
attr_reader :presenter
|
16
|
+
|
17
|
+
def initialize(presenter:, options: {})
|
18
|
+
@presenter = presenter
|
19
|
+
@options = options&.dup || {}
|
20
|
+
@color_theme = Models::ColorTheme.find(theme_name: theme_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def render
|
24
|
+
return display_project_does_not_exists if presenter.project_does_not_exist?
|
25
|
+
return display_project_errors if presenter.project_errors.any?
|
26
|
+
return display_project_is_default if presenter.project_default?
|
27
|
+
|
28
|
+
response = display_project_delete_prompt
|
29
|
+
if presenter.respond response: response
|
30
|
+
display_deleted_project_message
|
31
|
+
else
|
32
|
+
display_delete_project_cancelled_message
|
33
|
+
end
|
34
|
+
rescue StandardError => e
|
35
|
+
puts apply_theme(e.message, theme_color: color_theme.error)
|
36
|
+
puts apply_theme(e.backtrace_locations.join("\n"), theme_color: color_theme.error) if Dsu.env.local?
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :color_theme, :options
|
42
|
+
|
43
|
+
def display_project_delete_prompt
|
44
|
+
response = ask_while(prompt_with_options(prompt: delete_prompt,
|
45
|
+
options: delete_prompt_options), options: options) do |input|
|
46
|
+
message = I18n.t('information.input.try_again', options: delete_prompt_options.join(','))
|
47
|
+
puts apply_theme(message, theme_color: color_theme.info) unless delete_prompt_options.include?(input)
|
48
|
+
delete_prompt_options.include?(input)
|
49
|
+
end
|
50
|
+
response == delete_prompt_options.first
|
51
|
+
end
|
52
|
+
|
53
|
+
def display_delete_project_cancelled_message
|
54
|
+
message = I18n.t('subcommands.project.messages.cancelled', project_name: presenter.project_name)
|
55
|
+
puts apply_theme(message, theme_color: color_theme.info)
|
56
|
+
end
|
57
|
+
|
58
|
+
def display_project_errors
|
59
|
+
errors = presenter.project_errors.join("\n")
|
60
|
+
puts apply_theme(errors, theme_color: color_theme.error)
|
61
|
+
end
|
62
|
+
|
63
|
+
def display_project_does_not_exists
|
64
|
+
message = I18n.t('subcommands.project.messages.does_not_exist',
|
65
|
+
project_name: presenter.project_name)
|
66
|
+
puts apply_theme(message, theme_color: color_theme.error)
|
67
|
+
end
|
68
|
+
|
69
|
+
def display_project_is_default
|
70
|
+
message = I18n.t('models.project.errors.delete_default_project',
|
71
|
+
project_name: presenter.project_name)
|
72
|
+
puts apply_theme(message, theme_color: color_theme.error)
|
73
|
+
end
|
74
|
+
|
75
|
+
def display_deleted_project_message
|
76
|
+
message = I18n.t('subcommands.project.delete.messages.deleted',
|
77
|
+
project_name: presenter.project_name)
|
78
|
+
puts apply_theme(message, theme_color: color_theme.success)
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete_prompt
|
82
|
+
I18n.t('subcommands.project.delete.prompts.delete_confirm',
|
83
|
+
project_name: presenter.project_name, description: presenter.project_description)
|
84
|
+
end
|
85
|
+
|
86
|
+
def delete_prompt_options
|
87
|
+
I18n.t('subcommands.project.delete.prompts.delete_options')
|
88
|
+
end
|
89
|
+
|
90
|
+
def theme_name
|
91
|
+
@theme_name ||= options.fetch(:theme_name, Models::Configuration.new.theme_name)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'delete'
|
4
|
+
|
5
|
+
module Dsu
|
6
|
+
module Views
|
7
|
+
module Project
|
8
|
+
class DeleteByNumber < Delete
|
9
|
+
private
|
10
|
+
|
11
|
+
def display_project_does_not_exists
|
12
|
+
message = I18n.t('subcommands.project.messages.number_does_not_exist',
|
13
|
+
project_number: presenter.project_number)
|
14
|
+
puts apply_theme(message, theme_color: color_theme.error)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../base_list_view'
|
4
|
+
|
5
|
+
module Dsu
|
6
|
+
module Views
|
7
|
+
module Project
|
8
|
+
# TODO: I18n.
|
9
|
+
class List < Views::BaseListView
|
10
|
+
NO_JUSTIFICATION = 4
|
11
|
+
PROJECT_JUSTIFICATION = 15
|
12
|
+
DEFAULT_JUSTIFICATION = 10
|
13
|
+
CURRENT_JUSTIFICATION = 10
|
14
|
+
DESCRIPTION_JUSTIFICATION = 10
|
15
|
+
|
16
|
+
DETAIL_HEADER_STRING = "#{'No.'.ljust(NO_JUSTIFICATION)} " \
|
17
|
+
"#{'Project'.ljust(PROJECT_JUSTIFICATION)} " \
|
18
|
+
"#{'Default'.center(DEFAULT_JUSTIFICATION)} " \
|
19
|
+
"#{'Current'.center(CURRENT_JUSTIFICATION)} " \
|
20
|
+
"#{'Description'.ljust(DESCRIPTION_JUSTIFICATION)}".freeze
|
21
|
+
|
22
|
+
def render
|
23
|
+
super do
|
24
|
+
return display_no_projects if presenter.projects.none?
|
25
|
+
|
26
|
+
display_project_list
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def display_project_list
|
33
|
+
display_header
|
34
|
+
display_detail_header
|
35
|
+
display_detail
|
36
|
+
display_footer
|
37
|
+
end
|
38
|
+
|
39
|
+
def display_no_projects
|
40
|
+
# Should never happen
|
41
|
+
message = I18n.t('subcommands.project.messages.no_projects')
|
42
|
+
puts apply_theme(message, theme_color: color_theme.info)
|
43
|
+
end
|
44
|
+
|
45
|
+
def display_detail
|
46
|
+
presenter.projects.each_with_index do |project, index|
|
47
|
+
display_detail_data(
|
48
|
+
formatted_index(index: index),
|
49
|
+
project.project_name,
|
50
|
+
project.default_project?,
|
51
|
+
project.current_project?,
|
52
|
+
project.description
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def display_detail_header
|
58
|
+
puts apply_theme(DETAIL_HEADER_STRING, theme_color: color_theme.index)
|
59
|
+
end
|
60
|
+
|
61
|
+
def display_detail_data(index, project_name, default_project, current_project, project_desc)
|
62
|
+
puts "#{index_detail_data(index)} " \
|
63
|
+
"#{project_name_detail_data(project_name)} " \
|
64
|
+
"#{project_default_detail_data(default_project)} " \
|
65
|
+
"#{project_current_detail_data(current_project)} " \
|
66
|
+
"#{project_desc_detail_data(project_desc)}"
|
67
|
+
end
|
68
|
+
|
69
|
+
# def display_detail_data(index, project_name, default_project, current_project, project_desc)
|
70
|
+
# puts "#{index_detail_data(index)}|" \
|
71
|
+
# "#{project_name_detail_data(project_name)}|" \
|
72
|
+
# "#{project_default_detail_data(default_project)}|" \
|
73
|
+
# "#{project_current_detail_data(current_project)}|" \
|
74
|
+
# "#{project_desc_detail_data(project_desc)}"
|
75
|
+
# end
|
76
|
+
|
77
|
+
def display_footer
|
78
|
+
footer = "\nTotal projects: #{presenter.projects.count}"
|
79
|
+
puts apply_theme(footer, theme_color: color_theme.footer)
|
80
|
+
end
|
81
|
+
|
82
|
+
def display_header
|
83
|
+
header = "Project list\n"
|
84
|
+
puts apply_theme(header, theme_color: color_theme.subheader)
|
85
|
+
end
|
86
|
+
|
87
|
+
def index_detail_data(value)
|
88
|
+
apply_theme(value.to_s.ljust(NO_JUSTIFICATION), theme_color: color_theme.index)
|
89
|
+
end
|
90
|
+
|
91
|
+
def project_name_detail_data(value)
|
92
|
+
apply_theme(value.to_s.ljust(PROJECT_JUSTIFICATION), theme_color: color_theme.body.bold!)
|
93
|
+
end
|
94
|
+
|
95
|
+
def project_default_detail_data(value)
|
96
|
+
value = value ? '*' : ' '
|
97
|
+
apply_theme(value.to_s.center(DEFAULT_JUSTIFICATION), theme_color: color_theme.body.bold!)
|
98
|
+
end
|
99
|
+
|
100
|
+
def project_current_detail_data(value)
|
101
|
+
value = value ? '*' : ' '
|
102
|
+
apply_theme(value.to_s.center(CURRENT_JUSTIFICATION), theme_color: color_theme.body.bold!)
|
103
|
+
end
|
104
|
+
|
105
|
+
def project_desc_detail_data(value)
|
106
|
+
apply_theme(value.to_s.ljust(DESCRIPTION_JUSTIFICATION), theme_color: color_theme.body)
|
107
|
+
end
|
108
|
+
|
109
|
+
def theme_name
|
110
|
+
@theme_name ||= options.fetch(:theme_name, Models::Configuration.new.theme_name)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../env'
|
4
|
+
require_relative '../../models/color_theme'
|
5
|
+
require_relative '../../support/ask'
|
6
|
+
require_relative '../../support/color_themable'
|
7
|
+
|
8
|
+
module Dsu
|
9
|
+
module Views
|
10
|
+
module Project
|
11
|
+
class Rename
|
12
|
+
include Support::Ask
|
13
|
+
include Support::ColorThemable
|
14
|
+
|
15
|
+
attr_reader :presenter
|
16
|
+
|
17
|
+
def initialize(presenter:, options: {})
|
18
|
+
@presenter = presenter
|
19
|
+
@options = options&.dup || {}
|
20
|
+
@color_theme = Models::ColorTheme.find(theme_name: theme_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def render
|
24
|
+
return display_project_does_not_exist if presenter.project_does_not_exist?
|
25
|
+
return display_new_project_already_exists if presenter.new_project_already_exists?
|
26
|
+
return display_new_project_errors if presenter.new_project_errors.any?
|
27
|
+
|
28
|
+
response = display_project_rename_prompt
|
29
|
+
if presenter.respond response: response
|
30
|
+
display_renamed_project_message
|
31
|
+
else
|
32
|
+
display_rename_project_cancelled_message
|
33
|
+
end
|
34
|
+
rescue StandardError => e
|
35
|
+
puts apply_theme(e.message, theme_color: color_theme.error)
|
36
|
+
puts apply_theme(e.backtrace_locations.join("\n"), theme_color: color_theme.error) if Dsu.env.local?
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :color_theme, :options
|
42
|
+
|
43
|
+
def display_project_rename_prompt
|
44
|
+
response = ask_while(prompt_with_options(prompt: rename_prompt,
|
45
|
+
options: rename_prompt_options), options: options) do |input|
|
46
|
+
message = I18n.t('information.input.try_again', options: rename_prompt_options.join(','))
|
47
|
+
puts apply_theme(message, theme_color: color_theme.info) unless rename_prompt_options.include?(input)
|
48
|
+
rename_prompt_options.include?(input)
|
49
|
+
end
|
50
|
+
response == rename_prompt_options.first
|
51
|
+
end
|
52
|
+
|
53
|
+
def display_rename_project_cancelled_message
|
54
|
+
message = I18n.t('subcommands.project.messages.cancelled')
|
55
|
+
puts apply_theme(message, theme_color: color_theme.info)
|
56
|
+
end
|
57
|
+
|
58
|
+
def display_new_project_errors
|
59
|
+
errors = presenter.new_project_errors.join("\n")
|
60
|
+
puts apply_theme(errors, theme_color: color_theme.error)
|
61
|
+
end
|
62
|
+
|
63
|
+
def display_project_does_not_exist
|
64
|
+
message = I18n.t('subcommands.project.messages.does_not_exist',
|
65
|
+
project_name: presenter.project_name)
|
66
|
+
puts apply_theme(message, theme_color: color_theme.error)
|
67
|
+
end
|
68
|
+
|
69
|
+
def display_new_project_already_exists
|
70
|
+
message = I18n.t('subcommands.project.rename.messages.new_project_already_exists',
|
71
|
+
new_project_name: presenter.new_project_name)
|
72
|
+
puts apply_theme(message, theme_color: color_theme.error)
|
73
|
+
end
|
74
|
+
|
75
|
+
def display_renamed_project_message
|
76
|
+
message = I18n.t('subcommands.project.rename.messages.renamed_project',
|
77
|
+
project_name: presenter.project_name, new_project_name: presenter.new_project_name)
|
78
|
+
puts apply_theme(message, theme_color: color_theme.success)
|
79
|
+
end
|
80
|
+
|
81
|
+
def rename_prompt
|
82
|
+
I18n.t('subcommands.project.rename.prompts.rename_confirm',
|
83
|
+
project_name: presenter.project_name,
|
84
|
+
new_project_name: presenter.new_project_name,
|
85
|
+
new_project_description: presenter.new_project_description)
|
86
|
+
end
|
87
|
+
|
88
|
+
def rename_prompt_options
|
89
|
+
I18n.t('subcommands.project.rename.prompts.rename_options')
|
90
|
+
end
|
91
|
+
|
92
|
+
def theme_name
|
93
|
+
@theme_name ||= options.fetch(:theme_name, Models::Configuration.new.theme_name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../env'
|
4
|
+
require_relative '../../models/color_theme'
|
5
|
+
require_relative '../../support/ask'
|
6
|
+
require_relative '../../support/color_themable'
|
7
|
+
require_relative 'rename'
|
8
|
+
|
9
|
+
module Dsu
|
10
|
+
module Views
|
11
|
+
module Project
|
12
|
+
class RenameByNumber < Rename
|
13
|
+
def display_project_does_not_exist
|
14
|
+
message = I18n.t('subcommands.project.messages.number_does_not_exist',
|
15
|
+
project_number: presenter.project_number)
|
16
|
+
puts apply_theme(message, theme_color: color_theme.error)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../env'
|
4
|
+
require_relative '../../models/color_theme'
|
5
|
+
require_relative '../../support/ask'
|
6
|
+
require_relative '../../support/color_themable'
|
7
|
+
|
8
|
+
module Dsu
|
9
|
+
module Views
|
10
|
+
module Project
|
11
|
+
class Use
|
12
|
+
include Support::Ask
|
13
|
+
include Support::ColorThemable
|
14
|
+
|
15
|
+
attr_reader :presenter
|
16
|
+
|
17
|
+
def initialize(presenter:, options: {})
|
18
|
+
@presenter = presenter
|
19
|
+
@options = options&.dup || {}
|
20
|
+
@color_theme = Models::ColorTheme.find(theme_name: theme_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def render
|
24
|
+
return display_project_does_not_exist if presenter.project_does_not_exist?
|
25
|
+
return display_project_already_current_project if presenter.already_current_project?
|
26
|
+
return display_project_errors if presenter.project_errors.any?
|
27
|
+
|
28
|
+
response = display_project_use_prompt
|
29
|
+
if presenter.respond response: response
|
30
|
+
display_using_project_message
|
31
|
+
else
|
32
|
+
display_use_project_cancelled_message
|
33
|
+
end
|
34
|
+
rescue StandardError => e
|
35
|
+
puts apply_theme(e.message, theme_color: color_theme.error)
|
36
|
+
puts apply_theme(e.backtrace_locations.join("\n"), theme_color: color_theme.error) if Dsu.env.local?
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :color_theme, :options
|
42
|
+
|
43
|
+
def display_project_use_prompt
|
44
|
+
response = ask_while(prompt_with_options(prompt: use_prompt,
|
45
|
+
options: use_prompt_options), options: options) do |input|
|
46
|
+
message = I18n.t('information.input.try_again', options: use_prompt_options.join(','))
|
47
|
+
puts apply_theme(message, theme_color: color_theme.info) unless use_prompt_options.include?(input)
|
48
|
+
use_prompt_options.include?(input)
|
49
|
+
end
|
50
|
+
response == use_prompt_options.first
|
51
|
+
end
|
52
|
+
|
53
|
+
def display_use_project_cancelled_message
|
54
|
+
message = I18n.t('subcommands.project.messages.cancelled')
|
55
|
+
puts apply_theme(message, theme_color: color_theme.info)
|
56
|
+
end
|
57
|
+
|
58
|
+
def display_project_errors
|
59
|
+
errors = presenter.project_errors.join("\n")
|
60
|
+
puts apply_theme(errors, theme_color: color_theme.error)
|
61
|
+
end
|
62
|
+
|
63
|
+
def display_project_does_not_exist
|
64
|
+
message = I18n.t('subcommands.project.messages.does_not_exist',
|
65
|
+
project_name: presenter.project_name)
|
66
|
+
puts apply_theme(message, theme_color: color_theme.error)
|
67
|
+
end
|
68
|
+
|
69
|
+
def display_using_project_message
|
70
|
+
message = I18n.t('subcommands.project.use.messages.using_project',
|
71
|
+
project_name: presenter.project_name)
|
72
|
+
puts apply_theme(message, theme_color: color_theme.success)
|
73
|
+
end
|
74
|
+
|
75
|
+
def display_project_already_current_project
|
76
|
+
message = I18n.t('subcommands.project.messages.already_current_project',
|
77
|
+
project_name: presenter.project_name)
|
78
|
+
puts apply_theme(message, theme_color: color_theme.success)
|
79
|
+
end
|
80
|
+
|
81
|
+
def use_prompt
|
82
|
+
I18n.t('subcommands.project.use.prompts.use_confirm',
|
83
|
+
project_name: presenter.project_name,
|
84
|
+
description: presenter.project_description)
|
85
|
+
end
|
86
|
+
|
87
|
+
def use_prompt_options
|
88
|
+
I18n.t('subcommands.project.use.prompts.use_options')
|
89
|
+
end
|
90
|
+
|
91
|
+
def theme_name
|
92
|
+
@theme_name ||= options.fetch(:theme_name, Models::Configuration.new.theme_name)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'use'
|
4
|
+
|
5
|
+
module Dsu
|
6
|
+
module Views
|
7
|
+
module Project
|
8
|
+
class UseByNumber < Use
|
9
|
+
private
|
10
|
+
|
11
|
+
def display_project_does_not_exist
|
12
|
+
message = I18n.t('subcommands.project.messages.number_does_not_exist',
|
13
|
+
project_number: presenter.project_number)
|
14
|
+
puts apply_theme(message, theme_color: color_theme.error)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/dsu.rb
CHANGED
@@ -33,14 +33,6 @@ unless Dsu.env.test? || Dsu.env.development?
|
|
33
33
|
exit 1
|
34
34
|
end
|
35
35
|
end
|
36
|
-
|
37
|
-
|
38
|
-
%w[light.json christmas.json].each do |theme_file|
|
39
|
-
destination_theme_file_path = File.join(Dsu::Support::Fileable.themes_folder, theme_file)
|
40
|
-
next if File.exist?(destination_theme_file_path)
|
41
|
-
|
42
|
-
source_theme_file_path = File.join(Dsu::Support::Fileable.seed_data_folder, 'themes', theme_file)
|
43
|
-
FileUtils.cp(source_theme_file_path, destination_theme_file_path)
|
44
|
-
puts I18n.t('migrations.information.theme_copied', from: source_theme_file_path, to: destination_theme_file_path)
|
45
|
-
end
|
36
|
+
|
37
|
+
Migration::Factory.migrate_if!
|
46
38
|
end
|
@@ -1,4 +1,13 @@
|
|
1
1
|
en:
|
2
|
+
models:
|
3
|
+
project:
|
4
|
+
errors:
|
5
|
+
already_exists: "Project '%{project_name}' already exists."
|
6
|
+
delete_default_project: "Project '%{project_name}' is the default project. Change to a different default project before deleting this project."
|
7
|
+
delete_only_project: "Project '%{project_name}' is the only project and cannot be deleted."
|
8
|
+
does_not_exist: "Project '%{project_name}' does not exist."
|
9
|
+
new_project_already_exists: "Project cannot be renamed to '%{project_name}' because the project already exists."
|
10
|
+
project_file_not_exist: "Project file '%{project_file}' does not exist."
|
2
11
|
activerecord:
|
3
12
|
errors:
|
4
13
|
models:
|
data/lib/locales/en/commands.yml
CHANGED
@@ -30,16 +30,18 @@ en:
|
|
30
30
|
long_desc: |
|
31
31
|
Will add a DSU entry having DESCRIPTION to the date associated with the given OPTION.
|
32
32
|
|
33
|
-
$ dsu add [-d DATE|-n|-t|-y] DESCRIPTION
|
33
|
+
$ dsu add [-d DATE|MNEMONIC|-n|-t|-y] DESCRIPTION
|
34
34
|
|
35
|
-
$ dsu a [-d DATE|-n|-t|-y] DESCRIPTION
|
35
|
+
$ dsu a [-d DATE|MNEMONIC|-n|-t|-y] DESCRIPTION
|
36
36
|
|
37
37
|
OPTIONS:
|
38
38
|
|
39
|
-
-d DATE: Adds a DSU entry having DESCRIPTION to the DATE.
|
39
|
+
-d DATE|MNEMONIC: Adds a DSU entry having DESCRIPTION to the DATE or date referenced by the MNEMONIC.
|
40
40
|
|
41
41
|
%{date_option_description}
|
42
42
|
|
43
|
+
%{mnemonic_option_description}
|
44
|
+
|
43
45
|
-n: Adds a DSU entry having DESCRIPTION to today's date (`Time.now`).
|
44
46
|
|
45
47
|
-t: Adds a DSU entry having DESCRIPTION to tomorrow's date (`Time.new.tomorrow`).
|
@@ -98,6 +100,10 @@ en:
|
|
98
100
|
key_mappings: l
|
99
101
|
desc: list|l SUBCOMMAND
|
100
102
|
usage: Displays DSU entries for the given SUBCOMMAND
|
103
|
+
project:
|
104
|
+
key_mappings: p
|
105
|
+
desc: project|p SUBCOMMAND
|
106
|
+
usage: Manage DSU projects for the given SUBCOMMAND
|
101
107
|
theme:
|
102
108
|
key_mappings: t
|
103
109
|
desc: theme|t SUBCOMMAND
|
@@ -2,10 +2,12 @@ en:
|
|
2
2
|
configuration:
|
3
3
|
errors:
|
4
4
|
theme_file_missing: Theme file "%{theme_path}" does not exist.
|
5
|
+
project_path_missing: Default project "%{project_folder}" does not exist.
|
5
6
|
errors:
|
6
7
|
error: "Error: %{message}"
|
7
8
|
from_option_invalid: Option -f, [--from=DATE|MNEMONIC] value is invalid ["%{from_option}"]
|
8
9
|
to_option_invalid: Option -t, [--to=DATE|MNEMONIC] value is invalid ["%{to_option}"]
|
10
|
+
project_name_invalid: Project name "%{project_name}" is invalid.
|
9
11
|
headers:
|
10
12
|
entry:
|
11
13
|
could_not_be_added: "An error was encountered; the entry could not be added:"
|
@@ -19,6 +21,8 @@ en:
|
|
19
21
|
information:
|
20
22
|
dates:
|
21
23
|
through: "%{from} thru %{to}"
|
24
|
+
input:
|
25
|
+
try_again: "Please try again. Valid options are [%{options}]."
|
22
26
|
migrations:
|
23
27
|
error:
|
24
28
|
failed: "Error running migrations: %{message}"
|
data/lib/locales/en/services.yml
CHANGED
@@ -8,3 +8,7 @@ en:
|
|
8
8
|
Run `dsu help config` for more information.
|
9
9
|
messages:
|
10
10
|
editing: "Editing entry group %{formatted_time}..."
|
11
|
+
entry_group:
|
12
|
+
importer_service:
|
13
|
+
errors:
|
14
|
+
project_mismatch: The current project "%{current_project_name}" does not match the project "%{import_project_name}" being imported.
|