tapioca 0.2.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3bd7a0a3f7cb0bbb9ed158cd446ae76a2d89f5872c620f1dce5ecc41e898d330
4
- data.tar.gz: e66cc1b09f4c7b47bf493a9a1966c9cc6dd05c2f12f4d29e6a1900949554c3b0
3
+ metadata.gz: fbb47e890388bfea9f9413a20cc6768d16175f9b962291d04d35eaaf3e94e109
4
+ data.tar.gz: eb041465b3237e6f87872c1aeaf172fb0c28ebb445f1855f837dd1759888362f
5
5
  SHA512:
6
- metadata.gz: 8e12cfbd5404421a8ab57a0c85c8ee503bc0f87cfc569edac3e97da1f86d420aa137a91c79dae3d86220cdd5ea817476387613068e05fd7986fa8284e2b5da97
7
- data.tar.gz: 85603c8f06e73f214dac2c31b389ea1e4ac63c513e209da649a657e33951e7d2d0c7f9208a5c4eee597ba953937397bc3d630a4a0a01a29c092ed60d7aa935c5
6
+ metadata.gz: 703f76a6df75973603aebc4e2589a81d124b98196372dccc170da3e720dd02676749a6439e9538862a327945b182f71f826beecd1b9b99d032c4e6067b9afb66
7
+ data.tar.gz: 7a86ffe6ef50693f8f22fdc9bd4c853b0d7d5d4296d85f6ba7e0207471f6a1654d561f9a97a148e48e8bec80559c0c68c9afd95374e1b5a40ec2785172e7e43c
@@ -28,12 +28,14 @@ rescue
28
28
  nil
29
29
  end
30
30
 
31
- require_relative "tapioca/loader"
32
- require_relative "tapioca/constant_locator"
33
- require_relative "tapioca/generator"
34
- require_relative "tapioca/cli"
35
- require_relative "tapioca/gemfile"
36
- require_relative "tapioca/compilers/symbol_table_compiler"
37
- require_relative "tapioca/compilers/symbol_table/symbol_generator"
38
- require_relative "tapioca/compilers/symbol_table/symbol_loader"
39
- require_relative "tapioca/version"
31
+ require "tapioca/loader"
32
+ require "tapioca/constant_locator"
33
+ require "tapioca/config"
34
+ require "tapioca/config_builder"
35
+ require "tapioca/generator"
36
+ require "tapioca/cli"
37
+ require "tapioca/gemfile"
38
+ require "tapioca/compilers/symbol_table_compiler"
39
+ require "tapioca/compilers/symbol_table/symbol_generator"
40
+ require "tapioca/compilers/symbol_table/symbol_loader"
41
+ require "tapioca/version"
@@ -13,34 +13,36 @@ module Tapioca
13
13
  desc: "A file to be required before Bundler.require is called"
14
14
  class_option :postrequire,
15
15
  aliases: ["--post", "-a"],
16
- default: Generator::DEFAULT_POSTREQUIRE,
17
16
  banner: "file",
18
17
  desc: "A file to be required after Bundler.require is called"
19
18
  class_option :outdir,
20
19
  aliases: ["--out", "-o"],
21
- default: Generator::DEFAULT_OUTDIR,
22
20
  banner: "directory",
23
21
  desc: "The output directory for generated RBI files"
24
22
  class_option :generate_command,
25
23
  aliases: ["--cmd", "-c"],
26
24
  banner: "command",
27
25
  desc: "The command to run to regenerate RBI files"
26
+ class_option :exclude,
27
+ aliases: ["-x"],
28
+ type: :array,
29
+ banner: "gem [gem ...]",
30
+ desc: "Excludes the given gem(s) from RBI generation"
28
31
  class_option :typed_overrides,
29
32
  aliases: ["--typed", "-t"],
30
33
  type: :hash,
31
- default: {},
32
- banner: "gem:level",
34
+ banner: "gem:level [gem:level ...]",
33
35
  desc: "Overrides for typed sigils for generated gem RBIs"
34
36
 
35
37
  desc "init", "initializes folder structure"
36
38
  def init
37
- create_file(Generator::SORBET_CONFIG, skip: true) do
39
+ create_file(Config::SORBET_CONFIG, skip: true) do
38
40
  <<~CONTENT
39
41
  --dir
40
42
  .
41
43
  CONTENT
42
44
  end
43
- create_file(Generator::DEFAULT_POSTREQUIRE, skip: true) do
45
+ create_file(Config::DEFAULT_POSTREQUIRE, skip: true) do
44
46
  <<~CONTENT
45
47
  # frozen_string_literal: true
46
48
  # typed: false
@@ -65,14 +67,12 @@ module Tapioca
65
67
  end
66
68
 
67
69
  no_commands do
70
+ def self.exit_on_failure?
71
+ true
72
+ end
73
+
68
74
  def generator
69
- @generator ||= Generator.new(
70
- outdir: options[:outdir],
71
- prerequire: options[:prerequire],
72
- postrequire: options[:postrequire],
73
- command: options[:generate_command],
74
- typed_overrides: options[:typed_overrides]
75
- )
75
+ @generator ||= Generator.new(ConfigBuilder.from_options(options))
76
76
  end
77
77
  end
78
78
  end
@@ -137,14 +137,11 @@ module Tapioca
137
137
  end
138
138
  def compile_object(name, value)
139
139
  return if symbol_ignored?(name)
140
- indented("#{name} = T.let(T.unsafe(nil), #{type_name_of(value)})")
141
- end
142
-
143
- sig { params(value: BasicObject).returns(String).checked(:never) }
144
- def type_name_of(value)
145
140
  klass = class_of(value)
141
+ return if name_of(klass)&.start_with?("T::Types::", "T::Private::")
146
142
 
147
- public_module?(klass) && name_of(klass) || "T.untyped"
143
+ type_name = public_module?(klass) && name_of(klass) || "T.untyped"
144
+ indented("#{name} = T.let(T.unsafe(nil), #{type_name})")
148
145
  end
149
146
 
150
147
  sig { params(name: String, constant: Module).returns(T.nilable(String)) }
@@ -289,7 +286,7 @@ module Tapioca
289
286
  end
290
287
 
291
288
  prepends = prepend
292
- .select(&method(:name_of))
289
+ .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
293
290
  .select(&method(:public_module?))
294
291
  .map do |mod|
295
292
  # TODO: Sorbet currently does not handle prepend
@@ -299,14 +296,14 @@ module Tapioca
299
296
  end
300
297
 
301
298
  includes = include
302
- .select(&method(:name_of))
299
+ .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
303
300
  .select(&method(:public_module?))
304
301
  .map do |mod|
305
302
  indented("include(#{qualified_name_of(mod)})")
306
303
  end
307
304
 
308
305
  extends = extend
309
- .select(&method(:name_of))
306
+ .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
310
307
  .select(&method(:public_module?))
311
308
  .map do |mod|
312
309
  indented("extend(#{qualified_name_of(mod)})")
@@ -424,11 +421,6 @@ module Tapioca
424
421
  SymbolLoader.ignore_symbol?(symbol_name)
425
422
  end
426
423
 
427
- sig { params(path: String).returns(T::Boolean) }
428
- def path_in_gem?(path)
429
- File.realpath(path).start_with?(gem.full_gem_path)
430
- end
431
-
432
424
  SPECIAL_METHOD_NAMES = %w[! ~ +@ ** -@ * / % + - << >> & | ^ < <= => > >= == === != =~ !~ <=> [] []= `]
433
425
 
434
426
  sig { params(name: String).returns(T::Boolean) }
@@ -462,7 +454,7 @@ module Tapioca
462
454
  source_location = method.source_location&.first
463
455
  return false if source_location.nil?
464
456
 
465
- path_in_gem?(source_location)
457
+ gem.contains_path?(source_location)
466
458
  end
467
459
 
468
460
  sig { params(constant: Module, strict: T::Boolean).returns(T::Boolean) }
@@ -473,7 +465,7 @@ module Tapioca
473
465
  return !strict if files.empty?
474
466
 
475
467
  files.any? do |file|
476
- path_in_gem?(file)
468
+ gem.contains_path?(file)
477
469
  end
478
470
  end
479
471
 
@@ -0,0 +1,34 @@
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(:generate_command, String)
12
+ const(:exclude, T::Array[String])
13
+ const(:typed_overrides, T::Hash[String, String])
14
+
15
+ sig { returns(Pathname) }
16
+ def outpath
17
+ @outpath ||= T.let(Pathname.new(outdir), T.nilable(Pathname))
18
+ T.must(@outpath)
19
+ end
20
+
21
+ private_class_method :new
22
+
23
+ CONFIG_FILE_PATH = "sorbet/tapioca/config.yml"
24
+ SORBET_CONFIG = "sorbet/config"
25
+
26
+ DEFAULT_POSTREQUIRE = "sorbet/tapioca/require.rb"
27
+ DEFAULT_OUTDIR = "sorbet/rbi/gems"
28
+ DEFAULT_OVERRIDES = T.let({
29
+ # ActiveSupport overrides some core methods with different signatures
30
+ # so we generate a typed: false RBI for it to suppress errors
31
+ "activesupport" => "false",
32
+ }.freeze, T::Hash[String, String])
33
+ end
34
+ end
@@ -0,0 +1,62 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Tapioca
5
+ class ConfigBuilder
6
+ class << self
7
+ extend(T::Sig)
8
+
9
+ sig { params(options: T::Hash[String, T.untyped]).returns(Config) }
10
+ def from_options(options)
11
+ Config.from_hash(
12
+ merge_options(default_options, config_options, options)
13
+ )
14
+ end
15
+
16
+ private
17
+
18
+ sig { returns(T::Hash[String, T.untyped]) }
19
+ def config_options
20
+ if File.exist?(Config::CONFIG_FILE_PATH)
21
+ YAML.load_file(Config::CONFIG_FILE_PATH, fallback: {})
22
+ else
23
+ {}
24
+ end
25
+ end
26
+
27
+ sig { returns(T::Hash[String, T.untyped]) }
28
+ def default_options
29
+ DEFAULT_OPTIONS
30
+ end
31
+
32
+ sig { returns(String) }
33
+ def default_command
34
+ command = File.basename($PROGRAM_NAME)
35
+ args = ARGV.join(" ")
36
+
37
+ "#{command} #{args}".strip
38
+ end
39
+
40
+ sig { params(options: T::Hash[String, T.untyped]).returns(T::Hash[String, T.untyped]) }
41
+ def merge_options(*options)
42
+ options.each_with_object({}) do |option, result|
43
+ result.merge!(option) do |_, this_val, other_val|
44
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
45
+ this_val.merge(other_val)
46
+ else
47
+ other_val
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ DEFAULT_OPTIONS = T.let({
55
+ "postrequire" => Config::DEFAULT_POSTREQUIRE,
56
+ "outdir" => Config::DEFAULT_OUTDIR,
57
+ "generate_command" => default_command,
58
+ "exclude" => [],
59
+ "typed_overrides" => Config::DEFAULT_OVERRIDES,
60
+ }.freeze, T::Hash[String, T.untyped])
61
+ end
62
+ end
@@ -88,7 +88,7 @@ module Tapioca
88
88
  sig { params(spec: Spec).void }
89
89
  def initialize(spec)
90
90
  @spec = T.let(spec, Tapioca::Gemfile::Spec)
91
- real_gem_path = File.realpath(@spec.full_gem_path.to_s)
91
+ real_gem_path = to_realpath(@spec.full_gem_path)
92
92
  @full_gem_path = T.let(real_gem_path, String)
93
93
  end
94
94
 
@@ -119,8 +119,20 @@ module Tapioca
119
119
  "#{name}@#{version}.rbi"
120
120
  end
121
121
 
122
+ sig { params(path: String).returns(T::Boolean) }
123
+ def contains_path?(path)
124
+ to_realpath(path).start_with?(full_gem_path)
125
+ end
126
+
122
127
  private
123
128
 
129
+ sig { params(path: T.any(String, Pathname)).returns(String) }
130
+ def to_realpath(path)
131
+ path_string = path.to_s
132
+ path_string = File.realpath(path_string) if File.exist?(path_string)
133
+ path_string
134
+ end
135
+
124
136
  sig { returns(T::Boolean) }
125
137
  def gem_ignored?
126
138
  IGNORED_GEMS.include?(name)
@@ -8,41 +8,16 @@ module Tapioca
8
8
  class Generator < ::Thor::Shell::Color
9
9
  extend(T::Sig)
10
10
 
11
- SORBET_CONFIG = "sorbet/config"
12
- DEFAULT_POSTREQUIRE = "sorbet/tapioca/require.rb"
13
- DEFAULT_OUTDIR = "sorbet/rbi/gems"
14
- DEFAULT_OVERRIDES = T.let({
15
- # ActiveSupport overrides some core methods with different signatures
16
- # so we generate a typed: false RBI for it to suppress errors
17
- "activesupport" => "false",
18
- }.freeze, T::Hash[String, String])
19
-
20
- sig { returns(Pathname) }
21
- attr_reader :outdir
22
- sig { returns(T.nilable(String)) }
23
- attr_reader :prerequire
24
- sig { returns(T.nilable(String)) }
25
- attr_reader :postrequire
26
- sig { returns(String) }
27
- attr_reader :command
28
- sig { returns(T::Hash[String, String]) }
29
- attr_reader :typed_overrides
11
+ sig { returns(Config) }
12
+ attr_reader :config
30
13
 
31
14
  sig do
32
15
  params(
33
- outdir: T.nilable(String),
34
- prerequire: T.nilable(String),
35
- postrequire: T.nilable(String),
36
- command: T.nilable(String),
37
- typed_overrides: T.nilable(T::Hash[String, String])
16
+ config: Config
38
17
  ).void
39
18
  end
40
- def initialize(outdir: nil, prerequire: nil, postrequire: nil, command: nil, typed_overrides: nil)
41
- @outdir = T.let(Pathname.new(outdir || DEFAULT_OUTDIR), Pathname)
42
- @prerequire = T.let(prerequire, T.nilable(String))
43
- @postrequire = T.let(postrequire || DEFAULT_POSTREQUIRE, T.nilable(String))
44
- @command = T.let(command || default_command, String)
45
- @typed_overrides = T.let(typed_overrides || {}, T::Hash[String, String])
19
+ def initialize(config)
20
+ @config = config
46
21
  @bundle = T.let(nil, T.nilable(Gemfile))
47
22
  @loader = T.let(nil, T.nilable(Loader))
48
23
  @compiler = T.let(nil, T.nilable(Compilers::SymbolTableCompiler))
@@ -55,13 +30,15 @@ module Tapioca
55
30
  def build_gem_rbis(gem_names)
56
31
  require_gem_file
57
32
 
58
- gems_to_generate(gem_names).map do |gem|
59
- say("Processing '#{gem.name}' gem:", :green)
60
- indent do
61
- compile_rbi(gem)
62
- puts
33
+ gems_to_generate(gem_names)
34
+ .reject { |gem| config.exclude.include?(gem.name) }
35
+ .each do |gem|
36
+ say("Processing '#{gem.name}' gem:", :green)
37
+ indent do
38
+ compile_rbi(gem)
39
+ puts
40
+ end
63
41
  end
64
- end
65
42
 
66
43
  say("All operations performed in working directory.", [:green, :bold])
67
44
  say("Please review changes and commit them.", [:green, :bold])
@@ -86,14 +63,6 @@ module Tapioca
86
63
 
87
64
  private
88
65
 
89
- sig { returns(String) }
90
- def default_command
91
- command = File.basename($PROGRAM_NAME)
92
- args = ARGV.join(" ")
93
-
94
- "#{command} #{args}"
95
- end
96
-
97
66
  sig { returns(Gemfile) }
98
67
  def bundle
99
68
  @bundle ||= Gemfile.new
@@ -112,14 +81,14 @@ module Tapioca
112
81
  sig { void }
113
82
  def require_gem_file
114
83
  say("Requiring all gems to prepare for compiling... ")
115
- loader.load_bundle(prerequire, postrequire)
84
+ loader.load_bundle(config.prerequire, config.postrequire)
116
85
  say(" Done", :green)
117
86
  puts
118
87
  end
119
88
 
120
89
  sig { returns(T::Hash[String, String]) }
121
90
  def existing_rbis
122
- @existing_rbis ||= Pathname.glob((Pathname.new(outdir) / "*@*.rbi").to_s)
91
+ @existing_rbis ||= Pathname.glob((config.outpath / "*@*.rbi").to_s)
123
92
  .map { |f| f.basename(".*").to_s.split('@') }
124
93
  .to_h
125
94
  end
@@ -127,13 +96,14 @@ module Tapioca
127
96
  sig { returns(T::Hash[String, String]) }
128
97
  def expected_rbis
129
98
  @expected_rbis ||= bundle.dependencies
99
+ .reject { |gem| config.exclude.include?(gem.name) }
130
100
  .map { |gem| [gem.name, gem.version.to_s] }
131
101
  .to_h
132
102
  end
133
103
 
134
104
  sig { params(gem_name: String, version: String).returns(Pathname) }
135
105
  def rbi_filename(gem_name, version)
136
- outdir / "#{gem_name}@#{version}.rbi"
106
+ config.outpath / "#{gem_name}@#{version}.rbi"
137
107
  end
138
108
 
139
109
  sig { params(gem_name: String).returns(Pathname) }
@@ -280,18 +250,18 @@ module Tapioca
280
250
  gem_name = set_color(gem.name, :yellow, :bold)
281
251
  say("Compiling #{gem_name}, this may take a few seconds... ")
282
252
 
283
- typed_sigil = typed_overrides[gem.name] || DEFAULT_OVERRIDES[gem.name] || "true"
253
+ typed_sigil = config.typed_overrides[gem.name] || "true"
284
254
 
285
255
  content = compiler.compile(gem)
286
- content.prepend(rbi_header(command, typed_sigil))
256
+ content.prepend(rbi_header(config.generate_command, typed_sigil))
287
257
 
288
- FileUtils.mkdir_p(outdir)
289
- filename = outdir / gem.rbi_file_name
258
+ FileUtils.mkdir_p(config.outdir)
259
+ filename = config.outpath / gem.rbi_file_name
290
260
  File.write(filename.to_s, content)
291
261
 
292
262
  say("Done", :green)
293
263
 
294
- Pathname.glob((outdir / "#{gem.name}@*.rbi").to_s) do |file|
264
+ Pathname.glob((config.outpath / "#{gem.name}@*.rbi").to_s) do |file|
295
265
  remove(file) unless file.basename.to_s == gem.rbi_file_name
296
266
  end
297
267
  end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.2.8"
5
+ VERSION = "0.3.0"
6
6
  end
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.2.8
4
+ version: 0.3.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: 2020-03-24 00:00:00.000000000 Z
14
+ date: 2020-03-26 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: pry
@@ -142,6 +142,8 @@ files:
142
142
  - lib/tapioca/compilers/symbol_table/symbol_generator.rb
143
143
  - lib/tapioca/compilers/symbol_table/symbol_loader.rb
144
144
  - lib/tapioca/compilers/symbol_table_compiler.rb
145
+ - lib/tapioca/config.rb
146
+ - lib/tapioca/config_builder.rb
145
147
  - lib/tapioca/constant_locator.rb
146
148
  - lib/tapioca/gemfile.rb
147
149
  - lib/tapioca/generator.rb