require 'tap/support/assignments' require 'tap/support/instance_configuration' require 'tap/support/configuration' module Tap module Support # ClassConfiguration tracks and handles the class configurations defined in a # Configurable class. class ClassConfiguration include Enumerable # The class receiving new configurations attr_reader :receiver # Tracks the assignment of the config keys to receivers attr_reader :assignments # A map of (key, config) pairs. attr_reader :map def initialize(receiver, parent=nil) @receiver = receiver if parent != nil @map = parent.map.inject({}) do |hash, (key, config)| hash[key] = config.dup hash end @assignments = Assignments.new(parent.assignments) else @map = {} @assignments = Assignments.new end end # Initializes a Configuration using the inputs and sets it in self # using name as a key, overriding the current config by that name, # if it exists. Returns the new config. def add(name, default=nil, attributes={}) self[name] = Configuration.new(name.to_sym, default, attributes) end # Removes the specified configuration. def remove(key) self[key] = nil end # Gets the configuration specified by key. The key is symbolized. def [](key) map[key.to_sym] end # Assigns the configuration to key. A nil config unassigns the # configuration key. The key is symbolized. def []=(key, config) key = key.to_sym if config == nil assignments.unassign(key) map.delete(key) else assignments.assign(receiver, key) unless assignments.assigned?(key) map[key] = config end end # Returns true if key is a config key. def key?(key) map.has_key?(key) end # Returns all config keys. def keys map.keys end # Returns config keys in order. def ordered_keys assignments.values end # Returns all mapped configs. def values map.values end # True if map is empty. def empty? map.empty? end # Calls block once for each [receiver, key, config] in self, # passing those elements as parameters, in the order in # which they were assigned. def each assignments.each do |receiver, key| yield(receiver, key, map[key]) end end # Calls block once for each [key, config] pair in self, # passing those elements as parameters, in the order in # which they were assigned. def each_pair assignments.each do |receiver, key| yield(key, map[key]) end end # Initializes and returns a new InstanceConfiguration set to self # and bound to the receiver, if specified. def instance_config(receiver=nil, store={}) InstanceConfiguration.new(self, receiver, store) end # Returns a hash of the (key, config.default) values in self. def to_hash hash = {} each_pair {|key, config| hash[key] = config.default } hash end # An array of config descriptions that are Comment objects. def code_comments code_comments = [] values.each do |config| code_comments << config.desc if config.desc.kind_of?(Comment) end code_comments end # The path to the :doc template (see format_str) DOC_TEMPLATE_PATH = File.expand_path File.dirname(__FILE__) + "/../generator/generators/config/templates/doc.erb" # The path to the :nodoc template (see format_str) NODOC_TEMPLATE_PATH = File.expand_path File.dirname(__FILE__) + "/../generator/generators/config/templates/nodoc.erb" # Formats the configurations using the specified template. Two default # templates are defined, :doc and :nodoc. These map # to the contents of DOC_TEMPLATE_PATH and NODOC_TEMPLATE_PATH and # correspond to the documented and undocumented config generator templates. # # == Custom Templates # # format_str initializes a Templater which formats each [receiver, configurations] # pair in turn, and puts the output to the target using <<. The # templater is assigned the following attributes for use in formatting: # # receiver:: The receiver # configurations:: An array of configurations and associated comments # # In the template these can be accessed as any ERB locals, for example: # # <%= receiver.to_s %> # <% configurations.each do |key, config, comment| %> # ... # <% end %> # # The input template may be a String or an ERB; either may be used to # initialize the templater. def format_str(template=:doc, target="") Lazydoc.resolve_comments(code_comments) template = case template when :doc then File.read(DOC_TEMPLATE_PATH) when :nodoc then File.read(NODOC_TEMPLATE_PATH) else template end templater = Templater.new(template) assignments.each_pair do |receiver, keys| next if keys.empty? # set the template attributes templater.receiver = receiver templater.configurations = keys.collect do |key| # duplicate config so that any changes to it # during templation will not propogate back # into self [key, map[key].dup] end.compact yield(templater) if block_given? target << templater.build end target end end end end