@suss/adapter-ruby 0.22.4 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -248
- package/dist/index.cjs +828 -167
- package/dist/index.d.cts +91 -4
- package/dist/index.d.ts +91 -4
- package/dist/index.js +806 -144
- package/package.json +7 -7
package/dist/index.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import { Tree, Node } from 'web-tree-sitter';
|
|
|
2
2
|
export { Node as RbNode, Tree as RbTree } from 'web-tree-sitter';
|
|
3
3
|
import { Database } from '@suss/datalog';
|
|
4
4
|
import { TypeShape, BehavioralSummary } from '@suss/behavioral-ir';
|
|
5
|
-
import { RawCodeStructure, TimingReport, ExtractionReport, CacheDiagnostic } from '@suss/extractor';
|
|
5
|
+
import { RawCodeStructure, TimingReport, ExtractionReport, CacheDiagnostic, ExtractorOptions } from '@suss/extractor';
|
|
6
6
|
import { WhyExplained } from '@suss/resolution';
|
|
7
7
|
import { Value } from '@suss/values';
|
|
8
8
|
|
|
@@ -63,14 +63,20 @@ declare function readCallArgs(argumentList: Node | null): CallArgs;
|
|
|
63
63
|
*
|
|
64
64
|
* A Ruby codebase locates that file by a naming convention rather than through a
|
|
65
65
|
* load graph a static reader could follow, so we build one path from the
|
|
66
|
-
* constant's own name and
|
|
67
|
-
*
|
|
66
|
+
* constant's own name and look for it under the configured root and the
|
|
67
|
+
* directories Rails autoloads from. No other spelling is tried.
|
|
68
68
|
*/
|
|
69
69
|
/** A pack picks a convention by name, and the code for each one lives here. */
|
|
70
70
|
type ConstantPathConvention = "railsUnderscore";
|
|
71
71
|
/** Ported from ActiveSupport's own `String#underscore`, which is what Rails autoloading runs a constant path through. */
|
|
72
72
|
declare function underscoreConstantPath(qualifiedName: string): string;
|
|
73
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* Null when there is no file at that path. Rails autoloads from every
|
|
75
|
+
* directory directly under `app`, so `ApplicationController` is
|
|
76
|
+
* `app/controllers/application_controller.rb` and not
|
|
77
|
+
* `app/application_controller.rb`; those directories are tried after
|
|
78
|
+
* the root itself, in name order, and no other spelling is tried.
|
|
79
|
+
*/
|
|
74
80
|
declare function resolveConstantFile(root: string, qualifiedName: string, convention: ConstantPathConvention): string | null;
|
|
75
81
|
|
|
76
82
|
/**
|
|
@@ -234,9 +240,57 @@ interface RubyPack {
|
|
|
234
240
|
/** Wire protocol for the produced boundary bindings, e.g. "http-graphql". */
|
|
235
241
|
protocol: string;
|
|
236
242
|
discovery: RubyDiscoveryPattern[];
|
|
243
|
+
/** The calls the library gives a project for making a request. */
|
|
244
|
+
clients?: RbClientCall[];
|
|
237
245
|
/** What the library's own database calls look like. The README says how one is matched. */
|
|
238
246
|
storage?: RbStoragePattern[];
|
|
239
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* The calls a library gives a project for making a request. A method
|
|
250
|
+
* that makes one is a client of the boundary that call states, and gets
|
|
251
|
+
* a unit bound to its method and path.
|
|
252
|
+
*/
|
|
253
|
+
interface RbClientCall {
|
|
254
|
+
/** The constant the calls hang on, as a project writes it: `Faraday`, `Net::HTTP`. */
|
|
255
|
+
constantName: string;
|
|
256
|
+
/** Method names that state the request method themselves: `get` means GET. */
|
|
257
|
+
verbMethodNames: Record<string, string>;
|
|
258
|
+
/** Where a call states the URL. */
|
|
259
|
+
url: {
|
|
260
|
+
position: number;
|
|
261
|
+
keyword?: string;
|
|
262
|
+
};
|
|
263
|
+
/** Methods on the constant that build a value taking the same calls, `new` for a Faraday connection. */
|
|
264
|
+
receiverBuilders?: string[];
|
|
265
|
+
/** The keyword such a builder takes the base URL under, which comes in front of the path of a call on it. */
|
|
266
|
+
builderUrlKeyword?: string;
|
|
267
|
+
/** What the response object gives a caller, so a test on it reads as a test on a status. */
|
|
268
|
+
response?: RbClientResponse;
|
|
269
|
+
/** A call that sends a request object built somewhere else, which is how Net::HTTP sends anything with a body. */
|
|
270
|
+
requestObject?: {
|
|
271
|
+
/** The method that takes the request object, `request`. */
|
|
272
|
+
attribute: string;
|
|
273
|
+
/** Each request class, as a project writes it, and the method it sends: `Net::HTTP::Get` sends GET. */
|
|
274
|
+
constructors: Record<string, string>;
|
|
275
|
+
/** Where the request class takes the URL. */
|
|
276
|
+
urlPosition: number;
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* The members a library's response object gives a caller. A caller that
|
|
281
|
+
* tests one of them is saying which statuses it handles, and the
|
|
282
|
+
* checker compares that against what the other side produces.
|
|
283
|
+
*/
|
|
284
|
+
interface RbClientResponse {
|
|
285
|
+
/** Members whose value is the status: `status` for Faraday, `code` for Net::HTTP. */
|
|
286
|
+
statusCode?: string[];
|
|
287
|
+
/** Members that say the request succeeded, meaning a status in 200 to 299: `success?`. */
|
|
288
|
+
success?: string[];
|
|
289
|
+
/** Members that give the body: `body`. */
|
|
290
|
+
body?: string[];
|
|
291
|
+
/** Whether a refused request comes back as a response or raises where it was made. */
|
|
292
|
+
failureDelivery?: "response" | "exception";
|
|
293
|
+
}
|
|
240
294
|
/**
|
|
241
295
|
* Ruby writes no return type, so a call is matched by what its receiver
|
|
242
296
|
* inherits from. A model is a class that reaches one of these base classes,
|
|
@@ -284,6 +338,33 @@ interface ControllerActions {
|
|
|
284
338
|
} | null;
|
|
285
339
|
/** One message per routing declaration kind this pattern's reading left uncovered. A pure read: calling it again gives the same list. */
|
|
286
340
|
routingGaps?: () => readonly string[];
|
|
341
|
+
/** The class-level calls the library gives a controller for running one of its own methods around every action, Rails' `before_action` and `rescue_from`. */
|
|
342
|
+
filters?: RbControllerFilter[];
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* A call in a controller's class body naming a method the library runs
|
|
346
|
+
* around the action, and which actions that reaches. A class inherits
|
|
347
|
+
* what its ancestors declared, so a filter on `ApplicationController`
|
|
348
|
+
* reaches every action in the project.
|
|
349
|
+
*/
|
|
350
|
+
interface RbControllerFilter {
|
|
351
|
+
/** The call as a controller writes it, `before_action`. */
|
|
352
|
+
name: string;
|
|
353
|
+
/**
|
|
354
|
+
* Where the call names the method: `argument` for the leading symbol
|
|
355
|
+
* of `before_action :require_login`, `withKeyword` for the `with:` of
|
|
356
|
+
* `rescue_from ActiveRecord::RecordNotFound, with: :not_found`.
|
|
357
|
+
*/
|
|
358
|
+
methodFrom: "argument" | "withKeyword";
|
|
359
|
+
/** Set when the library runs the method only for an action that raised, which is what `rescue_from` does. */
|
|
360
|
+
onThrow?: boolean;
|
|
361
|
+
/** The name of the call that takes a filter back off, `skip_before_action` for `before_action`. */
|
|
362
|
+
skippedBy?: string;
|
|
363
|
+
/** The keywords that narrow a filter to some of the actions, Rails' `only` and `except`. */
|
|
364
|
+
actionKeywords?: {
|
|
365
|
+
include: string;
|
|
366
|
+
exclude: string;
|
|
367
|
+
};
|
|
287
368
|
}
|
|
288
369
|
/**
|
|
289
370
|
* One call an action writes to send a response, and where that call takes
|
|
@@ -385,6 +466,10 @@ interface DiscoveryOptions extends BodyReadOptions {
|
|
|
385
466
|
filePath: string;
|
|
386
467
|
/** Absolute path of the file being read, for a block's own `ReachedBody.file`. Falls back to `filePath` when nothing was written to disk. */
|
|
387
468
|
absoluteFile?: string;
|
|
469
|
+
/** What a summary's `location.file` says for a file other than the one being read, which a controller's filters need when an ancestor defines them. */
|
|
470
|
+
displayPathOf?: (absolute: string) => string;
|
|
471
|
+
/** The run's own facts, which the value evaluator reads a name through. Absent in a test that builds one file by hand. */
|
|
472
|
+
facts?: Database;
|
|
388
473
|
cache: FileCache;
|
|
389
474
|
/** Called once per discovered unit whose own body is a method this run can follow calls out of, so the reach walk has a place to start. */
|
|
390
475
|
onReachSeed?: (raw: RawCodeStructure, seed: ReachSeed) => void;
|
|
@@ -547,6 +632,8 @@ interface ExtractRubyOptions {
|
|
|
547
632
|
onCacheDiagnostic?: (diagnostic: CacheDiagnostic) => void;
|
|
548
633
|
/** Absolute. `<projectRoot>/.suss/cache` by default; `null` turns it off. */
|
|
549
634
|
cacheDir?: string | null;
|
|
635
|
+
/** What to do with gaps, which composing a controller's filters can add one of. */
|
|
636
|
+
gapHandling?: ExtractorOptions["gapHandling"];
|
|
550
637
|
}
|
|
551
638
|
interface ExtractRubyResult {
|
|
552
639
|
summaries: BehavioralSummary[];
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Tree, Node } from 'web-tree-sitter';
|
|
|
2
2
|
export { Node as RbNode, Tree as RbTree } from 'web-tree-sitter';
|
|
3
3
|
import { Database } from '@suss/datalog';
|
|
4
4
|
import { TypeShape, BehavioralSummary } from '@suss/behavioral-ir';
|
|
5
|
-
import { RawCodeStructure, TimingReport, ExtractionReport, CacheDiagnostic } from '@suss/extractor';
|
|
5
|
+
import { RawCodeStructure, TimingReport, ExtractionReport, CacheDiagnostic, ExtractorOptions } from '@suss/extractor';
|
|
6
6
|
import { WhyExplained } from '@suss/resolution';
|
|
7
7
|
import { Value } from '@suss/values';
|
|
8
8
|
|
|
@@ -63,14 +63,20 @@ declare function readCallArgs(argumentList: Node | null): CallArgs;
|
|
|
63
63
|
*
|
|
64
64
|
* A Ruby codebase locates that file by a naming convention rather than through a
|
|
65
65
|
* load graph a static reader could follow, so we build one path from the
|
|
66
|
-
* constant's own name and
|
|
67
|
-
*
|
|
66
|
+
* constant's own name and look for it under the configured root and the
|
|
67
|
+
* directories Rails autoloads from. No other spelling is tried.
|
|
68
68
|
*/
|
|
69
69
|
/** A pack picks a convention by name, and the code for each one lives here. */
|
|
70
70
|
type ConstantPathConvention = "railsUnderscore";
|
|
71
71
|
/** Ported from ActiveSupport's own `String#underscore`, which is what Rails autoloading runs a constant path through. */
|
|
72
72
|
declare function underscoreConstantPath(qualifiedName: string): string;
|
|
73
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* Null when there is no file at that path. Rails autoloads from every
|
|
75
|
+
* directory directly under `app`, so `ApplicationController` is
|
|
76
|
+
* `app/controllers/application_controller.rb` and not
|
|
77
|
+
* `app/application_controller.rb`; those directories are tried after
|
|
78
|
+
* the root itself, in name order, and no other spelling is tried.
|
|
79
|
+
*/
|
|
74
80
|
declare function resolveConstantFile(root: string, qualifiedName: string, convention: ConstantPathConvention): string | null;
|
|
75
81
|
|
|
76
82
|
/**
|
|
@@ -234,9 +240,57 @@ interface RubyPack {
|
|
|
234
240
|
/** Wire protocol for the produced boundary bindings, e.g. "http-graphql". */
|
|
235
241
|
protocol: string;
|
|
236
242
|
discovery: RubyDiscoveryPattern[];
|
|
243
|
+
/** The calls the library gives a project for making a request. */
|
|
244
|
+
clients?: RbClientCall[];
|
|
237
245
|
/** What the library's own database calls look like. The README says how one is matched. */
|
|
238
246
|
storage?: RbStoragePattern[];
|
|
239
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* The calls a library gives a project for making a request. A method
|
|
250
|
+
* that makes one is a client of the boundary that call states, and gets
|
|
251
|
+
* a unit bound to its method and path.
|
|
252
|
+
*/
|
|
253
|
+
interface RbClientCall {
|
|
254
|
+
/** The constant the calls hang on, as a project writes it: `Faraday`, `Net::HTTP`. */
|
|
255
|
+
constantName: string;
|
|
256
|
+
/** Method names that state the request method themselves: `get` means GET. */
|
|
257
|
+
verbMethodNames: Record<string, string>;
|
|
258
|
+
/** Where a call states the URL. */
|
|
259
|
+
url: {
|
|
260
|
+
position: number;
|
|
261
|
+
keyword?: string;
|
|
262
|
+
};
|
|
263
|
+
/** Methods on the constant that build a value taking the same calls, `new` for a Faraday connection. */
|
|
264
|
+
receiverBuilders?: string[];
|
|
265
|
+
/** The keyword such a builder takes the base URL under, which comes in front of the path of a call on it. */
|
|
266
|
+
builderUrlKeyword?: string;
|
|
267
|
+
/** What the response object gives a caller, so a test on it reads as a test on a status. */
|
|
268
|
+
response?: RbClientResponse;
|
|
269
|
+
/** A call that sends a request object built somewhere else, which is how Net::HTTP sends anything with a body. */
|
|
270
|
+
requestObject?: {
|
|
271
|
+
/** The method that takes the request object, `request`. */
|
|
272
|
+
attribute: string;
|
|
273
|
+
/** Each request class, as a project writes it, and the method it sends: `Net::HTTP::Get` sends GET. */
|
|
274
|
+
constructors: Record<string, string>;
|
|
275
|
+
/** Where the request class takes the URL. */
|
|
276
|
+
urlPosition: number;
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* The members a library's response object gives a caller. A caller that
|
|
281
|
+
* tests one of them is saying which statuses it handles, and the
|
|
282
|
+
* checker compares that against what the other side produces.
|
|
283
|
+
*/
|
|
284
|
+
interface RbClientResponse {
|
|
285
|
+
/** Members whose value is the status: `status` for Faraday, `code` for Net::HTTP. */
|
|
286
|
+
statusCode?: string[];
|
|
287
|
+
/** Members that say the request succeeded, meaning a status in 200 to 299: `success?`. */
|
|
288
|
+
success?: string[];
|
|
289
|
+
/** Members that give the body: `body`. */
|
|
290
|
+
body?: string[];
|
|
291
|
+
/** Whether a refused request comes back as a response or raises where it was made. */
|
|
292
|
+
failureDelivery?: "response" | "exception";
|
|
293
|
+
}
|
|
240
294
|
/**
|
|
241
295
|
* Ruby writes no return type, so a call is matched by what its receiver
|
|
242
296
|
* inherits from. A model is a class that reaches one of these base classes,
|
|
@@ -284,6 +338,33 @@ interface ControllerActions {
|
|
|
284
338
|
} | null;
|
|
285
339
|
/** One message per routing declaration kind this pattern's reading left uncovered. A pure read: calling it again gives the same list. */
|
|
286
340
|
routingGaps?: () => readonly string[];
|
|
341
|
+
/** The class-level calls the library gives a controller for running one of its own methods around every action, Rails' `before_action` and `rescue_from`. */
|
|
342
|
+
filters?: RbControllerFilter[];
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* A call in a controller's class body naming a method the library runs
|
|
346
|
+
* around the action, and which actions that reaches. A class inherits
|
|
347
|
+
* what its ancestors declared, so a filter on `ApplicationController`
|
|
348
|
+
* reaches every action in the project.
|
|
349
|
+
*/
|
|
350
|
+
interface RbControllerFilter {
|
|
351
|
+
/** The call as a controller writes it, `before_action`. */
|
|
352
|
+
name: string;
|
|
353
|
+
/**
|
|
354
|
+
* Where the call names the method: `argument` for the leading symbol
|
|
355
|
+
* of `before_action :require_login`, `withKeyword` for the `with:` of
|
|
356
|
+
* `rescue_from ActiveRecord::RecordNotFound, with: :not_found`.
|
|
357
|
+
*/
|
|
358
|
+
methodFrom: "argument" | "withKeyword";
|
|
359
|
+
/** Set when the library runs the method only for an action that raised, which is what `rescue_from` does. */
|
|
360
|
+
onThrow?: boolean;
|
|
361
|
+
/** The name of the call that takes a filter back off, `skip_before_action` for `before_action`. */
|
|
362
|
+
skippedBy?: string;
|
|
363
|
+
/** The keywords that narrow a filter to some of the actions, Rails' `only` and `except`. */
|
|
364
|
+
actionKeywords?: {
|
|
365
|
+
include: string;
|
|
366
|
+
exclude: string;
|
|
367
|
+
};
|
|
287
368
|
}
|
|
288
369
|
/**
|
|
289
370
|
* One call an action writes to send a response, and where that call takes
|
|
@@ -385,6 +466,10 @@ interface DiscoveryOptions extends BodyReadOptions {
|
|
|
385
466
|
filePath: string;
|
|
386
467
|
/** Absolute path of the file being read, for a block's own `ReachedBody.file`. Falls back to `filePath` when nothing was written to disk. */
|
|
387
468
|
absoluteFile?: string;
|
|
469
|
+
/** What a summary's `location.file` says for a file other than the one being read, which a controller's filters need when an ancestor defines them. */
|
|
470
|
+
displayPathOf?: (absolute: string) => string;
|
|
471
|
+
/** The run's own facts, which the value evaluator reads a name through. Absent in a test that builds one file by hand. */
|
|
472
|
+
facts?: Database;
|
|
388
473
|
cache: FileCache;
|
|
389
474
|
/** Called once per discovered unit whose own body is a method this run can follow calls out of, so the reach walk has a place to start. */
|
|
390
475
|
onReachSeed?: (raw: RawCodeStructure, seed: ReachSeed) => void;
|
|
@@ -547,6 +632,8 @@ interface ExtractRubyOptions {
|
|
|
547
632
|
onCacheDiagnostic?: (diagnostic: CacheDiagnostic) => void;
|
|
548
633
|
/** Absolute. `<projectRoot>/.suss/cache` by default; `null` turns it off. */
|
|
549
634
|
cacheDir?: string | null;
|
|
635
|
+
/** What to do with gaps, which composing a controller's filters can add one of. */
|
|
636
|
+
gapHandling?: ExtractorOptions["gapHandling"];
|
|
550
637
|
}
|
|
551
638
|
interface ExtractRubyResult {
|
|
552
639
|
summaries: BehavioralSummary[];
|