Ticket #2530093 (closed task)
Reporter Eric Ferraiuolo |
Opened: 04/6/11 Last modified: 08/18/11 Status: closed Type: task Resolution: fixed |
Owner Satyen Desai |
Target Release: 3.4.0 Priority: P3 (normal) |
|---|---|---|---|
| Summary: | [Pull Request] - Make Y.Attribute augmentation by not forcing the call to addAttrs in the Attribute constructor when the instance isn't a Y.Node instance. | ||
| Description: | Recently when creating a lower-level class (non Y.Base-based) with Y.Attribute support I ran into issues Y.augmenting my class with Attribute and also just trying to mix Y.Attribute's prototype with Inside the Y.Attribute constructor (which does useful stuff that I'd like to call) there's a forced call to `addAttrs` if the instance object (`this`) is not Y.Base-based. The reason this is a problem Inside Y.Base's constructor you see a call to `Y.Attribute(this)` and later a call during initialization to `this.addAttrs` where the user-provided values are passed to `addAttrs` along with the ATTRS In my class' constructor if I call `Y.Attribute.call(this)` (or let Y.augment call it for me) I'm forced to have `this.addAttrs` called on me because my class is not a Y.Base subclass. Looking at the If the reason this code path is added to provide a quick way to add Y.Attribute support via `Y.augment`ing your class, then the Y.Attribute constructor should also optionally take in the user-provide So not sure which approach you think would be better? A stricter conditional check for an instanceof Y.Node around the call to `this.addAttrs` in Y.Attribute's constructor, or a To get around this issue, I had to copy almost the entire Y.Attribute constructor logic and repeat it in my code so that I could make the call to `this.addAttrs` the way I wanted to, by passing on the |
||
| Type: | task | Observed in Version: | development master |
| Component: | Attribute | Severity: | S3 (normal) |
| Assigned To: | Satyen Desai | Target Release: | 3.4.0 |
| Location: | Priority: | P3 (normal) | |
| Tags: | pull request, github | Relates To: | |
| Browsers: | All | ||
| URL: | https://github.com/ericf/yui3/commit/7903173b2c5e64c69cccd6bf3a79a2a8e68965a7 | ||
| Test Information: | |||
Change History
|
Posted: 04/6/11
|
|
Posted: 04/27/11
|
|
Posted: 04/28/11
|
|
Posted: 05/15/11
|
|
Posted: 05/15/11
|
|
Posted: 05/17/11
Hey Eric, So your use case has a non-Base based object which also sets up it's own ATTRS static property? It seems like those 2 things combined are what is getting in your way (that is, if you didn't have an ATTRS property, you wouldn't hit the addAttrs code block either). I'm hesitant to add a Node specific check to attribute, so I won't pull in the pull-request as is, but something along the lines of your alternate solution seems like the better way to go. Assuming you're consciously using the static ATTRS support on the constructor built into Attribute (it was added as a hook for Node, but the intention was to keep in generic), the following seem like the 3 options on the table to support your use case: 1) Y.Attribute = function(userValues) { This is what you were proposing. 2) Y.Attribute = function(attrs, userValues) { 3) Move the addAttrs stuff out of the constructor, and into a separate method, which you can override and no-op. This seems like a good idea in general, and I can't see the performance impact being that great (which would be the only thing I'd pause on before extracting it). Let me know if one suits your needs better than another. My preference would be to implement both 2) and 3). The signature for 2) seems cleaner than 1) generically speaking - forgot to add, of course it does mean null would get passed in for the this.constructor.ATTRS use case. |
|
Posted: 05/17/11
Planning to go with this [ unit testing right now ].
|
|
Posted: 05/17/11
|
|
Posted: 05/17/11
Added param support to constructor. Fixes #2530093. Added unit tests too (just like Ryan does) |
|
Posted: 05/17/11
Satyen, will this still work for making ATTRS lazyAdd? I was passing `true` to `addAttrs`. Also I guess this new setup will still work for people using Y.Augment with Y.Attributes |
|
Posted: 05/18/11
So, it doesn't pass through lazy. I can add that too, although that was the debate I was having - about having to keep the constructor args in sync with addAttrs. At one point I was passing through all of them then backed off, thinking that if you needed lazy you could override the _initAttrs method. Let me know what you think. It will still work with folks using the formal Augment [ the unit tests have cases ] |
|
Posted: 05/18/11
btw, why not just use Base? That's really it's role - to manage the static ATTRS collection, lazy construction etc.. |
|
Posted: 05/18/11
Using Y.Attribute is incredibly useful and is "marketed" to developers as a stand-alone feature to mix-in/augment into their classes, and when using lazyAdd has high performance. When working on a Model (MVC) implementation I ran into blocking performance issues with the Y.Base-based impl, and decided to create a native constructor fn and mix-in Y.Attribute. The result was something more than an order of magnitude faster and much less complex (30x less fn calls). I am now working with Ryan since we both realized we are woking towards the same end-goal with the Model implementation and both were basing things on Backbone.js, so now we're collaborating on it. Ryan also saw the performance hit with Y.Base, but it's clear there's a lot of time taken up in Y.mix and he is currently looking into speeding up `mix`. That said, in general I'd prefer to use Y.Base for most things, but when creating hundreds or even thousands of instances of a class, Y.Base is too expensive as it currently stands. I'd love to see the init lifecycle of Y.Base become more performant, and if Y.mix optimizations are low-hanging fruit that would be a big win for Base and the library as a whole. |
|
Posted: 05/18/11
Added lazy to constructor signature. See #2530093 |
|
Posted: 05/18/11
I'll be looking at Base runtime performance in general for our second 3.4.0 sprint. The need to support 1000s of Base based instances on a page has come up before. A large part of it may be coming from the event stack [ 1000 publishes for init, 1000 fires for init ]. Based on some tests I tried with Tripp, that cuts down about 50% of the instantiation time. However the other 50% is still coming from Base, and needs looking into. All this is based on ad-hoc testing - no concrete numbers yet. A flyweight pattern for this type of scale may be something we need to look into also. |
|
Posted: 05/18/11
This is great, and let me know how I can help. I too realized it was the event stack which was causing the problems since `.publish()` is expensive, when looking deeper `Y.mix` seems to be in part the reason why it's so expensive to call `publish` and one reason Ryan was talking about doing a deep-dive to update mix. I guess I'm unclear what the flyweight pattern would look like, I'd me interested in hearing more about your thoughts here (maybe this ticket isn't the best place :), I remember you mentioning this pattern with Widget parent/child too... |
|
Posted: 06/2/11
|
|
Posted: 06/8/11
|
|
Posted: 06/8/11
|
|
Posted: 08/18/11
|
Pull Request from ericf
Commit: /ericf/yui3/commit/7903173b2c5e64c69cccd6bf3a79a2a8e68965a7
Files modified: