bunch 0.2.2 → 1.0.0pre1

Sign up to get free protection for your applications and to get access to all the features.
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