#--
# Author:: Daniel DeLeo (<dan@chef.io>)
# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require_relative"text_formatter"classChefclassKnifemoduleCore# Allows includer knife commands to return multiple attributes
# @brief knife node show NAME -a ATTR1 -a ATTR2
moduleMultiAttributeReturnOption# @private
defself.included(includer)includer.class_evaldooption:field_separator,short: "-S SEPARATOR",long: "--field-separator SEPARATOR",description: "Character separator used to delineate nesting in --attribute filters (default \".\")"option:attribute,short: "-a ATTR1 [-a ATTR2]",long: "--attribute ATTR1 [--attribute ATTR2] ",description: "Show one or more attributes",proc: Proc.new{|arg,accumulator|accumulator||=[]accumulator<<argaccumulator}endendend# The base presenter class for displaying structured data in knife commands.
# This is not an abstract base class, and it is suitable for displaying
# most kinds of objects that knife needs to display.
classGenericPresenterattr_reader:uiattr_reader:config# Instantiates a new GenericPresenter. This is generally handled by the
# Chef::Knife::UI object, though you need to match the signature of this
# method if you intend to use your own presenter instead.
definitialize(ui,config)@ui,@config=ui,configend# Is the selected output format a data interchange format?
# Returns true if the selected output format is json or yaml, false
# otherwise. Knife search uses this to adjust its data output so as not
# to produce invalid JSON output.
definterchange?caseparse_format_optionwhen:json,:yamltrueelsefalseendend# Returns a String representation of +data+ that is suitable for output
# to a terminal or perhaps for data interchange with another program.
# The representation of the +data+ depends on the value of the
# `config[:format]` setting.
defformat(data)caseparse_format_optionwhen:summarysummarize(data)when:texttext_format(data)when:jsonChef::JSONCompat.to_json_pretty(data)when:yamlrequire"yaml"unlessdefined?(YAML)YAML.dump(data)when:pprequire"stringio"unlessdefined?(StringIO)# If you were looking for some attribute and there is only one match
# just dump the attribute value
ifconfig[:attribute]&&data.length==1data.values[0]elseout=StringIO.newPP.pp(data,out)out.stringendendend# Converts the user-supplied value of `config[:format]` to a Symbol
# representing the desired output format.
# ===Returns
# returns one of :summary, :text, :json, :yaml, or :pp
# ===Raises
# Raises an ArgumentError if the desired output format could not be
# determined from the value of `config[:format]`
defparse_format_optioncaseconfig[:format]when"summary",/^s/,nil:summarywhen"text",/^t/:textwhen"json",/^j/:jsonwhen"yaml",/^y/:yamlwhen"pp",/^p/:ppelseraiseArgumentError,"Unknown output format #{config[:format]}"endend# Summarize the data. Defaults to text format output,
# which may not be very summary-like
defsummarize(data)text_format(data)end# Converts the +data+ to a String in the text format. Uses
# Chef::Knife::Core::TextFormatter
deftext_format(data)TextFormatter.new(data,ui).formatted_dataenddefformat_list_for_display(list)config[:with_uri]?list:list.keys.sortenddefformat_for_display(data)ifformatting_subset_of_data?format_data_subset_for_display(data)elsifconfig[:id_only]name_or_id_for(data)elsifconfig[:environment]&&data.respond_to?(:chef_environment){"chef_environment"=>data.chef_environment}elsedataendenddefformat_data_subset_for_display(data)subset=ifconfig[:attribute]result={}Array(config[:attribute]).eachdo|nested_value_spec|nested_value=extract_nested_value(data,nested_value_spec)result[nested_value_spec]=nested_valueendresultelsifconfig[:run_list]run_list=data.run_list.run_list{"run_list"=>run_list}elseraiseArgumentError,"format_data_subset_for_display requires attribute, run_list, or id_only config option to be set"end{name_or_id_for(data)=>subset}enddefname_or_id_for(data)data.respond_to?(:name)?data.name:data["id"]enddefformatting_subset_of_data?config[:attribute]||config[:run_list]end# GenericPresenter is used in contexts where MultiAttributeReturnOption
# is not, so we need to set the default value here rather than as part
# of the CLI option.
defattribute_field_separatorconfig[:field_separator]||"."enddefextract_nested_value(data,nested_value_spec)nested_value_spec.split(attribute_field_separator).eachdo|attr|data=ifdata.is_a?(Array)data[attr.to_i]elsifdata.respond_to?(:[],false)&&data.respond_to?(:key?)&&data.key?(attr)data[attr]elsifdata.respond_to?(attr.to_sym,false)# handles -a chef_environment and other things that hang of the node and aren't really attributes
data.public_send(attr.to_sym)elsenilendend# necessary (?) for coercing objects (the run_list object?) to hashes
(!data.is_a?(Array)&&data.respond_to?(:to_hash))?data.to_hash:dataenddefformat_cookbook_list_for_display(item)versions_by_cookbook=item.inject({})do|collected,(cookbook,versions)|ifconfig[:with_uri]collected[cookbook]={}versions["versions"].eachdo|ver|collected[cookbook][ver["version"]]=ver["url"]endelsecollected[cookbook]=versions["versions"].map{|v|v["version"]}endcollected.sort.to_hendifconfig[:with_uri]versions_by_cookbookelsecaseparse_format_optionwhen:summarycookbooks={}versions_by_cookbook.mapdo|cookbook,versions|cookbooks[cookbook]=versions.join(" ")endcookbookselseversions_by_cookbookendendendendendendend