Skip to content
Browse files

Merge remote branch 'origin/master'

  • Loading branch information...
2 parents b5be8fc + 46e0eab commit 601c0d8982a071fd7ec77d14f03c066a153e05c3 @mourner mourner committed Aug 15, 2011
Showing with 350 additions and 165 deletions.
  1. +22 −0 LICENSE
  2. +3 −3 README.md
  3. +11 −0 crude-min.js
  4. +193 −129 crude.js
  5. +12 −0 package.json
  6. +109 −33 spec/spec.js
View
22 LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2011, Vladimir Agafonkin, CloudMade
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
View
6 README.md
@@ -12,7 +12,7 @@ Initially a part of a closed-source client code for an upcoming CloudMade servic
```javascript
var myApi = Crude.api('', 'js', function(url, method, data) {
- // do requests with jQuery as example
+ // do requests with jQuery as an example
return $.ajax({url: url, type: method, data: data});
});
@@ -35,8 +35,8 @@ myApi.comments.inPost(123).create({author: 'Vladimir', message: 'Hi!'});
// POST /posts/123/comments.json, comment[author]=Vladimir&comment[message]=Hi!
myApi.comments.inPost(123).add(345).error(handleError);
-// PUT /posts/123/comments/345/add.json, call showError if not successful
+// PUT /posts/123/comments/345/add.json, call handleError if not successful
myApi.request('some/{foo}/url', 'get', {foo: 'custom', baz: 5});
// GET /some/custom/url.json?baz=5
-```
+```
View
11 crude-min.js
@@ -0,0 +1,11 @@
+/*
+ Copyright (c) 2011, Vladimir Agafonkin, CloudMade
+ Crude (v0.1) is a clever JavaScript library for working with RESTful services.
+ See https://github.com/CloudMade/Crude for more information.
+*/
+(function(h){var c={version:"0.1"},i=h.Crude;typeof module!=="undefined"&&module.exports?module.exports=c:h.Crude=c;c.noConflict=function(){h.Crude=i;return this};c.api=function(a,b,d){return new c.Api(a,b,d)};c.Api=function(a,b,d){this.baseUrl=a;this.format=b;this.requestFn=d;this.NestedResources=function(){c.Resources.apply(this,arguments)};c.inherit(this.NestedResources,c.Resources);this.nestedResources=this.NestedResources.prototype};c.Api.prototype={request:function(a,b,d){!d&&typeof b!=="string"&&
+(d=b,b="get");d=d||{};a=this.baseUrl+"/"+a+"."+this.format;a=c.template(a,d,!0);return this.requestFn(a,b,d)},resources:function(a,b){b=b||c.pluralize(a);return this[b]=new c.Resources(this,a,b)}};c.Resources=function(a,b,d,c){this.api=a;this.name=b;this.pluralName=d;this.prefix=c};c.Resources.prototype={request:function(a,b,d){return this.api.request((this.prefix?this.prefix+"/":"")+this.pluralName+(a?"/"+a:""),b,d)},get:function(a,b){!b&&typeof a==="object"&&(b=a,a=null);return this.request(a||
+"","get",b)},create:function(a,b){a=c.wrapKeys(a,this.name);b=c.extend({},b,a);return this.request("","post",b)},update:function(a,b,d){b=c.wrapKeys(b,this.name);d=c.extend({},d,b);return this.request(a,"put",d)},del:function(a,b){return this.request(a,"delete",b)},belongTo:function(a){function b(){e.apply(this,arguments)}var d="in"+c.capitalize(a.name),e=this.api.NestedResources,f=a.name+c.capitalize(this.pluralName);c.inherit(b,e);this.api[f]=b.prototype;this[d]=function(d){return new b(this.api,
+this.name,this.pluralName,a.pluralName+"/"+d)};return this},collectionAction:function(a,b){b=c.extend({method:"get",path:a},b);this[a]=function(a){b.argsToData&&(a=b.argsToData.apply(null,arguments));return this.request(b.path,b.method,a)};return this},memberAction:function(a,b){b=c.extend({method:"get",path:a},b);this[a]=function(a,c){if(b.argsToData)var f=Array.prototype.slice.call(arguments,1),c=b.argsToData.apply(null,f);return this.request(a+"/"+b.path,b.method,c)};return this}};c.inherit=function(a,
+b){function c(){}c.prototype=b.prototype;var e=new c;e.constructor=a;a.prototype=e};c.pluralRules=[[/$/,"s"],[/s$/i,"s"],[/(?:([^f])fe|([lr])f)$/i,"$1$2ves"],[/([^aeiouy]|qu)y$/i,"$1ies"],[/(x|ch|s|sh)$/i,"$1es"],["child","children"]];c.pluralize=function(a){for(var b=c.pluralRules,d=b.length,e;d;)if(d-=1,e=b[d],typeof e[0]==="string"){if(a===e[0])return e[1]}else if(e[0].test(a))return a.replace(e[0],e[1]);return a};c.capitalize=function(a){return a.charAt(0).toUpperCase()+a.slice(1)};c.extend=function(a){var b=
+Array.prototype.slice.call(arguments,1),c=b.length,e,f,g;for(g=0;g<c;g+=1)for(f in e=b[g]||{},e)e.hasOwnProperty(f)&&(a[f]=e[f]);return a};c.wrapKeys=function(a,b){var c={},e;for(e in a)a.hasOwnProperty(e)&&(c[b+"["+e+"]"]=a[e]);return c};c.template=function(a,b,c){return a.replace(/\{ *([^} ]+) *\}/g,function(a,f){var g=b[f];if(!b.hasOwnProperty(f))throw Error("No value provided for variable "+a);c&&delete b[f];return g})}})(this);
View
322 crude.js
@@ -1,170 +1,214 @@
/**
* @preserve Copyright (c) 2011, Vladimir Agafonkin, CloudMade
- * Crude is a clever JavaScript library for working with RESTful services.
+ * Crude (v0.1) is a clever JavaScript library for working with RESTful services.
* See https://github.com/CloudMade/Crude for more information.
*/
-(function(global, undefined) {
-
- var Crude = {},
- oldCrude = global.Crude;
-
- if (typeof module != 'undefined' && module.exports) {
+/*jslint regexp: true, browser: true, node: true */
+
+(function (global) {
+ "use strict";
+
+ var Crude = {version: '0.1'},
+ oldCrude = global.Crude;
+
+ if (typeof module !== 'undefined' && module.exports) {
module.exports = Crude;
} else {
- global['Crude'] = Crude;
+ global.Crude = Crude;
}
-
- Crude.noConflict = function() {
- global['Crude'] = oldCrude;
+
+ // restores the original global Crude property
+ Crude.noConflict = function () {
+ global.Crude = oldCrude;
return this;
};
-
-
- Crude.inherit = function(Child, Parent) {
- function F() {}
- F.prototype = Parent.prototype;
-
- var proto = new F();
- proto.constructor = Child;
- Child.prototype = proto;
- };
-
-
- Crude.api = function(baseUrl, format, requestFn) {
- return new Crude._Api(baseUrl, format, requestFn);
+
+
+ Crude.api = function (baseUrl, format, requestFn) {
+ return new Crude.Api(baseUrl, format, requestFn);
};
-
-
- Crude._Api = function(baseUrl, format, requestFn) {
- this._baseUrl = baseUrl;
- this._format = format;
- this._requestFn = requestFn;
+
+
+ // the root class of the API (handles basic configuration)
+
+ Crude.Api = function (baseUrl, format, requestFn) {
+ this.baseUrl = baseUrl;
+ this.format = format;
+ this.requestFn = requestFn;
+
+ // create a Resources-inherited class to allow extending nested resources api-wide
+ this.NestedResources = function () {
+ Crude.Resources.apply(this, arguments);
+ };
+ Crude.inherit(this.NestedResources, Crude.Resources);
+
+ this.nestedResources = this.NestedResources.prototype;
};
-
- Crude._Api.prototype = {
- request: function(path, method, data) {
- if (!data && typeof method != 'string') {
+
+ Crude.Api.prototype = {
+ request: function (path, method, data) {
+ // (path, data) signature
+ if (!data && typeof method !== 'string') {
data = method;
method = 'get';
}
+
data = data || {};
-
- var url = this._baseUrl + '/' + path + '.' + this._format;
-
- url = url.replace(/{ *([^} ]+) *}/g, function(a, key){
- var value = data[key];
- if (value === undefined) {
- throw new Error('No value provided for variable: ' + key);
- }
- delete data[key];
- return value;
- });
-
- return this._requestFn(url, method, data);
+
+ var url = this.baseUrl + '/' + path + '.' + this.format;
+
+ // evaluate {stuff} in the url
+ url = Crude.template(url, data, true);
+
+ return this.requestFn(url, method, data);
},
-
- resources: function(name, pluralName) {
+
+ // example: api.resources('post') creates Crude.Resources instance as api.posts
+ resources: function (name, pluralName) {
pluralName = pluralName || Crude.pluralize(name);
- var resources = this[pluralName] = new Crude._Resources(this, name, pluralName);
+ var resources = this[pluralName] = new Crude.Resources(this, name, pluralName);
return resources;
}
};
-
-
- Crude._Resources = function(api, name, pluralName, prefix) {
- this._api = api;
- this._name = name;
- this._pluralName = pluralName;
- this._prefix = prefix;
+
+
+ // the class where most of the magic happens (all the RESTful stuff)
+
+ Crude.Resources = function (api, name, pluralName, prefix) {
+ this.api = api;
+ this.name = name;
+ this.pluralName = pluralName;
+ this.prefix = prefix;
};
-
- Crude._Resources.prototype = {
- request: function(path, method, data) {
- var prefix = (this._prefix ? this._prefix + '/' : ''),
- postfix = (path ? '/' + path : '');
-
- return this._api.request(prefix + this._pluralName + postfix, method, data);
+
+ Crude.Resources.prototype = {
+ request: function (path, method, data) {
+ var prefix = (this.prefix ? this.prefix + '/' : ''),
+ postfix = (path ? '/' + path : '');
+
+ return this.api.request(prefix + this.pluralName + postfix, method, data);
},
-
- get: function(id, data) {
- if (!data && typeof id == 'object') {
+
+ get: function (id, data) {
+ // get(data) signature
+ if (!data && typeof id === 'object') {
data = id;
id = null;
}
+
return this.request(id || '', 'get', data);
},
-
- create: function(props, data) {
- var props = Crude.wrapKeys(props, this._name),
- data = Crude.extend({}, data, props);
-
+
+ create: function (props, data) {
+ props = Crude.wrapKeys(props, this.name);
+ data = Crude.extend({}, data, props);
+
return this.request('', 'post', data);
},
-
- update: function(id, props, data) {
- var props = Crude.wrapKeys(props, this._name),
- data = Crude.extend({}, data, props);
-
+
+ update: function (id, props, data) {
+ props = Crude.wrapKeys(props, this.name);
+ data = Crude.extend({}, data, props);
+
return this.request(id, 'put', data);
},
-
- del: function(id, data) {
+
+ del: function (id, data) {
return this.request(id, 'delete', data);
},
-
- belongTo: function(parent) {
- var methodName = 'in' + Crude.capitalize(parent._name);
- this[methodName] = function(id) {
- function NestedResources() {
- Crude._NestedResources.apply(this, arguments);
- }
- Crude.inherit(NestedResources, Crude._NestedResources);
-
- var protoAccessorName = parent._name + Crude.capitalize(this._pluralName);
- this._api[protoAccessorName] = NestedResources.prototype;
-
- var prefix = parent._pluralName + '/' + id;
- return new NestedResources(this._api, this._name, this._pluralName, prefix);
+
+ // e.g. after api.comments.belongTo(api.posts),
+ // api.comments.inPost(id) returns NestedResources instance
+ belongTo: function (parent) {
+ var methodName = 'in' + Crude.capitalize(parent.name),
+ ApiNestedResources = this.api.NestedResources,
+ protoAccessName = parent.name + Crude.capitalize(this.pluralName);
+
+ // created a separate inherited class to allow extending this resource pair
+ function NestedResources() {
+ ApiNestedResources.apply(this, arguments);
+ }
+ Crude.inherit(NestedResources, ApiNestedResources);
+
+ // e.g. allow prototype access through api.postComments
+ this.api[protoAccessName] = NestedResources.prototype;
+
+ this[methodName] = function (id) {
+ var prefix = parent.pluralName + '/' + id;
+ return new NestedResources(this.api, this.name, this.pluralName, prefix);
};
return this;
},
-
- memberAction: function(name, options) {
- // options: path, method, argsToDataFn
- // TODO member action
+
+ // for custom actions on collections, e.g. /posts/delete_all
+ collectionAction: function (name, options) {
+ options = Crude.extend({
+ method: 'get',
+ path: name
+ }, options);
+
+ this[name] = function (data) {
+ if (options.argsToData) {
+ data = options.argsToData.apply(null, arguments);
+ }
+ return this.request(options.path, options.method, data);
+ };
+
return this;
},
-
- collectionAction: function(name, options) {
- // TODO collection action
+
+ // for custom actions on members, e.g. /posts/1/voteup
+ // TODO remove repetition with collectionAction
+ memberAction: function (name, options) {
+ options = Crude.extend({
+ method: 'get',
+ path: name
+ }, options);
+
+ this[name] = function (id, data) {
+ if (options.argsToData) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ data = options.argsToData.apply(null, args);
+ }
+ return this.request(id + '/' + options.path, options.method, data);
+ };
+
return this;
}
};
-
-
- // create a Resources-inherited class to allow extending nested resources globally
- Crude._NestedResources = function() {
- Crude._Resources.apply(this, arguments);
+
+
+ // various utility functions
+
+ // classical inheritance for internal use
+ Crude.inherit = function (Child, Parent) {
+ function F() {}
+ F.prototype = Parent.prototype;
+
+ var proto = new F();
+ proto.constructor = Child;
+ Child.prototype = proto;
};
- Crude.inherit(Crude._NestedResources, Crude._Resources);
-
-
+
+ // feel free to add more rules from outside
Crude.pluralRules = [[/$/, 's'],
[/s$/i, 's'],
[/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
[/([^aeiouy]|qu)y$/i, '$1ies'],
[/(x|ch|s|sh)$/i, '$1es'],
['child', 'children']];
-
- Crude.pluralize = function(name) {
+
+ Crude.pluralize = function (name) {
var rules = Crude.pluralRules,
- i = rules.length, rule;
-
- while (i--) {
+ i = rules.length,
+ rule;
+
+ while (i) {
+ i -= 1; // conforming to strict JSLint for fun :)
rule = rules[i];
- if (typeof rule[0] == 'string') {
- if (name == rule[0]) {
+ if (typeof rule[0] === 'string') {
+ if (name === rule[0]) {
return rule[1];
}
} else {
@@ -175,32 +219,52 @@
}
return name;
};
-
- Crude.capitalize = function(str) {
+
+ Crude.capitalize = function (str) {
return str.charAt(0).toUpperCase() + str.slice(1);
};
-
- Crude.extend = function(dest) {
- var sources = Array.prototype.slice.call(arguments, 1);
- for (var j = 0, len = sources.length, src; j < len; j++) {
+
+ Crude.extend = function (dest) {
+ var sources = Array.prototype.slice.call(arguments, 1),
+ len = sources.length,
+ src,
+ i,
+ j;
+
+ for (j = 0; j < len; j += 1) {
src = sources[j] || {};
- for (var i in src) {
+ for (i in src) {
if (src.hasOwnProperty(i)) {
dest[i] = src[i];
}
}
}
return dest;
};
-
- Crude.wrapKeys = function(props, name) {
- var obj = {};
- for (var i in props) {
+
+ // turns {foo: 'bar'} into {'property[foo]': 'bar'}
+ Crude.wrapKeys = function (props, name) {
+ var obj = {}, i;
+ for (i in props) {
if (props.hasOwnProperty(i)) {
obj[name + '[' + i + ']'] = props[i];
}
}
return obj;
};
-
-}(this));
+
+ // Crude.template("Hello {foo}", {foo: "World"}) -> "Hello world"
+ Crude.template = function (str, data, cleanupData) {
+ return str.replace(/\{ *([^} ]+) *\}/g, function (str, key) {
+ var value = data[key];
+ if (!data.hasOwnProperty(key)) {
+ throw new Error('No value provided for variable ' + str);
+ }
+ if (cleanupData) {
+ delete data[key];
+ }
+ return value;
+ });
+ };
+
+}(this));
View
12 package.json
@@ -0,0 +1,12 @@
+{
+ "name": "Crude",
+ "description": "A clever JavaScript library for working with RESTful services, inspired by the Rails routing engine.",
+ "version": "0.1",
+ "keywords": ["rest", "crud", "api", "service", "client", "dsl", "routing"],
+ "author": "Vladimir Agafonkin (https://github.com/mourner)",
+ "contributors": [],
+ "dependencies": [],
+ "licenses": [{"type": "BSD", "url": "https://raw.github.com/CloudMade/Crude/master/LICENSE"}],
+ "repository": {"type": "git", "url": "git://github.com/CloudMade/Crude.git"},
+ "main": "crude.js"
+}
View
142 spec/spec.js
@@ -1,145 +1,221 @@
describe("Crude", function() {
var api;
-
+
beforeEach(function() {
api = Crude.api('http://example.com', 'js', function(url, method, data) {
return {url: url, method: method, data: data};
});
});
-
+
describe('request', function() {
it('should use the config passed in the Crude.api function correctly', function() {
var data = {},
result = api.request('test', 'put', data);
-
+
expect(result.url).toEqual('http://example.com/test.js');
expect(result.method).toEqual('put');
expect(result.data).toBe(data);
});
-
+
it('should work with two arguments, using "get" as the default method', function() {
var data = {},
result = api.request('test', data);
-
+
expect(result.method).toEqual('get');
expect(result.data).toBe(data);
});
-
+
it('should evaluate each {prop} in the url with corresponding property from the data, also removing the prop from the latter', function() {
var data = {
- foo: 1,
- bar: 2,
+ foo: 1,
+ bar: 2,
baz: 3
};
-
+
var result = api.request('test/{foo}/bla/{baz}', data);
-
+
expect(result.url).toEqual('http://example.com/test/1/bla/3.js');
expect(result.data).toEqual({bar: 2});
});
});
-
+
describe('resources', function() {
var data = {foo: 'bar'};
-
+
beforeEach(function() {
api.resources('post');
});
-
+
it('should create a correct pluralized property in the api from resource name', function() {
expect(api.posts).toBeDefined();
-
+
api.resources('kitty');
expect(api.kitties).toBeDefined();
-
+
api.resources('wolf');
expect(api.wolves).toBeDefined();
-
+
api.resources('badass');
expect(api.badasses).toBeDefined();
-
+
api.resources('child');
expect(api.children).toBeDefined();
});
-
+
it('should have get() working correctly', function() {
var result = api.posts.get(data);
-
+
expect(result.url).toEqual('http://example.com/posts.js');
expect(result.method).toEqual('get');
expect(result.data).toBe(data);
});
-
+
it('should have get(id) working correctly', function() {
var result = api.posts.get(1, data);
-
+
expect(result.url).toEqual('http://example.com/posts/1.js');
expect(result.method).toEqual('get');
expect(result.data).toBe(data);
});
-
+
it('should have create(props) working correctly', function() {
var props = {prop: 5},
result = api.posts.create(props, data);
-
+
expect(result.url).toEqual('http://example.com/posts.js');
expect(result.method).toEqual('post');
expect(result.data).toEqual({
'post[prop]': 5,
foo: 'bar'
});
});
-
+
it('should have update(id, props) working correctly', function() {
var props = {prop: 5},
result = api.posts.update(1, props, data);
-
+
expect(result.url).toEqual('http://example.com/posts/1.js');
expect(result.method).toEqual('put');
expect(result.data).toEqual({
'post[prop]': 5,
foo: 'bar'
});
});
-
+
it('should have del(id) working correctly', function() {
var result = api.posts.del(1, data);
-
+
expect(result.url).toEqual('http://example.com/posts/1.js');
expect(result.method).toEqual('delete');
expect(result.data).toBe(data);
});
-
+
+ it('memberAction(name, opts) should create corresponding method properly', function() {
+ api.posts.memberAction('custom');
+
+ var result = api.posts.custom(1, data);
+
+ expect(result.url).toEqual('http://example.com/posts/1/custom.js');
+ expect(result.method).toEqual('get');
+ expect(result.data).toBe(data);
+ });
+
+ it('memberAction options should work properly', function() {
+ function argsToData(ids) {
+ return {ids: ids.join(',')};
+ }
+
+ api.posts.memberAction('custom', {
+ method: 'put',
+ path: 'foo/bar',
+ argsToData: argsToData
+ });
+
+ var result = api.posts.custom(1, [1, 2, 3]);
+
+ expect(result.url).toEqual('http://example.com/posts/1/foo/bar.js');
+ expect(result.method).toEqual('put');
+ expect(result.data).toEqual({ids: '1,2,3'});
+ });
+
+ it('collectionAction(name, opts) should create corresponding method properly', function() {
+ api.posts.collectionAction('custom');
+
+ var result = api.posts.custom(data);
+
+ expect(result.url).toEqual('http://example.com/posts/custom.js');
+ expect(result.method).toEqual('get');
+ expect(result.data).toBe(data);
+ });
+
+ it('collectionAction options should work properly', function() {
+ function argsToData(ids) {
+ return {ids: ids.join(',')};
+ }
+
+ api.posts.collectionAction('custom', {
+ method: 'put',
+ path: 'foo/bar',
+ argsToData: argsToData
+ });
+
+ var result = api.posts.custom([1, 2, 3]);
+
+ expect(result.url).toEqual('http://example.com/posts/foo/bar.js');
+ expect(result.method).toEqual('put');
+ expect(result.data).toEqual({ids: '1,2,3'});
+ });
+
describe('child resources', function() {
beforeEach(function() {
api.resources('comment').belongTo(api.posts);
});
-
+
it('should create proper api.resources.in<Parent> accessor', function() {
expect(api.comments.inPost).toBeDefined();
});
-
+
it('should create child resources prototype accessor through api.<parent><Children>', function() {
var postComments = api.comments.inPost(2),
postCommentsProto = postComments.constructor.prototype,
resourcesConstructor = api.comments.constructor,
resourcesProto = resourcesConstructor.prototype;
-
+
expect(api.postComments).toBe(postCommentsProto);
expect(postCommentsProto).not.toBe(resourcesProto);
expect(postComments instanceof resourcesConstructor).toBeTruthy();
});
-
+
it('should have get() working correctly', function() {
var result = api.comments.inPost(2).get();
expect(result.url).toEqual('http://example.com/posts/2/comments.js');
});
-
+
it('should have get(id) working correctly', function() {
var result = api.comments.inPost(2).get(1);
expect(result.url).toEqual('http://example.com/posts/2/comments/1.js');
});
+
+ it('memberAction on particular nested resources through api.<parent><Children> should work correctly', function() {
+ api.postComments.memberAction('foo');
+
+ var result = api.comments.inPost(1).foo(2, data);
+
+ expect(result.url).toEqual('http://example.com/posts/1/comments/2/foo.js');
+ expect(result.method).toEqual('get');
+ expect(result.data).toBe(data);
+ });
+
+ it('api-wide memberAction on nested resources through api.nestedResources should work correctly', function() {
+ api.nestedResources.memberAction('bar');
+
+ var result = api.comments.inPost(1).bar(2, data);
+
+ expect(result.url).toEqual('http://example.com/posts/1/comments/2/bar.js');
+ expect(result.method).toEqual('get');
+ expect(result.data).toBe(data);
+ });
});
});
});

0 comments on commit 601c0d8

Please sign in to comment.
Something went wrong with that request. Please try again.