-
Notifications
You must be signed in to change notification settings - Fork 11
[DNM] Make factories support custom entity types #212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
manicki
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No tests for the new method.
I reckon this method would provide a serializer that could handle e.g. items, properties and entities of type X but it there still would not be a way to get a serializer fo entity of type X (I am not sure though if we need such thing, and if this is what you meant by "does not solve a pressing issue").
src/SerializerFactory.php
Outdated
| * | ||
| * @param DispatchableSerializer[]|callable[] $dispatchableSerializers A list of either | ||
| * DispatchableSerializer objects, or callbacks that return such objects. | ||
| * |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
give_n_
| $serializer = call_user_func( $serializer, $this ); | ||
| } | ||
|
|
||
| if ( !( $serializer instanceof DispatchableSerializer ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't there be some message?
ef7ce2c to
4fa0512
Compare
manicki
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no major issues with the production-code side of the PR. Usage of SerializerFactory still needs to adjusted (obviosuly), plus SerializerFactory tests should test pasing in the serializer for the entity type using both the actual object, and the instantiating callback.
src/SerializerFactory.php
Outdated
| * | ||
| * @return Serializer | ||
| * @throws IllegalValueException | ||
| * @return Serializer A dispatching serializer that can serialize whatever the entity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this then say @return DispatchingSerializer? Or is it only for compliance with the interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DispatchingSerializer is not private and we could expose it here. But there is not much value in this. The only difference is that this makes the addSerializer method visible. This should not be needed.
I'm always putting @throws before @return, because it reflects the actual control flow: code can either throw exceptions for different reasons, or return something in the end.
src/SerializerFactory.php
Outdated
| public function __construct( | ||
| array $entitySerializers, | ||
| Serializer $dataValueSerializer, | ||
| $options = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As suggested in the other patch, this could also default to self::OPTION_DEFAULT so that users don't have to wonder what is this zero about.
ccc73d3 to
53b765a
Compare
|
I continued working on this and believe this is finished now. The only detail still missing is a specific test scenario for the new constructor parameter. I also learned
|
src/SerializerFactory.php
Outdated
| * | ||
| * @param DispatchableSerializer[]|callable[] $entitySerializers A list of either | ||
| * DispatchableSerializer objects that are able to serialize Entities, or callbacks that return | ||
| * such objects. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should mention the expected signature of the callback.
| throw new InvalidArgumentException( '$options must be an integer' ); | ||
| } | ||
|
|
||
| $this->entitySerializers = $entitySerializers; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to check using Assert::parameterElementType. Depending on Assert should not be a problem, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is not much value in doing such sanity checks. They are already done in newEntitySerializer, and there is no way around this, because we don't want to run the callables on construction time just to check what they return.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe @brightbyte meant checking all elements in $entitySerializers are callables (or in the current form, either callable or DispatchableSerializer). This would be OK to check I think (so you can filter out garbage already in the constructor).
Running a callback to check what is the type of its return value would be quite overthinking the whole problem, I agree.
| $serializer = call_user_func( $serializer, $this ); | ||
| } | ||
|
|
||
| if ( !( $serializer instanceof DispatchableSerializer ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use Assert::postcondition
| /** | ||
| * @since 3.0 | ||
| * | ||
| * @param DispatchableSerializer[]|callable[] $entitySerializers A list of either |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why accept both? Do we have a use case for providing DispatchingSerializer objects here?
|
@thiemowmde wrote:
Yes, just support callables. |
| public function __construct( | ||
| array $entitySerializers, | ||
| Serializer $dataValueSerializer, | ||
| $options = self::OPTION_DEFAULT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure that $options should be in the constructor here. May also be a parameter of newEntitySerializer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not possible. All kinds of public methods (e.g. newTermListSerializer) use these options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this is what @brightbyte meant but you could have optional $options parameters in each newXyzSerializer method (or at least in those that make use of options), instead of setting $options in the constructor. I am not sure how these options are actually used (ie. at what point it makes more sense to specify options) so I have no opinion on this.
|
"Add SerializerFactory::newDispatchingSerializer" is no longer true, as far as I can tell. |
53b765a to
a4eae04
Compare
thiemowmde
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I minimized this patch a bit. But I can not approve it because of @manicki.
I decided to not remove the feature I talked about earlier: I believe it makes sense to allow the $entity(De)Serializers to contain (de)serializers instead of callables. This can be helpful in situations where a (de)serializer is so trivial that it does not need the factory to construct itself. Or in other words: I think this feature makes sense and is nice. Code-wise it does not make a difference anyway.
| public function __construct( | ||
| array $entitySerializers, | ||
| Serializer $dataValueSerializer, | ||
| $options = self::OPTION_DEFAULT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not possible. All kinds of public methods (e.g. newTermListSerializer) use these options.
| throw new InvalidArgumentException( '$options must be an integer' ); | ||
| } | ||
|
|
||
| $this->entitySerializers = $entitySerializers; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is not much value in doing such sanity checks. They are already done in newEntitySerializer, and there is no way around this, because we don't want to run the callables on construction time just to check what they return.
manicki
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I minimized this patch a bit. But I can not approve it because of @manicki.
Sorry for sleeping on this.
But wait, did we just catch you on attempting to self-merge your PR :)
I had a look at the current state of the patch and it looks quite OK to me. Please just note my question about the exception, I might be asking about something irrelevant there.
Few things @brightbyte suggested are still open but possibly those could be further improvements.
About this:
I decided to not remove the feature I talked about earlier: I believe it makes sense to allow the $entity(De)Serializers to contain (de)serializers instead of callables. This can be helpful in situations where a (de)serializer is so trivial that it does not need the factory to construct itself. Or in other words: I think this feature makes sense and is nice. Code-wise it does not make a difference anyway.
I agree this sounds like a nice feature but then: do we have a real case where this is/could be used? Be it a nice feature but without this being used (if not now than in a foreseeable future) this seems YAGNI to me. I am not strongly opposing this, and I'd be OK with having this, I am just wondering whether something being nice is a good enough justification.
src/DeserializerFactory.php
Outdated
| } | ||
|
|
||
| if ( !( $deserializer instanceof DispatchableDeserializer ) ) { | ||
| throw new IllegalValueException( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder about the exception choice here. Isn't IllegalValueException a DataValues-specific exception. How those serializers are related to DataValues? Why not throw standard InvalidArgumentException?
| throw new InvalidArgumentException( '$options must be an integer' ); | ||
| } | ||
|
|
||
| $this->entitySerializers = $entitySerializers; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe @brightbyte meant checking all elements in $entitySerializers are callables (or in the current form, either callable or DispatchableSerializer). This would be OK to check I think (so you can filter out garbage already in the constructor).
Running a callback to check what is the type of its return value would be quite overthinking the whole problem, I agree.
| public function __construct( | ||
| array $entitySerializers, | ||
| Serializer $dataValueSerializer, | ||
| $options = self::OPTION_DEFAULT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this is what @brightbyte meant but you could have optional $options parameters in each newXyzSerializer method (or at least in those that make use of options), instead of setting $options in the constructor. I am not sure how these options are actually used (ie. at what point it makes more sense to specify options) so I have no opinion on this.
Approval is not self-merge. ;-)
Not at the moment. But this will happen when an entity type does not reuse any of the concepts available in the (De)SerializerFactory.
Look at the code. Disallowing (de)serializer objects and enforcing callables would make it more complicated.
That was a mistake, good catch! Should be UnexpectedValueException. InvalidArgumentException does not fit because the method in question doesn't even have arguments.
Sure, but as said, I believe there is not much value in this. This is all checked anyway a few milliseconds later.
First, I think this is ugly. Second, this would make the return values incompatible to each other. You can no longer pass a SerializerFactory around that is able to construct all kinds of serializers that are compatible with each other, without also passing the |
a4eae04 to
15fbf8d
Compare
I don't know why you say that. I guess you did not run all tests when you tried this. Please check again and be more specific with your edit request. |
|
I checked out the code and run all the tests. They don't fail if it is replaced with []
|
|
Can't really review this one... part of a bigger approach which I think is fundamentally flawed (and counterproductive). Will bring that up again soonish at a better place. I'm a bit sadened no one else is seeing this |
You said that already. As I said you must have done something wrong. When I run the tests they fail when there are no entity (de)serializers. Which makes sense because the tests in question are actually testing the entity (de)serialization.
Do you realize your comment does not give the slightest hint at what you mean? Can you please clarify? |
I don't think @JeroenDeDauw thinks it does not make sense to explain this here, and I believe it is fine as he thinks the whole approach is wrong, and want to make a more general remark, not just one details of this particular PR. I am patiently looking forward to what he is going say in that better place :) It is not impossible that the approach is flawed. We're not going to have a code with this approach released in this very moment, so discussing it and fixing possible improvements seems like a very good idea to me. |
15fbf8d to
93b1a10
Compare
Sorry, my mistake. Was running wrong set of tests. |
93b1a10 to
6ebd9db
Compare
|
Wrong approach, this component is the base DataModel serialization, and not supposed to know about other entity types. See https://gerrit.wikimedia.org/r/338749 instead. |
Do not merge! I figured this is the wrong approach. See https://gerrit.wikimedia.org/r/338749 instead.
This is more than a convenient method, but solves an actual issue. This allows us to construct a SerializerFactory (and pass it around) that is able to serialize more than the basic entity types.Please review this along with wmde/WikibaseInternalSerialization#110. Both changes must be done together, or not. As @JeroenDeDauw hinted below the architecture proposed in these two patches might be flawed. Could it be that we are just confused by these duplicate (De)SerializerFactories? That other component is called "internal". What does this mean? Could it be that the only thing we need to do is to rename these components and classes to make it more obvious what they represent?
Bug: T157596
Bug: T157960