dsu 2.4.4 → 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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -0
  3. data/CHANGELOG.md +42 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +2 -2
  6. data/Rakefile +6 -0
  7. data/current_project.bak +4 -0
  8. data/lib/dsu/cli.rb +24 -6
  9. data/lib/dsu/crud/json_file.rb +3 -0
  10. data/lib/dsu/migration/version.rb +1 -1
  11. data/lib/dsu/models/color_theme.rb +7 -58
  12. data/lib/dsu/models/configuration.rb +18 -3
  13. data/lib/dsu/models/entry_group.rb +0 -7
  14. data/lib/dsu/models/migration_version.rb +0 -1
  15. data/lib/dsu/models/project.rb +295 -0
  16. data/lib/dsu/presenters/base_presenter_ex.rb +1 -12
  17. data/lib/dsu/presenters/export/all_presenter.rb +14 -19
  18. data/lib/dsu/presenters/export/dates_presenter.rb +17 -20
  19. data/lib/dsu/presenters/import/all_presenter.rb +20 -25
  20. data/lib/dsu/presenters/import/dates_presenter.rb +25 -27
  21. data/lib/dsu/presenters/import/import_entry.rb +22 -0
  22. data/lib/dsu/presenters/import/import_file.rb +9 -1
  23. data/lib/dsu/presenters/project/create_presenter.rb +44 -0
  24. data/lib/dsu/presenters/project/delete_by_number_presenter.rb +54 -0
  25. data/lib/dsu/presenters/project/delete_presenter.rb +53 -0
  26. data/lib/dsu/presenters/project/list_presenter.rb +24 -0
  27. data/lib/dsu/presenters/project/rename_by_number_presenter.rb +63 -0
  28. data/lib/dsu/presenters/project/rename_presenter.rb +57 -0
  29. data/lib/dsu/presenters/project/use_by_number_presenter.rb +53 -0
  30. data/lib/dsu/presenters/project/use_presenter.rb +52 -0
  31. data/lib/dsu/services/entry_group/exporter_service.rb +22 -5
  32. data/lib/dsu/services/entry_group/importer_service.rb +41 -8
  33. data/lib/dsu/services/project/hydrator_service.rb +40 -0
  34. data/lib/dsu/services/project/rename_service.rb +70 -0
  35. data/lib/dsu/subcommands/export.rb +4 -2
  36. data/lib/dsu/subcommands/import.rb +7 -3
  37. data/lib/dsu/subcommands/project.rb +149 -0
  38. data/lib/dsu/support/ask.rb +10 -3
  39. data/lib/dsu/support/color_themable.rb +1 -1
  40. data/lib/dsu/support/command_hookable.rb +7 -2
  41. data/lib/dsu/support/descriptable.rb +5 -21
  42. data/lib/dsu/support/fileable.rb +39 -1
  43. data/lib/dsu/support/project_file_system.rb +121 -0
  44. data/lib/dsu/support/short_string.rb +24 -0
  45. data/lib/dsu/support/time_comparable.rb +2 -0
  46. data/lib/dsu/support/transform_project_name.rb +24 -0
  47. data/lib/dsu/validators/project_name_validator.rb +58 -0
  48. data/lib/dsu/version.rb +1 -1
  49. data/lib/dsu/views/base_list_view.rb +41 -0
  50. data/lib/dsu/views/export.rb +60 -6
  51. data/lib/dsu/views/import.rb +83 -7
  52. data/lib/dsu/views/import_dates.rb +17 -0
  53. data/lib/dsu/views/project/create.rb +87 -0
  54. data/lib/dsu/views/project/delete.rb +96 -0
  55. data/lib/dsu/views/project/delete_by_number.rb +19 -0
  56. data/lib/dsu/views/project/list.rb +115 -0
  57. data/lib/dsu/views/project/rename.rb +98 -0
  58. data/lib/dsu/views/project/rename_by_number.rb +21 -0
  59. data/lib/dsu/views/project/use.rb +97 -0
  60. data/lib/dsu/views/project/use_by_number.rb +19 -0
  61. data/lib/dsu.rb +2 -10
  62. data/lib/locales/en/active_record.yml +9 -0
  63. data/lib/locales/en/commands.yml +9 -3
  64. data/lib/locales/en/miscellaneous.yml +4 -0
  65. data/lib/locales/en/services.yml +4 -0
  66. data/lib/locales/en/subcommands.yml +247 -15
  67. data/project.bak +0 -0
  68. metadata +34 -9
  69. data/lib/dsu/presenters/export/messages.rb +0 -32
  70. data/lib/dsu/presenters/export/nothing_to_export.rb +0 -13
  71. data/lib/dsu/presenters/export/service_callable.rb +0 -20
  72. data/lib/dsu/presenters/import/messages.rb +0 -42
  73. data/lib/dsu/presenters/import/service_callable.rb +0 -21
@@ -1,28 +1,82 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../models/color_theme'
4
- require_relative '../models/configuration'
4
+ require_relative '../support/ask'
5
5
  require_relative '../support/color_themable'
6
6
 
7
7
  module Dsu
8
8
  module Views
9
9
  class Export
10
+ include Support::Ask
10
11
  include Support::ColorThemable
11
12
 
12
- def initialize(presenter:)
13
+ def initialize(presenter:, options:)
13
14
  @presenter = presenter
15
+ @options = options&.dup || {}
16
+ @color_theme = Models::ColorTheme.find(theme_name: theme_name)
14
17
  end
15
18
 
16
19
  def render
17
- return presenter.display_nothing_to_export_message if presenter.nothing_to_export?
20
+ return display_nothing_to_export_message if presenter.nothing_to_export?
18
21
 
19
- response = presenter.display_export_prompt
20
- presenter.render response: response
22
+ response = display_export_prompt
23
+ if presenter.respond response: response
24
+ display_exported_message
25
+ display_exported_to_message(file_path: presenter.export_file_path)
26
+ else
27
+ display_cancelled_message
28
+ end
29
+ rescue StandardError => e
30
+ puts apply_theme(e.message, theme_color: color_theme.error)
31
+ puts apply_theme(e.backtrace_locations.join("\n"), theme_color: color_theme.error) if Dsu.env.local?
21
32
  end
22
33
 
23
34
  private
24
35
 
25
- attr_reader :presenter
36
+ attr_reader :presenter, :color_theme, :options
37
+
38
+ def project_name
39
+ presenter.project_name
40
+ end
41
+
42
+ def display_export_prompt
43
+ response = ask_while(prompt_with_options(prompt: export_prompt,
44
+ options: export_prompt_options), options: options) do |input|
45
+ message = I18n.t('information.input.try_again', options: export_prompt_options.join(','))
46
+ puts apply_theme(message, theme_color: color_theme.info) unless export_prompt_options.include?(input)
47
+ export_prompt_options.include?(input)
48
+ end
49
+ response == export_prompt_options.first
50
+ end
51
+
52
+ def display_cancelled_message
53
+ puts apply_theme(I18n.t('subcommands.export.messages.cancelled'), theme_color: color_theme.info)
54
+ end
55
+
56
+ def display_exported_message
57
+ puts apply_theme(I18n.t('subcommands.export.messages.exported'), theme_color: color_theme.success)
58
+ end
59
+
60
+ def display_exported_to_message(file_path:)
61
+ puts apply_theme(I18n.t('subcommands.export.messages.exported_to', file_path: file_path),
62
+ theme_color: color_theme.success)
63
+ end
64
+
65
+ def display_nothing_to_export_message
66
+ puts apply_theme(I18n.t('subcommands.export.messages.nothing_to_export'), theme_color: color_theme.info)
67
+ end
68
+
69
+ def export_prompt
70
+ I18n.t('subcommands.export.prompts.export_all_confirm', count: presenter.entry_group_count)
71
+ end
72
+
73
+ def export_prompt_options
74
+ I18n.t('subcommands.export.prompts.options')
75
+ end
76
+
77
+ def theme_name
78
+ @theme_name ||= options.fetch(:theme_name, Models::Configuration.new.theme_name)
79
+ end
26
80
  end
27
81
  end
28
82
  end
@@ -1,29 +1,105 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../models/color_theme'
4
- require_relative '../models/configuration'
4
+ require_relative '../support/ask'
5
5
  require_relative '../support/color_themable'
6
6
 
7
7
  module Dsu
8
8
  module Views
9
9
  class Import
10
+ include Support::Ask
10
11
  include Support::ColorThemable
11
12
 
12
- def initialize(presenter:)
13
+ def initialize(presenter:, options:)
13
14
  @presenter = presenter
15
+ @options = options&.dup || {}
16
+ @color_theme = Models::ColorTheme.find(theme_name: theme_name)
14
17
  end
15
18
 
16
19
  def render
17
- return presenter.display_import_file_not_exist_message unless presenter.import_file_path_exist?
18
- return presenter.display_nothing_to_import_message if presenter.nothing_to_import?
20
+ return display_import_file_not_exist_message unless presenter.import_file_path_exist?
21
+ return display_nothing_to_import_message if presenter.nothing_to_import?
19
22
 
20
- response = presenter.display_import_prompt
21
- presenter.render response: response
23
+ response = display_import_prompt
24
+ return display_cancelled_message unless response
25
+
26
+ response = display_project_override_prompt if presenter.overriding_project?
27
+ return display_cancelled_message unless response
28
+
29
+ display_import_messages presenter.respond
30
+ rescue StandardError => e
31
+ puts apply_theme(e.backtrace_locations.join("\n"), theme_color: color_theme.error) if Dsu.env.local?
32
+ message = I18n.t('subcommands.import.messages.import_error_raised', error: e.message)
33
+ puts apply_theme(message, theme_color: color_theme.error)
22
34
  end
23
35
 
24
36
  private
25
37
 
26
- attr_reader :presenter
38
+ attr_reader :presenter, :color_theme, :options
39
+
40
+ def display_import_prompt
41
+ response = ask_while(prompt_with_options(prompt: import_prompt,
42
+ options: import_prompt_options), options: options) do |input|
43
+ message = I18n.t('information.input.try_again', options: import_prompt_options.join(','))
44
+ puts apply_theme(message, theme_color: color_theme.info) unless import_prompt_options.include?(input)
45
+ import_prompt_options.include?(input)
46
+ end
47
+ response == import_prompt_options.first
48
+ end
49
+
50
+ def display_project_override_prompt
51
+ response = ask_while(prompt_with_options(prompt: project_override_prompt,
52
+ options: import_prompt_options), options: options) do |input|
53
+ message = I18n.t('information.input.try_again', options: import_prompt_options.join(','))
54
+ puts apply_theme(message, theme_color: color_theme.info) unless import_prompt_options.include?(input)
55
+ import_prompt_options.include?(input)
56
+ end
57
+ response == import_prompt_options.first
58
+ end
59
+
60
+ def display_cancelled_message
61
+ puts apply_theme(I18n.t('subcommands.import.messages.cancelled'), theme_color: color_theme.info)
62
+ end
63
+
64
+ def display_nothing_to_import_message
65
+ puts apply_theme(I18n.t('subcommands.import.messages.nothing_to_import'), theme_color: color_theme.info)
66
+ end
67
+
68
+ def import_prompt
69
+ I18n.t('subcommands.import.prompts.import_all_confirm',
70
+ count: presenter.import_entry_groups_count, project: presenter.project_name)
71
+ end
72
+
73
+ def project_override_prompt
74
+ I18n.t('subcommands.import.prompts.project_override_confirm')
75
+ end
76
+
77
+ def import_prompt_options
78
+ I18n.t('subcommands.import.prompts.options')
79
+ end
80
+
81
+ def display_import_file_not_exist_message
82
+ puts apply_theme(I18n.t('subcommands.import.messages.file_not_exist',
83
+ file_path: presenter.import_file_path), theme_color: color_theme.info)
84
+ end
85
+
86
+ def display_import_messages(import_results)
87
+ import_results.each_pair do |entry_group_date, errors|
88
+ if errors.empty?
89
+ puts apply_theme(I18n.t('subcommands.import.messages.import_success',
90
+ date: entry_group_date), theme_color: color_theme.success)
91
+ else
92
+ errors.each do |error|
93
+ puts apply_theme(I18n.t('subcommands.import.messages.import_error',
94
+ date: entry_group_date, error: error), theme_color: color_theme.error)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def theme_name
101
+ @theme_name ||= options.fetch(:theme_name, Models::Configuration.new.theme_name)
102
+ end
27
103
  end
28
104
  end
29
105
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'import'
4
+
5
+ module Dsu
6
+ module Views
7
+ class ImportDates < Import
8
+ private
9
+
10
+ def import_prompt
11
+ I18n.t('subcommands.import.prompts.import_dates_confirm',
12
+ from: presenter.from.to_date, to: presenter.to.to_date,
13
+ count: presenter.import_entry_groups_count, project: presenter.project_name)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -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