rubyx-py 0.2.0 → 0.2.1

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: 2815c45d6a58b835cfc97484894c6aff43abf13316f18006f46a38c059988ec9
4
- data.tar.gz: 2eb4aa61608a7553046b01143d7a704dd204a9eb0a13aa1c1a933350f092b660
3
+ metadata.gz: cf3d6decfdb8c4bf6dd12f891389511ce96deeaf3efffb1664bb35d6b9b6066f
4
+ data.tar.gz: 7dcbdcceac38a4a8dc0908e0f11fc19237080025fbb36e8d7cb8d38d0c8ec58f
5
5
  SHA512:
6
- metadata.gz: 4bd4ce8b2bf2e169a8b32adc9f3fefaf8445fdcc72b1eb1ac821d6c8b8ae192979b0f00260585844e22f0aa3b76a70648801c11130e82df8cb6b6da66bdfec4d
7
- data.tar.gz: 0ef30d48d7cbeb5b85e33cb51fa5abebc67808495ff2bcb840b78c1b7c962fa62cb484b7f88a3ee84cc0506e13c6a06c0d173aa0fb03f9f0074443e0aa3e2397
6
+ metadata.gz: 63cd8eba2c550fd0f4728d6a836ae3e2c80c0b81421830e07938a4d6acba4823e9cc11fb73e767384e33993c01fe4e4138097d9b26e53b361d87d46281ac8de4
7
+ data.tar.gz: 27c3168265e2e40186c2e0cb554192b03fd484400e34b788d125f9d4347abfecd9d3be260bf093deec48fc38f350c4655ce1e8632b3e7729f41cf967fce6d7ca
data/README.md CHANGED
@@ -6,14 +6,16 @@
6
6
 
7
7
  **Call Python from Ruby. No microservices, no REST APIs, no serialization overhead.**
8
8
 
9
- Powered by Rust for safety and performance. Built for Rails.
9
+ Powered by Rust for safety and performance. Built for Rails. Inspired by [Pythonx](https://github.com/livebook-dev/pythonx)
10
10
 
11
11
  [![Gem Version](https://badge.fury.io/rb/rubyx-py.svg)](https://badge.fury.io/rb/rubyx-py)
12
12
  [![CI](https://github.com/yinho999/rubyx/actions/workflows/ci.yml/badge.svg)](https://github.com/yinho999/rubyx/actions/workflows/ci.yml)
13
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
14
14
  [![Ruby](https://img.shields.io/badge/Ruby-%3E%3D%203.0-red.svg)](https://www.ruby-lang.org)
15
15
  [![Rust](https://img.shields.io/badge/Rust-powered-orange.svg)](https://www.rust-lang.org)
16
-
16
+ > *"Rubyx showed that the proxy-object pattern works beautifully for cross-language bridges in Rust, and its magnus + rb-sys architecture is exactly what [Boax](https://intertwingly.net/blog/2026/03/25/Calling-JavaScript-from-Ruby.html) uses."*
17
+ >
18
+ > — [Sam Ruby](https://en.wikipedia.org/wiki/Sam_Ruby), author of *Agile Web Development with Rails*
17
19
  </div>
18
20
 
19
21
  ---
@@ -93,7 +95,7 @@ dependencies = []
93
95
 
94
96
  ```python
95
97
  # app/python/example.py
96
- def hello(name="World"):
98
+ def hello(name):
97
99
  return f"Hello, {name}! From Python."
98
100
  ```
99
101
 
@@ -102,7 +104,7 @@ def hello(name="World"):
102
104
  class GreetingsController < ApplicationController
103
105
  def index
104
106
  example = Rubyx.import('example')
105
- render json: { message: example.hello(params[:name]).to_ruby }
107
+ render json: { message: example.hello(params[:name] || 'World').to_ruby }
106
108
  end
107
109
  end
108
110
  ```
@@ -159,7 +161,7 @@ class TasksController < ApplicationController
159
161
  tasks = Rubyx.import('tasks')
160
162
 
161
163
  # Non-blocking — returns a Future immediately
162
- future = Rubyx.async_await(tasks.delayed_greet(params[:name], seconds: 2))
164
+ future = Rubyx.async_await(tasks.delayed_greet(params[:name] || 'World', seconds: 2))
163
165
  do_other_work()
164
166
  render json: { message: future.await.to_ruby }
165
167
  end
@@ -331,7 +333,29 @@ Rubyx.eval("f'Hello, {name}!'", name: "World").to_ruby # => "Hello, World!"
331
333
  Rubyx.eval("max(items)", items: [3, 1, 4, 1, 5]).to_ruby # => 5
332
334
  ```
333
335
 
334
- Supports: Integer, Float, String, Symbol, Bool, nil, Array, Hash, and RubyxObject.
336
+ Supports: Integer, Float, String, Symbol, Bool, nil, Array, Hash, binary String (ASCII-8BIT), and RubyxObject.
337
+
338
+ ## Bytes / Binary Data
339
+
340
+ Python `bytes` and `bytearray` convert to Ruby `String` with `ASCII-8BIT` encoding. Ruby binary strings (`.b`) convert to Python `bytes`:
341
+
342
+ ```ruby
343
+ # Python bytes → Ruby
344
+ Rubyx.eval("b'hello'").to_ruby # => "hello" (ASCII-8BIT)
345
+ Rubyx.eval("bytearray(b'hello')").to_ruby # => "hello" (ASCII-8BIT)
346
+
347
+ # Ruby → Python
348
+ ctx = Rubyx.context
349
+ ctx.eval("type(data).__name__", data: "hello".b) # => "bytes"
350
+ ctx.eval("type(data).__name__", data: "hello") # => "str"
351
+
352
+ # Roundtrip binary data
353
+ ctx.eval("data", data: "\xff\x00\xfe".b).to_ruby # => "\xFF\x00\xFE" (ASCII-8BIT)
354
+
355
+ # Works with Python stdlib
356
+ ctx.eval("import base64")
357
+ ctx.eval("base64.b64encode(raw)", raw: "Hello".b).to_ruby # => "SGVsbG8=" (ASCII-8BIT)
358
+ ```
335
359
 
336
360
  ## Python Objects
337
361
 
@@ -470,6 +494,37 @@ svc.Analyzer([1, 2, 3]).summary.to_ruby # => {"count" => 3, "sum" => 6}
470
494
  | `.callable?` | Check if callable |
471
495
  | `.py_type` | Python type name |
472
496
 
497
+ ## Type Conversion
498
+
499
+ | Python | Ruby | Notes |
500
+ |-----------------------|------------------------------|------------------------|
501
+ | `int` | `Integer` | |
502
+ | `float` | `Float` | |
503
+ | `str` | `String` (UTF-8) | |
504
+ | `bytes` | `String` (ASCII-8BIT) | binary data |
505
+ | `bytearray` | `String` (ASCII-8BIT) | binary data |
506
+ | `bool` | `true` / `false` | |
507
+ | `None` | `nil` | |
508
+ | `list` / `tuple` | `Array` | |
509
+ | `dict` | `Hash` | |
510
+ | `set` / `frozenset` | `Array` | |
511
+ | everything else | `RubyxObject` | proxy to Python object |
512
+
513
+ **Ruby → Python** (via globals/kwargs):
514
+
515
+ | Ruby | Python |
516
+ |--------------------------------|-------------|
517
+ | `Integer` | `int` |
518
+ | `Float` | `float` |
519
+ | `String` (UTF-8) | `str` |
520
+ | `String` (ASCII-8BIT / `.b`) | `bytes` |
521
+ | `true` / `false` | `bool` |
522
+ | `nil` | `None` |
523
+ | `Array` | `list` |
524
+ | `Hash` | `dict` |
525
+ | `Symbol` | `str` |
526
+ | `RubyxObject` | original |
527
+
473
528
  ## Requirements
474
529
 
475
530
  - Ruby >= 3.0
data/ext/rubyx/src/lib.rs CHANGED
@@ -95,6 +95,7 @@ fn init(ruby: &magnus::Ruby) -> Result<(), magnus::Error> {
95
95
  py_object.define_method("truthy?", method!(RubyxObject::is_truthy, 0))?;
96
96
  py_object.define_method("falsy?", method!(RubyxObject::is_falsy, 0))?;
97
97
  py_object.define_method("callable?", method!(RubyxObject::is_callable, 0))?;
98
+ py_object.define_method("call", method!(RubyxObject::call, -1))?;
98
99
  py_object.define_method("py_type", method!(RubyxObject::py_type, 0))?;
99
100
  py_object.define_method("each", method!(RubyxObject::each, 0))?;
100
101
  py_object.include_module(ruby.module_enumerable())?;