t-ruby 0.0.36 → 0.0.38

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8847929796ade811f3b9c3cd3ddcb86448e18927e40c2318e562dc9c35e312b
4
- data.tar.gz: 526a69c0dc752147ed3a29b208ebb85cd019a20b03efbd21ed28a25d5badc37b
3
+ metadata.gz: dc0d0297e719aa0061465983334438eeb0ce5d4230e5d1f363cbfe5d3def7b66
4
+ data.tar.gz: 898d406d4ce3b0a415312ee2473fff6e7983accd0227aed7030ba5eb396c868f
5
5
  SHA512:
6
- metadata.gz: 55d2c62ad5fd9071da3bfd363fad13e14d08f340c9ed8b19ab1c5b677db4a4e583dc8f183f67006efbfa44a2fbd1e0c77f14c6dd7af39b6c2d745978b938dcdf
7
- data.tar.gz: 93d96b65f461f68dc3833a1be714b51a616edefc1b01e97d56016cddbba7a48843f098efa49634d3a55043c8ffca21bd47cad660200cd63fd75e5deb92a2811e
6
+ metadata.gz: '00942ff35f9b6dc3824959cf42e72d35731e41f34156cb063eccc21c18686382fc1faf7b17e3e91caeb8910f2c5f6663fcae30877660e7c09e1e9d90f735ac97'
7
+ data.tar.gz: cd5893ac01d1c9e4e969e20739a1e21b2bcf174b5aac6ca54ab0cb52a0f41d411bff883b204b1669de513399e801533e83beba2f46b9e8cc4b1c6cb5aa17ba46
data/README.md CHANGED
@@ -9,11 +9,11 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <img src="https://img.shields.io/badge/CI-passing-brightgreen" alt="CI: passing" />
12
+ <a href="https://github.com/type-ruby/t-ruby/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/type-ruby/t-ruby/ci.yml?label=CI" alt="CI" /></a>
13
13
  <img src="https://img.shields.io/badge/ruby-3.0+-cc342d" alt="Ruby 3.0+" />
14
14
  <a href="https://rubygems.org/gems/t-ruby"><img src="https://img.shields.io/gem/v/t-ruby" alt="Gem Version" /></a>
15
15
  <img src="https://img.shields.io/gem/dt/t-ruby" alt="Downloads" />
16
- <img src="https://img.shields.io/badge/coverage-90%25-brightgreen" alt="Coverage: 90%" />
16
+ <a href="https://coveralls.io/github/type-ruby/t-ruby?branch=main"><img src="https://coveralls.io/repos/github/type-ruby/t-ruby/badge.svg?branch=main" alt="Coverage" /></a>
17
17
  </p>
18
18
 
19
19
  <p align="center">
data/lib/t_ruby/cli.rb CHANGED
@@ -13,7 +13,8 @@ module TRuby
13
13
  trc --watch, -w Watch input files and recompile on change
14
14
  trc --decl <file.trb> Generate .d.trb declaration file
15
15
  trc --lsp Start LSP server (for IDE integration)
16
- trc --version, -v Show version
16
+ trc update Update t-ruby to the latest version
17
+ trc --version, -v Show version (and check for updates)
17
18
  trc --help, -h Show this help
18
19
 
19
20
  Examples:
@@ -44,6 +45,12 @@ module TRuby
44
45
 
45
46
  if @args.include?("--version") || @args.include?("-v")
46
47
  puts "trc #{VERSION}"
48
+ check_for_updates
49
+ return
50
+ end
51
+
52
+ if @args.include?("update")
53
+ update_gem
47
54
  return
48
55
  end
49
56
 
@@ -78,6 +85,24 @@ module TRuby
78
85
 
79
86
  private
80
87
 
88
+ def check_for_updates
89
+ result = VersionChecker.check
90
+ return unless result
91
+
92
+ puts ""
93
+ puts "New version available: #{result[:latest]} (current: #{result[:current]})"
94
+ puts "Run 'trc update' to update"
95
+ end
96
+
97
+ def update_gem
98
+ puts "Updating t-ruby..."
99
+ if VersionChecker.update
100
+ puts "Successfully updated t-ruby!"
101
+ else
102
+ puts "Update failed. Try: gem install t-ruby"
103
+ end
104
+ end
105
+
81
106
  def init_project
82
107
  config_file = "trbconfig.yml"
83
108
  src_dir = "src"
@@ -105,7 +130,6 @@ module TRuby
105
130
  output:
106
131
  ruby_dir: #{build_dir}
107
132
  # rbs_dir: sig # Optional: separate directory for .rbs files
108
- preserve_structure: true
109
133
  # clean_before_build: false
110
134
 
111
135
  compiler:
@@ -38,29 +38,27 @@ module TRuby
38
38
  # Transform source to Ruby code
39
39
  output = @use_ir ? transform_with_ir(source, parser) : transform_legacy(source, parse_result)
40
40
 
41
- out_dir = @config.out_dir
42
- FileUtils.mkdir_p(out_dir)
43
-
44
- base_filename = File.basename(input_path, ".trb")
45
- output_path = File.join(out_dir, "#{base_filename}.rb")
41
+ # Compute output path (respects preserve_structure setting)
42
+ output_path = compute_output_path(input_path, @config.ruby_dir, ".rb")
43
+ FileUtils.mkdir_p(File.dirname(output_path))
46
44
 
47
45
  File.write(output_path, output)
48
46
 
49
47
  # Generate .rbs file if enabled in config
50
48
  if @config.compiler["generate_rbs"]
51
- rbs_dir = @config.rbs_dir
52
- FileUtils.mkdir_p(rbs_dir)
49
+ rbs_path = compute_output_path(input_path, @config.rbs_dir, ".rbs")
50
+ FileUtils.mkdir_p(File.dirname(rbs_path))
53
51
  if @use_ir && parser.ir_program
54
- generate_rbs_from_ir(base_filename, rbs_dir, parser.ir_program)
52
+ generate_rbs_from_ir_to_path(rbs_path, parser.ir_program)
55
53
  else
56
- generate_rbs_file(base_filename, rbs_dir, parse_result)
54
+ generate_rbs_file_to_path(rbs_path, parse_result)
57
55
  end
58
56
  end
59
57
 
60
58
  # Generate .d.trb file if enabled in config (legacy support)
61
59
  # TODO: Add compiler.generate_dtrb option in future
62
60
  if @config.compiler.key?("generate_dtrb") && @config.compiler["generate_dtrb"]
63
- generate_dtrb_file(input_path, out_dir)
61
+ generate_dtrb_file(input_path, @config.ruby_dir)
64
62
  end
65
63
 
66
64
  output_path
@@ -161,8 +159,70 @@ module TRuby
161
159
  @optimizer&.stats
162
160
  end
163
161
 
162
+ # Compute output path for a source file
163
+ # @param input_path [String] path to source file
164
+ # @param output_dir [String] base output directory
165
+ # @param new_extension [String] new file extension (e.g., ".rb", ".rbs")
166
+ # @return [String] computed output path (always preserves directory structure)
167
+ def compute_output_path(input_path, output_dir, new_extension)
168
+ relative = compute_relative_path(input_path)
169
+ base = relative.sub(/\.[^.]+$/, new_extension)
170
+ File.join(output_dir, base)
171
+ end
172
+
173
+ # Compute relative path from source directory
174
+ # @param input_path [String] path to source file
175
+ # @return [String] relative path preserving directory structure
176
+ def compute_relative_path(input_path)
177
+ # Use realpath to resolve symlinks (e.g., /var vs /private/var on macOS)
178
+ absolute_input = resolve_path(input_path)
179
+ source_dirs = @config.source_include
180
+
181
+ # Check if file is inside any source_include directory
182
+ if source_dirs.size > 1
183
+ # Multiple source directories: include the source dir name in output
184
+ # src/models/user.trb → src/models/user.trb
185
+ source_dirs.each do |src_dir|
186
+ absolute_src = resolve_path(src_dir)
187
+ next unless absolute_input.start_with?("#{absolute_src}/")
188
+
189
+ # Return path relative to parent of source dir (includes src dir name)
190
+ parent_of_src = File.dirname(absolute_src)
191
+ return absolute_input.sub("#{parent_of_src}/", "")
192
+ end
193
+ else
194
+ # Single source directory: exclude the source dir name from output
195
+ # src/models/user.trb → models/user.trb
196
+ src_dir = source_dirs.first
197
+ if src_dir
198
+ absolute_src = resolve_path(src_dir)
199
+ if absolute_input.start_with?("#{absolute_src}/")
200
+ return absolute_input.sub("#{absolute_src}/", "")
201
+ end
202
+ end
203
+ end
204
+
205
+ # File outside source directories: use path relative to current working directory
206
+ # external/foo.trb → external/foo.trb
207
+ cwd = resolve_path(".")
208
+ if absolute_input.start_with?("#{cwd}/")
209
+ return absolute_input.sub("#{cwd}/", "")
210
+ end
211
+
212
+ # Absolute path from outside cwd: use basename only
213
+ File.basename(input_path)
214
+ end
215
+
164
216
  private
165
217
 
218
+ # Resolve path to absolute path, following symlinks
219
+ # Falls back to expand_path if realpath fails (e.g., file doesn't exist yet)
220
+ def resolve_path(path)
221
+ File.realpath(path)
222
+ rescue Errno::ENOENT
223
+ File.expand_path(path)
224
+ end
225
+
166
226
  def setup_declaration_paths
167
227
  # Add default declaration paths
168
228
  @declaration_loader.add_search_path(@config.out_dir)
@@ -197,30 +257,29 @@ module TRuby
197
257
  end
198
258
  end
199
259
 
200
- # Generate RBS from IR
201
- def generate_rbs_from_ir(base_filename, out_dir, ir_program)
260
+ # Generate RBS from IR to a specific path
261
+ def generate_rbs_from_ir_to_path(rbs_path, ir_program)
202
262
  generator = IR::RBSGenerator.new
203
263
  rbs_content = generator.generate(ir_program)
204
-
205
- rbs_path = File.join(out_dir, "#{base_filename}.rbs")
206
264
  File.write(rbs_path, rbs_content) unless rbs_content.strip.empty?
207
265
  end
208
266
 
209
- # Legacy RBS generation
210
- def generate_rbs_file(base_filename, out_dir, parse_result)
267
+ # Legacy RBS generation to a specific path
268
+ def generate_rbs_file_to_path(rbs_path, parse_result)
211
269
  generator = RBSGenerator.new
212
270
  rbs_content = generator.generate(
213
271
  parse_result[:functions] || [],
214
272
  parse_result[:type_aliases] || []
215
273
  )
216
-
217
- rbs_path = File.join(out_dir, "#{base_filename}.rbs")
218
274
  File.write(rbs_path, rbs_content) unless rbs_content.empty?
219
275
  end
220
276
 
221
277
  def generate_dtrb_file(input_path, out_dir)
278
+ dtrb_path = compute_output_path(input_path, out_dir, DeclarationGenerator::DECLARATION_EXTENSION)
279
+ FileUtils.mkdir_p(File.dirname(dtrb_path))
280
+
222
281
  generator = DeclarationGenerator.new
223
- generator.generate_file(input_path, out_dir)
282
+ generator.generate_file_to_path(input_path, dtrb_path)
224
283
  end
225
284
 
226
285
  # Copy .rb file to output directory and generate .rbs signature
@@ -229,28 +288,25 @@ module TRuby
229
288
  raise ArgumentError, "File not found: #{input_path}"
230
289
  end
231
290
 
232
- out_dir = @config.out_dir
233
- FileUtils.mkdir_p(out_dir)
234
-
235
- base_filename = File.basename(input_path, ".rb")
236
- output_path = File.join(out_dir, "#{base_filename}.rb")
291
+ # Compute output path (respects preserve_structure setting)
292
+ output_path = compute_output_path(input_path, @config.ruby_dir, ".rb")
293
+ FileUtils.mkdir_p(File.dirname(output_path))
237
294
 
238
295
  # Copy the .rb file to output directory
239
296
  FileUtils.cp(input_path, output_path)
240
297
 
241
298
  # Generate .rbs file if enabled in config
242
299
  if @config.compiler["generate_rbs"]
243
- rbs_dir = @config.rbs_dir
244
- FileUtils.mkdir_p(rbs_dir)
245
- generate_rbs_from_ruby(base_filename, rbs_dir, input_path)
300
+ rbs_path = compute_output_path(input_path, @config.rbs_dir, ".rbs")
301
+ FileUtils.mkdir_p(File.dirname(rbs_path))
302
+ generate_rbs_from_ruby_to_path(rbs_path, input_path)
246
303
  end
247
304
 
248
305
  output_path
249
306
  end
250
307
 
251
- # Generate RBS from Ruby file using rbs prototype
252
- def generate_rbs_from_ruby(base_filename, out_dir, input_path)
253
- rbs_path = File.join(out_dir, "#{base_filename}.rbs")
308
+ # Generate RBS from Ruby file using rbs prototype to a specific path
309
+ def generate_rbs_from_ruby_to_path(rbs_path, input_path)
254
310
  result = `rbs prototype rb #{input_path} 2>/dev/null`
255
311
  File.write(rbs_path, result) unless result.strip.empty?
256
312
  end
data/lib/t_ruby/config.rb CHANGED
@@ -19,7 +19,6 @@ module TRuby
19
19
  "output" => {
20
20
  "ruby_dir" => "build",
21
21
  "rbs_dir" => nil,
22
- "preserve_structure" => true,
23
22
  "clean_before_build" => false,
24
23
  },
25
24
  "compiler" => {
@@ -72,12 +71,6 @@ module TRuby
72
71
  @output["rbs_dir"] || ruby_dir
73
72
  end
74
73
 
75
- # Check if source directory structure should be preserved in output
76
- # @return [Boolean] true if structure should be preserved
77
- def preserve_structure?
78
- @output["preserve_structure"] != false
79
- end
80
-
81
74
  # Check if output directory should be cleaned before build
82
75
  # @return [Boolean] true if should clean before build
83
76
  def clean_before_build?
@@ -64,6 +64,23 @@ module TRuby
64
64
  output_path
65
65
  end
66
66
 
67
+ # Generate declaration file to a specific output path
68
+ def generate_file_to_path(input_path, output_path)
69
+ unless File.exist?(input_path)
70
+ raise ArgumentError, "File not found: #{input_path}"
71
+ end
72
+
73
+ unless input_path.end_with?(".trb")
74
+ raise ArgumentError, "Expected .trb file, got: #{input_path}"
75
+ end
76
+
77
+ source = File.read(input_path)
78
+ content = generate(source)
79
+
80
+ File.write(output_path, content)
81
+ output_path
82
+ end
83
+
67
84
  private
68
85
 
69
86
  def generate_type_alias(type_alias)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TRuby
4
- VERSION = "0.0.36"
4
+ VERSION = "0.0.38"
5
5
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+ require "uri"
6
+ require "openssl"
7
+
8
+ module TRuby
9
+ class VersionChecker
10
+ GEM_NAME = "t-ruby"
11
+ RUBYGEMS_API = "https://rubygems.org/api/v1/gems/#{GEM_NAME}.json".freeze
12
+
13
+ def self.check
14
+ new.check
15
+ end
16
+
17
+ def self.update
18
+ new.update
19
+ end
20
+
21
+ def check
22
+ latest = fetch_latest_version
23
+ return nil unless latest
24
+
25
+ current = Gem::Version.new(VERSION)
26
+ latest_version = Gem::Version.new(latest)
27
+
28
+ return nil if current >= latest_version
29
+
30
+ { current: VERSION, latest: latest }
31
+ end
32
+
33
+ def update
34
+ system("gem install #{GEM_NAME}")
35
+ end
36
+
37
+ private
38
+
39
+ def fetch_latest_version
40
+ uri = URI(RUBYGEMS_API)
41
+
42
+ http = Net::HTTP.new(uri.host, uri.port)
43
+ http.use_ssl = true
44
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
45
+ http.verify_callback = ->(_preverify_ok, _store_ctx) { true } # Skip CRL check
46
+ http.open_timeout = 3
47
+ http.read_timeout = 3
48
+
49
+ request = Net::HTTP::Get.new(uri)
50
+ response = http.request(request)
51
+
52
+ return nil unless response.is_a?(Net::HTTPSuccess)
53
+
54
+ data = JSON.parse(response.body)
55
+ data["version"]
56
+ rescue StandardError
57
+ nil
58
+ end
59
+ end
60
+ end
data/lib/t_ruby.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "t_ruby/version"
4
+ require_relative "t_ruby/version_checker"
4
5
  require_relative "t_ruby/config"
5
6
 
6
7
  # Core infrastructure (must be loaded first)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: t-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.36
4
+ version: 0.0.38
5
5
  platform: ruby
6
6
  authors:
7
7
  - Y. Fred Kim
@@ -65,6 +65,7 @@ files:
65
65
  - lib/t_ruby/type_inferencer.rb
66
66
  - lib/t_ruby/union_type_parser.rb
67
67
  - lib/t_ruby/version.rb
68
+ - lib/t_ruby/version_checker.rb
68
69
  - lib/t_ruby/watcher.rb
69
70
  homepage: https://type-ruby.github.io
70
71
  licenses: