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.
Files changed (52) hide show
  1. data/.autotest +10 -0
  2. data/.gitignore +2 -1
  3. data/.rspec +1 -0
  4. data/Gemfile +2 -1
  5. data/LICENSE.md +48 -0
  6. data/README.md +117 -5
  7. data/Rakefile +1 -0
  8. data/lib/lash.rb +17 -4
  9. data/lib/lash/assets_host.rb +74 -0
  10. data/lib/lash/bundle_helper.rb +6 -24
  11. data/lib/lash/capistrano.rb +2 -0
  12. data/lib/lash/closure_minifier.rb +20 -0
  13. data/lib/lash/files.rb +56 -0
  14. data/lib/lash/java_script_bundler.rb +117 -0
  15. data/lib/lash/java_script_minifier.rb +67 -0
  16. data/lib/lash/railtie.rb +4 -33
  17. data/lib/lash/sprite_bundler.rb +132 -0
  18. data/lib/lash/tasks/lash.rake +50 -181
  19. data/lib/lash/version.rb +1 -1
  20. data/spec/lib/assets_host_spec.rb +56 -0
  21. data/spec/lib/bundle_helper_spec.rb +99 -0
  22. data/spec/lib/closure_minifier_spec.rb +67 -0
  23. data/spec/lib/java_script_bundler_spec.rb +46 -0
  24. data/spec/lib/lash_files_spec.rb +68 -0
  25. data/spec/spec_helper.rb +15 -25
  26. data/spec/support/rails_fake.rb +31 -0
  27. data/spec/test_app/.gitignore +4 -0
  28. data/spec/test_app/Rakefile +8 -0
  29. data/spec/test_app/public/images/rails.png +0 -0
  30. data/spec/test_app/public/images/ui-sprite.png +0 -0
  31. data/spec/test_app/public/images/ui-sprite.png.sprite +3 -0
  32. data/spec/test_app/public/javascripts/application/README +0 -0
  33. data/spec/test_app/public/javascripts/application/application.bundleVersion.js +1 -0
  34. data/spec/test_app/public/javascripts/application/application.js +3 -0
  35. data/spec/test_app/public/javascripts/application/application2.js +3 -0
  36. data/spec/test_app/public/javascripts/application/tools/validate.js +3 -0
  37. data/spec/test_app/public/javascripts/bundle_application.js +1 -0
  38. data/spec/test_app/public/javascripts/bundle_application.js.gz +0 -0
  39. data/spec/test_app/public/javascripts/cdn/jquery.js +1 -0
  40. data/spec/test_app/public/javascripts/cdn/jquery.js.gz +0 -0
  41. data/spec/test_app/public/javascripts/cdn/jquery.min.js +1 -0
  42. data/spec/test_app/public/javascripts/cdn/jquery.min.js.gz +0 -0
  43. data/spec/test_app/public/javascripts/demand/huge.js +0 -0
  44. data/spec/test_app/public/javascripts/demand/huge.js.gz +0 -0
  45. data/spec/test_app/public/javascripts/demand/huge.min.js +1 -0
  46. data/spec/test_app/public/javascripts/demand/huge.min.js.gz +0 -0
  47. data/spec/test_app/public/sprites/ui/accept.png +0 -0
  48. data/spec/test_app/public/sprites/ui/add.png +0 -0
  49. data/spec/test_app/public/stylesheets/.gitkeep +0 -0
  50. data/spec/test_app/public/stylesheets/sass/_ui-sprite.scss +20 -0
  51. data/spec/test_app/public/stylesheets/sass/_version.scss +1 -0
  52. 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
@@ -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
- repo = Grit::Repo.new( Rails.root.to_s )
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
@@ -1,5 +1,4 @@
1
- require 'lash-sprites/sprite'
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
- minify_dirs = ['demand']
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
- paths = get_top_level_directories( javascripts_path )
34
- paths.each do |bundle_directory|
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
- paths = get_top_level_directories( javascripts_path )
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 minified javascripts for nginx gzip_static support'
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
- def compile_js(files,target)
72
-
73
- tmp_file = File.join( Rails.root, 'tmp', File.basename( target, '.js' ) );
74
- packs = []
75
-
76
- %W{ closure }.each do |packer|
77
- packs << pack = {
78
- :file => "#{tmp_file}_#{packer}",
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
- def compile_js_closure(files,target)
102
- closure_path = File.join( @bin_path, 'closure-compiler/compiler.jar' )
103
- `java -jar \"#{closure_path}\" --warning_level QUIET --js \"#{files.join("\" --js \"")}\" --js_output_file \"#{target}\"`
104
- abort "Could not minify files #{files.join(", ")}" unless $?.exitstatus
105
- end
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
- puts "=> bundled css at #{css_file}"
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 => [:css_sprites, :sass, :css_gzip] do |t|
149
-
96
+ task :css => [:sprites, :sass, :css_gzip, :png] do |t|
150
97
  end
151
98
 
152
-
153
- def load_css_template(sprite_dir, template_name)
154
- css_template_file = File.join( sprite_dir, template_name );
155
- return IO.read(css_template_file) if File.exists? css_template_file
156
- nil
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 => [:sass, :gzip_css, :js_gzip] do
108
+ task :deploy => [:js, :css, :png] do
240
109
  end
241
110
  end