tapioca 0.5.6 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +114 -23
- data/lib/tapioca/cli.rb +188 -64
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +94 -8
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +5 -4
- data/lib/tapioca/compilers/dsl/active_record_relations.rb +703 -0
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +43 -13
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +2 -4
- data/lib/tapioca/compilers/dsl/base.rb +25 -42
- data/lib/tapioca/compilers/dsl/extensions/frozen_record.rb +29 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +37 -0
- data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +27 -0
- data/lib/tapioca/compilers/dsl/param_helper.rb +52 -0
- data/lib/tapioca/compilers/dsl/rails_generators.rb +120 -0
- data/lib/tapioca/compilers/dsl_compiler.rb +32 -6
- data/lib/tapioca/compilers/sorbet.rb +2 -0
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +47 -46
- data/lib/tapioca/executor.rb +79 -0
- data/lib/tapioca/gemfile.rb +23 -0
- data/lib/tapioca/generators/base.rb +11 -18
- data/lib/tapioca/generators/dsl.rb +33 -38
- data/lib/tapioca/generators/gem.rb +50 -29
- data/lib/tapioca/generators/init.rb +41 -16
- data/lib/tapioca/generators/todo.rb +6 -6
- data/lib/tapioca/helpers/cli_helper.rb +26 -0
- data/lib/tapioca/helpers/config_helper.rb +84 -0
- data/lib/tapioca/helpers/test/content.rb +51 -0
- data/lib/tapioca/helpers/test/isolation.rb +125 -0
- data/lib/tapioca/helpers/test/template.rb +34 -0
- data/lib/tapioca/internal.rb +3 -2
- data/lib/tapioca/rbi_ext/model.rb +12 -9
- data/lib/tapioca/reflection.rb +13 -0
- data/lib/tapioca/trackers/autoload.rb +70 -0
- data/lib/tapioca/trackers/constant_definition.rb +42 -0
- data/lib/tapioca/trackers/mixin.rb +78 -0
- data/lib/tapioca/trackers.rb +14 -0
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +28 -2
- metadata +19 -7
- data/lib/tapioca/config.rb +0 -45
- data/lib/tapioca/config_builder.rb +0 -73
- data/lib/tapioca/constant_locator.rb +0 -40
@@ -67,10 +67,11 @@ module RBI
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
sig { params(name: String, block: T.nilable(T.proc.params(scope: Scope).void)).
|
70
|
+
sig { params(name: String, block: T.nilable(T.proc.params(scope: Scope).void)).returns(Scope) }
|
71
71
|
def create_module(name, &block)
|
72
|
-
|
73
|
-
|
72
|
+
T.cast(create_node(RBI::Module.new(name)), RBI::Scope).tap do |node|
|
73
|
+
block&.call(node)
|
74
|
+
end
|
74
75
|
end
|
75
76
|
|
76
77
|
sig do
|
@@ -78,11 +79,12 @@ module RBI
|
|
78
79
|
name: String,
|
79
80
|
superclass_name: T.nilable(String),
|
80
81
|
block: T.nilable(T.proc.params(scope: RBI::Scope).void)
|
81
|
-
).
|
82
|
+
).returns(Scope)
|
82
83
|
end
|
83
84
|
def create_class(name, superclass_name: nil, &block)
|
84
|
-
|
85
|
-
|
85
|
+
T.cast(create_node(RBI::Class.new(name, superclass_name: superclass_name)), RBI::Scope).tap do |node|
|
86
|
+
block&.call(node)
|
87
|
+
end
|
86
88
|
end
|
87
89
|
|
88
90
|
sig { params(name: String, value: String).void }
|
@@ -115,14 +117,15 @@ module RBI
|
|
115
117
|
name: String,
|
116
118
|
parameters: T::Array[TypedParam],
|
117
119
|
return_type: String,
|
118
|
-
class_method: T::Boolean
|
120
|
+
class_method: T::Boolean,
|
121
|
+
visibility: RBI::Visibility
|
119
122
|
).void
|
120
123
|
end
|
121
|
-
def create_method(name, parameters: [], return_type: "T.untyped", class_method: false)
|
124
|
+
def create_method(name, parameters: [], return_type: "T.untyped", class_method: false, visibility: RBI::Public.new)
|
122
125
|
return unless valid_method_name?(name)
|
123
126
|
|
124
127
|
sig = RBI::Sig.new(return_type: return_type)
|
125
|
-
method = RBI::Method.new(name, sigs: [sig], is_singleton: class_method)
|
128
|
+
method = RBI::Method.new(name, sigs: [sig], is_singleton: class_method, visibility: visibility)
|
126
129
|
parameters.each do |param|
|
127
130
|
method << param.param
|
128
131
|
sig << RBI::SigParam.new(param.param.name, param.type)
|
data/lib/tapioca/reflection.rb
CHANGED
@@ -19,6 +19,19 @@ module Tapioca
|
|
19
19
|
PRIVATE_INSTANCE_METHODS_METHOD = T.let(Module.instance_method(:private_instance_methods), UnboundMethod)
|
20
20
|
METHOD_METHOD = T.let(Kernel.instance_method(:method), UnboundMethod)
|
21
21
|
|
22
|
+
sig do
|
23
|
+
params(
|
24
|
+
symbol: String,
|
25
|
+
inherit: T::Boolean,
|
26
|
+
namespace: Module
|
27
|
+
).returns(BasicObject).checked(:never)
|
28
|
+
end
|
29
|
+
def constantize(symbol, inherit: false, namespace: Object)
|
30
|
+
namespace.const_get(symbol, inherit)
|
31
|
+
rescue NameError, LoadError, RuntimeError, ArgumentError, TypeError
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
22
35
|
sig { params(object: BasicObject).returns(Class).checked(:never) }
|
23
36
|
def class_of(object)
|
24
37
|
CLASS_METHOD.bind(object).call
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Tapioca
|
5
|
+
module Trackers
|
6
|
+
module Autoload
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
NOOP_METHOD = -> (*_args, **_kwargs, &_block) {}
|
10
|
+
|
11
|
+
@constant_names_registered_for_autoload = T.let([], T::Array[String])
|
12
|
+
|
13
|
+
class << self
|
14
|
+
extend T::Sig
|
15
|
+
|
16
|
+
sig { void }
|
17
|
+
def eager_load_all!
|
18
|
+
with_disabled_exits do
|
19
|
+
until @constant_names_registered_for_autoload.empty?
|
20
|
+
# Grab the next constant name
|
21
|
+
constant_name = T.must(@constant_names_registered_for_autoload.shift)
|
22
|
+
# Trigger autoload by constantizing the registered name
|
23
|
+
Reflection.constantize(constant_name, inherit: true)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
sig { params(constant_name: String).void }
|
29
|
+
def register(constant_name)
|
30
|
+
@constant_names_registered_for_autoload << constant_name
|
31
|
+
end
|
32
|
+
|
33
|
+
sig do
|
34
|
+
type_parameters(:Result)
|
35
|
+
.params(block: T.proc.returns(T.type_parameter(:Result)))
|
36
|
+
.returns(T.type_parameter(:Result))
|
37
|
+
end
|
38
|
+
def with_disabled_exits(&block)
|
39
|
+
original_abort = Kernel.instance_method(:abort)
|
40
|
+
original_exit = Kernel.instance_method(:exit)
|
41
|
+
|
42
|
+
begin
|
43
|
+
Kernel.define_method(:abort, NOOP_METHOD)
|
44
|
+
Kernel.define_method(:exit, NOOP_METHOD)
|
45
|
+
|
46
|
+
block.call
|
47
|
+
ensure
|
48
|
+
Kernel.define_method(:exit, original_exit)
|
49
|
+
Kernel.define_method(:abort, original_abort)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# We need to do the alias-method-chain dance since Bootsnap does the same,
|
58
|
+
# and prepended modules and alias-method-chain don't play well together.
|
59
|
+
#
|
60
|
+
# So, why does Bootsnap do alias-method-chain and not prepend? Glad you asked!
|
61
|
+
# That's because RubyGems does alias-method-chain for Kernel#require and such,
|
62
|
+
# so, if Bootsnap were to do prepend, it might end up breaking RubyGems.
|
63
|
+
class Module
|
64
|
+
alias_method(:autoload_without_tapioca, :autoload)
|
65
|
+
|
66
|
+
def autoload(const_name, path)
|
67
|
+
Tapioca::Trackers::Autoload.register("#{self}::#{const_name}")
|
68
|
+
autoload_without_tapioca(const_name, path)
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "set"
|
5
|
+
|
6
|
+
module Tapioca
|
7
|
+
module Trackers
|
8
|
+
# Registers a TracePoint immediately upon load to track points at which
|
9
|
+
# classes and modules are opened for definition. This is used to track
|
10
|
+
# correspondence between classes/modules and files, as this information isn't
|
11
|
+
# available in the ruby runtime without extra accounting.
|
12
|
+
module ConstantDefinition
|
13
|
+
extend Reflection
|
14
|
+
|
15
|
+
@class_files = {}
|
16
|
+
|
17
|
+
# Immediately activated upon load. Observes class/module definition.
|
18
|
+
TracePoint.trace(:class) do |tp|
|
19
|
+
unless tp.self.singleton_class?
|
20
|
+
key = name_of(tp.self)
|
21
|
+
file = tp.path
|
22
|
+
if file == "(eval)"
|
23
|
+
file = T.must(caller_locations)
|
24
|
+
.drop_while { |loc| loc.path == "(eval)" }
|
25
|
+
.first&.path
|
26
|
+
end
|
27
|
+
@class_files[key] ||= Set.new
|
28
|
+
@class_files[key] << file
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the files in which this class or module was opened. Doesn't know
|
33
|
+
# about situations where the class was opened prior to +require+ing,
|
34
|
+
# or where metaprogramming was used via +eval+, etc.
|
35
|
+
def self.files_for(klass)
|
36
|
+
name = String === klass ? klass : name_of(klass)
|
37
|
+
files = @class_files[name]
|
38
|
+
files || Set.new
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Tapioca
|
5
|
+
module Trackers
|
6
|
+
module Mixin
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
@mixin_map = {}.compare_by_identity
|
10
|
+
|
11
|
+
class Type < T::Enum
|
12
|
+
enums do
|
13
|
+
Prepend = new
|
14
|
+
Include = new
|
15
|
+
Extend = new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
sig do
|
20
|
+
params(
|
21
|
+
constant: Module,
|
22
|
+
mod: Module,
|
23
|
+
mixin_type: Type,
|
24
|
+
locations: T.nilable(T::Array[Thread::Backtrace::Location])
|
25
|
+
).void
|
26
|
+
end
|
27
|
+
def self.register(constant, mod, mixin_type, locations)
|
28
|
+
locations ||= []
|
29
|
+
locations.map!(&:absolute_path).uniq!
|
30
|
+
locs = mixin_locations_for(constant)
|
31
|
+
locs.fetch(mixin_type).store(mod, T.cast(locations, T::Array[String]))
|
32
|
+
end
|
33
|
+
|
34
|
+
sig { params(constant: Module).returns(T::Hash[Type, T::Hash[Module, T::Array[String]]]) }
|
35
|
+
def self.mixin_locations_for(constant)
|
36
|
+
@mixin_map[constant] ||= {
|
37
|
+
Type::Prepend => {}.compare_by_identity,
|
38
|
+
Type::Include => {}.compare_by_identity,
|
39
|
+
Type::Extend => {}.compare_by_identity,
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Module
|
47
|
+
prepend(Module.new do
|
48
|
+
def prepend_features(constant)
|
49
|
+
Tapioca::Trackers::Mixin.register(
|
50
|
+
constant,
|
51
|
+
self,
|
52
|
+
Tapioca::Trackers::Mixin::Type::Prepend,
|
53
|
+
caller_locations
|
54
|
+
)
|
55
|
+
super
|
56
|
+
end
|
57
|
+
|
58
|
+
def append_features(constant)
|
59
|
+
Tapioca::Trackers::Mixin.register(
|
60
|
+
constant,
|
61
|
+
self,
|
62
|
+
Tapioca::Trackers::Mixin::Type::Include,
|
63
|
+
caller_locations
|
64
|
+
)
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
68
|
+
def extend_object(obj)
|
69
|
+
Tapioca::Trackers::Mixin.register(
|
70
|
+
obj,
|
71
|
+
self,
|
72
|
+
Tapioca::Trackers::Mixin::Type::Extend,
|
73
|
+
caller_locations
|
74
|
+
) if Module === obj
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end)
|
78
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# The load order below is important:
|
5
|
+
# ----------------------------------
|
6
|
+
# We want the mixin tracker to be the first thing that is
|
7
|
+
# loaded because other trackers might apply their own mixins
|
8
|
+
# into core types (like `Module` and `Kernel`). In order to
|
9
|
+
# catch and filter those mixins as coming from Tapioca, we need
|
10
|
+
# the mixin tracker to be in place, before any mixin operations
|
11
|
+
# are performed.
|
12
|
+
require "tapioca/trackers/mixin"
|
13
|
+
require "tapioca/trackers/constant_definition"
|
14
|
+
require "tapioca/trackers/autoload"
|
data/lib/tapioca/version.rb
CHANGED
data/lib/tapioca.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "sorbet-runtime"
|
5
5
|
|
6
6
|
module Tapioca
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig do
|
10
|
+
type_parameters(:Result)
|
11
|
+
.params(blk: T.proc.returns(T.type_parameter(:Result)))
|
12
|
+
.returns(T.type_parameter(:Result))
|
13
|
+
end
|
7
14
|
def self.silence_warnings(&blk)
|
8
15
|
original_verbosity = $VERBOSE
|
9
16
|
$VERBOSE = nil
|
@@ -15,10 +22,29 @@ module Tapioca
|
|
15
22
|
end
|
16
23
|
|
17
24
|
class Error < StandardError; end
|
25
|
+
|
26
|
+
SORBET_DIR = T.let("sorbet", String)
|
27
|
+
SORBET_CONFIG_FILE = T.let("#{SORBET_DIR}/config", String)
|
28
|
+
TAPIOCA_DIR = T.let("#{SORBET_DIR}/tapioca", String)
|
29
|
+
TAPIOCA_CONFIG_FILE = T.let("#{TAPIOCA_DIR}/config.yml", String)
|
30
|
+
|
31
|
+
DEFAULT_COMMAND = T.let("bin/tapioca", String)
|
32
|
+
DEFAULT_POSTREQUIRE_FILE = T.let("#{TAPIOCA_DIR}/require.rb", String)
|
33
|
+
DEFAULT_RBI_DIR = T.let("#{SORBET_DIR}/rbi", String)
|
34
|
+
DEFAULT_DSL_DIR = T.let("#{DEFAULT_RBI_DIR}/dsl", String)
|
35
|
+
DEFAULT_GEM_DIR = T.let("#{DEFAULT_RBI_DIR}/gems", String)
|
36
|
+
DEFAULT_SHIM_DIR = T.let("#{DEFAULT_RBI_DIR}/shims", String)
|
37
|
+
DEFAULT_TODO_FILE = T.let("#{DEFAULT_RBI_DIR}/todo.rbi", String)
|
38
|
+
|
39
|
+
DEFAULT_OVERRIDES = T.let({
|
40
|
+
# ActiveSupport overrides some core methods with different signatures
|
41
|
+
# so we generate a typed: false RBI for it to suppress errors
|
42
|
+
"activesupport" => "false",
|
43
|
+
}.freeze, T::Hash[String, String])
|
18
44
|
end
|
19
45
|
|
20
46
|
require "tapioca/reflection"
|
21
|
-
require "tapioca/
|
47
|
+
require "tapioca/trackers"
|
22
48
|
require "tapioca/compilers/dsl/base"
|
23
49
|
require "tapioca/compilers/dynamic_mixin_compiler"
|
24
50
|
require "tapioca/helpers/active_record_column_type_helper"
|
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
|
+
version: 0.6.0
|
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: 2021-12-
|
14
|
+
date: 2021-12-17 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -50,7 +50,7 @@ dependencies:
|
|
50
50
|
version: 0.0.0
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 0.0.
|
53
|
+
version: 0.0.9
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -60,7 +60,7 @@ dependencies:
|
|
60
60
|
version: 0.0.0
|
61
61
|
- - ">="
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version: 0.0.
|
63
|
+
version: 0.0.9
|
64
64
|
- !ruby/object:Gem::Dependency
|
65
65
|
name: sorbet-static
|
66
66
|
requirement: !ruby/object:Gem::Requirement
|
@@ -161,6 +161,7 @@ files:
|
|
161
161
|
- lib/tapioca/compilers/dsl/active_record_columns.rb
|
162
162
|
- lib/tapioca/compilers/dsl/active_record_enum.rb
|
163
163
|
- lib/tapioca/compilers/dsl/active_record_fixtures.rb
|
164
|
+
- lib/tapioca/compilers/dsl/active_record_relations.rb
|
164
165
|
- lib/tapioca/compilers/dsl/active_record_scope.rb
|
165
166
|
- lib/tapioca/compilers/dsl/active_record_typed_store.rb
|
166
167
|
- lib/tapioca/compilers/dsl/active_resource.rb
|
@@ -169,10 +170,14 @@ files:
|
|
169
170
|
- lib/tapioca/compilers/dsl/active_support_current_attributes.rb
|
170
171
|
- lib/tapioca/compilers/dsl/base.rb
|
171
172
|
- lib/tapioca/compilers/dsl/config.rb
|
173
|
+
- lib/tapioca/compilers/dsl/extensions/frozen_record.rb
|
172
174
|
- lib/tapioca/compilers/dsl/frozen_record.rb
|
175
|
+
- lib/tapioca/compilers/dsl/helper/active_record_constants.rb
|
173
176
|
- lib/tapioca/compilers/dsl/identity_cache.rb
|
174
177
|
- lib/tapioca/compilers/dsl/mixed_in_class_attributes.rb
|
178
|
+
- lib/tapioca/compilers/dsl/param_helper.rb
|
175
179
|
- lib/tapioca/compilers/dsl/protobuf.rb
|
180
|
+
- lib/tapioca/compilers/dsl/rails_generators.rb
|
176
181
|
- lib/tapioca/compilers/dsl/sidekiq_worker.rb
|
177
182
|
- lib/tapioca/compilers/dsl/smart_properties.rb
|
178
183
|
- lib/tapioca/compilers/dsl/state_machines.rb
|
@@ -185,9 +190,7 @@ files:
|
|
185
190
|
- lib/tapioca/compilers/symbol_table/symbol_loader.rb
|
186
191
|
- lib/tapioca/compilers/symbol_table_compiler.rb
|
187
192
|
- lib/tapioca/compilers/todos_compiler.rb
|
188
|
-
- lib/tapioca/
|
189
|
-
- lib/tapioca/config_builder.rb
|
190
|
-
- lib/tapioca/constant_locator.rb
|
193
|
+
- lib/tapioca/executor.rb
|
191
194
|
- lib/tapioca/gemfile.rb
|
192
195
|
- lib/tapioca/generators.rb
|
193
196
|
- lib/tapioca/generators/base.rb
|
@@ -198,6 +201,11 @@ files:
|
|
198
201
|
- lib/tapioca/generators/todo.rb
|
199
202
|
- lib/tapioca/generic_type_registry.rb
|
200
203
|
- lib/tapioca/helpers/active_record_column_type_helper.rb
|
204
|
+
- lib/tapioca/helpers/cli_helper.rb
|
205
|
+
- lib/tapioca/helpers/config_helper.rb
|
206
|
+
- lib/tapioca/helpers/test/content.rb
|
207
|
+
- lib/tapioca/helpers/test/isolation.rb
|
208
|
+
- lib/tapioca/helpers/test/template.rb
|
201
209
|
- lib/tapioca/internal.rb
|
202
210
|
- lib/tapioca/loader.rb
|
203
211
|
- lib/tapioca/rbi_ext/model.rb
|
@@ -205,6 +213,10 @@ files:
|
|
205
213
|
- lib/tapioca/sorbet_ext/fixed_hash_patch.rb
|
206
214
|
- lib/tapioca/sorbet_ext/generic_name_patch.rb
|
207
215
|
- lib/tapioca/sorbet_ext/name_patch.rb
|
216
|
+
- lib/tapioca/trackers.rb
|
217
|
+
- lib/tapioca/trackers/autoload.rb
|
218
|
+
- lib/tapioca/trackers/constant_definition.rb
|
219
|
+
- lib/tapioca/trackers/mixin.rb
|
208
220
|
- lib/tapioca/version.rb
|
209
221
|
homepage: https://github.com/Shopify/tapioca
|
210
222
|
licenses:
|
data/lib/tapioca/config.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
module Tapioca
|
5
|
-
class Config < T::Struct
|
6
|
-
extend(T::Sig)
|
7
|
-
|
8
|
-
const(:outdir, String)
|
9
|
-
const(:prerequire, T.nilable(String))
|
10
|
-
const(:postrequire, String)
|
11
|
-
const(:exclude, T::Array[String])
|
12
|
-
const(:exclude_generators, T::Array[String])
|
13
|
-
const(:typed_overrides, T::Hash[String, String])
|
14
|
-
const(:todos_path, String)
|
15
|
-
const(:generators, T::Array[String])
|
16
|
-
const(:file_header, T::Boolean, default: true)
|
17
|
-
const(:doc, T::Boolean, default: false)
|
18
|
-
|
19
|
-
sig { returns(Pathname) }
|
20
|
-
def outpath
|
21
|
-
@outpath = T.let(@outpath, T.nilable(Pathname))
|
22
|
-
@outpath ||= Pathname.new(outdir)
|
23
|
-
end
|
24
|
-
|
25
|
-
private_class_method :new
|
26
|
-
|
27
|
-
SORBET_PATH = T.let("sorbet", String)
|
28
|
-
SORBET_CONFIG = T.let("#{SORBET_PATH}/config", String)
|
29
|
-
TAPIOCA_PATH = T.let("#{SORBET_PATH}/tapioca", String)
|
30
|
-
TAPIOCA_CONFIG = T.let("#{TAPIOCA_PATH}/config.yml", String)
|
31
|
-
|
32
|
-
DEFAULT_COMMAND = T.let("bin/tapioca", String)
|
33
|
-
DEFAULT_POSTREQUIRE = T.let("#{TAPIOCA_PATH}/require.rb", String)
|
34
|
-
DEFAULT_RBIDIR = T.let("#{SORBET_PATH}/rbi", String)
|
35
|
-
DEFAULT_DSLDIR = T.let("#{DEFAULT_RBIDIR}/dsl", String)
|
36
|
-
DEFAULT_GEMDIR = T.let("#{DEFAULT_RBIDIR}/gems", String)
|
37
|
-
DEFAULT_TODOSPATH = T.let("#{DEFAULT_RBIDIR}/todo.rbi", String)
|
38
|
-
|
39
|
-
DEFAULT_OVERRIDES = T.let({
|
40
|
-
# ActiveSupport overrides some core methods with different signatures
|
41
|
-
# so we generate a typed: false RBI for it to suppress errors
|
42
|
-
"activesupport" => "false",
|
43
|
-
}.freeze, T::Hash[String, String])
|
44
|
-
end
|
45
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "yaml"
|
5
|
-
|
6
|
-
module Tapioca
|
7
|
-
class ConfigBuilder
|
8
|
-
class << self
|
9
|
-
extend(T::Sig)
|
10
|
-
|
11
|
-
sig { params(command: Symbol, options: T::Hash[String, T.untyped]).returns(Config) }
|
12
|
-
def from_options(command, options)
|
13
|
-
merged_options = merge_options(default_options(command), config_options, options)
|
14
|
-
|
15
|
-
puts(<<~MSG) if merged_options.include?("generate_command")
|
16
|
-
DEPRECATION: The `-c` and `--cmd` flags will be removed in a future release.
|
17
|
-
MSG
|
18
|
-
|
19
|
-
Config.from_hash(merged_options)
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
sig { returns(T::Hash[String, T.untyped]) }
|
25
|
-
def config_options
|
26
|
-
if File.exist?(Config::TAPIOCA_CONFIG)
|
27
|
-
YAML.load_file(Config::TAPIOCA_CONFIG, fallback: {})
|
28
|
-
else
|
29
|
-
{}
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
sig { params(command: Symbol).returns(T::Hash[String, T.untyped]) }
|
34
|
-
def default_options(command)
|
35
|
-
default_outdir = case command
|
36
|
-
when :sync, :generate, :gem
|
37
|
-
Config::DEFAULT_GEMDIR
|
38
|
-
when :dsl
|
39
|
-
Config::DEFAULT_DSLDIR
|
40
|
-
else
|
41
|
-
Config::SORBET_PATH
|
42
|
-
end
|
43
|
-
|
44
|
-
DEFAULT_OPTIONS.merge("outdir" => default_outdir)
|
45
|
-
end
|
46
|
-
|
47
|
-
sig { params(options: T::Hash[String, T.untyped]).returns(T::Hash[String, T.untyped]) }
|
48
|
-
def merge_options(*options)
|
49
|
-
options.each_with_object({}) do |option, result|
|
50
|
-
result.merge!(option) do |_, this_val, other_val|
|
51
|
-
if this_val.is_a?(Hash) && other_val.is_a?(Hash)
|
52
|
-
this_val.merge(other_val)
|
53
|
-
else
|
54
|
-
other_val
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
DEFAULT_OPTIONS = T.let({
|
62
|
-
"postrequire" => Config::DEFAULT_POSTREQUIRE,
|
63
|
-
"outdir" => nil,
|
64
|
-
"exclude" => [],
|
65
|
-
"exclude_generators" => [],
|
66
|
-
"typed_overrides" => Config::DEFAULT_OVERRIDES,
|
67
|
-
"todos_path" => Config::DEFAULT_TODOSPATH,
|
68
|
-
"generators" => [],
|
69
|
-
"file_header" => true,
|
70
|
-
"doc" => false,
|
71
|
-
}.freeze, T::Hash[String, T.untyped])
|
72
|
-
end
|
73
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# typed: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "set"
|
5
|
-
|
6
|
-
module Tapioca
|
7
|
-
# Registers a TracePoint immediately upon load to track points at which
|
8
|
-
# classes and modules are opened for definition. This is used to track
|
9
|
-
# correspondence between classes/modules and files, as this information isn't
|
10
|
-
# available in the ruby runtime without extra accounting.
|
11
|
-
module ConstantLocator
|
12
|
-
extend Reflection
|
13
|
-
|
14
|
-
@class_files = {}
|
15
|
-
|
16
|
-
# Immediately activated upon load. Observes class/module definition.
|
17
|
-
TracePoint.trace(:class) do |tp|
|
18
|
-
unless tp.self.singleton_class?
|
19
|
-
key = name_of(tp.self)
|
20
|
-
file = tp.path
|
21
|
-
if file == "(eval)"
|
22
|
-
file = T.must(caller_locations)
|
23
|
-
.drop_while { |loc| loc.path == "(eval)" }
|
24
|
-
.first&.path
|
25
|
-
end
|
26
|
-
@class_files[key] ||= Set.new
|
27
|
-
@class_files[key] << file
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Returns the files in which this class or module was opened. Doesn't know
|
32
|
-
# about situations where the class was opened prior to +require+ing,
|
33
|
-
# or where metaprogramming was used via +eval+, etc.
|
34
|
-
def self.files_for(klass)
|
35
|
-
name = String === klass ? klass : name_of(klass)
|
36
|
-
files = @class_files[name]
|
37
|
-
files || Set.new
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|