spritely 0.0.1 → 0.0.2
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/collection.rb +58 -0
- data/lib/spritely/generators/chunky_png.rb +1 -1
- data/lib/spritely/image.rb +2 -17
- data/lib/spritely/image_set.rb +52 -0
- data/lib/spritely/options.rb +19 -0
- data/lib/spritely/sass_functions.rb +8 -6
- data/lib/spritely/sprite_map.rb +17 -12
- data/lib/spritely/sprockets/manifest.rb +13 -0
- data/lib/spritely/version.rb +1 -1
- data/lib/spritely.rb +1 -2
- data/spec/fixtures/correct-sprite.png +0 -0
- data/spec/fixtures/rails-app/app/assets/images/application/background.png +0 -0
- data/spec/fixtures/rails-app/app/assets/images/application/fool.png +0 -0
- data/spec/fixtures/rails-app/app/assets/images/application/football.png +0 -0
- data/spec/fixtures/rails-app/app/assets/images/application/mario.png +0 -0
- data/spec/fixtures/rails-app/app/assets/stylesheets/sprites.css.scss +13 -0
- data/spec/integration/application.png +0 -0
- data/spec/integration/precompilation_spec.rb +29 -0
- data/spec/integration/stylesheet_spec.rb +28 -0
- data/spec/spec_helper.rb +13 -2
- data/spec/spritely/collection_spec.rb +91 -0
- data/spec/spritely/generators/chunky_png_spec.rb +0 -1
- data/spec/spritely/image_set_spec.rb +85 -0
- data/spec/spritely/image_spec.rb +9 -7
- data/spec/spritely/options_spec.rb +16 -0
- data/spec/spritely/sprite_map_spec.rb +24 -6
- data/spec/support/rails_app_helpers.rb +33 -0
- data/spec/support/shared_examples.rb +2 -13
- metadata +67 -6
- data/lib/spritely/images_set.rb +0 -41
- data/spec/spritely/images_set_spec.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9409fd4cb51f3dc5d07f61de99861f9b9dbb12f
|
4
|
+
data.tar.gz: 9390ecc20122b1106bed57f394e0f93fd6a60040
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e639ea7030383d04cd530f4cfcef3e454ff764ebf7f5b625916ba03a99273ae05100473f5ca6e70ef20b3d161fed72d5d4f0c233a70cf6b39dfd37e99c571007
|
7
|
+
data.tar.gz: 2634ff4658e0d9d62a4d307ce7a89ba4e0f1fd53cb0190db39c7e2a91adb4f63dc260e7f8f7523497292795fdf6d9a8589e5bcc0caa53c2723c1db7be846ef18
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spritely/image_set'
|
2
|
+
|
3
|
+
module Spritely
|
4
|
+
class Collection < Struct.new(:files, :options)
|
5
|
+
def self.create(*args)
|
6
|
+
new(*args).tap do |collection|
|
7
|
+
collection.position!
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def images
|
12
|
+
image_sets.flat_map(&:images)
|
13
|
+
end
|
14
|
+
|
15
|
+
def find(name)
|
16
|
+
image_sets.find { |image_set| image_set.name == name }
|
17
|
+
end
|
18
|
+
|
19
|
+
def last_modification_time
|
20
|
+
files.collect { |file| Spritely.modification_time(file) }.max
|
21
|
+
end
|
22
|
+
|
23
|
+
def width
|
24
|
+
return @width if @width
|
25
|
+
|
26
|
+
max_width = image_sets.collect(&:width).max
|
27
|
+
if image_sets.none?(&:repeated?)
|
28
|
+
@width = max_width
|
29
|
+
else
|
30
|
+
@width = lcm = image_sets.select(&:repeated?).collect(&:width).reduce(:lcm)
|
31
|
+
@width += lcm while @width < max_width
|
32
|
+
end
|
33
|
+
|
34
|
+
@width
|
35
|
+
end
|
36
|
+
|
37
|
+
def height
|
38
|
+
heights.reduce(:+)
|
39
|
+
end
|
40
|
+
|
41
|
+
def position!
|
42
|
+
image_sets.each_with_index do |image_set, index|
|
43
|
+
image_set.top = heights[0..index].reduce(:+) - image_set.height
|
44
|
+
image_set.position_in!(width)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def image_sets
|
51
|
+
@image_sets ||= files.collect { |file| ImageSet.new(file, options[File.basename(file, ".png")]) }.sort_by(&:width).reverse
|
52
|
+
end
|
53
|
+
|
54
|
+
def heights
|
55
|
+
image_sets.collect(&:height)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/spritely/image.rb
CHANGED
@@ -1,20 +1,5 @@
|
|
1
1
|
module Spritely
|
2
|
-
class Image
|
3
|
-
attr_accessor :top
|
4
|
-
attr_reader :path, :data, :width, :height
|
5
|
-
|
6
|
-
def initialize(path)
|
7
|
-
@path = path
|
8
|
-
@data = File.read(path)
|
9
|
-
@width, @height = data[0x10..0x18].unpack('NN')
|
10
|
-
end
|
11
|
-
|
12
|
-
def left
|
13
|
-
0
|
14
|
-
end
|
15
|
-
|
16
|
-
def name
|
17
|
-
File.basename(path, ".png")
|
18
|
-
end
|
2
|
+
class Image < Struct.new(:data)
|
3
|
+
attr_accessor :top, :left
|
19
4
|
end
|
20
5
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spritely/image'
|
2
|
+
|
3
|
+
module Spritely
|
4
|
+
class ImageSet
|
5
|
+
attr_accessor :top
|
6
|
+
attr_reader :path, :options, :data, :width, :height
|
7
|
+
|
8
|
+
def initialize(path, options)
|
9
|
+
@path = path
|
10
|
+
@options = options
|
11
|
+
@data = File.read(path)
|
12
|
+
@width, @height = data[0x10..0x18].unpack('NN')
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
File.basename(path, ".png")
|
17
|
+
end
|
18
|
+
|
19
|
+
def images
|
20
|
+
@images ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
def left
|
24
|
+
0
|
25
|
+
end
|
26
|
+
|
27
|
+
def repeated?
|
28
|
+
!!options[:repeat]
|
29
|
+
end
|
30
|
+
|
31
|
+
def position_in!(collection_width)
|
32
|
+
if repeated?
|
33
|
+
left = 0
|
34
|
+
while left < collection_width
|
35
|
+
add_image!(left)
|
36
|
+
left += width
|
37
|
+
end
|
38
|
+
else
|
39
|
+
add_image!(0)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def add_image!(left)
|
46
|
+
images << Image.new(data).tap do |image|
|
47
|
+
image.top = top
|
48
|
+
image.left = left
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'active_support/core_ext/hash/deep_merge'
|
2
|
+
|
3
|
+
module Spritely
|
4
|
+
class Options
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
def initialize(hash)
|
8
|
+
@options = hash.inject({}) do |h, (key, value)|
|
9
|
+
split_key = key.split('_')
|
10
|
+
option = {split_key.pop.to_sym => value}
|
11
|
+
h.deep_merge!(split_key.join('-') => option)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
options[key] || {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -2,11 +2,11 @@ require 'spritely/sprite_map'
|
|
2
2
|
|
3
3
|
module Spritely
|
4
4
|
module SassFunctions
|
5
|
-
def sprite_map(glob)
|
6
|
-
SpriteMap.create(glob)
|
5
|
+
def sprite_map(glob, kwargs = {})
|
6
|
+
SpriteMap.create(glob, kwargs)
|
7
7
|
end
|
8
8
|
|
9
|
-
::Sass::Script::Functions.declare :sprite_map, [:glob]
|
9
|
+
::Sass::Script::Functions.declare :sprite_map, [:glob], var_kwargs: true
|
10
10
|
|
11
11
|
def sprite_url(sprite_map)
|
12
12
|
asset_url(Sass::Script::String.new("sprites/#{sprite_map.name}.png"))
|
@@ -15,7 +15,7 @@ module Spritely
|
|
15
15
|
::Sass::Script::Functions.declare :sprite_url, [:sprite_map]
|
16
16
|
|
17
17
|
def sprite_position(sprite_map, image_name)
|
18
|
-
image = sprite_map.
|
18
|
+
image = sprite_map.find(image_name.value)
|
19
19
|
|
20
20
|
x = Sass::Script::Number.new(image.left, image.left == 0 ? [] : ['px'])
|
21
21
|
y = Sass::Script::Number.new(-image.top, image.top == 0 ? [] : ['px'])
|
@@ -26,7 +26,7 @@ module Spritely
|
|
26
26
|
::Sass::Script::Functions.declare :sprite_position, [:sprite_map, :image_name]
|
27
27
|
|
28
28
|
def sprite_width(sprite_map, image_name)
|
29
|
-
image = sprite_map.
|
29
|
+
image = sprite_map.find(image_name.value)
|
30
30
|
|
31
31
|
Sass::Script::Number.new(image.width, ['px'])
|
32
32
|
end
|
@@ -34,7 +34,7 @@ module Spritely
|
|
34
34
|
::Sass::Script::Functions.declare :sprite_width, [:sprite_map, :image_name]
|
35
35
|
|
36
36
|
def sprite_height(sprite_map, image_name)
|
37
|
-
image = sprite_map.
|
37
|
+
image = sprite_map.find(image_name.value)
|
38
38
|
|
39
39
|
Sass::Script::Number.new(image.height, ['px'])
|
40
40
|
end
|
@@ -42,3 +42,5 @@ module Spritely
|
|
42
42
|
::Sass::Script::Functions.declare :sprite_height, [:sprite_map, :image_name]
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
::Sass::Script::Functions.send(:include, Spritely::SassFunctions)
|
data/lib/spritely/sprite_map.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
-
require 'spritely/
|
1
|
+
require 'spritely/options'
|
2
|
+
require 'spritely/collection'
|
2
3
|
require 'spritely/generators/chunky_png'
|
3
4
|
|
4
5
|
module Spritely
|
5
|
-
class SpriteMap
|
6
|
-
|
7
|
-
|
6
|
+
class SpriteMap < Sass::Script::Literal
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :collection, :find, :width, :height, :images
|
10
|
+
|
11
|
+
attr_reader :glob, :options
|
8
12
|
|
9
13
|
def self.create(*args)
|
10
14
|
new(*args).tap do |sprite_map|
|
@@ -12,12 +16,17 @@ module Spritely
|
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
15
|
-
def initialize(glob)
|
19
|
+
def initialize(glob, options = {})
|
16
20
|
@glob = glob.value
|
21
|
+
@options = Options.new(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
"#<Spritely::SpriteMap name=#{name} filename=#{filename}>"
|
17
26
|
end
|
18
27
|
|
19
|
-
def
|
20
|
-
@
|
28
|
+
def collection
|
29
|
+
@collection ||= Collection.create(files, options)
|
21
30
|
end
|
22
31
|
|
23
32
|
def generate!
|
@@ -43,11 +52,7 @@ module Spritely
|
|
43
52
|
end
|
44
53
|
|
45
54
|
def outdated?
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
def modification_time
|
50
|
-
Spritely.modification_time(filename)
|
55
|
+
collection.last_modification_time > Spritely.modification_time(filename)
|
51
56
|
end
|
52
57
|
end
|
53
58
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'sprockets/manifest'
|
2
|
+
require 'active_support/core_ext/module/aliasing'
|
3
|
+
|
4
|
+
module Sprockets
|
5
|
+
class Manifest
|
6
|
+
def compile_with_sprites(*args)
|
7
|
+
compile_without_sprites(*args)
|
8
|
+
Dir.glob(Spritely.directory.join('*.png')).each(&method(:compile_without_sprites))
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method_chain :compile, :sprites
|
12
|
+
end
|
13
|
+
end
|
data/lib/spritely/version.rb
CHANGED
data/lib/spritely.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'sass'
|
2
2
|
require 'spritely/sass_functions'
|
3
|
+
require 'spritely/sprockets/manifest'
|
3
4
|
|
4
5
|
raise LoadError, "Spritely cannot be used in conjunction with Compass. Hope you choose Spritely!" if defined?(::Compass)
|
5
6
|
|
@@ -16,5 +17,3 @@ module Spritely
|
|
16
17
|
File.mtime(filename).to_i
|
17
18
|
end
|
18
19
|
end
|
19
|
-
|
20
|
-
::Sass::Script::Functions.send(:include, Spritely::SassFunctions)
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,13 @@
|
|
1
|
+
$application-sprite: sprite-map("application/*.png", $background-repeat: true);
|
2
|
+
|
3
|
+
body {
|
4
|
+
background-image: sprite-url($application-sprite);
|
5
|
+
background-position: sprite-position($application-sprite, "background");
|
6
|
+
}
|
7
|
+
|
8
|
+
#mario {
|
9
|
+
background-image: sprite-url($application-sprite);
|
10
|
+
background-position: sprite-position($application-sprite, "mario");
|
11
|
+
width: sprite-width($application-sprite, "mario");
|
12
|
+
height: sprite-height($application-sprite, "mario");
|
13
|
+
}
|
Binary file
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_support/core_ext/string'
|
3
|
+
|
4
|
+
describe 'Precompilation', :integration do
|
5
|
+
it 'should create the sprite in the right location' do
|
6
|
+
render_asset('sprites.css')
|
7
|
+
expect(File.exist?(File.join('app', 'assets', 'images', 'sprites', 'application.png'))).to be_true
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should compile the correct-looking sprite file' do
|
11
|
+
render_asset('sprites.css')
|
12
|
+
compiled_sprite = ChunkyPNG::Image.from_file(File.join('app', 'assets', 'images', 'sprites', 'application.png'))
|
13
|
+
correct_sprite = ChunkyPNG::Image.from_file(File.join(__dir__, '..', 'fixtures', 'correct-sprite.png'))
|
14
|
+
expect(compiled_sprite).to eq(correct_sprite)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should compile all of the assets necessary' do
|
18
|
+
compile_assets
|
19
|
+
sprite_files = Dir.glob(File.join('public', 'assets', 'sprites', 'application-*.png'))
|
20
|
+
expect(sprite_files).to have(1).item
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should compile all of the assets necessary when sprites have been pre-generated' do
|
24
|
+
render_asset('sprites.css')
|
25
|
+
compile_assets
|
26
|
+
sprite_files = Dir.glob(File.join('public', 'assets', 'sprites', 'application-*.png'))
|
27
|
+
expect(sprite_files).to have(1).item
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_support/core_ext/string/strip'
|
3
|
+
|
4
|
+
describe 'Stylesheet generation', :integration do
|
5
|
+
subject(:stylesheet) { render_asset('sprites.css') }
|
6
|
+
|
7
|
+
describe 'body CSS with repetition' do
|
8
|
+
it { should include(<<-CSS.strip_heredoc
|
9
|
+
body {
|
10
|
+
background-image: url(/assets/sprites/application.png);
|
11
|
+
background-position: 0 -837px;
|
12
|
+
}
|
13
|
+
CSS
|
14
|
+
) }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#mario CSS with no repetition' do
|
18
|
+
it { should include(<<-CSS.strip_heredoc
|
19
|
+
#mario {
|
20
|
+
background-image: url(/assets/sprites/application.png);
|
21
|
+
background-position: 0 -623px;
|
22
|
+
width: 200px;
|
23
|
+
height: 214px;
|
24
|
+
}
|
25
|
+
CSS
|
26
|
+
) }
|
27
|
+
end
|
28
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
require 'rspec'
|
2
|
-
require 'pry-byebug'
|
3
2
|
require 'spritely'
|
4
3
|
|
5
|
-
|
4
|
+
require 'pry-byebug'
|
5
|
+
|
6
|
+
Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.expect_with :rspec do |c|
|
10
|
+
c.syntax = :expect
|
11
|
+
end
|
12
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
+
|
14
|
+
config.include RailsAppHelpers, :integration
|
15
|
+
config.around(:each, :integration) { |example| within_rails_app(&example) }
|
16
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spritely::Collection do
|
4
|
+
let(:first_set) { double(repeated?: true, name: 'foo', width: 1, height: 10, images: [1]) }
|
5
|
+
let(:second_set) { double(repeated?: false, name: 'bar', width: 100, height: 100, images: [2, 3]) }
|
6
|
+
|
7
|
+
subject { Spritely::Collection.new(['file-1.png', 'file-2.png'], {'file-1' => {repeat: true}}) }
|
8
|
+
|
9
|
+
before do
|
10
|
+
allow(Spritely::ImageSet).to receive(:new).with('file-1.png', repeat: true).and_return(first_set)
|
11
|
+
allow(Spritely::ImageSet).to receive(:new).with('file-2.png', nil).and_return(second_set)
|
12
|
+
end
|
13
|
+
|
14
|
+
its(:files) { should eq(['file-1.png', 'file-2.png']) }
|
15
|
+
its(:options) { should eq({'file-1' => {repeat: true}}) }
|
16
|
+
its(:images) { should eq([2, 3, 1]) }
|
17
|
+
its(:height) { should eq(110) }
|
18
|
+
|
19
|
+
describe '.create' do
|
20
|
+
let(:collection) { double }
|
21
|
+
|
22
|
+
it 'should attempt to generate the sprite' do
|
23
|
+
allow(Spritely::Collection).to receive(:new).with('something').and_return(collection)
|
24
|
+
expect(collection).to receive(:position!)
|
25
|
+
Spritely::Collection.create('something')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#width' do
|
30
|
+
let(:third_set) { double(repeated?: false, width: 65, height: 100) }
|
31
|
+
|
32
|
+
subject { Spritely::Collection.new(['file-1.png', 'file-2.png', 'file-3.png'], {}) }
|
33
|
+
|
34
|
+
before do
|
35
|
+
allow(Spritely::ImageSet).to receive(:new).with('file-1.png', nil).and_return(first_set)
|
36
|
+
allow(Spritely::ImageSet).to receive(:new).with('file-2.png', nil).and_return(second_set)
|
37
|
+
allow(Spritely::ImageSet).to receive(:new).with('file-3.png', nil).and_return(third_set)
|
38
|
+
end
|
39
|
+
|
40
|
+
its(:width) { should eq(100) }
|
41
|
+
|
42
|
+
context 'when the repetition will overflow the largest non-repeating image' do
|
43
|
+
let(:first_set) { double(repeated?: true, width: 12, height: 10) }
|
44
|
+
let(:second_set) { double(repeated?: false, width: 100, height: 100) }
|
45
|
+
|
46
|
+
its(:width) { should eq(108) }
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when there are multiple repeating images, find the lcm' do
|
50
|
+
let(:first_set) { double(repeated?: true, width: 12, height: 10) }
|
51
|
+
let(:second_set) { double(repeated?: true, width: 100, height: 100) }
|
52
|
+
let(:third_set) { double(repeated?: true, width: 65, height: 100) }
|
53
|
+
|
54
|
+
its(:width) { should eq(3900) }
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when there is a mix of repeating and non-repeating' do
|
58
|
+
let(:first_set) { double(repeated?: true, width: 12, height: 10) }
|
59
|
+
let(:second_set) { double(repeated?: false, width: 100, height: 100) }
|
60
|
+
let(:third_set) { double(repeated?: true, width: 5, height: 100) }
|
61
|
+
|
62
|
+
its(:width) { should eq(120) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#find' do
|
67
|
+
it 'should find the correct set by name' do
|
68
|
+
expect(subject.find('foo')).to eq(first_set)
|
69
|
+
expect(subject.find('bar')).to eq(second_set)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#last_modification_time' do
|
74
|
+
before do
|
75
|
+
allow(Spritely).to receive(:modification_time).with('file-1.png').and_return(10)
|
76
|
+
allow(Spritely).to receive(:modification_time).with('file-2.png').and_return(100)
|
77
|
+
end
|
78
|
+
|
79
|
+
its(:last_modification_time) { should eq(100) }
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#position!' do
|
83
|
+
it 'should call out to each image set in turn' do
|
84
|
+
expect(second_set).to receive(:top=).with(0)
|
85
|
+
expect(second_set).to receive(:position_in!).with(100)
|
86
|
+
expect(first_set).to receive(:top=).with(100)
|
87
|
+
expect(first_set).to receive(:position_in!).with(100)
|
88
|
+
subject.position!
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe Spritely::ImageSet do
|
5
|
+
let(:path) { "#{__dir__}/../fixtures/test/foo.png" }
|
6
|
+
let(:options) { {repeat: true} }
|
7
|
+
|
8
|
+
subject { Spritely::ImageSet.new(path, options) }
|
9
|
+
|
10
|
+
its(:path) { should eq(path) }
|
11
|
+
its(:options) { should eq(options) }
|
12
|
+
its(:data) { should eq(File.read(path)) }
|
13
|
+
its(:width) { should eq(1) }
|
14
|
+
its(:height) { should eq(1) }
|
15
|
+
its(:name) { should eq('foo') }
|
16
|
+
its(:left) { should eq(0) }
|
17
|
+
|
18
|
+
describe '#top' do
|
19
|
+
before { subject.top = 123 }
|
20
|
+
|
21
|
+
its(:top) { should eq(123) }
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#repeated?' do
|
25
|
+
it { should be_repeated }
|
26
|
+
|
27
|
+
context 'repeat option is passed as truthy' do
|
28
|
+
let(:options) { {repeat: 'repeat'} }
|
29
|
+
|
30
|
+
it { should be_repeated }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'repeat option is passed as false' do
|
34
|
+
let(:options) { {repeat: false} }
|
35
|
+
|
36
|
+
it { should_not be_repeated }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'repeat option is not passed' do
|
40
|
+
let(:options) { {} }
|
41
|
+
|
42
|
+
it { should_not be_repeated }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#position_in!' do
|
47
|
+
class ImageDouble
|
48
|
+
attr_accessor :top, :left
|
49
|
+
end
|
50
|
+
|
51
|
+
before { subject.top = 123 }
|
52
|
+
|
53
|
+
context 'the image is repeated' do
|
54
|
+
let(:first_image) { ImageDouble.new }
|
55
|
+
let(:second_image) { ImageDouble.new }
|
56
|
+
|
57
|
+
before do
|
58
|
+
allow(Spritely::Image).to receive(:new).with(File.read(path)).and_return(first_image, second_image)
|
59
|
+
subject.position_in!(2)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should set the position of the images' do
|
63
|
+
expect(first_image.top).to eq(123)
|
64
|
+
expect(first_image.left).to eq(0)
|
65
|
+
expect(second_image.top).to eq(123)
|
66
|
+
expect(second_image.left).to eq(1)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'the image is not repeated' do
|
71
|
+
let(:options) { {repeat: false} }
|
72
|
+
let(:image) { ImageDouble.new }
|
73
|
+
|
74
|
+
before do
|
75
|
+
allow(Spritely::Image).to receive(:new).with(File.read(path)).and_return(image)
|
76
|
+
subject.position_in!(1)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should set the position of the image' do
|
80
|
+
expect(image.top).to eq(123)
|
81
|
+
expect(image.left).to eq(0)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/spec/spritely/image_spec.rb
CHANGED
@@ -2,19 +2,21 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Spritely::Image do
|
4
4
|
let(:path) { "#{__dir__}/../fixtures/test/foo.png" }
|
5
|
+
let(:data) { File.read(path) }
|
5
6
|
|
6
|
-
subject { Spritely::Image.new(
|
7
|
+
subject { Spritely::Image.new(data) }
|
7
8
|
|
8
|
-
its(:
|
9
|
-
its(:data) { should eq(File.read(path)) }
|
10
|
-
its(:width) { should eq(1) }
|
11
|
-
its(:height) { should eq(1) }
|
12
|
-
its(:left) { should eq(0) }
|
13
|
-
its(:name) { should eq('foo') }
|
9
|
+
its(:data) { should eq(data) }
|
14
10
|
|
15
11
|
describe '#top' do
|
16
12
|
before { subject.top = 123 }
|
17
13
|
|
18
14
|
its(:top) { should eq(123) }
|
19
15
|
end
|
16
|
+
|
17
|
+
describe '#left' do
|
18
|
+
before { subject.left = 456 }
|
19
|
+
|
20
|
+
its(:left) { should eq(456) }
|
21
|
+
end
|
20
22
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spritely::Options do
|
4
|
+
let(:hash) { {'some_new_image_x' => 123, 'some_new_image_y' => 456, 'another_image_repeat' => true} }
|
5
|
+
|
6
|
+
subject(:options) { Spritely::Options.new(hash) }
|
7
|
+
|
8
|
+
its(['some-new-image']) { should eq({x: 123, y: 456}) }
|
9
|
+
its(['another-image']) { should eq({repeat: true}) }
|
10
|
+
|
11
|
+
describe '#[]' do
|
12
|
+
it 'should fall back to an empty hash' do
|
13
|
+
expect(options[:unknown]).to eq({})
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,13 +1,22 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Spritely::SpriteMap do
|
4
|
-
|
4
|
+
let(:options) { {'some_new_image_x' => 123, 'some_new_image_y' => 456, 'another_image_repeat' => true} }
|
5
5
|
|
6
|
-
|
6
|
+
subject { Spritely::SpriteMap.new(double(value: 'test/*.png'), options) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
Spritely.stub(:directory).and_return(File)
|
10
|
+
allow(Spritely::Options).to receive(:new).with(options).and_return('options')
|
11
|
+
end
|
12
|
+
|
13
|
+
it { should be_a(Sass::Script::Literal) }
|
7
14
|
|
8
15
|
its(:glob) { should eq('test/*.png') }
|
16
|
+
its(:options) { should eq('options') }
|
9
17
|
its(:name) { should eq('test') }
|
10
18
|
its(:filename) { should eq('test.png') }
|
19
|
+
its(:inspect) { should eq('#<Spritely::SpriteMap name=test filename=test.png>') }
|
11
20
|
|
12
21
|
describe '.create' do
|
13
22
|
let(:sprite_map) { double(needs_generation?: true) }
|
@@ -19,13 +28,22 @@ describe Spritely::SpriteMap do
|
|
19
28
|
end
|
20
29
|
end
|
21
30
|
|
22
|
-
describe '#
|
31
|
+
describe '#collection' do
|
32
|
+
let(:collection) { double(find: 'find value', width: 'width value', height: 'height value', images: 'images value') }
|
33
|
+
|
23
34
|
before do
|
24
35
|
Spritely.stub_chain(:environment, :paths).and_return(["#{__dir__}/../fixtures"])
|
25
|
-
allow(Spritely::
|
36
|
+
allow(Spritely::Collection).to receive(:create).with(["#{__dir__}/../fixtures/test/foo.png"], 'options').and_return(collection)
|
26
37
|
end
|
27
38
|
|
28
|
-
its(:
|
39
|
+
its(:collection) { should eq(collection) }
|
40
|
+
|
41
|
+
describe 'delegated methods' do
|
42
|
+
its(:find) { should eq('find value') }
|
43
|
+
its(:width) { should eq('width value') }
|
44
|
+
its(:height) { should eq('height value') }
|
45
|
+
its(:images) { should eq('images value') }
|
46
|
+
end
|
29
47
|
end
|
30
48
|
|
31
49
|
describe '#generate!' do
|
@@ -46,7 +64,7 @@ describe Spritely::SpriteMap do
|
|
46
64
|
let(:file_exists) { true }
|
47
65
|
|
48
66
|
before do
|
49
|
-
subject.stub_chain(:
|
67
|
+
subject.stub_chain(:collection, :last_modification_time).and_return(456)
|
50
68
|
allow(Spritely).to receive(:modification_time).and_return(123)
|
51
69
|
end
|
52
70
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RailsAppHelpers
|
2
|
+
def within_rails_app(&block)
|
3
|
+
Dir.mktmpdir do |tmpdir|
|
4
|
+
Dir.chdir(tmpdir) do
|
5
|
+
%x(rails new dummy --skip-active-record --skip-test-unit --skip-javascript --skip-spring --skip-git)
|
6
|
+
Dir.chdir('dummy') do
|
7
|
+
Bundler.with_clean_env do
|
8
|
+
File.open('Gemfile', 'a') do |f|
|
9
|
+
f.write("gem 'spritely', path: '#{__dir__}/../../'\n")
|
10
|
+
end
|
11
|
+
%x(bundle install)
|
12
|
+
FileUtils.cp_r "#{__dir__}/../fixtures/rails-app/.", "."
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def render_asset(filename)
|
21
|
+
runner "puts Rails.application.assets[#{filename.inspect}]"
|
22
|
+
end
|
23
|
+
|
24
|
+
def compile_assets
|
25
|
+
%x(RAILS_ENV=production rake assets:precompile)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def runner(command)
|
31
|
+
%x(rails runner -e development '#{command}')
|
32
|
+
end
|
33
|
+
end
|
@@ -1,17 +1,6 @@
|
|
1
1
|
shared_examples "a generator" do
|
2
|
-
|
3
|
-
|
4
|
-
def total_height; 200; end
|
5
|
-
|
6
|
-
def each(&block)
|
7
|
-
[
|
8
|
-
OpenStruct.new(data: 'first image data', left: 1, top: 10),
|
9
|
-
OpenStruct.new(data: 'second image data', left: 2, top: 20)
|
10
|
-
].each(&block)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
let(:sprite_map) { double(images: ImagesDouble.new, filename: 'blah.png') }
|
2
|
+
let(:images) { [OpenStruct.new(data: 'first image data', left: 1, top: 10), OpenStruct.new(data: 'second image data', left: 2, top: 20)] }
|
3
|
+
let(:sprite_map) { double(images: images, width: 100, height: 200, filename: 'blah.png') }
|
15
4
|
|
16
5
|
its(:sprite_map) { should eq(sprite_map) }
|
17
6
|
|
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.0.
|
4
|
+
version: 0.0.2
|
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-05-
|
11
|
+
date: 2014-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chunky_png
|
@@ -24,6 +24,26 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: railties
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 4.0.0
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '5.0'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 4.0.0
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '5.0'
|
27
47
|
- !ruby/object:Gem::Dependency
|
28
48
|
name: sass
|
29
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +86,20 @@ dependencies:
|
|
66
86
|
- - ">="
|
67
87
|
- !ruby/object:Gem::Version
|
68
88
|
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rake
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
69
103
|
- !ruby/object:Gem::Dependency
|
70
104
|
name: rspec
|
71
105
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,7 +115,7 @@ dependencies:
|
|
81
115
|
- !ruby/object:Gem::Version
|
82
116
|
version: '0'
|
83
117
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
118
|
+
name: appraisal
|
85
119
|
requirement: !ruby/object:Gem::Requirement
|
86
120
|
requirements:
|
87
121
|
- - ">="
|
@@ -102,20 +136,35 @@ extensions: []
|
|
102
136
|
extra_rdoc_files: []
|
103
137
|
files:
|
104
138
|
- lib/spritely.rb
|
139
|
+
- lib/spritely/collection.rb
|
105
140
|
- lib/spritely/generators/base.rb
|
106
141
|
- lib/spritely/generators/chunky_png.rb
|
107
142
|
- lib/spritely/image.rb
|
108
|
-
- lib/spritely/
|
143
|
+
- lib/spritely/image_set.rb
|
144
|
+
- lib/spritely/options.rb
|
109
145
|
- lib/spritely/sass_functions.rb
|
110
146
|
- lib/spritely/sprite_map.rb
|
147
|
+
- lib/spritely/sprockets/manifest.rb
|
111
148
|
- lib/spritely/version.rb
|
149
|
+
- spec/fixtures/correct-sprite.png
|
150
|
+
- spec/fixtures/rails-app/app/assets/images/application/background.png
|
151
|
+
- spec/fixtures/rails-app/app/assets/images/application/fool.png
|
152
|
+
- spec/fixtures/rails-app/app/assets/images/application/football.png
|
153
|
+
- spec/fixtures/rails-app/app/assets/images/application/mario.png
|
154
|
+
- spec/fixtures/rails-app/app/assets/stylesheets/sprites.css.scss
|
112
155
|
- spec/fixtures/test/foo.png
|
156
|
+
- spec/integration/application.png
|
157
|
+
- spec/integration/precompilation_spec.rb
|
158
|
+
- spec/integration/stylesheet_spec.rb
|
113
159
|
- spec/spec_helper.rb
|
160
|
+
- spec/spritely/collection_spec.rb
|
114
161
|
- spec/spritely/generators/chunky_png_spec.rb
|
162
|
+
- spec/spritely/image_set_spec.rb
|
115
163
|
- spec/spritely/image_spec.rb
|
116
|
-
- spec/spritely/
|
164
|
+
- spec/spritely/options_spec.rb
|
117
165
|
- spec/spritely/sprite_map_spec.rb
|
118
166
|
- spec/spritely_spec.rb
|
167
|
+
- spec/support/rails_app_helpers.rb
|
119
168
|
- spec/support/shared_examples.rb
|
120
169
|
homepage: http://www.robbinsweb.biz
|
121
170
|
licenses:
|
@@ -143,11 +192,23 @@ specification_version: 4
|
|
143
192
|
summary: Hooks into the Rails asset pipeline to allow you to easily generate sprite
|
144
193
|
maps
|
145
194
|
test_files:
|
195
|
+
- spec/fixtures/correct-sprite.png
|
196
|
+
- spec/fixtures/rails-app/app/assets/images/application/background.png
|
197
|
+
- spec/fixtures/rails-app/app/assets/images/application/fool.png
|
198
|
+
- spec/fixtures/rails-app/app/assets/images/application/football.png
|
199
|
+
- spec/fixtures/rails-app/app/assets/images/application/mario.png
|
200
|
+
- spec/fixtures/rails-app/app/assets/stylesheets/sprites.css.scss
|
146
201
|
- spec/fixtures/test/foo.png
|
202
|
+
- spec/integration/application.png
|
203
|
+
- spec/integration/precompilation_spec.rb
|
204
|
+
- spec/integration/stylesheet_spec.rb
|
147
205
|
- spec/spec_helper.rb
|
206
|
+
- spec/spritely/collection_spec.rb
|
148
207
|
- spec/spritely/generators/chunky_png_spec.rb
|
208
|
+
- spec/spritely/image_set_spec.rb
|
149
209
|
- spec/spritely/image_spec.rb
|
150
|
-
- spec/spritely/
|
210
|
+
- spec/spritely/options_spec.rb
|
151
211
|
- spec/spritely/sprite_map_spec.rb
|
152
212
|
- spec/spritely_spec.rb
|
213
|
+
- spec/support/rails_app_helpers.rb
|
153
214
|
- spec/support/shared_examples.rb
|
data/lib/spritely/images_set.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'spritely/image'
|
2
|
-
|
3
|
-
module Spritely
|
4
|
-
class ImagesSet
|
5
|
-
extend Forwardable
|
6
|
-
|
7
|
-
def_delegator :@images, :each
|
8
|
-
|
9
|
-
attr_reader :files, :images
|
10
|
-
|
11
|
-
def initialize(files)
|
12
|
-
@files = files
|
13
|
-
@images = files.collect { |file| Image.new(file) }.sort_by(&:width).reverse
|
14
|
-
position!
|
15
|
-
end
|
16
|
-
|
17
|
-
def find(name)
|
18
|
-
images.find { |image| image.name == name }
|
19
|
-
end
|
20
|
-
|
21
|
-
def max_width
|
22
|
-
@max_width ||= images.collect(&:width).max
|
23
|
-
end
|
24
|
-
|
25
|
-
def total_height
|
26
|
-
@total_height ||= images.collect(&:height).reduce(:+)
|
27
|
-
end
|
28
|
-
|
29
|
-
def last_modification_time
|
30
|
-
files.collect { |file| Spritely.modification_time(file) }.max
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def position!
|
36
|
-
images.each_with_index do |image, index|
|
37
|
-
image.top = images.collect(&:height)[0..index].reduce(:+) - image.height
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'ostruct'
|
3
|
-
|
4
|
-
describe Spritely::ImagesSet do
|
5
|
-
class ImageDouble < OpenStruct
|
6
|
-
attr_accessor :top
|
7
|
-
end
|
8
|
-
|
9
|
-
let(:first_image) { ImageDouble.new(name: 'foo', width: 1, height: 10) }
|
10
|
-
let(:second_image) { ImageDouble.new(name: 'bar', width: 100, height: 100) }
|
11
|
-
|
12
|
-
before do
|
13
|
-
allow(Spritely::Image).to receive(:new).with('first').and_return(first_image)
|
14
|
-
allow(Spritely::Image).to receive(:new).with('second').and_return(second_image)
|
15
|
-
end
|
16
|
-
|
17
|
-
subject! { Spritely::ImagesSet.new(['first', 'second']) }
|
18
|
-
|
19
|
-
its(:files) { should eq(['first', 'second']) }
|
20
|
-
its(:images) { should eq([second_image, first_image]) }
|
21
|
-
its(:max_width) { should eq(100) }
|
22
|
-
its(:total_height) { should eq(110) }
|
23
|
-
|
24
|
-
describe 'positioning' do
|
25
|
-
it 'should set the #top position of each image' do
|
26
|
-
expect(first_image.top).to eq(100)
|
27
|
-
expect(second_image.top).to eq(0)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
describe '#each delegation' do
|
32
|
-
it 'should pass along the #each method call to the internal #images array' do
|
33
|
-
expect(first_image).to receive(:blah!)
|
34
|
-
expect(second_image).to receive(:blah!)
|
35
|
-
subject.each(&:blah!)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
describe '#find' do
|
40
|
-
it 'should find the correct image by name' do
|
41
|
-
expect(subject.find('foo')).to eq(first_image)
|
42
|
-
expect(subject.find('bar')).to eq(second_image)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
describe '#last_modification_time' do
|
47
|
-
before do
|
48
|
-
allow(Spritely).to receive(:modification_time).with('first').and_return(10)
|
49
|
-
allow(Spritely).to receive(:modification_time).with('second').and_return(100)
|
50
|
-
end
|
51
|
-
|
52
|
-
its(:last_modification_time) { should eq(100) }
|
53
|
-
end
|
54
|
-
end
|