sprite 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/Gemfile +9 -0
  2. data/Gemfile.lock +50 -0
  3. data/README.md +27 -23
  4. data/Rakefile +37 -20
  5. data/lib/sprite.rb +5 -0
  6. data/lib/sprite/builder.rb +90 -101
  7. data/lib/sprite/config.rb +48 -0
  8. data/lib/sprite/image_combiner.rb +17 -10
  9. data/lib/sprite/image_config.rb +40 -0
  10. data/lib/sprite/image_reader.rb +14 -0
  11. data/lib/sprite/image_resizer.rb +19 -0
  12. data/lib/sprite/image_writer.rb +29 -0
  13. data/lib/sprite/runner.rb +6 -4
  14. data/lib/sprite/sass_extensions.rb +93 -48
  15. data/lib/sprite/styles.rb +3 -1
  16. data/lib/sprite/styles/css_generator.rb +19 -15
  17. data/lib/sprite/styles/sass_generator.rb +5 -4
  18. data/lib/sprite/styles/sass_mixin_generator.rb +7 -7
  19. data/lib/sprite/styles/sass_yml_generator.rb +15 -16
  20. data/lib/sprite/styles/templated_css_generator.rb +41 -0
  21. data/spec/output/android_horizontal/images/sprites/android-icons.png +0 -0
  22. data/spec/output/android_horizontal/stylesheets/android-icons.css +1 -0
  23. data/spec/output/android_vertical/images/sprites/android-icons.png +0 -0
  24. data/spec/output/android_vertical/stylesheets/android-icons.css +1 -0
  25. data/spec/resources/android_css.erb +5 -0
  26. data/spec/resources/configs/android-icons.yml +19 -0
  27. data/spec/resources/configs/config-test.yml +26 -0
  28. data/spec/resources/images/android-icons/LICENSE +1 -0
  29. data/spec/resources/images/android-icons/barcode.png +0 -0
  30. data/spec/resources/images/android-icons/cards.png +0 -0
  31. data/spec/resources/images/android-icons/chart.png +0 -0
  32. data/spec/resources/images/android-icons/clock.png +0 -0
  33. data/spec/resources/images/android-icons/cloud.png +0 -0
  34. data/spec/resources/images/android-icons/colour-picker.png +0 -0
  35. data/spec/resources/images/android-icons/dialog.png +0 -0
  36. data/spec/resources/images/android-icons/dice.png +0 -0
  37. data/spec/resources/images/android-icons/disc.png +0 -0
  38. data/spec/resources/images/android-icons/equalizer.png +0 -0
  39. data/spec/resources/images/android-icons/filter.png +0 -0
  40. data/spec/resources/images/android-icons/flag.png +0 -0
  41. data/spec/resources/images/android-icons/flash.png +0 -0
  42. data/spec/resources/images/android-icons/globe.png +0 -0
  43. data/spec/resources/images/android-icons/happy.png +0 -0
  44. data/spec/resources/images/android-icons/large-tiles.png +0 -0
  45. data/spec/resources/images/android-icons/light.png +0 -0
  46. data/spec/resources/images/android-icons/love.png +0 -0
  47. data/spec/resources/images/android-icons/magnet.png +0 -0
  48. data/spec/resources/images/android-icons/monitor.png +0 -0
  49. data/spec/resources/images/android-icons/music.png +0 -0
  50. data/spec/resources/images/android-icons/pie-chart.png +0 -0
  51. data/spec/resources/images/android-icons/ruler.png +0 -0
  52. data/spec/resources/images/android-icons/sad.png +0 -0
  53. data/spec/resources/images/android-icons/seal.png +0 -0
  54. data/spec/resources/images/android-icons/shopping.png +0 -0
  55. data/spec/resources/images/android-icons/small-tiles.png +0 -0
  56. data/spec/resources/images/android-icons/sun.png +0 -0
  57. data/spec/resources/images/android-icons/tag.png +0 -0
  58. data/spec/resources/images/android-icons/umbrella.png +0 -0
  59. data/spec/resources/images/topics/good-topic.gif +0 -0
  60. data/spec/resources/images/topics/mid-topic.gif +0 -0
  61. data/spec/spec_helper.rb +32 -0
  62. data/spec/sprite/builder_spec.rb +76 -0
  63. data/spec/sprite/config_spec.rb +64 -0
  64. data/spec/sprite/image_combiner_spec.rb +40 -0
  65. data/spec/sprite/image_reader_spec.rb +16 -0
  66. data/spec/sprite/styles/css_spec.rb +0 -0
  67. data/spec/sprite/styles/sass_mixin_spec.rb +0 -0
  68. data/spec/sprite/styles/sass_spec.rb +0 -0
  69. data/sprite.gemspec +30 -49
  70. metadata +142 -21
  71. data/VERSION +0 -1
@@ -0,0 +1,48 @@
1
+ module Sprite
2
+ class Config
3
+ DEFAULT_CONFIG_PATH = 'config/sprite.yml'
4
+
5
+ def self.read_config(path = nil)
6
+ config_path = File.join(Sprite.root, path || DEFAULT_CONFIG_PATH)
7
+
8
+ # read configuration
9
+ if File.exists?(config_path)
10
+ begin
11
+ File.open(config_path) {|f| YAML::load(f)} || {}
12
+ rescue => e
13
+ puts "Error reading sprite config: #{config_path}"
14
+ puts e.to_s
15
+ {}
16
+ end
17
+ else
18
+ {}
19
+ end
20
+ end
21
+
22
+ # chop off the trailing slash on a directory path (if it exists)
23
+ def self.chop_trailing_slash(path)
24
+ path = path[0...-1] if path[-1] == File::SEPARATOR
25
+ path
26
+ end
27
+
28
+ # check if the path is set
29
+ def self.path_present?(path)
30
+ path.to_s.strip != ""
31
+ end
32
+
33
+ def initialize(settings_hash)
34
+ @settings = settings_hash
35
+ end
36
+
37
+ # get the disk path for a location within the public folder (if set)
38
+ def public_path(location, relative = false)
39
+ path_parts = []
40
+ path_parts << Sprite.root unless relative
41
+ path_parts << Config.chop_trailing_slash(@settings['public_path']) if Config.path_present?(@settings['public_path'])
42
+ path_parts << location
43
+
44
+ File.join(*path_parts)
45
+ end
46
+
47
+ end
48
+ end
@@ -1,15 +1,25 @@
1
1
  module Sprite
2
2
  class ImageCombiner
3
- def initialize
4
- # avoid loading rmagick till the last possible moment
5
- require 'rmagick'
3
+ def initialize(image_config)
4
+ begin
5
+ # avoid loading rmagick till the last possible moment
6
+ require "RMagick"
7
+ rescue LoadError
8
+ require 'rmagick'
9
+ end
10
+
11
+ @image_config = image_config
6
12
  end
7
-
13
+
8
14
  def composite_images(dest_image, src_image, x, y)
9
15
  width = [src_image.columns + x, dest_image.columns].max
10
16
  height = [src_image.rows + y, dest_image.rows].max
11
17
  image = Magick::Image.new(width, height)
12
- image.opacity = Magick::MaxRGB
18
+ if @image_config.background_color
19
+ image.opacity = 0
20
+ else
21
+ image.opacity = Magick::MaxRGB
22
+ end
13
23
 
14
24
  image.composite!(dest_image, 0, 0, Magick::OverCompositeOp)
15
25
  image.composite!(src_image, x, y, Magick::OverCompositeOp)
@@ -17,17 +27,14 @@ module Sprite
17
27
  end
18
28
 
19
29
  # Image Utility Methods
20
- def get_image(image_filename)
21
- image = Magick::Image::read(image_filename).first
22
- end
23
30
 
24
31
  def image_properties(image)
25
32
  {:name => File.basename(image.filename).split('.')[0], :width => image.columns, :height => image.rows}
26
33
  end
27
-
34
+
28
35
  # REMOVE RMAGICK AND USE IMAGEMAGICK FROM THE COMMAND LINE
29
36
  # identify => find properties for an image
30
37
  # composite => combine 2 images
31
38
 
32
39
  end
33
- end
40
+ end
@@ -0,0 +1,40 @@
1
+ module Sprite
2
+ class ImageConfig
3
+ def initialize(image_info, global_config_info)
4
+ @image_info = image_info
5
+ @global_config_info = global_config_info
6
+ end
7
+
8
+ def sources
9
+ @image_info['sources'].to_a
10
+ end
11
+
12
+ def name
13
+ @image_info['name']
14
+ end
15
+
16
+ def format
17
+ @image_info['format'] || @global_config_info["default_format"]
18
+ end
19
+
20
+ def quality
21
+ @image_info['quality'] || @global_config_info["default_quality"]
22
+ end
23
+
24
+ def background_color
25
+ @image_info['background_color'] || @global_config_info["default_background_color"]
26
+ end
27
+
28
+ def spaced_by
29
+ @image_info['spaced_by'] || @global_config_info["default_spacing"] || 0
30
+ end
31
+
32
+ def resize_to
33
+ @image_info['resize_to'] || @global_config_info['resize_to']
34
+ end
35
+
36
+ def horizontal_layout?
37
+ @image_info['align'].to_s == 'horizontal'
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ module Sprite
2
+ class ImageReader
3
+ def self.read(image_filename)
4
+ # avoid loading rmagick till the last possible moment
5
+ begin
6
+ require "RMagick"
7
+ rescue LoadError
8
+ require 'rmagick'
9
+ end
10
+
11
+ Magick::Image::read(image_filename).first
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ module Sprite
2
+ class ImageResizer
3
+ def initialize(resize_to)
4
+ if resize_to
5
+ @resizing = true
6
+ @target_width, @target_height = *(resize_to.split('x').map(&:to_i))
7
+ end
8
+ end
9
+
10
+ def resize(image)
11
+ if @resizing
12
+ needs_resizing = image.columns != @target_width || image.rows != @target_height
13
+ if needs_resizing
14
+ image.scale!(@target_width, @target_height)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ module Sprite
2
+ class ImageWriter
3
+ def initialize(config)
4
+ @config = config
5
+ end
6
+
7
+ def write(image, name, format, quality = nil, background_color = nil)
8
+ # set up path
9
+ path = image_output_path(name, format)
10
+ FileUtils.mkdir_p(File.dirname(path))
11
+
12
+ # write sprite image file to disk
13
+ image.write(path) {
14
+ self.quality = quality unless quality.nil?
15
+ self.background_color = background_color unless background_color.nil?
16
+ }
17
+ end
18
+
19
+ # get the disk path for a location within the image output folder
20
+ def image_output_path(name, format, relative = false)
21
+ path_parts = []
22
+ path_parts << Config.chop_trailing_slash(@config['image_output_path']) if Config.path_present?(@config['image_output_path'])
23
+
24
+ cache_buster = "-#{@config['cache_buster']}" if @config['cache_buster']
25
+ path_parts << "#{name}#{cache_buster}.#{format}"
26
+ Config.new(@config).public_path(File.join(*path_parts), relative)
27
+ end
28
+ end
29
+ end
data/lib/sprite/runner.rb CHANGED
@@ -1,18 +1,20 @@
1
1
  module Sprite
2
2
  class Runner
3
-
3
+
4
4
  attr_accessor :options
5
5
  def initialize(args)
6
6
  self.options = set_options(args)
7
7
  end
8
-
8
+
9
9
  def set_options(args)
10
10
  opts = {}
11
11
  # TODO
12
12
  # edit options with passed in args
13
+ opts[:path] = args.first if args.any?
14
+
13
15
  opts
14
16
  end
15
-
17
+
16
18
  # run sprite creator
17
19
  def run!
18
20
  begin
@@ -25,6 +27,6 @@ module Sprite
25
27
  end
26
28
  0
27
29
  end
28
-
30
+
29
31
  end
30
32
  end
@@ -1,57 +1,102 @@
1
- module Sprite::Sass::Extensions
2
- def sprite_background(group, image)
3
- sprite = sprite_data(group, image)
4
- if sprite
5
- "url('#{sprite[:img]}') no-repeat #{sprite[:x]}px #{sprite[:y]}px"
6
- else
7
- ""
8
- end
9
- end
1
+ module Sprite
2
+ module Sass
3
+ module Extensions
4
+ def sprite_background(group, image)
5
+ sprite = sprite_data(group, image)
6
+ if sprite
7
+ sprite_path = sprite_builder.image_path(group.value)
8
+ ::Sass::Script::String.new "url('#{sprite_path}') no-repeat #{sprite[:x]}px #{sprite[:y]}px"
9
+ else
10
+ ::Sass::Script::String.new ""
11
+ end
12
+ end
10
13
 
11
- def sprite_width(group, image)
12
- sprite = sprite_data(group, image)
13
- if sprite
14
- "#{sprite[:width]}px"
15
- else
16
- ""
17
- end
18
- end
19
-
20
- def sprite_height(group, image)
21
- sprite = sprite_data(group, image)
22
- if sprite
23
- "#{sprite[:height]}px"
24
- else
25
- ""
26
- end
27
- end
28
-
29
- protected
30
- def sprite_data(group, image)
31
- unless @__sprite_data
32
-
33
- # TODO: read template from !sprite_data
34
- sprite_data_path = "public/sass/sprites.yml"
35
-
36
- # figure out the site root
37
- root = "./"
38
-
39
- # read sprite data from yml
40
- @__sprite_data = File.open(File.join(root, sprite_data_path)) { |yf| YAML::load( yf ) }
41
- end
42
-
43
- group_data = @__sprite_data[group.to_s]
44
- if group_data
45
- return group_data[image.to_s]
46
- else
47
- nil
14
+ def sprite_width(*args)
15
+ ::Sass::Script::String.new sprite_attr(:width, *args)
16
+ end
17
+
18
+ def sprite_height(*args)
19
+ ::Sass::Script::String.new sprite_attr(:height, *args)
20
+ end
21
+
22
+ def sprite_x_offset(*args)
23
+ ::Sass::Script::String.new sprite_attr(:x, *args)
24
+ end
25
+
26
+ def sprite_y_offset(*args)
27
+ ::Sass::Script::String.new sprite_attr(:y, *args)
28
+ end
29
+
30
+ def sprite_image(group)
31
+ ::Sass::Script::String.new sprite_builder.image_path(group.value)
32
+ end
33
+
34
+ def sprite_url(group)
35
+ ::Sass::Script::String.new "url('#{sprite_image(group)}')"
36
+ end
37
+
38
+ ##
39
+ # Return a sprite offset for the given image. When the optional x and y values are passed,
40
+ # numeric values are treated as additional offsets to the sprite image offset. Any string
41
+ # values are treated as replacement offsets.
42
+ #
43
+ # Examples:
44
+ # * sprite_offset("common", "icon", "100%", 5) => offset is "100% x+5" where x is the x offset in the sprite
45
+ # * sprite_offset("common", "icon", "top", "right") => offset is "top right"
46
+ def sprite_offset(group, image, x=nil, y=nil)
47
+ xoff = compute_offset(group, image, :x, x)
48
+ yoff = compute_offset(group, image, :y, y)
49
+ ::Sass::Script::String.new "#{xoff} #{yoff}"
50
+ end
51
+
52
+ protected
53
+ def compute_offset(group, image, axis, offset)
54
+ if offset
55
+ val = offset.value
56
+ if val.is_a? Fixnum
57
+ (sprite_attr(axis, group, image).to_i + val).to_s + 'px'
58
+ else
59
+ val
60
+ end
61
+ else
62
+ sprite_attr(axis, group, image)
63
+ end
64
+ end
65
+
66
+ def sprite_attr(attr, group, image)
67
+ sprite = sprite_data(group, image)
68
+ if sprite
69
+ "#{sprite[attr]}px"
70
+ else
71
+ ""
72
+ end
73
+ end
74
+
75
+ def sprite_builder
76
+ @__sprite_builder ||= Builder.from_config
77
+ end
78
+
79
+ def sprite_data(group, image)
80
+ unless @__sprite_data
81
+ sprite_data_path = sprite_builder.style_output_path
82
+
83
+ # read sprite data from yml
84
+ @__sprite_data = File.open(sprite_data_path) { |yf| YAML::load( yf ) }
85
+ end
86
+
87
+ group_data = @__sprite_data[group.value]
88
+ if group_data
89
+ return group_data[image.value]
90
+ else
91
+ nil
92
+ end
93
+ end
48
94
  end
49
95
  end
50
-
51
96
  end
52
97
 
53
98
  if defined?(Sass)
54
99
  module Sass::Script::Functions
55
100
  include Sprite::Sass::Extensions
56
101
  end
57
- end
102
+ end
data/lib/sprite/styles.rb CHANGED
@@ -2,15 +2,17 @@ require 'sprite/styles/sass_generator'
2
2
  require 'sprite/styles/css_generator'
3
3
  require 'sprite/styles/sass_yml_generator'
4
4
  require 'sprite/styles/sass_mixin_generator'
5
+ require 'sprite/styles/templated_css_generator'
5
6
 
6
7
  module Sprite::Styles
7
8
  GENERATORS = {
8
9
  "css" => "CssGenerator",
10
+ "templated_css" => "TemplatedCssGenerator",
9
11
  "sass" => "SassGenerator",
10
12
  "sass_mixin" => "SassMixinGenerator",
11
13
  "sass_yml" => "SassYmlGenerator"
12
14
  }
13
-
15
+
14
16
  def self.get(config)
15
17
  const_get(GENERATORS[config])
16
18
  rescue
@@ -5,26 +5,30 @@ module Sprite
5
5
  def initialize(builder)
6
6
  @builder = builder
7
7
  end
8
-
8
+
9
9
  def write(path, sprite_files)
10
- # set up class_name to append to each rule
11
- sprites_class = @builder.config['sprites_class'] ? ".#{@builder.config['sprites_class']}" : ""
12
-
13
10
  # write styles to disk
14
11
  File.open(File.join(Sprite.root, path), 'w') do |f|
15
- # write stylesheet file to disk
16
- sprite_files.each do |sprite_file, sprites|
17
- sprites.each do |sprite|
18
- f.puts "#{sprites_class}.#{sprite[:group]}#{@builder.config['class_separator']}#{sprite[:name]} {"
19
- f.puts " background: url('/#{@builder.config['image_output_path']}#{sprite_file}') no-repeat #{sprite[:x]}px #{sprite[:y]}px;"
20
- f.puts " width: #{sprite[:width]}px;"
21
- f.puts " height: #{sprite[:height]}px;"
22
- f.puts "}"
23
- end
12
+ write_standard_css(f, sprite_files)
13
+ end
14
+ end
15
+
16
+ def write_standard_css(f, sprite_files)
17
+ # set up class_name to append to each rule
18
+ sprites_class = @builder.config['sprites_class'] ? ".#{@builder.config['sprites_class']}" : ""
19
+
20
+ sprite_files.each do |sprite_file, sprites|
21
+ background_url = @builder.background_url(sprite_file)
22
+ sprites.each do |sprite|
23
+ f.puts "#{sprites_class}.#{sprite[:group]}#{@builder.config['class_separator']}#{sprite[:name]} {"
24
+ f.puts " background: #{background_url} no-repeat #{sprite[:x]}px #{sprite[:y]}px;"
25
+ f.puts " width: #{sprite[:width]}px;"
26
+ f.puts " height: #{sprite[:height]}px;"
27
+ f.puts "}"
24
28
  end
25
- end
29
+ end
26
30
  end
27
-
31
+
28
32
  def extension
29
33
  "css"
30
34
  end