InsightPulse is a Ruby on Rails portfolio project for an AI-assisted survey research platform. The goal is to build a small but credible internal tool where a project manager can create surveys, publish them, collect responses, and later generate LLM-based summaries from open-ended answers.
The project currently includes:
- Rails 8 app setup with PostgreSQL
- built-in authentication flow
- dashboard for signed-in users
- survey CRUD
- survey status handling for
draft,published, andclosed - nested question builder with multiple choice, scale, and text question types
- AI-assisted question wording: an "Enhance with AI" button rewrites a rough draft question and suggests further improvements, backed by OpenAI with an automatic offline fallback (see "Question Enhancement" below)
- public response flow without login
- results dashboard with per-question summaries
- CSV export of survey responses (see "Export CSV" on the survey show page)
- AI-generated report summaries, backed by Anthropic's API with an automatic offline fallback (see "AI Summary Provider" below), generated asynchronously with a live-updating UI (no manual refresh needed — see "Async AI Report Generation" below)
- seed data for local demoing
Requirements:
- Ruby
3.3.11 - PostgreSQL
16 - Bundler
Start PostgreSQL if it is not already running:
brew services start postgresql@16Install gems:
bundle installPrepare the database:
bin/rails db:create db:migrate db:seedCopy the env template and fill in whichever AI provider keys you have (both are optional — see below):
cp .env.example .envStart the app:
bin/devThen open http://localhost:3000.
Local secrets live in a gitignored .env file, loaded automatically by the dotenv-rails gem (development and test only — never in production, which should get real env vars from the hosting platform). .env.example is the committed, secret-free template.
.env.test is also committed on purpose: it blanks out both AI provider keys for the test environment specifically, so the test suite always exercises the deterministic heuristic fallbacks and never makes a real, billed API call — regardless of what's in a developer's local .env.
Survey open-text answers are summarized by AiReportGenerator, which tries a real LLM first and always has a working fallback:
- If
ANTHROPIC_API_KEYis set, reports are generated by Anthropic's Messages API (AiSummary::AnthropicProvider). The model defaults toclaude-3-5-haiku-latestand can be overridden withANTHROPIC_MODEL. - If the key is absent, or the API call fails for any reason, generation falls back to a deterministic local heuristic (
AiSummary::HeuristicProvider) so the feature never breaks a demo.
Set ANTHROPIC_API_KEY in your local .env to enable it.
The generated AiReport#provider_model shows which provider actually produced a given report.
Clicking "Generate AI summary" no longer blocks the request on the LLM call. Instead:
AiReportsController#createcreates theAiReportimmediately withstatus: pending(fast, no external call) and redirects right away — the page shows a "being generated" placeholder instantly.GenerateAiReportJob(run via Active Job,:asyncadapter in development — an in-process thread pool, no separate worker needed) does the slow part: it callsAiReportGenerator, which runs the LLM (or heuristic fallback) and updates the report tocompletedorfailed.AiReportbroadcasts that update over Turbo Streams (after_update_commit) to a channel scoped to the survey. The survey show page subscribes viaturbo_stream_from @surveyand replaces the#ai_summarysection live — no polling, no manual refresh.
This is why the project now has a JS pipeline (importmap-rails + turbo-rails, app/javascript/application.js, config/importmap.rb) and an Action Cable route (mount ActionCable.server => "/cable" in config/routes.rb) — neither existed before; the Gemfile had the gems but the app was 100% server-rendered with zero client-side JS. Action Cable uses the async adapter locally (see config/cable.yml), so no Redis is required for local dev.
The "Enhance with AI" button on the question form (new/edit) rewrites a rough
draft question into a clearer one, and suggests further improvements, via QuestionEnhancer:
- If
OPENAI_API_KEYis set, enhancement is done by OpenAI's Chat Completions API (QuestionEnhancement::OpenAiProvider). The model defaults togpt-4o-miniand can be overridden withOPENAI_MODEL. Besides rewriting the question, it returns 1-3 short "Ideas to make it even better" suggestions (e.g. switching to multiple choice, adding a follow-up question) shown under the field. - If the key is absent, or the API call fails for any reason, it falls back to a deterministic local heuristic (
QuestionEnhancement::HeuristicProvider) that just capitalizes the draft and ensures it ends with a question mark, with no suggestions (that needs a real LLM to be meaningful).
Set OPENAI_API_KEY in your local .env to enable it.
Note this is a separate provider/key from the AI Summary feature above — survey summaries still use Anthropic, question enhancement uses OpenAI. Both env vars can be set at once with no conflict.
- Email:
demo@example.com - Password:
password123
Done: CSV export of survey responses, AI question enhancement with suggestions, async/live-updating AI report generation.
The next slices are:
- a report history view for
AiReport - fix a pre-existing UX bug: when a multiple-choice question fails to save (no options given), the re-rendered form loses whatever the user typed in the options field
- additional product polish (empty states, publish/closed copy)
Core domain models:
UserSurveyQuestionQuestionOptionResponseSessionAnswerAiReport
Supporting planning docs: