tapioca 0.5.6 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +114 -23
  3. data/lib/tapioca/cli.rb +188 -64
  4. data/lib/tapioca/compilers/dsl/active_record_associations.rb +94 -8
  5. data/lib/tapioca/compilers/dsl/active_record_columns.rb +5 -4
  6. data/lib/tapioca/compilers/dsl/active_record_relations.rb +703 -0
  7. data/lib/tapioca/compilers/dsl/active_record_scope.rb +43 -13
  8. data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +2 -4
  9. data/lib/tapioca/compilers/dsl/base.rb +25 -42
  10. data/lib/tapioca/compilers/dsl/extensions/frozen_record.rb +29 -0
  11. data/lib/tapioca/compilers/dsl/frozen_record.rb +37 -0
  12. data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +27 -0
  13. data/lib/tapioca/compilers/dsl/param_helper.rb +52 -0
  14. data/lib/tapioca/compilers/dsl/rails_generators.rb +120 -0
  15. data/lib/tapioca/compilers/dsl_compiler.rb +32 -6
  16. data/lib/tapioca/compilers/sorbet.rb +2 -0
  17. data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +47 -46
  18. data/lib/tapioca/executor.rb +79 -0
  19. data/lib/tapioca/gemfile.rb +23 -0
  20. data/lib/tapioca/generators/base.rb +11 -18
  21. data/lib/tapioca/generators/dsl.rb +33 -38
  22. data/lib/tapioca/generators/gem.rb +50 -29
  23. data/lib/tapioca/generators/init.rb +41 -16
  24. data/lib/tapioca/generators/todo.rb +6 -6
  25. data/lib/tapioca/helpers/cli_helper.rb +26 -0
  26. data/lib/tapioca/helpers/config_helper.rb +84 -0
  27. data/lib/tapioca/helpers/test/content.rb +51 -0
  28. data/lib/tapioca/helpers/test/isolation.rb +125 -0
  29. data/lib/tapioca/helpers/test/template.rb +34 -0
  30. data/lib/tapioca/internal.rb +3 -2
  31. data/lib/tapioca/rbi_ext/model.rb +12 -9
  32. data/lib/tapioca/reflection.rb +13 -0
  33. data/lib/tapioca/trackers/autoload.rb +70 -0
  34. data/lib/tapioca/trackers/constant_definition.rb +42 -0
  35. data/lib/tapioca/trackers/mixin.rb +78 -0
  36. data/lib/tapioca/trackers.rb +14 -0
  37. data/lib/tapioca/version.rb +1 -1
  38. data/lib/tapioca.rb +28 -2
  39. metadata +19 -7
  40. data/lib/tapioca/config.rb +0 -45
  41. data/lib/tapioca/config_builder.rb +0 -73
  42. 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)).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
- node = create_node(RBI::Module.new(name))
73
- block&.call(T.cast(node, RBI::Scope))
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
- ).void
82
+ ).returns(Scope)
82
83
  end
83
84
  def create_class(name, superclass_name: nil, &block)
84
- node = create_node(RBI::Class.new(name, superclass_name: superclass_name))
85
- block&.call(T.cast(node, RBI::Scope))
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)
@@ -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"
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.5.6"
5
+ VERSION = "0.6.0"
6
6
  end
data/lib/tapioca.rb CHANGED
@@ -1,9 +1,16 @@
1
- # typed: true
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/constant_locator"
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.5.6
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-03 00:00:00.000000000 Z
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.8
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.8
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/config.rb
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:
@@ -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