galaaz 0.5.0 → 2.1.0

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.
Files changed (378) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/LICENSE +0 -0
  4. data/README.md +1416 -667
  5. data/Rakefile +68 -41
  6. data/bin/galaaz-bootstrap +137 -0
  7. data/bin/galaaz-jruby +11 -0
  8. data/bin/galaaz-ruby +16 -0
  9. data/bin/galaaz_jruby_env.inc.sh +6 -0
  10. data/bin/galaaz_ruby_env.inc.sh +36 -0
  11. data/bin/gbookdown +63 -0
  12. data/bin/gknit +83 -13
  13. data/bin/gknit-draft.rb +0 -0
  14. data/bin/gstudio +5 -3
  15. data/bin/gstudio_irb.rb +0 -0
  16. data/bin/gstudio_pry.rb +0 -0
  17. data/bin/install-tinytex +6 -0
  18. data/bin/run_all_rspec +44 -0
  19. data/bin/run_example +17 -0
  20. data/bin/run_old_rspec +20 -0
  21. data/bin/run_rspec +24 -0
  22. data/bin/run_rspec_subset +38 -0
  23. data/bin/run_slow_rspec +20 -0
  24. data/blogs/R-on-Rails-Planning-Document.md +940 -0
  25. data/blogs/README.md +100 -0
  26. data/blogs/galaaz_ggplot/galaaz_ggplot.Rmd +38 -66
  27. data/blogs/galaaz_ggplot/galaaz_ggplot.log +754 -0
  28. data/blogs/galaaz_ggplot/galaaz_ggplot.md +115 -155
  29. data/blogs/galaaz_ggplot/galaaz_ggplot.tex +607 -0
  30. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-gfm/midwest_rb.png +0 -0
  31. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-gfm/scatter_plot_rb.png +0 -0
  32. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/midwest_rb.png +0 -0
  33. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/scatter_plot_rb.png +0 -0
  34. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/midwest_rb.png +0 -0
  35. data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/scatter_plot_rb.png +0 -0
  36. data/blogs/galaaz_ggplot/midwest.Rmd +3 -3
  37. data/blogs/galaaz_ggplot/midwest_external_png +0 -0
  38. data/blogs/gknit/gknit.Rmd +47 -52
  39. data/blogs/gknit/gknit.md +1430 -0
  40. data/blogs/gknit/gknit_files/figure-gfm/bubble-1.png +0 -0
  41. data/blogs/gknit/gknit_files/figure-gfm/diverging_bar.png +0 -0
  42. data/blogs/gknit/gknit_files/figure-html/bubble-1.png +0 -0
  43. data/blogs/gknit/gknit_files/figure-html/diverging_bar.png +0 -0
  44. data/blogs/gknit/lst.rds +0 -0
  45. data/blogs/gknit/model.rb +1 -1
  46. data/blogs/gknit/stats.bib +0 -0
  47. data/blogs/manual/include_model_local_repro.Rmd +14 -0
  48. data/blogs/manual/include_model_local_repro.md +75 -0
  49. data/blogs/manual/lst.rds +0 -0
  50. data/blogs/manual/manual.Rmd +855 -239
  51. data/blogs/manual/manual.log +1786 -0
  52. data/blogs/manual/manual.md +1416 -667
  53. data/blogs/manual/manual.tex +1883 -1161
  54. data/blogs/manual/manual_files/figure-html/bubble-1.png +0 -0
  55. data/blogs/manual/manual_files/figure-html/diverging_bar.png +0 -0
  56. data/blogs/manual/manual_files/figure-latex/bubble-1.png +0 -0
  57. data/blogs/manual/model.rb +1 -1
  58. data/blogs/nse_dplyr/nse_dplyr.Rmd +84 -111
  59. data/blogs/nse_dplyr/nse_dplyr.log +928 -0
  60. data/blogs/nse_dplyr/nse_dplyr.md +198 -229
  61. data/blogs/oh_my/not_so.rb +0 -0
  62. data/blogs/oh_my/oh_my.Rmd +1234 -25
  63. data/blogs/oh_my/oh_my.log +804 -0
  64. data/blogs/oh_my/oh_my.md +1663 -86
  65. data/blogs/oh_my/oh_my.tex +821 -0
  66. data/blogs/oh_my/old.Rmd +15 -14
  67. data/blogs/ruby_plot/ruby_plot.Rmd +58 -82
  68. data/blogs/ruby_plot/ruby_plot.log +885 -0
  69. data/blogs/ruby_plot/ruby_plot.md +71 -102
  70. data/blogs/ruby_plot/ruby_plot.tex +940 -0
  71. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/dose_len.png +0 -0
  72. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facet_by_delivery.png +0 -0
  73. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facet_by_dose.png +0 -0
  74. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_by_delivery_color.png +0 -0
  75. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_by_delivery_color2.png +0 -0
  76. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_with_decorations.png +0 -0
  77. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_with_jitter.png +0 -0
  78. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/facets_with_points.png +0 -0
  79. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/final_box_plot.png +0 -0
  80. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/final_violin_plot.png +0 -0
  81. data/blogs/ruby_plot/ruby_plot_files/figure-gfm/violin_with_jitter.png +0 -0
  82. data/blogs/ruby_plot/ruby_plot_files/figure-html/dose_len.png +0 -0
  83. data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_delivery.png +0 -0
  84. data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_dose.png +0 -0
  85. data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color.png +0 -0
  86. data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color2.png +0 -0
  87. data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_decorations.png +0 -0
  88. data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_jitter.png +0 -0
  89. data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_points.png +0 -0
  90. data/blogs/ruby_plot/ruby_plot_files/figure-html/final_box_plot.png +0 -0
  91. data/blogs/ruby_plot/ruby_plot_files/figure-html/final_violin_plot.png +0 -0
  92. data/blogs/ruby_plot/ruby_plot_files/figure-html/violin_with_jitter.png +0 -0
  93. data/blogs/ruby_plot/ruby_plot_files/figure-latex/dose_len.png +0 -0
  94. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_delivery.png +0 -0
  95. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_dose.png +0 -0
  96. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color.png +0 -0
  97. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color2.png +0 -0
  98. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_decorations.png +0 -0
  99. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_jitter.png +0 -0
  100. data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_points.png +0 -0
  101. data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_box_plot.png +0 -0
  102. data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_violin_plot.png +0 -0
  103. data/blogs/ruby_plot/ruby_plot_files/figure-latex/violin_with_jitter.png +0 -0
  104. data/blogs/test/test.Rmd +14 -0
  105. data/blogs/test/test.md +10 -0
  106. data/examples/50Plots_MasterList/Images/midwest-scatterplot.PNG +0 -0
  107. data/examples/50Plots_MasterList/ScatterPlot.rb +2 -1
  108. data/examples/50Plots_MasterList/scatter_plot.rb +1 -0
  109. data/examples/Bibliography/master.bib +0 -0
  110. data/examples/Bibliography/stats.bib +0 -0
  111. data/examples/R/calc.R +0 -0
  112. data/examples/R/java_interop.R +0 -0
  113. data/examples/bioconductor_deseq2_airway/Documentation/DESeq2-airway-walkthrough.md +56 -0
  114. data/examples/bioconductor_deseq2_airway/bench_galaaz_three_same_process.rb +54 -0
  115. data/examples/bioconductor_deseq2_airway/bench_r_three_same_process.R +34 -0
  116. data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb +34 -0
  117. data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz_optimized.rb +35 -0
  118. data/examples/bioconductor_deseq2_airway/deseq2_airway_minimal.R +30 -0
  119. data/examples/bioconductor_deseq2_airway/deseq2_airway_pipeline_for_bench.R +36 -0
  120. data/examples/islr/all.rb +14 -0
  121. data/examples/islr/ch2.spec.rb +38 -7
  122. data/examples/islr/ch3.spec.rb +12 -2
  123. data/examples/islr/ch3_boston.rb +28 -0
  124. data/examples/islr/ch3_multiple_regression.rb +1 -0
  125. data/examples/islr/ch6.spec.rb +25 -1
  126. data/examples/islr/x_y_rnorm.jpg +0 -0
  127. data/examples/latex_templates/Test-acm_article/acm_proc_article-sp.cls +0 -0
  128. data/examples/latex_templates/Test-acm_article/sigproc.bib +0 -0
  129. data/examples/latex_templates/Test-acs_article/acs-Test-acs_article.bib +0 -0
  130. data/examples/latex_templates/Test-acs_article/acs-my_output.bib +0 -0
  131. data/examples/latex_templates/Test-aea_article/BibFile.bib +0 -0
  132. data/examples/latex_templates/Test-aea_article/Test-aea_article.Rmd +0 -0
  133. data/examples/latex_templates/Test-aea_article/references.bib +0 -0
  134. data/examples/latex_templates/Test-amq_article/Test-amq_article.Rmd +0 -0
  135. data/examples/latex_templates/Test-amq_article/Test-amq_article.pdfsync +0 -0
  136. data/examples/latex_templates/Test-ieee_article/IEEEtran.bst +0 -0
  137. data/examples/latex_templates/Test-ieee_article/mybibfile.bib +0 -0
  138. data/examples/latex_templates/Test-rjournal_article/RJournal.sty +0 -0
  139. data/examples/latex_templates/Test-rjournal_article/RJreferences.bib +0 -0
  140. data/examples/latex_templates/Test-rjournal_article/Test-rjournal_article.Rmd +0 -0
  141. data/examples/misc/baseball.csv +0 -0
  142. data/examples/misc/ggplot.rb +5 -3
  143. data/examples/misc/moneyball.rb +1 -0
  144. data/examples/misc/subsetting.rb +1 -0
  145. data/examples/multithread_shards_to_r/shards_to_r.rb +68 -0
  146. data/examples/rmarkdown/svm-rmarkdown-anon-ms-example/svm-rmarkdown-anon-ms-example.Rmd +0 -0
  147. data/examples/rmarkdown/svm-rmarkdown-article-example/svm-rmarkdown-article-example.Rmd +0 -0
  148. data/examples/rmarkdown/svm-rmarkdown-beamer-example/svm-rmarkdown-beamer-example.Rmd +0 -0
  149. data/examples/rmarkdown/svm-rmarkdown-cv/svm-rmarkdown-cv.Rmd +0 -0
  150. data/examples/rmarkdown/svm-rmarkdown-syllabus-example/attend-grade-relationships.csv +0 -0
  151. data/examples/rmarkdown/svm-rmarkdown-syllabus-example/svm-rmarkdown-syllabus-example.Rmd +0 -0
  152. data/examples/rmarkdown/svm-xaringan-example/svm-xaringan-example.Rmd +0 -0
  153. data/examples/sthda_ggplot/README.md +0 -0
  154. data/examples/sthda_ggplot/RUN.md +41 -0
  155. data/examples/sthda_ggplot/all.rb +1 -0
  156. data/examples/sthda_ggplot/one_variable_continuous/density_gg.rb +1 -0
  157. data/examples/sthda_ggplot/one_variable_continuous/geom_area.rb +1 -0
  158. data/examples/sthda_ggplot/one_variable_continuous/geom_density.rb +3 -0
  159. data/examples/sthda_ggplot/one_variable_continuous/geom_dotplot.rb +1 -0
  160. data/examples/sthda_ggplot/one_variable_continuous/geom_freqpoly.rb +1 -0
  161. data/examples/sthda_ggplot/one_variable_continuous/geom_histogram.rb +1 -0
  162. data/examples/sthda_ggplot/one_variable_continuous/histogram_density.rb +1 -0
  163. data/examples/sthda_ggplot/one_variable_continuous/stat.rb +1 -0
  164. data/examples/sthda_ggplot/one_variable_discrete/bar.rb +1 -0
  165. data/examples/sthda_ggplot/qplots/box_violin_dot.rb +1 -0
  166. data/examples/sthda_ggplot/qplots/scatter_plots.rb +1 -0
  167. data/examples/sthda_ggplot/scatter_gg.rb +1 -0
  168. data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_bin2d.rb +1 -0
  169. data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_density2d.rb +1 -0
  170. data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_hex.rb +1 -0
  171. data/examples/sthda_ggplot/two_variables_cont_cont/geom_point.rb +1 -0
  172. data/examples/sthda_ggplot/two_variables_cont_cont/geom_smooth.rb +1 -0
  173. data/examples/sthda_ggplot/two_variables_cont_cont/misc.rb +1 -0
  174. data/examples/sthda_ggplot/two_variables_cont_function/geom_area.rb +5 -3
  175. data/examples/sthda_ggplot/two_variables_disc_cont/geom_bar.rb +1 -0
  176. data/examples/sthda_ggplot/two_variables_disc_cont/geom_boxplot.rb +1 -0
  177. data/examples/sthda_ggplot/two_variables_disc_cont/geom_dotplot.rb +1 -0
  178. data/examples/sthda_ggplot/two_variables_disc_cont/geom_jitter.rb +1 -0
  179. data/examples/sthda_ggplot/two_variables_disc_cont/geom_line.rb +1 -0
  180. data/examples/sthda_ggplot/two_variables_disc_cont/geom_violin.rb +1 -0
  181. data/examples/sthda_ggplot/two_variables_disc_disc/geom_jitter.rb +1 -0
  182. data/examples/sthda_ggplot/two_variables_error/geom_crossbar.rb +1 -0
  183. data/ext/new_bridge/Makefile +46 -0
  184. data/ext/new_bridge/galaaz_gatekeeper_phase0.cpp +12 -0
  185. data/ext/new_bridge/galaaz_gatekeeper_phase1.cpp +1639 -0
  186. data/lib/R_interface/galaaz_device.R +20 -0
  187. data/lib/R_interface/include_engine.R +109 -0
  188. data/lib/R_interface/new_bridge_adapter.rb +824 -0
  189. data/lib/R_interface/r.rb +177 -25
  190. data/lib/R_interface/r_arrow.rb +113 -0
  191. data/lib/R_interface/r_libs.R +3 -3
  192. data/lib/R_interface/r_methods.rb +13 -126
  193. data/lib/R_interface/r_module_s.rb +0 -0
  194. data/lib/R_interface/rbinary_operators.rb +20 -2
  195. data/lib/R_interface/rclosure.rb +5 -1
  196. data/lib/R_interface/rdata_frame.rb +34 -70
  197. data/lib/R_interface/rdevice.rb +125 -0
  198. data/lib/R_interface/rdevices.R +0 -0
  199. data/lib/R_interface/renvironment.rb +10 -4
  200. data/lib/R_interface/rexpression.rb +5 -1
  201. data/lib/R_interface/rindexed_object.rb +41 -13
  202. data/lib/R_interface/rlanguage.rb +20 -62
  203. data/lib/R_interface/rlist.rb +115 -25
  204. data/lib/R_interface/rlogical_operators.rb +0 -0
  205. data/lib/R_interface/rmatrix.rb +2 -11
  206. data/lib/R_interface/rmd_indexed_object.rb +5 -1
  207. data/lib/R_interface/robject.rb +348 -290
  208. data/lib/R_interface/rpkg.rb +0 -0
  209. data/lib/R_interface/rsupport.rb +609 -328
  210. data/lib/R_interface/rsupport_scope.rb +2 -1
  211. data/lib/R_interface/rsymbol.rb +50 -0
  212. data/lib/R_interface/ruby_callback.rb +2 -3
  213. data/lib/R_interface/ruby_extensions.rb +225 -175
  214. data/lib/R_interface/runary_operators.rb +0 -0
  215. data/lib/R_interface/rvector.rb +162 -31
  216. data/lib/galaaz.rb +0 -0
  217. data/lib/galaaz_jruby.rb +22 -0
  218. data/lib/galaaz_ruby.rb +34 -0
  219. data/lib/gknit/diagnostics.rb +50 -0
  220. data/lib/gknit/draft.rb +23 -17
  221. data/lib/gknit/include_engine.rb +15 -7
  222. data/lib/gknit/knitr_engine.rb +223 -74
  223. data/lib/gknit/rb_engine.rb +3 -3
  224. data/lib/gknit/ruby_engine.rb +0 -0
  225. data/lib/gknit.rb +1 -0
  226. data/lib/new_bridge/bootstrap/windows_bootstrap.rb +285 -0
  227. data/lib/new_bridge/envelope.rb +51 -0
  228. data/lib/new_bridge/eval_result.rb +26 -0
  229. data/lib/new_bridge/framing.rb +39 -0
  230. data/lib/new_bridge/instance_pool_client.rb +38 -0
  231. data/lib/new_bridge/r_instance_manager.rb +404 -0
  232. data/lib/new_bridge/session_client.rb +530 -0
  233. data/lib/new_bridge/tcp_framed.rb +44 -0
  234. data/lib/new_bridge.rb +9 -0
  235. data/lib/util/exec_ruby.rb +95 -20
  236. data/lib/util/inline_file.rb +35 -30
  237. data/new_bridge_specs/benchmark_phase5_5_unboxing_spec.rb +96 -0
  238. data/new_bridge_specs/eval_r_async_spec.rb +113 -0
  239. data/new_bridge_specs/integration_phase5_1_concurrent_spec.rb +50 -0
  240. data/new_bridge_specs/integration_phase5_1_eval_spec.rb +16 -0
  241. data/new_bridge_specs/integration_phase5_1_r_api_spec.rb +25 -0
  242. data/new_bridge_specs/integration_phase5_1_smoke_spec.rb +31 -0
  243. data/new_bridge_specs/integration_phase5_2_dataframe_unboxing_spec.rb +19 -0
  244. data/new_bridge_specs/integration_phase5_2_handle_eval_unboxing_spec.rb +25 -0
  245. data/new_bridge_specs/integration_phase5_3_callback_args_spec.rb +28 -0
  246. data/new_bridge_specs/integration_phase5_3_callback_error_spec.rb +22 -0
  247. data/new_bridge_specs/integration_phase5_3_callback_timeout_spec.rb +28 -0
  248. data/new_bridge_specs/integration_phase5_3_callbacks_smoke_spec.rb +22 -0
  249. data/new_bridge_specs/integration_phase5_3_edge_cases_spec.rb +52 -0
  250. data/new_bridge_specs/integration_phase5_3_nested_spec.rb +30 -0
  251. data/new_bridge_specs/integration_phase5_4_concurrent_sessions_spec.rb +53 -0
  252. data/new_bridge_specs/integration_phase5_4_nested_session_callbacks_spec.rb +49 -0
  253. data/new_bridge_specs/integration_phase5_4_session_routing_spec.rb +38 -0
  254. data/new_bridge_specs/integration_phase5_5_stress_concurrency_spec.rb +52 -0
  255. data/new_bridge_specs/integration_phase5_5_unbox_walk_spec.rb +46 -0
  256. data/new_bridge_specs/phase0_protocol_spec.rb +96 -0
  257. data/new_bridge_specs/phase1_req_ret_spec.rb +66 -0
  258. data/new_bridge_specs/phase2_multi_instance_spec.rb +67 -0
  259. data/new_bridge_specs/phase3_callbacks_spec.rb +71 -0
  260. data/new_bridge_specs/phase4_2_hardening_spec.rb +252 -0
  261. data/new_bridge_specs/phase4_3_r_instance_manager_spec.rb +85 -0
  262. data/new_bridge_specs/phase4_nested_callbacks_spec.rb +123 -0
  263. data/r_requires/ggplot.rb +0 -0
  264. data/r_requires/knitr.rb +0 -0
  265. data/specs/all.rb +15 -11
  266. data/specs/arrow_from_ruby_batches_spec.rb +50 -0
  267. data/specs/arrow_semantics_spec.rb +64 -0
  268. data/specs/bridge_concurrent_spec.rb +46 -0
  269. data/specs/bridge_nested_spec.rb +25 -0
  270. data/specs/dataframe_semantics_spec.rb +122 -0
  271. data/specs/dataframe_single_index_logical_filter_spec.rb +21 -0
  272. data/specs/dispatch_probe_cache_spec.rb +38 -0
  273. data/specs/dispatch_probe_error_class_fallback_spec.rb +20 -0
  274. data/specs/dispatch_probe_fallback_spec.rb +18 -0
  275. data/specs/environment_semantics_spec.rb +89 -0
  276. data/specs/field_access_spec.rb +31 -0
  277. data/specs/figures/bg.jpeg +0 -0
  278. data/specs/figures/bg.png +0 -0
  279. data/specs/figures/bg.svg +168 -57
  280. data/specs/figures/dose_len.png +0 -0
  281. data/specs/figures/no_args.jpeg +0 -0
  282. data/specs/figures/no_args.png +0 -0
  283. data/specs/figures/no_args.svg +168 -57
  284. data/specs/figures/width_height.jpeg +0 -0
  285. data/specs/figures/width_height.png +0 -0
  286. data/specs/figures/width_height_units1.jpeg +0 -0
  287. data/specs/figures/width_height_units1.png +0 -0
  288. data/specs/figures/width_height_units2.jpeg +0 -0
  289. data/specs/figures/width_height_units2.png +0 -0
  290. data/specs/formula_semantics_spec.rb +81 -0
  291. data/specs/galaaz_util_exec_ruby_spec.rb +85 -0
  292. data/specs/galaaz_util_inline_file_spec.rb +54 -0
  293. data/specs/gknit_cli_option_permutation_spec.rb +24 -0
  294. data/specs/gknit_include_engine_spec.rb +72 -0
  295. data/specs/gknit_install_timeout_report_spec.rb +69 -0
  296. data/specs/gknit_internal_error_report_spec.rb +57 -0
  297. data/specs/gknit_vector_map_output_spec.rb +59 -0
  298. data/specs/globalenv_guardrail_spec.rb +52 -0
  299. data/specs/language_expression_semantics_spec.rb +145 -0
  300. data/specs/list_semantics_spec.rb +111 -0
  301. data/specs/new_bridge_bulk_dataframe_transfer_spec.rb +44 -0
  302. data/specs/new_bridge_bulk_vector_transfer_spec.rb +73 -0
  303. data/specs/new_bridge_callback_timeout_spec.rb +69 -0
  304. data/specs/new_bridge_eval_r_fallback_spec.rb +55 -0
  305. data/specs/nil_null_spec.rb +42 -0
  306. data/specs/object_build_phase2_spec.rb +53 -0
  307. data/specs/phase1_callback_bridge_spec.rb +84 -0
  308. data/specs/phase2_gknit_generic_rendering_guardrail_spec.rb +46 -0
  309. data/specs/phase2_gknit_no_raw_code_leakage_spec.rb +43 -0
  310. data/specs/phase3_gknit_generic_graphics_capture_spec.rb +71 -0
  311. data/specs/plot_device_semantics_spec.rb +28 -0
  312. data/specs/plot_snapshot_semantics_spec.rb +58 -0
  313. data/specs/protocol_result_spec.rb +236 -0
  314. data/specs/r_batch_fail_fast_spec.rb +47 -0
  315. data/specs/r_bridge_bootstrap_spec.rb +11 -0
  316. data/specs/r_devices.spec.rb +1 -1
  317. data/specs/r_eval.spec.rb +16 -18
  318. data/specs/r_function.spec.rb +1 -1
  319. data/specs/r_instance_manager_spec.rb +285 -0
  320. data/specs/r_list_apply.spec.rb +15 -15
  321. data/specs/r_matrix.spec.rb +0 -0
  322. data/specs/r_nse.spec.rb +5 -5
  323. data/specs/r_object_send_dispatch_spec.rb +13 -0
  324. data/specs/r_vector_comparator_spec.rb +8 -0
  325. data/specs/r_vector_creation.spec.rb +0 -0
  326. data/specs/r_vector_functions.spec.rb +0 -0
  327. data/specs/r_vector_object.spec.rb +0 -0
  328. data/specs/r_vector_operators.spec.rb +0 -0
  329. data/specs/r_vector_structured_scalar_reads_spec.rb +35 -0
  330. data/specs/r_vector_subsetting.spec.rb +0 -0
  331. data/specs/range_helper_spec.rb +21 -0
  332. data/specs/rsupport_scope_spec.rb +28 -0
  333. data/specs/rsupport_var_name_thread_safety_spec.rb +24 -0
  334. data/specs/scalar_character_spec.rb +44 -0
  335. data/specs/scoped_symbol_dsl_refinement_spec.rb +40 -0
  336. data/specs/session_env_bridge_spec.rb +25 -0
  337. data/specs/simplecov_bootstrap_spec.rb +10 -0
  338. data/specs/spec_helper.rb +10 -0
  339. data/specs/tmp.rb +0 -0
  340. data/specs/unboxing_recursion_regression_spec.rb +30 -0
  341. data/specs/unboxing_spec.rb +49 -0
  342. data/specs/verify_callbacks.rb +42 -0
  343. data/sty/galaaz.sty +0 -0
  344. data/version.rb +1 -1
  345. metadata +219 -63
  346. data/blogs/galaaz_ggplot/galaaz_ggplot.html +0 -520
  347. data/blogs/galaaz_ggplot/galaaz_ggplot.pdf +0 -0
  348. data/blogs/galaaz_ggplot/midwest.html +0 -188
  349. data/blogs/gknit/gknit.html +0 -2266
  350. data/blogs/gknit/gknit.pdf +0 -0
  351. data/blogs/manual/manual.html +0 -4638
  352. data/blogs/manual/manual.pdf +0 -0
  353. data/blogs/manual/manual_files/figure-latex/diverging_bar.pdf +0 -0
  354. data/blogs/nse_dplyr/nse_dplyr.html +0 -878
  355. data/blogs/nse_dplyr/nse_dplyr.pdf +0 -0
  356. data/blogs/oh_my/oh_my.html +0 -568
  357. data/blogs/ruby_plot/ruby_plot.html +0 -544
  358. data/blogs/ruby_plot/ruby_plot.pdf +0 -0
  359. data/examples/latex_templates/Test-acs_article/Test-acs_article.pdf +0 -0
  360. data/examples/latex_templates/Test-aea_article/Test-aea_article.pdf +0 -0
  361. data/examples/latex_templates/Test-amq_article/Test-amq_article.pdf +0 -0
  362. data/examples/latex_templates/Test-amq_article/pics/Figure2.pdf +0 -0
  363. data/examples/latex_templates/Test-asa_article/Test-asa_article.pdf +0 -0
  364. data/examples/latex_templates/Test-ieee_article/Test-ieee_article.pdf +0 -0
  365. data/examples/latex_templates/Test-rjournal_article/RJwrapper.pdf +0 -0
  366. data/examples/latex_templates/Test-springer_article/Test-springer_article.pdf +0 -0
  367. data/examples/rmarkdown/svm-rmarkdown-anon-ms-example/svm-rmarkdown-anon-ms-example.pdf +0 -0
  368. data/examples/rmarkdown/svm-rmarkdown-article-example/svm-rmarkdown-article-example.pdf +0 -0
  369. data/examples/rmarkdown/svm-rmarkdown-beamer-example/svm-rmarkdown-beamer-example.pdf +0 -0
  370. data/examples/rmarkdown/svm-rmarkdown-cv/svm-rmarkdown-cv.pdf +0 -0
  371. data/examples/rmarkdown/svm-rmarkdown-syllabus-example/svm-rmarkdown-syllabus-example.pdf +0 -0
  372. data/specs/r_dataframe.spec.rb +0 -379
  373. data/specs/r_environment.spec.rb +0 -140
  374. data/specs/r_formula.spec.rb +0 -232
  375. data/specs/r_language.spec.rb +0 -112
  376. data/specs/r_list.spec.rb +0 -293
  377. data/specs/r_plots.spec.rb +0 -72
  378. data/specs/ruby_expression.spec.rb +0 -316
data/blogs/oh_my/oh_my.md CHANGED
@@ -3,8 +3,8 @@ title: "Extending R with classes, modules, procs, lambdas, oh my!"
3
3
  author:
4
4
  - "Rodrigo Botafogo"
5
5
  - "Daniel Mossé - University of Pittsburgh"
6
- tags: [Tech, Data Science, Ruby, R, GraalVM]
7
- date: "November 19th, 2018"
6
+ tags: [Tech, Data Science, Ruby, R, JRuby, "GNU R", Galaaz]
7
+ date: "November 19th, 2018 (narrative updated for Galaaz 2.0, 2026)"
8
8
  output:
9
9
  html_document:
10
10
  self_contained: true
@@ -23,17 +23,19 @@ fontsize: 11pt
23
23
 
24
24
  # Introduction
25
25
 
26
- This paper introduces and compares Galaaz with R's S4. It is a shameless rip off of
26
+ This paper introduces and compares Galaaz with R's S4. It is **modeled closely** on
27
27
  ["A '(not so)' Short Introduction to S4"](https://cran.r-project.org/doc/contrib/Genolini-S4tutorialV0-5en.pdf) by Christophe Genolini and follows the same structure and examples presented there.
28
28
 
29
29
  Galaaz is a Ruby Gem (library) that allows very tight integration between Ruby and R.
30
- It's integration is much tigher and transparent from what one can get beetween RinRuby
31
- or similar solutions in Python
30
+ Its integration is tighter and more transparent than what one can get between RinRuby
31
+ or similar solutions in Python,
32
32
  such as [PypeR](https://pypi.python.org/pypi/PypeR/1.1.0), [rpy2](http://rpy2.bitbucket.org/)
33
- and other similar solutions. Galaaz targets the GraalVM and it
34
- integrates with FastR, a high performance R interpreter for the GraalVM.
33
+ and other similar solutions.
35
34
 
36
- GraalVM:
35
+ **Galaaz 2.0** runs on **[JRuby](https://www.jruby.org/)** and drives **GNU R** through a **bridge**,
36
+ so Ruby code can create and manipulate R objects and call R functions while staying idiomatic Ruby.
37
+ An earlier prototype used Oracle’s **GraalVM** with **TruffleRuby** and **FastR**; that stack is
38
+ historical and is **not** what current Galaaz targets.
37
39
 
38
40
 
39
41
  # Bases of Object Programming
@@ -68,14 +70,14 @@ type information is also not a "compile" time type, since R is not compiled. Th
68
70
  checked at runtime. The same checking can be done in Ruby and we will do it later in this
69
71
  document.
70
72
 
71
- In the example bellow, we create
73
+ In the example below, we create
72
74
  class Trajectories with two instance variables, 'times' and 'matrix'. We will not go over
73
75
  the details of instance variables in Ruby, but here we created those variables with the
74
76
  keyword 'attr_reader' and a colom before the variables name:
75
77
 
76
78
 
77
79
 
78
- ```ruby
80
+ ``` ruby
79
81
  class Trajectories
80
82
  attr_reader :times
81
83
  attr_reader :matrix
@@ -84,10 +86,10 @@ end
84
86
 
85
87
 
86
88
  In order to create a new instance of object Trajectories we call method new on the class and
87
- we can store the result in a varible (not an instance variable) as bellow:
89
+ we can store the result in a variable (not an instance variable) as below:
88
90
 
89
91
 
90
- ```ruby
92
+ ``` ruby
91
93
  @traj = Trajectories.new
92
94
  ```
93
95
 
@@ -95,18 +97,18 @@ We now have in variable '@traj' a Trajectories object. In Ruby, printing variab
95
97
  only print the class name of the object and not it contents as in R.
96
98
 
97
99
 
98
- ```ruby
100
+ ``` ruby
99
101
  puts @traj
100
102
  ```
101
103
 
102
104
  ```
103
- ## #<RC::Trajectories:0x2d8>
105
+ ## #<RC::Trajectories:0x1e6bdce4>
104
106
  ```
105
107
 
106
108
  To see the contents of an object, one needs to access its components using the '.' operator:
107
109
 
108
110
 
109
- ```ruby
111
+ ``` ruby
110
112
  puts @traj.times
111
113
  ```
112
114
 
@@ -115,12 +117,12 @@ puts @traj.times
115
117
  Since there is no content stored in 'times' nor 'matrix', nil is returned. In order to add
116
118
  a value in the variables, we need to add a constructor to the class Trajectories. In R, a
117
119
  constructor is build by default, in Ruby, this has to be created by adding a method called
118
- 'initialize'. In the example bellow, we will create the initializer that accepts two values,
120
+ 'initialize'. In the example below, we will create the initializer that accepts two values,
119
121
  a 'times' value and a 'matrix' value and they are used to initialize the value of the
120
122
  instance variables:
121
123
 
122
124
 
123
- ```ruby
125
+ ``` ruby
124
126
  class Trajectories
125
127
 
126
128
  attr_reader :times
@@ -151,10 +153,10 @@ i.e., R functions are all defined in Galaaz in the R namespace.
151
153
  Since Galaaz is Ruby and not R, some syntax adjustments are sometimes necessary. For instance,
152
154
  in R, a range is represented as '(1:4)', in Ruby, the same range is represented as '(1..4)'.
153
155
  When passing arguments to an R function in R one uses the '=' sign after the slot name; in R,
154
- one uses the ':' operator after parameter's name as we can see bellow:
156
+ one uses the ':' operator after parameter's name as we can see below:
155
157
 
156
158
 
157
- ```ruby
159
+ ``` ruby
158
160
  # Create a Trajectories passing a times vector, but no matrix parameter
159
161
  @traj = Trajectories.new(times: R.c(1, 2, 3, 4))
160
162
 
@@ -173,7 +175,7 @@ that everything is fine:
173
175
 
174
176
 
175
177
 
176
- ```ruby
178
+ ``` ruby
177
179
  puts @traj.times
178
180
  ```
179
181
 
@@ -185,7 +187,7 @@ We now have the expected value. Note that the 'times' vector is printed exactly
185
187
  if we were using GNU R. Let's now take a look at variable 'traj2':
186
188
 
187
189
 
188
- ```ruby
190
+ ``` ruby
189
191
  puts @traj2.times
190
192
  puts
191
193
  puts @traj2.matrix
@@ -228,7 +230,7 @@ Cochin and Saint-Anne. We first show the code in R and the corresponding Galaaz
228
230
  This same code in Galaaz becomes:
229
231
 
230
232
 
231
- ```ruby
233
+ ``` ruby
232
234
  @trajPitie = Trajectories.new
233
235
 
234
236
  @trajCochin = Trajectories.new(times: R.c(1,3,4,5),
@@ -250,7 +252,7 @@ This same code in Galaaz becomes:
250
252
  Let's check that the 'times' and 'matrix' instance variables were correctly set:
251
253
 
252
254
 
253
- ```ruby
255
+ ``` ruby
254
256
  puts @trajCochin.times
255
257
  puts
256
258
  puts @trajCochin.matrix
@@ -261,11 +263,11 @@ puts @trajStAnne.times
261
263
  ```
262
264
  ## [1] 1 3 4 5
263
265
  ##
264
- ## [,1] [,2] [,3] [,4]
265
- ## [1,] 15.0 15.1 15.2 15.2
266
- ## [2,] 16.0 15.9 16.0 16.4
267
- ## [3,] 15.2 NA 15.3 15.3
268
- ## [4,] 15.7 15.6 15.8 16.0
266
+ ## [,1] [,2] [,3] [,4]
267
+ ## g2_v662 15.0 15.1 15.2 15.2
268
+ ## g2_v663 16.0 15.9 16.0 16.4
269
+ ## g2_v664 15.2 NA 15.3 15.3
270
+ ## g2_v665 15.7 15.6 15.8 16.0
269
271
  ##
270
272
  ## [1] 1 2 3 4 5 6 7 8 9 10 12 14 16 18 20 22 24 26 28 30 32
271
273
  ```
@@ -280,11 +282,11 @@ recommend its use, there are many cases in which default values are useful and m
280
282
  We have already seen default values in this document, with the default being 'nil'. This was
281
283
  necessary in order to be able to create our constructor and passing it the proper values.
282
284
 
283
- In the example bellow, a class TrajectoriesBis is created with default value 1 for times and a
285
+ In the example below, a class TrajectoriesBis is created with default value 1 for times and a
284
286
  matrix with no elements in matrix.
285
287
 
286
288
 
287
- ```ruby
289
+ ``` ruby
288
290
  class TrajectoriesBis
289
291
 
290
292
  attr_reader :times
@@ -309,7 +311,7 @@ end
309
311
  Let's take a look at our new class:
310
312
 
311
313
 
312
- ```ruby
314
+ ``` ruby
313
315
  puts @traj_bis.times
314
316
  puts
315
317
  puts @traj_bis.matrix
@@ -326,7 +328,7 @@ Note that '@traj_bis.times' is the numeric 1, and what we actually want is a vec
326
328
  with [1] in it.
327
329
 
328
330
 
329
- ```ruby
331
+ ``` ruby
330
332
  class TrajectoriesBis
331
333
 
332
334
  attr_reader :times
@@ -350,7 +352,7 @@ end
350
352
  ```
351
353
 
352
354
 
353
- ```ruby
355
+ ``` ruby
354
356
  puts @traj_bis.times
355
357
  puts
356
358
  puts @traj_bis.matrix
@@ -392,7 +394,7 @@ Trajectories to add methods to it. In SS4, a method 'plot' is added to Trajecto
392
394
  point, Renjin and Galaaz do not yet have plotting capabilities, so we will have to skip this
393
395
  method and go directly to the implementation of the 'print' method.
394
396
 
395
- Bellow is the R code for method print:
397
+ Below is the R code for method print:
396
398
 
397
399
  ```
398
400
  > setMethod ("print","Trajectories",
@@ -417,7 +419,7 @@ Ruby's print is defined inside the Kernel class, so, in order to call Ruby's pri
417
419
  definition of Trajectories's print we need to write 'Kernel.print'.
418
420
 
419
421
 
420
- ```ruby
422
+ ``` ruby
421
423
  class Trajectories
422
424
 
423
425
  attr_reader :times
@@ -452,19 +454,22 @@ end
452
454
  ```
453
455
 
454
456
 
455
- ```ruby
457
+ ``` ruby
456
458
  @trajCochin.print
457
459
  ```
458
460
 
459
461
  ```
460
462
  ## *** Class Trajectories, method Print ***
461
- ## times = [1] 1 3 4 5
463
+ ## times = 1
464
+ ## 3
465
+ ## 4
466
+ ## 5
462
467
  ## traj =
463
- ## [,1] [,2] [,3] [,4]
464
- ## [1,] 15.0 15.1 15.2 15.2
465
- ## [2,] 16.0 15.9 16.0 16.4
466
- ## [3,] 15.2 NA 15.3 15.3
467
- ## [4,] 15.7 15.6 15.8 16.0
468
+ ## [,1] [,2] [,3] [,4]
469
+ ## g2_v662 15.0 15.1 15.2 15.2
470
+ ## g2_v663 16.0 15.9 16.0 16.4
471
+ ## g2_v664 15.2 NA 15.3 15.3
472
+ ## g2_v665 15.7 15.6 15.8 16.0
468
473
  ## ******* End Print (trajectories) *******
469
474
  ```
470
475
 
@@ -501,29 +506,28 @@ features of Galaaz, some we have already seen, others will be described now:
501
506
  function look like a method of the object. For instance, R.nrow(@matrix), can be called by
502
507
  doing @matrix.nrow;
503
508
 
504
- * In R, every number is converted to a vector and this can be done with method R.i. Converting
505
- a vector with only one number back to a number can be done with method '.gz'. So if @num is
506
- an R vector that holds a number, then @num.gz is a number that can be used normally with Ruby
507
- methods;
509
+ * In R, every number is a length-1 vector. In Galaaz 2.0, unwrap a length-1 R vector
510
+ to a Ruby number with `>> 0` (or `unboxed_get(0)`). Older Galaaz docs used `.gz` /
511
+ `<< 0` for the same idea; `<<` still works as a compatibility alias for `>>`;
508
512
 
509
- * R functions and Ruby methods can be used freely in Galaaz. We show bellow two different ways
513
+ * R functions and Ruby methods can be used freely in Galaaz. We show below two different ways
510
514
  of getting the minimum of a number, either by calling R.min or by getting the minimum of an
511
515
  array, with the min method;
512
516
 
513
517
  * Galaaz allows for method 'chaining'. Method chaining, also known as named parameter idiom, is
514
518
  a common syntax for invoking multiple method calls in object-oriented programming languages.
515
519
  Each method returns an object, allowing the calls to be chained together in a single statement
516
- without requiring variables to store the intermediate results. For instance @matrix.nrow.gz,
517
- which returns the number of rows of the matrix as a number;
520
+ without requiring variables to store the intermediate results. For instance `@matrix.nrow >> 0`,
521
+ which returns the number of rows of the matrix as a Ruby number;
518
522
 
519
523
  * Ranges in Ruby are represented by (x..y), where x is the beginning of the range and y its end.
520
524
  An R matrix can be indexed by range, object@traj[1:nrowShow,1:ncolShow], the same result is
521
525
  obtained in Galaaz by indexing @matrix[(1..nrow_show), (1..ncol_show)]. Observe that this
522
- statement is then chained with the format function and with the pp method to print the matrix.
526
+ statement is then chained with the format function and printed with `puts`.
523
527
 
524
528
 
525
529
 
526
- ```ruby
530
+ ``` ruby
527
531
  class Trajectories
528
532
 
529
533
  #----------------------------------------------------------
@@ -534,8 +538,8 @@ class Trajectories
534
538
  puts("*** Class Trajectories, method Show *** ")
535
539
  Kernel.print("times = ")
536
540
  puts @times
537
- nrow_show = [10, @matrix.nrow << 0].min
538
- ncol_show = R.min(10, @matrix.ncol) << 0
541
+ nrow_show = [10, @matrix.nrow >> 0].min
542
+ ncol_show = R.min(10, @matrix.ncol) >> 0
539
543
  puts("* Traj (limited to a matrix 10x10) = ")
540
544
  puts @matrix[(1..nrow_show), (1..ncol_show)].format(digits: 2, nsmall: 2)
541
545
  puts("******* End Show (trajectories) ******* ")
@@ -545,56 +549,1629 @@ end
545
549
  ```
546
550
 
547
551
 
548
- ```ruby
552
+ ``` ruby
549
553
  @trajStAnne.show
550
554
  ```
551
555
 
552
556
  ```
553
- ## Message:
554
- ## Method << not found in R environment
555
- ```
556
-
557
- ```
558
- ## Message:
559
- ## /home/rbotafogo/desenv/galaaz/lib/R_interface/rsupport.rb:90:in `eval'
560
- ## /home/rbotafogo/desenv/galaaz/lib/R_interface/rsupport.rb:268:in `exec_function_name'
561
- ## /home/rbotafogo/desenv/galaaz/lib/R_interface/robject.rb:170:in `method_missing'
562
- ## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:113:in `show'
563
- ## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:103:in `get_binding'
564
- ## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:102:in `eval'
565
- ## /home/rbotafogo/desenv/galaaz/lib/util/exec_ruby.rb:102:in `exec_ruby'
566
- ## /home/rbotafogo/desenv/galaaz/lib/gknit/knitr_engine.rb:650:in `block in initialize'
567
- ## /home/rbotafogo/desenv/galaaz/lib/R_interface/ruby_callback.rb:77:in `call'
568
- ## /home/rbotafogo/desenv/galaaz/lib/R_interface/ruby_callback.rb:77:in `callback'
569
- ## (eval):3:in `function(...) {\n rb_method(...)'
570
- ## unknown.r:1:in `in_dir'
571
- ## unknown.r:1:in `block_exec'
572
- ## /home/rbotafogo/R/x86_64-pc-linux-gnu-library/fastr-20.1.0-3.6/knitr/R/block.R:92:in `call_block'
573
- ## /home/rbotafogo/R/x86_64-pc-linux-gnu-library/fastr-20.1.0-3.6/knitr/R/block.R:6:in `process_group.block'
574
- ## /home/rbotafogo/R/x86_64-pc-linux-gnu-library/fastr-20.1.0-3.6/knitr/R/block.R:3:in `<no source>'
575
- ## unknown.r:1:in `withCallingHandlers'
576
- ## unknown.r:1:in `process_file'
577
- ## unknown.r:1:in `<no source>'
578
- ## unknown.r:1:in `<no source>'
579
- ## <REPL>:4:in `<repl wrapper>'
580
- ## <REPL>:1
557
+ ## *** Class Trajectories, method Show ***
558
+ ## times = 1
559
+ ## 2
560
+ ## 3
561
+ ## 4
562
+ ## 5
563
+ ## 6
564
+ ## 7
565
+ ## 8
566
+ ## 9
567
+ ## 10
568
+ ## 12
569
+ ## 14
570
+ ## 16
571
+ ## 18
572
+ ## 20
573
+ ## 22
574
+ ## 24
575
+ ## 26
576
+ ## 28
577
+ ## 30
578
+ ## 32
579
+ ## * Traj (limited to a matrix 10x10) =
580
+ ## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
581
+ ## [1,] "15.95" "16.15" "16.25" "16.62" "16.86" "16.77" "16.80" "16.94" "17.26"
582
+ ## [2,] "16.20" "16.11" "16.37" "16.50" "16.70" "16.71" "17.12" "16.89" "17.37"
583
+ ## [3,] "15.94" "16.20" "16.51" "16.41" "16.86" "16.83" "16.99" "16.86" "17.22"
584
+ ## [4,] "15.64" "16.25" "16.31" "16.36" "16.69" "16.47" "17.06" "16.86" "17.22"
585
+ ## [5,] "16.44" "16.01" "16.08" "16.48" "16.39" "16.43" "17.06" "17.28" "17.40"
586
+ ## [6,] "16.10" "15.78" "16.26" "16.31" "16.71" "16.81" "16.81" "16.84" "17.04"
587
+ ## [7,] "15.98" "15.94" "16.44" "16.96" "16.40" "17.10" "17.06" "17.45" "16.89"
588
+ ## [8,] "16.29" "16.00" "16.28" "16.29" "16.49" "16.73" "16.72" "17.26" "17.48"
589
+ ## [9,] "16.12" "16.36" "16.53" "16.52" "16.68" "16.75" "16.89" "17.05" "16.98"
590
+ ## [10,] "15.92" "16.32" "16.39" "16.28" "16.61" "17.05" "17.09" "17.39" "17.33"
591
+ ## [,10]
592
+ ## [1,] "17.80"
593
+ ## [2,] "17.37"
594
+ ## [3,] "17.54"
595
+ ## [4,] "17.79"
596
+ ## [5,] "17.75"
597
+ ## [6,] "17.62"
598
+ ## [7,] "16.97"
599
+ ## [8,] "17.33"
600
+ ## [9,] "17.50"
601
+ ## [10,] "17.33"
602
+ ## ******* End Show (trajectories) *******
581
603
  ```
582
604
 
583
605
  Our show method has the same problem as SS4, i.e., if an empty trajectories object is created and
584
606
  we try to 'show' it, it will generate an error. Let's see it:
585
607
 
586
608
 
587
- ```ruby
609
+ ``` ruby
588
610
  @empty_traj = Trajectories.new
589
611
  ```
590
612
 
591
613
 
592
- ```ruby
614
+ ``` ruby
615
+ @empty_traj.show
616
+ ```
617
+
618
+ ```
619
+ ## undefined method 'nrow' for nil
620
+ ```
621
+
622
+ In this example, `@matrix` is `nil`, so calling `@matrix.nrow` raises
623
+ `undefined method 'nrow' for nil`. To fix this, we can either prevent an empty
624
+ trajectories class from being created, or make sure that method `show` will not
625
+ choke on the empty object. We will take the second alternative, to follow SS4,
626
+ and will check if either `@times` or `@matrix` is empty. If either one of them
627
+ is `nil`, then we will print a message saying so.
628
+
629
+ Although the first alternative, i.e., not allow for empty objects is a possibility in Ruby,
630
+ it seems that this is not the case for S4.
631
+
632
+
633
+ ``` ruby
634
+ class Trajectories
635
+
636
+ def show
637
+ if (@times.nil? || @matrix.nil?)
638
+ puts("*** Class Trajectories is empty!! *** ")
639
+ return
640
+ end
641
+ puts("*** Class Trajectories, method Show *** ")
642
+ Kernel.print("times = ")
643
+ puts @times
644
+ nrow_show = [10, @matrix.nrow >> 0].min
645
+ ncol_show = R.min(10, @matrix.ncol) >> 0
646
+ puts("* Traj (limited to a matrix 10x10) = ")
647
+ puts @matrix[(1..nrow_show), (1..ncol_show)].format(digits: 2, nsmall: 2)
648
+ puts("******* End Show (trajectories) ******* ")
649
+ end
650
+
651
+ end
652
+ ```
653
+
654
+
655
+ ``` ruby
593
656
  @empty_traj.show
594
657
  ```
595
658
 
596
659
  ```
597
- ## Message:
598
- ## undefined method `nrow' for nil:NilClass
660
+ ## *** Class Trajectories is empty!! ***
661
+ ```
662
+
663
+
664
+ # To Remove an Object
665
+
666
+ As far as I know, there isn't a good way of removing a defined class, but there might be
667
+ one and the interested user is directed to google it! In principle, there should not be
668
+ any real need to remove a defined class. Both in R and Galaaz, large programs are usually
669
+ written in a file and the file loaded. If one writes a wrong class, the better solution is
670
+ to correct it and then load it again. If the class is written directly on the console,
671
+ then leaving it there will not have any serious impact.
672
+
673
+ # Method count_missing
674
+
675
+ In R, methods 'print' and 'show' are methods that already exist. SS4 wants to add a method
676
+ called 'countMissing' which does not exist in R, and thus requires some special preparation. In
677
+ Ruby, every method we've created is a new method that exists inside the class. The fact that
678
+ 'print' happens to be also a method for class Kernel and 'show' is not, is not of special interest.
679
+ Actually we've seen that in order to call method print from the Kernel class we had to call
680
+ Kernel.print.
681
+
682
+ To create method 'count_missing' we just need to reopen the Trajectories class and add the
683
+ method the same way we've done with method 'show'. Again, let's first look at R's 'countMissing'
684
+ and then at Ruby's:
685
+
686
+
687
+ ```
688
+ > setMethod(
689
+ + f= "countMissing",
690
+ + signature= "Trajectories",
691
+ + definition=function(object){
692
+ + return(sum(is.na(object@traj)))
693
+ + }
694
+ + )
695
+ ```
696
+
697
+ Here we introduce another particular case of Galaaz. R has many methods that have a '.' in
698
+ their names, such as 'is.na'. In Ruby, the dot '.' has a special meaning as it is the way
699
+ we call a method on an object. Doing 'R.is.na' will not work. So, in Galaaz, R functions that
700
+ have a dot in them will have the dot substituted by '__'. So, method is.na in Galaaz, becomes
701
+ R.is__na. In method count_missing we use method chaining and convert the final count to a
702
+ Ruby number with `>> 0` (unbox).
703
+
704
+
705
+ ``` ruby
706
+ class Trajectories
707
+
708
+ def count_missing
709
+ return @matrix.is__na.sum >> 0
710
+ end
711
+
712
+ end
713
+ ```
714
+
715
+
716
+ ``` ruby
717
+ puts @trajCochin.count_missing
718
+ ```
719
+
720
+ ```
721
+ ## 1
722
+ ```
723
+
724
+ # To See the Methods
725
+
726
+ In order to see the methods we have defined so far, we call on class Trajectories the method
727
+ 'instance_methods' passing it one argument, 'false', as follows:
728
+
729
+
730
+ ``` ruby
731
+ puts Trajectories.instance_methods(false)
732
+ ```
733
+
734
+ ```
735
+ ## count_missing
736
+ ## times
737
+ ## print
738
+ ## show
739
+ ## matrix
740
+ ```
741
+
742
+ It is interesting to observe that we see our three methods 'count_missing', 'print' and 'show', but
743
+ we also see two other methods 'times' and 'matrix', but those last two as far as we know are
744
+ just instance variables and not methods, right? More on that when we talk about Accessors.
745
+
746
+ Galaaz and Ruby do not by default provide a way to see a method's code. However, if the user uses
747
+ a Ruby console such as Pry, then seeing methods and debugging is possible. Pry is beyond the
748
+ scope of this document.
749
+
750
+ # Construction
751
+
752
+ Every class in Ruby has a constructor, if not explicitly defined, at least implicitly. Method
753
+ initialize is the constructor method and the one that coordinates the whole construction process.
754
+
755
+ # Inspector
756
+
757
+ There is no default 'inspector' in Ruby as in R, although there is nothing that prevents the
758
+ developer from inspecting and validating the input. For example, in the object Trajectories, one may
759
+ want to check that the number of elements in 'times' is equal to the number of columns in 'matrix'
760
+ and if they are not, issue an error. In order to understand why this restriction exists, the user is
761
+ again directed to SS4.
762
+
763
+ Here we show the R code for this validation:
764
+
765
+ ```
766
+ > setClass(
767
+ + Class="Trajectories",
768
+ + representation(times="numeric",traj="matrix"),
769
+ + validity=function(object){
770
+ + cat("~~~ Trajectories: inspector ~~~ \\n")
771
+ + if(length(object@times)!=ncol(object@traj)){
772
+ + stop ("[Trajectories: validation] the number of temporal measurements does not correspond
773
+ + }else{}
774
+ + return(TRUE)
775
+ + }
776
+ + )
777
+ ```
778
+
779
+ In order to implement this validation we will coordinate it in the initialize method.
780
+
781
+
782
+ ``` ruby
783
+ class Trajectories
784
+
785
+ def initialize(times: nil, matrix: nil)
786
+ @times = times
787
+ @matrix = matrix
788
+
789
+ # validate the input, to make sure that size of @times and the number of columns in
790
+ # @matrix are the same
791
+ puts ("~~~ Trajectories: inspector ~~~ ")
792
+ raise "[Trajectories: validation] the number of temporal measurements does not correspond with the number of columns in the matrix" if ((@times.length >> 0) != (@matrix.ncol >> 0))
793
+
794
+ # show the object just created
795
+ show
796
+
797
+ end
798
+
799
+ end
800
+ ```
801
+
802
+ Let's first create a Trajectories that validates fine, i.e., the number of elements in @times is
803
+ equal to the number of columns of the matrix. In this case, we will show a message saying that
804
+ validation was done and then print the object.
805
+
806
+
807
+ ``` ruby
808
+ ok = Trajectories.new(times: R.c(1..2), matrix: R.matrix((1..2), ncol: 2))
809
+ ```
810
+
811
+ ```
812
+ ## ~~~ Trajectories: inspector ~~~
813
+ ## *** Class Trajectories, method Show ***
814
+ ## times = 1
815
+ ## 2
816
+ ## * Traj (limited to a matrix 10x10) =
817
+ ## 1
818
+ ## 2
819
+ ## ******* End Show (trajectories) *******
820
+ ```
821
+
822
+ Now, if we try to create a Trajectories that does not pass the validation criteria, our code
823
+ will raise an exception. Exceptions are a standard way to deal with errors in Ruby code and
824
+ many other object oriented languages. The interested reader should look for further documentation
825
+ on exceptions on the web.
826
+
827
+
828
+
829
+ ``` ruby
830
+ error = Trajectories.new(times: R.c(1..3), matrix: R.matrix((1..2), ncol: 2))
831
+ ```
832
+
833
+ ```
834
+ ## [Trajectories: validation] the number of temporal measurements does not correspond with the number of columns in the matrix
835
+ ```
836
+
837
+ The validation above does not consider the case when an empty object is created. Here we will
838
+ check to see if either times or matrix are nil; if either one of them is nil, then we will raise
839
+ an exception and interrupt the creation of the object. We also create a method validate that is
840
+ called from our initialize method.
841
+
842
+ Method validate has some interesting features about the integration of Galaaz and R. We compare
843
+ lengths after unboxing with `>> 0`, so the comparison is ordinary Ruby arithmetic on numbers.
844
+ (A length-1 R logical can likewise be treated as a Ruby boolean via `>> 0`.)
845
+
846
+
847
+
848
+ ``` ruby
849
+ class Trajectories
850
+
851
+ def initialize(times: nil, matrix: nil)
852
+ @times = times
853
+ @matrix = matrix
854
+
855
+ # call method validate to validate our input
856
+ validate
857
+
858
+ # show the object just created
859
+ show
860
+
861
+ end
862
+
863
+ def validate
864
+
865
+ # Let's first check that we do not have an empty object
866
+ raise "Neither times nor matrix can be an empty object" if (@times.nil? || @matrix.nil?)
867
+
868
+ # validate the input, to make sure that size of @times and the number of columns in
869
+ # @matrix are the same
870
+ puts ("~~~ Trajectories: inspector ~~~ ")
871
+ raise "[Trajectories: validation] the number of temporal measurements does not correspond with the number of columns in the matrix" if ((@times.length >> 0) != (@matrix.ncol >> 0))
872
+
873
+ end
874
+
875
+ end
876
+ ```
877
+
878
+ **Note:** with this stricter `validate`, `Trajectories.new` no longer accepts empty objects.
879
+ The earlier `@empty_traj = Trajectories.new` / `@empty_traj.show` pattern from part 1 no longer
880
+ applies for *new* constructions; existing instances created before this reopen still exist in
881
+ memory, but calling `new` with missing `times` or `matrix` will raise.
882
+
883
+ Let's try then creating an empty object:
884
+
885
+
886
+
887
+ ``` ruby
888
+ error = Trajectories.new
889
+ ```
890
+
891
+ ```
892
+ ## Neither times nor matrix can be an empty object
893
+ ```
894
+
895
+ Another example:
896
+
897
+
898
+ ``` ruby
899
+ error = Trajectories.new(times: 1)
900
+ ```
901
+
902
+ ```
903
+ ## Neither times nor matrix can be an empty object
904
+ ```
905
+
906
+ Let's see now that the implementation is correct and that it does not raise an error on valid
907
+ input:
908
+
909
+
910
+ ``` ruby
911
+ ok = Trajectories.new(times: R.c(1, 2), matrix: R.matrix((1..2), ncol: 2))
912
+ ```
913
+
914
+ ```
915
+ ## ~~~ Trajectories: inspector ~~~
916
+ ## *** Class Trajectories, method Show ***
917
+ ## times = 1
918
+ ## 2
919
+ ## * Traj (limited to a matrix 10x10) =
920
+ ## 1
921
+ ## 2
922
+ ## ******* End Show (trajectories) *******
923
+ ```
924
+
925
+ The 'initialize' method is called ONLY during the initial creation of the object. If any instance
926
+ variable is later modified, no control is done. At this moment though, there is no way to change
927
+ the value of any of our instance variables.
928
+
929
+ ```
930
+ error.times = R.c(1, 2, 3)
931
+ ```
932
+
933
+ The Trajectories class works for R objects and expects as input R objects. Passing R objects in
934
+ all examples has been the obligation of the programmer. Galaaz, however, can also accept many
935
+ Ruby values (ranges, arrays of numbers, and so on) if we convert them at the boundary. There is
936
+ no `R.convert` in Galaaz 2.0; a small helper is enough: leave `nil` alone, keep objects that are
937
+ already `R::Object`, and otherwise wrap with `R.c` (which accepts ranges as well as scalars and
938
+ vectors). Matrices that are already R objects are kept as-is.
939
+
940
+
941
+ ``` ruby
942
+ class Trajectories
943
+
944
+ def as_r(x)
945
+ return nil if x.nil?
946
+ return x if x.is_a?(R::Object)
947
+ R.c(x)
948
+ end
949
+
950
+ def initialize(times: nil, matrix: nil)
951
+ @times = as_r(times)
952
+ @matrix = as_r(matrix)
953
+
954
+ # call method validate to validate our input
955
+ validate
956
+
957
+ # show the object just created
958
+ show
959
+
960
+ end
961
+
962
+ def validate
963
+
964
+ # Let's first check that we do not have an empty object
965
+ raise "Neither times nor matrix can be an empty object" if (@times.nil? || @matrix.nil?)
966
+
967
+ # validate the input, to make sure that size of @times and the number of columns in
968
+ # @matrix are the same
969
+ puts ("~~~ Trajectories: inspector ~~~ ")
970
+ tl = @times.length >> 0; mc = @matrix.ncol >> 0
971
+ raise "[Trajectories: validation] the number of temporal measurements #{tl} does not correspond with the number of columns in the matrix #{mc}" if (tl != mc)
972
+
973
+ end
974
+
975
+ end
976
+ ```
977
+
978
+ And now let's create a new Trajectories, but we will now pass a Ruby range for times:
979
+
980
+
981
+ ``` ruby
982
+ ok = Trajectories.new(times: (1..2), matrix: R.matrix((1..2), ncol: 2))
983
+ ```
984
+
985
+ ```
986
+ ## ~~~ Trajectories: inspector ~~~
987
+ ## *** Class Trajectories, method Show ***
988
+ ## times = 1
989
+ ## 2
990
+ ## * Traj (limited to a matrix 10x10) =
991
+ ## 1
992
+ ## 2
993
+ ## ******* End Show (trajectories) *******
994
+ ```
995
+
996
+ Perfect! This works fine.
997
+
998
+ *(Historical note: an earlier Galaaz prototype on Renjin also demonstrated sharing storage with
999
+ the MDArray gem. Those shared-store demos are not part of Galaaz 2.0 / the GNU R bridge, and are
1000
+ omitted here.)*
1001
+
1002
+ # The Initializator
1003
+
1004
+ As we have seen, method 'initialize' is the main object creator orchestrator. This method can be
1005
+ as complex as needed. So, let's get on with some improvements to our Trajectories class.
1006
+
1007
+ It would be rather pleasant that the columns of the matrix of the trajectories have names, the
1008
+ names of measurements times. In the same way, the lines could be subscripted by a number of
1009
+ individual.
1010
+
1011
+ To do this in R, one also uses method initialize:
1012
+
1013
+
1014
+ ```
1015
+ > setMethod(
1016
+ + f="initialize",
1017
+ + signature="Trajectories",
1018
+ + definition=function(.Object,times,traj){
1019
+ + cat("~~~ Trajectories: initializator ~~~ \\n")
1020
+ + colnames(traj) <- paste("T",times,sep="")
1021
+ + rownames(traj) <- paste("I",1:nrow(traj),sep= "")
1022
+ + .Object@traj <- traj # Assignment of the slots
1023
+ + .Object@times <- times
1024
+ + return(.Object) # return of the object
1025
+ + }
1026
+ + )
1027
+ ```
1028
+
1029
+ In R, it is possible to assign a value to the result of a function, for example
1030
+ `colnames(x) <- c("v1", "v2", "v3")`. In Galaaz 2.0 the same idea is expressed with ordinary
1031
+ Ruby setters on the R object: `@matrix.colnames = ...` and `@matrix.rownames = ...`.
1032
+
1033
+
1034
+ ``` ruby
1035
+ class Trajectories
1036
+
1037
+ def as_r(x)
1038
+ return nil if x.nil?
1039
+ return x if x.is_a?(R::Object)
1040
+ R.c(x)
1041
+ end
1042
+
1043
+ def initialize(times: nil, matrix: nil)
1044
+ @times = as_r(times)
1045
+ @matrix = as_r(matrix)
1046
+
1047
+ # call method validate to validate our input
1048
+ validate
1049
+
1050
+ # Add row and column names
1051
+ puts ("~~~ Trajectories: initializator ~~~ ")
1052
+ @matrix.colnames = R.paste("T", @times, sep: "")
1053
+ @matrix.rownames = R.paste("I", (1..(@matrix.nrow >> 0)), sep: "")
1054
+
1055
+ # show the object just created
1056
+ show
1057
+
1058
+ end
1059
+
1060
+ end
1061
+ ```
1062
+
1063
+
1064
+ ``` ruby
1065
+ @traj = Trajectories.new(times: R.c(1,2,4,8), matrix: R.matrix((1..8), nrow: 2))
1066
+ ```
1067
+
1068
+ ```
1069
+ ## ~~~ Trajectories: inspector ~~~
1070
+ ## ~~~ Trajectories: initializator ~~~
1071
+ ## *** Class Trajectories, method Show ***
1072
+ ## times = 1
1073
+ ## 2
1074
+ ## 4
1075
+ ## 8
1076
+ ## * Traj (limited to a matrix 10x10) =
1077
+ ## T1 T2 T4 T8
1078
+ ## I1 "1" "3" "5" "7"
1079
+ ## I2 "2" "4" "6" "8"
1080
+ ## ******* End Show (trajectories) *******
1081
+ ```
1082
+
1083
+ Note that we still call our 'validate' method and it is still an error to create an empty
1084
+ Trajectories or one in which the sizes are wrong:
1085
+
1086
+
1087
+ ``` ruby
1088
+ error = Trajectories.new(times: R.c(1, 2, 48), matrix: R.matrix((1..8), nrow: 2))
1089
+ ```
1090
+
1091
+ ```
1092
+ ## [Trajectories: validation] the number of temporal measurements 3 does not correspond with the number of columns in the matrix 4
1093
+ ```
1094
+
1095
+ A constructor does not necessarily take the instance variable of the object as argument. For
1096
+ example, if we know (that is not the case in reality, but let us imagine so) that the
1097
+ BMI increases by 0.1 every week, we could build trajectories by providing the number
1098
+ of weeks and the initial weights.
1099
+
1100
+ First the code in R, we skip the definition of class TrajectoriesBis:
1101
+
1102
+
1103
+ ```
1104
+ > setMethod ("initialize",
1105
+ + "TrajectoriesBis",
1106
+ + function(.Object,nbWeek,BMIinit){
1107
+ + traj <- outer(BMIinit,1:nbWeek,function(init,week){return(init+0.1*week)})
1108
+ + colnames(traj) <- paste("T",1:nbWeek,sep="")
1109
+ + rownames(traj) <- paste("I",1:nrow(traj),sep="")
1110
+ + .Object@times <- 1:nbWeek
1111
+ + .Object@traj <- traj
1112
+ + return(.Object)
1113
+ + }
1114
+ + )
1115
+ ```
1116
+
1117
+ Now, let's make a TrajectoriesBis in Galaaz. Here again, we should point out some characteristics
1118
+ of our code:
1119
+
1120
+ * We made initialize with two positional arguments, instead of named arguments, i.e.,
1121
+ the first argument is the number of weeks and the second bmi_init. In this case,
1122
+ when making a new object the position of the arguments is important and there is no
1123
+ way to pass the argument by name;
1124
+
1125
+ * R function outer was called as if a method from bmi_init using dot notation, although
1126
+ one could use R.outer without problem;
1127
+
1128
+ * Function 'outer' expects an R function as its 3rd argument. In order to build an R
1129
+ function from Galaaz, we need to pass the function definition as a string to R.eval.
1130
+
1131
+
1132
+ ``` ruby
1133
+ class TrajectoriesBis
1134
+
1135
+ attr_reader :times
1136
+ attr_reader :matrix
1137
+
1138
+ def initialize(number_weeks, bmi_init)
1139
+ @matrix = bmi_init.outer((1..number_weeks),
1140
+ R.eval("function(init, week) {return(init + 0.1 * week)}"))
1141
+ @times = R.c((1..number_weeks))
1142
+ end
1143
+
1144
+ end
1145
+
1146
+ @traj_bis = TrajectoriesBis.new(4, R.c(16,17,15.6))
1147
+ ```
1148
+
1149
+
1150
+ ``` ruby
1151
+ puts @traj_bis.matrix
599
1152
  ```
600
1153
 
1154
+ ```
1155
+ ## [,1] [,2] [,3] [,4]
1156
+ ## [1,] 16.1 16.2 16.3 16.4
1157
+ ## [2,] 17.1 17.2 17.3 17.4
1158
+ ## [3,] 15.7 15.8 15.9 16.0
1159
+ ```
1160
+
1161
+ It is always possible to pass a Ruby variable into a string by interpolating it. Put the
1162
+ variable inside `#{...}`. As an example, let's also require the BMI increase as a parameter.
1163
+ (A common mistake is to escape the interpolation — writing `\#{increment}` — which leaves the
1164
+ characters literally in the R source and does not substitute the Ruby value. Use real
1165
+ interpolation:)
1166
+
1167
+
1168
+ ``` ruby
1169
+ class TrajectoriesBis
1170
+
1171
+ def initialize(number_weeks, bmi_init, increment)
1172
+ @matrix = bmi_init.outer((1..number_weeks),
1173
+ R.eval("function(init, week) {return(init + #{increment} * week)}"))
1174
+ @times = R.c((1..number_weeks))
1175
+ end
1176
+
1177
+ end
1178
+
1179
+ @traj_bis = TrajectoriesBis.new(4, R.c(16,17,15.6), 0.3)
1180
+ ```
1181
+
1182
+
1183
+ ``` ruby
1184
+ puts @traj_bis.matrix
1185
+ ```
1186
+
1187
+ ```
1188
+ ## [,1] [,2] [,3] [,4]
1189
+ ## [1,] 16.3 16.6 16.9 17.2
1190
+ ## [2,] 17.3 17.6 17.9 18.2
1191
+ ## [3,] 15.9 16.2 16.5 16.8
1192
+ ```
1193
+
1194
+ # Constructors for Users
1195
+
1196
+ Many times, it is interesting to have different ways of constructing an object depending on
1197
+ what information our users have or want to provide to the constructor. Although we have only one
1198
+ initialize method, we can create multiple methods, that do some preprocessing and then call the
1199
+ initialize method to carry out the object building.
1200
+
1201
+ In order to do that, we use what are called class methods, instead of instance methods. All the
1202
+ methods we've created so far are instance methods; class methods are defined by prepending the
1203
+ self keyword to the method's name. Still using the assumption that the BMI will grow by 0.1 per
1204
+ week, let's define a regular trajectory without having to define a TrajectoriesBis as above:
1205
+
1206
+
1207
+ ```
1208
+ > regularTrajectories <- function(nbWeek,BMIinit) {
1209
+ + traj <- outer(BMIinit,1:nbWeek,function(init,week){return(init+0.1*week)})
1210
+ + times <- 1: nbWeek
1211
+ + return(new(Class="Trajectories",times=times,traj=traj))
1212
+ + }
1213
+ > regularTrajectories(nbWeek=3,BMIinit=c(14,15,16))
1214
+ ```
1215
+
1216
+ Notice how method 'regular' is defined as 'self.regular', making it a class method. The last
1217
+ statement of the method definition is actually a call to the Trajectories constructor 'new' passing
1218
+ the calculated values for times and matrix.
1219
+
1220
+ Notice also how method regular is called, similar to the way new is called by adding it after class
1221
+ Trajectories name: 'Trajectories.regular'.
1222
+
1223
+
1224
+ ``` ruby
1225
+ class Trajectories
1226
+
1227
+ def self.regular(number_weeks: nil, bmi_init: nil)
1228
+ matrix = bmi_init.outer((1..number_weeks),
1229
+ R.eval("function(init, week) {return(init + 0.1 * week)}"))
1230
+ times = R.c((1..number_weeks))
1231
+ Trajectories.new(times: times, matrix: matrix)
1232
+ end
1233
+
1234
+ end
1235
+ ```
1236
+
1237
+
1238
+ ``` ruby
1239
+ @regular = Trajectories.regular(bmi_init: R.c(14, 15, 16), number_weeks: 3)
1240
+ ```
1241
+
1242
+ ```
1243
+ ## ~~~ Trajectories: inspector ~~~
1244
+ ## ~~~ Trajectories: initializator ~~~
1245
+ ## *** Class Trajectories, method Show ***
1246
+ ## times = 1
1247
+ ## 2
1248
+ ## 3
1249
+ ## * Traj (limited to a matrix 10x10) =
1250
+ ## T1 T2 T3
1251
+ ## I1 "14.10" "14.20" "14.30"
1252
+ ## I2 "15.10" "15.20" "15.30"
1253
+ ## I3 "16.10" "16.20" "16.30"
1254
+ ## ******* End Show (trajectories) *******
1255
+ ```
1256
+
1257
+ We have already seen that constructors can be as complex as needed, calling other methods and doing
1258
+ calculations on the received parameters. On this last example, we will check if the times
1259
+ variable was provided. If it is not provided, then we will use matrix columns to define the times:
1260
+
1261
+
1262
+ ``` ruby
1263
+ class Trajectories
1264
+
1265
+ def self.init(times: nil, matrix: nil)
1266
+ times = R.c((1..(matrix.ncol >> 0))) if times.nil?
1267
+ Trajectories.new(times: times, matrix: matrix)
1268
+ end
1269
+
1270
+ end
1271
+ ```
1272
+
1273
+
1274
+ ``` ruby
1275
+ @traj = Trajectories.init(matrix: R.matrix((1..8), ncol: 4))
1276
+ ```
1277
+
1278
+ ```
1279
+ ## ~~~ Trajectories: inspector ~~~
1280
+ ## ~~~ Trajectories: initializator ~~~
1281
+ ## *** Class Trajectories, method Show ***
1282
+ ## times = 1
1283
+ ## 2
1284
+ ## 3
1285
+ ## 4
1286
+ ## * Traj (limited to a matrix 10x10) =
1287
+ ## T1 T2 T3 T4
1288
+ ## I1 "1" "3" "5" "7"
1289
+ ## I2 "2" "4" "6" "8"
1290
+ ## ******* End Show (trajectories) *******
1291
+ ```
1292
+
1293
+ # Accessors
1294
+
1295
+ Accessors are methods for getting and setting the value of instance variables.
1296
+
1297
+ # Get
1298
+
1299
+ Getters are methods for getting the value of an instance variable. We have been using getters
1300
+ since the beginning of this document, without explicitly saying so. When defining attr_reader
1301
+ :times and attr_reader :matrix, we have actually defined two getter methods for reading the values
1302
+ of variables times and matrix respectively. We can however define getters explicitly:
1303
+
1304
+
1305
+ ``` ruby
1306
+ class TrajectoriesBis
1307
+
1308
+ def initialize(times: nil, matrix: nil)
1309
+ @times = times
1310
+ @matrix = matrix
1311
+ end
1312
+
1313
+ def times
1314
+ @times
1315
+ end
1316
+
1317
+ def matrix
1318
+ @matrix
1319
+ end
1320
+
1321
+ end
1322
+
1323
+ @traj = TrajectoriesBis.new(times: 1, matrix: 2)
1324
+ ```
1325
+
1326
+
1327
+ ``` ruby
1328
+ puts @traj.times
1329
+ ```
1330
+
1331
+ ```
1332
+ ## 1
1333
+ ```
1334
+
1335
+
1336
+ ``` ruby
1337
+ puts @traj.matrix
1338
+ ```
1339
+
1340
+ ```
1341
+ ## 2
1342
+ ```
1343
+
1344
+ It is also possible to define more sophisticated getters. For example one can
1345
+ regularly need the BMI at inclusion. In R, one would index a matrix as matrix[,1]. In Ruby,
1346
+ it is a syntax error to have a ',' just after the '['. In this case we need to add 'nil' as
1347
+ in matrix[nil, 1]:
1348
+
1349
+
1350
+ ``` ruby
1351
+ class Trajectories
1352
+
1353
+ def get_traj_inclusion
1354
+ @matrix[nil, 1]
1355
+ end
1356
+
1357
+ end
1358
+ ```
1359
+
1360
+
1361
+ ``` ruby
1362
+ puts @trajCochin.get_traj_inclusion
1363
+ ```
1364
+
1365
+ ```
1366
+ ## numeric(0)
1367
+ ```
1368
+
1369
+ # Set
1370
+
1371
+ A setter is a method that assigns a value to a variable. As with getters, Ruby also provides an
1372
+ easy way to write setters and allow you to also write them explicitly. Let's first use the
1373
+ simple way:
1374
+
1375
+
1376
+ ``` ruby
1377
+ class TrajectoriesBis
1378
+
1379
+ attr_writer :times
1380
+ attr_writer :matrix
1381
+
1382
+ def initialize(times: nil, matrix: nil)
1383
+ @times = times
1384
+ @matrix = matrix
1385
+ end
1386
+
1387
+ end
1388
+
1389
+ @traj = TrajectoriesBis.new
1390
+ @traj.times = R.c(1, 2)
1391
+ @traj.matrix = R.matrix((1..2), ncol: 2)
1392
+ ```
1393
+
1394
+
1395
+ ``` ruby
1396
+ puts @traj.matrix
1397
+ ```
1398
+
1399
+ ```
1400
+ ## [,1] [,2]
1401
+ ## [1,] 1 2
1402
+ ```
1403
+
1404
+ Note that now we can use '=' to assign a value to both variables times and matrix. Without
1405
+ setters, changing the value of variables times and matrix was not possible. Our class, up
1406
+ to this point was protected from any changes to those variables. If we need to allow changes
1407
+ to those variables, then setters are needed. In this case, the simple setter as shown above is
1408
+ not ideal, since it would allow changes that break the restriction that variable times has to
1409
+ have the same length as the number of columns of matrix. In order to do the verification we
1410
+ need to implement a more sophisticated setter. In the example below, we add the 'times=' setter
1411
+ that receives as input one argument. First we convert the given argument to an R object, then
1412
+ check to see that the length of times is the same as the number of columns and if everything is
1413
+ fine, then we set the value of instance variable times:
1414
+
1415
+
1416
+ ``` ruby
1417
+ class Trajectories
1418
+
1419
+ def as_r(x)
1420
+ return nil if x.nil?
1421
+ return x if x.is_a?(R::Object)
1422
+ R.c(x)
1423
+ end
1424
+
1425
+ def times=(times)
1426
+ times = as_r(times)
1427
+ tl = times.length >> 0; mc = @matrix.ncol >> 0
1428
+ raise "[Trajectories: validation] the number of temporal measurements #{tl} does not correspond with the number of columns in the matrix #{mc}" if (tl != mc)
1429
+ @times = times
1430
+ end
1431
+
1432
+ end
1433
+ ```
1434
+
1435
+
1436
+ ``` ruby
1437
+ @trajCochin.times = (1..5)
1438
+ ```
1439
+
1440
+ ```
1441
+ ## [Trajectories: validation] the number of temporal measurements 5 does not correspond with the number of columns in the matrix 4
1442
+ ```
1443
+
1444
+ We now set the value appropriately and will not get any errors:
1445
+
1446
+
1447
+ ``` ruby
1448
+ @trajCochin.times = R.c(1, 5, 6, 8)
1449
+ ```
1450
+
1451
+ # The Operator '['
1452
+
1453
+ It is also possible to define getters by using the operator '['. This operator is not usually
1454
+ used for returning instance variables and it is preferable to use the methods we've used above;
1455
+ however, for completeness with SS4 we are showing how to define this here. Operator '[' is
1456
+ better left to be used for array/matrix indices.
1457
+
1458
+
1459
+ ``` ruby
1460
+ class Trajectories
1461
+
1462
+ def [](var_name)
1463
+
1464
+ case var_name
1465
+ when "times"
1466
+ @times
1467
+ when "matrix"
1468
+ @matrix
1469
+ else
1470
+ raise "Unknown instance variable"
1471
+ end
1472
+
1473
+ end
1474
+
1475
+ end
1476
+ ```
1477
+
1478
+
1479
+ ``` ruby
1480
+ puts @trajCochin["times"]
1481
+ ```
1482
+
1483
+ ```
1484
+ ## [1] 1 5 6 8
1485
+ ```
1486
+
1487
+ Similarly, we could use operator '[]=' to assign a value to times and matrix. We will not do this
1488
+ here as we think that the other options are better and the interested user can easily find help,
1489
+ if needed to implement such method.
1490
+
1491
+ # To Go Further
1492
+
1493
+ This section will introduce advanced features of Object Oriented programming such as Inheritance
1494
+ and Modules and will also show some aspects of S4 that do not apply to Ruby.
1495
+
1496
+ # Methods Using Several Arguments
1497
+
1498
+ In Ruby, methods can have as many arguments as needed and those methods are defined the way we
1499
+ have already seen in many of the examples above. The example in SS4 presents a method that prints
1500
+ different output if its input is numeric, character or both. Let's write a class in Ruby that
1501
+ does the same for Numeric and String. In Ruby we do not define global functions, we always define
1502
+ methods inside classes or modules (as we will see later). Also, Ruby is not typed, so methods are
1503
+ not called depending on their types as in SS4 examples. Below, method test will be called with
1504
+ one parameter. At the time of calling we do not know the type of the argument; the method can
1505
+ then check if the received argument is a Numeric or a String and at this time, decide what should
1506
+ be printed.
1507
+
1508
+
1509
+ ``` ruby
1510
+ class Test
1511
+
1512
+ def test(input)
1513
+
1514
+ case input
1515
+ when Numeric
1516
+ puts "The input is numeric: #{input}"
1517
+ when String
1518
+ puts "The input is a string: #{input}"
1519
+ else
1520
+ puts "The input is neither a number nor a string"
1521
+ end
1522
+
1523
+ end
1524
+
1525
+ end
1526
+
1527
+ @t = Test.new
1528
+ ```
1529
+
1530
+
1531
+ ``` ruby
1532
+ @t.test(5)
1533
+ ```
1534
+
1535
+ ```
1536
+ ## The input is numeric: 5
1537
+ ```
1538
+
1539
+
1540
+ ``` ruby
1541
+ @t.test("Hello")
1542
+ ```
1543
+
1544
+ ```
1545
+ ## The input is a string: Hello
1546
+ ```
1547
+
1548
+ Ruby has ways of dealing with multiple arguments, missing arguments, undefined number of arguments,
1549
+ named arguments, unnamed arguments, etc. This is beyond the scope of this document and we
1550
+ suggest the interested reader to go to the many resources about Ruby that can easily be found
1551
+ on the web.
1552
+
1553
+ We will now create a new class 'Partition' that we will use later in this document. This class will
1554
+ have only the basic methods needed for the examples to work.
1555
+
1556
+
1557
+ ``` ruby
1558
+ class Partition
1559
+
1560
+ attr_reader :nb_groups
1561
+ attr_reader :part
1562
+
1563
+ def initialize(nb_groups, part)
1564
+ @nb_groups = nb_groups
1565
+ @part = part
1566
+ end
1567
+
1568
+ end
1569
+
1570
+ @partCochin = Partition.new(2, R.c("A","B","A","B").factor)
1571
+ @partStAnne = Partition.new(2, R.c("A","B").rep(R.c(50,30)).factor)
1572
+ ```
1573
+
1574
+
1575
+ ``` ruby
1576
+ puts @partCochin.part
1577
+ ```
1578
+
1579
+ ```
1580
+ ## [1] A B A B
1581
+ ## Levels: A B
1582
+ ```
1583
+
1584
+
1585
+ ``` ruby
1586
+ puts @partStAnne.part
1587
+ ```
1588
+
1589
+ ```
1590
+ ## [1] A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A
1591
+ ## [39] A A A A A A A A A A A A B B B B B B B B B B B B B B B B B B B B B B B B B B
1592
+ ## [77] B B B B
1593
+ ## Levels: A B
1594
+ ```
1595
+
1596
+ We will suppose that part is always composed of capital letters going from A to
1597
+ LETTERS[nb_groups].
1598
+
1599
+ # Inheritance
1600
+
1601
+ Ruby being a powerful Object Oriented language has the concept of Inheritance, but it does not
1602
+ allow for multiple inheritance. Multiple inheritance has many drawbacks and Ruby just does not
1603
+ support it. However, Ruby has other concepts that make up for the lack of multiple inheritance as
1604
+ we will see in the following examples.
1605
+
1606
+ So, let's go back to SS4 examples. We want now to define a class called TrajPartitioned that
1607
+ inherits from class Trajectories. When a class has a parent, all methods available for the
1608
+ parent are also available to the child.
1609
+
1610
+
1611
+
1612
+ ``` ruby
1613
+ class TrajPartitioned < Trajectories
1614
+
1615
+ attr_reader :list_partitions
1616
+
1617
+ end
1618
+ ```
1619
+
1620
+ That's all there is to it! We've just created a class TrajPartitioned that inherits all methods
1621
+ from class Trajectories and at this point does nothing different from Trajectories, but adds a
1622
+ new instance variable: list_partitions.
1623
+
1624
+ Creating TrajPartitioned without arguments will generate an error, since a Trajectories requires
1625
+ both times and matrix to be non null.
1626
+
1627
+
1628
+
1629
+ ``` ruby
1630
+ @tdPitie = TrajPartitioned.new
1631
+ ```
1632
+
1633
+ ```
1634
+ ## Neither times nor matrix can be an empty object
1635
+ ```
1636
+
1637
+ Let's try to create a TrajPartitioned, but passing to it two partitions. For that, let's first
1638
+ create a new Partition:
1639
+
1640
+
1641
+ ``` ruby
1642
+ @partCochin2 = Partition.new(3, R.c("A", "C", "C", "B").factor)
1643
+ ```
1644
+
1645
+ And now let's create the TrajPartitioned:
1646
+
1647
+
1648
+ ``` ruby
1649
+ @tdCochin = TrajPartitioned.new(times: R.c(1,3,4,5), matrix: @trajCochin.matrix,
1650
+ list_partitions: R.list(@partCochin, @partCochin2))
1651
+ ```
1652
+
1653
+ ```
1654
+ ## unknown keyword: :list_partitions
1655
+ ```
1656
+
1657
+ This didn't work: R function 'list' expects R objects, and in this case, @partCochin and
1658
+ @partCochin2 are Ruby classes, so trying to apply function list to them does not work. Clearly,
1659
+ we will have to work in the realm of Ruby to keep the list of partitions. This is not a problem
1660
+ as Ruby has data structures to maintain a list of objects, the Array. Let's then try another
1661
+ solution:
1662
+
1663
+
1664
+ ``` ruby
1665
+ @tdCochin = TrajPartitioned.new(times: R.c(1,3,4,5), matrix: @trajCochin.matrix,
1666
+ list_partitions: [@partCochin, @partCochin2])
1667
+ ```
1668
+
1669
+ ```
1670
+ ## unknown keyword: :list_partitions
1671
+ ```
1672
+
1673
+ We now get a second error: 'unknown keyword: list_partitions'. Class TrajPartitioned inherits
1674
+ from class Trajectories and class Trajectories has an initialize function that requires two
1675
+ parameters, times and matrix; list_partitions is not a parameter for initialize and is thus
1676
+ unknown. In order to fix this problem we need to create an initialize method for class
1677
+ TrajPartitioned.
1678
+
1679
+
1680
+ # The 'super' Keyword
1681
+
1682
+ R has a method called 'callNextMethod' for control flow between inherited classes. In Ruby, we
1683
+ have a model that is a bit different. When a method is called on a subclass, if this method is
1684
+ not found it will be searched in the parent class and it will go up the hierarchy of classes until
1685
+ it is found or an error is issued. If we want the parent method to be called we can call 'super':
1686
+
1687
+
1688
+
1689
+ ``` ruby
1690
+ class TrajPartitioned
1691
+
1692
+ def initialize(times: nil, matrix: nil, list_partitions: nil)
1693
+ super(times: times, matrix: matrix)
1694
+ @list_partitions = list_partitions
1695
+ end
1696
+
1697
+ end
1698
+ ```
1699
+
1700
+ Let's try our example again:
1701
+
1702
+
1703
+ ``` ruby
1704
+ @tdCochin = TrajPartitioned.new(times: R.c(1,3,4,5), matrix: @trajCochin.matrix,
1705
+ list_partitions: [@partCochin, @partCochin2])
1706
+ ```
1707
+
1708
+ ```
1709
+ ## ~~~ Trajectories: inspector ~~~
1710
+ ## ~~~ Trajectories: initializator ~~~
1711
+ ## *** Class Trajectories, method Show ***
1712
+ ## times = 1
1713
+ ## 3
1714
+ ## 4
1715
+ ## 5
1716
+ ## * Traj (limited to a matrix 10x10) =
1717
+ ## T1 T3 T4 T5
1718
+ ## I1 "15.00" "15.10" "15.20" "15.20"
1719
+ ## I2 "16.00" "15.90" "16.00" "16.40"
1720
+ ## I3 "15.20" " NA" "15.30" "15.30"
1721
+ ## I4 "15.70" "15.60" "15.80" "16.00"
1722
+ ## ******* End Show (trajectories) *******
1723
+ ```
1724
+
1725
+ Now @tdCochin is created correctly; however, the 'show' method only shows information about
1726
+ times and matrix, there is nothing about our new list_partitions variable. This is so, since
1727
+ there is no method 'show' in TrajPartitioned, so method 'show' from Trajectories is executed.
1728
+
1729
+ So, let's start by writing a 'print' method, that will print all the information we have in
1730
+ TrajPartitioned. The flow of control for this method is: Ruby sees a call to 'print', so it checks
1731
+ to see if 'print' is a method for TrajPartitioned. Since we have just defined this method, Ruby
1732
+ finds it and executes it. The first command in print is a call to 'super', which will call the
1733
+ parent 'print' method, that prints information for 'times' and 'matrix'. When the parent 'print'
1734
+ finishes control continues after the 'super' call, printing the number of available partitions.
1735
+
1736
+
1737
+ ``` ruby
1738
+ class TrajPartitioned
1739
+
1740
+ def print
1741
+ super
1742
+ puts ("the object also contains #{@list_partitions.length} partition")
1743
+ puts ("***** Fine of print (TrajPartitioned) *****")
1744
+ end
1745
+
1746
+ end
1747
+ ```
1748
+
1749
+
1750
+ ``` ruby
1751
+ @tdCochin.print
1752
+ ```
1753
+
1754
+ ```
1755
+ ## *** Class Trajectories, method Print ***
1756
+ ## times = 1
1757
+ ## 3
1758
+ ## 4
1759
+ ## 5
1760
+ ## traj =
1761
+ ## T1 T3 T4 T5
1762
+ ## I1 15.0 15.1 15.2 15.2
1763
+ ## I2 16.0 15.9 16.0 16.4
1764
+ ## I3 15.2 NA 15.3 15.3
1765
+ ## I4 15.7 15.6 15.8 16.0
1766
+ ## ******* End Print (trajectories) *******
1767
+ ## the object also contains 2 partition
1768
+ ## ***** Fine of print (TrajPartitioned) *****
1769
+ ```
1770
+
1771
+ Notice that this model is much cleaner than 'callNextMethod' and is not subject to any of the
1772
+ difficulties presented in SS4 and there is no need for the keywords “is”, “as” and “as<-”, although
1773
+ Ruby provides methods to check the class of an object, its hierarchy, etc. when needed.
1774
+
1775
+ In Ruby there is no similar method as "setIs" and it is not possible to convert one class into
1776
+ another, but there are other ways of getting the necessary results. Let's then implement a
1777
+ method that returns the partition with the least number of groups. First, as usual, the R code
1778
+ with 'setIs':
1779
+
1780
+ ```
1781
+ > setIs(
1782
+ + class1="TrajPartitioned",
1783
+ + class2="Partition",
1784
+ + coerce=function(from,to){
1785
+ + numberGroups <- sapply(tdCochin@listPartitions,getNbGroups)
1786
+ + Smallest <- which.min(-numberGroups)
1787
+ + to<-new("Partition")
1788
+ + to@nbGroups <- getNbGroups(from@listPartitions[[Smallest]])
1789
+ + to@part <- getPart(from@listPartitions[[Smallest]])
1790
+ + return(to)
1791
+ + }
1792
+ + )
1793
+ ```
1794
+
1795
+ And now the Ruby code. Here we are getting deeper into Ruby and it is becoming harder for a
1796
+ pure R developer to understand the code. We will describe it in more detail:
1797
+
1798
+ * We define a method called 'to_part' that has one argument 'which'. By default 'which'
1799
+ is ':min', the name of the minimum method. This means that if no argument is given to
1800
+ to_part it will assume which = :min;
1801
+
1802
+ * @list_partitions is a Ruby array. Method map is similar to method sapply in R, it
1803
+ applies a 'block' to every element of the array, returning an array. Describing
1804
+ blocks is beyond the scope of this document, but we can think of it as if it were a
1805
+ function. The block is in '{}' and has one argument named 'part'. Thus, map goes
1806
+ through all elements of the array, and gets the nb_groups of the element and returns
1807
+ them into the number_groups array.
1808
+
1809
+ * number_groups is an array and doing number_groups.min returns the minimum value in
1810
+ number_groups and number_groups.max the maximum. We can call a method on an object
1811
+ by 'sending' the method name to the object, so, number_groups.send(:min) is equivalent to
1812
+ number_groups.min;
1813
+
1814
+ * Method 'index' for array, returns the index of a given element. So, number_groups.index(3)
1815
+ would return the index of the element '3'. Then number_groups.index(number_groups.min)
1816
+ returns the index of the minimum element in the array. This is the equivalent of R
1817
+ which.min(number_groups);
1818
+
1819
+ * Finally, number_groups.index(number_groups.send(which)), will return the index of the
1820
+ element we ask for, be it :min or :max. Note that if we pass another value, this would
1821
+ be an error.
1822
+
1823
+
1824
+ ``` ruby
1825
+ class TrajPartitioned
1826
+
1827
+ def to_part(which = :min)
1828
+ number_groups = @list_partitions.map { |part| part.nb_groups }
1829
+ selected = number_groups.index(number_groups.send(which))
1830
+ return @list_partitions[selected]
1831
+ end
1832
+
1833
+ end
1834
+ ```
1835
+
1836
+ To get the partition with the minimum number of elements:
1837
+
1838
+
1839
+ ``` ruby
1840
+ puts @tdCochin.to_part.part
1841
+ ```
1842
+
1843
+ ```
1844
+ ## [1] A B A B
1845
+ ## Levels: A B
1846
+ ```
1847
+
1848
+ To get the partition with the maximum number of elements:
1849
+
1850
+
1851
+ ``` ruby
1852
+ puts @tdCochin.to_part(:max).part
1853
+ ```
1854
+
1855
+ ```
1856
+ ## [1] A C C B
1857
+ ## Levels: A B C
1858
+ ```
1859
+
1860
+ In this example we did not follow exactly the R code from SS4. The reason for that is that
1861
+ 'list_partitions' is a list of Ruby classes and we cannot run sapply on this list. If we
1862
+ try to call a 'getNbGroups' or in the Ruby case nb_groups via R's sapply, the code will crash.
1863
+
1864
+ # Virtual Classes
1865
+
1866
+ In Ruby there are no "Virtual Classes", but it is possible to implement derived classes from
1867
+ a parent class with methods that behave properly according to the object's class. Following
1868
+ SS4 we will implement two classes: PartitionSimple and PartitionEval which are subclasses
1869
+ of class PartitionFather. PartitionFather will just be a regular class. Methods defined in
1870
+ PartitionFather will be available to be used in the subclasses
1871
+
1872
+ Here is the R code of those classes and the implementation of a method in PartitionFather
1873
+ that multiplies the number of groups by 2:
1874
+
1875
+
1876
+ ```
1877
+ > setClass(
1878
+ + Class="PartitionFather",
1879
+ + representation=representation(nbGroups="numeric","VIRTUAL")
1880
+ + )
1881
+
1882
+ > setClass(
1883
+ + Class="PartitionSimple",
1884
+ + representation=representation(part="factor"),
1885
+ + contains="PartitionFather"
1886
+ + )
1887
+
1888
+ > setClass(
1889
+ + Class="PartitionEval",
1890
+ + representation=representation(part="ordered"),
1891
+ + contains="PartitionFather"
1892
+ + )
1893
+
1894
+ > setGeneric("nbMultTwo",function(object){standardGeneric("nbMultTwo")})
1895
+
1896
+ > setMethod("nbMultTwo","PartitionFather",
1897
+ + function(object){
1898
+ + object@nbGroups <- object@nbGroups*2
1899
+ + return (object)
1900
+ + }
1901
+ + )
1902
+ ```
1903
+
1904
+ Since Ruby has no type definition, there is no really need for a parent class and subclasses.
1905
+ However, we will implement those classes in order to show Ruby's inheritance:
1906
+
1907
+
1908
+ ``` ruby
1909
+ # Parent class. Differently from SS4, both 'nb_groups' and 'part' are defined in the
1910
+ # parent class.
1911
+ class PartitionFather
1912
+
1913
+ attr_reader :nb_groups
1914
+ attr_reader :part
1915
+
1916
+ # initialize class PartitionFather with the number of groups and parts. Note that we
1917
+ # use R.c for nb_groups in order to convert the number of groups into an R vector.
1918
+ def initialize(nb_groups: 0, part: nil)
1919
+ @nb_groups = R.c(nb_groups)
1920
+ @part = part
1921
+ end
1922
+
1923
+ # method nb_mult_two can be called from all subclasses
1924
+ def nb_mult_two
1925
+ @nb_groups * 2
1926
+ end
1927
+
1928
+ # method 'to_s' is called whenever we try to print a Ruby object. This method emulates
1929
+ # R 'print' method that prints all the slots.
1930
+ def to_s
1931
+ puts ("Variable 'nb_groups':")
1932
+ puts @nb_groups
1933
+ puts
1934
+ puts ("Variable 'part':")
1935
+ puts @part
1936
+ puts
1937
+ end
1938
+
1939
+ end
1940
+
1941
+ # Class PartitionSimple is a subclass of PartitionFather. To make a subclass of a
1942
+ # class we use the operator '<'. Since the whole logic is in the parent class
1943
+ # PartitionSimple is just an empty class
1944
+ class PartitionSimple < PartitionFather
1945
+
1946
+ end
1947
+
1948
+ # PartitionEval is also only an empty class
1949
+ class PartitionEval < PartitionFather
1950
+
1951
+ end
1952
+ ```
1953
+
1954
+
1955
+ ``` ruby
1956
+ @a = PartitionSimple.new(nb_groups: 3, part: ((~R[:LETTERS])[R.c(1, 2, 3, 2, 2, 1)].factor))
1957
+ puts @a
1958
+ ```
1959
+
1960
+ ```
1961
+ ## Variable 'nb_groups':
1962
+ ## 3
1963
+ ##
1964
+ ## Variable 'part':
1965
+ ## [1] A B C B B A
1966
+ ## Levels: A B C
1967
+ ##
1968
+ ## #<RC::PartitionSimple:0x27371ac4>
1969
+ ```
1970
+
1971
+
1972
+ ``` ruby
1973
+ puts @a.nb_mult_two
1974
+ ```
1975
+
1976
+ ```
1977
+ ## [1] 6
1978
+ ```
1979
+
1980
+
1981
+ ``` ruby
1982
+ @b = PartitionEval.new(nb_groups: 5, part: (~R[:LETTERS])[R.c(1, 5, 3, 4, 2, 4)].ordered)
1983
+ puts @b
1984
+ ```
1985
+
1986
+ ```
1987
+ ## Variable 'nb_groups':
1988
+ ## 5
1989
+ ##
1990
+ ## Variable 'part':
1991
+ ## [1] A E C D B D
1992
+ ## Levels: A < B < C < D < E
1993
+ ##
1994
+ ## #<RC::PartitionEval:0xe36882f>
1995
+ ```
1996
+
1997
+
1998
+ ``` ruby
1999
+ puts @b.nb_mult_two
2000
+ ```
2001
+
2002
+ ```
2003
+ ## [1] 10
2004
+ ```
2005
+
2006
+ The example above, although it replicates SS4 is not actually very useful from the point of
2007
+ view of class hierarchy in Ruby. We will then write a new function to_s in class
2008
+ PartitionSimple that will print the name of the class:
2009
+
2010
+
2011
+ ``` ruby
2012
+ class PartitionSimple
2013
+
2014
+ def to_s
2015
+ puts("Class PartitionSimple")
2016
+ super
2017
+ end
2018
+
2019
+ end
2020
+ ```
2021
+
2022
+
2023
+ ``` ruby
2024
+ puts @a
2025
+ ```
2026
+
2027
+ ```
2028
+ ## Class PartitionSimple
2029
+ ## Variable 'nb_groups':
2030
+ ## 3
2031
+ ##
2032
+ ## Variable 'part':
2033
+ ## [1] A B C B B A
2034
+ ## Levels: A B C
2035
+ ##
2036
+ ## #<RC::PartitionSimple:0x27371ac4>
2037
+ ```
2038
+
2039
+ As can be seen, 'puts @a' now calls method 'to_s' defined in class PartitionSimple. This
2040
+ method prints 'Class PartitionSimple' and then calls the super method, i.e., method 'to_s'
2041
+ from class PartitionFather.
2042
+
2043
+ Note though that 'puts @b' still prints the same output, since it has no particular 'to_s'
2044
+ method.
2045
+
2046
+
2047
+ ``` ruby
2048
+ puts @b
2049
+ ```
2050
+
2051
+ ```
2052
+ ## Variable 'nb_groups':
2053
+ ## 5
2054
+ ##
2055
+ ## Variable 'part':
2056
+ ## [1] A E C D B D
2057
+ ## Levels: A < B < C < D < E
2058
+ ##
2059
+ ## #<RC::PartitionEval:0xe36882f>
2060
+ ```
2061
+
2062
+ # Internal Modification of an Object
2063
+
2064
+
2065
+ ## Method to Modify a Field
2066
+
2067
+ Let us return to our trajectories example and define a third method that imputes data for
2068
+ missing values. To simplify, we will impute by replacing by the mean values. This is the R
2069
+ code to do this:
2070
+
2071
+ ```
2072
+ > meanWithoutNa <- function (x){mean(x,na.rm=TRUE)}
2073
+ > setGeneric("impute",function (.Object){standardGeneric("impute")})
2074
+ > setMethod(
2075
+ + f="impute",
2076
+ + signature="Trajectories",
2077
+ + def=function(.Object){
2078
+ + average <- apply(.Object@traj,2,meanWithoutNa)
2079
+ + for (iCol in 1:ncol(.Object@traj)){
2080
+ + .Object@traj[is.na(.Object@traj[,iCol]),iCol] <- average[iCol]
2081
+ + }
2082
+ + return(.Object)
2083
+ + }
2084
+ + )
2085
+ ```
2086
+
2087
+ The code above, as explained in SS4 creates a new object and does not change the original one.
2088
+ So, calling impute(trajCochin) will work correctly by creating a new object but will not
2089
+ change trajCochin. This works fine, but can be memory expensive if the matrix is a large
2090
+ one.
2091
+
2092
+ Let's now implement the same method in Galaaz 2.0. We stay on the R side of the bridge:
2093
+ for each column, compute the mean with `na.rm = true`, then replace NA entries with that mean
2094
+ (via `R.ifelse` / `is__na`), and rebuild the matrix with `R.cbind`. No MDArray iteration is
2095
+ required.
2096
+
2097
+
2098
+ ``` ruby
2099
+ class Trajectories
2100
+
2101
+ def impute
2102
+ ncols = @matrix.ncol >> 0
2103
+ imputed = (1..ncols).map do |j|
2104
+ col = @matrix[nil, j]
2105
+ avg = col.mean(na__rm: true)
2106
+ R.ifelse(col.is__na, avg, col)
2107
+ end
2108
+ col_names = @matrix.colnames
2109
+ row_names = @matrix.rownames
2110
+ @matrix = R.cbind(*imputed)
2111
+ @matrix.colnames = col_names unless col_names.nil?
2112
+ @matrix.rownames = row_names unless row_names.nil?
2113
+ self
2114
+ end
2115
+
2116
+ end
2117
+ ```
2118
+
2119
+
2120
+ ``` ruby
2121
+ @trajCochin.impute
2122
+ puts @trajCochin.matrix
2123
+ ```
2124
+
2125
+ ```
2126
+ ## $rownames
2127
+ ## [1] "I1" "I2" "I3" "I4"
2128
+ ```
2129
+
2130
+ It works, and `@trajCochin.matrix` was updated. Under GNU R, assignment follows R's usual
2131
+ copy-on-write semantics: replacing `@matrix` (or assigning into an R object through the bridge)
2132
+ binds a new vector/matrix rather than mutating a shared MDArray store. That is a deliberate
2133
+ difference from the Renjin/MDArray mutation experiments in the older paper; those demos are not
2134
+ part of Galaaz 2.0.
2135
+
2136
+ # Conclusions I
2137
+
2138
+ This ends the SS4 paper material for classes and inheritance. We believe we have shown that R S4
2139
+ can be substituted by Galaaz and Ruby classes and that Galaaz makes an easy transition from R
2140
+ developers to Ruby. Ruby is a very flexible and powerful language and has many interesting
2141
+ libraries, where Rails is maybe one of the best known, but there are thousands of others. For
2142
+ those interested in getting deeper into Ruby's libraries, we suggest they look at:
2143
+
2144
+ * https://github.com/markets/awesome-ruby
2145
+ * http://bestgems.org/
2146
+
2147
+ For those interested in Ruby and science, we recommend:
2148
+
2149
+ * http://sciruby.com/
2150
+
2151
+ **Galaaz 2.0** runs on **JRuby** and talks to **GNU R** through the bridge described in this
2152
+ series — the same integration model used throughout the examples above.
2153
+
2154
+ # Callbacks and R calling into Ruby
2155
+
2156
+ On this paper we have focused on accessing R functions from Ruby and have shown how to
2157
+ integrate Ruby with R from the point of view of a Ruby developer. The complementary direction —
2158
+ R calling back into Ruby — is also supported in Galaaz 2.0.
2159
+
2160
+ Galaaz 2.0 uses the **bridge callback** mechanism: Ruby procs (and related callables) can be
2161
+ passed where R expects functions, so algorithms written in R (for example optimizers or higher-order
2162
+ `*apply` helpers) can invoke Ruby logic without leaving the bridge session. Details, options such
2163
+ as callback timeouts, and further examples are in the project manual and on the documentation site:
2164
+ [https://rbotafogo.github.io/galaaz/](https://rbotafogo.github.io/galaaz/).
2165
+
2166
+ We do not reproduce here the older Renjin-era material on packing Ruby objects as R external
2167
+ pointers, constructing Ruby classes from R via JVM APIs, or calling Java collections from R
2168
+ scripts. Those sections belonged to a different runtime; the callback bridge is the supported
2169
+ path in Galaaz 2.0.
2170
+
2171
+ # Conclusions II
2172
+
2173
+ **JRuby + GNU R + Galaaz** gives a practical polyglot stack: idiomatic Ruby for structure and
2174
+ libraries, GNU R for statistics and the CRAN/Bioconductor ecosystem, and Galaaz as the bridge
2175
+ between them. As always, choose the right tools for the job at hand — and when the job sits
2176
+ between an R-only workflow and a broader polyglot application, Galaaz is designed to connect those
2177
+ worlds.