bunch 0.2.2 → 1.0.0pre1

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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -6
  3. data/Gemfile +3 -1
  4. data/Guardfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/Rakefile +7 -12
  7. data/bin/bunch +2 -5
  8. data/bunch.gemspec +30 -23
  9. data/lib/bunch.rb +37 -81
  10. data/lib/bunch/cli.rb +40 -74
  11. data/lib/bunch/combiner.rb +121 -0
  12. data/lib/bunch/compiler.rb +52 -0
  13. data/lib/bunch/compilers/coffee_script.rb +23 -0
  14. data/lib/bunch/compilers/ejs.rb +28 -0
  15. data/lib/bunch/compilers/jade.rb +28 -0
  16. data/lib/bunch/compilers/jst.rb +38 -0
  17. data/lib/bunch/compilers/null.rb +19 -0
  18. data/lib/bunch/compilers/sass.rb +55 -0
  19. data/lib/bunch/content_hash.rb +37 -0
  20. data/lib/bunch/css_minifier.rb +121 -0
  21. data/lib/bunch/file.rb +18 -0
  22. data/lib/bunch/file_cache.rb +159 -0
  23. data/lib/bunch/file_tree.rb +153 -0
  24. data/lib/bunch/ignorer.rb +38 -0
  25. data/lib/bunch/js_minifier.rb +38 -0
  26. data/lib/bunch/middleware.rb +16 -67
  27. data/lib/bunch/pipeline.rb +30 -0
  28. data/lib/bunch/server.rb +56 -0
  29. data/lib/bunch/simple_cache.rb +36 -0
  30. data/lib/bunch/tree_merge.rb +29 -0
  31. data/lib/bunch/version.rb +3 -1
  32. data/spec/bunch/cli_spec.rb +85 -0
  33. data/spec/bunch/combiner_spec.rb +107 -0
  34. data/spec/bunch/compiler_spec.rb +73 -0
  35. data/spec/bunch/compilers/coffee_script_spec.rb +23 -0
  36. data/spec/bunch/compilers/ejs_spec.rb +27 -0
  37. data/spec/bunch/compilers/jade_spec.rb +28 -0
  38. data/spec/bunch/compilers/sass_spec.rb +120 -0
  39. data/spec/bunch/css_minifier_spec.rb +31 -0
  40. data/spec/bunch/file_cache_spec.rb +151 -0
  41. data/spec/bunch/file_tree_spec.rb +127 -0
  42. data/spec/bunch/ignorer_spec.rb +26 -0
  43. data/spec/bunch/js_minifier_spec.rb +35 -0
  44. data/spec/bunch/middleware_spec.rb +41 -0
  45. data/spec/bunch/pipeline_spec.rb +31 -0
  46. data/spec/bunch/server_spec.rb +90 -0
  47. data/spec/bunch/simple_cache_spec.rb +55 -0
  48. data/spec/bunch/tree_merge_spec.rb +30 -0
  49. data/spec/bunch_spec.rb +6 -0
  50. data/spec/example_tree/directory/_combine +2 -0
  51. data/{example/js/test1.js → spec/example_tree/directory/file1} +0 -0
  52. data/{example/js/test2/test2a.js → spec/example_tree/directory/file2} +0 -0
  53. data/{example/js/test2/test2c.js → spec/example_tree/file3} +0 -0
  54. data/spec/spec_helper.rb +38 -0
  55. metadata +224 -102
  56. data/.yardopts +0 -1
  57. data/README.md +0 -4
  58. data/config.ru +0 -6
  59. data/example/config.ru +0 -6
  60. data/example/css/test1.css +0 -1
  61. data/example/css/test2/test2a.scss +0 -1
  62. data/example/css/test2/test2b.css +0 -1
  63. data/example/js/.bunchignore +0 -1
  64. data/example/js/test2/_.yml +0 -2
  65. data/example/js/test2/foo.js +0 -1
  66. data/example/js/test2/test2b.js +0 -1
  67. data/example/js/test3/test3a.js +0 -1
  68. data/example/js/test3/test3b/_.yml +0 -1
  69. data/example/js/test3/test3b/test3bi.js +0 -1
  70. data/example/js/test3/test3b/test3bii.js +0 -1
  71. data/example/js/test4/_.yml +0 -1
  72. data/example/js/test4/test4a.js +0 -1
  73. data/example/js/test4/test4b.coffee +0 -1
  74. data/example/js/test4/test4c.coffee +0 -1
  75. data/example/js/test5/test5a.jst.ejs +0 -1
  76. data/lib/bunch/abstract_node.rb +0 -25
  77. data/lib/bunch/cache.rb +0 -40
  78. data/lib/bunch/coffee_node.rb +0 -39
  79. data/lib/bunch/directory_node.rb +0 -82
  80. data/lib/bunch/ejs_node.rb +0 -50
  81. data/lib/bunch/file_node.rb +0 -25
  82. data/lib/bunch/jade_node.rb +0 -50
  83. data/lib/bunch/null_node.rb +0 -11
  84. data/lib/bunch/rack.rb +0 -38
  85. data/lib/bunch/sass_node.rb +0 -39
  86. data/test/middleware_test.rb +0 -26
  87. data/test/rack_test.rb +0 -93
  88. data/test/test_helper.rb +0 -21
data/.yardopts DELETED
@@ -1 +0,0 @@
1
- --markup markdown --private
data/README.md DELETED
@@ -1,4 +0,0 @@
1
- Bunch: (Almost) configuration-free asset bundling.
2
- ==================================================
3
-
4
- <img src="http://i.imgur.com/QqufL.png">
data/config.ru DELETED
@@ -1,6 +0,0 @@
1
- $:.unshift File.expand_path('../lib', __FILE__)
2
- require 'bunch'
3
-
4
- run Rack::URLMap.new \
5
- "/stylesheets" => Bunch::Rack.new('example/css'),
6
- "/javascripts" => Bunch::Rack.new('example/js')
@@ -1,6 +0,0 @@
1
- $:.unshift File.expand_path('../../lib', __FILE__)
2
- require 'bunch'
3
-
4
- run Rack::URLMap.new \
5
- "/stylesheets" => Bunch::Rack.new('example/css'),
6
- "/javascripts" => Bunch::Rack.new('example/js')
@@ -1 +0,0 @@
1
- body { a: 1; }
@@ -1 +0,0 @@
1
- body { b: 2; }
@@ -1 +0,0 @@
1
- body { c: 3; }
@@ -1 +0,0 @@
1
- foo
@@ -1,2 +0,0 @@
1
- - test2a
2
- - test2c
@@ -1 +0,0 @@
1
- foo;
@@ -1 +0,0 @@
1
- 4
@@ -1 +0,0 @@
1
- 5
@@ -1 +0,0 @@
1
- - test3bii
@@ -1 +0,0 @@
1
- - test4c
@@ -1 +0,0 @@
1
- 9
@@ -1 +0,0 @@
1
- 10
@@ -1 +0,0 @@
1
- 8
@@ -1 +0,0 @@
1
- 11
@@ -1,25 +0,0 @@
1
- module Bunch
2
- class AbstractNode
3
- attr_accessor :options
4
-
5
- def content
6
- raise NotImplementedError
7
- end
8
- alias name content
9
- alias target_extension content
10
-
11
- def write_to_dir(dir)
12
- write_to_file(File.join(dir, name))
13
- end
14
-
15
- def write_to_file(fn)
16
- out_file = "#{fn}#{target_extension}"
17
- File.open(out_file, 'w') { |f| f.write(content) }
18
- end
19
-
20
- protected
21
- def fetch(fn, &blk)
22
- Cache.fetch(fn, &blk)
23
- end
24
- end
25
- end
@@ -1,40 +0,0 @@
1
- module Bunch
2
- module Cache
3
- end
4
-
5
- class << Cache
6
- def init
7
- @cache = Hash.new { |h, k| h[k] = {} }
8
- end
9
-
10
- def stored?(fn)
11
- @cache.keys.include?(fn)
12
- end
13
-
14
- def read(fn)
15
- @cache[fn][:content]
16
- end
17
-
18
- def mtime(fn)
19
- @cache[fn][:mtime]
20
- end
21
-
22
- def write(fn, mtime, content)
23
- @cache[fn] = {:mtime => mtime, :content => content}
24
- end
25
-
26
- def fetch(fn, &blk)
27
- current_mtime = File.mtime(fn)
28
-
29
- if stored?(fn) && mtime(fn) == current_mtime
30
- read(fn)
31
- else
32
- content = blk.call
33
- write(fn, current_mtime, content)
34
- content
35
- end
36
- end
37
- end
38
-
39
- Cache.init
40
- end
@@ -1,39 +0,0 @@
1
- module Bunch
2
- class CoffeeNode < FileNode
3
- def initialize(fn)
4
- CoffeeNode.require_coffee
5
- @filename = fn
6
- end
7
-
8
- def content
9
- @content ||= fetch(@filename) { CoffeeScript.compile(File.read(@filename), :bare => CoffeeNode.bare) }
10
- rescue => e
11
- raise CompileError.new(e, @filename)
12
- end
13
-
14
- def name
15
- File.basename(@filename, '.coffee')
16
- end
17
-
18
- def target_extension
19
- '.js'
20
- end
21
- end
22
-
23
- class << CoffeeNode
24
- attr_writer :bare
25
-
26
- def require_coffee
27
- unless @required
28
- require 'coffee-script'
29
- @required = true
30
- end
31
- rescue LoadError
32
- raise "'gem install coffee-script' to compile .coffee files."
33
- end
34
-
35
- def bare
36
- defined?(@bare) ? @bare : (@bare = false)
37
- end
38
- end
39
- end
@@ -1,82 +0,0 @@
1
- module Bunch
2
- class DirectoryNode < AbstractNode
3
- attr_reader :root
4
-
5
- def initialize(fn)
6
- @root = Pathname.new(fn)
7
- end
8
-
9
- def filenames
10
- Dir[@root.join("*")].select { |f| f !~ /_\.yml$/ }
11
- end
12
-
13
- def children
14
- @children ||= begin
15
- children = filenames.map do |filename|
16
- Bunch.tree_for(filename, @options)
17
- end
18
-
19
- ordering_file = @root.join('_.yml')
20
-
21
- if File.exist?(ordering_file)
22
- ordering = YAML.load_file(ordering_file)
23
- ordered, unordered = children.partition { |c| ordering.include?(c.name) }
24
-
25
- (ordering - ordered.map(&:name)).each do |f|
26
- $stderr.puts "WARNING: directory #{ full_name } has no object #{ f }"
27
- end
28
-
29
- ordered.sort_by { |c| ordering.index(c.name) } + unordered.sort_by(&:name)
30
- else
31
- children.sort_by(&:name)
32
- end
33
- end
34
- end
35
-
36
- def target_extension
37
- @target_extension ||= begin
38
- exts = children.map(&:target_extension).compact.uniq
39
- if exts.count == 1
40
- exts.first
41
- elsif exts.count > 1
42
- raise "Directory #{name} contains non-homogeneous nodes: #{exts.inspect}"
43
- else
44
- nil
45
- end
46
- end
47
- end
48
-
49
- def content
50
- @content ||= children.map(&:content).join
51
- end
52
-
53
- def name
54
- File.basename(@root)
55
- end
56
-
57
- def full_name
58
- @root
59
- end
60
-
61
- def write_to_file(dir)
62
- return if filenames.count == 0
63
- super
64
- end
65
-
66
- def write_to_dir(dir)
67
- super
68
-
69
- if @options[:recurse]
70
- directory_name = File.join(dir, name)
71
- FileUtils.mkdir(directory_name)
72
- children.each do |child|
73
- child.write_to_dir(directory_name)
74
- end
75
- end
76
- end
77
-
78
- def inspect
79
- "#<DirectoryNode @root=#{@root.inspect} @children=#{children.inspect}>"
80
- end
81
- end
82
- end
@@ -1,50 +0,0 @@
1
- module Bunch
2
- class EJSNode < FileNode
3
- def initialize(fn)
4
- EJSNode.require_ejs
5
- @filename = fn
6
- end
7
-
8
- def content
9
- @content ||= fetch(@filename) {
10
- <<-JAVASCRIPT
11
- (function() {
12
- this.JST || (this.JST = {});
13
- this.JST['#{template_name}'] = #{EJS.compile(File.read(@filename))};
14
- })();
15
- JAVASCRIPT
16
- }
17
- rescue => e
18
- raise CompileError.new(e, @filename)
19
- end
20
-
21
- def name
22
- File.basename(@filename, '.ejs')
23
- end
24
-
25
- def template_name
26
- name = @filename.sub(/\.jst\.ejs$/, '')
27
-
28
- if @options[:root]
29
- name.sub(/^#{Regexp.escape(@options[:root].to_s)}\//, '')
30
- else
31
- name
32
- end
33
- end
34
-
35
- def target_extension
36
- '.js'
37
- end
38
- end
39
-
40
- class << EJSNode
41
- def require_ejs
42
- unless @required
43
- require 'ejs'
44
- @required = true
45
- end
46
- rescue LoadError
47
- raise "'gem install ejs' to compile .ejs files."
48
- end
49
- end
50
- end
@@ -1,25 +0,0 @@
1
- module Bunch
2
- class FileNode < AbstractNode
3
- attr_accessor :name, :target_extension
4
-
5
- def initialize(fn)
6
- @filename = fn
7
-
8
- if fn =~ /\.[^.]*$/
9
- @name = File.basename($`)
10
- @target_extension = $&
11
- else
12
- @name = File.basename(@filename)
13
- @target_extension = nil
14
- end
15
- end
16
-
17
- def content
18
- File.read(@filename)
19
- end
20
-
21
- def inspect
22
- @filename.inspect
23
- end
24
- end
25
- end
@@ -1,50 +0,0 @@
1
- module Bunch
2
- class JadeNode < FileNode
3
- def initialize(fn)
4
- JadeNode.require_jade
5
- @filename = fn
6
- end
7
-
8
- def content
9
- @content ||= fetch(@filename) {
10
- <<-JAVASCRIPT
11
- (function() {
12
- this.JST || (this.JST = {});
13
- this.JST['#{template_name}'] = #{Jade.compile(File.read(@filename))};
14
- })();
15
- JAVASCRIPT
16
- }
17
- rescue => e
18
- raise CompileError.new(e, @filename)
19
- end
20
-
21
- def name
22
- File.basename(@filename, '.jade')
23
- end
24
-
25
- def template_name
26
- name = @filename.sub(/\.jst\.jade/, '')
27
-
28
- if @options[:root]
29
- name.sub(/^#{Regexp.escape(@options[:root].to_s)}\//, '')
30
- else
31
- name
32
- end
33
- end
34
-
35
- def target_extension
36
- '.js'
37
- end
38
- end
39
-
40
- class << JadeNode
41
- def require_jade
42
- unless @required
43
- require 'ruby-jade'
44
- @required = true
45
- end
46
- rescue LoadError
47
- raise "'gem install ruby-jade' to compile .jade files."
48
- end
49
- end
50
- end
@@ -1,11 +0,0 @@
1
- module Bunch
2
- class NullNode < FileNode
3
- def content
4
- ''
5
- end
6
-
7
- def target_extension
8
- nil
9
- end
10
- end
11
- end
@@ -1,38 +0,0 @@
1
- module Bunch
2
- class Rack
3
- def initialize(path, opts={})
4
- @root = Pathname.new(path)
5
- @headers = {}
6
-
7
- if opts.delete(:no_cache)
8
- @headers['Cache-Control'] = 'private, max-age=0, must-revalidate'
9
- @headers['Pragma'] = 'no-cache'
10
- @headers['Expires'] = 'Thu, 01 Dec 1994 16:00:00 GMT'
11
- end
12
-
13
- Bunch.load_ignores(@root)
14
- end
15
-
16
- def call(env)
17
- path = @root.join(env['PATH_INFO'].sub(/^\//, '')).to_s
18
- type = MIME::Types.type_for(path).first || 'text/plain'
19
-
20
- [200, headers(type), [content_for(path)]]
21
- rescue => e
22
- [500, headers('text/plain'), [error_log(e)]]
23
- end
24
-
25
- private
26
- def content_for(path)
27
- Bunch.content_for(path, :root => @root)
28
- end
29
-
30
- def headers(mime_type)
31
- @headers.merge('Content-Type' => mime_type.to_s)
32
- end
33
-
34
- def error_log(e)
35
- "#{e.class}: #{e.message}\n #{e.backtrace.join("\n ")}"
36
- end
37
- end
38
- end