Skip to content

Commit c3f2b54

Browse files
committed
Introduce markdownlint for guides
This is a follow up to rails#47186, this time for all markdown content. [markdownlint](https://github.com/markdownlint/markdownlint) is an excellent tool, and I've found it very useful for finding issues in the guides. Many of the rules are common style issues I'm correcting on PRs, so it will be nice to have that automated. We should also be able to use the same config with our editors, so that errors show up in real-time 🙏 and will update the contributing docs once this gets merged with how to debug and use mdl appropriately.
1 parent 99096b8 commit c3f2b54

26 files changed

+178
-92
lines changed

.github/workflows/mdl.yml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Markdown Lint [Guides]
2+
3+
on: [pull_request]
4+
5+
permissions:
6+
contents: read
7+
8+
jobs:
9+
mdl:
10+
runs-on: ubuntu-latest
11+
env:
12+
BUNDLE_ONLY: mdl
13+
14+
steps:
15+
- uses: actions/checkout@v3
16+
17+
- name: Set up Ruby 3.2
18+
uses: ruby/setup-ruby@v1
19+
with:
20+
ruby-version: 3.2
21+
bundler-cache: true
22+
23+
- name: Run mdl
24+
run: bundle exec rake -f guides/Rakefile guides:lint

.mdlrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
style "#{File.dirname(__FILE__)}/.mdlrc.rb"

.mdlrc.rb

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# frozen_string_literal: true
2+
3+
all
4+
5+
exclude_rule "MD003"
6+
exclude_rule "MD004"
7+
exclude_rule "MD005"
8+
exclude_rule "MD006"
9+
exclude_rule "MD007"
10+
exclude_rule "MD012"
11+
exclude_rule "MD014"
12+
exclude_rule "MD024"
13+
exclude_rule "MD026"
14+
exclude_rule "MD033"
15+
exclude_rule "MD034"
16+
exclude_rule "MD036"
17+
exclude_rule "MD040"
18+
exclude_rule "MD041"
19+
20+
rule "MD013", line_length: 2000, ignore_code_blocks: true
21+
# rule "MD024", allow_different_nesting: true # This did not work as intended, see action_cable_overview.md
22+
rule "MD029", style: :ordered
23+
# rule "MD046", style: :consistent # default (:fenced)

Gemfile

+5
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ group :rubocop do
4949
gem "rubocop-md", require: false
5050
end
5151

52+
group :mdl do
53+
gem "mdl", require: false
54+
gem "rake", ">= 13", require: false
55+
end
56+
5257
group :doc do
5358
gem "sdoc", ">= 2.6.0"
5459
gem "rdoc", "~> 6.5"

Gemfile.lock

+21-2
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ GEM
160160
regexp_parser (>= 1.5, < 3.0)
161161
xpath (~> 3.2)
162162
cgi (0.3.6)
163+
chef-utils (18.1.29)
164+
concurrent-ruby
163165
concurrent-ruby (1.1.10)
164166
connection_pool (2.3.0)
165167
crack (0.4.5)
@@ -275,6 +277,10 @@ GEM
275277
railties (>= 6.0.0)
276278
json (2.6.3)
277279
jwt (2.6.0)
280+
kramdown (2.4.0)
281+
rexml
282+
kramdown-parser-gfm (1.1.0)
283+
kramdown (~> 2.0)
278284
libxml-ruby (4.0.0)
279285
listen (3.8.0)
280286
rb-fsevent (~> 0.10, >= 0.10.3)
@@ -289,6 +295,12 @@ GEM
289295
net-smtp
290296
marcel (1.0.2)
291297
matrix (0.4.2)
298+
mdl (0.12.0)
299+
kramdown (~> 2.3)
300+
kramdown-parser-gfm (~> 1.1)
301+
mixlib-cli (~> 2.1, >= 2.1.1)
302+
mixlib-config (>= 2.2.1, < 4)
303+
mixlib-shellout
292304
memoist (0.16.2)
293305
mini_magick (4.12.0)
294306
mini_mime (1.1.2)
@@ -303,6 +315,11 @@ GEM
303315
minitest (>= 5.0)
304316
minitest-server (1.0.7)
305317
minitest (~> 5.16)
318+
mixlib-cli (2.1.8)
319+
mixlib-config (3.0.27)
320+
tomlrb
321+
mixlib-shellout (3.2.7)
322+
chef-utils
306323
mono_logger (1.1.1)
307324
msgpack (1.6.0)
308325
multi_json (1.15.0)
@@ -327,7 +344,7 @@ GEM
327344
racc (~> 1.4)
328345
os (1.1.4)
329346
parallel (1.22.1)
330-
parser (3.2.0.0)
347+
parser (3.2.1.1)
331348
ast (~> 2.4.1)
332349
path_expander (1.1.1)
333350
pg (1.4.5)
@@ -407,7 +424,7 @@ GEM
407424
rubocop-ast (>= 1.26.0, < 2.0)
408425
ruby-progressbar (~> 1.7)
409426
unicode-display_width (>= 2.4.0, < 3.0)
410-
rubocop-ast (1.27.0)
427+
rubocop-ast (1.28.0)
411428
parser (>= 3.2.1.0)
412429
rubocop-md (1.2.0)
413430
rubocop (>= 1.0)
@@ -489,6 +506,7 @@ GEM
489506
thor (1.2.1)
490507
tilt (2.0.11)
491508
timeout (0.3.2)
509+
tomlrb (2.0.3)
492510
trailblazer-option (0.1.2)
493511
turbo-rails (1.3.2)
494512
actionpack (>= 6.0.0)
@@ -558,6 +576,7 @@ DEPENDENCIES
558576
json (>= 2.0.0)
559577
libxml-ruby
560578
listen (~> 3.3)
579+
mdl
561580
minitest (>= 5.15.0)
562581
minitest-bisect
563582
minitest-ci

guides/Rakefile

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ namespace :guides do
2424
end
2525
end
2626

27+
desc "Lint guides, using `mdl`"
28+
task :lint do
29+
require "mdl"
30+
all = Dir.glob("#{__dir__}/source/*.md")
31+
files = all - Dir.glob("#{__dir__}/**/*_release_notes.md") # Ignore release notes
32+
MarkdownLint.run files
33+
end
34+
2735
# Validate guides -------------------------------------------------------------------------
2836
desc 'Validate guides, use ONLY=foo to process just "foo.html"'
2937
task :validate do

guides/source/action_cable_overview.md

+14-14
Original file line numberDiff line numberDiff line change
@@ -651,28 +651,28 @@ consumer.subscriptions.create("AppearanceChannel", {
651651
#### Client-Server Interaction
652652
653653
1. **Client** connects to the **Server** via `createConsumer()`. (`consumer.js`). The
654-
**Server** identifies this connection by `current_user`.
654+
**Server** identifies this connection by `current_user`.
655655
656656
2. **Client** subscribes to the appearance channel via
657-
`consumer.subscriptions.create({ channel: "AppearanceChannel" })`. (`appearance_channel.js`)
657+
`consumer.subscriptions.create({ channel: "AppearanceChannel" })`. (`appearance_channel.js`)
658658
659659
3. **Server** recognizes a new subscription has been initiated for the
660-
appearance channel and runs its `subscribed` callback, calling the `appear`
661-
method on `current_user`. (`appearance_channel.rb`)
660+
appearance channel and runs its `subscribed` callback, calling the `appear`
661+
method on `current_user`. (`appearance_channel.rb`)
662662
663663
4. **Client** recognizes that a subscription has been established and calls
664-
`connected` (`appearance_channel.js`), which in turn calls `install` and `appear`.
665-
`appear` calls `AppearanceChannel#appear(data)` on the server, and supplies a
666-
data hash of `{ appearing_on: this.appearingOn }`. This is
667-
possible because the server-side channel instance automatically exposes all
668-
public methods declared on the class (minus the callbacks), so that these can be
669-
reached as remote procedure calls via a subscription's `perform` method.
664+
`connected` (`appearance_channel.js`), which in turn calls `install` and `appear`.
665+
`appear` calls `AppearanceChannel#appear(data)` on the server, and supplies a
666+
data hash of `{ appearing_on: this.appearingOn }`. This is
667+
possible because the server-side channel instance automatically exposes all
668+
public methods declared on the class (minus the callbacks), so that these can be
669+
reached as remote procedure calls via a subscription's `perform` method.
670670
671671
5. **Server** receives the request for the `appear` action on the appearance
672-
channel for the connection identified by `current_user`
673-
(`appearance_channel.rb`). **Server** retrieves the data with the
674-
`:appearing_on` key from the data hash and sets it as the value for the `:on`
675-
key being passed to `current_user.appear`.
672+
channel for the connection identified by `current_user`
673+
(`appearance_channel.rb`). **Server** retrieves the data with the
674+
`:appearing_on` key from the data hash and sets it as the value for the `:on`
675+
key being passed to `current_user.appear`.
676676
677677
### Example 2: Receiving New Web Notifications
678678

guides/source/action_mailer_basics.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ a full list of all available options, please have a look further down at the
116116
Complete List of Action Mailer user-settable attributes section.
117117

118118
* The [`default`][] method sets default values for all emails sent from
119-
this mailer. In this case, we use it to set the `:from` header value for all
120-
messages in this class. This can be overridden on a per-email basis.
119+
this mailer. In this case, we use it to set the `:from` header value for all
120+
messages in this class. This can be overridden on a per-email basis.
121121
* The [`mail`][] method creates the actual email message. We use it to specify
122-
the values of headers like `:to` and `:subject` per email.
122+
the values of headers like `:to` and `:subject` per email.
123123

124124
[`default`]: https://api.rubyonrails.org/classes/ActionMailer/Base.html#method-c-default
125125
[`mail`]: https://api.rubyonrails.org/classes/ActionMailer/Base.html#method-i-mail

guides/source/active_record_basics.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ singularizing) both regular and irregular words. When using class names composed
8888
of two or more words, the model class name should follow the Ruby conventions,
8989
using the CamelCase form, while the table name must use the snake_case form. Examples:
9090

91-
* Model Class - Singular with the first letter of each word capitalized (e.g.,
92-
`BookClub`).
91+
* Model Class - Singular with the first letter of each word capitalized (e.g., `BookClub`).
9392
* Database Table - Plural with underscores separating words (e.g., `book_clubs`).
9493

9594
| Model / Class | Table / Schema |

guides/source/active_record_encryption.md

+3
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ When generating the filter parameter, it will use the model name as a prefix. E.
265265
```ruby
266266
config.active_record.encryption.add_to_filter_parameters = false
267267
```
268+
268269
In case you want exclude specific columns from this automatic filtering, add them to `config.active_record.encryption.excluded_from_filter_parameters`.
269270

270271
### Encoding
@@ -534,6 +535,7 @@ ActiveRecord::Encryption.without_encryption do
534535
...
535536
end
536537
```
538+
537539
This means that reading encrypted text will return the ciphertext, and saved content will be stored unencrypted.
538540

539541
##### Protect Encrypted Data
@@ -545,4 +547,5 @@ ActiveRecord::Encryption.protecting_encrypted_data do
545547
...
546548
end
547549
```
550+
548551
This can be handy if you want to protect encrypted data while still running arbitrary code against it (e.g. in a Rails console).

guides/source/active_record_migrations.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -517,14 +517,14 @@ Column modifiers can be applied when creating or changing a column:
517517
* `comment` Adds a comment for the column.
518518
* `collation` Specifies the collation for a `string` or `text` column.
519519
* `default` Allows to set a default value on the column. Note that if you
520-
are using a dynamic value (such as a date), the default will only be calculated
521-
the first time (i.e. on the date the migration is applied). Use `nil` for `NULL`.
520+
are using a dynamic value (such as a date), the default will only be calculated
521+
the first time (i.e. on the date the migration is applied). Use `nil` for `NULL`.
522522
* `limit` Sets the maximum number of characters for a `string` column
523-
and the maximum number of bytes for `text/binary/integer` columns.
523+
and the maximum number of bytes for `text/binary/integer` columns.
524524
* `null` Allows or disallows `NULL` values in the column.
525525
* `precision` Specifies the precision for `decimal/numeric/datetime/time` columns.
526526
* `scale` Specifies the scale for the `decimal` and `numeric` columns,
527-
representing the number of digits after the decimal point.
527+
representing the number of digits after the decimal point.
528528

529529
NOTE: For `add_column` or `change_column` there is no option for adding indexes.
530530
They need to be added separately using `add_index`.

guides/source/active_record_multiple_databases.md

+2-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,8 @@ At this time the following features are supported:
2323

2424
* Multiple writer databases and a replica for each
2525
* Automatic connection switching for the model you're working with
26-
* Automatic swapping between the writer and replica depending on the HTTP verb
27-
and recent writes
28-
* Rails tasks for creating, dropping, migrating, and interacting with the multiple
29-
databases
26+
* Automatic swapping between the writer and replica depending on the HTTP verb and recent writes
27+
* Rails tasks for creating, dropping, migrating, and interacting with the multiple databases
3028

3129
The following features are not (yet) supported:
3230

guides/source/active_storage_overview.md

+1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ amazon:
183183
server_side_encryption: "" # 'aws:kms' or 'AES256'
184184
cache_control: "private, max-age=<%= 1.day.to_i %>"
185185
```
186+
186187
TIP: Set sensible client HTTP timeouts and retry limits for your application. In certain failure scenarios, the default AWS client configuration may cause connections to be held for up to several minutes and lead to request queuing.
187188

188189
Add the [`aws-sdk-s3`](https://github.com/aws/aws-sdk-ruby) gem to your `Gemfile`:

guides/source/api_app.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,8 @@ Some common modules you might want to add:
548548
self.cache_store = :mem_cache_store
549549
end
550550
```
551-
Rails does *not* pass this configuration automatically.
551+
552+
Rails does *not* pass this configuration automatically.
552553

553554
The best place to add a module is in your `ApplicationController`, but you can
554555
also add modules to individual controllers.

guides/source/asset_pipeline.md

+15-22
Original file line numberDiff line numberDiff line change
@@ -113,26 +113,17 @@ with a built-in helper. In the source the generated code looked like this:
113113

114114
The query string strategy has several disadvantages:
115115

116-
1. **Not all caches will reliably cache content where the filename only differs by
117-
query parameters**
116+
1. **Not all caches will reliably cache content where the filename only differs by query parameters**
118117

119-
[Steve Souders recommends](https://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/),
120-
"...avoiding a querystring for cacheable resources". He found that in this
121-
case 5-20% of requests will not be cached. Query strings in particular do not
122-
work at all with some CDNs for cache invalidation.
118+
[Steve Souders recommends][], "...avoiding a querystring for cacheable resources". He found that in this case 5-20% of requests will not be cached. Query strings in particular do not work at all with some CDNs for cache invalidation.
123119

124120
2. **The file name can change between nodes in multi-server environments.**
125121

126-
The default query string in Rails 2.x is based on the modification time of
127-
the files. When assets are deployed to a cluster, there is no guarantee that the
128-
timestamps will be the same, resulting in different values being used depending
129-
on which server handles the request.
122+
The default query string in Rails 2.x is based on the modification time of the files. When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request.
130123

131124
3. **Too much cache invalidation**
132125

133-
When static assets are deployed with each new release of code, the mtime
134-
(time of last modification) of _all_ these files changes, forcing all remote
135-
clients to fetch them again, even when the content of those assets has not changed.
126+
When static assets are deployed with each new release of code, the mtime (time of last modification) of _all_ these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed.
136127

137128
Fingerprinting fixes these problems by avoiding query strings, and by ensuring
138129
that filenames are consistent based on their content.
@@ -147,7 +138,7 @@ More reading:
147138
* [Revving Filenames: don't use querystring](http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/)
148139

149140
[`config.assets.digest`]: configuring.html#config-assets-digest
150-
141+
[Steve Souders recommends]: https://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
151142

152143
How to Use the Asset Pipeline
153144
-----------------------------
@@ -207,15 +198,15 @@ Pipeline assets can be placed inside an application in one of three locations:
207198
`app/assets`, `lib/assets` or `vendor/assets`.
208199

209200
* `app/assets` is for assets that are owned by the application, such as custom
210-
images, JavaScript files, or stylesheets.
201+
images, JavaScript files, or stylesheets.
211202

212203
* `lib/assets` is for your own libraries' code that doesn't really fit into the
213-
scope of the application or those libraries which are shared across applications.
204+
scope of the application or those libraries which are shared across applications.
214205

215206
* `vendor/assets` is for assets that are owned by outside entities, such as
216-
code for JavaScript plugins and CSS frameworks. Keep in mind that third party
217-
code with references to other files also processed by the asset Pipeline (images,
218-
stylesheets, etc.), will need to be rewritten to use helpers like `asset_path`.
207+
code for JavaScript plugins and CSS frameworks. Keep in mind that third party
208+
code with references to other files also processed by the asset Pipeline (images,
209+
stylesheets, etc.), will need to be rewritten to use helpers like `asset_path`.
219210

220211
#### Search Paths
221212

@@ -760,9 +751,9 @@ $ RAILS_ENV=production rails assets:precompile
760751

761752
Note the following caveats:
762753

763-
* If precompiled assets are available, they will be served — even if they no
764-
longer match the original (uncompiled) assets, _even on the development
765-
server._
754+
* If precompiled assets are available, they will be served — even if they no
755+
longer match the original (uncompiled) assets, _even on the development
756+
server._
766757

767758
To ensure that the development server always compiles assets on-the-fly (and
768759
thus always reflects the most recent state of the code), the development
@@ -777,6 +768,7 @@ Note the following caveats:
777768
```ruby
778769
config.assets.prefix = "/dev-assets"
779770
```
771+
780772
* The asset precompile task in your deployment tool (_e.g.,_ Capistrano) should
781773
be disabled.
782774
* Any necessary compressors or minifiers must be available on your development
@@ -1141,6 +1133,7 @@ and any other environments you define with production behavior (not
11411133
`application.rb`).
11421134

11431135
TIP: For further details have a look at the docs of your production web server:
1136+
11441137
- [Apache](https://tn123.org/mod_xsendfile/)
11451138
- [NGINX](https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/)
11461139

0 commit comments

Comments
 (0)