crystalruby 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/README.md +389 -193
- data/Rakefile +4 -3
- data/crystalruby.gemspec +2 -2
- data/exe/crystalruby +1 -0
- data/lib/crystalruby/adapter.rb +131 -65
- data/lib/crystalruby/arc_mutex.rb +47 -0
- data/lib/crystalruby/compilation.rb +32 -3
- data/lib/crystalruby/config.rb +41 -37
- data/lib/crystalruby/function.rb +211 -68
- data/lib/crystalruby/library.rb +153 -48
- data/lib/crystalruby/reactor.rb +40 -23
- data/lib/crystalruby/source_reader.rb +86 -0
- data/lib/crystalruby/template.rb +16 -5
- data/lib/crystalruby/templates/function.cr +11 -10
- data/lib/crystalruby/templates/index.cr +53 -66
- data/lib/crystalruby/templates/inline_chunk.cr +1 -1
- data/lib/crystalruby/templates/ruby_interface.cr +34 -0
- data/lib/crystalruby/templates/top_level_function.cr +62 -0
- data/lib/crystalruby/templates/top_level_ruby_interface.cr +33 -0
- data/lib/crystalruby/typebuilder.rb +11 -55
- data/lib/crystalruby/typemaps.rb +92 -67
- data/lib/crystalruby/types/concerns/allocator.rb +80 -0
- data/lib/crystalruby/types/fixed_width/named_tuple.cr +80 -0
- data/lib/crystalruby/types/fixed_width/named_tuple.rb +86 -0
- data/lib/crystalruby/types/fixed_width/proc.cr +45 -0
- data/lib/crystalruby/types/fixed_width/proc.rb +79 -0
- data/lib/crystalruby/types/fixed_width/tagged_union.cr +53 -0
- data/lib/crystalruby/types/fixed_width/tagged_union.rb +109 -0
- data/lib/crystalruby/types/fixed_width/tuple.cr +82 -0
- data/lib/crystalruby/types/fixed_width/tuple.rb +92 -0
- data/lib/crystalruby/types/fixed_width.cr +138 -0
- data/lib/crystalruby/types/fixed_width.rb +205 -0
- data/lib/crystalruby/types/primitive.cr +21 -0
- data/lib/crystalruby/types/primitive.rb +117 -0
- data/lib/crystalruby/types/primitive_types/bool.cr +34 -0
- data/lib/crystalruby/types/primitive_types/bool.rb +11 -0
- data/lib/crystalruby/types/primitive_types/nil.cr +35 -0
- data/lib/crystalruby/types/primitive_types/nil.rb +16 -0
- data/lib/crystalruby/types/primitive_types/numbers.cr +37 -0
- data/lib/crystalruby/types/primitive_types/numbers.rb +28 -0
- data/lib/crystalruby/types/primitive_types/symbol.cr +55 -0
- data/lib/crystalruby/types/primitive_types/symbol.rb +35 -0
- data/lib/crystalruby/types/primitive_types/time.cr +35 -0
- data/lib/crystalruby/types/primitive_types/time.rb +25 -0
- data/lib/crystalruby/types/type.cr +64 -0
- data/lib/crystalruby/types/type.rb +239 -30
- data/lib/crystalruby/types/variable_width/array.cr +74 -0
- data/lib/crystalruby/types/variable_width/array.rb +88 -0
- data/lib/crystalruby/types/variable_width/hash.cr +146 -0
- data/lib/crystalruby/types/variable_width/hash.rb +117 -0
- data/lib/crystalruby/types/variable_width/string.cr +36 -0
- data/lib/crystalruby/types/variable_width/string.rb +18 -0
- data/lib/crystalruby/types/variable_width.cr +23 -0
- data/lib/crystalruby/types/variable_width.rb +46 -0
- data/lib/crystalruby/types.rb +32 -13
- data/lib/crystalruby/version.rb +2 -2
- data/lib/crystalruby.rb +13 -6
- metadata +41 -19
- data/lib/crystalruby/types/array.rb +0 -15
- data/lib/crystalruby/types/bool.rb +0 -3
- data/lib/crystalruby/types/hash.rb +0 -17
- data/lib/crystalruby/types/named_tuple.rb +0 -28
- data/lib/crystalruby/types/nil.rb +0 -3
- data/lib/crystalruby/types/numbers.rb +0 -5
- data/lib/crystalruby/types/string.rb +0 -3
- data/lib/crystalruby/types/symbol.rb +0 -3
- data/lib/crystalruby/types/time.rb +0 -8
- data/lib/crystalruby/types/tuple.rb +0 -17
- data/lib/crystalruby/types/type_serializer/json.rb +0 -41
- data/lib/crystalruby/types/type_serializer.rb +0 -37
- data/lib/crystalruby/types/typedef.rb +0 -57
- data/lib/crystalruby/types/union_type.rb +0 -43
- data/lib/module.rb +0 -3
data/Rakefile
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
|
-
require "minitest/test_task"
|
5
|
-
|
6
|
-
Minitest::TestTask.create
|
7
4
|
|
8
5
|
require "rubocop/rake_task"
|
9
6
|
|
10
7
|
RuboCop::RakeTask.new
|
11
8
|
|
12
9
|
task default: %i[test rubocop]
|
10
|
+
|
11
|
+
task :test do
|
12
|
+
require_relative "test/test_all"
|
13
|
+
end
|
data/crystalruby.gemspec
CHANGED
@@ -4,7 +4,7 @@ require_relative "lib/crystalruby/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "crystalruby"
|
7
|
-
spec.version =
|
7
|
+
spec.version = CrystalRuby::VERSION
|
8
8
|
spec.authors = ["Wouter Coppieters"]
|
9
9
|
spec.email = ["wc@pico.net.nz"]
|
10
10
|
|
@@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.add_dependency "digest"
|
36
36
|
spec.add_dependency "ffi"
|
37
37
|
spec.add_dependency "fileutils"
|
38
|
-
spec.add_dependency "
|
38
|
+
spec.add_dependency "syntax_tree"
|
39
39
|
# For more information and examples about making a new gem, check out our
|
40
40
|
# guide at: https://bundler.io/guides/creating_gem.html
|
41
41
|
end
|
data/exe/crystalruby
CHANGED
data/lib/crystalruby/adapter.rb
CHANGED
@@ -8,16 +8,16 @@ module CrystalRuby
|
|
8
8
|
#
|
9
9
|
# E.g.
|
10
10
|
#
|
11
|
-
# crystalize
|
12
|
-
# def add(a, b)
|
11
|
+
# crystalize :int32
|
12
|
+
# def add(a: :int32, b: :int32)
|
13
13
|
# a + b
|
14
14
|
# end
|
15
15
|
#
|
16
16
|
# Pass `raw: true` to pass Raw crystal code to the compiler as a string instead.
|
17
17
|
# (Useful for cases where the Crystal method body is not valid Ruby)
|
18
18
|
# E.g.
|
19
|
-
# crystalize
|
20
|
-
# def add(a, b)
|
19
|
+
# crystalize :int32, raw: true
|
20
|
+
# def add(a: :int32, b: :int32)
|
21
21
|
# <<~CRYSTAL
|
22
22
|
# a + b
|
23
23
|
# CRYSTAL
|
@@ -30,73 +30,135 @@ module CrystalRuby
|
|
30
30
|
#
|
31
31
|
# Pass lib: "name_of_lib" to compile Crystal code into several distinct libraries.
|
32
32
|
# This can help keep compilation times low, by packaging your Crystal code into separate shared objects.
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
33
|
+
# @param returns The return type of the method. Optional (defaults to :void).
|
34
|
+
# @param [Hash] options The options hash.
|
35
|
+
# @option options [Boolean] :raw (false) Pass raw Crystal code to the compiler as a string.
|
36
|
+
# @option options [Boolean] :async (false) Mark the method as async (allows multiplexing).
|
37
|
+
# @option options [String] :lib ("crystalruby") The name of the library to compile the Crystal code into.
|
38
|
+
# @option options [Proc] :block An optional wrapper Ruby block that wraps around any invocations of the crystal code
|
39
|
+
def crystalize( returns=:void, raw: false, async: false, lib: "crystalruby", &block)
|
40
|
+
(self == TOPLEVEL_BINDING.receiver ? Object : self).instance_eval do
|
41
|
+
@crystalize_next = {
|
42
|
+
raw: raw,
|
43
|
+
async: async,
|
44
|
+
returns: returns,
|
45
|
+
block: block,
|
46
|
+
lib: lib
|
47
|
+
}
|
48
|
+
end
|
46
49
|
end
|
47
50
|
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
51
|
+
# Exposes a Ruby method to one or more Crystal libraries.
|
52
|
+
# Type annotations follow the same rules as the `crystalize` method, but are
|
53
|
+
# applied in reverse.
|
54
|
+
# @param returns The return type of the method. Optional (defaults to :void).
|
55
|
+
# @param [Hash] options The options hash.
|
56
|
+
# @option options [Boolean] :raw (false) Pass raw Crystal code to the compiler as a string.
|
57
|
+
# @option options [String] :libs (["crystalruby"]) The name of the Crystal librarie(s) to expose the Ruby code to.
|
58
|
+
def expose_to_crystal( returns=:void, libs: ["crystalruby"])
|
59
|
+
(self == TOPLEVEL_BINDING.receiver ? Object : self).instance_eval do
|
60
|
+
@expose_next_to_crystal = {
|
61
|
+
returns: returns,
|
62
|
+
libs: libs
|
63
|
+
}
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
# Define a shard dependency
|
68
|
+
# This dependency will be automatically injected into the shard.yml file for
|
69
|
+
# the given library and installed upon compile if it is not already installed.
|
70
|
+
def shard(shard_name, lib: 'crystalruby', **opts)
|
71
|
+
CrystalRuby::Library[lib].require_shard(shard_name, **opts)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Use this method to define inline Crystal code that does not need to be bound to a Ruby method.
|
75
|
+
# This is useful for defining classes, modules, performing set-up tasks etc.
|
76
|
+
# See: docs for .crystalize to understand the `raw` and `lib` parameters.
|
77
|
+
def crystal(raw: false, lib: "crystalruby", &block)
|
78
|
+
inline_crystal_body = respond_to?(:name) ? Template::InlineChunk.render(
|
79
|
+
{
|
80
|
+
module_name: name,
|
81
|
+
body: SourceReader.extract_source_from_proc(block, raw: raw),
|
82
|
+
mod_or_class: self.kind_of?(Class) && self < Types::Type ? "class" : "module",
|
83
|
+
superclass: self.kind_of?(Class) && self < Types::Type ? "< #{self.crystal_supertype}" : ""
|
84
|
+
}) :
|
85
|
+
SourceReader.extract_source_from_proc(block, raw: raw)
|
86
|
+
|
87
|
+
CrystalRuby::Library[lib].crystalize_chunk(
|
88
|
+
self,
|
89
|
+
Digest::MD5.hexdigest(inline_crystal_body),
|
90
|
+
inline_crystal_body
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# This method provides a useful DSL for defining Crystal types in pure Ruby
|
96
|
+
# MyType = CRType{ Int32 | Hash(String, Array(Bool) | Float65 | Nil) }
|
97
|
+
# @param [Proc] block The block within which we build the type definition.
|
98
|
+
def CRType(&block)
|
99
|
+
TypeBuilder.build_from_source(block, context: self)
|
70
100
|
end
|
71
101
|
|
102
|
+
private
|
103
|
+
|
72
104
|
# We trigger attaching of crystalized instance methods here.
|
73
105
|
# If a method is added after a crystalize annotation we assume it's the target of the crystalize annotation.
|
106
|
+
# @param [Symbol] method_name The name of the method being added.
|
74
107
|
def method_added(method_name)
|
75
|
-
define_crystalized_method(
|
108
|
+
define_crystalized_method(instance_method(method_name)) if should_crystalize_next?
|
109
|
+
expose_ruby_method_to_crystal(instance_method(method_name)) if should_expose_next?
|
76
110
|
super
|
77
111
|
end
|
78
112
|
|
79
113
|
# We trigger attaching of crystalized class methods here.
|
80
114
|
# If a method is added after a crystalize annotation we assume it's the target of the crystalize annotation.
|
115
|
+
# @note This method is called when a method is added to the singleton class of the object.
|
116
|
+
# @param [Symbol] method_name The name of the method being added.
|
81
117
|
def singleton_method_added(method_name)
|
82
|
-
define_crystalized_method(
|
118
|
+
define_crystalized_method(singleton_method(method_name)) if should_crystalize_next?
|
119
|
+
expose_ruby_method_to_crystal(singleton_method(method_name)) if should_expose_next?
|
83
120
|
super
|
84
121
|
end
|
85
122
|
|
86
|
-
#
|
87
|
-
#
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
123
|
+
# Helper method to determine if the next method added should be crystalized.
|
124
|
+
# @return [Boolean] True if the next method added should be crystalized.
|
125
|
+
def should_crystalize_next?
|
126
|
+
defined?(@crystalize_next) && @crystalize_next
|
127
|
+
end
|
128
|
+
|
129
|
+
# Helper method to determine if the next method added should be exposed to Crystal libraries.
|
130
|
+
# @return [Boolean] True if the next method added should be exposed.
|
131
|
+
def should_expose_next?
|
132
|
+
defined?(@expose_next_to_crystal) && @expose_next_to_crystal
|
133
|
+
end
|
134
|
+
|
135
|
+
# This is where we extract the Ruby method metadata and invoke the Crystal::Library functionality
|
136
|
+
# to compile a stub for the Ruby method into the Crystal library.
|
137
|
+
def expose_ruby_method_to_crystal(method)
|
138
|
+
returns, libs = @expose_next_to_crystal.values_at(:returns, :libs)
|
139
|
+
@expose_next_to_crystal = nil
|
140
|
+
|
141
|
+
args, source = SourceReader.extract_args_and_source_from_method(method)
|
142
|
+
returns = args.delete(:returns) if args[:returns] && returns == :void
|
143
|
+
args[:__yield_to] = args.delete(:yield) if args[:yield]
|
144
|
+
src = <<~RUBY
|
145
|
+
def #{method.name} (#{(args.keys - [:__yield_to]).join(", ")})
|
146
|
+
#{source}
|
147
|
+
end
|
148
|
+
RUBY
|
149
|
+
|
150
|
+
owner = method.owner.singleton_class? ? method.owner.attached_object : method.owner
|
151
|
+
owner.class_eval(src)
|
152
|
+
owner.instance_eval(src) unless method.kind_of?(UnboundMethod) && method.owner.ancestors.include?(CrystalRuby::Types::Type)
|
153
|
+
method = owner.send(method.kind_of?(UnboundMethod) ? :instance_method : :method, method.name)
|
154
|
+
|
155
|
+
libs.each do |lib|
|
156
|
+
CrystalRuby::Library[lib].expose_method(
|
157
|
+
method,
|
158
|
+
args,
|
159
|
+
returns,
|
160
|
+
)
|
161
|
+
end
|
100
162
|
end
|
101
163
|
|
102
164
|
# We attach crystalized class methods here.
|
@@ -105,29 +167,33 @@ module CrystalRuby
|
|
105
167
|
# - Overwriting the method and class methods by the same name in the caller.
|
106
168
|
# - Lazily triggering compilation and attachment of the Ruby method to the Crystal code.
|
107
169
|
# - We also optionally prepend a block (if given) to the owner, to allow Ruby code to wrap around Crystal code.
|
108
|
-
|
109
|
-
|
170
|
+
# @param [Symbol] method_name The name of the method being added.
|
171
|
+
# @param [UnboundMethod] method The method being added.
|
172
|
+
def define_crystalized_method(method)
|
173
|
+
CrystalRuby.log_debug("Defining crystalized method #{name}.#{method.name}")
|
110
174
|
|
111
|
-
|
175
|
+
returns, block, async, lib, raw = @crystalize_next.values_at(:returns, :block, :async, :lib, :raw)
|
112
176
|
@crystalize_next = nil
|
113
177
|
|
178
|
+
args, source = SourceReader.extract_args_and_source_from_method(method, raw: raw)
|
179
|
+
|
180
|
+
# We can safely claim the `yield` argument name for typing the yielded block
|
181
|
+
# because this is an illegal identifier in Crystal anyway.
|
182
|
+
args[:__yield_to] = args.delete(:yield) if args[:yield]
|
183
|
+
returns = args.delete(:returns) if args[:returns] && returns == :void
|
184
|
+
|
114
185
|
CrystalRuby::Library[lib].crystalize_method(
|
115
186
|
method,
|
116
187
|
args,
|
117
188
|
returns,
|
118
|
-
|
189
|
+
source,
|
119
190
|
async,
|
120
191
|
&block
|
121
192
|
)
|
122
193
|
end
|
123
|
-
|
124
|
-
# Extract Ruby source to serve as Crystal code directly.
|
125
|
-
# If it's a raw method, we'll strip the string delimiters at either end of the definition.
|
126
|
-
# We need to clear the MethodSource cache here to allow for code reloading.
|
127
|
-
def extract_source(method_or_block, raw: false)
|
128
|
-
method_or_block.source.lines[raw ? 2...-2 : 1...-1].join("\n").tap do
|
129
|
-
MethodSource.instance_variable_get(:@lines_for_file).delete(method_or_block.source_location[0])
|
130
|
-
end
|
131
|
-
end
|
132
194
|
end
|
133
195
|
end
|
196
|
+
|
197
|
+
Module.prepend(CrystalRuby::Adapter)
|
198
|
+
BasicObject.prepend(CrystalRuby::Adapter)
|
199
|
+
BasicObject.singleton_class.prepend(CrystalRuby::Adapter)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module CrystalRuby
|
2
|
+
module LibC
|
3
|
+
extend FFI::Library
|
4
|
+
ffi_lib "c"
|
5
|
+
class PThreadMutexT < FFI::Struct
|
6
|
+
layout :__align, :int64, :__size, :char, 40
|
7
|
+
end
|
8
|
+
|
9
|
+
attach_function :pthread_mutex_init, [PThreadMutexT.by_ref, :pointer], :int
|
10
|
+
attach_function :pthread_mutex_lock, [PThreadMutexT.by_ref], :int
|
11
|
+
attach_function :pthread_mutex_unlock, [PThreadMutexT.by_ref], :int
|
12
|
+
end
|
13
|
+
|
14
|
+
class ArcMutex
|
15
|
+
def phtread_mutex
|
16
|
+
@phtread_mutex ||= init_mutex!
|
17
|
+
end
|
18
|
+
|
19
|
+
def synchronize
|
20
|
+
lock
|
21
|
+
yield
|
22
|
+
unlock
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_ptr
|
26
|
+
phtread_mutex.pointer
|
27
|
+
end
|
28
|
+
|
29
|
+
def init_mutex!
|
30
|
+
mutex = LibC::PThreadMutexT.new
|
31
|
+
res = LibC.pthread_mutex_init(mutex, nil)
|
32
|
+
raise "Failed to initialize mutex" unless res.zero?
|
33
|
+
|
34
|
+
mutex
|
35
|
+
end
|
36
|
+
|
37
|
+
def lock
|
38
|
+
res = LibC.pthread_mutex_lock(phtread_mutex)
|
39
|
+
raise "Failed to lock mutex" unless res.zero?
|
40
|
+
end
|
41
|
+
|
42
|
+
def unlock
|
43
|
+
res = LibC.pthread_mutex_unlock(phtread_mutex)
|
44
|
+
raise "Failed to unlock mutex" unless res.zero?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -1,10 +1,18 @@
|
|
1
1
|
require "tmpdir"
|
2
2
|
require "shellwords"
|
3
|
+
require "timeout"
|
3
4
|
|
4
5
|
module CrystalRuby
|
5
6
|
module Compilation
|
6
7
|
class CompilationFailedError < StandardError; end
|
7
8
|
|
9
|
+
# Simple wrapper around invocation of the Crystal compiler
|
10
|
+
# @param src [String] path to the source file
|
11
|
+
# @param lib [String] path to the library file
|
12
|
+
# @param verbose [Boolean] whether to print the compiler output
|
13
|
+
# @param debug [Boolean] whether to compile in debug mode
|
14
|
+
# @raise [CompilationFailedError] if the compilation fails
|
15
|
+
# @return [void]
|
8
16
|
def self.compile!(
|
9
17
|
src:,
|
10
18
|
lib:,
|
@@ -13,15 +21,36 @@ module CrystalRuby
|
|
13
21
|
)
|
14
22
|
compile_command = build_compile_command(verbose: verbose, debug: debug, lib: lib, src: src)
|
15
23
|
CrystalRuby.log_debug "Compiling Crystal code #{verbose ? ": #{compile_command}" : ""}"
|
16
|
-
|
24
|
+
IO.popen(compile_command, chdir: File.dirname(src), &:read)
|
25
|
+
raise CompilationFailedError, "Compilation failed in #{src}" unless $?&.success?
|
17
26
|
end
|
18
27
|
|
28
|
+
# Builds the command to compile the Crystal source file
|
29
|
+
# @param verbose [Boolean] whether to print the compiler output
|
30
|
+
# @param debug [Boolean] whether to compile in debug mode
|
31
|
+
# @param lib [String] path to the library file
|
32
|
+
# @param src [String] path to the source file
|
33
|
+
# @return [String] the command to compile the Crystal source file
|
19
34
|
def self.build_compile_command(verbose:, debug:, lib:, src:)
|
20
35
|
verbose_flag = verbose ? "--verbose --progress" : ""
|
21
36
|
debug_flag = debug ? "" : "--release --no-debug"
|
22
37
|
redirect_output = " > /dev/null " unless verbose
|
23
|
-
lib, src
|
24
|
-
%(
|
38
|
+
lib, src = [lib, src].map(&Shellwords.method(:escape))
|
39
|
+
%(crystal build #{verbose_flag} #{debug_flag} --single-module --link-flags "-shared" -o #{lib} #{src}#{redirect_output})
|
40
|
+
end
|
41
|
+
|
42
|
+
# Trigger the shards install command in the given source directory
|
43
|
+
def self.install_shards!(src_dir)
|
44
|
+
CrystalRuby.log_debug "Running shards install inside #{src_dir}"
|
45
|
+
output = IO.popen("shards update", chdir: src_dir, &:read)
|
46
|
+
CrystalRuby.log_debug output if CrystalRuby.config.verbose
|
47
|
+
raise CompilationFailedError, "Shards install failed" unless $?&.success?
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return whether the shards check command succeeds in the given source directory
|
51
|
+
def self.shard_check?(src_dir)
|
52
|
+
IO.popen("shards check", chdir: src_dir, &:read)
|
53
|
+
$?&.success?
|
25
54
|
end
|
26
55
|
end
|
27
56
|
end
|
data/lib/crystalruby/config.rb
CHANGED
@@ -17,59 +17,63 @@ module CrystalRuby
|
|
17
17
|
# - CrystalRuby.configure block
|
18
18
|
class Configuration
|
19
19
|
include Singleton
|
20
|
-
attr_accessor :debug, :verbose, :logger, :colorize_log_output,
|
20
|
+
attr_accessor :debug, :verbose, :logger, :colorize_log_output,
|
21
|
+
:single_thread_mode, :crystal_missing_ignore
|
21
22
|
|
22
23
|
def initialize
|
23
|
-
@debug = true
|
24
24
|
@paths_cache = {}
|
25
|
-
config =
|
25
|
+
config = read_config || {}
|
26
|
+
@crystal_src_dir = config.fetch("crystal_src_dir", "./crystalruby")
|
27
|
+
@crystal_codegen_dir = config.fetch("crystal_codegen_dir", "generated")
|
28
|
+
@crystal_project_root = config.fetch("crystal_project_root", Pathname.pwd)
|
29
|
+
@crystal_missing_ignore = config.fetch("crystal_missing_ignore", false)
|
30
|
+
@debug = config.fetch("debug", false)
|
31
|
+
@verbose = config.fetch("verbose", false)
|
32
|
+
@single_thread_mode = config.fetch("single_thread_mode", false)
|
33
|
+
@colorize_log_output = config.fetch("colorize_log_output", false)
|
34
|
+
@log_level = config.fetch("log_level", ENV.fetch("CRYSTALRUBY_LOG_LEVEL", "info"))
|
35
|
+
@logger = Logger.new($stdout)
|
36
|
+
@logger.level = Logger.const_get(@log_level.to_s.upcase)
|
37
|
+
end
|
38
|
+
|
39
|
+
def file_config
|
40
|
+
@file_config ||= File.exist?("crystalruby.yaml") && begin
|
26
41
|
YAML.safe_load(IO.read("crystalruby.yaml"))
|
27
42
|
rescue StandardError
|
28
43
|
nil
|
29
44
|
end || {}
|
30
|
-
@crystal_src_dir = config.fetch("crystal_src_dir", "./crystalruby")
|
31
|
-
@crystal_codegen_dir = config.fetch("crystal_codegen_dir", "generated")
|
32
|
-
@crystal_project_root = config.fetch("crystal_project_root", Pathname.pwd)
|
33
|
-
@debug = config.fetch("debug", true)
|
34
|
-
@verbose = config.fetch("verbose", false)
|
35
|
-
@single_thread_mode = config.fetch("single_thread_mode", false)
|
36
|
-
@colorize_log_output = config.fetch("colorize_log_output", false)
|
37
|
-
@log_level = config.fetch("log_level", ENV.fetch("CRYSTALRUBY_LOG_LEVEL", "info"))
|
38
|
-
@logger = Logger.new(STDOUT)
|
39
|
-
@logger.level = Logger.const_get(@log_level.to_s.upcase)
|
40
45
|
end
|
41
46
|
|
42
|
-
|
43
|
-
|
44
|
-
|
47
|
+
def self.define_directory_accessors!(parent, directory_node)
|
48
|
+
case directory_node
|
49
|
+
when Array then directory_node.each(&method(:define_directory_accessors!).curry[parent])
|
50
|
+
when Hash
|
51
|
+
directory_node.each do |par, children|
|
52
|
+
define_directory_accessors!(parent, par)
|
53
|
+
define_directory_accessors!(par, children)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
define_method(directory_node) do
|
57
|
+
@paths_cache[directory_node] ||= Pathname.new(instance_variable_get(:"@#{directory_node}"))
|
58
|
+
end
|
59
|
+
define_method("#{directory_node}_abs") do
|
60
|
+
@paths_cache["#{directory_node}_abs"] ||= parent ? send("#{parent}_abs") / Pathname.new(instance_variable_get(:"@#{directory_node}")) : send(directory_node)
|
61
|
+
end
|
45
62
|
end
|
46
63
|
end
|
47
64
|
|
48
|
-
|
49
|
-
abs_method_name = "#{method_name}_abs"
|
50
|
-
define_method(abs_method_name) do
|
51
|
-
@paths_cache[abs_method_name] ||= crystal_src_dir_abs / instance_variable_get(:"@#{method_name}")
|
52
|
-
end
|
65
|
+
define_directory_accessors!(nil, { crystal_project_root: { crystal_src_dir: [:crystal_codegen_dir] } })
|
53
66
|
|
54
|
-
|
55
|
-
|
56
|
-
end
|
67
|
+
def log_level=(level)
|
68
|
+
@logger.level = Logger.const_get(@log_level = level.to_s.upcase)
|
57
69
|
end
|
58
70
|
|
59
|
-
|
60
|
-
abs_method_name = "#{method_name}_abs"
|
61
|
-
define_method(abs_method_name) do
|
62
|
-
@paths_cache[abs_method_name] ||= crystal_project_root / instance_variable_get(:"@#{method_name}")
|
63
|
-
end
|
71
|
+
private
|
64
72
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
def log_level=(level)
|
71
|
-
@log_level = level
|
72
|
-
@logger.level = Logger.const_get(level.to_s.upcase)
|
73
|
+
def read_config
|
74
|
+
YAML.safe_load(IO.read("crystalruby.yaml"))
|
75
|
+
rescue
|
76
|
+
{}
|
73
77
|
end
|
74
78
|
end
|
75
79
|
|