lib/make_menu/menu.rb in make_menu-0.1.1 vs lib/make_menu/menu.rb in make_menu-1.0.0

- old
+ new

@@ -1,173 +1,133 @@ # frozen_string_literal: true +require_relative 'builder' require_relative 'menu_item_group' -require_relative 'color_string' -require_relative 'text_table' +require_relative 'text/table' +require_relative 'console/prompter' module MakeMenu # This class builds and displays a number-selection menu from a Makefile # then prompts for a number and executes the target. class Menu - # @param [String] makefile Makefile name def initialize(makefile) + @makefile = makefile + @groups = [] @items = [] - build makefile + + @options = { + group_title_color: :underline, + clear_screen: true, + pause_on_success: false + } + @highlights = {} + @header = nil end - attr_reader :groups, :items + attr_reader :makefile, :groups, :items, :options, :highlights - # Display menu and prompt for command - # rubocop:disable Metrics/MethodLength def run - running = true + yield self if block_given? - while running + Builder.build(makefile, self) + + loop do system 'clear' if clear_screen? display_header - puts colorize(TextTable.new(groups).to_s) + puts colorize(MakeMenu::Text::Table.new(groups).to_s) puts - puts 'Press ENTER to quit'.align(:center).bold + puts 'Press ESC to quit'.align(:center).bold puts - print 'Select option: '.align(:center) - running = false unless execute_option(gets.strip) - end + prompt = 'Select option: '.align(:center) - puts - - system 'clear' if clear_screen? - end - - # rubocop:enable Metrics/MethodLength - - # Display the company logo and the status bar (if set) - def display_header - puts formatted_logo if logo - end - - private - - # Build a menu from the specified Makefile - # @param [String] makefile Filename - # rubocop:disable Metrics/MethodLength - def build(makefile) - File.open(makefile, 'r') do |file| - option_number = 1 - current_group = nil - - file.each_line do |line| - if line.start_with? '###' - # Group header - group_title = line.gsub(/###\s+/, '').strip - current_group = MenuItemGroup.new(group_title.color(group_title_color)) - groups << current_group - - elsif line.match(/^[a-zA-Z_-]+:.*?## .*$$/) - # Menu item - target = line.split(':').first.strip - description = line.split('##').last.strip - - # Target 'menu' should not appear - next if target == 'menu' - - unless current_group - current_group = MenuItemGroup.new('Commands'.color(group_title_color)) - groups << current_group - end - - items << current_group.add_item( - MenuItem.new(option_number, target, description) - ) - - option_number += 1 - end - end - - if option_number == 1 + begin + execute_option(MakeMenu::Console::Prompter.prompt(prompt).strip) puts - puts 'No annotated targets found!'.red.bold - puts - puts 'Expecting something like this....' - puts " #{'my_target:'.cyan} #{'## Do some things'.yellow}" - puts - exit 1 + rescue MakeMenu::Console::Prompter::PressedEscape + break end end - rescue Errno::ENOENT => _e puts - puts 'No Makefile!'.red.bold - puts - puts "File '#{makefile}' could not be found.".yellow - puts - exit 1 + + system 'clear' if clear_screen? end - # rubocop:enable Metrics/MethodLength - - # Execute the selected menu item - # @param [String] selected Value entered by user - # @return [Boolean] False to signify that menu should exit def execute_option(selected) - return false if selected.empty? - selected = selected.to_i items.each do |item| next unless item.option_number == selected system 'clear' if clear_screen? + item.execute - return true - end - true + if pause_on_success? + puts "\nPress ENTER to continue....".dark + gets + end + end end - # Apply word colorings - # @param [String] string - # @return [String] - def colorize(string) - return string unless Object.const_defined?("#{self.class.name}::HIGHLIGHTS") - - Object.const_get("#{self.class.name}::HIGHLIGHTS").each do |word, color| - case color - when Array - color.each { |c| string.gsub!(word, word.send(c)) } - else - string.gsub!(word, word.send(color)) - end + def display_header + if @header + @header.call + else + logo = " #{Dir.pwd.split('/').last.upcase} ".invert.bold.to_s + puts "\n#{logo.align(:center)}\n \n" end - string end - # Center each line of the logo across the screen - def formatted_logo - logo.split("\n") - .map { |line| line.align(:center) } - .join("\n") + def options + @options.merge!(yield) if block_given? + @options end - # Get the menu logo from the LOGO constant - def logo - return "\n#{" #{Dir.pwd.split('/').last} ".light_yellow_bg.black.bold}\n \n" unless Object.const_defined?("#{self.class.name}::LOGO") + def highlights + @highlights.merge!(yield) if block_given? + @highlights + end - Object.const_get("#{self.class.name}::LOGO") + def header(&block) + @header = block end - protected + def add_group(group) + groups << group + group + end - # @return [Symbol,Array[Symbol]] Color for group title + def add_item(item) + items << item + item + end + def group_title_color - %i[yellow bold] + options[:group_title_color] end - # Clear screen before and after each command def clear_screen? - true + options[:clear_screen] + end + + def pause_on_success? + options[:pause_on_success] + end + + def colorize(text) + highlights.each do |word, color| + case color + when Array + color.each { |c| text.gsub!(word, word.send(c)) } + else + text.gsub!(word, word.send(color)) + end + end + text end end end