Files
codegraph/__tests__/rails-only-except-forms.test.ts
Colby Mchenry c0d3285b55 fix(rails): only:/except: are read as %i[], %w(), a string or :only => (#2269)
Only a bracketed list (and the scoped reader's bare :sym) was read, so
maybe's `resources :family_exports, only: %i[new create index]` drew all
seven RESTful routes — four to actions the controller lacks, listed by the
viewer as routes bound to nothing. Both route readers now share one option
reader for every form Rails accepts.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:31:06 +00:00

58 lines
2.0 KiB
TypeScript

/**
* A `resources` line's `only:` / `except:` is read however Rails lets it be
* written. maybe's `resources :family_exports, only: %i[new create index]` was
* not read, so it drew all seven RESTful routes — four of them to actions the
* controller does not have, which the viewer listed as routes bound to nothing.
*/
import { describe, it, expect, afterAll, beforeAll } from 'vitest';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { CodeGraph } from '../src';
let root = '';
let cg: CodeGraph;
beforeAll(async () => {
root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-rails-only-'));
const files: Record<string, string> = {
'Gemfile': 'source "https://rubygems.org"\ngem "rails", "~> 7.1"\n',
'config/routes.rb': `Rails.application.routes.draw do
resources :family_exports, only: %i[new create index] do
member do
get :download
end
end
resources :tags, except: %w(destroy edit update)
resources :imports, :only => [:index, :show]
resource :session, only: "show"
end
`,
};
for (const [rel, content] of Object.entries(files)) {
fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true });
fs.writeFileSync(path.join(root, rel), content);
}
cg = await CodeGraph.init(root, { index: true });
});
afterAll(() => {
cg?.close();
if (root) fs.rmSync(root, { recursive: true, force: true });
});
const routes = (controller: string) =>
cg.getNodesByKind('route').map((r) => r.qualifiedName).filter((q) => q.includes(`${controller}#`)).map((q) => q.slice(q.lastIndexOf('#') + 1)).sort();
describe('Rails resources only: / except:', () => {
it('reads %i[] and %w() lists', () => {
expect(routes('family_exports')).toEqual(['create', 'download', 'index', 'new']);
expect(routes('tags')).toEqual(['create', 'index', 'new', 'show']);
});
it('reads the hash-rocket and string forms', () => {
expect(routes('imports')).toEqual(['index', 'show']);
expect(routes('sessions')).toEqual(['show']);
});
});