amiel-sprockets 1.0.4 → 1.0.5

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