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.
data/README.rdoc ADDED
@@ -0,0 +1,220 @@
1
+ = asset_library
2
+
3
+ Bundles your JavaScript and CSS, so your development environment is simple to
4
+ code and your production environment is as fast as possible.
5
+
6
+ == Installation
7
+
8
+ First, install the gem:
9
+
10
+ sudo gem install alegscogs-asset_library --source http://gems.github.com
11
+
12
+ Next, add the gem into your Rails project's config/environment.rb:
13
+
14
+ config.gem 'alegscogs-asset_library', :lib => 'asset_library', :source => 'http://gems.github.com'
15
+
16
+ Finally, include the Rake tasks in your project:
17
+
18
+ echo "begin; require 'asset_library/rake_tasks'; rescue LoadError; end" > lib/tasks/asset_library.rake
19
+
20
+ == Usage
21
+
22
+ In your Rails project, edit <tt>config/asset_library.yml</tt> as described
23
+ in the following section.
24
+
25
+ Once configured, asset_library provides two helper methods for your views:
26
+
27
+ <%# outputs library.js (production) or its files (development) %>
28
+ <%= asset_library_javascript_tags(:javascripts) %>
29
+
30
+ <%# outputs library.css (production) or its files (development) %>
31
+ <%= asset_library_stylesheet_tags(:stylesheets) %>
32
+
33
+ <%# outputs library.ie6.css (production) or its files (development) %>
34
+ <!--[if lte IE 6]>
35
+ <%= asset_library_stylesheet_tags(:stylesheets, :ie6) %>
36
+ <![endif]-->
37
+
38
+ Both helpers behave differently depending on whether
39
+ <tt>ActionController::Base.perform_caching</tt> is true (that is, whether you
40
+ are in <tt>development</tt> environment or not). When caching is disabled, each
41
+ file in the module will be included. (Internet Explorer only allows 30
42
+ <tt>style</tt> and <tt>link</tt> stylesheet tags; in development mode,
43
+ <tt>import</tt> rules are used to work around the bug.) When caching is
44
+ enabled, a single tag is output.
45
+
46
+ When caching is enabled, the modules to include must be generated using:
47
+
48
+ rake asset_library:write
49
+
50
+ These moduels can be removed using:
51
+
52
+ rake asset_library:clean
53
+
54
+ A cached module is simply the concatenation of its constituent files.
55
+
56
+ == Configuration
57
+
58
+ A typical configuration (Yaml) file might look like this. In Rails, this
59
+ should be in <tt>config/asset_library.yml</tt>.
60
+
61
+ modules:
62
+ javascripts:
63
+ cache: library
64
+ optional_suffix: compressed
65
+ base: javascripts
66
+ suffix: js
67
+ files:
68
+ - vendor/jquery
69
+
70
+ # jQuery UI parts we need
71
+ - vendor/jquery-ui/ui.core
72
+ - vendor/jquery-ui/ui.draggable
73
+ - vendor/jquery-ui/ui.droppable
74
+ - vendor/jquery-ui/ui.sortable
75
+ - vendor/jquery-ui/effects.core
76
+
77
+ - lib
78
+ - plugins/*
79
+ - application
80
+ - initializers/*
81
+
82
+ tiny_mce_javascripts:
83
+ # TinyMCE doesn't give us a choice on cache name
84
+ cache: vendor/tiny_mce/tiny_mce_gzip
85
+ optional_suffix: compressed
86
+ base: javascripts
87
+ suffix: js
88
+ files:
89
+ - vendor/tiny_mce/tiny_mce
90
+ # ... it's possible to bundle all of TinyMCE together with a bit of magic
91
+
92
+ stylesheets:
93
+ cache: library
94
+ base: stylesheets
95
+ suffix: css
96
+ formats:
97
+ - ie6: [null, ie8, ie7, ie6]
98
+ - ie7: [null, ie8, ie7]
99
+ - ie8: [null, ie8]
100
+ files:
101
+ - reset
102
+ - application
103
+ - layout
104
+ - views/**/*
105
+
106
+ # in general...
107
+ # modules:
108
+ # module_name:
109
+ # cache: cache_file
110
+ # base: base_path_of_assets_in_web_root
111
+ # suffix: suffix ("css" or "js")
112
+ # formats:
113
+ # format1: ["extra_suffix_1", "extra_suffix_2"]
114
+ # format2: [null, "extra_suffix3"]
115
+ # optional_suffix: optional_suffix
116
+ # files:
117
+ # - file1_relative_to_base
118
+ # - file2_relative_to_base
119
+ # - ...
120
+ # ...
121
+
122
+ Here are what the various configuration elements mean:
123
+
124
+ <tt>module_name</tt> is the name of the module. It is passed as the first
125
+ parameter to <tt>asset_library_javascript_tags</tt> or
126
+ <tt>asset_library_stylesheet_tags</tt>.
127
+
128
+ <tt>cache</tt> is a filename, without suffix, relative to <tt>base</tt>, where
129
+ the module will be bundled when caching is enabled. (Ensure that <tt>files</tt>
130
+ does not include <tt>cache_file</tt>, even with globbing.)
131
+
132
+ <tt>base</tt> is the base path of the assets in URLs. For instance, in Rails,
133
+ where stylesheets are usually served under <tt>/stylesheets</tt>, <tt>base</tt>
134
+ should be <tt>stylesheets</tt>.
135
+
136
+ <tt>suffix</tt> is either "js" or "css", depending on whether you are including
137
+ JavaScript or CSS files.
138
+
139
+ <tt>formats</tt> allows construction of parallel modules. By default, for a
140
+ module named <tt>styles</tt> will use <tt>*.css</tt> (where <tt>*</tt> is
141
+ specified by the <tt>files</tt> option) to generate <tt>styles.css</tt>; but
142
+ filenames of the format <tt>*.suffix.css</tt> will be ignored in that search.
143
+ If a <tt>format</tt> called <tt>format1</tt> is specified as
144
+ <tt>[suffix1, suffix2]</tt>, then <tt>*.suffix1.css</tt> and
145
+ <tt>*.suffix2.css</tt> will be included, but not <tt>*.css</tt>. (To specify
146
+ <tt>*.css</tt>, put <tt>null</tt> in the format list.)
147
+
148
+ <tt>optional_suffix</tt> will cause asset_library to check for the existence of
149
+ files with <tt>optional_suffix</tt> suffixes, falling back to files without
150
+ <tt>optional_suffix</tt> if necessary. For instance, if you run all your
151
+ JavaScript files through
152
+ {YUI Compressor}[http://developer.yahoo.com/yui/compressor/] and output the
153
+ compressed version of <tt>file1.js</tt> as <tt>file1.compressed.js</tt>, then
154
+ set <tt>optional_suffix</tt> to <tt>compressed</tt>. In your development
155
+ environment, where <tt>compressed</tt> javascripts are missing,
156
+ <tt>file1.js</tt> will be included and you can debug your JavaScript. In your
157
+ production environment, create the <tt>compressed</tt> JavaScripts in the same
158
+ directory, and they will be included instead, for optimal download speed.
159
+
160
+ <tt>files</tt> is a list of files, relative to <tt>base</tt>. File globbing is
161
+ allowed; <tt>**</tt> expands to "any nesting of directories". Files which do
162
+ not exist will be excluded; globbing will include/exclude files with
163
+ <tt>formats</tt> as appropriate; and files without <tt>optional_suffix</tt>
164
+ will not be output alongside files with the same name endowed with the suffix.
165
+
166
+ == Compilers
167
+
168
+ If you would like asset library to do more than just concatenate the
169
+ files to produce your modules, then you need to specify a compiler.
170
+ Asset Library currently ships with support for one compiler: Closure
171
+ Compiler, and has an extendable framework to add more.
172
+
173
+ To specify that a module is to be compiled with the Closure Compiler,
174
+ use the <tt>compiler</tt> key for that module. You may pass
175
+ additional flags with the <tt>compiler_flags</tt> option:
176
+
177
+ modules:
178
+ javascripts:
179
+ cache: library
180
+ compiler: closure
181
+ compiler_flags: --warning_level QUIET
182
+ base: javascripts
183
+ suffix: js
184
+ files:
185
+ - javascripts/**/*
186
+
187
+ You also need to configure the compiler by using a
188
+ <tt>compilers.closure</tt> key:
189
+
190
+ compilers:
191
+ closure:
192
+ path: /path/to/closure.jar
193
+ flags: --warning_level QUIET
194
+
195
+ The full list of Closure Compiler settings are:
196
+
197
+ <tt>path</tt>: The path to the Closure Compiler jar. This is
198
+ mandatory.
199
+
200
+ <tt>flags</tt>: Flags to pass to Closure Compiler. These are combined
201
+ with any per-module flags given via the <tt>compiler_flags</tt> option
202
+ in the individual modules. Default: no flags.
203
+
204
+ <tt>java</tt>: The java command to use. This will be searched for in
205
+ the <tt>PATH</tt> unless it is an absolute filename. Default:
206
+ <tt>java</tt>.
207
+
208
+ <tt>java_flags</tt>: Flags to pass to the java interpreter. Default:
209
+ no flags.
210
+
211
+ == Copyright
212
+
213
+ I believe in software freedom, not any abomination thereof. This project is
214
+ released under the Public Domain, meaning I relinquish my copyright (to any
215
+ extend the law allows) and grant you all rights to use, modify, sell, or
216
+ otherwise take advantage of my software.
217
+
218
+ However, I do kindly request that, as a favor, you refrain from using my
219
+ software as part of an evil plan involving velociraptors and mind-controlling
220
+ robots (even though I would not be legally entitled to sue you for doing so).
@@ -0,0 +1,121 @@
1
+ begin
2
+ require 'glob_fu'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'glob_fu'
6
+ end
7
+
8
+ require 'yaml'
9
+ require File.dirname(__FILE__) + '/asset_library/compiler'
10
+ require File.dirname(__FILE__) + '/asset_library/asset_module'
11
+ require File.dirname(__FILE__) + '/asset_library/util'
12
+
13
+ class AssetLibrary
14
+ ConfigurationError = Class.new(RuntimeError)
15
+
16
+ class << self
17
+ def config_path
18
+ @config_path
19
+ end
20
+
21
+ def config_path=(config_path)
22
+ @config_path = config_path
23
+ end
24
+
25
+ #
26
+ # Root of your application.
27
+ #
28
+ # Paths of external programs (if required) are resolved relative
29
+ # to this path.
30
+ #
31
+ attr_accessor :app_root
32
+
33
+ #
34
+ # Root directory of your output files.
35
+ #
36
+ # Output files are resolved relative to this path.
37
+ #
38
+ attr_accessor :root
39
+
40
+ def cache
41
+ return true if @cache.nil?
42
+ @cache
43
+ end
44
+
45
+ def cache=(cache)
46
+ reset!
47
+ @cache = cache
48
+ end
49
+
50
+ def cache_vars
51
+ # We store cache_vars even if not caching--this is our "globals"
52
+ @cache_vars ||= {}
53
+ end
54
+
55
+ def config
56
+ return @config if cache && @config
57
+ ret = if File.exist?(config_path)
58
+ yaml = YAML.load_file(config_path) || {}
59
+ Util::symbolize_hash_keys(yaml)
60
+ else
61
+ {}
62
+ end
63
+ if !ret[:modules] && ret.values.any?{|value| value.is_a?(Hash) && value[:files]}
64
+ warn <<-EOS.gsub(/^ *\|/, '')
65
+ | WARNING: Your asset library configuration follows a
66
+ | deprecated format. Please move all your asset modules
67
+ | under a "modules:" key, as described in the
68
+ | documentation.
69
+ EOS
70
+ ret = { :modules => ret }
71
+ end
72
+ ret[:modules] ||= {}
73
+ ret[:compilers] ||= {}
74
+ @config = cache ? ret : nil
75
+ ret
76
+ end
77
+
78
+ def compilers
79
+ @compilers ||= {}
80
+ end
81
+
82
+ def asset_module(key)
83
+ module_config = config[:modules][key.to_sym]
84
+ if module_config
85
+ AssetModule.new(key, module_config)
86
+ end
87
+ end
88
+
89
+ def compiler(asset_module)
90
+ type = asset_module.compiler_type
91
+ config = self.config[:compilers][type] || {}
92
+ compilers[type] ||= Compiler.create(type, config)
93
+ end
94
+
95
+ def write_all_caches
96
+ config[:modules].keys.each do |key|
97
+ m = asset_module(key)
98
+ c = compiler(m)
99
+ c.add_asset_module(m)
100
+ end
101
+
102
+ compilers.values.each do |compiler|
103
+ compiler.write_all_caches
104
+ end
105
+ end
106
+
107
+ def delete_all_caches
108
+ asset_modules = config[:modules].keys.collect{|k| AssetLibrary.asset_module(k)}
109
+ asset_modules.each do |m|
110
+ FileUtils.rm_f(m.cache_asset.absolute_path)
111
+ end
112
+ end
113
+
114
+ def reset!
115
+ @config = nil
116
+ @cache_vars = nil
117
+ @compilers = nil
118
+ Compiler.reset!
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,35 @@
1
+ class AssetLibrary
2
+ class Asset
3
+ attr_reader(:absolute_path)
4
+
5
+ def initialize(absolute_path)
6
+ @absolute_path = absolute_path
7
+ end
8
+
9
+ def eql?(other)
10
+ self.class === other && absolute_path == other.absolute_path
11
+ end
12
+
13
+ def hash
14
+ self.absolute_path.hash
15
+ end
16
+
17
+ def relative_path
18
+ if AssetLibrary.root == '/'
19
+ absolute_path
20
+ else
21
+ absolute_path[AssetLibrary.root.length..-1]
22
+ end
23
+ end
24
+
25
+ def timestamp
26
+ File.mtime(absolute_path)
27
+ rescue Errno::ENOENT
28
+ nil
29
+ end
30
+
31
+ def relative_url
32
+ "#{relative_path}?#{timestamp.to_i.to_s}"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,94 @@
1
+ require File.dirname(__FILE__) + '/asset'
2
+
3
+ class AssetLibrary
4
+ class AssetModule
5
+ attr_reader(:config)
6
+
7
+ def initialize(name, config)
8
+ @name = name.to_s
9
+ @config = config
10
+ add_default_format
11
+ end
12
+
13
+ attr_reader :name
14
+
15
+ # Returns the type of compiler to use for this asset module.
16
+ def compiler_type
17
+ (config[:compiler] || :default).to_sym
18
+ end
19
+
20
+ # Returns the Array of compiler flags to use.
21
+ def compiler_flags
22
+ Util.normalize_flags(config[:compiler_flags])
23
+ end
24
+
25
+ # Returns an Array of Assets to include.
26
+ #
27
+ # Arguments:
28
+ # extra_suffix: if set, finds files with the given extra suffix
29
+ def assets(format = nil)
30
+ if format
31
+ assets_with_format(format)
32
+ else
33
+ assets_with_extra_suffix(nil)
34
+ end
35
+ end
36
+
37
+ # Returns an Array of Assets to include.
38
+ #
39
+ # Arguments:
40
+ # extra_suffix: if set, finds files with the given extra suffix
41
+ def assets_with_extra_suffix(extra_suffix)
42
+ return nil unless config
43
+
44
+ GlobFu.find(
45
+ config[:files],
46
+ :suffix => config[:suffix],
47
+ :extra_suffix => extra_suffix,
48
+ :root => File.join(*([AssetLibrary.root, config[:base]].compact)),
49
+ :optional_suffix => config[:optional_suffix]
50
+ ).collect { |f| Asset.new(f) }
51
+ end
52
+
53
+ # Returns an Array of Assets to include.
54
+ #
55
+ # Calls assets_with_extra_suffix for each suffix in the given format
56
+ #
57
+ # Arguments:
58
+ # format: format specified in the config
59
+ def assets_with_format(format)
60
+ return nil unless config
61
+
62
+ extra_suffixes = config[:formats][format.to_sym]
63
+ extra_suffixes.inject([]) { |r, s| r.concat(assets_with_extra_suffix(s)) }
64
+ end
65
+
66
+ # Returns an Asset representing the cache file
67
+ def cache_asset(format = nil)
68
+ extra = format ? ".#{format}" : ''
69
+ Asset.new(File.join(AssetLibrary.root, config[:base], "#{config[:cache]}#{extra}.#{config[:suffix]}"))
70
+ end
71
+
72
+ # Return the formats to generate.
73
+ def formats
74
+ (config[:formats] || {}).keys.sort_by{|sym| sym.to_s}
75
+ end
76
+
77
+ # Yield each set of input paths with their output path. All paths
78
+ # are absolute.
79
+ def each_compilation
80
+ formats.each do |format|
81
+ inputs = assets(format).map { |asset| asset.absolute_path }
82
+ output = cache_asset(format).absolute_path
83
+ yield inputs, output
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def add_default_format
90
+ config[:formats] ||= {}
91
+ config[:formats][nil] = [nil]
92
+ end
93
+ end
94
+ end