backbone_extensions 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.markdown CHANGED
@@ -13,5 +13,6 @@ Installing
13
13
  3. In your application.js //= require backbone
14
14
  4. In your application.js //= require backbone_extensions/include
15
15
  5. In your application.js //= require backbone_extensions/decorator
16
+ 6. In your application.js //= require backbone_extensions/associations
16
17
 
17
- Copyright (c) 2012 Ryan Dy, released under the MIT license
18
+ Copyright (c) 2012-3 Ryan Dy, Thomas Bukowski, released under the MIT license
data/config/jshint.yml CHANGED
@@ -8,6 +8,7 @@
8
8
 
9
9
  paths:
10
10
  - lib/assets/javascripts/**/*.js
11
+ - spec/javascripts/**/*.js
11
12
 
12
13
  exclude_paths:
13
14
 
@@ -20,7 +21,7 @@ exclude_paths:
20
21
  adsafe: false # true if ADsafe rules should be enforced. See http://www.ADsafe.org/
21
22
  bitwise: true # true if bitwise operators should not be allowed
22
23
  newcap: true # true if Initial Caps must be used with constructor functions
23
- eqeqeq: false # true if === should be required (for ALL equality comparisons)
24
+ eqeqeq: true # true if === should be required (for ALL equality comparisons)
24
25
  immed: false # true if immediate function invocations must be wrapped in parens
25
26
  nomen: false # true if initial or trailing underscore in identifiers should be forbidden
26
27
  onevar: false # true if only one var statement per function should be allowed
@@ -43,6 +44,7 @@ fragment: false # true if HTML fragments should be allowed
43
44
  laxbreak: false # true if statement breaks should not be checked
44
45
  on: false # true if HTML event handlers (e.g. onclick="...") should be allowed
45
46
  sub: false # true if subscript notation may be used for expressions better expressed in dot notation
47
+ validthis: true # true if suppresses warnings about possible strict violations when the code is running in strict mode and you use this in a non-constructor function
46
48
 
47
49
  # other options
48
50
 
@@ -55,6 +57,14 @@ predef: # Names of predefined global variables - comma-separated strin
55
57
  - _
56
58
  - Backbone
57
59
  - jQuery
60
+ - $
61
+ - describe
62
+ - it
63
+ - beforeEach
64
+ - afterEach
65
+ - expect
66
+ - jasmine
67
+ - spyOn
58
68
 
59
69
  browser: true # true if the standard browser globals should be predefined
60
70
  rhino: false # true if the Rhino environment globals should be predefined
@@ -64,12 +74,12 @@ devel: false # true if functions like alert, confirm, console, prompt etc
64
74
 
65
75
  # jshint options
66
76
  loopfunc: true # true if functions should be allowed to be defined within loops
67
- asi: true # true if automatic semicolon insertion should be tolerated
68
- boss: true # true if advanced usage of assignments and == should be allowed
69
- couch: true # true if CouchDB globals should be predefined
77
+ asi: false # true if automatic semicolon insertion should be tolerated
78
+ boss: false # true if advanced usage of assignments and == should be allowed
79
+ couch: false # true if CouchDB globals should be predefined
70
80
  curly: false # true if curly braces around blocks should be required (even in if/for/while)
71
81
  noarg: true # true if arguments.caller and arguments.callee should be disallowed
72
- node: true # true if the Node.js environment globals should be predefined
82
+ node: false # true if the Node.js environment globals should be predefined
73
83
  noempty: true # true if empty blocks should be disallowed
74
84
  nonew: true # true if using `new` for side-effects should be disallowed
75
85
 
@@ -0,0 +1,166 @@
1
+ //= require backbone_extensions/include
2
+ //= require underscore.string
3
+ (function(_, Backbone) {
4
+ 'use strict';
5
+ function mixin(namespace) {
6
+ namespace = namespace || {};
7
+
8
+ function mergeAssociationOptions() {
9
+ return _(arguments).chain().toArray().reduce(function(result, options) { return _(result).extend(options); }, {})
10
+ .omit('class', 'className', 'inverseOf', 'parseName', 'through').value();
11
+ }
12
+
13
+ function buildAssociation(associationType, associationName, options) {
14
+ function through() {
15
+ function association() {
16
+ var t = (_(options.through).isFunction() && options.through.call(this)) || _.str.camelize(options.through);
17
+ return this[t] && this[t]() && this[t]()[associationName] && this[t]()[associationName]();
18
+ }
19
+ return options.through && association.call(this);
20
+ }
21
+
22
+ function throughCollection() {
23
+ return (this.collection && this.collection[associationName] && this.collection[associationName]()) ||
24
+ (this._options && this._options.collection && this._options.collection[associationName] && this._options.collection[associationName]());
25
+ }
26
+
27
+ function createAssociation() {
28
+ var collectionName = _.str.classify(associationName), className = options.className && _.str.classify(options.className),
29
+ newOptions = mergeAssociationOptions(options, this._options);
30
+
31
+ if (options.inverseOf) {
32
+ newOptions[_.str.camelize(options.inverseOf)] = _(function() { return this; }).bind(this);
33
+ }
34
+
35
+ return _((options['class'] && new options['class'](null, newOptions)) ||
36
+ (className && namespace[className] && new namespace[className](null, newOptions)) ||
37
+ (namespace[collectionName] && new namespace[collectionName](null, newOptions))).tap(_(function(association) {
38
+ through.call(this, association);
39
+ }).bind(this));
40
+ }
41
+
42
+ var associations = {
43
+ hasMany: createAssociation,
44
+ hasOne: function() { return throughCollection.call(this) || createAssociation.call(this); },
45
+ belongsTo: function() { return throughCollection.call(this) || through.call(this); }
46
+ };
47
+
48
+ this.prototype[associationName] = function() {
49
+ return (this._associations || (this._associations = {})) && this._associations[associationName] ||
50
+ (this._associations[associationName] = (this._options && _(this._options).result(associationName)) || associations[associationType].call(this));
51
+ };
52
+ }
53
+
54
+ function parseAssociation(associationType, associationName, options) {
55
+ function parseResponseWith(key, response) {
56
+ var camelized = _.str.camelize(key), underscored = _.str.underscored(key);
57
+ if (response[camelized]) {
58
+ return {key: camelized, response: response[camelized]};
59
+ }
60
+ else if (response[underscored]) {
61
+ return {key: underscored, response: response[underscored]};
62
+ }
63
+ else {
64
+ return {response: null};
65
+ }
66
+ }
67
+
68
+ function through(response) {
69
+ var t = parseResponseWith(_(options).result('through'), response).response,
70
+ singularAssociationName = _.singularize && _(associationName).singularize(),
71
+ p = options.parseName || singularAssociationName;
72
+ return {response: t && p && _(t)[associationType === 'hasOne' ? 'result' : 'pluck'](p)};
73
+ }
74
+
75
+ if (options.parse) {
76
+ var associations = {
77
+ hasMany: function(assocResponse, association, newOptions) {
78
+ association.add(assocResponse, newOptions);
79
+ },
80
+ hasOne: function(assocResponse, association, newOptions) {
81
+ association.clear({silent: true}).set(assocResponse, newOptions);
82
+ }
83
+ };
84
+
85
+ if (associations[associationType]) {
86
+ var parseFunc = _(options.parse).isFunction() &&
87
+ function(response) { return {response: options.parse.call(this, response) }; } ||
88
+ function(response) {
89
+ return (options.through && through.call(this, response)) ||
90
+ (options.parseName && parseResponseWith(options.parseName, response)) ||
91
+ (options.className && parseResponseWith(options.className, response)) ||
92
+ parseResponseWith(associationName, response);
93
+ };
94
+
95
+ if(!this._parsers) {
96
+ var originalParse = this.prototype.parse,
97
+ parsers = this._parsers = [];
98
+ this.prototype.parse = function(response) {
99
+ return _(originalParse.call(this, response)).tap(_(function(parsedResponse) {
100
+ _(parsers)
101
+ .chain()
102
+ .map(function(parser) {
103
+ return _(parser.parseFn.call(this, parsedResponse)).tap(_(function(result) {
104
+ parser.associationFn.call(this, result.response);
105
+ }).bind(this)).key;
106
+ }, this)
107
+ .each(function(key) {
108
+ return key && delete parsedResponse[key];
109
+ });
110
+ }).bind(this));
111
+ };
112
+ }
113
+
114
+ this._parsers.push({
115
+ parseFn: parseFunc,
116
+ associationFn: function(assocResponse) {
117
+ associations[associationType].call(this, assocResponse, this[associationName](), mergeAssociationOptions(options, this.options));
118
+ }
119
+ });
120
+ }
121
+ }
122
+ }
123
+
124
+ return {
125
+ included: function(source) {
126
+ var associations = _({
127
+ belongsTo: {}, hasMany: {parse: true}, hasOne: {parse: true}
128
+ }).reduce(function(associations, defaultOptions, associationType) {
129
+ associations[associationType] = function(name, options) {
130
+ var associationName = _.str.camelize(name);
131
+ options = _({}).extend(defaultOptions, options);
132
+ buildAssociation.call(this, associationType, associationName, options);
133
+ parseAssociation.call(this, associationType, associationName, options);
134
+ return this;
135
+ };
136
+ return associations;
137
+ }, {});
138
+
139
+ _(source).extend(associations, {
140
+ associations: function() {
141
+ _(arguments).chain().toArray().each(function(options) {
142
+ _(associations).chain().keys().each(function(associationType) {
143
+ if (options[associationType]) {
144
+ associations[associationType].call(source, options[associationType], _(options).omit(associationType));
145
+ }
146
+ });
147
+ });
148
+ },
149
+
150
+ extend: _(source.extend).wrap(function(oldExtend, protoProps, classProps) {
151
+ return _(oldExtend.call(this, protoProps, classProps)).tap(function() {
152
+ source.associations((protoProps || {}).associations);
153
+ });
154
+ })
155
+ });
156
+
157
+ source.prototype.initialize = _(source.prototype.initialize).wrap(function(oldInitialize, attrsOrModels, options) {
158
+ this._options = _(options).clone();
159
+ oldInitialize.call(this, attrsOrModels, options);
160
+ });
161
+ }
162
+ };
163
+ }
164
+
165
+ Backbone.extensions = _(Backbone.extensions || {}).extend({associations: mixin});
166
+ })(_, Backbone);
@@ -1,59 +1,45 @@
1
- (function(Backbone, $) {
2
- function decorateModels(self, models) {
3
- var decoratee = models instanceof Backbone.Collection ? models.models : models,
4
- proto = _(self)
5
- .chain()
6
- .functions()
7
- .without('collection', 'constructor', 'initialize', 'model')
8
- .inject(function(result, method) { result[method] = self[method]; return result; }, {})
9
- .value();
10
-
11
- if (decoratee instanceof Array) {
12
- _(proto).each(function(fn, name) {
13
- self[name] = function() {
14
- var args = arguments;
15
- return _(decoratee).map(function(model) {
16
- return fn.apply(model, args);
17
- });
18
- };
19
- });
20
- } else {
21
- _(proto).each(function(fn, name) {
22
- self[name] = function() {
23
- return fn.apply(decoratee, arguments);
24
- };
25
- });
26
- }
1
+ (function(_, Backbone) {
2
+ 'use strict';
3
+ function Decorator(models, options) {
4
+ this._decoratee = models instanceof Backbone.Collection ? models.models : models;
5
+ this.initialize.call(this, models, options);
27
6
  }
28
7
 
29
- function Decorator(models, options) {
30
- decorateModels(this, models);
31
- this.initialize.apply(this, arguments);
8
+ function wrapDecorator(fn) {
9
+ return function() {
10
+ var args = arguments;
11
+ if (_(this._decoratee).isArray()) {
12
+ return _(this._decoratee).map(function(model) {
13
+ return fn.apply(model, args);
14
+ });
15
+ } else {
16
+ return fn.apply(this._decoratee, args);
17
+ }
18
+ };
32
19
  }
33
20
 
34
21
  _(Decorator).extend({
35
22
  extend: function(protoProps, classProps) {
36
- var Klass = Backbone.Model.extend.apply(this, arguments);
37
-
38
- if (protoProps.model) {
39
- protoProps.model.prototype.decorator = function() {
40
- return new Klass(this);
41
- };
42
- }
43
-
44
- if (protoProps.collection) {
45
- protoProps.collection.prototype.decorator = function() {
46
- return new Klass(this);
47
- };
48
- }
49
-
50
- return Klass;
23
+ var proto = _(protoProps).chain().omit('collection', 'constructor', 'initialize', 'model').reduce(function(proto, fn, name) {
24
+ proto[name] = wrapDecorator(fn);
25
+ return proto;
26
+ }, {}).value();
27
+
28
+ return _(Backbone.Model.extend.call(this, _(protoProps).extend(proto), classProps)).tap(function(Klass) {
29
+ _(['model', 'collection']).each(function(type) {
30
+ if (protoProps[type]) {
31
+ protoProps[type].prototype.decorator = function() {
32
+ return new Klass(this);
33
+ };
34
+ }
35
+ });
36
+ });
51
37
  }
52
- }, Backbone.include ? Backbone.include : {});
38
+ }, Backbone.extensions && Backbone.extensions.include || {});
53
39
 
54
40
  _(Decorator.prototype).extend({
55
- initialize: $.noop
41
+ initialize: function(models, options) {}
56
42
  });
57
43
 
58
- Backbone.Decorator = Decorator;
59
- })(Backbone, jQuery);
44
+ Backbone.extensions = _(Backbone.extensions || {}).extend({Decorator: Decorator});
45
+ })(_, Backbone);
@@ -0,0 +1,94 @@
1
+ (function(_, Backbone) {
2
+ 'use strict';
3
+ function bindModelEvents(tuple) {
4
+ var self = this, subject = tuple[0], eventNames = tuple[1], modelEvents = self._modelEvents;
5
+ modelEvents.push(tuple);
6
+
7
+ _(subject && eventNames).each(function(callback, event) {
8
+ _(event.split(' ')).each(function(e) {
9
+ _([callback]).chain().flatten().each(function(c) {
10
+ if (_(c).isFunction()) {
11
+ subject.on(e, c, self);
12
+ } else {
13
+ subject.on(e, self[c], self);
14
+ }
15
+ });
16
+ });
17
+ });
18
+ }
19
+
20
+ function unbindModelEvents() {
21
+ var self = this, modelEvents = self._modelEvents;
22
+ _(modelEvents).each(function(tuple) {
23
+ var subject = tuple[0], events = tuple[1];
24
+
25
+ _(subject && events).each(function(callback, event) {
26
+ _(event.split(' ')).each(function(e) {
27
+ _([callback]).chain().flatten().each(function(c) {
28
+ if (_(c).isFunction()) {
29
+ subject.off(e, c);
30
+ } else {
31
+ subject.off(e, self[c]);
32
+ }
33
+ });
34
+ });
35
+ });
36
+ });
37
+ }
38
+
39
+ function wrapUndelegateEvents(callback) {
40
+ var oldUndelegateEvents = this.undelegateEvents;
41
+ this.undelegateEvents();
42
+ this.undelegateEvents = function() {};
43
+ callback.call(this);
44
+ this.undelegateEvents = oldUndelegateEvents;
45
+ }
46
+
47
+ var delegateEvents = {
48
+ included: function(source) {
49
+ _(source.prototype).extend({
50
+ initialize: _(source.prototype.initialize).wrap(function(oldInitialize, attrsOrModels, options) {
51
+ this._modelEvents = [];
52
+ oldInitialize.call(this, attrsOrModels, options);
53
+ }),
54
+
55
+ delegateEvents: _(source.prototype.delegateEvents).wrap(function(oldDelegateEvents) {
56
+ var self = this, args = _(arguments).rest(1);
57
+
58
+ if (!args.length) {
59
+ return oldDelegateEvents.call(this);
60
+ }
61
+
62
+ wrapUndelegateEvents.call(this, function() {
63
+ _(args).chain().toArray().compact().each(function(obj) {
64
+ var arg = _(obj);
65
+ if (arg.isArray()) {
66
+ bindModelEvents.call(self, obj);
67
+ } else {
68
+ arg.each(function(callbacks, event) {
69
+ _([callbacks]).chain().flatten().each(function(callback) {
70
+ oldDelegateEvents.call(self, _({}).tap(function(obj) {
71
+ obj[event] = callback;
72
+ }));
73
+ });
74
+ });
75
+ }
76
+ });
77
+ });
78
+ }),
79
+
80
+ undelegateEvents: _(source.prototype.undelegateEvents).wrap(function(oldUndelegateEvents) {
81
+ unbindModelEvents.call(this);
82
+ return oldUndelegateEvents.call(this);
83
+ }),
84
+
85
+ remove: _(source.prototype.remove).wrap(function(oldRemove) {
86
+ this.undelegateEvents();
87
+ return oldRemove.call(this);
88
+ })
89
+ });
90
+ }
91
+ };
92
+
93
+ Backbone.extensions = _(Backbone.extensions || {}).extend({delegateEvents: delegateEvents});
94
+ })(_, Backbone);
@@ -1,4 +1,5 @@
1
- (function(Backbone) {
1
+ (function(_, Backbone) {
2
+ 'use strict';
2
3
  var include = {
3
4
  include: function() {
4
5
  var self = this;
@@ -11,5 +12,5 @@
11
12
  }
12
13
  };
13
14
 
14
- Backbone.include = include;
15
- })(Backbone);
15
+ Backbone.extensions = _(Backbone.extensions || {}).extend({include: include});
16
+ })(_, Backbone);
@@ -1,3 +1,3 @@
1
1
  module BackboneExtensions
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backbone_extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Ryan Dy
9
+ - Thomas Bukowski
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2012-11-02 00:00:00.000000000 Z
13
+ date: 2013-01-07 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: fuubar
@@ -96,12 +97,15 @@ description: Adds extensions to the backbone javascript library. It adds the jav
96
97
  underscore, backbone and the extensions you need.
97
98
  email:
98
99
  - ryan.dy@gmail.com
100
+ - me@neodude.net
99
101
  executables: []
100
102
  extensions: []
101
103
  extra_rdoc_files: []
102
104
  files:
103
105
  - config/jshint.yml
106
+ - lib/assets/javascripts/backbone_extensions/associations.js
104
107
  - lib/assets/javascripts/backbone_extensions/decorator.js
108
+ - lib/assets/javascripts/backbone_extensions/delegate_events.js
105
109
  - lib/assets/javascripts/backbone_extensions/include.js
106
110
  - lib/backbone_extensions/version.rb
107
111
  - lib/backbone_extensions.rb
@@ -122,7 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
122
126
  version: '0'
123
127
  segments:
124
128
  - 0
125
- hash: -446599863856035963
129
+ hash: 1650966973307354674
126
130
  required_rubygems_version: !ruby/object:Gem::Requirement
127
131
  none: false
128
132
  requirements:
@@ -131,10 +135,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
135
  version: '0'
132
136
  segments:
133
137
  - 0
134
- hash: -446599863856035963
138
+ hash: 1650966973307354674
135
139
  requirements: []
136
140
  rubyforge_project:
137
- rubygems_version: 1.8.19
141
+ rubygems_version: 1.8.24
138
142
  signing_key:
139
143
  specification_version: 3
140
144
  summary: Extensions to backbone javascript library as a rails engine