galaaz 2.1.3 → 2.1.5
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 +4 -4
- data/CHANGELOG.md +46 -1
- data/README.md +90 -9
- data/bin/gknit +13 -10
- data/bin/gknit_Rscript +2 -4
- data/bin/gstudio_irb.rb +71 -22
- data/blogs/README.md +1 -1
- data/blogs/galaaz_ggplot/galaaz_ggplot.Rmd +8 -8
- data/blogs/galaaz_ggplot/galaaz_ggplot.md +6 -6
- data/blogs/gknit/gknit.Rmd +12 -11
- data/blogs/gknit/gknit.md +12 -11
- data/blogs/manual/manual.Rmd +120 -33
- data/blogs/manual/manual.md +90 -9
- data/blogs/oh_my/oh_my.Rmd +7 -7
- data/blogs/oh_my/oh_my.md +7 -7
- data/blogs/ruby_plot/ruby_plot.Rmd +13 -14
- data/blogs/ruby_plot/ruby_plot.md +13 -14
- data/examples/misc/ggplot.rb +1 -1
- data/lib/R_interface/new_bridge_adapter.rb +14 -1
- data/lib/R_interface/r.rb +60 -53
- data/lib/R_interface/r_job.rb +429 -0
- data/lib/galaaz/cli.rb +58 -11
- data/lib/gknit/diagnostics.rb +3 -1
- data/lib/new_bridge/session_client.rb +45 -0
- data/specs/gknit_install_timeout_report_spec.rb +27 -7
- data/specs/r_job_spec.rb +81 -0
- data/version.rb +1 -1
- metadata +4 -2
|
@@ -3,7 +3,7 @@ title: "How to make Beautiful Ruby Plots with Galaaz"
|
|
|
3
3
|
author:
|
|
4
4
|
- "Rodrigo Botafogo"
|
|
5
5
|
- "Daniel Mossé - University of Pittsburgh"
|
|
6
|
-
tags: [Tech, Data Science, Ruby, R, JRuby, "GNU R", Galaaz]
|
|
6
|
+
tags: [Tech, Data Science, Ruby, R, JRuby, CRuby, "GNU R", Galaaz]
|
|
7
7
|
date: "November 19th, 2018 (narrative updated for Galaaz 2.0, 2026)"
|
|
8
8
|
output:
|
|
9
9
|
html_document:
|
|
@@ -46,16 +46,16 @@ of libraries for data analysis.
|
|
|
46
46
|
Until recently, there was no real perspective for Ruby to bridge this gap.
|
|
47
47
|
Implementing a complete scientific computing infrastructure would take too long.
|
|
48
48
|
|
|
49
|
-
**Galaaz 2.0**
|
|
50
|
-
same R distribution used for data science everywhere. A **bridge** evaluates R from
|
|
51
|
-
exchanges data between the two processes so that, from Ruby, you call R functions and work
|
|
49
|
+
**Galaaz 2.0** supports **[JRuby](https://www.jruby.org/)** or **CRuby** equally and drives
|
|
50
|
+
**GNU R**—the same R distribution used for data science everywhere. A **bridge** evaluates R from
|
|
51
|
+
Ruby and exchanges data between the two processes so that, from Ruby, you call R functions and work
|
|
52
52
|
with R objects using familiar Ruby syntax. In other words, a Ruby programmer can use the
|
|
53
53
|
capabilities of R without memorizing all of R’s syntax for day-to-day tasks.
|
|
54
54
|
|
|
55
55
|
An **earlier line of work** used Oracle’s **GraalVM** with **TruffleRuby** and **FastR** so that
|
|
56
56
|
Ruby and R could share one JVM runtime. That stack is **no longer** what Galaaz targets;
|
|
57
|
-
today’s Galaaz is developed and tested with **JRuby + GNU R** (see the project manual for
|
|
58
|
-
and command-line tools).
|
|
57
|
+
today’s Galaaz is developed and tested with **JRuby or CRuby + GNU R** (see the project manual for
|
|
58
|
+
setup and command-line tools).
|
|
59
59
|
|
|
60
60
|
Library wrapping is a common way to bring features from one language into another.
|
|
61
61
|
To improve performance, Python often wraps more efficient C libraries. For the
|
|
@@ -84,7 +84,7 @@ ggplot themes that will work the same as the Ruby module. Yet, writing a new th
|
|
|
84
84
|
requires specific knowledge on theme writing. Ruby modules are standard to the
|
|
85
85
|
language and don't need special knowledge.
|
|
86
86
|
|
|
87
|
-
[Here](https://towardsdatascience.com/ruby-plotting-with-galaaz-an-example-of-tightly-coupling-ruby-and-r-in-graalvm-520b69e21021) is an older article (GraalVM-era Galaaz) with a scatter plot in Ruby; the **ideas** still apply under Galaaz 2.0 with JRuby and GNU R.
|
|
87
|
+
[Here](https://towardsdatascience.com/ruby-plotting-with-galaaz-an-example-of-tightly-coupling-ruby-and-r-in-graalvm-520b69e21021) is an older article (GraalVM-era Galaaz) with a scatter plot in Ruby; the **ideas** still apply under Galaaz 2.0 with JRuby or CRuby and GNU R.
|
|
88
88
|
|
|
89
89
|
# gKnit
|
|
90
90
|
|
|
@@ -549,12 +549,12 @@ Trying to bring to Ruby the power of R starting from scratch is an enormous ende
|
|
|
549
549
|
and would probably never be accomplished. Today's data scientists would certainly
|
|
550
550
|
stick with either Python or R. Now, both the Ruby and R communities can benefit
|
|
551
551
|
from this marriage: **Galaaz 2.0** uses **standard GNU R** for statistics and graphics
|
|
552
|
-
and **JRuby** for application code
|
|
553
|
-
the process to couple Ruby and R; the coupling is implemented by the
|
|
554
|
-
**gKnit** for literate documents, not by a single GraalVM polyglot runtime.
|
|
552
|
+
and **JRuby or CRuby** for application code (prefer **JRuby** when you want real threads and the
|
|
553
|
+
JVM ecosystem). We presented the process to couple Ruby and R; the coupling is implemented by the
|
|
554
|
+
Galaaz bridge and **gKnit** for literate documents, not by a single GraalVM polyglot runtime.
|
|
555
555
|
|
|
556
556
|
For performance, expect **ordinary GNU R** behaviour for model fitting and plotting, while
|
|
557
|
-
**JRuby**
|
|
557
|
+
**JRuby** can give **real parallel threads** on the Ruby side and access to Java libraries when
|
|
558
558
|
you need them.
|
|
559
559
|
|
|
560
560
|
This article has shown how to improve a plot step-by-step. Starting from a very simple
|
|
@@ -571,8 +571,7 @@ be of great help for any Rubyist trying to write articles, blogs or documentatio
|
|
|
571
571
|
|
|
572
572
|
## Prerequisites (Galaaz 2.0)
|
|
573
573
|
|
|
574
|
-
* **JRuby**
|
|
575
|
-
* A **JDK** compatible with your JRuby version
|
|
574
|
+
* **JRuby** and a compatible **JDK**, *or* **CRuby 3.3+**
|
|
576
575
|
* **GNU R** — `R` on your `PATH`, with compilers/tools available if packages must be built from source
|
|
577
576
|
|
|
578
577
|
The following R packages will be automatically installed when necessary, but could be installed prior
|
|
@@ -594,4 +593,4 @@ are commonly required.
|
|
|
594
593
|
|
|
595
594
|
* From the Galaaz repository (or your installed layout), render documents with **`bin/gknit`** (see the project manual for flags such as `--output_format all`).
|
|
596
595
|
* In Ruby code: `require 'galaaz'`
|
|
597
|
-
*
|
|
596
|
+
* Run Ruby with **`bin/galaaz-ruby`** (either engine) or **`bin/galaaz-jruby`** when you need JRuby JVM flags (see the manual).
|
|
@@ -3,7 +3,7 @@ title: "How to make Beautiful Ruby Plots with Galaaz"
|
|
|
3
3
|
author:
|
|
4
4
|
- "Rodrigo Botafogo"
|
|
5
5
|
- "Daniel Mossé - University of Pittsburgh"
|
|
6
|
-
tags: [Tech, Data Science, Ruby, R, JRuby, "GNU R", Galaaz]
|
|
6
|
+
tags: [Tech, Data Science, Ruby, R, JRuby, CRuby, "GNU R", Galaaz]
|
|
7
7
|
date: "November 19th, 2018 (narrative updated for Galaaz 2.0, 2026)"
|
|
8
8
|
output:
|
|
9
9
|
html_document:
|
|
@@ -42,16 +42,16 @@ of libraries for data analysis.
|
|
|
42
42
|
Until recently, there was no real perspective for Ruby to bridge this gap.
|
|
43
43
|
Implementing a complete scientific computing infrastructure would take too long.
|
|
44
44
|
|
|
45
|
-
**Galaaz 2.0**
|
|
46
|
-
same R distribution used for data science everywhere. A **bridge** evaluates R from
|
|
47
|
-
exchanges data between the two processes so that, from Ruby, you call R functions and work
|
|
45
|
+
**Galaaz 2.0** supports **[JRuby](https://www.jruby.org/)** or **CRuby** equally and drives
|
|
46
|
+
**GNU R**—the same R distribution used for data science everywhere. A **bridge** evaluates R from
|
|
47
|
+
Ruby and exchanges data between the two processes so that, from Ruby, you call R functions and work
|
|
48
48
|
with R objects using familiar Ruby syntax. In other words, a Ruby programmer can use the
|
|
49
49
|
capabilities of R without memorizing all of R’s syntax for day-to-day tasks.
|
|
50
50
|
|
|
51
51
|
An **earlier line of work** used Oracle’s **GraalVM** with **TruffleRuby** and **FastR** so that
|
|
52
52
|
Ruby and R could share one JVM runtime. That stack is **no longer** what Galaaz targets;
|
|
53
|
-
today’s Galaaz is developed and tested with **JRuby + GNU R** (see the project manual for
|
|
54
|
-
and command-line tools).
|
|
53
|
+
today’s Galaaz is developed and tested with **JRuby or CRuby + GNU R** (see the project manual for
|
|
54
|
+
setup and command-line tools).
|
|
55
55
|
|
|
56
56
|
Library wrapping is a usual way of bringing features from one language into another.
|
|
57
57
|
To improve performance, Python often wraps more efficient C libraries. For the
|
|
@@ -80,7 +80,7 @@ ggplot themes that will work the same as the Ruby module. Yet, writing a new th
|
|
|
80
80
|
requires specific knowledge on theme writing. Ruby modules are standard to the
|
|
81
81
|
language and don't need special knowledge.
|
|
82
82
|
|
|
83
|
-
[Here](https://towardsdatascience.com/ruby-plotting-with-galaaz-an-example-of-tightly-coupling-ruby-and-r-in-graalvm-520b69e21021) is an older article (GraalVM-era Galaaz) with a scatter plot in Ruby; the **ideas** still apply under Galaaz 2.0 with JRuby and GNU R.
|
|
83
|
+
[Here](https://towardsdatascience.com/ruby-plotting-with-galaaz-an-example-of-tightly-coupling-ruby-and-r-in-graalvm-520b69e21021) is an older article (GraalVM-era Galaaz) with a scatter plot in Ruby; the **ideas** still apply under Galaaz 2.0 with JRuby or CRuby and GNU R.
|
|
84
84
|
|
|
85
85
|
# gKnit
|
|
86
86
|
|
|
@@ -622,12 +622,12 @@ Trying to bring to Ruby the power of R starting from scratch is an enormous ende
|
|
|
622
622
|
and would probably never be accomplished. Today's data scientists would certainly
|
|
623
623
|
stick with either Python or R. Now, both the Ruby and R communities can benefit
|
|
624
624
|
from this marriage: **Galaaz 2.0** uses **standard GNU R** for statistics and graphics
|
|
625
|
-
and **JRuby** for application code
|
|
626
|
-
the process to couple Ruby and R; the coupling is implemented by the
|
|
627
|
-
**gKnit** for literate documents, not by a single GraalVM polyglot runtime.
|
|
625
|
+
and **JRuby or CRuby** for application code (prefer **JRuby** when you want real threads and the
|
|
626
|
+
JVM ecosystem). We presented the process to couple Ruby and R; the coupling is implemented by the
|
|
627
|
+
Galaaz bridge and **gKnit** for literate documents, not by a single GraalVM polyglot runtime.
|
|
628
628
|
|
|
629
629
|
For performance, expect **ordinary GNU R** behaviour for model fitting and plotting, while
|
|
630
|
-
**JRuby**
|
|
630
|
+
**JRuby** can give **real parallel threads** on the Ruby side and access to Java libraries when
|
|
631
631
|
you need them.
|
|
632
632
|
|
|
633
633
|
This article has shown how to improve a plot step-by-step. Starting from a very simple
|
|
@@ -644,8 +644,7 @@ be of great help for any Rubyist trying to write articles, blogs or documentatio
|
|
|
644
644
|
|
|
645
645
|
## Prerequisites (Galaaz 2.0)
|
|
646
646
|
|
|
647
|
-
* **JRuby**
|
|
648
|
-
* A **JDK** compatible with your JRuby version
|
|
647
|
+
* **JRuby** and a compatible **JDK**, *or* **CRuby 3.3+**
|
|
649
648
|
* **GNU R** — `R` on your `PATH`, with compilers/tools available if packages must be built from source
|
|
650
649
|
|
|
651
650
|
The following R packages will be automatically installed when necessary, but could be installed prior
|
|
@@ -667,4 +666,4 @@ are commonly required.
|
|
|
667
666
|
|
|
668
667
|
* From the Galaaz repository (or your installed layout), render documents with **`bin/gknit`** (see the project manual for flags such as `--output_format all`).
|
|
669
668
|
* In Ruby code: `require 'galaaz'`
|
|
670
|
-
*
|
|
669
|
+
* Run Ruby with **`bin/galaaz-ruby`** (either engine) or **`bin/galaaz-jruby`** when you need JRuby JVM flags (see the manual).
|
data/examples/misc/ggplot.rb
CHANGED
|
@@ -24,7 +24,21 @@ module R
|
|
|
24
24
|
File.expand_path('../../ext/new_bridge/galaaz_gatekeeper_phase1.cpp', __dir__)
|
|
25
25
|
@client = NewBridge::SessionClient.new(source_path: source_path)
|
|
26
26
|
@client.start(accept_timeout: 120)
|
|
27
|
+
bootstrap_session!
|
|
28
|
+
@ready = true
|
|
29
|
+
end
|
|
27
30
|
|
|
31
|
+
# After a hung/timed-out R eval (e.g. install.packages), kill R and start a fresh runtime
|
|
32
|
+
# so the compile does not keep running and OOM the host shell.
|
|
33
|
+
def restart_runtime!(accept_timeout: 120)
|
|
34
|
+
@ready = false
|
|
35
|
+
@client.restart!(accept_timeout: accept_timeout)
|
|
36
|
+
bootstrap_session!
|
|
37
|
+
@ready = true
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def bootstrap_session!
|
|
28
42
|
# Compatibility with the legacy ShadowBridge setup:
|
|
29
43
|
# - `R.awt` / X11 for plotting (examples/sthda_ggplot).
|
|
30
44
|
# - `missing_arg()` for Ruby :all in `[` / tbl subset (R::Support.parse_arg).
|
|
@@ -117,7 +131,6 @@ module R
|
|
|
117
131
|
parent_id: callback_parent_id,
|
|
118
132
|
timeout: init_timeout
|
|
119
133
|
)
|
|
120
|
-
@ready = true
|
|
121
134
|
end
|
|
122
135
|
|
|
123
136
|
def ready?
|
data/lib/R_interface/r.rb
CHANGED
|
@@ -30,6 +30,7 @@ dir = File.dirname(File.expand_path('.', __FILE__))
|
|
|
30
30
|
|
|
31
31
|
# Bridge and Support first
|
|
32
32
|
require_relative 'rsupport'
|
|
33
|
+
require_relative 'r_job'
|
|
33
34
|
|
|
34
35
|
# Operator modules next (so R::Object can include them)
|
|
35
36
|
require_relative 'rbinary_operators'
|
|
@@ -134,68 +135,74 @@ module R
|
|
|
134
135
|
|
|
135
136
|
def self.install_rlibs(*libs, install_timeout_sec: nil, callback_timeout_ms: nil, bridge_timeout_sec: nil)
|
|
136
137
|
with_callback_timeout_ms(callback_timeout_ms) do
|
|
138
|
+
# Await wall-clock for the child Rscript job (nil = wait forever).
|
|
139
|
+
# Prefer install_timeout_sec / GALAAZ_INSTALL_TIMEOUT_SEC; bridge_timeout_sec
|
|
140
|
+
# is legacy and only used if the others are unset.
|
|
141
|
+
await_timeout = install_timeout_sec
|
|
142
|
+
if await_timeout.nil?
|
|
143
|
+
raw = ENV['GALAAZ_INSTALL_TIMEOUT_SEC']
|
|
144
|
+
unless raw.nil? || raw.to_s.strip.empty?
|
|
145
|
+
await_timeout = begin
|
|
146
|
+
Integer(raw)
|
|
147
|
+
rescue StandardError, ArgumentError
|
|
148
|
+
nil
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
await_timeout = bridge_timeout_sec if await_timeout.nil? && !bridge_timeout_sec.nil?
|
|
137
153
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
R.bridge.eval_r(".libPaths(c('#{lib_dir.gsub("'", "\\\\'")}', .libPaths()))", timeout: bridge_timeout_sec)
|
|
154
|
+
lib_dir = R::Job.default_lib_dir
|
|
155
|
+
FileUtils.mkdir_p(lib_dir)
|
|
156
|
+
R.bridge.eval_r(".libPaths(c('#{lib_dir.gsub("'", "\\\\'")}', .libPaths()))", timeout: 60)
|
|
142
157
|
|
|
143
158
|
packages = R.c(*libs)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
r_cmd = "install.packages('#{pkg.gsub("'", "\\\\'")}', repos='https://cloud.r-project.org', lib='#{lib_dir.gsub("'", "\\\\'")}', dependencies=NA)"
|
|
172
|
-
$stderr.puts "[RUBY] R command: #{r_cmd}"
|
|
173
|
-
result = R.bridge.eval_r(r_cmd, timeout: bridge_timeout_sec)
|
|
174
|
-
$stderr.puts "[RUBY] Install output: #{result.inspect}"
|
|
175
|
-
if result.to_s.include?("ANTICONF") || result.to_s.include?("Configuration failed")
|
|
176
|
-
$stderr.puts "[RUBY] WARNING: Package #{pkg} failed to install due to missing system libraries."
|
|
177
|
-
$stderr.puts "[RUBY] For kableExtra, you may need: libfontconfig1-dev libxml2-dev libfreetype6-dev"
|
|
159
|
+
installed_mat = R.installed__packages(nil)
|
|
160
|
+
installed_names = R.rownames(installed_mat)
|
|
161
|
+
new_packages = packages[!(packages._ :in, installed_names)]
|
|
162
|
+
new_packages_str = new_packages.to_s
|
|
163
|
+
|
|
164
|
+
if new_packages.length > 0 && new_packages_str != 'character(0)'
|
|
165
|
+
pkg_list = []
|
|
166
|
+
new_packages.each(:native) { |pkg| pkg_list << pkg }
|
|
167
|
+
$stderr.puts "[RUBY] The following packages are missing and will be installed via R::Job: #{pkg_list.inspect}"
|
|
168
|
+
$stderr.puts "[RUBY] Installing to: #{lib_dir}"
|
|
169
|
+
$stderr.puts "[RUBY] Bridge R stays free; awaiting child Rscript" +
|
|
170
|
+
(await_timeout ? " (timeout=#{await_timeout}s)" : ' (no await timeout)')
|
|
171
|
+
|
|
172
|
+
pkg_list.each do |pkg|
|
|
173
|
+
$stderr.puts "[RUBY] R::Job.install #{pkg}..."
|
|
174
|
+
begin
|
|
175
|
+
job = R::Job.install(pkg, lib_dir: lib_dir, wait: true, timeout: await_timeout)
|
|
176
|
+
job.raise_if_failed!
|
|
177
|
+
$stderr.puts "[RUBY] R::Job #{job.id} OK for #{pkg}"
|
|
178
|
+
rescue R::Job::Timeout => e
|
|
179
|
+
# Child Rscript process group already killed inside Job#wait.
|
|
180
|
+
raise "R package '#{pkg}' install job timed out. #{e.message}\n" \
|
|
181
|
+
"Install outside gknit if needed, then re-run:\n" \
|
|
182
|
+
" Rscript -e \"install.packages('#{pkg}', lib='#{lib_dir}', repos='#{R::Job::DEFAULT_REPOS}')\""
|
|
183
|
+
rescue R::Job::Failed => e
|
|
184
|
+
raise "Failed to install R package '#{pkg}' via R::Job.\n#{e.message}"
|
|
185
|
+
end
|
|
178
186
|
end
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
187
|
+
|
|
188
|
+
# Refresh bridge view of libraries after child install.
|
|
189
|
+
R.bridge.eval_r(".libPaths(c('#{lib_dir.gsub("'", "\\\\'")}', .libPaths()))", timeout: 60)
|
|
190
|
+
installed_mat = R.installed__packages(nil)
|
|
191
|
+
installed_names = R.rownames(installed_mat)
|
|
192
|
+
still_missing = packages[!(packages._ :in, installed_names)]
|
|
193
|
+
still_missing_str = still_missing.to_s
|
|
194
|
+
if still_missing.length > 0 && still_missing_str != 'character(0)'
|
|
195
|
+
raise "Failed to install packages: #{still_missing}. Check ~/.local/share/galaaz/jobs/*/job.log"
|
|
184
196
|
end
|
|
185
197
|
end
|
|
186
|
-
|
|
187
|
-
# Re-check installed packages after install attempt
|
|
188
|
-
installed_mat = R.installed__packages(nil)
|
|
189
|
-
installed_names = R.rownames(installed_mat)
|
|
190
|
-
still_missing = packages[!(packages._ :in, installed_names)]
|
|
191
|
-
if still_missing.length > 0
|
|
192
|
-
raise "Failed to install packages: #{still_missing.to_s}. Check stderr output above for [RUBY] debug messages."
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
|
|
196
198
|
end
|
|
197
199
|
end
|
|
198
200
|
|
|
201
|
+
# Start a CRAN install Job without awaiting (advanced). Prefer +install_and_loads+.
|
|
202
|
+
def self.install_async(*libs, lib_dir: R::Job.default_lib_dir)
|
|
203
|
+
R::Job.install(*libs, lib_dir: lib_dir, wait: false)
|
|
204
|
+
end
|
|
205
|
+
|
|
199
206
|
#----------------------------------------------------------------------------------------
|
|
200
207
|
#
|
|
201
208
|
#----------------------------------------------------------------------------------------
|