Skip to content

Commit 849f955

Browse files
committed
no-mistakes(review): Fix reverse-hop message, doc and Java isAbstract dup
1 parent 10505fd commit 849f955

6 files changed

Lines changed: 26 additions & 18 deletions

File tree

‎docs/features/relationships.md‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,12 @@ reverse-engineered model can `@via` its reference directly:
173173
```
174174
175175
A reference hop is inherently **to-one** (a child names the parent it points at), so it
176-
is valid in a `passthrough` and rejected in an `aggregate`. Inverse navigation
177-
(parent → many children) still needs a `relationship.composition` on the parent — a bare
178-
FK has no inverse edge. Explicit `@via` resolves either kind; single-hop-unique
179-
inference stays relationship-only.
176+
is valid in a `passthrough` and rejected in an `aggregate`. An origin `@via` hop may
177+
also walk an `identity.reference` **backwards** (parent → many children), named by the
178+
holder's collection name in lowerCamel (`Patient.consents`). A reverse hop is to-many, so
179+
it is valid in an `aggregate` and rejected in a `passthrough` (`ERR_ORIGIN_CARDINALITY`);
180+
two candidate holders give `ERR_AMBIGUOUS_PATH`, and a declared relationship or reference
181+
of the same name wins. Single-hop-unique inference stays relationship-only.
180182

181183
## When one entity has two references to the same target
182184

‎server/csharp/MetaObjects/Loader/ValidationPasses.cs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,10 @@ private static void CheckPassthroughCardinality(
13681368
{
13691369
errors.Add(new MetaError(
13701370
$"origin.passthrough on {obj.Name}.{fieldName}: @via hop \"{rel.Name}\" is to-many " +
1371-
$"(@cardinality \"{CARDINALITY_MANY}\") — a row-multiplying passthrough — you meant aggregate (ADR-0029).",
1371+
(reverse?.Contains(rel) == true
1372+
? $"(reverse hop over identity.reference \"{rel.Name}\")"
1373+
: $"(@cardinality \"{CARDINALITY_MANY}\")") +
1374+
" — a row-multiplying passthrough — you meant aggregate (ADR-0029).",
13721375
ErrorCode.ERR_ORIGIN_CARDINALITY,
13731376
Envelope: originSource));
13741377
return;

‎server/java/metadata/src/main/java/com/metaobjects/loader/ReverseReferenceHops.java‎

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static String hopName(MetaObject holder) {
3939
public static List<Hop> find(MetaObject target, String hopName, List<MetaObject> objects) {
4040
List<Hop> found = new ArrayList<>();
4141
for (MetaObject holder : objects) {
42-
if (isAbstract(holder) || !hopName.equals(hopName(holder))) continue;
42+
if (ValidationPhase.isAbstract(holder) || !hopName.equals(hopName(holder))) continue;
4343
// ADR-0039: resolving — a reference may be inherited through extends.
4444
for (MetaData c : holder.getChildren(MetaData.class, true)) {
4545
if (!(c instanceof MetaIdentity) || !MetaIdentity.SUBTYPE_REFERENCE.equals(c.getSubType())) continue;
@@ -51,11 +51,4 @@ public static List<Hop> find(MetaObject target, String hopName, List<MetaObject>
5151
}
5252
return found;
5353
}
54-
55-
private static boolean isAbstract(MetaData node) {
56-
// ADR-0039: own — abstractness is a declaration-layer marker.
57-
if (!node.hasMetaAttr(MetaData.ATTR_IS_ABSTRACT, false)) return false;
58-
Object v = node.getMetaAttr(MetaData.ATTR_IS_ABSTRACT, false).getValue();
59-
return v instanceof Boolean ? (Boolean) v : v instanceof String && "true".equalsIgnoreCase((String) v);
60-
}
6154
}

‎server/java/metadata/src/main/java/com/metaobjects/loader/ValidationPhase.java‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4040,7 +4040,7 @@ private static boolean subtypeSupportsFiltering(String st) {
40404040
* boolean-true. Reads only the own attribute (not effective) — matches the
40414041
* own-only validation contract used throughout this class.
40424042
*/
4043-
private static boolean isAbstract(MetaData node) {
4043+
static boolean isAbstract(MetaData node) {
40444044
if (!node.hasMetaAttr(MetaData.ATTR_IS_ABSTRACT, false)) {
40454045
return false;
40464046
}
@@ -4924,7 +4924,10 @@ private static void checkPassthroughCardinality(java.util.List<MetaData> hops,
49244924
throw new MetaDataException(
49254925
"ERR_ORIGIN_CARDINALITY"
49264926
+ ": origin.passthrough on " + obj.getName() + "." + fieldName
4927-
+ ": @via hop \"" + rel.getName() + "\" is to-many — you meant aggregate (ADR-0029).",
4927+
+ ": @via hop \"" + rel.getName() + "\" is to-many"
4928+
+ (reverse != null && reverse.contains(rel)
4929+
? " (reverse hop over identity.reference \"" + rel.getName() + "\")" : "")
4930+
+ " — you meant aggregate (ADR-0029).",
49284931
ErrorCode.ERR_ORIGIN_CARDINALITY, originSource);
49294932
}
49304933
}

‎server/python/src/metaobjects/loader/validation_passes.py‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1993,8 +1993,12 @@ def _check_passthrough_cardinality(
19931993
errors.append(
19941994
MetaError(
19951995
f"origin.passthrough on {field_name}: @via hop '{rel.name}' is to-many "
1996-
f'(@cardinality "{CARDINALITY_MANY}") — a row-multiplying passthrough — '
1997-
f"you meant aggregate (ADR-0029).",
1996+
+ (
1997+
f'(reverse hop over identity.reference "{rel.name}")'
1998+
if id(rel) in reverse
1999+
else f'(@cardinality "{CARDINALITY_MANY}")'
2000+
)
2001+
+ " — a row-multiplying passthrough — you meant aggregate (ADR-0029).",
19982002
ErrorCode.ERR_ORIGIN_CARDINALITY,
19992003
envelope=origin_source,
20002004
)

‎server/typescript/packages/metadata/src/loader/validation-passes.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,10 @@ function _checkPassthroughCardinality(
10631063
errors.push(
10641064
new ParseError(
10651065
`origin.passthrough on ${obj.name}.${fieldName}: @via hop "${rel.name}" is to-many ` +
1066-
`(@cardinality "${CARDINALITY_MANY}") — a row-multiplying passthrough — you meant aggregate (ADR-0029).`,
1066+
(reverse?.has(rel)
1067+
? `(reverse hop over identity.reference "${rel.name}")`
1068+
: `(@cardinality "${CARDINALITY_MANY}")`) +
1069+
` — a row-multiplying passthrough — you meant aggregate (ADR-0029).`,
10671070
{ code: "ERR_ORIGIN_CARDINALITY", source: originSource },
10681071
),
10691072
);

0 commit comments

Comments
 (0)