crystalruby 0.1.8 → 0.1.9

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: 95d9eecc4d47a8d9f83417779c6174eea95ebeea593e5127a89b6cfbc7884664
4
- data.tar.gz: b4f241f826e4f2b6c704bda0fd01c201e79a9bc26027bd4702717fd371687650
3
+ metadata.gz: ca096d0563b6201c3ef4f084fff3304cfa212fda88e81085580b2de5ff10536f
4
+ data.tar.gz: ccc68dc96c30f973c9bafcbaec75a2461bbaa275e32913a0781c4cc5488f034f
5
5
  SHA512:
6
- metadata.gz: d98f1e4a671679cfb8d9da45298954e01def202802bd5a64db9d0d9cba08de0776971d031648642f402fe0310d741dc6b2d163253884ad25082298226c1be1a0
7
- data.tar.gz: 141658885f763ca3088a7058131e7227434920d6213e755e21f85d6bb4d68a062a2a3163127f4e9f0db03a8c9a27a0549837714b38789647028075cc3b77660c
6
+ metadata.gz: 134e9888d563673d78dc0b9075a239ee3bab6c4a07eb63ff903c748b97684ec87e9bb33011e9f276504f855edd2a25c29860ebc626aa181f74350f68ace8b9e8
7
+ data.tar.gz: f3f7a25d12459178e5c8d3cb6a46cfa0e375f65099cdcde15c834a1ba0872198b8475919ec7e5472fa10529bfb71ede67e0edfb88105be5eb220226f2d0ba4ad
data/README.md CHANGED
@@ -295,6 +295,131 @@ end
295
295
  MyModule.add("1", "2")
296
296
  ```
297
297
 
298
+ ## Inline Chunks
299
+
300
+ `crystalruby` also allows you to write inline Crystal code that does not require binding to Ruby. This can be useful for e.g. performing setup or teardown operations.
301
+
302
+ Follow these steps for a toy example of how we can use crystalized ruby and inline chunks to expose the [crystal-redis](https://github.com/stefanwille/crystal-redis) library to Ruby.
303
+
304
+ 1. Start our toy project
305
+
306
+ ```bash
307
+ mkdir crystalredis
308
+ cd crystalredis
309
+ bundle init
310
+ ```
311
+
312
+ 2. Add dependencies to our Gemfile and run `bundle install`
313
+
314
+ ```ruby
315
+ # frozen_string_literal: true
316
+
317
+ source "https://rubygems.org"
318
+
319
+ gem 'crystalruby'
320
+
321
+ # Let's see if performance is comparable to that of the redis gem.
322
+ gem 'benchmark-ips'
323
+ gem 'redis'
324
+ ```
325
+
326
+ 3. Write our Redis client
327
+
328
+ ```ruby
329
+ # Filename: crystalredis.rb
330
+ require 'crystalruby'
331
+
332
+ module CrystalRedis
333
+
334
+ crystal do
335
+ CLIENT = Redis.new
336
+ def self.client
337
+ CLIENT
338
+ end
339
+ end
340
+
341
+ crystalize [key: :string, value: :string] => :void
342
+ def set(key, value)
343
+ client.set(key, value)
344
+ end
345
+
346
+ crystalize [key: :string] => :string
347
+ def get(key)
348
+ client.get(key).to_s
349
+ end
350
+ end
351
+ ```
352
+
353
+ 4. Load the modules (without running them) to generate our Crystal project skeleton.
354
+
355
+ ```bash
356
+ bundle exec ruby crystalredis.rb
357
+ ```
358
+
359
+ 5. Add the missing Redis dependency to our shard.yml
360
+
361
+ ```yaml
362
+ # filename: crystalruby/src/shard.yml
363
+ dependencies:
364
+ redis:
365
+ github: stefanwille/crystal-redis
366
+ ```
367
+
368
+ ```ruby
369
+ # filename: main.cr
370
+ require "redis"
371
+ require "./generated/index"
372
+ ```
373
+
374
+ ```bash
375
+ bundle exec crystalruby install
376
+ ```
377
+
378
+ 6. Compile and benchmark our new module in Ruby
379
+
380
+ ```ruby
381
+ # Filename: benchmark.rb
382
+ # Let's compare the performance of our CrystalRedis module to the Ruby Redis gem
383
+ require_relative "crystalredis"
384
+ require 'redis'
385
+ require 'benchmark/ips'
386
+
387
+ Benchmark.ips do |x|
388
+ rbredis = Redis.new
389
+
390
+ x.report(:crredis) do
391
+ CrystalRedis.set("hello", "world")
392
+ CrystalRedis.get("hello")
393
+ end
394
+
395
+ x.report(:rbredis) do
396
+ rbredis.set("hello", "world")
397
+ rbredis.get("hello")
398
+ end
399
+ end
400
+ ```
401
+
402
+ 7. Run the benchmark
403
+
404
+ ```bash
405
+ $ bundle exec ruby benchmark.rb
406
+ ```
407
+
408
+ ### Output
409
+
410
+ ```bash
411
+
412
+ #crystalredis wins! (Warm up during first run will be slow for crredis, due to first compilation)
413
+
414
+ ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin22]
415
+ Warming up --------------------------------------
416
+ crredis 1.946k i/100ms
417
+ rbredis 1.749k i/100ms
418
+ Calculating -------------------------------------
419
+ crredis 22.319k (± 1.7%) i/s - 112.868k in 5.058448s
420
+ rbredis 16.861k (± 9.1%) i/s - 83.952k in 5.024941s
421
+ ```
422
+
298
423
  ## Release Builds
299
424
 
300
425
  You can control whether CrystalRuby builds in debug or release mode by setting following config option
@@ -1,7 +1,7 @@
1
1
  module CrystalRuby
2
2
  module Template
3
3
  Dir[File.join(File.dirname(__FILE__), "templates", "*.cr")].each do |file|
4
- template_name = File.basename(file, File.extname(file)).capitalize
4
+ template_name = File.basename(file, File.extname(file)).split("_").map(&:capitalize).join
5
5
  const_set(template_name, File.read(file))
6
6
  end
7
7
 
@@ -0,0 +1,6 @@
1
+ # This is the template used for writing inline chunks of Crystal code without direct
2
+ # Ruby integration
3
+
4
+ module %{module_name}
5
+ %{body}
6
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crystalruby
4
- VERSION = "0.1.8"
4
+ VERSION = "0.1.9"
5
5
  end
data/lib/crystalruby.rb CHANGED
@@ -22,6 +22,19 @@ module CrystalRuby
22
22
  @crystalize_next = { raw: type.to_sym == :raw, args: args, returns: returns, block: block }
23
23
  end
24
24
 
25
+ def crystal(type = :src, &block)
26
+ inline_crystal_body = Template.render(
27
+ Template::InlineChunk,
28
+ {
29
+ module_name: name,
30
+ body: block.source.lines[
31
+ type == :raw ? 2...-2 : 1...-1
32
+ ].join("\n")
33
+ }
34
+ )
35
+ CrystalRuby.write_chunk(self, body: inline_crystal_body)
36
+ end
37
+
25
38
  def crtype(&block)
26
39
  TypeBuilder.with_injected_type_dsl(self) do
27
40
  TypeBuilder.build(&block)
@@ -56,7 +69,7 @@ module CrystalRuby
56
69
  args ||= {}
57
70
  @crystalize_next = nil
58
71
  function = build_function(self, method_name, args, returns, function_body)
59
- CrystalRuby.write_function(self, name: function[:name], body: function[:body]) do
72
+ CrystalRuby.write_chunk(self, name: function[:name], body: function[:body]) do
60
73
  extend FFI::Library
61
74
  ffi_lib "#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
62
75
  attach_function method_name, fname, function[:ffi_types], function[:return_ffi_type]
@@ -226,6 +239,7 @@ module CrystalRuby
226
239
  extend FFI::Library
227
240
  ffi_lib "#{config.crystal_lib_dir}/#{config.crystal_lib_name}"
228
241
  attach_function "init!", :init, [:pointer], :void
242
+ send(:remove_const, :ErrorCallback) if defined?(ErrorCallback)
229
243
  const_set(:ErrorCallback, FFI::Function.new(:void, %i[string string]) do |error_type, message|
230
244
  error_type = error_type.to_sym
231
245
  is_exception_type = Object.const_defined?(error_type) && Object.const_get(error_type).ancestors.include?(Exception)
@@ -314,7 +328,7 @@ module CrystalRuby
314
328
 
315
329
  def self.attach!
316
330
  @block_store.each do |function|
317
- function[:compile_callback].call
331
+ function[:compile_callback]&.call
318
332
  end
319
333
  @attached = true
320
334
  end
@@ -335,7 +349,7 @@ module CrystalRuby
335
349
  File.read(digest_file_name) if File.exist?(digest_file_name)
336
350
  end
337
351
 
338
- def self.write_function(owner, name:, body:, &compile_callback)
352
+ def self.write_chunk(owner, body:, name: Digest::MD5.hexdigest(body), &compile_callback)
339
353
  @block_store ||= []
340
354
  @block_store << { owner: owner, name: name, body: body, compile_callback: compile_callback }
341
355
  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.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wouter Coppieters
@@ -87,6 +87,7 @@ files:
87
87
  - lib/crystalruby/template.rb
88
88
  - lib/crystalruby/templates/function.cr
89
89
  - lib/crystalruby/templates/index.cr
90
+ - lib/crystalruby/templates/inline_chunk.cr
90
91
  - lib/crystalruby/typebuilder.rb
91
92
  - lib/crystalruby/typemaps.rb
92
93
  - lib/crystalruby/types.rb