montage 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +31 -0
- data/History.md +40 -0
- data/LICENSE +19 -0
- data/README.md +89 -0
- data/Rakefile +41 -0
- data/VERSION +1 -0
- data/bin/montage +22 -0
- data/lib/montage.rb +33 -0
- data/lib/montage/commands.rb +32 -0
- data/lib/montage/commands/generate.rb +234 -0
- data/lib/montage/commands/init.rb +119 -0
- data/lib/montage/core_ext.rb +80 -0
- data/lib/montage/project.rb +185 -0
- data/lib/montage/sass_builder.rb +37 -0
- data/lib/montage/source.rb +75 -0
- data/lib/montage/sprite.rb +132 -0
- data/lib/montage/templates/montage.yml +26 -0
- data/lib/montage/templates/sass_mixins.erb +20 -0
- data/lib/montage/templates/sources/book.png +0 -0
- data/lib/montage/templates/sources/box-label.png +0 -0
- data/lib/montage/templates/sources/calculator.png +0 -0
- data/lib/montage/templates/sources/calendar-month.png +0 -0
- data/lib/montage/templates/sources/camera.png +0 -0
- data/lib/montage/templates/sources/eraser.png +0 -0
- data/lib/montage/version.rb +3 -0
- data/montage.gemspec +145 -0
- data/spec/fixtures/custom_dirs/montage.yml +8 -0
- data/spec/fixtures/default/montage.yml +7 -0
- 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 +5 -0
- data/spec/fixtures/missing_source/montage.yml +3 -0
- data/spec/fixtures/missing_source_dir/montage.yml +5 -0
- data/spec/fixtures/root_config/montage.yml +5 -0
- 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 +5 -0
- data/spec/fixtures/subdirs/sub/sub/keep +0 -0
- data/spec/lib/command_runner.rb +140 -0
- data/spec/lib/fixtures.rb +7 -0
- data/spec/lib/have_public_method_defined.rb +19 -0
- data/spec/lib/project_helper.rb +135 -0
- data/spec/lib/shared_project_specs.rb +32 -0
- data/spec/lib/shared_sprite_specs.rb +30 -0
- data/spec/montage/commands/generate_spec.rb +308 -0
- data/spec/montage/commands/init_spec.rb +120 -0
- data/spec/montage/core_ext_spec.rb +33 -0
- data/spec/montage/project_spec.rb +181 -0
- data/spec/montage/sass_builder_spec.rb +269 -0
- data/spec/montage/source_spec.rb +53 -0
- data/spec/montage/spec/have_public_method_defined_spec.rb +31 -0
- data/spec/montage/sprite_spec.rb +170 -0
- data/spec/rcov.opts +8 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +19 -0
- data/tasks/spec.rake +17 -0
- data/tasks/yard.rake +11 -0
- metadata +249 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
module Montage
|
2
|
+
module Commands
|
3
|
+
# Creates a new project.
|
4
|
+
class Init
|
5
|
+
extend Commands
|
6
|
+
|
7
|
+
# Path to lib/templates
|
8
|
+
TEMPLATES = Pathname.new(__FILE__).dirname.parent + 'templates'
|
9
|
+
|
10
|
+
# === Class Methods ====================================================
|
11
|
+
|
12
|
+
# Creates a new project in the current directory.
|
13
|
+
#
|
14
|
+
# @param [Array] argv
|
15
|
+
# The arguments given on the command line.
|
16
|
+
#
|
17
|
+
def self.run(*)
|
18
|
+
new(Dir.pwd).run!
|
19
|
+
|
20
|
+
rescue Montage::ProjectExists => e
|
21
|
+
if e.message.match(/`(.*)'$/)[1] == Dir.pwd
|
22
|
+
say color("A Montage project already exists in the " \
|
23
|
+
"current directory", :red)
|
24
|
+
else
|
25
|
+
say color(e.message.compress_lines, :red)
|
26
|
+
end
|
27
|
+
|
28
|
+
exit(1)
|
29
|
+
end
|
30
|
+
|
31
|
+
# === Instance Methods =================================================
|
32
|
+
|
33
|
+
# Creates a new Generate instance.
|
34
|
+
#
|
35
|
+
# @param [Pathname]
|
36
|
+
# The directory in which a new project is to be created.
|
37
|
+
#
|
38
|
+
def initialize(dir)
|
39
|
+
@dir = Pathname.new(dir)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Runs the command, creating the new project structure.
|
43
|
+
#
|
44
|
+
def run!
|
45
|
+
begin
|
46
|
+
found = Project.find(@dir)
|
47
|
+
rescue MissingProject
|
48
|
+
ask_questions!
|
49
|
+
make_directories!
|
50
|
+
create_config!
|
51
|
+
copy_sources!
|
52
|
+
say color("Your project was created", :green)
|
53
|
+
say Montage::Commands::BLANK
|
54
|
+
else
|
55
|
+
raise Montage::ProjectExists, <<-ERROR.compress_lines
|
56
|
+
A Montage project exists in a parent directory at
|
57
|
+
`#{found.paths.root}'
|
58
|
+
ERROR
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private # ==============================================================
|
63
|
+
|
64
|
+
# Step 1: Ask the user where they want files to be stored.
|
65
|
+
#
|
66
|
+
def ask_questions!
|
67
|
+
normalise_path = lambda do |path|
|
68
|
+
npath = Pathname.new(path).expand_path
|
69
|
+
npath = npath.relative_path_from(@dir) unless path[0].chr == '/'
|
70
|
+
npath
|
71
|
+
end
|
72
|
+
|
73
|
+
@sprites_path =
|
74
|
+
ask("Where do you want generated sprites to be stored?") do |query|
|
75
|
+
query.default = 'public/images/sprites'
|
76
|
+
query.answer_type = normalise_path
|
77
|
+
end
|
78
|
+
|
79
|
+
@sources_path =
|
80
|
+
ask("Where are the source images stored?") do |query|
|
81
|
+
query.default = "#{@sprites_path}/src"
|
82
|
+
query.answer_type = normalise_path
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Step 2: Make the sprites and sources directories.
|
87
|
+
#
|
88
|
+
def make_directories!
|
89
|
+
@sprites_path.mkpath unless @sprites_path.directory?
|
90
|
+
@sources_path.mkpath unless @sources_path.directory?
|
91
|
+
end
|
92
|
+
|
93
|
+
# Step 3: Write the configuration.
|
94
|
+
#
|
95
|
+
def create_config!
|
96
|
+
template = File.read(TEMPLATES + 'montage.yml')
|
97
|
+
template.gsub!(/<sprites>/, %("#{@sprites_path.to_s}"))
|
98
|
+
template.gsub!(/<sources>/, %("#{@sources_path.to_s}"))
|
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|
|
107
|
+
config.puts template
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Step 4: Copy the sample source images.
|
112
|
+
#
|
113
|
+
def copy_sources!
|
114
|
+
FileUtils.cp_r(TEMPLATES + 'sources/.', @sources_path)
|
115
|
+
end
|
116
|
+
|
117
|
+
end # Init
|
118
|
+
end # Commands
|
119
|
+
end # Montage
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
# Replace sequences of whitespace (including newlines) with either
|
4
|
+
# a single space or remove them entirely (according to param _spaced_)
|
5
|
+
#
|
6
|
+
# <<QUERY.compress_lines
|
7
|
+
# SELECT name
|
8
|
+
# FROM users
|
9
|
+
# QUERY => "SELECT name FROM users"
|
10
|
+
#
|
11
|
+
# @param [TrueClass, FalseClass] spaced (default=true)
|
12
|
+
# Determines whether returned string has whitespace collapsed or removed
|
13
|
+
#
|
14
|
+
# @return [String] Receiver with whitespace (including newlines) replaced
|
15
|
+
#
|
16
|
+
def compress_lines(spaced = true)
|
17
|
+
split($/).map { |line| line.strip }.join(spaced ? ' ' : '')
|
18
|
+
end
|
19
|
+
|
20
|
+
# Removes leading whitespace from each line, such as might be added when
|
21
|
+
# using a HEREDOC string.
|
22
|
+
#
|
23
|
+
# @return [String] Receiver with leading whitespace removed.
|
24
|
+
#
|
25
|
+
def unindent
|
26
|
+
(other = dup) and other.unindent! and other
|
27
|
+
end
|
28
|
+
|
29
|
+
# Bang version of #unindent.
|
30
|
+
#
|
31
|
+
# @return [String] Receiver with leading whitespace removed.
|
32
|
+
#
|
33
|
+
def unindent!
|
34
|
+
gsub!(/^[ \t]{#{minimum_leading_whitespace}}/, '')
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Checks each line and determines the minimum amount of leading whitespace.
|
40
|
+
#
|
41
|
+
# @return [Integer] The number of leading whitespace characters.
|
42
|
+
#
|
43
|
+
def minimum_leading_whitespace
|
44
|
+
whitespace = split("\n", -1).inject(0) do |indent, line|
|
45
|
+
if line.strip.empty?
|
46
|
+
indent # Ignore completely blank lines.
|
47
|
+
elsif line =~ /^(\s+)/
|
48
|
+
(1.0 / $1.length) > indent ? 1.0 / $1.length : indent
|
49
|
+
else
|
50
|
+
1.0
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
whitespace == 1.0 ? 0 : (1.0 / whitespace).to_i
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
# String#compress_lines is extracted from the extlib gem
|
60
|
+
# ------------------------------------------------------
|
61
|
+
#
|
62
|
+
# Copyright (c) 2009 Dan Kubb
|
63
|
+
#
|
64
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
65
|
+
# of this software and associated documentation files (the "Software"), to
|
66
|
+
# deal in the Software without restriction, including without limitation the
|
67
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
68
|
+
# sell copies of the Software, and to permit persons to whom the Software is
|
69
|
+
# furnished to do so, subject to the following conditions:
|
70
|
+
#
|
71
|
+
# The above copyright notice and this permission notice shall be included in
|
72
|
+
# all copies or substantial portions of the Software.
|
73
|
+
#
|
74
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
75
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
76
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
77
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
78
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
79
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
80
|
+
# IN THE SOFTWARE.
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module Montage
|
2
|
+
# Represents a directory in which it is expected that there be a
|
3
|
+
# configuration file, and source images.
|
4
|
+
class Project
|
5
|
+
DEFAULTS = {
|
6
|
+
:sources => 'public/images/sprites/src',
|
7
|
+
:sprites => 'public/images/sprites',
|
8
|
+
:sass => 'public/stylesheets/sass',
|
9
|
+
:sprite_url => '/images/sprites'
|
10
|
+
}
|
11
|
+
|
12
|
+
# Stores all the paths the project needs.
|
13
|
+
Paths = Struct.new(:root, :config, :sources, :sprites, :sass, :url)
|
14
|
+
|
15
|
+
# Returns the Paths instance for the project.
|
16
|
+
#
|
17
|
+
# @return [Montage::Project::Paths]
|
18
|
+
#
|
19
|
+
attr_reader :paths
|
20
|
+
|
21
|
+
# Returns an Array containing all the Sprites defined in the project.
|
22
|
+
#
|
23
|
+
# @return [Array<Montage::Sprite>]
|
24
|
+
#
|
25
|
+
attr_reader :sprites
|
26
|
+
|
27
|
+
# Returns the amount of space to be used between each source image when
|
28
|
+
# saving sprites.
|
29
|
+
#
|
30
|
+
# @return [Integer]
|
31
|
+
#
|
32
|
+
attr_reader :padding
|
33
|
+
|
34
|
+
# Creates a new Project instance.
|
35
|
+
#
|
36
|
+
# Note that +new+ does no validation of the given paths: it expects them
|
37
|
+
# to be correct. If you're not sure of the exact paths, use +Project.find+
|
38
|
+
# instead.
|
39
|
+
#
|
40
|
+
# @param [String, Pathname] root_path
|
41
|
+
# Path to the root of the Montage project.
|
42
|
+
# @param [String, Pathname] config_path
|
43
|
+
# Path to the config file.
|
44
|
+
#
|
45
|
+
def initialize(root_path, config_path)
|
46
|
+
root_path = Pathname.new(root_path)
|
47
|
+
config_path = Pathname.new(config_path)
|
48
|
+
|
49
|
+
config = YAML.load_file(config_path)
|
50
|
+
|
51
|
+
@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] }
|
57
|
+
)
|
58
|
+
|
59
|
+
@padding = (config.delete('config.padding') || 20).to_i
|
60
|
+
|
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
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a particular sprite identified by +name+.
|
68
|
+
#
|
69
|
+
# @param [String] name
|
70
|
+
# The name of the sprite to be retrieved.
|
71
|
+
#
|
72
|
+
# @return [Montage::Sprite]
|
73
|
+
#
|
74
|
+
def sprite(name)
|
75
|
+
sprites.detect { |sprite| sprite.name == name }
|
76
|
+
end
|
77
|
+
|
78
|
+
private # ================================================================
|
79
|
+
|
80
|
+
# Extracts a configuration value from a configuration hash. If the value
|
81
|
+
# exists, and is a string, it will be appended to the +root+ path.
|
82
|
+
#
|
83
|
+
# The configuration item will be _removed_ from the hash.
|
84
|
+
#
|
85
|
+
# @param [Hash] config The configuration Hash.
|
86
|
+
# @param [Symbol] key The configuration key.
|
87
|
+
# @param [Pathname] root The project root path.
|
88
|
+
#
|
89
|
+
# @return [Pathname, false]
|
90
|
+
#
|
91
|
+
def extract_path_from_config(config, key, root)
|
92
|
+
value = config.delete("config.#{key}") { DEFAULTS[key] }
|
93
|
+
value.is_a?(String) ? root + value : value
|
94
|
+
end
|
95
|
+
|
96
|
+
# === Class Methods ======================================================
|
97
|
+
|
98
|
+
class << self
|
99
|
+
|
100
|
+
# Given a path to a directory, or config file, attempts to find the
|
101
|
+
# Montage root.
|
102
|
+
#
|
103
|
+
# If given a path to a file:
|
104
|
+
#
|
105
|
+
# * Montage assumes that the file is the configuration.
|
106
|
+
#
|
107
|
+
# * The root directory is assumed to be the same directory as the one
|
108
|
+
# in which the configuration resides _unless_ the directory is
|
109
|
+
# called 'config', in which case the root is considered to be the
|
110
|
+
# parent directory.
|
111
|
+
#
|
112
|
+
# If given a path to a directory:
|
113
|
+
#
|
114
|
+
# * Montage will look for montage.yml, or config/montage.yml.
|
115
|
+
#
|
116
|
+
# * If a configuration couldn't be found, +find+ looks in the next
|
117
|
+
# directory up. It continues until it finds a valid project or runs
|
118
|
+
# out of parent directories.
|
119
|
+
#
|
120
|
+
# @param [String, Pathname] path
|
121
|
+
# Path to the configuration or directory.
|
122
|
+
#
|
123
|
+
# @return [Montage::Project]
|
124
|
+
# Returns the project.
|
125
|
+
#
|
126
|
+
# @raise [MissingProject]
|
127
|
+
# Raised when a project directory couldn't be found.
|
128
|
+
#
|
129
|
+
def find(path)
|
130
|
+
path = Pathname(path).expand_path
|
131
|
+
config_path, root_path = nil, nil
|
132
|
+
|
133
|
+
if path.file?
|
134
|
+
root_path = find_root(path)
|
135
|
+
config_path = path
|
136
|
+
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
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
raise MissingProject, "Montage couldn't find a project to work " \
|
150
|
+
"on at `#{path}'" if root_path.nil?
|
151
|
+
|
152
|
+
new(root_path, config_path)
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
# Attempt to find the configuration file, first by looking in
|
158
|
+
# ./montage.yml, then ./config/montage.yml
|
159
|
+
#
|
160
|
+
# @return [String]
|
161
|
+
#
|
162
|
+
def find_config(dir)
|
163
|
+
config_paths = [ dir + 'montage.yml', dir + 'config/montage.yml' ]
|
164
|
+
config_paths.detect { |config| config.file? }
|
165
|
+
end
|
166
|
+
|
167
|
+
# Attempts to find the project root for the configuration file. If the
|
168
|
+
# config file is in a directory called 'config' then the project root is
|
169
|
+
# assumed to be one level up.
|
170
|
+
#
|
171
|
+
# @return [String]
|
172
|
+
#
|
173
|
+
def find_root(config)
|
174
|
+
config_dir = config.dirname
|
175
|
+
|
176
|
+
if config_dir.split.last.to_s == 'config'
|
177
|
+
(config_dir + '..').expand_path
|
178
|
+
else
|
179
|
+
config_dir
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
end # class << self
|
184
|
+
end # Project
|
185
|
+
end # Montage
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Montage
|
2
|
+
# Given a project, builds a SASS file containing mixin to simplify use of
|
3
|
+
# the generated sprites in a project.
|
4
|
+
#
|
5
|
+
class SassBuilder
|
6
|
+
|
7
|
+
TEMPLATE = Pathname.new(__FILE__).dirname + 'templates/sass_mixins.erb'
|
8
|
+
|
9
|
+
# Creates a new SassBuilder instance.
|
10
|
+
#
|
11
|
+
# @param [Montage::Project] project
|
12
|
+
# The project whose Sass file is to be built.
|
13
|
+
#
|
14
|
+
def initialize(project)
|
15
|
+
@project = project
|
16
|
+
end
|
17
|
+
|
18
|
+
# Builds the Sass mixin file, then writes it to disk.
|
19
|
+
#
|
20
|
+
# @return [Boolean]
|
21
|
+
#
|
22
|
+
def write
|
23
|
+
if @project.paths.sass.to_s[-5..-1] == '.sass'
|
24
|
+
@project.paths.sass.dirname.mkpath
|
25
|
+
save_to = @project.paths.sass
|
26
|
+
else
|
27
|
+
@project.paths.sass.mkpath
|
28
|
+
save_to = @project.paths.sass + '_montage.sass'
|
29
|
+
end
|
30
|
+
|
31
|
+
File.open(save_to, 'w') do |file|
|
32
|
+
file.puts ERB.new(File.read(TEMPLATE), nil, '<>').result(binding)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end # SassBuilder
|
37
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Montage
|
2
|
+
# Represents a single source file used in a sprite.
|
3
|
+
#
|
4
|
+
class Source
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
alias_method :to_s, :name
|
8
|
+
|
9
|
+
# Creates a new Source instance.
|
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.
|
18
|
+
#
|
19
|
+
def initialize(dir, name, sprite_name)
|
20
|
+
@dir, @name, @sprite_name = dir, name, sprite_name
|
21
|
+
end
|
22
|
+
|
23
|
+
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
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the RMagick image instance representing the source.
|
59
|
+
#
|
60
|
+
# @return [Magick::Image]
|
61
|
+
#
|
62
|
+
def image
|
63
|
+
@image ||= Magick::Image.read(path).first
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns a digest which represents the sprite name and file contents.
|
67
|
+
#
|
68
|
+
# @return [String]
|
69
|
+
#
|
70
|
+
def digest
|
71
|
+
Digest::SHA256.hexdigest(@name + Digest::SHA256.file(path).to_s)
|
72
|
+
end
|
73
|
+
|
74
|
+
end # Source
|
75
|
+
end # Montage
|