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,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Phase 3: R -> Ruby callbacks via CALL/RET.
|
|
4
|
+
# Run: bundle exec rspec specs/new_bridge/phase3_callbacks_spec.rb
|
|
5
|
+
|
|
6
|
+
root = File.expand_path('../..', __dir__)
|
|
7
|
+
$LOAD_PATH.unshift(File.join(root, 'lib')) unless $LOAD_PATH.include?(File.join(root, 'lib'))
|
|
8
|
+
|
|
9
|
+
require 'new_bridge'
|
|
10
|
+
|
|
11
|
+
RSpec.describe 'NewBridge Phase 3 (CALL/RET callbacks)' do
|
|
12
|
+
let(:phase1_cpp) { File.expand_path('../ext/new_bridge/galaaz_gatekeeper_phase1.cpp', __dir__) }
|
|
13
|
+
let(:instance_id) { 'instance-a' }
|
|
14
|
+
|
|
15
|
+
before(:all) do
|
|
16
|
+
skip 'R not on PATH' unless system('command -v R >/dev/null 2>&1')
|
|
17
|
+
cpp = File.expand_path('../ext/new_bridge/galaaz_gatekeeper_phase1.cpp', __dir__)
|
|
18
|
+
@client = NewBridge::SessionClient.new(source_path: cpp)
|
|
19
|
+
@client.start
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
after(:all) do
|
|
23
|
+
@client&.stop
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def with_client(instance_id: 'instance-a')
|
|
27
|
+
yield @client, instance_id
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'callback ping: Ruby callback returns a scalar' do
|
|
31
|
+
with_client(instance_id: instance_id) do |c, iid|
|
|
32
|
+
cb_call_id = c.register_callback do |payload|
|
|
33
|
+
payload.to_i + 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
out = c.eval_r("galaaz_callback_call_phase3('#{cb_call_id}', '41', 500)",
|
|
37
|
+
session_id: 'sess-a', instance_id: iid)
|
|
38
|
+
|
|
39
|
+
expect(out['kind']).to eq('double')
|
|
40
|
+
expect(out['value']).to eq(42)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'callback error propagation: Ruby callback raises' do
|
|
45
|
+
with_client(instance_id: instance_id) do |c, iid|
|
|
46
|
+
cb_call_id = c.register_callback do |_payload|
|
|
47
|
+
raise 'boom'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
expect do
|
|
51
|
+
c.eval_r("galaaz_callback_call_phase3('#{cb_call_id}', '0', 500)",
|
|
52
|
+
session_id: 'sess-a', instance_id: iid)
|
|
53
|
+
end.to raise_error(NewBridge::SessionClient::RProcessError, /boom|evaluation error/)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'callback timeout: Ruby callback does not respond in time' do
|
|
58
|
+
with_client(instance_id: instance_id) do |c, iid|
|
|
59
|
+
cb_call_id = c.register_callback do |_payload|
|
|
60
|
+
sleep 0.2
|
|
61
|
+
999
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
expect do
|
|
65
|
+
c.eval_r("galaaz_callback_call_phase3('#{cb_call_id}', '0', 50)",
|
|
66
|
+
session_id: 'sess-a', instance_id: iid, timeout: 2)
|
|
67
|
+
end.to raise_error(NewBridge::SessionClient::RProcessError, /evaluation error|timeout/)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Phase 4.2: hardening coverage (edge cases + stress) on current bridge API.
|
|
4
|
+
# Run: bundle exec rspec specs/new_bridge/phase4_2_hardening_spec.rb
|
|
5
|
+
|
|
6
|
+
root = File.expand_path('../..', __dir__)
|
|
7
|
+
$LOAD_PATH.unshift(File.join(root, 'lib')) unless $LOAD_PATH.include?(File.join(root, 'lib'))
|
|
8
|
+
|
|
9
|
+
require 'new_bridge'
|
|
10
|
+
|
|
11
|
+
RSpec.describe 'NewBridge Phase 4.2 (Hardening Coverage)' do
|
|
12
|
+
let(:phase1_cpp) { File.expand_path('../ext/new_bridge/galaaz_gatekeeper_phase1.cpp', __dir__) }
|
|
13
|
+
|
|
14
|
+
before(:all) do
|
|
15
|
+
skip 'R not on PATH' unless system('command -v R >/dev/null 2>&1')
|
|
16
|
+
cpp = File.expand_path('../ext/new_bridge/galaaz_gatekeeper_phase1.cpp', __dir__)
|
|
17
|
+
@client = NewBridge::SessionClient.new(source_path: cpp)
|
|
18
|
+
@client.start
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
after(:all) do
|
|
22
|
+
@client&.stop
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe 'edge cases: envelope + eval error paths' do
|
|
26
|
+
it 'rejects msgpack payload that is not a map' do
|
|
27
|
+
bytes = MessagePack.pack(['not', 'a', 'map'])
|
|
28
|
+
expect do
|
|
29
|
+
NewBridge::Envelope.decode(bytes)
|
|
30
|
+
end.to raise_error(NewBridge::Envelope::Error, /expected a map/)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'rejects malformed envelope bytes' do
|
|
34
|
+
expect do
|
|
35
|
+
NewBridge::Envelope.decode("\x81\xA4type".b)
|
|
36
|
+
end.to raise_error(NewBridge::Envelope::Error)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'rejects envelope with unknown keys on encode' do
|
|
40
|
+
expect do
|
|
41
|
+
NewBridge::Envelope.encode(
|
|
42
|
+
'call_id' => 'abc',
|
|
43
|
+
'type' => 'REQ',
|
|
44
|
+
'bogus' => 'x'
|
|
45
|
+
)
|
|
46
|
+
end.to raise_error(NewBridge::Envelope::InvalidEnvelope, /unknown keys/)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'rejects empty envelope bytes' do
|
|
50
|
+
expect do
|
|
51
|
+
NewBridge::Envelope.decode(''.b)
|
|
52
|
+
end.to raise_error(NewBridge::Envelope::InvalidEnvelope, /empty envelope/)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'raises on invalid R syntax' do
|
|
56
|
+
expect do
|
|
57
|
+
@client.eval_r('x <-')
|
|
58
|
+
end.to raise_error(NewBridge::SessionClient::RProcessError)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'raises on empty R code payload' do
|
|
62
|
+
expect do
|
|
63
|
+
@client.eval_r('')
|
|
64
|
+
end.to raise_error(NewBridge::SessionClient::RProcessError)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'returns nil JSON value for NA integer scalar' do
|
|
68
|
+
out = @client.eval_r('as.integer(NA)')
|
|
69
|
+
expect(out['kind']).to eq('integer')
|
|
70
|
+
expect(out['value']).to be_nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'returns nil JSON value for NA logical scalar' do
|
|
74
|
+
out = @client.eval_r('as.logical(NA)')
|
|
75
|
+
expect(out['kind']).to eq('logical')
|
|
76
|
+
expect(out['value']).to be_nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'returns nil JSON value for NA double scalar' do
|
|
80
|
+
out = @client.eval_r('as.numeric(NA)')
|
|
81
|
+
expect(out['kind']).to eq('double')
|
|
82
|
+
expect(out['value']).to be_nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'raises on non scalar results' do
|
|
86
|
+
expect do
|
|
87
|
+
@client.eval_r('1:3')
|
|
88
|
+
end.to raise_error(NewBridge::SessionClient::RProcessError, /length-1 scalar/)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'handles very short timeout from Ruby side' do
|
|
92
|
+
expect do
|
|
93
|
+
@client.eval_r('Sys.sleep(0.2); 1L', timeout: 0.05)
|
|
94
|
+
end.to raise_error(NewBridge::SessionClient::TimeoutError)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe 'concurrency and stress on one instance' do
|
|
99
|
+
it 'handles 50 concurrent requests with unique sessions' do
|
|
100
|
+
threads = 50.times.map do |i|
|
|
101
|
+
Thread.new do
|
|
102
|
+
sid = "p42-conc-#{i}"
|
|
103
|
+
@client.eval_r("v <- #{i}L; v", session_id: sid)['value']
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
values = threads.map(&:value)
|
|
108
|
+
expect(values.sort).to eq((0..49).to_a)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'handles repeated calls in same session without leaking type info' do
|
|
112
|
+
sid = 'p42-same-session'
|
|
113
|
+
20.times do |i|
|
|
114
|
+
@client.eval_r("x <- #{i}L", session_id: sid)
|
|
115
|
+
end
|
|
116
|
+
out = @client.eval_r('x', session_id: sid)
|
|
117
|
+
expect(out['kind']).to eq('integer')
|
|
118
|
+
expect(out['value']).to eq(19)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'supports larger payload code strings' do
|
|
122
|
+
big = 'a' * 20_000
|
|
123
|
+
code = "nchar('#{big}')"
|
|
124
|
+
out = @client.eval_r(code)
|
|
125
|
+
expect(out['value']).to eq(20_000)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'keeps nested callback recursion stable for depth 5' do
|
|
129
|
+
cb_call_id = @client.register_callback do |_payload, call_id|
|
|
130
|
+
acc = 0
|
|
131
|
+
5.times do |i|
|
|
132
|
+
r = @client.eval_r("#{i}L + 1L", parent_id: call_id)
|
|
133
|
+
acc += r['value']
|
|
134
|
+
end
|
|
135
|
+
acc
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
out = @client.eval_r("galaaz_callback_call_phase3('#{cb_call_id}', 'unused', 5000)")
|
|
139
|
+
expect(out['kind']).to eq('double')
|
|
140
|
+
expect(out['value']).to eq(15)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'handles 200 sequential requests without state corruption' do
|
|
144
|
+
sid = 'p42-seq'
|
|
145
|
+
200.times do |i|
|
|
146
|
+
@client.eval_r("k <- #{i}L", session_id: sid)
|
|
147
|
+
end
|
|
148
|
+
out = @client.eval_r('k', session_id: sid)
|
|
149
|
+
expect(out['value']).to eq(199)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it 'supports concurrent requests to the same session deterministically for last write readback' do
|
|
153
|
+
sid = 'p42-same-session-concurrent'
|
|
154
|
+
threads = 20.times.map do |i|
|
|
155
|
+
Thread.new { @client.eval_r("x <- #{i}L", session_id: sid) }
|
|
156
|
+
end
|
|
157
|
+
threads.each(&:join)
|
|
158
|
+
out = @client.eval_r('x', session_id: sid)
|
|
159
|
+
expect(out['kind']).to eq('integer')
|
|
160
|
+
expect(out['value']).to be_between(0, 19).inclusive
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe 'multi-instance hardening on current pool API' do
|
|
165
|
+
before(:all) do
|
|
166
|
+
cpp = File.expand_path('../ext/new_bridge/galaaz_gatekeeper_phase1.cpp', __dir__)
|
|
167
|
+
@pool = NewBridge::InstancePoolClient.new(
|
|
168
|
+
source_path: cpp,
|
|
169
|
+
instances: ['p42-a', 'p42-b']
|
|
170
|
+
).start
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
after(:all) do
|
|
174
|
+
@pool&.stop
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it 'maintains session isolation across instances with same session_id' do
|
|
178
|
+
@pool.eval_r('x <- 101L', session_id: 'same-sid', instance_id: 'p42-a')
|
|
179
|
+
@pool.eval_r('x <- 202L', session_id: 'same-sid', instance_id: 'p42-b')
|
|
180
|
+
|
|
181
|
+
a = @pool.eval_r('x', session_id: 'same-sid', instance_id: 'p42-a')
|
|
182
|
+
b = @pool.eval_r('x', session_id: 'same-sid', instance_id: 'p42-b')
|
|
183
|
+
|
|
184
|
+
expect(a['value']).to eq(101)
|
|
185
|
+
expect(b['value']).to eq(202)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it 'handles 80 pool requests under mixed concurrency' do
|
|
189
|
+
threads = 8.times.map do |t|
|
|
190
|
+
Thread.new do
|
|
191
|
+
inst = t.even? ? 'p42-a' : 'p42-b'
|
|
192
|
+
10.times.map do |i|
|
|
193
|
+
n = (t * 10) + i
|
|
194
|
+
sid = "p42-pool-#{t}-#{i}"
|
|
195
|
+
@pool.eval_r("z <- #{n}L; z", session_id: sid, instance_id: inst)['value']
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
values = threads.flat_map(&:value)
|
|
201
|
+
expect(values.sort).to eq((0..79).to_a)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it 'raises when routing to unknown instance_id' do
|
|
205
|
+
expect do
|
|
206
|
+
@pool.eval_r('1L + 1L', session_id: 'x', instance_id: 'unknown-instance')
|
|
207
|
+
end.to raise_error(KeyError)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it 'keeps instance local state isolated after updates on both instances' do
|
|
211
|
+
@pool.eval_r('counter <- 1L', session_id: 'iso', instance_id: 'p42-a')
|
|
212
|
+
@pool.eval_r('counter <- 10L', session_id: 'iso', instance_id: 'p42-b')
|
|
213
|
+
|
|
214
|
+
@pool.eval_r('counter <- counter + 1L', session_id: 'iso', instance_id: 'p42-a')
|
|
215
|
+
@pool.eval_r('counter <- counter + 1L', session_id: 'iso', instance_id: 'p42-b')
|
|
216
|
+
|
|
217
|
+
a = @pool.eval_r('counter', session_id: 'iso', instance_id: 'p42-a')
|
|
218
|
+
b = @pool.eval_r('counter', session_id: 'iso', instance_id: 'p42-b')
|
|
219
|
+
expect(a['value']).to eq(2)
|
|
220
|
+
expect(b['value']).to eq(11)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it 'continues serving healthy instances after an unknown-instance routing error' do
|
|
224
|
+
expect do
|
|
225
|
+
@pool.eval_r('1L', session_id: 'err', instance_id: 'nope')
|
|
226
|
+
end.to raise_error(KeyError)
|
|
227
|
+
|
|
228
|
+
ok = @pool.eval_r('7L + 5L', session_id: 'ok', instance_id: 'p42-a')
|
|
229
|
+
expect(ok['value']).to eq(12)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
describe 'rapid lifecycle checks' do
|
|
234
|
+
it 'supports repeated start/stop cycles' do
|
|
235
|
+
5.times do
|
|
236
|
+
c = NewBridge::SessionClient.new(source_path: phase1_cpp)
|
|
237
|
+
c.start
|
|
238
|
+
out = c.eval_r('2L + 3L')
|
|
239
|
+
expect(out['value']).to eq(5)
|
|
240
|
+
c.stop
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it 'allows stop to be called multiple times safely' do
|
|
245
|
+
c = NewBridge::SessionClient.new(source_path: phase1_cpp)
|
|
246
|
+
c.start
|
|
247
|
+
c.stop
|
|
248
|
+
expect { c.stop }.not_to raise_error
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Phase 4.3: local R instance management API (first slice).
|
|
4
|
+
# Run: bundle exec rspec specs/new_bridge/phase4_3_r_instance_manager_spec.rb
|
|
5
|
+
|
|
6
|
+
root = File.expand_path('../..', __dir__)
|
|
7
|
+
$LOAD_PATH.unshift(File.join(root, 'lib')) unless $LOAD_PATH.include?(File.join(root, 'lib'))
|
|
8
|
+
|
|
9
|
+
require 'new_bridge'
|
|
10
|
+
|
|
11
|
+
RSpec.describe 'NewBridge Phase 4.3 (RInstanceManager - local)' do
|
|
12
|
+
before(:all) do
|
|
13
|
+
skip 'R not on PATH' unless system('command -v R >/dev/null 2>&1')
|
|
14
|
+
phase1_cpp = File.expand_path('../ext/new_bridge/galaaz_gatekeeper_phase1.cpp', __dir__)
|
|
15
|
+
@mgr = NewBridge::RInstanceManager.new(source_path: phase1_cpp)
|
|
16
|
+
@mgr.spawn_local(instance_id: 'mgr-a', version: 'v-current')
|
|
17
|
+
@mgr.spawn_local(instance_id: 'mgr-b', version: 'v-legacy')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
after(:all) do
|
|
21
|
+
@mgr&.stop
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'evaluates code in isolated instances with routing by instance_id' do
|
|
25
|
+
@mgr.eval_r('x <- 11L', session_id: 'shared-sid', instance_id: 'mgr-a')
|
|
26
|
+
@mgr.eval_r('x <- 22L', session_id: 'shared-sid', instance_id: 'mgr-b')
|
|
27
|
+
|
|
28
|
+
a = @mgr.eval_r('x', session_id: 'shared-sid', instance_id: 'mgr-a')
|
|
29
|
+
b = @mgr.eval_r('x', session_id: 'shared-sid', instance_id: 'mgr-b')
|
|
30
|
+
|
|
31
|
+
expect(a['value']).to eq(11)
|
|
32
|
+
expect(b['value']).to eq(22)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'raises on spawning an existing instance_id' do
|
|
36
|
+
expect do
|
|
37
|
+
@mgr.spawn_local(instance_id: 'mgr-a')
|
|
38
|
+
end.to raise_error(ArgumentError, /instance already exists/)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'exposes instance metadata including version labels' do
|
|
42
|
+
list = @mgr.instances
|
|
43
|
+
ids = list.map { |h| h[:id] }
|
|
44
|
+
expect(ids).to include('mgr-a', 'mgr-b')
|
|
45
|
+
|
|
46
|
+
a = list.find { |h| h[:id] == 'mgr-a' }
|
|
47
|
+
b = list.find { |h| h[:id] == 'mgr-b' }
|
|
48
|
+
expect(a[:version]).to eq('v-current')
|
|
49
|
+
expect(b[:version]).to eq('v-legacy')
|
|
50
|
+
expect(a[:status]).to eq(:healthy)
|
|
51
|
+
expect(b[:provider]).to eq('local')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'routes eval by version with eval_with_version' do
|
|
55
|
+
out = @mgr.eval_with_version('10L + 5L', version: 'v-current', session_id: 'ver-sess')
|
|
56
|
+
expect(out['value']).to eq(15)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'returns one result per version with eval_all_versions' do
|
|
60
|
+
results = @mgr.eval_all_versions('3L + 4L', session_id: 'all-vers')
|
|
61
|
+
expect(results.keys.sort).to eq(%w[v-current v-legacy])
|
|
62
|
+
expect(results['v-current']['value']).to eq(7)
|
|
63
|
+
expect(results['v-legacy']['value']).to eq(7)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'stops instances on demand' do
|
|
67
|
+
@mgr.stop('mgr-a')
|
|
68
|
+
expect(@mgr.instance_ids).not_to include('mgr-a')
|
|
69
|
+
|
|
70
|
+
expect do
|
|
71
|
+
@mgr.eval_r('1L + 1L', session_id: 's', instance_id: 'mgr-a')
|
|
72
|
+
end.to raise_error(KeyError)
|
|
73
|
+
|
|
74
|
+
# remaining instance still works
|
|
75
|
+
out = @mgr.eval_r('2L + 2L', session_id: 's', instance_id: 'mgr-b')
|
|
76
|
+
expect(out['value']).to eq(4)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'raises when eval_with_version targets unknown version' do
|
|
80
|
+
expect do
|
|
81
|
+
@mgr.eval_with_version('1L + 1L', version: 'no-such-version')
|
|
82
|
+
end.to raise_error(KeyError, /no instance for version/)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Phase 4: Nested Ruby->R during R callback waits.
|
|
4
|
+
# Run: bundle exec rspec specs/new_bridge/phase4_nested_callbacks_spec.rb
|
|
5
|
+
|
|
6
|
+
root = File.expand_path('../..', __dir__)
|
|
7
|
+
$LOAD_PATH.unshift(File.join(root, 'lib')) unless $LOAD_PATH.include?(File.join(root, 'lib'))
|
|
8
|
+
|
|
9
|
+
require 'new_bridge'
|
|
10
|
+
|
|
11
|
+
RSpec.describe 'NewBridge Phase 4 (Nested callbacks)' do
|
|
12
|
+
let(:phase1_cpp) { File.expand_path('../ext/new_bridge/galaaz_gatekeeper_phase1.cpp', __dir__) }
|
|
13
|
+
|
|
14
|
+
before(:all) do
|
|
15
|
+
skip 'R not on PATH' unless system('command -v R >/dev/null 2>&1')
|
|
16
|
+
cpp = File.expand_path('../ext/new_bridge/galaaz_gatekeeper_phase1.cpp', __dir__)
|
|
17
|
+
@client = NewBridge::SessionClient.new(source_path: cpp)
|
|
18
|
+
@client.start
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
after(:all) do
|
|
22
|
+
@client&.stop
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def with_client
|
|
26
|
+
yield @client
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'deep recursion: Ruby callback calls back into R and returns correct value' do
|
|
30
|
+
with_client do |c|
|
|
31
|
+
# Register callback that does nested eval_r into R
|
|
32
|
+
cb_call_id = c.register_callback do |_payload, call_id|
|
|
33
|
+
# Nested call into R - this tests the core Phase 4 scenario
|
|
34
|
+
nested_result = c.eval_r('41L + 1L', parent_id: call_id)
|
|
35
|
+
nested_result['value'] # Return 42
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# R calls the callback via galaaz_callback_call_phase3
|
|
39
|
+
out = c.eval_r("galaaz_callback_call_phase3('#{cb_call_id}', 'unused', 5000)")
|
|
40
|
+
|
|
41
|
+
expect(out['kind']).to eq('double')
|
|
42
|
+
expect(out['value']).to eq(42)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'error unwinding: Ruby callback raises, error propagates correctly' do
|
|
47
|
+
with_client do |c|
|
|
48
|
+
cb_call_id = c.register_callback do |_payload, _call_id|
|
|
49
|
+
raise 'callback error from Ruby'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Note: Error message is generic because R_tryEval catches Rcpp::stop
|
|
53
|
+
# The important thing is that an error is raised, not the specific message
|
|
54
|
+
expect do
|
|
55
|
+
c.eval_r("galaaz_callback_call_phase3('#{cb_call_id}', 'unused', 5000)")
|
|
56
|
+
end.to raise_error(NewBridge::SessionClient::RProcessError)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'timeout: Ruby callback sleeps too long, raises timeout' do
|
|
61
|
+
with_client do |c|
|
|
62
|
+
cb_call_id = c.register_callback do |_payload, _call_id|
|
|
63
|
+
sleep 0.5 # Sleep longer than callback timeout
|
|
64
|
+
999
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The callback times out after 50ms, but the Ruby sleep is 500ms
|
|
68
|
+
# Note: Error message is generic because R_tryEval catches Rcpp::stop
|
|
69
|
+
expect do
|
|
70
|
+
c.eval_r("galaaz_callback_call_phase3('#{cb_call_id}', 'unused', 50)", timeout: 2)
|
|
71
|
+
end.to raise_error(NewBridge::SessionClient::RProcessError)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'nested ordering: multiple nested calls complete correctly' do
|
|
76
|
+
with_client do |c|
|
|
77
|
+
call_count = 0
|
|
78
|
+
|
|
79
|
+
cb_call_id = c.register_callback do |_payload, call_id|
|
|
80
|
+
call_count += 1
|
|
81
|
+
# Multiple nested calls
|
|
82
|
+
r1 = c.eval_r('10L + 1L', parent_id: call_id)
|
|
83
|
+
r2 = c.eval_r('20L + 2L', parent_id: call_id)
|
|
84
|
+
r3 = c.eval_r('30L + 3L', parent_id: call_id)
|
|
85
|
+
|
|
86
|
+
r1['value'] + r2['value'] + r3['value'] # 11 + 22 + 33 = 66
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
out = c.eval_r("galaaz_callback_call_phase3('#{cb_call_id}', 'unused', 5000)")
|
|
90
|
+
|
|
91
|
+
expect(out['value']).to eq(66)
|
|
92
|
+
expect(call_count).to eq(1)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# dplyr/rlang fancy errors can lose their causal message under nested callback
|
|
97
|
+
# stacks (path_trim_prefix/strsplit). Gatekeeper wraps nested evals so Ruby
|
|
98
|
+
# still sees "object 'a' not found".
|
|
99
|
+
it 'preserves causal dplyr error messages under nested eval', :slow do
|
|
100
|
+
with_client do |c|
|
|
101
|
+
probe = c.eval_r("isTRUE(requireNamespace('dplyr', quietly = TRUE))")
|
|
102
|
+
skip 'dplyr not installed' unless probe['value'] == true
|
|
103
|
+
|
|
104
|
+
nested_msg = nil
|
|
105
|
+
cb_call_id = c.register_callback do |_payload, call_id|
|
|
106
|
+
begin
|
|
107
|
+
c.eval_r(
|
|
108
|
+
"__G_EVAL_WITH_RESULT__tmp <- { library(dplyr); mutate(data.frame(x = 1:3), y = a + x) }",
|
|
109
|
+
parent_id: call_id
|
|
110
|
+
)
|
|
111
|
+
rescue NewBridge::SessionClient::RProcessError => e
|
|
112
|
+
nested_msg = e.message.to_s
|
|
113
|
+
end
|
|
114
|
+
1.0
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
c.eval_r("galaaz_callback_call_phase3('#{cb_call_id}', 'unused', 15000)", timeout: 30)
|
|
118
|
+
|
|
119
|
+
expect(nested_msg).to match(/object 'a' not found/)
|
|
120
|
+
expect(nested_msg).not_to match(/strsplit|path_trim_prefix/)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
data/r_requires/ggplot.rb
CHANGED
|
File without changes
|
data/r_requires/knitr.rb
CHANGED
|
File without changes
|
data/specs/all.rb
CHANGED
|
@@ -23,14 +23,20 @@
|
|
|
23
23
|
|
|
24
24
|
require_relative 'r_eval.spec'
|
|
25
25
|
|
|
26
|
-
#
|
|
27
|
-
require_relative '
|
|
26
|
+
# Nil/null: nil.is__null and nil.isTRUE (R NULL → Ruby nil)
|
|
27
|
+
require_relative 'nil_null_spec'
|
|
28
|
+
|
|
29
|
+
# Unboxing: single scalar/list, nested list, depth limit
|
|
30
|
+
require_relative 'unboxing_spec'
|
|
31
|
+
|
|
32
|
+
# Field access: obj.name uses R [[ not $ (avoids "$ operator is invalid for atomic vectors")
|
|
33
|
+
require_relative 'field_access_spec'
|
|
28
34
|
|
|
29
|
-
#
|
|
30
|
-
require_relative '
|
|
35
|
+
# Scalar character: bridge unescapes \n and \" so Ruby gets real newlines/quotes
|
|
36
|
+
require_relative 'scalar_character_spec'
|
|
31
37
|
|
|
32
|
-
# Specification for
|
|
33
|
-
require_relative '
|
|
38
|
+
# Specification for Functions
|
|
39
|
+
require_relative 'r_function.spec'
|
|
34
40
|
|
|
35
41
|
# Specification for R::Vector
|
|
36
42
|
require_relative 'r_vector_creation.spec'
|
|
@@ -40,21 +46,19 @@ require_relative 'r_vector_functions.spec'
|
|
|
40
46
|
require_relative 'r_vector_operators.spec'
|
|
41
47
|
|
|
42
48
|
# Specification for R::Lists
|
|
43
|
-
require_relative 'r_list.spec'
|
|
44
49
|
require_relative 'r_list_apply.spec'
|
|
45
50
|
|
|
46
51
|
# Specification for R::Matrix
|
|
47
52
|
require_relative 'r_matrix.spec'
|
|
48
53
|
|
|
49
|
-
# Specification for R::Dataframes
|
|
50
|
-
require_relative 'r_dataframe.spec'
|
|
51
|
-
|
|
52
54
|
# Specification for language features
|
|
53
55
|
require_relative 'r_function.spec'
|
|
54
|
-
require_relative 'r_formula.spec'
|
|
55
56
|
|
|
56
57
|
# Test file generation
|
|
57
58
|
require_relative 'r_devices.spec'
|
|
58
59
|
|
|
60
|
+
# Phase 1: NewBridge callback stub + galaaz_bridge_env (see Documentation/Gknit_Generic_Output_Phased_Plan.md)
|
|
61
|
+
require_relative 'phase1_callback_bridge_spec'
|
|
62
|
+
|
|
59
63
|
# Testes for NSE
|
|
60
64
|
# require_relative 'r_nse.spec'
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'galaaz'
|
|
4
|
+
|
|
5
|
+
describe 'Arrow from Ruby batches' do
|
|
6
|
+
before(:all) do
|
|
7
|
+
ok = R::Support.eval("requireNamespace('arrow', quietly=TRUE) && requireNamespace('dplyr', quietly=TRUE)")
|
|
8
|
+
skip 'R packages arrow/dplyr are not available' unless ok == true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'builds Arrow table from Ruby-threaded batches and reduces in R' do
|
|
12
|
+
batches = []
|
|
13
|
+
mutex = Mutex.new
|
|
14
|
+
threads = []
|
|
15
|
+
|
|
16
|
+
# Simulate parallel Ruby ingestion.
|
|
17
|
+
[0, 1, 2, 3].each do |tid|
|
|
18
|
+
threads << Thread.new do
|
|
19
|
+
start = tid * 1000
|
|
20
|
+
local = (start...(start + 1000)).map do |i|
|
|
21
|
+
{ id: i, grp: "g#{i % 5}", value: (i % 10) + 1 }
|
|
22
|
+
end
|
|
23
|
+
mutex.synchronize { batches << local }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
threads.each(&:join)
|
|
27
|
+
|
|
28
|
+
tbl = R::Arrow.from_ruby_batches(batches)
|
|
29
|
+
expect(tbl.rclass.split(' ')).to include('Table')
|
|
30
|
+
|
|
31
|
+
grouped = R.dplyr___group_by(tbl, :grp)
|
|
32
|
+
summed = R.dplyr___summarise(grouped, total: E.sum(:value))
|
|
33
|
+
out = R.dplyr___collect(summed)
|
|
34
|
+
|
|
35
|
+
groups = out[['grp']]
|
|
36
|
+
totals = out[['total']]
|
|
37
|
+
result = {}
|
|
38
|
+
(1..(groups.length >> 0)).each do |i|
|
|
39
|
+
grp = groups[i]
|
|
40
|
+
tot = totals[i]
|
|
41
|
+
grp = grp.respond_to?(:>>) ? (grp >> 0) : grp
|
|
42
|
+
tot = tot.respond_to?(:>>) ? (tot >> 0) : tot
|
|
43
|
+
result[grp] = tot
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
expected = Hash.new(0)
|
|
47
|
+
batches.flatten.each { |r| expected[r[:grp]] += r[:value] }
|
|
48
|
+
expect(result).to eq(expected)
|
|
49
|
+
end
|
|
50
|
+
end
|