tapioca 0.4.5 → 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +9 -7
- data/lib/tapioca/cli.rb +8 -1
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +10 -10
- data/lib/tapioca/compilers/dsl/active_record_identity_cache.rb +2 -2
- data/lib/tapioca/compilers/dsl/base.rb +14 -10
- data/lib/tapioca/compilers/dsl_compiler.rb +5 -5
- data/lib/tapioca/compilers/requires_compiler.rb +34 -6
- data/lib/tapioca/compilers/sorbet.rb +1 -1
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +12 -5
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +1 -1
- data/lib/tapioca/compilers/symbol_table_compiler.rb +1 -1
- data/lib/tapioca/compilers/todos_compiler.rb +1 -1
- data/lib/tapioca/core_ext/class.rb +8 -3
- data/lib/tapioca/gemfile.rb +1 -1
- data/lib/tapioca/generator.rb +1 -1
- data/lib/tapioca/loader.rb +1 -1
- data/lib/tapioca/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db90e001ad45dbd8edd11b2288405dd406588e1bb46120c7cd5f0cb560647ea6
|
4
|
+
data.tar.gz: 1eab20c32a1785046f814ca2fc54866bbc0c9d083bf682a8b1ea15bf92c9cc1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77d4598557fc72b8a76dea56b626d05a3f91dd88d53cb0e96156ed92419534a32beeed17cd0f4791e37c972f2da86c07bb6a386a0dbb5813a5b4f998438e3127
|
7
|
+
data.tar.gz: 102c5591a319f1846506d0e1e439f52613bc9f5d6110eab98ee857d7a943921584e12b56733ec7b4f6e95824396ea1052ffc691eed0505bf2b00a7438ceabbe0
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Tapioca
|
2
2
|
|
3
|
-
|
3
|
+
![Build Status](https://github.com/Shopify/tapioca/workflows/CI/badge.svg)
|
4
4
|
|
5
5
|
Tapioca is a library used to generate RBI (Ruby interface) files for use with [Sorbet](https://sorbet.org). RBI files provide the structure (classes, modules, methods, parameters) of the gem/library to Sorbet to assist with typechecking.
|
6
6
|
|
@@ -55,6 +55,8 @@ and do not forget to execute `tapioca` using `bundler`:
|
|
55
55
|
```shell
|
56
56
|
$ bundle exec tapioca
|
57
57
|
Commands:
|
58
|
+
tapioca --version, -v # show version
|
59
|
+
tapioca dsl [constant...] # generate RBIs for dynamic methods
|
58
60
|
tapioca generate [gem...] # generate RBIs from gems
|
59
61
|
tapioca help [COMMAND] # Describe available commands or one specific command
|
60
62
|
tapioca init # initializes folder structure
|
@@ -63,12 +65,12 @@ Commands:
|
|
63
65
|
tapioca todo # generate the list of unresolved constants
|
64
66
|
|
65
67
|
Options:
|
66
|
-
--pre, -b, [--prerequire=file]
|
67
|
-
--post, -a, [--postrequire=file]
|
68
|
-
--out, -o, [--outdir=directory]
|
69
|
-
|
70
|
-
|
71
|
-
--typed, -t, [--typed-overrides=gem:level] # Overrides for typed sigils for generated gem RBIs
|
68
|
+
--pre, -b, [--prerequire=file] # A file to be required before Bundler.require is called
|
69
|
+
--post, -a, [--postrequire=file] # A file to be required after Bundler.require is called
|
70
|
+
--out, -o, [--outdir=directory] # The output directory for generated RBI files
|
71
|
+
--cmd, -c, [--generate-command=command] # The command to run to regenerate RBI files
|
72
|
+
-x, [--exclude=gem [gem ...]] # Excludes the given gem(s) from RBI generation
|
73
|
+
--typed, -t, [--typed-overrides=gem:level [gem:level ...]] # Overrides for typed sigils for generated gem RBIs
|
72
74
|
```
|
73
75
|
|
74
76
|
## Usage
|
data/lib/tapioca/cli.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'thor'
|
5
5
|
|
@@ -34,6 +34,8 @@ module Tapioca
|
|
34
34
|
banner: "gem:level [gem:level ...]",
|
35
35
|
desc: "Overrides for typed sigils for generated gem RBIs"
|
36
36
|
|
37
|
+
map T.unsafe(%w[--version -v] => :__print_version)
|
38
|
+
|
37
39
|
desc "init", "initializes folder structure"
|
38
40
|
def init
|
39
41
|
create_file(Config::SORBET_CONFIG, skip: true) do
|
@@ -92,6 +94,11 @@ module Tapioca
|
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
97
|
+
desc "--version, -v", "show version"
|
98
|
+
def __print_version
|
99
|
+
puts "Tapioca v#{Tapioca::VERSION}"
|
100
|
+
end
|
101
|
+
|
95
102
|
no_commands do
|
96
103
|
def self.exit_on_failure?
|
97
104
|
true
|
@@ -45,10 +45,10 @@ module Tapioca
|
|
45
45
|
# sig { params(value: T.nilable(::User)).void }
|
46
46
|
# def author=(value); end
|
47
47
|
#
|
48
|
-
# sig { params(args: T.untyped, blk: T.untyped).returns(
|
48
|
+
# sig { params(args: T.untyped, blk: T.untyped).returns(::User) }
|
49
49
|
# def build_author(*args, &blk); end
|
50
50
|
#
|
51
|
-
# sig { params(args: T.untyped, blk: T.untyped).returns(
|
51
|
+
# sig { params(args: T.untyped, blk: T.untyped).returns(::Category) }
|
52
52
|
# def build_category(*args, &blk); end
|
53
53
|
#
|
54
54
|
# sig { returns(T.nilable(::Category)) }
|
@@ -69,16 +69,16 @@ module Tapioca
|
|
69
69
|
# sig { params(value: T::Enumerable[::Comment]).void }
|
70
70
|
# def comments=(value); end
|
71
71
|
#
|
72
|
-
# sig { params(args: T.untyped, blk: T.untyped).returns(
|
72
|
+
# sig { params(args: T.untyped, blk: T.untyped).returns(::User) }
|
73
73
|
# def create_author(*args, &blk); end
|
74
74
|
#
|
75
|
-
# sig { params(args: T.untyped, blk: T.untyped).returns(
|
75
|
+
# sig { params(args: T.untyped, blk: T.untyped).returns(::User) }
|
76
76
|
# def create_author!(*args, &blk); end
|
77
77
|
#
|
78
|
-
# sig { params(args: T.untyped, blk: T.untyped).returns(
|
78
|
+
# sig { params(args: T.untyped, blk: T.untyped).returns(::Category) }
|
79
79
|
# def create_category(*args, &blk); end
|
80
80
|
#
|
81
|
-
# sig { params(args: T.untyped, blk: T.untyped).returns(
|
81
|
+
# sig { params(args: T.untyped, blk: T.untyped).returns(::Category) }
|
82
82
|
# def create_category!(*args, &blk); end
|
83
83
|
#
|
84
84
|
# sig { returns(T.nilable(::User)) }
|
@@ -152,7 +152,7 @@ module Tapioca
|
|
152
152
|
"reload_#{association_name}",
|
153
153
|
return_type: association_type,
|
154
154
|
)
|
155
|
-
|
155
|
+
unless reflection.polymorphic?
|
156
156
|
create_method(
|
157
157
|
klass,
|
158
158
|
"build_#{association_name}",
|
@@ -160,7 +160,7 @@ module Tapioca
|
|
160
160
|
Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped"),
|
161
161
|
Parlour::RbiGenerator::Parameter.new("&blk", type: "T.untyped"),
|
162
162
|
],
|
163
|
-
return_type:
|
163
|
+
return_type: association_class
|
164
164
|
)
|
165
165
|
create_method(
|
166
166
|
klass,
|
@@ -169,7 +169,7 @@ module Tapioca
|
|
169
169
|
Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped"),
|
170
170
|
Parlour::RbiGenerator::Parameter.new("&blk", type: "T.untyped"),
|
171
171
|
],
|
172
|
-
return_type:
|
172
|
+
return_type: association_class
|
173
173
|
)
|
174
174
|
create_method(
|
175
175
|
klass,
|
@@ -178,7 +178,7 @@ module Tapioca
|
|
178
178
|
Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped"),
|
179
179
|
Parlour::RbiGenerator::Parameter.new("&blk", type: "T.untyped"),
|
180
180
|
],
|
181
|
-
return_type:
|
181
|
+
return_type: association_class
|
182
182
|
)
|
183
183
|
end
|
184
184
|
end
|
@@ -108,7 +108,7 @@ module Tapioca
|
|
108
108
|
sig { override.returns(T::Enumerable[Module]) }
|
109
109
|
def gather_constants
|
110
110
|
::ActiveRecord::Base.descendants.select do |klass|
|
111
|
-
klass < IdentityCache
|
111
|
+
klass < IdentityCache::WithoutPrimaryIndex
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
@@ -126,7 +126,7 @@ module Tapioca
|
|
126
126
|
if returns_collection
|
127
127
|
COLLECTION_TYPE.call(cache_type)
|
128
128
|
else
|
129
|
-
"::#{cache_type}"
|
129
|
+
"T.nilable(::#{cache_type})"
|
130
130
|
end
|
131
131
|
rescue ArgumentError
|
132
132
|
"T.untyped"
|
@@ -92,24 +92,28 @@ module Tapioca
|
|
92
92
|
method_def = signature.nil? ? method_def : signature.method
|
93
93
|
method_types = parameters_types_from_signature(method_def, signature)
|
94
94
|
|
95
|
-
method_def.parameters.each_with_index.map do |(type, name),
|
96
|
-
|
97
|
-
|
95
|
+
method_def.parameters.each_with_index.map do |(type, name), index|
|
96
|
+
fallback_arg_name = "_arg#{index}"
|
97
|
+
|
98
|
+
name ||= fallback_arg_name
|
99
|
+
name = name.to_s.gsub(/&|\*/, fallback_arg_name) # avoid incorrect names from `delegate`
|
100
|
+
method_type = method_types[index]
|
101
|
+
|
98
102
|
case type
|
99
103
|
when :req
|
100
|
-
::Parlour::RbiGenerator::Parameter.new(name, type:
|
104
|
+
::Parlour::RbiGenerator::Parameter.new(name, type: method_type)
|
101
105
|
when :opt
|
102
|
-
::Parlour::RbiGenerator::Parameter.new(name, type:
|
106
|
+
::Parlour::RbiGenerator::Parameter.new(name, type: method_type, default: 'T.unsafe(nil)')
|
103
107
|
when :rest
|
104
|
-
::Parlour::RbiGenerator::Parameter.new("*#{name}", type:
|
108
|
+
::Parlour::RbiGenerator::Parameter.new("*#{name}", type: method_type)
|
105
109
|
when :keyreq
|
106
|
-
::Parlour::RbiGenerator::Parameter.new("#{name}:", type:
|
110
|
+
::Parlour::RbiGenerator::Parameter.new("#{name}:", type: method_type)
|
107
111
|
when :key
|
108
|
-
::Parlour::RbiGenerator::Parameter.new("#{name}:", type:
|
112
|
+
::Parlour::RbiGenerator::Parameter.new("#{name}:", type: method_type, default: 'T.unsafe(nil)')
|
109
113
|
when :keyrest
|
110
|
-
::Parlour::RbiGenerator::Parameter.new("**#{name}", type:
|
114
|
+
::Parlour::RbiGenerator::Parameter.new("**#{name}", type: method_type)
|
111
115
|
when :block
|
112
|
-
::Parlour::RbiGenerator::Parameter.new("&#{name}", type:
|
116
|
+
::Parlour::RbiGenerator::Parameter.new("&#{name}", type: method_type)
|
113
117
|
else
|
114
118
|
raise "Unknown type `#{type}`."
|
115
119
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
+
# typed: strict
|
1
2
|
# frozen_string_literal: true
|
2
|
-
# typed: true
|
3
3
|
|
4
4
|
require "tapioca/compilers/dsl/base"
|
5
5
|
|
@@ -30,7 +30,7 @@ module Tapioca
|
|
30
30
|
T::Enumerable[Dsl::Base]
|
31
31
|
)
|
32
32
|
@requested_constants = requested_constants
|
33
|
-
@error_handler = error_handler || $stderr.method(:puts)
|
33
|
+
@error_handler = T.let(error_handler || $stderr.method(:puts), T.proc.params(error: String).void)
|
34
34
|
end
|
35
35
|
|
36
36
|
sig { params(blk: T.proc.params(constant: Module, rbi: String).void).void }
|
@@ -54,9 +54,9 @@ module Tapioca
|
|
54
54
|
|
55
55
|
private
|
56
56
|
|
57
|
-
sig { params(requested_generators: T::Array[String]).returns(
|
57
|
+
sig { params(requested_generators: T::Array[String]).returns(T.proc.params(klass: Class).returns(T::Boolean)) }
|
58
58
|
def generator_filter(requested_generators)
|
59
|
-
return
|
59
|
+
return ->(_klass) { true } if requested_generators.empty?
|
60
60
|
|
61
61
|
generators = requested_generators.map(&:downcase)
|
62
62
|
|
@@ -70,7 +70,7 @@ module Tapioca
|
|
70
70
|
def gather_generators(requested_generators)
|
71
71
|
generator_filter = generator_filter(requested_generators)
|
72
72
|
|
73
|
-
Dsl::Base.descendants.select(&generator_filter).map(&:new)
|
73
|
+
T.cast(Dsl::Base.descendants.select(&generator_filter).map(&:new), T::Enumerable[Dsl::Base])
|
74
74
|
end
|
75
75
|
|
76
76
|
sig { params(requested_constants: T::Array[Module]).returns(T::Set[Module]) }
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
1
|
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'spoom'
|
5
5
|
|
@@ -34,7 +34,8 @@ module Tapioca
|
|
34
34
|
path = (Pathname.new(@sorbet_path) / "../.." / path).cleanpath
|
35
35
|
if path.directory?
|
36
36
|
Dir.glob("#{path}/**/*.rb", File::FNM_EXTGLOB).reject do |file|
|
37
|
-
|
37
|
+
relative_file_path = Pathname.new(file).relative_path_from(path)
|
38
|
+
file_ignored_by_sorbet?(config, relative_file_path)
|
38
39
|
end
|
39
40
|
else
|
40
41
|
[path.to_s]
|
@@ -49,13 +50,40 @@ module Tapioca
|
|
49
50
|
end.compact
|
50
51
|
end
|
51
52
|
|
52
|
-
sig { params(config: Spoom::Sorbet::Config,
|
53
|
-
def file_ignored_by_sorbet?(config,
|
54
|
-
|
55
|
-
|
53
|
+
sig { params(config: Spoom::Sorbet::Config, file_path: Pathname).returns(T::Boolean) }
|
54
|
+
def file_ignored_by_sorbet?(config, file_path)
|
55
|
+
file_path_parts = path_parts(file_path)
|
56
|
+
|
57
|
+
config.ignore.any? do |ignore|
|
58
|
+
# Sorbet --ignore matching method:
|
59
|
+
# ---
|
60
|
+
# Ignores input files that contain the given
|
61
|
+
# string in their paths (relative to the input
|
62
|
+
# path passed to Sorbet).
|
63
|
+
#
|
64
|
+
# Strings beginning with / match against the
|
65
|
+
# prefix of these relative paths; others are
|
66
|
+
# substring matchs.
|
67
|
+
|
68
|
+
# Matches must be against whole folder and file
|
69
|
+
# names, so `foo` matches `/foo/bar.rb` and
|
70
|
+
# `/bar/foo/baz.rb` but not `/foo.rb` or
|
71
|
+
# `/foo2/bar.rb`.
|
72
|
+
ignore_parts = path_parts(Pathname.new(ignore))
|
73
|
+
file_path_part_sequences = file_path_parts.each_cons(ignore_parts.size)
|
74
|
+
# if ignore string begins with /, we only want the first sequence to match
|
75
|
+
file_path_part_sequences = [file_path_part_sequences.first].to_enum if ignore.start_with?("/")
|
76
|
+
|
77
|
+
# we need to match whole segments
|
78
|
+
file_path_part_sequences.include?(ignore_parts)
|
56
79
|
end
|
57
80
|
end
|
58
81
|
|
82
|
+
sig { params(path: Pathname).returns(T::Array[String]) }
|
83
|
+
def path_parts(path)
|
84
|
+
T.unsafe(path).descend.map { |part| part.basename.to_s }
|
85
|
+
end
|
86
|
+
|
59
87
|
sig { params(files: T::Enumerable[String], name: String).returns(T::Boolean) }
|
60
88
|
def name_in_project?(files, name)
|
61
89
|
files.any? do |file|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'pathname'
|
5
5
|
|
@@ -209,7 +209,11 @@ module Tapioca
|
|
209
209
|
method = "const" if prop.fetch(:immutable, false)
|
210
210
|
type = prop.fetch(:type_object, "T.untyped")
|
211
211
|
|
212
|
-
|
212
|
+
if prop.key?(:default)
|
213
|
+
indented("#{method} :#{name}, #{type}, default: T.unsafe(nil)")
|
214
|
+
else
|
215
|
+
indented("#{method} :#{name}, #{type}")
|
216
|
+
end
|
213
217
|
end.join("\n")
|
214
218
|
end
|
215
219
|
|
@@ -513,7 +517,9 @@ module Tapioca
|
|
513
517
|
|
514
518
|
parameters = T.let(method.parameters, T::Array[[Symbol, T.nilable(Symbol)]])
|
515
519
|
|
516
|
-
sanitized_parameters = parameters.map do |type, name|
|
520
|
+
sanitized_parameters = parameters.each_with_index.map do |(type, name), index|
|
521
|
+
fallback_arg_name = "_arg#{index}"
|
522
|
+
|
517
523
|
unless name
|
518
524
|
# For attr_writer methods, Sorbet signatures have the name
|
519
525
|
# of the method (without the trailing = sign) as the name of
|
@@ -533,12 +539,12 @@ module Tapioca
|
|
533
539
|
name = if writer_method_with_sig
|
534
540
|
T.must(method_name[0...-1]).to_sym
|
535
541
|
else
|
536
|
-
|
542
|
+
fallback_arg_name
|
537
543
|
end
|
538
544
|
end
|
539
545
|
|
540
546
|
# Sanitize param names
|
541
|
-
name = name.to_s.gsub(/[^a-zA-Z0-9_]/,
|
547
|
+
name = name.to_s.gsub(/[^a-zA-Z0-9_]/, fallback_arg_name)
|
542
548
|
|
543
549
|
[type, name]
|
544
550
|
end
|
@@ -612,6 +618,7 @@ module Tapioca
|
|
612
618
|
signature_body = signature_body
|
613
619
|
.gsub(".returns(<VOID>)", ".void")
|
614
620
|
.gsub("<NOT-TYPED>", "T.untyped")
|
621
|
+
.gsub(".params()", "")
|
615
622
|
.gsub(TYPE_PARAMETER_MATCHER, "T.type_parameter(:\\1)")[1..-1]
|
616
623
|
|
617
624
|
"sig { #{signature_body} }"
|
@@ -1,7 +1,9 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
class Class
|
5
|
+
extend T::Sig
|
6
|
+
|
5
7
|
# Returns an array with all classes that are < than its receiver.
|
6
8
|
#
|
7
9
|
# class C; end
|
@@ -15,9 +17,12 @@ class Class
|
|
15
17
|
#
|
16
18
|
# class D < C; end
|
17
19
|
# C.descendants # => [B, A, D]
|
20
|
+
sig { returns(T::Array[Class]) }
|
18
21
|
def descendants
|
19
|
-
ObjectSpace.each_object(singleton_class).reject do |k|
|
20
|
-
k.singleton_class? || k == self
|
22
|
+
result = ObjectSpace.each_object(singleton_class).reject do |k|
|
23
|
+
T.cast(k, Module).singleton_class? || k == self
|
21
24
|
end
|
25
|
+
|
26
|
+
T.cast(result, T::Array[Class])
|
22
27
|
end
|
23
28
|
end
|
data/lib/tapioca/gemfile.rb
CHANGED
data/lib/tapioca/generator.rb
CHANGED
data/lib/tapioca/loader.rb
CHANGED
data/lib/tapioca/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tapioca
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ufuk Kayserilioglu
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2020-
|
14
|
+
date: 2020-12-01 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: pry
|