crystalruby 0.1.6 → 0.1.7

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 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