crystalruby 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c552102391f468b4974cb4ac36c4876001743fdec5fa8f2a69577255de4c52d
4
- data.tar.gz: f7c104b10cf70eb03ab064117be946d4268c97063ba9302a99613e9c473ded97
3
+ metadata.gz: fe68e58781b8314afb53b268ba9314472ee93c611565f7398d0ff29b445f8ece
4
+ data.tar.gz: acd5e80d5709cc6f831daa39f58f74fe95642e16a4c44d25c8fab24106b47bcd
5
5
  SHA512:
6
- metadata.gz: 0c01cefa9a6f820cd59aed62e02846bd90cd53a742c203b8de0023908c53e77ef2a558adf26023008dcd921e9c9cbc0466a63598361c18ec7cc6bee29a57ce2c
7
- data.tar.gz: a9fe76d0b8a93dab4afd94d696d9955a81e75384fd1d4af8a0af5249c30fd51920d46de4728caab630862c889e799f1207f9915176fe861c6624e7a13d994e5a
6
+ metadata.gz: 85516d1372b2467147cb5c61e6d33dc27e6e2c0d11876b32dbee1007152096fd54551926177b56e066fa9bc8e4329b8ef13d133280305018428775267eb4a24e
7
+ data.tar.gz: 207284740bef726d1a8c5df35b21ebe33b8611725363acac451bbadc3b7c714585513027be2398dd4cdd6ff94761c00585c94546fb198a4b90047d91edbef2ce
data/README.md CHANGED
@@ -25,7 +25,7 @@ require 'crystalruby'
25
25
  module MyTestModule
26
26
  # The below method will be replaced by a compiled Crystal version
27
27
  # linked using FFI.
28
- crystalize [:int, :int] => :int
28
+ crystalize [a: :int, b: :int] => :int
29
29
  def add(a, b)
30
30
  a + b
31
31
  end
@@ -151,7 +151,7 @@ require 'crystalruby'
151
151
  module Adder
152
152
  crystalize [a: :int, b: :int] => :int
153
153
  def add(a, b)
154
- a + b * 3
154
+ a + b
155
155
  end
156
156
  end
157
157
 
@@ -349,7 +349,6 @@ It should support escape hatches to allow it to coexist with code that performs
349
349
 
350
350
  The library is currently in its infancy. Planned additions are:
351
351
 
352
- - Replace existing checksum process, with one that combines results of inline and external crystal to more accurately detect when recompilation is necessary.
353
352
  - Simple mixin/concern that utilises `FFI::Struct` for bi-directional passing of Ruby objects and Crystal objects (by value).
354
353
  - Install command to generate a sample build script, and supports build command (which simply verifies then invokes this script)
355
354
  - Call Ruby from Crystal using FFI callbacks (implement `.expose_to_crystal`)
@@ -1,9 +1,18 @@
1
+ # This is the template used for all CrystalRuby functions
2
+ # Calls to this method *from ruby* are first transformed through the lib function.
3
+ # Crystal code can simply call this method directly, enabling generated crystal code
4
+ # to call other generated crystal code without overhead.
5
+
1
6
  module %{module_name}
2
7
  def self.%{fn_name}(%{fn_args}) : %{fn_ret_type}
3
8
  %{fn_body}
4
9
  end
5
10
  end
6
11
 
12
+ # This function is the entry point for the CrystalRuby code, exposed through FFI.
13
+ # We apply some basic error handling here, and convert the arguments and return values
14
+ # to ensure that we are using Crystal native types.
15
+
7
16
  fun %{lib_fn_name}(%{lib_fn_args}): %{lib_fn_ret_type}
8
17
  begin
9
18
  %{convert_lib_args}
@@ -1,38 +1,46 @@
1
1
  FAKE_ARG = "crystal"
2
- alias Callback = (Pointer(UInt8), Pointer(UInt8) -> Void)
2
+
3
+ alias ErrorCallback = (Pointer(UInt8), Pointer(UInt8) -> Void)
3
4
 
4
5
  module CrystalRuby
6
+ # Initializing Crystal Ruby invokes init on the Crystal garbage collector.
7
+ # We need to be sure to only do this once.
5
8
  @@initialized = false
6
- def self.init
7
- if @@initialized
8
- return
9
- end
10
- @@initialized = true
9
+
10
+ # We won't natively handle Crystal Exceptions in Ruby
11
+ # Instead, we'll catch them in Crystal, and explicitly expose them to Ruby via
12
+ # the error_callback.
13
+ @@error_callback
14
+
15
+ # This is the entry point for instantiating CrystalRuby
16
+ # We:
17
+ # 1. Initialize the Crystal garbage collector
18
+ # 2. Set the error callback
19
+ # 3. Call the Crystal main function
20
+ def self.init(error_callback : ErrorCallback)
21
+ return if @@initialized
11
22
  GC.init
23
+ @@initialized = true
24
+ @@error_callback = error_callback
12
25
  ptr = FAKE_ARG.to_unsafe
13
26
  LibCrystalMain.__crystal_main(1, pointerof(ptr))
14
27
  end
15
28
 
16
- def self.attach_rb_error_handler(cb : Callback)
17
- @@rb_error_handler = cb
18
- end
19
-
29
+ # Explicit error handling (triggers exception within Ruby on the same thread)
20
30
  def self.report_error(error_type : String, str : String)
21
- handler = @@rb_error_handler
22
- if handler
31
+ if handler = @@error_callback
23
32
  handler.call(error_type.to_unsafe, str.to_unsafe)
24
33
  end
25
34
  end
26
35
  end
27
36
 
28
-
29
- fun init(): Void
30
- CrystalRuby.init
31
- end
32
-
33
- fun attach_rb_error_handler(cb : Callback) : Void
34
- CrystalRuby.attach_rb_error_handler(cb)
37
+ fun init(cb : ErrorCallback): Void
38
+ CrystalRuby.init(cb)
35
39
  end
36
40
 
41
+ # This is where we define all our Crystal modules and types
42
+ # derived from their Ruby counterparts.
37
43
  %{type_modules}
44
+
45
+ # Require all generated crystal files
38
46
  %{requires}
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crystalruby
4
- VERSION = "0.1.6"
4
+ VERSION = "0.1.7"
5
5
  end
data/lib/crystalruby.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "ffi"
2
4
  require "digest"
3
5
  require "fileutils"
@@ -10,6 +12,7 @@ require_relative "crystalruby/typebuilder"
10
12
  require_relative "crystalruby/template"
11
13
 
12
14
  module CrystalRuby
15
+ CR_SRC_FILES_PATTERN = "./**/*.cr"
13
16
  # Define a method to set the @crystalize proc if it doesn't already exist
14
17
  def crystalize(type = :src, **options, &block)
15
18
  (args,), returns = options.first
@@ -29,14 +32,6 @@ module CrystalRuby
29
32
  crtype(&block).serialize_as(:json)
30
33
  end
31
34
 
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
39
-
40
35
  def method_added(method_name)
41
36
  if @crystalize_next
42
37
  attach_crystalized_method(method_name)
@@ -64,8 +59,7 @@ module CrystalRuby
64
59
  CrystalRuby.write_function(self, name: function[:name], body: function[:body]) do
65
60
  extend FFI::Library
66
61
  ffi_lib "#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
67
- attach_function "#{method_name}", fname, function[:ffi_types], function[:return_ffi_type]
68
- attach_function "init!", "init", [], :void
62
+ attach_function method_name, fname, function[:ffi_types], function[:return_ffi_type]
69
63
  if block
70
64
  [singleton_class, self].each do |receiver|
71
65
  receiver.prepend(Module.new do
@@ -73,8 +67,6 @@ module CrystalRuby
73
67
  end)
74
68
  end
75
69
  end
76
-
77
- init!
78
70
  end
79
71
 
80
72
  [singleton_class, self].each do |receiver|
@@ -219,6 +211,9 @@ module CrystalRuby
219
211
  "require \"./#{config.crystal_codegen_dir}/index\"\n"
220
212
  )
221
213
  end
214
+
215
+ attach_crystal_ruby_lib! if compiled?
216
+
222
217
  return if File.exist?("#{config.crystal_src_dir}/shard.yml")
223
218
 
224
219
  IO.write("#{config.crystal_src_dir}/shard.yml", <<~CRYSTAL)
@@ -227,11 +222,25 @@ module CrystalRuby
227
222
  CRYSTAL
228
223
  end
229
224
 
225
+ def attach_crystal_ruby_lib!
226
+ extend FFI::Library
227
+ ffi_lib "#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
228
+ attach_function "init!", :init, [:pointer], :void
229
+ const_set(:ErrorCallback, FFI::Function.new(:void, %i[string string]) do |error_type, message|
230
+ error_type = error_type.to_sym
231
+ is_exception_type = Object.const_defined?(error_type) && Object.const_get(error_type).ancestors.include?(Exception)
232
+ error_type = is_exception_type ? Object.const_get(error_type) : RuntimeError
233
+ raise error_type.new(message)
234
+ end)
235
+ init!(ErrorCallback)
236
+ end
237
+
230
238
  def self.instantiated?
231
239
  @instantiated
232
240
  end
233
241
 
234
242
  def self.compiled?
243
+ @compiled = get_current_crystal_lib_digest == get_cr_src_files_digest unless defined?(@compiled)
235
244
  @compiled
236
245
  end
237
246
 
@@ -293,21 +302,14 @@ module CrystalRuby
293
302
  end
294
303
 
295
304
  unless result = system(cmd)
296
- File.delete("#{config.crystal_codegen_dir}/index.cr") if File.exist?("#{config.crystal_codegen_dir}/index.cr")
305
+ File.delete(digest_file_name) if File.exist?(digest_file_name)
297
306
  raise "Error compiling crystal code"
298
307
  end
299
- @compiled = true
300
308
  end
301
- extend FFI::Library
302
- ffi_lib "#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
303
- attach_function :attach_rb_error_handler, [:pointer], :int
304
- const_set(:ErrorCallback, FFI::Function.new(:void, %i[string string]) do |error_type, message|
305
- error_type = error_type.to_sym
306
- is_exception_type = Object.const_defined?(error_type) && Object.const_get(error_type).ancestors.include?(Exception)
307
- error_type = is_exception_type ? Object.const_get(error_type) : RuntimeError
308
- raise error_type.new(message)
309
- end)
310
- attach_rb_error_handler(ErrorCallback)
309
+
310
+ IO.write(digest_file_name, get_cr_src_files_digest)
311
+ @compiled = true
312
+ attach_crystal_ruby_lib!
311
313
  end
312
314
 
313
315
  def self.attach!
@@ -317,10 +319,23 @@ module CrystalRuby
317
319
  @attached = true
318
320
  end
319
321
 
322
+ def self.get_cr_src_files_digest
323
+ file_digests = Dir.glob(CR_SRC_FILES_PATTERN).sort.map do |file_path|
324
+ content = File.read(file_path)
325
+ Digest::MD5.hexdigest(content)
326
+ end.join
327
+ Digest::MD5.hexdigest(file_digests)
328
+ end
329
+
330
+ def self.digest_file_name
331
+ @digest_file_name ||= "#{config.crystal_lib_dir}/#{config.crystal_lib_name}.digest"
332
+ end
333
+
334
+ def self.get_current_crystal_lib_digest
335
+ File.read(digest_file_name) if File.exist?(digest_file_name)
336
+ end
337
+
320
338
  def self.write_function(owner, name:, body:, &compile_callback)
321
- unless defined?(@compiled)
322
- @compiled = File.exist?("#{config.crystal_src_dir}/#{config.crystal_codegen_dir}/index.cr")
323
- end
324
339
  @block_store ||= []
325
340
  @block_store << { owner: owner, name: name, body: body, compile_callback: compile_callback }
326
341
  FileUtils.mkdir_p("#{config.crystal_src_dir}/#{config.crystal_codegen_dir}")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crystalruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wouter Coppieters