t-ruby 0.0.1
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +221 -0
- data/bin/trc +6 -0
- data/lib/t_ruby/benchmark.rb +592 -0
- data/lib/t_ruby/bundler_integration.rb +569 -0
- data/lib/t_ruby/cache.rb +774 -0
- data/lib/t_ruby/cli.rb +106 -0
- data/lib/t_ruby/compiler.rb +299 -0
- data/lib/t_ruby/config.rb +53 -0
- data/lib/t_ruby/constraint_checker.rb +441 -0
- data/lib/t_ruby/declaration_generator.rb +298 -0
- data/lib/t_ruby/doc_generator.rb +474 -0
- data/lib/t_ruby/error_handler.rb +132 -0
- data/lib/t_ruby/generic_type_parser.rb +68 -0
- data/lib/t_ruby/intersection_type_parser.rb +30 -0
- data/lib/t_ruby/ir.rb +1301 -0
- data/lib/t_ruby/lsp_server.rb +994 -0
- data/lib/t_ruby/package_manager.rb +735 -0
- data/lib/t_ruby/parser.rb +245 -0
- data/lib/t_ruby/parser_combinator.rb +942 -0
- data/lib/t_ruby/rbs_generator.rb +71 -0
- data/lib/t_ruby/runtime_validator.rb +367 -0
- data/lib/t_ruby/smt_solver.rb +1076 -0
- data/lib/t_ruby/type_alias_registry.rb +102 -0
- data/lib/t_ruby/type_checker.rb +770 -0
- data/lib/t_ruby/type_erasure.rb +26 -0
- data/lib/t_ruby/type_inferencer.rb +580 -0
- data/lib/t_ruby/union_type_parser.rb +38 -0
- data/lib/t_ruby/version.rb +5 -0
- data/lib/t_ruby/watcher.rb +320 -0
- data/lib/t_ruby.rb +42 -0
- metadata +87 -0
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module TRuby
|
|
6
|
+
# Integrates T-Ruby type packages with Bundler/RubyGems ecosystem
|
|
7
|
+
class BundlerIntegration
|
|
8
|
+
TYPES_GROUP = :types
|
|
9
|
+
TYPE_SUFFIX = "-types"
|
|
10
|
+
GEMFILE = "Gemfile"
|
|
11
|
+
GEMFILE_LOCK = "Gemfile.lock"
|
|
12
|
+
|
|
13
|
+
attr_reader :project_dir, :errors
|
|
14
|
+
|
|
15
|
+
def initialize(project_dir: ".")
|
|
16
|
+
@project_dir = project_dir
|
|
17
|
+
@errors = []
|
|
18
|
+
@type_gems = {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Check if project uses Bundler
|
|
22
|
+
def bundler_project?
|
|
23
|
+
File.exist?(gemfile_path)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Initialize T-Ruby types support in existing Bundler project
|
|
27
|
+
def init
|
|
28
|
+
unless bundler_project?
|
|
29
|
+
@errors << "No Gemfile found. Run 'bundle init' first."
|
|
30
|
+
return false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
add_types_group_to_gemfile unless types_group_exists?
|
|
34
|
+
create_types_directory
|
|
35
|
+
true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Find type packages for installed gems
|
|
39
|
+
def discover_type_packages
|
|
40
|
+
return {} unless bundler_project?
|
|
41
|
+
|
|
42
|
+
installed_gems = parse_gemfile_lock
|
|
43
|
+
type_packages = {}
|
|
44
|
+
|
|
45
|
+
installed_gems.each do |gem_name, version|
|
|
46
|
+
type_gem = find_type_gem(gem_name)
|
|
47
|
+
type_packages[gem_name] = type_gem if type_gem
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
type_packages
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Add a type package dependency
|
|
54
|
+
def add_type_gem(gem_name, version: nil)
|
|
55
|
+
type_gem_name = "#{gem_name}#{TYPE_SUFFIX}"
|
|
56
|
+
version_constraint = version || ">= 0"
|
|
57
|
+
|
|
58
|
+
append_to_gemfile(type_gem_name, version_constraint, group: TYPES_GROUP)
|
|
59
|
+
@type_gems[gem_name] = { name: type_gem_name, version: version_constraint }
|
|
60
|
+
|
|
61
|
+
{ gem: type_gem_name, version: version_constraint, status: :added }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Remove a type package dependency
|
|
65
|
+
def remove_type_gem(gem_name)
|
|
66
|
+
type_gem_name = "#{gem_name}#{TYPE_SUFFIX}"
|
|
67
|
+
remove_from_gemfile(type_gem_name)
|
|
68
|
+
@type_gems.delete(gem_name)
|
|
69
|
+
|
|
70
|
+
{ gem: type_gem_name, status: :removed }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Sync type definitions from installed type gems
|
|
74
|
+
def sync_types
|
|
75
|
+
return { synced: [], errors: @errors } unless bundler_project?
|
|
76
|
+
|
|
77
|
+
synced = []
|
|
78
|
+
type_gems = find_installed_type_gems
|
|
79
|
+
|
|
80
|
+
type_gems.each do |gem_info|
|
|
81
|
+
result = sync_gem_types(gem_info)
|
|
82
|
+
synced << result if result[:success]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
{ synced: synced, errors: @errors }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Generate a .trb-bundle.json manifest for compatibility
|
|
89
|
+
def generate_bundle_manifest
|
|
90
|
+
manifest = {
|
|
91
|
+
bundler_integration: true,
|
|
92
|
+
version: TRuby::VERSION,
|
|
93
|
+
types_group: TYPES_GROUP.to_s,
|
|
94
|
+
type_gems: list_type_gems,
|
|
95
|
+
local_types: list_local_types,
|
|
96
|
+
generated_at: Time.now.iso8601
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
manifest_path = File.join(@project_dir, ".trb-bundle.json")
|
|
100
|
+
File.write(manifest_path, JSON.pretty_generate(manifest))
|
|
101
|
+
manifest_path
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Load type definitions from Bundler-managed gems
|
|
105
|
+
def load_bundled_types
|
|
106
|
+
type_definitions = {}
|
|
107
|
+
|
|
108
|
+
find_installed_type_gems.each do |gem_info|
|
|
109
|
+
defs = load_gem_type_definitions(gem_info)
|
|
110
|
+
type_definitions.merge!(defs)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Also load local types
|
|
114
|
+
local_types = load_local_type_definitions
|
|
115
|
+
type_definitions.merge!(local_types)
|
|
116
|
+
|
|
117
|
+
type_definitions
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Check compatibility between gem version and type version
|
|
121
|
+
def check_version_compatibility
|
|
122
|
+
issues = []
|
|
123
|
+
gemfile_lock = parse_gemfile_lock
|
|
124
|
+
|
|
125
|
+
@type_gems.each do |base_gem, type_info|
|
|
126
|
+
base_version = gemfile_lock[base_gem]
|
|
127
|
+
type_version = gemfile_lock[type_info[:name]]
|
|
128
|
+
|
|
129
|
+
next unless base_version && type_version
|
|
130
|
+
|
|
131
|
+
unless versions_compatible?(base_version, type_version)
|
|
132
|
+
issues << {
|
|
133
|
+
gem: base_gem,
|
|
134
|
+
gem_version: base_version,
|
|
135
|
+
type_gem: type_info[:name],
|
|
136
|
+
type_version: type_version,
|
|
137
|
+
message: "Version mismatch: #{base_gem}@#{base_version} vs #{type_info[:name]}@#{type_version}"
|
|
138
|
+
}
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
issues
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Create a new type gem scaffold
|
|
146
|
+
def create_type_gem_scaffold(gem_name, output_dir: nil)
|
|
147
|
+
type_gem_name = "#{gem_name}#{TYPE_SUFFIX}"
|
|
148
|
+
output = output_dir || File.join(@project_dir, type_gem_name)
|
|
149
|
+
|
|
150
|
+
FileUtils.mkdir_p(output)
|
|
151
|
+
FileUtils.mkdir_p(File.join(output, "lib", type_gem_name.gsub("-", "_")))
|
|
152
|
+
FileUtils.mkdir_p(File.join(output, "sig"))
|
|
153
|
+
|
|
154
|
+
# Create gemspec
|
|
155
|
+
create_type_gemspec(type_gem_name, gem_name, output)
|
|
156
|
+
|
|
157
|
+
# Create main type file
|
|
158
|
+
create_main_type_file(type_gem_name, gem_name, output)
|
|
159
|
+
|
|
160
|
+
# Create README
|
|
161
|
+
create_type_gem_readme(type_gem_name, gem_name, output)
|
|
162
|
+
|
|
163
|
+
{ path: output, gem_name: type_gem_name, status: :created }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
def gemfile_path
|
|
169
|
+
File.join(@project_dir, GEMFILE)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def gemfile_lock_path
|
|
173
|
+
File.join(@project_dir, GEMFILE_LOCK)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def types_group_exists?
|
|
177
|
+
return false unless File.exist?(gemfile_path)
|
|
178
|
+
|
|
179
|
+
content = File.read(gemfile_path)
|
|
180
|
+
content.include?("group :#{TYPES_GROUP}") || content.include?("group :types")
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def add_types_group_to_gemfile
|
|
184
|
+
content = File.read(gemfile_path)
|
|
185
|
+
|
|
186
|
+
types_group = <<~RUBY
|
|
187
|
+
|
|
188
|
+
# T-Ruby type definitions
|
|
189
|
+
group :types do
|
|
190
|
+
# Add type gems here, e.g.:
|
|
191
|
+
# gem 'rails-types', '~> 7.0'
|
|
192
|
+
end
|
|
193
|
+
RUBY
|
|
194
|
+
|
|
195
|
+
File.write(gemfile_path, content + types_group)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def create_types_directory
|
|
199
|
+
types_dir = File.join(@project_dir, "types")
|
|
200
|
+
FileUtils.mkdir_p(types_dir)
|
|
201
|
+
|
|
202
|
+
# Create a sample .d.trb file
|
|
203
|
+
sample_path = File.join(types_dir, "custom.d.trb")
|
|
204
|
+
unless File.exist?(sample_path)
|
|
205
|
+
File.write(sample_path, <<~TRB)
|
|
206
|
+
# Custom type definitions for your project
|
|
207
|
+
# These types are available throughout your T-Ruby code
|
|
208
|
+
|
|
209
|
+
# Example type alias
|
|
210
|
+
# type UserId = String
|
|
211
|
+
|
|
212
|
+
# Example interface
|
|
213
|
+
# interface Serializable
|
|
214
|
+
# to_json: String
|
|
215
|
+
# from_json: (String) -> self
|
|
216
|
+
# end
|
|
217
|
+
TRB
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def append_to_gemfile(gem_name, version, group:)
|
|
222
|
+
content = File.read(gemfile_path)
|
|
223
|
+
|
|
224
|
+
# Find the types group and add gem there
|
|
225
|
+
if content.include?("group :#{group}")
|
|
226
|
+
# Add inside existing group
|
|
227
|
+
new_content = content.gsub(
|
|
228
|
+
/(group :#{group}.*?do\s*\n)/m,
|
|
229
|
+
"\\1 gem '#{gem_name}', '#{version}'\n"
|
|
230
|
+
)
|
|
231
|
+
File.write(gemfile_path, new_content)
|
|
232
|
+
else
|
|
233
|
+
# Create group with gem
|
|
234
|
+
File.write(gemfile_path, content + <<~RUBY)
|
|
235
|
+
|
|
236
|
+
group :#{group} do
|
|
237
|
+
gem '#{gem_name}', '#{version}'
|
|
238
|
+
end
|
|
239
|
+
RUBY
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def remove_from_gemfile(gem_name)
|
|
244
|
+
content = File.read(gemfile_path)
|
|
245
|
+
new_content = content.gsub(/^\s*gem ['"]#{gem_name}['"].*$\n?/, "")
|
|
246
|
+
File.write(gemfile_path, new_content)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def parse_gemfile_lock
|
|
250
|
+
return {} unless File.exist?(gemfile_lock_path)
|
|
251
|
+
|
|
252
|
+
gems = {}
|
|
253
|
+
in_specs = false
|
|
254
|
+
|
|
255
|
+
File.readlines(gemfile_lock_path).each do |line|
|
|
256
|
+
if line.strip == "specs:"
|
|
257
|
+
in_specs = true
|
|
258
|
+
next
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
if in_specs && line.match?(/^\s{4}(\S+)\s+\((.+)\)/)
|
|
262
|
+
match = line.match(/^\s{4}(\S+)\s+\((.+)\)/)
|
|
263
|
+
gems[match[1]] = match[2]
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
in_specs = false if in_specs && !line.start_with?(" ")
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
gems
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def find_type_gem(gem_name)
|
|
273
|
+
type_gem_name = "#{gem_name}#{TYPE_SUFFIX}"
|
|
274
|
+
|
|
275
|
+
# Check if type gem exists in known registries
|
|
276
|
+
# This is a simplified check - in production would query RubyGems API
|
|
277
|
+
{
|
|
278
|
+
name: type_gem_name,
|
|
279
|
+
available: check_gem_availability(type_gem_name)
|
|
280
|
+
}
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def check_gem_availability(gem_name)
|
|
284
|
+
# Simplified availability check
|
|
285
|
+
# In production, would use: Gem::SpecFetcher.fetcher.detect(:latest)
|
|
286
|
+
# For now, return based on common type packages
|
|
287
|
+
common_type_gems = %w[
|
|
288
|
+
rails-types
|
|
289
|
+
activerecord-types
|
|
290
|
+
activesupport-types
|
|
291
|
+
rspec-types
|
|
292
|
+
sidekiq-types
|
|
293
|
+
redis-types
|
|
294
|
+
pg-types
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
common_type_gems.include?(gem_name)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def find_installed_type_gems
|
|
301
|
+
gems = parse_gemfile_lock
|
|
302
|
+
gems.select { |name, _| name.end_with?(TYPE_SUFFIX) }.map do |name, version|
|
|
303
|
+
base_gem = name.sub(/#{TYPE_SUFFIX}$/, "")
|
|
304
|
+
{
|
|
305
|
+
name: name,
|
|
306
|
+
base_gem: base_gem,
|
|
307
|
+
version: version,
|
|
308
|
+
path: find_gem_path(name, version)
|
|
309
|
+
}
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def find_gem_path(gem_name, version)
|
|
314
|
+
# Try to find gem in standard locations
|
|
315
|
+
possible_paths = [
|
|
316
|
+
File.join(ENV["GEM_HOME"] || "", "gems", "#{gem_name}-#{version}"),
|
|
317
|
+
File.join(Dir.home, ".gem", "ruby", "*", "gems", "#{gem_name}-#{version}"),
|
|
318
|
+
File.join(@project_dir, "vendor", "bundle", "**", "gems", "#{gem_name}-#{version}")
|
|
319
|
+
]
|
|
320
|
+
|
|
321
|
+
possible_paths.each do |pattern|
|
|
322
|
+
matches = Dir.glob(pattern)
|
|
323
|
+
return matches.first if matches.any?
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
nil
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def sync_gem_types(gem_info)
|
|
330
|
+
return { success: false, gem: gem_info[:name] } unless gem_info[:path]
|
|
331
|
+
|
|
332
|
+
# Look for type definitions in the gem
|
|
333
|
+
type_files = Dir.glob(File.join(gem_info[:path], "**", "*.d.trb"))
|
|
334
|
+
rbs_files = Dir.glob(File.join(gem_info[:path], "sig", "**", "*.rbs"))
|
|
335
|
+
|
|
336
|
+
target_dir = File.join(@project_dir, ".trb-types", gem_info[:name])
|
|
337
|
+
FileUtils.mkdir_p(target_dir)
|
|
338
|
+
|
|
339
|
+
copied = []
|
|
340
|
+
|
|
341
|
+
(type_files + rbs_files).each do |file|
|
|
342
|
+
target = File.join(target_dir, File.basename(file))
|
|
343
|
+
FileUtils.cp(file, target)
|
|
344
|
+
copied << target
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
{ success: true, gem: gem_info[:name], files: copied }
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def load_gem_type_definitions(gem_info)
|
|
351
|
+
definitions = {}
|
|
352
|
+
return definitions unless gem_info[:path]
|
|
353
|
+
|
|
354
|
+
type_files = Dir.glob(File.join(gem_info[:path], "**", "*.d.trb"))
|
|
355
|
+
|
|
356
|
+
type_files.each do |file|
|
|
357
|
+
content = File.read(file)
|
|
358
|
+
parsed = parse_type_definitions(content)
|
|
359
|
+
definitions.merge!(parsed)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
definitions
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def load_local_type_definitions
|
|
366
|
+
definitions = {}
|
|
367
|
+
types_dir = File.join(@project_dir, "types")
|
|
368
|
+
|
|
369
|
+
return definitions unless Dir.exist?(types_dir)
|
|
370
|
+
|
|
371
|
+
Dir.glob(File.join(types_dir, "**", "*.d.trb")).each do |file|
|
|
372
|
+
content = File.read(file)
|
|
373
|
+
parsed = parse_type_definitions(content)
|
|
374
|
+
definitions.merge!(parsed)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
definitions
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def parse_type_definitions(content)
|
|
381
|
+
definitions = {}
|
|
382
|
+
|
|
383
|
+
# Parse type aliases
|
|
384
|
+
content.scan(/^\s*type\s+(\w+)\s*=\s*(.+)$/).each do |match|
|
|
385
|
+
definitions[match[0]] = { kind: :alias, definition: match[1] }
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# Parse interfaces
|
|
389
|
+
content.scan(/^\s*interface\s+(\w+)/).each do |match|
|
|
390
|
+
definitions[match[0]] = { kind: :interface }
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
definitions
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def list_type_gems
|
|
397
|
+
find_installed_type_gems.map do |gem_info|
|
|
398
|
+
{
|
|
399
|
+
name: gem_info[:name],
|
|
400
|
+
base_gem: gem_info[:base_gem],
|
|
401
|
+
version: gem_info[:version]
|
|
402
|
+
}
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def list_local_types
|
|
407
|
+
types_dir = File.join(@project_dir, "types")
|
|
408
|
+
return [] unless Dir.exist?(types_dir)
|
|
409
|
+
|
|
410
|
+
Dir.glob(File.join(types_dir, "**", "*.d.trb")).map do |file|
|
|
411
|
+
File.basename(file)
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def versions_compatible?(gem_version, type_version)
|
|
416
|
+
# Check if major.minor versions match
|
|
417
|
+
gem_parts = gem_version.split(".")
|
|
418
|
+
type_parts = type_version.split(".")
|
|
419
|
+
|
|
420
|
+
gem_parts[0] == type_parts[0] && gem_parts[1] == type_parts[1]
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def create_type_gemspec(type_gem_name, base_gem, output_dir)
|
|
424
|
+
gemspec_content = <<~RUBY
|
|
425
|
+
# frozen_string_literal: true
|
|
426
|
+
|
|
427
|
+
Gem::Specification.new do |spec|
|
|
428
|
+
spec.name = "#{type_gem_name}"
|
|
429
|
+
spec.version = "0.1.0"
|
|
430
|
+
spec.authors = ["Your Name"]
|
|
431
|
+
spec.email = ["your.email@example.com"]
|
|
432
|
+
|
|
433
|
+
spec.summary = "T-Ruby type definitions for #{base_gem}"
|
|
434
|
+
spec.description = "Type definitions for #{base_gem} to be used with T-Ruby"
|
|
435
|
+
spec.homepage = "https://github.com/your-username/#{type_gem_name}"
|
|
436
|
+
spec.license = "MIT"
|
|
437
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
438
|
+
|
|
439
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
440
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
441
|
+
spec.metadata["changelog_uri"] = "\#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
442
|
+
|
|
443
|
+
spec.files = Dir.glob("{lib,sig}/**/*") + %w[README.md LICENSE.txt]
|
|
444
|
+
spec.require_paths = ["lib"]
|
|
445
|
+
|
|
446
|
+
# Match the base gem version
|
|
447
|
+
spec.add_dependency "#{base_gem}"
|
|
448
|
+
end
|
|
449
|
+
RUBY
|
|
450
|
+
|
|
451
|
+
File.write(File.join(output_dir, "#{type_gem_name}.gemspec"), gemspec_content)
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
def create_main_type_file(type_gem_name, base_gem, output_dir)
|
|
455
|
+
module_name = type_gem_name.gsub("-", "_").split("_").map(&:capitalize).join
|
|
456
|
+
lib_dir = File.join(output_dir, "lib", type_gem_name.gsub("-", "_"))
|
|
457
|
+
|
|
458
|
+
main_file = <<~RUBY
|
|
459
|
+
# frozen_string_literal: true
|
|
460
|
+
|
|
461
|
+
# Type definitions for #{base_gem}
|
|
462
|
+
# Auto-generated scaffold - customize as needed
|
|
463
|
+
|
|
464
|
+
module #{module_name}
|
|
465
|
+
VERSION = "0.1.0"
|
|
466
|
+
end
|
|
467
|
+
RUBY
|
|
468
|
+
|
|
469
|
+
File.write(File.join(lib_dir, "version.rb"), main_file)
|
|
470
|
+
|
|
471
|
+
# Create types directory and sample file
|
|
472
|
+
types_dir = File.join(output_dir, "sig")
|
|
473
|
+
FileUtils.mkdir_p(types_dir)
|
|
474
|
+
|
|
475
|
+
types_file = <<~TRB
|
|
476
|
+
# Type definitions for #{base_gem}
|
|
477
|
+
# Add your type definitions here
|
|
478
|
+
|
|
479
|
+
# Example:
|
|
480
|
+
# interface #{base_gem.capitalize}Client
|
|
481
|
+
# connect: (String) -> Boolean
|
|
482
|
+
# disconnect: () -> void
|
|
483
|
+
# end
|
|
484
|
+
TRB
|
|
485
|
+
|
|
486
|
+
File.write(File.join(types_dir, "#{base_gem}.d.trb"), types_file)
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def create_type_gem_readme(type_gem_name, base_gem, output_dir)
|
|
490
|
+
readme = <<~MARKDOWN
|
|
491
|
+
# #{type_gem_name}
|
|
492
|
+
|
|
493
|
+
T-Ruby type definitions for [#{base_gem}](https://rubygems.org/gems/#{base_gem}).
|
|
494
|
+
|
|
495
|
+
## Installation
|
|
496
|
+
|
|
497
|
+
Add this line to your Gemfile:
|
|
498
|
+
|
|
499
|
+
```ruby
|
|
500
|
+
group :types do
|
|
501
|
+
gem '#{type_gem_name}'
|
|
502
|
+
end
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Then run:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
bundle install
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
## Usage
|
|
512
|
+
|
|
513
|
+
The type definitions will be automatically loaded by T-Ruby when compiling your `.trb` files.
|
|
514
|
+
|
|
515
|
+
## Contributing
|
|
516
|
+
|
|
517
|
+
Bug reports and pull requests are welcome.
|
|
518
|
+
|
|
519
|
+
## License
|
|
520
|
+
|
|
521
|
+
MIT License
|
|
522
|
+
MARKDOWN
|
|
523
|
+
|
|
524
|
+
File.write(File.join(output_dir, "README.md"), readme)
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
# Extension to PackageManager for Bundler support
|
|
529
|
+
class PackageManager
|
|
530
|
+
attr_reader :bundler
|
|
531
|
+
|
|
532
|
+
def initialize(project_dir: ".")
|
|
533
|
+
@project_dir = project_dir
|
|
534
|
+
@manifest = PackageManifest.load(File.join(project_dir, PackageManifest::MANIFEST_FILE))
|
|
535
|
+
@registry = PackageRegistry.new(local_path: File.join(project_dir, ".trb-packages"))
|
|
536
|
+
@resolver = DependencyResolver.new(@registry)
|
|
537
|
+
@bundler = BundlerIntegration.new(project_dir: project_dir)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
# Use Bundler if available, fall back to native package management
|
|
541
|
+
def install_with_bundler_fallback
|
|
542
|
+
if @bundler.bundler_project?
|
|
543
|
+
@bundler.sync_types
|
|
544
|
+
else
|
|
545
|
+
install
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
# Migrate from native T-Ruby packages to Bundler
|
|
550
|
+
def migrate_to_bundler
|
|
551
|
+
return { success: false, error: "Not a Bundler project" } unless @bundler.bundler_project?
|
|
552
|
+
|
|
553
|
+
migrated = []
|
|
554
|
+
|
|
555
|
+
# Read existing T-Ruby manifest
|
|
556
|
+
if @manifest
|
|
557
|
+
@manifest.dependencies.each do |name, version|
|
|
558
|
+
result = @bundler.add_type_gem(name, version: version)
|
|
559
|
+
migrated << result
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
# Generate new bundle manifest
|
|
564
|
+
@bundler.generate_bundle_manifest
|
|
565
|
+
|
|
566
|
+
{ success: true, migrated: migrated }
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
end
|