sprockets 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sprockets might be problematic. Click here for more details.

Files changed (37) hide show
  1. data/Rakefile +16 -0
  2. data/bin/sprocketize +54 -0
  3. data/lib/sprockets.rb +14 -0
  4. data/lib/sprockets/concatenation.rb +36 -0
  5. data/lib/sprockets/environment.rb +56 -0
  6. data/lib/sprockets/error.rb +5 -0
  7. data/lib/sprockets/pathname.rb +37 -0
  8. data/lib/sprockets/preprocessor.rb +109 -0
  9. data/lib/sprockets/secretary.rb +106 -0
  10. data/lib/sprockets/source_file.rb +32 -0
  11. data/lib/sprockets/source_line.rb +58 -0
  12. data/lib/sprockets/version.rb +9 -0
  13. data/test/fixtures/assets/images/script_with_assets/one.png +1 -0
  14. data/test/fixtures/assets/images/script_with_assets/two.png +1 -0
  15. data/test/fixtures/assets/stylesheets/script_with_assets.css +1 -0
  16. data/test/fixtures/constants.yml +1 -0
  17. data/test/fixtures/double_slash_comments_that_are_not_requires_should_be_ignored_when_strip_comments_is_false.js +2 -0
  18. data/test/fixtures/double_slash_comments_that_are_not_requires_should_be_removed_by_default.js +2 -0
  19. data/test/fixtures/multiline_comments_should_be_removed_by_default.js +4 -0
  20. data/test/fixtures/requiring_a_file_after_it_has_already_been_required_should_do_nothing.js +5 -0
  21. data/test/fixtures/requiring_a_file_that_does_not_exist_should_raise_an_error.js +1 -0
  22. data/test/fixtures/requiring_a_single_file_should_replace_the_require_comment_with_the_file_contents.js +3 -0
  23. data/test/fixtures/requiring_the_current_file_should_do_nothing.js +1 -0
  24. data/test/fixtures/src/constants.yml +3 -0
  25. data/test/fixtures/src/foo.js +1 -0
  26. data/test/fixtures/src/foo/bar.js +4 -0
  27. data/test/fixtures/src/foo/foo.js +1 -0
  28. data/test/fixtures/src/script_with_assets.js +3 -0
  29. data/test/test_concatenation.rb +28 -0
  30. data/test/test_environment.rb +64 -0
  31. data/test/test_helper.rb +55 -0
  32. data/test/test_pathname.rb +43 -0
  33. data/test/test_preprocessor.rb +101 -0
  34. data/test/test_secretary.rb +83 -0
  35. data/test/test_source_file.rb +29 -0
  36. data/test/test_source_line.rb +75 -0
  37. metadata +103 -0
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require "rubygems"
2
+ require "rake/testtask"
3
+ require "rake/gempackagetask"
4
+
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "test"
9
+ t.test_files = FileList["test/test_*.rb"]
10
+ t.verbose = true
11
+ end
12
+
13
+ Rake::GemPackageTask.new(eval(IO.read(File.join(File.dirname(__FILE__), "sprockets.gemspec")))) do |pkg|
14
+ pkg.need_zip = true
15
+ pkg.need_tar = true
16
+ end
data/bin/sprocketize ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), *%w".. lib sprockets")
4
+ require "optparse"
5
+
6
+ load_path = (ENV["SPROCKETS_PATH"] || "").split(":")
7
+ filenames = []
8
+ options = { :load_path => load_path, :source_files => filenames, :expand_paths => false }
9
+
10
+ OptionParser.new do |opts|
11
+ opts.summary_width = 28
12
+ opts.banner = "Usage: sprocketize [options] filename [filename ...]"
13
+
14
+ def opts.show_usage
15
+ puts self
16
+ exit
17
+ end
18
+
19
+ opts.on("-C DIRECTORY", "--directory=DIRECTORY", "Change to DIRECTORY before doing anything") do |directory|
20
+ Dir.chdir(directory)
21
+ end
22
+
23
+ opts.on("-I DIRECTORY", "--include-dir=DIRECTORY", "Adds the directory to the Sprockets load path") do |directory|
24
+ load_path << directory
25
+ end
26
+
27
+ opts.on("-a DIRECTORY", "--asset-root=DIRECTORY", "Copy provided assets into DIRECTORY") do |directory|
28
+ options[:asset_root] = directory
29
+ end
30
+
31
+ opts.on_tail("-h", "--help", "Shows this help message") do
32
+ opts.show_usage
33
+ end
34
+
35
+ opts.on_tail("-v", "--version", "Shows version") do
36
+ puts Sprockets::Version::STRING
37
+ exit
38
+ end
39
+
40
+ opts.show_usage if ARGV.empty?
41
+
42
+ begin
43
+ opts.order(ARGV) do |filename|
44
+ filenames << filename
45
+ end
46
+ rescue OptionParser::ParseError => e
47
+ opts.warn e.message
48
+ opts.show_usage
49
+ end
50
+ end
51
+
52
+ secretary = Sprockets::Secretary.new(options)
53
+ secretary.install_assets if options[:asset_root]
54
+ print secretary.concatenation
data/lib/sprockets.rb ADDED
@@ -0,0 +1,14 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require "yaml"
4
+ require "fileutils"
5
+
6
+ require "sprockets/version"
7
+ require "sprockets/error"
8
+ require "sprockets/environment"
9
+ require "sprockets/pathname"
10
+ require "sprockets/source_line"
11
+ require "sprockets/source_file"
12
+ require "sprockets/concatenation"
13
+ require "sprockets/preprocessor"
14
+ require "sprockets/secretary"
@@ -0,0 +1,36 @@
1
+ module Sprockets
2
+ class Concatenation
3
+ attr_reader :source_lines
4
+
5
+ def initialize
6
+ @source_lines = []
7
+ @source_file_mtimes = {}
8
+ end
9
+
10
+ def record(source_line)
11
+ source_lines << source_line
12
+ record_mtime_for(source_line.source_file)
13
+ source_line
14
+ end
15
+
16
+ def to_s
17
+ source_lines.join
18
+ end
19
+
20
+ def mtime
21
+ @source_file_mtimes.values.max
22
+ end
23
+
24
+ def save_to(filename)
25
+ timestamp = mtime
26
+ File.open(filename, "w") { |file| file.write(to_s) }
27
+ File.utime(timestamp, timestamp, filename)
28
+ true
29
+ end
30
+
31
+ protected
32
+ def record_mtime_for(source_file)
33
+ @source_file_mtimes[source_file] ||= source_file.mtime
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,56 @@
1
+ module Sprockets
2
+ class Environment
3
+ attr_reader :root, :load_path
4
+
5
+ def initialize(root, load_path = [])
6
+ @load_path = [@root = Pathname.new(self, root)]
7
+
8
+ load_path.reverse_each do |location|
9
+ register_load_location(location)
10
+ end
11
+ end
12
+
13
+ def pathname_from(location)
14
+ Pathname.new(self, absolute_location_from(location))
15
+ end
16
+
17
+ def register_load_location(location)
18
+ pathname = pathname_from(location)
19
+ load_path.delete(pathname)
20
+ load_path.unshift(pathname)
21
+ location
22
+ end
23
+
24
+ def find(location)
25
+ if absolute?(location) && File.exists?(location)
26
+ pathname_from(location)
27
+ else
28
+ find_all(location).first
29
+ end
30
+ end
31
+
32
+ def constants(reload = false)
33
+ @constants = nil if reload
34
+ @constants ||= find_all("constants.yml").inject({}) do |constants, pathname|
35
+ contents = YAML.load(pathname.contents) rescue nil
36
+ contents = {} unless contents.is_a?(Hash)
37
+ constants.merge(contents)
38
+ end
39
+ end
40
+
41
+ protected
42
+ def absolute?(location)
43
+ location[0, 1] == File::SEPARATOR
44
+ end
45
+
46
+ def absolute_location_from(location)
47
+ location = location.to_s
48
+ location = File.join(root.absolute_location, location) unless absolute?(location)
49
+ File.expand_path(location)
50
+ end
51
+
52
+ def find_all(location)
53
+ load_path.map { |pathname| pathname.find(location) }.compact
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,5 @@
1
+ module Sprockets
2
+ class Error < ::StandardError; end
3
+ class LoadError < Error; end
4
+ class UndefinedConstantError < Error; end
5
+ end
@@ -0,0 +1,37 @@
1
+ module Sprockets
2
+ class Pathname
3
+ attr_reader :environment, :absolute_location
4
+
5
+ def initialize(environment, absolute_location)
6
+ @environment = environment
7
+ @absolute_location = File.expand_path(absolute_location)
8
+ end
9
+
10
+ # Returns a Pathname for the location relative to this pathname's absolute location.
11
+ def find(location, kind = :file)
12
+ location = File.join(absolute_location, location)
13
+ File.send("#{kind}?", location) ? Pathname.new(environment, location) : nil
14
+ end
15
+
16
+ def parent_pathname
17
+ Pathname.new(environment, File.dirname(absolute_location))
18
+ end
19
+
20
+ def source_file
21
+ SourceFile.new(environment, self)
22
+ end
23
+
24
+ def contents
25
+ IO.read(absolute_location)
26
+ end
27
+
28
+ def ==(pathname)
29
+ environment == pathname.environment &&
30
+ absolute_location == pathname.absolute_location
31
+ end
32
+
33
+ def to_s
34
+ absolute_location
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,109 @@
1
+ module Sprockets
2
+ class Preprocessor
3
+ attr_reader :environment, :concatenation, :source_files, :asset_paths
4
+
5
+ def initialize(environment, options = {})
6
+ @environment = environment
7
+ @concatenation = Concatenation.new
8
+ @source_files = []
9
+ @asset_paths = []
10
+ @options = options
11
+ end
12
+
13
+ def require(source_file)
14
+ return if source_files.include?(source_file)
15
+ source_files << source_file
16
+
17
+ source_file.each_source_line do |source_line|
18
+ if source_line.require?
19
+ require_from_source_line(source_line)
20
+ elsif source_line.provide?
21
+ provide_from_source_line(source_line)
22
+ else
23
+ record_source_line(source_line)
24
+ end
25
+ end
26
+ end
27
+
28
+ def provide(asset_path)
29
+ return if !asset_path || asset_paths.include?(asset_path)
30
+ asset_paths << asset_path
31
+ end
32
+
33
+ protected
34
+ attr_reader :options
35
+
36
+ def require_from_source_line(source_line)
37
+ require pathname_from(source_line).source_file
38
+ end
39
+
40
+ def provide_from_source_line(source_line)
41
+ provide asset_path_from(source_line)
42
+ end
43
+
44
+ def record_source_line(source_line)
45
+ skip_pdoc_comments(source_line) do
46
+ unless source_line.comment? && strip_comments?
47
+ concatenation.record(source_line)
48
+ end
49
+ end
50
+ end
51
+
52
+ def skip_pdoc_comments(source_line)
53
+ yield unless strip_comments?
54
+
55
+ @commented ||= false
56
+
57
+ if source_line.begins_multiline_comment?
58
+ @commented = true
59
+ end
60
+
61
+ yield unless @commented
62
+
63
+ if source_line.closes_multiline_comment?
64
+ @commented = false
65
+ end
66
+ end
67
+
68
+ def strip_comments?
69
+ options[:strip_comments] != false
70
+ end
71
+
72
+ def pathname_from(source_line)
73
+ pathname = send(pathname_finder_from(source_line), source_line)
74
+ raise_load_error_for(source_line) unless pathname
75
+ pathname
76
+ end
77
+
78
+ def pathname_for_require_from(source_line)
79
+ environment.find(location_from(source_line))
80
+ end
81
+
82
+ def pathname_for_relative_require_from(source_line)
83
+ source_line.source_file.find(location_from(source_line))
84
+ end
85
+
86
+ def pathname_finder_from(source_line)
87
+ "pathname_for_#{kind_of_require_from(source_line)}_from"
88
+ end
89
+
90
+ def kind_of_require_from(source_line)
91
+ source_line.require[/^(.)/, 1] == '"' ? :relative_require : :require
92
+ end
93
+
94
+ def location_from(source_line)
95
+ location = source_line.require[/^.(.*).$/, 1]
96
+ File.join(File.dirname(location), File.basename(location, ".js") + ".js")
97
+ end
98
+
99
+ def asset_path_from(source_line)
100
+ source_line.source_file.find(source_line.provide, :directory)
101
+ end
102
+
103
+ def raise_load_error_for(source_line)
104
+ kind = kind_of_require_from(source_line).to_s.tr("_", " ")
105
+ file = File.split(location_from(source_line)).last
106
+ raise LoadError, "can't find file for #{kind} `#{file}' (#{source_line.inspect})"
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,106 @@
1
+ module Sprockets
2
+ class Secretary
3
+ DEFAULT_OPTIONS = {
4
+ :root => ".",
5
+ :load_path => [],
6
+ :source_files => [],
7
+ :expand_paths => true
8
+ }
9
+
10
+ attr_reader :environment, :preprocessor
11
+
12
+ def initialize(options = {})
13
+ reset!(options)
14
+ end
15
+
16
+ def reset!(options = @options)
17
+ @options = DEFAULT_OPTIONS.merge(options)
18
+ @environment = Sprockets::Environment.new(@options[:root])
19
+ @preprocessor = Sprockets::Preprocessor.new(@environment)
20
+
21
+ add_load_locations(@options[:load_path])
22
+ add_source_files(@options[:source_files])
23
+ end
24
+
25
+ def add_load_location(load_location, options = {})
26
+ add_load_locations([load_location], options)
27
+ end
28
+
29
+ def add_load_locations(load_locations, options = {})
30
+ expand_paths(load_locations, options).each do |load_location|
31
+ environment.register_load_location(load_location)
32
+ end
33
+ end
34
+
35
+ def add_source_file(source_file, options = {})
36
+ add_source_files([source_file], options)
37
+ end
38
+
39
+ def add_source_files(source_files, options = {})
40
+ expand_paths(source_files, options).each do |source_file|
41
+ if pathname = environment.find(source_file)
42
+ preprocessor.require(pathname.source_file)
43
+ else
44
+ raise Sprockets::LoadError, "no such file `#{source_file}'"
45
+ end
46
+ end
47
+ end
48
+
49
+ def concatenation
50
+ preprocessor.concatenation
51
+ end
52
+
53
+ def install_assets
54
+ if @options[:asset_root]
55
+ preprocessor.asset_paths.each do |asset_path|
56
+ copy_assets_from(asset_path.absolute_location)
57
+ end
58
+ end
59
+ end
60
+
61
+ def source_last_modified
62
+ preprocessor.source_files.map { |s| s.mtime }.max
63
+ end
64
+
65
+ protected
66
+ def expand_paths(paths, options = {})
67
+ if options.has_key?(:expand_paths) ? options[:expand_paths] : @options[:expand_paths]
68
+ paths.map { |path| Dir[from_root(path)].sort }.flatten.compact
69
+ else
70
+ paths.map { |path| from_root(path) }
71
+ end
72
+ end
73
+
74
+ def from_root(path)
75
+ if path[0, 1] == File::SEPARATOR
76
+ path
77
+ else
78
+ File.join(@options[:root], path)
79
+ end
80
+ end
81
+
82
+ def copy_assets_from(asset_path)
83
+ relative_file_paths_beneath(asset_path).each do |filename|
84
+ source, destination = File.join(asset_path, filename), File.join(asset_root, File.dirname(filename))
85
+ if !File.directory?(source)
86
+ FileUtils.mkdir_p(destination)
87
+ FileUtils.cp(source, destination)
88
+ end
89
+ end
90
+ end
91
+
92
+ def relative_file_paths_beneath(path)
93
+ Dir[File.join(path, "**", "*")].map do |filename|
94
+ File.join(*path_pieces(filename)[path_pieces(path).length..-1])
95
+ end
96
+ end
97
+
98
+ def asset_root
99
+ from_root(@options[:asset_root])
100
+ end
101
+
102
+ def path_pieces(path)
103
+ path.split(File::SEPARATOR)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,32 @@
1
+ module Sprockets
2
+ class SourceFile
3
+ attr_reader :environment, :pathname
4
+
5
+ def initialize(environment, pathname)
6
+ @environment = environment
7
+ @pathname = pathname
8
+ end
9
+
10
+ def each_source_line
11
+ File.open(pathname.absolute_location) do |file|
12
+ file.each do |line|
13
+ yield SourceLine.new(self, line, file.lineno)
14
+ end
15
+ end
16
+ end
17
+
18
+ def find(location, kind = :file)
19
+ pathname.parent_pathname.find(location, kind)
20
+ end
21
+
22
+ def ==(source_file)
23
+ pathname == source_file.pathname
24
+ end
25
+
26
+ def mtime
27
+ File.mtime(pathname.absolute_location)
28
+ rescue Errno::ENOENT
29
+ 0
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,58 @@
1
+ module Sprockets
2
+ class SourceLine
3
+ attr_reader :source_file, :line, :number
4
+
5
+ def initialize(source_file, line, number)
6
+ @source_file = source_file
7
+ @line = line
8
+ @number = number
9
+ end
10
+
11
+ def comment
12
+ @comment ||= line[/^\s*\/\/(.*)/, 1]
13
+ end
14
+
15
+ def comment?
16
+ !!comment
17
+ end
18
+
19
+ def begins_multiline_comment?
20
+ line =~ /^\s*\/\*\*(.*)/
21
+ end
22
+
23
+ def closes_multiline_comment?
24
+ line =~ /^(.*)*\*\*\/\s*/
25
+ end
26
+
27
+ def require
28
+ @require ||= (comment || "")[/^=\s+require\s+(\"(.*?)\"|<(.*?)>)\s*$/, 1]
29
+ end
30
+
31
+ def require?
32
+ !!require
33
+ end
34
+
35
+ def provide
36
+ @provide ||= (comment || "")[/^=\s+provide\s+\"(.*?)\"\s*$/, 1]
37
+ end
38
+
39
+ def provide?
40
+ !!provide
41
+ end
42
+
43
+ def inspect
44
+ "line #@number of #{@source_file.pathname}"
45
+ end
46
+
47
+ def to_s(constants = source_file.environment.constants)
48
+ line.chomp.gsub(/<%=(.*?)%>/) do
49
+ constant = $1.strip
50
+ if value = constants[constant]
51
+ value
52
+ else
53
+ raise UndefinedConstantError, "couldn't find constant `#{constant}' in #{inspect}"
54
+ end
55
+ end + $/
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,9 @@
1
+ module Sprockets
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 9
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join(".")
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ /* nothing */
@@ -0,0 +1 @@
1
+ VERSION: 1.0
@@ -0,0 +1,2 @@
1
+ // This is a double-slash comment that should appear in the resulting output file.
2
+ /* This is a slash-star comment that should appear in the resulting output file. */
@@ -0,0 +1,2 @@
1
+ // This is a double-slash comment that should not appear in the resulting output file.
2
+ /* This is a slash-star comment that should not appear in the resulting output file. */
@@ -0,0 +1,4 @@
1
+ /**
2
+ * This is a slash-star comment
3
+ * that should appear in the resulting output file.
4
+ **/
@@ -0,0 +1,5 @@
1
+ var before_first_require;
2
+ //= require <foo>
3
+ var after_first_require_and_before_second_require;
4
+ //= require <foo>
5
+ var after_second_require;
@@ -0,0 +1 @@
1
+ //= require <nonexistent>
@@ -0,0 +1,3 @@
1
+ var before_require;
2
+ //= require <foo>
3
+ var after_require;
@@ -0,0 +1 @@
1
+ //= require "requiring_the_current_file_should_do_nothing"
@@ -0,0 +1,3 @@
1
+ ONE: one
2
+ TWO: two
3
+ HELLO: Hello world!
@@ -0,0 +1 @@
1
+ var Foo = { };
@@ -0,0 +1,4 @@
1
+ //= require "bar"
2
+ var FooBar = { };
3
+
4
+ /* Hello! */
@@ -0,0 +1 @@
1
+ var FooFoo = { };
@@ -0,0 +1,3 @@
1
+ //= provide "../assets"
2
+
3
+ var ScriptWithAssets = { };
@@ -0,0 +1,28 @@
1
+ require "test_helper"
2
+
3
+ class ConcatenationTest < Test::Unit::TestCase
4
+ def setup
5
+ @concatenation = Sprockets::Concatenation.new
6
+ @environment = environment_for_fixtures
7
+ end
8
+
9
+ def test_record
10
+ assert_equal [], @concatenation.source_lines
11
+ assert_equal "hello\n", @concatenation.record(source_line("hello\n")).to_s
12
+ assert_equal "world\n", @concatenation.record(source_line("world\n")).to_s
13
+ assert_equal ["hello\n", "world\n"], @concatenation.source_lines.map { |source_line| source_line.to_s }
14
+ end
15
+
16
+ def test_to_s
17
+ @concatenation.record(source_line("hello\n"))
18
+ @concatenation.record(source_line("world\n"))
19
+ assert_equal "hello\nworld\n", @concatenation.to_s
20
+ end
21
+
22
+ def test_save_to
23
+ filename = File.join(FIXTURES_PATH, "output.js")
24
+ @concatenation.save_to(filename)
25
+ assert_equal @concatenation.to_s, IO.read(filename)
26
+ File.unlink(filename)
27
+ end
28
+ end
@@ -0,0 +1,64 @@
1
+ require "test_helper"
2
+
3
+ class EnvironmentTest < Test::Unit::TestCase
4
+ def test_load_path_locations_become_pathnames_for_absolute_locations_from_the_root
5
+ environment = Sprockets::Environment.new("/root", ["/a", "b"])
6
+ assert_load_path_equals ["/a", "/root/b", "/root"], environment
7
+ end
8
+
9
+ def test_pathname_from_for_location_with_leading_slash_should_return_a_pathname_with_the_location_unchanged
10
+ environment = Sprockets::Environment.new("/root")
11
+ assert_absolute_location "/a", environment.pathname_from("/a")
12
+ end
13
+
14
+ def test_pathname_from_for_relative_location_should_return_a_pathname_for_the_expanded_absolute_location_from_root
15
+ environment = Sprockets::Environment.new("/root")
16
+ assert_absolute_location "/root/a", environment.pathname_from("a")
17
+ assert_absolute_location "/root/a", environment.pathname_from("./a")
18
+ assert_absolute_location "/a", environment.pathname_from("../a")
19
+ end
20
+
21
+ def test_register_load_location_should_unshift_the_location_onto_the_load_path
22
+ environment = Sprockets::Environment.new("/root")
23
+ environment.register_load_location("a")
24
+ assert_load_path_equals ["/root/a", "/root"], environment
25
+ environment.register_load_location("b")
26
+ assert_load_path_equals ["/root/b", "/root/a", "/root"], environment
27
+ end
28
+
29
+ def test_register_load_location_should_remove_already_existing_locations_before_unshifting
30
+ environment = Sprockets::Environment.new("/root")
31
+ environment.register_load_location("a")
32
+ environment.register_load_location("b")
33
+ assert_load_path_equals ["/root/b", "/root/a", "/root"], environment
34
+ environment.register_load_location("a")
35
+ assert_load_path_equals ["/root/a", "/root/b", "/root"], environment
36
+ end
37
+
38
+ def test_find_should_return_the_first_matching_pathname_in_the_load_path
39
+ environment = environment_for_fixtures
40
+ first_pathname = environment.find("foo.js")
41
+ assert_absolute_location_ends_with "src/foo.js", first_pathname
42
+
43
+ environment.register_load_location(File.join(FIXTURES_PATH, "src", "foo"))
44
+ second_pathname = environment.find("foo.js")
45
+ assert_not_equal first_pathname, second_pathname
46
+ assert_absolute_location_ends_with "foo/foo.js", second_pathname
47
+ end
48
+
49
+ def test_find_should_return_nil_when_no_matching_source_file_is_found
50
+ environment = environment_for_fixtures
51
+ assert_nil environment.find("nonexistent.js")
52
+ end
53
+
54
+ def test_constants_should_return_a_hash_of_all_constants_defined_in_the_load_path
55
+ constants = environment_for_fixtures.constants
56
+ assert_kind_of Hash, constants
57
+ assert_equal %w(HELLO ONE TWO VERSION), constants.keys.sort
58
+ end
59
+
60
+ protected
61
+ def assert_load_path_equals(load_path_absolute_locations, environment)
62
+ assert load_path_absolute_locations.zip(environment.load_path).map { |location, pathname| location == pathname.absolute_location }.all?
63
+ end
64
+ end
@@ -0,0 +1,55 @@
1
+ require File.join(File.dirname(__FILE__), *%w".. lib sprockets")
2
+ require "test/unit"
3
+ require "fileutils"
4
+ require "tmpdir"
5
+
6
+ class Test::Unit::TestCase
7
+ FIXTURES_PATH = File.expand_path(File.join(File.dirname(__FILE__), "fixtures")) unless defined?(FIXTURES_PATH)
8
+
9
+ protected
10
+ def location_for_fixture(fixture)
11
+ File.join(FIXTURES_PATH, fixture)
12
+ end
13
+
14
+ def content_of_fixture(fixture)
15
+ IO.read(location_for_fixture(fixture))
16
+ end
17
+
18
+ def environment_for_fixtures
19
+ Sprockets::Environment.new(FIXTURES_PATH, source_directories_in_fixtures_path)
20
+ end
21
+
22
+ def source_directories_in_fixtures_path
23
+ Dir[File.join(FIXTURES_PATH, "**", "src")]
24
+ end
25
+
26
+ def assert_absolute_location(location, pathname)
27
+ assert_equal location, pathname.absolute_location
28
+ end
29
+
30
+ def assert_absolute_location_ends_with(location_ending, pathname)
31
+ assert pathname.absolute_location[/#{Regexp.escape(location_ending)}$/]
32
+ end
33
+
34
+ def pathname(location, environment = @environment)
35
+ Sprockets::Pathname.new(environment, File.join(FIXTURES_PATH, location))
36
+ end
37
+
38
+ def source_file(location, environment = @environment)
39
+ Sprockets::SourceFile.new(environment, pathname(location, environment))
40
+ end
41
+
42
+ def source_line(line, source_file = nil, line_number = 1)
43
+ Sprockets::SourceLine.new(source_file || source_file("dummy"), line, line_number)
44
+ end
45
+
46
+ def with_temporary_directory
47
+ path = File.join(Dir.tmpdir, [caller[0][/`(.*)'/, 1], Time.now.to_f].join("_"))
48
+ begin
49
+ FileUtils.mkdir(path)
50
+ yield path
51
+ ensure
52
+ FileUtils.rm_rf(path)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,43 @@
1
+ require "test_helper"
2
+
3
+ class PathnameTest < Test::Unit::TestCase
4
+ def setup
5
+ @environment = environment_for_fixtures
6
+ end
7
+
8
+ def test_absolute_location_is_automatically_expanded
9
+ expanded_location = File.expand_path(File.join(FIXTURES_PATH, "foo"))
10
+ assert_absolute_location expanded_location, pathname("foo")
11
+ assert_absolute_location expanded_location, pathname("./foo")
12
+ assert_absolute_location expanded_location, pathname("./foo/../foo")
13
+ end
14
+
15
+ def test_find_should_return_a_pathname_for_the_location_relative_to_the_absolute_location_of_the_pathname
16
+ assert_absolute_location_ends_with "src/foo/bar.js", pathname("src/foo").find("bar.js")
17
+ end
18
+
19
+ def test_find_should_return_nil_when_the_location_relative_to_the_absolute_location_of_the_pathname_is_not_a_file_or_does_not_exist
20
+ assert_nil pathname("src/foo").find("nonexistent.js")
21
+ end
22
+
23
+ def test_parent_pathname_should_return_a_pathname_for_the_parent_directory
24
+ assert_absolute_location_ends_with "src", pathname("src/foo").parent_pathname
25
+ assert_absolute_location_ends_with "foo", pathname("src/foo/foo.js").parent_pathname
26
+ end
27
+
28
+ def test_source_file_should_return_a_source_file_for_the_pathname
29
+ source_file = pathname("src/foo.js").source_file
30
+ assert_kind_of Sprockets::SourceFile, source_file
31
+ assert_equal pathname("src/foo.js"), source_file.pathname
32
+ end
33
+
34
+ def test_equality_of_pathnames
35
+ assert_equal pathname("src/foo.js"), pathname("src/foo.js")
36
+ assert_equal pathname("src/foo.js"), pathname("src/foo/../foo.js")
37
+ assert_not_equal pathname("src/foo.js"), pathname("src/foo/foo.js")
38
+ end
39
+
40
+ def test_to_s_should_return_absolute_location
41
+ assert_equal pathname("src/foo.js").to_s, pathname("src/foo.js").absolute_location
42
+ end
43
+ end
@@ -0,0 +1,101 @@
1
+ require "test_helper"
2
+
3
+ class PreprocessorTest < Test::Unit::TestCase
4
+ def setup
5
+ @environment = environment_for_fixtures
6
+ @preprocessor = Sprockets::Preprocessor.new(@environment)
7
+ end
8
+
9
+ def test_double_slash_comments_that_are_not_requires_should_be_removed_by_default
10
+ require_file_for_this_test
11
+ assert_concatenation_does_not_contain_line "// This is a double-slash comment that should not appear in the resulting output file."
12
+ assert_concatenation_contains_line "/* This is a slash-star comment that should not appear in the resulting output file. */"
13
+ end
14
+
15
+ def test_double_slash_comments_that_are_not_requires_should_be_ignored_when_strip_comments_is_false
16
+ @preprocessor = Sprockets::Preprocessor.new(@environment, :strip_comments => false)
17
+ require_file_for_this_test
18
+ assert_concatenation_contains_line "// This is a double-slash comment that should appear in the resulting output file."
19
+ assert_concatenation_contains_line "/* This is a slash-star comment that should appear in the resulting output file. */"
20
+ end
21
+
22
+ def test_multiline_comments_should_be_removed_by_default
23
+ require_file_for_this_test
24
+ assert_concatenation_does_not_contain_line "/**"
25
+ assert_concatenation_does_not_contain_line " * This is a slash-star comment"
26
+ assert_concatenation_does_not_contain_line " * that should appear in the resulting output file."
27
+ assert_concatenation_does_not_contain_line "**/"
28
+ end
29
+
30
+ def test_requiring_a_single_file_should_replace_the_require_comment_with_the_file_contents
31
+ require_file_for_this_test
32
+ assert_concatenation_contains <<-LINES
33
+ var before_require;
34
+ var Foo = { };
35
+ var after_require;
36
+ LINES
37
+ end
38
+
39
+ def test_requiring_a_file_that_does_not_exist_should_raise_an_error
40
+ assert_raises(Sprockets::LoadError) do
41
+ require_file_for_this_test
42
+ end
43
+ end
44
+
45
+ def test_requiring_the_current_file_should_do_nothing
46
+ require_file_for_this_test
47
+ assert_equal "", output_text
48
+ end
49
+
50
+ def test_requiring_a_file_after_it_has_already_been_required_should_do_nothing
51
+ require_file_for_this_test
52
+ assert_concatenation_contains <<-LINES
53
+ var before_first_require;
54
+ var Foo = { };
55
+ var after_first_require_and_before_second_require;
56
+ var after_second_require;
57
+ LINES
58
+ end
59
+
60
+ protected
61
+ attr_reader :environment, :preprocessor
62
+
63
+ def concatenation
64
+ preprocessor.concatenation
65
+ end
66
+
67
+ def output_text
68
+ preprocessor.concatenation.to_s
69
+ end
70
+
71
+ def source_lines_matching(line)
72
+ concatenation.source_lines.select { |source_line| source_line.line.strip == line }
73
+ end
74
+
75
+ def require_file(location)
76
+ preprocessor.require(environment.find(location).source_file)
77
+ end
78
+
79
+ def require_file_for_this_test
80
+ require_file(file_for_this_test)
81
+ end
82
+
83
+ def file_for_this_test
84
+ caller.map { |c| c[/`(.*?)'$/, 1] }.grep(/^test_/).first[5..-1] + ".js"
85
+ end
86
+
87
+ def assert_concatenation_does_not_contain_line(line)
88
+ assert source_lines_matching(line).empty?, "Expected #{line.inspect} to not exist"
89
+ end
90
+
91
+ def assert_concatenation_contains_line(line)
92
+ assert source_lines_matching(line).any?, "Expected #{line.inspect} to exist"
93
+ end
94
+
95
+ def assert_concatenation_contains(indented_text)
96
+ lines = indented_text.split($/)
97
+ initial_indent = lines.first[/^\s*/].length
98
+ unindented_text = lines.map { |line| line[initial_indent..-1] }.join($/)
99
+ assert output_text[unindented_text]
100
+ end
101
+ end
@@ -0,0 +1,83 @@
1
+ require "test_helper"
2
+
3
+ class SecretaryTest < Test::Unit::TestCase
4
+ def test_load_locations_are_not_expanded_when_expand_paths_is_false
5
+ secretary = Sprockets::Secretary.new(:root => FIXTURES_PATH)
6
+ secretary.add_load_locations("src/**/", :expand_paths => false)
7
+
8
+ assert_equal [File.join(FIXTURES_PATH, "src/**"), FIXTURES_PATH],
9
+ secretary.environment.load_path.map { |pathname| pathname.absolute_location }
10
+ end
11
+
12
+ def test_load_locations_are_expanded_when_expand_paths_is_true
13
+ secretary = Sprockets::Secretary.new(:root => FIXTURES_PATH)
14
+ secretary.add_load_locations("src/**/", :expand_paths => true)
15
+
16
+ assert_equal [File.join(FIXTURES_PATH, "src", "foo"), File.join(FIXTURES_PATH, "src"), FIXTURES_PATH],
17
+ secretary.environment.load_path.map { |pathname| pathname.absolute_location }
18
+ end
19
+
20
+ def test_source_files_are_not_expanded_when_expand_paths_is_false
21
+ secretary = Sprockets::Secretary.new(:root => FIXTURES_PATH)
22
+ assert_raises(Sprockets::LoadError) do
23
+ secretary.add_source_files("src/f*.js", :expand_paths => false)
24
+ end
25
+ end
26
+
27
+ def test_source_files_are_expanded_when_expand_paths_is_true
28
+ secretary = Sprockets::Secretary.new(:root => FIXTURES_PATH)
29
+ secretary.add_source_files("src/f*.js", :expand_paths => true)
30
+
31
+ assert_equal [File.join(FIXTURES_PATH, "src", "foo.js")],
32
+ secretary.preprocessor.source_files.map { |source_file| source_file.pathname.absolute_location }
33
+ end
34
+
35
+ def test_install_assets_into_empty_directory
36
+ with_temporary_directory do |temp|
37
+ secretary = Sprockets::Secretary.new(:root => FIXTURES_PATH, :asset_root => temp)
38
+ secretary.add_source_files("src/script_with_assets.js")
39
+
40
+ assert_equal [], Dir[File.join(temp, "**", "*")]
41
+ secretary.install_assets
42
+ assert_equal paths_relative_to(temp,
43
+ "images", "images/script_with_assets", "images/script_with_assets/one.png",
44
+ "images/script_with_assets/two.png", "stylesheets", "stylesheets/script_with_assets.css"),
45
+ Dir[File.join(temp, "**", "*")]
46
+ end
47
+ end
48
+
49
+ def test_install_assets_into_nonexistent_directory
50
+ with_temporary_directory do |temp|
51
+ temp = File.join(temp, "assets")
52
+ secretary = Sprockets::Secretary.new(:root => FIXTURES_PATH, :asset_root => temp)
53
+ secretary.add_source_files("src/script_with_assets.js")
54
+
55
+ assert_equal [], Dir[File.join(temp, "**", "*")]
56
+ secretary.install_assets
57
+ assert_equal paths_relative_to(temp,
58
+ "images", "images/script_with_assets", "images/script_with_assets/one.png",
59
+ "images/script_with_assets/two.png", "stylesheets", "stylesheets/script_with_assets.css"),
60
+ Dir[File.join(temp, "**", "*")]
61
+ end
62
+ end
63
+
64
+ def test_install_assets_into_subdirectories_that_already_exist
65
+ with_temporary_directory do |temp|
66
+ secretary = Sprockets::Secretary.new(:root => FIXTURES_PATH, :asset_root => temp)
67
+ secretary.add_source_files("src/script_with_assets.js")
68
+
69
+ FileUtils.mkdir_p(File.join(temp, "images", "script_with_assets"))
70
+ assert_equal paths_relative_to(temp, "images", "images/script_with_assets"), Dir[File.join(temp, "**", "*")]
71
+ secretary.install_assets
72
+ assert_equal paths_relative_to(temp,
73
+ "images", "images/script_with_assets", "images/script_with_assets/one.png",
74
+ "images/script_with_assets/two.png", "stylesheets", "stylesheets/script_with_assets.css"),
75
+ Dir[File.join(temp, "**", "*")]
76
+ end
77
+ end
78
+
79
+ protected
80
+ def paths_relative_to(root, *paths)
81
+ paths.map { |path| File.join(root, path) }
82
+ end
83
+ end
@@ -0,0 +1,29 @@
1
+ require "test_helper"
2
+ require "enumerator"
3
+
4
+ class SourceFileTest < Test::Unit::TestCase
5
+ def setup
6
+ @environment = environment_for_fixtures
7
+ end
8
+
9
+ def test_each_source_line
10
+ source_file_lines = Enumerable::Enumerator.new(source_file("src/foo/bar.js"), :each_source_line).to_a
11
+ assert_equal content_of_fixture("src/foo/bar.js"), source_file_lines.map { |line| line.line }.join
12
+ assert_equal 4, source_file_lines.length
13
+ end
14
+
15
+ def test_find_should_return_pathname_for_file_relative_to_the_current_pathname
16
+ assert_absolute_location_ends_with "test/fixtures/src/foo/bar.js", source_file("src/foo/foo.js").find("bar.js")
17
+ end
18
+
19
+ def test_find_should_return_nil_for_nonexistent_file
20
+ assert_nil source_file("src/foo/foo.js").find("nonexistent.js")
21
+ end
22
+
23
+ def test_equality_of_source_files
24
+ assert_equal source_file("src/foo/foo.js"), source_file("src/foo/foo.js")
25
+ assert_equal source_file("src/foo/foo.js"), source_file("src/foo/../foo/foo.js")
26
+ assert_not_equal source_file("src/foo/foo.js"), source_file("src/foo.js")
27
+ assert_not_equal source_file("src/foo/foo.js"), source_file("src/foo/bar.js")
28
+ end
29
+ end
@@ -0,0 +1,75 @@
1
+ require "test_helper"
2
+
3
+ class SourceLineTest < Test::Unit::TestCase
4
+ def test_line_that_begins_with_double_slash_should_be_a_comment
5
+ assert source_line("//").comment?
6
+ assert source_line("//test").comment?
7
+ assert source_line("//= require").comment?
8
+ assert source_line("//= require <foo>").comment?
9
+ assert source_line(" //").comment?
10
+ assert source_line("\t//").comment?
11
+ end
12
+
13
+ def test_line_that_begins_a_multiline_comment
14
+ assert !source_line(" /*").begins_multiline_comment?
15
+ assert source_line(" /**").begins_multiline_comment?
16
+ end
17
+
18
+ def test_line_that_closes_a_multiline_comment
19
+ assert !source_line(" */").closes_multiline_comment?
20
+ assert source_line(" **/").closes_multiline_comment?
21
+ end
22
+
23
+ def test_line_that_contains_but_does_not_begin_with_double_slash_should_not_be_a_comment
24
+ assert !source_line("f //").comment?
25
+ assert !source_line("f //= require <foo>").comment?
26
+ end
27
+
28
+ def test_comment_should_be_extracted_from_comment_lines
29
+ assert_equal "test", source_line("//test").comment
30
+ assert_equal " test", source_line("// test").comment
31
+ assert_equal nil, source_line("f //test").comment
32
+ end
33
+
34
+ def test_line_that_contains_require_comment_should_be_a_require
35
+ assert source_line("//= require <foo>").require?
36
+ assert !source_line("//= require<foo>").require?
37
+ assert source_line("//= require \"foo\"").require?
38
+ assert !source_line("//= require <foo> f").require?
39
+ end
40
+
41
+ def test_require_should_be_extracted_from_require_lines
42
+ assert_nil source_line("//= require").require
43
+ assert_equal "<foo>", source_line("//= require <foo>").require
44
+ assert_equal "<foo>", source_line("//= require <foo> ").require
45
+ assert_equal "\"foo\"", source_line("//= require \"foo\"").require
46
+ end
47
+
48
+ def test_line_that_contains_a_provide_comment_should_be_a_provide
49
+ assert source_line("//= provide \"../assets\"").provide?
50
+ assert !source_line("//= provide").provide?
51
+ assert !source_line("//= provide <../assets>").provide?
52
+ end
53
+
54
+ def test_provide_should_be_extracted_from_provide_lines
55
+ assert_nil source_line("//= provide").provide
56
+ assert_equal "../assets", source_line("//= provide \"../assets\"").provide
57
+ end
58
+
59
+ def test_inspect_should_include_source_file_location_and_line_number
60
+ environment = environment_for_fixtures
61
+ pathname = Sprockets::Pathname.new(environment, "/a/b/c.js")
62
+ source_file = Sprockets::SourceFile.new(environment, pathname)
63
+ assert_equal "line 25 of /a/b/c.js", source_line("hello", source_file, 25).inspect
64
+ end
65
+
66
+ def test_interpolation_of_constants
67
+ assert_equal %(var VERSION = "1.0";\n), source_line('var VERSION = "<%= VERSION %>";').to_s("VERSION" => "1.0")
68
+ end
69
+
70
+ def test_interpolation_of_missing_constant_raises_undefined_constant_error
71
+ assert_raises(Sprockets::UndefinedConstantError) do
72
+ source_line('<%= NONEXISTENT %>').to_s("VERSION" => "1.0")
73
+ end
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sprockets
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Sam Stephenson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-09 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Sprockets is a Ruby library that preprocesses and concatenates JavaScript source files.
17
+ email: sstephenson@gmail.com
18
+ executables:
19
+ - sprocketize
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Rakefile
26
+ - bin/sprocketize
27
+ - lib/sprockets
28
+ - lib/sprockets/concatenation.rb
29
+ - lib/sprockets/environment.rb
30
+ - lib/sprockets/error.rb
31
+ - lib/sprockets/pathname.rb
32
+ - lib/sprockets/preprocessor.rb
33
+ - lib/sprockets/secretary.rb
34
+ - lib/sprockets/source_file.rb
35
+ - lib/sprockets/source_line.rb
36
+ - lib/sprockets/version.rb
37
+ - lib/sprockets.rb
38
+ - test/fixtures
39
+ - test/fixtures/assets
40
+ - test/fixtures/assets/images
41
+ - test/fixtures/assets/images/script_with_assets
42
+ - test/fixtures/assets/images/script_with_assets/one.png
43
+ - test/fixtures/assets/images/script_with_assets/two.png
44
+ - test/fixtures/assets/stylesheets
45
+ - test/fixtures/assets/stylesheets/script_with_assets.css
46
+ - test/fixtures/constants.yml
47
+ - test/fixtures/double_slash_comments_that_are_not_requires_should_be_ignored_when_strip_comments_is_false.js
48
+ - test/fixtures/double_slash_comments_that_are_not_requires_should_be_removed_by_default.js
49
+ - test/fixtures/multiline_comments_should_be_removed_by_default.js
50
+ - test/fixtures/requiring_a_file_after_it_has_already_been_required_should_do_nothing.js
51
+ - test/fixtures/requiring_a_file_that_does_not_exist_should_raise_an_error.js
52
+ - test/fixtures/requiring_a_single_file_should_replace_the_require_comment_with_the_file_contents.js
53
+ - test/fixtures/requiring_the_current_file_should_do_nothing.js
54
+ - test/fixtures/src
55
+ - test/fixtures/src/constants.yml
56
+ - test/fixtures/src/foo
57
+ - test/fixtures/src/foo/bar.js
58
+ - test/fixtures/src/foo/foo.js
59
+ - test/fixtures/src/foo.js
60
+ - test/fixtures/src/script_with_assets.js
61
+ - test/test_concatenation.rb
62
+ - test/test_environment.rb
63
+ - test/test_helper.rb
64
+ - test/test_pathname.rb
65
+ - test/test_preprocessor.rb
66
+ - test/test_secretary.rb
67
+ - test/test_source_file.rb
68
+ - test/test_source_line.rb
69
+ has_rdoc: false
70
+ homepage: http://github.com/sstephenson/sprockets
71
+ post_install_message:
72
+ rdoc_options: []
73
+
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: "0"
87
+ version:
88
+ requirements: []
89
+
90
+ rubyforge_project:
91
+ rubygems_version: 1.3.1
92
+ signing_key:
93
+ specification_version: 2
94
+ summary: JavaScript dependency management and concatenation
95
+ test_files:
96
+ - test/test_concatenation.rb
97
+ - test/test_environment.rb
98
+ - test/test_helper.rb
99
+ - test/test_pathname.rb
100
+ - test/test_preprocessor.rb
101
+ - test/test_secretary.rb
102
+ - test/test_source_file.rb
103
+ - test/test_source_line.rb