amiel-sprockets 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/Rakefile +26 -0
  2. data/ext/nph-sprockets.cgi +127 -0
  3. data/lib/sprockets.rb +42 -0
  4. data/lib/sprockets/concatenation.rb +36 -0
  5. data/lib/sprockets/environment.rb +52 -0
  6. data/lib/sprockets/error.rb +5 -0
  7. data/lib/sprockets/pathname.rb +37 -0
  8. data/lib/sprockets/preprocessor.rb +92 -0
  9. data/lib/sprockets/secretary.rb +110 -0
  10. data/lib/sprockets/source_file.rb +54 -0
  11. data/lib/sprockets/source_line.rb +86 -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 +8 -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_file_with_css_style_comments_should_replace_the_require_comment_with_file_contents.css +4 -0
  23. data/test/fixtures/requiring_a_single_file_should_replace_the_require_comment_with_the_file_contents.js +3 -0
  24. data/test/fixtures/requiring_the_current_file_should_do_nothing.js +1 -0
  25. data/test/fixtures/src/constants.yml +3 -0
  26. data/test/fixtures/src/foo.css +1 -0
  27. data/test/fixtures/src/foo.js +1 -0
  28. data/test/fixtures/src/foo/bar.js +4 -0
  29. data/test/fixtures/src/foo/foo.js +1 -0
  30. data/test/fixtures/src/script_with_assets.js +3 -0
  31. data/test/test_concatenation.rb +28 -0
  32. data/test/test_environment.rb +64 -0
  33. data/test/test_helper.rb +55 -0
  34. data/test/test_pathname.rb +43 -0
  35. data/test/test_preprocessor.rb +116 -0
  36. data/test/test_secretary.rb +83 -0
  37. data/test/test_source_file.rb +34 -0
  38. data/test/test_source_line.rb +89 -0
  39. metadata +50 -4
@@ -0,0 +1,54 @@
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 source_lines
11
+ @lines ||= begin
12
+ lines = []
13
+
14
+ comments = []
15
+ File.open(pathname.absolute_location) do |file|
16
+ file.each do |line|
17
+ lines << line = SourceLine.new(self, line, file.lineno)
18
+
19
+ if line.begins_pdoc_comment? || comments.any?
20
+ comments << line
21
+ end
22
+
23
+ if line.ends_multiline_comment?
24
+ if line.ends_pdoc_comment?
25
+ comments.each { |l| l.comment! }
26
+ end
27
+ comments.clear
28
+ end
29
+ end
30
+ end
31
+
32
+ lines
33
+ end
34
+ end
35
+
36
+ def each_source_line(&block)
37
+ source_lines.each(&block)
38
+ end
39
+
40
+ def find(location, kind = :file)
41
+ pathname.parent_pathname.find(location, kind)
42
+ end
43
+
44
+ def ==(source_file)
45
+ pathname == source_file.pathname
46
+ end
47
+
48
+ def mtime
49
+ File.mtime(pathname.absolute_location)
50
+ rescue Errno::ENOENT
51
+ Time.now
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,86 @@
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 parsable_comment
16
+ @parsable_comment ||= comment || line[/^\s*\/\*(.*)\s*\*\/$/, 1]
17
+ end
18
+
19
+ def comment?
20
+ !!comment
21
+ end
22
+
23
+ def comment!
24
+ @comment = line
25
+ end
26
+
27
+ def begins_multiline_comment?
28
+ line =~ /^\s*\/\*(.*)/
29
+ end
30
+
31
+ def begins_pdoc_comment?
32
+ line =~ /^\s*\/\*\*(.*)/
33
+ end
34
+
35
+ def ends_multiline_comment?
36
+ line =~ /^(.*)\*\/\s*/
37
+ end
38
+
39
+ def ends_pdoc_comment?
40
+ line =~ /^(.*)\*\*\/\s*/
41
+ end
42
+
43
+ def require
44
+ @require ||= (parsable_comment || "")[/^=\s+require\s+(\"(.*?)\"|<(.*?)>)\s*$/, 1]
45
+ end
46
+
47
+ def require?
48
+ !!require
49
+ end
50
+
51
+ def provide
52
+ @provide ||= (parsable_comment || "")[/^=\s+provide\s+\"(.*?)\"\s*$/, 1]
53
+ end
54
+
55
+ def provide?
56
+ !!provide
57
+ end
58
+
59
+ def inspect
60
+ "line #@number of #{@source_file.pathname}"
61
+ end
62
+
63
+ def to_s(constants = source_file.environment.constants)
64
+ result = line.chomp
65
+ interpolate_constants!(result, constants)
66
+ strip_trailing_whitespace!(result)
67
+ result + $/
68
+ end
69
+
70
+ protected
71
+ def interpolate_constants!(result, constants)
72
+ result.gsub!(/<%=(.*?)%>/) do
73
+ constant = $1.strip
74
+ if value = constants[constant]
75
+ value
76
+ else
77
+ raise UndefinedConstantError, "couldn't find constant `#{constant}' in #{inspect}"
78
+ end
79
+ end
80
+ end
81
+
82
+ def strip_trailing_whitespace!(result)
83
+ result.gsub!(/\s+$/, "")
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,9 @@
1
+ module Sprockets
2
+ module Version
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 5
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join(".")
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ VERSION: 1.0
@@ -0,0 +1,8 @@
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. */
3
+ /* This is multiline slash-star comment
4
+ * that should appear in the resulting
5
+ * output file */
6
+ /**
7
+ This is not a PDoc comment that should appear in the resulting output file.
8
+ */
@@ -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 PDoc 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,4 @@
1
+ .before_require{ color: #000; }
2
+ /*= require <foo> */
3
+ .after_require{ color: #111; }
4
+
@@ -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
+ #required_content{ color: #222; }
@@ -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| File.expand_path(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 File.expand_path(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,116 @@
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
+
21
+ assert_concatenation_contains_line "/* This is multiline slash-star comment"
22
+ assert_concatenation_contains_line "* that should appear in the resulting"
23
+ assert_concatenation_contains_line "* output file */"
24
+
25
+ assert_concatenation_contains_line "This is not a PDoc comment that should appear in the resulting output file."
26
+ end
27
+
28
+ def test_multiline_comments_should_be_removed_by_default
29
+ require_file_for_this_test
30
+ assert_concatenation_does_not_contain_line "/**"
31
+ assert_concatenation_does_not_contain_line " * This is a PDoc comment"
32
+ assert_concatenation_does_not_contain_line " * that should appear in the resulting output file."
33
+ assert_concatenation_does_not_contain_line "**/"
34
+ end
35
+
36
+ def test_requiring_a_single_file_should_replace_the_require_comment_with_the_file_contents
37
+ require_file_for_this_test
38
+ assert_concatenation_contains <<-LINES
39
+ var before_require;
40
+ var Foo = { };
41
+ var after_require;
42
+ LINES
43
+ end
44
+
45
+ def test_requiring_a_file_that_does_not_exist_should_raise_an_error
46
+ assert_raises(Sprockets::LoadError) do
47
+ require_file_for_this_test
48
+ end
49
+ end
50
+
51
+ def test_requiring_the_current_file_should_do_nothing
52
+ require_file_for_this_test
53
+ assert_equal "", output_text
54
+ end
55
+
56
+ def test_requiring_a_file_after_it_has_already_been_required_should_do_nothing
57
+ require_file_for_this_test
58
+ assert_concatenation_contains <<-LINES
59
+ var before_first_require;
60
+ var Foo = { };
61
+ var after_first_require_and_before_second_require;
62
+ var after_second_require;
63
+ LINES
64
+ end
65
+
66
+ def test_requiring_a_file_with_css_style_comments_should_replace_the_require_comment_with_file_contents
67
+ require_file_for_this_test(:css)
68
+ assert_concatenation_contains <<-LINES
69
+ .before_require{ color: #000; }
70
+ #required_content{ color: #222; }
71
+ .after_require{ color: #111; }
72
+ LINES
73
+ end
74
+
75
+ protected
76
+ attr_reader :environment, :preprocessor
77
+
78
+ def concatenation
79
+ preprocessor.concatenation
80
+ end
81
+
82
+ def output_text
83
+ preprocessor.concatenation.to_s
84
+ end
85
+
86
+ def source_lines_matching(line)
87
+ concatenation.source_lines.select { |source_line| source_line.line.strip == line }
88
+ end
89
+
90
+ def require_file(location)
91
+ preprocessor.require(environment.find(location).source_file)
92
+ end
93
+
94
+ def require_file_for_this_test(ext = 'js')
95
+ require_file(file_for_this_test(ext))
96
+ end
97
+
98
+ def file_for_this_test(ext)
99
+ caller.map { |c| c[/`(.*?)'$/, 1] }.grep(/^test_/).first[5..-1] + ".#{ext.to_s}"
100
+ end
101
+
102
+ def assert_concatenation_does_not_contain_line(line)
103
+ assert source_lines_matching(line).empty?, "Expected #{line.inspect} to not exist"
104
+ end
105
+
106
+ def assert_concatenation_contains_line(line)
107
+ assert source_lines_matching(line).any?, "Expected #{line.inspect} to exist"
108
+ end
109
+
110
+ def assert_concatenation_contains(indented_text)
111
+ lines = indented_text.split($/)
112
+ initial_indent = lines.first[/^\s*/].length
113
+ unindented_text = lines.map { |line| line[initial_indent..-1] }.join($/)
114
+ assert output_text[unindented_text]
115
+ end
116
+ end