lash 0.1.1 → 1.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/.autotest +10 -0
- data/.gitignore +2 -1
- data/.rspec +1 -0
- data/Gemfile +2 -1
- data/LICENSE.md +48 -0
- data/README.md +117 -5
- data/Rakefile +1 -0
- data/lib/lash.rb +17 -4
- data/lib/lash/assets_host.rb +74 -0
- data/lib/lash/bundle_helper.rb +6 -24
- data/lib/lash/capistrano.rb +2 -0
- data/lib/lash/closure_minifier.rb +20 -0
- data/lib/lash/files.rb +56 -0
- data/lib/lash/java_script_bundler.rb +117 -0
- data/lib/lash/java_script_minifier.rb +67 -0
- data/lib/lash/railtie.rb +4 -33
- data/lib/lash/sprite_bundler.rb +132 -0
- data/lib/lash/tasks/lash.rake +50 -181
- data/lib/lash/version.rb +1 -1
- data/spec/lib/assets_host_spec.rb +56 -0
- data/spec/lib/bundle_helper_spec.rb +99 -0
- data/spec/lib/closure_minifier_spec.rb +67 -0
- data/spec/lib/java_script_bundler_spec.rb +46 -0
- data/spec/lib/lash_files_spec.rb +68 -0
- data/spec/spec_helper.rb +15 -25
- data/spec/support/rails_fake.rb +31 -0
- data/spec/test_app/.gitignore +4 -0
- data/spec/test_app/Rakefile +8 -0
- data/spec/test_app/public/images/rails.png +0 -0
- data/spec/test_app/public/images/ui-sprite.png +0 -0
- data/spec/test_app/public/images/ui-sprite.png.sprite +3 -0
- data/spec/test_app/public/javascripts/application/README +0 -0
- data/spec/test_app/public/javascripts/application/application.bundleVersion.js +1 -0
- data/spec/test_app/public/javascripts/application/application.js +3 -0
- data/spec/test_app/public/javascripts/application/application2.js +3 -0
- data/spec/test_app/public/javascripts/application/tools/validate.js +3 -0
- data/spec/test_app/public/javascripts/bundle_application.js +1 -0
- data/spec/test_app/public/javascripts/bundle_application.js.gz +0 -0
- data/spec/test_app/public/javascripts/cdn/jquery.js +1 -0
- data/spec/test_app/public/javascripts/cdn/jquery.js.gz +0 -0
- data/spec/test_app/public/javascripts/cdn/jquery.min.js +1 -0
- data/spec/test_app/public/javascripts/cdn/jquery.min.js.gz +0 -0
- data/spec/test_app/public/javascripts/demand/huge.js +0 -0
- data/spec/test_app/public/javascripts/demand/huge.js.gz +0 -0
- data/spec/test_app/public/javascripts/demand/huge.min.js +1 -0
- data/spec/test_app/public/javascripts/demand/huge.min.js.gz +0 -0
- data/spec/test_app/public/sprites/ui/accept.png +0 -0
- data/spec/test_app/public/sprites/ui/add.png +0 -0
- data/spec/test_app/public/stylesheets/.gitkeep +0 -0
- data/spec/test_app/public/stylesheets/sass/_ui-sprite.scss +20 -0
- data/spec/test_app/public/stylesheets/sass/_version.scss +1 -0
- metadata +81 -10
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'lash/files'
|
3
|
+
require 'lash/closure_minifier'
|
4
|
+
|
5
|
+
module Lash
|
6
|
+
|
7
|
+
# Bundles JavaScripts into single files or individually minified scripts
|
8
|
+
#
|
9
|
+
# ## Example
|
10
|
+
#
|
11
|
+
# bundler = JavaScriptBundler.new
|
12
|
+
# bundler.bundle( "application", :style => :single ) # => bundle_application.js
|
13
|
+
# bundler.bundle( "demand", :style => :individual ) # => demand/form.min.js
|
14
|
+
# # => demand/fancybox.min.js
|
15
|
+
#
|
16
|
+
class JavaScriptBundler
|
17
|
+
|
18
|
+
|
19
|
+
def initialize( options = nil )
|
20
|
+
@options ||= {}
|
21
|
+
@minifiers = [ options[:minifiers] || ClosureMinifier.new ].flatten
|
22
|
+
@options[:javascripts_path] ||= File.expand_path( File.join( Rails.root, "public/javascripts" ) )
|
23
|
+
@demand_dirs ||= options[:demand_dirs] || ["demand"]
|
24
|
+
@cdn_dirs ||= options[:cdn_dirs] || ["cdn"]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Bundles all the scripts in the {directory}
|
28
|
+
def bundle( directory, options = nil )
|
29
|
+
options = resolve_options( options || {}, directory )
|
30
|
+
files = Lash::Files.recursive_file_list( options[:directory], '.js' )
|
31
|
+
|
32
|
+
case options[:style]
|
33
|
+
when :single
|
34
|
+
bundle_into_single_script files, options
|
35
|
+
when :individual
|
36
|
+
files = files.reject{ |f| f.end_with? 'min.js' }
|
37
|
+
bundle_individual_scripts files, options
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def bundle_style( directory )
|
42
|
+
infer_style_from_directory directory
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def resolve_directory( directory )
|
48
|
+
File.expand_path( directory, @options[:javascripts_path] )
|
49
|
+
end
|
50
|
+
|
51
|
+
def infer_style_from_directory( directory )
|
52
|
+
individual = (( @cdn_dirs || [] ) + ( @demand_dirs || [] )).map{ |d| resolve_directory d }
|
53
|
+
individual.include?( directory ) ? :individual : :single
|
54
|
+
end
|
55
|
+
|
56
|
+
def resolve_options( options, directory )
|
57
|
+
options[:directory] = resolve_directory( directory )
|
58
|
+
options[:style] ||= infer_style_from_directory( options[:directory] )
|
59
|
+
options[:target_dir] ||= @options[:javascripts_path]
|
60
|
+
options[:log] = @options[:log] unless options.has_key? :log
|
61
|
+
options
|
62
|
+
end
|
63
|
+
|
64
|
+
def bundle_into_single_script( files, options )
|
65
|
+
bundle_scripts_with_best_minifier( files, options )
|
66
|
+
end
|
67
|
+
|
68
|
+
def bundle_individual_scripts( files, options )
|
69
|
+
files.each do |f|
|
70
|
+
unless bundle_scripts_with_best_minifier( [f], options )
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def bundle_scripts_with_best_minifier( files, options )
|
77
|
+
targets = []
|
78
|
+
@minifiers.each do |m|
|
79
|
+
target = bundle_scripts_with_minifier( files, options, m )
|
80
|
+
targets << target if target
|
81
|
+
end
|
82
|
+
|
83
|
+
return nil unless targets.length > 0
|
84
|
+
|
85
|
+
migrate_best_target( targets, options.merge({
|
86
|
+
:target_bundle => bundle_target_name( files, options )
|
87
|
+
}))
|
88
|
+
true
|
89
|
+
ensure
|
90
|
+
targets.each { |f| FileUtils.rm_f f }
|
91
|
+
end
|
92
|
+
|
93
|
+
def bundle_target_name( files, options )
|
94
|
+
options[:style] == :single \
|
95
|
+
? File.join( options[:target_dir], "bundle_#{File.basename( options[:directory] ).parameterize}.js" )
|
96
|
+
: File.join( options[:directory], "#{File.basename( files.first, '.js' )}.min.js" )
|
97
|
+
end
|
98
|
+
|
99
|
+
def migrate_best_target( targets, options )
|
100
|
+
smallest = targets.min_by { |f| File.size( f ) }
|
101
|
+
FileUtils.mv smallest, options[:target_bundle], :force => true
|
102
|
+
FileUtils.mv smallest + '.gz', options[:target_bundle] + '.gz', :force => true if File.exist? smallest + ".gz"
|
103
|
+
|
104
|
+
puts "Bundled to #{options[:target_bundle]}" if options[:log]
|
105
|
+
end
|
106
|
+
|
107
|
+
def bundle_scripts_with_minifier( files, options, minifier )
|
108
|
+
target = bundle_target_name( files, options ) + ".#{minifier.class.name.parameterize}"
|
109
|
+
if minifier.minify( files, target )
|
110
|
+
target
|
111
|
+
else
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Lash
|
4
|
+
# Utility class for minifying a set of javascript sources
|
5
|
+
class JavaScriptMinifier
|
6
|
+
|
7
|
+
attr_accessor :options
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@options = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Compresses array of files into a single target minified script.
|
14
|
+
def minify( files, target )
|
15
|
+
target = File.expand_path( target )
|
16
|
+
outdir = File.dirname( target )
|
17
|
+
|
18
|
+
FileUtils.mkdir_p outdir
|
19
|
+
|
20
|
+
if minify_scripts( files, target )
|
21
|
+
gzip_bundle target
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def command_options
|
27
|
+
return unless @options
|
28
|
+
@options.inject("") do |o,p|
|
29
|
+
v = if p[1]
|
30
|
+
"#{p[0]} #{p[1]}"
|
31
|
+
else
|
32
|
+
p[0]
|
33
|
+
end
|
34
|
+
o = o + " " if o.length > 0
|
35
|
+
o + v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def minify_scripts( files, target )
|
40
|
+
tmp = "#{target}.tmp"
|
41
|
+
bundle_scripts files, tmp
|
42
|
+
File.delete target if File.exist? target
|
43
|
+
File.rename tmp, target
|
44
|
+
end
|
45
|
+
|
46
|
+
def bundle_scripts( files, bundle_target )
|
47
|
+
File.open( bundle_target, "w+" ) do |t|
|
48
|
+
files.each do |file|
|
49
|
+
File.open( file, "rb" ) do |f|
|
50
|
+
t.write ";\n"
|
51
|
+
t.write f.read
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def temporary_bundle_file_for_target( target )
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def gzip_bundle( target )
|
62
|
+
%x{gzip -c -9 "#{target}" > "#{target}.gz"}
|
63
|
+
File.size "#{target}.gz"
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
data/lib/lash/railtie.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'lash'
|
2
|
+
require 'lash/assets_host'
|
2
3
|
require 'rails'
|
3
|
-
require 'grit'
|
4
4
|
|
5
5
|
module Lash
|
6
|
+
# Adds Lash helper methods to your rails application. See {Lash::AssetsHost} for more details.
|
6
7
|
class Railtie < Rails::Railtie
|
7
8
|
|
8
9
|
rake_tasks do
|
@@ -15,38 +16,8 @@ module Lash
|
|
15
16
|
end
|
16
17
|
|
17
18
|
initializer "lash.setup_git_asset_id", :before => "action_controller.set_configs" do |app|
|
18
|
-
|
19
|
-
|
20
|
-
ENV['RAILS_ASSET_ID'] = \
|
21
|
-
[:javascripts, :stylesheets, :images] \
|
22
|
-
.map { |folder| repo.log( 'master', "public/#{folder}", :max_count => 1 ).first } \
|
23
|
-
.max_by { |log| log.committed_date }
|
24
|
-
.id
|
25
|
-
|
26
|
-
app.config.action_controller.asset_host = Proc.new do |source,request|
|
27
|
-
if /\/\// =~ source
|
28
|
-
nil
|
29
|
-
elsif request.ssl? and ! Lash.lash_options[:use_asset_servers_in_ssl]
|
30
|
-
nil
|
31
|
-
elsif !Lash.lash_options[:use_asset_servers]
|
32
|
-
nil
|
33
|
-
else
|
34
|
-
|
35
|
-
# Change the host name to include a randomized asset name at the same domain
|
36
|
-
# level. This is required so that HTTPS requests can use a wildcard domain
|
37
|
-
# without using subject alt name.
|
38
|
-
|
39
|
-
host = request.host_with_port
|
40
|
-
parts = host.split( /\./ )
|
41
|
-
if parts.length > 2
|
42
|
-
parts[0] = "#{parts[0]}-assets#{source.hash % 4}"
|
43
|
-
else
|
44
|
-
parts.unshift "assets#{source.hash % 4}"
|
45
|
-
end
|
46
|
-
|
47
|
-
"http#{'s' if request.ssl?}://#{parts.join('.')}"
|
48
|
-
end
|
49
|
-
end
|
19
|
+
AssetsHost.use_git_asset_id
|
20
|
+
app.config.action_controller.asset_host = AssetsHosts.resolve_static_asset_server_for_source
|
50
21
|
end
|
51
22
|
|
52
23
|
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'lash'
|
2
|
+
require 'lash-sprites/sprite'
|
3
|
+
require 'lash-sprites/css'
|
4
|
+
require 'lash/assets_host'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Lash
|
8
|
+
# Bundles a series of loose images into a single CSS sprite and generates a corresponding CSS support
|
9
|
+
# files
|
10
|
+
class SpriteBundler
|
11
|
+
|
12
|
+
# Bundles the images in the given directory into a single large CSS sprite image
|
13
|
+
#
|
14
|
+
# @param [String] directory the directory of images to bundle into a sprite
|
15
|
+
#
|
16
|
+
# @option options [Boolean] :sass Generate a SASS script for importing into a master.scss script
|
17
|
+
# @option options [Boolean] :class_name The css class name, defaults to name of directory
|
18
|
+
# @option options [Boolean] :sprite_file The name of the png sprite to generate. Defaults to `#{directory}-sprite.png`
|
19
|
+
# @option options [Boolean] :css_file The name of the CSS file to generate
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
#
|
23
|
+
# bundler.bundle "ui" # => public/images/ui-sprite.png
|
24
|
+
# # => public/sprites/sass/_ui-sprite.scss
|
25
|
+
def bundle( directory, options = nil )
|
26
|
+
options = resolve_options( options || {}, directory )
|
27
|
+
|
28
|
+
sprite = create_sprite_image( options )
|
29
|
+
generate_css( sprite, options )
|
30
|
+
|
31
|
+
puts "=> bundled css at #{ Lash::Files.relative_to( options[:css_file], Rails.root )}" if options[:log]
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def resolve_options( options, directory )
|
36
|
+
options[:directory] = File.expand_path( directory, File.join( Rails.root, "public/sprites" ) )
|
37
|
+
options[:sass] = Lash.lash_options[:use_sass] unless options.has_key?(:sass) or !Gem.available?(:sass)
|
38
|
+
options[:class_name] ||= ( File.basename( directory ) ).parameterize
|
39
|
+
options[:sprite_name] ||= ( File.basename( directory ) + '-sprite' ).parameterize
|
40
|
+
options[:sprite_file] ||= File.join( Rails.root, "public/images", "#{options[:sprite_name]}.png")
|
41
|
+
options[:css_file] ||= options[:sass] \
|
42
|
+
? File.join( Rails.root, 'public/stylesheets/sass/', "_#{options[:sprite_name]}.scss" )
|
43
|
+
: File.join( Rails.root, 'public/stylesheets/', "#{options[:sprite_name]}.css" )
|
44
|
+
options
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_sprite_image( options )
|
48
|
+
sprite_files = Lash::Files.recursive_file_list( options[:directory], /\.(jpe?g|png|gif)$/ )
|
49
|
+
FileUtils.rm_rf( options[:sprite_file] )
|
50
|
+
sprite = RubySprites::Sprite.new( Lash::Files.relative_to( options[:sprite_file], Rails.root ),
|
51
|
+
Rails.root,
|
52
|
+
:graphics_manager => :rmagick,
|
53
|
+
:pack_type => 'both_smart' )
|
54
|
+
|
55
|
+
sprite_files.each do |f|
|
56
|
+
sprite.add_image( Lash::Files.relative_to( f, Rails.root ) )
|
57
|
+
end
|
58
|
+
|
59
|
+
sprite.update
|
60
|
+
`#{File.expand_path ::File.join( __FILE__, '../../../bin/optipng')} -o7 \"#{sprite.filename}\"` if File.exists? sprite.filename
|
61
|
+
|
62
|
+
|
63
|
+
sprite
|
64
|
+
end
|
65
|
+
|
66
|
+
def load_css_template( sprite_dir, template_name )
|
67
|
+
css_template_file = File.join( sprite_dir, template_name );
|
68
|
+
return IO.read(css_template_file) if File.exists? css_template_file
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
# Generates the acutal CSS sprite file
|
73
|
+
def generate_css( sprite, options )
|
74
|
+
width, height = best_width_and_height( sprite )
|
75
|
+
|
76
|
+
asset_id = Lash::AssetsHost.asset_id( options[:sprite_file] )
|
77
|
+
png_file = sprite.filename[6,sprite.filename.size-6]
|
78
|
+
css = eval ( load_css_template( options[:directory], "template.css" ) ||
|
79
|
+
'"
|
80
|
+
.#{options[:class_name]}-background {
|
81
|
+
background-image: url(..#{png_file}?#{asset_id});
|
82
|
+
background-repeat: no-repeat;
|
83
|
+
}
|
84
|
+
|
85
|
+
.#{options[:class_name]} {
|
86
|
+
background-color: transparent;
|
87
|
+
background-image: url(..#{png_file}?#{asset_id});
|
88
|
+
background-repeat: no-repeat;
|
89
|
+
overflow: hidden;
|
90
|
+
text-indent: 99999px;
|
91
|
+
*text-indent: 999px; // IE 7 fix
|
92
|
+
text-align: left!important;
|
93
|
+
width: #{width}px;
|
94
|
+
height: #{height}px;
|
95
|
+
}
|
96
|
+
|
97
|
+
"' )
|
98
|
+
|
99
|
+
css_sprite_template = load_css_template( options[:directory], "sprite-template.css" ) || '
|
100
|
+
".#{options[:class_name]}-#{name} { background-position: -#{image.x}px -#{image.y}px;#{ "width: #{image.width}px; " if image.width != width }#{ "height: #{image.height}px; " if image.height != height } }\n"
|
101
|
+
'
|
102
|
+
|
103
|
+
sprite.images.each do |path, image|
|
104
|
+
name = File.basename( path )
|
105
|
+
name = name[ 0, name.rindex( '.' ) ].gsub( '/', '-' )
|
106
|
+
css += eval css_sprite_template
|
107
|
+
end
|
108
|
+
|
109
|
+
FileUtils.mkdir_p File.dirname( options[:css_file] )
|
110
|
+
|
111
|
+
fp = File.open( options[:css_file], 'w+' )
|
112
|
+
fp.write( css )
|
113
|
+
fp.close
|
114
|
+
end
|
115
|
+
|
116
|
+
# Calculate most common width and height so sprites can default to most common dimensions
|
117
|
+
def best_width_and_height( sprite )
|
118
|
+
width_freq, height_freq = {}, {}
|
119
|
+
|
120
|
+
sprite.images.each do |path,image|
|
121
|
+
next unless image.width and image.height
|
122
|
+
|
123
|
+
width_freq[image.width] = ( width_freq[image.width] || 0 ) + 1
|
124
|
+
height_freq[image.height] = ( height_freq[image.height] || 0 ) + 1
|
125
|
+
end
|
126
|
+
|
127
|
+
[ width_freq.max_by { |e| e[1] }[0], height_freq.max_by { |e| e[1] }[0] ]
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
data/lib/lash/tasks/lash.rake
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require 'lash
|
2
|
-
require 'lash-sprites/css'
|
1
|
+
require 'lash'
|
3
2
|
require 'fileutils'
|
4
3
|
if Lash.lash_options[:use_sass]
|
5
4
|
require 'sass'
|
@@ -15,128 +14,77 @@ namespace :lash do
|
|
15
14
|
task :all => [:js, :css]
|
16
15
|
|
17
16
|
cdn_dirs = ['cdn']
|
18
|
-
|
17
|
+
demand_dirs = ['demand']
|
18
|
+
bundler = Lash::JavaScriptBundler.new :cdn_dirs => cdn_dirs, :demand_dirs => demand_dirs, :log => true
|
19
|
+
|
19
20
|
@bin_path = File.expand_path( File.join( __FILE__, '../../../../bin' ) )
|
20
21
|
|
21
|
-
exclude_dirs = cdn_dirs + minify_dirs
|
22
|
-
|
23
22
|
javascripts_path = File.join( ::Rails.root, 'public', 'javascripts', '' )
|
23
|
+
|
24
|
+
CLEAN.include( "#{javascripts_path}/**/bundle_*.js")
|
24
25
|
CLEAN.include( "#{javascripts_path}/**/*.gz")
|
25
26
|
CLEAN.include( "#{File.join ::Rails.root, 'public/stylesheets' }/**/*.gz")
|
26
27
|
|
27
28
|
desc 'Bundles and minifies javascripts'
|
28
29
|
task :js => [:js_bundle, :js_min]
|
29
30
|
|
31
|
+
# Creates an application.bundleVersion.js file
|
32
|
+
task :js_bundle_version do
|
33
|
+
target = File.join( Rails.root, "public/javascripts/application" )
|
34
|
+
if File.exist? target
|
35
|
+
asset_id = Lash::AssetsHost.asset_id( target )
|
36
|
+
File.open( File.join( target, "application.bundleVersion.js" ), "w+" ) { |f| f.write "window.bundleVersion = '#{asset_id}';"}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
30
40
|
desc 'Bundles javascripts folders into single minified files'
|
31
|
-
task :js_bundle do
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
bundle_name = bundle_directory.gsub( javascripts_path, "")
|
36
|
-
|
37
|
-
next if exclude_dirs.include? bundle_name
|
38
|
-
|
39
|
-
files = recursive_file_list( bundle_directory, ".js" )
|
40
|
-
next if files.empty?
|
41
|
-
|
42
|
-
target = File.join( javascripts_path, "bundle_#{bundle_name}.js" )
|
43
|
-
|
44
|
-
compile_js files, target
|
41
|
+
task :js_bundle => [:js_bundle_version] do
|
42
|
+
Lash::Files.get_top_level_directories( javascripts_path ).each do |bundle_directory|
|
43
|
+
next unless bundler.bundle_style( bundle_directory ) == :single
|
44
|
+
bundler.bundle bundle_directory, :log => true
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
desc 'Minifies all javascripts'
|
49
|
-
task :js_min do
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
paths.each do |bundle_directory|
|
54
|
-
bundle_name = bundle_directory.gsub( javascripts_path, "")
|
55
|
-
next unless minify_dirs.include? bundle_name
|
56
|
-
|
57
|
-
recursive_file_list(bundle_directory,'.js').reject{|f| f =~ /\.min\.js$/}.each do |file|
|
58
|
-
compile_js( [file],
|
59
|
-
file.gsub( /.js$/, ".min.js" ) )
|
60
|
-
end
|
48
|
+
desc 'Minifies all loose javascripts'
|
49
|
+
task :js_min => [:js_bundle_version] do
|
50
|
+
Lash::Files.get_top_level_directories( javascripts_path ).each do |bundle_directory|
|
51
|
+
next unless bundler.bundle_style( bundle_directory ) == :individual
|
52
|
+
bundler.bundle bundle_directory
|
61
53
|
end
|
62
54
|
end
|
63
55
|
|
64
|
-
desc 'Compresses
|
56
|
+
desc 'Compresses all javascripts for nginx gzip_static support'
|
65
57
|
task :js_gzip do |t|
|
66
58
|
Dir[ File.join Rails.root, 'public/javascripts/**/*.js' ].each do |file|
|
59
|
+
next unless bundler.bundle_style( File.dirname( file ) ) == :individual
|
67
60
|
%x{gzip -c -9 "#{file}" > "#{file}.gz" }
|
68
61
|
end
|
69
62
|
end
|
70
63
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
}
|
80
|
-
|
81
|
-
self.send "compile_js_#{packer}", files, pack[:file]
|
82
|
-
%x{gzip -c -9 "#{pack[:file]}" > "#{pack[:file]}.gz" }
|
83
|
-
pack[:size] = File.size "#{pack[:file]}.gz"
|
64
|
+
desc 'Generate CSS sprites from the public/sprites folders'
|
65
|
+
task :sprites do |t|
|
66
|
+
sprites_folder = File.join( Rails.root, "public/sprites" )
|
67
|
+
if File.exist? sprites_folder
|
68
|
+
sprite_bundler = Lash::SpriteBundler.new
|
69
|
+
Lash::Files.get_top_level_directories( sprites_folder ).each do |bundle_directory|
|
70
|
+
sprite_bundler.bundle bundle_directory, :log => true
|
71
|
+
end
|
84
72
|
end
|
85
|
-
|
86
|
-
smallest = packs.min{ |a,b| a[:size] <=> b[:size] }[:file]
|
87
|
-
|
88
|
-
FileUtils.cp smallest, target
|
89
|
-
FileUtils.cp "#{smallest}.gz", "#{target}.gz"
|
90
|
-
puts "=> bundled js at #{target}"
|
91
|
-
|
92
|
-
ensure
|
93
|
-
packs.each { |pack|
|
94
|
-
File.delete pack[:file] if File.exist? pack[:file]
|
95
|
-
File.delete "#{pack[:file]}.gz" if File.exist? "#{pack[:file]}.gz"
|
96
|
-
}
|
97
73
|
end
|
98
74
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
desc 'Generate CSS sprites from the public/sprites folders'
|
108
|
-
task :css_sprites do |t|
|
109
|
-
|
110
|
-
Dir[ 'public/sprites/*' ].select{ |d| File.directory? d }.each do |sprite_dir|
|
111
|
-
|
112
|
-
sprite_name = ( File.basename( sprite_dir ) + '-sprite' ).gsub(' ', '-')
|
113
|
-
css_file = File.join( 'public/stylesheets/sass/', "_#{sprite_name}.scss" )
|
114
|
-
sprite_files = FileList[ File.join( sprite_dir, '*.*' ) ].exclude(/.*\.css$/)
|
115
|
-
|
116
|
-
sprite_file = "public/images/#{sprite_name}.png";
|
117
|
-
FileUtils.rm_rf( File.join( Rails.root, sprite_file ) )
|
118
|
-
|
119
|
-
sprite = RubySprites::Sprite.new( "public/images/#{sprite_name}.png",
|
120
|
-
Rails.root,
|
121
|
-
:graphics_manager => :rmagick,
|
122
|
-
:pack_type => 'both_smart' )
|
123
|
-
|
124
|
-
sprite_files.each do |f|
|
125
|
-
sprite.add_image( f )
|
75
|
+
desc 'Pre-generate sass scripts' if Lash.lash_options[:use_sass]
|
76
|
+
task :sass do |t|
|
77
|
+
if Lash.lash_options[:use_sass]
|
78
|
+
sass_target = File.join( Rails.root, "public/stylesheets/sass" )
|
79
|
+
if File.exist? sass_target
|
80
|
+
asset_id = Lash::AssetsHost.asset_id( sass_target )
|
81
|
+
File.open( File.join( sass_target, "_version.scss" ), "w+" ) { |f| f.write "$bundle-version: '#{asset_id}';"}
|
126
82
|
end
|
127
|
-
|
128
|
-
sprite.update
|
129
|
-
generate_css( sprite, File.basename( sprite_dir ), css_file, sprite_dir )
|
130
83
|
|
131
|
-
|
84
|
+
Sass::Plugin.force_update_stylesheets
|
132
85
|
end
|
133
86
|
end
|
134
87
|
|
135
|
-
desc 'Pre-generate sass scripts'
|
136
|
-
task :sass do |t|
|
137
|
-
puts "SASS env = #{Rails.env} / #{Sass::Plugin.options[:style]}"
|
138
|
-
Sass::Plugin.force_update_stylesheets
|
139
|
-
end
|
140
88
|
desc "Compresses stylesheets for use with nginx gzip_static"
|
141
89
|
task :css_gzip do |t|
|
142
90
|
Dir[ File.join Rails.root, 'public/stylesheets/*.css' ].each do |file|
|
@@ -145,97 +93,18 @@ namespace :lash do
|
|
145
93
|
end
|
146
94
|
|
147
95
|
desc 'Process CSS scripts'
|
148
|
-
task :css => [:
|
149
|
-
|
96
|
+
task :css => [:sprites, :sass, :css_gzip, :png] do |t|
|
150
97
|
end
|
151
98
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
end
|
158
|
-
|
159
|
-
def generate_css(sprite,class_name,file,sprite_dir)
|
160
|
-
|
161
|
-
# Calculate most common width and height so sprites can default
|
162
|
-
width_freq, height_freq = {}, {}
|
163
|
-
|
164
|
-
sprite.images.each do |path,image|
|
165
|
-
next unless image.width and image.height
|
166
|
-
|
167
|
-
width_freq[image.width] = ( width_freq[image.width] || 0 ) + 1
|
168
|
-
height_freq[image.height] = ( height_freq[image.height] || 0 ) + 1
|
99
|
+
desc 'Optimizes all PNG images in the public/images folder'
|
100
|
+
task :png do |t|
|
101
|
+
Lash::Files.recursive_file_list( File.join( Rails.root, "public/images" ), ".png" ).each do |p|
|
102
|
+
`#{File.expand_path ::File.join( __FILE__, '../../../../bin/optipng')} -o7 \"#{p}\"`
|
103
|
+
abort "Failed to optimize png #{p}, status = #{$?.exitstatus}" unless $?.exitstatus == 0
|
169
104
|
end
|
170
|
-
|
171
|
-
width = width_freq.max_by { |e| e[1] }[0]
|
172
|
-
height = height_freq.max_by { |e| e[1] }[0]
|
173
|
-
|
174
|
-
|
175
|
-
png_file = sprite.filename[6,sprite.filename.size-6]
|
176
|
-
css = eval ( load_css_template( sprite_dir, "template.css" ) ||
|
177
|
-
'"
|
178
|
-
.#{class_name}-background {
|
179
|
-
background-image: url(..#{png_file}?#{Date.new.to_time.to_i});
|
180
|
-
background-repeat: no-repeat;
|
181
|
-
}
|
182
|
-
|
183
|
-
.#{class_name} {
|
184
|
-
background-color: transparent;
|
185
|
-
background-image: url(..#{png_file}?#{Date.new.to_time.to_i});
|
186
|
-
background-repeat: no-repeat;
|
187
|
-
overflow: hidden;
|
188
|
-
text-indent: 99999px;
|
189
|
-
*text-indent: 999px; // IE 7 fix
|
190
|
-
text-align: left!important;
|
191
|
-
width: #{width}px;
|
192
|
-
height: #{height}px;
|
193
|
-
}
|
194
|
-
|
195
|
-
"' )
|
105
|
+
end
|
196
106
|
|
197
|
-
css_sprite_template = load_css_template( sprite_dir, "sprite-template.css" ) || '
|
198
|
-
".#{class_name}-#{name} { background-position: -#{image.x}px -#{image.y}px;#{ "width: #{image.width}px; " if image.width != width }#{ "height: #{image.height}px; " if image.height != height } }\n"
|
199
|
-
'
|
200
|
-
|
201
|
-
sprite.images.each do |path, image|
|
202
|
-
name = File.basename(path)
|
203
|
-
name = name[ 0, name.rindex( '.' ) ].gsub( '/', '-' )
|
204
|
-
css += eval css_sprite_template
|
205
|
-
end
|
206
|
-
|
207
|
-
fp = File.open( sprite.file_root + file, 'w' )
|
208
|
-
fp.write( css )
|
209
|
-
fp.close
|
210
|
-
|
211
|
-
`#{File.join( @bin_path, 'optipng')} -o7 \"#{sprite.filename}\"` if File.exists? sprite.filename
|
212
|
-
end
|
213
|
-
|
214
|
-
|
215
|
-
require 'find'
|
216
|
-
def recursive_file_list(basedir, ext)
|
217
|
-
files = []
|
218
|
-
Find.find( basedir ) do |path|
|
219
|
-
if FileTest.directory?( path )
|
220
|
-
if File.basename( path )[0] == ?. # Skip dot directories
|
221
|
-
Find.prune
|
222
|
-
else
|
223
|
-
next
|
224
|
-
end
|
225
|
-
end
|
226
|
-
files << path if File.extname( path ) == ext
|
227
|
-
end
|
228
|
-
files.sort
|
229
|
-
end
|
230
|
-
|
231
|
-
def get_top_level_directories( base_path )
|
232
|
-
Dir.entries( base_path ).collect do |path|
|
233
|
-
path = File.join( base_path, path )
|
234
|
-
File.basename( path )[0] == ?. || !File.directory?( path ) ? nil : path # not dot directories or files
|
235
|
-
end - [nil]
|
236
|
-
end
|
237
|
-
|
238
107
|
desc 'Called by capistrano to generate static assets on the server'
|
239
|
-
task :deploy => [:
|
108
|
+
task :deploy => [:js, :css, :png] do
|
240
109
|
end
|
241
110
|
end
|