gloss 0.0.6 → 0.1.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.
- checksums.yaml +4 -4
- data/.gloss.yml +1 -0
- data/Gemfile.lock +3 -3
- data/exe/gloss +10 -13
- data/ext/gloss/src/cr_ast.cr +61 -71
- data/ext/gloss/src/rb_ast.cr +37 -36
- data/lib/gloss.rb +14 -7
- data/lib/gloss/cli.rb +61 -40
- data/lib/gloss/config.rb +3 -1
- data/lib/gloss/errors.rb +1 -1
- data/lib/gloss/logger.rb +1 -6
- data/lib/gloss/prog_loader.rb +141 -0
- data/lib/gloss/scope.rb +1 -1
- data/lib/gloss/source.rb +1 -1
- data/lib/gloss/type_checker.rb +46 -28
- data/lib/gloss/utils.rb +44 -0
- data/lib/gloss/version.rb +1 -1
- data/lib/gloss/{builder.rb → visitor.rb} +80 -53
- data/lib/gloss/watcher.rb +2 -2
- data/lib/gloss/writer.rb +21 -10
- data/sig/core.rbs +2 -0
- data/sig/fast_blank.rbs +4 -0
- data/sig/{gloss.rbs → gls.rbs} +0 -0
- data/sig/optparse.rbs +6 -0
- data/sig/rubygems.rbs +9 -0
- data/sig/yaml.rbs +3 -0
- data/src/exe/gloss +19 -0
- data/src/lib/gloss.gl +26 -0
- data/src/lib/gloss/cli.gl +41 -26
- data/src/lib/gloss/config.gl +2 -2
- data/src/lib/gloss/logger.gl +1 -6
- data/src/lib/gloss/prog_loader.gl +133 -0
- data/src/lib/gloss/scope.gl +0 -2
- data/src/lib/gloss/type_checker.gl +42 -24
- data/src/lib/gloss/utils.gl +38 -0
- data/src/lib/gloss/version.gl +1 -1
- data/src/lib/gloss/{builder.gl → visitor.gl} +73 -44
- data/src/lib/gloss/watcher.gl +3 -4
- data/src/lib/gloss/writer.gl +15 -13
- metadata +16 -5
data/lib/gloss/watcher.rb
CHANGED
@@ -10,7 +10,7 @@ module Gloss
|
|
10
10
|
@paths = paths
|
11
11
|
(if @paths.empty?
|
12
12
|
@paths = [File.join(Dir.pwd, Config.src_dir)]
|
13
|
-
@only =
|
13
|
+
@only = /(?:(\.gl|(?:(?<=\/)[^\.\/]+))\z|\A[^\.\/]+\z)/
|
14
14
|
else
|
15
15
|
file_names = Array.new
|
16
16
|
paths = Array.new
|
@@ -39,7 +39,7 @@ module Gloss
|
|
39
39
|
.info("Rewriting #{f}")
|
40
40
|
content = File.read(f)
|
41
41
|
err = catch(:"error") { ||
|
42
|
-
Writer.new(
|
42
|
+
Writer.new(Visitor.new(Parser.new(content)
|
43
43
|
.run)
|
44
44
|
.run, f)
|
45
45
|
.run
|
data/lib/gloss/writer.rb
CHANGED
@@ -3,20 +3,13 @@
|
|
3
3
|
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
4
|
##### See src/ to make changes
|
5
5
|
|
6
|
-
|
6
|
+
require "pathname"
|
7
7
|
require "fileutils"
|
8
8
|
module Gloss
|
9
|
-
module Utils
|
10
|
-
module_function
|
11
|
-
def src_path_to_output_path(src_path)
|
12
|
-
src_path.sub("#{Config.src_dir}/", "")
|
13
|
-
.sub(/\.gl$/, ".rb")
|
14
|
-
end
|
15
|
-
end
|
16
9
|
class Writer
|
17
|
-
|
18
|
-
def initialize(content, src_path, output_path = Pathname.new(src_path_to_output_path(src_path)))
|
10
|
+
def initialize(content, src_path, output_path = Pathname.new(Utils.src_path_to_output_path(src_path)))
|
19
11
|
@content = content
|
12
|
+
@src_path = src_path
|
20
13
|
@output_path = output_path
|
21
14
|
end
|
22
15
|
def run()
|
@@ -25,8 +18,26 @@ module Gloss
|
|
25
18
|
FileUtils.mkdir_p(@output_path.parent)
|
26
19
|
end
|
27
20
|
File.open(@output_path, "wb") { |file|
|
21
|
+
sb = shebang
|
22
|
+
(if sb
|
23
|
+
file.puts(sb)
|
24
|
+
end)
|
28
25
|
file.puts(@content)
|
29
26
|
}
|
30
27
|
end
|
28
|
+
private def shebang()
|
29
|
+
(if @output_path.executable?
|
30
|
+
first_line = File.open(@src_path) { |f|
|
31
|
+
f.readline
|
32
|
+
}
|
33
|
+
(if first_line.start_with?("#!")
|
34
|
+
first_line
|
35
|
+
else
|
36
|
+
nil
|
37
|
+
end)
|
38
|
+
else
|
39
|
+
nil
|
40
|
+
end)
|
41
|
+
end
|
31
42
|
end
|
32
43
|
end
|
data/sig/core.rbs
ADDED
data/sig/fast_blank.rbs
ADDED
data/sig/{gloss.rbs → gls.rbs}
RENAMED
File without changes
|
data/sig/optparse.rbs
ADDED
data/sig/rubygems.rbs
ADDED
data/sig/yaml.rbs
ADDED
data/src/exe/gloss
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "gloss"
|
5
|
+
|
6
|
+
begin
|
7
|
+
Gloss::CLI.new(ARGV).run
|
8
|
+
rescue SystemExit
|
9
|
+
# raised by `abort` or `exit`; no op
|
10
|
+
rescue => e
|
11
|
+
abort <<~MSG
|
12
|
+
Unexpected error: #{e.class.name}
|
13
|
+
Message: #{e.message}
|
14
|
+
Trace:
|
15
|
+
#{e.backtrace.join("\n")}
|
16
|
+
|
17
|
+
This is probably a bug and may warrant a bug report at https://github.com/johansenja/gloss/issues
|
18
|
+
MSG
|
19
|
+
end
|
data/src/lib/gloss.gl
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "rbs"
|
2
|
+
require "json"
|
3
|
+
require "steep"
|
4
|
+
require "fast_blank"
|
5
|
+
|
6
|
+
require "gloss/version"
|
7
|
+
require "gloss/cli"
|
8
|
+
require "gloss/watcher"
|
9
|
+
require "gloss/type_checker"
|
10
|
+
require "gloss/parser"
|
11
|
+
require "gloss/initializer"
|
12
|
+
require "gloss/config"
|
13
|
+
require "gloss/writer"
|
14
|
+
require "gloss/source"
|
15
|
+
require "gloss/scope"
|
16
|
+
require "gloss/visitor"
|
17
|
+
require "gloss/errors"
|
18
|
+
require "gloss/logger"
|
19
|
+
require "gloss/prog_loader"
|
20
|
+
require "gloss/utils"
|
21
|
+
|
22
|
+
is_ci = ENV.fetch("CI") { false }
|
23
|
+
require "gls" unless is_ci # a bit of a hack for now
|
24
|
+
|
25
|
+
EMPTY_ARRAY = Array.new.freeze
|
26
|
+
EMPTY_HASH = {}.freeze
|
data/src/lib/gloss/cli.gl
CHANGED
@@ -12,44 +12,59 @@ module Gloss
|
|
12
12
|
files = @argv[1..-1]
|
13
13
|
err_msg = catch :error do
|
14
14
|
case command
|
15
|
-
when "watch"
|
16
|
-
files = files.map do |f|
|
17
|
-
path = Pathname.new(f).absolute? ? f : File.join(Dir.pwd, f)
|
18
|
-
if Pathname.new(path).exist?
|
19
|
-
path
|
20
|
-
else
|
21
|
-
throw :error, "Pathname #{f} does not exist"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
Watcher.new(files).watch
|
25
|
-
when "build"
|
26
|
-
(files.empty? ? Dir.glob("#{Config.src_dir}/**/*.gl") : files).each do |fp|
|
27
|
-
Gloss.logger.info "Building #{fp}"
|
28
|
-
content = File.read(fp)
|
29
|
-
tree_hash = Parser.new(content).run
|
30
|
-
type_checker = TypeChecker.new
|
31
|
-
rb_output = Builder.new(tree_hash, type_checker).run
|
32
|
-
type_checker.run(rb_output)
|
33
|
-
|
34
|
-
Gloss.logger.info "Writing #{fp}"
|
35
|
-
Writer.new(rb_output, fp).run
|
36
|
-
end
|
37
15
|
when "init"
|
38
16
|
force = false
|
39
17
|
OptionParser.new do |opt|
|
40
18
|
opt.on("--force", "-f") { force = true }
|
41
19
|
end.parse(@argv)
|
42
20
|
Initializer.new(force).run
|
21
|
+
when "version", "--version", "-v"
|
22
|
+
puts Gloss::VERSION
|
23
|
+
when "watch", "build"
|
24
|
+
type_checker = ProgLoader.new.run
|
25
|
+
if command == "watch"
|
26
|
+
files = files.map do |f|
|
27
|
+
path = Pathname.new(f).absolute? ? f : File.join(Dir.pwd, f)
|
28
|
+
if Pathname.new(path).exist?
|
29
|
+
path
|
30
|
+
else
|
31
|
+
throw :error, "Pathname #{f} does not exist"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
Watcher.new(files).watch
|
35
|
+
elsif command == "build"
|
36
|
+
entry_tree = Parser.new(File.read(Config.entrypoint)).run
|
37
|
+
Visitor.new(entry_tree, type_checker).run
|
38
|
+
files = Dir.glob("#{Config.src_dir}/**/*.gl") if files.empty?
|
39
|
+
files.each do |fp|
|
40
|
+
fp = File.absolute_path(fp)
|
41
|
+
preloaded_output = OUTPUT_BY_PATH.fetch(fp) { nil }
|
42
|
+
if preloaded_output
|
43
|
+
rb_output = preloaded_output
|
44
|
+
else
|
45
|
+
Gloss.logger.info "Building #{fp}"
|
46
|
+
content = File.read(fp)
|
47
|
+
tree_hash = Parser.new(content).run
|
48
|
+
rb_output = Visitor.new(tree_hash, type_checker).run
|
49
|
+
end
|
50
|
+
Gloss.logger.info "Type checking #{fp}"
|
51
|
+
type_checker.run(fp, rb_output)
|
52
|
+
end
|
53
|
+
# ensure all files are type checked before anything is written
|
54
|
+
files.each do |fp|
|
55
|
+
fp = File.absolute_path(fp)
|
56
|
+
rb_output = OUTPUT_BY_PATH.fetch(fp)
|
57
|
+
Gloss.logger.info "Writing #{fp}"
|
58
|
+
Writer.new(rb_output, fp).run
|
59
|
+
end
|
60
|
+
end
|
43
61
|
else
|
44
62
|
throw :error, "Gloss doesn't know how to #{command}"
|
45
63
|
end
|
46
64
|
nil
|
47
65
|
end
|
48
66
|
|
49
|
-
if err_msg
|
50
|
-
Gloss.logger.fatal err_msg
|
51
|
-
exit 1
|
52
|
-
end
|
67
|
+
abort err_msg if err_msg
|
53
68
|
end
|
54
69
|
end
|
55
70
|
end
|
data/src/lib/gloss/config.gl
CHANGED
data/src/lib/gloss/logger.gl
CHANGED
@@ -15,12 +15,7 @@ module Gloss
|
|
15
15
|
nil => nil,
|
16
16
|
"" => nil
|
17
17
|
}.fetch env_log_level
|
18
|
-
@logger = Logger.new(real_log_level ? STDOUT :
|
19
|
-
formatter = Logger::Formatter.new
|
20
|
-
@logger.formatter = proc do |severity, datetime, progname, msg|
|
21
|
-
formatter.call(severity, datetime, progname, msg)
|
22
|
-
end
|
23
|
-
@logger
|
18
|
+
@logger = Logger.new(real_log_level ? STDOUT : IO::NULL)
|
24
19
|
end
|
25
20
|
end
|
26
21
|
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require "rubygems/gem_runner"
|
2
|
+
|
3
|
+
module Gloss
|
4
|
+
OUTPUT_BY_PATH = Hash.new
|
5
|
+
|
6
|
+
class ProgLoader
|
7
|
+
def initialize
|
8
|
+
entrypoint = Config.entrypoint
|
9
|
+
if entrypoint == nil || entrypoint == ""
|
10
|
+
throw :error, "Entrypoint is not yet set in .gloss.yml"
|
11
|
+
end
|
12
|
+
@files_to_process = [
|
13
|
+
Utils.absolute_path(Config.entrypoint),
|
14
|
+
# __dir__ is typed as String? - but it shouldn't be nil here
|
15
|
+
Utils.absolute_path(File.join((__dir__||""), "..", "..", "sig", "core.rbs"))
|
16
|
+
]
|
17
|
+
@processed_files = Set.new
|
18
|
+
@type_checker = TypeChecker.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
@files_to_process.each do |path_string|
|
23
|
+
# currently steep would give an `unexpected jump` if next was used
|
24
|
+
unless @processed_files.member?(path_string) || OUTPUT_BY_PATH.[](path_string)
|
25
|
+
Gloss.logger.debug "Loading #{path_string}"
|
26
|
+
path = Utils.absolute_path(path_string)
|
27
|
+
file_contents = File.open(path).read
|
28
|
+
contents_tree = Parser.new(file_contents).run
|
29
|
+
on_new_file_referenced = proc do |pa, relative|
|
30
|
+
if relative
|
31
|
+
handle_require_relative pa
|
32
|
+
else
|
33
|
+
handle_require pa
|
34
|
+
end
|
35
|
+
end
|
36
|
+
OUTPUT_BY_PATH.[](path_string) = Visitor.new(contents_tree, @type_checker, on_new_file_referenced).run
|
37
|
+
@processed_files.add path_string
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@type_checker
|
42
|
+
end
|
43
|
+
|
44
|
+
STDLIB_TYPE_DEPENDENCIES = {
|
45
|
+
"yaml" => %w[pstore dbm],
|
46
|
+
"rbs" => %w[logger set tsort],
|
47
|
+
"logger" => %w[monitor],
|
48
|
+
}
|
49
|
+
|
50
|
+
private def handle_require(path)
|
51
|
+
if path.start_with? "."
|
52
|
+
base = File.join(Dir.pwd, path)
|
53
|
+
fp = base + ".gl"
|
54
|
+
if File.exist? fp
|
55
|
+
@files_to_process << fp
|
56
|
+
end
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
# look for .gl file if the "require" refers to the lib directory of current project dir
|
61
|
+
full = File.absolute_path("#{File.join(Config.src_dir, "lib", path)}.gl")
|
62
|
+
pathn = Pathname.new full
|
63
|
+
if pathn.file?
|
64
|
+
@files_to_process << pathn.to_s
|
65
|
+
else
|
66
|
+
# no .gl file available - .rbs file available?
|
67
|
+
# TODO: verify file is still actually requireable
|
68
|
+
pathn = Pathname.new("#{File.join(Dir.pwd, "sig", path)}.rbs")
|
69
|
+
gem_path = Utils.gem_path_for(path)
|
70
|
+
if gem_path
|
71
|
+
sig_files = Dir.glob(File.absolute_path(File.join(gem_path, "..", "..", "sig", "**", "*.rbs")))
|
72
|
+
if sig_files.length.positive?
|
73
|
+
sig_files.each do |fp|
|
74
|
+
@type_checker.load_sig_path fp
|
75
|
+
end
|
76
|
+
@processed_files.add path
|
77
|
+
rbs_type_deps = STDLIB_TYPE_DEPENDENCIES.fetch(path) { nil }
|
78
|
+
if rbs_type_deps
|
79
|
+
rbs_type_deps.each { |d| handle_require d }
|
80
|
+
end
|
81
|
+
return
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if pathn.file?
|
86
|
+
@type_checker.load_sig_path(pathn.to_s)
|
87
|
+
@processed_files.add pathn.to_s
|
88
|
+
else
|
89
|
+
rbs_stdlib_dir = File.absolute_path(File.join(@type_checker.rbs_gem_dir, "..", "..", "stdlib", path))
|
90
|
+
if Pathname.new(rbs_stdlib_dir).exist?
|
91
|
+
load_rbs_from_require_path(path)
|
92
|
+
rbs_type_deps = STDLIB_TYPE_DEPENDENCIES.fetch(path) { nil }
|
93
|
+
if rbs_type_deps
|
94
|
+
rbs_type_deps.each { |d| load_rbs_from_require_path d }
|
95
|
+
end
|
96
|
+
elsif Config.strict_require
|
97
|
+
throw :error, "Cannot resolve require path for #{path}"
|
98
|
+
else
|
99
|
+
Gloss.logger.debug "No path found for #{path}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private def handle_require_relative(path)
|
106
|
+
base = File.join(@filepath, "..", path)
|
107
|
+
pn : String? = nil
|
108
|
+
Gem.suffixes.each do |ext|
|
109
|
+
full = File.absolute_path(base + ext)
|
110
|
+
pn = full if File.exist?(full)
|
111
|
+
end
|
112
|
+
|
113
|
+
if pn
|
114
|
+
@files_to_process << pn unless @files_to_process.include? pn
|
115
|
+
elsif Config.strict_require
|
116
|
+
throw :error, "Cannot resolve require path for #{pn}"
|
117
|
+
else
|
118
|
+
Gloss.logger.debug "No path found for #{path}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private def rbs_stdlib_path_for(libr)
|
123
|
+
File.absolute_path(File.join(@type_checker.rbs_gem_dir, "..", "..", "stdlib", libr))
|
124
|
+
end
|
125
|
+
|
126
|
+
private def load_rbs_from_require_path(path)
|
127
|
+
Dir.glob(File.join(rbs_stdlib_path_for(path), "**", "*.rbs")).each do |fp|
|
128
|
+
@type_checker.load_sig_path(fp)
|
129
|
+
@processed_files.add fp
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/src/lib/gloss/scope.gl
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
|
2
|
-
require "pry-byebug"
|
1
|
+
require "set"
|
3
2
|
|
4
3
|
module Gloss
|
5
4
|
class TypeChecker
|
6
5
|
Project = Struct.new :targets
|
7
6
|
|
8
|
-
attr_reader :steep_target, :top_level_decls
|
7
|
+
attr_reader :steep_target, :top_level_decls, :env, :rbs_gem_dir
|
9
8
|
|
10
9
|
def initialize
|
11
10
|
@steep_target = Steep::Project::Target.new(
|
@@ -13,16 +12,23 @@ module Gloss
|
|
13
12
|
options: Steep::Project::Options.new.tap do |o|
|
14
13
|
o.allow_unknown_constant_assignment = true
|
15
14
|
end,
|
16
|
-
source_patterns: ["
|
15
|
+
source_patterns: ["**/*.rb"],
|
17
16
|
ignore_patterns: Array.new,
|
18
17
|
signature_patterns: ["sig"]
|
19
18
|
)
|
20
|
-
@top_level_decls =
|
19
|
+
@top_level_decls = Set.new
|
20
|
+
@rbs_gem_dir = Utils.gem_path_for("rbs")
|
21
|
+
env_loader = RBS::EnvironmentLoader.new
|
22
|
+
@env = RBS::Environment.from_loader(env_loader)
|
23
|
+
project = Steep::Project.new(steepfile_path: Pathname.new(Config.src_dir).realpath)
|
24
|
+
project.targets << @steep_target
|
25
|
+
loader = Steep::Project::FileLoader.new(project: project)
|
26
|
+
#loader.load_signatures
|
21
27
|
end
|
22
28
|
|
23
|
-
def run(rb_str)
|
29
|
+
def run(filepath, rb_str)
|
24
30
|
begin
|
25
|
-
valid_types = check_types rb_str
|
31
|
+
valid_types = check_types filepath, rb_str
|
26
32
|
rescue ParseError => e
|
27
33
|
throw :error, ""
|
28
34
|
rescue => e
|
@@ -38,8 +44,8 @@ module Gloss
|
|
38
44
|
"Invalid method body type - expected: #{e.expected}, actual: #{e.actual}"
|
39
45
|
when Steep::Diagnostic::Ruby::IncompatibleArguments
|
40
46
|
<<-ERR
|
41
|
-
Invalid argmuents - method type: #{e.
|
42
|
-
method name: #{e.
|
47
|
+
Invalid argmuents - method type: #{e.method_types.first}
|
48
|
+
method name: #{e.method_name}
|
43
49
|
ERR
|
44
50
|
when Steep::Diagnostic::Ruby::ReturnTypeMismatch
|
45
51
|
"Invalid return type - expected: #{e.expected}, actual: #{e.actual}"
|
@@ -48,7 +54,7 @@ module Gloss
|
|
48
54
|
when Steep::Diagnostic::Ruby::UnexpectedBlockGiven
|
49
55
|
"Unexpected block given"
|
50
56
|
else
|
51
|
-
e.inspect
|
57
|
+
"#{e.header_line}\n#{e.inspect}"
|
52
58
|
end
|
53
59
|
}.join("\n")
|
54
60
|
throw :error, errors
|
@@ -57,31 +63,38 @@ module Gloss
|
|
57
63
|
true
|
58
64
|
end
|
59
65
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
loader = Steep::Project::FileLoader.new(project: project)
|
66
|
-
loader.load_signatures
|
66
|
+
def ready_for_checking!
|
67
|
+
@top_level_decls.each do |decl|
|
68
|
+
@env << decl
|
69
|
+
end
|
70
|
+
@env = @env.resolve_type_names
|
67
71
|
|
68
|
-
@steep_target.
|
72
|
+
@steep_target.instance_variable_set("@environment", @env)
|
73
|
+
end
|
69
74
|
|
70
|
-
|
71
|
-
|
72
|
-
end
|
73
|
-
env = env.resolve_type_names
|
75
|
+
def check_types(filepath, rb_str)
|
76
|
+
@steep_target.add_source(filepath, rb_str)
|
74
77
|
|
75
|
-
|
78
|
+
ready_for_checking!
|
76
79
|
|
77
80
|
@steep_target.type_check
|
78
81
|
|
79
82
|
if @steep_target.status.is_a? Steep::Project::Target::SignatureErrorStatus
|
80
83
|
throw :error, @steep_target.status.errors.map { |e|
|
84
|
+
msg = case e
|
85
|
+
when Steep::Diagnostic::Signature::UnknownTypeName
|
86
|
+
"Unknown type name: #{e.name.name} (#{e.location.source[/^.*$/]})"
|
87
|
+
when Steep::Diagnostic::Signature::InvalidTypeApplication
|
88
|
+
"Invalid type application: #{e.header_line}"
|
89
|
+
when Steep::Diagnostic::Signature::DuplicatedMethodDefinition
|
90
|
+
"Duplicated method: #{e.header_line}"
|
91
|
+
else
|
92
|
+
e.header_line
|
93
|
+
end
|
81
94
|
<<~MSG
|
82
95
|
SignatureSyntaxError:
|
83
96
|
Location: #{e.location}
|
84
|
-
Message: "#{
|
97
|
+
Message: "#{msg}"
|
85
98
|
MSG
|
86
99
|
}.join("\n")
|
87
100
|
end
|
@@ -97,5 +110,10 @@ module Gloss
|
|
97
110
|
@steep_target.no_error? &&
|
98
111
|
@steep_target.errors.empty?
|
99
112
|
end
|
113
|
+
|
114
|
+
def load_sig_path(path : String)
|
115
|
+
Gloss.logger.debug "Loading signature file for #{path}"
|
116
|
+
@steep_target.add_signature path, File.open(path).read
|
117
|
+
end
|
100
118
|
end
|
101
119
|
end
|