From c05ce673bfe867d99f074fd9fc078190c3428086 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Tue, 16 Oct 2012 02:42:51 +0200 Subject: [PATCH] Load incomplete records when trying to get unknown attribute In order to minimize ajax requests, I implemented isComplete property, which can be used to check if record is fetched from the API or if it was just partially loaded (for example by pusher event). This is nice in terms of requests reduction, but caries risk of showing incomplete data. This commit fixes this situation by saving which attributes were provided on "incomplete" load and triggering refresh when any unknown attribute is tried to be fetched. The implementation is really simple and will probably need refactoring, but I would like to test it in the wild before putting much more time into it. --- AssetFile | 2 +- Gemfile.lock | 4 +- assets/scripts/app/store.coffee | 11 +- assets/scripts/lib/travis/model.coffee | 22 + public/scripts/app.js | 2 +- public/scripts/min/app.js | 2 +- public/scripts/specs.js | 70 + public/styles/app.css | 3202 ++++++++++++------------ public/version | 2 +- 9 files changed, 1706 insertions(+), 1611 deletions(-) diff --git a/AssetFile b/AssetFile index 6acfcebb..9d6af5d4 100644 --- a/AssetFile +++ b/AssetFile @@ -23,7 +23,7 @@ input assets.scripts do safe_concat assets.vendor_order, 'vendor.js' end - match 'spec/*.js' do + match '{spec,spec/unit}/*.js' do concat 'spec/specs.js' end diff --git a/Gemfile.lock b/Gemfile.lock index 9c926cea..8e97af0a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -43,7 +43,7 @@ GIT GIT remote: git://github.com/travis-ci/travis-api.git - revision: 816ebc66c8b65c44d6144e721b1b4f048e86d7df + revision: 558847f6555202adb7da4f7d31a7b2b80ab4bac2 specs: travis-api (0.0.1) backports (~> 2.5) @@ -61,7 +61,7 @@ GIT GIT remote: git://github.com/travis-ci/travis-core.git - revision: 56ca16046cba99cc0b4cd0c520c6bb13ace9932a + revision: abac97114a36f3a6715e118cc1f90c05a08004f6 branch: sf-travis-api specs: travis-core (0.0.1) diff --git a/assets/scripts/app/store.coffee b/assets/scripts/app/store.coffee index 4b9a2b6f..c49a25f7 100644 --- a/assets/scripts/app/store.coffee +++ b/assets/scripts/app/store.coffee @@ -76,18 +76,21 @@ Travis.Store = DS.Store.extend # if we need sideload becasue we have side records with other events it needs to # be revised if type == Travis.Build && json.repository - result = @_loadIncomplete(Travis.Repo, 'repository', json.repository) - @_loadIncomplete(type, root, json[root]) + result = @loadIncomplete(Travis.Repo, json.repository) + @loadIncomplete(type, json[root]) - _loadIncomplete: (type, root, hash) -> + loadIncomplete: (type, hash) -> result = @merge(type, hash) if result && result.clientId record = @findByClientId(type, result.clientId) unless record.get('complete') record.set 'incomplete', true + record.loadedAttributes = Object.keys hash - @_updateAssociations(type, root, hash) + @_updateAssociations(type, type.singularName(), hash) + + record _loadMany: (store, type, json) -> root = type.pluralName() diff --git a/assets/scripts/lib/travis/model.coffee b/assets/scripts/lib/travis/model.coffee index 1d497c97..ee574d1c 100644 --- a/assets/scripts/lib/travis/model.coffee +++ b/assets/scripts/lib/travis/model.coffee @@ -2,6 +2,12 @@ primaryKey: 'id' id: DS.attr('number') + get: (name) -> + if @constructor.isAttribute(name) && @get('incomplete') && !@isAttributeLoaded(name) + @loadTheRest() + + @_super.apply this, arguments + refresh: -> if id = @get('id') store = @get('store') @@ -12,6 +18,9 @@ @set(key, value) unless key is 'id' this + isAttributeLoaded: (name) -> + @loadedAttributes.contains(name) + isComplete: (-> if @get 'incomplete' @loadTheRest() @@ -63,3 +72,16 @@ pluralName: -> Travis.app.store.adapter.pluralize(@singularName()) + isAttribute: (name) -> + unless @attributesSaved + @_saveAttributes() + @cachedAttributes.contains(name) + + _saveAttributes: -> + @attributesSaved = true + + cachedAttributes = [] + @eachComputedProperty (name, meta) -> + cachedAttributes.pushObject name if meta.isAttribute + + @cachedAttributes = cachedAttributes diff --git a/public/scripts/app.js b/public/scripts/app.js index 40adecf5..39509cb5 100644 --- a/public/scripts/app.js +++ b/public/scripts/app.js @@ -29619,4 +29619,4 @@ var _require=function(){function c(a,c){document.addEventListener?a.addEventList ++g&&setTimeout(c,0)})}}(); (function(){!window.WebSocket&&window.MozWebSocket&&(window.WebSocket=window.MozWebSocket);if(window.WebSocket)Pusher.Transport=window.WebSocket,Pusher.TransportType="native";var c=(document.location.protocol=="http:"?Pusher.cdn_http:Pusher.cdn_https)+Pusher.VERSION,a=[];window.JSON||a.push(c+"/json2"+Pusher.dependency_suffix+".js");if(!window.WebSocket)window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION=!0,a.push(c+"/flashfallback"+Pusher.dependency_suffix+".js");var b=function(){return window.WebSocket?function(){Pusher.ready()}: function(){window.WebSocket?(Pusher.Transport=window.WebSocket,Pusher.TransportType="flash",window.WEB_SOCKET_SWF_LOCATION=c+"/WebSocketMain.swf",WebSocket.__addTask(function(){Pusher.ready()}),WebSocket.__initialize()):(Pusher.Transport=null,Pusher.TransportType="none",Pusher.ready())}}(),e=function(a){var b=function(){document.body?a():setTimeout(b,0)};b()},g=function(){e(b)};a.length>0?_require(a,g):g()})(); -;minispade.register('app', "(function() {(function() {\nminispade.require('auth');\nminispade.require('controllers');\nminispade.require('helpers');\nminispade.require('models');\nminispade.require('pusher');\nminispade.require('routes');\nminispade.require('slider');\nminispade.require('store');\nminispade.require('tailing');\nminispade.require('templates');\nminispade.require('views');\nminispade.require('config/locales');\nminispade.require('data/sponsors');\n\n Travis.reopen({\n App: Em.Application.extend({\n autoinit: false,\n currentUserBinding: 'auth.user',\n authStateBinding: 'auth.state',\n init: function() {\n this._super.apply(this, arguments);\n this.store = Travis.Store.create();\n this.store.loadMany(Travis.Sponsor, Travis.SPONSORS);\n this.set('auth', Travis.Auth.create({\n app: this,\n endpoint: Travis.config.api_endpoint\n }));\n this.slider = new Travis.Slider();\n this.pusher = new Travis.Pusher(Travis.config.pusher_key);\n return this.tailing = new Travis.Tailing();\n },\n signIn: function() {\n return this.get('auth').signIn();\n },\n autoSignIn: function() {\n return this.get('auth').autoSignIn();\n },\n signOut: function() {\n this.get('auth').signOut();\n return this.get('router').send('afterSignOut');\n },\n receive: function() {\n return this.store.receive.apply(this.store, arguments);\n },\n toggleSidebar: function() {\n var element;\n $('body').toggleClass('maximized');\n element = $('');\n $('#top .profile').append(element);\n Em.run.later((function() {\n return element.remove();\n }), 10);\n element = $('');\n $('#repo').append(element);\n return Em.run.later((function() {\n return element.remove();\n }), 10);\n }\n })\n });\n\n}).call(this);\n\n})();\n//@ sourceURL=app");minispade.register('auth', "(function() {(function() {\n\n this.Travis.Auth = Ember.Object.extend({\n iframe: $('