patch-asset_library 0.5.0.1

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