@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/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 check it once against the configured root. Nothing
67
- * here searches several roots or chooses between candidates.
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
- /** Null when there is no file at that path. We do not fall back to another root or another spelling. */
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 check it once against the configured root. Nothing
67
- * here searches several roots or chooses between candidates.
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
- /** Null when there is no file at that path. We do not fall back to another root or another spelling. */
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[];