crystalruby 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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