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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -0
  3. data/CHANGELOG.md +181 -204
  4. data/Gemfile.lock +13 -13
  5. data/README.md +7 -8
  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
@@ -2,32 +2,26 @@
2
2
 
3
3
  require_relative '../../models/entry_group'
4
4
  require_relative '../../services/entry_group/exporter_service'
5
- require_relative '../../support/ask'
6
5
  require_relative '../base_presenter_ex'
7
- require_relative 'messages'
8
- require_relative 'nothing_to_export'
9
- require_relative 'service_callable'
10
6
 
11
7
  module Dsu
12
8
  module Presenters
13
9
  module Export
14
10
  class AllPresenter < BasePresenterEx
15
- include Messages
16
- include NothingToExport
17
- include ServiceCallable
18
- include Support::Ask
11
+ attr_reader :export_file_path
19
12
 
20
- def render(response:)
21
- return display_cancelled_message unless response
13
+ def respond(response:)
14
+ return false unless response
22
15
 
23
- export_file_path = exporter_service_call
16
+ @export_file_path = exporter_service.call
17
+ end
24
18
 
25
- display_exported_message
26
- display_exported_to_message(file_path: export_file_path)
19
+ def nothing_to_export?
20
+ entry_group_count.zero?
27
21
  end
28
22
 
29
- def display_export_prompt
30
- yes?(prompt_with_options(prompt: export_prompt, options: export_prompt_options), options: options)
23
+ def entry_group_count
24
+ entry_groups&.count || 0
31
25
  end
32
26
 
33
27
  private
@@ -36,12 +30,13 @@ module Dsu
36
30
  @entry_groups ||= Models::EntryGroup.all
37
31
  end
38
32
 
39
- def export_prompt
40
- I18n.t('subcommands.export.prompts.export_all_confirm', count: entry_groups.count)
33
+ def exporter_service
34
+ Services::EntryGroup::ExporterService.new(project_name: project_name,
35
+ entry_groups: entry_groups, options: options)
41
36
  end
42
37
 
43
- def export_prompt_options
44
- I18n.t('subcommands.export.prompts.options')
38
+ def project_name
39
+ @project_name ||= Models::Project.current_project.project_name
45
40
  end
46
41
  end
47
42
  end
@@ -1,39 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../models/entry_group'
4
- require_relative '../../support/ask'
4
+ require_relative '../../services/entry_group/exporter_service'
5
5
  require_relative '../base_presenter_ex'
6
- require_relative 'messages'
7
- require_relative 'nothing_to_export'
8
- require_relative 'service_callable'
9
6
 
10
7
  module Dsu
11
8
  module Presenters
12
9
  module Export
13
10
  class DatesPresenter < BasePresenterEx
14
- include Messages
15
- include NothingToExport
16
- include ServiceCallable
17
- include Support::Ask
11
+ attr_reader :export_file_path
18
12
 
19
13
  def initialize(from:, to:, options: {})
20
14
  super(options: options)
21
15
 
22
16
  @from = from
23
17
  @to = to
18
+
19
+ self.options[:times] = [from, to]
24
20
  end
25
21
 
26
- def render(response:)
27
- return display_cancelled_message unless response
22
+ def respond(response:)
23
+ return false unless response
28
24
 
29
- export_file_path = exporter_service_call
25
+ @export_file_path = exporter_service.call
26
+ end
30
27
 
31
- display_exported_message
32
- display_exported_to_message(file_path: export_file_path)
28
+ def nothing_to_export?
29
+ entry_groups.empty?
33
30
  end
34
31
 
35
- def display_export_prompt
36
- yes?(prompt_with_options(prompt: export_prompt, options: export_prompt_options), options: options)
32
+ def entry_group_count
33
+ entry_groups&.count || 0
37
34
  end
38
35
 
39
36
  private
@@ -44,13 +41,13 @@ module Dsu
44
41
  Models::EntryGroup.entry_groups(between: [from, to])
45
42
  end
46
43
 
47
- def export_prompt
48
- I18n.t('subcommands.export.prompts.export_dates_confirm',
49
- from: from.to_date, to: to.to_date, count: entry_groups.count)
44
+ def exporter_service
45
+ Services::EntryGroup::ExporterService.new(project_name: project_name,
46
+ entry_groups: entry_groups, options: options)
50
47
  end
51
48
 
52
- def export_prompt_options
53
- I18n.t('subcommands.export.prompts.options')
49
+ def project_name
50
+ @project_name ||= Models::Project.current_project.project_name
54
51
  end
55
52
  end
56
53
  end
@@ -2,20 +2,18 @@
2
2
 
3
3
  require_relative '../../models/entry_group'
4
4
  require_relative '../../services/entry_group/importer_service'
5
- require_relative '../../support/ask'
6
5
  require_relative '../base_presenter_ex'
6
+ require_relative 'import_entry'
7
7
  require_relative 'import_file'
8
- require_relative 'messages'
9
- require_relative 'service_callable'
10
8
 
11
9
  module Dsu
12
10
  module Presenters
13
11
  module Import
14
12
  class AllPresenter < BasePresenterEx
13
+ include ImportEntry
15
14
  include ImportFile
16
- include Messages
17
- include ServiceCallable
18
- include Support::Ask
15
+
16
+ attr_reader :import_file_path, :import_messages
19
17
 
20
18
  def initialize(import_file_path:, options: {})
21
19
  super(options: options)
@@ -23,38 +21,35 @@ module Dsu
23
21
  @import_file_path = import_file_path
24
22
  end
25
23
 
26
- def render(response:)
27
- return display_cancelled_message unless response
28
-
29
- display_import_messages importer_service_call
24
+ def respond
25
+ @import_messages = importer_service.call
30
26
  end
31
27
 
32
- def display_import_prompt
33
- yes?(prompt_with_options(prompt: import_prompt, options: import_prompt_options), options: options)
28
+ def project_name
29
+ @project_name ||= Models::Project.current_project.project_name
34
30
  end
35
31
 
36
32
  private
37
33
 
38
- attr_reader :import_file_path, :options
39
-
40
34
  def import_entry_groups
41
35
  @import_entry_groups ||= CSV.foreach(import_file_path,
42
- headers: true).with_object({}) do |entry_group_entry, entry_groups_hash|
43
- next unless entry_group_entry['version'].to_i == Dsu::Migration::VERSION
36
+ headers: true, header_converters: :symbol).with_object({}) do |entry_group_entry, entry_groups_hash|
37
+ next unless import_entry?(entry_group_entry)
44
38
 
45
- Date.parse(entry_group_entry['entry_group']).to_s.tap do |time|
46
- entry_groups_hash[time] = [] unless entry_groups_hash.key?(time)
47
- entry_groups_hash[time] << entry_group_entry['entry_group_entry']
39
+ project_name = entry_group_entry[:project_name]
40
+ entry_groups_hash[project_name] = {} unless entry_groups_hash.key?(project_name)
41
+
42
+ Date.parse(entry_group_entry[:entry_group]).to_s.tap do |time|
43
+ entry_groups_hash[project_name][time] = [] unless entry_groups_hash[project_name].key?(time)
44
+ entry_groups_hash[project_name][time] << entry_group_entry[:entry_group_entry]
48
45
  end
49
46
  end
50
47
  end
51
48
 
52
- def import_prompt
53
- I18n.t('subcommands.import.prompts.import_all_confirm', count: import_entry_groups.count)
54
- end
55
-
56
- def import_prompt_options
57
- I18n.t('subcommands.import.prompts.options')
49
+ def importer_service
50
+ @importer_service ||= Services::EntryGroup::ImporterService.new(
51
+ import_projects: import_entry_groups, options: options
52
+ )
58
53
  end
59
54
  end
60
55
  end
@@ -2,20 +2,18 @@
2
2
 
3
3
  require_relative '../../models/entry_group'
4
4
  require_relative '../../services/entry_group/importer_service'
5
- require_relative '../../support/ask'
6
5
  require_relative '../base_presenter_ex'
6
+ require_relative 'import_entry'
7
7
  require_relative 'import_file'
8
- require_relative 'messages'
9
- require_relative 'service_callable'
10
8
 
11
9
  module Dsu
12
10
  module Presenters
13
11
  module Import
14
12
  class DatesPresenter < BasePresenterEx
13
+ include ImportEntry
15
14
  include ImportFile
16
- include Messages
17
- include ServiceCallable
18
- include Support::Ask
15
+
16
+ attr_reader :from, :to, :import_file_path, :import_messages
19
17
 
20
18
  def initialize(from:, to:, import_file_path:, options: {})
21
19
  super(options: options)
@@ -25,47 +23,47 @@ module Dsu
25
23
  @import_file_path = import_file_path
26
24
  end
27
25
 
28
- def render(response:)
29
- return display_cancelled_message unless response
30
-
31
- display_import_messages importer_service_call
26
+ def respond
27
+ @import_messages = importer_service.call
32
28
  end
33
29
 
34
- def display_import_prompt
35
- yes?(prompt_with_options(prompt: import_prompt, options: import_prompt_options), options: options)
30
+ def project_name
31
+ @project_name ||= Models::Project.current_project.project_name
36
32
  end
37
33
 
38
34
  private
39
35
 
40
- attr_reader :from, :to, :import_file_path, :options
41
-
42
36
  def import_entry_groups
43
37
  @import_entry_groups ||= CSV.foreach(import_file_path,
44
- headers: true).with_object({}) do |entry_group_entry, entry_groups_hash|
45
- next unless entry_group_entry['version'].to_i == Dsu::Migration::VERSION
38
+ headers: true, header_converters: :symbol).with_object({}) do |entry_group_entry, entry_groups_hash|
39
+ next unless import_entry?(entry_group_entry)
40
+
41
+ entry_group_time = middle_of_day_for(entry_group_entry[:entry_group])
42
+ next unless time_between_to_and_from_dates?(entry_group_time)
46
43
 
47
- entry_group_time = middle_of_day_for(entry_group_entry['entry_group'])
48
- next unless entry_group_time.to_date.between?(from.to_date, to.to_date)
44
+ project_name = entry_group_entry[:project_name]
45
+ entry_groups_hash[project_name] = {} unless entry_groups_hash.key?(project_name)
49
46
 
50
47
  entry_group_time.to_date.to_s.tap do |time|
51
- entry_groups_hash[time] = [] unless entry_groups_hash.key?(time)
52
- entry_groups_hash[time] << entry_group_entry['entry_group_entry']
48
+ entry_groups_hash[project_name][time] = [] unless entry_groups_hash[project_name].key?(time)
49
+ entry_groups_hash[project_name][time] << entry_group_entry[:entry_group_entry]
53
50
  end
54
51
  end
55
52
  end
56
53
 
57
- def import_prompt
58
- I18n.t('subcommands.import.prompts.import_dates_confirm',
59
- from: from.to_date, to: to.to_date, count: import_entry_groups.keys.count)
60
- end
61
-
62
- def import_prompt_options
63
- I18n.t('subcommands.import.prompts.options')
54
+ def time_between_to_and_from_dates?(entry_group_time)
55
+ entry_group_time.to_date.between?(from.to_date, to.to_date)
64
56
  end
65
57
 
66
58
  def middle_of_day_for(date_string)
67
59
  Time.parse(date_string).in_time_zone.middle_of_day
68
60
  end
61
+
62
+ def importer_service
63
+ @importer_service ||= Services::EntryGroup::ImporterService.new(
64
+ import_projects: import_entry_groups, options: options
65
+ )
66
+ end
69
67
  end
70
68
  end
71
69
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../migration/version'
4
+
5
+ module Dsu
6
+ module Presenters
7
+ module Import
8
+ module ImportEntry
9
+ def overriding_project?
10
+ options&.fetch(:override, false)
11
+ end
12
+
13
+ private
14
+
15
+ def import_entry?(entry_group_entry)
16
+ entry_group_entry[:version].to_i == Dsu::Migration::VERSION &&
17
+ (overriding_project? || entry_group_entry[:project_name] == project_name)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -11,7 +11,7 @@ module Dsu
11
11
  def nothing_to_import?
12
12
  return true unless import_file_path_exist?
13
13
 
14
- import_entry_groups.empty?
14
+ import_entry_groups.count.zero?
15
15
  end
16
16
 
17
17
  def import_entry_groups
@@ -19,6 +19,14 @@ module Dsu
19
19
  # Example: { '2023-12-32' => ['Entry description 1', 'Entry description 2', ...] }
20
20
  raise NotImplementedError
21
21
  end
22
+
23
+ def import_entry_groups_count
24
+ if overriding_project?
25
+ import_entry_groups&.first&.count || 0
26
+ else
27
+ import_entry_groups[project_name]&.count || 0
28
+ end
29
+ end
22
30
  end
23
31
  end
24
32
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../models/project'
4
+ require_relative '../base_presenter_ex'
5
+
6
+ module Dsu
7
+ module Presenters
8
+ module Project
9
+ class CreatePresenter < BasePresenterEx
10
+ delegate :project_name, to: :project
11
+
12
+ def initialize(project_name:, description:, options: {})
13
+ super(options: options)
14
+
15
+ @project = Models::Project.new(project_name: project_name, description: description, options: options)
16
+ end
17
+
18
+ def respond(response:)
19
+ return false unless response
20
+
21
+ project.create!
22
+ end
23
+
24
+ def project_already_exists?
25
+ project.exist?
26
+ end
27
+
28
+ def project_errors?
29
+ project.invalid?
30
+ end
31
+
32
+ def project_errors
33
+ return [] unless project_errors?
34
+
35
+ project.errors.full_messages
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :project, :options
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../models/project'
4
+ require_relative '../base_presenter_ex'
5
+
6
+ module Dsu
7
+ module Presenters
8
+ module Project
9
+ class DeleteByNumberPresenter < BasePresenterEx
10
+ attr_reader :project_number
11
+
12
+ delegate :project_name, to: :project, allow_nil: true
13
+ delegate :description, to: :project, prefix: true, allow_nil: true
14
+
15
+ def initialize(project_number:, options: {})
16
+ super(options: options)
17
+
18
+ raise ArgumentError, 'project_number is blank' if project_number.blank?
19
+
20
+ self.project_number = project_number
21
+ end
22
+
23
+ def respond(response:)
24
+ return false unless response
25
+ return false if project_does_not_exist? || project_default?
26
+
27
+ project.delete! if project&.present?
28
+ end
29
+
30
+ def project_does_not_exist?
31
+ !project&.exist?
32
+ end
33
+
34
+ def project_default?
35
+ project&.default_project?
36
+ end
37
+
38
+ def project_errors
39
+ return false unless project&.persisted?
40
+
41
+ project.errors.full_messages
42
+ end
43
+
44
+ private
45
+
46
+ attr_writer :project_number
47
+
48
+ def project
49
+ @project ||= Models::Project.find_by_number(project_number: project_number)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../models/project'
4
+ require_relative '../base_presenter_ex'
5
+
6
+ module Dsu
7
+ module Presenters
8
+ module Project
9
+ class DeletePresenter < BasePresenterEx
10
+ attr_reader :project_name
11
+
12
+ delegate :description, to: :project, prefix: true, allow_nil: true
13
+
14
+ def initialize(project_name:, options: {})
15
+ super(options: options)
16
+
17
+ raise ArgumentError, 'project_name is blank' if project_name.blank?
18
+
19
+ self.project_name = project_name
20
+ end
21
+
22
+ def respond(response:)
23
+ return false unless response
24
+ return false if project_does_not_exist? || project_default?
25
+
26
+ project.delete! if project&.present?
27
+ end
28
+
29
+ def project_does_not_exist?
30
+ !project.exist?
31
+ end
32
+
33
+ def project_default?
34
+ project.default_project?
35
+ end
36
+
37
+ def project_errors
38
+ return false unless project.persisted?
39
+
40
+ project.errors.full_messages
41
+ end
42
+
43
+ private
44
+
45
+ attr_writer :project_name
46
+
47
+ def project
48
+ @project ||= Models::Project.find_or_initialize(project_name: project_name)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../models/project'
4
+ require_relative '../base_presenter_ex'
5
+
6
+ module Dsu
7
+ module Presenters
8
+ module Project
9
+ class ListPresenter < BasePresenterEx
10
+ def initialize(options: {})
11
+ super(options: options)
12
+ end
13
+
14
+ def projects
15
+ @projects ||= Dsu::Models::Project.all
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :options
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../models/project'
4
+ require_relative '../base_presenter_ex'
5
+
6
+ module Dsu
7
+ module Presenters
8
+ module Project
9
+ class RenameByNumberPresenter < BasePresenterEx
10
+ delegate :project_name, :description, to: :project, allow_nil: true
11
+
12
+ attr_reader :project_number
13
+
14
+ def initialize(project_number:, new_project_name:, new_project_description:, options: {})
15
+ super(options: options)
16
+
17
+ raise ArgumentError, 'project_number is blank' if project_number.blank?
18
+ raise ArgumentError, 'new_project_name is blank' if new_project_name.blank?
19
+
20
+ @project_number = project_number
21
+
22
+ @project = Models::Project.find_by_number(project_number: project_number)
23
+
24
+ @new_project = Models::Project.new(project_name: new_project_name.strip,
25
+ description: new_project_description&.strip, options: options).tap(&:validate)
26
+ end
27
+
28
+ def respond(response:)
29
+ return false unless response
30
+ return false if new_project.invalid?
31
+
32
+ project.rename!(new_project_name: new_project_name, new_project_description: new_project_description)
33
+ end
34
+
35
+ def project_does_not_exist?
36
+ return true unless project.present?
37
+
38
+ !project.exist?
39
+ end
40
+
41
+ def new_project_already_exists?
42
+ new_project.exist?
43
+ end
44
+
45
+ def new_project_name
46
+ new_project.project_name
47
+ end
48
+
49
+ def new_project_description
50
+ new_project.description
51
+ end
52
+
53
+ def new_project_errors
54
+ new_project.errors.full_messages
55
+ end
56
+
57
+ private
58
+
59
+ attr_reader :new_project, :project
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../models/project'
4
+ require_relative '../base_presenter_ex'
5
+
6
+ module Dsu
7
+ module Presenters
8
+ module Project
9
+ class RenamePresenter < BasePresenterEx
10
+ delegate :project_name, :description, to: :project
11
+
12
+ def initialize(project_name:, new_project_name:, new_project_description:, options: {})
13
+ super(options: options)
14
+
15
+ raise ArgumentError, 'project_name is blank' if project_name.blank?
16
+ raise ArgumentError, 'new_project_name is blank' if new_project_name.blank?
17
+
18
+ @project = Models::Project.find_or_initialize(project_name: project_name)
19
+
20
+ @new_project = Models::Project.new(project_name: new_project_name.strip,
21
+ description: new_project_description&.strip, options: options).tap(&:validate)
22
+ end
23
+
24
+ def respond(response:)
25
+ return false unless response
26
+ return false if new_project.invalid?
27
+
28
+ project.rename!(new_project_name: new_project_name, new_project_description: new_project_description)
29
+ end
30
+
31
+ def project_does_not_exist?
32
+ !project.exist?
33
+ end
34
+
35
+ def new_project_already_exists?
36
+ new_project.exist?
37
+ end
38
+
39
+ def new_project_name
40
+ new_project.project_name
41
+ end
42
+
43
+ def new_project_description
44
+ new_project.description
45
+ end
46
+
47
+ def new_project_errors
48
+ new_project.errors.full_messages
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :new_project, :project
54
+ end
55
+ end
56
+ end
57
+ end