|
| 1 | +# Structure |
| 2 | + |
| 3 | +To help you in your contributing journey, |
| 4 | +this page offers insight into how the SuperDiff codebase is laid out. |
| 5 | + |
| 6 | +Note that this page does not mention every single file: |
| 7 | +some files which are unimportant, |
| 8 | +such as those which only serve to require other files, |
| 9 | +have been omitted. |
| 10 | + |
| 11 | +## Implementation files |
| 12 | + |
| 13 | +All implementation files in the project are located in `lib/`, |
| 14 | +and most are located in `lib/super_diff/`. |
| 15 | + |
| 16 | +In `lib/super_diff` there are 7 notable directories, |
| 17 | +which create 4 layers: |
| 18 | + |
| 19 | +```mermaid |
| 20 | +flowchart BT |
| 21 | + subgraph rails |
| 22 | + active_record |
| 23 | + active_support |
| 24 | + end |
| 25 | +
|
| 26 | + basic --> core |
| 27 | + rspec --> basic |
| 28 | + active_record --> basic |
| 29 | + active_support --> basic |
| 30 | + active_record --> core |
| 31 | + active_support --> core |
| 32 | + rspec --> core |
| 33 | + equality_matchers --> core |
| 34 | + equality_matchers --> basic |
| 35 | + core --> csi |
| 36 | + rspec --> csi |
| 37 | +``` |
| 38 | + |
| 39 | +Following is a breakdown of each directory. |
| 40 | + |
| 41 | +### Top level (`lib/`) |
| 42 | + |
| 43 | +- **`super_diff.rb`:** |
| 44 | + In addition to loading the whole library, |
| 45 | + this file contains helpers which are so useful that they can be called anywhere. |
| 46 | + |
| 47 | +### `SuperDiff::Core` (`lib/super_diff/core/`) |
| 48 | + |
| 49 | +The Core module provides building blocks, utilities, and other primitives |
| 50 | +that are used throughout the library. |
| 51 | + |
| 52 | +This directory can be grouped into the following themes: |
| 53 | + |
| 54 | +#### Initialization |
| 55 | + |
| 56 | +- **`configuration.rb`:** |
| 57 | + Stores settings for SuperDiff |
| 58 | + which can be used to control the behavior of various features. |
| 59 | + |
| 60 | +#### Differ |
| 61 | + |
| 62 | +- **`abstract_differ.rb`:** |
| 63 | + The superclass for all differ classes. |
| 64 | +- **`differ_dispatcher.rb`:** |
| 65 | + Finds the differ class that best matches a combination of "expected" and "actual" values, |
| 66 | + then uses it to run the diff between them. |
| 67 | +- **`no_differ_available_error.rb`:** |
| 68 | + The error produced when the DifferDispatcher fails to find a matching class. |
| 69 | + |
| 70 | +#### Operation tree |
| 71 | + |
| 72 | +- **`abstract_operation_tree_builder.rb`:** |
| 73 | + The superclass for all operation tree builder classes, |
| 74 | + defined elsewhere in SuperDiff or by users. |
| 75 | +- **`abstract_operation_tree.rb`:** |
| 76 | + The superclass for all operation tree classes, |
| 77 | + defined elsewhere in SuperDiff or by users. |
| 78 | +- **`abstract_operation_tree_flattener.rb`:** |
| 79 | + The superclass for all operation tree flattener classes, |
| 80 | + defined elsewhere in SuperDiff or by users. |
| 81 | +- **`binary_operation.rb`:** |
| 82 | + A node in an operation tree which represents a comparison between two values. |
| 83 | +- **`no_operation_tree_available_error.rb`:** |
| 84 | + The error produced when the OperationTreeFinder fails to find a matching class. |
| 85 | +- **`no_operation_tree_builder_available_error.rb`:** |
| 86 | + The error produced when the OperationTreeBuilderDispatcher fails to find a matching class. |
| 87 | +- **`operation_tree_builder_dispatcher.rb`:** |
| 88 | + Finds the operation tree builder class |
| 89 | + that best matches a combination of "expected" and "actual" values, |
| 90 | + then uses it to build the operation tree representing the difference between them. |
| 91 | +- **`operation_tree_finder.rb`:** |
| 92 | + Finds the operation tree matching a value. |
| 93 | +- **`unary_operation.rb`:** |
| 94 | + A node in an operation tree which represents an operation to a value. |
| 95 | + |
| 96 | +#### Lines |
| 97 | + |
| 98 | +- **`line.rb`:** |
| 99 | + A deconstructed, mutable version of a line in the final diff output. |
| 100 | +- **`tiered_lines_elider.rb`:** |
| 101 | + Collapses unchanged sections of a diff, |
| 102 | + represented by lines which are indented hierarchically. |
| 103 | +- **`tiered_lines_formatter.rb`:** |
| 104 | + Takes a collection of diff line objects and produces a diff string. |
| 105 | + |
| 106 | +#### Object inspection |
| 107 | + |
| 108 | +- **`inspection_tree_nodes/`:** |
| 109 | + Classes which represent directives that can be given |
| 110 | + when constructing an inspection tree. |
| 111 | + Different directives format the resulting text different ways |
| 112 | + or even hide text if it does not match a certain condition. |
| 113 | +- **`abstract_inspection_tree_builder.rb`:** |
| 114 | + The superclass for all inspection tree builder classes, |
| 115 | + defined elsewhere in SuperDiff or by users. |
| 116 | +- **`inspection_tree.rb`:** |
| 117 | + A domain-specific language used to format the contents of an object. |
| 118 | + The result can either be a single-line string, |
| 119 | + useful for descriptions and failure messages, |
| 120 | + or a series of Line objects, |
| 121 | + useful for inserting into diffs. |
| 122 | +- **`inspection_tree_builder_dispatcher.rb`:** |
| 123 | + Finds the inspection tree builder class that best matches a value, |
| 124 | + then uses it to build the inspection tree. |
| 125 | +- **`no_inspection_tree_builder_available_error.rb`:** |
| 126 | + The error produced when the InspectionTreeBuilderDispatcher fails to find a matching class. |
| 127 | +- **`prefix_for_next_inspection_tree_node.rb`:** |
| 128 | + A special token used to represent |
| 129 | + the result of the prefix passed to the `as_prefix_when_rendering_to_lines` directive. |
| 130 | + This prefix needs to be handled specially in the inspection tree rendering logic. |
| 131 | +- **`prelude_for_next_inspection_tree_node.rb`:** |
| 132 | + A special token used to represent |
| 133 | + the result of the prelude passed to the `as_prelude_when_rendering_to_lines` directive. |
| 134 | + This prelude needs to be handled specially in the inspection tree rendering logic. |
| 135 | +- **`tiered_lines.rb`:** |
| 136 | + Represents the final output of an inspection tree, |
| 137 | + broken up by lines which are indented hierarchically. |
| 138 | + |
| 139 | +#### Utilities |
| 140 | + |
| 141 | +- **`colorized_document_extensions.rb`:** |
| 142 | + Extends the ColorizedDocument DSL |
| 143 | + so that text can be colored |
| 144 | + using names that are not abstract |
| 145 | + but rather related to SuperDiff concepts. |
| 146 | +- **`gem_version.rb`:** |
| 147 | + A wrapper around Gem::Version |
| 148 | + which simplifies the interface |
| 149 | + so that operators can be used to compare versions. |
| 150 | +- **`helpers.rb`:** |
| 151 | + Utility methods which can be mixed in various places to do various things. |
| 152 | +- **`implementation_checks.rb`:** |
| 153 | + Provides a way to define an unimplemented method. |
| 154 | + Such a method will raise an error when called |
| 155 | + unless it is overridden in a superclass. |
| 156 | +- **`recursion_guard.rb`:** |
| 157 | + A set of globally accessible methods |
| 158 | + which is used to track repeated encounters of objects |
| 159 | + as a data structure is descended into |
| 160 | + and prevent cyclical or infinite recursion. |
| 161 | + |
| 162 | +### Feature modules |
| 163 | + |
| 164 | +There are 4 directories in `lib/super_diff/` which are collectively known as _feature modules_. |
| 165 | +Using the primitives from the Core module, |
| 166 | +they implement building blocks |
| 167 | +to instruct SuperDiff on how to diff many kinds of objects. |
| 168 | +As such, you may see one or more of the following directories: |
| 169 | + |
| 170 | +- `*/differs/` |
| 171 | +- `*/inspection_tree_builders/` |
| 172 | +- `*/operation_tree_builders/` |
| 173 | +- `*/operation_tree_flatteners/` |
| 174 | +- `*/operation_trees/` |
| 175 | + |
| 176 | +With that in mind, here is a list of feature modules. |
| 177 | + |
| 178 | +#### `SuperDiff::Basic` (`lib/super_diff/basic`) |
| 179 | + |
| 180 | +This module is so named |
| 181 | +because it provides functionality that begins to make SuperDiff useful for users |
| 182 | +by adding support for objects that are built into Ruby. |
| 183 | + |
| 184 | +#### `SuperDiff::ActiveSupport` (`lib/super_diff/active_support`) |
| 185 | + |
| 186 | +This module makes use of the building blocks in the Basic module |
| 187 | +to instruct SuperDiff how to diff ActiveSupport-specific objects, |
| 188 | +such as HashWithIndifferentAccess. |
| 189 | + |
| 190 | +#### `SuperDiff::ActiveRecord` (`lib/super_diff/active_record`) |
| 191 | + |
| 192 | +This module makes use of the building blocks in the Basic module |
| 193 | +to instruct SuperDiff how to diff ActiveRecord-specific objects, |
| 194 | +such as models and relation objects. |
| 195 | + |
| 196 | +#### `SuperDiff::RSpec` (`lib/super_diff/rspec`) |
| 197 | + |
| 198 | +This module is the largest one |
| 199 | +and makes use of the building blocks defined by the Basic module |
| 200 | +to provide support for expectation and argument matchers in diffs. |
| 201 | +It also patches sections of RSpec to replace its differ with SuperDiff's |
| 202 | +and to change failure messages so that they fit better with SuperDiff's philosophy. |
| 203 | + |
| 204 | +There are a few files and directories of note here: |
| 205 | + |
| 206 | +- **`matcher_text_builders/`:** |
| 207 | + These classes are used to craft descriptions and failure messages for RSpec matchers. |
| 208 | +- **`differ.rb`:** |
| 209 | + This class stands in for RSpec::Differ |
| 210 | + and conditionally diffs an expected and actual value |
| 211 | + that has been passed to a matcher. |
| 212 | +- **`matcher_text_template.rb`:** |
| 213 | + This class is used by matcher text builders |
| 214 | + to progressively build up a string by concatenating tokens together. |
| 215 | + It is similar to an inspection tree |
| 216 | + in that some parts of the string can be rendered differently |
| 217 | + depending on whether we've chosen to display the message |
| 218 | + in a single line or across multiple lines. |
| 219 | + Some tokens can also be colored differently as well. |
| 220 | +- **`monkey_patches.rb`:** |
| 221 | + The metaphorical skeleton closet, |
| 222 | + this file does the dirty and outright sinful work |
| 223 | + of overriding various private APIs of the whole RSpec suite of gems |
| 224 | + in order to integrate SuperDiff fully into the test framework. |
| 225 | + |
| 226 | +### Equality matchers (`lib/super_diff/equality_matchers/`) |
| 227 | + |
| 228 | +This directory offers a way to try out SuperDiff |
| 229 | +without integrating it into a test framework. |
| 230 | +The classes here are merely thin wrappers around building blocks in the Basic module. |
| 231 | + |
| 232 | +### CSI (`lib/super_diff/csi/`) |
| 233 | + |
| 234 | +This directory contains a small library, private to the codebase, |
| 235 | +which provides a DSL for colorizing text in the running terminal emulator |
| 236 | +with the use of CSI escape sequences. |
| 237 | + |
| 238 | +## Tests |
| 239 | + |
| 240 | +Tests are located in `spec/`. |
| 241 | +They are divided into two kinds: |
| 242 | + |
| 243 | +- **Unit tests**, located in `spec/unit/`, |
| 244 | + holds tests for individual classes and methods. |
| 245 | +- **Integration tests**, located in `spec/integration/`, |
| 246 | + construct tiny test suites which import SuperDiff |
| 247 | + and run them in isolated environments |
| 248 | + to ensure that SuperDiff works as designed |
| 249 | + in more real-world scenarios. |
| 250 | + |
| 251 | +The files in `spec/support/` (imported via `spec_helper.rb`) |
| 252 | +contain helpers, matchers, and tests |
| 253 | +that are shared among unit and integration tests, |
| 254 | +at their respective levels. |
| 255 | + |
| 256 | +Beyond this, [Zeus][zeus], a way to speed up integration tests, |
| 257 | +and is run and configured via these files: |
| 258 | + |
| 259 | +- `bin/start-dev` |
| 260 | +- `config/zeus_plan.rb` |
| 261 | +- `support/test_plan.rb` |
| 262 | +- `zeus.json` |
| 263 | + |
| 264 | +The [official Zeus docs](https://github.com/burke/zeus/blob/master/docs/ruby/modifying.md) |
| 265 | +is helpful for understanding how these files work together. |
| 266 | + |
| 267 | +[zeus]: https://github.com/burke/zeus |
| 268 | + |
| 269 | +## Ruby |
| 270 | + |
| 271 | +The following files are used to set up Ruby, |
| 272 | +specify dependencies and gem publishing settings, |
| 273 | +and run tasks: |
| 274 | + |
| 275 | +- **`.ruby-version`:** |
| 276 | + Specifies the Ruby version required for development. |
| 277 | + Used by tools like `rbenv` and `asdf.` |
| 278 | +- **`bin/setup`:** |
| 279 | + Ensures that Ruby, Node, Python, and anything else necessary for development |
| 280 | + is installed on your machine. |
| 281 | +- **`gemfiles/`:** |
| 282 | + Holds generated gemspecs and lockfiles for appraisals. |
| 283 | +- **`lib/super_diff/version.rb`:** |
| 284 | + Holds the current version of the gem, |
| 285 | + which is used by the gemspec. |
| 286 | + Updating the constant in this file changes the published version. |
| 287 | +- **`spec/support/current_bundle.rb`:** |
| 288 | + A support file used by common tasks |
| 289 | + which tracks the currently set appraisal or sets a default. |
| 290 | +- **`Appraisals`:** |
| 291 | + Specifies different gemfiles for different testing environments. |
| 292 | +- **`Gemfile`:** |
| 293 | + Specifies common development dependencies. |
| 294 | +- **`Rakefile`:** |
| 295 | + Contains tasks for running tests and publishing the gem. |
| 296 | +- **`super_diff.gemspec`:** |
| 297 | + Standard configuration file for the gem, |
| 298 | + containing the name, description, and production dependencies. |
| 299 | + |
| 300 | +## Linting |
| 301 | + |
| 302 | +The following files are used for linting and formatting the codebase: |
| 303 | + |
| 304 | +- **`.husky/`:** |
| 305 | + Contains Git hooks which are installed via `husky install`. |
| 306 | +- **`.yarn/`:** |
| 307 | + Holds the executable and plugins for [Yarn][yarn], |
| 308 | + a package manager for JavaScript. |
| 309 | +- **`.prettierignore`:** |
| 310 | + Excludes files from being formatted with [Prettier][prettier]. |
| 311 | +- **`.prettierrc.json`:** |
| 312 | + Configures Prettier. |
| 313 | +- **`.nvmrc`:** |
| 314 | + Specifies the Node version required to run Prettier. |
| 315 | + Used by tools like NVM and `asdf`. |
| 316 | +- **`.yarnrc.yml`:** |
| 317 | + Configures Yarn. |
| 318 | +- **`package.json`:** |
| 319 | + Specifies JavaScript dependencies needed to run Prettier. |
| 320 | + |
| 321 | +[prettier]: https://prettier.io/ |
| 322 | +[yarn]: https://yarnpkg.com/ |
| 323 | + |
| 324 | +## Documentation |
| 325 | + |
| 326 | +The following files are used for documentation purposes: |
| 327 | + |
| 328 | +- **`docs/`:** |
| 329 | + Contains the documentation that you're reading now! |
| 330 | +- **`docs-support/`:** |
| 331 | + Other files which are used for documentation purposes, |
| 332 | + but are not included in any Markdown files. |
| 333 | + Currently only used to generate the screenshots in the README. |
| 334 | +- **`.python-version`:** |
| 335 | + Specifies the Python version to use for documentation generation. |
| 336 | + Used by tools like `pyenv` and `asdf`. |
| 337 | +- **`mkdocs.yml`:** |
| 338 | + The configuration file for [Mkdocs][mkdocs], |
| 339 | + which reads the Markdown files in `docs/` and generates an HTML version. |
| 340 | +- **`pyproject.toml`:** |
| 341 | + Specifies Python dependencies needed to run Mkdocs. |
| 342 | +- **`README.md`:** |
| 343 | + The "front of house" for this repository; |
| 344 | + summarizes the project's purpose and goals and how to use it. |
| 345 | +- **`CHANGELOG.md`:** |
| 346 | + Describes the changes that appeared in each release over time. |
| 347 | +- **`LICENSE`:** |
| 348 | + The full text of the license granted to users of this gem. |
| 349 | + |
| 350 | +[mkdocs]: https://www.mkdocs.org/ |
| 351 | + |
| 352 | +## CI |
| 353 | + |
| 354 | +The following files are used for CI and other automated tasks on GitHub: |
| 355 | + |
| 356 | +- **`.github/workflows/`:** |
| 357 | + Describes tasks to run on GitHub when events occur, |
| 358 | + such as automatically running tests on pull requests. |
| 359 | +- **`scripts/collect-release-info.rb`:** |
| 360 | + A script which is used in one of the GitHub workflows |
| 361 | + to determine whether a commit refers to a new release |
| 362 | + and, if so, determine the release version. |
| 363 | + |
| 364 | +## Miscellaneous |
| 365 | + |
| 366 | +These files don't belong in a particular category: |
| 367 | + |
| 368 | +- **`.editorconfig`:** |
| 369 | + Keeps basic editor-specific configuration, |
| 370 | + such as how many spaces to use, line length, etc. |
| 371 | +- **`.gitattributes`:** |
| 372 | + Used by Git and GitHub |
| 373 | + to configure presentation of certain types of files in diffs. |
| 374 | +- **`.gitignore`:** |
| 375 | + Specifies files that shouldn't show up in the repository. |
0 commit comments