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.



228
229
230
231
232
233
# File 'lib/jsus/container.rb', line 228

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)


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

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

#output_cycles(graph) ⇒ 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.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/jsus/container.rb', line 141

def output_cycles(graph)
  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 << "-" * 30
    error_msg = error_msg.join("\n")
    Jsus.logger.fatal(error_msg)
  end
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:



169
170
171
# File 'lib/jsus/container.rb', line 169

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:



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/jsus/container.rb', line 177

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.



197
198
199
200
201
# File 'lib/jsus/container.rb', line 197

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:



207
208
209
# File 'lib/jsus/container.rb', line 207

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:



215
216
217
218
219
220
221
222
223
# File 'lib/jsus/container.rb', line 215

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
# 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

  begin
    graph.topsorted_vertices
  rescue RGL::TopsortedGraphHasCycles => e
    output_cycles(graph)
    raise e # fail fast
  end
end