{"id":20683976,"url":"https://github.com/aaronc81/parlour","last_synced_at":"2025-05-15T11:04:20.029Z","repository":{"id":35001271,"uuid":"195240288","full_name":"AaronC81/parlour","owner":"AaronC81","description":"A type signature generator, merger and parser system for Sorbet and Ruby 3/Steep","archived":false,"fork":false,"pushed_at":"2025-04-01T18:25:52.000Z","size":34363,"stargazers_count":94,"open_issues_count":18,"forks_count":18,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-02T10:33:08.759Z","etag":null,"topics":["parlour","rbi","rbs","ruby","ruby3","sorbet"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AaronC81.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-04T12:46:55.000Z","updated_at":"2025-03-03T19:31:59.000Z","dependencies_parsed_at":"2023-02-12T12:01:52.288Z","dependency_job_id":"325ff30b-60fd-435d-a71f-87af1c8dab3a","html_url":"https://github.com/AaronC81/parlour","commit_stats":{"total_commits":487,"total_committers":15,"mean_commits":32.46666666666667,"dds":"0.18275154004106775","last_synced_commit":"8789d419ab32f66fa220069221ab0be175b3808c"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AaronC81%2Fparlour","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AaronC81%2Fparlour/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AaronC81%2Fparlour/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AaronC81%2Fparlour/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AaronC81","download_url":"https://codeload.github.com/AaronC81/parlour/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247622989,"owners_count":20968574,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["parlour","rbi","rbs","ruby","ruby3","sorbet"],"created_at":"2024-11-16T22:18:27.678Z","updated_at":"2025-04-07T09:10:44.628Z","avatar_url":"https://github.com/AaronC81.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Parlour\n\n[![Build Status](https://travis-ci.org/AaronC81/parlour.svg?branch=master)](https://travis-ci.org/AaronC81/parlour)\n![Gem](https://img.shields.io/gem/v/parlour.svg)\n\nParlour is a Ruby type information generator, merger and parser, supporting both\n**Sorbet RBI files and Ruby 3/Steep RBS files**. It consists of three key parts:\n\n  - The generator, which outputs beautifully formatted RBI/RBS files, created \n    using an intuitive DSL.\n\n  - The plugin/build system, which allows multiple Parlour plugins to generate\n    RBIs for the same codebase. These are combined automatically as much as\n    possible, but any other conflicts can be resolved manually through prompts.\n\n  - The parser (currently RBI-only), which can read an RBI and convert it back\n    into a tree of generator objects.\n\n## Why should I use this?\n\n  - Parlour enables **much easier creation of RBI/RBS generators**, as\n    formatting is all handled for you, and you don't need to write your own CLI.\n\n  - You can **use many plugins together seamlessly**, running them all with a\n    single command and consolidating all of their definitions into a single\n    output file.\n\n  - You can **effortlessly build tools which need to access types within an RBI**;\n    no need to write your own parser!\n\n  - You can **generate RBI/RBS to ship with your gem** for consuming projects to\n    use ([see \"RBIs within gems\" in Sorbet's\n    docs](https://sorbet.org/docs/rbi#rbis-within-gems)).\n\nPlease [**read the wiki**](https://github.com/AaronC81/parlour/wiki) to get\nstarted!\n\n## Feature Support\n\n| Feature | RBI | RBS |\n|---------|-----|-----|\n| **GENERATION** |  |  |\n| Classes | ✅ | ⚠️ (missing `extension`) |\n| Modules | ✅ | ⚠️ (missing `extension`) |\n| Interfaces | ✅ | ✅ |\n| Attributes | ✅ | ✅ |\n| Methods | ✅ | ✅ |\n| Overloaded methods | ❌* | ✅ |\n| Structs | ✅ | ✅† |\n| Enums | ✅ | ✅† |\n| Generation with plugins | ✅ | ❌ |\n| **MANIPULATION** |  |  |\n| Parsing | ✅ | ❌ |\n| Merging | ✅ | ❌ |\n\n- ✅ - Well supported\n- ⚠️ - Some missing features\n- ❌ - Not currently supported\n\n- \\* Only supported in stdlib types anyway\n- † Not natively supported; available as a one-way conversion from RBI\n\n## Creating Type Information\n\nEach file format has its own type information generator class, so there are two\ndifferent generators you can use: `RbiGenerator` and `RbsGenerator`. Both\ngenerators are similar to use, however they provide different object types and\nparameters to match the functionality of their underlying type systems.\n\nYou can also convert your type information between formats; see\n[converting between formats](#converting-between-formats).\n\n### Using Just the Generator\n\nHere's a quick example of how you can generate some type information. Here\nwe'll generate an RBI using the `RbiGenerator` classes:\n\n```ruby\nrequire 'parlour'\n\ngenerator = Parlour::RbiGenerator.new\ngenerator.root.create_module('A') do |a|\n  a.create_class('Foo') do |foo|\n    foo.create_method('add_two_integers', parameters: [\n      Parlour::RbiGenerator::Parameter.new('a', type: 'Integer'),\n      Parlour::RbiGenerator::Parameter.new('b', type: 'Integer')\n    ], return_type: 'Integer')\n  end\n\n  a.create_class('Bar', superclass: 'Foo')\nend\n\ngenerator.rbi # =\u003e Our RBI as a string\n```\n\nThis will generate the following RBI:\n\n```ruby\nmodule A\n  class Foo\n    sig { params(a: Integer, b: Integer).returns(Integer) }\n    def add_two_integers(a, b); end\n  end\n\n  class Bar \u003c Foo\n  end\nend\n```\n\nUsing the RBS generator looks similar, but has an intermediary \n`MethodSignature` class to support RBS' method overloading:\n\n```ruby\nrequire 'parlour'\n\ngenerator = Parlour::RbsGenerator.new\ngenerator.root.create_module('A') do |a|\n  a.create_class('Foo') do |foo|\n    foo.create_method('add_two_integers', [\n      Parlour::RbsGenerator::MethodSignature.new(\n        [\n          Parlour::RbsGenerator::Parameter.new('a', type: 'Integer'),\n          Parlour::RbsGenerator::Parameter.new('b', type: 'Integer')\n        ],\n        'Integer'\n      )\n    ])\n  end\n\n  a.create_class('Bar', superclass: 'Foo')\nend\n\ngenerator.rbs # =\u003e Our RBS as a string\n```\n\nThis generates an equivalent RBS file:\n\n```ruby\nmodule A\n  class Foo\n    def add_two_integers: (Integer a, Integer b) -\u003e Integer\n  end\n\n  class Bar \u003c Foo\n  end\nend\n```\n\n### Writing a Plugin\nPlugins are better than using the generator alone, as your plugin can be\ncombined with others to produce larger files without conflicts.\n\nWe could write the above example as an RBI plugin like this:\n\n```ruby\nrequire 'parlour'\n\nclass MyPlugin \u003c Parlour::Plugin\n  def generate(root)\n    root.create_module('A') do |a|\n      a.create_class('Foo') do |foo|\n        foo.create_method('add_two_integers', parameters: [\n          Parlour::RbiGenerator::Parameter.new('a', type: 'Integer'),\n          Parlour::RbiGenerator::Parameter.new('b', type: 'Integer')\n        ], return_type: 'Integer')\n      end\n\n      a.create_class('Bar', superclass: 'Foo')\n    end\n  end\nend\n```\n\n(Obviously, your plugin will probably examine a codebase somehow, to be more\nuseful!)\n\nYou can then run several plugins, combining their output and saving it into one\nRBI file, using the command-line tool. The command line tool is configurated\nusing a `.parlour` YAML file. For example, if that code was in a file\ncalled `plugin.rb`, then using this `.parlour` file and then running `parlour`\nwould save the RBI into `output.rbi`:\n\n```yaml\noutput_file:\n  rbi: output.rbi\n\nrelative_requires:\n  - plugin.rb\n  - app/models/*.rb\n\nplugins:\n  MyPlugin: {}\n```\n\nThe `{}` indicates that this plugin needs no extra configuration. If it did need\nconfiguration, this could be specified like so:\n\n```yaml\nplugins:\n  MyPlugin:\n    foo: something\n    bar: something else\n```\n\nYou can also use plugins from gems. If that plugin was published as a gem called\n`parlour-gem`:\n\n```yaml\noutput_file:\n  rbi: output.rbi\n\nrequires:\n  - parlour-gem\n\nplugins:\n  MyPlugin: {}\n```\n\nThe real power of this is the ability to use many plugins at once:\n\n```yaml\noutput_file:\n  rbi: output.rbi\n\nrequires:\n  - gem1\n  - gem2\n  - gem3\n\nplugins:\n  Gem1::Plugin: {}\n  Gem2::Plugin: {}\n  Gem3::Plugin: {}\n```\n\nCurrently, only plugins which generate RBI files are supported. However, you can\nuse [Parlour's type conversion](#converting-between-formats) to convert the RBI\ntypes into RBS types:\n\n```yaml\noutput_file:\n  rbi: output.rbi\n  rbs: output.rbs\n```\n\n## Using Types\n\nThe most important part of your type information is the types themselves, which\nyou'll be specifying for method parameters, method returns, and attributes. \nThese include simple types like `String`, up to more complex types like\n\"an array of elements which are one of `Integer`, `String`, or nil\".\n\nThere are two ways to represent these types in Parlour:\n\n1. As **generalized types**; that is, instances of classes in the\n   `Parlour::Types` namespace. This is the **recommended way**, as it is\n   format-agnostic and can be compiled to RBI or RBS. For more information\n   about these types and how to use them, see\n   [this wiki page](https://github.com/AaronC81/parlour/wiki/The-Types-namespace).\n\n2. As **strings of code** written in the format that your type system expects.\n   The given strings are directly inserted into the final type file. These types\n   are **not portable across formats**, and as such are\n   **not recommended and may be phased out** in the future.\n\nCurrently most type values within Parlour are typed as `Types::TypeLike`,\nwhich accepts a `String` or a `Types::Type` subclass.\n\n```ruby\ninclude Parlour\n\n# Two ways to express an attribute called 'example', which is:\n#   an array of nilable strings or integers\n\n# 1. With generalised types - type is agnostic to the underlying type system\nroot.create_attr_accessor('example', type:\n  Types::Array.new(\n    Types::Nilable.new(\n      Types::Union.new(['String', 'Integer'])\n    )\n  )\n)\n\n# 2. With string types - format depends on type system\n#    If using RBI...\nroot.create_attr_accessor('example', type:\n  'T::Array[T.nilable(T.any(String, Integer))]'\n)\n#    If using RBS...\nroot.create_attr_accessor('example', type:\n  'Array[?(String | Integer)]'\n)\n```\n\n### Generalizing String Types\n\nIf you have loaded an RBI project or created a structure of nodes on an\n`RbiGenerator`, you can use `#generalize_from_rbi!` on your root namespace\nto attempt to permanently convert the RBI string types into generalized types:\n\n```ruby\n# Build up an RBI tree with string types\nroot.create_attr_accessor('example', type:\n  'T::Array[T.nilable(T.any(String, Integer))]'\n)\n\n# Generalize it\nroot.generalize_from_rbi!\n\n# Take a look at our generalized type!\npp root.children.first.type\n# =\u003e #\u003cParlour::Types::Array:0x0000557cdcebfdf8\n#     @element=\n#      #\u003cParlour::Types::Nilable:0x0000557cdcef8c70\n#       @type=\n#        #\u003cParlour::Types::Union:0x0000557cdcea0a70\n#         @types=\n#          [#\u003cParlour::Types::Raw:0x0000557cdcea1920 @str=\"String\"\u003e,\n#           #\u003cParlour::Types::Raw:0x0000557cdcea0ae8 @str=\"Integer\"\u003e]\u003e\u003e\u003e\n```\n\n## Parsing RBIs\n\nYou can either parse individual RBI files, or point Parlour to the root of a\nproject and it will locate, parse and merge all RBI files.\n\nNote that Parlour isn't limited to just RBIs; it can parse inline `sigs` out\nof your Ruby source too!\n\n```ruby\nrequire 'parlour'\n\n# Return the object tree of a particular file\nParlour::TypeLoader.load_file('path/to/your/file.rbis')\n\n# Return the object tree for an entire Sorbet project - slow but thorough!\nParlour::TypeLoader.load_project('root/of/the/project')\n```\n\nThe structure of the returned object trees is identical to those you would\ncreate when generating an RBI, built of instances of `RbiObject` subclasses.\n\n## Generating RBI for a Gem\n\nInclude `parlour` as a development_dependency in your `.gemspec`:\n\n```ruby\nspec.add_development_dependency 'parlour'\n```\n\nRun Parlour from the command line:\n\n```ruby\nbundle exec parlour\n```\n\nParlour is configured to use sane defaults assuming a standard gem structure\nto generate an RBI that Sorbet will automatically find when your gem is included\nas a dependency. If you require more advanced configuration you can add a\n`.parlour` YAML file in the root of your project (see this project's `.parlour`\nfile as an example).\n\nTo disable the parsing step entire and just run plugins you can set `parser: false`\nin your `.parlour` file.\n\n## Converting Between Formats\n\n_For more information, see the [wiki page](https://github.com/AaronC81/parlour/wiki/Converting-between-RBI-and-RBS)._\n\nCurrently, only RBI to RBS conversion is supported, and if you've used string\ntypes (or are using a freshly-loaded project) you\n**must [generalize them](#generalizing-string-types) first**.\n\nThen, all you need to do is create an `RbsGenerator` (which the converter will\nadd your converted types to) and a `Conversion::RbiToRbs` instance (which \nperforms the conversion). Then you can convert each object at your\n`RbiGenerator`'s root namespace:\n\n```ruby\nrbi_gen = Parlour::RbiGenerator.new\n# Then, after populating the RbiGenerator with types...\n\n# Create an RbsGenerator and a converter\nrbs_gen = Parlour::RbsGenerator.new\nconverter = Parlour::Conversion::RbiToRbs.new(rbs_gen)\n\n# Convert each item at the root of the RbiGenerator and it to the root of the RbsGenerator\nconverter.convert_all(rbi_gen.root, rbs_gen.root)\n```\n\n## Parlour Plugins\n\n_Have you written an awesome Parlour plugin? Please submit a PR to add it to this list!_\n\n  - [Sord](https://github.com/AaronC81/sord) - Generate RBIs from YARD documentation\n  - [parlour-datamapper](https://github.com/AaronC81/parlour-datamapper) - Simple plugin for generating DataMapper model types\n  - [sorbet-rails](https://github.com/chanzuckerberg/sorbet-rails) - Generate RBIs for Rails models, routes, mailers, etc.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/AaronC81/parlour. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\nAfter making changes, you may wish to regenerate the RBI definitions in the `sorbet` folder by running these `srb rbi` commands:\n\n```\nsrb rbi gems\nsrb rbi sorbet-typed\n```\n\nYou should also regenerate the parlour.rbi file by running `bundle exec parlour`. Don't edit this file manually, as your changes will be overwritten!\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the Parlour project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/AaronC81/parlour/blob/master/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faaronc81%2Fparlour","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faaronc81%2Fparlour","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faaronc81%2Fparlour/lists"}