@suss/adapter-ruby 0.3.2-0 → 0.4.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 +66 -0
- package/dist/index.cjs +819 -6
- package/dist/index.js +815 -2
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -32,6 +32,72 @@ What a body does still goes unread. Reading it needs the path engine: statements
|
|
|
32
32
|
|
|
33
33
|
One thing the walk deliberately leaves out. A `field` declared on a base object type is not discovered on its subclasses. That would change which units exist rather than what each one says about itself, and it is a separate piece of work.
|
|
34
34
|
|
|
35
|
+
## What a body lowers to
|
|
36
|
+
|
|
37
|
+
The adapter lowers a method body into the statement form the shared path
|
|
38
|
+
engine in `@suss/extractor` walks, the same engine the Python and TypeScript
|
|
39
|
+
adapters use. It is generic over the language's own condition handle and never
|
|
40
|
+
looks inside one, so the enumeration and the negation of an earlier arm are
|
|
41
|
+
shared rather than written again here.
|
|
42
|
+
|
|
43
|
+
| Ruby | Lowers to |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `if` / `elsif` / `else`, `unless` | one `if` per test, with the elsif chain nested into the else arm |
|
|
46
|
+
| `while`, `until`, `for` | `loop` |
|
|
47
|
+
| a call with a `do` block, such as `items.each do \|i\|` | `loop`, because the block runs per iteration |
|
|
48
|
+
| `begin` / `rescue` / `ensure` | `try` |
|
|
49
|
+
| `case` / `when` / `else` | `switch`, with `else` as the default group |
|
|
50
|
+
| `return`, `raise`, `break`, `next` | `exit` |
|
|
51
|
+
| anything else | `opaque` |
|
|
52
|
+
|
|
53
|
+
Three things read differently from Python, and each one is why this file
|
|
54
|
+
exists rather than a shared lowering:
|
|
55
|
+
|
|
56
|
+
- **`raise` is an ordinary method call**, not a keyword, so a throw is
|
|
57
|
+
recognised by the call's name rather than by a node type.
|
|
58
|
+
- **A `return` inside a `do` block returns from the method**, so the scan
|
|
59
|
+
descends into one. A lambda captures its own return, so the scan stops
|
|
60
|
+
there.
|
|
61
|
+
- **A method returns its last expression** with no `return` written, which
|
|
62
|
+
Python has no equivalent of.
|
|
63
|
+
|
|
64
|
+
### Keying anything on a node
|
|
65
|
+
|
|
66
|
+
tree-sitter hands back a fresh wrapper object every time a child is read, so
|
|
67
|
+
two reads of one node are never `===` and a plain `Set` or `Map` keyed on a
|
|
68
|
+
node matches nothing. Use `NodeSet` and `NodeMap`, which key on the node id.
|
|
69
|
+
`npm run check:style` fails a build that keys either on a node.
|
|
70
|
+
|
|
71
|
+
## Finding the definition behind a constant
|
|
72
|
+
|
|
73
|
+
Ruby has no imports. A file says `require` to load another file, and after
|
|
74
|
+
that every constant either one defines is reachable by name, so which file a
|
|
75
|
+
name came from is settled by where the constant is defined rather than by
|
|
76
|
+
anything written at the reading site. The other two adapters emit `imports`
|
|
77
|
+
and this one binds a reference straight to its definition.
|
|
78
|
+
|
|
79
|
+
Lookup follows Ruby's own rule. A name written inside `module Types; class
|
|
80
|
+
Wrapper` is looked for as `Types::Wrapper::Order`, then `Types::Order`, then
|
|
81
|
+
`Order`, and the first one that settles wins:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
module Types
|
|
85
|
+
class Order; end
|
|
86
|
+
class Wrapper
|
|
87
|
+
def build
|
|
88
|
+
Order # Types::Order, not the top-level one
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
A name two files define under the same nesting says nothing, because choosing
|
|
95
|
+
between them would be a guess. Neither does a constant built at run time
|
|
96
|
+
through `const_set` or `Object.const_get`, which nothing here reads.
|
|
97
|
+
|
|
98
|
+
The definitions are collected per file and matched afterwards, since which
|
|
99
|
+
file defines a constant is only settled once every file has been read.
|
|
100
|
+
|
|
35
101
|
## Where it fits in suss
|
|
36
102
|
|
|
37
103
|
Depends on `@suss/extractor` (for `RawCodeStructure` / `assembleSummary`), `@suss/behavioral-ir`, `@suss/datalog` (for the fact database), and `web-tree-sitter`. Framework packs under `packages/framework/*` (starting with `@suss/framework-graphql-ruby`) consume its `RubyPack` contract; nothing in this package knows what any particular library's classes or DSL calls are named beyond graphql-ruby's own `field` / `argument` / `type` verbs, which the discovery logic reads structurally rather than through pack configuration.
|