Is there anyone out there who’s running an app on Ember 0.9.x and wants to upgrade to 1.0 (any version)? Would you like to swap tips? Would you be interested in a library that helps you make the transition?
Our app runs on 0.9.8.1 with CP_DEFAULT_CACHEABLE
and VIEW_PRESERVES_CONTEXT
both set to false
.
CP_DEFAULT_CACHEABLE
We have been fairly successful with the following technique for upgrading CP_DEFAULT_CACHEABLE:
- set
CP_DEFAULT_CACHEABLE
tofalse
(0.9.7.1 compatibility) - create a “style test”-- a Ruby test (but any language would do) that does static analysis against our codebase. It Looks for calls to
.property()
that lack a subsequent.cacheable()
orvolatile()
call. At this point, the style test doesn’t run in the CI environment - run the test and find all violations
- if there are very few, fix them in a single PR and enable the style test in CI
- if there are many, fix a few in a single PR, then go back to (3) when that is merged
- wait some time to make sure you’ve worked all the kinks out
- set
CP_DEFAULT_CACHEABLE
totrue
(1.0) compatibility - wait some more time
- remove the test and calls to
.cacheable()
I’m not sure static analysis is the best answer here. It might have been better to monkey-patch Ember.ComputedProperty
to emit errors or warnings (depending on the environment) if it’s using the default cacheability behavior. If I do write a library, I’ll likely do this.
VIEW_PRESERVES_CONTEXT
We have been less successful with upgrading all our views and templates to 1.0-compatibility. Our first attempt was to
Em.View.reopen({
view: function() { return this; }.property().cacheable()
})
That would let us change {{foo}}
to {{view.foo}}
in any template where foo
is a property on the view’s context. The problem is that there’s no static analysis that could tell us how much progress we’ve made. We scrapped that and created a mixin:
Em.Mixin.create({
_preservesContext: !!Ember.VIEW_PRESERVES_CONTEXT,
_templateContext: Em.computed(function(property, value) {
if (arguments.length > 1) {
return value;
}
if (this.get('_preservesContext')) {
var parentView = get(this, '_parentView');
if (parentView) { return get(parentView, '_templateContext'); }
}
return this;
}).cacheable()
});
This lets us set whether views preserve context on a class-by-class basis. It’s still not possible to do static analysis (since there’s no easy way to determine whether a class is a subclass of Em.View statically), but we can do runtime analysis. We simply iterate over all classes (recursively from App.*
) and check whether they’re Em.View
subclasses; if so, we fail if they don’t preserve context.
One trick we use here is grandfathering in existing code. We want to stop the bleeding by forcing people to set _preservesContext: true
on all new views. Then, we can whittle down the list of grandfathered view classes over time. Once it gets to 0, we remove the mixin and the text and set VIEW_PRESERVES_CONTEXT
to true
globally.
We’re not there yet, so I don’t know how well that will go.
/cc @stefan.penner
@jamesarosen thanks for sharing this! I’ll probably have to port an old ember app soon,
I still have to start, so far it seems that the biggest chunk of work for me will be to refactor a lot of the logic that I now have in the old StateMachine based router…
I would be really interested if you want to share your scripts!
I can definitely talk about how we’re transitioning from Ember.StateMachine
to the router. I think the lessons there won’t be quite as generally useful since there was no common starting point. That is, every Ember 0.9 app had to make up routing on its own. We, for example, use Sammy to map URLs to states.
I’ll add a post here about that transition shortly.