# # Container is an array which contains source files. Main difference # between it and array is the fact that container maintains topological # sort for the source files. # module Jsus class Container # # Every argument for initializer is pushed into the container. # def initialize(*sources) sources.each do |source| self << source end end # Public API # # Pushes an item to container and sorts container. # # When given an array or another container, enumerates its contents. # def <<(source) push(source) sort! end # Pushes an item to container *without sorting it*. Use with care. def push(source) if source if source.kind_of?(Array) || source.kind_of?(Container) source.each {|s| self.push(s) } else sources.push(source) end end self end # Flattens the container items. def flatten map {|item| item.respond_to?(:flatten) ? item.flatten : item }.flatten end # Contains the source files in the correct order. def sources @sources ||= [] end alias_method :to_a, :sources def sources=(new_value) # :nodoc: @sources = new_value end # Performs a sort and returns self. Executed automatically by #<< . def sort! self.sources = topsort self end def inspect # :nodoc: "#<#{self.class.name}:#{self.object_id} #{self.sources.inspect}>" end # Private API def topsort # :nodoc: graph = RGL::DirectedAdjacencyGraph.new provides_map = {} # init vertices items = self.sources items.each do |item| graph.add_vertex(item) item.provides.each do |provides| provides_map[provides] = item end end # init edges items.each do |item| item.dependencies.each do |dependency| if required_item = provides_map[dependency] graph.add_edge(required_item, item) end end end result = [] graph.topsort_iterator.each { |item| result << item } result end DELEGATED_METHODS = [ "==", "map", "map!", "each", "inject", "inject!", "reject", "reject!", "detect", "size", "length", "[]", "empty?", "index", "include?", "select", "-", "+", "|", "&" ] # :nodoc: # delegates most Enumerable methods to #sources (DELEGATED_METHODS).each {|m| delegate m, :to => :sources } end end