spritely 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/spritely/collection.rb +58 -0
  3. data/lib/spritely/generators/chunky_png.rb +1 -1
  4. data/lib/spritely/image.rb +2 -17
  5. data/lib/spritely/image_set.rb +52 -0
  6. data/lib/spritely/options.rb +19 -0
  7. data/lib/spritely/sass_functions.rb +8 -6
  8. data/lib/spritely/sprite_map.rb +17 -12
  9. data/lib/spritely/sprockets/manifest.rb +13 -0
  10. data/lib/spritely/version.rb +1 -1
  11. data/lib/spritely.rb +1 -2
  12. data/spec/fixtures/correct-sprite.png +0 -0
  13. data/spec/fixtures/rails-app/app/assets/images/application/background.png +0 -0
  14. data/spec/fixtures/rails-app/app/assets/images/application/fool.png +0 -0
  15. data/spec/fixtures/rails-app/app/assets/images/application/football.png +0 -0
  16. data/spec/fixtures/rails-app/app/assets/images/application/mario.png +0 -0
  17. data/spec/fixtures/rails-app/app/assets/stylesheets/sprites.css.scss +13 -0
  18. data/spec/integration/application.png +0 -0
  19. data/spec/integration/precompilation_spec.rb +29 -0
  20. data/spec/integration/stylesheet_spec.rb +28 -0
  21. data/spec/spec_helper.rb +13 -2
  22. data/spec/spritely/collection_spec.rb +91 -0
  23. data/spec/spritely/generators/chunky_png_spec.rb +0 -1
  24. data/spec/spritely/image_set_spec.rb +85 -0
  25. data/spec/spritely/image_spec.rb +9 -7
  26. data/spec/spritely/options_spec.rb +16 -0
  27. data/spec/spritely/sprite_map_spec.rb +24 -6
  28. data/spec/support/rails_app_helpers.rb +33 -0
  29. data/spec/support/shared_examples.rb +2 -13
  30. metadata +67 -6
  31. data/lib/spritely/images_set.rb +0 -41
  32. data/spec/spritely/images_set_spec.rb +0 -54
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d722c5631f45aecb25c733b67c27477cabb3087b
4
- data.tar.gz: cacdcf82d6ec8c69a7916443c076ec2cb3f6471d
3
+ metadata.gz: f9409fd4cb51f3dc5d07f61de99861f9b9dbb12f
4
+ data.tar.gz: 9390ecc20122b1106bed57f394e0f93fd6a60040
5
5
  SHA512:
6
- metadata.gz: 47639393b85c3b0a5bf61103de2d2ccbfd68fc9facae1a21e97b1c492f71edb6e3374e2379cf5af5f48e03605475da54f2caea7500ff001c5039d1bdbf413bdd
7
- data.tar.gz: d2e6d843569997d59e82ee7228dea3cd494b2f7e0ad2dba5d2313c6ff34026dd30e3fe97154224a95d9c6f4cd0b10306e1e3482b9ea44b5403d2535038cf20f0
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
@@ -18,7 +18,7 @@ module Spritely
18
18
  private
19
19
 
20
20
  def canvas
21
- @canvas ||= ::ChunkyPNG::Image.new(sprite_map.images.max_width, sprite_map.images.total_height)
21
+ @canvas ||= ::ChunkyPNG::Image.new(sprite_map.width, sprite_map.height)
22
22
  end
23
23
  end
24
24
  end
@@ -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.images.find(image_name.value)
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.images.find(image_name.value)
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.images.find(image_name.value)
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)
@@ -1,10 +1,14 @@
1
- require 'spritely/images_set'
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
- attr_accessor :options # Sass expects an object returned from a script function to have an #options accessor
7
- attr_reader :glob
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 images
20
- @images ||= ImagesSet.new(files)
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
- images.last_modification_time > modification_time
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
@@ -1,3 +1,3 @@
1
1
  module Spritely
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
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
@@ -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
- Dir["./spec/support/**/*.rb"].sort.each {|f| require f}
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
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'ostruct'
3
2
 
4
3
  describe Spritely::Generators::ChunkyPng do
5
4
  let(:png_canvas) { double }
@@ -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
@@ -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(path) }
7
+ subject { Spritely::Image.new(data) }
7
8
 
8
- its(:path) { should eq(path) }
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
- subject { Spritely::SpriteMap.new(double(value: 'test/*.png')) }
4
+ let(:options) { {'some_new_image_x' => 123, 'some_new_image_y' => 456, 'another_image_repeat' => true} }
5
5
 
6
- before { Spritely.stub(:directory).and_return(File) }
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 '#images' do
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::ImagesSet).to receive(:new).with(["#{__dir__}/../fixtures/test/foo.png"]).and_return('images set')
36
+ allow(Spritely::Collection).to receive(:create).with(["#{__dir__}/../fixtures/test/foo.png"], 'options').and_return(collection)
26
37
  end
27
38
 
28
- its(:images) { should eq('images set') }
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(:images, :last_modification_time).and_return(456)
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
- class ImagesDouble
3
- def max_width; 100; end
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.1
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-12 00:00:00.000000000 Z
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: rake
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/images_set.rb
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/images_set_spec.rb
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/images_set_spec.rb
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
@@ -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