parlour 4.0.1 → 5.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +208 -20
  4. data/exe/parlour +45 -6
  5. data/lib/parlour.rb +27 -1
  6. data/lib/parlour/conversion/converter.rb +34 -0
  7. data/lib/parlour/conversion/rbi_to_rbs.rb +223 -0
  8. data/lib/parlour/detached_rbs_generator.rb +25 -0
  9. data/lib/parlour/generator.rb +34 -0
  10. data/lib/parlour/options.rb +71 -0
  11. data/lib/parlour/rbi_generator.rb +24 -37
  12. data/lib/parlour/rbi_generator/arbitrary.rb +5 -2
  13. data/lib/parlour/rbi_generator/attribute.rb +14 -5
  14. data/lib/parlour/rbi_generator/class_namespace.rb +8 -3
  15. data/lib/parlour/rbi_generator/constant.rb +17 -6
  16. data/lib/parlour/rbi_generator/enum_class_namespace.rb +8 -3
  17. data/lib/parlour/rbi_generator/extend.rb +5 -2
  18. data/lib/parlour/rbi_generator/include.rb +5 -2
  19. data/lib/parlour/rbi_generator/method.rb +15 -10
  20. data/lib/parlour/rbi_generator/module_namespace.rb +7 -2
  21. data/lib/parlour/rbi_generator/namespace.rb +39 -12
  22. data/lib/parlour/rbi_generator/parameter.rb +11 -5
  23. data/lib/parlour/rbi_generator/rbi_object.rb +19 -78
  24. data/lib/parlour/rbi_generator/struct_class_namespace.rb +9 -2
  25. data/lib/parlour/rbi_generator/struct_prop.rb +12 -9
  26. data/lib/parlour/rbi_generator/type_alias.rb +101 -0
  27. data/lib/parlour/rbs_generator.rb +24 -0
  28. data/lib/parlour/rbs_generator/arbitrary.rb +92 -0
  29. data/lib/parlour/rbs_generator/attribute.rb +82 -0
  30. data/lib/parlour/rbs_generator/block.rb +49 -0
  31. data/lib/parlour/rbs_generator/class_namespace.rb +106 -0
  32. data/lib/parlour/rbs_generator/constant.rb +95 -0
  33. data/lib/parlour/rbs_generator/extend.rb +92 -0
  34. data/lib/parlour/rbs_generator/include.rb +92 -0
  35. data/lib/parlour/rbs_generator/interface_namespace.rb +34 -0
  36. data/lib/parlour/rbs_generator/method.rb +146 -0
  37. data/lib/parlour/rbs_generator/method_signature.rb +104 -0
  38. data/lib/parlour/rbs_generator/module_namespace.rb +35 -0
  39. data/lib/parlour/rbs_generator/namespace.rb +627 -0
  40. data/lib/parlour/rbs_generator/parameter.rb +145 -0
  41. data/lib/parlour/rbs_generator/rbs_object.rb +78 -0
  42. data/lib/parlour/rbs_generator/type_alias.rb +96 -0
  43. data/lib/parlour/type_parser.rb +152 -0
  44. data/lib/parlour/typed_object.rb +87 -0
  45. data/lib/parlour/types.rb +445 -0
  46. data/lib/parlour/version.rb +1 -1
  47. data/parlour.gemspec +1 -1
  48. data/rbi/parlour.rbi +982 -76
  49. metadata +30 -7
  50. data/lib/parlour/rbi_generator/options.rb +0 -74
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 707934b89fd83fbb876582bd7e66679bc2f8cf2179961eed62358ba6266bf524
4
- data.tar.gz: 3d0a0c8be95e09275a9b02232ce9b9b062a9f00eb3216a0e74ce63a036025d76
3
+ metadata.gz: 8117de48194856013f343430fd893e1b8bc75e51fc8b8811e70bc89a3f9989aa
4
+ data.tar.gz: bc980c3cb70a33c60fc92c23f10f2d844da076161a65e2c24fde49e24d4aceb9
5
5
  SHA512:
6
- metadata.gz: ae723cbe6bebbba12ee3d802b223fa91fcda085f6b1fce4ec107efa3f9e3cc5df53ee0269a6e9d1391ff32c903d15b72153c67fae61392b7e5495688b69b7b0a
7
- data.tar.gz: 98626719eb9d2c08f7e87c1ae48933d8b1afb9badab000bb408a8df26c57716846b71278654193c821ce59a552c83456d1f61523ce5bd9ff0e52d296ab34e274
6
+ metadata.gz: 6bcf142fd126d52f38d2d0d5f597e85d7a4274c321b927230ce100aae9d3941bd6abb7e2cf35ccf597c2b5eaf71121126d5bd85c814d63cdece0e479536740c3
7
+ data.tar.gz: 5af1d6099e12dff2105dca26c4a6db90bba1190018ab7e514dd413e272e3942ef3b608666e91e5fa26b6bdc23f2d886e41ab1d1655085e36ca5f0b94b461448f
@@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file.
3
3
 
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
+ ## [5.0.0.beta.1] - 2020-09-13
7
+ ### Added
8
+ - Added RBS generation support! This includes:
9
+ - The new `RbsGenerator` class
10
+ - `RbsObject` and a set of subclasses representing different RBS components
11
+ - Added the `Types` module, which is used to describe types agnostic of the
12
+ underlying type system
13
+ - Added `RbiGenerator::Namespace#generalize_from_rbi!` to convert RBI string
14
+ types into `Types` types
15
+ - **Specifying types as strings is still currently supported, but may be
16
+ phased out in future, and should be avoided in new projects**.
17
+ - Added conversion from RBI to RBS type trees
18
+ - Added a couple of classes to deduplicate functionality between type systems:
19
+ - `TypedObject`, which `RbiObject` and `RbsObject` both inherit from
20
+ - `Generator`, which `RbiGenerator` and `RbsGenerator` both inherit from
21
+ - Added RBI type aliases
22
+
23
+ ### Changed
24
+ - `Parlour::RbiGenerator::Options` is now `Parlour::Options`. An alias exists
25
+ for now, but **`Parlour::RbiGenerator::Options` is deprecated** and could be
26
+ removed in future versions.
27
+ - Updated README and gem metadata to refer to Parlour as a type information
28
+ generator, rather than just an RBI generator
29
+
6
30
  ## [4.0.1] - 2020-08-05
7
31
  ### Fixed
8
32
  - Fixed duplicate includes and extends.
data/README.md CHANGED
@@ -3,42 +3,77 @@
3
3
  [![Build Status](https://travis-ci.org/AaronC81/parlour.svg?branch=master)](https://travis-ci.org/AaronC81/parlour)
4
4
  ![Gem](https://img.shields.io/gem/v/parlour.svg)
5
5
 
6
- Parlour is an RBI generator, merger and parser for Sorbet. It consists of three
7
- key parts:
6
+ Parlour is a Ruby type information generator, merger and parser, supporting both
7
+ **Sorbet RBI files and Ruby 3/Steep RBS files**. It consists of three key parts:
8
8
 
9
- - The generator, which outputs beautifully formatted RBI files, created using
10
- an intuitive DSL.
9
+ - The generator, which outputs beautifully formatted RBI/RBS files, created
10
+ using an intuitive DSL.
11
11
 
12
12
  - The plugin/build system, which allows multiple Parlour plugins to generate
13
13
  RBIs for the same codebase. These are combined automatically as much as
14
14
  possible, but any other conflicts can be resolved manually through prompts.
15
15
 
16
- - The parser, which can read an RBI and convert it back into a tree of
17
- generator objects.
16
+ - The parser (currently RBI-only), which can read an RBI and convert it back
17
+ into a tree of generator objects.
18
18
 
19
19
  ## Why should I use this?
20
20
 
21
- - Parlour enables **much easier creation of RBI generators**, as formatting
22
- is all handled for you, and you don't need to write your own CLI.
21
+ - Parlour enables **much easier creation of RBI/RBS generators**, as
22
+ formatting is all handled for you, and you don't need to write your own CLI.
23
23
 
24
24
  - You can **use many plugins together seamlessly**, running them all with a
25
25
  single command and consolidating all of their definitions into a single
26
- RBI output file.
26
+ output file.
27
27
 
28
28
  - You can **effortlessly build tools which need to access types within an RBI**;
29
29
  no need to write your own parser!
30
30
 
31
- - You can **generate RBI to ship with your gem** for consuming projects to use
32
- ([see "RBIs within gems" in Sorbet's docs](https://sorbet.org/docs/rbi#rbis-within-gems)).
31
+ - You can **generate RBI/RBS to ship with your gem** for consuming projects to
32
+ use ([see "RBIs within gems" in Sorbet's
33
+ docs](https://sorbet.org/docs/rbi#rbis-within-gems)).
33
34
 
34
35
  Please [**read the wiki**](https://github.com/AaronC81/parlour/wiki) to get
35
36
  started!
36
37
 
37
- ## Creating RBIs
38
+ ## Feature Support
38
39
 
39
- ### Using just the generator
40
+ | Feature | RBI | RBS |
41
+ |---------|-----|-----|
42
+ | **GENERATION** | | |
43
+ | Classes | ✅ | ⚠️ (missing `extension`) |
44
+ | Modules | ✅ | ⚠️ (missing `extension`) |
45
+ | Interfaces | ✅ | ✅ |
46
+ | Attributes | ✅ | ✅ |
47
+ | Methods | ✅ | ✅ |
48
+ | Overloaded methods | ❌* | ✅ |
49
+ | Structs | ✅ | ✅† |
50
+ | Enums | ✅ | ✅† |
51
+ | Generation with plugins | ✅ | ❌ |
52
+ | **MANIPULATION** | | |
53
+ | Parsing | ✅ | ❌ |
54
+ | Merging | ✅ | ❌ |
40
55
 
41
- Here's a quick example of how you can generate an RBI:
56
+ - - Well supported
57
+ - ⚠️ - Some missing features
58
+ - ❌ - Not currently supported
59
+
60
+ - \* Only supported in stdlib types anyway
61
+ - † Not natively supported; available as a one-way conversion from RBI
62
+
63
+ ## Creating Type Information
64
+
65
+ Each file format has its own type information generator class, so there are two
66
+ different generators you can use: `RbiGenerator` and `RbsGenerator`. Both
67
+ generators are similar to use, however they provide different object types and
68
+ parameters to match the functionality of their underlying type systems.
69
+
70
+ You can also convert your type information between formats; see
71
+ [converting between formats](#converting-between-formats).
72
+
73
+ ### Using Just the Generator
74
+
75
+ Here's a quick example of how you can generate some type information. Here
76
+ we'll generate an RBI using the `RbiGenerator` classes:
42
77
 
43
78
  ```ruby
44
79
  require 'parlour'
@@ -72,11 +107,50 @@ module A
72
107
  end
73
108
  ```
74
109
 
75
- ### Writing a plugin
110
+ Using the RBS generator looks similar, but has an intermediary
111
+ `MethodSignature` class to support RBS' method overloading:
112
+
113
+ ```ruby
114
+ require 'parlour'
115
+
116
+ generator = Parlour::RbsGenerator.new
117
+ generator.root.create_module('A') do |a|
118
+ a.create_class('Foo') do |foo|
119
+ foo.create_method('add_two_integers', [
120
+ Parlour::RbsGenerator::MethodSignature.new(
121
+ [
122
+ Parlour::RbsGenerator::Parameter.new('a', type: 'Integer'),
123
+ Parlour::RbsGenerator::Parameter.new('b', type: 'Integer')
124
+ ],
125
+ 'Integer'
126
+ )
127
+ ])
128
+ end
129
+
130
+ a.create_class('Bar', superclass: 'Foo')
131
+ end
132
+
133
+ generator.rbs # => Our RBS as a string
134
+ ```
135
+
136
+ This generates an equivalent RBS file:
137
+
138
+ ```ruby
139
+ module A
140
+ class Foo
141
+ def add_two_integers: (Integer a, Integer b) -> Integer
142
+ end
143
+
144
+ class Bar < Foo
145
+ end
146
+ end
147
+ ```
148
+
149
+ ### Writing a Plugin
76
150
  Plugins are better than using the generator alone, as your plugin can be
77
- combined with others to produce larger RBIs without conflicts.
151
+ combined with others to produce larger files without conflicts.
78
152
 
79
- We could write the above example as a plugin like this:
153
+ We could write the above example as an RBI plugin like this:
80
154
 
81
155
  ```ruby
82
156
  require 'parlour'
@@ -107,7 +181,8 @@ called `plugin.rb`, then using this `.parlour` file and then running `parlour`
107
181
  would save the RBI into `output.rbi`:
108
182
 
109
183
  ```yaml
110
- output_file: output.rbi
184
+ output_file:
185
+ rbi: output.rbi
111
186
 
112
187
  relative_requires:
113
188
  - plugin.rb
@@ -131,7 +206,8 @@ You can also use plugins from gems. If that plugin was published as a gem called
131
206
  `parlour-gem`:
132
207
 
133
208
  ```yaml
134
- output_file: output.rbi
209
+ output_file:
210
+ rbi: output.rbi
135
211
 
136
212
  requires:
137
213
  - parlour-gem
@@ -143,7 +219,8 @@ plugins:
143
219
  The real power of this is the ability to use many plugins at once:
144
220
 
145
221
  ```yaml
146
- output_file: output.rbi
222
+ output_file:
223
+ rbi: output.rbi
147
224
 
148
225
  requires:
149
226
  - gem1
@@ -156,6 +233,92 @@ plugins:
156
233
  Gem3::Plugin: {}
157
234
  ```
158
235
 
236
+ Currently, only plugins which generate RBI files are supported. However, you can
237
+ use [Parlour's type conversion](#converting-between-formats) to convert the RBI
238
+ types into RBS types:
239
+
240
+ ```yaml
241
+ output_file:
242
+ rbi: output.rbi
243
+ rbs: output.rbs
244
+ ```
245
+
246
+ ## Using Types
247
+
248
+ The most important part of your type information is the types themselves, which
249
+ you'll be specifying for method parameters, method returns, and attributes.
250
+ These include simple types like `String`, up to more complex types like
251
+ "an array of elements which are one of `Integer`, `String`, or nil".
252
+
253
+ There are two ways to represent these types in Parlour:
254
+
255
+ 1. As **generalized types**; that is, instances of classes in the
256
+ `Parlour::Types` namespace. This is the **recommended way**, as it is
257
+ format-agnostic and can be compiled to RBI or RBS. For more information
258
+ about these types and how to use them, see
259
+ [this wiki page](https://github.com/AaronC81/parlour/wiki/The-Types-namespace).
260
+
261
+ 2. As **strings of code** written in the format that your type system expects.
262
+ The given strings are directly inserted into the final type file. These types
263
+ are **not portable across formats**, and as such are
264
+ **not recommended and may be phased out** in the future.
265
+
266
+ Currently most type values within Parlour are typed as `Types::TypeLike`,
267
+ which accepts a `String` or a `Types::Type` subclass.
268
+
269
+ ```ruby
270
+ include Parlour
271
+
272
+ # Two ways to express an attribute called 'example', which is:
273
+ # an array of nilable strings or integers
274
+
275
+ # 1. With generalised types - type is agnostic to the underlying type system
276
+ root.create_attr_accessor('example', type:
277
+ Types::Array.new(
278
+ Types::Nilable.new(
279
+ Types::Union.new(['String', 'Integer'])
280
+ )
281
+ )
282
+ )
283
+
284
+ # 2. With string types - format depends on type system
285
+ # If using RBI...
286
+ root.create_attr_accessor('example', type:
287
+ 'T::Array[T.nilable(T.any(String, Integer))]'
288
+ )
289
+ # If using RBS...
290
+ root.create_attr_accessor('example', type:
291
+ 'Array[?(String | Integer)]'
292
+ )
293
+ ```
294
+
295
+ ### Generalizing String Types
296
+
297
+ If you have loaded an RBI project or created a structure of nodes on an
298
+ `RbiGenerator`, you can use `#generalize_from_rbi!` on your root namespace
299
+ to attempt to permanently convert the RBI string types into generalized types:
300
+
301
+ ```ruby
302
+ # Build up an RBI tree with string types
303
+ root.create_attr_accessor('example', type:
304
+ 'T::Array[T.nilable(T.any(String, Integer))]'
305
+ )
306
+
307
+ # Generalize it
308
+ root.generalize_from_rbi!
309
+
310
+ # Take a look at our generalized type!
311
+ pp root.children.first.type
312
+ # => #<Parlour::Types::Array:0x0000557cdcebfdf8
313
+ # @element=
314
+ # #<Parlour::Types::Nilable:0x0000557cdcef8c70
315
+ # @type=
316
+ # #<Parlour::Types::Union:0x0000557cdcea0a70
317
+ # @types=
318
+ # [#<Parlour::Types::Raw:0x0000557cdcea1920 @str="String">,
319
+ # #<Parlour::Types::Raw:0x0000557cdcea0ae8 @str="Integer">]>>>
320
+ ```
321
+
159
322
  ## Parsing RBIs
160
323
 
161
324
  You can either parse individual RBI files, or point Parlour to the root of a
@@ -200,6 +363,31 @@ file as an example).
200
363
  To disable the parsing step entire and just run plugins you can set `parser: false`
201
364
  in your `.parlour` file.
202
365
 
366
+ ## Converting Between Formats
367
+
368
+ _For more information, see the [wiki page](https://github.com/AaronC81/parlour/wiki/Converting-between-RBI-and-RBS)._
369
+
370
+ Currently, only RBI to RBS conversion is supported, and if you've used string
371
+ types (or are using a freshly-loaded project) you
372
+ **must [generalize them](#generalizing-string-types) first**.
373
+
374
+ Then, all you need to do is create an `RbsGenerator` (which the converter will
375
+ add your converted types to) and a `Conversion::RbiToRbs` instance (which
376
+ performs the conversion). Then you can convert each object at your
377
+ `RbiGenerator`'s root namespace:
378
+
379
+ ```ruby
380
+ rbi_gen = Parlour::RbiGenerator.new
381
+ # Then, after populating the RbiGenerator with types...
382
+
383
+ # Create an RbsGenerator and a converter
384
+ rbs_gen = Parlour::RbsGenerator.new
385
+ converter = Parlour::Conversion::RbiToRbs.new(rbs_gen)
386
+
387
+ # Convert each item at the root of the RbiGenerator and it to the root of the RbsGenerator
388
+ converter.convert_all(rbi_gen.root, rbs_gen.root)
389
+ ```
390
+
203
391
  ## Parlour Plugins
204
392
 
205
393
  _Have you written an awesome Parlour plugin? Please submit a PR to add it to this list!_
@@ -25,8 +25,33 @@ command :run do |c|
25
25
  configuration = {}
26
26
  end
27
27
 
28
- # Output default
29
- configuration[:output_file] ||= "rbi/#{File.basename(working_dir)}.rbi"
28
+ # Output file
29
+ if configuration[:output_file].is_a?(String)
30
+ assumed_format = \
31
+ if configuration[:output_file].end_with?('.rbi')
32
+ :rbi
33
+ elsif configuration[:output_file].end_with?('.rbs')
34
+ :rbs
35
+ else
36
+ raise 'invalid output file; please specify an RBI or RBS file'
37
+ end
38
+
39
+ unless $VERBOSE.nil?
40
+ print Rainbow("Parlour warning: ").yellow.dark.bold
41
+ print Rainbow("CLI: ").magenta.bright.bold
42
+ puts "Specifying output_file in .parlour as a string is deprecated."
43
+ puts "For now, generating an #{assumed_format.to_s.upcase} file based on the file extension."
44
+ puts "Please update your .parlour to use the new form:"
45
+ puts " output_file:"
46
+ puts " #{assumed_format}: #{configuration[:output_file]}"
47
+ end
48
+ configuration[:output_file] = {
49
+ assumed_format => configuration[:output_file]
50
+ }
51
+ end
52
+ configuration[:output_file] ||= {
53
+ rbi: "rbi/#{File.basename(working_dir)}.rbi"
54
+ }
30
55
 
31
56
  # Style defaults
32
57
  configuration[:style] ||= {}
@@ -45,7 +70,7 @@ command :run do |c|
45
70
  configuration[:parser][:excluded_paths] ||= ['sorbet', 'spec']
46
71
 
47
72
  # Defaults can be overridden but we always want to exclude the output file
48
- configuration[:parser][:excluded_paths] << configuration[:output_file]
73
+ configuration[:parser][:excluded_paths] << configuration[:output_file][:rbi]
49
74
  end
50
75
 
51
76
  # Included/Excluded module defaults
@@ -141,9 +166,23 @@ command :run do |c|
141
166
  end
142
167
  end
143
168
 
144
- # Write the final RBI
145
- FileUtils.mkdir_p(File.dirname(configuration[:output_file]))
146
- File.write(configuration[:output_file], gen.rbi(strictness))
169
+ # Write the final files
170
+ if configuration[:output_file][:rbi]
171
+ FileUtils.mkdir_p(File.dirname(configuration[:output_file][:rbi]))
172
+ File.write(configuration[:output_file][:rbi], gen.rbi(strictness))
173
+ end
174
+ if configuration[:output_file][:rbs]
175
+ gen.root.generalize_from_rbi!
176
+ rbs_gen = Parlour::RbsGenerator.new
177
+
178
+ converter = Parlour::Conversion::RbiToRbs.new(rbs_gen)
179
+ gen.root.children.each do |child|
180
+ converter.convert_object(child, rbs_gen.root)
181
+ end
182
+
183
+ FileUtils.mkdir_p(File.dirname(configuration[:output_file][:rbs]))
184
+ File.write(configuration[:output_file][:rbs], rbs_gen.rbs)
185
+ end
147
186
  end
148
187
  end
149
188
 
@@ -9,12 +9,17 @@ require 'parlour/kernel_hack'
9
9
 
10
10
  require 'parlour/plugin'
11
11
 
12
+ require 'parlour/types'
13
+
14
+ require 'parlour/options'
15
+ require 'parlour/typed_object'
16
+ require 'parlour/generator'
12
17
  require 'parlour/rbi_generator/parameter'
13
18
  require 'parlour/rbi_generator/rbi_object'
19
+ require 'parlour/rbi_generator/type_alias'
14
20
  require 'parlour/rbi_generator/method'
15
21
  require 'parlour/rbi_generator/attribute'
16
22
  require 'parlour/rbi_generator/arbitrary'
17
- require 'parlour/rbi_generator/options'
18
23
  require 'parlour/rbi_generator/include'
19
24
  require 'parlour/rbi_generator/extend'
20
25
  require 'parlour/rbi_generator/constant'
@@ -27,6 +32,27 @@ require 'parlour/rbi_generator/struct_class_namespace'
27
32
  require 'parlour/rbi_generator'
28
33
  require 'parlour/detached_rbi_generator'
29
34
 
35
+ require 'parlour/rbs_generator/rbs_object'
36
+ require 'parlour/rbs_generator/type_alias'
37
+ require 'parlour/rbs_generator/namespace'
38
+ require 'parlour/rbs_generator/method'
39
+ require 'parlour/rbs_generator/arbitrary'
40
+ require 'parlour/rbs_generator/attribute'
41
+ require 'parlour/rbs_generator/block'
42
+ require 'parlour/rbs_generator/class_namespace'
43
+ require 'parlour/rbs_generator/constant'
44
+ require 'parlour/rbs_generator/extend'
45
+ require 'parlour/rbs_generator/include'
46
+ require 'parlour/rbs_generator/method_signature'
47
+ require 'parlour/rbs_generator/module_namespace'
48
+ require 'parlour/rbs_generator/interface_namespace'
49
+ require 'parlour/rbs_generator/parameter'
50
+ require 'parlour/rbs_generator'
51
+ require 'parlour/detached_rbs_generator'
52
+
53
+ require 'parlour/conversion/converter'
54
+ require 'parlour/conversion/rbi_to_rbs'
55
+
30
56
  require 'parlour/conflict_resolver'
31
57
 
32
58
  require 'parlour/parse_error'