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