rbs_rails 0.4.0 → 0.8.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/.gitattributes +1 -0
- data/.github/workflows/ci.yml +18 -0
- data/.gitignore +3 -1
- data/.gitmodules +0 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +2 -2
- data/README.md +24 -43
- data/Rakefile +2 -1
- data/Steepfile +12 -1
- data/bin/add-type-params.rb +2 -1
- data/bin/gem_rbs +94 -0
- data/bin/postprocess.rb +1 -1
- data/bin/rbs +29 -2
- data/bin/rbs-prototype-rb.rb +59 -6
- data/lib/rbs_rails.rb +4 -0
- data/lib/rbs_rails/active_record.rb +74 -46
- data/lib/rbs_rails/dependency_builder.rb +43 -0
- data/lib/rbs_rails/rake_task.rb +73 -0
- data/lib/rbs_rails/util.rb +25 -0
- data/lib/rbs_rails/version.rb +1 -1
- data/rbs_rails.gemspec +3 -2
- data/sig/fileutils.rbs +1 -0
- data/sig/rake.rbs +6 -0
- data/sig/rbs_rails/active_record.rbs +8 -2
- data/sig/rbs_rails/dependency_builder.rbs +9 -0
- data/sig/rbs_rails/rake_task.rbs +26 -0
- data/sig/rbs_rails/util.rbs +11 -0
- metadata +30 -46
- data/.travis.yml +0 -11
- data/assets/sig/action_controller.rbs +0 -49
- data/assets/sig/action_mailer.rbs +0 -8
- data/assets/sig/active_record.rbs +0 -137
- data/assets/sig/builtin.rbs +0 -7
- data/assets/sig/capybara.rbs +0 -14
- data/assets/sig/concurrent.rbs +0 -4
- data/assets/sig/erb.rbs +0 -4
- data/assets/sig/erubi.rbs +0 -4
- data/assets/sig/generated/actionpack.rbs +0 -11831
- data/assets/sig/generated/actionview.rbs +0 -10591
- data/assets/sig/generated/activejob.rbs +0 -1920
- data/assets/sig/generated/activemodel.rbs +0 -4214
- data/assets/sig/generated/activerecord-meta-programming.rbs +0 -98
- data/assets/sig/generated/activerecord.rbs +0 -24602
- data/assets/sig/generated/activesupport.rbs +0 -12613
- data/assets/sig/generated/railties.rbs +0 -4687
- data/assets/sig/i18n.rbs +0 -4
- data/assets/sig/libxml.rbs +0 -10
- data/assets/sig/minitest.rbs +0 -13
- data/assets/sig/nokogiri.rbs +0 -8
- data/assets/sig/patches/README.md +0 -4
- data/assets/sig/patches/for_actionpack.rbs +0 -74
- data/assets/sig/patches/for_actionview.rbs +0 -19
- data/assets/sig/patches/for_activemodel.rbs +0 -11
- data/assets/sig/patches/for_activerecord.rbs +0 -84
- data/assets/sig/patches/for_activesupport.rbs +0 -48
- data/assets/sig/patches/for_railties.rbs +0 -30
- data/assets/sig/pg.rbs +0 -5
- data/assets/sig/que.rbs +0 -4
- data/assets/sig/queue_classic.rbs +0 -4
- data/assets/sig/racc.rbs +0 -4
- data/assets/sig/rack-test.rbs +0 -6
- data/assets/sig/rack.rbs +0 -47
- data/assets/sig/rails.rbs +0 -14
- data/assets/sig/rdoc.rbs +0 -9
- data/assets/sig/sidekiq.rbs +0 -4
- data/assets/sig/sneakers.rbs +0 -4
- data/assets/sig/stdlib.rbs +0 -24
- data/assets/sig/sucker_punch.rbs +0 -4
- data/assets/sig/thor.rbs +0 -12
- data/assets/sig/tzinfo.rbs +0 -4
- data/bin/generate_rbs_from_rails_source_code.rb +0 -195
data/lib/rbs_rails.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
require 'parser/current'
|
2
|
+
require 'rbs'
|
3
|
+
require 'stringio'
|
2
4
|
|
3
5
|
require_relative "rbs_rails/version"
|
6
|
+
require_relative "rbs_rails/util"
|
4
7
|
require_relative 'rbs_rails/active_record'
|
5
8
|
require_relative 'rbs_rails/path_helpers'
|
9
|
+
require_relative 'rbs_rails/dependency_builder'
|
6
10
|
|
7
11
|
module RbsRails
|
8
12
|
class Error < StandardError; end
|
@@ -1,59 +1,84 @@
|
|
1
1
|
module RbsRails
|
2
2
|
module ActiveRecord
|
3
|
-
|
4
|
-
|
3
|
+
|
4
|
+
def self.class_to_rbs(klass, dependencies: [])
|
5
|
+
Generator.new(klass, dependencies: dependencies).generate
|
5
6
|
end
|
6
7
|
|
7
8
|
class Generator
|
8
|
-
def initialize(klass)
|
9
|
+
def initialize(klass, dependencies:)
|
9
10
|
@klass = klass
|
11
|
+
@dependencies = dependencies
|
12
|
+
@klass_name = Util.module_name(klass)
|
13
|
+
|
14
|
+
namespaces = klass_name.split('::').tap{ |names| names.pop }
|
15
|
+
@dependencies << namespaces.join('::') unless namespaces.empty?
|
10
16
|
end
|
11
17
|
|
12
18
|
def generate
|
13
|
-
|
14
|
-
klass_decl,
|
15
|
-
relation_decl,
|
16
|
-
collection_proxy_decl,
|
17
|
-
].join("\n")
|
19
|
+
Util.format_rbs klass_decl
|
18
20
|
end
|
19
21
|
|
20
22
|
private def klass_decl
|
21
23
|
<<~RBS
|
22
24
|
#{header}
|
23
|
-
extend _ActiveRecord_Relation_ClassMethods[#{
|
25
|
+
extend _ActiveRecord_Relation_ClassMethods[#{klass_name}, #{relation_class_name}]
|
24
26
|
|
25
|
-
#{columns
|
26
|
-
#{associations
|
27
|
-
#{enum_instance_methods
|
28
|
-
#{enum_scope_methods(singleton: true)
|
29
|
-
#{scopes(singleton: true)
|
30
|
-
|
27
|
+
#{columns}
|
28
|
+
#{associations}
|
29
|
+
#{enum_instance_methods}
|
30
|
+
#{enum_scope_methods(singleton: true)}
|
31
|
+
#{scopes(singleton: true)}
|
32
|
+
|
33
|
+
#{relation_decl}
|
34
|
+
|
35
|
+
#{collection_proxy_decl}
|
36
|
+
|
37
|
+
#{footer}
|
31
38
|
RBS
|
32
39
|
end
|
33
40
|
|
34
41
|
private def relation_decl
|
35
42
|
<<~RBS
|
36
43
|
class #{relation_class_name} < ActiveRecord::Relation
|
37
|
-
include _ActiveRecord_Relation[#{
|
38
|
-
include Enumerable[#{
|
39
|
-
#{enum_scope_methods(singleton: false)
|
40
|
-
#{scopes(singleton: false)
|
44
|
+
include _ActiveRecord_Relation[#{klass_name}]
|
45
|
+
include Enumerable[#{klass_name}]
|
46
|
+
#{enum_scope_methods(singleton: false)}
|
47
|
+
#{scopes(singleton: false)}
|
41
48
|
end
|
42
49
|
RBS
|
43
50
|
end
|
44
51
|
|
45
52
|
private def collection_proxy_decl
|
46
53
|
<<~RBS
|
47
|
-
class
|
54
|
+
class ActiveRecord_Associations_CollectionProxy < ActiveRecord::Associations::CollectionProxy
|
48
55
|
end
|
49
56
|
RBS
|
50
57
|
end
|
51
58
|
|
52
|
-
|
53
59
|
private def header
|
54
|
-
|
55
|
-
|
56
|
-
|
60
|
+
namespace = +''
|
61
|
+
klass_name.split('::').map do |mod_name|
|
62
|
+
namespace += "::#{mod_name}"
|
63
|
+
mod_object = Object.const_get(namespace)
|
64
|
+
case mod_object
|
65
|
+
when Class
|
66
|
+
# @type var superclass: Class
|
67
|
+
superclass = _ = mod_object.superclass
|
68
|
+
superclass_name = Util.module_name(superclass)
|
69
|
+
@dependencies << superclass_name
|
70
|
+
|
71
|
+
"class #{mod_name} < #{superclass_name}"
|
72
|
+
when Module
|
73
|
+
"module #{mod_name}"
|
74
|
+
else
|
75
|
+
raise 'unreachable'
|
76
|
+
end
|
77
|
+
end.join("\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
private def footer
|
81
|
+
"end\n" * klass_name.split('::').size
|
57
82
|
end
|
58
83
|
|
59
84
|
private def associations
|
@@ -67,7 +92,7 @@ module RbsRails
|
|
67
92
|
private def has_many
|
68
93
|
klass.reflect_on_all_associations(:has_many).map do |a|
|
69
94
|
singular_name = a.name.to_s.singularize
|
70
|
-
type = a.klass
|
95
|
+
type = Util.module_name(a.klass)
|
71
96
|
collection_type = "#{type}::ActiveRecord_Associations_CollectionProxy"
|
72
97
|
<<~RUBY.chomp
|
73
98
|
def #{a.name}: () -> #{collection_type}
|
@@ -80,7 +105,7 @@ module RbsRails
|
|
80
105
|
|
81
106
|
private def has_one
|
82
107
|
klass.reflect_on_all_associations(:has_one).map do |a|
|
83
|
-
type = a.polymorphic? ? 'untyped' : a.klass
|
108
|
+
type = a.polymorphic? ? 'untyped' : Util.module_name(a.klass)
|
84
109
|
type_optional = optional(type)
|
85
110
|
<<~RUBY.chomp
|
86
111
|
def #{a.name}: () -> #{type}
|
@@ -95,7 +120,7 @@ module RbsRails
|
|
95
120
|
|
96
121
|
private def belongs_to
|
97
122
|
klass.reflect_on_all_associations(:belongs_to).map do |a|
|
98
|
-
type = a.polymorphic? ? 'untyped' : a.klass
|
123
|
+
type = a.polymorphic? ? 'untyped' : Util.module_name(a.klass)
|
99
124
|
type_optional = optional(type)
|
100
125
|
<<~RUBY.chomp
|
101
126
|
def #{a.name}: () -> #{type}
|
@@ -154,6 +179,7 @@ module RbsRails
|
|
154
179
|
return [] unless ast
|
155
180
|
|
156
181
|
traverse(ast).map do |node|
|
182
|
+
# @type block: nil | Hash[untyped, untyped]
|
157
183
|
next unless node.type == :send
|
158
184
|
next unless node.children[0].nil?
|
159
185
|
next unless node.children[1] == :enum
|
@@ -192,6 +218,7 @@ module RbsRails
|
|
192
218
|
return '' unless ast
|
193
219
|
|
194
220
|
traverse(ast).map do |node|
|
221
|
+
# @type block: nil | String
|
195
222
|
next unless node.type == :send
|
196
223
|
next unless node.children[0].nil?
|
197
224
|
next unless node.children[1] == :scope
|
@@ -206,37 +233,42 @@ module RbsRails
|
|
206
233
|
next unless body_node.type == :block
|
207
234
|
|
208
235
|
args = args_to_type(body_node.children[1])
|
209
|
-
"def #{singleton ? 'self.' : ''}#{name}:
|
236
|
+
"def #{singleton ? 'self.' : ''}#{name}: #{args} -> #{relation_class_name}"
|
210
237
|
end.compact.join("\n")
|
211
238
|
end
|
212
239
|
|
213
240
|
private def args_to_type(args_node)
|
214
241
|
# @type var res: Array[String]
|
215
242
|
res = []
|
243
|
+
# @type var block: String?
|
244
|
+
block = nil
|
216
245
|
args_node.children.each do |node|
|
217
246
|
case node.type
|
218
247
|
when :arg
|
219
|
-
res << "untyped"
|
248
|
+
res << "untyped #{node.children[0]}"
|
220
249
|
when :optarg
|
221
|
-
res << "?untyped"
|
250
|
+
res << "?untyped #{node.children[0]}"
|
222
251
|
when :kwarg
|
223
252
|
res << "#{node.children[0]}: untyped"
|
224
253
|
when :kwoptarg
|
225
254
|
res << "?#{node.children[0]}: untyped"
|
255
|
+
when :restarg
|
256
|
+
res << "*untyped #{node.children[0]}"
|
257
|
+
when :kwrestarg
|
258
|
+
res << "**untyped #{node.children[0]}"
|
259
|
+
when :blockarg
|
260
|
+
block = " { (*untyped) -> untyped }"
|
226
261
|
else
|
227
262
|
raise "unexpected: #{node}"
|
228
263
|
end
|
229
264
|
end
|
230
|
-
res.join(", ")
|
265
|
+
"(#{res.join(", ")})#{block}"
|
231
266
|
end
|
232
267
|
|
233
268
|
private def parse_model_file
|
234
269
|
return @parse_model_file if defined?(@parse_model_file)
|
235
270
|
|
236
|
-
|
237
|
-
# @type var class_name: String
|
238
|
-
class_name = _ = klass.name
|
239
|
-
path = Rails.root.join('app/models/', class_name.underscore + '.rb')
|
271
|
+
path = Rails.root.join('app/models/', klass_name.underscore + '.rb')
|
240
272
|
return @parse_model_file = nil unless path.exist?
|
241
273
|
return [] unless path.exist?
|
242
274
|
|
@@ -257,7 +289,7 @@ module RbsRails
|
|
257
289
|
end
|
258
290
|
|
259
291
|
private def relation_class_name
|
260
|
-
"
|
292
|
+
"ActiveRecord_Relation"
|
261
293
|
end
|
262
294
|
|
263
295
|
private def columns
|
@@ -301,29 +333,25 @@ module RbsRails
|
|
301
333
|
when :string, :text, :citext, :uuid, :binary
|
302
334
|
'String'
|
303
335
|
when :datetime
|
304
|
-
|
305
|
-
# ActiveSupport::TimeWithZone.name
|
306
|
-
'Time'
|
336
|
+
'ActiveSupport::TimeWithZone'
|
307
337
|
when :boolean
|
308
|
-
"
|
338
|
+
"bool"
|
309
339
|
when :jsonb, :json
|
310
340
|
"untyped"
|
311
341
|
when :date
|
312
|
-
|
313
|
-
# Date.name
|
314
|
-
'untyped'
|
342
|
+
'Date'
|
315
343
|
when :time
|
316
344
|
'Time'
|
317
345
|
when :inet
|
318
346
|
"IPAddr"
|
319
347
|
else
|
320
|
-
|
348
|
+
# Unknown column type, give up
|
349
|
+
'untyped'
|
321
350
|
end
|
322
351
|
end
|
323
352
|
|
324
353
|
private
|
325
|
-
|
326
|
-
attr_reader :klass
|
354
|
+
attr_reader :klass, :klass_name
|
327
355
|
end
|
328
356
|
end
|
329
357
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RbsRails
|
2
|
+
class DependencyBuilder
|
3
|
+
attr_reader :deps
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@deps = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def build
|
10
|
+
dep_rbs = +""
|
11
|
+
done = Set.new(['ActiveRecord::Base', 'ActiveRecord', 'Object'])
|
12
|
+
deps.uniq!
|
13
|
+
while dep = deps.shift
|
14
|
+
next unless done.add?(dep)
|
15
|
+
|
16
|
+
case dep_object = Object.const_get(dep)
|
17
|
+
when Class
|
18
|
+
superclass = dep_object.superclass or raise
|
19
|
+
super_name = Util.module_name(superclass)
|
20
|
+
deps << super_name
|
21
|
+
dep_rbs << "class #{dep} < #{super_name} end\n"
|
22
|
+
when Module
|
23
|
+
dep_rbs << "module #{dep} end\n"
|
24
|
+
else
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
|
28
|
+
# push namespaces
|
29
|
+
namespaces = dep.split('::')
|
30
|
+
namespaces.pop
|
31
|
+
namespaces.inject('') do |base, name|
|
32
|
+
full_name = base.empty? ? name : [base, name].join('::')
|
33
|
+
deps << full_name
|
34
|
+
full_name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
unless dep_rbs.empty?
|
39
|
+
Util.format_rbs(dep_rbs)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
module RbsRails
|
5
|
+
class RakeTask < Rake::TaskLib
|
6
|
+
attr_accessor :ignore_model_if, :name, :signature_root_dir
|
7
|
+
|
8
|
+
def initialize(name = :rbs_rails, &block)
|
9
|
+
super()
|
10
|
+
|
11
|
+
@name = name
|
12
|
+
|
13
|
+
block.call(self) if block
|
14
|
+
|
15
|
+
setup_signature_root_dir!
|
16
|
+
|
17
|
+
def_generate_rbs_for_models
|
18
|
+
def_generate_rbs_for_path_helpers
|
19
|
+
def_all
|
20
|
+
end
|
21
|
+
|
22
|
+
def def_all
|
23
|
+
desc 'Run all tasks of rbs_rails'
|
24
|
+
|
25
|
+
deps = [:"#{name}:generate_rbs_for_models", :"#{name}:generate_rbs_for_path_helpers"]
|
26
|
+
task("#{name}:all": deps)
|
27
|
+
end
|
28
|
+
|
29
|
+
def def_generate_rbs_for_models
|
30
|
+
desc 'Generate RBS files for Active Record models'
|
31
|
+
task("#{name}:generate_rbs_for_models": :environment) do
|
32
|
+
require 'rbs_rails'
|
33
|
+
|
34
|
+
Rails.application.eager_load!
|
35
|
+
|
36
|
+
dep_builder = DependencyBuilder.new
|
37
|
+
|
38
|
+
# HACK: for steep
|
39
|
+
(_ = ::ActiveRecord::Base).descendants.each do |klass|
|
40
|
+
next if klass.abstract_class?
|
41
|
+
next if ignore_model_if&.call(klass)
|
42
|
+
|
43
|
+
path = signature_root_dir / "app/models/#{klass.name.underscore}.rbs"
|
44
|
+
path.dirname.mkpath
|
45
|
+
|
46
|
+
sig = RbsRails::ActiveRecord.class_to_rbs(klass, dependencies: dep_builder.deps)
|
47
|
+
path.write sig
|
48
|
+
end
|
49
|
+
|
50
|
+
if dep_rbs = dep_builder.build
|
51
|
+
signature_root_dir.join('model_dependencies.rbs').write(dep_rbs)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def def_generate_rbs_for_path_helpers
|
57
|
+
desc 'Generate RBS files for path helpers'
|
58
|
+
task("#{name}:generate_rbs_for_path_helpers": :environment) do
|
59
|
+
require 'rbs_rails'
|
60
|
+
|
61
|
+
out_path = signature_root_dir.join 'path_helpers.rbs'
|
62
|
+
rbs = RbsRails::PathHelpers.generate
|
63
|
+
out_path.write rbs
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private def setup_signature_root_dir!
|
68
|
+
@signature_root_dir ||= Rails.root / 'sig/rbs_rails'
|
69
|
+
@signature_root_dir = Pathname(@signature_root_dir)
|
70
|
+
@signature_root_dir.mkpath
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RbsRails
|
2
|
+
module Util
|
3
|
+
MODULE_NAME = Module.instance_method(:name)
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
if '2.7' <= RUBY_VERSION
|
8
|
+
def module_name(mod)
|
9
|
+
# HACK: RBS doesn't have UnboundMethod#bind_call
|
10
|
+
(_ = MODULE_NAME).bind_call(mod)
|
11
|
+
end
|
12
|
+
else
|
13
|
+
def module_name(mod)
|
14
|
+
MODULE_NAME.bind(mod).call
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def format_rbs(rbs)
|
19
|
+
decls = RBS::Parser.parse_signature(rbs)
|
20
|
+
StringIO.new.tap do |io|
|
21
|
+
RBS::Writer.new(out: io).write(decls)
|
22
|
+
end.string
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/rbs_rails/version.rb
CHANGED
data/rbs_rails.gemspec
CHANGED
@@ -14,16 +14,17 @@ Gem::Specification.new do |spec|
|
|
14
14
|
|
15
15
|
spec.metadata["homepage_uri"] = spec.homepage
|
16
16
|
spec.metadata["source_code_uri"] = spec.homepage
|
17
|
-
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/pocke/rbs_rails/blob/master/CHANGELOG.md"
|
18
18
|
|
19
19
|
# Specify which files should be added to the gem when it is released.
|
20
20
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
21
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
22
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|gem_rbs)/}) }
|
23
23
|
end
|
24
24
|
spec.bindir = "exe"
|
25
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
26
|
spec.require_paths = ["lib"]
|
27
27
|
|
28
28
|
spec.add_runtime_dependency 'parser'
|
29
|
+
spec.add_runtime_dependency 'rbs', '>= 1'
|
29
30
|
end
|
data/sig/fileutils.rbs
CHANGED
data/sig/rake.rbs
ADDED
@@ -1,11 +1,11 @@
|
|
1
1
|
module RbsRails::ActiveRecord
|
2
|
-
def self.class_to_rbs: (untyped klass) -> untyped
|
2
|
+
def self.class_to_rbs: (untyped klass, ?dependencies: Array[String]) -> untyped
|
3
3
|
end
|
4
4
|
|
5
5
|
class RbsRails::ActiveRecord::Generator
|
6
6
|
@parse_model_file: nil | Parser::AST::Node
|
7
7
|
|
8
|
-
def initialize: (singleton(ActiveRecord::Base) klass) -> untyped
|
8
|
+
def initialize: (singleton(ActiveRecord::Base) klass, dependencies: Array[String]) -> untyped
|
9
9
|
|
10
10
|
def generate: () -> String
|
11
11
|
|
@@ -17,6 +17,8 @@ class RbsRails::ActiveRecord::Generator
|
|
17
17
|
|
18
18
|
def header: () -> String
|
19
19
|
|
20
|
+
def footer: () -> String
|
21
|
+
|
20
22
|
def associations: () -> String
|
21
23
|
|
22
24
|
def has_many: () -> String
|
@@ -55,5 +57,9 @@ class RbsRails::ActiveRecord::Generator
|
|
55
57
|
|
56
58
|
def optional: (String) -> String
|
57
59
|
|
60
|
+
private
|
61
|
+
|
58
62
|
attr_reader klass: singleton(ActiveRecord::Base)
|
63
|
+
|
64
|
+
attr_reader klass_name: String
|
59
65
|
end
|