Skip to content

Commit 61339a8

Browse files
Merge pull request #301 from jenweber/bugfix-duplicate-properties
bugfix: duplicate properties (#276)
2 parents 3736291 + ebd5f73 commit 61339a8

File tree

3 files changed

+71
-4
lines changed

3 files changed

+71
-4
lines changed

app/components/api-index-filter.js

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ export default Component.extend({
4848
if (!this.get('filterData.showDeprecated')) {
4949
items = items.filter(item => item.deprecated !== true);
5050
}
51-
return uniq(sortBy(items, 'name'), true, (item => item.name));
51+
52+
let sortedUniqueItems = uniq(sortBy(items, 'name'), true, (item => item.name));
53+
return this.filterMultipleInheritance(sortedUniqueItems);
5254
},
5355

5456
filteredData: computed('filteredMethods', 'filteredProperties', 'filteredEvents', function() {
@@ -57,6 +59,54 @@ export default Component.extend({
5759
properties: this.get('filteredProperties'),
5860
events: this.get('filteredEvents')
5961
};
60-
})
62+
}),
6163

62-
});
64+
/**
65+
* Returns an array where duplicate methods (by name) are removed.
66+
* The docs for the nearest inheritance are typically more helpful to users,
67+
* so in cases of duplicates, "more local" is preferred.
68+
* Without this, multiple entries for some methods will show up.
69+
* @method filterMultipleInheritance
70+
*/
71+
filterMultipleInheritance(items) {
72+
let dedupedArray = [];
73+
for (let i = 0; i < items.length; i++) {
74+
let currentItem = items[i];
75+
if (i === items.length - 1) {
76+
// if it's the last item, keep it
77+
dedupedArray.push(currentItem);
78+
} else {
79+
let nextItem = items[i + 1];
80+
if (currentItem.name === nextItem.name) {
81+
// if the method would be listed twice, find the more local documentation
82+
let mostLocal = this.findMostLocal(currentItem, nextItem)
83+
dedupedArray.push(mostLocal);
84+
i += 1; // skip the next item with duplicate name
85+
} else {
86+
dedupedArray.push(currentItem);
87+
}
88+
}
89+
}
90+
return dedupedArray;
91+
},
92+
/**
93+
* Returns whichever item is most local.
94+
* What is "most local" is determined by looking at the file path for the
95+
* method, the file path for the class being viewed, and the parent if needed.
96+
* @method findMostLocal
97+
*/
98+
findMostLocal(currentItem, nextItem) {
99+
let currentScope = this.get('model.file');
100+
let parentClassScope = this.get('model.parentClass.file');
101+
if (currentScope === currentItem.file) {
102+
// if the item belongs to the class, keep it
103+
return currentItem;
104+
} else if (parentClassScope === currentItem.file) {
105+
// or if the item belongs to the parent class, keep it
106+
return currentItem;
107+
} else {
108+
// otherwise, the next item must be "more local"
109+
return nextItem;
110+
}
111+
}
112+
})

app/templates/components/class-field-description.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<span class="access">deprecated</span>
2020
{{/if}}
2121
</h3>
22-
<p class="github-link">
22+
<p class="github-link" data-test-file={{field.file}}>
2323
{{#if field.inherited}}
2424
Inherited from
2525
<a href="{{github-link model.project.id model.projectVersion.version field.file field.line}}" target="_blank" rel="noopener">
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import moduleForAcceptance from 'ember-api-docs/tests/helpers/module-for-acceptance';
2+
import {test} from 'qunit';
3+
import { visit, click, findAll, findWithAssert } from 'ember-native-dom-helpers';
4+
import testSelector from 'ember-test-selectors';
5+
6+
moduleForAcceptance('Acceptance | method inheritance')
7+
8+
test('no duplicate methods displayed', async function (assert) {
9+
await visit('/ember-data/2.14/classes/DS.JSONAPIAdapter');
10+
assert.equal(findAll("[data-test-item='createRecord']").length, 1);
11+
});
12+
13+
test('most local inherited method is shown', async function (assert) {
14+
await visit('/ember-data/2.14/classes/DS.JSONAPIAdapter');
15+
await click(`${testSelector('item', 'createRecord')} a`)
16+
assert.ok(findWithAssert("[data-test-file='addon/adapters/rest.js']"));
17+
});

0 commit comments

Comments
 (0)