lib/jsus/container.rb in jsus-0.3.6 vs lib/jsus/container.rb in jsus-0.4.0

- old
+ new

@@ -9,10 +9,14 @@ class Container # Instantiates a container from given sources. # # @param [*SourceFile] sources def initialize(*sources) + @sources = [] + @normal_sources = [] + @extensions = [] + @replacements = [] sources.each do |source| push(source) end end @@ -21,14 +25,22 @@ # Pushes an item to the container # # @param [SourceFile] source source pushed file def push(source) if source - if source.kind_of?(Array) || source.kind_of?(Container) + if source.kind_of?(Array) source.each {|s| self.push(s) } + elsif source.kind_of?(Container) + source.all_sources.each {|s| self.push(s) } else - sources.push(source) unless sources.include?(source) + if source.extension? + @extensions << source unless @extensions.include?(source) + elsif source.replacement? + @replacements << source unless @replacements.include?(source) + else + @normal_sources << source unless @normal_sources.include?(source) + end end end clear_cache! self end @@ -39,36 +51,39 @@ # @return [Array] def flatten map {|item| item.respond_to?(:flatten) ? item.flatten : item }.flatten end - # Contains the source files. Please, don't use sources directly, if you - # depend on them to be topologically sorted. Use collection methods like - # inject/reject/map directly on the container instead. + # Contains the source files. # # @return [Array] - # @api semipublic + # @api public def sources - @sources ||= [] + sort! + @sources end alias_method :to_a, :sources - # Sets sources to new value. + # Includes all sources, even those that would normally be replaced. + # Without any order. # + # @return [Array] # @api semipublic - def sources=(new_value) # :nodoc: - @sources = new_value - end + def all_sources + @normal_sources + @extensions + @replacements + end # all_sources # Topologically sorts items in container if required. # # @return [self] # @api semipublic def sort! unless sorted? - remove_replaced_files! - self.sources = topsort + @sources = topsort + insert_extensions! + insert_replacements! + @sources.uniq! @sorted = true end self end @@ -104,19 +119,36 @@ # @api public def inspect "#<#{self.class.name}:#{self.object_id} #{self.sources.inspect}>" end + # Returns all the tags provided by source files. + # @return [Array] + # @api public + def provides + sort! + sources.map {|s| s.provides }.flatten + end # provides + + # Returns all the tags required by source files, except for those which are + # provided by other files in the container (i.e. unresolved dependencies) + # @return [Array] + # @api public + def requires + sort! + sources.map {|s| s.requires }.flatten - provides + end # requires + # Private API # Performs topological sort inside current container. # # @api private def topsort graph = RGL::DirectedAdjacencyGraph.new # init vertices - items = sources + items = @normal_sources items.each {|item| graph.add_vertex(item) } # init edges items.each do |item| item.dependencies.each do |dependency| # If we can find items that provide the required dependency... @@ -175,62 +207,51 @@ # @api private # @return [Jsus::Util::Tree] def provides_tree! tree = Util::Tree.new # Provisions - sources.each do |file| - file.provides.each do |tag| + @normal_sources.each do |file| + provisions = file.provides + if replacement = @replacements.detect {|r| provisions.any? {|tag| tag == r.replaces } } + file = replacement + end + provisions.each do |tag| tree[tag] = file end end - # Replacements - sources.each do |file| - if file.replaces - tree[file.replaces] = file - end - end tree end - # Removes files which are marked as replaced by other sources. - # # @api private - def remove_replaced_files! - sources.reject! do |sf| - !sf.provides.empty? && sf.provides.any? { |tag| replacements_tree[tag] && replacements_tree[tag] != sf } + def insert_extensions! + @extensions.each do |ext| + ext_tag = ext.extends + @sources.dup.each_with_index do |src, i| + if src.provides.any? {|tag| tag == ext_tag } + @sources.insert(i+1, ext) + break + end + end end - end + end # insert_extensions! - # Cached tree of what source files replace. - # # @api private - # @return [Jsus::Util::Tree] - def replacements_tree - @replacements_tree ||= replacements_tree! - end - - # Returns tree of what source files replace. - # - # @api private - # @return [Jsus::Util::Tree] - def replacements_tree! - tree = Util::Tree.new - sources.each do |file| - if file.replaces - tree[file.replaces] = file + def insert_replacements! + @replacements.each do |repl| + @sources.each_with_index do |src, i| + if src.provides.any? {|tag| tag == repl.replaces } + @sources[i] = repl + break + end end end - tree - end + end # insert_replacements! # Clears all caches for given container. # # @api private def clear_cache! - @provides_tree = nil - @replacements_tree = nil - @dependency_cache = nil @sorted = false end # List of methods that clear cached state of container when called. CACHE_CLEAR_METHODS = [ @@ -244,12 +265,11 @@ "length", "[]", "empty?", "index", "include?", "select", "delete_if", "delete", "-", "+", "|", "&" ] (DELEGATED_METHODS).each do |m| - class_eval <<-EVAL + class_eval(<<-EVAL, __FILE__, __LINE__ + 1) def #{m}(*args, &block) - sort! #{"clear_cache!" if CACHE_CLEAR_METHODS.include?(m)} self.sources.send(:#{m}, *args, &block) end EVAL end