active_assets 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,15 +1,10 @@
1
1
  Active Assets
2
2
  =============
3
3
 
4
- A Railtie that provides a full asset management system, including support for development and deployment. This includes building sprites, concatenating javascript and css via expansion definitions. Active Assets includes two libraries, Active Expansions and Active Sprites.
4
+ A Railtie that provides an asset management system for css, javascript, and sprites in your Rails applications and engines. ActiveAssets includes two libraries, ActiveExpansions and ActiveSprites. ActiveSprites generates sprites defined by a dsl similar to a route definition. Similarly, ActiveExpansions' dsl creates `ActionView::Helpers::AssetTagHelper` javascript and stylesheet expansions, and adds additional features:
5
5
 
6
- gem install rmagick
7
- ... OR ...
8
- gem install oily_png
9
- ... OR ...
10
- gem install chunky_png
11
-
12
- gem install active_assets
6
+ * Concatenation of included assets for expansions at boot or deploy time.
7
+ * Support for environment specific assets, so that, say, you can use one file for development and another for production or one file for development and then a cdn resource for production.
13
8
 
14
9
  Gemfile
15
10
  -------
@@ -24,11 +19,14 @@ Gemfile
24
19
  gem 'oily_png'
25
20
  ... OR ...
26
21
  gem 'chunky_png'
27
- ...
22
+ ... OR ...
23
+ gem 'mini_magick
28
24
  end
29
25
  ...
30
26
 
31
- In your rails app
27
+ The above order of image libraries is also the load order precedence.
28
+
29
+ In your Rails app
32
30
  -----------------
33
31
  ### application.rb
34
32
 
@@ -37,25 +35,27 @@ In your rails app
37
35
  require 'active_assets/railtie'
38
36
  ...
39
37
 
40
- You can also include only ActiveSprites or only ActiveExpansions in your application
41
-
38
+ #### You can also include only ActiveSprites or only ActiveExpansions in your application
42
39
  ### application.rb
40
+ Instead of the above,
41
+
43
42
  ...
44
43
  require 'rails/all'
45
- require 'active\_assets/active\_expansions/railtie'
44
+ require 'active_assets/active_expansions/railtie'
46
45
  ... OR ...
47
- require 'active\_assets/active\_sprites/railtie'
46
+ require 'active_assets/active_sprites/railtie'
48
47
  ...
49
48
 
50
- ## The dsls
51
-
49
+ ## The DSLs
52
50
  ### Introduction to Active Sprites
53
51
 
54
- ActiveSprites allows you to generate sprites within your Rails apps with `rake sprites`! All you need is rmagick and you are on your way. If you don't have rmagick installed, ActiveSprites will just fail silently when you try to generate the sprites. Store the images that make up your sprites in your rails project, use the dsl below to tell ActiveSprites which images to include in your sprites, the css selector the corresponds to each image in the sprite, the location to write the sprite, and the location to write the stylesheet.
52
+ ActiveSprites allows you to generate sprites within your Rails apps with `rake sprites`! All you need is `rmagick`, `chunky_png`, or `mini_magick` and you are on your way. Store the images that make up your sprites within your Rails project, use the dsl below to inform ActiveSprites of which images to include in your sprites as well as the css selector corresponding to each image, the location to write the sprite, and the location to write the stylesheet.
53
+
54
+ A basic example ...
55
55
 
56
56
  #### config/sprites.rb
57
57
  Rails.application.sprites do
58
- sprite 'sprites/world_flags.png' => 'sprites/world_flags.css'
58
+ sprite 'sprites/world_flags.png' => 'sprites/world_flags.css' do
59
59
  _"sprite_images/world_flags/Argentina.gif" => ".flags.argentina"
60
60
  _"sprite_images/world_flags/Australia.gif" => ".flags.australia"
61
61
  ...
@@ -74,7 +74,7 @@ or
74
74
  It is possible to add all of the world flags! Haha, see the following example,
75
75
 
76
76
  Rails.application.sprites do
77
- sprite :world_flags
77
+ sprite :world_flags do
78
78
  Dir[Rails.root.join('public/images/sprite_images/world_flags/*.{png,gif,jpg}')].each do |path|
79
79
  image_path = path[%r{^.*/public/images/(.*)$}, 1]
80
80
  klass_name = ".flag.#{File.basename(image_path, File.extname(image_path)).split(' ').join('_')}"
@@ -88,7 +88,7 @@ It is possible to add all of the world flags! Haha, see the following example,
88
88
  Also, you will notice that I gave a symbol for the sprite instead of a mapping. This will assume that you wish to store your sprite at `path/to/your/public/images/sprites/world_flags.png` and you wish to store your stylesheet at `path/to/your/public/stylesheets/sprites/world_flags.css`.
89
89
 
90
90
  #### ActiveSprites configuration
91
- Rmagick is used by default. To switch to `oily_png`, which is a c extension for `chunky_png` and has the same api as `chunky_png`, apply the configuration below.
91
+ Rmagick is used by default and is by far the fastest. You can use one of two methods to select a backend to use. Simply install any from the list above, and ActiveSprites, will automatically use the one that is available, OR configure ActiveSprites to use a specific backend:
92
92
 
93
93
  ##### config/application.rb
94
94
 
@@ -96,7 +96,6 @@ Rmagick is used by default. To switch to `oily_png`, which is a c extension for
96
96
  config.active_sprites.sprite_backend = :chunky_png
97
97
  ...
98
98
 
99
- In summary, if `rmagick` is installed, it will used by default. If `oily_png` is installed AND the `sprite_backend` is set to `:chunky_png` (see below), then `oliy_png` will be used. If `oily_png` is not installed but `chunky_png` is, AND the `sprite_backend` is set to `:chunky_png`, then `chunky_png` will be used.
100
99
 
101
100
  ### Introduction to Active Expansions
102
101
 
@@ -1,13 +1,13 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |s|
3
3
  s.name = "active_assets"
4
- s.version = '0.2.3'
4
+ s.version = '0.2.4'
5
5
  s.platform = Gem::Platform::RUBY
6
6
  s.authors = ["Sam Woodard"]
7
7
  s.email = ["sam@wildfireapp.com"]
8
8
  s.homepage = "http://github.com/shwoodard/active_assets"
9
- s.summary = %q{A Railtie that provides a full asset management system, including support for development and deployment.}
10
- s.description = %q{A Railtie that provides a full asset management system, including support for development and deployment. It is comprised of two libraries, ActiveSprites and ActiveExpansions. ActiveSprites generates sprites and their corresponding stylesheet from dsl definition. ActiveExpansions manages javascript and css, including concatenation support for deployment, using Rails expansions plus a dsl.}
9
+ s.summary = %q{A Railtie that provides an asset management system for css, javascript, and sprites in your Rails applications and engines.}
10
+ s.description = %q{A Railtie that provides an asset management system for css, javascript, and sprites in your Rails applications and engines. ActiveAssets includes two libraries, ActiveExpansions and ActiveSprites. ActiveSprites generates sprites defined by a dsl similar to a route definition. Similarly, ActiveExpansions' dsl creates ActionView::Helpers::AssetTagHelper javascript and stylesheet expansions, and adds additional features}
11
11
 
12
12
  s.rubyforge_project = "activeassets"
13
13
 
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_development_dependency 'oily_png'
20
20
  s.add_development_dependency 'chunky_png'
21
21
  s.add_development_dependency 'rmagick'
22
+ s.add_development_dependency 'mini_magick'
22
23
  s.add_development_dependency 'css_parser', '~>1.1.5'
23
24
  s.add_development_dependency "rails", "~>3.0.3"
24
25
  s.add_development_dependency "test-unit", "> 2.0"
@@ -9,8 +9,12 @@ module ActiveAssets
9
9
  autoload :Sprites
10
10
  autoload :Configurable
11
11
  autoload :SpriteStylesheet
12
- autoload :RmagickRunner
13
- autoload :ChunkyPngRunner
12
+ autoload_under "runners" do
13
+ autoload :AbstractRunner
14
+ autoload :RmagickRunner
15
+ autoload :MiniMagickRunner
16
+ autoload :ChunkyPngRunner
17
+ end
14
18
 
15
19
  def self.load_engine_tasks(engine_class)
16
20
  desc "Generate sprites"
@@ -7,7 +7,6 @@ module ActiveAssets
7
7
 
8
8
  included do
9
9
  config_accessor :sprite_backend
10
- self.sprite_backend = :rmagick if sprite_backend.nil?
11
10
  end
12
11
  end
13
12
  end
@@ -0,0 +1,124 @@
1
+ require 'action_controller'
2
+ require 'action_view'
3
+ require 'rack/mount'
4
+ require 'action_view'
5
+ require 'fileutils'
6
+
7
+ module ActiveAssets
8
+ module ActiveSprites
9
+ class AbstractRunner
10
+ class AssetContext < ActionView::Base
11
+ end
12
+
13
+ def initialize(railtie, sprites)
14
+ @railtie = railtie
15
+ setup_context
16
+ @sprites = if ENV['SPRITE']
17
+ sprites.select do |name, sprite|
18
+ ENV['SPRITE'].split(',').map(&:strip).any? do |sp|
19
+ # were going to be very forgiving
20
+ name == sp ||
21
+ name == sp.to_sym ||
22
+ name == ::Rack::Mount::Utils.normalize_path(sp)
23
+ end
24
+ end.map(&:last)
25
+ else
26
+ sprites.values
27
+ end
28
+ end
29
+
30
+ def generate!
31
+ verbose = ENV['VERBOSE'] == 'true' || ENV['DEBUG']
32
+
33
+ if verbose
34
+ t = Time.now
35
+ $stdout << "#{t}: Active Sprites: \"I'm starting my run using #{runner_name}.\"\n"
36
+ $stdout << "\nSprites to create:\n\"#{@sprites.map(&:path).join('", "')}\"\n"
37
+ end
38
+
39
+ @sprites.each do |sprite|
40
+ next if sprite.sprite_pieces.empty?
41
+
42
+ if verbose
43
+ t_sprite = Time.now
44
+ $stdout << "\n=================================================\n"
45
+ $stdout << "Starting Sprite, #{sprite.path}\n"
46
+ end
47
+
48
+ sprite_path = sanitize_asset_path(context.image_path(sprite.path))
49
+ sprite_stylesheet_path = sanitize_asset_path(context.stylesheet_path(sprite.stylesheet_path))
50
+
51
+ orientation = sprite.orientation.to_s
52
+ sprite_pieces = sprite.sprite_pieces
53
+
54
+ begin
55
+ $stdout << "Gathering sprite details..." if verbose
56
+ image_list, width, height = set_sprite_details_and_return_image_list(sprite, sprite_path, sprite_pieces, orientation)
57
+ $stdout << "done.\n" if verbose
58
+
59
+ if ENV['DEBUG']
60
+ $stdout << "|\tpath\t|\tselectors\t|\tx\t|\ty\t|\twidth\t|\theight\t|\n"
61
+ $stdout << "#{sprite_pieces.map(&:to_s).join("\n")}\n"
62
+ end
63
+
64
+ stylesheet = SpriteStylesheet.new(sprite_pieces)
65
+ stylesheet_file_path = File.join(@railtie.config.paths.public.to_a.first, sprite_stylesheet_path)
66
+ $stdout << "Writing stylesheet to #{stylesheet_file_path} ... " if verbose
67
+ stylesheet.write stylesheet_file_path
68
+ $stdout << "done.\n" if verbose
69
+
70
+ $stdout << "Beginning sprite generation using #{runner_name.humanize}.\n" if verbose
71
+ create_sprite(sprite, sprite_path, sprite_pieces, image_list, width, height, orientation, verbose)
72
+ $stdout << "Success!\n" if verbose
73
+
74
+ sprite_file_path = File.join(@railtie.config.paths.public.to_a.first, sprite_path)
75
+ $stdout << "Writing sprite to #{sprite_file_path} ... " if verbose
76
+ write sprite_file_path, sprite.quality
77
+ $stdout << "done.\n" if verbose
78
+
79
+ $stdout << "Finished #{sprite.path} in #{Time.now - t_sprite} seconds.\n" if verbose
80
+ $stdout << "=================================================\n\n" if verbose
81
+ ensure
82
+ finish
83
+ end
84
+ end
85
+
86
+ $stdout << "#{Time.now}: ActiveSprites \"I finished my run in #{Time.now - t} seconds.\"\n" if verbose
87
+ end
88
+
89
+ private
90
+ def image_full_path(path)
91
+ File.join(@railtie.config.paths.public.to_a.first, sanitize_asset_path(context.image_path(path)))
92
+ end
93
+
94
+ def context
95
+ @context
96
+ end
97
+
98
+ def sanitize_asset_path(path)
99
+ path.split('?').first
100
+ end
101
+
102
+ def setup_context
103
+ unless @railtie.config.respond_to?(:action_controller)
104
+ @railtie.config.action_controller = ActiveSupport::OrderedOptions.new
105
+
106
+ paths = @railtie.config.paths
107
+ options = @railtie.config.action_controller
108
+
109
+ options.assets_dir ||= paths.public.to_a.first
110
+ options.javascripts_dir ||= paths.public.javascripts.to_a.first
111
+ options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
112
+
113
+ ActiveSupport.on_load(:action_controller) do
114
+ options.each { |k,v| send("#{k}=", v) }
115
+ end
116
+ end
117
+
118
+ controller = ActionController::Base.new
119
+ @context = AssetContext.new(@railtie.config.action_controller, {}, controller)
120
+ end
121
+
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,62 @@
1
+ begin
2
+ require 'oily_png'
3
+ rescue LoadError
4
+ require 'chunky_png'
5
+ end
6
+
7
+ module ActiveAssets
8
+ module ActiveSprites
9
+ class ChunkyPngRunner < AbstractRunner
10
+ private
11
+ def set_sprite_details_and_return_image_list(sprite, sprite_path, sprite_pieces, orientation)
12
+ width, height = 0, 0
13
+
14
+ image_list = sprite_pieces.map do |sp|
15
+ sprite_piece_path = image_full_path(sp.path)
16
+ sp_image = ChunkyPNG::Image.from_file(sprite_piece_path)
17
+ sp.details = SpritePiece::Details.new(
18
+ sprite.url.present? ? sprite.url : sprite_path,
19
+ orientation == Sprite::Orientation::VERTICAL ? 0 : width,
20
+ orientation == Sprite::Orientation::VERTICAL ? height : 0,
21
+ sp_image.width,
22
+ sp_image.height
23
+ )
24
+
25
+ width = orientation == Sprite::Orientation::HORIZONTAL ? width + sp_image.width : [width, sp_image.width].max
26
+ height = orientation == Sprite::Orientation::VERTICAL ? height + sp_image.height : [height, sp_image.height].max
27
+
28
+ sp_image
29
+ end
30
+ [image_list, width, height]
31
+ end
32
+
33
+ def create_sprite(sprite, sprite_path, sprite_pieces, image_list, width, height, orientation, verbose)
34
+ @sprite = ChunkyPNG::Image.new(width, height, ChunkyPNG::Color::TRANSPARENT)
35
+
36
+ image_list.each_with_index do |image, i|
37
+ @sprite.replace(image, sprite_pieces[i].details.x, sprite_pieces[i].details.y)
38
+ $stdout << '.' if verbose
39
+ end
40
+ $stdout << "\n" if verbose
41
+ end
42
+
43
+ def write(path, quality = nil)
44
+ FileUtils.mkdir_p(File.dirname(path))
45
+ @sprite.save(path)
46
+ end
47
+
48
+ def finish
49
+ @sprite = nil
50
+ end
51
+
52
+ def runner_name
53
+ begin
54
+ require 'oily_png'
55
+ 'oily_png'
56
+ rescue LoadError
57
+ 'chunky_png'
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,74 @@
1
+ require 'mini_magick'
2
+
3
+ module ActiveAssets
4
+ module ActiveSprites
5
+ class MiniMagickRunner < AbstractRunner
6
+ class ImageTempfile < Tempfile
7
+ def make_tmpname(ext, n)
8
+ 'mini_magick%d-%d%s' % [$$, n, ext ? ".#{ext}" : '']
9
+ end
10
+ end
11
+
12
+ private
13
+ def set_sprite_details_and_return_image_list(sprite, sprite_path, sprite_pieces, orientation)
14
+ offset = 0
15
+
16
+ image_list = sprite_pieces.map do |sp|
17
+ sp_path = image_full_path(sp.path)
18
+ image = MiniMagick::Image.open(sp_path)
19
+ sp.details = SpritePiece::Details.new(
20
+ sprite.url.present? ? sprite.url : sprite_path,
21
+ orientation == Sprite::Orientation::VERTICAL ? 0 : offset,
22
+ orientation == Sprite::Orientation::VERTICAL ? offset : 0,
23
+ image["width"],
24
+ image["height"]
25
+ )
26
+ offset += orientation == Sprite::Orientation::VERTICAL ? image["width"] : image["height"]
27
+
28
+ image
29
+ end
30
+ [image_list]
31
+ end
32
+
33
+ def create_sprite(sprite, sprite_path, sprite_pieces, image_list, width, height, orientation, verbose)
34
+ begin
35
+ tempfile = ImageTempfile.new(File.extname(sprite_path)[1..-1])
36
+ tempfile.binmode
37
+ ensure
38
+ tempfile.close
39
+ end
40
+
41
+ options = {
42
+ :tile => orientation == Sprite::Orientation::VERTICAL ? "1x#{sprite_pieces.size}" : "#{sprite_pieces.size}x1",
43
+ :geometry => "+0+0",
44
+ :background => "transparent",
45
+ :mattecolor => sprite.matte_color || '#bdbdbd'
46
+ }
47
+
48
+ args = options.map {|o, v| "-#{o} '#{v}'"}
49
+ image_list.each {|img| args << img.path}
50
+ args << tempfile.path
51
+
52
+ MiniMagick::Image.new(image_list.first.path).run_command('montage', *args)
53
+ image_list.size.times { $stdout << '.' } if verbose
54
+ $stdout << "\n" if verbose
55
+ @sprite = MiniMagick::Image.open(tempfile.path)
56
+ end
57
+
58
+ def write(path, quality = nil)
59
+ FileUtils.mkdir_p(File.dirname(path))
60
+ @sprite.write(path)
61
+ end
62
+
63
+ def finish
64
+ @sprite.destroy! if @sprite
65
+ @sprite = nil
66
+ end
67
+
68
+ def runner_name
69
+ 'mini_magick'
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,66 @@
1
+ require 'rmagick'
2
+
3
+ module ActiveAssets
4
+ module ActiveSprites
5
+ class RmagickRunner < AbstractRunner
6
+ include Magick
7
+
8
+ DEFAULT_SPRITE = Image.new(0,0).freeze
9
+
10
+ private
11
+ def set_sprite_details_and_return_image_list(sprite, sprite_path, sprite_pieces, orientation)
12
+ sprite_piece_paths = sprite_pieces.map do |sp|
13
+ image_full_path(sp.path)
14
+ end
15
+ image_list = ImageList.new(*sprite_piece_paths)
16
+
17
+ offset = 0
18
+
19
+ image_list.each_with_index do |image, i|
20
+ sprite_pieces[i].details = SpritePiece::Details.new(
21
+ sprite.url.present? ? sprite.url : sprite_path,
22
+ orientation == Sprite::Orientation::VERTICAL ? 0 : offset,
23
+ orientation == Sprite::Orientation::VERTICAL ? offset : 0,
24
+ image.columns,
25
+ image.rows
26
+ )
27
+ offset += orientation == Sprite::Orientation::VERTICAL ? image.rows : image.columns
28
+ end
29
+
30
+ [image_list]
31
+ end
32
+
33
+ def create_sprite(sprite, sprite_path, sprite_pieces, image_list, width, height, orientation, verbose)
34
+ @sprite = image_list.montage do
35
+ self.tile = orientation == Sprite::Orientation::VERTICAL ? "1x#{sprite_pieces.size}" : "#{sprite_pieces.size}x1"
36
+ self.geometry = "+0+0"
37
+ self.background_color = 'transparent'
38
+ self.matte_color = sprite.matte_color || '#bdbdbd'
39
+ end
40
+
41
+ image_list.size.times { $stdout << '.' } if verbose
42
+ $stdout << "\n" if verbose
43
+ end
44
+
45
+ def write(path, quality = nil)
46
+ FileUtils.mkdir_p(File.dirname(path))
47
+ @sprite.write("#{File.extname(path)[1..-1]}:#{path}") do
48
+ self.quality = quality || 75
49
+ end
50
+ end
51
+
52
+ def finish
53
+ if @sprite
54
+ @sprite.strip!
55
+ @sprite.destroy! unless @sprite == DEFAULT_SPRITE
56
+ end
57
+ @sprite = DEFAULT_SPRITE
58
+ end
59
+
60
+ def runner_name
61
+ 'rmagick'
62
+ end
63
+
64
+ end
65
+ end
66
+ end