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.
- data/History.md +23 -0
- data/Rakefile +2 -4
- data/VERSION +1 -1
- data/bin/montage +11 -4
- data/lib/montage/commands/generate.rb +72 -11
- data/lib/montage/commands/init.rb +5 -11
- data/lib/montage/commands.rb +51 -18
- data/lib/montage/project.rb +54 -39
- data/lib/montage/source.rb +15 -44
- data/lib/montage/sprite.rb +25 -37
- data/lib/montage/sprite_definition.rb +127 -0
- data/lib/montage/templates/montage.yml +29 -19
- data/lib/montage/templates/sass_mixins.erb +1 -1
- data/lib/montage/templates/sources/{book.png → one/book.png} +0 -0
- data/lib/montage/templates/sources/{box-label.png → one/box-label.png} +0 -0
- data/lib/montage/templates/sources/{calculator.png → one/calculator.png} +0 -0
- data/lib/montage/templates/sources/{calendar-month.png → one/calendar-month.png} +0 -0
- data/lib/montage/templates/sources/{camera.png → one/camera.png} +0 -0
- data/lib/montage/templates/sources/{eraser.png → one/eraser.png} +0 -0
- data/lib/montage/templates/sources/two/inbox-image.png +0 -0
- data/lib/montage/templates/sources/two/magnet.png +0 -0
- data/lib/montage/templates/sources/two/newspaper.png +0 -0
- data/lib/montage/templates/sources/two/television.png +0 -0
- data/lib/montage/templates/sources/two/wand-hat.png +0 -0
- data/lib/montage/templates/sources/two/wooden-box-label.png +0 -0
- data/lib/montage.rb +10 -1
- data/montage.gemspec +17 -34
- data/spec/lib/project_helper.rb +41 -6
- data/spec/lib/shared_project_specs.rb +10 -11
- data/spec/montage/commands/generate_spec.rb +86 -132
- data/spec/montage/commands/init_spec.rb +18 -43
- data/spec/montage/project_spec.rb +77 -63
- data/spec/montage/sass_builder_spec.rb +33 -57
- data/spec/montage/source_spec.rb +10 -31
- data/spec/montage/sprite_definition_spec.rb +361 -0
- data/spec/montage/sprite_spec.rb +58 -57
- metadata +31 -70
- data/spec/fixtures/custom_dirs/montage.yml +0 -8
- data/spec/fixtures/default/montage.yml +0 -7
- data/spec/fixtures/default/public/images/sprites/src/one.png +0 -0
- data/spec/fixtures/default/public/images/sprites/src/three.png +0 -0
- data/spec/fixtures/default/public/images/sprites/src/two.png +0 -0
- data/spec/fixtures/directory_config/config/montage.yml +0 -5
- data/spec/fixtures/missing_source/montage.yml +0 -3
- data/spec/fixtures/missing_source_dir/montage.yml +0 -5
- data/spec/fixtures/root_config/montage.yml +0 -5
- data/spec/fixtures/root_config/public/images/sprites/src/source_one.png +0 -0
- data/spec/fixtures/root_config/public/images/sprites/src/source_three.jpg +0 -0
- data/spec/fixtures/root_config/public/images/sprites/src/source_two +0 -0
- data/spec/fixtures/sources/hundred.png +0 -0
- data/spec/fixtures/sources/mammoth.png +0 -0
- data/spec/fixtures/sources/other.png +0 -0
- data/spec/fixtures/sources/twenty.png +0 -0
- data/spec/fixtures/subdirs/montage.yml +0 -5
- data/spec/fixtures/subdirs/sub/sub/keep +0 -0
- 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 '
|
24
|
-
gem.add_dependency '
|
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.
|
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
|
-
|
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(
|
25
|
+
Montage::Commands::Init.run(args)
|
18
26
|
else
|
19
27
|
require 'montage/commands/generate'
|
20
|
-
Montage::Commands::Generate.run(
|
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
|
-
|
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.
|
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
|
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.
|
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} #{
|
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 =
|
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.
|
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
|
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 =
|
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>/,
|
98
|
-
template.gsub!(/<sources>/,
|
97
|
+
template.gsub!(/<sprites>/, @sprites_path.to_s)
|
98
|
+
template.gsub!(/<sources>/, @sources_path.to_s)
|
99
99
|
|
100
|
-
|
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
|
data/lib/montage/commands.rb
CHANGED
@@ -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
|
-
|
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
|
-
#
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
data/lib/montage/project.rb
CHANGED
@@ -3,14 +3,16 @@ module Montage
|
|
3
3
|
# configuration file, and source images.
|
4
4
|
class Project
|
5
5
|
DEFAULTS = {
|
6
|
-
:sources
|
7
|
-
:sprites
|
8
|
-
:sass
|
9
|
-
:
|
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, :
|
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(
|
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
|
-
|
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
|
-
|
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.
|
63
|
-
|
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]
|
86
|
-
# @param [Symbol]
|
87
|
-
# @param [Pathname] root
|
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
|
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
|
-
|
138
|
-
|
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}'"
|
166
|
+
"on at `#{path}'" unless config_path
|
151
167
|
|
152
|
-
new(
|
168
|
+
new(config_path)
|
153
169
|
end
|
154
170
|
|
155
171
|
private
|
156
172
|
|
157
|
-
#
|
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
|
163
|
-
|
164
|
-
|
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
|
data/lib/montage/source.rb
CHANGED
@@ -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]
|
12
|
-
# The
|
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(
|
20
|
-
@
|
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 #{@
|
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
|
-
|
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
|
-
|
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
|