patch-asset_library 0.5.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/compiler/base'
2
+ require File.dirname(__FILE__) + '/compiler/default'
3
+ require File.dirname(__FILE__) + '/compiler/closure'
4
+
5
+ class AssetLibrary
6
+ module Compiler
7
+ Error = Class.new(RuntimeError)
8
+
9
+ class << self
10
+ # Create an instance of a compiler for the given compiler type.
11
+ def create(type, config={})
12
+ klass = compiler_classes[type] ||= built_in_class_for(type)
13
+ klass.new(config)
14
+ end
15
+
16
+ # Register a custom compiler class.
17
+ def register(type, klass)
18
+ compiler_classes[type] = klass
19
+ end
20
+
21
+ def reset!
22
+ compiler_classes.clear
23
+ end
24
+
25
+ private
26
+
27
+ def compiler_classes
28
+ @compiler_classes ||= {}
29
+ end
30
+
31
+ def built_in_class_for(type)
32
+ class_name = type.to_s.gsub(/(?:\A|_)(.)/){$1.upcase}
33
+ const_get(class_name)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ class AssetLibrary
2
+ module Compiler
3
+ class Base
4
+ def initialize(config)
5
+ @config = config || {}
6
+ @asset_modules = []
7
+ end
8
+
9
+ attr_reader :config, :asset_modules
10
+
11
+ def add_asset_module(asset_module)
12
+ @asset_modules << asset_module
13
+ end
14
+
15
+ def write_all_caches(format = nil)
16
+ raise NotImplementedError, "abstract method"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ require 'tmpdir'
2
+
3
+ class AssetLibrary
4
+ module Compiler
5
+ class Closure < Base
6
+ def initialize(config)
7
+ super
8
+ config[:path] or
9
+ raise ConfigurationError, "Please set path of closure jar with compilers.closure.path configuration setting"
10
+ config[:java] ||= 'java'
11
+ config[:java_flags] = Util.normalize_flags(config[:java_flags])
12
+ config[:path] = normalize_path(config[:path])
13
+ config[:flags] = Util.normalize_flags(config[:flags])
14
+ end
15
+
16
+ def write_all_caches(format = nil)
17
+ asset_modules.each do |asset_module|
18
+ asset_module.each_compilation do |inputs, output|
19
+ command = [config[:java]]
20
+ command.concat(config[:java_flags])
21
+ command << '-jar' << config[:path]
22
+ command.concat(config[:flags])
23
+ command.concat(asset_module.compiler_flags)
24
+ command << '--js_output_file' << "#{output}"
25
+ inputs.each do |input|
26
+ command << '--js' << input
27
+ end
28
+ system *command or
29
+ raise Error, "closure compiler failed"
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def normalize_path(path)
37
+ (Pathname(AssetLibrary.app_root) + path).to_s
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ class AssetLibrary
2
+ module Compiler
3
+ class Default < Base
4
+ def write_all_caches(format = nil)
5
+ asset_modules.each do |asset_module|
6
+ asset_module.each_compilation do |inputs, output|
7
+ open(output, 'w') do |file|
8
+ inputs.each { |path| file << File.read(path) }
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,98 @@
1
+ require 'enumerator'
2
+
3
+ class AssetLibrary
4
+ module Helpers
5
+ def asset_library_javascript_tags(module_key, format = nil)
6
+
7
+ m = AssetLibrary.asset_module(module_key)
8
+ if AssetLibrary.cache
9
+ AssetLibrary.cache_vars[:javascript_tags] ||= {}
10
+ AssetLibrary.cache_vars[:javascript_tags][module_key] ||= asset_library_priv.script_tag(m.cache_asset)
11
+ else
12
+ m.assets(format).collect{|a| asset_library_priv.script_tag(a)}.join("\n")
13
+ end
14
+ end
15
+
16
+ def asset_library_stylesheet_tags(module_key, *args)
17
+ html_options = args.last.is_a?(Hash) ? args.pop : {}
18
+ format = args[0]
19
+
20
+ m = AssetLibrary.asset_module(module_key)
21
+ if AssetLibrary.cache
22
+ AssetLibrary.cache_vars[:stylesheet_tags] ||= {}
23
+ AssetLibrary.cache_vars[:stylesheet_tags][[module_key, format, request.protocol, request.host_with_port]] ||= asset_library_priv.style_tag(m.cache_asset(format), html_options)
24
+ else
25
+ asset_library_priv.import_styles_tag(m.assets(format), html_options)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def asset_library_priv
32
+ @asset_library_priv ||= Priv.new(self)
33
+ end
34
+
35
+ class Priv
36
+ # Don't pollute helper's class's namespace with all our methods; put
37
+ # them here instead
38
+
39
+ attr_accessor :helper
40
+
41
+ def initialize(helper)
42
+ @helper = helper
43
+ end
44
+
45
+ def url(asset)
46
+ absolute_url(asset.relative_url)
47
+ end
48
+
49
+ def absolute_url(relative_url)
50
+ host = helper.__send__(:compute_asset_host, relative_url) if helper.respond_to?(:compute_asset_host, true)
51
+
52
+ host = nil if host == '' # Rails sets '' by default
53
+
54
+ if host && !(host =~ %r{^[-a-z]+://})
55
+ controller = helper.instance_variable_get(:@controller)
56
+ request = controller && controller.respond_to?(:request) && controller.request
57
+ host = request && "#{request.protocol}#{host}"
58
+ end
59
+
60
+ if host
61
+ "#{host}#{relative_url}"
62
+ else
63
+ relative_url
64
+ end
65
+ end
66
+
67
+ def script_tag(asset)
68
+ content_tag(:script, "", {:type => "text/javascript", :src => url(asset)})
69
+ end
70
+
71
+ def style_tag(asset, html_options = {})
72
+ "<link rel=\"stylesheet\" type=\"text/css\" href=\"#{url(asset)}\" #{attributes_from_hash(html_options)}/>"
73
+ end
74
+
75
+ def import_styles_tag(assets, html_options = {})
76
+ a = []
77
+ assets.each_slice(30) do |subset|
78
+ a << import_style_tag(subset, html_options)
79
+ end
80
+ a.join("\n")
81
+ end
82
+
83
+ def import_style_tag(assets, html_options = {})
84
+ imports = assets.collect{ |a| "@import \"#{url(a)}\";" }
85
+ content_tag(:style, "\n#{imports.join("\n")}\n", html_options.merge(:type => "text/css"))
86
+ end
87
+
88
+ def content_tag(name, content, options = {})
89
+ "<#{name} #{attributes_from_hash(options)}>#{content}</#{name}>"
90
+ end
91
+
92
+ def attributes_from_hash(options = {})
93
+ options.to_a.collect{|k, v| "#{k}=\"#{v}\""}.join(" ")
94
+ end
95
+
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,22 @@
1
+ def init_asset_library
2
+ require 'asset_library'
3
+
4
+ # TODO: Find a way to not-hard-code these paths?
5
+ AssetLibrary.config_path = Rails.root + 'config/asset_library.yml'
6
+ AssetLibrary.root = Rails.public_path
7
+ AssetLibrary.app_root = Rails.root
8
+ end
9
+
10
+ namespace(:asset_library) do
11
+ desc "Writes all asset caches specified in config/asset.yml by concatenating the constituent files."
12
+ task(:write) do
13
+ init_asset_library
14
+ AssetLibrary.write_all_caches
15
+ end
16
+
17
+ desc "Deletes all asset caches specified in config/asset.yml"
18
+ task(:clean) do
19
+ init_asset_library
20
+ AssetLibrary.delete_all_caches
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ require 'shellwords'
2
+
3
+ class AssetLibrary
4
+ module Util
5
+ class << self
6
+ def symbolize_hash_keys(hash)
7
+ return hash unless Hash === hash # because we recurse
8
+ hash.inject({}) do |ret, (key, value)|
9
+ ret[(key.to_sym rescue key) || key] = symbolize_hash_keys(value)
10
+ ret
11
+ end
12
+ end
13
+
14
+ def normalize_flags(flags)
15
+ case flags
16
+ when String
17
+ Shellwords.shellwords(flags)
18
+ when nil
19
+ []
20
+ else
21
+ flags
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ AssetLibrary.cache = ActionController::Base.perform_caching
2
+ AssetLibrary.config_path = Rails.root + 'config/asset_library.yml'
3
+ AssetLibrary.root = Rails.public_path
4
+ AssetLibrary.app_root = Rails.root
@@ -0,0 +1,183 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ require 'set'
4
+
5
+ describe(AssetLibrary::AssetModule) do
6
+ before(:each) do
7
+ AssetLibrary.stub!(:root).and_return(prefix)
8
+ end
9
+
10
+ after(:each) do
11
+ wipe_fs
12
+ end
13
+
14
+ describe('#assets') do
15
+ it('should include file1 and file2') do
16
+ files = [ '/c/file1.css', '/c/file2.css' ]
17
+ stub_fs(files)
18
+ m(css_config(:files => ['file1', 'file2'])).assets.collect{|a| a.absolute_path}.should == ["#{prefix}/c/file1.css", "#{prefix}/c/file2.css"]
19
+ end
20
+
21
+ it('should not include file2 if that does not exist') do
22
+ files = [ '/c/file1.css' ]
23
+ stub_fs(files)
24
+ m(css_config(:files => ['file1', 'file2'])).assets.collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.css" ]
25
+ end
26
+
27
+ it('should not include other files') do
28
+ files = [ '/c/file1.css', '/c/file2.css' ]
29
+ stub_fs(files)
30
+ m(css_config(:files => ['file1'])).assets.collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.css" ]
31
+ end
32
+
33
+ it('should glob filenames') do
34
+ files = [ '/c/file1.css', '/c/file2.css', '/c/other_file.css' ]
35
+ stub_fs(files)
36
+ m(css_config(:files => ['file*'])).assets.collect{|a| a.absolute_path}.should == ["#{prefix}/c/file1.css", "#{prefix}/c/file2.css"]
37
+ end
38
+
39
+ it('should glob directories') do
40
+ files = [ '/c/file1.css', '/c/a/file2.css', '/c/b/a/file3.css' ]
41
+ stub_fs(files)
42
+ m(css_config(:files => ['**/file*'])).assets.collect{|a| a.absolute_path}.should == ["#{prefix}/c/a/file2.css", "#{prefix}/c/b/a/file3.css", "#{prefix}/c/file1.css"]
43
+ end
44
+
45
+ it('should use :optional_suffix when appropriate') do
46
+ files = [ '/c/file1.css', '/c/file1.css.o' ]
47
+ stub_fs(files)
48
+ m(css_config(:optional_suffix => 'o', :files => ['file1'])).assets.collect{|a| a.absolute_path}.should == ["#{prefix}/c/file1.css.o"]
49
+ end
50
+
51
+ it('should show :optional_suffix file even if original is absent') do
52
+ files = [ '/c/file1.css.o' ]
53
+ stub_fs(files)
54
+ m(css_config(:optional_suffix => 'o', :files => ['file1'])).assets.collect{|a| a.absolute_path}.should == ["#{prefix}/c/file1.css.o"]
55
+ end
56
+
57
+ it('should ignore :optional_suffix when suffixed file is not present') do
58
+ stub_fs([ '/c/file1.css' ])
59
+ m(css_config(:optional_suffix => 'o', :files => ['file1'])).assets.collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.css" ]
60
+ end
61
+
62
+ it('should pick files with :extra_suffix') do
63
+ stub_fs([ '/c/file1.e.css' ])
64
+ m(css_config(:files => ['file1'])).assets_with_extra_suffix('e').collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.e.css" ]
65
+ end
66
+
67
+ it('should ignore non-suffixed files when :extra_suffix is set') do
68
+ stub_fs([ '/c/file1.css' ])
69
+ m(css_config(:files => ['file1'])).assets_with_extra_suffix('e').collect{|a| a.absolute_path}.should == []
70
+ end
71
+
72
+ it('should use extra suffixes with format') do
73
+ stub_fs([ '/c/file1.e1.css', '/c/file1.e2.css' ])
74
+ m(css_config(:files => ['file1'], :formats => { :f1 => [ 'e1', 'e2' ] })).assets_with_format(:f1).collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.e1.css", "#{prefix}/c/file1.e2.css" ]
75
+ end
76
+
77
+ it('should ignore extra suffixes unspecified in format') do
78
+ stub_fs([ '/c/file1.e1.css', '/c/file1.e2.css' ])
79
+ m(css_config(:files => ['file1'], :formats => { :f1 => [ 'e1' ] })).assets_with_format(:f1).collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.e1.css" ]
80
+ end
81
+
82
+ it('should allow nil suffixes in format') do
83
+ stub_fs([ '/c/file1.css', '/c/file1.e1.css' ])
84
+ m(css_config(:files => ['file1'], :formats => { :f1 => [nil, 'e1'] })).assets_with_format(:f1).collect{|a| a.absolute_path}.should == ["#{prefix}/c/file1.css", "#{prefix}/c/file1.e1.css" ]
85
+ end
86
+
87
+ it('should combine :extra_suffix with :optional_suffix') do
88
+ stub_fs([ '/c/file1.e.css', '/c/file1.e.css.o' ])
89
+ m(css_config(:files => ['file1'], :optional_suffix => 'o')).assets_with_extra_suffix('e').collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.e.css.o" ]
90
+ end
91
+
92
+ it('should ignore too many dots when globbing') do
93
+ stub_fs([ '/c/file1.x.css' ])
94
+ m(css_config(:files => ['file1*'])).assets.collect{|a| a.absolute_path}.should == []
95
+ end
96
+
97
+ it('should pick files with :extra_suffix when globbing') do
98
+ stub_fs([ '/c/file1.e.css', '/c/file2.css' ])
99
+ m(css_config(:files => ['file*'])).assets_with_extra_suffix('e').collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.e.css" ]
100
+ end
101
+
102
+ it('should pick files with :optional_suffix when globbing') do
103
+ stub_fs([ '/c/file.css', '/c/file.css.o' ])
104
+ m(css_config(:optional_suffix => 'o', :files => ['file*'])).assets.collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file.css.o" ]
105
+ end
106
+
107
+ it('should pick files with both :extra_suffix and :optional_suffix when globbing') do
108
+ stub_fs([ '/c/file.css', '/c/file.e.css', '/c/file.e.css.o' ])
109
+ m(css_config(:optional_suffix => 'o', :files => ['file*'])).assets_with_extra_suffix('e').collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file.e.css.o" ]
110
+ end
111
+ end
112
+
113
+ describe('#cache_asset') do
114
+ it('should use options[:cache]') do
115
+ m(css_config).cache_asset.absolute_path.should == "#{prefix}/c/cache.css"
116
+ end
117
+
118
+ it('should use :format if set') do
119
+ m(css_config).cache_asset(:e).absolute_path.should == "#{prefix}/c/cache.e.css"
120
+ end
121
+ end
122
+
123
+ describe('#each_compilation') do
124
+ it('should yield each set of input and output files') do
125
+ stub_fs([ '/c/file1.css', '/c/file1.e1.css', '/c/file2.css' ])
126
+ asset_module = m(css_config( :files => ['file1', 'file2'], :formats => { :f1 => [nil, 'e1'], :f2 => ['e1'] } ))
127
+ yields = asset_module.to_enum(:each_compilation).to_a
128
+ yields.should == [
129
+ [["#{prefix}/c/file1.css", "#{prefix}/c/file2.css"], "#{prefix}/c/cache.css"],
130
+ [["#{prefix}/c/file1.css", "#{prefix}/c/file2.css", "#{prefix}/c/file1.e1.css"], "#{prefix}/c/cache.f1.css"],
131
+ [["#{prefix}/c/file1.e1.css"], "#{prefix}/c/cache.f2.css"],
132
+ ]
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def m(config)
139
+ AssetLibrary::AssetModule.new(:name, config)
140
+ end
141
+
142
+ def js_config(options = {})
143
+ {
144
+ :cache => 'cache',
145
+ :base => 'j',
146
+ :suffix => 'js',
147
+ :files => [ 'file1', 'file2' ]
148
+ }.merge(options)
149
+ end
150
+
151
+ def css_config(options = {})
152
+ {
153
+ :cache => 'cache',
154
+ :base => 'c',
155
+ :suffix => 'css',
156
+ :files => [ 'file1', 'file2' ]
157
+ }.merge(options)
158
+ end
159
+
160
+ def prefix
161
+ @prefix ||= File.dirname(__FILE__) + '/deleteme'
162
+ end
163
+
164
+ def stub_fs(filenames)
165
+ wipe_fs
166
+ FileUtils.mkdir(prefix)
167
+
168
+ filenames.each do |file|
169
+ path = File.join(prefix, file)
170
+ dir = File.dirname(path)
171
+ unless File.exist?(dir)
172
+ FileUtils.mkdir_p(dir)
173
+ end
174
+ File.open(path, 'w') { |f| f.write("#{file}\n") }
175
+ end
176
+ end
177
+
178
+ def wipe_fs
179
+ if File.exist?(prefix)
180
+ FileUtils.rm_r(prefix)
181
+ end
182
+ end
183
+ end