require 'r10k/settings/helpers' require 'r10k/settings/collection' require 'r10k/errors' require 'r10k/util/setopts' module R10K module Settings # A container for an arbitrarily long list of other settings. class List include R10K::Settings::Helpers include R10K::Util::Setopts # @!attribute [r] name # @return [String] The name of this collection attr_reader :name # @param name [Symbol] The name of the setting for this definition. # @param item_proc [#call] An object whose #call method will return a # new instance of another R10K::Settings class to hold each item # added to this list. # @param opts [Hash] Additional options for this definition to control # validation, normalization, and the like. # # @options opts [String] :desc Extended description of this setting. # @options opts [Array] :default Initial/default contents of the list. def initialize(name, item_proc, opts = {}) @name = name @item_proc = item_proc @items = [] setopts(opts, allowed_initialize_opts) end # Takes an array of key/value pairs and assigns each into a # new instance created by invoking @item_proc. # # @param items [Array] List of items to add to this list. def assign(items) return if items.nil? items.each do |values| new_item = @item_proc.call new_item.parent = self new_item.assign(values) @items << new_item end end # Validate all items in the list and return validation errors # # @return [nil, Hash] If all validation passed nil will be returned; if # validation failed then a hash of those errors will be returned. def validate errors = {} @items.each_with_index do |item, idx| begin item.validate rescue => error errors[idx+1] = error end end if !errors.empty? raise ValidationError.new(_("Validation failed for '%{name}' settings list") % {name: @name}, :errors => errors) end end # Evaluate all items in the list and return a frozen array of the final values. # @return [Array] def resolve @items.collect { |item| item.resolve }.freeze end class ValidationError < R10K::Error attr_reader :errors def initialize(mesg, options = {}) super @errors = options[:errors] end def format struct = [] struct << "#{message}:" @errors.each do |item, error| struct << indent(structure_exception("Item #{item}", error)) end struct.join("\n") end end private # Subclasses may define additional params that are accepted at # initialization; they should override this method to add any # additional fields that should be respected. def allowed_initialize_opts { :desc => true, :default => true, } end end end end