blender 0.20

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