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
Review and improve Extension API #246
Comments
Some random ideas I've come up with while developing a Reports extension: PHP
JavaScript
|
I'm currently trying to develop a small extension, so here's my (little) feedback : I just encounter the monkey patch constructor problem, I wanted to add some props to the TextEditor component. I think switching to an event-based api (or action based like rackt/redux ) could improve code decoupling. EDIT : I'm also encountering another trouble in PHP part : Post::saved(function($post) {
// do some stuff
}); but since the Post is a CommentPost, it doesn't fire the event... See http://laravel.io/forum/06-08-2015-events-not-firing-on-inherited-model-how-can-i-fix-this or maybe I'm doing it wrong. |
@YoruNoHikage Thanks for the feedback! Regarding the $post::saved(function ($post) {
// do some stuff
}); I'm actually hesitant to switch away from monkey patching now, because it will be a big time loss. I think we can make the monkey patching work, especially since it's effectively just a different form of event handling. I've added an |
Oh right, thanks for the tip, I totally missed the fact that I could use |
Something wrong with the init function, see : class Component {
constructor() {
// define some properties
this.init();
}
}
class SubComponent extends Component {
constructor(...args) {
super(...args);
// define some new properties
}
} And here's the problem when monkeypatching SubComponent like this : extend(SubComponent.prototype, 'init', function() {
// Unable to access to the new properties defined in SubComponent
}); To see a concrete case, just look at |
Interesting. I can think of two possible solutions:
if (!this.hasBeenMonkeyPatched) {
applyMonkeyPatch(this);
this.hasBeenMonkeyPatched = true;
} I think number 1 is the way to go. Thoughts? |
Not really. First solution requires to be very strict and means a bit of refactoring but that could be good. |
I don't want to be disruptive here, but would you be open to refactoring? Prototypal OO has a lot to offer here that may writing extensions a whole lot easier. Maybe I'll be just doing this myself, just to test how it works out. Not yet sure if this would be a good or bad idea. ;) |
@darkspotinthecorner Probably not at this stage, but I would still be interested to see a concrete example of how this would actually look (e.g. a gist with an example component and an example extension). :) |
@tobscure I'll have a go, then. ;) |
- Reorganised all namespaces and class names for consistency and structure. Following PSR bylaws (Abstract prefix, Interface/Trait suffix). - Move models into root of Core, because writing `use Flarum\Core\Discussion` is nice. Namespace the rest by type. (Namespacing by entity was too arbitrary.) - Moved some non-domain stuff out of Core: Database, Formatter, Settings. - Renamed config table and all references to "settings" for consistency. - Remove Core class and add url()/isInstalled()/inDebugMode() as instance methods of Foundation\Application. - Cleanup, docblocking, etc. - Improvements to HTTP architecture - API and forum/admin Actions are now actually all the same thing (simple PSR-7 Request handlers), renamed to Controllers. - Upgrade to tobscure/json-api 0.2 branch. - Where possible, moved generic functionality to tobscure/json-api (e.g. pagination links). I'm quite happy with the backend balance now re: #262 - Improvements to other architecture - Use Illuminate's Auth\Access\Gate interface/implementation instead of our old Locked trait. We still use events to actually determine the permissions though. Our Policy classes are actually glorified event subscribers. - Extract model validation into Core\Validator classes. - Make post visibility permission stuff much more efficient and DRY. - Renamed Flarum\Event classes for consistency. ref #246 - `Configure` prefix for events dedicated to configuring an object. - `Get` prefix for events whose listeners should return something. - `Prepare` prefix when a variable is passed by reference so it can be modified. - `Scope` prefix when a query builder is passed. - Miscellaneous improvements/bug-fixes. I'm easily distracted! - Increase default height of post composer. - Improve post stream redraw flickering in Safari by keying loading post placeholders with their IDs. ref #451 - Use a PHP JavaScript minification library for minifying TextFormatter's JavaScript, instead of ClosureCompilerService (can't rely on external service!) - Use UrlGenerator properly in various places. closes #123 - Make Api\Client return Response object. closes #128 - Allow extensions to specify custom icon images. - Allow external API/admin URLs to be optionally specified in config.php. If the value or "url" is an array, we look for the corresponding path inside. Otherwise, we append the path to the base URL, using the corresponding value in "paths" if present. closes #244
This allows component state to be overridden via monkey-patch. ref #246
Alright @YoruNoHikage, all components now initialise their state in init() instead of the constructor :) |
Ok, thanks, I hope there would be no problem now. :) |
can we see monkey patching in back-end too? |
Monkey patching works very well with JS because of its prototype-based inheritance model (and the very loose typing). But unless you have some example of an extensibility use case that you cannot solve without it, we will probably keep the purely OO based, event-centric approach we currently use in the backend... |
The remaining relevant items here seem to be:
|
Split into #2700, https://github.com/flarum/core/issues/2701 |
I would love to get some feedback on Flarum's Extension API. If you haven't seen much of it, check out the documentation, as well as the source code for all of Flarum's packaged extensions.
Scoping
Currently we don't distinguish very well between what's part of the public API and what's not – and if we want to do SemVer, we need to do this much better. Given how the API is set up, how can we effectively do this? Some ideas:
_
. Make sure docblocks reflect that too.@api
docblock tag to denote our public API?private
instead ofprotected
.final
?Monkey Patching
Extending the JavaScript application uses monkey patching as its primary mechanism. I chose this because it was the simplest, most straightforward option – however, I'm wondering about a few potential pitfalls:
constructor
s cannot be reliably monkey patched because sometimes the constructor property is used to access a static method (e.g.this.constructor.whatever()
). We could work around this by making our monkeypatch methods copy over the original object's properties.The obvious alternative is to have an event-based API, like on the backend. This would entail firing events in all methods that were previously subject to monkey-patching, and then changing all instances of
extend
andoverride
into event listener registrations. (This would help to better define the scope of the API too.) While a big change for this stage of development, it's certainly not too late. I would just like to get some second opinions.Naming
I think I want to tighten up the naming of Flarum\Event classes, as they're a bit inconsistent at the moment. That and elsewhere, are there any names that you find confusing, and do you have any suggestions for replacements?
General Feedback
If you have experience with public APIs, can you tell us: What could be better about Flarum's API? What parts are good, what parts are bad?
The text was updated successfully, but these errors were encountered: