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
module inheritance (moog) is difficult to master, developers don't know where to put their code in modules #1940
Comments
From my perspective, only take a first glance, It is sooooo familiar and know how to hands on it. Although , it would be nice to have consistent syntax name. Like before, moog have |
Good points. My concern about keeping beforeConstruct is that it might be
clear to experienced apostrophe developers but I doubt that new developers
know what it means. adjustOptions is new to us, but clear to newcomers.
…On Wed, Jun 5, 2019, 7:53 AM Amin Shazrin ***@***.***> wrote:
From my perspective, only take a first glance, It is sooooo familiar and
know how to hands on it. Although , it would be nice to have consistent
syntax name. Like before, moog have beforeConstruct, construct and
afterConstruct . Same goes to react , they have componentDidMount,
componentDidCatch and etc. Also react have special event method such as
snapshotBeforeUpdate and others. But still has its own familiar
consistent syntax to work on . So, adjustOptions would be a quite
confusing method name for adjusting options than using previous method
name on beforeConstruct . Above all that, I loveeeeee the new replacement
for module inheritance and I notice that extend has been change into
extends to maintain syntax consistency across all familiar frameworks.
Good job ! 👍
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1940?email_source=notifications&email_token=AAAH27LBUXJAVD73KGPRFQTPY6SMFA5CNFSM4HTK3NIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODW7O3GQ#issuecomment-499051930>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAH27I7ZMA6MF55OYMC3Z3PY6SMFANCNFSM4HTK3NIA>
.
|
Agree , using explicit matches syntax name like |
Another question to answer: should we introduce an |
I think the way you pronounce the idea of |
In conventional Apostrophe, when you write:
```
{
extend: 'foo',
construct: function(self, options) { ... },
color: 'green'
}
```
It's obvious what the first two keys do.
The third key becomes `self.options.color`.
This is how we're able to configure modules simply in `app.js`.
The problem is that as we add more "special" keys in this new proposal,
there are more and more chances it could conflict with something someone
wants to use as a "plain old option."
If we move the "plain old options" like this:
```
{
extends: 'foo',
startup: function(self, options) { ... },
options: {
color: 'green'
}
}
```
Then there are no restrictions on option names.
One consequence is that in "app.js" we would have to write:
…On Thu, Jun 6, 2019 at 12:15 PM Amin Shazrin ***@***.***> wrote:
I think the way you pronounce the idea of options key is similar to React
state or Vue data , is it ? If that so, yes , i think its a good idea to
replace with our 'plain old options' and way more familiar to work with
using options : {}. I hope that answers to your question
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1940?email_source=notifications&email_token=AAAH27L4NTIPHRPBXCMH5NDPZEZ3NA5CNFSM4HTK3NIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXDMCUQ#issuecomment-499564882>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAH27OKKJY4A6T6OKIJZH3PZEZ3NANCNFSM4HTK3NIA>
.
--
*Thomas Boutell, Chief Software Architect*
P'unk Avenue | (215) 755-1330 | punkave.com
|
... in app.js we would have to write:
```
modules: {
'apostrophe-assets': {
options: {
lean: true
}
}
}
```
For example.
We could also decide that app.js can ONLY set options, and anything else
MUST live in an index.js file. But, a lot of people like to put `extend` in
app.js because it makes it easier to see what everything is at a glance.
…On Thu, Jun 6, 2019 at 1:11 PM Tom Boutell ***@***.***> wrote:
In conventional Apostrophe, when you write:
```
{
extend: 'foo',
construct: function(self, options) { ... },
color: 'green'
}
```
It's obvious what the first two keys do.
The third key becomes `self.options.color`.
This is how we're able to configure modules simply in `app.js`.
The problem is that as we add more "special" keys in this new proposal,
there are more and more chances it could conflict with something someone
wants to use as a "plain old option."
If we move the "plain old options" like this:
```
{
extends: 'foo',
startup: function(self, options) { ... },
options: {
color: 'green'
}
}
```
Then there are no restrictions on option names.
One consequence is that in "app.js" we would have to write:
On Thu, Jun 6, 2019 at 12:15 PM Amin Shazrin ***@***.***>
wrote:
> I think the way you pronounce the idea of options key is similar to
> React state or Vue data , is it ? If that so, yes , i think its a good
> idea to replace with our 'plain old options' and way more familiar to work
> with using options : {}. I hope that answers to your question
>
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub
> <#1940?email_source=notifications&email_token=AAAH27L4NTIPHRPBXCMH5NDPZEZ3NA5CNFSM4HTK3NIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXDMCUQ#issuecomment-499564882>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAAH27OKKJY4A6T6OKIJZH3PZEZ3NANCNFSM4HTK3NIA>
> .
>
--
*Thomas Boutell, Chief Software Architect*
P'unk Avenue | (215) 755-1330 | punkave.com
--
*Thomas Boutell, Chief Software Architect*
P'unk Avenue | (215) 755-1330 | punkave.com
|
Hmm, I understand the issue. If we set this as default , it will conflict our options setup through all modules. I like the idea of inserting an |
You can imagine that a wrapper is similar to |
I think that for 3.0 we could do without the wrapper. If we want to be able
to use this syntax in 2.x without a bc break, the wrapper makes a lot of
sense.
…On Thu, Jun 6, 2019 at 2:51 PM Amin Shazrin ***@***.***> wrote:
You can imagine that a wrapper is similar to React.memo(App) and others.
The reason to make it as a wrapper is because we can adjust the way module
is structured are much more cleaner than needed to adjust the whole
apostrophe core modules. What do you think ?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1940?email_source=notifications&email_token=AAAH27IPTT67UTL6FJ3NMHTPZFMCDA5CNFSM4HTK3NIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXDZ25A#issuecomment-499621236>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAH27P5THQZD2EMA7XJV6LPZFMCDANCNFSM4HTK3NIA>
.
--
*Thomas Boutell, Chief Software Architect*
P'unk Avenue | (215) 755-1330 | punkave.com
|
Change "override" to "extend" because if you just want to override you can just declare again. |
@boutell or maybe change to "super" ? That could be much more convenience, right ? Superpower ! 😅 |
|
Hmm. Changing it to "super" is tempting, but that sounds like it... is the super function, as seen in other languages... which, it is not. The first argument to each extended method will be |
@boutell Yeah, I guess you're right. In other language like Java are using "extends" & "override" kinds of methods. So it would be wise to follow familiar syntax name and consistent. |
A new wrinkle: what about middleware? The above syntax has no way of accommodating middleware in, say, an Here is a syntax that does allow middleware. I also included a regular route without middleware to show that the simplified syntax isn't going away. This is what I'm implementing for now but discussion is welcome.
What if a middleware method is a method of the same module? Will it exist in time to be passed to |
I changed I am merging moog into a3 rather than releasing another separate npm version of moog, which has never generated much interest by itself, so it makes sense to drop an npm dependency. moog-require too. |
We can't use
|
Hmm, indeed super is reserved word. However, I was thinking about a proper naming arguments without using underscore at begining of super word. Maybe we could teach devs to use superMethod as an argument ,for instance |
It provides clear naming like previous apostrophe tutorial that using super method like |
I hear you but I think one of the wins here is that it is already explicit
which method we are extending so we don't need the extra language.
…On Sat, Jun 15, 2019, 3:50 AM Amin Shazrin ***@***.***> wrote:
It provides clear naming like previous apostrophe tutorial that using
super method like superGetCreateSingleton
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1940?email_source=notifications&email_token=AAAH27NOVIRFUKDXC4QAYP3P2SNLNA5CNFSM4HTK3NIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXYSUUQ#issuecomment-502344274>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAH27MQ77YLEY2BZWIFQHTP2SNLNANCNFSM4HTK3NIA>
.
|
No reason you can't do it though. It's just the first argument, whatever
you choose to call it.
…On Sat, Jun 15, 2019, 8:00 AM Tom Boutell ***@***.***> wrote:
I hear you but I think one of the wins here is that it is already explicit
which method we are extending so we don't need the extra language.
On Sat, Jun 15, 2019, 3:50 AM Amin Shazrin ***@***.***>
wrote:
> It provides clear naming like previous apostrophe tutorial that using
> super method like superGetCreateSingleton
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <#1940?email_source=notifications&email_token=AAAH27NOVIRFUKDXC4QAYP3P2SNLNA5CNFSM4HTK3NIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXYSUUQ#issuecomment-502344274>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAAH27MQ77YLEY2BZWIFQHTP2SNLNANCNFSM4HTK3NIA>
> .
>
|
Noted. I think it makes sense too and much more shorter. Besides , its in argument rather than declare it as variable which may cause duplication declare variable name. |
Now that these will just be "apostrophe types", the "moogBundle" property can just become "bundle". |
Per discussion today, revising the proposal with just a single, exported (self, options)... function containing everything else. Also adding a section for fields (with add, remove, arrange... subsections) that will eliminate pretty much all "options.addFields.concat..." boilerplate. Moving to objects rather than arrays for schema configuration because it's clear and succinct to use the field's name as a key rather than a "name" property, because it expresses the requirement of uniqueness nicely, and because we can: ES6+ guarantees order when iterating properties. |
I've just updated the proposal at the top of this ticket with two important changes:
There are smaller changes too; please have a look! We are continuing with implementation of the 2.x module converter and the 3.x code to power these modules. |
Today we sorted out that the single wrapper function is indeed not a good idea after all. The first invocation will have a bogus So we're back to an older version of the proposal with more However we also changed |
Added |
beforeBaseClass renamed to beforeSuperClass, as "super" is the term of art in both apostrophe and ES6 classes (which we're not using, but there's no point in introducing a competing word). |
I am actively implementing this in 3.x. Most of it is great, but after getting some experience with |
Updated to add |
Revised that to a
|
Today I spent a lot of time working out a thorny issue with the design of A3 modules. It took a while, but I figured out that:
I wrote epic notes as I worked through this thought process, but the end result is simple and you can see it here. It's better to write the right code, not more code. Tomorrow I will implement this tweak and then conditional configuration of middleware will behave sensibly. |
I have completed the refactoring to call init() when methods, fields and options are ready but before sections are built, so that the section functions can invoke methods to decide what routes they want to register, etc. I did also wind up introducing |
We talked about this a little bit, but I think it might be worth us thinking about documenting extenders with something like |
Errata:
* A bunch of cases where the opportunity to use method syntax was missed in
the examples (still writing function).
* extendMiddleware is probably just a bad idea because of the ambiguity of
what `next` does. If you want your middleware to be extend-friendly it
should be implemented by invoking a series of methods.
* "beforeSave" is a poor example of a method for this guide because we are
replacing that particular method with promise events.
Can't remember if I said "oh yeah, that's a goof" about anything else on
the call just now.
…On Tue, Apr 14, 2020 at 2:34 PM Alex Bea ***@***.***> wrote:
We talked about this a little bit, but I think it might be worth us
thinking about documenting extenders with something like _original rather
than _super as the extended function. super is a JS reserved word
referring to something similar, however it's specific to Classes, so I
don't know if we get the full value of using a familiar term.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1940 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAH27JZFDI3ERXPAUTGY6LRMSUE5ANCNFSM4HTK3NIA>
.
--
THOMAS BOUTELL | CHIEF TECHNOLOGY OFFICER
APOSTROPHECMS | apostrophecms.com | he/him/his
|
Here is the planned replacement for the current
index.js
syntax for modules. This is not backwards compatible, it's the 3.0 format. However a conversion tool is under construction and has already been used to help convert apostrophe core itself.In general, we are deprecating the imperative, "build the module by calling stuff" style and replacing it with a declarative style, while avoiding technical terms and invented language.
Why do we think this is better?
self, options
function for each section that contains functions provides a scope with access to themodule. Separating those functions by section is a little wordy, but attempting to merge them in a single function leads
to significant confusion. For instance, you can't pass a method as the function for a route unless methods are initialized
first in a separate call. And properties like
extend
must be sniffable before construction of the object begins, whichmeans we can't just export one big function that returns one object, or else we'd have to invoke it twice; the first time it would
be without a meaningful
self
oroptions
, leading to obvious potential for bugs.methods
is a simple and descriptive name, familiar from Vue, which has been very successful in achieving developer acceptance, even though Vue also does not use ES6 classes for not entirely dissimilar reasons. In general, Vue components have been designed with simple and descriptive language wherever possible, and we can learn from that and avoid inside baseball jargon.extendMethods
is a similar, however here each method's first argument is_super
, where_super
is a reference to the method we're overriding from a parent class. We now have complete freedom to call_super
first, last, or in the middle in our new function. It is much less verbose than our currentsuper
pattern. Organizing all of these extensions inextendMethods
makes the intent very clear. Note that if you just want to "override" (replace) a method, you declare it inmethods
and that straight up crushes the inherited method.extendMethods
is for scenarios where you need reuse of the original method as part of the new one. We use_super
becausesuper
is a reserved word.handlers
andextendHandlers
provide similar structure for promise event handlers. Again, these get grouped together, making them easier to find, just like Vue groups togethercomputed
,methods
, etc. As always, handlers must be named. Handlers for the same event are grouped beneath it. This is loosely similar to Vue lifecycle hooks, but intentionally not identical because Apostrophe involves inheritance, and everything needs to be named uniquely so it can be overridden or extended easily.helpers
andextendHelpers
: you get the idea. For nunjucks helpers.apiRoutes
andextendApiRoutes
: you'd actually be able to addapiRoutes
,htmlRoutes
and plain oldroutes
. see recent Apostrophe changelogs if this is unfamiliar. Note subproperties separating routes of the same name with different HTTP methods.fields
: just... just look at it. This clobbers addFields/removeFields with tons of beforeConstruct boilerplate.middleware
andextendMiddleware
: replaces the currentexpressMiddleware
property, which is a bit of a hack, with a clear way to define and activate middleware globally. Note thebefore
option which covers some of the less common but most important uses ofexpressMiddleware
in 2.x. As for middleware that is only used by certain routes, methods are a good way to deliver that, as shown here. Note that methods will completely initialize, from base class through to child class, before routes start to initialize, so they will see the final version of each method.init
is a common name in other frameworks for a function that runs as soon as the module is fully constructed and ready to support method calls, etc. This replaces the inside-baseball nameafterConstruct
.beforeSuperClass
is very explicit and hopefully, finally, clarifies when this function runs: before the base class is constructed. It is theonly function that runs "bottom to top," i.e. the one in the subclass goes first. We used to call it
beforeConstruct
which says nothingabout the timing relative to the base class. It is used to manipulate
options
before the parent class sees them,however most 2.x use cases have been eliminated by the introduction of the
fields
section.queries
replaces what people used to put incursor.js
files, specificallyaddFilters
calls as well as custom methods. It obviates the need for a separatemoog
type for cursors,moog
is now used only to instantiate modules and ceases to have its own "brand."queries
only makes sense in a module that inherits fromapostrophe-doc-type-manager
."What about breaking a module into multiple files?" Well that's a good question, we do this now and it's a good thing. But, nobody's stopping anyone from using
require
in here. It would work like it does today, you'd pass inself
orself, options
to a function in the required file.The text was updated successfully, but these errors were encountered: