Skip to content

Commit 6b87150

Browse files
Feat: Add ADR Template, Generator, and First Example (#24)
* feat: add ADRs readme * feat: adr generator * docs: initial ADR * refator: readability * chore: lint * feat: add ADRs readme * feat: adr generator * docs: initial ADR * refator: readability * chore: lint
1 parent 54be49e commit 6b87150

5 files changed

Lines changed: 157 additions & 0 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# 0001. Use Separate Model And Authentication For Students
2+
3+
Date: 2026-05-09
4+
5+
## Authors
6+
7+
- Sean Dickinson
8+
9+
## Context
10+
11+
Students are a very different type of user compared to administrators. Some students using the application may have low reading comprehension and struggle with basic typing skills.
12+
They also may not all have email addresses and be able to remember passwords.
13+
14+
The goal is to make the student login process as simple and frictionless as possible. There is no real need for security, as the application does not contain any sensitive information tied to students.
15+
There also is no incentive for a student to login as another student, as they can only earn rewards via their actions in the application.
16+
There are no actions they take in the application would cause the logged in account to lose rewards or cause any negative consequences for another student.
17+
18+
## Decision
19+
- We will create a separate model for students, and use a simple authentication system that does not require email addresses or passwords.
20+
- The students will be provided a unique link to the login page for their class, and will be able to select their name from a list of students in their class to log in.
21+
- From the perspective of the application, students will be a separate type of user with their own authentication system, and will not be able to access any of the administrator features or data.
22+
- Students will have their own session model and authentication concern that is separate from the administrator session model and authentication concern so there is no risk of students being able to access administrator features.
23+
- We will use Rails' built-in authentication scaffold for the administrator authentication system
24+
- We will use a modified version of the Rails' built-in authentication scaffold for the student authentication system that is tailored to the needs of students.
25+
26+
## Consequences
27+
28+
- This approach allows us to provide a simple and user-friendly login experience for students, while still maintaining a clear separation between students and administrators in the application.
29+
- We will have to maintain and support 2 separate authentication systems and sets of models

docs/adrs/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Architecture Decision Records (ADRs)
2+
3+
## Purpose
4+
ADRs are a way to document the architectural decisions made during the development of a software project.
5+
They provide a clear and concise record of the reasoning behind each decision, making it easier for future developers to understand the context and rationale behind the choices made.
6+
A good ADR provides a clear "why" regarding a decision around the architecture of the system, especially when the decision is not obvious.
7+
8+
9+
## Creating a New ADR
10+
11+
Use the built-in Rails generator to create a new ADR with the correct filename and template pre-filled:
12+
13+
```sh
14+
bin/rails generate adr <title_in_snake_case>
15+
```
16+
17+
For example:
18+
19+
```sh
20+
bin/rails generate adr use_postgres_as_primary_database
21+
```
22+
23+
This will create `docs/adrs/0001_use_postgres_as_primary_database.md` with today's date and all required sections. The number is assigned automatically based on existing ADRs. The title is normalized to snake_case regardless of how it's provided — kebab-case, CamelCase, and spaces all work.
24+
25+
## Format
26+
Each ADR should follow a consistent format to ensure clarity and ease of understanding. A common format includes the following sections:
27+
1. **Title**: A brief and descriptive title for the decision.
28+
2. **Context**: A description of the problem or situation that led to the need for a decision.
29+
3. **Authors**: The name of the person(s) or team responsible for making the decision.
30+
4. **Decision**: A clear statement of the decision that was made.
31+
5. **Consequences**: An explanation of the consequences of the decision, including any trade-offs or implications.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class AdrGenerator < Rails::Generators::NamedBase
2+
source_root File.expand_path("templates", __dir__)
3+
4+
def create_adr_file
5+
template "adr.md.erb", adr_path
6+
end
7+
8+
private
9+
10+
def adr_path
11+
"docs/adrs/#{next_number}_#{name.underscore.parameterize(separator: "_")}.md"
12+
end
13+
14+
def next_number
15+
@next_number ||= begin
16+
highest_existing_adr = Dir.glob(File.join(destination_root, "docs/adrs/[0-9]*.md")).last
17+
highest_existing_number = highest_existing_adr ? File.basename(highest_existing_adr).to_i : 0
18+
format("%04d", highest_existing_number + 1)
19+
end
20+
end
21+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# <%= next_number %>. <%= human_name.titleize %>
2+
3+
Date: <%= Time.now.strftime("%Y-%m-%d") %>
4+
5+
## Authors
6+
7+
-
8+
9+
## Context
10+
11+
<!-- Describe the problem or situation that led to this decision. What forces are at play? -->
12+
13+
## Decision
14+
15+
<!-- State the decision clearly. "We will..." -->
16+
17+
## Consequences
18+
19+
<!-- What becomes easier or harder as a result of this decision? Include trade-offs. -->
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
require "test_helper"
2+
require "rails/generators/testing/behavior"
3+
require "rails/generators/testing/assertions"
4+
require "generators/adr/adr_generator"
5+
6+
class ADRGeneratorTest < Rails::Generators::TestCase
7+
tests AdrGenerator
8+
destination Rails.root.join("tmp/generator_tests")
9+
setup :prepare_destination
10+
11+
test "creates adr file with sequential number" do
12+
run_generator [ "use_postgres_as_primary_database" ]
13+
assert_file "docs/adrs/0001_use_postgres_as_primary_database.md"
14+
end
15+
16+
test "normalizes kebab-case input" do
17+
run_generator [ "use-postgres-as-primary-database" ]
18+
assert_file "docs/adrs/0001_use_postgres_as_primary_database.md"
19+
end
20+
21+
test "normalizes CamelCase input" do
22+
run_generator [ "UsePostgresAsPrimaryDatabase" ]
23+
assert_file "docs/adrs/0001_use_postgres_as_primary_database.md"
24+
end
25+
26+
test "normalizes spaces input" do
27+
run_generator [ "Use Postgres As Primary Database" ]
28+
assert_file "docs/adrs/0001_use_postgres_as_primary_database.md"
29+
end
30+
31+
test "increments number based on existing adrs" do
32+
FileUtils.mkdir_p(File.join(destination_root, "docs/adrs"))
33+
FileUtils.touch(File.join(destination_root, "docs/adrs/0001_existing_decision.md"))
34+
run_generator [ "second_decision" ]
35+
assert_file "docs/adrs/0002_second_decision.md"
36+
end
37+
38+
test "populates template with title and date" do
39+
run_generator [ "use_postgres_as_primary_database" ]
40+
assert_file "docs/adrs/0001_use_postgres_as_primary_database.md" do |content|
41+
assert_match "# 0001. Use Postgres As Primary Database", content
42+
assert_match Date.today.strftime("%Y-%m-%d"), content
43+
assert_match "## Context", content
44+
assert_match "## Decision", content
45+
assert_match "## Consequences", content
46+
end
47+
end
48+
49+
test "title number matches filename number" do
50+
FileUtils.mkdir_p(File.join(destination_root, "docs/adrs"))
51+
FileUtils.touch(File.join(destination_root, "docs/adrs/0001_existing_decision.md"))
52+
run_generator [ "second_decision" ]
53+
assert_file "docs/adrs/0002_second_decision.md" do |content|
54+
assert_match "# 0002.", content
55+
end
56+
end
57+
end

0 commit comments

Comments
 (0)