lda-ruby 0.3.9 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +5 -13
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile +9 -0
  4. data/README.md +126 -3
  5. data/VERSION.yml +3 -3
  6. data/docs/modernization-handoff.md +233 -0
  7. data/docs/porting-strategy.md +148 -0
  8. data/docs/precompiled-platform-policy.md +81 -0
  9. data/docs/precompiled-target-evaluation.md +67 -0
  10. data/docs/release-runbook.md +192 -0
  11. data/docs/rust-orchestration-guardrails.md +50 -0
  12. data/ext/lda-ruby/cokus.c +10 -11
  13. data/ext/lda-ruby/cokus.h +3 -3
  14. data/ext/lda-ruby/extconf.rb +10 -6
  15. data/ext/lda-ruby/lda-inference.c +23 -7
  16. data/ext/lda-ruby/utils.c +8 -0
  17. data/ext/lda-ruby-rust/Cargo.toml +12 -0
  18. data/ext/lda-ruby-rust/README.md +73 -0
  19. data/ext/lda-ruby-rust/extconf.rb +135 -0
  20. data/ext/lda-ruby-rust/include/strings.h +35 -0
  21. data/ext/lda-ruby-rust/src/lib.rs +1263 -0
  22. data/lda-ruby.gemspec +0 -0
  23. data/lib/lda-ruby/backends/base.rb +133 -0
  24. data/lib/lda-ruby/backends/native.rb +158 -0
  25. data/lib/lda-ruby/backends/pure_ruby.rb +675 -0
  26. data/lib/lda-ruby/backends/rust.rb +607 -0
  27. data/lib/lda-ruby/backends.rb +58 -0
  28. data/lib/lda-ruby/corpus/corpus.rb +17 -15
  29. data/lib/lda-ruby/corpus/data_corpus.rb +2 -2
  30. data/lib/lda-ruby/corpus/directory_corpus.rb +2 -2
  31. data/lib/lda-ruby/corpus/text_corpus.rb +2 -2
  32. data/lib/lda-ruby/document/document.rb +6 -6
  33. data/lib/lda-ruby/document/text_document.rb +5 -4
  34. data/lib/lda-ruby/rust_build_policy.rb +21 -0
  35. data/lib/lda-ruby/version.rb +5 -0
  36. data/lib/lda-ruby.rb +293 -48
  37. data/test/backend_compatibility_test.rb +146 -0
  38. data/test/backends_selection_test.rb +100 -0
  39. data/test/benchmark_scripts_test.rb +23 -0
  40. data/test/gemspec_test.rb +27 -0
  41. data/test/lda_ruby_test.rb +49 -11
  42. data/test/packaged_gem_smoke_test.rb +33 -0
  43. data/test/pure_ruby_orchestration_test.rb +109 -0
  44. data/test/release_scripts_test.rb +93 -0
  45. data/test/rust_build_policy_test.rb +23 -0
  46. data/test/rust_orchestration_test.rb +911 -0
  47. data/test/simple_pipeline_test.rb +22 -0
  48. data/test/simple_yaml.rb +1 -7
  49. data/test/test_helper.rb +5 -6
  50. metadata +54 -38
  51. data/Rakefile +0 -61
  52. data/ext/lda-ruby/Makefile +0 -181
  53. data/test/data/.gitignore +0 -2
  54. data/test/simple_test.rb +0 -26
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "rbconfig"
5
+
6
+ require_relative "../../lib/lda-ruby/rust_build_policy"
7
+
8
+ module Lda
9
+ module RustExtensionBuild
10
+ module_function
11
+
12
+ def run
13
+ policy = RustBuildPolicy.resolve
14
+ puts("Rust extension build policy: #{policy} (#{RustBuildPolicy::ENV_KEY})")
15
+
16
+ case policy
17
+ when RustBuildPolicy::NEVER
18
+ puts("Skipping Rust extension build (policy=#{RustBuildPolicy::NEVER}).")
19
+ when RustBuildPolicy::ALWAYS
20
+ ensure_cargo_available!
21
+ build_and_stage!
22
+ else
23
+ if cargo_available?
24
+ build_and_stage!
25
+ else
26
+ puts("cargo not found; skipping Rust extension build (policy=#{RustBuildPolicy::AUTO}).")
27
+ end
28
+ end
29
+
30
+ write_noop_makefile
31
+ rescue StandardError => e
32
+ if policy == RustBuildPolicy::ALWAYS
33
+ abort("Rust extension build failed with #{RustBuildPolicy::ENV_KEY}=#{RustBuildPolicy::ALWAYS}: #{e.message}")
34
+ end
35
+
36
+ warn("Rust extension build skipped after error in auto mode: #{e.message}")
37
+ write_noop_makefile
38
+ end
39
+
40
+ def ensure_cargo_available!
41
+ return if cargo_available?
42
+
43
+ abort("cargo not found in PATH but #{RustBuildPolicy::ENV_KEY}=#{RustBuildPolicy::ALWAYS} was requested.")
44
+ end
45
+
46
+ def cargo_available?
47
+ cargo = ENV.fetch("CARGO", "cargo")
48
+ system(cargo, "--version", out: File::NULL, err: File::NULL)
49
+ end
50
+
51
+ def build_and_stage!
52
+ cargo = ENV.fetch("CARGO", "cargo")
53
+ Dir.chdir(__dir__) do
54
+ env = rust_build_env
55
+ success =
56
+ if env.empty?
57
+ system(cargo, "build", "--release")
58
+ else
59
+ system(env, cargo, "build", "--release")
60
+ end
61
+ success or raise "cargo build --release failed"
62
+ end
63
+
64
+ source = rust_cdylib_source
65
+ raise "Rust extension artifact not found at #{rust_cdylib_candidates.join(', ')}" unless source
66
+
67
+ destination = File.expand_path("../../lib/lda_ruby_rust.#{RbConfig::CONFIG.fetch('DLEXT')}", __dir__)
68
+ FileUtils.cp(source, destination)
69
+ puts("Staged Rust extension to #{destination}")
70
+ end
71
+
72
+ def rust_cdylib_source
73
+ rust_cdylib_candidates.find { |path| File.exist?(path) }
74
+ end
75
+
76
+ def rust_cdylib_candidates
77
+ rust_cdylib_filenames.map { |filename| File.join(__dir__, "target", "release", filename) }
78
+ end
79
+
80
+ def rust_cdylib_filenames
81
+ host_os = RbConfig::CONFIG.fetch("host_os")
82
+ case host_os
83
+ when /mswin|mingw|cygwin/
84
+ # On Windows cargo may emit either prefixed or unprefixed DLL names.
85
+ ["lda_ruby_rust.dll", "liblda_ruby_rust.dll"]
86
+ else
87
+ extension =
88
+ case host_os
89
+ when /darwin/
90
+ "dylib"
91
+ else
92
+ "so"
93
+ end
94
+
95
+ ["liblda_ruby_rust.#{extension}"]
96
+ end
97
+ end
98
+
99
+ def rust_build_env
100
+ host_os = RbConfig::CONFIG.fetch("host_os")
101
+ return {} unless host_os.match?(/darwin/)
102
+
103
+ dynamic_lookup_flag = "-C link-arg=-Wl,-undefined,dynamic_lookup"
104
+ existing = ENV.fetch("RUSTFLAGS", "")
105
+ merged =
106
+ if existing.include?(dynamic_lookup_flag)
107
+ existing
108
+ else
109
+ [existing, dynamic_lookup_flag].reject(&:empty?).join(" ")
110
+ end
111
+
112
+ { "RUSTFLAGS" => merged }
113
+ end
114
+
115
+ def write_noop_makefile
116
+ File.write(
117
+ File.join(__dir__, "Makefile"),
118
+ <<~MAKEFILE
119
+ all:
120
+ \t@echo "Rust extension handled by extconf.rb"
121
+
122
+ install:
123
+ \t@echo "Rust extension handled by extconf.rb"
124
+
125
+ clean:
126
+ \t@true
127
+
128
+ distclean: clean
129
+ MAKEFILE
130
+ )
131
+ end
132
+ end
133
+ end
134
+
135
+ Lda::RustExtensionBuild.run
@@ -0,0 +1,35 @@
1
+ #ifndef LDA_RUBY_BINDGEN_STRINGS_H
2
+ #define LDA_RUBY_BINDGEN_STRINGS_H
3
+
4
+ #include <string.h>
5
+
6
+ /*
7
+ * RubyInstaller headers may include <strings.h> on Windows, but Clang-based
8
+ * bindgen runs can miss that header in this environment. Provide compatibility
9
+ * aliases for bindgen preprocessing.
10
+ */
11
+ #if defined(_WIN32) && !defined(__MINGW32__)
12
+ #ifndef bzero
13
+ #define bzero(ptr, size) memset((ptr), 0, (size))
14
+ #endif
15
+ #ifndef bcmp
16
+ #define bcmp(a, b, n) memcmp((a), (b), (n))
17
+ #endif
18
+ #ifndef bcopy
19
+ #define bcopy(src, dst, n) memmove((dst), (src), (n))
20
+ #endif
21
+ #ifndef index
22
+ #define index strchr
23
+ #endif
24
+ #ifndef rindex
25
+ #define rindex strrchr
26
+ #endif
27
+ #ifndef strcasecmp
28
+ #define strcasecmp _stricmp
29
+ #endif
30
+ #ifndef strncasecmp
31
+ #define strncasecmp _strnicmp
32
+ #endif
33
+ #endif
34
+
35
+ #endif