crystalruby 0.1.1 → 0.1.3

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.
@@ -0,0 +1,66 @@
1
+ module CrystalRuby
2
+ module Types
3
+ class Type
4
+ attr_accessor :name, :error, :inner_types, :inner_keys, :accept_if, :convert
5
+
6
+ def initialize(name, error: nil, inner_types: nil, inner_keys: nil, accept_if: [], &convert)
7
+ self.name = name
8
+ self.error = error
9
+ self.inner_types = inner_types
10
+ self.inner_keys = inner_keys
11
+ self.accept_if = accept_if
12
+ self.convert = convert
13
+ end
14
+
15
+ def union_types
16
+ [self]
17
+ end
18
+
19
+ def valid?
20
+ !error
21
+ end
22
+
23
+ def |(other)
24
+ raise "Cannot union non-crystal type #{other}" unless other.is_a?(Type) || (
25
+ other.is_a?(Class) && other.ancestors.include?(Typedef)
26
+ )
27
+
28
+ UnionType.new(*union_types, *other.union_types)
29
+ end
30
+
31
+ def type_expr
32
+ inspect
33
+ end
34
+
35
+ def inspect
36
+ if !inner_types
37
+ name
38
+ elsif !inner_keys
39
+ "#{name}(#{inner_types.map(&:inspect).join(", ")})"
40
+ else
41
+ "#{name}(#{inner_keys.zip(inner_types).map { |k, v| "#{k}: #{v.inspect}" }.join(", ")})"
42
+ end
43
+ end
44
+
45
+ def interprets?(raw)
46
+ accept_if.any? { |type| raw.is_a?(type) }
47
+ end
48
+
49
+ def interpret!(raw)
50
+ if interprets?(raw)
51
+ convert ? convert.call(raw) : raw
52
+ else
53
+ raise "Invalid deserialized value #{raw} for type #{inspect}"
54
+ end
55
+ end
56
+
57
+ def self.validate!(type)
58
+ unless type.is_a?(Types::Type) || (type.is_a?(Class) && type.ancestors.include?(Types::Typedef))
59
+ raise "Result #{type} is not a valid CrystalRuby type"
60
+ end
61
+
62
+ raise "Invalid type: #{type.error}" unless type.valid?
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,40 @@
1
+ require 'json'
2
+
3
+ module CrystalRuby::Types
4
+ class TypeSerializer
5
+ class JSON < TypeSerializer
6
+
7
+ def lib_type
8
+ "UInt8*"
9
+ end
10
+
11
+ def crystal_type
12
+ type_expr
13
+ end
14
+
15
+ def error_value
16
+ '"{}".to_unsafe'
17
+ end
18
+
19
+ def ffi_type
20
+ :string
21
+ end
22
+
23
+ def prepare_argument(arg)
24
+ arg.to_json
25
+ end
26
+
27
+ def prepare_retval(retval)
28
+ @typedef.interpret!(::JSON.parse(retval))
29
+ end
30
+
31
+ def lib_to_crystal_type_expr(expr)
32
+ "(#{type_expr}).from_json(String.new(%s))" % expr
33
+ end
34
+
35
+ def crystal_to_lib_type_expr(expr)
36
+ "%s.to_json.to_unsafe" % expr
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ module CrystalRuby::Types
2
+ class TypeSerializer
3
+ include FFI::DataConverter
4
+ def self.for(format)
5
+ case format
6
+ when :json then JSON
7
+ else raise "Unknown type format: #{format}"
8
+ end
9
+ end
10
+
11
+ def error_value
12
+ 0
13
+ end
14
+
15
+ def initialize(typedef)
16
+ @typedef = typedef
17
+ end
18
+
19
+ def type_expr
20
+ @typedef.type_expr
21
+ end
22
+
23
+ def type_defn
24
+ @typedef.type_defn
25
+ end
26
+
27
+ def anonymous?
28
+ @typedef.anonymous?
29
+ end
30
+
31
+ def name
32
+ @typedef.name
33
+ end
34
+ end
35
+ end
36
+
37
+ require_relative "type_serializer/json"
@@ -0,0 +1,55 @@
1
+ require_relative "type_serializer"
2
+
3
+ module CrystalRuby
4
+ module Types
5
+ class Typedef; end
6
+
7
+ def self.Typedef(type)
8
+ return type if type.kind_of?(Class) && type < Typedef
9
+
10
+ Class.new(Typedef) do
11
+ define_singleton_method(:union_types) do
12
+ [self]
13
+ end
14
+
15
+ define_singleton_method(:anonymous?) do
16
+ name.nil?
17
+ end
18
+
19
+ define_singleton_method(:valid?) do
20
+ type.valid?
21
+ end
22
+
23
+ define_singleton_method(:type) do
24
+ type
25
+ end
26
+
27
+ define_singleton_method(:type_expr) do
28
+ anonymous? ? type.type_expr : name
29
+ end
30
+
31
+ define_singleton_method(:type_defn) do
32
+ type.type_expr
33
+ end
34
+
35
+ define_singleton_method(:|) do |other|
36
+ raise "Cannot union non-crystal type #{other}" unless other.is_a?(Type) || other.is_a?(Typedef)
37
+
38
+ UnionType.new(*union_types, *other.union_types)
39
+ end
40
+
41
+ define_singleton_method(:inspect) do
42
+ "<#{name || "AnonymousType"} #{type.inspect}>"
43
+ end
44
+
45
+ define_singleton_method(:serialize_as) do |format|
46
+ TypeSerializer.for(format).new(self)
47
+ end
48
+
49
+ define_singleton_method(:interpret!) do |raw|
50
+ type.interpret!(raw)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,37 @@
1
+ module CrystalRuby
2
+ module Types
3
+ class UnionType < Type
4
+ attr_accessor :name, :union_types
5
+
6
+ def initialize(*union_types)
7
+ self.name = "UnionType"
8
+ self.union_types = union_types
9
+ end
10
+
11
+ def valid?
12
+ union_types.all?(&:valid?)
13
+ end
14
+
15
+ def error
16
+ union_types.map(&:error).join(", ")
17
+ end
18
+
19
+ def inspect
20
+ union_types.map(&:inspect).join(" | ")
21
+ end
22
+
23
+ def interprets?(raw)
24
+ union_types.any? { |type| type.interprets?(raw) }
25
+ end
26
+
27
+ def interpret!(raw)
28
+ union_types.each do |type|
29
+ if type.interprets?(raw)
30
+ return type.interpret!(raw)
31
+ end
32
+ end
33
+ raise "Invalid deserialized value #{raw} for type #{inspect}"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ require_relative "types/type"
2
+ require_relative "types/union_type"
3
+ require_relative "types/typedef"
4
+ require_relative "types/string"
5
+ require_relative "types/time"
6
+ require_relative "types/symbol"
7
+ require_relative "types/array"
8
+ require_relative "types/hash"
9
+ require_relative "types/nil"
10
+ require_relative "types/bool"
11
+ require_relative "types/named_tuple"
12
+ require_relative "types/tuple"
13
+ require_relative "types/numbers"
14
+
15
+ module CrystalRuby
16
+ module Types
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crystalruby
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/crystalruby.rb CHANGED
@@ -1,33 +1,41 @@
1
- require 'ffi'
2
- require 'digest'
3
- require 'fileutils'
4
- require 'method_source'
1
+ require "ffi"
2
+ require "digest"
3
+ require "fileutils"
4
+ require "method_source"
5
5
  require_relative "crystalruby/config"
6
6
  require_relative "crystalruby/version"
7
7
  require_relative "crystalruby/typemaps"
8
- # TODO
9
- # Shards
10
- # Object methods
11
- # Fix bigint issues
12
- # * Initialize Crystal project
13
- # * Clear build artifacts
14
- # * Add config file
15
- # * Set release flag (Changes build target locations)
16
- # Struct Conversions
17
- # Classes
18
- # Test Nesting
19
-
8
+ require_relative "crystalruby/types"
9
+ require_relative "crystalruby/typebuilder"
10
+ require_relative "crystalruby/template"
20
11
 
21
12
  module CrystalRuby
22
-
23
13
  # Define a method to set the @crystalize proc if it doesn't already exist
24
- def crystalize(type=:src, **options, &block)
14
+ def crystalize(type = :src, **options, &block)
25
15
  (args,), returns = options.first
26
16
  args ||= {}
27
- raise "Arguments should be of the form name: :type. Got #{args}" unless args.kind_of?(Hash)
28
- @crystalize_next = {raw: type.to_sym == :raw, args:, returns:, block: }
17
+ raise "Arguments should be of the form name: :type. Got #{args}" unless args.is_a?(Hash)
18
+
19
+ @crystalize_next = { raw: type.to_sym == :raw, args: args, returns: returns, block: block }
20
+ end
21
+
22
+ def crtype(&block)
23
+ TypeBuilder.with_injected_type_dsl(self) do
24
+ TypeBuilder.build(&block)
25
+ end
29
26
  end
30
27
 
28
+ def json(&block)
29
+ crtype(&block).serialize_as(:json)
30
+ end
31
+
32
+ def with_temporary_constant(constant_name, new_value)
33
+ old_value = const_get(constant_name)
34
+ const_set(constant_name, new_value)
35
+ yield
36
+ ensure
37
+ const_set(constant_name, old_value)
38
+ end
31
39
 
32
40
  def method_added(method_name)
33
41
  if @crystalize_next
@@ -42,7 +50,6 @@ module CrystalRuby
42
50
  end
43
51
 
44
52
  def attach_crystalized_method(method_name)
45
-
46
53
  CrystalRuby.instantiate_crystal_ruby! unless CrystalRuby.instantiated?
47
54
 
48
55
  function_body = instance_method(method_name).source.lines[
@@ -54,17 +61,18 @@ module CrystalRuby
54
61
  args ||= {}
55
62
  @crystalize_next = nil
56
63
  function = build_function(self, method_name, args, returns, function_body)
57
-
58
- CrystalRuby.write_function(self, **function) do
64
+ CrystalRuby.write_function(self, name: function[:name], body: function[:body]) do
59
65
  extend FFI::Library
60
66
  ffi_lib "#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
61
- attach_function "#{method_name}", fname, args.map(&:last), returns
62
- attach_function 'init!', 'init', [], :void
63
- [singleton_class, self].each do |receiver|
64
- receiver.prepend(Module.new do
65
- define_method(method_name, &block)
66
- end)
67
- end if block
67
+ attach_function "#{method_name}", fname, function[:ffi_types], function[:return_ffi_type]
68
+ attach_function "init!", "init", [], :void
69
+ if block
70
+ [singleton_class, self].each do |receiver|
71
+ receiver.prepend(Module.new do
72
+ define_method(method_name, &block)
73
+ end)
74
+ end
75
+ end
68
76
 
69
77
  init!
70
78
  end
@@ -74,7 +82,15 @@ module CrystalRuby
74
82
  define_method(method_name) do |*args|
75
83
  CrystalRuby.compile! unless CrystalRuby.compiled?
76
84
  CrystalRuby.attach! unless CrystalRuby.attached?
77
- super(*args)
85
+ args.each_with_index do |arg, i|
86
+ args[i] = function[:arg_maps][i][arg] if function[:arg_maps][i]
87
+ end
88
+ result = super(*args)
89
+ if function[:retval_map]
90
+ function[:retval_map][result]
91
+ else
92
+ result
93
+ end
78
94
  end
79
95
  end)
80
96
  end
@@ -82,63 +98,128 @@ module CrystalRuby
82
98
 
83
99
  module_function
84
100
 
85
-
86
101
  def build_function(owner, name, args, returns, body)
87
- fnname = "#{owner.name.downcase}_#{name}"
88
- args ||= {}
89
- string_conversions = args.select { |_k, v| v.eql?(:string) }.keys
90
- function_body = <<~CRYSTAL
91
- module #{owner.name}
92
- def self.#{name}(#{args.map { |k, v| "#{k} : #{native_type(v)}" }.join(',')}) : #{native_type(returns)}
93
- #{body}
94
- end
95
- end
102
+ arg_types = args.transform_values(&method(:build_type_map))
103
+ return_type = build_type_map(returns)
104
+ function_body = Template.render(
105
+ Template::Function,
106
+ {
107
+ module_name: owner.name,
108
+ lib_fn_name: "#{owner.name.downcase}_#{name}",
109
+ fn_name: name,
110
+ fn_body: body,
111
+ fn_args: arg_types.map { |k, arg_type| "#{k} : #{arg_type[:crystal_type]}" }.join(","),
112
+ fn_ret_type: return_type[:crystal_type],
113
+ lib_fn_args: arg_types.map { |k, arg_type| "_#{k}: #{arg_type[:lib_type]}" }.join(","),
114
+ lib_fn_ret_type: return_type[:lib_type],
115
+ convert_lib_args: arg_types.map{|k, arg_type| "#{k} = #{arg_type[:convert_lib_to_crystal_type]["_#{k}"]}" }.join("\n "),
116
+ arg_names: args.keys.join(","),
117
+ convert_return_type: return_type[:convert_crystal_to_lib_type]["return_value"],
118
+ error_value: return_type[:error_value]
119
+ }
120
+ )
121
+ {
122
+ name: name,
123
+ body: function_body,
124
+ retval_map: returns.is_a?(Types::TypeSerializer) ? ->(rv) { returns.prepare_retval(rv) } : nil,
125
+ ffi_types: arg_types.map { |_k, arg_type| arg_type[:ffi_type] },
126
+ arg_maps: arg_types.map { |_k, arg_type| arg_type[:mapper] },
127
+ return_ffi_type: return_type[:return_ffi_type]
128
+ }
129
+ end
96
130
 
97
- fun #{fnname}(#{args.map { |k, v| "_#{k}: #{lib_type(v)}" }.join(',')}): #{lib_type(returns)}
98
- #{args.map { |k, v| "#{k} = #{convert_to_native_type("_#{k}", v)}" }.join("\n\t")}
99
- #{convert_to_return_type("#{owner.name}.#{name}(#{args.keys.map { |k| "#{k}" }.join(',')})", returns)}
100
- end
101
- CRYSTAL
131
+ def build_type_map(crystalruby_type)
132
+ if crystalruby_type.is_a?(Types::TypeSerializer) && !crystalruby_type.anonymous?
133
+ CrystalRuby.register_type!(crystalruby_type)
134
+ end
102
135
 
103
136
  {
104
- name: fnname,
105
- body: function_body
137
+ ffi_type: ffi_type(crystalruby_type),
138
+ return_ffi_type: ffi_type(crystalruby_type),
139
+ crystal_type: crystal_type(crystalruby_type),
140
+ lib_type: lib_type(crystalruby_type),
141
+ error_value: error_value(crystalruby_type),
142
+ mapper: crystalruby_type.is_a?(Types::TypeSerializer) ? ->(arg) { crystalruby_type.prepare_argument(arg) } : nil,
143
+ convert_crystal_to_lib_type: ->(expr) { convert_crystal_to_lib_type(expr, crystalruby_type) },
144
+ convert_lib_to_crystal_type: ->(expr) { convert_lib_to_crystal_type(expr, crystalruby_type) }
106
145
  }
107
146
  end
108
147
 
148
+ def ffi_type(type)
149
+ case type
150
+ when Symbol then type
151
+ when Types::TypeSerializer then type.ffi_type
152
+ end
153
+ end
154
+
109
155
  def lib_type(type)
110
- Typemaps::C_TYPE_MAP[type]
156
+ if type.is_a?(Types::TypeSerializer)
157
+ type.lib_type
158
+ else
159
+ Typemaps::C_TYPE_MAP.fetch(type)
160
+ end
161
+ rescue StandardError => e
162
+ raise "Unsupported type #{type}"
111
163
  end
112
164
 
113
- def native_type(type)
114
- Typemaps::CRYSTAL_TYPE_MAP[type]
165
+ def error_value(type)
166
+ if type.is_a?(Types::TypeSerializer)
167
+ type.error_value
168
+ else
169
+ Typemaps::ERROR_VALUE.fetch(type)
170
+ end
171
+ rescue StandardError => e
172
+ raise "Unsupported type #{type}"
115
173
  end
116
174
 
117
- def convert_to_native_type(expr, outtype)
118
- Typemaps::C_TYPE_CONVERSIONS[outtype] ? Typemaps::C_TYPE_CONVERSIONS[outtype][:from] % expr : expr
175
+ def crystal_type(type)
176
+ if type.is_a?(Types::TypeSerializer)
177
+ type.crystal_type
178
+ else
179
+ Typemaps::CRYSTAL_TYPE_MAP.fetch(type)
180
+ end
181
+ rescue StandardError => e
182
+ raise "Unsupported type #{type}"
119
183
  end
120
184
 
121
- def convert_to_return_type(expr, outtype)
122
- Typemaps::C_TYPE_CONVERSIONS[outtype] ? Typemaps::C_TYPE_CONVERSIONS[outtype][:to] % expr : expr
185
+ def convert_lib_to_crystal_type(expr, type)
186
+ if type.is_a?(Types::TypeSerializer)
187
+ type.lib_to_crystal_type_expr(expr)
188
+ else
189
+ Typemaps::C_TYPE_CONVERSIONS[type] ? Typemaps::C_TYPE_CONVERSIONS[type][:from] % expr : expr
190
+ end
191
+ end
192
+
193
+ def convert_crystal_to_lib_type(expr, type)
194
+ if type.is_a?(Types::TypeSerializer)
195
+ type.crystal_to_lib_type_expr(expr)
196
+ else
197
+ Typemaps::C_TYPE_CONVERSIONS[type] ? Typemaps::C_TYPE_CONVERSIONS[type][:to] % expr : expr
198
+ end
123
199
  end
124
200
 
125
201
  def self.instantiate_crystal_ruby!
126
- raise "Crystal executable not found. Please ensure Crystal is installed and in your PATH." unless system("which crystal > /dev/null 2>&1")
202
+ unless system("which crystal > /dev/null 2>&1")
203
+ raise "Crystal executable not found. Please ensure Crystal is installed and in your PATH."
204
+ end
205
+
127
206
  @instantiated = true
128
207
  %w[crystal_lib_dir crystal_main_file crystal_src_dir crystal_lib_name].each do |config_key|
129
- raise "Missing config option `#{config_key}`. \nProvide this inside crystalruby.yaml (run `bundle exec crystalruby init` to generate this file with detaults)" unless config.send(config_key)
208
+ unless config.send(config_key)
209
+ raise "Missing config option `#{config_key}`. \nProvide this inside crystalruby.yaml (run `bundle exec crystalruby init` to generate this file with detaults)"
210
+ end
130
211
  end
131
- FileUtils.mkdir_p "#{config.crystal_src_dir}/generated"
212
+ FileUtils.mkdir_p "#{config.crystal_src_dir}/#{config.crystal_codegen_dir}"
132
213
  FileUtils.mkdir_p "#{config.crystal_lib_dir}"
133
214
  unless File.exist?("#{config.crystal_src_dir}/#{config.crystal_main_file}")
134
- IO.write("#{config.crystal_src_dir}/#{config.crystal_main_file}", "require \"./generated/index\"\n")
215
+ IO.write("#{config.crystal_src_dir}/#{config.crystal_main_file}", "require \"./#{config.crystal_codegen_dir}/index\"\n")
135
216
  end
136
- unless File.exist?("#{config.crystal_src_dir}/shard.yml")
137
- IO.write("#{config.crystal_src_dir}/shard.yml", <<~CRYSTAL)
217
+ return if File.exist?("#{config.crystal_src_dir}/shard.yml")
218
+
219
+ IO.write("#{config.crystal_src_dir}/shard.yml", <<~CRYSTAL)
138
220
  name: src
139
221
  version: 0.1.0
140
- CRYSTAL
141
- end
222
+ CRYSTAL
142
223
  end
143
224
 
144
225
  def self.instantiated?
@@ -153,39 +234,75 @@ module CrystalRuby
153
234
  !!@attached
154
235
  end
155
236
 
156
- def self.compile!
157
- return unless @block_store
158
- index_content = <<~CRYSTAL
159
- FAKE_ARG = "crystal"
160
- fun init(): Void
161
- GC.init
162
- ptr = FAKE_ARG.to_unsafe
163
- LibCrystalMain.__crystal_main(1, pointerof(ptr))
164
- end
165
- CRYSTAL
237
+ def self.register_type!(type)
238
+ @types_cache ||= {}
239
+ @types_cache[type.name] = type.type_defn
240
+ end
166
241
 
167
- index_content += @block_store.map do |function|
242
+ def type_modules
243
+ (@types_cache || {}).map do |type_name, expr|
244
+ typedef = ""
245
+ parts = type_name.split("::")
246
+ indent = ""
247
+ parts[0...-1].each do |part|
248
+ typedef << "#{indent} module #{part}\n"
249
+ indent += " "
250
+ end
251
+ typedef << "#{indent}alias #{parts[-1]} = #{expr}\n"
252
+ parts[0...-1].each do |_part|
253
+ indent = indent[0...-2]
254
+ typedef << "#{indent} end\n"
255
+ end
256
+ typedef
257
+ end.join("\n")
258
+ end
259
+
260
+ def self.requires
261
+ @block_store.map do |function|
168
262
  function_data = function[:body]
169
263
  file_digest = Digest::MD5.hexdigest function_data
170
264
  fname = function[:name]
171
265
  "require \"./#{function[:owner].name}/#{fname}_#{file_digest}.cr\"\n"
172
266
  end.join("\n")
267
+ end
173
268
 
174
- File.write("#{config.crystal_src_dir}/generated/index.cr", index_content)
175
- begin
176
- lib_target = "#{Dir.pwd}/#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
177
- Dir.chdir(config.crystal_src_dir) do
178
- config.debug ?
179
- `crystal build -o #{lib_target} #{config.crystal_main_file}` :
180
- `crystal build --release --no-debug -o #{lib_target} #{config.crystal_main_file}`
181
- end
269
+ def self.compile!
270
+ return unless @block_store
271
+
272
+ index_content = Template.render(
273
+ Template::Index,
274
+ {
275
+ type_modules: type_modules,
276
+ requires: requires
277
+ }
278
+ )
279
+
280
+ File.write("#{config.crystal_src_dir}/#{config.crystal_codegen_dir}/index.cr", index_content)
281
+ lib_target = "#{Dir.pwd}/#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
282
+
283
+ Dir.chdir(config.crystal_src_dir) do
284
+ cmd = if config.debug
285
+ "crystal build -o #{lib_target} #{config.crystal_main_file}"
286
+ else
287
+ "crystal build --release --no-debug -o #{lib_target} #{config.crystal_main_file}"
288
+ end
289
+
290
+ raise "Error compiling crystal code" unless result = system(cmd)
182
291
 
183
292
  @compiled = true
184
- rescue StandardError => e
185
- puts 'Error compiling crystal code'
186
- puts e
187
- File.delete("#{config.crystal_src_dir}/generated/index.cr")
293
+ File.delete("#{config.crystal_codegen_dir}/index.cr") if File.exist?("#{config.crystal_codegen_dir}/index.cr")
188
294
  end
295
+ extend FFI::Library
296
+ ffi_lib "#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
297
+ attach_function :attach_rb_error_handler, [:pointer], :int
298
+ const_set(:ErrorCallback, FFI::Function.new(:void, [:string, :string]) do |error_type, message|
299
+ error_type = error_type.to_sym
300
+ error_type = Object.const_defined?(error_type) && Object.const_get(error_type).ancestors.include?(Exception) ?
301
+ Object.const_get(error_type) :
302
+ RuntimeError
303
+ raise error_type.new(message)
304
+ end)
305
+ attach_rb_error_handler(ErrorCallback)
189
306
  end
190
307
 
191
308
  def self.attach!
@@ -196,25 +313,25 @@ module CrystalRuby
196
313
  end
197
314
 
198
315
  def self.write_function(owner, name:, body:, &compile_callback)
199
- @compiled = File.exist?("#{config.crystal_src_dir}/generated/index.cr") unless defined?(@compiled)
316
+ @compiled = File.exist?("#{config.crystal_src_dir}/#{config.crystal_codegen_dir}/index.cr") unless defined?(@compiled)
200
317
  @block_store ||= []
201
- @block_store << {owner: owner, name: name, body: body, compile_callback: compile_callback}
202
- FileUtils.mkdir_p("#{config.crystal_src_dir}/generated")
203
- existing = Dir.glob("#{config.crystal_src_dir}/generated/**/*.cr")
318
+ @block_store << { owner: owner, name: name, body: body, compile_callback: compile_callback }
319
+ FileUtils.mkdir_p("#{config.crystal_src_dir}/#{config.crystal_codegen_dir}")
320
+ existing = Dir.glob("#{config.crystal_src_dir}/#{config.crystal_codegen_dir}/**/*.cr")
204
321
  @block_store.each do |function|
205
322
  owner_name = function[:owner].name
206
- FileUtils.mkdir_p("#{config.crystal_src_dir}/generated/#{owner_name}")
323
+ FileUtils.mkdir_p("#{config.crystal_src_dir}/#{config.crystal_codegen_dir}/#{owner_name}")
207
324
  function_data = function[:body]
208
325
  fname = function[:name]
209
326
  file_digest = Digest::MD5.hexdigest function_data
210
- filename = "#{config.crystal_src_dir}/generated/#{owner_name}/#{fname}_#{file_digest}.cr"
327
+ filename = "#{config.crystal_src_dir}/#{config.crystal_codegen_dir}/#{owner_name}/#{fname}_#{file_digest}.cr"
211
328
  unless existing.delete(filename)
212
329
  @compiled = false
213
330
  @attached = false
214
331
  File.write(filename, function_data)
215
332
  end
216
333
  existing.select do |f|
217
- f =~ %r{#{config.crystal_src_dir}/generated/#{owner_name}/#{fname}_[a-f0-9]{32}\.cr}
334
+ f =~ %r{#{config.crystal_src_dir}/#{config.crystal_codegen_dir}/#{owner_name}/#{fname}_[a-f0-9]{32}\.cr}
218
335
  end.each do |fl|
219
336
  File.delete(fl) unless fl.eql?(filename)
220
337
  end