galaaz 2.1.7 → 2.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 +4 -4
- data/CHANGELOG.md +39 -0
- data/Rakefile +20 -2
- data/bin/check_gemfile_lock_version +46 -0
- data/bin/release_bump +26 -0
- data/blogs/README.md +4 -0
- data/blogs/galaaz_2_0/galaaz_2_0.Rmd +385 -0
- data/blogs/galaaz_2_0/galaaz_2_0.md +409 -0
- data/blogs/galaaz_2_0/galaaz_2_0.tex +756 -0
- data/blogs/galaaz_2_0/images/galaaz-header.png +0 -0
- data/blogs/galaaz_2_0/images/galaaz-lockup-stacked.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot.Rmd +76 -58
- data/blogs/galaaz_ggplot/galaaz_ggplot.md +188 -161
- data/blogs/galaaz_ggplot/galaaz_ggplot.tex +185 -117
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/midwest_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/scatter_plot_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/midwest_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/scatter_plot_rb.png +0 -0
- data/blogs/galaaz_ggplot/images/galaaz-lockup-stacked.png +0 -0
- data/blogs/gknit/gknit.Rmd +48 -28
- data/blogs/gknit/gknit.md +60 -43
- data/blogs/gknit/gknit.tex +1409 -0
- data/blogs/gknit/gknit_files/figure-html/bubble-1.png +0 -0
- data/blogs/gknit/gknit_files/figure-html/diverging_bar.png +0 -0
- data/blogs/gknit/gknit_files/figure-latex/bubble-1.png +0 -0
- data/blogs/gknit/images/galaaz-lockup-stacked.png +0 -0
- data/blogs/manual/images/galaaz-lockup-stacked.png +0 -0
- data/blogs/manual/manual.Rmd +161 -71
- data/blogs/manual/manual.md +573 -482
- data/blogs/manual/manual.tex +1113 -517
- data/blogs/manual/manual_files/figure-html/bubble-1.png +0 -0
- data/blogs/manual/manual_files/figure-latex/bubble-1.png +0 -0
- data/blogs/manual/manual_files/figure-markdown_github/bubble-1.png +0 -0
- data/blogs/manual/manual_files/figure-markdown_github/diverging_bar.png +0 -0
- data/blogs/nse_dplyr/images/galaaz-lockup-stacked.png +0 -0
- data/blogs/nse_dplyr/nse_dplyr.Rmd +42 -8
- data/blogs/nse_dplyr/nse_dplyr.md +69 -50
- data/blogs/nse_dplyr/nse_dplyr.tex +1626 -0
- data/blogs/oh_my/images/galaaz-lockup-stacked.png +0 -0
- data/blogs/oh_my/oh_my.Rmd +206 -55
- data/blogs/oh_my/oh_my.md +247 -96
- data/blogs/oh_my/oh_my.tex +2038 -93
- data/blogs/r_on_rails_ledger/images/00_portfolio_page.png +0 -0
- data/blogs/r_on_rails_ledger/images/01_results_panel.png +0 -0
- data/blogs/r_on_rails_ledger/images/02_density_tail_risk.png +0 -0
- data/blogs/r_on_rails_ledger/images/03_mc_cone.png +0 -0
- data/blogs/r_on_rails_ledger/images/04_rolling_var.png +0 -0
- data/blogs/r_on_rails_ledger/images/galaaz-lockup-stacked.png +0 -0
- data/blogs/r_on_rails_ledger/r_on_rails_ledger.Rmd +354 -0
- data/blogs/r_on_rails_ledger/r_on_rails_ledger.md +365 -0
- data/blogs/r_on_rails_ledger/r_on_rails_ledger.tex +670 -0
- data/blogs/ruby_plot/images/galaaz-lockup-stacked.png +0 -0
- data/blogs/ruby_plot/ruby_plot.Rmd +56 -35
- data/blogs/ruby_plot/ruby_plot.md +63 -47
- data/blogs/ruby_plot/ruby_plot.tex +194 -125
- data/blogs/ruby_plot/ruby_plot_files/figure-html/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/violin_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/violin_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/ruby_plot_files/figure-latex/violin_with_jitter.png +0 -0
- data/lib/galaaz/cli.rb +98 -13
- data/logos/icon-font/README.md +27 -0
- data/logos/icon-font/build_font.py +130 -0
- data/logos/icon-font/galaaz-mark.svg +34 -0
- data/script/omarchy/README.md +9 -2
- data/script/omarchy/fonts/galaaz.ttf +0 -0
- data/script/omarchy/galaaz-guide.sh +1 -1
- data/script/omarchy/install-galaaz.sh +8 -1
- data/script/omarchy/omarchy-menu.jsonc +21 -9
- data/sty/galaaz-header.png +0 -0
- data/sty/galaaz-headers-from-p3.tex +4 -0
- data/sty/galaaz.sty +76 -23
- data/version.rb +1 -1
- metadata +46 -7
- data/blogs/galaaz_ggplot/galaaz_ggplot.log +0 -754
- data/blogs/manual/manual.log +0 -1786
- data/blogs/nse_dplyr/nse_dplyr.log +0 -928
- data/blogs/oh_my/oh_my.log +0 -804
- data/blogs/ruby_plot/ruby_plot.log +0 -885
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
% Options for packages loaded elsewhere
|
|
2
|
+
\PassOptionsToPackage{unicode}{hyperref}
|
|
3
|
+
\PassOptionsToPackage{hyphens}{url}
|
|
4
|
+
\documentclass[
|
|
5
|
+
11pt,
|
|
6
|
+
]{article}
|
|
7
|
+
\usepackage{xcolor}
|
|
8
|
+
\usepackage[margin=1in]{geometry}
|
|
9
|
+
\usepackage{amsmath,amssymb}
|
|
10
|
+
\setcounter{secnumdepth}{5}
|
|
11
|
+
\usepackage{iftex}
|
|
12
|
+
\ifPDFTeX
|
|
13
|
+
\usepackage[T1]{fontenc}
|
|
14
|
+
\usepackage[utf8]{inputenc}
|
|
15
|
+
\usepackage{textcomp} % provide euro and other symbols
|
|
16
|
+
\else % if luatex or xetex
|
|
17
|
+
\usepackage{unicode-math} % this also loads fontspec
|
|
18
|
+
\defaultfontfeatures{Scale=MatchLowercase}
|
|
19
|
+
\defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
|
|
20
|
+
\fi
|
|
21
|
+
\usepackage{lmodern}
|
|
22
|
+
\ifPDFTeX\else
|
|
23
|
+
% xetex/luatex font selection
|
|
24
|
+
\fi
|
|
25
|
+
% Use upquote if available, for straight quotes in verbatim environments
|
|
26
|
+
\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
|
|
27
|
+
\IfFileExists{microtype.sty}{% use microtype if available
|
|
28
|
+
\usepackage[]{microtype}
|
|
29
|
+
\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
|
|
30
|
+
}{}
|
|
31
|
+
\makeatletter
|
|
32
|
+
\@ifundefined{KOMAClassName}{% if non-KOMA class
|
|
33
|
+
\IfFileExists{parskip.sty}{%
|
|
34
|
+
\usepackage{parskip}
|
|
35
|
+
}{% else
|
|
36
|
+
\setlength{\parindent}{0pt}
|
|
37
|
+
\setlength{\parskip}{6pt plus 2pt minus 1pt}}
|
|
38
|
+
}{% if KOMA class
|
|
39
|
+
\KOMAoptions{parskip=half}}
|
|
40
|
+
\makeatother
|
|
41
|
+
\usepackage{color}
|
|
42
|
+
\usepackage{fancyvrb}
|
|
43
|
+
\newcommand{\VerbBar}{|}
|
|
44
|
+
\newcommand{\VERB}{\Verb[commandchars=\\\{\}]}
|
|
45
|
+
\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
|
|
46
|
+
% Add ',fontsize=\small' for more characters per line
|
|
47
|
+
\usepackage{framed}
|
|
48
|
+
\definecolor{shadecolor}{RGB}{248,248,248}
|
|
49
|
+
\newenvironment{Shaded}{\begin{snugshade}}{\end{snugshade}}
|
|
50
|
+
\newcommand{\AlertTok}[1]{\textcolor[rgb]{0.94,0.16,0.16}{#1}}
|
|
51
|
+
\newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textbf{\textit{#1}}}}
|
|
52
|
+
\newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.13,0.29,0.53}{#1}}
|
|
53
|
+
\newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.00,0.00,0.81}{#1}}
|
|
54
|
+
\newcommand{\BuiltInTok}[1]{#1}
|
|
55
|
+
\newcommand{\CharTok}[1]{\textcolor[rgb]{0.31,0.60,0.02}{#1}}
|
|
56
|
+
\newcommand{\CommentTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textit{#1}}}
|
|
57
|
+
\newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textbf{\textit{#1}}}}
|
|
58
|
+
\newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{#1}}
|
|
59
|
+
\newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.13,0.29,0.53}{\textbf{#1}}}
|
|
60
|
+
\newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.13,0.29,0.53}{#1}}
|
|
61
|
+
\newcommand{\DecValTok}[1]{\textcolor[rgb]{0.00,0.00,0.81}{#1}}
|
|
62
|
+
\newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textbf{\textit{#1}}}}
|
|
63
|
+
\newcommand{\ErrorTok}[1]{\textcolor[rgb]{0.64,0.00,0.00}{\textbf{#1}}}
|
|
64
|
+
\newcommand{\ExtensionTok}[1]{#1}
|
|
65
|
+
\newcommand{\FloatTok}[1]{\textcolor[rgb]{0.00,0.00,0.81}{#1}}
|
|
66
|
+
\newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.13,0.29,0.53}{\textbf{#1}}}
|
|
67
|
+
\newcommand{\ImportTok}[1]{#1}
|
|
68
|
+
\newcommand{\InformationTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textbf{\textit{#1}}}}
|
|
69
|
+
\newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.13,0.29,0.53}{\textbf{#1}}}
|
|
70
|
+
\newcommand{\NormalTok}[1]{#1}
|
|
71
|
+
\newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.81,0.36,0.00}{\textbf{#1}}}
|
|
72
|
+
\newcommand{\OtherTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{#1}}
|
|
73
|
+
\newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textit{#1}}}
|
|
74
|
+
\newcommand{\RegionMarkerTok}[1]{#1}
|
|
75
|
+
\newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.81,0.36,0.00}{\textbf{#1}}}
|
|
76
|
+
\newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.31,0.60,0.02}{#1}}
|
|
77
|
+
\newcommand{\StringTok}[1]{\textcolor[rgb]{0.31,0.60,0.02}{#1}}
|
|
78
|
+
\newcommand{\VariableTok}[1]{\textcolor[rgb]{0.00,0.00,0.00}{#1}}
|
|
79
|
+
\newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.31,0.60,0.02}{#1}}
|
|
80
|
+
\newcommand{\WarningTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textbf{\textit{#1}}}}
|
|
81
|
+
\usepackage{longtable,booktabs,array}
|
|
82
|
+
\newcounter{none} % for unnumbered tables
|
|
83
|
+
\usepackage{calc} % for calculating minipage widths
|
|
84
|
+
% Correct order of tables after \paragraph or \subparagraph
|
|
85
|
+
\usepackage{etoolbox}
|
|
86
|
+
\makeatletter
|
|
87
|
+
\patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{}
|
|
88
|
+
\makeatother
|
|
89
|
+
% Allow footnotes in longtable head/foot
|
|
90
|
+
\IfFileExists{footnotehyper.sty}{\usepackage{footnotehyper}}{\usepackage{footnote}}
|
|
91
|
+
\makesavenoteenv{longtable}
|
|
92
|
+
\usepackage{graphicx}
|
|
93
|
+
\makeatletter
|
|
94
|
+
\newsavebox\pandoc@box
|
|
95
|
+
\newcommand*\pandocbounded[1]{% scales image to fit in text height/width
|
|
96
|
+
\sbox\pandoc@box{#1}%
|
|
97
|
+
\Gscale@div\@tempa{\textheight}{\dimexpr\ht\pandoc@box+\dp\pandoc@box\relax}%
|
|
98
|
+
\Gscale@div\@tempb{\linewidth}{\wd\pandoc@box}%
|
|
99
|
+
\ifdim\@tempb\p@<\@tempa\p@\let\@tempa\@tempb\fi% select the smaller of both
|
|
100
|
+
\ifdim\@tempa\p@<\p@\scalebox{\@tempa}{\usebox\pandoc@box}%
|
|
101
|
+
\else\usebox{\pandoc@box}%
|
|
102
|
+
\fi%
|
|
103
|
+
}
|
|
104
|
+
% Set default figure placement to htbp
|
|
105
|
+
\def\fps@figure{htbp}
|
|
106
|
+
\makeatother
|
|
107
|
+
\setlength{\emergencystretch}{3em} % prevent overfull lines
|
|
108
|
+
\providecommand{\tightlist}{%
|
|
109
|
+
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
|
|
110
|
+
% usar portugues do Brasil
|
|
111
|
+
% \usepackage[brazilian]{babel}
|
|
112
|
+
\usepackage[utf8]{inputenc}
|
|
113
|
+
|
|
114
|
+
\usepackage{geometry}
|
|
115
|
+
\geometry{a4paper, top=1.05in, headheight=30pt, headsep=14pt}
|
|
116
|
+
|
|
117
|
+
% needed for kableExtra
|
|
118
|
+
\usepackage{longtable}
|
|
119
|
+
\usepackage{multirow}
|
|
120
|
+
\usepackage[table]{xcolor}
|
|
121
|
+
\usepackage{wrapfig}
|
|
122
|
+
\usepackage{float}
|
|
123
|
+
\usepackage{colortbl}
|
|
124
|
+
\usepackage{pdflscape}
|
|
125
|
+
\usepackage{tabu}
|
|
126
|
+
\usepackage{threeparttable}
|
|
127
|
+
\usepackage[normalem]{ulem}
|
|
128
|
+
|
|
129
|
+
\usepackage{bbm}
|
|
130
|
+
\usepackage{booktabs}
|
|
131
|
+
\usepackage{expex}
|
|
132
|
+
|
|
133
|
+
\usepackage{graphicx}
|
|
134
|
+
|
|
135
|
+
\usepackage{fancyhdr}
|
|
136
|
+
% Header: small Galaaz mark (left); page number + section title (right),
|
|
137
|
+
% matching classic LaTeX style: "4 AN EXTENSION TO THE EXAMPLE"
|
|
138
|
+
% (upright page number, uppercase italic section name).
|
|
139
|
+
% Logo paths cover repo blogs (../../sty/), installed ~/sty/, and local images/.
|
|
140
|
+
% Blogs may delay headers with \renewcommand{\galaazheaderfrompage}{3} after this sty
|
|
141
|
+
% (e.g. skip title/TOC and a logo splash page).
|
|
142
|
+
\newcommand{\galaazheaderfrompage}{1}
|
|
143
|
+
\newcommand{\galaazheaderlogo}{%
|
|
144
|
+
\raisebox{-0.12\height}{%
|
|
145
|
+
\IfFileExists{../../sty/galaaz-header.png}{%
|
|
146
|
+
\includegraphics[height=0.32in]{../../sty/galaaz-header.png}%
|
|
147
|
+
}{%
|
|
148
|
+
\IfFileExists{galaaz-header.png}{%
|
|
149
|
+
\includegraphics[height=0.32in]{galaaz-header.png}%
|
|
150
|
+
}{%
|
|
151
|
+
\IfFileExists{images/galaaz-header.png}{%
|
|
152
|
+
\includegraphics[height=0.32in]{images/galaaz-header.png}%
|
|
153
|
+
}{}%
|
|
154
|
+
}%
|
|
155
|
+
}%
|
|
156
|
+
}%
|
|
157
|
+
}
|
|
158
|
+
% Section title only in marks (no \thesection); page number is separate.
|
|
159
|
+
\renewcommand{\sectionmark}[1]{\markboth{#1}{}}
|
|
160
|
+
\renewcommand{\subsectionmark}[1]{}% keep the current section in the header
|
|
161
|
+
\newcommand{\galaazmaybeheaderlogo}{%
|
|
162
|
+
\ifnum\value{page}<\galaazheaderfrompage\relax\else\galaazheaderlogo\fi}
|
|
163
|
+
\newcommand{\galaazmayberighthead}{%
|
|
164
|
+
\ifnum\value{page}<\galaazheaderfrompage\relax
|
|
165
|
+
\else
|
|
166
|
+
\textup{\thepage}\hspace{1em}\textit{\MakeUppercase{\leftmark}}%
|
|
167
|
+
\fi
|
|
168
|
+
}
|
|
169
|
+
\pagestyle{fancy}
|
|
170
|
+
\fancyhf{}
|
|
171
|
+
\fancyhead[L]{\galaazmaybeheaderlogo}
|
|
172
|
+
\fancyhead[R]{\galaazmayberighthead}
|
|
173
|
+
\renewcommand{\headrulewidth}{0.3pt}
|
|
174
|
+
\makeatletter
|
|
175
|
+
\renewcommand{\headrule}{%
|
|
176
|
+
\ifnum\value{page}<\galaazheaderfrompage\relax
|
|
177
|
+
\else
|
|
178
|
+
\hrule\@height\headrulewidth\@width\headwidth
|
|
179
|
+
\vskip-\headrulewidth
|
|
180
|
+
\fi
|
|
181
|
+
}
|
|
182
|
+
\makeatother
|
|
183
|
+
% First page often uses plain; same conditional header logic.
|
|
184
|
+
\fancypagestyle{plain}{%
|
|
185
|
+
\fancyhf{}%
|
|
186
|
+
\fancyhead[L]{\galaazmaybeheaderlogo}%
|
|
187
|
+
\fancyhead[R]{\galaazmayberighthead}%
|
|
188
|
+
\renewcommand{\headrulewidth}{0.3pt}%
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
% make available command lastpage
|
|
192
|
+
\usepackage{lastpage}
|
|
193
|
+
|
|
194
|
+
% default fontsize 11pt better to add
|
|
195
|
+
% fontsize on the yaml header
|
|
196
|
+
% \usepackage[fontsize=11pt]{scrextend}
|
|
197
|
+
|
|
198
|
+
% comandos para formatar uma tabela
|
|
199
|
+
\usepackage{array}
|
|
200
|
+
\newcolumntype{L}[1]{>{\raggedright\let\newline\\\arraybackslash\hspace{0pt}}m{#1}}
|
|
201
|
+
\newcolumntype{C}[1]{>{\centering\let\newline\\\arraybackslash\hspace{0pt}}m{#1}}
|
|
202
|
+
\newcolumntype{R}[1]{>{\raggedleft\let\newline\\\arraybackslash\hspace{0pt}}m{#1}}
|
|
203
|
+
|
|
204
|
+
% necessário if we need to import other latex documents
|
|
205
|
+
\usepackage{import}
|
|
206
|
+
|
|
207
|
+
% Command to import an R variable to latex
|
|
208
|
+
\newcommand{\RtoLatex}[2]{\newcommand{#1}{#2}}
|
|
209
|
+
|
|
210
|
+
% Soft-wrap Pandoc highlighted code/output boxes (Shaded + Highlighting).
|
|
211
|
+
% This runs after Pandoc's default \DefineVerbatimEnvironment{Highlighting}
|
|
212
|
+
% (header includes come later in the generated .tex). Prefer manual line
|
|
213
|
+
% breaks in Rmd sources; breaklines is a safety net for leftovers/output.
|
|
214
|
+
% Requires TinyTeX/TeX Live package: tlmgr install fvextra
|
|
215
|
+
\IfFileExists{fvextra.sty}{%
|
|
216
|
+
\usepackage{fvextra}%
|
|
217
|
+
\DefineVerbatimEnvironment{Highlighting}{Verbatim}{%
|
|
218
|
+
breaklines=true,
|
|
219
|
+
breakanywhere=true,
|
|
220
|
+
breakindent=1.5em,
|
|
221
|
+
fontsize=\small,
|
|
222
|
+
commandchars=\\\{\}%
|
|
223
|
+
}%
|
|
224
|
+
}{%
|
|
225
|
+
% fancyvrb is already loaded by Pandoc; shrink code so more fits per line.
|
|
226
|
+
\DefineVerbatimEnvironment{Highlighting}{Verbatim}{%
|
|
227
|
+
fontsize=\small,
|
|
228
|
+
commandchars=\\\{\}%
|
|
229
|
+
}%
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
%
|
|
233
|
+
%\newcommand{\atraso}[1]{\color{red} \textbf {Tempo desde a Assinatura do Contrato: #1 dias}}
|
|
234
|
+
% No header on title/TOC (p.1) or splash logo page (p.2);
|
|
235
|
+
% logo + page number + section title from page 3 onward.
|
|
236
|
+
% Load after galaaz.sty.
|
|
237
|
+
\renewcommand{\galaazheaderfrompage}{3}
|
|
238
|
+
\usepackage{bookmark}
|
|
239
|
+
\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
|
|
240
|
+
\urlstyle{same}
|
|
241
|
+
\hypersetup{
|
|
242
|
+
pdftitle={Galaaz 2.0: Ruby meets real GNU R (again)},
|
|
243
|
+
pdfauthor={Rodrigo Botafogo},
|
|
244
|
+
hidelinks,
|
|
245
|
+
pdfcreator={LaTeX via pandoc}}
|
|
246
|
+
|
|
247
|
+
\title{Galaaz 2.0: Ruby meets real GNU R (again)}
|
|
248
|
+
\usepackage{etoolbox}
|
|
249
|
+
\makeatletter
|
|
250
|
+
\providecommand{\subtitle}[1]{% add subtitle to \maketitle
|
|
251
|
+
\apptocmd{\@title}{\par {\large #1 \par}}{}{}
|
|
252
|
+
}
|
|
253
|
+
\makeatother
|
|
254
|
+
\subtitle{NewBridge, JRuby or CRuby, CRAN/Bioconductor, and Arrow-shaped
|
|
255
|
+
data paths}
|
|
256
|
+
\author{Rodrigo Botafogo}
|
|
257
|
+
\date{2026}
|
|
258
|
+
|
|
259
|
+
\begin{document}
|
|
260
|
+
\maketitle
|
|
261
|
+
|
|
262
|
+
{
|
|
263
|
+
\setcounter{tocdepth}{2}
|
|
264
|
+
\tableofcontents
|
|
265
|
+
}
|
|
266
|
+
\begin{center}\includegraphics[width=0.45\linewidth]{images/galaaz-lockup-stacked} \end{center}
|
|
267
|
+
|
|
268
|
+
\section{Introduction}\label{introduction}
|
|
269
|
+
|
|
270
|
+
Ruby is excellent for web apps, orchestration, and expressive object
|
|
271
|
+
models. It has never matched \textbf{R} for statistics, Bioconductor
|
|
272
|
+
pipelines, or the depth of CRAN. \textbf{Python} closed a similar gap
|
|
273
|
+
with NumPy, pandas, and a huge scientific ecosystem. For Ruby, the
|
|
274
|
+
practical answer is not to reimplement that world --- it is to
|
|
275
|
+
\textbf{drive real R} from idiomatic Ruby.
|
|
276
|
+
|
|
277
|
+
\textbf{Galaaz} is that coupling. An earlier line of work ran on
|
|
278
|
+
Oracle's \textbf{GraalVM} with \textbf{TruffleRuby} and \textbf{FastR}
|
|
279
|
+
in one JVM. \textbf{Galaaz 2.0} is a different architecture:
|
|
280
|
+
\textbf{JRuby or CRuby} talk to \textbf{standard GNU R} over a process
|
|
281
|
+
bridge we call \textbf{NewBridge}. You keep CRAN and Bioconductor
|
|
282
|
+
(including compiled packages), and you keep a normal Ruby toolchain.
|
|
283
|
+
|
|
284
|
+
This post is the 2.0 story: what changed, how the bridge works, how to
|
|
285
|
+
pin R versions in containers, how data moves (including \textbf{Apache
|
|
286
|
+
Arrow}), and how to keep the bridge free for long R jobs. Deep API
|
|
287
|
+
detail lives in the \href{https://rbotafogo.github.io/galaaz/}{Galaaz
|
|
288
|
+
Manual}; here we stay at blog length with a few runnable sketches.
|
|
289
|
+
|
|
290
|
+
\section{What changed}\label{what-changed}
|
|
291
|
+
|
|
292
|
+
{\def\LTcaptype{none} % do not increment counter
|
|
293
|
+
\begin{longtable}[]{@{}
|
|
294
|
+
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.3333}}
|
|
295
|
+
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.3333}}
|
|
296
|
+
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.3333}}@{}}
|
|
297
|
+
\toprule\noalign{}
|
|
298
|
+
\begin{minipage}[b]{\linewidth}\raggedright
|
|
299
|
+
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
|
|
300
|
+
Historical stack
|
|
301
|
+
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
|
|
302
|
+
Galaaz 2.0
|
|
303
|
+
\end{minipage} \\
|
|
304
|
+
\midrule\noalign{}
|
|
305
|
+
\endhead
|
|
306
|
+
\bottomrule\noalign{}
|
|
307
|
+
\endlastfoot
|
|
308
|
+
Ruby & TruffleRuby (Graal) & \textbf{JRuby} or \textbf{CRuby} \\
|
|
309
|
+
R & FastR (subset of GNU R) & \textbf{GNU R} (CRAN / Bioconductor) \\
|
|
310
|
+
Coupling & Same-JVM interop & \textbf{NewBridge} (separate processes) \\
|
|
311
|
+
Install & Special Graal distribution & \texttt{gem\ install} + compile
|
|
312
|
+
gatekeeper \\
|
|
313
|
+
Legacy tools & \texttt{grun}, polyglot \texttt{gknit-draft} &
|
|
314
|
+
\texttt{bin/galaaz-ruby}, \texttt{bin/gknit} \\
|
|
315
|
+
\end{longtable}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
Ideas from the older articles still matter --- ggplot layers, dplyr
|
|
319
|
+
pipes, gKnit literate docs --- but the \textbf{engine} underneath is
|
|
320
|
+
different. If you land on a 2018 ``Ruby + R on GraalVM'' post, treat the
|
|
321
|
+
plots and narrative as inspiration and this post (plus the manual) as
|
|
322
|
+
the current stack.
|
|
323
|
+
|
|
324
|
+
\section{NewBridge in one picture}\label{newbridge-in-one-picture}
|
|
325
|
+
|
|
326
|
+
Galaaz 2.0 does \textbf{not} embed Renjin or FastR. Roughly:
|
|
327
|
+
|
|
328
|
+
\begin{enumerate}
|
|
329
|
+
\def\labelenumi{\arabic{enumi}.}
|
|
330
|
+
\tightlist
|
|
331
|
+
\item
|
|
332
|
+
Your \textbf{Ruby} process (JRuby or CRuby) loads Galaaz.
|
|
333
|
+
\item
|
|
334
|
+
A native \textbf{gatekeeper} (\texttt{ext/new\_bridge}, Rcpp / C++)
|
|
335
|
+
mediates the protocol.
|
|
336
|
+
\item
|
|
337
|
+
A \textbf{GNU R} process evaluates calls, holds large objects, and
|
|
338
|
+
runs packages.
|
|
339
|
+
\end{enumerate}
|
|
340
|
+
|
|
341
|
+
Ruby stays Ruby: \texttt{R.mean}, \texttt{E.aes}, method chaining,
|
|
342
|
+
classes and modules. R stays R: the same \texttt{lm}, \texttt{DESeq2},
|
|
343
|
+
or \texttt{ggplot2} you would run in RStudio. Large panels should
|
|
344
|
+
usually \textbf{stay in R}; Ruby orchestrates and pulls small results
|
|
345
|
+
(Remote Control). Opt in to unbox scalars when you need a plain Ruby
|
|
346
|
+
number (\texttt{\textgreater{}\textgreater{}\ 0} for length-1 vectors is
|
|
347
|
+
the common 2.0 idiom).
|
|
348
|
+
|
|
349
|
+
A minimal session looks like this:
|
|
350
|
+
|
|
351
|
+
\begin{Shaded}
|
|
352
|
+
\begin{Highlighting}[]
|
|
353
|
+
\FunctionTok{require} \VerbatimStringTok{\textquotesingle{}galaaz\textquotesingle{}}
|
|
354
|
+
|
|
355
|
+
\FunctionTok{puts} \ConstantTok{R}\AttributeTok{.sum}\NormalTok{(}\ConstantTok{R}\AttributeTok{.c}\NormalTok{(}\DecValTok{1}\NormalTok{, }\DecValTok{2}\NormalTok{, }\DecValTok{3}\NormalTok{, }\DecValTok{4}\NormalTok{, }\DecValTok{5}\NormalTok{))}
|
|
356
|
+
\NormalTok{mtcars }\OperatorTok{=} \OperatorTok{\textasciitilde{}}\ConstantTok{R}\KeywordTok{[}\WarningTok{:mtcars}\KeywordTok{]}
|
|
357
|
+
\FunctionTok{puts}\NormalTok{ mtcars}\AttributeTok{.dim}
|
|
358
|
+
\FunctionTok{puts} \ConstantTok{R}\AttributeTok{.summary}\NormalTok{(mtcars}\AttributeTok{.mpg}\NormalTok{)}
|
|
359
|
+
\end{Highlighting}
|
|
360
|
+
\end{Shaded}
|
|
361
|
+
|
|
362
|
+
\begin{verbatim}
|
|
363
|
+
## [1] 15
|
|
364
|
+
## [1] 32 11
|
|
365
|
+
## Min. 1st Qu. Median Mean 3rd Qu. Max.
|
|
366
|
+
## 10.40 15.43 19.20 20.09 22.80 33.90
|
|
367
|
+
\end{verbatim}
|
|
368
|
+
|
|
369
|
+
Callbacks still exist: Ruby procs can be registered so R code can call
|
|
370
|
+
back into Ruby when a package expects an R function. That path is
|
|
371
|
+
documented in the manual; the important product point is that
|
|
372
|
+
\textbf{real GNU R} is on the other end of the wire.
|
|
373
|
+
|
|
374
|
+
\section{Either Ruby engine}\label{either-ruby-engine}
|
|
375
|
+
|
|
376
|
+
NewBridge is tested on both:
|
|
377
|
+
|
|
378
|
+
\begin{itemize}
|
|
379
|
+
\tightlist
|
|
380
|
+
\item
|
|
381
|
+
\textbf{JRuby} (e.g.~10.x + JDK 21) --- real OS threads, JVM
|
|
382
|
+
ecosystem, natural fit for Rails under load.
|
|
383
|
+
\item
|
|
384
|
+
\textbf{CRuby} (e.g.~3.3+) --- MRI gem ecosystem, familiar for many
|
|
385
|
+
Ruby shops.
|
|
386
|
+
\end{itemize}
|
|
387
|
+
|
|
388
|
+
The public API is the same. Launchers honor
|
|
389
|
+
\textbf{\texttt{GALAAZ\_RUBY}}. Use \textbf{\texttt{bin/galaaz-ruby}},
|
|
390
|
+
or \textbf{\texttt{bin/galaaz-jruby}} to force JRuby. Prefer
|
|
391
|
+
\textbf{JRuby} when you want parallel Ruby threads feeding R; prefer
|
|
392
|
+
\textbf{CRuby} when your app and gems already live there. Arrow helpers
|
|
393
|
+
need engine-specific native bits (Java Arrow + \texttt{JAVA\_OPTS} nio
|
|
394
|
+
opens on JRuby; Arrow GLib + \texttt{red-arrow} on CRuby) --- see the
|
|
395
|
+
manual's Arrow section.
|
|
396
|
+
|
|
397
|
+
\begin{Shaded}
|
|
398
|
+
\begin{Highlighting}[]
|
|
399
|
+
\CommentTok{\# From a checkout or installed layout:}
|
|
400
|
+
\ExtensionTok{bin/galaaz{-}ruby} \AttributeTok{{-}e} \DataTypeTok{\textbackslash{}}
|
|
401
|
+
\StringTok{\textquotesingle{}require "galaaz"; puts R.R\_\_version[["version.string"]]\textquotesingle{}}
|
|
402
|
+
\VariableTok{GALAAZ\_RUBY}\OperatorTok{=}\NormalTok{jruby }\ExtensionTok{bin/galaaz{-}ruby} \AttributeTok{{-}e} \DataTypeTok{\textbackslash{}}
|
|
403
|
+
\StringTok{\textquotesingle{}require "galaaz"; puts R.pi\textquotesingle{}}
|
|
404
|
+
\end{Highlighting}
|
|
405
|
+
\end{Shaded}
|
|
406
|
+
|
|
407
|
+
\section{Multiple R versions via
|
|
408
|
+
containers}\label{multiple-r-versions-via-containers}
|
|
409
|
+
|
|
410
|
+
Because R is a \textbf{separate process}, the Ruby gem does not
|
|
411
|
+
hard-wire one R build. Whatever \texttt{R} / \texttt{Rscript} is on
|
|
412
|
+
\texttt{PATH} (and can load the gatekeeper) is the R you drive. That
|
|
413
|
+
makes \textbf{containers} a clean way to pin versions:
|
|
414
|
+
|
|
415
|
+
\begin{itemize}
|
|
416
|
+
\tightlist
|
|
417
|
+
\item
|
|
418
|
+
Image A: Ubuntu + \textbf{R 4.3} + system libs for your Bioconductor
|
|
419
|
+
set.
|
|
420
|
+
\item
|
|
421
|
+
Image B: same app code + \textbf{R 4.4} (or a rocker image) for
|
|
422
|
+
comparison.
|
|
423
|
+
\item
|
|
424
|
+
Your Galaaz gem and Ruby code stay the same; only the image's R
|
|
425
|
+
changes.
|
|
426
|
+
\end{itemize}
|
|
427
|
+
|
|
428
|
+
Sketch (conceptual):
|
|
429
|
+
|
|
430
|
+
\begin{Shaded}
|
|
431
|
+
\begin{Highlighting}[]
|
|
432
|
+
\CommentTok{\# Dev against the R that the image provides}
|
|
433
|
+
\ExtensionTok{docker}\NormalTok{ run }\AttributeTok{{-}{-}rm} \AttributeTok{{-}it}\NormalTok{ myorg/galaaz{-}r43 }\DataTypeTok{\textbackslash{}}
|
|
434
|
+
\NormalTok{ bin/galaaz{-}ruby script/analysis.rb}
|
|
435
|
+
|
|
436
|
+
\ExtensionTok{docker}\NormalTok{ run }\AttributeTok{{-}{-}rm} \AttributeTok{{-}it}\NormalTok{ myorg/galaaz{-}r44 }\DataTypeTok{\textbackslash{}}
|
|
437
|
+
\NormalTok{ bin/galaaz{-}ruby script/analysis.rb}
|
|
438
|
+
\end{Highlighting}
|
|
439
|
+
\end{Shaded}
|
|
440
|
+
|
|
441
|
+
Inside each image, \texttt{R\ -\/-version} differs; NewBridge still
|
|
442
|
+
speaks the same protocol. For a first taste without building your own
|
|
443
|
+
Dockerfile, the repo ships \textbf{try} images and runners:
|
|
444
|
+
|
|
445
|
+
\begin{Shaded}
|
|
446
|
+
\begin{Highlighting}[]
|
|
447
|
+
\CommentTok{\# From a Galaaz checkout}
|
|
448
|
+
\ExtensionTok{./docker/try{-}gstudio/run.sh} \CommentTok{\# JRuby + gstudio{-}oriented image}
|
|
449
|
+
\ExtensionTok{./docker/try{-}cruby/run.sh} \CommentTok{\# CRuby try image}
|
|
450
|
+
\end{Highlighting}
|
|
451
|
+
\end{Shaded}
|
|
452
|
+
|
|
453
|
+
\textbf{Cold-install} proofs (\texttt{docker/cold-install},
|
|
454
|
+
\texttt{docker/cold-install-cruby}) install the gem on a throwaway
|
|
455
|
+
Ubuntu and compile the gatekeeper --- useful when you want ``empty
|
|
456
|
+
machine → working bridge'' confidence. Multi-runtime Docker R matrices
|
|
457
|
+
are still evolving (slow integration coverage); the sketch above is the
|
|
458
|
+
pattern to build on.
|
|
459
|
+
|
|
460
|
+
\section{Moving data: Apache Arrow}\label{moving-data-apache-arrow}
|
|
461
|
+
|
|
462
|
+
Crossing a process boundary means you must be honest about
|
|
463
|
+
\textbf{copies}. Galaaz's Arrow roadmap has three stages:
|
|
464
|
+
|
|
465
|
+
{\def\LTcaptype{none} % do not increment counter
|
|
466
|
+
\begin{longtable}[]{@{}
|
|
467
|
+
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.3333}}
|
|
468
|
+
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.3333}}
|
|
469
|
+
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.3333}}@{}}
|
|
470
|
+
\toprule\noalign{}
|
|
471
|
+
\begin{minipage}[b]{\linewidth}\raggedright
|
|
472
|
+
Stage
|
|
473
|
+
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
|
|
474
|
+
Idea
|
|
475
|
+
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
|
|
476
|
+
Status
|
|
477
|
+
\end{minipage} \\
|
|
478
|
+
\midrule\noalign{}
|
|
479
|
+
\endhead
|
|
480
|
+
\bottomrule\noalign{}
|
|
481
|
+
\endlastfoot
|
|
482
|
+
\textbf{A} & Copy columnar data into R; return an R-side Arrow/proxy
|
|
483
|
+
handle & Available (\texttt{from\_ruby\_batches}, \texttt{table\_from},
|
|
484
|
+
\ldots) \\
|
|
485
|
+
\textbf{B} & Same-machine \textbf{IPC file}: bulk bytes stay off the
|
|
486
|
+
MsgPack bridge; only a \textbf{file path} crosses NewBridge & Shipped
|
|
487
|
+
(two directions: B1 and B2) \\
|
|
488
|
+
\textbf{C} & Shared-memory bus (true shared RAM) & Future \\
|
|
489
|
+
\end{longtable}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
Do \textbf{not} read Stage A as ``zero-copy shared RAM.'' Stage A is
|
|
493
|
+
``build the table in R efficiently, then Remote-Control it.'' Stage B is
|
|
494
|
+
still a file/\texttt{mmap} handoff on one machine---not Stage C---but it
|
|
495
|
+
keeps large columns out of the control plane.
|
|
496
|
+
|
|
497
|
+
\subsection{Stage B in plain words}\label{stage-b-in-plain-words}
|
|
498
|
+
|
|
499
|
+
\textbf{IPC} here means Apache Arrow's \textbf{Inter-Process
|
|
500
|
+
Communication} file format: a portable columnar dump on disk (often
|
|
501
|
+
under \texttt{/dev/shm} so it lives in RAM-backed tmpfs). Ruby and R
|
|
502
|
+
never shove megabytes through NewBridge; they agree on a path, then each
|
|
503
|
+
side's Arrow stack reads or writes that file.
|
|
504
|
+
|
|
505
|
+
Stage B splits by \textbf{direction}:
|
|
506
|
+
|
|
507
|
+
\begin{itemize}
|
|
508
|
+
\tightlist
|
|
509
|
+
\item
|
|
510
|
+
\textbf{B1 --- Ruby → R (ingest).} Ruby writes an IPC file
|
|
511
|
+
(\texttt{Galaaz::ArrowIpc.write} / \texttt{write\_batches}). R opens
|
|
512
|
+
it with \texttt{R::Arrow.open\_ipc(path)} and gets a table proxy for
|
|
513
|
+
dplyr or modeling. After R has materialised the table, Ruby can
|
|
514
|
+
\texttt{Galaaz::ArrowIpc.release(path)} (unlink the scratch file).
|
|
515
|
+
\item
|
|
516
|
+
\textbf{B2 --- R → Ruby (export).} After analytics in R,
|
|
517
|
+
\texttt{R::Arrow.write\_ipc} writes another IPC file and returns its
|
|
518
|
+
path over the bridge. Ruby reads columns or row hashes with
|
|
519
|
+
\texttt{Galaaz::ArrowIpc.read} / \texttt{read\_batches}---handy when
|
|
520
|
+
the next step is a DB write or an API payload in Ruby.
|
|
521
|
+
\end{itemize}
|
|
522
|
+
|
|
523
|
+
Needs: R package \texttt{arrow}, plus a Ruby writer/reader (CRuby:
|
|
524
|
+
\textbf{red-arrow}; JRuby: Arrow Java JARs). Specs skip when those are
|
|
525
|
+
missing; the sketch below does the same.
|
|
526
|
+
|
|
527
|
+
\subsection{Stage B sketch (B1 ingest + B2
|
|
528
|
+
export)}\label{stage-b-sketch-b1-ingest-b2-export}
|
|
529
|
+
|
|
530
|
+
This is the path to prefer for large same-machine handoffs. Ruby threads
|
|
531
|
+
build row batches; \textbf{B1} writes one IPC file and R opens it by
|
|
532
|
+
path; dplyr summarises in R; \textbf{B2} writes the result IPC and Ruby
|
|
533
|
+
reads row hashes. NewBridge only carries paths.
|
|
534
|
+
|
|
535
|
+
\begin{Shaded}
|
|
536
|
+
\begin{Highlighting}[]
|
|
537
|
+
\NormalTok{ipc\_ok }\OperatorTok{=} \DataTypeTok{Galaaz}\OperatorTok{::}\DataTypeTok{ArrowIpc}\AttributeTok{.available?} \OperatorTok{\&\&}
|
|
538
|
+
\NormalTok{ (}\ConstantTok{R}\OperatorTok{::}\DataTypeTok{Support}\AttributeTok{.eval}\NormalTok{(}
|
|
539
|
+
\StringTok{"requireNamespace(\textquotesingle{}arrow\textquotesingle{}, quietly=TRUE) \&\& "} \OperatorTok{+}
|
|
540
|
+
\StringTok{"requireNamespace(\textquotesingle{}dplyr\textquotesingle{}, quietly=TRUE)"}\NormalTok{) }\OperatorTok{==} \DecValTok{true}\NormalTok{)}
|
|
541
|
+
\ControlFlowTok{unless}\NormalTok{ ipc\_ok}
|
|
542
|
+
\FunctionTok{puts} \VerbatimStringTok{\textquotesingle{}(Skip: need Arrow IPC backend + R arrow/dplyr.)\textquotesingle{}}
|
|
543
|
+
\ControlFlowTok{else}
|
|
544
|
+
\NormalTok{ thread\_count }\OperatorTok{=} \DecValTok{2}
|
|
545
|
+
\NormalTok{ rows\_per\_thread }\OperatorTok{=} \DecValTok{250}
|
|
546
|
+
\NormalTok{ group\_count }\OperatorTok{=} \DecValTok{5}
|
|
547
|
+
\NormalTok{ batches }\OperatorTok{=} \KeywordTok{[]}
|
|
548
|
+
\NormalTok{ mutex }\OperatorTok{=} \DataTypeTok{Mutex}\AttributeTok{.new}
|
|
549
|
+
\NormalTok{ threads }\OperatorTok{=} \KeywordTok{[]}
|
|
550
|
+
|
|
551
|
+
\NormalTok{ thread\_count}\AttributeTok{.times} \ControlFlowTok{do} \OperatorTok{|}\NormalTok{tid}\OperatorTok{|}
|
|
552
|
+
\NormalTok{ threads }\OperatorTok{\textless{}\textless{}} \DataTypeTok{Thread}\AttributeTok{.new} \ControlFlowTok{do}
|
|
553
|
+
\NormalTok{ start }\OperatorTok{=}\NormalTok{ tid }\OperatorTok{*}\NormalTok{ rows\_per\_thread}
|
|
554
|
+
\NormalTok{ local }\OperatorTok{=}\NormalTok{ (start}\OperatorTok{...}\NormalTok{(start }\OperatorTok{+}\NormalTok{ rows\_per\_thread))}\AttributeTok{.map} \ControlFlowTok{do} \OperatorTok{|}\NormalTok{i}\OperatorTok{|}
|
|
555
|
+
\OperatorTok{\{}
|
|
556
|
+
\WarningTok{id:}\NormalTok{ i,}
|
|
557
|
+
\WarningTok{grp:} \StringTok{"g}\SpecialCharTok{\#\{}\NormalTok{i }\OperatorTok{\%}\NormalTok{ group\_count}\SpecialCharTok{\}}\StringTok{"}\NormalTok{,}
|
|
558
|
+
\WarningTok{value:}\NormalTok{ (i }\OperatorTok{\%} \DecValTok{17}\NormalTok{) }\OperatorTok{+} \DecValTok{1}\NormalTok{,}
|
|
559
|
+
\WarningTok{weight:}\NormalTok{ ((i }\OperatorTok{\%} \DecValTok{5}\NormalTok{) }\OperatorTok{+} \DecValTok{1}\NormalTok{) }\OperatorTok{*} \FloatTok{0.5}
|
|
560
|
+
\OperatorTok{\}}
|
|
561
|
+
\ControlFlowTok{end}
|
|
562
|
+
\NormalTok{ mutex}\AttributeTok{.synchronize} \OperatorTok{\{}\NormalTok{ batches }\OperatorTok{\textless{}\textless{}}\NormalTok{ local }\OperatorTok{\}}
|
|
563
|
+
\ControlFlowTok{end}
|
|
564
|
+
\ControlFlowTok{end}
|
|
565
|
+
\NormalTok{ threads}\AttributeTok{.each}\NormalTok{(}\OperatorTok{\&}\WarningTok{:join}\NormalTok{)}
|
|
566
|
+
|
|
567
|
+
\NormalTok{ paths }\OperatorTok{=} \KeywordTok{[]}
|
|
568
|
+
\ControlFlowTok{begin}
|
|
569
|
+
\CommentTok{\# B1: Ruby → IPC file → R table proxy}
|
|
570
|
+
\CommentTok{\# write\_batches wants flat row Hashes (not nested batches)}
|
|
571
|
+
\NormalTok{ in\_path }\OperatorTok{=} \DataTypeTok{Galaaz}\OperatorTok{::}\DataTypeTok{ArrowIpc}\AttributeTok{.write\_batches}\NormalTok{(batches}\AttributeTok{.flatten}\NormalTok{)}
|
|
572
|
+
\NormalTok{ paths }\OperatorTok{\textless{}\textless{}}\NormalTok{ in\_path}
|
|
573
|
+
\NormalTok{ tbl }\OperatorTok{=} \ConstantTok{R}\OperatorTok{::}\DataTypeTok{Arrow}\AttributeTok{.open\_ipc}\NormalTok{(in\_path)}
|
|
574
|
+
\FunctionTok{puts} \StringTok{"R class: }\SpecialCharTok{\#\{}\NormalTok{tbl}\AttributeTok{.rclass}\SpecialCharTok{\}}\StringTok{"}
|
|
575
|
+
\FunctionTok{puts} \StringTok{"IPC in: }\SpecialCharTok{\#\{}\DataTypeTok{File}\AttributeTok{.basename}\NormalTok{(in\_path)}\SpecialCharTok{\}}\StringTok{"}
|
|
576
|
+
|
|
577
|
+
\NormalTok{ grouped }\OperatorTok{=} \ConstantTok{R}\AttributeTok{.dplyr\_\_\_group\_by}\NormalTok{(tbl, }\WarningTok{:grp}\NormalTok{)}
|
|
578
|
+
\NormalTok{ summed }\OperatorTok{=} \ConstantTok{R}\AttributeTok{.dplyr\_\_\_summarise}\NormalTok{(}
|
|
579
|
+
\NormalTok{ grouped,}
|
|
580
|
+
\WarningTok{n:} \ConstantTok{E}\AttributeTok{.n}\NormalTok{(),}
|
|
581
|
+
\WarningTok{total:} \ConstantTok{E}\AttributeTok{.sum}\NormalTok{(}\WarningTok{:value}\NormalTok{),}
|
|
582
|
+
\WarningTok{wsum:} \ConstantTok{E}\AttributeTok{.sum}\NormalTok{(}\ConstantTok{R}\KeywordTok{[}\WarningTok{:value}\KeywordTok{]} \OperatorTok{*} \ConstantTok{R}\KeywordTok{[}\WarningTok{:weight}\KeywordTok{]}\NormalTok{)}
|
|
583
|
+
\NormalTok{ )}
|
|
584
|
+
|
|
585
|
+
\CommentTok{\# B2: R → IPC file → Ruby row hashes}
|
|
586
|
+
\NormalTok{ out\_path }\OperatorTok{=} \ConstantTok{R}\OperatorTok{::}\DataTypeTok{Arrow}\AttributeTok{.write\_ipc}\NormalTok{(summed)}
|
|
587
|
+
\NormalTok{ paths }\OperatorTok{\textless{}\textless{}}\NormalTok{ out\_path}
|
|
588
|
+
\NormalTok{ rows }\OperatorTok{=} \DataTypeTok{Galaaz}\OperatorTok{::}\DataTypeTok{ArrowIpc}\AttributeTok{.read\_batches}\NormalTok{(out\_path)}
|
|
589
|
+
\FunctionTok{puts} \StringTok{"IPC out: }\SpecialCharTok{\#\{}\DataTypeTok{File}\AttributeTok{.basename}\NormalTok{(out\_path)}\SpecialCharTok{\}}\StringTok{"}
|
|
590
|
+
\NormalTok{ rows}\AttributeTok{.first}\NormalTok{(}\DecValTok{5}\NormalTok{)}\AttributeTok{.each} \ControlFlowTok{do} \OperatorTok{|}\NormalTok{r}\OperatorTok{|}
|
|
591
|
+
\FunctionTok{puts} \StringTok{"}\SpecialCharTok{\#\{}\NormalTok{r}\KeywordTok{[}\WarningTok{:grp}\KeywordTok{]}\SpecialCharTok{\}}\StringTok{ n=}\SpecialCharTok{\#\{}\NormalTok{r}\KeywordTok{[}\WarningTok{:n}\KeywordTok{]}\SpecialCharTok{\}}\StringTok{ "} \OperatorTok{+}
|
|
592
|
+
\StringTok{"total=}\SpecialCharTok{\#\{}\NormalTok{r}\KeywordTok{[}\WarningTok{:total}\KeywordTok{]}\SpecialCharTok{\}}\StringTok{ wsum=}\SpecialCharTok{\#\{}\NormalTok{r}\KeywordTok{[}\WarningTok{:wsum}\KeywordTok{]}\SpecialCharTok{\}}\StringTok{"}
|
|
593
|
+
\ControlFlowTok{end}
|
|
594
|
+
|
|
595
|
+
\NormalTok{ total\_n }\OperatorTok{=}\NormalTok{ rows}\AttributeTok{.map} \OperatorTok{\{} \OperatorTok{|}\NormalTok{r}\OperatorTok{|}\NormalTok{ r}\KeywordTok{[}\WarningTok{:n}\KeywordTok{]}\AttributeTok{.to\_i} \OperatorTok{\}}\AttributeTok{.sum}
|
|
596
|
+
\NormalTok{ expect\_n }\OperatorTok{=}\NormalTok{ thread\_count }\OperatorTok{*}\NormalTok{ rows\_per\_thread}
|
|
597
|
+
\FunctionTok{puts} \StringTok{"Sum of n (expect }\SpecialCharTok{\#\{}\NormalTok{expect\_n}\SpecialCharTok{\}}\StringTok{): }\SpecialCharTok{\#\{}\NormalTok{total\_n}\SpecialCharTok{\}}\StringTok{"}
|
|
598
|
+
\ControlFlowTok{ensure}
|
|
599
|
+
\NormalTok{ paths}\AttributeTok{.each} \OperatorTok{\{} \OperatorTok{|}\FunctionTok{p}\OperatorTok{|} \DataTypeTok{Galaaz}\OperatorTok{::}\DataTypeTok{ArrowIpc}\AttributeTok{.release}\NormalTok{(}\FunctionTok{p}\NormalTok{) }\OperatorTok{\}}
|
|
600
|
+
\ControlFlowTok{end}
|
|
601
|
+
\ControlFlowTok{end}
|
|
602
|
+
\end{Highlighting}
|
|
603
|
+
\end{Shaded}
|
|
604
|
+
|
|
605
|
+
\begin{verbatim}
|
|
606
|
+
## R class: Table
|
|
607
|
+
## IPC in: galaaz_ipc_34797_f9f16fd1b739f5ed.arrow
|
|
608
|
+
## IPC out: galaaz_ipc_34797_0f200cc9e2be2726.arrow
|
|
609
|
+
## g0 n=100 total=897 wsum=448.5
|
|
610
|
+
## g1 n=100 total=895 wsum=895.0
|
|
611
|
+
## g2 n=100 total=893 wsum=1339.5
|
|
612
|
+
## g3 n=100 total=891 wsum=1782.0
|
|
613
|
+
## g4 n=100 total=889 wsum=2222.5
|
|
614
|
+
## Sum of n (expect 500): 500
|
|
615
|
+
\end{verbatim}
|
|
616
|
+
|
|
617
|
+
What to notice: \texttt{write\_batches} / \texttt{open\_ipc} is
|
|
618
|
+
\textbf{B1}; after dplyr, \texttt{write\_ipc} / \texttt{read\_batches}
|
|
619
|
+
is \textbf{B2}. Specs: \texttt{arrow\_ipc\_handoff\_spec.rb},
|
|
620
|
+
\texttt{arrow\_ipc\_export\_spec.rb}. Deeper notes:
|
|
621
|
+
\texttt{Documentation/ROADMAP\_ARROW\_RUBY\_R.md}.
|
|
622
|
+
|
|
623
|
+
\subsection{Stage A (still useful)}\label{stage-a-still-useful}
|
|
624
|
+
|
|
625
|
+
Stage A copies batches straight into R with
|
|
626
|
+
\texttt{R::Arrow.from\_ruby\_batches}---no IPC file. Fine for modest
|
|
627
|
+
tables or when you do not have a Ruby Arrow writer installed. Same
|
|
628
|
+
threaded shape as above, one call instead of B1:
|
|
629
|
+
|
|
630
|
+
\begin{Shaded}
|
|
631
|
+
\begin{Highlighting}[]
|
|
632
|
+
\NormalTok{arrow\_ok }\OperatorTok{=} \ConstantTok{R}\OperatorTok{::}\DataTypeTok{Support}\AttributeTok{.eval}\NormalTok{(}
|
|
633
|
+
\StringTok{"requireNamespace(\textquotesingle{}arrow\textquotesingle{}, quietly=TRUE) \&\& "} \OperatorTok{+}
|
|
634
|
+
\StringTok{"requireNamespace(\textquotesingle{}dplyr\textquotesingle{}, quietly=TRUE)"}\NormalTok{)}
|
|
635
|
+
\ControlFlowTok{unless}\NormalTok{ arrow\_ok }\OperatorTok{==} \DecValTok{true}
|
|
636
|
+
\FunctionTok{puts} \VerbatimStringTok{\textquotesingle{}(Skip: need arrow + dplyr in R.)\textquotesingle{}}
|
|
637
|
+
\ControlFlowTok{else}
|
|
638
|
+
\NormalTok{ batches }\OperatorTok{=} \KeywordTok{[}
|
|
639
|
+
\KeywordTok{[}\OperatorTok{\{} \WarningTok{id:} \DecValTok{1}\NormalTok{, }\WarningTok{grp:} \CharTok{\textquotesingle{}a\textquotesingle{}}\NormalTok{, }\WarningTok{value:} \DecValTok{10}\NormalTok{, }\WarningTok{weight:} \FloatTok{1.0} \OperatorTok{\}}\NormalTok{,}
|
|
640
|
+
\OperatorTok{\{} \WarningTok{id:} \DecValTok{2}\NormalTok{, }\WarningTok{grp:} \CharTok{\textquotesingle{}b\textquotesingle{}}\NormalTok{, }\WarningTok{value:} \DecValTok{20}\NormalTok{, }\WarningTok{weight:} \FloatTok{0.5} \OperatorTok{\}}\KeywordTok{]}\NormalTok{,}
|
|
641
|
+
\KeywordTok{[}\OperatorTok{\{} \WarningTok{id:} \DecValTok{3}\NormalTok{, }\WarningTok{grp:} \CharTok{\textquotesingle{}a\textquotesingle{}}\NormalTok{, }\WarningTok{value:} \DecValTok{30}\NormalTok{, }\WarningTok{weight:} \FloatTok{1.5} \OperatorTok{\}}\KeywordTok{]}
|
|
642
|
+
\KeywordTok{]}
|
|
643
|
+
\NormalTok{ tbl }\OperatorTok{=} \ConstantTok{R}\OperatorTok{::}\DataTypeTok{Arrow}\AttributeTok{.from\_ruby\_batches}\NormalTok{(batches)}
|
|
644
|
+
\FunctionTok{puts} \StringTok{"R class: }\SpecialCharTok{\#\{}\NormalTok{tbl}\AttributeTok{.rclass}\SpecialCharTok{\}}\StringTok{"}
|
|
645
|
+
\NormalTok{ grouped }\OperatorTok{=} \ConstantTok{R}\AttributeTok{.dplyr\_\_\_group\_by}\NormalTok{(tbl, }\WarningTok{:grp}\NormalTok{)}
|
|
646
|
+
\NormalTok{ summarised }\OperatorTok{=} \ConstantTok{R}\AttributeTok{.dplyr\_\_\_summarise}\NormalTok{(}
|
|
647
|
+
\NormalTok{ grouped, }\WarningTok{n:} \ConstantTok{E}\AttributeTok{.n}\NormalTok{(), }\WarningTok{total:} \ConstantTok{E}\AttributeTok{.sum}\NormalTok{(}\WarningTok{:value}\NormalTok{))}
|
|
648
|
+
\NormalTok{ out }\OperatorTok{=} \ConstantTok{R}\AttributeTok{.dplyr\_\_\_collect}\NormalTok{(summarised)}
|
|
649
|
+
\FunctionTok{puts} \ConstantTok{R}\AttributeTok{.as\_\_data\_\_frame}\NormalTok{(out)}
|
|
650
|
+
\ControlFlowTok{end}
|
|
651
|
+
\end{Highlighting}
|
|
652
|
+
\end{Shaded}
|
|
653
|
+
|
|
654
|
+
\begin{verbatim}
|
|
655
|
+
## R class: Table
|
|
656
|
+
## grp n total
|
|
657
|
+
## 1 a 2 40
|
|
658
|
+
## 2 b 1 20
|
|
659
|
+
\end{verbatim}
|
|
660
|
+
|
|
661
|
+
Larger Stage A demos live in
|
|
662
|
+
\texttt{arrow\_from\_ruby\_batches\_spec.rb} and
|
|
663
|
+
\texttt{arrow\_large\_pipeline\_spec.rb} (under \texttt{specs/} /
|
|
664
|
+
\texttt{slow-specs/}).
|
|
665
|
+
|
|
666
|
+
\section{Keeping the bridge free}\label{keeping-the-bridge-free}
|
|
667
|
+
|
|
668
|
+
Two different ``don't block'' stories:
|
|
669
|
+
|
|
670
|
+
\begin{itemize}
|
|
671
|
+
\tightlist
|
|
672
|
+
\item
|
|
673
|
+
\textbf{\texttt{R::Async} / \texttt{R.eval\_r\_async}} --- free a
|
|
674
|
+
\textbf{Ruby} thread while the \textbf{same} bridge R process works.
|
|
675
|
+
Good for short/medium calls when you still want one R session.
|
|
676
|
+
\item
|
|
677
|
+
\textbf{\texttt{R::Job}} --- run heavy work in a \textbf{child
|
|
678
|
+
\texttt{Rscript}}. The bridge stays free for other traffic. Package
|
|
679
|
+
installs (\texttt{R.install\_and\_loads}) use this path so
|
|
680
|
+
\texttt{make}/\texttt{gcc} do not wedge the gatekeeper.
|
|
681
|
+
\end{itemize}
|
|
682
|
+
|
|
683
|
+
\begin{Shaded}
|
|
684
|
+
\begin{Highlighting}[]
|
|
685
|
+
\ControlFlowTok{begin}
|
|
686
|
+
\NormalTok{ coef }\OperatorTok{=} \ConstantTok{R}\OperatorTok{::}\DataTypeTok{Job}\AttributeTok{.eval}\NormalTok{(}\OperatorTok{\textless{}\textless{}\textasciitilde{}}\ControlFlowTok{R}\DocumentationTok{) \{ |job| job.load\_rds \}}
|
|
687
|
+
\DocumentationTok{ fit \textless{}{-} lm(mpg \textasciitilde{} wt, data = mtcars)}
|
|
688
|
+
\DocumentationTok{ saveRDS(unname(coef(fit)), result\_path)}
|
|
689
|
+
\DocumentationTok{ }\ControlFlowTok{R}
|
|
690
|
+
\FunctionTok{puts}\NormalTok{ coef}
|
|
691
|
+
\ControlFlowTok{rescue} \OperatorTok{=\textgreater{}}\NormalTok{ e}
|
|
692
|
+
\FunctionTok{puts}\NormalTok{ e}\AttributeTok{.class.to\_s}
|
|
693
|
+
\NormalTok{ e}\AttributeTok{.message.to\_s.scan}\NormalTok{(}\SpecialStringTok{/.\{1,68\}/}\NormalTok{)}\AttributeTok{.each} \OperatorTok{\{} \OperatorTok{|}\NormalTok{line}\OperatorTok{|} \FunctionTok{puts}\NormalTok{ line }\OperatorTok{\}}
|
|
694
|
+
\ControlFlowTok{end}
|
|
695
|
+
\end{Highlighting}
|
|
696
|
+
\end{Shaded}
|
|
697
|
+
|
|
698
|
+
\begin{verbatim}
|
|
699
|
+
## [1] 37.285126 -5.344472
|
|
700
|
+
\end{verbatim}
|
|
701
|
+
|
|
702
|
+
Rule of thumb: short interactive analytics on the bridge; installs and
|
|
703
|
+
multi-minute fits in \textbf{\texttt{R::Job}}. Details and timeouts
|
|
704
|
+
(\texttt{GALAAZ\_INSTALL\_TIMEOUT\_SEC}, \texttt{GALAAZ\_JOBS\_DIR}) are
|
|
705
|
+
in the manual.
|
|
706
|
+
|
|
707
|
+
\section{What stayed the same}\label{what-stayed-the-same}
|
|
708
|
+
|
|
709
|
+
Galaaz 2.0 is a new \textbf{runtime}, not a new plotting language. These
|
|
710
|
+
still apply:
|
|
711
|
+
|
|
712
|
+
\begin{itemize}
|
|
713
|
+
\tightlist
|
|
714
|
+
\item
|
|
715
|
+
\textbf{ggplot2} from Ruby --- see \emph{Ruby Plotting with Galaaz} /
|
|
716
|
+
\emph{Beautiful Ruby Plots}.
|
|
717
|
+
\item
|
|
718
|
+
\textbf{dplyr / NSE} --- see \emph{Non Standard Evaluation in dplyr
|
|
719
|
+
with Galaaz}.
|
|
720
|
+
\item
|
|
721
|
+
\textbf{Literate docs} --- \textbf{gKnit} still wraps knitr so Ruby
|
|
722
|
+
chunks render next to R (HTML/PDF).
|
|
723
|
+
\item
|
|
724
|
+
\textbf{Classes, modules, procs} --- the \emph{oh my} S4 comparison
|
|
725
|
+
still teaches structure; unwrap and bridge details follow 2.0 rules.
|
|
726
|
+
\end{itemize}
|
|
727
|
+
|
|
728
|
+
R-on-Rails (Ruby web + R science) is the product framing: Rails (or any
|
|
729
|
+
Ruby app) orchestrates; GNU R computes.
|
|
730
|
+
|
|
731
|
+
\section{Conclusion}\label{conclusion}
|
|
732
|
+
|
|
733
|
+
\textbf{Galaaz 2.0} bets on \textbf{boring, powerful defaults}: standard
|
|
734
|
+
Ruby engines, standard GNU R, a clear process bridge, containers to pin
|
|
735
|
+
R, and Arrow-shaped paths for larger tables --- with zero-copy shared
|
|
736
|
+
memory still on the roadmap. The Graal-era prototype proved the
|
|
737
|
+
\emph{idea}; 2.0 makes the \emph{ecosystem} reachable.
|
|
738
|
+
|
|
739
|
+
\subsection{Further reading}\label{further-reading}
|
|
740
|
+
|
|
741
|
+
\begin{itemize}
|
|
742
|
+
\tightlist
|
|
743
|
+
\item
|
|
744
|
+
Manual (GitHub Pages): \url{https://rbotafogo.github.io/galaaz/}
|
|
745
|
+
\item
|
|
746
|
+
Arrow roadmap: \texttt{Documentation/ROADMAP\_ARROW\_RUBY\_R.md}
|
|
747
|
+
\item
|
|
748
|
+
Repo blogs under \texttt{blogs/} (ggplot, dplyr, gKnit, oh my, manual)
|
|
749
|
+
\item
|
|
750
|
+
Try images: \texttt{docker/try-gstudio}, \texttt{docker/try-cruby}
|
|
751
|
+
\item
|
|
752
|
+
Older Graal-era plot narrative (historical): search ``Ruby Plotting
|
|
753
|
+
with Galaaz in GraalVM'' on Towards Data Science
|
|
754
|
+
\end{itemize}
|
|
755
|
+
|
|
756
|
+
\end{document}
|