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.
@@ -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** couples **[JRuby](https://www.jruby.org/)** (Ruby on the JVM) with **GNU R**—the
50
- same R distribution used for data science everywhere. A **bridge** evaluates R from Ruby and
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 setup
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, threading, and the JVM ecosystem. We presented
553
- the process to couple Ruby and R; the coupling is implemented by the Galaaz bridge and
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** gives **real parallel threads** on the Ruby side and access to Java libraries when
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** Ruby on the JVM ([jruby.org](https://www.jruby.org/))
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
- * For running scripts with the correct JRuby and JVM options, use **`bin/galaaz-jruby`** as described in the manual.
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** couples **[JRuby](https://www.jruby.org/)** (Ruby on the JVM) with **GNU R**—the
46
- same R distribution used for data science everywhere. A **bridge** evaluates R from Ruby and
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 setup
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, threading, and the JVM ecosystem. We presented
626
- the process to couple Ruby and R; the coupling is implemented by the Galaaz bridge and
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** gives **real parallel threads** on the Ruby side and access to Java libraries when
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** Ruby on the JVM ([jruby.org](https://www.jruby.org/))
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
- * For running scripts with the correct JRuby and JVM options, use **`bin/galaaz-jruby`** as described in the manual.
669
+ * Run Ruby with **`bin/galaaz-ruby`** (either engine) or **`bin/galaaz-jruby`** when you need JRuby JVM flags (see the manual).
@@ -151,7 +151,7 @@ R.grid__newpage
151
151
  # R.dev__off('')
152
152
  # R.awt
153
153
 
154
- (R.ggplot(R.mtcars, E.aes(x: :wt, y: :mpg)) +
154
+ (R.ggplot(~:mtcars, E.aes(x: :wt, y: :mpg)) +
155
155
  R.geom_point('')).print
156
156
  =end
157
157
 
@@ -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
- # Use a fixed local library directory and ensure R can see it
139
- lib_dir = File.expand_path("~/R/x86_64-pc-linux-gnu-library/galaaz")
140
- FileUtils.mkdir_p(lib_dir) unless Dir.exist?(lib_dir)
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
- # installed.packages() returns a matrix; package names are in the row names.
146
- # Using [:all, "Package"] on this matrix ends up calling [[ with a missing
147
- # row subscript in R, which raises "missing subscript". Instead, rely on
148
- # the row names vector for the list of installed packages.
149
- installed_mat = R.installed__packages(nil)
150
- installed_names = R.rownames(installed_mat)
151
-
152
- new_packages = packages[!(packages._ :in, installed_names)]
153
- new_packages_str = new_packages.to_s
154
-
155
- if(new_packages.length > 0 && new_packages_str != "character(0)")
156
- $stderr.puts "[RUBY] The following packages are missing and will be installed: #{new_packages_str}"
157
- $stderr.puts "[RUBY] Installing to: #{lib_dir}"
158
-
159
- # Get packages as Ruby array using :native mode
160
- pkg_list = []
161
- new_packages.each(:native) { |pkg| pkg_list << pkg }
162
- $stderr.puts "[RUBY] Package list: #{pkg_list.inspect}"
163
-
164
- # Install each package
165
- pkg_list.each do |pkg|
166
- $stderr.puts "[RUBY] Installing #{pkg}..."
167
- if install_timeout_sec
168
- timeout_i = Integer(install_timeout_sec)
169
- R.bridge.eval_r("options(timeout=#{timeout_i})", timeout: bridge_timeout_sec)
170
- end
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
- ensure
180
- begin
181
- R.bridge.eval_r("options(timeout=60)", timeout: bridge_timeout_sec)
182
- rescue StandardError
183
- nil
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
  #----------------------------------------------------------------------------------------