From 8e34ecc08dd637eea9c634e6f2c386c34a00fe7e Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Sat, 24 Feb 2024 14:33:22 -0700 Subject: [PATCH 1/2] Document the file structure of the project Also customize styling of headers so that the smaller ones are a little larger. --- docs/contributors/architecture/structure.md | 375 ++++++++++++++++++++ docs/stylesheets/extra.css | 20 ++ mkdocs.yml | 5 + 3 files changed, 400 insertions(+) create mode 100644 docs/contributors/architecture/structure.md create mode 100644 docs/stylesheets/extra.css diff --git a/docs/contributors/architecture/structure.md b/docs/contributors/architecture/structure.md new file mode 100644 index 00000000..c14f259a --- /dev/null +++ b/docs/contributors/architecture/structure.md @@ -0,0 +1,375 @@ +# Structure + +To help you in your contributing journey, +this page offers insight into how the SuperDiff codebase is laid out. + +Note that this page does not mention every single file: +some files which are unimportant, +such as those which only serve to require other files, +have been omitted. + +## Implementation files + +All implementation files in the project are located in `lib/`, +and most are located in `lib/super_diff/`. + +In `lib/super_diff` there are 7 notable directories, +which create 4 layers: + +```mermaid +flowchart BT + subgraph rails + active_record + active_support + end + + basic --> core + rspec --> basic + active_record --> basic + active_support --> basic + active_record --> core + active_support --> core + rspec --> core + equality_matchers --> core + equality_matchers --> basic + core --> csi + rspec --> csi +``` + +Following is a breakdown of each directory. + +### Top level (`lib/`) + +- **`super_diff.rb`:** + In addition to loading the whole library, + this file contains helpers which are so useful that they can be called anywhere. + +### `SuperDiff::Core` (`lib/super_diff/core/`) + +The Core module provides building blocks, utilities, and other primitives +that are used throughout the library. + +This directory can be grouped into the following themes: + +#### Initialization + +- **`configuration.rb`:** + Stores settings for SuperDiff + which can be used to control the behavior of various features. + +#### Differ + +- **`abstract_differ.rb`:** + The superclass for all differ classes. +- **`differ_dispatcher.rb`:** + Finds the differ class that best matches a combination of "expected" and "actual" values, + then uses it to run the diff between them. +- **`no_differ_available_error.rb`:** + The error produced when the DifferDispatcher fails to find a matching class. + +#### Operation tree + +- **`abstract_operation_tree_builder.rb`:** + The superclass for all operation tree builder classes, + defined elsewhere in SuperDiff or by users. +- **`abstract_operation_tree.rb`:** + The superclass for all operation tree classes, + defined elsewhere in SuperDiff or by users. +- **`abstract_operation_tree_flattener.rb`:** + The superclass for all operation tree flattener classes, + defined elsewhere in SuperDiff or by users. +- **`binary_operation.rb`:** + A node in an operation tree which represents a comparison between two values. +- **`no_operation_tree_available_error.rb`:** + The error produced when the OperationTreeFinder fails to find a matching class. +- **`no_operation_tree_builder_available_error.rb`:** + The error produced when the OperationTreeBuilderDispatcher fails to find a matching class. +- **`operation_tree_builder_dispatcher.rb`:** + Finds the operation tree builder class + that best matches a combination of "expected" and "actual" values, + then uses it to build the operation tree representing the difference between them. +- **`operation_tree_finder.rb`:** + Finds the operation tree matching a value. +- **`unary_operation.rb`:** + A node in an operation tree which represents an operation to a value. + +#### Lines + +- **`line.rb`:** + A deconstructed, mutable version of a line in the final diff output. +- **`tiered_lines_elider.rb`:** + Collapses unchanged sections of a diff, + represented by lines which are indented hierarchically. +- **`tiered_lines_formatter.rb`:** + Takes a collection of diff line objects and produces a diff string. + +#### Object inspection + +- **`inspection_tree_nodes/`:** + Classes which represent directives that can be given + when constructing an inspection tree. + Different directives format the resulting text different ways + or even hide text if it does not match a certain condition. +- **`abstract_inspection_tree_builder.rb`:** + The superclass for all inspection tree builder classes, + defined elsewhere in SuperDiff or by users. +- **`inspection_tree.rb`:** + A domain-specific language used to format the contents of an object. + The result can either be a single-line string, + useful for descriptions and failure messages, + or a series of Line objects, + useful for inserting into diffs. +- **`inspection_tree_builder_dispatcher.rb`:** + Finds the inspection tree builder class that best matches a value, + then uses it to build the inspection tree. +- **`no_inspection_tree_builder_available_error.rb`:** + The error produced when the InspectionTreeBuilderDispatcher fails to find a matching class. +- **`prefix_for_next_inspection_tree_node.rb`:** + A special token used to represent + the result of the prefix passed to the `as_prefix_when_rendering_to_lines` directive. + This prefix needs to be handled specially in the inspection tree rendering logic. +- **`prelude_for_next_inspection_tree_node.rb`:** + A special token used to represent + the result of the prelude passed to the `as_prelude_when_rendering_to_lines` directive. + This prelude needs to be handled specially in the inspection tree rendering logic. +- **`tiered_lines.rb`:** + Represents the final output of an inspection tree, + broken up by lines which are indented hierarchically. + +#### Utilities + +- **`colorized_document_extensions.rb`:** + Extends the ColorizedDocument DSL + so that text can be colored + using names that are not abstract + but rather related to SuperDiff concepts. +- **`gem_version.rb`:** + A wrapper around Gem::Version + which simplifies the interface + so that operators can be used to compare versions. +- **`helpers.rb`:** + Utility methods which can be mixed in various places to do various things. +- **`implementation_checks.rb`:** + Provides a way to define an unimplemented method. + Such a method will raise an error when called + unless it is overridden in a superclass. +- **`recursion_guard.rb`:** + A set of globally accessible methods + which is used to track repeated encounters of objects + as a data structure is descended into + and prevent cyclical or infinite recursion. + +### Feature modules + +There are 4 directories in `lib/super_diff/` which are collectively known as _feature modules_. +Using the primitives from the Core module, +they implement building blocks +to instruct SuperDiff on how to diff many kinds of objects. +As such, you may see one or more of the following directories: + +- `*/differs/` +- `*/inspection_tree_builders/` +- `*/operation_tree_builders/` +- `*/operation_tree_flatteners/` +- `*/operation_trees/` + +With that in mind, here is a list of feature modules. + +#### `SuperDiff::Basic` (`lib/super_diff/basic`) + +This module is so named +because it provides functionality that begins to make SuperDiff useful for users +by adding support for objects that are built into Ruby. + +#### `SuperDiff::ActiveSupport` (`lib/super_diff/active_support`) + +This module makes use of the building blocks in the Basic module +to instruct SuperDiff how to diff ActiveSupport-specific objects, +such as HashWithIndifferentAccess. + +#### `SuperDiff::ActiveRecord` (`lib/super_diff/active_record`) + +This module makes use of the building blocks in the Basic module +to instruct SuperDiff how to diff ActiveRecord-specific objects, +such as models and relation objects. + +#### `SuperDiff::RSpec` (`lib/super_diff/rspec`) + +This module is the largest one +and makes use of the building blocks defined by the Basic module +to provide support for expectation and argument matchers in diffs. +It also patches sections of RSpec to replace its differ with SuperDiff's +and to change failure messages so that they fit better with SuperDiff's philosophy. + +There are a few files and directories of note here: + +- **`matcher_text_builders/`:** + These classes are used to craft descriptions and failure messages for RSpec matchers. +- **`differ.rb`:** + This class stands in for RSpec::Differ + and conditionally diffs an expected and actual value + that has been passed to a matcher. +- **`matcher_text_template.rb`:** + This class is used by matcher text builders + to progressively build up a string by concatenating tokens together. + It is similar to an inspection tree + in that some parts of the string can be rendered differently + depending on whether we've chosen to display the message + in a single line or across multiple lines. + Some tokens can also be colored differently as well. +- **`monkey_patches.rb`:** + The metaphorical skeleton closet, + this file does the dirty and outright sinful work + of overriding various private APIs of the whole RSpec suite of gems + in order to integrate SuperDiff fully into the test framework. + +### Equality matchers (`lib/super_diff/equality_matchers/`) + +This directory offers a way to try out SuperDiff +without integrating it into a test framework. +The classes here are merely thin wrappers around building blocks in the Basic module. + +### CSI (`lib/super_diff/csi/`) + +This directory contains a small library, private to the codebase, +which provides a DSL for colorizing text in the running terminal emulator +with the use of CSI escape sequences. + +## Tests + +Tests are located in `spec/`. +They are divided into two kinds: + +- **Unit tests**, located in `spec/unit/`, + holds tests for individual classes and methods. +- **Integration tests**, located in `spec/integration/`, + construct tiny test suites which import SuperDiff + and run them in isolated environments + to ensure that SuperDiff works as designed + in more real-world scenarios. + +The files in `spec/support/` (imported via `spec_helper.rb`) +contain helpers, matchers, and tests +that are shared among unit and integration tests, +at their respective levels. + +Beyond this, [Zeus][zeus], a way to speed up integration tests, +and is run and configured via these files: + +- `bin/start-dev` +- `config/zeus_plan.rb` +- `support/test_plan.rb` +- `zeus.json` + +The [official Zeus docs](https://github.com/burke/zeus/blob/master/docs/ruby/modifying.md) +is helpful for understanding how these files work together. + +[zeus]: https://github.com/burke/zeus + +## Ruby + +The following files are used to set up Ruby, +specify dependencies and gem publishing settings, +and run tasks: + +- **`.ruby-version`:** + Specifies the Ruby version required for development. + Used by tools like `rbenv` and `asdf.` +- **`bin/setup`:** + Ensures that Ruby, Node, Python, and anything else necessary for development + is installed on your machine. +- **`gemfiles/`:** + Holds generated gemspecs and lockfiles for appraisals. +- **`lib/super_diff/version.rb`:** + Holds the current version of the gem, + which is used by the gemspec. + Updating the constant in this file changes the published version. +- **`spec/support/current_bundle.rb`:** + A support file used by common tasks + which tracks the currently set appraisal or sets a default. +- **`Appraisals`:** + Specifies different gemfiles for different testing environments. +- **`Gemfile`:** + Specifies common development dependencies. +- **`Rakefile`:** + Contains tasks for running tests and publishing the gem. +- **`super_diff.gemspec`:** + Standard configuration file for the gem, + containing the name, description, and production dependencies. + +## Linting + +The following files are used for linting and formatting the codebase: + +- **`.husky/`:** + Contains Git hooks which are installed via `husky install`. +- **`.yarn/`:** + Holds the executable and plugins for [Yarn][yarn], + a package manager for JavaScript. +- **`.prettierignore`:** + Excludes files from being formatted with [Prettier][prettier]. +- **`.prettierrc.json`:** + Configures Prettier. +- **`.nvmrc`:** + Specifies the Node version required to run Prettier. + Used by tools like NVM and `asdf`. +- **`.yarnrc.yml`:** + Configures Yarn. +- **`package.json`:** + Specifies JavaScript dependencies needed to run Prettier. + +[prettier]: https://prettier.io/ +[yarn]: https://yarnpkg.com/ + +## Documentation + +The following files are used for documentation purposes: + +- **`docs/`:** + Contains the documentation that you're reading now! +- **`docs-support/`:** + Other files which are used for documentation purposes, + but are not included in any Markdown files. + Currently only used to generate the screenshots in the README. +- **`.python-version`:** + Specifies the Python version to use for documentation generation. + Used by tools like `pyenv` and `asdf`. +- **`mkdocs.yml`:** + The configuration file for [Mkdocs][mkdocs], + which reads the Markdown files in `docs/` and generates an HTML version. +- **`pyproject.toml`:** + Specifies Python dependencies needed to run Mkdocs. +- **`README.md`:** + The "front of house" for this repository; + summarizes the project's purpose and goals and how to use it. +- **`CHANGELOG.md`:** + Describes the changes that appeared in each release over time. +- **`LICENSE`:** + The full text of the license granted to users of this gem. + +[mkdocs]: https://www.mkdocs.org/ + +## CI + +The following files are used for CI and other automated tasks on GitHub: + +- **`.github/workflows/`:** + Describes tasks to run on GitHub when events occur, + such as automatically running tests on pull requests. +- **`scripts/collect-release-info.rb`:** + A script which is used in one of the GitHub workflows + to determine whether a commit refers to a new release + and, if so, determine the release version. + +## Miscellaneous + +These files don't belong in a particular category: + +- **`.editorconfig`:** + Keeps basic editor-specific configuration, + such as how many spaces to use, line length, etc. +- **`.gitattributes`:** + Used by Git and GitHub + to configure presentation of certain types of files in diffs. +- **`.gitignore`:** + Specifies files that shouldn't show up in the repository. diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 00000000..58a66729 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,20 @@ +.md-typeset h1 { + font-size: 2em; +} + +.md-typeset h2 { + font-size: 1.7em; +} + +.md-typeset h3 { + font-size: 1.445em; +} + +.md-typeset h4 { + font-size: 1.23em; +} + +.md-sidebar__inner > .md-nav > .md-nav__list > .md-nav__item > .md-nav__link { + text-transform: uppercase; + font-size: 0.9em; +} diff --git a/mkdocs.yml b/mkdocs.yml index e2fb439d..22a4ed99 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,14 +29,19 @@ nav: - "How to Contribute": "contributors/how-to-contribute.md" - Architecture: - "Introduction": "contributors/architecture/introduction.md" + - "Structure": "contributors/architecture/structure.md" - "How RSpec works": "contributors/architecture/how-rspec-works.md" - "How SuperDiff works": "contributors/architecture/how-super-diff-works.md" #plugins: #- entangled # this also runs `entangled sync` as a pre-build action +extra_css: + - stylesheets/extra.css + markdown_extensions: - admonition + - def_list - footnotes - smarty - pymdownx.superfences: From 6ab5dc984319794e73552c0dd5c8d699919c42a2 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Fri, 1 Mar 2024 21:58:44 -0700 Subject: [PATCH 2/2] Update the intro to the contributor docs area, place Structure third --- docs/contributors/architecture/introduction.md | 2 ++ mkdocs.yml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/contributors/architecture/introduction.md b/docs/contributors/architecture/introduction.md index 3ca96d41..7e67209a 100644 --- a/docs/contributors/architecture/introduction.md +++ b/docs/contributors/architecture/introduction.md @@ -8,3 +8,5 @@ Since most of the code in this codebase services the RSpec integration, these guides heavily skew toward that topic, and you can start with a [guide to how RSpec works if you like](./how-rspec-works.md). But you can also find [details around the diff engine](./how-super-diff-works.md). +Finally, if you're curious about the files in this project and how they're organized, +feel free to consult the [structure](./structure) document. diff --git a/mkdocs.yml b/mkdocs.yml index 22a4ed99..5b27ef76 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,9 +29,9 @@ nav: - "How to Contribute": "contributors/how-to-contribute.md" - Architecture: - "Introduction": "contributors/architecture/introduction.md" - - "Structure": "contributors/architecture/structure.md" - "How RSpec works": "contributors/architecture/how-rspec-works.md" - "How SuperDiff works": "contributors/architecture/how-super-diff-works.md" + - "Structure": "contributors/architecture/structure.md" #plugins: #- entangled # this also runs `entangled sync` as a pre-build action