class HighLine::Menu
to {HighLine#choose} can detail all aspects of menu display and control.
Using the accessors and {Menu#choice} and {Menu#choices}, the block passed
{HighLine#choose HighLine#choose}.
Menu objects encapsulate all the details of a call to
def add_item(item)
def add_item(item) @items << item @help.merge!(item.item_help) update_responses end
def all_items
def all_items @items + @hidden_items end
def build_item(*args)
def build_item(*args) Menu::Item.new(*args) end
def choice(name, help = nil, text = nil, &action)
def choice(name, help = nil, text = nil, &action) item = Menu::Item.new(name, text: text, help: help, action: action) @items << item @help.merge!(item.item_help) update_responses # rebuild responses based on our settings end
def choices(*names, &action)
- Example: -
Returns:
-
(void)
-
Parameters:
-
action
() --
-
names
(Array<#to_s>
) -- menu item titles/headers/names to be
def choices(*names, &action) names.each { |n| choice(n, &action) } end
def decorate_index(index)
def decorate_index(index) if index_color HighLine.color(index, index_color) else index end end
def decorate_item(text, ix)
def decorate_item(text, ix) decorated, non_decorated = mark_for_decoration(text, ix) decorate_index(decorated) + non_decorated end
def find_item_from_selection(items, selection)
def find_item_from_selection(items, selection) if selection =~ /^\d+$/ # is a number? get_item_by_number(items, selection) else get_item_by_letter(items, selection) end end
def gather_selected(highline_context, selections, details = nil)
def gather_selected(highline_context, selections, details = nil) @highline = highline_context # add in any hidden menu commands items = all_items if selections.is_a?(Array) value_for_array_selections(items, selections, details) elsif selections.is_a?(Hash) value_for_hash_selections(items, selections, details) else raise ArgumentError, "selections must be either Array or Hash" end end
def get_item_by_letter(items, selection)
-
selection
(String
) -- menu's title/header/name
def get_item_by_letter(items, selection) item = items.find { |i| i.name == selection } return item if item # 97 is the "a" letter at ascii table # Ex: For "a" it will return 0, and for "c" it will return 2 index = selection.downcase.ord - 97 items[index] end
def get_item_by_number(items, selection)
-
selection
(Integer
) -- menu item's index.
def get_item_by_number(items, selection) items[selection.to_i - 1] end
def help(topic, help)
-
help
(String
) -- the help message to be associated with the menu -
topic
(String
) -- the menu item header/title/name to be associated
def help(topic, help) @help[topic] = help end
def hidden(name, help = nil, &action)
def hidden(name, help = nil, &action) item = Menu::Item.new(name, text: name, help: help, action: action) @hidden_items << item @help.merge!(item.item_help) end
def index=(style)
Because of this, you should make a habit of setting the _index_ first.
_index_suffix_ to a single space and _select_by_ to :name.
Setting the _index_ to :none or a literal String also adjusts
any String:: Will be used as the literal _index_.
:none:: No index will be appended to menu items.
with a.
:letter:: Items will be indexed alphabetically, starting
with 1. This is the default method of indexing.
:number:: Menu items will be indexed numerically, starting
menu items, when displayed in list form. The available settings are:
Sets the indexing style for this Menu object. Indexes are appended to
def index=(style) @index = style return unless @index == :none || @index.is_a?(::String) # Default settings. @index_suffix = " " @select_by = :name end
def init_help
action code, and the default help listing.
Initializes the help system by adding a :help choice, some
def init_help return if @items.include?(:help) topics = @help.keys.sort help_help = if @help.include?("help") @help["help"] else "This command will display helpful messages about " \ "functionality, like this one. To see the help for " \ "a specific topic enter:\n\thelp [TOPIC]\nTry asking " \ "for help on any of the following:\n\n" \ "<%= list(#{topics.inspect}, :columns_across) %>" end choice(:help, help_help) do |_command, topic| topic.strip! topic.downcase! if topic.empty? @highline.say(@help["help"]) else @highline.say("= #{topic}\n\n#{@help[topic]}") end end end
def initialize
def initialize # # Initialize Question objects with ignored values, we'll # adjust ours as needed. # super("Ignored", [], &nil) # avoiding passing the block along @items = [] @hidden_items = [] @help = Hash.new("There's no help for that topic.") @index = :number @index_suffix = ". " @select_by = :index_or_name @flow = :rows @list_option = nil @header = nil @prompt = "? " @layout = :list @shell = false @nil_on_handled = false # Used for coloring Menu indices. # Set it to default. But you may override it. @index_color = self.class.index_color # Override Questions responses, we'll set our own. @responses = {} # Context for action code. @highline = nil yield self if block_given? init_help if @shell && !@help.empty? end
def layout=(new_layout)
:inline.
will default to :none and _flow_ will default to
If set to either :one_line, or :menu_only, _index_
context so each method is properly delegated.
otherwise evaluated in the TemplateRenderer
menu and prompt, but is
String can access header,
any ERb String:: Will be taken as the literal _layout_. This
short _prompt_.
:menu_only:: Just the menu items, followed up by a likely
items between trailing parenthesis.
colon and spaces, then the _prompt_ with menu
The _header_ comes first followed by a
:one_line:: A shorter _layout_ that fits on one line.
will be used as the ask()-like question.
items will follow. Finally, the _prompt_
a trailing colon. Then the list of menu
will appear at the top on its own line with
:list:: The default _layout_. The _header_ if set
Accepted settings for _layout_ are:
configuration block, if needed.
account for that, you probably want to set a _layout_ first in your
of the Menu object, to ideal defaults for the chosen _layout_. To
Setting a _layout_ with this method also adjusts some other attributes
def layout=(new_layout) @layout = new_layout # Default settings. case @layout when :one_line, :menu_only self.index = :none @flow = :inline end end
def map_items_by_index
def map_items_by_index size = all_items.size case @index when :letter ("a".."z").first(size) when :capital_letter ("A".."Z").first(size) else (1..size).map(&:to_s) end end
def map_items_by_name
def map_items_by_name all_items.map(&:name) end
def mark_for_decoration(text, ix)
def mark_for_decoration(text, ix) case @index when :number ["#{ix + 1}#{@index_suffix}", text] when :letter, :capital_letter first_letter = (@index == :capital_letter ? 'A' : 'a') ["#{(first_letter.ord + ix).chr}#{@index_suffix}", text] when :none [text, ""] else ["#{index}#{@index_suffix}", text] end end
def options
on the settings of _index_ and _select_by_.
This method returns all possible options for auto-completion, based
def options case @select_by when :index map_items_by_index when :name map_items_by_name else map_items_by_index + map_items_by_name end end
def parse_list
def parse_list "<%= list( menu, #{@flow.inspect}, #{@list_option.inspect} ) %>" end
def select(highline_context, selection, details = nil)
-
(nil, Object)
- if @nil_on_handled is set it returns +nil+,
Parameters:
-
details
() -- additional parameter to be passed when in shell mode.
-
selection
(String, Integer
) -- index or title of the selected -
highline_context
(HighLine
) -- a HighLine instance to be used
def select(highline_context, selection, details = nil) # add in any hidden menu commands items = all_items # Find the selected action. selected_item = find_item_from_selection(items, selection) # Run or return it. @highline = highline_context value_for_selected_item(selected_item, details) end
def show_default_if_any
def show_default_if_any default.to_s.empty? ? "" : "(#{default}) " end
def to_ary
indexes.
This method returns all menu items to be displayed, complete with
Allows Menu objects to pass as Arrays, for use with HighLine.list().
def to_ary @items.map.with_index { |item, ix| decorate_item(item.text.to_s, ix) } end
def to_s
_layout_ to be rendered, which is used by HighLine.say().
Allows Menu to behave as a String, just like Question. Returns the
def to_s case @layout when :list %(<%= header ? "#{header}:\n" : '' %>) + parse_list + show_default_if_any + "<%= prompt %>" when :one_line %(<%= header ? "#{header}: " : '' %>) + "<%= prompt %>" \ "(" + parse_list + ")" + show_default_if_any + "<%= prompt[/\s*$/] %>" when :menu_only parse_list + show_default_if_any + "<%= prompt %>" else @layout end end
def update_responses
'options' will be used to populate choice lists.
build_responses method, overriding its default arguments to specify
Menu specific differences. Calls the superclass' (Question's)
This method will update the intelligent responses to account for
def update_responses build_responses(options) end
def value_for_array_selections(items, selections, details)
def value_for_array_selections(items, selections, details) # Find the selected items and return values selected_items = selections.map do |selection| find_item_from_selection(items, selection) end index = 0 selected_items.map do |selected_item| value = value_for_selected_item(selected_item, self.shell ? details[index] : nil) index += 1 value end end
def value_for_hash_selections(items, selections, details)
def value_for_hash_selections(items, selections, details) # Find the selected items and return in hash form index = 0 selections.each_with_object({}) do |(key, selection), memo| selected_item = find_item_from_selection(items, selection) value = value_for_selected_item(selected_item, self.shell ? details[index] : nil) index += 1 memo[key] = value end end
def value_for_selected_item(item, details)
def value_for_selected_item(item, details) if item.action result = if @shell item.action.call(item.name, details) else item.action.call(item.name) end @nil_on_handled ? nil : result else item.name end end