js-model-rails 0.0.1
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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +13 -0
- data/Rakefile +1 -0
- data/js-model-rails.gemspec +22 -0
- data/lib/assets/javascripts/js-model/index.js +1 -0
- data/lib/assets/javascripts/js-model/js-model.js +688 -0
- data/lib/js-model-rails.rb +1 -0
- data/lib/js-model.rb +5 -0
- data/lib/js-model/version.rb +3 -0
- metadata +91 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Work with models in your JavaScript in your Rails (3.1)
|
2
|
+
|
3
|
+
At the moment all this Gem does is bundle the latest [js-model](https://github.com/benpickles/js-model) and set `include_root_in_json = false` - which is something I always forget to do.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
Gemfile:
|
8
|
+
|
9
|
+
gem 'js-model-rails'
|
10
|
+
|
11
|
+
app/assets/javascripts/application.js:
|
12
|
+
|
13
|
+
//= require js-model
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "js-model/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "js-model-rails"
|
7
|
+
s.version = JsModel::VERSION
|
8
|
+
s.authors = ["Ben Pickles"]
|
9
|
+
s.email = ["spideryoung@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/benpickles/js-model-rails"
|
11
|
+
s.summary = 'Work with models in your JavaScript in your Rails'
|
12
|
+
s.description = s.summary
|
13
|
+
|
14
|
+
s.rubyforge_project = "js-model-rails"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency 'railties', '~> 3.0'
|
22
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require ./js-model
|
@@ -0,0 +1,688 @@
|
|
1
|
+
/* js-model JavaScript library, version 0.10.1
|
2
|
+
* (c) 2010-2011 Ben Pickles
|
3
|
+
*
|
4
|
+
* Released under MIT license.
|
5
|
+
*/
|
6
|
+
var Model = function(name, func) {
|
7
|
+
// The model constructor.
|
8
|
+
var model = function(attributes) {
|
9
|
+
this.attributes = jQuery.extend({}, attributes)
|
10
|
+
this.changes = {};
|
11
|
+
this.errors = new Model.Errors(this);
|
12
|
+
this.uid = [name, Model.UID.generate()].join("-")
|
13
|
+
if (jQuery.isFunction(this.initialize)) this.initialize()
|
14
|
+
};
|
15
|
+
|
16
|
+
// Use module functionality to extend itself onto the constructor. Meta!
|
17
|
+
Model.Module.extend.call(model, Model.Module)
|
18
|
+
|
19
|
+
model._name = name
|
20
|
+
model.collection = []
|
21
|
+
model.unique_key = "id"
|
22
|
+
model
|
23
|
+
.extend(Model.Callbacks)
|
24
|
+
.extend(Model.ClassMethods)
|
25
|
+
|
26
|
+
model.prototype = new Model.Base
|
27
|
+
model.prototype.constructor = model
|
28
|
+
|
29
|
+
if (jQuery.isFunction(func)) func.call(model, model, model.prototype)
|
30
|
+
|
31
|
+
return model;
|
32
|
+
};
|
33
|
+
|
34
|
+
Model.Callbacks = {
|
35
|
+
bind: function(event, callback) {
|
36
|
+
this.callbacks = this.callbacks || {}
|
37
|
+
this.callbacks[event] = this.callbacks[event] || [];
|
38
|
+
this.callbacks[event].push(callback);
|
39
|
+
return this;
|
40
|
+
},
|
41
|
+
|
42
|
+
trigger: function(name, data) {
|
43
|
+
this.callbacks = this.callbacks || {}
|
44
|
+
|
45
|
+
var callbacks = this.callbacks[name];
|
46
|
+
|
47
|
+
if (callbacks) {
|
48
|
+
for (var i = 0; i < callbacks.length; i++) {
|
49
|
+
callbacks[i].apply(this, data || []);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
return this;
|
54
|
+
},
|
55
|
+
|
56
|
+
unbind: function(event, callback) {
|
57
|
+
this.callbacks = this.callbacks || {}
|
58
|
+
|
59
|
+
if (callback) {
|
60
|
+
var callbacks = this.callbacks[event] || [];
|
61
|
+
|
62
|
+
for (var i = 0; i < callbacks.length; i++) {
|
63
|
+
if (callbacks[i] === callback) {
|
64
|
+
this.callbacks[event].splice(i, 1);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
} else {
|
68
|
+
delete this.callbacks[event];
|
69
|
+
}
|
70
|
+
|
71
|
+
return this;
|
72
|
+
}
|
73
|
+
};
|
74
|
+
|
75
|
+
Model.ClassMethods = {
|
76
|
+
add: function(model) {
|
77
|
+
var id = model.id()
|
78
|
+
|
79
|
+
if (jQuery.inArray(model, this.collection) === -1 && !(id && this.find(id))) {
|
80
|
+
this.collection.push(model)
|
81
|
+
this.trigger("add", [model])
|
82
|
+
}
|
83
|
+
|
84
|
+
return this;
|
85
|
+
},
|
86
|
+
|
87
|
+
all: function() {
|
88
|
+
return this.collection.slice()
|
89
|
+
},
|
90
|
+
|
91
|
+
// Convenience method to allow a simple method of chaining class methods.
|
92
|
+
chain: function(collection) {
|
93
|
+
return jQuery.extend({}, this, { collection: collection })
|
94
|
+
},
|
95
|
+
|
96
|
+
count: function() {
|
97
|
+
return this.all().length;
|
98
|
+
},
|
99
|
+
|
100
|
+
detect: function(func) {
|
101
|
+
var all = this.all(),
|
102
|
+
model
|
103
|
+
|
104
|
+
for (var i = 0, length = all.length; i < length; i++) {
|
105
|
+
model = all[i]
|
106
|
+
if (func.call(model, model, i)) return model
|
107
|
+
}
|
108
|
+
},
|
109
|
+
|
110
|
+
each: function(func, context) {
|
111
|
+
var all = this.all()
|
112
|
+
|
113
|
+
for (var i = 0, length = all.length; i < length; i++) {
|
114
|
+
func.call(context || all[i], all[i], i, all)
|
115
|
+
}
|
116
|
+
|
117
|
+
return this;
|
118
|
+
},
|
119
|
+
|
120
|
+
find: function(id) {
|
121
|
+
return this.detect(function() {
|
122
|
+
return this.id() == id;
|
123
|
+
})
|
124
|
+
},
|
125
|
+
|
126
|
+
first: function() {
|
127
|
+
return this.all()[0]
|
128
|
+
},
|
129
|
+
|
130
|
+
load: function(callback) {
|
131
|
+
if (this._persistence) {
|
132
|
+
var self = this
|
133
|
+
|
134
|
+
this._persistence.read(function(models) {
|
135
|
+
for (var i = 0, length = models.length; i < length; i++) {
|
136
|
+
self.add(models[i])
|
137
|
+
}
|
138
|
+
|
139
|
+
if (callback) callback.call(self, models)
|
140
|
+
})
|
141
|
+
}
|
142
|
+
|
143
|
+
return this
|
144
|
+
},
|
145
|
+
|
146
|
+
last: function() {
|
147
|
+
var all = this.all();
|
148
|
+
return all[all.length - 1]
|
149
|
+
},
|
150
|
+
|
151
|
+
map: function(func, context) {
|
152
|
+
var all = this.all()
|
153
|
+
var values = []
|
154
|
+
|
155
|
+
for (var i = 0, length = all.length; i < length; i++) {
|
156
|
+
values.push(func.call(context || all[i], all[i], i, all))
|
157
|
+
}
|
158
|
+
|
159
|
+
return values
|
160
|
+
},
|
161
|
+
|
162
|
+
persistence: function(adapter) {
|
163
|
+
if (arguments.length == 0) {
|
164
|
+
return this._persistence
|
165
|
+
} else {
|
166
|
+
var options = Array.prototype.slice.call(arguments, 1)
|
167
|
+
options.unshift(this)
|
168
|
+
this._persistence = adapter.apply(adapter, options)
|
169
|
+
return this
|
170
|
+
}
|
171
|
+
},
|
172
|
+
|
173
|
+
pluck: function(attribute) {
|
174
|
+
var all = this.all()
|
175
|
+
var plucked = []
|
176
|
+
|
177
|
+
for (var i = 0, length = all.length; i < length; i++) {
|
178
|
+
plucked.push(all[i].attr(attribute))
|
179
|
+
}
|
180
|
+
|
181
|
+
return plucked
|
182
|
+
},
|
183
|
+
|
184
|
+
remove: function(model) {
|
185
|
+
var index
|
186
|
+
|
187
|
+
for (var i = 0, length = this.collection.length; i < length; i++) {
|
188
|
+
if (this.collection[i] === model) {
|
189
|
+
index = i
|
190
|
+
break
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
if (index != undefined) {
|
195
|
+
this.collection.splice(index, 1);
|
196
|
+
this.trigger("remove", [model]);
|
197
|
+
return true;
|
198
|
+
} else {
|
199
|
+
return false;
|
200
|
+
}
|
201
|
+
},
|
202
|
+
|
203
|
+
reverse: function() {
|
204
|
+
return this.chain(this.all().reverse())
|
205
|
+
},
|
206
|
+
|
207
|
+
select: function(func, context) {
|
208
|
+
var all = this.all(),
|
209
|
+
selected = [],
|
210
|
+
model
|
211
|
+
|
212
|
+
for (var i = 0, length = all.length; i < length; i++) {
|
213
|
+
model = all[i]
|
214
|
+
if (func.call(context || model, model, i, all)) selected.push(model)
|
215
|
+
}
|
216
|
+
|
217
|
+
return this.chain(selected);
|
218
|
+
},
|
219
|
+
|
220
|
+
sort: function(func) {
|
221
|
+
var sorted = this.all().sort(func)
|
222
|
+
return this.chain(sorted);
|
223
|
+
},
|
224
|
+
|
225
|
+
sortBy: function(attribute_or_func) {
|
226
|
+
var is_func = jQuery.isFunction(attribute_or_func)
|
227
|
+
var extract = function(model) {
|
228
|
+
return attribute_or_func.call(model)
|
229
|
+
}
|
230
|
+
|
231
|
+
return this.sort(function(a, b) {
|
232
|
+
var a_attr = is_func ? extract(a) : a.attr(attribute_or_func)
|
233
|
+
var b_attr = is_func ? extract(b) : b.attr(attribute_or_func)
|
234
|
+
|
235
|
+
if (a_attr < b_attr) {
|
236
|
+
return -1
|
237
|
+
} else if (a_attr > b_attr) {
|
238
|
+
return 1
|
239
|
+
} else {
|
240
|
+
return 0
|
241
|
+
}
|
242
|
+
})
|
243
|
+
},
|
244
|
+
|
245
|
+
use: function(klass) {
|
246
|
+
var args = Array.prototype.slice.call(arguments, 1)
|
247
|
+
args.unshift(this)
|
248
|
+
klass.apply(this, args)
|
249
|
+
return this
|
250
|
+
}
|
251
|
+
};
|
252
|
+
|
253
|
+
Model.Errors = function(model) {
|
254
|
+
this.errors = {};
|
255
|
+
this.model = model;
|
256
|
+
};
|
257
|
+
|
258
|
+
Model.Errors.prototype = {
|
259
|
+
add: function(attribute, message) {
|
260
|
+
if (!this.errors[attribute]) this.errors[attribute] = [];
|
261
|
+
this.errors[attribute].push(message);
|
262
|
+
return this
|
263
|
+
},
|
264
|
+
|
265
|
+
all: function() {
|
266
|
+
return this.errors;
|
267
|
+
},
|
268
|
+
|
269
|
+
clear: function() {
|
270
|
+
this.errors = {};
|
271
|
+
return this
|
272
|
+
},
|
273
|
+
|
274
|
+
each: function(func) {
|
275
|
+
for (var attribute in this.errors) {
|
276
|
+
for (var i = 0; i < this.errors[attribute].length; i++) {
|
277
|
+
func.call(this, attribute, this.errors[attribute][i]);
|
278
|
+
}
|
279
|
+
}
|
280
|
+
return this
|
281
|
+
},
|
282
|
+
|
283
|
+
on: function(attribute) {
|
284
|
+
return this.errors[attribute] || [];
|
285
|
+
},
|
286
|
+
|
287
|
+
size: function() {
|
288
|
+
var count = 0;
|
289
|
+
this.each(function() { count++; });
|
290
|
+
return count;
|
291
|
+
}
|
292
|
+
};
|
293
|
+
|
294
|
+
Model.InstanceMethods = {
|
295
|
+
asJSON: function() {
|
296
|
+
return this.attr()
|
297
|
+
},
|
298
|
+
|
299
|
+
attr: function(name, value) {
|
300
|
+
if (arguments.length === 0) {
|
301
|
+
// Combined attributes/changes object.
|
302
|
+
return jQuery.extend({}, this.attributes, this.changes);
|
303
|
+
} else if (arguments.length === 2) {
|
304
|
+
// Don't write to attributes yet, store in changes for now.
|
305
|
+
if (this.attributes[name] === value) {
|
306
|
+
// Clean up any stale changes.
|
307
|
+
delete this.changes[name];
|
308
|
+
} else {
|
309
|
+
this.changes[name] = value;
|
310
|
+
}
|
311
|
+
return this;
|
312
|
+
} else if (typeof name === "object") {
|
313
|
+
// Mass-assign attributes.
|
314
|
+
for (var key in name) {
|
315
|
+
this.attr(key, name[key]);
|
316
|
+
}
|
317
|
+
return this;
|
318
|
+
} else {
|
319
|
+
// Changes take precedent over attributes.
|
320
|
+
return (name in this.changes) ?
|
321
|
+
this.changes[name] :
|
322
|
+
this.attributes[name];
|
323
|
+
}
|
324
|
+
},
|
325
|
+
|
326
|
+
callPersistMethod: function(method, callback) {
|
327
|
+
var self = this;
|
328
|
+
|
329
|
+
// Automatically manage adding and removing from the model's Collection.
|
330
|
+
var manageCollection = function() {
|
331
|
+
if (method === "destroy") {
|
332
|
+
self.constructor.remove(self)
|
333
|
+
} else {
|
334
|
+
self.constructor.add(self)
|
335
|
+
}
|
336
|
+
};
|
337
|
+
|
338
|
+
// Wrap the existing callback in this function so we always manage the
|
339
|
+
// collection and trigger events from here rather than relying on the
|
340
|
+
// persistence adapter to do it for us. The persistence adapter is
|
341
|
+
// only required to execute the callback with a single argument - a
|
342
|
+
// boolean to indicate whether the call was a success - though any
|
343
|
+
// other arguments will also be forwarded to the original callback.
|
344
|
+
var wrappedCallback = function(success) {
|
345
|
+
if (success) {
|
346
|
+
// Merge any changes into attributes and clear changes.
|
347
|
+
self.merge(self.changes).reset();
|
348
|
+
|
349
|
+
// Add/remove from collection if persist was successful.
|
350
|
+
manageCollection();
|
351
|
+
|
352
|
+
// Trigger the event before executing the callback.
|
353
|
+
self.trigger(method);
|
354
|
+
}
|
355
|
+
|
356
|
+
// Store the return value of the callback.
|
357
|
+
var value;
|
358
|
+
|
359
|
+
// Run the supplied callback.
|
360
|
+
if (callback) value = callback.apply(self, arguments);
|
361
|
+
|
362
|
+
return value;
|
363
|
+
};
|
364
|
+
|
365
|
+
if (this.constructor._persistence) {
|
366
|
+
this.constructor._persistence[method](this, wrappedCallback);
|
367
|
+
} else {
|
368
|
+
wrappedCallback.call(this, true);
|
369
|
+
}
|
370
|
+
},
|
371
|
+
|
372
|
+
destroy: function(callback) {
|
373
|
+
this.callPersistMethod("destroy", callback);
|
374
|
+
return this;
|
375
|
+
},
|
376
|
+
|
377
|
+
id: function() {
|
378
|
+
return this.attributes[this.constructor.unique_key];
|
379
|
+
},
|
380
|
+
|
381
|
+
merge: function(attributes) {
|
382
|
+
jQuery.extend(this.attributes, attributes);
|
383
|
+
return this;
|
384
|
+
},
|
385
|
+
|
386
|
+
newRecord: function() {
|
387
|
+
return this.id() === undefined
|
388
|
+
},
|
389
|
+
|
390
|
+
reset: function() {
|
391
|
+
this.errors.clear();
|
392
|
+
this.changes = {};
|
393
|
+
return this;
|
394
|
+
},
|
395
|
+
|
396
|
+
save: function(callback) {
|
397
|
+
if (this.valid()) {
|
398
|
+
var method = this.newRecord() ? "create" : "update";
|
399
|
+
this.callPersistMethod(method, callback);
|
400
|
+
} else if (callback) {
|
401
|
+
callback(false);
|
402
|
+
}
|
403
|
+
|
404
|
+
return this;
|
405
|
+
},
|
406
|
+
|
407
|
+
valid: function() {
|
408
|
+
this.errors.clear();
|
409
|
+
this.validate();
|
410
|
+
return this.errors.size() === 0;
|
411
|
+
},
|
412
|
+
|
413
|
+
validate: function() {
|
414
|
+
return this;
|
415
|
+
}
|
416
|
+
};
|
417
|
+
|
418
|
+
Model.localStorage = function(klass) {
|
419
|
+
if (!window.localStorage) {
|
420
|
+
return {
|
421
|
+
create: function(model, callback) {
|
422
|
+
callback(true)
|
423
|
+
},
|
424
|
+
|
425
|
+
destroy: function(model, callback) {
|
426
|
+
callback(true)
|
427
|
+
},
|
428
|
+
|
429
|
+
read: function(callback) {
|
430
|
+
callback([])
|
431
|
+
},
|
432
|
+
|
433
|
+
update: function(model, callback) {
|
434
|
+
callback(true)
|
435
|
+
}
|
436
|
+
}
|
437
|
+
}
|
438
|
+
|
439
|
+
var collection_uid = [klass._name, "collection"].join("-")
|
440
|
+
var readIndex = function() {
|
441
|
+
var data = localStorage[collection_uid]
|
442
|
+
return data ? JSON.parse(data) : []
|
443
|
+
}
|
444
|
+
var writeIndex = function(uids) {
|
445
|
+
localStorage.setItem(collection_uid, JSON.stringify(uids))
|
446
|
+
}
|
447
|
+
var addToIndex = function(uid) {
|
448
|
+
var uids = readIndex()
|
449
|
+
|
450
|
+
if (jQuery.inArray(uid, uids) === -1) {
|
451
|
+
uids.push(uid)
|
452
|
+
writeIndex(uids)
|
453
|
+
}
|
454
|
+
}
|
455
|
+
var removeFromIndex = function(uid) {
|
456
|
+
var uids = readIndex()
|
457
|
+
var index = jQuery.inArray(uid, uids)
|
458
|
+
|
459
|
+
if (index > -1) {
|
460
|
+
uids.splice(index, 1)
|
461
|
+
writeIndex(uids)
|
462
|
+
}
|
463
|
+
}
|
464
|
+
var store = function(model) {
|
465
|
+
localStorage.setItem(model.uid, JSON.stringify(model.asJSON()))
|
466
|
+
addToIndex(model.uid)
|
467
|
+
}
|
468
|
+
|
469
|
+
return {
|
470
|
+
create: function(model, callback) {
|
471
|
+
store(model)
|
472
|
+
callback(true)
|
473
|
+
},
|
474
|
+
|
475
|
+
destroy: function(model, callback) {
|
476
|
+
localStorage.removeItem(model.uid)
|
477
|
+
removeFromIndex(model.uid)
|
478
|
+
callback(true)
|
479
|
+
},
|
480
|
+
|
481
|
+
read: function(callback) {
|
482
|
+
if (!callback) return false
|
483
|
+
|
484
|
+
var existing_uids = klass.map(function() { return this.uid })
|
485
|
+
var uids = readIndex()
|
486
|
+
var models = []
|
487
|
+
var attributes, model, uid
|
488
|
+
|
489
|
+
for (var i = 0, length = uids.length; i < length; i++) {
|
490
|
+
uid = uids[i]
|
491
|
+
|
492
|
+
if (jQuery.inArray(uid, existing_uids) == -1) {
|
493
|
+
attributes = JSON.parse(localStorage[uid])
|
494
|
+
model = new klass(attributes)
|
495
|
+
model.uid = uid
|
496
|
+
models.push(model)
|
497
|
+
}
|
498
|
+
}
|
499
|
+
|
500
|
+
callback(models)
|
501
|
+
},
|
502
|
+
|
503
|
+
update: function(model, callback) {
|
504
|
+
store(model)
|
505
|
+
callback(true)
|
506
|
+
}
|
507
|
+
}
|
508
|
+
};
|
509
|
+
|
510
|
+
Model.Log = function() {
|
511
|
+
if (window.console) window.console.log.apply(window.console, arguments);
|
512
|
+
};
|
513
|
+
|
514
|
+
Model.Module = {
|
515
|
+
extend: function(obj) {
|
516
|
+
jQuery.extend(this, obj)
|
517
|
+
return this
|
518
|
+
},
|
519
|
+
|
520
|
+
include: function(obj) {
|
521
|
+
jQuery.extend(this.prototype, obj)
|
522
|
+
return this
|
523
|
+
}
|
524
|
+
};
|
525
|
+
|
526
|
+
Model.REST = function(klass, resource, methods) {
|
527
|
+
var PARAM_NAME_MATCHER = /:([\w\d]+)/g;
|
528
|
+
var resource_param_names = (function() {
|
529
|
+
var resource_param_names = []
|
530
|
+
var param_name
|
531
|
+
|
532
|
+
while ((param_name = PARAM_NAME_MATCHER.exec(resource)) !== null) {
|
533
|
+
resource_param_names.push(param_name[1])
|
534
|
+
}
|
535
|
+
|
536
|
+
return resource_param_names
|
537
|
+
})()
|
538
|
+
|
539
|
+
return jQuery.extend({
|
540
|
+
path: function(model) {
|
541
|
+
var path = resource;
|
542
|
+
jQuery.each(resource_param_names, function(i, param) {
|
543
|
+
path = path.replace(":" + param, model.attributes[param]);
|
544
|
+
});
|
545
|
+
return path;
|
546
|
+
},
|
547
|
+
|
548
|
+
create: function(model, callback) {
|
549
|
+
return this.xhr('POST', this.create_path(model), model, callback);
|
550
|
+
},
|
551
|
+
|
552
|
+
create_path: function(model) {
|
553
|
+
return this.path(model);
|
554
|
+
},
|
555
|
+
|
556
|
+
destroy: function(model, callback) {
|
557
|
+
return this.xhr('DELETE', this.destroy_path(model), model, callback);
|
558
|
+
},
|
559
|
+
|
560
|
+
destroy_path: function(model) {
|
561
|
+
return this.update_path(model);
|
562
|
+
},
|
563
|
+
|
564
|
+
params: function(model) {
|
565
|
+
var params;
|
566
|
+
if (model) {
|
567
|
+
var attributes = model.asJSON()
|
568
|
+
delete attributes[model.constructor.unique_key];
|
569
|
+
params = {};
|
570
|
+
params[model.constructor._name.toLowerCase()] = attributes;
|
571
|
+
} else {
|
572
|
+
params = null;
|
573
|
+
}
|
574
|
+
if(jQuery.ajaxSettings.data){
|
575
|
+
params = jQuery.extend({}, jQuery.ajaxSettings.data, params)
|
576
|
+
}
|
577
|
+
return JSON.stringify(params)
|
578
|
+
},
|
579
|
+
|
580
|
+
read: function(callback) {
|
581
|
+
return this.xhr("GET", this.read_path(), null, function(success, xhr, data) {
|
582
|
+
data = jQuery.makeArray(data)
|
583
|
+
var models = []
|
584
|
+
|
585
|
+
for (var i = 0, length = data.length; i < length; i++) {
|
586
|
+
models.push(new klass(data[i]))
|
587
|
+
}
|
588
|
+
|
589
|
+
callback(models)
|
590
|
+
})
|
591
|
+
},
|
592
|
+
|
593
|
+
read_path: function() {
|
594
|
+
return resource
|
595
|
+
},
|
596
|
+
|
597
|
+
update: function(model, callback) {
|
598
|
+
return this.xhr('PUT', this.update_path(model), model, callback);
|
599
|
+
},
|
600
|
+
|
601
|
+
update_path: function(model) {
|
602
|
+
return [this.path(model), model.id()].join('/');
|
603
|
+
},
|
604
|
+
|
605
|
+
xhr: function(method, url, model, callback) {
|
606
|
+
var self = this;
|
607
|
+
var data = method == 'GET' ? undefined : this.params(model);
|
608
|
+
|
609
|
+
return jQuery.ajax({
|
610
|
+
type: method,
|
611
|
+
url: url,
|
612
|
+
contentType: "application/json",
|
613
|
+
dataType: "json",
|
614
|
+
data: data,
|
615
|
+
dataFilter: function(data, type) {
|
616
|
+
return /\S/.test(data) ? data : undefined;
|
617
|
+
},
|
618
|
+
complete: function(xhr, textStatus) {
|
619
|
+
self.xhrComplete(xhr, textStatus, model, callback)
|
620
|
+
}
|
621
|
+
});
|
622
|
+
},
|
623
|
+
|
624
|
+
xhrComplete: function(xhr, textStatus, model, callback) {
|
625
|
+
// Allow custom handlers to be defined per-HTTP status code.
|
626
|
+
var handler = Model.REST["handle" + xhr.status]
|
627
|
+
if (handler) handler.call(this, xhr, textStatus, model)
|
628
|
+
|
629
|
+
var success = textStatus === "success"
|
630
|
+
var data = Model.REST.parseResponseData(xhr)
|
631
|
+
|
632
|
+
// Remote data is the definitive source, update model.
|
633
|
+
if (success && model && data) model.attr(data)
|
634
|
+
|
635
|
+
if (callback) callback.call(model, success, xhr, data)
|
636
|
+
}
|
637
|
+
}, methods)
|
638
|
+
};
|
639
|
+
|
640
|
+
// TODO: Remove in v1 if it ever gets there.
|
641
|
+
Model.RestPersistence = Model.REST;
|
642
|
+
|
643
|
+
// Rails' preferred failed validation response code, assume the response
|
644
|
+
// contains errors and replace current model errors with them.
|
645
|
+
Model.REST.handle422 = function(xhr, textStatus, model) {
|
646
|
+
var data = Model.REST.parseResponseData(xhr);
|
647
|
+
|
648
|
+
if (data) {
|
649
|
+
model.errors.clear()
|
650
|
+
|
651
|
+
for (var attribute in data) {
|
652
|
+
for (var i = 0; i < data[attribute].length; i++) {
|
653
|
+
model.errors.add(attribute, data[attribute][i])
|
654
|
+
}
|
655
|
+
}
|
656
|
+
}
|
657
|
+
};
|
658
|
+
|
659
|
+
Model.REST.parseResponseData = function(xhr) {
|
660
|
+
try {
|
661
|
+
return /\S/.test(xhr.responseText) ?
|
662
|
+
jQuery.parseJSON(xhr.responseText) :
|
663
|
+
undefined;
|
664
|
+
} catch(e) {
|
665
|
+
Model.Log(e);
|
666
|
+
}
|
667
|
+
};
|
668
|
+
|
669
|
+
Model.UID = {
|
670
|
+
counter: 0,
|
671
|
+
|
672
|
+
generate: function() {
|
673
|
+
return [new Date().valueOf(), this.counter++].join("-")
|
674
|
+
},
|
675
|
+
|
676
|
+
reset: function() {
|
677
|
+
this.counter = 0
|
678
|
+
return this
|
679
|
+
}
|
680
|
+
};
|
681
|
+
|
682
|
+
Model.VERSION = "0.10.1";
|
683
|
+
|
684
|
+
Model.Base = (function() {
|
685
|
+
function Base() {}
|
686
|
+
Base.prototype = jQuery.extend({}, Model.Callbacks, Model.InstanceMethods)
|
687
|
+
return Base
|
688
|
+
})();
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'js-model'
|
data/lib/js-model.rb
ADDED
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: js-model-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ben Pickles
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-06-28 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: railties
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
version: "3.0"
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
description: Work with models in your JavaScript in your Rails
|
37
|
+
email:
|
38
|
+
- spideryoung@gmail.com
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files: []
|
44
|
+
|
45
|
+
files:
|
46
|
+
- .gitignore
|
47
|
+
- Gemfile
|
48
|
+
- README.md
|
49
|
+
- Rakefile
|
50
|
+
- js-model-rails.gemspec
|
51
|
+
- lib/assets/javascripts/js-model/index.js
|
52
|
+
- lib/assets/javascripts/js-model/js-model.js
|
53
|
+
- lib/js-model-rails.rb
|
54
|
+
- lib/js-model.rb
|
55
|
+
- lib/js-model/version.rb
|
56
|
+
has_rdoc: true
|
57
|
+
homepage: https://github.com/benpickles/js-model-rails
|
58
|
+
licenses: []
|
59
|
+
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
hash: 3
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project: js-model-rails
|
86
|
+
rubygems_version: 1.6.2
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Work with models in your JavaScript in your Rails
|
90
|
+
test_files: []
|
91
|
+
|