vault-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/LICENSE +21 -0
- data/Rakefile +2 -0
- data/lib/vault-rails.rb +6 -0
- data/lib/vault-rails/version.rb +5 -0
- data/vault-rails.gemspec +19 -0
- data/vendor/assets/javascripts/vault.js.coffee +521 -0
- metadata +53 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Dennis Reimann
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
data/lib/vault-rails.rb
ADDED
data/vault-rails.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "vault-rails/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "vault-rails"
|
7
|
+
s.version = Vault::Rails::VERSION
|
8
|
+
s.authors = ["Jordan MacDonald"]
|
9
|
+
s.email = ["jordan@wastedintelligence.com"]
|
10
|
+
s.homepage = 'https://github.com/cityofgreatersudbury/vault-rails'
|
11
|
+
s.summary = 'CoffeeScript collection class for offline use.'
|
12
|
+
s.description = 'Store and manage collections of objects without a connection.'
|
13
|
+
|
14
|
+
s.rubyforge_project = "vault-rails"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
end
|
@@ -0,0 +1,521 @@
|
|
1
|
+
class Vault
|
2
|
+
constructor: (name, urls, options = {}) ->
|
3
|
+
# Setup some internal variables.
|
4
|
+
@objects = []
|
5
|
+
@dirty_object_count = 0
|
6
|
+
@errors = []
|
7
|
+
@save_error_count = 0
|
8
|
+
|
9
|
+
# This property is used to temporarily lock the vault during mutation methods.
|
10
|
+
@locked = false
|
11
|
+
|
12
|
+
# Create a date object which will be used to
|
13
|
+
# generate unique IDs for new records.
|
14
|
+
@date = new Date
|
15
|
+
|
16
|
+
# Import required parameters for the data store.
|
17
|
+
@name = name
|
18
|
+
@urls = urls
|
19
|
+
|
20
|
+
# Declare default options.
|
21
|
+
@options =
|
22
|
+
autoload: true
|
23
|
+
after_load: ->
|
24
|
+
id_attribute: "id"
|
25
|
+
offline: false
|
26
|
+
sub_collections: []
|
27
|
+
|
28
|
+
# Merge default options with user-defined ones.
|
29
|
+
for option, value of options
|
30
|
+
@options[option] = value
|
31
|
+
|
32
|
+
# Setup the vault for offline use.
|
33
|
+
if @options.offline
|
34
|
+
# Bind a cache routine to save data should the window be closed or url changed.
|
35
|
+
$(window).unload =>
|
36
|
+
@store()
|
37
|
+
|
38
|
+
# Load the collection if configured to do so.
|
39
|
+
if @options.autoload
|
40
|
+
# Check the offline data store first, if configured to do so.
|
41
|
+
if @options.offline
|
42
|
+
if @load()
|
43
|
+
if @dirty_object_count > 0
|
44
|
+
# Offline data loaded and modifications found; keep existing data.
|
45
|
+
|
46
|
+
# Extend the loaded objects with vault-specific variables and functions.
|
47
|
+
for object in @objects
|
48
|
+
@extend object
|
49
|
+
|
50
|
+
# Detach the callback to after_load so that the call to the
|
51
|
+
# vault constructor can complete/return, allowing any post-load code
|
52
|
+
# to use the newly instantiated vault object as required.
|
53
|
+
window.setTimeout @options.after_load, 100
|
54
|
+
else
|
55
|
+
# No modifications in offline data; reload fresh data.
|
56
|
+
@reload(@options.after_load)
|
57
|
+
else
|
58
|
+
if navigator.onLine
|
59
|
+
# Load failed, but we're connected; reload fresh data.
|
60
|
+
@reload(@options.after_load)
|
61
|
+
else
|
62
|
+
# Load failed and we're offline; log an error.
|
63
|
+
@errors.push "Offline data failed to load. Could not load live data as browser is offline."
|
64
|
+
else
|
65
|
+
@reload(@options.after_load)
|
66
|
+
|
67
|
+
# Create convenience attributes for sub-collections.
|
68
|
+
for sub_collection in @options.sub_collections
|
69
|
+
do (sub_collection) =>
|
70
|
+
@[sub_collection] = {
|
71
|
+
'find': (id) =>
|
72
|
+
for object in @objects
|
73
|
+
for sub_object in object[sub_collection]
|
74
|
+
if sub_object[@options.id_attribute] is parseInt(id)
|
75
|
+
return sub_object
|
76
|
+
|
77
|
+
# Object with specified id couldn't be found.
|
78
|
+
return false
|
79
|
+
}
|
80
|
+
|
81
|
+
# Iterate over non-deleted items in the collection.
|
82
|
+
each: (logic) ->
|
83
|
+
for object in @objects
|
84
|
+
unless object.status == "deleted"
|
85
|
+
logic object
|
86
|
+
|
87
|
+
# Add a new item to the collection.
|
88
|
+
add: (object) ->
|
89
|
+
# Don't bother if the vault is locked.
|
90
|
+
if @locked
|
91
|
+
@errors.push 'Cannot add, vault is locked.'
|
92
|
+
return false
|
93
|
+
|
94
|
+
# If the object has no id, generate a temporary one and add it to the object.
|
95
|
+
unless object[@options.id_attribute]? and object[@options.id_attribute] isnt ''
|
96
|
+
object[@options.id_attribute] = @date.getTime()
|
97
|
+
|
98
|
+
# Extend the object with vault-specific variables and functions.
|
99
|
+
@extend object,"new"
|
100
|
+
|
101
|
+
# Add the object to the collection.
|
102
|
+
@objects.push object
|
103
|
+
|
104
|
+
# Increase the count of dirty objects.
|
105
|
+
@dirty_object_count++
|
106
|
+
|
107
|
+
# Store the collection.
|
108
|
+
@store
|
109
|
+
|
110
|
+
# Return the extended object.
|
111
|
+
return object
|
112
|
+
|
113
|
+
# Find an object in the collection using its id.
|
114
|
+
find: (id) ->
|
115
|
+
for object in @objects
|
116
|
+
if object[@options.id_attribute] == parseInt(id)
|
117
|
+
return object
|
118
|
+
|
119
|
+
# Object with specified id couldn't be found.
|
120
|
+
return false
|
121
|
+
|
122
|
+
# Update an existing item in the collection.
|
123
|
+
update: (attributes, id) ->
|
124
|
+
# Don't bother if the vault is locked.
|
125
|
+
if @locked
|
126
|
+
@errors.push 'Cannot update, vault is locked.'
|
127
|
+
return false
|
128
|
+
|
129
|
+
# Get the id of the object from the attributes if it's not explicitly defined.
|
130
|
+
id = attributes[@options.id_attribute] unless id?
|
131
|
+
|
132
|
+
# Get the object; return if it's undefined.
|
133
|
+
object = @find(id)
|
134
|
+
unless object?
|
135
|
+
@errors.push 'Cannot update, object not found.'
|
136
|
+
return false
|
137
|
+
|
138
|
+
# Flag it as dirty.
|
139
|
+
if object.status is "clean"
|
140
|
+
object.status = "dirty"
|
141
|
+
@dirty_object_count++
|
142
|
+
|
143
|
+
# Merge in the updated attributes, if they're specified and defined on the object.
|
144
|
+
if attributes?
|
145
|
+
for attribute, value of attributes
|
146
|
+
if object[attribute]? and attribute isnt @options['id_attribute']
|
147
|
+
object[attribute] = value
|
148
|
+
|
149
|
+
# Store the collection.
|
150
|
+
@store
|
151
|
+
|
152
|
+
# Update was successful.
|
153
|
+
return true
|
154
|
+
|
155
|
+
# Flag an object in the collection for deletion,
|
156
|
+
# or if the object is new, remove it.
|
157
|
+
delete: (id) ->
|
158
|
+
# Don't bother if the vault is locked.
|
159
|
+
if @locked
|
160
|
+
@errors.push 'Cannot delete, vault is locked.'
|
161
|
+
return false
|
162
|
+
|
163
|
+
for object, index in @objects
|
164
|
+
if object[@options.id_attribute] == id
|
165
|
+
switch object.status
|
166
|
+
when "new"
|
167
|
+
# New objects are special; we essentially want to
|
168
|
+
# reverse the steps taken during the add operation.
|
169
|
+
@objects.splice(index, 1)
|
170
|
+
@dirty_object_count--
|
171
|
+
when "clean"
|
172
|
+
object.status = "deleted"
|
173
|
+
@dirty_object_count++
|
174
|
+
when "dirty"
|
175
|
+
object.status = "deleted"
|
176
|
+
|
177
|
+
# Store the collection.
|
178
|
+
@store
|
179
|
+
|
180
|
+
# Delete was successful.
|
181
|
+
return true
|
182
|
+
|
183
|
+
# Object not found.
|
184
|
+
return false
|
185
|
+
|
186
|
+
# Write an object back to the server.
|
187
|
+
save: (id, after_save = ->) ->
|
188
|
+
# Don't bother if the vault is locked, we're offline or there's nothing to sync.
|
189
|
+
if @locked
|
190
|
+
@errors.push 'Cannot save, vault is locked.'
|
191
|
+
return after_save()
|
192
|
+
else if not navigator.onLine
|
193
|
+
@errors.push 'Cannot save, navigator is offline.'
|
194
|
+
return after_save()
|
195
|
+
else if @dirty_object_count is 0
|
196
|
+
@errors.push 'Nothing to save.'
|
197
|
+
return after_save()
|
198
|
+
|
199
|
+
# Lock the vault until the save is complete.
|
200
|
+
@locked = true
|
201
|
+
|
202
|
+
# Find the object using the specified id.
|
203
|
+
object = @find(id)
|
204
|
+
|
205
|
+
switch object.status
|
206
|
+
when "deleted"
|
207
|
+
$.ajax
|
208
|
+
type: 'DELETE'
|
209
|
+
url: @urls.delete
|
210
|
+
data: @strip object
|
211
|
+
fixture: (settings) ->
|
212
|
+
return true
|
213
|
+
success: (data) =>
|
214
|
+
# Forcibly remove the deleted object from the collection.
|
215
|
+
for vault_object, index in @objects
|
216
|
+
if vault_object.id == object.id
|
217
|
+
@objects.splice(index, 1)
|
218
|
+
@dirty_object_count--
|
219
|
+
error: =>
|
220
|
+
@errors.push 'Failed to delete.'
|
221
|
+
complete: =>
|
222
|
+
# Store the collection, unlock the vault, and execute the callback method.
|
223
|
+
@store
|
224
|
+
@locked = false
|
225
|
+
after_save()
|
226
|
+
dataType: 'json'
|
227
|
+
when "new"
|
228
|
+
$.ajax
|
229
|
+
type: 'POST'
|
230
|
+
url: @urls.create
|
231
|
+
data: @strip object
|
232
|
+
fixture: (settings) =>
|
233
|
+
settings.data.id = @date.getTime()
|
234
|
+
|
235
|
+
return settings.data
|
236
|
+
success: (data) =>
|
237
|
+
# Replace the existing object with the new one from the server and extend it.
|
238
|
+
object = @extend data # This will also set its status to clean.
|
239
|
+
@dirty_object_count--
|
240
|
+
error: =>
|
241
|
+
@errors.push 'Failed to create.'
|
242
|
+
complete: =>
|
243
|
+
# Store the collection, unlock the vault, and execute the callback method.
|
244
|
+
@store
|
245
|
+
@locked = false
|
246
|
+
after_save()
|
247
|
+
dataType: 'json'
|
248
|
+
when "dirty"
|
249
|
+
$.ajax
|
250
|
+
type: 'POST'
|
251
|
+
url: @urls.update
|
252
|
+
data: @strip object
|
253
|
+
fixture: (settings) ->
|
254
|
+
return true
|
255
|
+
success: (data) =>
|
256
|
+
object.status = "clean"
|
257
|
+
@dirty_object_count--
|
258
|
+
error: =>
|
259
|
+
@errors.push 'Failed to update.'
|
260
|
+
complete: =>
|
261
|
+
# Store the collection, unlock the vault, and execute the callback method.
|
262
|
+
@store
|
263
|
+
@locked = false
|
264
|
+
after_save()
|
265
|
+
dataType: 'json'
|
266
|
+
|
267
|
+
# Used to wipe out the in-memory object list with a fresh one from the server.
|
268
|
+
reload: (after_load = ->) ->
|
269
|
+
# Don't bother if the vault is locked or we're offline.
|
270
|
+
if @locked
|
271
|
+
@errors.push 'Cannot reload, vault is locked.'
|
272
|
+
return after_load()
|
273
|
+
else if not navigator.onLine
|
274
|
+
@errors.push 'Cannot reload, navigator is offline.'
|
275
|
+
return after_load()
|
276
|
+
|
277
|
+
# Lock the vault until the reload is complete.
|
278
|
+
@locked = true
|
279
|
+
|
280
|
+
$.ajax
|
281
|
+
url: @urls.list
|
282
|
+
dataType: 'json'
|
283
|
+
success: (data) =>
|
284
|
+
# Replace the list of in-memory objects with the new data.
|
285
|
+
@objects = data
|
286
|
+
|
287
|
+
# Extend the objects with vault-specific variables and functions.
|
288
|
+
for object in @objects
|
289
|
+
@extend object
|
290
|
+
|
291
|
+
# Reset the count of dirty objects.
|
292
|
+
@dirty_object_count = 0
|
293
|
+
|
294
|
+
# Store the collection.
|
295
|
+
@store
|
296
|
+
|
297
|
+
# Call the callback function as the reload is complete.
|
298
|
+
after_load()
|
299
|
+
error: =>
|
300
|
+
@errors.push 'Failed to list.'
|
301
|
+
|
302
|
+
# Call the callback function as the reload is complete (albeit unsuccessful).
|
303
|
+
after_load()
|
304
|
+
complete: =>
|
305
|
+
# Unlock the vault as the reload is complete.
|
306
|
+
@locked = false
|
307
|
+
|
308
|
+
# Convenience method for saving and reloading in one shot.
|
309
|
+
synchronize: (after_sync = ->) ->
|
310
|
+
# Don't bother if we're offline.
|
311
|
+
unless navigator.onLine
|
312
|
+
@errors.push 'Cannot synchronize, navigator is offline.'
|
313
|
+
return after_sync()
|
314
|
+
|
315
|
+
@save =>
|
316
|
+
# Only reload the collection if there were no save errors.
|
317
|
+
if @errors.length is 0
|
318
|
+
@reload(after_sync)
|
319
|
+
else
|
320
|
+
after_sync()
|
321
|
+
|
322
|
+
# Load the collection from offline storage.
|
323
|
+
load: ->
|
324
|
+
# Don't bother if offline support is disabled.
|
325
|
+
unless @options.offline
|
326
|
+
return false
|
327
|
+
|
328
|
+
# Try to load the collection.
|
329
|
+
if localStorage.getItem(@name)
|
330
|
+
@objects = $.parseJSON(localStorage.getItem @name)
|
331
|
+
|
332
|
+
# Calculate the number of dirty objects.
|
333
|
+
for object in @objects
|
334
|
+
unless object.status == "clean"
|
335
|
+
@dirty_object_count++
|
336
|
+
|
337
|
+
return true
|
338
|
+
else
|
339
|
+
return false
|
340
|
+
|
341
|
+
# Store the collection for offline use.
|
342
|
+
store: ->
|
343
|
+
# Don't bother if offline support is disabled.
|
344
|
+
unless @options.offline
|
345
|
+
return false
|
346
|
+
|
347
|
+
# Store the collection.
|
348
|
+
localStorage.setItem(@name, JSON.stringify(@objects))
|
349
|
+
return true
|
350
|
+
|
351
|
+
# Extend an object with vault-specific variables and functions.
|
352
|
+
extend: (object, status="clean") ->
|
353
|
+
|
354
|
+
# Add simple variables and methods.
|
355
|
+
object.status = status
|
356
|
+
object.update = (attributes) =>
|
357
|
+
@update(attributes, object.id)
|
358
|
+
object.delete = =>
|
359
|
+
@delete(object.id)
|
360
|
+
object.save = (after_save) =>
|
361
|
+
@save(object.id, after_save)
|
362
|
+
|
363
|
+
# Iterate through all of the sub-collections, and if present
|
364
|
+
# extend them with some basic functionality.
|
365
|
+
for sub_collection in @options.sub_collections
|
366
|
+
do (sub_collection) =>
|
367
|
+
if object[sub_collection]?
|
368
|
+
# Find functionality.
|
369
|
+
object[sub_collection].find = (id) =>
|
370
|
+
for sub_collection_object in object[sub_collection]
|
371
|
+
if sub_collection_object[@options.id_attribute] is parseInt(id)
|
372
|
+
return sub_collection_object
|
373
|
+
|
374
|
+
# Object with specified id couldn't be found.
|
375
|
+
return false
|
376
|
+
|
377
|
+
# Add functionality.
|
378
|
+
object[sub_collection].add = (sub_object) =>
|
379
|
+
# Don't bother if the vault is locked.
|
380
|
+
if @locked
|
381
|
+
@errors.push 'Cannot add sub-object, vault is locked.'
|
382
|
+
return false
|
383
|
+
|
384
|
+
# Set a status on the object.
|
385
|
+
sub_object.status = "new"
|
386
|
+
|
387
|
+
# If the sub-object has no id, generate a temporary one and add it to the sub-object.
|
388
|
+
unless sub_object[@options.id_attribute]? and sub_object[@options.id_attribute] isnt ''
|
389
|
+
sub_object[@options.id_attribute] = @date.getTime()
|
390
|
+
|
391
|
+
# Add a delete method to the sub-object.
|
392
|
+
sub_object.delete = =>
|
393
|
+
object[sub_collection].delete(sub_object[@options.id_attribute])
|
394
|
+
|
395
|
+
# Add an update method to the sub-object.
|
396
|
+
sub_object.update = (attributes) =>
|
397
|
+
object[sub_collection].update(attributes, sub_object[@options.id_attribute])
|
398
|
+
|
399
|
+
# Add the object to the collection.
|
400
|
+
object[sub_collection].push sub_object
|
401
|
+
|
402
|
+
# If the root object was clean, flag it and increase the count of dirty objects.
|
403
|
+
if object.status is "clean"
|
404
|
+
object.status = "dirty"
|
405
|
+
@dirty_object_count++
|
406
|
+
|
407
|
+
# Store the collection.
|
408
|
+
@store
|
409
|
+
|
410
|
+
return sub_object
|
411
|
+
|
412
|
+
# Delete functionality.
|
413
|
+
object[sub_collection].delete = (id) =>
|
414
|
+
# Don't bother if the vault is locked.
|
415
|
+
if @locked
|
416
|
+
@errors.push 'Cannot delete sub-object, vault is locked.'
|
417
|
+
return false
|
418
|
+
|
419
|
+
# Remove the sub-object from its collection.
|
420
|
+
for sub_object, index in object[sub_collection]
|
421
|
+
if sub_object[@options.id_attribute] is id
|
422
|
+
object[sub_collection].splice(index, 1)
|
423
|
+
|
424
|
+
# If the root object was clean, flag it and increase the count of dirty objects.
|
425
|
+
if object.status is "clean"
|
426
|
+
object.status = "dirty"
|
427
|
+
@dirty_object_count++
|
428
|
+
|
429
|
+
# Store the collection.
|
430
|
+
@store
|
431
|
+
|
432
|
+
# Add a delete instance method for pre-existing objects.
|
433
|
+
for sub_object, index in object[sub_collection]
|
434
|
+
sub_object.delete = =>
|
435
|
+
object[sub_collection].delete(sub_object[@options.id_attribute])
|
436
|
+
|
437
|
+
# Update functionality.
|
438
|
+
object[sub_collection].update = (attributes, id) =>
|
439
|
+
# Don't bother if the vault is locked.
|
440
|
+
if @locked
|
441
|
+
@errors.push 'Cannot update sub-object, vault is locked.'
|
442
|
+
return false
|
443
|
+
|
444
|
+
# Get the id of the sub-object from the attributes if it's not explicitly defined.
|
445
|
+
id = attributes[@options.id_attribute] unless id?
|
446
|
+
|
447
|
+
# Get the sub-object; return if it's undefined.
|
448
|
+
sub_object = object[sub_collection].find(id)
|
449
|
+
unless sub_object?
|
450
|
+
@errors.push 'Cannot update, sub-object not found.'
|
451
|
+
return false
|
452
|
+
|
453
|
+
# If the root object was clean, flag it and increase the count of dirty objects.
|
454
|
+
if object.status is "clean"
|
455
|
+
object.status = "dirty"
|
456
|
+
@dirty_object_count++
|
457
|
+
|
458
|
+
# Merge in the updated attributes, if they're specified and defined on the sub-object.
|
459
|
+
if attributes?
|
460
|
+
for attribute, value of attributes
|
461
|
+
if sub_object[attribute]? and attribute isnt @options['id_attribute']
|
462
|
+
sub_object[attribute] = value
|
463
|
+
|
464
|
+
# Store the collection.
|
465
|
+
@store
|
466
|
+
|
467
|
+
# Add an update instance method for pre-existing objects.
|
468
|
+
for sub_object in object[sub_collection]
|
469
|
+
do (sub_object) =>
|
470
|
+
sub_object.update = (attributes) =>
|
471
|
+
object[sub_collection].update(attributes, sub_object[@options.id_attribute])
|
472
|
+
|
473
|
+
return object
|
474
|
+
|
475
|
+
# Return a copy of an object with vault-specific variables and functions removed.
|
476
|
+
strip: (object) ->
|
477
|
+
# Clone the object so that we don't strip the original.
|
478
|
+
object_clone = @clone object
|
479
|
+
|
480
|
+
# Remove the temporary id given to new objects.
|
481
|
+
if object_clone.status is "new"
|
482
|
+
delete object_clone[@options.id_attribute]
|
483
|
+
|
484
|
+
delete object_clone.status
|
485
|
+
delete object_clone.update
|
486
|
+
delete object_clone.delete
|
487
|
+
delete object_clone.save
|
488
|
+
|
489
|
+
# Iterate through all of the sub-collections, and if present
|
490
|
+
# strip them of their extended functionality.
|
491
|
+
for sub_collection in @options.sub_collections
|
492
|
+
if object_clone[sub_collection]?
|
493
|
+
# Remove the sub-collection's methods.
|
494
|
+
delete object_clone[sub_collection].find
|
495
|
+
delete object_clone[sub_collection].add
|
496
|
+
delete object_clone[sub_collection].delete
|
497
|
+
delete object_clone[sub_collection].update
|
498
|
+
|
499
|
+
# Iterate through and remove the extended instances' methods.
|
500
|
+
for sub_object in object_clone[sub_collection]
|
501
|
+
if sub_object.status is "new"
|
502
|
+
delete sub_object[@options.id_attribute]
|
503
|
+
delete sub_object.status
|
504
|
+
delete sub_object.delete
|
505
|
+
delete sub_object.update
|
506
|
+
return object_clone
|
507
|
+
|
508
|
+
# Clone (deep copy) an object.
|
509
|
+
clone: (object) ->
|
510
|
+
unless object? and typeof object is 'object'
|
511
|
+
return object
|
512
|
+
|
513
|
+
new_instance = new object.constructor()
|
514
|
+
|
515
|
+
for key of object
|
516
|
+
new_instance[key] = @clone object[key]
|
517
|
+
|
518
|
+
return new_instance
|
519
|
+
|
520
|
+
# Attach the Vault class to the window so that it can be used by other scripts.
|
521
|
+
window.Vault = this
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vault-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jordan MacDonald
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-21 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: Store and manage collections of objects without a connection.
|
15
|
+
email:
|
16
|
+
- jordan@wastedintelligence.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- Gemfile
|
23
|
+
- LICENSE
|
24
|
+
- Rakefile
|
25
|
+
- lib/vault-rails.rb
|
26
|
+
- lib/vault-rails/version.rb
|
27
|
+
- vault-rails.gemspec
|
28
|
+
- vendor/assets/javascripts/vault.js.coffee
|
29
|
+
homepage: https://github.com/cityofgreatersudbury/vault-rails
|
30
|
+
licenses: []
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project: vault-rails
|
49
|
+
rubygems_version: 1.8.11
|
50
|
+
signing_key:
|
51
|
+
specification_version: 3
|
52
|
+
summary: CoffeeScript collection class for offline use.
|
53
|
+
test_files: []
|