sprite 0.1.7 → 0.2.0

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 (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