Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .erb_lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
# ERB Lint β€” fills the gap RuboCop leaves: RuboCop only parses .rb, so nothing
# checked the Ruby inside <% %> tags or our ERB/HTML conventions. Follows the
# gem's recommended setup (https://github.com/Shopify/erb_lint): default linters
# stay on, and the Rubocop linter inherits our .rubocop.yml so the Ruby in tags
# is held to the same omakase style as our .rb files β€” no rules duplicated here.
EnableDefaultLinters: true
# --lint-all globs every *.html.erb under the repo. In CI, `bundler-cache`
# installs gems into vendor/bundle, so without this exclude erb_lint would lint
# third-party gem templates. (RuboCop excludes vendor by default; erb_lint does
# not.) Paths are matched from the repo root.
exclude:
- vendor/**/*
- node_modules/**/*
- tmp/**/*
linters:
# Adding an autocomplete token to each input is a per-field autofill/UX
# decision, not a mechanical fix, so enforcing it across the existing forms is
# out of scope here. Left off deliberately; could be revisited on its own.
RequireInputAutocomplete:
enabled: false
# <script type="module"> is legitimate ESM (e.g. the mermaid import on the flow
# diagram); allow it alongside the default text/javascript.
AllowedScriptType:
allowed_types:
- text/javascript
- module
Rubocop:
enabled: true
Comment on lines +28 to +29

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Rubocop:
enabled: true
Rubocop:
enabled: true
rubocop_config:
inherit_from:
- .rubocop.yml

Why duplicate everything in our rubocop.yml ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ€– From Claude: Done β€” the Rubocop linter now inherit_from: .rubocop.yml, so nothing is duplicated. The only cops set here are the ones that can't work on the Ruby extracted from a single tag: the README's recommended list plus the block/indentation-alignment family (a multi-line if/case is split across separate <% %> tags, so RuboCop sees misaligned fragments). Net effect: it also surfaced more real style issues (space after colon/comma) than my earlier hand-picked subset.

rubocop_config:
inherit_from:
- .rubocop.yml
# Cops that don't make sense against the Ruby extracted from an ERB tag β€”
# erb_lint hands RuboCop each tag's Ruby without its surrounding HTML
# indent context, so file-level layout/naming cops misfire. This is the
# gem README's recommended disable list.
Layout/InitialIndentation:
Enabled: false
# Block/indentation alignment cops need the whole method body, but a
# multi-line if/case/block is split across separate <% %> tags with the
# HTML stripped out, so RuboCop sees misaligned fragments β€” all false
# positives in ERB.
Layout/IndentationWidth:
Enabled: false
Layout/IndentationConsistency:
Enabled: false
Layout/ElseAlignment:
Enabled: false
Layout/EndAlignment:
Enabled: false
Layout/CaseIndentation:
Enabled: false
Layout/BeginEndAlignment:
Enabled: false
Layout/LineLength:
Enabled: false
Layout/TrailingEmptyLines:
Enabled: false
Layout/TrailingWhitespace:
Enabled: false
Naming/FileName:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false
Lint/UselessAssignment:
Enabled: false
Rails/OutputSafety:
Enabled: false
7 changes: 5 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ picker), use `MoneyFormatter.compact_from_cents(cents)` (`$12.5k`, `$1.2m`) β€”

## HTML/ERB Formatting

**These are enforced by ERB Lint** (`.erb_lint.yml`, run via `ai/lint` and in CI) β€”
RuboCop only parses `.rb` and never sees templates. Run `ai/lint --fix` to autocorrect.

### Tag Attributes
- **Closing `>` on same line as last attribute** β€” do not put `>` on its own line
- When attributes span multiple lines, keep the closing `>` with the last attribute
Expand Down Expand Up @@ -339,8 +342,8 @@ See `ai/` directory for executable scripts:
| `ai/recap` | Session recap: accomplishments + unresolved items (see above) |
| `ai/review` | Code review: agent reviews the workspace diff, posts inline comments, and gives a Recap + Risks + Outstanding decisions summary (runs the `ai-review` skill) |
| `ai/test [args]` | Run RSpec |
| `ai/lint` | Rubocop on all files |
| `ai/lint --fix` | Auto-fix lint issues |
| `ai/lint` | RuboCop on Ruby + ERB Lint on templates |
| `ai/lint --fix` | Auto-fix lint issues (both RuboCop and ERB Lint) |
| `ai/server` | Start dev services (web + vite) |
| `ai/console` | Rails console |
| `ai/routes -g pattern` | Search Rails routes |
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ jobs:
rubocop-cache-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}-
- name: Run RuboCop
run: bundle exec rubocop --format github --format clang
- name: Run ERB Lint
run: bundle exec erb_lint --lint-all
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ picker), use `MoneyFormatter.compact_from_cents(cents)` (`$12.5k`, `$1.2m`) β€”

## HTML/ERB Formatting

**These are enforced by ERB Lint** (`.erb_lint.yml`, run via `ai/lint` and in CI) β€”
RuboCop only parses `.rb` and never sees templates. Run `ai/lint --fix` to autocorrect.

### Tag Attributes
- **Closing `>` on same line as last attribute** β€” do not put `>` on its own line
- When attributes span multiple lines, keep the closing `>` with the last attribute
Expand Down Expand Up @@ -348,8 +351,8 @@ See `ai/` directory for executable scripts:
| `ai/recap` | Session recap: accomplishments + unresolved items (see above) |
| `ai/review` | Code review: agent reviews the workspace diff, posts inline comments, and gives a Recap + Risks + Outstanding decisions summary (runs the `ai-review` skill) |
| `ai/test [args]` | Run RSpec |
| `ai/lint` | Rubocop on all files |
| `ai/lint --fix` | Auto-fix lint issues |
| `ai/lint` | RuboCop on Ruby + ERB Lint on templates |
| `ai/lint --fix` | Auto-fix lint issues (both RuboCop and ERB Lint) |
| `ai/server` | Start dev services (web + vite) |
| `ai/console` | Rails console |
| `ai/routes -g pattern` | Search Rails routes |
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ gem "paper_trail", "~> 17.0"

group :development do
gem "rubocop-rails-omakase", require: false
gem "erb_lint", require: false
end

group :development, :test do
Expand Down
19 changes: 19 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ GEM
erubi (>= 1.0.0)
rack (>= 0.9.0)
rouge (>= 1.0.0)
better_html (2.2.0)
actionview (>= 7.0)
activesupport (>= 7.0)
ast (~> 2.0)
erubi (~> 1.4)
parser (>= 2.4)
smart_properties
bigdecimal (4.1.2)
blazer (3.3.0)
activerecord (>= 7.1)
Expand Down Expand Up @@ -201,6 +208,13 @@ GEM
drb (2.2.3)
dry-cli (1.4.1)
erb (6.0.4)
erb_lint (0.9.0)
activesupport
better_html (>= 2.0.1)
parser (>= 2.7.1.4)
rainbow
rubocop (>= 1)
smart_properties
erubi (1.13.1)
et-orbi (1.4.0)
tzinfo
Expand Down Expand Up @@ -695,6 +709,7 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.13.2)
simplecov_json_formatter (0.1.4)
smart_properties (1.17.0)
solid_cache (1.0.10)
activejob (>= 7.2)
activerecord (>= 7.2)
Expand Down Expand Up @@ -794,6 +809,7 @@ DEPENDENCIES
devise (~> 5.0.4)
dotenv-rails
draper
erb_lint
factory_bot_rails
faker
feature_flipper
Expand Down Expand Up @@ -869,6 +885,7 @@ CHECKSUMS
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
bcrypt (3.1.22) sha256=1f0072e88c2d705d94aff7f2c5cb02eb3f1ec4b8368671e19112527489f29032
better_errors (2.10.1) sha256=f798f1bac93f3e775925b7fcb24cffbcf0bb62ee2210f5350f161a6b75fc0a73
better_html (2.2.0) sha256=e68ab66ab09696b708333bbf35e8aa3c107500ba7892f528e2111624bdd8cf76
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
binding_of_caller (1.0.1)
blazer (3.3.0) sha256=01e151091ce1e7d27c156243916b2f13109ef2ef1a16cfa62bef67f4c5fd169f
Expand Down Expand Up @@ -904,6 +921,7 @@ CHECKSUMS
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
dry-cli (1.4.1) sha256=b8015bb76c708aa8705a36faf694973e75eeeffca39b89c8e172dc6f66a7d874
erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9
erb_lint (0.9.0) sha256=dfb5e40ad839e8d1f0d56ca85ec9a7ac4c9cd966ec281138282f35b323ca7c31
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
et-orbi (1.4.0) sha256=6c7e3c90779821f9e3b324c5e96fda9767f72995d6ae435b96678a4f3e2de8bc
execjs (2.10.0) sha256=6bcb8be8f0052ff9d370b65d1c080f2406656e150452a0abdb185a133048450d
Expand Down Expand Up @@ -1094,6 +1112,7 @@ CHECKSUMS
simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
smart_properties (1.17.0) sha256=f9323f8122e932341756ddec8e0ac9ec6e238408a7661508be99439ca6d6384b
solid_cache (1.0.10) sha256=bc05a2fb3ac78a6f43cbb5946679cf9db67dd30d22939ededc385cb93e120d41
solid_queue (1.3.1) sha256=d9580111180c339804ff1a810a7768f69f5dc694d31e86cf1535ff2cd7a87428
sprockets (4.2.2) sha256=761e5a49f1c288704763f73139763564c845a8f856d52fba013458f8af1b59b1
Expand Down
15 changes: 11 additions & 4 deletions ai/lint
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
#!/usr/bin/env bash
# Lint Ruby files with Rubocop. Use --fix to auto-correct.
set -euo pipefail
# Lint Ruby with RuboCop and ERB templates with ERB Lint. Use --fix to
# auto-correct both. File args are passed to RuboCop; ERB Lint always runs
# across all ERB files.
set -uo pipefail
source "$(dirname "$0")/.ruby-env"

status=0
if [[ "${1:-}" == "--fix" ]]; then
shift
bundle exec rubocop -a "$@"
bundle exec rubocop -a "$@" || status=$?
bundle exec erb_lint --lint-all --autocorrect || status=$?
else
bundle exec rubocop "$@"
bundle exec rubocop "$@" || status=$?
bundle exec erb_lint --lint-all || status=$?
fi
exit $status
11 changes: 4 additions & 7 deletions app/views/active_storage/blobs/_blob_thumbnail.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
relative attachment attachment--<%= blob.previewable? || blob.image? ? "preview" : "file" %>
attachment--<%= blob.filename.extension %> w-fit rounded-md p-2
bg-gray-50 shadow-md
"
>
">
<% if blob.previewable? %>
<%= image_tag attachment.preview(resize_to_limit: [256, 256]),
<%= image_tag attachment.preview(resize_to_limit: [ 256, 256 ]),
class: "w-48 h-48 object-contain" %>
<% elsif blob.image? %>
<%= image_tag attachment.variant(:thumbnail), class: "w-48 h-48 object-contain" %>
Expand All @@ -20,8 +19,7 @@
w-48 h-48 flex items-center justify-center bg-gray-200 rounded text-center p-2
text-xs break-words whitespace-normal overflow-hidden
"
title="<%= blob.filename.to_s %>"
>
title="<%= blob.filename.to_s %>">
<span class="line-clamp-3"><%= blob.filename.to_s %></span>
</div>
<% end %>
Expand All @@ -31,8 +29,7 @@
class="
absolute top-1 right-1 bg-gray-700 text-white text-xs rounded px-1.5 py-0.5
pointer-events-none
"
>
">
<%= asset.id %>
</span>
<% end %>
Expand Down
4 changes: 1 addition & 3 deletions app/views/addresses/_address_fields.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<%= f.input :address_type,
as: :select,
required: true,
collection: Address::CONTACT_TYPES.compact.map { |t| [t.humanize, t] } %>
collection: Address::CONTACT_TYPES.compact.map { |t| [ t.humanize, t ] } %>
<div class="flex items-center">
<%= f.input :primary,
as: :boolean,
Expand All @@ -36,7 +36,6 @@
</div>
</div>


<!-- LA-SPECIFIC FIELDS -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<%= f.input :locality,
Expand Down Expand Up @@ -109,7 +108,6 @@
class: "rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500"
} %>


</div>

<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
Expand Down
4 changes: 2 additions & 2 deletions app/views/admin/ahoy_activities/_recent_activity.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
overflow-hidden flex flex-col md:flex-row">
<div class="w-60 items-center p-2">
<div class="pt-2">
<span class="font-bold"><%= activity.event.name.gsub("."," ").titleize %></span>
<span class="font-bold"><%= activity.event.name.gsub(".", " ").titleize %></span>
</div>

<div class="text-sm text-gray-500 font-medium pt-2 whitespace-nowrap">
Expand Down Expand Up @@ -47,7 +47,7 @@
<% detail = activity.event.properties %>
<% elsif activity.event.name.include?("auth") %>
<% path = edit_user_path(user) %>
<% title = activity.event.name.gsub("auth.","").titleize %>
<% title = activity.event.name.gsub("auth.", "").titleize %>
<% detail = "" %>
<% else %>
<% path = polymorphic_path(activity) %>
Expand Down
16 changes: 8 additions & 8 deletions app/views/admin/ahoy_activities/charts.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@
<%= select_tag :time_period,
options_for_select(
[
['All time', 'all_time'],
['Past day', 'past_day'],
['Past week', 'past_week'],
['Past month', 'past_month'],
['Past year', 'past_year']
[ "All time", "all_time" ],
[ "Past day", "past_day" ],
[ "Past week", "past_week" ],
[ "Past month", "past_month" ],
[ "Past year", "past_year" ]
],
params[:time_period] || 'past_month'
params[:time_period] || "past_month"
),
class: "px-3 py-2 rounded-md border-gray-300 shadow-sm text-sm text-gray-700",
onchange: "this.form.submit()" %>
Expand Down Expand Up @@ -243,12 +243,12 @@
<tr>
<td class="p-1 text-right text-gray-500 whitespace-nowrap font-mono"><%= "#{hour}:00" %></td>
<% (1..7).each do |dow| %>
<% val = @heatmap_data[[hour, dow]] || 0 %>
<% val = @heatmap_data[[ hour, dow ]] || 0 %>
<% opacity = (val.to_f / max_val).round(2) %>
<td class="p-1 text-center rounded-sm"
style="background-color: rgba(59, 130, 246, <%= opacity %>);"
title="<%= days[dow - 1] %> <%= hour %>:00 β€” <%= val %> activities">
<span class="<%= opacity > 0.5 ? 'text-white' : 'text-gray-700' %>">
<span class="<%= opacity > 0.5 ? "text-white" : "text-gray-700" %>">
<%= val > 0 ? val : "" %>
</span>
</td>
Expand Down
8 changes: 4 additions & 4 deletions app/views/admin/ahoy_activities/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
</label>
<%= select_tag :time_period,
options_for_select(
[["All time", "all_time"], ["Past day", "past_day"], ["Past week", "past_week"], ["Past month", "past_month"], ["Past year", "past_year"]],
[ [ "All time", "all_time" ], [ "Past day", "past_day" ], [ "Past week", "past_week" ], [ "Past month", "past_month" ], [ "Past year", "past_year" ] ],
params[:time_period] || "past_month"
),
class: "w-full px-3 py-2 border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" %>
Expand Down Expand Up @@ -200,17 +200,17 @@
prefix = type_key.chomp("_type")
id_key = "#{prefix}_id"
if props.key?(id_key)
display_props << [prefix, "#{props[type_key]}.find(#{props[id_key]})"]
display_props << [ prefix, "#{props[type_key]}.find(#{props[id_key]})" ]
props.delete(type_key)
props.delete(id_key)
end
end
# Show resource_title first, then remaining props
if props.key?("resource_title")
display_props.unshift(["resource_title", props.delete("resource_title")])
display_props.unshift([ "resource_title", props.delete("resource_title") ])
end
props.each do |key, value|
display_props << [key, value.is_a?(Hash) ? value.to_json : value]
display_props << [ key, value.is_a?(Hash) ? value.to_json : value ]
end
%>
<div class="hidden group-hover:block absolute z-50 right-0 top-full bg-gray-900 text-gray-100 text-xs rounded-lg shadow-lg px-3 py-3 max-h-64 overflow-y-auto" style="width: 400px;">
Expand Down
2 changes: 1 addition & 1 deletion app/views/admin/ahoy_activities/visits.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
</label>
<%= select_tag :time_period,
options_for_select(
[["All time", "all_time"], ["Past day", "past_day"], ["Past week", "past_week"], ["Past month", "past_month"], ["Past year", "past_year"]],
[ [ "All time", "all_time" ], [ "Past day", "past_day" ], [ "Past week", "past_week" ], [ "Past month", "past_month" ], [ "Past year", "past_year" ] ],
params[:time_period] || "past_month"
),
class: "w-full px-3 py-2 border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" %>
Expand Down
Loading