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 ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in js-model-rails.gemspec
4
+ gemspec
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
@@ -0,0 +1,5 @@
1
+ module JsModel
2
+ class Engine < Rails::Engine
3
+ config.active_record.include_root_in_json = false
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module JsModel
2
+ VERSION = '0.0.1'
3
+ end
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
+