Skip to content

Conversation

@newstler
Copy link
Owner

@newstler newstler commented Feb 9, 2026

Summary

This branch adds the full community platform features to WhyRuby.info and RubyCommunity.org.

Ruby & Rails upgrade

  • Upgrade Ruby 3.4.4 → 4.0.1
  • Upgrade Rails 8.1 → 8.2
  • Migrate primary keys from ULID to UUIDv7

Testimonial system

  • Users write why they love Ruby on their profile
  • AI generates headline, subheadline, and polished quote (GenerateTestimonialFieldsJob)
  • LLM validates content for appropriateness (ValidateTestimonialJob)
  • Testimonial carousel on the home page with auto-rotation

Multi-domain setup (rubycommunity.org)

  • Community pages served at rubycommunity.org in production, /community in development
  • Cross-domain OAuth with single-use token session sync (AuthController)
  • Cross-domain sign-out flow
  • Production 301 redirects from /community/* to rubycommunity.org/*
  • Footer legal links resolve to primary domain via main_site_url helper

Interactive community map

  • Geocoded user locations on a Leaflet.js world map with clustering
  • LocationNormalizer service: geocodes free-text locations via OpenAI
  • TimezoneResolver service: resolves timezone from coordinates
  • Filter by location, company, country; map panning filters the user list
  • Bounds-based filtering with infinite scroll

GitHub project tracking & star trends

  • Project model replaces github_repos JSON column
  • StarSnapshot model tracks daily star counts
  • Daily star trends showing which projects are popular right now
  • Sorting: trending, top, new, projects, posts
  • GitHub data fetched via GraphQL API with batching (GithubDataFetcher)

Newsletter system

  • Newsletter mailer with HTML and plain-text templates
  • Timezone-aware scheduling (10:10 AM local time per user)
  • Open tracking via tracking pixel
  • One-click unsubscribe via token URL
  • Rake tasks: send, schedule, preview_schedule, count, backfill_timezones, normalize_timezones

Profile & UX improvements

  • Ruby gem-shaped avatars replacing circle avatars
  • Profile settings: hide repositories, "Open to Work" badge
  • Linkified bios with @mention and URL detection
  • Sticky nav on home and community pages
  • Infinite scroll for user listings
  • Company filter with multi-token search
  • Mobile-responsive profile and carousel

Security & code quality

  • Fix brakeman XSS warnings: replace .html_safe with sanitize for bio_html
  • Fix brakeman open redirect warnings: validate return_to URLs against allowed domains
  • Add brakeman to lefthook pre-commit hooks (zero-warning policy)
  • Add malicious path blocker middleware
  • Replace Google Analytics with Nullitics

Performance

  • Optimize controllers, views, and helpers (from RorVsWild analysis)
  • Counter caches for comments
  • Precomputed bio_html on user save

Other

  • Automated community OG image generation
  • Updated favicon
  • MetadataFetcher service for link post OpenGraph data
  • Updated AGENTS.md and README.md to reflect all changes

Test plan

  • Run full test suite: rails test
  • Verify brakeman passes: bin/brakeman --no-pager
  • Verify RuboCop passes: bundle exec rubocop
  • Test cross-domain auth flow between whyruby.info and rubycommunity.org
  • Test community map filtering (location, company, bounds)
  • Test testimonial creation and AI field generation
  • Test newsletter timezone scheduling: rails newsletter:preview_schedule[1,2026-02-10]
  • Verify star trends display on community page and admin

🤖 Generated with Claude Code

newstler and others added 30 commits January 3, 2026 01:16
…e carousel

Users can answer "Why do you love Ruby?" on their profile page. A two-job
AI pipeline (GenerateTestimonialFieldsJob → ValidateTestimonialJob) generates
structured fields (heading, subheading, body_text), validates content, and
auto-publishes. Headings are kept unique via AI retry with synonym generation.
Published testimonials display in a Stimulus-powered carousel on the home page
with auto-advance, arrow/dot navigation, and fade transitions.

Includes Avo admin resource, invitation mailer with rake task, seed data with
4 pre-published testimonials, and full test coverage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… flow

Embed writing style guidelines into GenerateTestimonialFieldsJob and
AI-sounding language checks into ValidateTestimonialJob so generated
testimonial text reads naturally. Add reject_reason column to distinguish
quote vs generation rejections, improve validation prompt with content
policy and structured reject reasons, and refine form UI with pill-style
character counter and turbo stream placement fix.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…config

- Add swipe gesture support and navigation dots for mobile carousel
- Make testimonial layout responsive with stacked view on mobile/tablet
- Add compact mode to user tiles for testimonial display
- Show mobile CTA button when testimonial section nav is hidden
- Fix sticky header on community page to not stick on horizontal phones
- Hide empty tabs on user profile pages
- Update deploy.yml to use local registry and new.whyruby.info domain

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace sqlite-ulid gem with sqlean for UUIDv7 support
- Update database.yml to use SQLean::UUID extension
- Remove obsolete ULID initializers (sqlite.rb, active_storage_ulid.rb)
- Add migration to switch all table defaults from ULID() to uuid7()
- Add rake task for transforming existing ULID data to UUIDv7
- Add data transfer verification tasks
- Update test fixtures to use UUIDv7 format

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Route rubycommunity.org to users#index as its own root
- Override header logo and navigation on community pages
- Add content_for blocks in layout for header customization
- Update deploy.yml to serve both whyruby.info and rubycommunity.org
- Fix total users count display in community header

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When users sign in from rubycommunity.org, redirect them through
whyruby.info for GitHub OAuth (since only one callback URL is allowed),
then redirect back to the community domain after successful auth.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ubycommunity.org

- Add separate GitHub OAuth apps per domain with dynamic setup in devise.rb
- Add cross_domain_token to users for one-time session sync tokens
- Create AuthController for receiving cross-domain auth tokens
- Update routes for rubycommunity.org users at root level (/:username)
- Centralize domain config in config/initializers/domains.rb
- Add cross_domain_url and community_index_url helpers
- Sign in redirects to user profile, sign out from community stays on community
- Disable Turbo on sign-out buttons for proper page refresh

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix logo 404 on rubycommunity.org by using community_index_url instead of users_path
- Don't show "Session sync failed" error when user is already signed in on target domain
- Memoize cross-domain token per request to avoid generating multiple tokens

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
newstler and others added 25 commits February 9, 2026 18:47
LocationNormalizer now falls back to Photon's "name" property when
the result type is "city" (previously only checked "city" key, which
is absent for city-level results, causing fallback to country code only).
Replaced OpenStruct with Data.define. Also adjusts map zoom for mobile
and fixes sticky header behavior on horizontal tablets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tars consistently

Consolidated normalize and backfill_coordinates into a single
locations:refresh task that clears all data, re-normalizes, and
clears the map cache. Use gem avatar partial in comments, post
tiles, and post show page for consistent styling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use collection rendering instead of loop rendering in views (~2x faster),
bulk job enqueuing with perform_all_later (~3.5x faster), .size instead
of .count on loaded relations, and .find_by/.take instead of .where.first.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve user timezones from coordinates using wheretz gem so newsletter
emails arrive at 10:10 AM local time, spreading delivery across ~17 hours
instead of one burst.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Block common bot/scanner requests (wp-login, .env, .php, etc.) early
in the middleware stack before they hit Rails routing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add interactive map and daily star trends to the community section
copy. Remove daily star trends from future plans since it's shipped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The wheretz gem returns Europe/Kiev which newer tzdata packages
(like in our production Docker image) no longer recognize. Normalize
legacy IANA identifiers in TimezoneResolver and add a rake task to
fix existing stored data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix sign-out redirects between primary and community domains
- Add /community -> rubycommunity.org production redirects (301)
- Route footer legal links through main_site_url helper so they resolve correctly on community domain
- Default sort to "trending" instead of "top", reorder sort tabs
- Multi-token company search (e.g. "Basecamp 37signals" matches either)
- Linkify @Company mentions in company filter display
- Keep company filter when panning/zooming map
- Add mobile avatar link to profile in community header
- Add bio edit tooltip for profile owners
- Replace "gaining traction" with "popular" in newsletter and article draft

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show daily stars gained next to star count in admin project index.
Add missing Avo resource controllers for projects, star snapshots,
and testimonials.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace .html_safe with sanitize for bio_html output (XSS)
- Validate return_to redirect URLs against allowed domains (open redirect)
- Add brakeman to lefthook pre-commit checks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reflect new features and architecture added in this branch:
- Ruby 4.0.1 / Rails 8.2, UUIDv7 primary keys
- Testimonials, Projects, StarSnapshots models
- Community map, cross-domain auth, newsletter system
- LocationNormalizer, TimezoneResolver, MetadataFetcher services
- Brakeman added to pre-commit hooks
- Multi-domain setup documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve conflicts keeping Ruby 4.0.1, AGENTS.md reference in CLAUDE.md,
and consistent mobile/desktop footer layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /:id catch-all on the community domain was treating "community" as
a username lookup, which doesn't exist. Add 301 redirects for
/community and /community/:id within the community domain constraint
so stale links resolve correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
newstler and others added 3 commits February 9, 2026 18:53
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add turbo-action=advance to community list frame so filtering updates
the URL. Wrap profile tabs in a turbo-frame so project sorting navigates
without a full page reload.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, username and email were only set during initial account
creation. If a user renamed their GitHub account, the app would keep
the stale username — breaking API calls and showing outdated profiles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@newstler newstler merged commit cc71404 into main Feb 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant