diff --git a/concerns/askable.rb b/concerns/askable.rb new file mode 100644 index 0000000..90da018 --- /dev/null +++ b/concerns/askable.rb @@ -0,0 +1,38 @@ +module Askable + def self.included(base) + base.send(:extend, ClassMethods) + base.send(:include, InstanceMethods) + end + + module ClassMethods + attr_accessor :ask, :default + + def askable(value) + @ask = value + end + + def default_answer(value) + @default = value + end + end + + module InstanceMethods + def ask? + self.class.ask.present? + end + + def ask! + return unless ask? + + if self.class.options.any? + @pathfinder.utils.ask_with_options(self.class.ask, self.class.options) + elsif self.class.default + @pathfinder.utils.ask_with_default(self.class.ask, self.class.default) + elsif self.class.confirmable? + @pathfinder.utils.ask_with_confirmation(self.class.ask) + else + @pathfinder.utils.ask(self.class.ask) + end + end + end +end diff --git a/concerns/auto_runnable.rb b/concerns/auto_runnable.rb new file mode 100644 index 0000000..ad21820 --- /dev/null +++ b/concerns/auto_runnable.rb @@ -0,0 +1,21 @@ +module AutoRunnable + def self.included(base) + base.send(:extend, ClassMethods) + end + + module ClassMethods + attr_accessor :auto_runnable + + def is_auto_runnable + @auto_runnable = true + end + end + + def self.auto_runnables_for_module(mod) + mod.constants.reduce([]) do |acc, c| + klass = mod.const_get(c) + auto_runnable = klass.is_a?(Class) && klass.auto_runnable ? klass : nil + (acc << auto_runnable).compact + end + end +end diff --git a/concerns/confirmable.rb b/concerns/confirmable.rb new file mode 100644 index 0000000..a72d5a0 --- /dev/null +++ b/concerns/confirmable.rb @@ -0,0 +1,17 @@ +module Confirmable + def self.included(base) + base.send(:extend, ClassMethods) + end + + module ClassMethods + attr_accessor :confirmable + + def is_confirmable + @confirmable = true + end + + def confirmable? + @confirmable || false + end + end +end diff --git a/concerns/dependable.rb b/concerns/dependable.rb new file mode 100644 index 0000000..4885fbb --- /dev/null +++ b/concerns/dependable.rb @@ -0,0 +1,24 @@ +module Dependable + def self.included(base) + base.send(:extend, ClassMethods) + base.send(:include, InstanceMethods) + end + + module ClassMethods + attr_accessor :dependent + + def dependable(value) + @dependent = value + end + end + + module InstanceMethods + def dependent? + self.class.dependent || false + end + + def perform? + @pathfinder.recipes_list.map(&:class).include?(self.class.dependent.constantize) + end + end +end diff --git a/concerns/optionable.rb b/concerns/optionable.rb new file mode 100644 index 0000000..ce5bab5 --- /dev/null +++ b/concerns/optionable.rb @@ -0,0 +1,17 @@ +module Optionable + def self.included(base) + base.send(:extend, ClassMethods) + end + + module ClassMethods + attr_accessor :options + + def optionable(options) + @options = options + end + + def options + @options || [] + end + end +end diff --git a/configurators.rb b/configurators.rb new file mode 100644 index 0000000..1dec202 --- /dev/null +++ b/configurators.rb @@ -0,0 +1,5 @@ +require_relative 'configurators/base' +require_relative 'configurators/active_admin' +require_relative 'configurators/form_framework' +require_relative 'configurators/image_magick' +require_relative 'configurators/monitoring' diff --git a/configurators/active_admin.rb b/configurators/active_admin.rb new file mode 100644 index 0000000..1fe26ae --- /dev/null +++ b/configurators/active_admin.rb @@ -0,0 +1,14 @@ +module Configurators + class ActiveAdmin < Base + + askable 'What will be the main user class for Devise and ActiveAdmin?' + default_answer 'AdminUser' + dependable 'Recipes::ActiveAdmin' + + def cook + admin_class_name = ask! + @template.run "rails g active_admin:install #{admin_class_name}" + end + + end +end diff --git a/configurators/base.rb b/configurators/base.rb new file mode 100644 index 0000000..55b6653 --- /dev/null +++ b/configurators/base.rb @@ -0,0 +1,26 @@ +require_relative '../concerns/askable' +require_relative '../concerns/optionable' +require_relative '../concerns/confirmable' +require_relative '../concerns/dependable' + +module Configurators + class Base + + include ::Askable + include ::Optionable + include ::Confirmable + include ::Dependable + + def initialize(pathfinder) + @pathfinder = pathfinder + @template = pathfinder.template + end + + def cook + end + + def recipe + end + + end +end diff --git a/configurators/form_framework.rb b/configurators/form_framework.rb new file mode 100644 index 0000000..d0f6830 --- /dev/null +++ b/configurators/form_framework.rb @@ -0,0 +1,27 @@ +module Configurators + class FormFramework < Base + + askable 'What framework do you want to use for your forms?' + optionable %w(Default Marsman Bootstrap) + + def cook + case ask! + when 'Marsman' + @template.initializer('simple_form.rb', simple_form_template('marsman.rb')) + when 'Bootstrap' + @template.generate('simple_form:install --bootstrap') + else + @template.generate('simple_form:install') + end + end + + private + + def simple_form_template(filename) + base_path = File.dirname(__FILE__).split('/') + base_path.pop + File.read(File.join(base_path, 'recipes', 'simple_form', filename)) + end + + end +end diff --git a/configurators/image_magick.rb b/configurators/image_magick.rb new file mode 100644 index 0000000..69bc413 --- /dev/null +++ b/configurators/image_magick.rb @@ -0,0 +1,15 @@ +module Configurators + class ImageMagick < Base + + askable 'Are you going to handle images with CarrierWave?' + dependable 'Recipes::CarrierWave' + is_confirmable + + def cook + if ask! + @template.gem 'mini_magick' + end + end + + end +end diff --git a/configurators/monitoring.rb b/configurators/monitoring.rb new file mode 100644 index 0000000..d16427a --- /dev/null +++ b/configurators/monitoring.rb @@ -0,0 +1,17 @@ +module Configurators + class Monitoring < Base + + askable 'Choose Monitoring Engine:' + optionable %w(None Rollbar Airbrake) + + def recipe + case ask! + when 'Rollbar' + @pathfinder.add_recipe(Recipes::Rollbar.new(@pathfinder)) + when 'Airbrake' + @pathfinder.add_recipe(Recipes::Airbrake.new(@pathfinder)) + end + end + + end +end diff --git a/pathfinder.rb b/pathfinder.rb index 12e6d3e..f1807fe 100644 --- a/pathfinder.rb +++ b/pathfinder.rb @@ -1,108 +1,111 @@ -require_relative 'recipes/base' -require_relative 'recipes/active_admin' -require_relative 'recipes/airbrake' -require_relative 'recipes/assets' -require_relative 'recipes/carrier_wave' -require_relative 'recipes/configuration' -require_relative 'recipes/database' -require_relative 'recipes/devise' -require_relative 'recipes/git_ignore' -require_relative 'recipes/modernizr' -require_relative 'recipes/mailgun' -require_relative 'recipes/pundit' -require_relative 'recipes/redis' -require_relative 'recipes/rollbar' -require_relative 'recipes/sidekiq' -require_relative 'recipes/simple_form' -require_relative 'recipes/status' -require_relative 'recipes/testing' -require_relative 'recipes/utils' -require_relative 'recipes/webpacker' +require_relative 'configurators' +require_relative 'utils' +require 'tty-prompt' + +Dir[File.join(__dir__, 'recipes', '*.rb')].each do |recipe_file| + require recipe_file +end class Pathfinder - attr_reader :template, :app_name - - def initialize(app_name, template) - @template = template - @recipes_list = [] - @app_name = app_name - end - - def add_recipe(recipe) - @recipes_list << recipe - recipe - end - - def generate_gems - recipes_operation(:gems) - end - - def generate_initializers - recipes_operation(:cook) - end - - def ask_for_recipes - add_recipe(Recipes::Database.new(self)) - add_recipe(Recipes::CarrierWave.new(self)) if @template.yes?('Do you want to use Carrierwave?') - if @template.yes?('Do you want to use Mailgun for production emails?') - add_recipe(Recipes::Mailgun.new(self)) - end - aux = case @template.ask('Choose Monitoring Engine:', - limited_to: %w(rollbar airbrake none)) - when 'rollbar' - Recipes::Rollbar.new(self) - when 'airbrake' - Recipes::Airbrake.new(self) - else - end - add_recipe(aux) if aux.present? - add_recipe(Recipes::Assets.new(self)) - add_recipe(Recipes::Devise.new(self)) - add_recipe(Recipes::Pundit.new(self)) - add_recipe(Recipes::GitIgnore.new(self)) - add_recipe(Recipes::Redis.new(self)) - add_recipe(Recipes::Sidekiq.new(self)) - add_recipe(Recipes::SimpleForm.new(self)) - add_recipe(Recipes::Status.new(self)) - add_recipe(Recipes::Webpacker.new(self)) - if @template.yes?('Do you want to use Modernizr?') - add_recipe(Recipes::Modernizr.new(self)) - end - add_recipe(Recipes::ActiveAdmin.new(self)) - add_recipe(Recipes::Testing.new(self)) - end - - def call - ask_for_recipes - @template.instance_exec(self) do |pathfinder| - utils = Recipes::Utils.new(self) - configuration = Recipes::Configuration.new(self) - - remove_file 'Gemfile' - run 'touch Gemfile' - add_source 'https://rubygems.org' - - append_file 'Gemfile', "ruby \'#{utils.ask_with_default('Which version of ruby do you want to use?', default: RUBY_VERSION)}\'" - - configuration.gems do - pathfinder.generate_gems - end - - after_bundle do - run 'spring stop' - - configuration.cook - - pathfinder.generate_initializers - end - end - end - - private - - def recipes_operation(method) - @recipes_list.map(&method.to_sym) - end + attr_reader :template, :app_name, :utils, :prompt, :recipes_list + + def initialize(app_name, template) + @app_name = app_name + @template = template + @prompt = TTY::Prompt.new + @utils = ::Utils.new(@prompt) + @recipes_list = [] + @configurators_list = [] + end + + def ask_for_recipes + ::AutoRunnable.auto_runnables_for_module(::Recipes).each do |recipe| + add_recipe(recipe.new(self)) + end + end + + def ask_for_configurators + add_recipe_from_configurator(Configurators::Monitoring.new(self)) + add_configurator(Configurators::ActiveAdmin.new(self)) + add_configurator(Configurators::FormFramework.new(self)) + add_configurator(Configurators::ImageMagick.new(self)) + end + + def call + ask_for_recipes + ask_for_configurators + @template.instance_exec(self) do |pathfinder| + configuration = Recipes::Configuration.new(self) + + remove_file 'Gemfile' + run 'touch Gemfile' + add_source 'https://rubygems.org' + + ruby_version = pathfinder.utils.ask_with_default( + 'Which version of ruby do you want to use?', + RUBY_VERSION) + + append_file 'Gemfile', "ruby '#{ruby_version}'" + + configuration.gems do + pathfinder.generate_recipes_gems + pathfinder.generate_configurators_gems + end + + after_bundle do + run 'spring stop' + + configuration.cook + + pathfinder.generate_recipes_initializers + end + end + end + + def add_recipe(recipe) + if recipe.ask? + @recipes_list << recipe if recipe.ask! + else + @recipes_list << recipe + end + + recipe + end + + def add_configurator(configurator) + if configurator.dependent? + @configurators_list << configurator if configurator.perform? + else + @configurators_list << configurator + end + end + + def add_recipe_from_configurator(configurator) + recipe = configurator.recipe + add_recipe(recipe) if recipe + end + + def generate_configurators_gems + configurators_operation(:cook) + end + + def generate_recipes_gems + recipes_operation(:gems) + end + + def generate_recipes_initializers + recipes_operation(:cook) + end + + private + + def recipes_operation(method) + @recipes_list.map(&method.to_sym) + end + + def configurators_operation(method) + @configurators_list.map(&method.to_sym) + end end diff --git a/recipes/active_admin.rb b/recipes/active_admin.rb index 24cf72d..594f30e 100644 --- a/recipes/active_admin.rb +++ b/recipes/active_admin.rb @@ -1,18 +1,13 @@ module Recipes class ActiveAdmin < Base - def gems - if @template.yes? 'Will you need ActiveAdmin to have an admin area?' - @install = true - @template.gem 'activeadmin' - end - end + is_auto_runnable - def cook - return unless @install - msg = 'What will be the main user class for Devise and ActiveAdmin?' - user_classname = @template.ask msg, default: 'AdminUser' - @template.run "rails g active_admin:install #{user_classname}" + askable 'Will you need ActiveAdmin to have an admin area?' + is_confirmable + + def gems + @template.gem 'activeadmin' end end end diff --git a/recipes/assets.rb b/recipes/assets.rb index df9a165..00d89c4 100644 --- a/recipes/assets.rb +++ b/recipes/assets.rb @@ -1,9 +1,10 @@ module Recipes class Assets < Base + is_auto_runnable + def gems @template.gem 'bootstrap-sass', '~> 3.3.3' - @template.gem 'bootstrap-datepicker-rails', '~> 1.6.0' if @template.yes?('Do you want to use Bootstrap datepicker?') @template.gem 'font-awesome-sass', '~> 4.7.0' @template.gem 'sass-rails' @template.gem 'autoprefixer-rails' diff --git a/recipes/base.rb b/recipes/base.rb index 1ba2e19..ebb5bdf 100644 --- a/recipes/base.rb +++ b/recipes/base.rb @@ -1,6 +1,18 @@ +require_relative '../concerns/askable' +require_relative '../concerns/optionable' +require_relative '../concerns/confirmable' +require_relative '../concerns/dependable' +require_relative '../concerns/auto_runnable' + module Recipes class Base + include ::Askable + include ::Optionable + include ::Confirmable + include ::Dependable + include ::AutoRunnable + def initialize(pathfinder) @pathfinder = pathfinder @template = pathfinder.template diff --git a/recipes/bootstrap_datepicker.rb b/recipes/bootstrap_datepicker.rb new file mode 100644 index 0000000..29f812f --- /dev/null +++ b/recipes/bootstrap_datepicker.rb @@ -0,0 +1,14 @@ +module Recipes + class BootstrapDatepicker < Base + + is_auto_runnable + + askable 'Do you want to use Bootstrap datepicker?' + is_confirmable + + def gems + @template.gem 'bootstrap-datepicker-rails', '~> 1.6.0' + end + + end +end diff --git a/recipes/carrier_wave.rb b/recipes/carrier_wave.rb index 64d3b94..5a1fb77 100644 --- a/recipes/carrier_wave.rb +++ b/recipes/carrier_wave.rb @@ -1,10 +1,14 @@ module Recipes class CarrierWave < Base + is_auto_runnable + + askable 'Do you want to use Carrierwave?' + is_confirmable + def gems @template.gem 'carrierwave' @template.gem 'fog-aws' - @template.gem 'mini_magick' if @template.yes?("Are you going to handle images with CarrierWave?") end def cook @@ -35,5 +39,6 @@ def cook @template.append_file 'application.yml', "\nAWS_S3_BUCKET: ''" end end + end end diff --git a/recipes/configuration.rb b/recipes/configuration.rb index 085d8f3..abe1d60 100644 --- a/recipes/configuration.rb +++ b/recipes/configuration.rb @@ -6,15 +6,10 @@ def initialize(template) def gems @template.gem 'rails', '~> 5.1.0' - # Model + @template.gem 'aasm' @template.gem 'keynote' - @template.gem 'paranoia' if @template.yes?('Do you want to use Soft Deletes?') - # Searchs - @template.gem 'ransack' if @template.yes?('Do you want to use Ransack?') @template.gem 'kaminari' - @template.gem 'searchkick' if @template.yes?('Are you going to use ElasticSearch?') - # Emails @template.gem 'premailer-rails' yield if block_given? diff --git a/recipes/database.rb b/recipes/database.rb index 4bdefac..e71d3e4 100644 --- a/recipes/database.rb +++ b/recipes/database.rb @@ -1,6 +1,8 @@ module Recipes class Database < Base + is_auto_runnable + def gems case @template.options.database when 'postgresql' diff --git a/recipes/devise.rb b/recipes/devise.rb index 7a47ac8..da247ba 100644 --- a/recipes/devise.rb +++ b/recipes/devise.rb @@ -1,6 +1,8 @@ module Recipes class Devise < Base + is_auto_runnable + def gems @template.gem 'devise' end @@ -14,7 +16,8 @@ def cook private def add_development_env_config - @template.insert_into_file 'config/environments/development.rb', after: "Rails.application.configure do\n" do <<~RUBY + @template.insert_into_file 'config/environments/development.rb', + after: "Rails.application.configure do\n" do <<~RUBY \n \s\sconfig.action_controller.asset_host = 'http://localhost:3000' \s\sconfig.action_mailer.default_url_options = { host: 'localhost', port: 3000 } @@ -25,7 +28,8 @@ def add_development_env_config end def add_route_config - @template.insert_into_file 'config/routes.rb', after: "Rails.application.routes.draw do\n" do <<~RUBY + @template.insert_into_file 'config/routes.rb', + after: "Rails.application.routes.draw do\n" do <<~RUBY \s\sif Rails.env.development? \s\s\s\smount LetterOpenerWeb::Engine, at: "/letter_opener" \s\send diff --git a/recipes/elastic_search.rb b/recipes/elastic_search.rb new file mode 100644 index 0000000..28eead3 --- /dev/null +++ b/recipes/elastic_search.rb @@ -0,0 +1,14 @@ +module Recipes + class ElasticSearch < Base + + is_auto_runnable + + askable 'Are you going to use ElasticSearch?' + is_confirmable + + def gems + @template.gem 'searchkick' + end + + end +end diff --git a/recipes/git_ignore.rb b/recipes/git_ignore.rb index b85974b..2fa750e 100644 --- a/recipes/git_ignore.rb +++ b/recipes/git_ignore.rb @@ -1,6 +1,8 @@ module Recipes class GitIgnore < Base + is_auto_runnable + def cook @template.remove_file '.gitignore' @template.create_file '.gitignore', File.open(gitignore_path).read diff --git a/recipes/mailgun.rb b/recipes/mailgun.rb index e263a95..b5ea9e6 100644 --- a/recipes/mailgun.rb +++ b/recipes/mailgun.rb @@ -1,6 +1,11 @@ module Recipes class Mailgun < Base + is_auto_runnable + + askable 'Do you want to use Mailgun for production emails?' + is_confirmable + def gems @template.gem 'mailgun-ruby' end diff --git a/recipes/modernizr.rb b/recipes/modernizr.rb index 1d7b92b..4952a98 100644 --- a/recipes/modernizr.rb +++ b/recipes/modernizr.rb @@ -1,6 +1,11 @@ module Recipes class Modernizr < Base + is_auto_runnable + + askable 'Do you want to use Modernizr?' + is_confirmable + def gems @template.gem 'modernizr-rails' end diff --git a/recipes/paranoia.rb b/recipes/paranoia.rb new file mode 100644 index 0000000..5a80993 --- /dev/null +++ b/recipes/paranoia.rb @@ -0,0 +1,14 @@ +module Recipes + class Paranoia < Base + + is_auto_runnable + + askable 'Do you want to use Soft Deletes?' + is_confirmable + + def gems + @template.gem 'paranoia' + end + + end +end diff --git a/recipes/pundit.rb b/recipes/pundit.rb index 340cdaa..27cae5e 100644 --- a/recipes/pundit.rb +++ b/recipes/pundit.rb @@ -1,13 +1,16 @@ module Recipes class Pundit < Base + is_auto_runnable + def gems @template.gem 'pundit' end def cook @template.generate 'pundit:install' - @template.insert_into_file 'app/controllers/application_controller.rb', after: "class ApplicationController < ActionController::Base\n" do <<-RUBY + @template.insert_into_file 'app/controllers/application_controller.rb', + after: "class ApplicationController < ActionController::Base\n" do <<-RUBY include Pundit RUBY end diff --git a/recipes/ransack.rb b/recipes/ransack.rb new file mode 100644 index 0000000..399d3eb --- /dev/null +++ b/recipes/ransack.rb @@ -0,0 +1,14 @@ +module Recipes + class Ransack < Base + + is_auto_runnable + + askable 'Do you want to use Ransack?' + is_confirmable + + def gems + @template.gem 'ransack' + end + + end +end diff --git a/recipes/redis.rb b/recipes/redis.rb index 3cf7bab..77b695b 100644 --- a/recipes/redis.rb +++ b/recipes/redis.rb @@ -1,6 +1,8 @@ module Recipes class Redis < Base + is_auto_runnable + def gems @template.gem 'redis', '~> 3.3' end diff --git a/recipes/sidekiq.rb b/recipes/sidekiq.rb index cad410f..55cd5ff 100644 --- a/recipes/sidekiq.rb +++ b/recipes/sidekiq.rb @@ -1,6 +1,8 @@ module Recipes class Sidekiq < Base + is_auto_runnable + def gems @template.gem 'sidekiq' @template.gem 'sidekiq-failures' diff --git a/recipes/simple_form.rb b/recipes/simple_form.rb index 7ff0a6b..d55e91a 100644 --- a/recipes/simple_form.rb +++ b/recipes/simple_form.rb @@ -1,40 +1,28 @@ module Recipes class SimpleForm < Base + is_auto_runnable + def gems @template.gem 'simple_form' end def cook - run_generators add_sample_i18n end private - def run_generators - case ask_framework_for_forms - when 'marsman' - @template.initializer 'simple_form.rb', simple_form_template('marsman.rb') - when 'bootstrap' - @template.generate 'simple_form:install --bootstrap' - else - @template.generate 'simple_form:install' - end - end - def add_sample_i18n @template.run 'rm config/locales/simple_form.en.yml' - @template.append_to_file 'config/locales/en.yml', simple_form_template('en.yml') + @template.append_to_file 'config/locales/en.yml', simple_form_locale('en.yml') end - def ask_framework_for_forms - @template.ask('What framework do you want to use for your forms?', - limited_to: %w(marsman bootstrap default)) + def simple_form_locale(filename) + base_path = File.dirname(__FILE__).split('/') + base_path.pop + File.read(File.join(base_path, 'recipes', 'simple_form', filename)) end - def simple_form_template(filename) - File.read(File.join(File.dirname(__FILE__), 'simple_form', filename)) - end end end diff --git a/recipes/status.rb b/recipes/status.rb index ed48703..f061de7 100644 --- a/recipes/status.rb +++ b/recipes/status.rb @@ -1,6 +1,8 @@ module Recipes class Status < Base + is_auto_runnable + def cook set_route_path add_controller @@ -9,14 +11,17 @@ def cook private def set_route_path - @template.insert_into_file 'config/routes.rb', after: "Rails.application.routes.draw do\n" do <<~RUBY + @template.insert_into_file 'config/routes.rb', + after: "Rails.application.routes.draw do\n" do <<~RUBY \s\sresource :status, only: :show RUBY end end def add_controller - @template.copy_file(File.join(File.dirname(__FILE__), 'controllers', 'statuses_controller.rb'), 'app/controllers/statuses_controller.rb') + @template.copy_file( + File.join(File.dirname(__FILE__), 'controllers', 'statuses_controller.rb'), + 'app/controllers/statuses_controller.rb') end end diff --git a/recipes/testing.rb b/recipes/testing.rb index eddcbd5..7f9a0c2 100644 --- a/recipes/testing.rb +++ b/recipes/testing.rb @@ -1,5 +1,8 @@ module Recipes class Testing < Base + + is_auto_runnable + RSPEC_ROOT_FOLDER = 'spec' FACTORIES_FOLDERS = [RSPEC_ROOT_FOLDER, 'factories'] RSPEC_UNIT_FOLDERS = [RSPEC_ROOT_FOLDER, 'models'] diff --git a/recipes/utils.rb b/recipes/utils.rb deleted file mode 100644 index 9098492..0000000 --- a/recipes/utils.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Recipes - class Utils - - def initialize(template) - @template = template - end - - def ask_with_default(question, default:) - answer = @template.ask("#{question} (Default #{default})") - answer.empty? ? default : answer - end - - end -end diff --git a/recipes/webpacker.rb b/recipes/webpacker.rb index 8f55c90..071de62 100644 --- a/recipes/webpacker.rb +++ b/recipes/webpacker.rb @@ -1,6 +1,8 @@ module Recipes class Webpacker < Base + is_auto_runnable + def gems @template.gem 'webpacker', '~> 3.0' end diff --git a/utils.rb b/utils.rb new file mode 100644 index 0000000..1bb0608 --- /dev/null +++ b/utils.rb @@ -0,0 +1,21 @@ +class Utils + def initialize(prompt) + @prompt = prompt + end + + def ask(question) + @prompt.ask(question) + end + + def ask_with_confirmation(question) + @prompt.select(question, %w[yes no]) == 'yes' + end + + def ask_with_options(question, options) + @prompt.select(question, options) + end + + def ask_with_default(question, default) + @prompt.ask("#{question} (#{default})", default: default) + end +end