require 'sfn' require 'bogo-config' module Sfn # Top level configuration class Config < Bogo::Config # Override attribute helper to detect Hash types and automatically # add type conversion for CLI provided values + description update # # @param name [String, Symbol] name of attribute # @param type [Class, Array] valid types # @param info [Hash] attribute information # @return [Hash] def self.attribute(name, type, info = Smash.new) if [type].flatten.any? { |t| t.ancestors.include?(Hash) } unless info[:coerce] info[:coerce] = lambda do |v| case v when String Smash[ v.split(/,(?=[^,]*:)/).map do |item_pair| item_pair.split(/[=:]/, 2) end ] when Hash v.to_smash else v end end info[:description] ||= '' info[:description] << ' (Key:Value[,Key:Value,...])' end end super(name, type, info) end # Only values allowed designating bool type BOOLEAN_VALUES = [TrueClass, FalseClass] autoload :Conf, 'sfn/config/conf' autoload :Create, 'sfn/config/create' autoload :Describe, 'sfn/config/describe' autoload :Destroy, 'sfn/config/destroy' autoload :Describe, 'sfn/config/describe' autoload :Diff, 'sfn/config/diff' autoload :Events, 'sfn/config/events' autoload :Export, 'sfn/config/export' autoload :Graph, 'sfn/config/graph' autoload :Import, 'sfn/config/import' autoload :Init, 'sfn/config/init' autoload :Inspect, 'sfn/config/inspect' autoload :Lint, 'sfn/config/lint' autoload :List, 'sfn/config/list' autoload :Print, 'sfn/config/print' autoload :Promote, 'sfn/config/promote' autoload :Update, 'sfn/config/update' autoload :Validate, 'sfn/config/validate' attribute( :config, String, :description => 'Configuration file path', :short_flag => 'c', ) attribute( :credentials, Smash, :description => 'Provider credentials', :short_flag => 'C', ) attribute( :ignore_parameters, String, :multiple => true, :description => 'Parameters to ignore during modifications', :short_flag => 'i', ) attribute( :interactive_parameters, [TrueClass, FalseClass], :default => true, :description => 'Prompt for template parameters', :short_flag => 'I', ) attribute( :poll, [TrueClass, FalseClass], :default => true, :description => 'Poll stack events on modification actions', :short_flag => 'p', ) attribute( :defaults, [TrueClass, FalseClass], :description => 'Automatically accept default values', :short_flag => 'd', ) attribute( :yes, [TrueClass, FalseClass], :description => 'Automatically accept any requests for confirmation', :short_flag => 'y', ) attribute( :debug, [TrueClass, FalseClass], :description => 'Enable debug output', :short_flag => 'u', ) attribute( :colors, [TrueClass, FalseClass], :description => 'Enable colorized output', :default => true, ) attribute :conf, Conf, :coerce => proc { |v| Conf.new(v) } attribute :create, Create, :coerce => proc { |v| Create.new(v) } attribute :update, Update, :coerce => proc { |v| Update.new(v) } attribute :destroy, Destroy, :coerce => proc { |v| Destroy.new(v) } attribute :events, Events, :coerce => proc { |v| Events.new(v) } attribute :export, Export, :coerce => proc { |v| Export.new(v) } attribute :import, Import, :coerce => proc { |v| Import.new(v) } attribute :inspect, Inspect, :coerce => proc { |v| Inpsect.new(v) } attribute :describe, Describe, :coerce => proc { |v| Describe.new(v) } attribute :list, List, :coerce => proc { |v| List.new(v) } attribute :promote, Promote, :coerce => proc { |v| Promote.new(v) } attribute :validate, Validate, :coerce => proc { |v| Validate.new(v) } # Provide all options for config class (includes global configs) # # @param klass [Class] # @return [Smash] def self.options_for(klass) shorts = ['h'] # always reserve `-h` for help _options_for(klass, shorts) end # Provide options for config class # # @param klass [Class] # @param shorts [Array] # @return [Smash] def self._options_for(klass, shorts) Smash[ ([klass] + klass.ancestors).map do |a| if a.ancestors.include?(Bogo::Config) && !a.attributes.empty? a.attributes end end.compact.reverse.inject(Smash.new) { |m, n| m.deep_merge(n) }.map do |name, info| next unless info[:description] short = info[:short_flag] if !short.to_s.empty? && shorts.include?(short) raise ArgumentError.new "Short flag already in use! (`#{short}` not available for `#{klass}`)" end unless short.to_s.empty? shorts << short info[:short] = short end info[:long] = name.tr('_', '-') info[:boolean] = [info[:type]].compact.flatten.all? { |t| BOOLEAN_VALUES.include?(t) } [name, info] end.compact ] end end end