Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/js/utils/class.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function extendThis(childClass, parentClass, config) {
Object.defineProperty(childClass.prototype, prop, { get, set });
}
else {
childClass.prototype[prop] = parentClass.prototype[prop];
childClass.prototype[prop] = protos[prop];
}
seen.push(prop); // don't override with older definition in hierarchy
return null;
Expand Down
2 changes: 1 addition & 1 deletion src/js/utils/class.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function extendThis(childClass, parentClass, config) {
if (get || set) {
Object.defineProperty(childClass.prototype, prop, { get, set });
} else {
childClass.prototype[prop] = parentClass.prototype[prop];
childClass.prototype[prop] = protos[prop];
}
seen.push(prop); // don't override with older definition in hierarchy
return null;
Expand Down
1 change: 1 addition & 0 deletions src/py/mat3ra/code/mixins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from mat3ra.esse.models.system.description import DescriptionSchema
from mat3ra.esse.models.system.metadata import MetadataSchema
from mat3ra.esse.models.system.name import NameEntitySchema
from .hashed_entity import HashedEntityMixin


class DefaultableMixin(DefaultableEntitySchema):
Expand Down
23 changes: 23 additions & 0 deletions src/py/mat3ra/code/mixins/hashed_entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Any, Dict

from mat3ra.utils.object import calculate_hash_from_object


class HashedEntityMixin:
"""
Mixin for entities that compute a deterministic hash from "meaningful fields".

Mirrors the JS `HashedEntityMixin`: child classes override `get_hash_object()`,
while `calculate_hash()` hashes that object.
"""

def get_hash_object(self) -> Dict[str, Any]: # pragma: no cover
return {}

def calculate_hash(self) -> str:
return calculate_hash_from_object(self.get_hash_object())

@property
def hash(self) -> str:
return self.calculate_hash()

21 changes: 21 additions & 0 deletions tests/js/utils/class.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ class BaseEntity extends RuntimeItemsMixin(InMemoryEntity) {
}
}

const PrototypeMethodMixin = (superclass: any) =>
class extends superclass {
methodFromMixinPrototype() {
return "from-mixin";
}
};

class BaseEntityWithPrototypeMethodFromMixin extends PrototypeMethodMixin(BaseEntity) {}

class ExtendClassEntity extends NamedInMemoryEntity {
declare results: unknown;

Expand Down Expand Up @@ -75,6 +84,13 @@ class ExtendThisEntity extends BetweenEntity {
}
}

class ExtendThisEntityFromMixedBase extends BetweenEntity {
constructor(config: object) {
super(config);
extendThis(ExtendThisEntityFromMixedBase, BaseEntityWithPrototypeMethodFromMixin, config);
}
}

defaultableEntityStaticMixin(ExtendThisEntity);
defaultableEntityMixin(ExtendThisEntity.prototype);

Expand Down Expand Up @@ -106,6 +122,11 @@ describe("extendThis", () => {
expect(JSON.stringify(obj.results)).to.be.equal(JSON.stringify([{ name: "test" }]));
});

it("copies prototype methods defined by mixins", () => {
const obj = new ExtendThisEntityFromMixedBase({});
expect((obj as any).methodFromMixinPrototype()).to.be.equal("from-mixin");
});

it("remembers intermediate methods", () => {
const base = new BaseBetweenEntity({});
expect(base.betweenMethod()).to.be.equal("base");
Expand Down
13 changes: 12 additions & 1 deletion tests/py/unit/test_mixins.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Optional

from mat3ra.code.mixins import DefaultableMixin, NamedMixin
from mat3ra.code.mixins import DefaultableMixin, HashedEntityMixin, NamedMixin
from mat3ra.utils.object import calculate_hash_from_object


def test_defaultable_mixin():
Expand Down Expand Up @@ -60,3 +61,13 @@ class ExampleComplex(DefaultableMixin, NamedMixin):
assert instance.key is None
assert instance.number is None
assert hasattr(instance, "isDefault")


def test_hashed_entity_mixin():
class ExampleHashed(HashedEntityMixin):
def get_hash_object(self):
return {"b": 1, "a": 2}

instance = ExampleHashed()
assert instance.calculate_hash() == calculate_hash_from_object({"b": 1, "a": 2})
assert instance.hash == instance.calculate_hash()
Loading