spritely 0.1.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9a3b754bc1e6fd1111a7eab9259f7239e45e4c4d
4
- data.tar.gz: 03f79678a60dd3967883a42828956e7b74074eb4
3
+ metadata.gz: 667e7648ac4d154e6ec6f9a98b08f1f50a2aae93
4
+ data.tar.gz: 8ce617668fea3fc218ac4bff2aad24988ba9bdc1
5
5
  SHA512:
6
- metadata.gz: 1fd3cba6154c6c96082e4c06331de30838f42edddf3607c462617a5e5ced06a2194ee5319092702a66656f71bcea910c1260598daab3fb57da4e72debea22b36
7
- data.tar.gz: 2af4928b574b2dbcba9d3bad26332a92a67dad95b767857fb02db5c41f798c1e7111e799e98d4134866d67ebbc5716d088319182c2a6f13a3eecb6df14a586e9
6
+ metadata.gz: 0b8fb5a5c96ebb17a0c3aefca9a4081f4036c259b182b82cbcfb37085b64da7f10cea73aa1c9b10bd74a0afc4a24ede9ca431ac8db8853559024236ef46aea82
7
+ data.tar.gz: ac6fc8c395a4a934217090846e458347cc6d150c09dca1ff87000dd6df91191662e0dca3eba6861da2356f5b2ce8a885224c024d9e9f1479df6ceeb9fe156c3c
@@ -1,6 +1,15 @@
1
1
  require 'digest/md5'
2
2
 
3
3
  module Spritely
4
+ # `Cache` is responsible for generating and fetching the current sprite cache
5
+ # `value. It is used to determine whether to generate a new sprite map or not.
6
+ #
7
+ # Passing in a set of objects that respond to `cache_key` will generate an MD5
8
+ # hash that can then be stored in the image itself.
9
+ #
10
+ # Fetching an existing cache key actually reads the PNG file itself. It scans
11
+ # the chunks of the PNG until it finds the `cache_key` keyword, returning the
12
+ # subsequent value found.
4
13
  class Cache < Struct.new(:filename)
5
14
  PNG_SIGNATURE_LENGTH = 8 # http://www.w3.org/TR/PNG/#5PNG-file-signature
6
15
  PNG_INFO_LENGTH = 8 # http://www.w3.org/TR/PNG/#5DataRep
@@ -1,6 +1,8 @@
1
1
  require 'spritely/image_set'
2
2
 
3
3
  module Spritely
4
+ # A `SpriteMap` has a `Collection` that knows how to calculate the size of the
5
+ # sprite, based on image repetition and spacing.
4
6
  class Collection < Struct.new(:files, :options)
5
7
  def self.create(*args)
6
8
  new(*args).tap do |collection|
@@ -20,6 +22,12 @@ module Spritely
20
22
  files.collect { |file| File.mtime(file) }.join
21
23
  end
22
24
 
25
+ # Returns the width of the to-be-generated sprite image. When none of the
26
+ # images repeat, it is simply the max width of all images in the sprite.
27
+ # When an image in the sprite is repeated, a calculation is performed based
28
+ # on the least common multiple of all repeated images. That least common
29
+ # multiple is then multiplied by the minimum multiple that will result in a
30
+ # value greater than or equal to the max width of all images in the sprite.
23
31
  def width
24
32
  return @width if @width
25
33
 
@@ -38,6 +46,8 @@ module Spritely
38
46
  heights.reduce(:+)
39
47
  end
40
48
 
49
+ # Upon creation, the collection is then positioned appropriately by
50
+ # positioning each image within the sprite.
41
51
  def position!
42
52
  image_sets.each_with_index do |image_set, index|
43
53
  image_set.top = heights[0..index].reduce(:+) - image_set.outer_height
@@ -1,6 +1,8 @@
1
1
  require 'spritely/image'
2
2
 
3
3
  module Spritely
4
+ # Each image in the sprite maps to an instance of `ImageSet` that stores the
5
+ # image data, width, height, and outer positioning.
4
6
  class ImageSet
5
7
  attr_accessor :top
6
8
  attr_reader :path, :options, :data, :width, :height, :left
@@ -37,6 +39,9 @@ module Spritely
37
39
  options[:position] == 'right'
38
40
  end
39
41
 
42
+ # When positioned in the sprite, we must take into account whether the image
43
+ # is configured to repeat, or is positioned to the right-hand side of the
44
+ # sprite map.
40
45
  def position_in!(collection_width)
41
46
  if repeated?
42
47
  left_position = 0
@@ -1,10 +1,28 @@
1
1
  require 'active_support/core_ext/hash/deep_merge'
2
2
  require 'active_support/core_ext/hash/except'
3
+ require 'active_support/core_ext/hash/keys'
3
4
  require 'active_support/core_ext/hash/slice'
4
5
 
5
6
  module Spritely
7
+ # Provides a simpler method of querying a particular image's options. Rather
8
+ # than each `Image` needing to know how to parse the keyword arguments passed
9
+ # into the `spritely-map` function, this simplifies and de-duplicates the
10
+ # passed option keys.
11
+ #
12
+ # $application-sprite: spritely-map("application/*.png",
13
+ # $arrow-repeat: true,
14
+ # $arrow-spacing: 10px,
15
+ # $another-image-position: right,
16
+ # $spacing: 5px
17
+ # );
18
+ #
19
+ # The options passed in above will be easily accessible via an instance of
20
+ # this class.
21
+ #
22
+ # options['arrow'] => {repeat: true, spacing: 10}
23
+ # options['another-image'] => {position: 'right', spacing: 5}
6
24
  class Options < Struct.new(:hash)
7
- GLOBAL_OPTIONS = [:spacing, :position]
25
+ GLOBAL_OPTIONS = ['spacing', 'position']
8
26
 
9
27
  def cache_key
10
28
  stripped_hash.to_s
@@ -15,28 +33,26 @@ module Spritely
15
33
  end
16
34
 
17
35
  def [](key)
18
- options[key] || global_options
36
+ options[key.gsub('-', '_')] || global_options
19
37
  end
20
38
 
21
39
  private
22
40
 
23
41
  def options
24
42
  @options ||= stripped_hash.except(*GLOBAL_OPTIONS).inject({}) do |h, (key, value)|
25
- split_key = key.to_s.split('_')
26
- option = {split_key.pop.to_sym => value}
27
- image = split_key.join('-')
43
+ image, _, option = key.rpartition('_')
28
44
  h[image] ||= global_options.dup
29
- h.deep_merge!(image => option)
45
+ h.deep_merge!(image => {option.to_sym => value})
30
46
  end
31
47
  end
32
48
 
33
49
  def global_options
34
- @global_options ||= stripped_hash.slice(*GLOBAL_OPTIONS)
50
+ @global_options ||= stripped_hash.slice(*GLOBAL_OPTIONS).symbolize_keys!
35
51
  end
36
52
 
37
53
  def stripped_hash
38
54
  @stripped_hash ||= hash.inject({}) do |h, (key, sass_object)|
39
- h.merge!(key.to_sym => sass_object.value)
55
+ h.merge!(key => sass_object.value)
40
56
  end
41
57
  end
42
58
  end
@@ -30,6 +30,12 @@ module Spritely
30
30
 
31
31
  ::Sass::Script::Functions.declare :spritely_position, [:sprite_map, :image_name]
32
32
 
33
+ def spritely_background(sprite_map, image_name)
34
+ Sass::Script::List.new([spritely_url(sprite_map), spritely_position(sprite_map, image_name)], :space)
35
+ end
36
+
37
+ ::Sass::Script::Functions.declare :spritely_background, [:sprite_map, :image_name]
38
+
33
39
  def spritely_width(sprite_map, image_name)
34
40
  image = find_image(sprite_map, image_name)
35
41
 
@@ -2,6 +2,10 @@ require 'sprockets/manifest'
2
2
  require 'active_support/core_ext/module/aliasing'
3
3
 
4
4
  module Sprockets
5
+ # In order to hook into Sprockets' asset compilation appropriately, we must
6
+ # chain our own implementation of `compile`. Our extension calls up to the
7
+ # original, and then performs the same action on the generated sprite images,
8
+ # forcing the sprites to be part of the compiled asset manifest.
5
9
  class Manifest
6
10
  def compile_with_sprites(*args)
7
11
  compile_without_sprites(*args)
@@ -1,3 +1,3 @@
1
1
  module Spritely
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -14,8 +14,8 @@ describe Spritely::Options do
14
14
 
15
15
  subject(:options) { Spritely::Options.new(hash) }
16
16
 
17
- its(:inspect) { should eq("#<Spritely::Options global_options=#{{spacing: 901, position: 'left'}} options=#{{'some-new-image' => {spacing: 789, position: 'right', x: 123, y: 456}, 'another-image' => {spacing: 901, position: 'left', repeat: true}, 'yet-another-image' => {spacing: 901, position: 'left', repeat: false}}}>") }
18
- its(:cache_key) { should eq({some_new_image_spacing: 789, some_new_image_x: 123, some_new_image_y: 456, some_new_image_position: 'right', another_image_repeat: true, yet_another_image_repeat: false, spacing: 901, position: 'left'}.to_s) }
17
+ its(:inspect) { should eq("#<Spritely::Options global_options=#{{spacing: 901, position: 'left'}} options=#{{'some_new_image' => {spacing: 789, position: 'right', x: 123, y: 456}, 'another_image' => {spacing: 901, position: 'left', repeat: true}, 'yet_another_image' => {spacing: 901, position: 'left', repeat: false}}}>") }
18
+ its(:cache_key) { should eq({"some_new_image_spacing" => 789, "some_new_image_x" => 123, "some_new_image_y" => 456, "some_new_image_position" => 'right', "another_image_repeat" => true, "yet_another_image_repeat" => false, "spacing" => 901, "position" => 'left'}.to_s) }
19
19
 
20
20
  its(['some-new-image']) { should eq({spacing: 789, position: 'right', x: 123, y: 456}) }
21
21
  its(['another-image']) { should eq({spacing: 901, repeat: true, position: 'left'}) }
@@ -23,7 +23,7 @@ describe Spritely::Options do
23
23
 
24
24
  describe '#[]' do
25
25
  it 'should fall back to an empty hash' do
26
- expect(options[:unknown]).to eq({spacing: 901, position: 'left'})
26
+ expect(options['unknown']).to eq({spacing: 901, position: 'left'})
27
27
  end
28
28
  end
29
29
  end
@@ -6,47 +6,41 @@ describe Spritely::SassFunctions do
6
6
  def files; []; end
7
7
  end
8
8
 
9
+ module AssetUrlModule
10
+ def asset_url(path)
11
+ Sass::Script::String.new("url(#{path})")
12
+ end
13
+ end
14
+
9
15
  let(:sprite_map) { SpriteMapDouble.new }
16
+ let(:image) { double(left: 10, top: 12, width: 25, height: 50) }
10
17
 
11
- before { allow(Spritely::SpriteMap).to receive(:create).and_return(sprite_map) }
18
+ before do
19
+ allow(Spritely::SpriteMap).to receive(:create).and_return(sprite_map)
20
+ allow(sprite_map).to receive(:find).with('bar').and_return(image)
21
+ end
12
22
 
13
- shared_examples "a sprite function that checks image existence" do
14
- before { allow(sprite_map).to receive(:find).with('bar').and_return(image) }
23
+ before(:all) { ::Sass::Script::Functions.send(:include, AssetUrlModule) }
24
+ after(:all) { ::Sass::Script::Functions.send(:undef_method, :asset_url) }
15
25
 
16
- context 'the image does not exist' do
17
- let(:image) { nil }
26
+ shared_examples "a sprite function that checks image existence" do
27
+ let(:image) { nil }
18
28
 
19
- it 'should raise a nice exception' do
20
- expect { subject }.to raise_error(Sass::SyntaxError, "No image 'bar' found in sprite map 'test'.")
21
- end
29
+ it 'should raise a nice exception' do
30
+ expect { subject }.to raise_error(Sass::SyntaxError, "No image 'bar' found in sprite map 'test'.")
22
31
  end
23
32
  end
24
33
 
25
34
  describe '#spritely_url' do
26
- before do
27
- asset_url_module = Module.new do
28
- def asset_url(path)
29
- path
30
- end
31
- end
32
- ::Sass::Script::Functions.send(:include, asset_url_module)
33
- end
34
-
35
- after do
36
- ::Sass::Script::Functions.send(:undef_method, :asset_url)
37
- end
35
+ subject { evaluate("spritely-url(spritely-map('test/*.png'))") }
38
36
 
39
- it "should use Rails' built-in asset_url function" do
40
- expect(evaluate("spritely-url(spritely-map('test/*.png'))")).to eq('sprites/test.png')
41
- end
37
+ it { should eq('url(sprites/test.png)') }
42
38
  end
43
39
 
44
40
  describe '#spritely_position' do
45
- let(:image) { double(left: 10, top: 12) }
46
-
47
41
  subject { evaluate("spritely-position(spritely-map('test/*.png'), 'bar')") }
48
42
 
49
- include_examples "a sprite function that checks image existence"
43
+ it_should_behave_like "a sprite function that checks image existence"
50
44
 
51
45
  it { should eq('-10px -12px') }
52
46
 
@@ -57,22 +51,26 @@ describe Spritely::SassFunctions do
57
51
  end
58
52
  end
59
53
 
60
- describe '#spritely_width' do
61
- let(:image) { double(width: 25) }
54
+ describe '#spritely_background' do
55
+ subject { evaluate("spritely-background(spritely-map('test/*.png'), 'bar')") }
56
+
57
+ it_should_behave_like "a sprite function that checks image existence"
62
58
 
59
+ it { should eq('url(sprites/test.png) -10px -12px') }
60
+ end
61
+
62
+ describe '#spritely_width' do
63
63
  subject { evaluate("spritely-width(spritely-map('test/*.png'), 'bar')") }
64
64
 
65
- include_examples "a sprite function that checks image existence"
65
+ it_should_behave_like "a sprite function that checks image existence"
66
66
 
67
67
  it { should eq('25px') }
68
68
  end
69
69
 
70
70
  describe '#spritely_height' do
71
- let(:image) { double(height: 50) }
72
-
73
71
  subject { evaluate("spritely-height(spritely-map('test/*.png'), 'bar')") }
74
72
 
75
- include_examples "a sprite function that checks image existence"
73
+ it_should_behave_like "a sprite function that checks image existence"
76
74
 
77
75
  it { should eq('50px') }
78
76
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spritely
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Robbin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-05 00:00:00.000000000 Z
11
+ date: 2014-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chunky_png