tapioca 0.2.3 → 0.2.4

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: a35a04281a0cc2d423185b2c4ae0425834f9ad2f9b734c47952729759ebc0a37
4
- data.tar.gz: c62118648b16807320ca7fe4ddeac778f9cd158390a9207dc3c27da19d258d02
3
+ metadata.gz: 344a5afaf8a19df78713917f87adf663691d9b3c1ffee9c9e565b1a583eb01f6
4
+ data.tar.gz: d8c867d006f9c37c06d96652f1dd5c7c0311f50dfedc18bab6366fb9b4458319
5
5
  SHA512:
6
- metadata.gz: 7ee51fc4436840502f4ca598922721465d2c9c79f1299a69bc432ead8024429ce97582b71d4777c948013068b37b41147a41ca6b7dcb9ac7118b358ebe387997
7
- data.tar.gz: a4dbac3f24b67b8f99a863869dd6761e5d32fb3baa056a6286449e1bb394cff9f8ca14eaca49f4fac67ccfa916230d8893df70cace62db689e8e14e87bd035a2
6
+ metadata.gz: 353e2c32784e7b4f318a5b2d63d2403b4893adee3623f0957bbf341d159fd77db12abf3a0f18c5f74453553cf62105daeca5c18daa5e5ea43e1ee2fcdd4ad3a6
7
+ data.tar.gz: cb22c7aaa370ca3a4a0d5b7accb907e1f91cac5f94d4161311151fbd1e2abe39662659284d10fde664e0fb92fa309f84dc3ce9feb320f45dbec8fa23417df966
data/README.md CHANGED
@@ -4,13 +4,40 @@
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
 
7
+ As yet, no gem exports type information in a consumable format and it would be a huge effort to manually maintain such an interface file for all the gems that your codebase depends on. Thus, there is a need for an automated way to generate the appropriate RBI file for a given gem. The `tapioca` gem, developed at Shopify, is able to do exactly that to almost 99% accuracy. It can generate the definitions for all statically defined types and most of the runtime defined types exported from Ruby gems (non-Ruby gems are not handled yet).
8
+
9
+ When you run `tapioca sync` in a project, `tapioca` loads all the gems that are in your dependency list from the Gemfile into memory. It then performs runtime introspection on the loaded types to understand their structure and generates an appropriate RBI file for each gem with a versioned filename.
10
+
11
+ For gems that have a normal default `require` and load all of their constants through such a require, everything works seamlessly. However, for gems that are marked as `require: false` in the Gemfile, or for gems that export optionally loaded types via different requires, where a single require does not load the whole gem code into memory, `tapioca` will not be able to load some of the types into memory and, thus, won't be able to generate complete RBIs for them. For this reason, we need to keep a small external file named `sorbet/tapioca/require.rb` that is executed after all the gems in the Gemfile have been required and before generation of gem RBIs have started. This file is responsible for adding the requires for additional files from gems, which are not covered by the default require.
12
+
13
+ For example, suppose you are using the class `BetterHtml::Parser` exported from the `better_html` gem. Just doing a `require "better_html"` (which is the default require) does not load that type:
14
+
15
+ ```ruby
16
+ $ bundle exec pry
17
+ [1] pry(main)> require 'better_html'
18
+ => true
19
+ [2] pry(main)> BetterHtml
20
+ => BetterHtml
21
+ [3] pry(main)> BetterHtml::Parser
22
+ NameError: uninitialized constant BetterHtml::Parser
23
+ from (pry):3:in `__pry__`
24
+ [4] pry(main)> require 'better_html/parser'
25
+ => true
26
+ [5] pry(main)> BetterHtml::Parser
27
+ => BetterHtml::Parser
28
+ ```
29
+
30
+ In order to make sure that `tapioca` can reflect on that type, we need to add the line `require "better_html/parser"` to the `sorbet/tapioca/require.rb` file. This will make sure `BetterHtml::Parser` is loaded into memory and a type annotation is generated for it in the `better_html.rbi` file. If this extra `require` line is not added to `sorbet/tapioca/require.rb` file, then the definition for that type will be missing from the RBI file.
31
+
32
+ If you ever run into a case, where you add a gem or update the version of a gem and run `tapioca sync` but don't have some types you expect in the generated gem RBI files, you will need to make sure you have added the necessary requires to the `sorbet/tapioca/require.rb` file.
33
+
7
34
  ## Installation
8
35
 
9
36
  Add this line to your application's `Gemfile`:
10
37
 
11
38
  ```ruby
12
39
  group :development do
13
- gem 'tapioca', '~> 0.1.2', require: false
40
+ gem 'tapioca', require: false
14
41
  end
15
42
  ```
16
43
 
@@ -257,7 +257,7 @@ module Tapioca
257
257
 
258
258
  inherited_singleton_class_ancestors =
259
259
  if constant.is_a?(Class)
260
- Set.new(constant.superclass.singleton_class.ancestors)
260
+ Set.new(singleton_class_of(constant.superclass).ancestors)
261
261
  else
262
262
  Module.ancestors
263
263
  end
@@ -267,9 +267,9 @@ module Tapioca
267
267
 
268
268
  prepend = interesting_ancestors.take_while { |c| !are_equal?(constant, c) }
269
269
  include = interesting_ancestors.drop(prepend.size + 1)
270
- extend = constant.singleton_class.ancestors
270
+ extend = singleton_class_of(constant).ancestors
271
271
  .reject do |mod|
272
- mod == constant.singleton_class ||
272
+ mod == singleton_class_of(constant) ||
273
273
  inherited_singleton_class_ancestors.include?(mod) ||
274
274
  !public_module?(mod) ||
275
275
  Module != class_of(mod)
@@ -321,7 +321,7 @@ module Tapioca
321
321
  )
322
322
 
323
323
  instance_methods = compile_directly_owned_methods(name, constant)
324
- singleton_methods = compile_directly_owned_methods(name, constant.singleton_class, [:public])
324
+ singleton_methods = compile_directly_owned_methods(name, singleton_class_of(constant), [:public])
325
325
 
326
326
  return if symbol_ignored?(name) && instance_methods.empty? && singleton_methods.empty?
327
327
 
@@ -400,7 +400,7 @@ module Tapioca
400
400
  end
401
401
  end.join(', ')
402
402
 
403
- method_name.prepend(constant.singleton_class? ? 'self.' : '')
403
+ method_name = "#{'self.' if constant.singleton_class?}#{method_name}"
404
404
  parameters = "(#{parameters})" if parameters != ""
405
405
 
406
406
  indented("def #{method_name}#{parameters}; end")
@@ -543,6 +543,11 @@ module Tapioca
543
543
  Module.instance_method(:name).bind(constant).call
544
544
  end
545
545
 
546
+ sig { params(constant: BasicObject).returns(Class) }
547
+ def singleton_class_of(constant)
548
+ Object.instance_method(:singleton_class).bind(constant).call
549
+ end
550
+
546
551
  sig { params(constant: Module).returns(T.nilable(String)) }
547
552
  def name_of(constant)
548
553
  name = name_of_proxy_target(constant)
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.2.3"
5
+ VERSION = "0.2.4"
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.3
4
+ version: 0.2.4
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: 2019-10-07 00:00:00.000000000 Z
14
+ date: 2019-12-10 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: pry