Skip to content

Commit

Permalink
dsu project rename subcommand views and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gangelo committed Feb 4, 2024
1 parent 0c57d85 commit dcb07eb
Show file tree
Hide file tree
Showing 16 changed files with 487 additions and 87 deletions.
5 changes: 2 additions & 3 deletions lib/dsu/models/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -295,12 +295,11 @@ def use!(project:)
attr_writer :current_project_file, :options, :version

def description=(value)
description = if value.blank?
"#{project_name.capitalize} project"
@description = if value.blank?
"#{project_name} project"
else
value
end
@description = description
end

def project_name=(value)
Expand Down
47 changes: 30 additions & 17 deletions lib/dsu/presenters/project/rename_by_number_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,56 @@ module Dsu
module Presenters
module Project
class RenameByNumberPresenter < BasePresenterEx
attr_reader :project_number, :description
delegate :project_name, :description, to: :project, allow_nil: true

# delegate :project_name, to: :project, allow_nil: true
# delegate :description, to: :project, prefix: true, allow_nil: true
attr_reader :project_number

def initialize(project_number:, description:, options: {})
def initialize(project_number:, new_project_name:, new_project_description:, options: {})
super(options: options)

raise ArgumentError, 'project_number is blank' if project_number.blank?
raise ArgumentError, 'new_project_name is blank' if new_project_name.blank?

self.project_number = project_number
self.description = description
@project_number = project_number

@project = Models::Project.find_by_number(project_number: project_number)

@new_project = Models::Project.new(project_name: new_project_name.strip,
description: new_project_description&.strip, options: options).tap(&:validate)
end

def respond(response:)
return false unless response
return false if new_project.invalid?

project.rename(project_name: :project_name, description: description) if project_already_exists?
project.rename!(new_project_name: new_project_name, new_project_description: new_project_description)
end

def project_already_exists?
project.exist?
end
def project_does_not_exist?
return true unless project.present?

def project_errors
return false unless project&.persisted?
!project.exist?
end

project.errors.full_messages
def new_project_already_exists?
new_project.exist?
end

private
def new_project_name
new_project.project_name
end

attr_writer :project_number, :description
def new_project_description
new_project.description
end

def project
@project ||= Models::Project.find_by_number(project_number: project_number)
def new_project_errors
new_project.errors.full_messages
end

private

attr_reader :new_project, :project
end
end
end
Expand Down
38 changes: 23 additions & 15 deletions lib/dsu/presenters/project/rename_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,50 @@ module Dsu
module Presenters
module Project
class RenamePresenter < BasePresenterEx
attr_reader :project_name, :new_project_name, :new_project_description
delegate :project_name, :description, to: :project

def initialize(project_name:, new_project_name:, new_project_description:, options: {})
super(options: options)

raise ArgumentError, 'project_name is blank' if project_name.blank?
raise ArgumentError, 'new_project_name is blank' if new_project_name.blank?

self.project_name = project_name.strip
self.new_project_name = new_project_name.strip
self.new_project_description = new_project_description&.strip
@project = Models::Project.find_or_initialize(project_name: project_name)

@new_project = Models::Project.new(project_name: new_project_name.strip,
description: new_project_description&.strip, options: options).tap(&:validate)
end

def respond(response:)
return false unless response
return false if new_project.invalid?

project.rename(project_name: :project_name, description: description) if project_already_exists?
project.rename!(new_project_name: new_project_name, new_project_description: new_project_description)
end

def project_already_exists?
project.exist?
def project_does_not_exist?
!project.exist?
end

def project_errors
return false unless project.persisted?

project.errors.full_messages
def new_project_already_exists?
new_project.exist?
end

private
def new_project_name
new_project.project_name
end

attr_writer :project_name, :description
def new_project_description
new_project.description
end

def project
@project ||= Models::Project.find_or_initialize(project_name: project_name)
def new_project_errors
new_project.errors.full_messages
end

private

attr_reader :new_project, :project
end
end
end
Expand Down
26 changes: 5 additions & 21 deletions lib/dsu/support/descriptable.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# frozen_string_literal: true

require_relative 'short_string'

module Dsu
module Support
module Descriptable
DESCRIPTION_MAX_COUNT = 25

class << self
def included(base)
base.extend(ClassMethods)
Expand All @@ -18,26 +18,10 @@ def short_description
end

module ClassMethods
def short_description(string:, count: DESCRIPTION_MAX_COUNT, elipsis: '...')
return elipsis unless string.is_a?(String)

elipsis_length = elipsis.length
count = elipsis_length if count.nil? || count < elipsis_length

return string if string.length <= count

tokens = string.split
string = ''

return "#{tokens.first[0...(count - elipsis_length)]}#{elipsis}" if tokens.count == 1

tokens.each do |token|
break if string.length + token.length + elipsis_length > count

string = "#{string} #{token}"
end
include ShortString

"#{string.strip}#{elipsis}"
def short_description(string:, count: ShortString::SHORT_STRING_MAX_COUNT, elipsis: '...')
short_string(string: string, count: count, elipsis: elipsis)
end
end
end
Expand Down
24 changes: 24 additions & 0 deletions lib/dsu/support/short_string.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module Dsu
module Support
module ShortString
SHORT_STRING_MAX_COUNT = 25

module_function

def short_string(string:, count: SHORT_STRING_MAX_COUNT, elipsis: '...')
return '' if string.blank?
return string if string.length <= count

# Trim to max count and cut at the last space within the limit
trimmed_string = string[0...count].rpartition(' ')[0]

# If no space found, trim by characters
trimmed_string = string[0...(count - elipsis.length)] if trimmed_string.empty? && !string.empty?

"#{trimmed_string}#{elipsis}"
end
end
end
end
5 changes: 4 additions & 1 deletion lib/dsu/validators/project_name_validator.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# frozen_string_literal: true

require_relative '../support/field_errors'
require_relative '../support/short_string'

# https://guides.rubyonrails.org/active_record_validations.html#validates-with
module Dsu
module Validators
# TODO: I18n.
class ProjectNameValidator < ActiveModel::Validator
include Support::FieldErrors
include Support::ShortString

def validate(record)
unless record.project_name.is_a?(String)
Expand Down Expand Up @@ -38,7 +40,8 @@ def validate_project_name(record)
"(minimum is #{min_project_name_length(record)} characters).")
elsif project_name.length > max_project_name_length(record)
# TODO: I18n.
record.errors.add(:project_name, "is too long: \"#{record.project_name}\" " \
short_project_name = short_string(string: project_name, count: max_project_name_length(record))
record.errors.add(:project_name, "is too long: \"#{short_project_name}\" " \
"(maximum is #{max_project_name_length(record)} characters).")
end
end
Expand Down
98 changes: 98 additions & 0 deletions lib/dsu/views/project/rename.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# frozen_string_literal: true

require_relative '../../env'
require_relative '../../models/color_theme'
require_relative '../../support/ask'
require_relative '../../support/color_themable'

module Dsu
module Views
module Project
class Rename
include Support::Ask
include Support::ColorThemable

attr_reader :presenter

def initialize(presenter:, options: {})
@presenter = presenter
@options = options&.dup || {}
@color_theme = Models::ColorTheme.find(theme_name: theme_name)
end

def render
return display_project_does_not_exist if presenter.project_does_not_exist?
return display_new_project_already_exists if presenter.new_project_already_exists?
return display_new_project_errors if presenter.new_project_errors.any?

response = display_project_rename_prompt
if presenter.respond response: response
display_renamed_project_message
else
display_rename_project_cancelled_message
end
rescue StandardError => e
puts apply_theme(e.message, theme_color: color_theme.error)
puts apply_theme(e.backtrace_locations.join("\n"), theme_color: color_theme.error) if Dsu.env.local?
end

private

attr_reader :color_theme, :options

def display_project_rename_prompt
response = ask_while(prompt_with_options(prompt: rename_prompt,
options: rename_prompt_options), options: options) do |input|
message = I18n.t('information.input.try_again', options: rename_prompt_options.join(','))
puts apply_theme(message, theme_color: color_theme.info) unless rename_prompt_options.include?(input)
rename_prompt_options.include?(input)
end
response == rename_prompt_options.first
end

def display_rename_project_cancelled_message
message = I18n.t('subcommands.project.messages.cancelled')
puts apply_theme(message, theme_color: color_theme.info)
end

def display_new_project_errors
errors = presenter.new_project_errors.join("\n")
puts apply_theme(errors, theme_color: color_theme.error)
end

def display_project_does_not_exist
message = I18n.t('subcommands.project.messages.does_not_exist',
project_name: presenter.project_name)
puts apply_theme(message, theme_color: color_theme.error)
end

def display_new_project_already_exists
message = I18n.t('subcommands.project.rename.messages.new_project_already_exists',
new_project_name: presenter.new_project_name)
puts apply_theme(message, theme_color: color_theme.error)
end

def display_renamed_project_message
message = I18n.t('subcommands.project.rename.messages.renamed_project',
project_name: presenter.project_name, new_project_name: presenter.new_project_name)
puts apply_theme(message, theme_color: color_theme.success)
end

def rename_prompt
I18n.t('subcommands.project.rename.prompts.rename_confirm',
project_name: presenter.project_name,
new_project_name: presenter.new_project_name,
new_project_description: presenter.new_project_description)
end

def rename_prompt_options
I18n.t('subcommands.project.rename.prompts.rename_options')
end

def theme_name
@theme_name ||= options.fetch(:theme_name, Models::Configuration.new.theme_name)
end
end
end
end
end
21 changes: 21 additions & 0 deletions lib/dsu/views/project/rename_by_number.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

require_relative '../../env'
require_relative '../../models/color_theme'
require_relative '../../support/ask'
require_relative '../../support/color_themable'
require_relative 'rename'

module Dsu
module Views
module Project
class RenameByNumber < Rename
def display_project_does_not_exist
message = I18n.t('subcommands.project.messages.number_does_not_exist',
project_number: presenter.project_number)
puts apply_theme(message, theme_color: color_theme.error)
end
end
end
end
end
17 changes: 7 additions & 10 deletions lib/locales/en/subcommands.yml
Original file line number Diff line number Diff line change
Expand Up @@ -628,18 +628,15 @@ en:
$ dsu project rename 1 "My New Project" "My new project description"
$ dsu p r 1 "My New Project" "My new project description"
$ dsu p r 1 "My New Project" "My new project description"
messages:
created: Created project "%{project_name}".
already_exists: Project "%{project_name}" already exists.
project_name_blank: No value provided for project name.
# created: Created project "%{project_name}".
new_project_already_exists: A project for new project name "%{new_project_name}" already exists.
# project_name_blank: No value provided for project name.
renamed_project: Renamed project "%{project_name}" to "%{new_project_name}".
prompts:
create_confirm: Create project "%{project_name}"?
create_options:
- Y
- n
use_confirm: Use project "%{project_name}"?
use_options:
rename_confirm: Rename project "%{project_name}" to "%{new_project_name} - %{new_project_description}"?.
rename_options:
- Y
- n
use:
Expand Down
Loading

0 comments on commit dcb07eb

Please sign in to comment.