t-ruby 0.0.36 → 0.0.37

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: af10457f1fd4a0f0f3290c1cfead779eeca70c90cf81fa18320532e8c079a529
4
+ data.tar.gz: 15f674d20a11d3bb305198822fdd398447c9a7915377c2000eb480b4ab92318c
5
5
  SHA512:
6
- metadata.gz: 55d2c62ad5fd9071da3bfd363fad13e14d08f340c9ed8b19ab1c5b677db4a4e583dc8f183f67006efbfa44a2fbd1e0c77f14c6dd7af39b6c2d745978b938dcdf
7
- data.tar.gz: 93d96b65f461f68dc3833a1be714b51a616edefc1b01e97d56016cddbba7a48843f098efa49634d3a55043c8ffca21bd47cad660200cd63fd75e5deb92a2811e
6
+ metadata.gz: c0f281354fe79043c23ce37760c8a177bcab6de92d135d499ae3757b27e0944daed045fc3ba3334b32ab82a0ea16e028b0076afb0d8964238b042f644f5a1e5c
7
+ data.tar.gz: 5f91cac0ffedca7444fb68e4974435a6c01157ec0373b7ebe430e240643802e946c02fb7d91da147e823db17982b342be8e5ca03b74c1ca8263312227da82d78
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.37"
5
5
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+ require "uri"
6
+
7
+ module TRuby
8
+ class VersionChecker
9
+ GEM_NAME = "t-ruby"
10
+ RUBYGEMS_API = "https://rubygems.org/api/v1/gems/#{GEM_NAME}.json".freeze
11
+
12
+ def self.check
13
+ new.check
14
+ end
15
+
16
+ def self.update
17
+ new.update
18
+ end
19
+
20
+ def check
21
+ latest = fetch_latest_version
22
+ return nil unless latest
23
+
24
+ current = Gem::Version.new(VERSION)
25
+ latest_version = Gem::Version.new(latest)
26
+
27
+ return nil if current >= latest_version
28
+
29
+ { current: VERSION, latest: latest }
30
+ end
31
+
32
+ def update
33
+ system("gem install #{GEM_NAME}")
34
+ end
35
+
36
+ private
37
+
38
+ def fetch_latest_version
39
+ uri = URI(RUBYGEMS_API)
40
+ response = Net::HTTP.get_response(uri)
41
+
42
+ return nil unless response.is_a?(Net::HTTPSuccess)
43
+
44
+ data = JSON.parse(response.body)
45
+ data["version"]
46
+ rescue StandardError
47
+ nil
48
+ end
49
+ end
50
+ 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.37
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: