blender 0.20

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,3 @@
1
+ === 0.20.0 / 2008-10-13
2
+
3
+ Release Candidate 1
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Chris Griego
2
+ (c) 2008 Blake Elshire
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ History.txt
2
+ MIT-LICENSE
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/blend
7
+ lib/front_end_architect/blender.rb
8
+ lib/front_end_architect/hash.rb
9
+ lib/yui/LICENSE
10
+ lib/yui/yuicompressor.jar
@@ -0,0 +1,83 @@
1
+ = Front-End Blender
2
+
3
+ == What is Blender?
4
+
5
+ Blender is like ant or make for the front-end. It aggregates and compresses
6
+ CSS and/or JavaScript assets for a site into efficient, production-ready files.
7
+
8
+ == The Blendfile
9
+
10
+ The Blendfile, named Blendfile.yaml by default, is the configuration file
11
+ that tells Blender which source files are combined into which output files.
12
+ The file uses the YAML format. The output file is listed as hash key and
13
+ source files are the hash values as an array. Here is a sample Blendfile:
14
+
15
+ # Blendfile.yaml for www.boldpx.com
16
+ _behavior:
17
+ _global-min.js:
18
+ - ../_vendor/jquery/jquery.js
19
+ - ../_vendor/shadowbox/src/js/adapter/shadowbox-jquery.js
20
+ - ../_vendor/shadowbox/src/js/shadowbox.js
21
+ - _global.js
22
+ - _analytics.js
23
+ - ../vendor/google-analytics/ga.js
24
+ _style:
25
+ _global:
26
+ min.css:
27
+ - ../../_vendor/shadowbox/src/css/shadowbox.css
28
+ - typography.css
29
+ - typography-print.css
30
+ - colors.css
31
+ - colors-print.css
32
+ - layout-screen.css
33
+ - layout-print.css
34
+
35
+ == Usage
36
+
37
+ Usage: blend [options]
38
+ -h, --help Show this message
39
+ -V, --version Prints Blender's version number
40
+ -g, --generate Generate a stub Blendfile
41
+ -f, --file FILE Use given Blendfile
42
+ -r, --root ROOT Specify the path to the web root directory
43
+ -t, --type TYPE Select file type to blend (css, js)
44
+ -m, --min [MINIFIER] Select minifier to use (yui, none)
45
+ -c, --cache-buster [BUSTER] Append cache busters to URLs in CSS, defaults to timestamps
46
+ --force Force blending when source files aren't newer than output files
47
+ --yui=YUIOPTS Pass arguments to YUI Compressor
48
+ -d, --data EXPERIMENTAL Convert url(file.ext) to url(data:) in CSS files
49
+ -z, --gzip EXPERIMENTAL Also create gzip output files
50
+
51
+ == Examples
52
+
53
+ In your site directory run 'blend' to minify CSS and JavaScript.
54
+ blend
55
+
56
+ Other examples:
57
+ blend --generate
58
+ blend --yui='--preserve-semi'
59
+ blend -t css
60
+ blend -t css -d
61
+ blend -f public/Blendfile.yaml
62
+
63
+ == Installation
64
+
65
+ To install the RubyGem, run the following at the command line (you may need to use a command such as su or sudo):
66
+ gem install mime-types
67
+ gem install colored
68
+ gem install front-end-blender --source http://gems.github.com
69
+
70
+ If you're using Windows, you'll also need to run the following:
71
+ gem install win32console
72
+
73
+ * Java v1.4 or greater is required, http://java.com/en/
74
+ * Ruby v1.8.6 or greater is required, http://www.ruby-lang.org/en/downloads/
75
+ * RubyGems v1.2 or greater is recommended, http://rubygems.org/read/chapter/3
76
+
77
+ == License
78
+
79
+ Copyright (c) 2008 Chris Griego
80
+ (c) 2008 Blake Elshire
81
+
82
+ Blender is freely distributable under the terms of an MIT-style license.
83
+ For details, see http://www.opensource.org/licenses/mit-license.php
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2008 Chris Griego
2
+ # (c) 2008 Blake Elshire
3
+ #
4
+ # Blender is freely distributable under the terms of an MIT-style license.
5
+ # For details, see http://www.opensource.org/licenses/mit-license.php
6
+
7
+ $:.unshift File.join(File.dirname(File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__), *%w[lib])
8
+
9
+ require 'rubygems'
10
+ require 'hoe'
11
+ require 'front_end_architect/blender.rb'
12
+
13
+ hoe = Hoe.new('blender', FrontEndArchitect::Blender::VERSION) do |p|
14
+ p.author = ['Blake Elshire', 'Chris Griego']
15
+ p.email = 'blender@front-end-architect.com'
16
+ p.summary = 'Blender outputs efficient, production-ready CSS and/or JavaScript assets.'
17
+ p.description = 'Blender is like ant or make for the front-end. It aggregates and compresses CSS and/or JavaScript assets for a site into efficient, production-ready files.'
18
+
19
+ p.url = 'http://www.front-end-architect.com/blender/'
20
+
21
+ p.extra_deps << ['mime-types', '>= 1.15']
22
+ p.extra_deps << ['colored', '>= 1.1']
23
+
24
+ p.spec_extras[:requirements] = 'Java, v1.4 or greater'
25
+
26
+ p.remote_rdoc_dir = '' # Release to root
27
+ end
28
+
29
+ task :update_gemspec do
30
+ File.open("#{hoe.name}.gemspec", 'w') do |gemspec|
31
+ gemspec << hoe.spec.to_ruby
32
+ end
33
+ end
34
+
35
+ task :debug_changes do
36
+ puts hoe.changes
37
+ end
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright (c) 2008 Chris Griego
4
+ # (c) 2008 Blake Elshire
5
+ #
6
+ # Blender is freely distributable under the terms of an MIT-style license.
7
+ # For details, see http://www.opensource.org/licenses/mit-license.php
8
+
9
+ $:.unshift File.join(File.dirname(File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__), *%w[.. lib])
10
+ require 'optparse'
11
+ require 'front_end_architect/blender'
12
+
13
+ options = {}
14
+
15
+ opts = OptionParser.new do |opts|
16
+ opts.on('-h', '--help', "Show this message") { puts opts; exit 0 }
17
+ opts.on('-V', '--version', "Show the version number") { puts "Front-End Blender v#{Blender::VERSION}"; exit 0 }
18
+ opts.on('-g', '--generate', String, "Generate a stub Blendfile") { options[:generate] = true }
19
+ opts.on('-f FILE', '--file FILE', String, "Use given Blendfile") {|f| options[:blendfile] = f }
20
+ opts.on('-r ROOT', '--root ROOT', String, "Set the path to the web root directory") {|r| options[:root] = r }
21
+ opts.on('-t TYPE', '--type TYPE', [:css, :js], "Select file type to blend (css, js)") {|t| options[:file_type] = t }
22
+ opts.on('-m [MINIFIER]', '--min [MINIFIER]', [:yui, :none], "Select minifier to use (yui, none)") {|m| options[:min] = m.nil? ? :none : m.to_sym }
23
+ opts.on('-c [BUSTER]', '--cache-buster [BUSTER]', String, "Append cache busters to URLs in CSS, defaults to timestamps") {|b| options[:cache_buster] = b.nil? ? :mtime : b }
24
+ opts.on( '--force', String, "Force blending when source files aren't newer than output files") { options[:force] = true }
25
+ opts.on( '--yui=YUIOPTS', String, "Pass arguments to YUI Compressor") {|o| options[:yuiopts] = o }
26
+ opts.on('-d', '--data', String, "EXPERIMENTAL Convert url(file.ext) to url(data:) in CSS files") { options[:data] = true }
27
+ opts.on('-z', '--gzip', String, "EXPERIMENTAL Also create gzip output files") { options[:gzip] = true }
28
+
29
+ opts.parse!(ARGV) rescue return false
30
+ end
31
+
32
+ begin
33
+ blender = FrontEndArchitect::Blender.new(options)
34
+
35
+ if (options[:generate])
36
+ blender.generate
37
+ else
38
+ blender.blend
39
+ end
40
+ rescue Exception => e
41
+ puts e
42
+ exit 1
43
+ end
@@ -0,0 +1,402 @@
1
+ # Copyright (c) 2008 Chris Griego
2
+ # (c) 2008 Blake Elshire
3
+ #
4
+ # Blender is freely distributable under the terms of an MIT-style license.
5
+ # For details, see http://www.opensource.org/licenses/mit-license.php
6
+
7
+ $:.unshift File.join(File.dirname(File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__), *%w[..])
8
+
9
+ require 'rubygems'
10
+ require 'yaml'
11
+ require 'base64'
12
+ require 'benchmark'
13
+ require 'colored'
14
+ require 'mime/types'
15
+ require 'find'
16
+ require 'pathname'
17
+ require 'zlib'
18
+ require 'front_end_architect/hash'
19
+
20
+ module FrontEndArchitect
21
+ class Blender
22
+ VERSION = '0.20'
23
+
24
+ FILTER_REGEX = /filter: ?[^?]+\(src=(['"])([^\?'"]+)(\?(?:[^'"]+)?)?\1,[^?]+\1\);/im
25
+ IMPORT_REGEX = /@import(?: url\(| )(['"]?)([^\?'"\)\s]+)(\?(?:[^'"\)]+)?)?\1\)?(?:[^?;]+)?;/im
26
+ URL_REGEX = /url\((['"]?)([^\?'"\)]+)(\?(?:[^'"\)]+)?)?\1?\)/im
27
+
28
+ DEFAULT_OPTIONS = {
29
+ :blendfile => 'Blendfile.yaml',
30
+ :data => false,
31
+ :force => false,
32
+ :root => File.dirname(:blendfile.to_s),
33
+ :min => :yui,
34
+ }
35
+
36
+ def initialize(opts)
37
+ @options = DEFAULT_OPTIONS.merge(opts)
38
+ end
39
+
40
+ def blend
41
+ elapsed = Benchmark.realtime do
42
+ unless File.exists? @options[:blendfile]
43
+ raise "Couldn't find '#{@options[:blendfile]}'"
44
+ end
45
+
46
+ blendfile_mtime = File.mtime(@options[:blendfile])
47
+ blender = YAML::load_file @options[:blendfile]
48
+
49
+ Dir.chdir(File.dirname(@options[:blendfile]))
50
+
51
+ blender = flatten_blendfile(blender)
52
+
53
+ blender.each do |output_name, sources|
54
+ output_name = Pathname.new(output_name).cleanpath.to_s
55
+
56
+ output_new = false
57
+ gzip_output_name = output_name + '.gz'
58
+
59
+ # Checks the type flag and if the current file meets the type requirements continues
60
+ if output_name.match '.' + @options[:file_type].to_s
61
+ file_type = output_name.match(/\.css/) ? 'css' : 'js'
62
+
63
+ # Checks if output file exists and checks the mtimes of the source files to the output file if new creates a new file
64
+ if File.exists?(output_name) && (!@options[:gzip] || File.exists?(gzip_output_name))
65
+ output_files = []
66
+ output_files << File.mtime(output_name)
67
+ output_files << File.mtime(gzip_output_name) if @options[:gzip] && File.exists?(gzip_output_name)
68
+
69
+ oldest_output = output_files.sort.first
70
+
71
+ if blendfile_mtime > oldest_output
72
+ output_new = true
73
+ else
74
+ sources.each do |i|
75
+ if File.mtime(i) > oldest_output
76
+ output_new = true
77
+ break
78
+ end
79
+ end
80
+ end
81
+
82
+ if output_new || @options[:force]
83
+ if File.writable?(output_name) && !(@options[:gzip] && !File.writable?(gzip_output_name))
84
+ create_output(output_name, sources, file_type)
85
+ else
86
+ puts 'Permission Denied:'.white_on_red + ' ' + output_name.red
87
+ puts 'Permission Denied:'.white_on_red + ' ' + gzip_output_name.red if @options[:gzip]
88
+ end
89
+ else
90
+ puts 'Skipping: '.yellow + output_name.yellow
91
+ puts 'Skipping: '.yellow + gzip_output_name.yellow if @options[:gzip]
92
+ end
93
+ else
94
+ create_output(output_name, sources, file_type)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ puts sprintf('%.5f', elapsed) + ' seconds'
101
+ end
102
+
103
+ def generate
104
+ if File.exists?(@options[:blendfile]) && !@options[:force]
105
+ raise "'#{@options[:blendfile]}' already exists"
106
+ end
107
+
108
+ blend_files = Hash.new
109
+
110
+ Find.find(Dir.getwd) do |f|
111
+ basename = File.basename(f)
112
+
113
+ if FileTest.directory?(f) && (basename[0] == ?. || basename.match(/^(yui|tinymce|dojo|wp-includes|wp-admin|mint)$/i) || (File.basename(f).downcase == 'rails' && File.basename(File.dirname(f)).downcase == 'vendor'))
114
+ Find.prune
115
+ elsif !(basename.match(/(^|[-.])(pack|min)\.(css|js)$/i) || basename.match(/^(sifr\.js|ext\.js|mootools.*\.js)$/i))
116
+ # TODO Check file contents instead of name for minification (port YSlow's isMinified)
117
+ f.gsub!(Dir.getwd.to_s + '/', '')
118
+
119
+ if File.extname(f).downcase == '.css' || File.extname(f).downcase == '.js'
120
+ min_file = basename.sub(/\.(css|js)$/i, '-min.\1')
121
+ path = File.dirname(f).split('/') # File::dirname depends on /
122
+
123
+ path.push min_file
124
+ path.push [basename]
125
+
126
+ h = path.reverse.inject { |m,v| { v => m } }
127
+
128
+ blend_files.deep_merge!(h).inspect
129
+ end
130
+ end
131
+ end
132
+
133
+ File.open(@options[:blendfile], 'w') do |blendfile|
134
+ blendfile << blend_files.to_yaml
135
+ end
136
+ end
137
+
138
+ protected
139
+
140
+ def flatten_blendfile(value, key=nil, context=[])
141
+ if value.is_a? Hash
142
+ context << key unless key.nil?
143
+
144
+ new_hash = {}
145
+
146
+ value.each do |k, v|
147
+ new_hash.merge! flatten_blendfile(v, k, context.dup)
148
+ end
149
+
150
+ new_hash
151
+ else
152
+ prefix = context.join(File::SEPARATOR)
153
+ prefix += File::SEPARATOR unless context.empty?
154
+
155
+ value.each_index do |i|
156
+ unless value[i].match(/^(\/[^\/]+.+)$/)
157
+ value[i] = prefix + value[i]
158
+ else
159
+ value[i] = @options[:root] + value[i]
160
+ end
161
+ end
162
+
163
+ return { (prefix + key) => value }
164
+ end
165
+ end
166
+
167
+ def create_output(output_name, sources, type)
168
+ output = ''
169
+
170
+ File.open(output_name, 'w') do |output_file|
171
+ # Determine full path of the output file
172
+ output_path = Pathname.new(File.expand_path(File.dirname(output_name)))
173
+ imports = ''
174
+
175
+ sources.each do |i|
176
+ if File.extname(i).downcase == '.css'
177
+ processed_output, processed_imports = process_css(i, output_path)
178
+
179
+ output << processed_output
180
+ imports << processed_imports
181
+ else
182
+ output << IO.read(i)
183
+ end
184
+ end
185
+
186
+ if File.extname(output_name).downcase == '.css' && !imports.empty?
187
+ output.insert(0, imports)
188
+ end
189
+
190
+ # Compress
191
+ if @options[:min] == :yui
192
+ libdir = File.join(File.dirname(File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__), *%w[.. .. lib])
193
+
194
+ IO.popen("java -jar #{libdir}/yui/yuicompressor.jar #{@options[:yuiopts]} --type #{type}", 'r+') do |io|
195
+ io.write output
196
+ io.close_write
197
+
198
+ output = io.read
199
+
200
+ if File.extname(output_name) == '.css'
201
+ output.gsub! ' and(', ' and (' # Workaround for YUI Compressor Bug #1938329
202
+ output.gsub! '*/;}', '*/}' # Workaround for YUI Compressor Bug #1961175
203
+ end
204
+ end
205
+
206
+ if $? == 32512 # command not found
207
+ raise "\nBlender requires Java, v1.4 or greater, to be installed for YUI Compressor"
208
+ end
209
+ end
210
+
211
+ # Data
212
+ if @options[:data]
213
+ if File.extname(output_name).downcase == '.css'
214
+ output = output.gsub(URL_REGEX) do
215
+ unless $2.downcase.include?('.css')
216
+ mime_type = MIME::Types.type_for($2)
217
+ url_contents = make_data_uri(IO.read($2), mime_type[0])
218
+ else
219
+ url_contents = $2
220
+ end
221
+ %Q!url(#{url_contents})!
222
+ end
223
+ end
224
+ end
225
+
226
+ output_file << output
227
+ end
228
+
229
+ puts output_name.green
230
+
231
+ if @options[:gzip]
232
+ output_gzip = output_name + '.gz'
233
+
234
+ Zlib::GzipWriter.open(output_gzip) do |gz|
235
+ gz.write(output)
236
+ end
237
+
238
+ puts output_gzip.green
239
+ end
240
+ end
241
+
242
+ # TODO Move this to a seperate class and clean it up A LOT. For 1.1
243
+ def process_css(input_file, output_path)
244
+ # Determine full path of input file
245
+ input_path = Pathname.new(File.dirname(input_file))
246
+ input = IO.read(input_file)
247
+ found_imports = ''
248
+
249
+ # Find filter statements and append cache busters to URLs
250
+ if @options[:cache_buster]
251
+ input = input.gsub(FILTER_REGEX) do |filter|
252
+ uri = $2
253
+ cbuster = $3
254
+ unless uri.match(/^(https:\/\/|http:\/\/|\/\/)/i)
255
+ full_path = File.expand_path($2, File.dirname(input_file))
256
+ buster = make_cache_buster(full_path, $3)
257
+ new_path = uri.to_s + buster
258
+
259
+ %Q!filter='#{new_path}'!
260
+ else
261
+ %Q!filter='#{uri}#{cbuster}'!
262
+ end
263
+ end
264
+ end
265
+
266
+ # Handle @import statements URL rewrite and adding cache busters
267
+ input = input.gsub(IMPORT_REGEX) do |import|
268
+ uri = $2
269
+ asset_path = Pathname.new(File.expand_path(uri, input_path))
270
+
271
+ if uri.match(/^(\/[^\/]+.+)$/)
272
+ asset_path = Pathname.new(File.join(File.expand_path(@options[:root]), uri))
273
+ end
274
+
275
+ unless uri.match(/^(https:\/\/|http:\/\/|\/\/)/i)
276
+ if (output_path != input_path)
277
+
278
+ new_path = asset_path.relative_path_from(output_path)
279
+
280
+ if @options[:cache_buster]
281
+ buster = make_cache_buster(asset_path, $3)
282
+ import.gsub!(uri, new_path.to_s+buster)
283
+ else
284
+ import.gsub!(uri, new_path)
285
+ end
286
+ else
287
+ if @options[:cache_buster]
288
+ buster = make_cache_buster(asset_path, $3)
289
+ import.gsub!(uri, asset_path.to_s+buster)
290
+ end
291
+ end
292
+ end
293
+
294
+ found_imports << import
295
+
296
+ %Q!!
297
+ end
298
+
299
+ if output_path == input_path
300
+ if @options[:data]
301
+ input = input.gsub(URL_REGEX) do
302
+ uri = $2
303
+ cbuster = $3
304
+
305
+ unless uri.match(/^(https:\/\/|http:\/\/|\/\/)/i)
306
+ new_path = File.expand_path($2, File.dirname(input_file))
307
+
308
+ if uri.match(/^(\/[^\/]+.+)$/)
309
+ new_path = Pathname.new(File.join(File.expand_path(@options[:root]), uri))
310
+ end
311
+
312
+ %Q!url(#{new_path})!
313
+ else
314
+ %Q!url(#{uri}#{cbuster})!
315
+ end
316
+ end
317
+ elsif @options[:cache_buster]
318
+ input = input.gsub(URL_REGEX) do
319
+ unless uri.match(/^(https:\/\/|http:\/\/|\/\/)/i)
320
+ uri = $2
321
+ cbuster = $3
322
+
323
+ if uri.match(/^(\/[^\/]+.+)$/)
324
+ uri = Pathname.new(File.join(File.expand_path(@options[:root]), uri))
325
+ end
326
+
327
+ if @options[:cache_buster]
328
+ buster = make_cache_buster(uri, $3)
329
+ new_path = uri.to_s+buster
330
+ end
331
+
332
+ %Q!url(#{new_path})!
333
+ else
334
+ %Q!url(#{uri}#{cbuster})!
335
+ end
336
+ end
337
+ end
338
+
339
+ return input, found_imports
340
+ else
341
+ # Find all url(.ext) in file and rewrite relative url from output directory.
342
+ input = input.gsub(URL_REGEX) do
343
+ uri = $2
344
+ cbuster = $3
345
+
346
+ unless uri.match(/^(https:\/\/|http:\/\/|\/\/)/i)
347
+ if @options[:data]
348
+ # if doing data conversion rewrite url as an absolute path
349
+ new_path = File.expand_path(uri, File.dirname(input_file))
350
+
351
+ if uri.match(/^(\/[^\/]+.+)$/)
352
+ new_path = Pathname.new(File.join(File.expand_path(@options[:root]), uri))
353
+ end
354
+ else
355
+ asset_path = Pathname.new(File.expand_path(uri, File.dirname(input_file)))
356
+
357
+ if uri.match(/^(\/[^\/]+.+)$/)
358
+ asset_path = Pathname.new(File.join(File.expand_path(@options[:root]), uri))
359
+ end
360
+
361
+ new_path = asset_path.relative_path_from(output_path)
362
+
363
+ if @options[:cache_buster]
364
+ buster = make_cache_buster(asset_path, $3)
365
+ new_path = new_path.to_s+buster
366
+ else
367
+ new_path = new_path.to_s+$3 unless $3.nil?
368
+ end
369
+ end
370
+
371
+ %Q!url(#{new_path})!
372
+ else
373
+ %Q!url(#{uri}#{cbuster})!
374
+ end
375
+ end
376
+
377
+ return input, found_imports
378
+ end
379
+ end
380
+
381
+ def make_cache_buster(asset_path, query_string)
382
+ unless query_string.nil?
383
+ query_string += '&'
384
+ else
385
+ query_string = '?'
386
+ end
387
+
388
+ if @options[:cache_buster] == :mtime
389
+ file_mtime = File.mtime(asset_path).to_i
390
+ buster = query_string + file_mtime.to_s
391
+ else
392
+ buster = query_string + @options[:cache_buster]
393
+ end
394
+
395
+ return buster
396
+ end
397
+
398
+ def make_data_uri(content, content_type)
399
+ "data:#{content_type};base64,#{Base64.encode64(content)}".gsub("\n", '')
400
+ end
401
+ end
402
+ end
@@ -0,0 +1,26 @@
1
+ class Hash
2
+ def deep_merge(hash)
3
+ target = dup
4
+
5
+ hash.keys.each do |key|
6
+ if hash[key].is_a? Hash and self[key].is_a? Hash
7
+ target[key] = target[key].deep_merge(hash[key])
8
+ next
9
+ end
10
+
11
+ target[key] = hash[key]
12
+ end
13
+
14
+ target
15
+ end
16
+
17
+ def deep_merge!(second)
18
+ second.each_pair do |k,v|
19
+ if self[k].is_a?(Hash) and second[k].is_a? Hash
20
+ self[k].deep_merge!(second[k])
21
+ else
22
+ self[k] = second[k]
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ Software License Agreement (BSD License)
2
+
3
+ Copyright (c) 2008, Yahoo! Inc.
4
+ All rights reserved.
5
+
6
+ Redistribution and use of this software in source and binary forms, with or without modification, are
7
+ permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above
10
+ copyright notice, this list of conditions and the
11
+ following disclaimer.
12
+
13
+ * Redistributions in binary form must reproduce the above
14
+ copyright notice, this list of conditions and the
15
+ following disclaimer in the documentation and/or other
16
+ materials provided with the distribution.
17
+
18
+ * Neither the name of Yahoo! Inc. nor the names of its
19
+ contributors may be used to endorse or promote products
20
+ derived from this software without specific prior
21
+ written permission of Yahoo! Inc.
22
+
23
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
24
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
29
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Binary file
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blender
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.20"
5
+ platform: ruby
6
+ authors:
7
+ - Blake Elshire
8
+ - Chris Griego
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2008-10-13 00:00:00 -05:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: mime-types
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "1.15"
25
+ version:
26
+ - !ruby/object:Gem::Dependency
27
+ name: colored
28
+ type: :runtime
29
+ version_requirement:
30
+ version_requirements: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "1.1"
35
+ version:
36
+ - !ruby/object:Gem::Dependency
37
+ name: hoe
38
+ type: :development
39
+ version_requirement:
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 1.7.0
45
+ version:
46
+ description: Blender is like ant or make for the front-end. It aggregates and compresses CSS and/or JavaScript assets for a site into efficient, production-ready files.
47
+ email: blender@front-end-architect.com
48
+ executables:
49
+ - blend
50
+ extensions: []
51
+
52
+ extra_rdoc_files:
53
+ - History.txt
54
+ - Manifest.txt
55
+ - README.txt
56
+ files:
57
+ - History.txt
58
+ - MIT-LICENSE
59
+ - Manifest.txt
60
+ - README.txt
61
+ - Rakefile
62
+ - bin/blend
63
+ - lib/front_end_architect/blender.rb
64
+ - lib/front_end_architect/hash.rb
65
+ - lib/yui/LICENSE
66
+ - lib/yui/yuicompressor.jar
67
+ has_rdoc: true
68
+ homepage: http://www.front-end-architect.com/blender/
69
+ post_install_message:
70
+ rdoc_options:
71
+ - --main
72
+ - README.txt
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ requirements:
88
+ - Java, v1.4 or greater
89
+ rubyforge_project: blender
90
+ rubygems_version: 1.2.0
91
+ signing_key:
92
+ specification_version: 2
93
+ summary: Blender outputs efficient, production-ready CSS and/or JavaScript assets.
94
+ test_files: []
95
+