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 +4 -4
- data/lib/spritely/cache.rb +9 -0
- data/lib/spritely/collection.rb +10 -0
- data/lib/spritely/image_set.rb +5 -0
- data/lib/spritely/options.rb +24 -8
- data/lib/spritely/sass_functions.rb +6 -0
- data/lib/spritely/sprockets/manifest.rb +4 -0
- data/lib/spritely/version.rb +1 -1
- data/spec/spritely/options_spec.rb +3 -3
- data/spec/spritely/sass_functions_spec.rb +30 -32
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 667e7648ac4d154e6ec6f9a98b08f1f50a2aae93
|
4
|
+
data.tar.gz: 8ce617668fea3fc218ac4bff2aad24988ba9bdc1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b8fb5a5c96ebb17a0c3aefca9a4081f4036c259b182b82cbcfb37085b64da7f10cea73aa1c9b10bd74a0afc4a24ede9ca431ac8db8853559024236ef46aea82
|
7
|
+
data.tar.gz: ac6fc8c395a4a934217090846e458347cc6d150c09dca1ff87000dd6df91191662e0dca3eba6861da2356f5b2ce8a885224c024d9e9f1479df6ceeb9fe156c3c
|
data/lib/spritely/cache.rb
CHANGED
@@ -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
|
data/lib/spritely/collection.rb
CHANGED
@@ -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
|
data/lib/spritely/image_set.rb
CHANGED
@@ -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
|
data/lib/spritely/options.rb
CHANGED
@@ -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 = [
|
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
|
-
|
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
|
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)
|
data/lib/spritely/version.rb
CHANGED
@@ -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=#{{'
|
18
|
-
its(:cache_key) { should eq({some_new_image_spacing
|
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[
|
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
|
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
|
-
|
14
|
-
|
23
|
+
before(:all) { ::Sass::Script::Functions.send(:include, AssetUrlModule) }
|
24
|
+
after(:all) { ::Sass::Script::Functions.send(:undef_method, :asset_url) }
|
15
25
|
|
16
|
-
|
17
|
-
|
26
|
+
shared_examples "a sprite function that checks image existence" do
|
27
|
+
let(:image) { nil }
|
18
28
|
|
19
|
-
|
20
|
-
|
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
|
-
|
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
|
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
|
-
|
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 '#
|
61
|
-
|
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
|
-
|
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
|
-
|
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.
|
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-
|
11
|
+
date: 2014-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chunky_png
|