From b927551ef360b61878e603f61bd13298395fe970 Mon Sep 17 00:00:00 2001 From: gangelo Date: Sat, 27 Jan 2024 18:53:33 -0500 Subject: [PATCH] Refactor dsu export subcommand to new presenter pattern --- lib/dsu/presenters/export/all_presenter.rb | 32 +++---- lib/dsu/presenters/export/dates_presenter.rb | 29 +++---- lib/dsu/presenters/export/messages.rb | 32 ------- .../presenters/export/nothing_to_export.rb | 13 --- lib/dsu/presenters/export/service_callable.rb | 20 ----- lib/dsu/subcommands/export.rb | 6 +- lib/dsu/views/export.rb | 66 +++++++++++++-- .../presenters/export/all_presenter_spec.rb | 74 ----------------- .../presenters/export/dates_presenter_spec.rb | 83 ------------------- 9 files changed, 87 insertions(+), 268 deletions(-) delete mode 100644 lib/dsu/presenters/export/messages.rb delete mode 100644 lib/dsu/presenters/export/nothing_to_export.rb delete mode 100644 lib/dsu/presenters/export/service_callable.rb delete mode 100644 spec/dsu/presenters/export/all_presenter_spec.rb delete mode 100644 spec/dsu/presenters/export/dates_presenter_spec.rb diff --git a/lib/dsu/presenters/export/all_presenter.rb b/lib/dsu/presenters/export/all_presenter.rb index c46005ac..efcc662b 100644 --- a/lib/dsu/presenters/export/all_presenter.rb +++ b/lib/dsu/presenters/export/all_presenter.rb @@ -2,32 +2,26 @@ require_relative '../../models/entry_group' require_relative '../../services/entry_group/exporter_service' -require_relative '../../support/ask' require_relative '../base_presenter_ex' -require_relative 'messages' -require_relative 'nothing_to_export' -require_relative 'service_callable' module Dsu module Presenters module Export class AllPresenter < BasePresenterEx - include Messages - include NothingToExport - include ServiceCallable - include Support::Ask + attr_reader :export_file_path - def render(response:) - return display_cancelled_message unless response + def respond(response:) + return false unless response - export_file_path = exporter_service_call + @export_file_path = exporter_service.call + end - display_exported_message - display_exported_to_message(file_path: export_file_path) + def nothing_to_export? + entry_groups.empty? end - def display_export_prompt - yes?(prompt_with_options(prompt: export_prompt, options: export_prompt_options), options: options) + def entry_group_count + entry_groups&.count || 0 end private @@ -36,12 +30,8 @@ def entry_groups @entry_groups ||= Models::EntryGroup.all end - def export_prompt - I18n.t('subcommands.export.prompts.export_all_confirm', count: entry_groups.count) - end - - def export_prompt_options - I18n.t('subcommands.export.prompts.options') + def exporter_service + Services::EntryGroup::ExporterService.new(entry_groups: entry_groups, options: options) end end end diff --git a/lib/dsu/presenters/export/dates_presenter.rb b/lib/dsu/presenters/export/dates_presenter.rb index 9fda0f04..4b6f12fa 100644 --- a/lib/dsu/presenters/export/dates_presenter.rb +++ b/lib/dsu/presenters/export/dates_presenter.rb @@ -1,20 +1,14 @@ # frozen_string_literal: true require_relative '../../models/entry_group' -require_relative '../../support/ask' +require_relative '../../services/entry_group/exporter_service' require_relative '../base_presenter_ex' -require_relative 'messages' -require_relative 'nothing_to_export' -require_relative 'service_callable' module Dsu module Presenters module Export class DatesPresenter < BasePresenterEx - include Messages - include NothingToExport - include ServiceCallable - include Support::Ask + attr_reader :export_file_path def initialize(from:, to:, options: {}) super(options: options) @@ -23,17 +17,18 @@ def initialize(from:, to:, options: {}) @to = to end - def render(response:) - return display_cancelled_message unless response + def respond(response:) + return false unless response - export_file_path = exporter_service_call + @export_file_path = exporter_service.call + end - display_exported_message - display_exported_to_message(file_path: export_file_path) + def nothing_to_export? + entry_groups.empty? end - def display_export_prompt - yes?(prompt_with_options(prompt: export_prompt, options: export_prompt_options), options: options) + def entry_group_count + entry_groups&.count || 0 end private @@ -49,8 +44,8 @@ def export_prompt from: from.to_date, to: to.to_date, count: entry_groups.count) end - def export_prompt_options - I18n.t('subcommands.export.prompts.options') + def exporter_service + Services::EntryGroup::ExporterService.new(entry_groups: entry_groups, options: options) end end end diff --git a/lib/dsu/presenters/export/messages.rb b/lib/dsu/presenters/export/messages.rb deleted file mode 100644 index 4e814413..00000000 --- a/lib/dsu/presenters/export/messages.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -module Dsu - module Presenters - module Export - module Messages - def display_export_prompt - raise NotImplementedError - end - - def display_nothing_to_export_message - puts apply_theme(I18n.t('subcommands.export.messages.nothing_to_export'), theme_color: color_theme.info) - end - - private - - def display_cancelled_message - puts apply_theme(I18n.t('subcommands.export.messages.cancelled'), theme_color: color_theme.info) - end - - def display_exported_message - puts apply_theme(I18n.t('subcommands.export.messages.exported'), theme_color: color_theme.success) - end - - def display_exported_to_message(file_path:) - puts apply_theme(I18n.t('subcommands.export.messages.exported_to', file_path: file_path), - theme_color: color_theme.success) - end - end - end - end -end diff --git a/lib/dsu/presenters/export/nothing_to_export.rb b/lib/dsu/presenters/export/nothing_to_export.rb deleted file mode 100644 index 4add66e7..00000000 --- a/lib/dsu/presenters/export/nothing_to_export.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module Dsu - module Presenters - module Export - module NothingToExport - def nothing_to_export? - entry_groups.empty? - end - end - end - end -end diff --git a/lib/dsu/presenters/export/service_callable.rb b/lib/dsu/presenters/export/service_callable.rb deleted file mode 100644 index 9b4c7896..00000000 --- a/lib/dsu/presenters/export/service_callable.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../services/entry_group/exporter_service' - -module Dsu - module Presenters - module Export - module ServiceCallable - private - - def exporter_service_call - @exporter_service_call ||= begin - exporter_service = Services::EntryGroup::ExporterService.new(entry_groups: entry_groups, options: options) - exporter_service.call - end - end - end - end - end -end diff --git a/lib/dsu/subcommands/export.rb b/lib/dsu/subcommands/export.rb index 5a842961..0f478bcb 100644 --- a/lib/dsu/subcommands/export.rb +++ b/lib/dsu/subcommands/export.rb @@ -23,7 +23,8 @@ class Export < BaseSubcommand long_desc I18n.t('subcommands.export.all.long_desc') option :prompts, type: :hash, default: {}, hide: true, aliases: '-p' def all - Views::Export.new(presenter: all_presenter(options: options)).render + options = configuration.to_h.merge(self.options).with_indifferent_access + Views::Export.new(presenter: all_presenter(options: options), options: options).render end desc I18n.t('subcommands.export.dates.desc'), I18n.t('subcommands.export.dates.usage') @@ -41,7 +42,8 @@ def dates return end - Views::Export.new(presenter: dates_presenter_for(from: times.min, to: times.max, options: options)).render + Views::Export.new(presenter: + dates_presenter_for(from: times.min, to: times.max, options: options), options: options).render rescue ArgumentError => e Views::Shared::Error.new(messages: e.message).render end diff --git a/lib/dsu/views/export.rb b/lib/dsu/views/export.rb index c1841013..69537918 100644 --- a/lib/dsu/views/export.rb +++ b/lib/dsu/views/export.rb @@ -1,28 +1,82 @@ # frozen_string_literal: true require_relative '../models/color_theme' -require_relative '../models/configuration' +require_relative '../support/ask' require_relative '../support/color_themable' module Dsu module Views class Export + include Support::Ask include Support::ColorThemable - def initialize(presenter:) + def initialize(presenter:, options:) @presenter = presenter + @options = options&.dup || {} + @color_theme = Models::ColorTheme.find(theme_name: theme_name) end def render - return presenter.display_nothing_to_export_message if presenter.nothing_to_export? + return display_nothing_to_export_message if presenter.nothing_to_export? - response = presenter.display_export_prompt - presenter.render response: response + response = display_export_prompt + if presenter.respond response: response + display_exported_message + display_exported_to_message(file_path: presenter.export_file_path) + else + display_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 :presenter + attr_reader :presenter, :color_theme, :options + + def project_name + presenter.project_name + end + + def display_export_prompt + response = ask_while(prompt_with_options(prompt: export_prompt, + options: export_prompt_options), options: options) do |input| + message = I18n.t('information.input.try_again', options: export_prompt_options.join(',')) + puts apply_theme(message, theme_color: color_theme.info) unless export_prompt_options.include?(input) + export_prompt_options.include?(input) + end + response == export_prompt_options.first + end + + def display_cancelled_message + puts apply_theme(I18n.t('subcommands.export.messages.cancelled'), theme_color: color_theme.info) + end + + def display_exported_message + puts apply_theme(I18n.t('subcommands.export.messages.exported'), theme_color: color_theme.success) + end + + def display_exported_to_message(file_path:) + puts apply_theme(I18n.t('subcommands.export.messages.exported_to', file_path: file_path), + theme_color: color_theme.success) + end + + def display_nothing_to_export_message + puts apply_theme(I18n.t('subcommands.export.messages.nothing_to_export'), theme_color: color_theme.info) + end + + def export_prompt + I18n.t('subcommands.export.prompts.export_all_confirm', count: presenter.entry_group_count) + end + + def export_prompt_options + I18n.t('subcommands.export.prompts.options') + end + + def theme_name + @theme_name ||= options.fetch(:theme_name, Models::Configuration.new.theme_name) + end end end end diff --git a/spec/dsu/presenters/export/all_presenter_spec.rb b/spec/dsu/presenters/export/all_presenter_spec.rb deleted file mode 100644 index 3527e9b2..00000000 --- a/spec/dsu/presenters/export/all_presenter_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe Dsu::Presenters::Export::AllPresenter do - subject(:presenter) do - strip_escapes(Dsu::Services::StdoutRedirectorService.call do - described_class.new(options: options).render(response: response) - end) - end - - let(:options) { {} } - - describe '#render' do - context 'when the response is falsey' do - let(:response) { false } - - it 'displays the cancelled message' do - expect(presenter).to include(I18n.t('subcommands.export.messages.cancelled')) - end - end - - context 'when response is true' do - before do - create(:entry_group, :with_entries) - end - - let(:response) { true } - - it 'displays the exported message' do - expect(presenter).to include(I18n.t('subcommands.export.messages.exported')) - end - - it 'displays the exported to message with the file path' do - export_prompt_regex = /#{I18n.t('subcommands.export.messages.exported_to', file_path: 'x')[...-3]}/ - expect(presenter).to match(export_prompt_regex) - end - end - end - - describe '#display_export_prompt' do - subject(:presenter) do - strip_escapes(Dsu::Services::StdoutRedirectorService.call do - described_class.new(options: options).display_export_prompt - end) - end - - let(:options) { { prompts: { any: true } } } - let!(:entry_groups) { [create(:entry_group, :with_entries)] } - - it 'displays the display_export_prompt' do - export_prompt = I18n.t('subcommands.export.prompts.export_all_confirm', count: entry_groups.count) - expect(presenter).to include(export_prompt) - end - end - - describe '#nothing_to_export?' do - subject(:presenter) { described_class.new(options: options).nothing_to_export? } - - context 'when there is nothing to export' do - it 'returns true' do - expect(presenter).to be true - end - end - - context 'when there is something to export' do - before do - create(:entry_group, :with_entries) - end - - it 'returns false' do - expect(presenter).to be false - end - end - end -end diff --git a/spec/dsu/presenters/export/dates_presenter_spec.rb b/spec/dsu/presenters/export/dates_presenter_spec.rb deleted file mode 100644 index fdfe7c07..00000000 --- a/spec/dsu/presenters/export/dates_presenter_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe Dsu::Presenters::Export::DatesPresenter do - subject(:presenter) do - strip_escapes(Dsu::Services::StdoutRedirectorService.call do - described_class.new(from: from, to: to, options: options).render(response: response) - end) - end - - let(:options) { {} } - let(:times) { times_for_week_of(Time.now.in_time_zone) } - let(:from) { times.min } - let(:to) { times.max } - - describe '#render' do - context 'when response is falsey' do - let(:response) { false } - - it 'displays the cancelled message' do - expect(presenter).to include(I18n.t('subcommands.export.messages.cancelled')) - end - end - - context 'when response is true' do - before do - times.each do |time| - create(:entry_group, :with_entries, time: time) - end - end - - let(:response) { true } - - it 'displays the exported message' do - expect(presenter).to include(I18n.t('subcommands.export.messages.exported')) - end - - it 'displays the exported to message with the file path' do - regex = /#{I18n.t('subcommands.export.messages.exported_to', file_path: 'x')[...-3]}/ - expect(presenter).to match(regex) - end - end - end - - describe '#display_export_prompt' do - subject(:presenter) do - strip_escapes(Dsu::Services::StdoutRedirectorService.call do - described_class.new(from: from, to: to, options: options).display_export_prompt - end) - end - - let(:options) { { prompts: { any: true } } } - let!(:entry_groups) do - times.each.map do |time| - create(:entry_group, :with_entries, time: time) - end - end - - it 'displays the display_export_prompt' do - export_prompt = I18n.t('subcommands.export.prompts.export_dates_confirm', from: from.to_date, to: to.to_date, count: entry_groups.count) - expect(presenter).to include(export_prompt) - end - end - - describe '#nothing_to_export?' do - subject(:presenter) { described_class.new(from: from, to: to, options: options).nothing_to_export? } - - context 'when there is nothing to export' do - it 'returns true' do - expect(presenter).to be true - end - end - - context 'when there is something to export' do - before do - create(:entry_group, :with_entries, time: from) - end - - it 'returns false' do - expect(presenter).to be false - end - end - end -end