Repository: ember-cli/eslint-plugin-ember
Pull request #152 (2017-10-09 12:32:20)

Detect models based on their files' path
This allows models to be detected based on their path, even when they extend another model, the same way #37 did for controllers, components and routes.

rmachielse: @michalsnik what do you think about this? Any chance this gets merged?
rmachielse: @michalsnik @Turbo87 @rwjblue anyone who can pick this up?
rmachielse: @Turbo87 thanks for your review! I basically updated `isDSModel` based on `isEmberCoreModule`, which is similar. I can update according to your requests but then those functions would divert. What should I do?
Turbo87: @rmachielse I agree that consistency is somewhat important, but in this case I think it's better to have correct code than to have consistency 😉 

I would propose to adjust your implementation here, and once that is agreed upon and merged you could create a followup PR that fixes the implementation in `isEmberCoreModule` (or you could even do that in parallel already).

Patch:
diff --git a/lib/rules/no-empty-attrs.js b/lib/rules/no-empty-attrs.js
index 1434186c..45d80573 100644
--- a/lib/rules/no-empty-attrs.js
+++ b/lib/rules/no-empty-attrs.js
@@ -18,6 +18,7 @@ module.exports = {
 
   create(context) {
     const message = 'Supply proper attribute type';
+    const filePath = context.getFilename();
 
     const report = function (node) {
       context.report(node, message);
@@ -25,7 +26,7 @@ module.exports = {
 
     return {
       CallExpression(node) {
-        if (!ember.isDSModel(node)) return;
+        if (!ember.isDSModel(node, filePath)) return;
 
         const allProperties = ember.getModuleProperties(node);
         const isDSAttr = allProperties.filter(property => ember.isModule(property.value, 'attr', 'DS'));
diff --git a/lib/rules/order-in-models.js b/lib/rules/order-in-models.js
index e07fc1e4..20d74b01 100644
--- a/lib/rules/order-in-models.js
+++ b/lib/rules/order-in-models.js
@@ -31,10 +31,11 @@ module.exports = {
   create(context) {
     const options = context.options[0] || {};
     const order = options.order ? addBackwardsPosition(options.order, 'empty-method', 'method') : ORDER;
+    const filePath = context.getFilename();
 
     return {
       CallExpression(node) {
-        if (!ember.isDSModel(node)) return;
+        if (!ember.isDSModel(node, filePath)) return;
 
         reportUnorderedProperties(node, context, 'model', order);
       },
diff --git a/lib/utils/ember.js b/lib/utils/ember.js
index a4f1cbdf..888a23ed 100644
--- a/lib/utils/ember.js
+++ b/lib/utils/ember.js
@@ -92,8 +92,15 @@ function isModule(node, element, module) {
     (isLocalModule(node.callee, element) || isEmberModule(node.callee, element, moduleName));
 }
 
-function isDSModel(node) {
-  return isModule(node, 'Model', 'DS');
+function isDSModel(node, filePath) {
+  const isExtended = isEmberObject(node);
+  let isModuleByPath = false;
+
+  if (filePath && isExtended) {
+    isModuleByPath = isModuleByFilePath(filePath, 'model');
+  }
+
+  return isModule(node, 'Model', 'DS') || isModuleByPath;
 }
 
 function isModuleByFilePath(filePath, module) {


Test patch:
diff --git a/tests/lib/rules/no-empty-attrs.js b/tests/lib/rules/no-empty-attrs.js
index 670c0a84..2334ef7a 100644
--- a/tests/lib/rules/no-empty-attrs.js
+++ b/tests/lib/rules/no-empty-attrs.js
@@ -88,5 +88,11 @@ eslintTester.run('no-empty-attrs', rule, {
       parserOptions: { ecmaVersion: 6, sourceType: 'module' },
       errors: [{ message, line: 2 }, { message, line: 3 }, { message, line: 4 }],
     },
+    {
+      filename: 'example-app/models/some-model.js',
+      code: 'export default CustomModel.extend({name: attr()});',
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+      errors: [{ message, line: 1 }]
+    },
   ],
 });
diff --git a/tests/lib/utils/ember-test.js b/tests/lib/utils/ember-test.js
index 15c72ca2..e0b28e1a 100644
--- a/tests/lib/utils/ember-test.js
+++ b/tests/lib/utils/ember-test.js
@@ -30,11 +30,26 @@ describe('isModule', () => {
 });
 
 describe('isDSModel', () => {
-  const node = parse('DS.Model()');
-
   it('should check if it\'s a DS Model', () => {
+    const node = parse('DS.Model()');
+
     expect(emberUtils.isDSModel(node)).toBeTruthy();
   });
+
+  it('should check if it\'s a DS Model even if it uses custom name', () => {
+    const node = parse('CustomModel.extend()');
+    const filePath = 'example-app/models/path/to/some-model.js';
+
+    expect(
+      emberUtils.isDSModel(node),
+      'it shouldn\'t detect Model when no file path is provided'
+    ).toBeFalsy();
+
+    expect(
+      emberUtils.isDSModel(node, filePath),
+      'it should detect Model when file path is provided'
+    ).toBeTruthy();
+  });
 });
 
 describe('isModuleByFilePath', () => {