galaaz 0.5.0 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/LICENSE +0 -0
- data/README.md +1360 -636
- data/Rakefile +61 -41
- data/bin/galaaz-bootstrap +137 -0
- data/bin/galaaz-jruby +14 -0
- data/bin/galaaz_jruby_env.inc.sh +6 -0
- data/bin/gbookdown +64 -0
- data/bin/gknit +84 -13
- data/bin/gknit-draft.rb +0 -0
- data/bin/gstudio +5 -3
- data/bin/gstudio_irb.rb +0 -0
- data/bin/gstudio_pry.rb +0 -0
- data/bin/install-tinytex +6 -0
- data/bin/run_all_rspec +43 -0
- data/bin/run_example +14 -0
- data/bin/run_old_rspec +19 -0
- data/bin/run_rspec +23 -0
- data/bin/run_rspec_subset +38 -0
- data/bin/run_slow_rspec +19 -0
- data/blogs/R-on-Rails-Planning-Document.md +940 -0
- data/blogs/README.md +100 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot.Rmd +38 -66
- data/blogs/galaaz_ggplot/galaaz_ggplot.log +754 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot.md +115 -155
- data/blogs/galaaz_ggplot/galaaz_ggplot.tex +607 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/midwest_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-html/scatter_plot_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/midwest_rb.png +0 -0
- data/blogs/galaaz_ggplot/galaaz_ggplot_files/figure-markdown_github/scatter_plot_rb.png +0 -0
- data/blogs/galaaz_ggplot/midwest.Rmd +3 -3
- data/blogs/galaaz_ggplot/midwest_external_png +0 -0
- data/blogs/gknit/gknit.Rmd +47 -52
- data/blogs/gknit/gknit.md +1430 -0
- data/blogs/gknit/gknit_files/figure-html/bubble-1.png +0 -0
- data/blogs/gknit/gknit_files/figure-html/diverging_bar.png +0 -0
- data/blogs/gknit/lst.rds +0 -0
- data/blogs/gknit/model.rb +1 -1
- data/blogs/gknit/stats.bib +0 -0
- data/blogs/manual/include_model_local_repro.Rmd +14 -0
- data/blogs/manual/include_model_local_repro.md +75 -0
- data/blogs/manual/lst.rds +0 -0
- data/blogs/manual/manual.Rmd +852 -239
- data/blogs/manual/manual.log +1786 -0
- data/blogs/manual/manual.md +1360 -636
- data/blogs/manual/manual.tex +1883 -1161
- data/blogs/manual/manual_files/figure-html/bubble-1.png +0 -0
- data/blogs/manual/manual_files/figure-html/diverging_bar.png +0 -0
- data/blogs/manual/manual_files/figure-latex/bubble-1.png +0 -0
- data/blogs/manual/model.rb +1 -1
- data/blogs/nse_dplyr/nse_dplyr.Rmd +84 -111
- data/blogs/nse_dplyr/nse_dplyr.log +928 -0
- data/blogs/nse_dplyr/nse_dplyr.md +198 -229
- data/blogs/oh_my/not_so.rb +0 -0
- data/blogs/oh_my/oh_my.Rmd +1234 -25
- data/blogs/oh_my/oh_my.log +804 -0
- data/blogs/oh_my/oh_my.md +1663 -86
- data/blogs/oh_my/oh_my.tex +821 -0
- data/blogs/oh_my/old.Rmd +15 -14
- data/blogs/ruby_plot/ruby_plot.Rmd +58 -82
- data/blogs/ruby_plot/ruby_plot.log +885 -0
- data/blogs/ruby_plot/ruby_plot.md +71 -102
- data/blogs/ruby_plot/ruby_plot.tex +940 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-html/violin_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/dose_len.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_delivery.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facet_by_dose.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_by_delivery_color2.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_decorations.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_jitter.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/facets_with_points.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_box_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/final_violin_plot.png +0 -0
- data/blogs/ruby_plot/ruby_plot_files/figure-latex/violin_with_jitter.png +0 -0
- data/blogs/test/test.Rmd +14 -0
- data/examples/50Plots_MasterList/Images/midwest-scatterplot.PNG +0 -0
- data/examples/50Plots_MasterList/ScatterPlot.rb +0 -0
- data/examples/50Plots_MasterList/scatter_plot.rb +0 -0
- data/examples/Bibliography/master.bib +0 -0
- data/examples/Bibliography/stats.bib +0 -0
- data/examples/R/calc.R +0 -0
- data/examples/R/java_interop.R +0 -0
- data/examples/bioconductor_deseq2_airway/Documentation/DESeq2-airway-walkthrough.md +56 -0
- data/examples/bioconductor_deseq2_airway/bench_galaaz_three_same_process.rb +53 -0
- data/examples/bioconductor_deseq2_airway/bench_r_three_same_process.R +34 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz.rb +33 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_galaaz_optimized.rb +34 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_minimal.R +30 -0
- data/examples/bioconductor_deseq2_airway/deseq2_airway_pipeline_for_bench.R +36 -0
- data/examples/islr/all.rb +13 -0
- data/examples/islr/ch2.spec.rb +37 -7
- data/examples/islr/ch3.spec.rb +11 -2
- data/examples/islr/ch3_boston.rb +27 -0
- data/examples/islr/ch3_multiple_regression.rb +0 -0
- data/examples/islr/ch6.spec.rb +24 -1
- data/examples/islr/x_y_rnorm.jpg +0 -0
- data/examples/latex_templates/Test-acm_article/acm_proc_article-sp.cls +0 -0
- data/examples/latex_templates/Test-acm_article/sigproc.bib +0 -0
- data/examples/latex_templates/Test-acs_article/acs-Test-acs_article.bib +0 -0
- data/examples/latex_templates/Test-acs_article/acs-my_output.bib +0 -0
- data/examples/latex_templates/Test-aea_article/BibFile.bib +0 -0
- data/examples/latex_templates/Test-aea_article/Test-aea_article.Rmd +0 -0
- data/examples/latex_templates/Test-aea_article/references.bib +0 -0
- data/examples/latex_templates/Test-amq_article/Test-amq_article.Rmd +0 -0
- data/examples/latex_templates/Test-amq_article/Test-amq_article.pdfsync +0 -0
- data/examples/latex_templates/Test-ieee_article/IEEEtran.bst +0 -0
- data/examples/latex_templates/Test-ieee_article/mybibfile.bib +0 -0
- data/examples/latex_templates/Test-rjournal_article/RJournal.sty +0 -0
- data/examples/latex_templates/Test-rjournal_article/RJreferences.bib +0 -0
- data/examples/latex_templates/Test-rjournal_article/Test-rjournal_article.Rmd +0 -0
- data/examples/misc/baseball.csv +0 -0
- data/examples/misc/ggplot.rb +3 -2
- data/examples/misc/moneyball.rb +0 -0
- data/examples/misc/subsetting.rb +0 -0
- data/examples/multithread_shards_to_r/shards_to_r.rb +67 -0
- data/examples/rmarkdown/svm-rmarkdown-anon-ms-example/svm-rmarkdown-anon-ms-example.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-article-example/svm-rmarkdown-article-example.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-beamer-example/svm-rmarkdown-beamer-example.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-cv/svm-rmarkdown-cv.Rmd +0 -0
- data/examples/rmarkdown/svm-rmarkdown-syllabus-example/attend-grade-relationships.csv +0 -0
- data/examples/rmarkdown/svm-rmarkdown-syllabus-example/svm-rmarkdown-syllabus-example.Rmd +0 -0
- data/examples/rmarkdown/svm-xaringan-example/svm-xaringan-example.Rmd +0 -0
- data/examples/sthda_ggplot/README.md +0 -0
- data/examples/sthda_ggplot/RUN.md +41 -0
- data/examples/sthda_ggplot/all.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/density_gg.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_area.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_density.rb +2 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_dotplot.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_freqpoly.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/geom_histogram.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/histogram_density.rb +0 -0
- data/examples/sthda_ggplot/one_variable_continuous/stat.rb +0 -0
- data/examples/sthda_ggplot/one_variable_discrete/bar.rb +0 -0
- data/examples/sthda_ggplot/qplots/box_violin_dot.rb +0 -0
- data/examples/sthda_ggplot/qplots/scatter_plots.rb +0 -0
- data/examples/sthda_ggplot/scatter_gg.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_bin2d.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_density2d.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_bivariate/geom_hex.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/geom_point.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/geom_smooth.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_cont/misc.rb +0 -0
- data/examples/sthda_ggplot/two_variables_cont_function/geom_area.rb +4 -3
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_bar.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_boxplot.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_dotplot.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_jitter.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_line.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_cont/geom_violin.rb +0 -0
- data/examples/sthda_ggplot/two_variables_disc_disc/geom_jitter.rb +0 -0
- data/examples/sthda_ggplot/two_variables_error/geom_crossbar.rb +0 -0
- data/ext/new_bridge/Makefile +46 -0
- data/ext/new_bridge/galaaz_gatekeeper_phase0.cpp +12 -0
- data/ext/new_bridge/galaaz_gatekeeper_phase1.cpp +1639 -0
- data/lib/R_interface/galaaz_device.R +20 -0
- data/lib/R_interface/include_engine.R +109 -0
- data/lib/R_interface/new_bridge_adapter.rb +824 -0
- data/lib/R_interface/r.rb +177 -25
- data/lib/R_interface/r_arrow.rb +113 -0
- data/lib/R_interface/r_libs.R +3 -3
- data/lib/R_interface/r_methods.rb +13 -126
- data/lib/R_interface/r_module_s.rb +0 -0
- data/lib/R_interface/rbinary_operators.rb +20 -2
- data/lib/R_interface/rclosure.rb +5 -1
- data/lib/R_interface/rdata_frame.rb +34 -70
- data/lib/R_interface/rdevice.rb +125 -0
- data/lib/R_interface/rdevices.R +0 -0
- data/lib/R_interface/renvironment.rb +10 -4
- data/lib/R_interface/rexpression.rb +5 -1
- data/lib/R_interface/rindexed_object.rb +41 -13
- data/lib/R_interface/rlanguage.rb +20 -62
- data/lib/R_interface/rlist.rb +115 -25
- data/lib/R_interface/rlogical_operators.rb +0 -0
- data/lib/R_interface/rmatrix.rb +2 -11
- data/lib/R_interface/rmd_indexed_object.rb +5 -1
- data/lib/R_interface/robject.rb +348 -290
- data/lib/R_interface/rpkg.rb +0 -0
- data/lib/R_interface/rsupport.rb +609 -328
- data/lib/R_interface/rsupport_scope.rb +2 -1
- data/lib/R_interface/rsymbol.rb +50 -0
- data/lib/R_interface/ruby_callback.rb +2 -3
- data/lib/R_interface/ruby_extensions.rb +225 -175
- data/lib/R_interface/runary_operators.rb +0 -0
- data/lib/R_interface/rvector.rb +147 -31
- data/lib/galaaz.rb +0 -0
- data/lib/galaaz_jruby.rb +22 -0
- data/lib/gknit/diagnostics.rb +50 -0
- data/lib/gknit/draft.rb +23 -17
- data/lib/gknit/include_engine.rb +15 -7
- data/lib/gknit/knitr_engine.rb +223 -74
- data/lib/gknit/rb_engine.rb +3 -3
- data/lib/gknit/ruby_engine.rb +0 -0
- data/lib/gknit.rb +1 -0
- data/lib/new_bridge/bootstrap/windows_bootstrap.rb +285 -0
- data/lib/new_bridge/envelope.rb +51 -0
- data/lib/new_bridge/eval_result.rb +26 -0
- data/lib/new_bridge/framing.rb +39 -0
- data/lib/new_bridge/instance_pool_client.rb +38 -0
- data/lib/new_bridge/r_instance_manager.rb +404 -0
- data/lib/new_bridge/session_client.rb +530 -0
- data/lib/new_bridge/tcp_framed.rb +44 -0
- data/lib/new_bridge.rb +9 -0
- data/lib/util/exec_ruby.rb +95 -20
- data/lib/util/inline_file.rb +35 -30
- data/new_bridge_specs/benchmark_phase5_5_unboxing_spec.rb +96 -0
- data/new_bridge_specs/eval_r_async_spec.rb +113 -0
- data/new_bridge_specs/integration_phase5_1_concurrent_spec.rb +50 -0
- data/new_bridge_specs/integration_phase5_1_eval_spec.rb +16 -0
- data/new_bridge_specs/integration_phase5_1_r_api_spec.rb +25 -0
- data/new_bridge_specs/integration_phase5_1_smoke_spec.rb +31 -0
- data/new_bridge_specs/integration_phase5_2_dataframe_unboxing_spec.rb +19 -0
- data/new_bridge_specs/integration_phase5_2_handle_eval_unboxing_spec.rb +25 -0
- data/new_bridge_specs/integration_phase5_3_callback_args_spec.rb +28 -0
- data/new_bridge_specs/integration_phase5_3_callback_error_spec.rb +22 -0
- data/new_bridge_specs/integration_phase5_3_callback_timeout_spec.rb +28 -0
- data/new_bridge_specs/integration_phase5_3_callbacks_smoke_spec.rb +22 -0
- data/new_bridge_specs/integration_phase5_3_edge_cases_spec.rb +52 -0
- data/new_bridge_specs/integration_phase5_3_nested_spec.rb +30 -0
- data/new_bridge_specs/integration_phase5_4_concurrent_sessions_spec.rb +53 -0
- data/new_bridge_specs/integration_phase5_4_nested_session_callbacks_spec.rb +49 -0
- data/new_bridge_specs/integration_phase5_4_session_routing_spec.rb +38 -0
- data/new_bridge_specs/integration_phase5_5_stress_concurrency_spec.rb +52 -0
- data/new_bridge_specs/integration_phase5_5_unbox_walk_spec.rb +46 -0
- data/new_bridge_specs/phase0_protocol_spec.rb +96 -0
- data/new_bridge_specs/phase1_req_ret_spec.rb +66 -0
- data/new_bridge_specs/phase2_multi_instance_spec.rb +67 -0
- data/new_bridge_specs/phase3_callbacks_spec.rb +71 -0
- data/new_bridge_specs/phase4_2_hardening_spec.rb +252 -0
- data/new_bridge_specs/phase4_3_r_instance_manager_spec.rb +85 -0
- data/new_bridge_specs/phase4_nested_callbacks_spec.rb +123 -0
- data/r_requires/ggplot.rb +0 -0
- data/r_requires/knitr.rb +0 -0
- data/specs/all.rb +15 -11
- data/specs/arrow_from_ruby_batches_spec.rb +50 -0
- data/specs/arrow_semantics_spec.rb +64 -0
- data/specs/bridge_concurrent_spec.rb +46 -0
- data/specs/bridge_nested_spec.rb +25 -0
- data/specs/dataframe_semantics_spec.rb +122 -0
- data/specs/dataframe_single_index_logical_filter_spec.rb +21 -0
- data/specs/dispatch_probe_cache_spec.rb +38 -0
- data/specs/dispatch_probe_error_class_fallback_spec.rb +20 -0
- data/specs/dispatch_probe_fallback_spec.rb +18 -0
- data/specs/environment_semantics_spec.rb +89 -0
- data/specs/field_access_spec.rb +31 -0
- data/specs/figures/bg.jpeg +0 -0
- data/specs/figures/bg.png +0 -0
- data/specs/figures/bg.svg +168 -57
- data/specs/figures/dose_len.png +0 -0
- data/specs/figures/no_args.jpeg +0 -0
- data/specs/figures/no_args.png +0 -0
- data/specs/figures/no_args.svg +168 -57
- data/specs/figures/width_height.jpeg +0 -0
- data/specs/figures/width_height.png +0 -0
- data/specs/figures/width_height_units1.jpeg +0 -0
- data/specs/figures/width_height_units1.png +0 -0
- data/specs/figures/width_height_units2.jpeg +0 -0
- data/specs/figures/width_height_units2.png +0 -0
- data/specs/formula_semantics_spec.rb +81 -0
- data/specs/galaaz_util_exec_ruby_spec.rb +85 -0
- data/specs/galaaz_util_inline_file_spec.rb +54 -0
- data/specs/gknit_cli_option_permutation_spec.rb +24 -0
- data/specs/gknit_include_engine_spec.rb +72 -0
- data/specs/gknit_install_timeout_report_spec.rb +69 -0
- data/specs/gknit_internal_error_report_spec.rb +57 -0
- data/specs/gknit_vector_map_output_spec.rb +59 -0
- data/specs/globalenv_guardrail_spec.rb +52 -0
- data/specs/language_expression_semantics_spec.rb +145 -0
- data/specs/list_semantics_spec.rb +111 -0
- data/specs/new_bridge_bulk_dataframe_transfer_spec.rb +44 -0
- data/specs/new_bridge_bulk_vector_transfer_spec.rb +73 -0
- data/specs/new_bridge_callback_timeout_spec.rb +69 -0
- data/specs/new_bridge_eval_r_fallback_spec.rb +55 -0
- data/specs/nil_null_spec.rb +42 -0
- data/specs/object_build_phase2_spec.rb +53 -0
- data/specs/phase1_callback_bridge_spec.rb +84 -0
- data/specs/phase2_gknit_generic_rendering_guardrail_spec.rb +46 -0
- data/specs/phase2_gknit_no_raw_code_leakage_spec.rb +43 -0
- data/specs/phase3_gknit_generic_graphics_capture_spec.rb +71 -0
- data/specs/plot_device_semantics_spec.rb +28 -0
- data/specs/plot_snapshot_semantics_spec.rb +58 -0
- data/specs/protocol_result_spec.rb +236 -0
- data/specs/r_batch_fail_fast_spec.rb +47 -0
- data/specs/r_bridge_bootstrap_spec.rb +11 -0
- data/specs/r_devices.spec.rb +1 -1
- data/specs/r_eval.spec.rb +16 -18
- data/specs/r_function.spec.rb +1 -1
- data/specs/r_instance_manager_spec.rb +285 -0
- data/specs/r_list_apply.spec.rb +15 -15
- data/specs/r_matrix.spec.rb +0 -0
- data/specs/r_nse.spec.rb +5 -5
- data/specs/r_object_send_dispatch_spec.rb +13 -0
- data/specs/r_vector_comparator_spec.rb +8 -0
- data/specs/r_vector_creation.spec.rb +0 -0
- data/specs/r_vector_functions.spec.rb +0 -0
- data/specs/r_vector_object.spec.rb +0 -0
- data/specs/r_vector_operators.spec.rb +0 -0
- data/specs/r_vector_structured_scalar_reads_spec.rb +35 -0
- data/specs/r_vector_subsetting.spec.rb +0 -0
- data/specs/range_helper_spec.rb +21 -0
- data/specs/rsupport_scope_spec.rb +28 -0
- data/specs/rsupport_var_name_thread_safety_spec.rb +24 -0
- data/specs/scalar_character_spec.rb +44 -0
- data/specs/scoped_symbol_dsl_refinement_spec.rb +40 -0
- data/specs/session_env_bridge_spec.rb +25 -0
- data/specs/simplecov_bootstrap_spec.rb +10 -0
- data/specs/spec_helper.rb +10 -0
- data/specs/tmp.rb +0 -0
- data/specs/unboxing_recursion_regression_spec.rb +30 -0
- data/specs/unboxing_spec.rb +49 -0
- data/specs/verify_callbacks.rb +42 -0
- data/sty/galaaz.sty +0 -0
- data/version.rb +1 -1
- metadata +194 -64
- data/blogs/galaaz_ggplot/galaaz_ggplot.html +0 -520
- data/blogs/galaaz_ggplot/galaaz_ggplot.pdf +0 -0
- data/blogs/galaaz_ggplot/midwest.html +0 -188
- data/blogs/gknit/gknit.html +0 -2266
- data/blogs/gknit/gknit.pdf +0 -0
- data/blogs/manual/manual.html +0 -4638
- data/blogs/manual/manual.pdf +0 -0
- data/blogs/manual/manual_files/figure-latex/diverging_bar.pdf +0 -0
- data/blogs/nse_dplyr/nse_dplyr.html +0 -878
- data/blogs/nse_dplyr/nse_dplyr.pdf +0 -0
- data/blogs/oh_my/oh_my.html +0 -568
- data/blogs/ruby_plot/ruby_plot.html +0 -544
- data/blogs/ruby_plot/ruby_plot.pdf +0 -0
- data/examples/latex_templates/Test-acs_article/Test-acs_article.pdf +0 -0
- data/examples/latex_templates/Test-aea_article/Test-aea_article.pdf +0 -0
- data/examples/latex_templates/Test-amq_article/Test-amq_article.pdf +0 -0
- data/examples/latex_templates/Test-amq_article/pics/Figure2.pdf +0 -0
- data/examples/latex_templates/Test-asa_article/Test-asa_article.pdf +0 -0
- data/examples/latex_templates/Test-ieee_article/Test-ieee_article.pdf +0 -0
- data/examples/latex_templates/Test-rjournal_article/RJwrapper.pdf +0 -0
- data/examples/latex_templates/Test-springer_article/Test-springer_article.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-anon-ms-example/svm-rmarkdown-anon-ms-example.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-article-example/svm-rmarkdown-article-example.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-beamer-example/svm-rmarkdown-beamer-example.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-cv/svm-rmarkdown-cv.pdf +0 -0
- data/examples/rmarkdown/svm-rmarkdown-syllabus-example/svm-rmarkdown-syllabus-example.pdf +0 -0
- data/specs/r_dataframe.spec.rb +0 -379
- data/specs/r_environment.spec.rb +0 -140
- data/specs/r_formula.spec.rb +0 -232
- data/specs/r_language.spec.rb +0 -112
- data/specs/r_list.spec.rb +0 -293
- data/specs/r_plots.spec.rb +0 -72
- data/specs/ruby_expression.spec.rb +0 -316
|
@@ -0,0 +1,940 @@
|
|
|
1
|
+
# R-on-Rails (Galaaz 2.0): Planning Document
|
|
2
|
+
|
|
3
|
+
## Vision Statement
|
|
4
|
+
|
|
5
|
+
A landmark document that positions **R-on-Rails** as the natural evolution for R developers who need production-grade infrastructure—just as Ruby on Rails revolutionized web development by making it accessible and productive.
|
|
6
|
+
|
|
7
|
+
**Target Audience**: R developers, data scientists, bioinformaticians, and scientific computing practitioners who need to move their R code from analysis notebooks to production systems.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Introduction: The Two-Engine Architecture
|
|
12
|
+
|
|
13
|
+
### Opening Hook (Compelling Narrative)
|
|
14
|
+
|
|
15
|
+
Start with the familiar story: In 2005, Ruby on Rails transformed web development. Before Rails, building web applications required deep expertise in multiple technologies, complex configuration, and weeks of boilerplate code. Rails introduced convention over configuration, scaffolding, and a productive framework that let developers focus on their application logic rather than infrastructure.
|
|
16
|
+
|
|
17
|
+
Today, data science faces a similar challenge. R is unmatched for statistical analysis, visualization, and scientific computing. But taking an R script from a Jupyter notebook or RStudio into a production environment—where it needs user authentication, database connections, job scheduling, error recovery, and API endpoints—is still a formidable barrier.
|
|
18
|
+
|
|
19
|
+
### The Problem: R's Strengths and Production Weaknesses
|
|
20
|
+
|
|
21
|
+
**R's Strengths (Why we love R):**
|
|
22
|
+
- Unparalleled statistical and bioinformatics ecosystem (Bioconductor, thousands of packages)
|
|
23
|
+
- Excellent visualization with ggplot2, lattice, plotly
|
|
24
|
+
- Interactive, REPL-driven development perfect for exploration
|
|
25
|
+
- Domain-specific languages for data manipulation (dplyr, data.table)
|
|
26
|
+
- Rich modeling capabilities (caret, glm, randomForest, etc.)
|
|
27
|
+
- Reproducible research with R Markdown
|
|
28
|
+
|
|
29
|
+
**R's Production Challenges (Why R struggles in production):**
|
|
30
|
+
- **Single-threaded**: R's interpreter is fundamentally single-threaded. Parallel processing requires forking or external orchestration
|
|
31
|
+
- **Memory management**: R's copy-on-modify semantics can be inefficient for large-scale data processing
|
|
32
|
+
- **Web infrastructure**: Shiny is great for dashboards but lacks enterprise-grade user management, authentication flows, and API design patterns
|
|
33
|
+
- **Error isolation**: A crash in one R session takes down the entire process
|
|
34
|
+
- **Database integration**: DBI works but lacks the Rails ActiveRecord ecosystem for migrations, connection pooling, ORM patterns
|
|
35
|
+
- **Data transfer costs**: Moving data between R and other languages typically requires expensive serialization (CSV, JSON)
|
|
36
|
+
- **Deployment complexity**: R environments are notoriously difficult to reproduce (packrat, renv help but aren't seamless)
|
|
37
|
+
- **Security**: No built-in user session management, authorization patterns, or CSRF protection
|
|
38
|
+
|
|
39
|
+
### Why Python Took the Mindshare
|
|
40
|
+
|
|
41
|
+
Python became the lingua franca of data science not because it's better than R at statistics (it isn't), but because it offered a **path to production**:
|
|
42
|
+
|
|
43
|
+
- **Django/Flask**: Web frameworks with auth, ORM, migrations, testing built-in
|
|
44
|
+
- **Jupyter + Production**: Easy transition from notebook to `.py` script to deployed service
|
|
45
|
+
- **Industry adoption**: Tech companies standardized on Python for ML pipelines
|
|
46
|
+
- **Deep Learning**: PyTorch and TensorFlow's Python-first APIs
|
|
47
|
+
|
|
48
|
+
But R still dominates in:
|
|
49
|
+
- Biostatistics and clinical trials (FDA submissions often require R)
|
|
50
|
+
- Epidemiology and public health research
|
|
51
|
+
- Academic statistical research
|
|
52
|
+
- Survey analysis and social sciences
|
|
53
|
+
- Any domain where the statistical method is the product
|
|
54
|
+
|
|
55
|
+
### Why Ruby on Rails Now (The Rails Renaissance)
|
|
56
|
+
|
|
57
|
+
Rails never went away—it matured. Several factors make Rails compelling again:
|
|
58
|
+
|
|
59
|
+
- **HTML5 and Hotwire**: Modern browsers reduce the need for complex JavaScript SPAs. Rails with Hotwire delivers reactive UIs with minimal JS.
|
|
60
|
+
- **JRuby and Modern JVM**: JRuby on modern JVMs delivers excellent performance, true multi-threading, and Java ecosystem integration
|
|
61
|
+
- **Developer Experience**: Rails 7+ with import maps, esbuild, and modern asset pipeline is cleaner than ever
|
|
62
|
+
- **The "One-Person Framework"**: Rails excels when a small team (or single developer) needs to ship a complete product
|
|
63
|
+
- **Convention over Configuration**: Less decision fatigue, faster development
|
|
64
|
+
|
|
65
|
+
### The Thesis: R-on-Rails
|
|
66
|
+
|
|
67
|
+
**R-on-Rails** is a two-engine architecture:
|
|
68
|
+
|
|
69
|
+
1. **Rails (Ruby)**: Handles web requests, authentication, authorization, database operations, background jobs, email, APIs, caching, sessions, and orchestration
|
|
70
|
+
2. **R**: Handles statistical modeling, machine learning, visualization, and data analysis
|
|
71
|
+
3. **Galaaz**: The seamless bridge that makes this integration feel like a single system—using Apache Arrow for zero-copy data transfer at scale
|
|
72
|
+
|
|
73
|
+
**The Value Proposition**: Keep everything you love about R—the packages, the syntax, the statistical rigor—while gaining everything Rails provides for production applications. Move data between languages at memory speed, not serialization speed.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## The Galaaz 2.0 Architecture (Important Technical Clarification)
|
|
78
|
+
|
|
79
|
+
**Galaaz 2.0 uses JRuby + GNU R, not GraalVM/FastR**
|
|
80
|
+
|
|
81
|
+
Earlier versions of Galaaz explored GraalVM's polyglot capabilities with FastR (Oracle's R implementation). However, **FastR is no longer actively developed**, and Galaaz 2.0 has moved to a more robust architecture:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
85
|
+
│ Galaaz 2.0 Architecture │
|
|
86
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
87
|
+
│ │
|
|
88
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
|
|
89
|
+
│ │ JRuby │ ←─────→ │ Galaaz Bridge│ ←─────→ │ GNU R │ │
|
|
90
|
+
│ │ (JVM) │ (Java) │ (JNI/R API) │ (C/R) │ Process │ │
|
|
91
|
+
│ └──────────────┘ └──────────────┘ └──────────┘ │
|
|
92
|
+
│ │ │ │
|
|
93
|
+
│ │ │ │
|
|
94
|
+
│ ▼ ▼ │
|
|
95
|
+
│ ┌──────────────┐ ┌──────────┐│
|
|
96
|
+
│ │ Rails App │ │ Arrow ││
|
|
97
|
+
│ │ Multi-thread │ │ Shared ││
|
|
98
|
+
│ │ ActiveRecord │ │ Memory ││
|
|
99
|
+
│ └──────────────┘ └──────────┘│
|
|
100
|
+
│ │
|
|
101
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**How it works:**
|
|
105
|
+
1. **JRuby** runs on the JVM with true multi-threading, running Rails and application code
|
|
106
|
+
2. **Galaaz Bridge** communicates with a GNU R process via R's C API (or alternative mechanisms)
|
|
107
|
+
3. **Apache Arrow** (optional) provides efficient, shared-memory data transfer for large datasets
|
|
108
|
+
4. **GNU R** is the actual R interpreter—full compatibility with all R packages (ggplot2, dplyr, Bioconductor, etc.)
|
|
109
|
+
|
|
110
|
+
**Why this is better than FastR/GraalVM:**
|
|
111
|
+
- **Full package compatibility**: GNU R runs every CRAN and Bioconductor package
|
|
112
|
+
- **Stable foundation**: No dependency on Oracle's experimental R implementation
|
|
113
|
+
- **Production-ready**: Battle-tested JRuby + battle-tested GNU R
|
|
114
|
+
- **Process isolation**: R runs in separate processes—crash in one R process doesn't take down the Rails app
|
|
115
|
+
- **Multiple R workers**: Ruby can orchestrate multiple R processes for parallel computation
|
|
116
|
+
|
|
117
|
+
**Comparison with other bridges:**
|
|
118
|
+
|
|
119
|
+
| Approach | How it works | Limitations |
|
|
120
|
+
|----------|--------------|-------------|
|
|
121
|
+
| **rpy2** (Python) | Embeds R in Python process | Single-threaded, crashes affect host |
|
|
122
|
+
| **RinRuby** | Parses R code over stdout | String-based, no type safety, slow |
|
|
123
|
+
| **reticulate** (R→Python) | Python embedded in R | Limited to Python, not R-in-Python |
|
|
124
|
+
| **Galaaz 1.0** (GraalVM) | FastR + TruffleRuby polyglot | FastR discontinued, limited packages |
|
|
125
|
+
| **Galaaz 2.0** (JRuby+GNU R) | JRuby → Bridge → GNU R process | Full compatibility, process isolation |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Section-by-Section Outline with Code Examples
|
|
130
|
+
|
|
131
|
+
### Section 1: What Makes Galaaz Different (The Bridge Architecture)
|
|
132
|
+
|
|
133
|
+
**Key Message**: Traditional bridges (rpy2, RinRuby) require string manipulation. Galaaz provides native object interaction without marshaling overhead.
|
|
134
|
+
|
|
135
|
+
**Comparison:**
|
|
136
|
+
|
|
137
|
+
Traditional approach (rpy2):
|
|
138
|
+
```python
|
|
139
|
+
# Python with rpy2 - string-based interaction
|
|
140
|
+
import rpy2.robjects as ro
|
|
141
|
+
ro.r('library(ggplot2)')
|
|
142
|
+
ro.r('data(mpg, package="ggplot2")')
|
|
143
|
+
ro.r('''
|
|
144
|
+
ggplot(mpg, aes(displ, hwy)) +
|
|
145
|
+
geom_point() +
|
|
146
|
+
geom_smooth(method="loess")
|
|
147
|
+
''')
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Galaaz approach:
|
|
151
|
+
```ruby
|
|
152
|
+
# Ruby with Galaaz - native object interaction
|
|
153
|
+
require 'galaaz'
|
|
154
|
+
require 'ggplot'
|
|
155
|
+
|
|
156
|
+
R.install_and_loads('ggplot2')
|
|
157
|
+
mpg = ~R[:mpg] # Access R's mpg dataset
|
|
158
|
+
|
|
159
|
+
# Native Ruby method calls, not string evaluation
|
|
160
|
+
plot = mpg.ggplot(E.aes(x: :displ, y: :hwy)) +
|
|
161
|
+
R.geom_point +
|
|
162
|
+
R.geom_smooth(method: "loess")
|
|
163
|
+
|
|
164
|
+
plot.print
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Why this matters**:
|
|
168
|
+
- Type safety and IDE autocomplete work naturally
|
|
169
|
+
- No string escaping hell
|
|
170
|
+
- R errors propagate as Ruby exceptions
|
|
171
|
+
- Efficient data transfer between JRuby and GNU R processes
|
|
172
|
+
|
|
173
|
+
**Example from specs** (r_vector_functions.spec.rb):
|
|
174
|
+
```ruby
|
|
175
|
+
# R's %in% operator becomes a native method call
|
|
176
|
+
vec1 = R.c(1, 2, 3, 4)
|
|
177
|
+
vec2 = R.c(3, 4, 5)
|
|
178
|
+
|
|
179
|
+
# In R: vec1 %in% vec2
|
|
180
|
+
# In Galaaz:
|
|
181
|
+
result = vec1._ :in, vec2 # Returns R vector of booleans
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### Section 2: dplyr and Non-Standard Evaluation (NSE) - Solved
|
|
187
|
+
|
|
188
|
+
**Key Message**: Programming with dplyr in R is famously tricky due to NSE. Galaaz makes it straightforward with explicit expression handling.
|
|
189
|
+
|
|
190
|
+
**The Problem** (from nse_dplyr blog):
|
|
191
|
+
In R, this works interactively:
|
|
192
|
+
```r
|
|
193
|
+
library(dplyr)
|
|
194
|
+
df %>% filter(x == 1) # x refers to column, not variable
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
But programming with it fails:
|
|
198
|
+
```r
|
|
199
|
+
my_var <- 1
|
|
200
|
+
df %>% filter(x == my_var) # Error: object 'my_var' not found
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
You need tidyeval, `{{}}` operators, `ensym()`, `enquo()`—concepts that take months to master.
|
|
204
|
+
|
|
205
|
+
**The Galaaz Solution**:
|
|
206
|
+
```ruby
|
|
207
|
+
require 'galaaz'
|
|
208
|
+
|
|
209
|
+
df = R.data__frame(x: (1..5), y: (5..1))
|
|
210
|
+
|
|
211
|
+
# Simple filtering with expressions
|
|
212
|
+
filtered = df.filter(R[:x].eq 1) # Filter where column x equals 1
|
|
213
|
+
|
|
214
|
+
# Using variables is explicit and unambiguous
|
|
215
|
+
my_var = 1
|
|
216
|
+
filtered = df.filter(R[:x].eq my_var) # Column x equals Ruby variable my_var
|
|
217
|
+
|
|
218
|
+
# Column-to-column comparison
|
|
219
|
+
filtered = df.filter(R[:x].eq :y) # Column x equals column y
|
|
220
|
+
|
|
221
|
+
# Complex expressions
|
|
222
|
+
filtered = df.filter((R[:x] + R[:y]).gte 5)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Why this works**: Galaaz expressions (`R[:x].eq 1`) are explicit about what's a column reference (symbol `:x`) vs. what's a Ruby value (`my_var`). No ambiguity, no tidyeval complexity.
|
|
226
|
+
|
|
227
|
+
**Example from specs** (r_nse.spec.rb):
|
|
228
|
+
```ruby
|
|
229
|
+
def subset(df, condition)
|
|
230
|
+
# Evaluate condition in the scope of the dataframe
|
|
231
|
+
r = R.eval(condition, df)
|
|
232
|
+
df[r, :all]
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Usage: explicit expression with symbol :a
|
|
236
|
+
subset(df, :a >= 4)
|
|
237
|
+
subset(df, R[:a].eq 4)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
### Section 3: ggplot2 Continuity - Same Power, Better Orchestration
|
|
243
|
+
|
|
244
|
+
**Key Message**: Your ggplot2 skills transfer completely. Galaaz adds Ruby's organizational power—classes, modules, themes as code.
|
|
245
|
+
|
|
246
|
+
**Side-by-Side Example** (from ruby_plot blog):
|
|
247
|
+
|
|
248
|
+
R code:
|
|
249
|
+
```r
|
|
250
|
+
library(ggplot2)
|
|
251
|
+
data(ToothGrowth)
|
|
252
|
+
|
|
253
|
+
ggplot(ToothGrowth, aes(x=dose, y=len)) +
|
|
254
|
+
geom_boxplot() +
|
|
255
|
+
facet_grid(. ~ supp) +
|
|
256
|
+
labs(title="Tooth Growth", x="Dose", y="Length")
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Galaaz code:
|
|
260
|
+
```ruby
|
|
261
|
+
require 'galaaz'
|
|
262
|
+
require 'ggplot'
|
|
263
|
+
|
|
264
|
+
tooth_growth = ~R[:ToothGrowth]
|
|
265
|
+
|
|
266
|
+
plot = tooth_growth.ggplot(E.aes(x: :dose, y: :len)) +
|
|
267
|
+
R.geom_boxplot +
|
|
268
|
+
R.facet_grid(R[:all].til R[:supp]) + # Formula: ~supp
|
|
269
|
+
R.labs(title: "Tooth Growth", x: "Dose", y: "Length")
|
|
270
|
+
|
|
271
|
+
plot.print
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**The Ruby Advantage - Corporate Themes as Modules** (from ruby_plot blog):
|
|
275
|
+
```ruby
|
|
276
|
+
module CorpTheme
|
|
277
|
+
def self.global_theme(faceted: false)
|
|
278
|
+
R.options(scipen: 999) # No scientific notation
|
|
279
|
+
|
|
280
|
+
gb = R.theme(panel__grid__major: E.element_blank)
|
|
281
|
+
gb = gb + R.theme(panel__grid__minor: E.element_blank)
|
|
282
|
+
gb = gb + R.theme(panel__border: E.element_blank)
|
|
283
|
+
gb = gb + R.theme(panel__background: E.element_blank) unless faceted
|
|
284
|
+
gb = gb + R.theme(axis__title: text_element(10, face: "bold", hjust: 1))
|
|
285
|
+
gb
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Apply theme consistently
|
|
290
|
+
final_plot = plot + CorpTheme.global_theme(faceted: true)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Key insight**: In R, creating a reusable theme requires understanding ggplot2's theme system. In Galaaz, it's just a Ruby module—standard Ruby knowledge any developer has.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
### Section 4: Modeling and Statistics - ISLR Examples
|
|
298
|
+
|
|
299
|
+
**Key Message**: Statistical modeling with caret, lm, glm works identically. Ruby adds train/test split abstractions and model management.
|
|
300
|
+
|
|
301
|
+
**Example** (from examples/islr/ch3_boston.rb):
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
require 'galaaz'
|
|
305
|
+
require 'ggplot'
|
|
306
|
+
|
|
307
|
+
R.install_and_loads('ISLR', 'MASS') # Install if needed, then load
|
|
308
|
+
|
|
309
|
+
# Access R's Boston dataset
|
|
310
|
+
boston = ~R[:Boston]
|
|
311
|
+
|
|
312
|
+
# Simple linear regression
|
|
313
|
+
# R: lm(medv ~ lstat, data=Boston)
|
|
314
|
+
boston_lm = R.lm((R[:medv].til :lstat), data: :Boston)
|
|
315
|
+
|
|
316
|
+
puts boston_lm.coef # Coefficients
|
|
317
|
+
puts boston_lm.confint # Confidence intervals
|
|
318
|
+
|
|
319
|
+
# Predictions with intervals
|
|
320
|
+
conf = R.predict(boston_lm,
|
|
321
|
+
R.data__frame(lstat: R.c(5, 10, 15)),
|
|
322
|
+
interval: "confidence")
|
|
323
|
+
|
|
324
|
+
pred = R.predict(boston_lm,
|
|
325
|
+
R.data__frame(lstat: R.c(5, 10, 15)),
|
|
326
|
+
interval: "prediction")
|
|
327
|
+
|
|
328
|
+
# Multiple regression (from ch3_multiple_regression.rb)
|
|
329
|
+
lm_fit = R.lm((R[:medv].til R[:lstat] + R[:age]), data: :Boston)
|
|
330
|
+
puts lm_fit.summary
|
|
331
|
+
|
|
332
|
+
# Polynomial terms
|
|
333
|
+
lm_fit5 = R.lm((R[:medv].til E.poly(:lstat, 5)), data: :Boston)
|
|
334
|
+
puts lm_fit5.summary
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Multiple Regression with caret** (from model.rb):
|
|
338
|
+
```ruby
|
|
339
|
+
require 'galaaz'
|
|
340
|
+
|
|
341
|
+
class Model
|
|
342
|
+
attr_reader :data, :test, :train
|
|
343
|
+
|
|
344
|
+
def initialize(data, percent_train:, seed: 123)
|
|
345
|
+
R.set__seed(seed)
|
|
346
|
+
@data = data
|
|
347
|
+
@percent_train = percent_train
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def partition(field)
|
|
351
|
+
# R's createDataPartition for train/test split
|
|
352
|
+
train_index = R.createDataPartition(
|
|
353
|
+
@data.send(field),
|
|
354
|
+
p: @percent_train,
|
|
355
|
+
list: false,
|
|
356
|
+
times: 1
|
|
357
|
+
)
|
|
358
|
+
@train = @data[train_index, :all]
|
|
359
|
+
@test = @data[-train_index, :all]
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Usage
|
|
364
|
+
model = Model.new(boston_data, percent_train: 0.8, seed: 42)
|
|
365
|
+
model.partition(:medv) # Partition based on medv column
|
|
366
|
+
# model.train and model.test now available
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
### Section 5: Object-Oriented R - Classes, Modules, and Reusable Code
|
|
372
|
+
|
|
373
|
+
**Key Message**: R's S4 and R6 provide OOP, but Ruby's object model is more expressive and easier to learn. Galaaz brings real OOP to statistical workflows.
|
|
374
|
+
|
|
375
|
+
**Comparison** (from oh_my blog):
|
|
376
|
+
|
|
377
|
+
R S4:
|
|
378
|
+
```r
|
|
379
|
+
setClass(
|
|
380
|
+
Class="Trajectories",
|
|
381
|
+
representation=representation(
|
|
382
|
+
times = "numeric",
|
|
383
|
+
traj = "matrix"
|
|
384
|
+
)
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
trajCochin <- new(
|
|
388
|
+
Class="Trajectories",
|
|
389
|
+
times=c(1,3,4,5),
|
|
390
|
+
traj=rbind(
|
|
391
|
+
c(15,15.1, 15.2, 15.2),
|
|
392
|
+
c(16,15.9, 16,16.4)
|
|
393
|
+
)
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
# Add a method
|
|
397
|
+
setMethod("print", "Trajectories",
|
|
398
|
+
function(x, ...) {
|
|
399
|
+
cat("*** Class Trajectories ***\n")
|
|
400
|
+
cat("* Times ="); print(x@times)
|
|
401
|
+
}
|
|
402
|
+
)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Galaaz Ruby:
|
|
406
|
+
```ruby
|
|
407
|
+
class Trajectories
|
|
408
|
+
attr_reader :times, :matrix
|
|
409
|
+
|
|
410
|
+
def initialize(times: nil, matrix: nil)
|
|
411
|
+
@times = times
|
|
412
|
+
@matrix = matrix
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def print
|
|
416
|
+
puts("*** Class Trajectories, method Print ***")
|
|
417
|
+
puts("times = #{@times}")
|
|
418
|
+
puts("traj = #{@matrix}")
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
# Can add methods later without redefining class
|
|
422
|
+
def show
|
|
423
|
+
nrow_show = [10, @matrix.nrow.gz].min
|
|
424
|
+
puts("* Traj (limited to 10x10) =")
|
|
425
|
+
puts @matrix[(1..nrow_show), (1..nrow_show)].format(digits: 2)
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# Usage with R data
|
|
430
|
+
@trajCochin = Trajectories.new(
|
|
431
|
+
times: R.c(1, 3, 4, 5),
|
|
432
|
+
matrix: R.rbind(
|
|
433
|
+
R.c(15, 15.1, 15.2, 15.2),
|
|
434
|
+
R.c(16, 15.9, 16, 16.4)
|
|
435
|
+
)
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
@trajCochin.print
|
|
439
|
+
@trajCochin.show
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Key advantages**:
|
|
443
|
+
- Constructor with default parameters
|
|
444
|
+
- Easy method addition (reopen classes)
|
|
445
|
+
- Mixins and modules for shared behavior
|
|
446
|
+
- Clear separation between R data and Ruby logic
|
|
447
|
+
- Full introspection: `@traj.instance_variables`
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
### Section 6: Apache Arrow - Zero-Copy Data at Scale
|
|
452
|
+
|
|
453
|
+
**Key Message**: Traditional bridges serialize data (JSON, CSV) to pass between languages. Galaaz uses Apache Arrow for zero-copy, memory-mapped data transfer—enabling huge datasets to move from Ruby to R instantly.
|
|
454
|
+
|
|
455
|
+
**The Problem: Data Transfer Overhead**
|
|
456
|
+
|
|
457
|
+
In traditional polyglot systems (rpy2, reticulate), passing data between languages requires serialization:
|
|
458
|
+
|
|
459
|
+
```python
|
|
460
|
+
# Python → R via JSON/CSV serialization (SLOW)
|
|
461
|
+
import pandas as pd
|
|
462
|
+
import rpy2.robjects as ro
|
|
463
|
+
from rpy2.robjects import pandas2ri
|
|
464
|
+
|
|
465
|
+
df = pd.DataFrame({'x': range(1000000), 'y': range(1000000)})
|
|
466
|
+
# Converts Python DataFrame → CSV string → R data.frame (expensive!)
|
|
467
|
+
ro.globalenv['r_df'] = pandas2ri.py2rpy(df)
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
This approach:
|
|
471
|
+
- Copies all data through a text format
|
|
472
|
+
- Loses type information (factors become strings, dates become strings)
|
|
473
|
+
- Consumes 2-3x memory during transfer
|
|
474
|
+
- Becomes a bottleneck at ~100K+ rows
|
|
475
|
+
|
|
476
|
+
**The Galaaz Solution: Apache Arrow**
|
|
477
|
+
|
|
478
|
+
Apache Arrow is a columnar, in-memory format designed for zero-copy data sharing across languages. With Galaaz:
|
|
479
|
+
|
|
480
|
+
```ruby
|
|
481
|
+
require 'galaaz'
|
|
482
|
+
|
|
483
|
+
# 1. Build data in Ruby—using parallel threads for multi-source ingestion
|
|
484
|
+
batches = []
|
|
485
|
+
mutex = Mutex.new
|
|
486
|
+
threads = []
|
|
487
|
+
|
|
488
|
+
# Simulate parallel data ingestion from multiple sources
|
|
489
|
+
[0, 1, 2, 3].each do |tid|
|
|
490
|
+
threads << Thread.new do
|
|
491
|
+
# Each thread queries its own database/shard/API
|
|
492
|
+
local_data = fetch_from_source(tid) # Returns array of hashes
|
|
493
|
+
mutex.synchronize { batches << local_data }
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
threads.each(&:join)
|
|
497
|
+
|
|
498
|
+
# 2. Convert to Arrow Table—zero-copy transfer to R
|
|
499
|
+
# This is the key: data moves to R without serialization!
|
|
500
|
+
table = R::Arrow.from_ruby_batches(batches)
|
|
501
|
+
|
|
502
|
+
# 3. Use R's dplyr directly on the Arrow table
|
|
503
|
+
R.install_and_loads('dplyr', 'arrow')
|
|
504
|
+
|
|
505
|
+
grouped = R.dplyr___group_by(table, :region)
|
|
506
|
+
summarised = R.dplyr___summarise(
|
|
507
|
+
grouped,
|
|
508
|
+
count: E.n(),
|
|
509
|
+
avg_value: E.mean(:value),
|
|
510
|
+
total: E.sum(:value)
|
|
511
|
+
)
|
|
512
|
+
results = R.dplyr___collect(summarised)
|
|
513
|
+
|
|
514
|
+
puts results
|
|
515
|
+
#> # A tibble: 4 × 4
|
|
516
|
+
#> region count avg_value total
|
|
517
|
+
#> <chr> <int> <dbl> <dbl>
|
|
518
|
+
#> 1 North 25000 5.5 137500
|
|
519
|
+
#> 2 South 25000 5.5 137500
|
|
520
|
+
#> 3 East 25000 5.5 137500
|
|
521
|
+
#> 4 West 25000 5.5 137500
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
**Why This Matters:**
|
|
525
|
+
|
|
526
|
+
- **No serialization cost**: Data stays in Arrow's columnar format throughout
|
|
527
|
+
- **Type preservation**: Factors, dates, timestamps remain intact
|
|
528
|
+
- **Memory efficiency**: No intermediate copies; Ruby and R share the same memory
|
|
529
|
+
- **Scale**: Tested with millions of rows (see slow-specs/arrow_large_pipeline_spec.rb with 200K+ rows)
|
|
530
|
+
- **Lazy evaluation**: Arrow datasets can be filtered/aggregated before materializing
|
|
531
|
+
|
|
532
|
+
**Real-World Example: Multi-DB Aggregation**:
|
|
533
|
+
|
|
534
|
+
```ruby
|
|
535
|
+
# Aggregate data from multiple PostgreSQL shards in Ruby,
|
|
536
|
+
# then analyze in R with zero-copy transfer
|
|
537
|
+
|
|
538
|
+
require 'galaaz'
|
|
539
|
+
require 'active_record'
|
|
540
|
+
|
|
541
|
+
# Connect to multiple database shards
|
|
542
|
+
SHARDS = ['shard_1', 'shard_2', 'shard_3', 'shard_4'].map do |shard_name|
|
|
543
|
+
ActiveRecord::Base.establish_connection(
|
|
544
|
+
adapter: 'postgresql',
|
|
545
|
+
host: "#{shard_name}.db.internal",
|
|
546
|
+
database: 'analytics'
|
|
547
|
+
)
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
# Parallel data collection from all shards
|
|
551
|
+
batches = []
|
|
552
|
+
mutex = Mutex.new
|
|
553
|
+
|
|
554
|
+
SHARDS.each do |shard|
|
|
555
|
+
Thread.new do
|
|
556
|
+
# Query this shard
|
|
557
|
+
records = shard.connection.select_all(<<-SQL).cast_values
|
|
558
|
+
SELECT user_id, region, event_type, value, created_at
|
|
559
|
+
FROM events
|
|
560
|
+
WHERE created_at > NOW() - INTERVAL '7 days'
|
|
561
|
+
SQL
|
|
562
|
+
|
|
563
|
+
# Convert to array of hashes for Arrow
|
|
564
|
+
batch = records.map do |row|
|
|
565
|
+
{
|
|
566
|
+
user_id: row[0],
|
|
567
|
+
region: row[1],
|
|
568
|
+
event_type: row[2],
|
|
569
|
+
value: row[3],
|
|
570
|
+
created_at: row[4]
|
|
571
|
+
}
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
mutex.synchronize { batches << batch }
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
# Zero-copy transfer to R as Arrow Table
|
|
579
|
+
table = R::Arrow.from_ruby_batches(batches)
|
|
580
|
+
|
|
581
|
+
# Now use R's full power on the complete dataset
|
|
582
|
+
R.install_and_loads('dplyr', 'arrow', 'lubridate')
|
|
583
|
+
|
|
584
|
+
# R's dplyr works directly on Arrow tables (lazy evaluation)
|
|
585
|
+
analysis = table \
|
|
586
|
+
.dplyr___filter(E.created_at > (E.now() - E.days(7))) \
|
|
587
|
+
.dplyr___group_by(:region, :event_type) \
|
|
588
|
+
.dplyr___summarise(
|
|
589
|
+
count: E.n(),
|
|
590
|
+
total_value: E.sum(:value),
|
|
591
|
+
avg_value: E.mean(:value),
|
|
592
|
+
unique_users: E.n_distinct(:user_id)
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
# Materialize results when ready
|
|
596
|
+
results = R.dplyr___collect(analysis)
|
|
597
|
+
|
|
598
|
+
# Convert back to Ruby objects for API response
|
|
599
|
+
results_hash = results.to_ruby
|
|
600
|
+
render json: { analytics: results_hash }
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
**Parquet and Feather: Persistence Without Conversion**:
|
|
604
|
+
|
|
605
|
+
```ruby
|
|
606
|
+
# Save Ruby data as Parquet (columnar, compressed)
|
|
607
|
+
# Then read directly into R without parsing
|
|
608
|
+
|
|
609
|
+
# 1. Build large dataset in Ruby
|
|
610
|
+
data = (1..1_000_000).map do |i|
|
|
611
|
+
{
|
|
612
|
+
id: i,
|
|
613
|
+
category: ["A", "B", "C", "D"][i % 4],
|
|
614
|
+
value: rand * 100,
|
|
615
|
+
timestamp: Time.now - (i % 86400)
|
|
616
|
+
}
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# 2. Convert to R data.frame, then write as Parquet
|
|
620
|
+
df = R.data__frame(data)
|
|
621
|
+
R::Arrow.write_parquet(df, '/data/events.parquet')
|
|
622
|
+
|
|
623
|
+
# 3. Later, read directly into R as a dataset (lazy, memory-mapped)
|
|
624
|
+
dataset = R::Arrow.dataset('/data/events.parquet')
|
|
625
|
+
|
|
626
|
+
# Query without loading entire file
|
|
627
|
+
summary = R.dplyr___collect(
|
|
628
|
+
dataset \
|
|
629
|
+
.dplyr___filter(R[:value] > 50) \
|
|
630
|
+
.dplyr___group_by(:category) \
|
|
631
|
+
.dplyr___summarise(count: E.n(), avg: E.mean(:value))
|
|
632
|
+
)
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
**Large-Scale Test Results** (from `slow-specs/arrow_large_pipeline_spec.rb`):
|
|
636
|
+
|
|
637
|
+
```ruby
|
|
638
|
+
# Test: 8 threads × 25,000 rows = 200,000 rows
|
|
639
|
+
# With weighted aggregations in R
|
|
640
|
+
thread_count = 8
|
|
641
|
+
rows_per_thread = 25_000
|
|
642
|
+
group_count = 10
|
|
643
|
+
|
|
644
|
+
# Ruby parallel batch construction... 8 threads
|
|
645
|
+
# Arrow table creation from batches → instant
|
|
646
|
+
# R dplyr group_by + summarise → native speed
|
|
647
|
+
# Results verified accurate against Ruby reference implementation
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
**Key Insight**: With Arrow, the boundary between Ruby and R disappears for data. You can build data pipelines in Ruby (with its superior concurrency and database libraries) and analyze in R (with its statistical ecosystem)—with **zero overhead** at the language boundary.
|
|
651
|
+
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
### Section 7: Parallelism and Process Orchestration
|
|
655
|
+
|
|
656
|
+
**Key Message**: R is single-threaded. Galaaz solves this by orchestrating multiple R processes from Ruby's multi-threaded environment—now with Arrow for efficient data distribution.
|
|
657
|
+
|
|
658
|
+
**The Architecture**:
|
|
659
|
+
```
|
|
660
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
661
|
+
│ Rails Application │
|
|
662
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
663
|
+
│ │ Request │ │ Request │ │ Request │ │
|
|
664
|
+
│ │ Handler 1 │ │ Handler 2 │ │ Handler 3 │ │
|
|
665
|
+
│ │ (Thread 1) │ │ (Thread 2) │ │ (Fiber) │ │
|
|
666
|
+
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
|
667
|
+
│ │ │ │ │
|
|
668
|
+
│ └──────────────────┼──────────────────┘ │
|
|
669
|
+
│ │ │
|
|
670
|
+
│ ┌─────────▼─────────┐ │
|
|
671
|
+
│ │ Galaaz Bridge │ │
|
|
672
|
+
│ │ (Process Pool) │ │
|
|
673
|
+
│ └─────────┬─────────┘ │
|
|
674
|
+
│ │ │
|
|
675
|
+
│ ┌──────────────────┼──────────────────┐ │
|
|
676
|
+
│ │ │ │ │
|
|
677
|
+
│ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │
|
|
678
|
+
│ │ R Process 1 │ │ R Process 2 │ │ R Process 3 │ │
|
|
679
|
+
│ │ (Worker) │ │ (Worker) │ │ (Worker) │ │
|
|
680
|
+
│ │ Arrow Table │ │ Arrow Table │ │ Arrow Table │ │
|
|
681
|
+
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
682
|
+
└─────────────────────────────────────────────────────────────┘
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
**Code Example**:
|
|
686
|
+
```ruby
|
|
687
|
+
# Parallel data processing with multiple R workers
|
|
688
|
+
require 'galaaz'
|
|
689
|
+
|
|
690
|
+
# Data partitioned by region
|
|
691
|
+
regions = ['North', 'South', 'East', 'West']
|
|
692
|
+
data_partitions = partition_data_by_region(large_dataset, regions)
|
|
693
|
+
|
|
694
|
+
# Launch parallel R computations
|
|
695
|
+
results = Parallel.map(data_partitions, in_processes: 4) do |region_data|
|
|
696
|
+
R.session do |r|
|
|
697
|
+
# Each block runs in its own R process
|
|
698
|
+
r.install_and_loads('dplyr', 'caret')
|
|
699
|
+
|
|
700
|
+
# R computation
|
|
701
|
+
model = r.lm((R[:sales].til :marketing_spend + :competition_index),
|
|
702
|
+
data: region_data)
|
|
703
|
+
|
|
704
|
+
predictions = r.predict(model, newdata: region_data)
|
|
705
|
+
|
|
706
|
+
# Return Ruby object (automatic unboxing)
|
|
707
|
+
{ region: region_data.name,
|
|
708
|
+
r_squared: model.summary.r.squared.gz,
|
|
709
|
+
predictions: predictions }
|
|
710
|
+
end
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
# Consolidate results in Ruby
|
|
714
|
+
combined = consolidate_predictions(results)
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
**Fault Isolation**:
|
|
718
|
+
```ruby
|
|
719
|
+
# If an R process crashes, Galaaz can restart and retry
|
|
720
|
+
R.session(restart_on_error: true, max_retries: 3) do |r|
|
|
721
|
+
r.long_running_computation(data)
|
|
722
|
+
end
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
### Section 8: Production Architecture - The Full Stack
|
|
728
|
+
|
|
729
|
+
**Key Message**: A complete production system with Rails handling web concerns and R handling analytics.
|
|
730
|
+
|
|
731
|
+
**Example: Analytics API Endpoint**:
|
|
732
|
+
```ruby
|
|
733
|
+
# app/controllers/analytics_controller.rb
|
|
734
|
+
class AnalyticsController < ApplicationController
|
|
735
|
+
before_action :authenticate_user!
|
|
736
|
+
before_action :authorize_analytics!
|
|
737
|
+
|
|
738
|
+
def forecast
|
|
739
|
+
# Rails handles auth, params, response formatting
|
|
740
|
+
product_id = params[:product_id]
|
|
741
|
+
days = params[:days]&.to_i || 30
|
|
742
|
+
|
|
743
|
+
# Database query with ActiveRecord
|
|
744
|
+
historical_data = SalesData
|
|
745
|
+
.where(product_id: product_id)
|
|
746
|
+
.where('date > ?', days.days.ago)
|
|
747
|
+
.to_galaaz_df # Convert to R dataframe
|
|
748
|
+
|
|
749
|
+
# Delegate to R for statistical modeling
|
|
750
|
+
forecast_result = R.session do |r|
|
|
751
|
+
r.install_and_loads('forecast')
|
|
752
|
+
|
|
753
|
+
ts = r.ts(historical_data.sales, frequency: 7)
|
|
754
|
+
model = r.auto.arima(ts)
|
|
755
|
+
forecast = r.forecast(model, h: days)
|
|
756
|
+
|
|
757
|
+
{
|
|
758
|
+
mean: forecast.mean,
|
|
759
|
+
lower: forecast.lower,
|
|
760
|
+
upper: forecast.upper,
|
|
761
|
+
model_aic: model.aic
|
|
762
|
+
}
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
# Rails formats response
|
|
766
|
+
render json: {
|
|
767
|
+
product_id: product_id,
|
|
768
|
+
forecast: forecast_result,
|
|
769
|
+
generated_at: Time.current
|
|
770
|
+
}
|
|
771
|
+
end
|
|
772
|
+
end
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
**Background Job with Sidekiq**:
|
|
776
|
+
```ruby
|
|
777
|
+
# app/jobs/model_training_job.rb
|
|
778
|
+
class ModelTrainingJob < ApplicationJob
|
|
779
|
+
queue_as :ml_training
|
|
780
|
+
|
|
781
|
+
def perform(training_data_id, model_config)
|
|
782
|
+
training_data = TrainingData.find(training_data_id)
|
|
783
|
+
|
|
784
|
+
# Large computation in isolated R process
|
|
785
|
+
R.session(timeout: 1.hour) do |r|
|
|
786
|
+
r.install_and_loads('caret', 'randomForest')
|
|
787
|
+
|
|
788
|
+
# Train model
|
|
789
|
+
model = r.train(
|
|
790
|
+
target ~ .,
|
|
791
|
+
data: training_data.to_r,
|
|
792
|
+
method: model_config[:algorithm],
|
|
793
|
+
trControl: r.trainControl(method: "cv", number: 5)
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
# Save model (R's native format)
|
|
797
|
+
model_path = "/models/#{training_data_id}.rds"
|
|
798
|
+
r.saveRDS(model, model_path)
|
|
799
|
+
|
|
800
|
+
# Store metadata in Rails database
|
|
801
|
+
training_data.update!(
|
|
802
|
+
model_path: model_path,
|
|
803
|
+
accuracy: model.results.Accuracy.max.gz,
|
|
804
|
+
trained_at: Time.current
|
|
805
|
+
)
|
|
806
|
+
end
|
|
807
|
+
end
|
|
808
|
+
end
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
813
|
+
### Section 9: Migration Path - From R Script to R-on-Rails
|
|
814
|
+
|
|
815
|
+
**Key Message**: You don't rewrite everything. You migrate incrementally, starting with the web layer.
|
|
816
|
+
|
|
817
|
+
**Phase 1: Wrap R Script in API** (Week 1):
|
|
818
|
+
```ruby
|
|
819
|
+
# Before: R script run manually
|
|
820
|
+
# Rscript generate_report.R --input data.csv --output report.pdf
|
|
821
|
+
|
|
822
|
+
# After: Rails API endpoint
|
|
823
|
+
class ReportsController < ApplicationController
|
|
824
|
+
def create
|
|
825
|
+
uploaded_file = params[:data_file]
|
|
826
|
+
|
|
827
|
+
R.session do |r|
|
|
828
|
+
r.eval("data <- read.csv('#{uploaded_file.path}')")
|
|
829
|
+
r.eval("rmarkdown::render('report_template.Rmd', output_file='report.pdf')")
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
send_file 'report.pdf', type: 'application/pdf'
|
|
833
|
+
end
|
|
834
|
+
end
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
**Phase 2: Replace R Data Manipulation with Ruby** (Weeks 2-4):
|
|
838
|
+
```ruby
|
|
839
|
+
# Gradually move from R dplyr to Ruby ActiveRecord/SQL
|
|
840
|
+
# Before (R):
|
|
841
|
+
# R.filter(df, :region == "North" & :sales > 1000)
|
|
842
|
+
|
|
843
|
+
# After (Rails):
|
|
844
|
+
SalesData.where(region: "North").where("sales > ?", 1000)
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
**Phase 3: Full Object-Oriented Refactor** (Month 2+):
|
|
848
|
+
```ruby
|
|
849
|
+
# Extract R logic into Ruby service objects
|
|
850
|
+
class SalesForecaster
|
|
851
|
+
def initialize(product)
|
|
852
|
+
@product = product
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
def forecast(days: 30)
|
|
856
|
+
R.session do |r|
|
|
857
|
+
# Only statistical core remains in R
|
|
858
|
+
# Everything else is Ruby
|
|
859
|
+
end
|
|
860
|
+
end
|
|
861
|
+
end
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
---
|
|
865
|
+
|
|
866
|
+
## Conclusion: The Best of Both Worlds
|
|
867
|
+
|
|
868
|
+
### Summary of Value Propositions
|
|
869
|
+
|
|
870
|
+
**For R Developers**:
|
|
871
|
+
- Keep using ggplot2, dplyr, caret, Bioconductor—your knowledge transfers
|
|
872
|
+
- Write statistical code the way you always have
|
|
873
|
+
- Gain real web infrastructure: auth, APIs, databases, caching
|
|
874
|
+
- Deploy with confidence using Rails' battle-tested patterns
|
|
875
|
+
- No need to learn Python or JavaScript frameworks
|
|
876
|
+
|
|
877
|
+
**For Rails Developers**:
|
|
878
|
+
- Add world-class statistical capabilities to your applications
|
|
879
|
+
- Access thousands of R packages without rewriting them
|
|
880
|
+
- Maintain your productivity with familiar Rails patterns
|
|
881
|
+
- Serve data science customers without context-switching languages
|
|
882
|
+
|
|
883
|
+
**For Organizations**:
|
|
884
|
+
- Bridge the gap between data science and engineering teams
|
|
885
|
+
- Deploy R models in production with proper infrastructure
|
|
886
|
+
- Reduce technical debt from "R scripts running on someone's laptop"
|
|
887
|
+
- Enable reproducible research that scales to production
|
|
888
|
+
|
|
889
|
+
### The Future: Galaaz 2.0
|
|
890
|
+
|
|
891
|
+
Galaaz 2.0 represents the maturation of the Ruby-R bridge:
|
|
892
|
+
- **JRuby + GNU R architecture**: Moved from experimental GraalVM/FastR to battle-tested JRuby and standard GNU R for full package compatibility and production stability
|
|
893
|
+
- **New bridge architecture**: More robust, faster, better error handling
|
|
894
|
+
- **Process management**: Built-in support for R process pools
|
|
895
|
+
- **Apache Arrow integration**: Zero-copy data transfer for large datasets—build data pipelines in Ruby's multi-threaded environment, analyze in R with zero serialization overhead
|
|
896
|
+
- **gKnit improvements**: Better R Markdown integration
|
|
897
|
+
- **Rails integration**: First-class support for Rails patterns
|
|
898
|
+
|
|
899
|
+
---
|
|
900
|
+
|
|
901
|
+
## Appendix: Code Reference Quick Links
|
|
902
|
+
|
|
903
|
+
For the full blog post, include references to:
|
|
904
|
+
|
|
905
|
+
- **ISLR Examples**: `examples/islr/ch3_boston.rb`, `examples/islr/ch3_multiple_regression.rb`
|
|
906
|
+
- **ggplot2 Examples**: `examples/sthda_ggplot/` (all geom types)
|
|
907
|
+
- **NSE/dplyr**: `blogs/nse_dplyr/nse_dplyr.md`
|
|
908
|
+
- **Object-Oriented**: `blogs/oh_my/oh_my.md`
|
|
909
|
+
- **Plotting Tutorial**: `blogs/ruby_plot/ruby_plot.md`
|
|
910
|
+
- **Apache Arrow**: `specs/arrow_semantics_spec.rb`, `specs/arrow_from_ruby_batches_spec.rb`, `slow-specs/arrow_large_pipeline_spec.rb`
|
|
911
|
+
- **Specs**: `specs/r_nse.spec.rb`, `specs/r_vector_functions.spec.rb`
|
|
912
|
+
|
|
913
|
+
---
|
|
914
|
+
|
|
915
|
+
## Writing Notes for the Final Post
|
|
916
|
+
|
|
917
|
+
### Tone Guidelines
|
|
918
|
+
- **Respectful of R**: Never disparage R. Position this as "adding production capabilities" not "fixing R."
|
|
919
|
+
- **Practical**: Show real code, not theoretical benefits.
|
|
920
|
+
- **Incremental**: Emphasize that migration can happen piece by piece.
|
|
921
|
+
- **Excited**: This is a genuine breakthrough—let that enthusiasm show.
|
|
922
|
+
|
|
923
|
+
### Code Example Format
|
|
924
|
+
Each section should have:
|
|
925
|
+
1. R version (familiar to readers)
|
|
926
|
+
2. Galaaz version (showing similarity)
|
|
927
|
+
3. Ruby-only enhancement (showing the value add)
|
|
928
|
+
|
|
929
|
+
### Visual Diagrams to Create
|
|
930
|
+
1. Two-engine architecture diagram
|
|
931
|
+
2. Process pool for parallel R
|
|
932
|
+
3. Request flow through Rails-R integration
|
|
933
|
+
4. Migration path timeline
|
|
934
|
+
|
|
935
|
+
### Call to Action
|
|
936
|
+
Invite readers to:
|
|
937
|
+
- Try the examples in `examples/islr/`
|
|
938
|
+
- Read the full dplyr tutorial in `blogs/nse_dplyr/`
|
|
939
|
+
- Explore ggplot examples in `examples/sthda_ggplot/`
|
|
940
|
+
- Join the Galaaz community (GitHub discussions, etc.)
|