montage 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/History.md +23 -0
  2. data/Rakefile +2 -4
  3. data/VERSION +1 -1
  4. data/bin/montage +11 -4
  5. data/lib/montage/commands/generate.rb +72 -11
  6. data/lib/montage/commands/init.rb +5 -11
  7. data/lib/montage/commands.rb +51 -18
  8. data/lib/montage/project.rb +54 -39
  9. data/lib/montage/source.rb +15 -44
  10. data/lib/montage/sprite.rb +25 -37
  11. data/lib/montage/sprite_definition.rb +127 -0
  12. data/lib/montage/templates/montage.yml +29 -19
  13. data/lib/montage/templates/sass_mixins.erb +1 -1
  14. data/lib/montage/templates/sources/{book.png → one/book.png} +0 -0
  15. data/lib/montage/templates/sources/{box-label.png → one/box-label.png} +0 -0
  16. data/lib/montage/templates/sources/{calculator.png → one/calculator.png} +0 -0
  17. data/lib/montage/templates/sources/{calendar-month.png → one/calendar-month.png} +0 -0
  18. data/lib/montage/templates/sources/{camera.png → one/camera.png} +0 -0
  19. data/lib/montage/templates/sources/{eraser.png → one/eraser.png} +0 -0
  20. data/lib/montage/templates/sources/two/inbox-image.png +0 -0
  21. data/lib/montage/templates/sources/two/magnet.png +0 -0
  22. data/lib/montage/templates/sources/two/newspaper.png +0 -0
  23. data/lib/montage/templates/sources/two/television.png +0 -0
  24. data/lib/montage/templates/sources/two/wand-hat.png +0 -0
  25. data/lib/montage/templates/sources/two/wooden-box-label.png +0 -0
  26. data/lib/montage.rb +10 -1
  27. data/montage.gemspec +17 -34
  28. data/spec/lib/project_helper.rb +41 -6
  29. data/spec/lib/shared_project_specs.rb +10 -11
  30. data/spec/montage/commands/generate_spec.rb +86 -132
  31. data/spec/montage/commands/init_spec.rb +18 -43
  32. data/spec/montage/project_spec.rb +77 -63
  33. data/spec/montage/sass_builder_spec.rb +33 -57
  34. data/spec/montage/source_spec.rb +10 -31
  35. data/spec/montage/sprite_definition_spec.rb +361 -0
  36. data/spec/montage/sprite_spec.rb +58 -57
  37. metadata +31 -70
  38. data/spec/fixtures/custom_dirs/montage.yml +0 -8
  39. data/spec/fixtures/default/montage.yml +0 -7
  40. data/spec/fixtures/default/public/images/sprites/src/one.png +0 -0
  41. data/spec/fixtures/default/public/images/sprites/src/three.png +0 -0
  42. data/spec/fixtures/default/public/images/sprites/src/two.png +0 -0
  43. data/spec/fixtures/directory_config/config/montage.yml +0 -5
  44. data/spec/fixtures/missing_source/montage.yml +0 -3
  45. data/spec/fixtures/missing_source_dir/montage.yml +0 -5
  46. data/spec/fixtures/root_config/montage.yml +0 -5
  47. data/spec/fixtures/root_config/public/images/sprites/src/source_one.png +0 -0
  48. data/spec/fixtures/root_config/public/images/sprites/src/source_three.jpg +0 -0
  49. data/spec/fixtures/root_config/public/images/sprites/src/source_two +0 -0
  50. data/spec/fixtures/sources/hundred.png +0 -0
  51. data/spec/fixtures/sources/mammoth.png +0 -0
  52. data/spec/fixtures/sources/other.png +0 -0
  53. data/spec/fixtures/sources/twenty.png +0 -0
  54. data/spec/fixtures/subdirs/montage.yml +0 -5
  55. data/spec/fixtures/subdirs/sub/sub/keep +0 -0
  56. data/spec/lib/fixtures.rb +0 -7
data/History.md CHANGED
@@ -1,3 +1,26 @@
1
+ v0.3.0 - 2010-04-12
2
+ -------------------
3
+
4
+ * The "montage.yml" file has been replaced with ".montage" which should be
5
+ located in your project root. In addition, the file is now rather different,
6
+ and in most cases will never need to be edited when you want to add new
7
+ sources to the sprite.
8
+
9
+ * By default Montage will now save sprites to public/images, expected source
10
+ images to be in public/images/subdir -- where "subdir" will become the name
11
+ of the sprite. All sources in a subdirectory will be added to the same
12
+ sprite.
13
+
14
+ This behavior is entirely customisable in the .montage file.
15
+
16
+ * The ".montage_cache" file which was previously saved in the same directory
17
+ as sprites is now saved in the project root.
18
+
19
+ * The `montage` command now allows you to specify a path to a Montage
20
+ configuration file; for example `montage path/to/montage.yml`. When using
21
+ a non-standard directory structure, you can specify a "config.root" option
22
+ in the configuration file, containing the path to the project root.
23
+
1
24
  v0.2.0 - 2010-04-08
2
25
  -------------------
3
26
 
data/Rakefile CHANGED
@@ -20,13 +20,11 @@ begin
20
20
  gem.has_rdoc = false
21
21
 
22
22
  # Dependencies.
23
- gem.add_dependency 'activesupport', '>= 3.0.0.beta'
24
- gem.add_dependency 'rmagick', '>= 2.12'
25
- gem.add_dependency 'highline', '>= 1.5'
23
+ gem.add_dependency 'rmagick', '>= 2.12'
24
+ gem.add_dependency 'highline', '>= 1.5'
26
25
 
27
26
  # Development dependencies.
28
27
  gem.add_development_dependency 'rspec', '>= 1.3.0'
29
- gem.add_development_dependency 'cucumber', '>= 0.6'
30
28
  gem.add_development_dependency 'open4', '>= 1.0'
31
29
  gem.add_development_dependency 'haml', '>= 3.0.0.beta.1'
32
30
  gem.add_development_dependency 'yard', '>= 0.5'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
data/bin/montage CHANGED
@@ -4,19 +4,26 @@ unless $:.include?(File.dirname(__FILE__) + '/../lib')
4
4
  $:.unshift(File.dirname(__FILE__) + '/../lib')
5
5
  end
6
6
 
7
+ require 'optparse'
7
8
  require 'rubygems' unless ENV['NORUBYGEMS']
8
9
 
9
10
  require 'montage'
10
11
  require 'montage/commands'
11
12
 
12
- Montage::Commands.print_masthead
13
+ require 'highline/import'
14
+
15
+ module Kernel
16
+ def_delegators :$terminal, :color
17
+ end
18
+
19
+ args = Montage::Commands.parse_options!(ARGV.dup)
20
+ puts
13
21
 
14
22
  case ARGV[0]
15
23
  when 'init'
16
24
  require 'montage/commands/init'
17
- Montage::Commands::Init.run(ARGV)
25
+ Montage::Commands::Init.run(args)
18
26
  else
19
27
  require 'montage/commands/generate'
20
- Montage::Commands::Generate.run(ARGV)
28
+ Montage::Commands::Generate.run(args)
21
29
  end
22
-
@@ -17,7 +17,20 @@ module Montage
17
17
  # The arguments given on the command line.
18
18
  #
19
19
  def self.run(argv)
20
- new(Montage::Project.find(Dir.pwd), argv.include?('--force')).run!
20
+ # If there are any arguments, the first one is a path to a montage
21
+ # config file.
22
+ if argv.first and not Pathname.new(argv.first).file?
23
+ say color(<<-ERROR.compress_lines, :red)
24
+ Couldn't find `#{argv.first}' configuration file. Are you
25
+ sure you got the path right?
26
+ ERROR
27
+
28
+ exit(1)
29
+ end
30
+
31
+
32
+ new(Montage::Project.find(argv.first || Dir.pwd),
33
+ Montage::Commands.config[:force]).run!
21
34
 
22
35
  rescue Montage::MissingProject
23
36
  say color(<<-ERROR.compress_lines, :red)
@@ -52,6 +65,7 @@ module Montage
52
65
  optimise_with_pngout!
53
66
  write_cache!
54
67
  write_sass!
68
+ warn_deviants!
55
69
  end
56
70
  end
57
71
 
@@ -63,7 +77,7 @@ module Montage
63
77
  #
64
78
  def cache
65
79
  @_sprite_caches ||= begin
66
- cache_path = @project.paths.sprites + '.montage_cache'
80
+ cache_path = @project.paths.root + '.montage_cache'
67
81
  cache_path.file? ? YAML.load_file(cache_path) || {} : {}
68
82
  end
69
83
  end
@@ -75,15 +89,16 @@ module Montage
75
89
  # Returns true if at least one sprite has been updated.
76
90
  #
77
91
  def generate_sprites!
78
- unless @project.paths.sprites.directory?
79
- @project.paths.sprites.mkpath
80
- end
81
-
82
92
  @project.sprites.each do |sprite|
83
93
  digest = sprite.digest
84
94
 
95
+ # Ensure that we can write to the output directory.
96
+ unless sprite.save_path.dirname.directory?
97
+ sprite.save_path.dirname.mkpath
98
+ end
85
99
 
86
- if @force or cache[sprite.name] != digest or not sprite.path.file?
100
+ if @force or cache[sprite.name] != digest or
101
+ not sprite.save_path.file?
87
102
  with_feedback %(Generating "#{sprite.name}"), 'Generating' do
88
103
  sprite.write
89
104
  cache[sprite.name] = digest
@@ -124,18 +139,19 @@ module Montage
124
139
  end
125
140
 
126
141
  @generated.each do |sprite|
127
- original_size = sprite.path.size
142
+ original_size = sprite.save_path.size
143
+ save_path = sprite.save_path
128
144
 
129
145
  with_feedback %(Optimising "#{sprite.name}"), 'Optimising' do
130
146
  5.times do |i|
131
147
  # Optimise until pngout reports that it can't compress further,
132
148
  # or until we've tried five times.
133
- out = `#{pngout} #{sprite.path} #{sprite.path} -s0 -k0 -y`
149
+ out = `#{pngout} #{save_path} #{save_path} -s0 -k0 -y`
134
150
  break if out =~ /Unable to compress further/
135
151
  end
136
152
  end
137
153
 
138
- new_size = sprite.path.size
154
+ new_size = save_path.size
139
155
 
140
156
  reduction = ('%.1fkb (%d' % [
141
157
  (original_size.to_f - new_size) / 1024,
@@ -158,7 +174,7 @@ module Montage
158
174
  # Step 3: Writes the cached digests to the cache file.
159
175
  #
160
176
  def write_cache!
161
- cache_path = @project.paths.sprites + '.montage_cache'
177
+ cache_path = @project.paths.root + '.montage_cache'
162
178
 
163
179
  File.open(cache_path, 'w') do |cache_writer|
164
180
  cache_writer.puts YAML.dump(cache)
@@ -176,6 +192,37 @@ module Montage
176
192
  end
177
193
  end
178
194
 
195
+ # Step 5: Warn about images which are more than one standard deviation
196
+ # from the mean width.
197
+ #
198
+ def warn_deviants!
199
+ @generated.each do |sprite|
200
+ next if sprite.sources.size < 2
201
+
202
+ mean, std_dev = standard_deviation(sprite.sources.map do |source|
203
+ source.image.columns
204
+ end)
205
+
206
+ next if std_dev < 100 # Skip relatively narrow images.
207
+
208
+ sprite.sources.each do |source|
209
+ width = source.image.columns
210
+ if width > mean + std_dev || width < mean - std_dev
211
+ say <<-MESSAGE.compress_lines
212
+ The "#{source.name}" source image in the "#{sprite.name}"
213
+ sprite deviates significantly from the average width. You
214
+ might want to consider removing this source from the sprite.
215
+
216
+ The mean width for sources in this sprite is #{mean}px,
217
+ while this source is #{width}px wide.
218
+
219
+ MESSAGE
220
+ say Montage::Commands::BLANK
221
+ end
222
+ end
223
+ end
224
+ end
225
+
179
226
  # --- Optimisation Output ----------------------------------------------
180
227
 
181
228
  # Executes a block while providing live feedback to the user.
@@ -229,6 +276,20 @@ module Montage
229
276
  say "#{RESET}#{prefix}"
230
277
  end
231
278
 
279
+ # Knuth. via Wikipedia. :/
280
+ def standard_deviation(data)
281
+ n, mean, m2 = 0, 0, 0
282
+
283
+ data.each do |x|
284
+ n = n + 1
285
+ delta = x - mean
286
+ mean = mean + delta / n
287
+ m2 = m2 + delta * (x - mean)
288
+ end
289
+
290
+ [mean, Math.sqrt(m2 / (n - 1))]
291
+ end
292
+
232
293
  end # Generate
233
294
  end # Commands
234
295
  end # Montage
@@ -72,13 +72,13 @@ module Montage
72
72
 
73
73
  @sprites_path =
74
74
  ask("Where do you want generated sprites to be stored?") do |query|
75
- query.default = 'public/images/sprites'
75
+ query.default = File.join('public', 'images')
76
76
  query.answer_type = normalise_path
77
77
  end
78
78
 
79
79
  @sources_path =
80
80
  ask("Where are the source images stored?") do |query|
81
- query.default = "#{@sprites_path}/src"
81
+ query.default = (@sprites_path + "sprites").to_s
82
82
  query.answer_type = normalise_path
83
83
  end
84
84
  end
@@ -94,16 +94,10 @@ module Montage
94
94
  #
95
95
  def create_config!
96
96
  template = File.read(TEMPLATES + 'montage.yml')
97
- template.gsub!(/<sprites>/, %("#{@sprites_path.to_s}"))
98
- template.gsub!(/<sources>/, %("#{@sources_path.to_s}"))
97
+ template.gsub!(/<sprites>/, @sprites_path.to_s)
98
+ template.gsub!(/<sources>/, @sources_path.to_s)
99
99
 
100
- if (@dir + 'config').directory?
101
- config_path = @dir + 'config/montage.yml'
102
- else
103
- config_path = @dir + 'montage.yml'
104
- end
105
-
106
- File.open(config_path, 'w') do |config|
100
+ File.open(@dir + '.montage', 'w') do |config|
107
101
  config.puts template
108
102
  end
109
103
  end
@@ -1,29 +1,62 @@
1
- require 'highline/import'
2
-
3
- HighLine.use_color = ! ARGV.delete('--no-color') && ! ARGV.delete('--no-colour')
4
- HighLine.use_color = false if !STDOUT.tty? && !ENV.has_key?("AUTOTEST")
5
-
6
- module Kernel
7
- def_delegators :$terminal, :color
8
- end
9
-
10
1
  module Montage
11
2
  module Commands
3
+ extend self
4
+
5
+ # A blank line; HighLine doesn't allow calling +say+ without an argument.
12
6
  BLANK = "\n".freeze
13
7
 
14
- extend self
8
+ # Returns a configuration hash, containing options defined on the
9
+ # command-line.
10
+ #
11
+ # @return [Hash]
12
+ #
13
+ def config
14
+ @config ||= { :force => false, :quiet => false }
15
+ end
15
16
 
16
- # Prints the Montage masthead, introducing the programme, and including
17
- # the current version number.
18
- def print_masthead
19
- say BLANK
20
- say "Montage v#{Montage::VERSION}"
21
- say "=========#{'=' * Montage::VERSION.length}"
22
- say BLANK
17
+ # Uses OptParse to parse command-line arguments.
18
+ #
19
+ # Returns any unparsed command-line arguments.
20
+ #
21
+ def parse_options!(argv)
22
+ HighLine.use_color = false if !STDOUT.tty? && !ENV.has_key?("AUTOTEST")
23
+
24
+ OptionParser.new do |opts|
25
+ opts.banner = "Usage: montage [config file path] [options]"
26
+
27
+ opts.on('-c', '--[no-]color', '--[no-]colour',
28
+ 'Enables and disables colour output.') do |color|
29
+ HighLine.use_color = color
30
+ end
31
+
32
+ opts.on('-f', '--force',
33
+ 'Regenerate sprites even if no changes have been made.') do
34
+ Montage::Commands.config[:force] = true
35
+ end
36
+
37
+ # opts.on('-q', '--quiet',
38
+ # 'Tell Montage to shut up. No messages sent to STDOUT.') do
39
+ # Montage::Commands.config[:quiet] = true
40
+ # end
41
+
42
+ opts.on_tail("-h", "--help", "Shows this message.") do
43
+ say BLANK
44
+ say opts.to_s
45
+ exit
46
+ end
47
+
48
+ opts.on_tail("--version", "Print the current Montage version.") do
49
+ say BLANK
50
+ say "Montage v#{Montage::VERSION}"
51
+ exit
52
+ end
53
+ end.parse!(argv)
54
+
55
+ argv
23
56
  end
24
57
 
25
58
  # Exits immediately, outputting a blank line first.
26
- def exit(status)
59
+ def exit(status = 0)
27
60
  say BLANK
28
61
  Kernel.exit(status)
29
62
  end
@@ -3,14 +3,16 @@ module Montage
3
3
  # configuration file, and source images.
4
4
  class Project
5
5
  DEFAULTS = {
6
- :sources => 'public/images/sprites/src',
7
- :sprites => 'public/images/sprites',
8
- :sass => 'public/stylesheets/sass',
9
- :sprite_url => '/images/sprites'
6
+ :sources => 'public/images/sprites/src',
7
+ :sprites => 'public/images/sprites',
8
+ :sass => 'public/stylesheets/sass',
9
+ :to => "public/images/:name.png",
10
+ :url => "/images/:name.png",
11
+ :padding => 20
10
12
  }
11
13
 
12
14
  # Stores all the paths the project needs.
13
- Paths = Struct.new(:root, :config, :sources, :sprites, :sass, :url)
15
+ Paths = Struct.new(:root, :config, :sass, :url)
14
16
 
15
17
  # Returns the Paths instance for the project.
16
18
  #
@@ -37,31 +39,29 @@ module Montage
37
39
  # to be correct. If you're not sure of the exact paths, use +Project.find+
38
40
  # instead.
39
41
  #
40
- # @param [String, Pathname] root_path
41
- # Path to the root of the Montage project.
42
42
  # @param [String, Pathname] config_path
43
43
  # Path to the config file.
44
44
  #
45
- def initialize(root_path, config_path)
46
- root_path = Pathname.new(root_path)
45
+ def initialize(config_path)
47
46
  config_path = Pathname.new(config_path)
47
+ config = YAML.load_file(config_path)
48
+ root_path = determine_project_root(config_path, config)
48
49
 
49
- config = YAML.load_file(config_path)
50
+ # Sass path may be a string representing a path, or `false`.
51
+ sass_path = config.delete("config.sass") { DEFAULTS[:sass] }
52
+ sass_path = sass_path.is_a?(String) ? root_path + sass_path : sass_path
50
53
 
51
54
  @paths = Paths.new(
52
- root_path, config_path,
53
- extract_path_from_config(config, :sources, root_path),
54
- extract_path_from_config(config, :sprites, root_path),
55
- extract_path_from_config(config, :sass, root_path),
56
- config.delete('config.sprite_url') { DEFAULTS[:sprite_url] }
55
+ root_path, config_path, sass_path,
56
+ config.delete('config.url') { DEFAULTS[:url] }
57
57
  )
58
58
 
59
59
  @padding = (config.delete('config.padding') || 20).to_i
60
60
 
61
61
  # All remaining config keys are sprite defintions.
62
- @sprites = config.inject([]) do |sprites, (name, sources)|
63
- sprites << Sprite.new(name, sources, self)
64
- end
62
+ @sprites = config.map do |path, opts|
63
+ Montage::SpriteDefinition.new(self, path, opts).to_sprites
64
+ end.flatten
65
65
  end
66
66
 
67
67
  # Returns a particular sprite identified by +name+.
@@ -82,9 +82,9 @@ module Montage
82
82
  #
83
83
  # The configuration item will be _removed_ from the hash.
84
84
  #
85
- # @param [Hash] config The configuration Hash.
86
- # @param [Symbol] key The configuration key.
87
- # @param [Pathname] root The project root path.
85
+ # @param [Hash] config The configuration Hash.
86
+ # @param [Symbol] key The configuration key.
87
+ # @param [Pathname] root The project root path.
88
88
  #
89
89
  # @return [Pathname, false]
90
90
  #
@@ -93,6 +93,30 @@ module Montage
93
93
  value.is_a?(String) ? root + value : value
94
94
  end
95
95
 
96
+ # Attempts to find the project root for the configuration file. If the
97
+ # config file is in a directory called 'config' then the project root is
98
+ # assumed to be one level up.
99
+ #
100
+ # @param [Pathname] config_path The path to the config file.
101
+ # @param [Hash] config The project configuration.
102
+ #
103
+ # @return [Pathname]
104
+ #
105
+ def determine_project_root(config_path, config)
106
+ config_dir = config_path.dirname
107
+
108
+ if config.has_key?('config.root')
109
+ root_dir = Pathname.new(config.delete('config.root'))
110
+ root_dir.absolute? ? root_dir : (config_dir + root_dir)
111
+ else
112
+ if config_dir.split.last.to_s == 'config'
113
+ config_dir + '..'
114
+ else
115
+ config_dir
116
+ end
117
+ end
118
+ end
119
+
96
120
  # === Class Methods ======================================================
97
121
 
98
122
  class << self
@@ -111,7 +135,7 @@ module Montage
111
135
  #
112
136
  # If given a path to a directory:
113
137
  #
114
- # * Montage will look for montage.yml, or config/montage.yml.
138
+ # * Montage will look for a .montage file.
115
139
  #
116
140
  # * If a configuration couldn't be found, +find+ looks in the next
117
141
  # directory up. It continues until it finds a valid project or runs
@@ -131,37 +155,28 @@ module Montage
131
155
  config_path, root_path = nil, nil
132
156
 
133
157
  if path.file?
134
- root_path = find_root(path)
135
158
  config_path = path
136
159
  elsif path.directory?
137
- if config_path = find_config(path)
138
- root_path = path
139
- else
140
- # Assume we're in a subdirectory of the current project.
141
- path.split.first.ascend do |directory|
142
- if config_path = find_config(directory)
143
- break if root_path = find_root(config_path)
144
- end
145
- end
160
+ path.ascend do |directory|
161
+ break if config_path = contains_config?(directory)
146
162
  end
147
163
  end
148
164
 
149
165
  raise MissingProject, "Montage couldn't find a project to work " \
150
- "on at `#{path}'" if root_path.nil?
166
+ "on at `#{path}'" unless config_path
151
167
 
152
- new(root_path, config_path)
168
+ new(config_path)
153
169
  end
154
170
 
155
171
  private
156
172
 
157
- # Attempt to find the configuration file, first by looking in
158
- # ./montage.yml, then ./config/montage.yml
173
+ # Looks for a .montage configuration file in the given directory.
159
174
  #
160
175
  # @return [String]
161
176
  #
162
- def find_config(dir)
163
- config_paths = [ dir + 'montage.yml', dir + 'config/montage.yml' ]
164
- config_paths.detect { |config| config.file? }
177
+ def contains_config?(dir)
178
+ expected = (dir + '.montage')
179
+ expected.file? and expected
165
180
  end
166
181
 
167
182
  # Attempts to find the project root for the configuration file. If the
@@ -3,56 +3,21 @@ module Montage
3
3
  #
4
4
  class Source
5
5
 
6
- attr_reader :name
6
+ attr_reader :name, :path
7
7
  alias_method :to_s, :name
8
8
 
9
9
  # Creates a new Source instance.
10
10
  #
11
- # @param [Pathname] dir
12
- # The directory in which the source image should stored.
13
- # @param [String] name
14
- # The name of the source image, sans extension
15
- # @param [String] sprite
16
- # The name of the sprite to which the source belongs. Used only in
17
- # error messages.
11
+ # @param [Pathname] path
12
+ # The path the the source image.
18
13
  #
19
- def initialize(dir, name, sprite_name)
20
- @dir, @name, @sprite_name = dir, name, sprite_name
14
+ def initialize(path)
15
+ @path = Pathname.new(path)
16
+ @name = @path.basename.to_s.chomp(@path.extname.to_s)
21
17
  end
22
18
 
23
19
  def inspect # :nodoc
24
- "#<Montage::Source #{@sprite_name}:#{@name}>"
25
- end
26
-
27
- # Returns the full path to the source image.
28
- #
29
- # @return [Pathname]
30
- # @raise [Montage::MissingSource]
31
- #
32
- def path
33
- @path ||= begin
34
- unless @dir.directory?
35
- raise MissingSource, <<-MESSAGE.compress_lines
36
- Couldn't find the source directory for the `#{@sprite_name}'
37
- sprite. Montage was looking for #{@dir}; if your sprites are in a
38
- different location, add a 'config.sources' option your config
39
- file.
40
- MESSAGE
41
- end
42
-
43
- path = @dir.entries.detect do |entry|
44
- entry.to_s.chomp(entry.extname) == @name
45
- end
46
-
47
- if path.nil?
48
- raise MissingSource, <<-MESSAGE.compress_lines
49
- Couldn't find a matching file for source image `#{@name}' as part
50
- of the `#{@sprite_name}' sprite. Was looking in `#{@dir}'.
51
- MESSAGE
52
- end
53
-
54
- @dir + path
55
- end
20
+ "#<Montage::Source #{@name}>"
56
21
  end
57
22
 
58
23
  # Returns the RMagick image instance representing the source.
@@ -60,7 +25,10 @@ module Montage
60
25
  # @return [Magick::Image]
61
26
  #
62
27
  def image
63
- @image ||= Magick::Image.read(path).first
28
+ raise MissingSource, "Couldn't find the source file `#{@path}'" \
29
+ unless @path.file?
30
+
31
+ @image ||= Magick::Image.read(@path).first
64
32
  end
65
33
 
66
34
  # Returns a digest which represents the sprite name and file contents.
@@ -68,7 +36,10 @@ module Montage
68
36
  # @return [String]
69
37
  #
70
38
  def digest
71
- Digest::SHA256.hexdigest(@name + Digest::SHA256.file(path).to_s)
39
+ raise MissingSource, "Couldn't find the source file `#{@path}'" \
40
+ unless @path.file?
41
+
42
+ Digest::SHA256.hexdigest(@name + Digest::SHA256.file(@path).to_s)
72
43
  end
73
44
 
74
45
  end # Source