Skip to content

Commit

Permalink
Changed from decorator to replacement. Added z-sails-mock.
Browse files Browse the repository at this point in the history
  • Loading branch information
Zeroto committed Jul 26, 2014
1 parent c874ea8 commit a0ba283
Show file tree
Hide file tree
Showing 14 changed files with 665 additions and 109 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,16 @@ default: false

This strategy first tries to get the url through sails socket requests. If this fails(e.g. 404 status) it will try again using $http/XHR.

## Tests

### Unit-tests

When unit-testing your modules we need to make sure we have access to the mocked $httpBackend. Because of how angular-mocks works(it forces ng and ngMock to load before other modules), z-sails' $httpBackend replaces the mocked $httpBackend. To fix this you will need to load z-sails-mock.js and add the following line to your tests:

beforeEach(module('z-sails-mock'));

This will put back the angular-mocks' $httpBackend.

### E2E tests

During E2E tests no changes should be needed except the normal ones.
1 change: 1 addition & 0 deletions dist/maps/z-sails-mocks.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/maps/z-sails.min.js.map

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions dist/z-sails-mocks.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

278 changes: 241 additions & 37 deletions dist/z-sails.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/**
* Created by Zerot on 7/23/14.
* Created by Sander Homan on 7/23/14.
*/

angular.module('z-sails', []);

(function(module){

module.provider('zSails', [function(){
Expand All @@ -16,42 +18,244 @@
};
};
}]);
module.config(['$provide', function($provide){

// replace $httpBackend to have it put out sails socket requests instead of XHR
$provide.decorator('$httpBackend', ['$delegate', '$browser', 'zSails', '$window', function($delegate, $browser, zsails, $window){
var $httpBackend = function(method, url, post, callback, headers, timeout, withCredentials, responseType){
url = url || $browser.url();

var lowercaseUrl;
if (zsails.useFileCheck)
lowercaseUrl = angular.lowercase(url);

var methodLowercase = angular.lowercase(method);
if ((methodLowercase !== 'get' && methodLowercase !== 'post' && methodLowercase !== 'put' && methodLowercase !== 'delete') ||
(zsails.useFileCheck && lowercaseUrl.length > 5 && (lowercaseUrl[lowercaseUrl.length-5] == '.' || lowercaseUrl[lowercaseUrl.length-4] == '.'))) //check if file. files will have a . at 3rd or 4th last character
{
return $delegate(method, url, post, callback, headers, timeout, withCredentials, responseType);
}
else
{
$window.io.socket[methodLowercase](url, angular.fromJson(post), function(data, jwr){
if (zsails.useFallback && jwr.statusCode != 200)
{
$delegate(method, url, post, callback, headers, timeout, withCredentials, responseType);
}
else
callback(jwr.statusCode, data, jwr.headers, "");
});
}
module.factory('$httpBackend', ['$httpXHRBackend', '$browser', 'zSails', '$window', function($httpXHRBackend, $browser, zsails, $window){
var $httpBackend = function(method, url, post, callback, headers, timeout, withCredentials, responseType){
url = url || $browser.url();

};

$httpBackend.originalBackend = $delegate; // expose the original backend

return $httpBackend;
}]);
var lowercaseUrl;
if (zsails.useFileCheck)
lowercaseUrl = angular.lowercase(url);

}]);
var methodLowercase = angular.lowercase(method);
if ((methodLowercase !== 'get' && methodLowercase !== 'post' && methodLowercase !== 'put' && methodLowercase !== 'delete') ||
(zsails.useFileCheck && lowercaseUrl.length > 5 && (lowercaseUrl[lowercaseUrl.length-5] == '.' || lowercaseUrl[lowercaseUrl.length-4] == '.'))) //check if file. files will have a . at 3rd or 4th last character
{
return $httpXHRBackend(method, url, post, callback, headers, timeout, withCredentials, responseType);
}
else
{
$window.io.socket[methodLowercase](url, angular.fromJson(post), function(data, jwr){
if (zsails.useFallback && jwr.statusCode != 200)
{
$httpXHRBackend(method, url, post, callback, headers, timeout, withCredentials, responseType);
}
else
callback(jwr.statusCode, data, jwr.headers, "");
});
}
};

return $httpBackend;
}]);

})(angular.module('z-sails'));
(function(module){
'use strict';

module.provider('$httpXHRBackend', $HttpBackendProvider);

var lowercase = angular.lowercase;

// below is a copy from the angular source. This will require updating when this updates in angular, but it is a low maintenance file, so this will not happen often.

function createXhr(method) {
//if IE and the method is not RFC2616 compliant, or if XMLHttpRequest
//is not available, try getting an ActiveXObject. Otherwise, use XMLHttpRequest
//if it is available
if (msie <= 8 && (!method.match(/^(get|post|head|put|delete|options)$/i) ||
!window.XMLHttpRequest)) {
return new window.ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) {
return new window.XMLHttpRequest();
}

throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest.");
}

/**
* @ngdoc service
* @name $httpBackend
* @requires $window
* @requires $document
*
* @description
* HTTP backend used by the {@link ng.$http service} that delegates to
* XMLHttpRequest object or JSONP and deals with browser incompatibilities.
*
* You should never need to use this service directly, instead use the higher-level abstractions:
* {@link ng.$http $http} or {@link ngResource.$resource $resource}.
*
* During testing this implementation is swapped with {@link ngMock.$httpBackend mock
* $httpBackend} which can be trained with responses.
*/
function $HttpBackendProvider() {
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
}];
}

function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
var ABORTED = -1;

// TODO(vojta): fix the signature
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
var status;
$browser.$$incOutstandingRequestCount();
url = url || $browser.url();

if (lowercase(method) == 'jsonp') {
var callbackId = '_' + (callbacks.counter++).toString(36);
callbacks[callbackId] = function(data) {
callbacks[callbackId].data = data;
callbacks[callbackId].called = true;
};

var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
callbackId, function(status, text) {
completeRequest(callback, status, callbacks[callbackId].data, "", text);
callbacks[callbackId] = noop;
});
} else {

var xhr = createXhr(method);

xhr.open(method, url, true);
forEach(headers, function(value, key) {
if (isDefined(value)) {
xhr.setRequestHeader(key, value);
}
});

// In IE6 and 7, this might be called synchronously when xhr.send below is called and the
// response is in the cache. the promise api will ensure that to the app code the api is
// always async
xhr.onreadystatechange = function() {
// onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by
// xhrs that are resolved while the app is in the background (see #5426).
// since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before
// continuing
//
// we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and
// Safari respectively.
if (xhr && xhr.readyState == 4) {
var responseHeaders = null,
response = null,
statusText = '';

if(status !== ABORTED) {
responseHeaders = xhr.getAllResponseHeaders();

// responseText is the old-school way of retrieving response (supported by IE8 & 9)
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
response = ('response' in xhr) ? xhr.response : xhr.responseText;
}

// Accessing statusText on an aborted xhr object will
// throw an 'c00c023f error' in IE9 and lower, don't touch it.
if (!(status === ABORTED && msie < 10)) {
statusText = xhr.statusText;
}

completeRequest(callback,
status || xhr.status,
response,
responseHeaders,
statusText);
}
};

if (withCredentials) {
xhr.withCredentials = true;
}

if (responseType) {
try {
xhr.responseType = responseType;
} catch (e) {
// WebKit added support for the json responseType value on 09/03/2013
// https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
// known to throw when setting the value "json" as the response type. Other older
// browsers implementing the responseType
//
// The json response type can be ignored if not supported, because JSON payloads are
// parsed on the client-side regardless.
if (responseType !== 'json') {
throw e;
}
}
}

xhr.send(post || null);
}

if (timeout > 0) {
var timeoutId = $browserDefer(timeoutRequest, timeout);
} else if (isPromiseLike(timeout)) {
timeout.then(timeoutRequest);
}


function timeoutRequest() {
status = ABORTED;
jsonpDone && jsonpDone();
xhr && xhr.abort();
}

function completeRequest(callback, status, response, headersString, statusText) {
// cancel timeout and subsequent timeout promise resolution
timeoutId && $browserDefer.cancel(timeoutId);
jsonpDone = xhr = null;

// fix status code when it is 0 (0 status is undocumented).
// Occurs when accessing file resources or on Android 4.1 stock browser
// while retrieving files from application cache.
if (status === 0) {
status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
}

// normalize IE bug (http://bugs.jquery.com/ticket/1450)
status = status === 1223 ? 204 : status;
statusText = statusText || '';

callback(status, response, headersString, statusText);
$browser.$$completeOutstandingRequest(noop);
}
};

function jsonpReq(url, callbackId, done) {
// we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
// - fetches local scripts via XHR and evals them
// - adds and immediately removes script elements from the document
var script = rawDocument.createElement('script'), callback = null;
script.type = "text/javascript";
script.src = url;
script.async = true;

callback = function(event) {
removeEventListenerFn(script, "load", callback);
removeEventListenerFn(script, "error", callback);
rawDocument.body.removeChild(script);
script = null;
var status = -1;
var text = "unknown";

if (event) {
if (event.type === "load" && !callbacks[callbackId].called) {
event = { type: "error" };
}
text = event.type;
status = event.type === "error" ? 404 : 200;
}

if (done) {
done(status, text);
}
};

})(angular.module('z-sails', []));
addEventListenerFn(script, "load", callback);
addEventListenerFn(script, "error", callback);
rawDocument.body.appendChild(script);
return callback;
}
}
})(angular.module('z-sails'));
2 changes: 1 addition & 1 deletion dist/z-sails.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 12 additions & 3 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ var sourcemaps = require('gulp-sourcemaps');
var del = require('del');

var paths = {
scripts: ['src/**/*.js'],
scripts: ['src/z-sails.js', 'src/**/*.js'],
mocks: ['srcMock/**/*.js']
};

gulp.task('clean', function(cb) {
// You can use multiple globbing patterns as you would with `gulp.src`
del(['dist'], cb);
return del(['dist'], cb);
});

gulp.task('scripts', ['clean'], function() {
Expand All @@ -29,6 +30,14 @@ gulp.task('scripts', ['clean'], function() {
.pipe(gulp.dest('dist'));
});

gulp.task('copyMocks', ['clean'], function(){
return gulp.src(paths.mocks)
.pipe(sourcemaps.init())
.pipe(concat('z-sails-mocks.js'))
.pipe(sourcemaps.write('maps'))
.pipe(gulp.dest('dist'));
});

gulp.task('test', function(){
return gulp.src('foobar') // invalid name so karma still loads from config
.pipe(karma({
Expand All @@ -37,4 +46,4 @@ gulp.task('test', function(){
}));
});

gulp.task('build', ['scripts']);
gulp.task('build', ['scripts','copyMocks']);
Loading

0 comments on commit a0ba283

Please sign in to comment.