jsdm 0.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ require 'fileutils'
2
+ require 'rake'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/rdoctask'
5
+
6
+ spec = Gem::Specification.new do |s|
7
+ s.platform = Gem::Platform::RUBY
8
+ s.name = "jsdm"
9
+ s.author = "Min Huang"
10
+ s.email = "min.huang@alumni.usc.edu"
11
+ s.files = Dir["lib/**/*"] + %w(Rakefile)
12
+ s.require_path = "lib"
13
+ s.has_rdoc = true
14
+ s.homepage = "http://www.borderstylo.com/#{s.name}"
15
+ s.version = "0.2.20"
16
+ s.summary = "Javascript dependency manager"
17
+ s.description = "Use #require statements to declare dependencies"
18
+ end
19
+
20
+ Rake::GemPackageTask.new(spec) do |pkg|
21
+ pkg.need_tar_bz2 = true
22
+ end
23
+
24
+ desc "Clean the build"
25
+ task :clean do
26
+ FileUtils.rm_rf "pkg", :verbose => true
27
+ end
28
+
29
+ desc "Run tests (no arg), or single test (with arg)"
30
+ task :test, :name do |t, args|
31
+ opts = args.name.nil? ? "" : "-n test_#{args.name}"
32
+ cmd = "ruby test/run_tests.rb #{opts}"
33
+ puts cmd
34
+ system(cmd) || raise("Build error")
35
+ end
36
+
37
+ task :install => [:test, :package] do
38
+ g = "pkg/#{spec.name}-#{spec.version}.gem"
39
+ system "sudo gem install -l #{g}"
40
+ end
41
+
42
+ task :default => :test
@@ -0,0 +1,42 @@
1
+ require 'jsdm'
2
+ require 'jsdm/depth_first_search'
3
+ require 'jsdm/directed_graph'
4
+ require 'jsdm/natural_loops'
5
+ require 'jsdm/errors'
6
+
7
+ class JSDM
8
+ class DependencyManager
9
+ attr_accessor :sources, :dependencies, :graph
10
+
11
+ def initialize(sources)
12
+ self.sources = sources
13
+ self.dependencies = []
14
+ self.graph = nil
15
+ end
16
+
17
+ def add_dependency(source, dependency)
18
+ unless JSDM.same_file? dependency, source
19
+ dependencies << [dependency, source]
20
+ end
21
+ end
22
+
23
+ def dependencies_of(source, acc = [])
24
+ ds = dependencies.select { |d| d.last == source }.
25
+ map { |d| d.first }
26
+ return acc if ds.empty?
27
+ acc = acc | ds
28
+ ds.each { |d| acc = acc | dependencies_of(d, acc) }
29
+ acc
30
+ end
31
+
32
+ def process
33
+ self.sources = sources.uniq.delete_if { |e| e.empty? }
34
+ self.dependencies = dependencies.uniq.delete_if { |e| e.empty? }
35
+ self.graph = DirectedGraph.new sources, dependencies
36
+ result = DepthFirstSearch.dfs graph
37
+ loops = NaturalLoops.find graph, result[:back_edges]
38
+ raise CircularDependencyError.new(loops) unless loops.empty?
39
+ result[:sorted]
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ require 'jsdm/errors'
2
+
3
+ class JSDM
4
+ class DependencyResolver
5
+ attr_accessor :load_path
6
+
7
+ def initialize(load_path)
8
+ self.load_path = load_path
9
+ end
10
+
11
+ def process(entries)
12
+ entries = entries.map { |entry| process_single entry }
13
+ entries = entries.flatten
14
+ entries
15
+ end
16
+
17
+ def process_single(entry)
18
+ resolved = load_path.map { |path| Dir["#{path}/#{entry.strip}"] }
19
+ resolved = resolved.drop_while { |sources| sources.empty? }
20
+ resolved = resolved.first
21
+ raise FileNotFoundError.new(entry) if resolved.nil? || resolved.empty?
22
+ resolved
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,77 @@
1
+ require 'set'
2
+
3
+ class JSDM
4
+ class DepthFirstSearch
5
+ def initialize(graph)
6
+ self.graph = graph
7
+ self.discovered_times = Hash.new { |h, k| h[k] = 0 }
8
+ self.finished_times = Hash.new { |h, k| h[k] = 0 }
9
+ self.node_colors = Hash.new { |h, k| h[k] = :white }
10
+ self.edge_colors = Hash.new { |h, k| h[k] = [] }
11
+ self.predecessors = Hash.new
12
+ self.sorted = []
13
+ self.time = 0
14
+ end
15
+
16
+ def process
17
+ graph.nodes.each { |u| visit(u) if node_colors[u] == :white }
18
+ return {
19
+ :discovered_times => discovered_times,
20
+ :finished_times => finished_times,
21
+ :node_colors => node_colors,
22
+ :edge_colors => edge_colors,
23
+ :predecessors => predecessors,
24
+ :sorted => sorted,
25
+ :tree_edges => edge_colors[:white],
26
+ :forward_edges => edge_colors[:black].select do |e|
27
+ t = discovered_times[e.first]
28
+ u = discovered_times[e.last]
29
+ t < u
30
+ end,
31
+ :cross_edges => edge_colors[:black].select do |e|
32
+ t = discovered_times[e.first]
33
+ u = discovered_times[e.last]
34
+ t > u
35
+ end,
36
+ :back_edges => edge_colors[:gray]
37
+ }
38
+ end
39
+
40
+ def visit(u)
41
+ self.time += 1
42
+ discovered_times[u] = time
43
+ node_colors[u] = :gray
44
+ graph.successors(u).each do |v|
45
+ case node_colors[v]
46
+ when :white
47
+ edge_colors[:white] << [u, v]
48
+ predecessors[v] = u
49
+ visit(v)
50
+ when :gray
51
+ edge_colors[:gray] << [u, v]
52
+ else
53
+ edge_colors[:black] << [u, v]
54
+ end
55
+ end
56
+ node_colors[u] = :black
57
+ finished_times[u] = time
58
+ sorted.unshift u
59
+ self.time += 1
60
+ end
61
+
62
+ def self.dfs(graph)
63
+ search = DepthFirstSearch.new(graph)
64
+ search.process
65
+ end
66
+
67
+ protected
68
+ attr_accessor :graph,
69
+ :discovered_times,
70
+ :finished_times,
71
+ :node_colors,
72
+ :edge_colors,
73
+ :predecessors,
74
+ :sorted,
75
+ :time
76
+ end
77
+ end
@@ -0,0 +1,15 @@
1
+ class JSDM
2
+ class DirectedGraph
3
+ attr_accessor :nodes, :arcs
4
+ private :nodes=, :arcs=
5
+
6
+ def initialize(nodes, arcs)
7
+ self.nodes = nodes
8
+ self.arcs = arcs
9
+ end
10
+
11
+ def successors(node)
12
+ arcs.select { |a| a.first == node }.map { |a| a.last }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ class JSDM
2
+ class CircularDependencyError < StandardError
3
+ attr_accessor :deps
4
+ private :deps=
5
+
6
+ def initialize(deps)
7
+ msg = 'The following sets of files are involved in circular ' +
8
+ 'dependencies: ' +
9
+ deps.inspect
10
+ self.deps = deps
11
+ super(msg)
12
+ end
13
+ end
14
+
15
+ class FileNotFoundError < StandardError
16
+ attr_accessor :file
17
+ private :file=
18
+
19
+ def initialize(file)
20
+ super("File not found: #{file}")
21
+ self.file = file
22
+ end
23
+ end
24
+
25
+ class UnsatisfiableDependencyError < StandardError
26
+ attr_accessor :source, :dep
27
+ private :source=, :dep=
28
+
29
+ def initialize(source, dep)
30
+ msg = "File #{source} has unsatisfiable dependency:\n" +
31
+ " #{dep}"
32
+ self.source = source
33
+ self.dep = dep
34
+ super(msg)
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,28 @@
1
+ require 'set'
2
+
3
+ class JSDM
4
+ class NaturalLoops
5
+ def self.find(graph, back_edges)
6
+ loops = Set.new
7
+ back_edges.each do |arc|
8
+ l = [arc.first, arc.last]
9
+ stack = []
10
+ stack.push arc.first if arc.first != arc.last
11
+ while !stack.empty?
12
+ u = stack.pop
13
+ neighbors = graph.arcs.
14
+ select { |a| a.last == u }.
15
+ map { |a| a.first }.each
16
+ neighbors.each do |v|
17
+ if !l.include?(v)
18
+ l << v
19
+ stack.push v
20
+ end
21
+ end
22
+ end
23
+ loops << l.to_set
24
+ end
25
+ loops
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ class JSDM
2
+ class Preprocessor
3
+ attr_accessor :sources, :comment_pattern, :require_pattern
4
+
5
+ def initialize(sources)
6
+ self.sources = sources
7
+ self.comment_pattern = /^\s*\/\/\s*/
8
+ self.require_pattern = /#{comment_pattern}#\s*require\s*/
9
+ end
10
+
11
+ def process_single(source)
12
+ File.open(source).
13
+ each_line.
14
+ take_while { |line| line =~ comment_pattern }.
15
+ select { |line| line =~ require_pattern }.
16
+ map { |line| line.sub!(require_pattern, "").split(",") }.
17
+ flatten.
18
+ map { |entry| entry.strip }
19
+ end
20
+
21
+ def process
22
+ sources.map { |source| [source, process_single(source)] }
23
+ end
24
+ end
25
+ end
data/lib/jsdm.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'jsdm/dependency_manager'
2
+ require 'jsdm/dependency_resolver'
3
+ require 'jsdm/errors'
4
+ require 'jsdm/preprocessor'
5
+
6
+ class JSDM
7
+ attr_accessor :load_path, :sources, :preprocessor, :manager, :resolver, :ext
8
+
9
+ def initialize(load_path = [])
10
+ self.load_path = load_path
11
+ self.sources = []
12
+ self.preprocessor = nil
13
+ self.manager = nil
14
+ self.resolver = nil
15
+ self.ext = 'js'
16
+ process!
17
+ end
18
+
19
+ def process!
20
+ self.sources = load_path.map { |path| Dir["#{path}/**/*.#{ext}"] }.
21
+ flatten.
22
+ sort
23
+ self.sources = sources.sort { rand }
24
+ self.preprocessor = Preprocessor.new sources
25
+ self.manager = DependencyManager.new sources
26
+ self.resolver = DependencyResolver.new load_path
27
+ begin
28
+ source = nil
29
+ preprocessor.process.each do |element|
30
+ source = element.first
31
+ dependencies = resolver.process element.last
32
+ dependencies.each do |dependency|
33
+ manager.add_dependency source, dependency
34
+ end
35
+ end
36
+ rescue FileNotFoundError => e
37
+ raise UnsatisfiableDependencyError.new(source, e.file)
38
+ end
39
+ self.sources = manager.process
40
+ sources
41
+ end
42
+
43
+ def sources_for(source)
44
+ ds = manager.dependencies_of(source)
45
+ ds.push(source)
46
+ ds
47
+ end
48
+
49
+ def dependencies
50
+ manager.dependencies
51
+ end
52
+
53
+ def self.same_file?(a, b)
54
+ File.expand_path(a) == File.expand_path(b)
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jsdm
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 20
9
+ version: 0.2.20
10
+ platform: ruby
11
+ authors:
12
+ - Min Huang
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-31 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: "Use #require statements to declare dependencies"
22
+ email: min.huang@alumni.usc.edu
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/jsdm.rb
31
+ - lib/jsdm/dependency_manager.rb
32
+ - lib/jsdm/errors.rb
33
+ - lib/jsdm/depth_first_search.rb
34
+ - lib/jsdm/preprocessor.rb
35
+ - lib/jsdm/directed_graph.rb
36
+ - lib/jsdm/natural_loops.rb
37
+ - lib/jsdm/dependency_resolver.rb
38
+ - Rakefile
39
+ has_rdoc: true
40
+ homepage: http://www.borderstylo.com/jsdm
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ segments:
53
+ - 0
54
+ version: "0"
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.3.6
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Javascript dependency manager
69
+ test_files: []
70
+