crystalruby 0.2.3 → 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.
- 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
|
|