Class: Jsus::Container

Inherits:
Object
  • Object
show all
Defined in:
lib/jsus/container.rb

Overview

Container is an array that contains source files. Main difference from an array is the fact that container maintains topological sort for the source files.

This class is mostly used internally.

Constant Summary collapse

CACHE_CLEAR_METHODS =

List of methods that clear cached state of container when called.

[
  "map!", "reject!", "inject!", "collect!", "delete", "delete_at"
]
DELEGATED_METHODS =

List of methods that are delegated to underlying array of sources.

[
  "==", "to_a", "map", "map!", "each", "inject", "inject!",
  "collect", "collect!", "reject", "reject!", "detect", "size",
  "length", "[]", "empty?", "index", "include?", "select",
  "delete_if", "delete", "-", "+", "|", "&"
]

Instance Method Summary collapse

Constructor Details

#initialize(*sources) ⇒ Container

Instantiates a container from given sources.

Parameters:



13
14
15
16
17
# File 'lib/jsus/container.rb', line 13

def initialize(*sources)
  sources.each do |source|
    push(source)
  end
end

Instance Method Details

#clear_cache!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Clears all caches for given container.



221
222
223
224
225
226
# File 'lib/jsus/container.rb', line 221

def clear_cache!
  @provides_tree = nil
  @replacements_tree = nil
  @dependency_cache = nil
  @sorted = false
end

#dependency_cacheHash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Cached map of dependencies pointing to source files.

Returns:

  • (Hash)


154
155
156
# File 'lib/jsus/container.rb', line 154

def dependency_cache
  @dependency_cache ||= {}
end

#flattenArray

Flattens the container items

Returns:

  • (Array)


40
41
42
# File 'lib/jsus/container.rb', line 40

def flatten
  map {|item| item.respond_to?(:flatten) ? item.flatten : item }.flatten
end

#inspectObject

Shows inspection of the container.



105
106
107
# File 'lib/jsus/container.rb', line 105

def inspect
  "#<#{self.class.name}:#{self.object_id} #{self.sources.inspect}>"
end

#provides_treeJsus::Util::Tree

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Cached tree of what source files provide.

Returns:



162
163
164
# File 'lib/jsus/container.rb', line 162

def provides_tree
  @provides_tree ||= provides_tree!
end

#provides_tree!Jsus::Util::Tree

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns tree of what source files provide.

Returns:



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/jsus/container.rb', line 170

def provides_tree!
  tree = Util::Tree.new
  # Provisions
  sources.each do |file|
    file.provides.each do |tag|
      tree[tag] = file
    end
  end
  # Replacements
  sources.each do |file|
    if file.replaces
      tree[file.replaces] = file
    end
  end
  tree
end

#push(source) ⇒ Object Also known as: <<

Pushes an item to the container

Parameters:



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/jsus/container.rb', line 24

def push(source)
  if source
    if source.kind_of?(Array) || source.kind_of?(Container)
      source.each {|s| self.push(s) }
    else
      sources.push(source) unless sources.include?(source)
    end
  end
  clear_cache!
  self
end

#remove_replaced_files!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Removes files which are marked as replaced by other sources.



190
191
192
193
194
# File 'lib/jsus/container.rb', line 190

def remove_replaced_files!
  sources.reject! do |sf|
    !sf.provides.empty? && sf.provides.any? { |tag| replacements_tree[tag] && replacements_tree[tag] != sf }
  end
end

#replacements_treeJsus::Util::Tree

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Cached tree of what source files replace.

Returns:



200
201
202
# File 'lib/jsus/container.rb', line 200

def replacements_tree
  @replacements_tree ||= replacements_tree!
end

#replacements_tree!Jsus::Util::Tree

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns tree of what source files replace.

Returns:



208
209
210
211
212
213
214
215
216
# File 'lib/jsus/container.rb', line 208

def replacements_tree!
  tree = Util::Tree.new
  sources.each do |file|
    if file.replaces
      tree[file.replaces] = file
    end
  end
  tree
end

#required_files(root = nil) ⇒ Array

Lists all the required files (dependencies and extensions) for the sources in the container. Consider it a projection from source files space onto filesystem space.

Optionally accepts a filesystem point to calculate relative paths from.

Parameters:

  • root (String) (defaults to: nil)

    root point from which the relative paths are calculated. When omitted, full paths are returned.

Returns:

  • (Array)

    ordered list of required files



93
94
95
96
97
98
99
100
101
# File 'lib/jsus/container.rb', line 93

def required_files(root = nil)
  sort!
  files = sources.map {|s| s.required_files }.flatten
  if root
    root = Pathname.new(File.expand_path(root))
    files = files.map {|f| Pathname.new(File.expand_path(f)).relative_path_from(root).to_s }
  end
  files
end

#sort!self

Topologically sorts items in container if required.

Returns:

  • (self)


66
67
68
69
70
71
72
73
# File 'lib/jsus/container.rb', line 66

def sort!
  unless sorted?
    remove_replaced_files!
    self.sources = topsort
    @sorted = true
  end
  self
end

#sorted?Boolean

Returns whether container requires sorting.

Returns:

  • (Boolean)


79
80
81
# File 'lib/jsus/container.rb', line 79

def sorted?
  !!@sorted
end

#sourcesArray Also known as: to_a

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.

Returns:

  • (Array)


50
51
52
# File 'lib/jsus/container.rb', line 50

def sources
  @sources ||= []
end

#sources=(new_value) ⇒ Object

Sets sources to new value.



58
59
60
# File 'lib/jsus/container.rb', line 58

def sources=(new_value) # :nodoc:
  @sources = new_value
end

#topsortObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Performs topological sort inside current container.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/jsus/container.rb', line 114

def topsort
  graph = RGL::DirectedAdjacencyGraph.new
  # init vertices
  items = 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...
      # (dependency could be a wildcard as well, hence items)
      dependency_cache[dependency] ||= provides_tree.glob(dependency)
      # ... we draw an edge from every required item to the dependant item
      dependency_cache[dependency].each do |required_item|
        graph.add_edge(required_item, item)
      end
    end
  end
  result = []
  if Jsus.look_for_cycles?
    cycles = graph.cycles
    error_msg = []
    unless cycles.empty?
      error_msg << "Jsus has discovered you have circular dependencies in your code."
      error_msg << "Please resolve them immediately!"
      error_msg << "List of circular dependencies:"
      cycles.each do |cycle|
        error_msg << "-" * 30
        error_msg << (cycle + [cycle.first]).map {|sf| sf.filename}.join(" => ")
      end
      error_msg = error_msg.join("\n")
      Jsus.logger.fatal(error_msg)
    end
  end
  graph.topsort_iterator.each { |item| result << item }
  result
end