Compare commits

...

1 Commits

Author SHA1 Message Date
Curtis Ekstrom
142a7217d4
Remove *Binding(s) from project
These bindings can be replaced wholesale with the more idiomatic
alternative: aliases.

In addition, avoid passing in user to components where it can be injected directly.

One of the perceived downsides of dependency injection can be
that it can make debugging feel more difficult because it's not
immediately clear where the value is coming from, which the explicit
variant we previously used does not suffer from. It might also be
argued that we also lose out on a seam that could be useful in the
future where a component doesn't care about the specific type of
user, just that one is passed in.

While explicitness is often a virtue, it comes at the cost of increased
noise that pervades multiple layers of components. I'd argue this makes
the parent components more difficult to understand, given they are
littered with unnecessary references to data they themselves do not
need.

This decreases the noise/ceremony around accessing
userPermissions/auth data and restricts access to that data to
the child components that actually need to know about it.

As to losing a seam, it appears 1) that this isn't
currently necessary and 2) we can use an internal computed
property should the need arise in the future.
2016-04-13 13:54:51 +02:00
36 changed files with 229 additions and 92 deletions

View File

@ -1,10 +1,13 @@
import Ember from 'ember';
const { alias } = Ember.computed;
const { service } = Ember.inject;
export default Ember.Component.extend({
flashes: Ember.inject.service(),
flashes: service(),
classNames: ['flash'],
tagName: 'ul',
messagesBinding: 'flashes.messages',
messages: alias('flashes.messages'),
actions: {
closeMessage(msg) {

View File

@ -1,14 +1,15 @@
import Ember from 'ember';
const { alias } = Ember.computed;
export default Ember.Component.extend({
tagName: 'a',
classNames: ['switch--icon'],
classNameBindings: ['active'],
activeBinding: "hook.active",
active: alias('hook.active'),
click() {
var hook;
this.sendAction('onToggle');
hook = this.get('hook');
let hook = this.get('hook');
return hook.toggle().then((function() {}), () => {
this.toggleProperty('hook.active');
return this.sendAction('onToggleError', hook);

View File

@ -1,5 +1,7 @@
import Ember from 'ember';
const { alias } = Ember.computed;
export default Ember.Component.extend({
logBinding: 'job.log',
classNames: ['job-log'],

View File

@ -5,8 +5,6 @@ import Polling from 'travis/mixins/polling';
export default Ember.Component.extend({
pollModels: 'job.build',
commitBinding: 'job.commit',
currentItemBinding: 'job',
color: function() {
return colorForState(this.get('job.state'));

View File

@ -4,6 +4,9 @@ import LogFolder from 'travis/utils/log-folder';
import config from 'travis/config/environment';
import { plainTextLog as plainTextLogUrl } from 'travis/utils/urls';
const { service } = Ember.inject;
const { alias } = Ember.computed;
Log.DEBUG = false;
Log.LIMIT = 10000;
@ -59,10 +62,12 @@ Object.defineProperty(Log.Limit.prototype, 'limited', {
});
export default Ember.Component.extend({
popup: Ember.inject.service(),
auth: service(),
popup: service(),
classNameBindings: ['logIsVisible:is-open'],
logIsVisible: false,
currentUserBinding: 'auth.currentUser',
currentUser: alias('auth.currentUser'),
didInsertElement() {
if (Log.DEBUG) {

View File

@ -1,3 +1,9 @@
import Ember from 'ember';
export default Ember.Component.extend();
const { service } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Component.extend({
auth: service(),
user: alias('auth.currentUser')
});

View File

@ -1,11 +1,13 @@
import Ember from 'ember';
const { alias } = Ember.computed;
export default Ember.Component.extend({
classNames: ['media', 'account'],
tagName: 'li',
classNameBindings: ['type', 'selected'],
typeBinding: 'account.type',
selectedBinding: 'account.selected',
type: alias('account.type'),
selected: alias('account.selected'),
tokenIsVisible: false,
name: function() {

View File

@ -2,12 +2,18 @@ import Ember from 'ember';
import config from 'travis/config/environment';
import { hasPermission, hasPushPermission } from 'travis/utils/permission';
const { service } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Component.extend({
popup: Ember.inject.service(),
auth: service(),
popup: service(),
classNames: ['option-button'],
classNameBindings: ['isOpen:display'],
isOpen: false,
currentUser: alias('auth.currentUser'),
click(event) {
if ($(event.target).is('a') && $(event.target).parents('.settings-dropdown').length) {
return this.closeMenu();

View File

@ -1,9 +1,12 @@
import Ember from 'ember';
export default Ember.Component.extend({
auth: Ember.inject.service(),
const { service } = Ember.inject;
const { alias } = Ember.computed;
currentUserBinding: 'auth.currentUser',
export default Ember.Component.extend({
auth: service(),
currentUser: alias('auth.currentUser'),
classRecent: function() {
if (this.get('tab') === 'recent') {

View File

@ -1,12 +1,14 @@
import Ember from 'ember';
const { alias } = Ember.computed;
export default Ember.Component.extend({
tagName: 'button',
classNames: ['showmore-button'],
classNameBindings: ['isLoading', 'showMore'],
showMore: true,
attributeBindings: ['disabled'],
disabledBinding: 'isLoading',
disabled: alias('isLoading'),
buttonLabel: function() {
if (this.get('isLoading')) {

View File

@ -2,10 +2,13 @@ import Ember from 'ember';
import { format as formatStatusImage } from 'travis/utils/status-image-formats';
import Config from 'travis/config/environment';
const { service } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Component.extend({
popup: Ember.inject.service(),
auth: Ember.inject.service(),
popupNameBinding: 'popup.popupName',
popup: service(),
auth: service(),
popupName: alias('popup.popupName'),
id: 'status-images',
attributeBindings: ['id'],

View File

@ -1,6 +1,11 @@
import Ember from 'ember';
const { service } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Component.extend({
auth: service(),
user: alias('auth.currentUser'),
classNames: ["sync-button"],
actions: {
sync() {

View File

@ -1,8 +1,12 @@
import Ember from 'ember';
const { service } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Controller.extend({
auth: service(),
allHooks: [],
userBinding: 'auth.currentUser',
user: alias('auth.currentUser'),
init() {
var self;

View File

@ -1,6 +1,11 @@
import Ember from 'ember';
const { alias } = Ember.computed;
const { controller, service } = Ember.inject;
export default Ember.Controller.extend({
repos: Ember.inject.controller(),
userBinding: 'auth.currentUser'
auth: service(),
repos: controller(),
user: alias('auth.currentUser'),
});

View File

@ -1,14 +1,17 @@
import Ember from 'ember';
import GithubUrlProperties from 'travis/mixins/github-url-properties';
const { service, controller } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Controller.extend(GithubUrlProperties, {
repoController: Ember.inject.controller('repo'),
repoBinding: 'repoController.repo',
commitBinding: 'build.commit',
currentUserBinding: 'auth.currentUser',
tabBinding: 'repoController.tab',
auth: service(),
repoController: controller('repo'),
repo: alias('repoController.repo'),
currentUser: alias('auth.currentUser'),
tab: alias('repoController.tab'),
sendFaviconStateChanges: true,
currentItemBinding: 'build',
jobsLoaded: function() {
var jobs;

View File

@ -1,13 +1,16 @@
import Ember from 'ember';
const { controller } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Controller.extend({
buildsSorting: ['number:desc'],
builds: Ember.computed.sort('model', 'buildsSorting'),
repoController: Ember.inject.controller('repo'),
repoBinding: 'repoController.repo',
tabBinding: 'repoController.tab',
isLoadedBinding: 'model.isLoaded',
isLoadingBinding: 'model.isLoading',
repoController: controller('repo'),
repo: alias('repoController.repo'),
tab: alias('repoController.tab'),
isLoaded: alias('model.isLoaded'),
isLoading: alias('model.isLoading'),
showMore() {
var id, number, type;

View File

@ -1,13 +1,15 @@
import Ember from 'ember';
import { githubCommit } from 'travis/utils/urls';
const { service, controller } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Controller.extend({
repoController: Ember.inject.controller('repo'),
repoBinding: 'repoController.repo',
commitBinding: 'job.commit',
currentUserBinding: 'auth.currentUser',
tabBinding: 'repoController.tab',
currentItemBinding: 'job',
auth: service(),
repoController: controller('repo'),
repo: alias('repoController.repo'),
currentUser: alias('auth.currentUser'),
tab: alias('repoController.tab'),
urlGithubCommit: function() {
return githubCommit(this.get('repo.slug'), this.get('commit.sha'));

View File

@ -1,11 +1,16 @@
import Ember from 'ember';
const { service, controller } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Controller.extend({
name: 'profile',
accountController: Ember.inject.controller('account'),
accountsController: Ember.inject.controller('accounts'),
userBinding: 'auth.currentUser',
accountBinding: 'accountController.model',
auth: service(),
accountController: controller('account'),
accountsController: controller('accounts'),
user: alias('auth.currentUser'),
account: alias('accountController.model'),
activate(action, params) {
return this[("view_" + action).camelize()]();

View File

@ -2,17 +2,19 @@ import Ember from 'ember';
import { githubRepo, statusImage } from 'travis/utils/urls';
import config from 'travis/config/environment';
const { service, controller } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Controller.extend({
updateTimesService: Ember.inject.service('updateTimes'),
popup: Ember.inject.service(),
updateTimesService: service('updateTimes'),
popup: service(),
jobController: Ember.inject.controller('job'),
buildController: Ember.inject.controller('build'),
buildsController: Ember.inject.controller('builds'),
reposController: Ember.inject.controller('repos'),
reposBinding: 'reposController.repos',
currentUserBinding: 'auth.currentUser',
jobController: controller('job'),
buildController: controller('build'),
buildsController: controller('builds'),
reposController: controller('repos'),
repos: alias('reposController.repos'),
currentUser: alias('auth.currentUser'),
classNames: ['repo'],

View File

@ -2,6 +2,9 @@ import Ember from 'ember';
import Repo from 'travis/models/repo';
import Config from 'travis/config/environment';
const { service, controller } = Ember.inject;
const { alias } = Ember.computed;
var sortCallback = function(repo1, repo2) {
// this function could be made simpler, but I think it's clearer this way
// what're we really trying to achieve
@ -50,8 +53,9 @@ var sortCallback = function(repo1, repo2) {
var Controller = Ember.Controller.extend({
ajax: Ember.inject.service(),
updateTimesService: Ember.inject.service('updateTimes'),
auth: service(),
ajax: service(),
updateTimesService: service('updateTimes'),
actions: {
activate: function(name) {
@ -82,8 +86,8 @@ var Controller = Ember.Controller.extend({
},
isLoaded: false,
repoController: Ember.inject.controller('repo'),
currentUserBinding: 'auth.currentUser',
repoController: controller('repo'),
currentUser: alias('auth.currentUser'),
selectedRepo: function() {
return this.get('repoController.repo.content') || this.get('repoController.repo');

View File

@ -1,11 +1,15 @@
import Ember from 'ember';
import config from 'travis/config/environment';
const { alias } = Ember.computed;
const { service } = Ember.inject;
export default Ember.Controller.extend({
userBinding: 'auth.currentUser',
store: Ember.inject.service(),
storage: Ember.inject.service(),
currentUserBinding: 'auth.currentUser',
auth: service(),
store: service(),
storage: service(),
user: alias('auth.currentUser'),
userName: function() {
return this.get('user.name') || this.get('user.login');

View File

@ -2,6 +2,8 @@ import Ember from 'ember';
import attr from 'ember-data/attr';
import Model from 'travis/models/model';
const { alias } = Ember.computed;
export default Model.extend({
name: attr(),
type: attr(),
@ -9,5 +11,5 @@ export default Model.extend({
reposCount: attr('number'),
subscribed: attr('boolean'),
education: attr('boolean'),
loginBinding: 'id'
login: alias('id')
});

View File

@ -1,7 +1,45 @@
import BasicRoute from 'travis/routes/basic';
import limit from 'travis/utils/computed-limit';
import Ember from 'ember';
const { alias } = Ember.computed;
export default BasicRoute.extend({
init: function() {
var repos, store;
store = this.store;
repos = Ember.ArrayProxy.extend({
isLoaded: alias('repos.isLoaded'),
repos: [],
sorted: Ember.computed.sort('repos', 'sortedReposKeys'),
content: limit('sorted', 'limit'),
sortedReposKeys: ['sortOrderForLandingPage:desc'],
limit: 3
}).create();
this.set('repos', repos);
this.loadMoreRepos();
return this._super.apply(this, arguments);
},
loadMoreRepos() {
return this.store.findAll('build').then( (builds) => {
var repoIds, repos;
repoIds = builds.mapBy('data.repo').uniq();
repos = this.get('repos.repos');
return this.store.query('repo', {
ids: repoIds
}).then(function(reposFromRequest) {
return reposFromRequest.toArray().forEach(function(repo) {
if (!repos.contains(repo)) {
return repos.pushObject(repo);
}
});
});
});
},
activate() {
return this.controllerFor('top').set('landingPage', true);

View File

@ -1,9 +1,13 @@
import Ember from 'ember';
import LimitedArray from 'travis/utils/limited-array';
const { service } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Service.extend({
store: Ember.inject.service(),
currentUserBinding: 'auth.currentUser',
auth: service(),
store: service(),
currentUser: alias('auth.currentUser'),
init() {
this._super(...arguments);

View File

@ -19,7 +19,7 @@
<h1>{{accountName}}</h1>
</div>
{{sync-button user=auth.currentUser}}
{{sync-button}}
{{#if user.isSyncing}}
{{#unless config.enterprise}}

View File

@ -3,7 +3,7 @@
{{loading-indicator}}
{{else}}
{{build-header item=build user=auth.currentUser commit=commit repo=repo}}
{{build-header item=build commit=build.commit repo=repo}}
{{#if build.isMatrix}}
{{#if jobsLoaded}}

View File

@ -82,8 +82,8 @@
</div>
<div class="build-tools">
{{#if isJob}}
{{repo-actions job=item repo=item.repo user=auth.currentUser}}
{{repo-actions job=item repo=item.repo}}
{{else}}
{{repo-actions build=item repo=item.repo user=user}}
{{repo-actions build=item repo=item.repo}}
{{/if}}
</div>

View File

@ -1,7 +1,7 @@
{{! TODO: when `component` helper is available we could just use
with a component name based on type that is passed here }}
{{#if job}}
{{job-repo-actions job=job user=user repo=repo}}
{{job-repo-actions job=job repo=repo}}
{{else}}
{{build-repo-actions build=build user=user repo=repo}}
{{build-repo-actions build=build repo=repo}}
{{/if}}

View File

@ -1,7 +1,7 @@
<div class="inner">
<div class="dashboard-header">
{{orgs-filter orgs=orgs selected=selectedOrg action="selectOrg"}}
{{sync-button user=auth.currentUser}}
{{sync-button}}
</div>
<div class="dashboard-repos">

View File

@ -1,7 +1,7 @@
{{#job-wrapper repo=repo job=job}}
{{#if job.isLoaded}}
{{build-header item=job user=auth.currentUser commit=job.commit repo=repo}}
{{build-header item=job commit=job.commit repo=repo}}
{{job-log job=job}}

View File

@ -18,7 +18,7 @@
</header>
<main class="repo-main">
<div class="repo-navigation">
{{repo-show-tools repo=repo build=build job=job tab=tab currentUser=auth.currentUser}}
{{repo-show-tools repo=repo build=build job=job tab=tab}}
{{repo-show-tabs repo=repo tab=tab build=build job=job}}
</div>

View File

@ -1 +1 @@
{{not-active user=currentUser repo=repo}}
{{not-active repo=repo}}

View File

@ -1,9 +1,11 @@
import Ember from 'ember';
import limit from 'travis/utils/computed-limit';
const { alias } = Ember.computed;
export default Ember.ArrayProxy.extend({
limit: 10,
isLoadedBinding: 'content.isLoaded',
isLoaded: alias('content.isLoaded'),
arrangedContent: limit('content', 'limit'),
totalLength: function() {

View File

@ -1,9 +1,15 @@
import Ember from 'ember';
const { service } = Ember.inject;
const { alias } = Ember.computed;
export default Ember.Mixin.create({
auth: service(),
restarting: false,
cancelling: false,
user: alias('auth.currentUser'),
userHasPermissionForRepo: function() {
var repo, user;
repo = this.get('repo');

View File

@ -1,7 +1,24 @@
import { test, moduleForComponent } from 'ember-qunit';
import Ember from 'ember';
let userStub = Ember.Object.extend({
hasAccessToRepo: function(repo) {
ok(repo.get('id', 44));
ok(true, 'hasAccessToRepo was called');
return false;
}
}).create();
// stub auth service
const authStub = Ember.Service.extend({
currentUser: userStub
});
moduleForComponent('build-repo-actions', 'BuildRepoActionsComponent', {
unit: true
unit: true,
beforeEach() {
this.register('service:auth', authStub);
}
});
test('it shows cancel button if canCancel is true', function() {
@ -62,15 +79,7 @@ test('it properly checks for user permissions for a repo', function() {
repo = Ember.Object.create({
id: 44
});
user = Ember.Object.extend({
hasAccessToRepo: function(repo) {
ok(repo.get('id', 44));
ok(true, 'hasAccessToRepo was called');
return false;
}
}).create();
component = this.subject({
user: user,
repo: repo
});
return ok(!component.get('userHasPermissionForRepo'), 'user should not have access to a repo');

View File

@ -1,8 +1,24 @@
import { test, moduleForComponent } from 'ember-qunit';
import Ember from 'ember';
let userStub = Ember.Object.extend({
hasAccessToRepo: function(repo) {
ok(repo.get('id', 44));
ok(true, 'hasAccessToRepo was called');
return false;
}
}).create();
// stub auth service
const authStub = Ember.Service.extend({
currentUser: userStub
});
moduleForComponent('job-repo-actions', 'JobRepoActionsComponent', {
unit: true
unit: true,
beforeEach() {
this.register('service:auth', authStub);
}
});
test('it shows cancel button if canCancel is true', function() {
@ -63,15 +79,7 @@ test('it properly checks for user permissions for a repo', function() {
repo = Ember.Object.create({
id: 44
});
user = Ember.Object.extend({
hasAccessToRepo: function(repo) {
ok(repo.get('id', 44));
ok(true, 'hasAccessToRepo was called');
return false;
}
}).create();
component = this.subject({
user: user,
repo: repo
});
return ok(!component.get('userHasPermissionForRepo'), 'user should not have access to a repo');