Skip to content

Latest commit

 

History

History
392 lines (306 loc) · 14.3 KB

File metadata and controls

392 lines (306 loc) · 14.3 KB

AGENTS.md

Architecture reference for AI agents. For coding rules and conventions, see CLAUDE.md (single source of truth).

Project Overview

AWBW Portal is a Rails 8.1 application (Ruby 4.0.1) for A Window Between Worlds — a platform where workshop leaders manage workshops, resources, community news, stories, and events.

Tech Stack

  • Backend: Rails 8.1, Ruby 4.0.1, MySQL (via Trilogy adapter)
  • Frontend: Vite, Tailwind CSS v4, Stimulus, Turbo Rails
  • Auth: Devise with JWT token support
  • Authorization: ActionPolicy (app/policies/)
  • Rich text: ActionText with Rhino editor (TipTap-based)
  • File uploads: ActiveStorage with DigitalOcean Spaces
  • Background jobs: SolidQueue
  • Caching: SolidCache

Setup

Full setup (bundle, npm, database create/migrate/seed):

bin/setup

If you just need frontend dependencies:

npm ci

Architecture Overview

This codebase (Rails 8.1)
├── Main app (app/)              — Workshops, resources, stories, events, people, organizations
├── Frontend                     — Stimulus + Turbo + Tailwind CSS v4 (Vite bundler)
├── Background jobs              — SolidQueue
├── Caching                      — SolidCache
├── Auth                         — Devise (database + JWT)
├── Authorization                — ActionPolicy
└── Database                     — MySQL (Trilogy adapter)

Directory Guide

Business Logic

Directory Purpose Count
app/models/ ActiveRecord models ~66 files
app/services/ Service objects for complex logic ~21 files
app/jobs/ SolidQueue background jobs 3 files
app/models/concerns/ Shared model modules 12 concerns

Presentation

Directory Purpose Count
app/controllers/ Rails controllers (admin/, events/) ~63 files
app/views/ ERB templates ~465 files
app/decorators/ Draper decorators for view logic ~36 files
app/policies/ ActionPolicy authorization rules ~44 files
app/presenters/ Presentation objects 1 file
app/helpers/ View helpers ~19 files
app/mailers/ ActionMailer classes 6 files
app/inputs/ Custom SimpleForm inputs 1 file

Frontend

Directory Purpose
app/frontend/entrypoints/ Vite entry points (application.js, application.css)
app/frontend/javascript/controllers/ Stimulus controllers (34)
app/frontend/javascript/rhino/ Rich text editor customizations (mentions, grid)
app/frontend/stylesheets/ Tailwind CSS and component styles

Configuration

File/Directory Purpose
config/routes.rb All routes (single file)
config/database.yml MySQL via Trilogy adapter
config/initializers/ ~28 initializer files
.github/workflows/ GitHub Actions CI
Procfile.dev Dev services: vite + web
ai/ Shell script shortcuts for common dev tasks (see ai/README.md)

Key Models

Core Content Models

Model Purpose
User Devise authentication, SearchCop search, super_user admin flag
Workshop Core content: rich text fields, categories, sectors, bookmarks, variations
Event Events with registrations, featured/published states
Story Editorial content with facilitators, primary/gallery assets
Resource Handouts, toolkits, templates with downloadable assets
Person Organization affiliates with contacts, addresses, sectors
Organization Groups with affiliations, addresses, logos via ActiveStorage
Report STI base class for MonthlyReport
WorkshopLog Standalone model for workshop log submissions (attendance, form fields)

STI Models

  • Asset (inheritance column: type): PrimaryAsset, GalleryAsset, RichTextAsset, DownloadableAsset, ThumbnailAsset
  • Report: MonthlyReport

Polymorphic Associations

  • Bookmarks (bookmarkable): Workshop, Event, Resource, etc.
  • Assets (owner): Workshop, Story, Resource, Report, etc.
  • Comments (commentable): User, Person, Organization, etc.
  • Categorizable/Sectorable items: Workshop, Story, Resource, etc.
  • Forms (owner): Resource, Report, etc.

Model Concerns

Concern Purpose
AhoyTrackable Event tracking integration
AuthorCreditable Author attribution
Featureable featured, publicly_featured scopes
Mentioner ActionText @mention extraction and grouping
NameFilterable Name-based filtering
Publishable published, publicly_visible scopes
PunctuationStrippable Strips punctuation from strings
RemoteSearchable AJAX remote search by column
RichTextSearchable Full-text search on ActionText rich_text fields
TagFilterable Scope-based filtering by tag names
Trendable Trending metrics tracking
WindowsTypeFilterable Filter by WindowsType association

Controllers

Namespaces

  • Root level (~51 controllers): Workshops, stories, resources, events, people, organizations, etc.
  • admin/: HomeController, AnalyticsController, AhoyActivitiesController
  • events/: Registrations sub-resource (create/destroy + slug-based show at /registration/:slug)
  • Devise overrides: Registrations, Confirmations, Passwords

Base Controller Pattern

class ApplicationController < ActionController::Base
  before_action :authenticate_user!
  verify_authorized                    # ActionPolicy enforcement

  # Common helpers:
  # authorize! @record               — check policy
  # authorized_scope(Model.all)      — filtered relation
  # @record.decorate                 — Draper decorator
end

Controller Concerns

  • AhoyTracking — Event tracking integration
  • Dedupable — Data deduplication helpers
  • ExternallyRedirectable — External URL redirection
  • TagAssignable — Tag assignment helpers

Services

Analytics

  • Analytics::LifecycleBuffer — Thread-safe event buffer for batch tracking
  • Analytics::EventBuilder — Constructs analytics event payloads
  • Analytics::AhoyTracker — Coordinates ahoy event tracking

Business Logic

  • WorkshopSearchService — Complex filtering, sorting, pagination with ActionPolicy
  • WorkshopFromIdeaService — Converts WorkshopIdea to Workshop with asset migration
  • WorkshopVariationFromIdeaService — Variation creation from ideas
  • TaggingSearchService — Search and filter tagging data
  • PersonFromUserService — Create Person from User account
  • BulkInviteService — Bulk send welcome instructions and reset created_at for users
  • ModelDeduper — Deduplication logic
  • RichTextMigrator — Rich text migration utility
  • DisplayImagePresenter — Image display logic

Event Registrations

  • EventRegistrationServices::ProcessConfirmation — Registration confirmation flow
  • EventRegistrationServices::PublicRegistration — Public registration handling
  • ExtendedEventRegistrationFormBuilder — Extended registration form builder
  • ShortEventRegistrationFormBuilder — Short registration form builder
  • ScholarshipApplicationFormBuilder — Scholarship form builder

Notifications

  • NotificationServices::CreateNotification — Notification creation
  • NotificationServices::PersistDeliveredEmail — Email delivery tracking

User Management

  • UserServices::ProcessEmailChange — Email change processing
  • UserServices::ProcessEmailManualConfirm — Manual email confirmation

Decorators (Draper)

All inherit from ApplicationDecorator which provides:

  • delegate_all for transparent delegation
  • display_image — selects primary/gallery/downloadable asset intelligently
  • link_target — polymorphic path generation

Key decorators: WorkshopDecorator, StoryDecorator, ResourceDecorator, PersonDecorator, OrganizationDecorator, UserDecorator, EventDecorator, ReportDecorator.

Policies (ActionPolicy)

Base Pattern

class ApplicationPolicy < ActionPolicy::Base
  authorize :user, optional: true, allow_nil: true
  default_rule :manage?
  alias_rule :index?, :show?, :new?, :create?, :edit?, :update?, to: :manage?

  def manage? = admin?
  def destroy? = record.persisted? && manage?

  private
  def admin? = user&.super_user?
  def authenticated? = user.present?
  def owner? = record.created_by_id == user.id || record.user_id == user.id
end

Relation Scoping

relation_scope do |relation|
  next relation if admin?
  authenticated? ? relation.published : relation.publicly_visible
end

Mailers

  • ApplicationMailer — Base, from: ENV["REPLY_TO_EMAIL"]
  • DeviseMailer — Custom Devise emails
  • EventMailer — Event registration confirmations
  • NotificationMailer — Notification delivery
  • ContactUsMailer — Contact form submissions
  • All use premailer-rails for inline CSS
  • Previews live in test/mailers/previews/ (viewable at /rails/mailers/ in development)

Frontend

Stimulus Controllers

  • affiliation_dates — Recalculate affiliation date ranges
  • anchor_highlight — Highlight anchored elements
  • asset_picker — Asset selection UI
  • autosave — Auto-save form state
  • carousel — Swiper-based carousels
  • cocoon — Nested form handling (cocoon gem)
  • collection — Filter form auto-submit with debounce
  • column_toggle — Toggle table column visibility
  • comment_edit_toggle — Inline comment editing mode
  • confirm_email — Email confirmation UI
  • dirty_form — Unsaved changes detection
  • dismiss — Dismissable elements
  • dropdown — Dropdown menus with keyboard/click-outside handling
  • file_preview — File upload preview
  • inactive_toggle — Gray out expired affiliations
  • optimistic_bookmark — Instant bookmark UI feedback
  • org_toggle — Organization toggle UI
  • paginated_fields — Client-side pagination of nested fields
  • password_toggle — Show/hide password fields
  • prefetch_lazy — Prefetch lazy-loaded content
  • print_options — Print options toggle for analytics
  • remote_select — AJAX-powered select dropdown
  • rhino_source — Rich text editor integration
  • searchable_checkbox — TomSelect checkbox-style multi-select
  • searchable_select — Tom Select autocomplete
  • share_url — URL sharing/copying
  • sortable — Drag-drop sorting (SortableJS)
  • tabs — Tab panel navigation
  • tag_link_loading — Loading state for tag links
  • tags_combination_highlight — Highlight tags matching selected filters
  • tags_sync_list_heights — Sync tag list column heights
  • timeframe — Date range filtering
  • toggle_lock — Lock/unlock toggle UI
  • toggle_user_icon — User icon visibility toggle

JS Dependencies

Library Purpose
TipTap + ProseMirror Rich text editor (Rhino)
Tom Select Advanced select components
Chart.js + Chartkick Analytics charts
Swiper Image carousels
SortableJS Drag-and-drop sorting
Tippy.js Tooltips
Font Awesome 7 Icons

Tailwind Theme

Custom colors defined in app/frontend/stylesheets/application.tailwind.css:

  • --color-primary: #063b8d (dark blue)
  • Standard semantic colors: secondary, danger, warning, info, success

Testing

Structure

Directory Count Purpose
spec/models/ ~58 Model unit tests
spec/views/ ~73 View template tests
spec/requests/ ~47 HTTP request/integration tests
spec/system/ ~25 End-to-end browser tests (Capybara)
spec/routing/ ~13 Route definition tests
spec/policies/ ~9 Authorization policy tests
spec/decorators/ ~10 Decorator tests
spec/services/ ~12 Service object tests
spec/mailers/ ~5 Mailer tests
spec/helpers/ ~1 Helper tests
spec/factories/ ~53 FactoryBot factory definitions

Configuration

  • rails_helper.rb: Loads RSpec Rails, FactoryBot, Shoulda Matchers, ActionPolicy, Devise test helpers, ActiveStorage validation matchers. Transactional fixtures enabled. ActiveJob uses :test adapter.
  • spec_helper.rb: SimpleCov with branch coverage (minimum 20%), random test ordering, profile of 10 slowest examples.

Support Files

  • spec/support/capybara.rb — Selenium Chrome headless driver
  • spec/support/devise.rb — Devise integration for request/view/system specs
  • spec/support/eventually_matcher.rb — Custom async assertion matcher
  • spec/support/shared_examples/featureable.rb — Shared tests for featured content
  • spec/support/shared_examples/mentioner.rb — Shared tests for @mention functionality
  • spec/support/system_helpers/asset_upload_helpers.rb — Upload/delete helpers for system tests

Factory Traits

Common factory traits across models:

  • :featured, :published, :unpublished
  • :publicly_visible, :publicly_featured
  • :admin (User with super_user=true)
  • :with_primary_asset, :with_gallery_assets

Linting & Security

bundle exec rubocop        # lint
bundle exec rubocop -a     # auto-fix
bundle exec brakeman       # security scan
bundle exec bundle-audit check --update

CI Pipeline (GitHub Actions)

ci.yml

  1. scan_ruby: Brakeman security analysis + bundler-audit
  2. build-and-test: MySQL 8.0 service, Ruby + Node 22 setup, npm ci, schema load, bundle exec rspec

rubocop.yml

RuboCop linting on PRs and pushes to main.

Key Library Usage

Need Library
Authentication Devise (database + confirmable + lockable + trackable)
Authorization ActionPolicy with relation scoping
Search SearchCop (multi-field, rich text joins)
Decorators Draper (with ActionPolicy integration via initializer)
Forms SimpleForm + Cocoon (nested forms)
Pagination WillPaginate with TailwindPaginationRenderer
Rich text ActionText + Rhino editor (TipTap-based)
File uploads ActiveStorage (DigitalOcean Spaces) + legacy Paperclip
Feature flags FeatureFlipper
Analytics Ahoy (events + visits), Chartkick, Groupdate, Blazer
Geocoding Geocoder + MaxMind GeoIP2
Email styling Premailer-rails (inline CSS)
Positioning Positioning gem for ordered records

Rake Tasks

Located in lib/tasks/ (4 files):

  • dev.rake — Development database seeding from XML/CSV
  • rhino_migrator.rake — Rich text editor migration
  • attachment_report.rake — Attachment reporting
  • migrate_internal_id_to_filemaker_code.rake — FileMaker code migration