rbs_rails 0.4.1 → 0.8.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 +4 -4
- data/.github/workflows/ci.yml +29 -0
- data/.gitignore +4 -1
- data/.gitmodules +0 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +2 -2
- data/README.md +28 -44
- 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/bin/setup +1 -0
- data/lib/rbs_rails.rb +4 -0
- data/lib/rbs_rails/active_record.rb +80 -46
- data/lib/rbs_rails/dependency_builder.rb +43 -0
- data/lib/rbs_rails/rake_task.rb +72 -0
- data/lib/rbs_rails/util.rb +25 -0
- data/lib/rbs_rails/version.rb +1 -1
- data/rbs_rails.gemspec +2 -2
- data/sig/fileutils.rbs +1 -0
- data/sig/rake.rbs +6 -0
- data/sig/rbs_rails/active_record.rbs +9 -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 +17 -48
- 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 -201
data/bin/setup
CHANGED
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,90 @@
|
|
1
1
|
module RbsRails
|
2
2
|
module ActiveRecord
|
3
|
-
|
4
|
-
|
3
|
+
|
4
|
+
def self.generatable?(klass)
|
5
|
+
return false if klass.abstract_class?
|
6
|
+
|
7
|
+
klass.connection.table_exists?(klass.table_name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.class_to_rbs(klass, dependencies: [])
|
11
|
+
Generator.new(klass, dependencies: dependencies).generate
|
5
12
|
end
|
6
13
|
|
7
14
|
class Generator
|
8
|
-
def initialize(klass)
|
15
|
+
def initialize(klass, dependencies:)
|
9
16
|
@klass = klass
|
17
|
+
@dependencies = dependencies
|
18
|
+
@klass_name = Util.module_name(klass)
|
19
|
+
|
20
|
+
namespaces = klass_name.split('::').tap{ |names| names.pop }
|
21
|
+
@dependencies << namespaces.join('::') unless namespaces.empty?
|
10
22
|
end
|
11
23
|
|
12
24
|
def generate
|
13
|
-
|
14
|
-
klass_decl,
|
15
|
-
relation_decl,
|
16
|
-
collection_proxy_decl,
|
17
|
-
].join("\n")
|
25
|
+
Util.format_rbs klass_decl
|
18
26
|
end
|
19
27
|
|
20
28
|
private def klass_decl
|
21
29
|
<<~RBS
|
22
30
|
#{header}
|
23
|
-
extend _ActiveRecord_Relation_ClassMethods[#{
|
31
|
+
extend _ActiveRecord_Relation_ClassMethods[#{klass_name}, #{relation_class_name}]
|
24
32
|
|
25
|
-
#{columns
|
26
|
-
#{associations
|
27
|
-
#{enum_instance_methods
|
28
|
-
#{enum_scope_methods(singleton: true)
|
29
|
-
#{scopes(singleton: true)
|
30
|
-
|
33
|
+
#{columns}
|
34
|
+
#{associations}
|
35
|
+
#{enum_instance_methods}
|
36
|
+
#{enum_scope_methods(singleton: true)}
|
37
|
+
#{scopes(singleton: true)}
|
38
|
+
|
39
|
+
#{relation_decl}
|
40
|
+
|
41
|
+
#{collection_proxy_decl}
|
42
|
+
|
43
|
+
#{footer}
|
31
44
|
RBS
|
32
45
|
end
|
33
46
|
|
34
47
|
private def relation_decl
|
35
48
|
<<~RBS
|
36
49
|
class #{relation_class_name} < ActiveRecord::Relation
|
37
|
-
include _ActiveRecord_Relation[#{
|
38
|
-
include Enumerable[#{
|
39
|
-
#{enum_scope_methods(singleton: false)
|
40
|
-
#{scopes(singleton: false)
|
50
|
+
include _ActiveRecord_Relation[#{klass_name}]
|
51
|
+
include Enumerable[#{klass_name}]
|
52
|
+
#{enum_scope_methods(singleton: false)}
|
53
|
+
#{scopes(singleton: false)}
|
41
54
|
end
|
42
55
|
RBS
|
43
56
|
end
|
44
57
|
|
45
58
|
private def collection_proxy_decl
|
46
59
|
<<~RBS
|
47
|
-
class
|
60
|
+
class ActiveRecord_Associations_CollectionProxy < ActiveRecord::Associations::CollectionProxy
|
48
61
|
end
|
49
62
|
RBS
|
50
63
|
end
|
51
64
|
|
52
|
-
|
53
65
|
private def header
|
54
|
-
|
55
|
-
|
56
|
-
|
66
|
+
namespace = +''
|
67
|
+
klass_name.split('::').map do |mod_name|
|
68
|
+
namespace += "::#{mod_name}"
|
69
|
+
mod_object = Object.const_get(namespace)
|
70
|
+
case mod_object
|
71
|
+
when Class
|
72
|
+
# @type var superclass: Class
|
73
|
+
superclass = _ = mod_object.superclass
|
74
|
+
superclass_name = Util.module_name(superclass)
|
75
|
+
@dependencies << superclass_name
|
76
|
+
|
77
|
+
"class #{mod_name} < #{superclass_name}"
|
78
|
+
when Module
|
79
|
+
"module #{mod_name}"
|
80
|
+
else
|
81
|
+
raise 'unreachable'
|
82
|
+
end
|
83
|
+
end.join("\n")
|
84
|
+
end
|
85
|
+
|
86
|
+
private def footer
|
87
|
+
"end\n" * klass_name.split('::').size
|
57
88
|
end
|
58
89
|
|
59
90
|
private def associations
|
@@ -67,7 +98,7 @@ module RbsRails
|
|
67
98
|
private def has_many
|
68
99
|
klass.reflect_on_all_associations(:has_many).map do |a|
|
69
100
|
singular_name = a.name.to_s.singularize
|
70
|
-
type = a.klass
|
101
|
+
type = Util.module_name(a.klass)
|
71
102
|
collection_type = "#{type}::ActiveRecord_Associations_CollectionProxy"
|
72
103
|
<<~RUBY.chomp
|
73
104
|
def #{a.name}: () -> #{collection_type}
|
@@ -80,7 +111,7 @@ module RbsRails
|
|
80
111
|
|
81
112
|
private def has_one
|
82
113
|
klass.reflect_on_all_associations(:has_one).map do |a|
|
83
|
-
type = a.polymorphic? ? 'untyped' : a.klass
|
114
|
+
type = a.polymorphic? ? 'untyped' : Util.module_name(a.klass)
|
84
115
|
type_optional = optional(type)
|
85
116
|
<<~RUBY.chomp
|
86
117
|
def #{a.name}: () -> #{type}
|
@@ -95,7 +126,7 @@ module RbsRails
|
|
95
126
|
|
96
127
|
private def belongs_to
|
97
128
|
klass.reflect_on_all_associations(:belongs_to).map do |a|
|
98
|
-
type = a.polymorphic? ? 'untyped' : a.klass
|
129
|
+
type = a.polymorphic? ? 'untyped' : Util.module_name(a.klass)
|
99
130
|
type_optional = optional(type)
|
100
131
|
<<~RUBY.chomp
|
101
132
|
def #{a.name}: () -> #{type}
|
@@ -154,6 +185,7 @@ module RbsRails
|
|
154
185
|
return [] unless ast
|
155
186
|
|
156
187
|
traverse(ast).map do |node|
|
188
|
+
# @type block: nil | Hash[untyped, untyped]
|
157
189
|
next unless node.type == :send
|
158
190
|
next unless node.children[0].nil?
|
159
191
|
next unless node.children[1] == :enum
|
@@ -192,6 +224,7 @@ module RbsRails
|
|
192
224
|
return '' unless ast
|
193
225
|
|
194
226
|
traverse(ast).map do |node|
|
227
|
+
# @type block: nil | String
|
195
228
|
next unless node.type == :send
|
196
229
|
next unless node.children[0].nil?
|
197
230
|
next unless node.children[1] == :scope
|
@@ -206,37 +239,42 @@ module RbsRails
|
|
206
239
|
next unless body_node.type == :block
|
207
240
|
|
208
241
|
args = args_to_type(body_node.children[1])
|
209
|
-
"def #{singleton ? 'self.' : ''}#{name}:
|
242
|
+
"def #{singleton ? 'self.' : ''}#{name}: #{args} -> #{relation_class_name}"
|
210
243
|
end.compact.join("\n")
|
211
244
|
end
|
212
245
|
|
213
246
|
private def args_to_type(args_node)
|
214
247
|
# @type var res: Array[String]
|
215
248
|
res = []
|
249
|
+
# @type var block: String?
|
250
|
+
block = nil
|
216
251
|
args_node.children.each do |node|
|
217
252
|
case node.type
|
218
253
|
when :arg
|
219
|
-
res << "untyped"
|
254
|
+
res << "untyped #{node.children[0]}"
|
220
255
|
when :optarg
|
221
|
-
res << "?untyped"
|
256
|
+
res << "?untyped #{node.children[0]}"
|
222
257
|
when :kwarg
|
223
258
|
res << "#{node.children[0]}: untyped"
|
224
259
|
when :kwoptarg
|
225
260
|
res << "?#{node.children[0]}: untyped"
|
261
|
+
when :restarg
|
262
|
+
res << "*untyped #{node.children[0]}"
|
263
|
+
when :kwrestarg
|
264
|
+
res << "**untyped #{node.children[0]}"
|
265
|
+
when :blockarg
|
266
|
+
block = " { (*untyped) -> untyped }"
|
226
267
|
else
|
227
268
|
raise "unexpected: #{node}"
|
228
269
|
end
|
229
270
|
end
|
230
|
-
res.join(", ")
|
271
|
+
"(#{res.join(", ")})#{block}"
|
231
272
|
end
|
232
273
|
|
233
274
|
private def parse_model_file
|
234
275
|
return @parse_model_file if defined?(@parse_model_file)
|
235
276
|
|
236
|
-
|
237
|
-
# @type var class_name: String
|
238
|
-
class_name = _ = klass.name
|
239
|
-
path = Rails.root.join('app/models/', class_name.underscore + '.rb')
|
277
|
+
path = Rails.root.join('app/models/', klass_name.underscore + '.rb')
|
240
278
|
return @parse_model_file = nil unless path.exist?
|
241
279
|
return [] unless path.exist?
|
242
280
|
|
@@ -257,7 +295,7 @@ module RbsRails
|
|
257
295
|
end
|
258
296
|
|
259
297
|
private def relation_class_name
|
260
|
-
"
|
298
|
+
"ActiveRecord_Relation"
|
261
299
|
end
|
262
300
|
|
263
301
|
private def columns
|
@@ -301,29 +339,25 @@ module RbsRails
|
|
301
339
|
when :string, :text, :citext, :uuid, :binary
|
302
340
|
'String'
|
303
341
|
when :datetime
|
304
|
-
|
305
|
-
# ActiveSupport::TimeWithZone.name
|
306
|
-
'Time'
|
342
|
+
'ActiveSupport::TimeWithZone'
|
307
343
|
when :boolean
|
308
|
-
"
|
344
|
+
"bool"
|
309
345
|
when :jsonb, :json
|
310
346
|
"untyped"
|
311
347
|
when :date
|
312
|
-
|
313
|
-
# Date.name
|
314
|
-
'untyped'
|
348
|
+
'Date'
|
315
349
|
when :time
|
316
350
|
'Time'
|
317
351
|
when :inet
|
318
352
|
"IPAddr"
|
319
353
|
else
|
320
|
-
|
354
|
+
# Unknown column type, give up
|
355
|
+
'untyped'
|
321
356
|
end
|
322
357
|
end
|
323
358
|
|
324
359
|
private
|
325
|
-
|
326
|
-
attr_reader :klass
|
360
|
+
attr_reader :klass, :klass_name
|
327
361
|
end
|
328
362
|
end
|
329
363
|
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,72 @@
|
|
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
|
+
::ActiveRecord::Base.descendants.each do |klass|
|
39
|
+
next unless RbsRails::ActiveRecord.generatable?(klass)
|
40
|
+
next if ignore_model_if&.call(klass)
|
41
|
+
|
42
|
+
path = signature_root_dir / "app/models/#{klass.name.underscore}.rbs"
|
43
|
+
path.dirname.mkpath
|
44
|
+
|
45
|
+
sig = RbsRails::ActiveRecord.class_to_rbs(klass, dependencies: dep_builder.deps)
|
46
|
+
path.write sig
|
47
|
+
end
|
48
|
+
|
49
|
+
if dep_rbs = dep_builder.build
|
50
|
+
signature_root_dir.join('model_dependencies.rbs').write(dep_rbs)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def def_generate_rbs_for_path_helpers
|
56
|
+
desc 'Generate RBS files for path helpers'
|
57
|
+
task("#{name}:generate_rbs_for_path_helpers": :environment) do
|
58
|
+
require 'rbs_rails'
|
59
|
+
|
60
|
+
out_path = signature_root_dir.join 'path_helpers.rbs'
|
61
|
+
rbs = RbsRails::PathHelpers.generate
|
62
|
+
out_path.write rbs
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private def setup_signature_root_dir!
|
67
|
+
@signature_root_dir ||= Rails.root / 'sig/rbs_rails'
|
68
|
+
@signature_root_dir = Pathname(@signature_root_dir)
|
69
|
+
@signature_root_dir.mkpath
|
70
|
+
end
|
71
|
+
end
|
72
|
+
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,7 +14,7 @@ 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.
|
@@ -26,5 +26,5 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.require_paths = ["lib"]
|
27
27
|
|
28
28
|
spec.add_runtime_dependency 'parser'
|
29
|
-
spec.add_runtime_dependency 'rbs', '>=
|
29
|
+
spec.add_runtime_dependency 'rbs', '>= 1'
|
30
30
|
end
|
data/sig/fileutils.rbs
CHANGED