angularjs-rails-resource 0.2.5 → 1.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * A resource factory inspired by $resource from AngularJS
3
- * @version v0.2.5 - 2013-11-24
3
+ * @version v1.0.0-pre1 - 2013-10-06
4
4
  * @link https://github.com/FineLinePrototyping/angularjs-rails-resource.git
5
5
  * @author
6
6
  */
@@ -120,7 +120,7 @@
120
120
  return function (url) {
121
121
  var expression;
122
122
 
123
- if (angular.isFunction(url)) {
123
+ if (angular.isFunction(url) || angular.isUndefined(url)) {
124
124
  return url;
125
125
  }
126
126
 
@@ -352,7 +352,7 @@
352
352
  * Specify a custom serializer to use for an attribute.
353
353
  *
354
354
  * @param attributeName {string} The name of the attribute
355
- * @param serializer {string | constructor} A reference to the custom serializer to use for the attribute.
355
+ * @param serializer {string | function} A reference to the custom serializer to use for the attribute.
356
356
  * @returns {Serializer} this for chaining support
357
357
  */
358
358
  Serializer.prototype.serializeWith = function (attributeName, serializer) {
@@ -408,17 +408,8 @@
408
408
  Serializer.prototype.getSerializedAttributeName = function (attributeName) {
409
409
  var mappedName = this.serializeMappings[attributeName] || attributeName;
410
410
 
411
- var mappedNameExcluded = this.isExcludedFromSerialization(mappedName),
412
- attributeNameExcluded = this.isExcludedFromSerialization(attributeName);
413
-
414
- if(this.options.excludeByDefault) {
415
- if(mappedNameExcluded && attributeNameExcluded) {
416
- return undefined;
417
- }
418
- } else {
419
- if (mappedNameExcluded || attributeNameExcluded) {
420
- return undefined;
421
- }
411
+ if (this.isExcludedFromSerialization(attributeName) || this.isExcludedFromSerialization(mappedName)) {
412
+ return undefined;
422
413
  }
423
414
 
424
415
  return this.underscore(mappedName);
@@ -483,9 +474,9 @@
483
474
 
484
475
  // custom serializer takes precedence over resource serializer
485
476
  if (serializer) {
486
- return RailsResourceInjector.createService(serializer);
477
+ return RailsResourceInjector.createService(serializer)
487
478
  } else if (resource) {
488
- return resource.serializer;
479
+ return resource.config.serializer;
489
480
  }
490
481
 
491
482
  return undefined;
@@ -597,7 +588,7 @@
597
588
  result = {};
598
589
 
599
590
  if (Resource) {
600
- result = new Resource();
591
+ result = new Resource.config.resourceConstructor();
601
592
  }
602
593
 
603
594
  angular.forEach(data, function (value, key) {
@@ -687,7 +678,7 @@
687
678
  angular.module('rails').factory('railsRootWrappingTransformer', function () {
688
679
  return function (data, resource) {
689
680
  var result = {};
690
- result[angular.isArray(data) ? resource.rootPluralName : resource.rootName] = data;
681
+ result[angular.isArray(data) ? resource.config.pluralName : resource.config.name] = data;
691
682
  return result;
692
683
  };
693
684
  });
@@ -701,10 +692,10 @@
701
692
  }
702
693
 
703
694
  return promise.then(function (response) {
704
- if (response.data && response.data.hasOwnProperty(resource.rootName)) {
705
- response.data = response.data[resource.rootName];
706
- } else if (response.data && response.data.hasOwnProperty(resource.rootPluralName)) {
707
- response.data = response.data[resource.rootPluralName];
695
+ if (response.data && response.data.hasOwnProperty(resource.config.name)) {
696
+ response.data = response.data[resource.config.name];
697
+ } else if (response.data && response.data.hasOwnProperty(resource.config.pluralName)) {
698
+ response.data = response.data[resource.config.pluralName];
708
699
  }
709
700
 
710
701
  return response;
@@ -712,16 +703,16 @@
712
703
  };
713
704
  });
714
705
 
715
- angular.module('rails').provider('railsResourceFactory', function () {
706
+ angular.module('rails').provider('RailsResource', function () {
716
707
  var defaultOptions = {
717
- enableRootWrapping: true,
708
+ rootWrapping: true,
718
709
  updateMethod: 'put',
719
710
  httpConfig: {},
720
711
  defaultParams: undefined
721
712
  };
722
713
 
723
- this.enableRootWrapping = function (value) {
724
- defaultOptions.enableRootWrapping = value;
714
+ this.rootWrapping = function (value) {
715
+ defaultOptions.rootWrapping = value;
725
716
  return this;
726
717
  };
727
718
 
@@ -743,11 +734,6 @@
743
734
  this.$get = ['$http', '$q', 'railsUrlBuilder', 'railsSerializer', 'railsRootWrappingTransformer', 'railsRootWrappingInterceptor', 'RailsResourceInjector',
744
735
  function ($http, $q, railsUrlBuilder, railsSerializer, railsRootWrappingTransformer, railsRootWrappingInterceptor, RailsResourceInjector) {
745
736
 
746
- function railsResourceFactory(config) {
747
- var transformers = config.requestTransformers,
748
- interceptors = config.responseInterceptors,
749
- afterInterceptors = config.afterResponseInterceptors;
750
-
751
737
  function appendPath(url, path) {
752
738
  if (path) {
753
739
  if (path[0] !== '/') {
@@ -760,43 +746,100 @@
760
746
  return url;
761
747
  }
762
748
 
749
+ function forEachDependency(list, callback) {
750
+ var dependency;
751
+
752
+ for (var i = 0, len = list.length; i < len; i++) {
753
+ dependency = list[i];
754
+
755
+ if (angular.isString(dependency)) {
756
+ dependency = list[i] = RailsResourceInjector.getDependency(dependency);
757
+ }
758
+
759
+ callback(dependency);
760
+ }
761
+ }
762
+
763
763
  function RailsResource(value) {
764
764
  var instance = this;
765
765
  if (value) {
766
- var immediatePromise = function(data) {
766
+ var immediatePromise = function (data) {
767
767
  return {
768
768
  resource: RailsResource,
769
769
  context: instance,
770
770
  response: data,
771
- then: function(callback) {
771
+ then: function (callback) {
772
772
  this.response = callback(this.response, this.resource, this.context);
773
773
  return immediatePromise(this.response);
774
774
  }
775
775
  }
776
776
  };
777
777
 
778
- var data = RailsResource.callInterceptors(immediatePromise({data: value}), this).response.data;
778
+ var data = this.constructor.callInterceptors(immediatePromise({data: value}), this).response.data;
779
779
  angular.extend(this, data);
780
780
  }
781
781
  }
782
782
 
783
- RailsResource.setUrl = function(url) {
784
- RailsResource.url = railsUrlBuilder(url);
783
+ RailsResource.extend = function (child) {
784
+ // Extend logic copied from CoffeeScript generated code
785
+ var __hasProp = {}.hasOwnProperty, parent = this;
786
+ for (var key in parent) {
787
+ if (__hasProp.call(parent, key)) child[key] = parent[key];
788
+ }
789
+
790
+ function ctor() {
791
+ this.constructor = child;
792
+ }
793
+
794
+ ctor.prototype = parent.prototype;
795
+ child.prototype = new ctor();
796
+ child.__super__ = parent.prototype;
797
+ return child;
785
798
  };
786
- RailsResource.setUrl(config.url);
787
799
 
788
- RailsResource.enableRootWrapping = config.wrapData === undefined ? defaultOptions.enableRootWrapping : config.wrapData; // using undefined check because config.wrapData || true would be true when config.wrapData === false
789
- RailsResource.httpConfig = config.httpConfig || defaultOptions.httpConfig;
790
- RailsResource.httpConfig.headers = angular.extend({'Accept': 'application/json', 'Content-Type': 'application/json'}, RailsResource.httpConfig.headers || {});
791
- RailsResource.defaultParams = config.defaultParams || defaultOptions.defaultParams;
792
- RailsResource.updateMethod = (config.updateMethod || defaultOptions.updateMethod).toLowerCase();
800
+ // allow calling configure multiple times to set configuration options and override values from inherited resources
801
+ RailsResource.configure = function (cfg) {
802
+ cfg = cfg || {};
803
+
804
+ if (this.config) {
805
+ cfg = angular.extend({}, this.config, cfg);
806
+ }
807
+
808
+ this.config = {};
809
+ this.config.url = cfg.url;
810
+ this.config.rootWrapping = cfg.rootWrapping === undefined ? defaultOptions.rootWrapping : cfg.rootWrapping; // using undefined check because config.rootWrapping || true would be true when config.rootWrapping === false
811
+ this.config.httpConfig = cfg.httpConfig || defaultOptions.httpConfig;
812
+ this.config.httpConfig.headers = angular.extend({'Accept': 'application/json', 'Content-Type': 'application/json'}, this.config.httpConfig.headers || {});
813
+ this.config.defaultParams = cfg.defaultParams || defaultOptions.defaultParams;
814
+ this.config.updateMethod = (cfg.updateMethod || defaultOptions.updateMethod).toLowerCase();
815
+
816
+ this.config.requestTransformers = cfg.requestTransformers ? cfg.requestTransformers.slice(0) : [];
817
+ this.config.responseInterceptors = cfg.responseInterceptors ? cfg.responseInterceptors.slice(0) : [];
818
+ this.config.afterResponseInterceptors = cfg.afterResponseInterceptors ? cfg.afterResponseInterceptors.slice(0) : [];
819
+
820
+ // strings and functions are not considered objects by angular.isObject()
821
+ if (angular.isObject(cfg.serializer)) {
822
+ this.config.serializer = cfg.serializer;
823
+ } else {
824
+ this.config.serializer = RailsResourceInjector.createService(cfg.serializer || railsSerializer());
825
+ }
826
+
827
+ this.config.name = this.config.serializer.underscore(cfg.name);
828
+ this.config.pluralName = this.config.serializer.underscore(cfg.pluralName || this.config.serializer.pluralize(this.config.name));
829
+
830
+ this.config.urlBuilder = railsUrlBuilder(this.config.url);
831
+ this.config.resourceConstructor = this;
832
+ };
793
833
 
794
- RailsResource.requestTransformers = [];
795
- RailsResource.responseInterceptors = [];
796
- RailsResource.afterResponseInterceptors = [];
797
- RailsResource.serializer = RailsResourceInjector.createService(config.serializer || railsSerializer());
798
- RailsResource.rootName = RailsResource.serializer.underscore(config.name);
799
- RailsResource.rootPluralName = RailsResource.serializer.underscore(config.pluralName || RailsResource.serializer.pluralize(config.name));
834
+ RailsResource.configure({});
835
+
836
+ RailsResource.setUrl = function (url) {
837
+ this.configure({url: url});
838
+ };
839
+
840
+ RailsResource.buildUrl = function (context) {
841
+ return this.config.urlBuilder(context);
842
+ };
800
843
 
801
844
  /**
802
845
  * Add a callback to run on response and construction.
@@ -804,11 +847,11 @@
804
847
  * constructor is the resource class calling the function,
805
848
  * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
806
849
  */
807
- RailsResource.beforeResponse = function(fn) {
850
+ RailsResource.beforeResponse = function (fn) {
808
851
  fn = RailsResourceInjector.getDependency(fn);
809
- RailsResource.responseInterceptors.push(function(promise) {
810
- return promise.then(function(response) {
811
- fn(response.data, promise.resource, promise.context);
852
+ this.config.responseInterceptors.push(function (promise) {
853
+ return promise.then(function (response) {
854
+ fn(response.data, promise.resource.config.resourceConstructor, promise.context);
812
855
  return response;
813
856
  });
814
857
  });
@@ -818,11 +861,11 @@
818
861
  * Add a callback to run after response has been processed. These callbacks are not called on object construction.
819
862
  * @param fn(response data, constructor) - response data is either the resource instance returned or an array of resource instances and constructor is the resource class calling the function
820
863
  */
821
- RailsResource.afterResponse = function(fn) {
864
+ RailsResource.afterResponse = function (fn) {
822
865
  fn = RailsResourceInjector.getDependency(fn);
823
- RailsResource.afterResponseInterceptors.push(function(promise) {
824
- return promise.then(function(response) {
825
- fn(response, promise.resource);
866
+ this.config.afterResponseInterceptors.push(function (promise) {
867
+ return promise.then(function (response) {
868
+ fn(response, promise.resource.config.resourceConstructor);
826
869
  return response;
827
870
  });
828
871
  });
@@ -832,38 +875,24 @@
832
875
  * Adds a function to run after serializing the data to send to the server, but before root-wrapping it.
833
876
  * @param fn (data, constructor) - data object is the serialized resource instance, and constructor the resource class calling the function
834
877
  */
835
- RailsResource.beforeRequest = function(fn) {
878
+ RailsResource.beforeRequest = function (fn) {
836
879
  fn = RailsResourceInjector.getDependency(fn);
837
- RailsResource.requestTransformers.push(function(data, resource) {
838
- return fn(data, resource) || data;
880
+ this.config.requestTransformers.push(function (data, resource) {
881
+ return fn(data, resource.config.resourceConstructor) || data;
839
882
  });
840
883
  };
841
884
 
842
- // copied from $HttpProvider to support interceptors being dependency names or anonymous factory functions
843
- angular.forEach(interceptors, function (interceptor) {
844
- RailsResource.responseInterceptors.push(RailsResourceInjector.getDependency(interceptor));
845
- });
846
-
847
- angular.forEach(afterInterceptors, function (interceptor) {
848
- RailsResource.afterResponseInterceptors.push(RailsResourceInjector.getDependency(interceptor));
849
- });
850
-
851
- angular.forEach(transformers, function (transformer) {
852
- RailsResource.requestTransformers.push(RailsResourceInjector.getDependency(transformer));
853
- });
854
-
855
885
  // transform data for request:
856
886
  RailsResource.transformData = function (data) {
857
- data = RailsResource.serializer.serialize(data);
887
+ var config = this.config;
888
+ data = config.serializer.serialize(data);
858
889
 
859
- // data is now serialized. call request transformers including beforeRequest
860
- angular.forEach(RailsResource.requestTransformers, function (transformer) {
861
- data = transformer(data, RailsResource);
890
+ forEachDependency(this.config.requestTransformers, function (transformer) {
891
+ data = transformer(data, config.resourceConstructor);
862
892
  });
863
893
 
864
-
865
- if (RailsResource.enableRootWrapping) {
866
- data = railsRootWrappingTransformer(data, RailsResource);
894
+ if (config.rootWrapping) {
895
+ data = railsRootWrappingTransformer(data, config.resourceConstructor);
867
896
  }
868
897
 
869
898
  return data;
@@ -871,25 +900,27 @@
871
900
 
872
901
  // transform data on response:
873
902
  RailsResource.callInterceptors = function (promise, context) {
903
+ var config = this.config;
904
+
874
905
  promise = promise.then(function (response) {
875
906
  // store off the data in case something (like our root unwrapping) assigns data as a new object
876
907
  response.originalData = response.data;
877
908
  return response;
878
909
  });
879
910
 
880
- if (RailsResource.enableRootWrapping) {
881
- promise.resource = RailsResource;
911
+ if (config.rootWrapping) {
912
+ promise.resource = config.resourceConstructor;
882
913
  promise = railsRootWrappingInterceptor(promise);
883
914
  }
884
915
 
885
916
  promise.then(function (response) {
886
- response.data = RailsResource.serializer.deserialize(response.data, RailsResource);
917
+ response.data = config.serializer.deserialize(response.data, config.resourceConstructor);
887
918
  return response;
888
919
  });
889
920
 
890
921
  // data is now deserialized. call response interceptors including beforeResponse
891
- angular.forEach(RailsResource.responseInterceptors, function (interceptor) {
892
- promise.resource = RailsResource;
922
+ forEachDependency(config.responseInterceptors, function (interceptor) {
923
+ promise.resource = config.resourceConstructor;
893
924
  promise.context = context;
894
925
  promise = interceptor(promise);
895
926
  });
@@ -899,9 +930,10 @@
899
930
 
900
931
  // transform data after response has been converted to a resource instance:
901
932
  RailsResource.callAfterInterceptors = function (promise) {
933
+ var config = this.config;
902
934
  // data is now deserialized. call response interceptors including afterResponse
903
- angular.forEach(RailsResource.afterResponseInterceptors, function (interceptor) {
904
- promise.resource = RailsResource;
935
+ forEachDependency(config.afterResponseInterceptors, function (interceptor) {
936
+ promise.resource = config.resourceConstructor;
905
937
  promise = interceptor(promise);
906
938
  });
907
939
 
@@ -909,18 +941,18 @@
909
941
  };
910
942
 
911
943
  RailsResource.processResponse = function (promise) {
912
- promise = RailsResource.callInterceptors(promise).then(function (response) {
944
+ promise = this.callInterceptors(promise).then(function (response) {
913
945
  return response.data;
914
946
  });
915
947
 
916
- return RailsResource.callAfterInterceptors(promise);
948
+ return this.callAfterInterceptors(promise);
917
949
  };
918
950
 
919
951
  RailsResource.getParameters = function (queryParams) {
920
952
  var params;
921
953
 
922
- if (RailsResource.defaultParams) {
923
- params = RailsResource.defaultParams;
954
+ if (this.config.defaultParams) {
955
+ params = this.config.defaultParams;
924
956
  }
925
957
 
926
958
  if (angular.isObject(queryParams)) {
@@ -931,13 +963,13 @@
931
963
  };
932
964
 
933
965
  RailsResource.getHttpConfig = function (queryParams) {
934
- var params = RailsResource.getParameters(queryParams);
966
+ var params = this.getParameters(queryParams);
935
967
 
936
968
  if (params) {
937
- return angular.extend({params: params}, RailsResource.httpConfig);
969
+ return angular.extend({params: params}, this.config.httpConfig);
938
970
  }
939
971
 
940
- return angular.copy(RailsResource.httpConfig);
972
+ return angular.copy(this.config.httpConfig);
941
973
  };
942
974
 
943
975
  /**
@@ -960,19 +992,19 @@
960
992
  context = {id: context};
961
993
  }
962
994
 
963
- return appendPath(RailsResource.url(context || {}), path);
995
+ return appendPath(this.buildUrl(context || {}), path);
964
996
  };
965
997
 
966
998
  RailsResource.$get = function (url, queryParams) {
967
- return RailsResource.processResponse($http.get(url, RailsResource.getHttpConfig(queryParams)));
999
+ return this.processResponse($http.get(url, this.getHttpConfig(queryParams)));
968
1000
  };
969
1001
 
970
1002
  RailsResource.query = function (queryParams, context) {
971
- return RailsResource.$get(RailsResource.resourceUrl(context), queryParams);
1003
+ return this.$get(this.resourceUrl(context), queryParams);
972
1004
  };
973
1005
 
974
1006
  RailsResource.get = function (context, queryParams) {
975
- return RailsResource.$get(RailsResource.resourceUrl(context), queryParams);
1007
+ return this.$get(this.resourceUrl(context), queryParams);
976
1008
  };
977
1009
 
978
1010
  /**
@@ -981,12 +1013,12 @@
981
1013
  * @param path {string} (optional) An additional path to append to the URL
982
1014
  * @returns {string} The URL for the resource
983
1015
  */
984
- RailsResource.prototype.$url = function(path) {
985
- return appendPath(RailsResource.resourceUrl(this), path);
1016
+ RailsResource.prototype.$url = function (path) {
1017
+ return appendPath(this.constructor.resourceUrl(this), path);
986
1018
  };
987
1019
 
988
1020
  RailsResource.prototype.processResponse = function (promise) {
989
- promise = RailsResource.callInterceptors(promise, this);
1021
+ promise = this.constructor.callInterceptors(promise, this);
990
1022
 
991
1023
  promise = promise.then(angular.bind(this, function (response) {
992
1024
  // we may not have response data
@@ -997,23 +1029,23 @@
997
1029
  return this;
998
1030
  }));
999
1031
 
1000
- return RailsResource.callAfterInterceptors(promise);
1032
+ return this.constructor.callAfterInterceptors(promise);
1001
1033
  };
1002
1034
 
1003
1035
  angular.forEach(['post', 'put', 'patch'], function (method) {
1004
1036
  RailsResource['$' + method] = function (url, data) {
1005
1037
  var config;
1006
1038
  // clone so we can manipulate w/o modifying the actual instance
1007
- data = RailsResource.transformData(angular.copy(data));
1008
- config = angular.extend({method: method, url: url, data: data}, RailsResource.getHttpConfig());
1009
- return RailsResource.processResponse($http(config));
1039
+ data = this.transformData(angular.copy(data, {}));
1040
+ config = angular.extend({method: method, url: url, data: data}, this.getHttpConfig());
1041
+ return this.processResponse($http(config));
1010
1042
  };
1011
1043
 
1012
1044
  RailsResource.prototype['$' + method] = function (url) {
1013
1045
  var data, config;
1014
1046
  // clone so we can manipulate w/o modifying the actual instance
1015
- data = RailsResource.transformData(angular.copy(this, {}));
1016
- config = angular.extend({method: method, url: url, data: data}, RailsResource.getHttpConfig());
1047
+ data = this.constructor.transformData(angular.copy(this, {}));
1048
+ config = angular.extend({method: method, url: url, data: data}, this.constructor.getHttpConfig());
1017
1049
  return this.processResponse($http(config));
1018
1050
 
1019
1051
  };
@@ -1024,12 +1056,12 @@
1024
1056
  };
1025
1057
 
1026
1058
  RailsResource.prototype.update = function () {
1027
- return this['$' + RailsResource.updateMethod](this.$url(), this);
1059
+ return this['$' + this.constructor.config.updateMethod](this.$url(), this);
1028
1060
  };
1029
1061
 
1030
1062
  RailsResource.prototype.isNew = function () {
1031
1063
  return this.id == null;
1032
- }
1064
+ };
1033
1065
 
1034
1066
  RailsResource.prototype.save = function () {
1035
1067
  if (this.isNew()) {
@@ -1037,14 +1069,14 @@
1037
1069
  } else {
1038
1070
  return this.update();
1039
1071
  }
1040
- }
1072
+ };
1041
1073
 
1042
1074
  RailsResource['$delete'] = function (url) {
1043
- return RailsResource.processResponse($http['delete'](url, RailsResource.getHttpConfig()));
1075
+ return this.processResponse($http['delete'](url, this.getHttpConfig()));
1044
1076
  };
1045
1077
 
1046
1078
  RailsResource.prototype['$delete'] = function (url) {
1047
- return this.processResponse($http['delete'](url, RailsResource.getHttpConfig()));
1079
+ return this.processResponse($http['delete'](url, this.constructor.getHttpConfig()));
1048
1080
  };
1049
1081
 
1050
1082
  //using ['delete'] instead of .delete for IE7/8 compatibility
@@ -1053,9 +1085,20 @@
1053
1085
  };
1054
1086
 
1055
1087
  return RailsResource;
1088
+ }];
1089
+ });
1090
+
1091
+ angular.module('rails').factory('railsResourceFactory', ['RailsResource', function (RailsResource) {
1092
+ return function (config) {
1093
+ function Resource() {
1094
+ Resource.__super__.constructor.apply(this, arguments);
1056
1095
  }
1057
1096
 
1058
- return railsResourceFactory;
1059
- }];
1060
- });
1097
+ RailsResource.extend(Resource);
1098
+ Resource.configure(config);
1099
+
1100
+ return Resource;
1101
+ }
1102
+ }]);
1103
+
1061
1104
  }());