Repository: abuiles/ember-watson
Pull request #34 (2015-05-03 23:06:53)

more ember data model transforms
This fixes the command line version and also adds the ability to change store methods like findAll, find, findQuery, all, etc to use the string form.


Patch:
diff --git a/bin/ember-watson b/bin/ember-watson
index 9d74931..ba0b5ea 100755
--- a/bin/ember-watson
+++ b/bin/ember-watson
@@ -25,11 +25,10 @@ program
 
 program
   .command('convert-ember-data-model-lookups [appPath]')
-  .command('convert Ember Data model lookups to use a dasherized string')
+  .description('convert Ember Data model lookups to use a dasherized string')
   .action(function(appPath) {
     appPath = appPath || 'app';
     watson.transformEmberDataModelLookups(appPath);
   });
 
-
 program.parse(process.argv);
diff --git a/lib/formulas/ember-data-model-lookup-transform.js b/lib/formulas/ember-data-model-lookup-transform.js
index 072cc9c..62ed7d8 100644
--- a/lib/formulas/ember-data-model-lookup-transform.js
+++ b/lib/formulas/ember-data-model-lookup-transform.js
@@ -14,6 +14,33 @@ function normalize(string) {
 module.exports = function transformEmberDataModelLookups(source){
   var ast = recast.parse(source);
 
+  var STORE_METHODS = [
+    'all',
+    'createRecord',
+    'filter',
+    'find',
+    'fetch',
+    'fetchAll',
+    'findById',
+    'findByIds',
+    'findQuery',
+    'getById',
+    'hasRecordForId',
+    'metaForType',
+    'modelFor',
+    'modelFactoryFor',
+    'normalize',
+    'push',
+    'pushMany',
+    'pushPayload',
+    'recordForId',
+    'recordIsLoaded',
+    'update',
+    'serializerFor',
+    'setMetadataFor',
+    'unloadAll'
+  ];
+
   recast.visit(ast, {
     visitCallExpression: function(path) {
       var node = path.node;
@@ -22,26 +49,51 @@ module.exports = function transformEmberDataModelLookups(source){
         replaceArguments(path);
       }
 
+      if (isStoreMethod(node)) {
+        replaceArguments(path);
+      }
+
       this.traverse(path);
     }
   });
 
   function isHasMany(node) {
+    var mightBeHasMany;
+
     if (types.MemberExpression.check(node.callee)) {
-      return node.callee.object.name === 'DS' &&
+      mightBeHasMany = node.callee.object.name === 'DS' &&
         node.callee.property.name === 'hasMany';
     } else if (types.Identifier.check(node.callee)) {
-      return node.callee.name === 'hasMany';
+      mightBeHasMany = node.callee.name === 'hasMany';
     }
-    return false;
+
+    return mightBeHasMany && isSupportedModelCall(node);
   }
 
   function isBelongsTo(node) {
+    var mightBeBelongsTo;
+
     if (types.MemberExpression.check(node.callee)) {
-      return node.callee.object.name === 'DS' &&
+      mightBeBelongsTo = node.callee.object.name === 'DS' &&
         node.callee.property.name === 'belongsTo';
     } else if (types.Identifier.check(node.callee)) {
-      return node.callee.name === 'belongsTo';
+      mightBeBelongsTo = node.callee.name === 'belongsTo';
+    }
+
+    return mightBeBelongsTo && isSupportedModelCall(node);
+  }
+
+  // First arguments is a string, or a variable that is capitalized, or a member expression.
+  function isSupportedModelCall(node) {
+    var firstArg;
+    if (node.arguments.length) {
+      firstArg = node.arguments[0];
+      if (types.Literal.check(firstArg)) {
+        return typeof firstArg.value === 'string';
+      } else if (types.Identifier.check(firstArg)) {
+        return firstArg.name[0] === firstArg.name[0].toUpperCase();
+      }
+      return types.MemberExpression.check(firstArg);
     }
     return false;
   }
@@ -82,5 +134,15 @@ module.exports = function transformEmberDataModelLookups(source){
     return modelName;
   }
 
+
+  function isStoreMethod(node) {
+    var maybeStoreMethod;
+    if (types.MemberExpression.check(node.callee)) {
+      maybeStoreMethod = node.callee.object.property && node.callee.object.property.name === 'store' &&
+        STORE_METHODS.indexOf(node.callee.property.name) >= 0;
+    }
+    return maybeStoreMethod && isSupportedModelCall(node);
+  }
+
   return recast.print(ast, {tabWidth: 2, quote: 'single'}).code;
 };


Test patch:
diff --git a/tests/ember-data-model-lookups-test.js b/tests/ember-data-model-lookups-test.js
index dc57bda..078eee2 100644
--- a/tests/ember-data-model-lookups-test.js
+++ b/tests/ember-data-model-lookups-test.js
@@ -13,6 +13,16 @@ describe('ember data model lookups', function(){
     watson = new Watson();
   });
 
+  describe('store methods', function(){
+    it('migrates to a string', function(){
+      var source            = fs.readFileSync(baseDir + '/route-old.js');
+      var newSource         = watson._transformEmberDataModelLookups(source);
+      var expectedNewSource = fs.readFileSync(baseDir + '/route-new.js');
+
+      astEquality(newSource, expectedNewSource);
+    });
+  });
+
   describe('hasMany relationship macro', function(){
 
     describe('when using the string form', function(){
diff --git a/tests/fixtures/ember-data-model-lookups/route-new.js b/tests/fixtures/ember-data-model-lookups/route-new.js
new file mode 100644
index 0000000..44fb1f0
--- /dev/null
+++ b/tests/fixtures/ember-data-model-lookups/route-new.js
@@ -0,0 +1,95 @@
+import Ember from 'ember';
+import Post from 'my-app/models/post';
+
+export default Ember.Route.extend({
+  model: function() {
+    return this.store.createRecord('post', {
+      title: 'DHHHHHHHHHHHH'
+    });
+  },
+
+  afterModel: function(model) {
+    return this.store.find('post');
+  },
+
+  actions: {
+
+    findOne: function(){
+      return this.store.findById('post', '1');
+    },
+
+    findQuery: function(){
+      return this.store.findQuery('post', {foo: 'bar'})
+    },
+
+    findSerializer: function(){
+      return this.store.serializerFor('post');
+    },
+
+    findMetaForType: function(){
+      return this.store.metaForType('post');
+    },
+
+    pushMany: function(){
+      return this.store.pushMany('post', []);
+    },
+
+    update: function(){
+      return this.store.update('post', {id: '1'});
+    },
+
+    normalize: function(){
+      return this.store.normalize('post', {});
+    },
+
+    pushPayload: function(){
+      return this.store.pushPayload('post', {});
+    },
+
+    push: function(){
+      return this.store.push('post', {});
+    },
+
+    modelFactoryFor: function(){
+      return this.store.modelFactoryFor('post');
+    },
+
+    modelFor: function(){
+      return this.store.modelFor('post');
+    },
+
+    setMetaDataFor: function(){
+      return this.store.setMetadataFor('post', {});
+    },
+
+    recordIsLoaded: function(){
+      return this.store.recordIsLoaded('post', '1');
+    },
+
+    filter: function(){
+      return this.store.filter('post', {}, function(post) {
+        return post.get('published');
+      });
+    },
+
+    unloadAll: function(){
+      return this.store.unloadAll('post');
+    },
+
+    recordForId: function(){
+      return this.store.recordForId('post', '1');
+    },
+
+    hasRecordForId: function(){
+      return this.store.hasRecordForId('post', '1');
+    },
+
+    getById: function(){
+      return this.store.getById('post', '1');
+    },
+
+    findByIds: function(){
+      return this.store.findByIds('post', ['1', '2', '3']);
+    }
+  }
+});
diff --git a/tests/fixtures/ember-data-model-lookups/route-old.js b/tests/fixtures/ember-data-model-lookups/route-old.js
new file mode 100644
index 0000000..aed66b2
--- /dev/null
+++ b/tests/fixtures/ember-data-model-lookups/route-old.js
@@ -0,0 +1,95 @@
+import Ember from 'ember';
+import Post from 'my-app/models/post';
+
+export default Ember.Route.extend({
+  model: function() {
+    return this.store.createRecord(Post, {
+      title: 'DHHHHHHHHHHHH'
+    });
+  },
+
+  afterModel: function(model) {
+    return this.store.find(Post);
+  },
+
+  actions: {
+
+    findOne: function(){
+      return this.store.findById(Post, '1');
+    },
+
+    findQuery: function(){
+      return this.store.findQuery(Post, {foo: 'bar'})
+    },
+
+    findSerializer: function(){
+      return this.store.serializerFor(Post);
+    },
+
+    findMetaForType: function(){
+      return this.store.metaForType(Post);
+    },
+
+    pushMany: function(){
+      return this.store.pushMany(Post, []);
+    },
+
+    update: function(){
+      return this.store.update(Post, {id: '1'});
+    },
+
+    normalize: function(){
+      return this.store.normalize(Post, {});
+    },
+
+    pushPayload: function(){
+      return this.store.pushPayload(Post, {});
+    },
+
+    push: function(){
+      return this.store.push(Post, {});
+    },
+
+    modelFactoryFor: function(){
+      return this.store.modelFactoryFor(Post);
+    },
+
+    modelFor: function(){
+      return this.store.modelFor(Post);
+    },
+
+    setMetaDataFor: function(){
+      return this.store.setMetadataFor(Post, {});
+    },
+
+    recordIsLoaded: function(){
+      return this.store.recordIsLoaded(Post, '1');
+    },
+
+    filter: function(){
+      return this.store.filter(Post, {}, function(post) {
+        return post.get('published');
+      });
+    },
+
+    unloadAll: function(){
+      return this.store.unloadAll(Post);
+    },
+
+    recordForId: function(){
+      return this.store.recordForId(Post, '1');
+    },
+
+    hasRecordForId: function(){
+      return this.store.hasRecordForId(Post, '1');
+    },
+
+    getById: function(){
+      return this.store.getById(Post, '1');
+    },
+
+    findByIds: function(){
+      return this.store.findByIds(Post, ['1', '2', '3']);
+    }
+  }
+});