Skip to content

Commit a13efa5

Browse files
authored
Document the file structure of the project (#232)
Also customize styling of headers so that the smaller ones are a little larger.
1 parent de1f313 commit a13efa5

File tree

4 files changed

+402
-0
lines changed

4 files changed

+402
-0
lines changed

docs/contributors/architecture/introduction.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ Since most of the code in this codebase services the RSpec integration,
88
these guides heavily skew toward that topic,
99
and you can start with a [guide to how RSpec works if you like](./how-rspec-works.md).
1010
But you can also find [details around the diff engine](./how-super-diff-works.md).
11+
Finally, if you're curious about the files in this project and how they're organized,
12+
feel free to consult the [structure](./structure) document.
Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
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

Comments
 (0)