vault-rails 0.0.11 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +362 -0
  3. data/README.md +106 -0
  4. data/Rakefile +18 -2
  5. data/lib/vault/encrypted_model.rb +112 -0
  6. data/lib/vault/rails.rb +30 -0
  7. data/lib/vault/rails/version.rb +1 -1
  8. data/spec/dummy/Rakefile +6 -0
  9. data/spec/dummy/app/models/person.rb +8 -0
  10. data/spec/dummy/bin/bundle +3 -0
  11. data/spec/dummy/bin/rails +4 -0
  12. data/spec/dummy/bin/rake +4 -0
  13. data/spec/dummy/config.ru +4 -0
  14. data/spec/dummy/config/application.rb +29 -0
  15. data/spec/dummy/config/boot.rb +5 -0
  16. data/spec/dummy/config/database.yml +12 -0
  17. data/spec/dummy/config/environment.rb +5 -0
  18. data/spec/dummy/config/environments/development.rb +37 -0
  19. data/spec/dummy/config/environments/test.rb +39 -0
  20. data/spec/dummy/config/initializers/assets.rb +8 -0
  21. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  22. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  23. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  24. data/spec/dummy/config/initializers/inflections.rb +16 -0
  25. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  26. data/spec/dummy/config/initializers/session_store.rb +3 -0
  27. data/spec/dummy/config/initializers/vault.rb +9 -0
  28. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  29. data/spec/dummy/config/locales/en.yml +23 -0
  30. data/spec/dummy/config/routes.rb +3 -0
  31. data/spec/dummy/config/secrets.yml +22 -0
  32. data/spec/dummy/db/development.sqlite3 +0 -0
  33. data/spec/dummy/db/migrate/20150428220101_create_people.rb +11 -0
  34. data/spec/dummy/db/schema.rb +24 -0
  35. data/spec/dummy/log/development.log +124 -0
  36. data/spec/dummy/public/404.html +67 -0
  37. data/spec/dummy/public/422.html +67 -0
  38. data/spec/dummy/public/500.html +66 -0
  39. data/spec/dummy/public/favicon.ico +0 -0
  40. data/spec/integration/rails_spec.rb +27 -0
  41. data/spec/spec_helper.rb +24 -0
  42. data/spec/support/vault_server.rb +68 -0
  43. data/spec/unit/rails_spec.rb +28 -0
  44. metadata +178 -23
  45. data/lib/vault-rails.rb +0 -5
  46. data/lib/vault/rails/engine.rb +0 -8
  47. data/vendor/assets/javascripts/vault.js.coffee +0 -629
  48. data/vendor/assets/javascripts/vault/vault.js.coffee +0 -629
@@ -0,0 +1,68 @@
1
+ require "open-uri"
2
+ require "singleton"
3
+ require "timeout"
4
+ require "tempfile"
5
+
6
+ module RSpec
7
+ class VaultServer
8
+ include Singleton
9
+
10
+ def self.method_missing(m, *args, &block)
11
+ self.instance.public_send(m, *args, &block)
12
+ end
13
+
14
+ attr_reader :token
15
+
16
+ def initialize
17
+ io = Tempfile.new("vault-server")
18
+ pid = Process.spawn({}, "vault server -dev", out: io.to_i, err: io.to_i)
19
+
20
+ at_exit do
21
+ Process.kill("INT", pid)
22
+ Process.waitpid2(pid)
23
+
24
+ io.close
25
+ io.unlink
26
+ end
27
+
28
+ wait_for_ready do
29
+ output = ""
30
+
31
+ while
32
+ io.rewind
33
+ output = io.read
34
+ break if !output.empty?
35
+ end
36
+
37
+ if output.match(/Root Token: (.+)/)
38
+ @token = $1.strip
39
+ else
40
+ raise "Vault did not return a token!"
41
+ end
42
+ end
43
+ end
44
+
45
+ def address
46
+ "http://127.0.0.1:8200"
47
+ end
48
+
49
+ def wait_for_ready(&block)
50
+ Timeout.timeout(5) do
51
+ while
52
+ begin
53
+ open(address)
54
+ rescue SocketError, Errno::ECONNREFUSED, EOFError
55
+ rescue OpenURI::HTTPError => e
56
+ break if e.message =~ /404/
57
+ end
58
+
59
+ sleep(0.25)
60
+ end
61
+ end
62
+
63
+ yield
64
+ rescue Timeout::Error
65
+ raise "Vault did not start in 5 seconds!"
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ describe Vault do
4
+ describe ".application" do
5
+ it "returns the application" do
6
+ Vault.instance_variable_set(:@application, "test")
7
+ expect(Vault.application).to eq("test")
8
+ end
9
+
10
+ it "raises an error if unset" do
11
+ Vault.instance_variable_set(:@application, nil)
12
+ expect { Vault.application }.to raise_error
13
+ end
14
+ end
15
+
16
+ describe ".application=" do
17
+ it "sets the value" do
18
+ Vault.application = "test"
19
+ expect(Vault.instance_variable_get(:@application)).to eq("test")
20
+ end
21
+ end
22
+
23
+ describe "Rails" do
24
+ it "is defined" do
25
+ expect { Vault.const_get(:Rails) }.to_not raise_error
26
+ end
27
+ end
28
+ end
metadata CHANGED
@@ -1,51 +1,206 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vault-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
- - Jordan MacDonald
7
+ - Seth Vargo
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-01-05 00:00:00.000000000Z
13
- dependencies: []
14
- description: Store and manage collections of objects without a connection.
11
+ date: 2015-05-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: vault
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Official Vault plugin for Rails
15
98
  email:
16
- - jordan.macdonald@greatersudbury.ca
99
+ - sethvargo@gmail.com
17
100
  executables: []
18
101
  extensions: []
19
102
  extra_rdoc_files: []
20
103
  files:
21
- - lib/vault-rails.rb
22
- - lib/vault/rails/version.rb
23
- - lib/vault/rails/engine.rb
24
- - vendor/assets/javascripts/vault/vault.js.coffee
25
- - vendor/assets/javascripts/vault.js.coffee
104
+ - LICENSE
105
+ - README.md
26
106
  - Rakefile
27
- homepage: https://github.com/cityofgreatersudbury/vault-rails
28
- licenses: []
107
+ - lib/vault/encrypted_model.rb
108
+ - lib/vault/rails.rb
109
+ - lib/vault/rails/version.rb
110
+ - spec/dummy/Rakefile
111
+ - spec/dummy/app/models/person.rb
112
+ - spec/dummy/bin/bundle
113
+ - spec/dummy/bin/rails
114
+ - spec/dummy/bin/rake
115
+ - spec/dummy/config.ru
116
+ - spec/dummy/config/application.rb
117
+ - spec/dummy/config/boot.rb
118
+ - spec/dummy/config/database.yml
119
+ - spec/dummy/config/environment.rb
120
+ - spec/dummy/config/environments/development.rb
121
+ - spec/dummy/config/environments/test.rb
122
+ - spec/dummy/config/initializers/assets.rb
123
+ - spec/dummy/config/initializers/backtrace_silencers.rb
124
+ - spec/dummy/config/initializers/cookies_serializer.rb
125
+ - spec/dummy/config/initializers/filter_parameter_logging.rb
126
+ - spec/dummy/config/initializers/inflections.rb
127
+ - spec/dummy/config/initializers/mime_types.rb
128
+ - spec/dummy/config/initializers/session_store.rb
129
+ - spec/dummy/config/initializers/vault.rb
130
+ - spec/dummy/config/initializers/wrap_parameters.rb
131
+ - spec/dummy/config/locales/en.yml
132
+ - spec/dummy/config/routes.rb
133
+ - spec/dummy/config/secrets.yml
134
+ - spec/dummy/db/development.sqlite3
135
+ - spec/dummy/db/migrate/20150428220101_create_people.rb
136
+ - spec/dummy/db/schema.rb
137
+ - spec/dummy/log/development.log
138
+ - spec/dummy/public/404.html
139
+ - spec/dummy/public/422.html
140
+ - spec/dummy/public/500.html
141
+ - spec/dummy/public/favicon.ico
142
+ - spec/integration/rails_spec.rb
143
+ - spec/spec_helper.rb
144
+ - spec/support/vault_server.rb
145
+ - spec/unit/rails_spec.rb
146
+ homepage: https://github.com/hashicorp/vault-rails
147
+ licenses:
148
+ - MPLv3
149
+ metadata: {}
29
150
  post_install_message:
30
151
  rdoc_options: []
31
152
  require_paths:
32
153
  - lib
33
154
  required_ruby_version: !ruby/object:Gem::Requirement
34
- none: false
35
155
  requirements:
36
- - - ! '>='
156
+ - - ">="
37
157
  - !ruby/object:Gem::Version
38
158
  version: '0'
39
159
  required_rubygems_version: !ruby/object:Gem::Requirement
40
- none: false
41
160
  requirements:
42
- - - ! '>='
161
+ - - ">="
43
162
  - !ruby/object:Gem::Version
44
163
  version: '0'
45
164
  requirements: []
46
- rubyforge_project: vault-rails
47
- rubygems_version: 1.8.10
165
+ rubyforge_project:
166
+ rubygems_version: 2.2.3
48
167
  signing_key:
49
- specification_version: 3
50
- summary: CoffeeScript collection class for offline use.
51
- test_files: []
168
+ specification_version: 4
169
+ summary: Official Vault plugin for Rails
170
+ test_files:
171
+ - spec/dummy/app/models/person.rb
172
+ - spec/dummy/bin/bundle
173
+ - spec/dummy/bin/rails
174
+ - spec/dummy/bin/rake
175
+ - spec/dummy/config/application.rb
176
+ - spec/dummy/config/boot.rb
177
+ - spec/dummy/config/database.yml
178
+ - spec/dummy/config/environment.rb
179
+ - spec/dummy/config/environments/development.rb
180
+ - spec/dummy/config/environments/test.rb
181
+ - spec/dummy/config/initializers/assets.rb
182
+ - spec/dummy/config/initializers/backtrace_silencers.rb
183
+ - spec/dummy/config/initializers/cookies_serializer.rb
184
+ - spec/dummy/config/initializers/filter_parameter_logging.rb
185
+ - spec/dummy/config/initializers/inflections.rb
186
+ - spec/dummy/config/initializers/mime_types.rb
187
+ - spec/dummy/config/initializers/session_store.rb
188
+ - spec/dummy/config/initializers/vault.rb
189
+ - spec/dummy/config/initializers/wrap_parameters.rb
190
+ - spec/dummy/config/locales/en.yml
191
+ - spec/dummy/config/routes.rb
192
+ - spec/dummy/config/secrets.yml
193
+ - spec/dummy/config.ru
194
+ - spec/dummy/db/development.sqlite3
195
+ - spec/dummy/db/migrate/20150428220101_create_people.rb
196
+ - spec/dummy/db/schema.rb
197
+ - spec/dummy/log/development.log
198
+ - spec/dummy/public/404.html
199
+ - spec/dummy/public/422.html
200
+ - spec/dummy/public/500.html
201
+ - spec/dummy/public/favicon.ico
202
+ - spec/dummy/Rakefile
203
+ - spec/integration/rails_spec.rb
204
+ - spec/spec_helper.rb
205
+ - spec/support/vault_server.rb
206
+ - spec/unit/rails_spec.rb
data/lib/vault-rails.rb DELETED
@@ -1,5 +0,0 @@
1
- module Vault
2
- module Rails
3
- require 'vault/rails/engine' if defined?(Rails)
4
- end
5
- end
@@ -1,8 +0,0 @@
1
- require 'rails'
2
-
3
- module Vault
4
- module Rails
5
- class Engine < ::Rails::Engine
6
- end
7
- end
8
- end
@@ -1,629 +0,0 @@
1
- class Vault
2
- constructor: (@name, @urls, options = {}) ->
3
- # Setup some internal variables.
4
- @objects = []
5
- @dirty_object_count = 0
6
- @save_error_count = 0
7
- @messages =
8
- notices: []
9
- warnings: []
10
- errors: []
11
-
12
- # This property is used to temporarily lock the vault during mutation methods.
13
- @locked = false
14
-
15
- # Declare default options.
16
- @options =
17
- autoload: true
18
- after_load: ->
19
- id_attribute: "id"
20
- offline: false
21
- sub_collections: []
22
-
23
- # Declare an array used to track ids that are in use,
24
- # so as to prevent duplicates when generating new ones.
25
- @ids_in_use = []
26
-
27
- # Merge default options with user-defined ones.
28
- for option, value of options
29
- @options[option] = value
30
-
31
- # Setup the vault for offline use.
32
- if @options.offline
33
- # Bind a cache routine to save data should the window be closed or url changed.
34
- $(window).unload =>
35
- @store()
36
-
37
- # Load the collection if configured to do so.
38
- if @options.autoload
39
- # Check the offline data store first, if configured to do so.
40
- if @options.offline
41
- if @load()
42
- if @dirty_object_count > 0
43
- # Offline data loaded and modifications found; keep existing data.
44
- @messages.notices.push "Found and using dirty offline data."
45
-
46
- # Detach the callback to after_load so that the call to the
47
- # vault constructor can complete/return, allowing any post-load code
48
- # to use the newly instantiated vault object as required.
49
- window.setTimeout @options.after_load, 100
50
- else
51
- # No modifications in offline data; reload fresh data.
52
- @messages.notices.push "No modifications found in offline data. Reloading..."
53
-
54
- if @urls.list?
55
- @reload(@options.after_load)
56
- else
57
- # Can't reload without a list url; use the offline data we've loaded.
58
- @messages.notices.push "List url not configured; using offline data instead."
59
-
60
- # Detach the callback to after_load so that the call to the
61
- # vault constructor can complete/return, allowing any post-load code
62
- # to use the newly instantiated vault object as required.
63
- window.setTimeout @options.after_load, 100
64
- else
65
- if navigator.onLine
66
- # Load failed, but we're connected; reload fresh data.
67
- @messages.warnings.push "Offline data load failed. Reloading..."
68
-
69
- if @urls.list?
70
- @reload(@options.after_load)
71
- else
72
- # Can't reload without a list url; use an empty dataset.
73
- @messages.warnings.push "List url not configured; using empty dataset instead."
74
-
75
- # Detach the callback to after_load so that the call to the
76
- # vault constructor can complete/return, allowing any post-load code
77
- # to use the newly instantiated vault object as required.
78
- window.setTimeout @options.after_load, 100
79
- else
80
- # Load failed and we're offline; use an empty dataset.
81
- @messages.warnings.push "Browser is offline and cannot reload; using empty dataset instead."
82
-
83
- # Detach the callback to after_load so that the call to the
84
- # vault constructor can complete/return, allowing any post-load code
85
- # to use the newly instantiated vault object as required.
86
- window.setTimeout @options.after_load, 100
87
- else
88
- # Not using offline data; reload fresh data.
89
- @messages.notices.push "Not configured for offline data. Reloading..."
90
-
91
- if @urls.list?
92
- @reload(@options.after_load)
93
- else
94
- # Can't reload without a list url; use an empty dataset.
95
- @messages.notices.push "List url not configured; using empty dataset instead."
96
-
97
- # Detach the callback to after_load so that the call to the
98
- # vault constructor can complete/return, allowing any post-load code
99
- # to use the newly instantiated vault object as required.
100
- window.setTimeout @options.after_load, 100
101
-
102
- # Create convenience attributes for sub-collections.
103
- for sub_collection in @options.sub_collections
104
- do (sub_collection) =>
105
- @[sub_collection] = {
106
- 'find': (id) =>
107
- for object in @objects
108
- for sub_object in object[sub_collection]
109
- if sub_object[@options.id_attribute].toString() is id.toString()
110
- return sub_object
111
-
112
- # Object with specified id couldn't be found.
113
- return false
114
- }
115
-
116
- # Iterate over non-deleted items in the collection.
117
- each: (logic) ->
118
- for object in @objects
119
- unless object.status == "deleted"
120
- logic object
121
-
122
- # Add a new item to the collection.
123
- add: (object) ->
124
- # Don't bother if the vault is locked.
125
- if @locked
126
- @messages.errors.push 'Cannot add, vault is locked.'
127
- return false
128
-
129
- # If the object has no id, generate a temporary one and add it to the object.
130
- unless object[@options.id_attribute]? and object[@options.id_attribute] isnt ''
131
- object[@options.id_attribute] = @generate_id()
132
-
133
- # Extend the object with vault-specific variables and functions.
134
- @extend object,"new"
135
-
136
- # Add the object to the collection.
137
- @objects.push object
138
-
139
- # Increase the count of dirty objects.
140
- @dirty_object_count++
141
-
142
- # Store the collection.
143
- @store
144
-
145
- # Return the extended object.
146
- return object
147
-
148
- # Find an object in the collection using its id.
149
- find: (id) ->
150
- for object in @objects
151
- if object[@options.id_attribute].toString() is id.toString()
152
- return object
153
-
154
- # Object with specified id couldn't be found.
155
- return false
156
-
157
- # Update an existing item in the collection.
158
- update: (attributes, id) ->
159
- # Don't bother if the vault is locked.
160
- if @locked
161
- @messages.errors.push 'Cannot update, vault is locked.'
162
- return false
163
-
164
- # Get the id of the object from the attributes if it's not explicitly defined.
165
- id = attributes[@options.id_attribute] unless id?
166
-
167
- # Get the object; return if it's undefined.
168
- object = @find(id)
169
- unless object?
170
- @messages.errors.push 'Cannot update, object not found.'
171
- return false
172
-
173
- # Flag it as dirty.
174
- if object.status is "clean"
175
- object.status = "dirty"
176
- @dirty_object_count++
177
-
178
- # Merge in the updated attributes, if they're specified and defined on the object.
179
- if attributes?
180
- for attribute, value of attributes
181
- if object[attribute]?
182
- object[attribute] = value
183
-
184
- # Store the collection.
185
- @store
186
-
187
- # Update was successful.
188
- return true
189
-
190
- # Flag an object in the collection for deletion,
191
- # or if the object is new, remove it.
192
- delete: (id) ->
193
- # Don't bother if the vault is locked.
194
- if @locked
195
- @messages.errors.push 'Cannot delete, vault is locked.'
196
- return false
197
-
198
- for object, index in @objects
199
- if object[@options.id_attribute] == id
200
- switch object.status
201
- when "new"
202
- # New objects are special; we essentially want to
203
- # reverse the steps taken during the add operation.
204
- @objects.splice(index, 1)
205
- @dirty_object_count--
206
- when "clean"
207
- object.status = "deleted"
208
- @dirty_object_count++
209
- when "dirty"
210
- object.status = "deleted"
211
-
212
- # Store the collection.
213
- @store
214
-
215
- # Delete was successful.
216
- return true
217
-
218
- # Object not found.
219
- return false
220
-
221
- # Forcibly remove an object from the collection.
222
- destroy: (id) ->
223
- # Don't bother if the vault is locked.
224
- if @locked
225
- @messages.errors.push 'Cannot delete, vault is locked.'
226
- return false
227
-
228
- for object, index in @objects
229
- if object[@options.id_attribute] == id
230
- # Remove the object from the collection.
231
- @objects.splice(index, 1)
232
-
233
- # Reduce the dirty count if this object
234
- # was dirty, since we're no longer managing it.
235
- switch object.status
236
- when "new", "dirty"
237
- @dirty_object_count--
238
-
239
- # Store the collection.
240
- @store
241
-
242
- # Destroy was successful.
243
- return true
244
-
245
- # Object not found.
246
- return false
247
-
248
- # Write an object back to the server.
249
- save: (id, after_save = ->) ->
250
- # Don't bother if the vault is locked, we're offline or there's nothing to sync.
251
- if @locked
252
- @messages.errors.push 'Cannot save, vault is locked.'
253
- return after_save()
254
- else if not navigator.onLine
255
- @messages.errors.push 'Cannot save, navigator is offline.'
256
- return after_save()
257
- else if @dirty_object_count is 0
258
- @messages.errors.push 'Nothing to save.'
259
- return after_save()
260
-
261
- # Lock the vault until the save is complete.
262
- @locked = true
263
-
264
- # Find the object using the specified id.
265
- object = @find(id)
266
-
267
- # Package up the object to be sent to the server.
268
- packaged_object = {}
269
- packaged_object[@name] = JSON.stringify @strip object
270
-
271
- switch object.status
272
- when "deleted"
273
- $.ajax
274
- type: 'DELETE'
275
- url: @urls.delete
276
- data: packaged_object
277
- fixture: (settings) ->
278
- return true
279
- success: (data) =>
280
- # Forcibly remove the deleted object from the collection.
281
- for vault_object, index in @objects
282
- if vault_object.id == object.id
283
- @objects.splice(index, 1)
284
- @dirty_object_count--
285
- error: =>
286
- @messages.errors.push 'Failed to delete.'
287
- complete: =>
288
- # Store the collection, unlock the vault, and execute the callback method.
289
- @store
290
- @locked = false
291
- after_save()
292
- dataType: 'json'
293
- when "new"
294
- $.ajax
295
- type: 'POST'
296
- url: @urls.create
297
- data: packaged_object
298
- fixture: (settings) =>
299
- return {
300
- id: 123
301
- make: "Dodge",
302
- model: "Viper SRT-10",
303
- year: 2008}
304
- success: (data) =>
305
- # Unlock the vault prematurely so that we can update it.
306
- @locked = false
307
-
308
- # Update the object with the attributes sent from the server.
309
- object.update data, object.id
310
-
311
- object.status = "clean"
312
- @dirty_object_count--
313
- error: =>
314
- @messages.errors.push 'Failed to create.'
315
- complete: =>
316
- # Store the collection, unlock the vault, and execute the callback method.
317
- @store
318
- @locked = false
319
- after_save()
320
- dataType: 'json'
321
- when "dirty"
322
- $.ajax
323
- type: 'POST'
324
- url: @urls.update
325
- data: packaged_object
326
- fixture: (settings) ->
327
- return true
328
- success: (data) =>
329
- object.status = "clean"
330
- @dirty_object_count--
331
- error: =>
332
- @messages.errors.push 'Failed to update.'
333
- complete: =>
334
- # Store the collection, unlock the vault, and execute the callback method.
335
- @store
336
- @locked = false
337
- after_save()
338
- dataType: 'json'
339
-
340
- # Used to wipe out the in-memory object list with a fresh one from the server.
341
- reload: (after_load = ->) ->
342
- # Don't bother if the vault is locked or we're offline.
343
- if @locked
344
- @messages.errors.push 'Cannot reload, vault is locked.'
345
- return after_load()
346
- else if not navigator.onLine
347
- @messages.errors.push 'Cannot reload, navigator is offline.'
348
- return after_load()
349
- else if not @urls.list?
350
- @messages.errors.push 'Cannot reload, list url is not configured.'
351
- return after_load()
352
-
353
- # Lock the vault until the reload is complete.
354
- @locked = true
355
-
356
- $.ajax
357
- url: @urls.list
358
- dataType: 'json'
359
- success: (data) =>
360
- # Replace the list of in-memory objects with the new data.
361
- @objects = data
362
-
363
- # Extend the objects with vault-specific variables and functions.
364
- for object in @objects
365
- @extend object
366
-
367
- # Reset the count of dirty objects.
368
- @dirty_object_count = 0
369
-
370
- # Store the collection.
371
- @store
372
-
373
- # Call the callback function as the reload is complete.
374
- after_load()
375
- error: =>
376
- @messages.errors.push 'Failed to list.'
377
-
378
- # Call the callback function as the reload is complete (albeit unsuccessful).
379
- after_load()
380
- complete: =>
381
- # Unlock the vault as the reload is complete.
382
- @locked = false
383
-
384
- # Convenience method for saving and reloading in one shot.
385
- synchronize: (after_sync = ->) ->
386
- # Don't bother if we're offline.
387
- unless navigator.onLine
388
- @messages.errors.push 'Cannot synchronize, navigator is offline.'
389
- return after_sync()
390
-
391
- @save =>
392
- # Only reload the collection if there were no save errors.
393
- if @messages.errors.length is 0
394
- @reload(after_sync)
395
- else
396
- after_sync()
397
-
398
- # Load the collection from offline storage.
399
- load: ->
400
- # Don't bother if offline support is disabled.
401
- unless @options.offline
402
- return false
403
-
404
- # Try to load the collection.
405
- if localStorage.getItem(@name)
406
- @objects = $.parseJSON(localStorage.getItem @name)
407
-
408
- # Extend the loaded objects with vault-specific variables and functions.
409
- for object in @objects
410
- @extend object
411
-
412
- # Calculate the number of dirty objects.
413
- for object in @objects
414
- unless object.status is "clean"
415
- @dirty_object_count++
416
-
417
- return true
418
- else
419
- return false
420
-
421
- # Store the collection for offline use.
422
- store: ->
423
- # Don't bother if offline support is disabled.
424
- unless @options.offline
425
- return false
426
-
427
- # Store the collection.
428
- localStorage.setItem(@name, JSON.stringify(@objects))
429
- return true
430
-
431
- # Extend an object with vault-specific variables and functions.
432
- extend: (object, status) ->
433
- # Validate the status argument.
434
- if status?
435
- throw "Invalid status specified: cannot extend object." unless status in ['clean', 'dirty', 'new']
436
-
437
- # Add simple variables and methods.
438
- object.update = (attributes) =>
439
- @update(attributes, object.id)
440
- object.delete = =>
441
- @delete(object.id)
442
- object.destroy = =>
443
- @destroy(object.id)
444
- object.save = (after_save) =>
445
- @save(object.id, after_save)
446
-
447
- if status?
448
- # Status has been explicitly defined; force it on the object.
449
- object.status = status
450
- else
451
- # Default the object's status to clean if it doesn't exist.
452
- object.status = "clean" unless object.status?
453
-
454
- # Iterate through all of the sub-collections, and if present
455
- # extend them with some basic functionality.
456
- for sub_collection in @options.sub_collections
457
- do (sub_collection) =>
458
- if object[sub_collection]?
459
- # Find functionality.
460
- object[sub_collection].find = (id) =>
461
- for sub_collection_object in object[sub_collection]
462
- if sub_collection_object[@options.id_attribute].toString() is id.toString()
463
- return sub_collection_object
464
-
465
- # Object with specified id couldn't be found.
466
- return false
467
-
468
- # Add functionality.
469
- object[sub_collection].add = (sub_object) =>
470
- # Don't bother if the vault is locked.
471
- if @locked
472
- @messages.errors.push 'Cannot add sub-object, vault is locked.'
473
- return false
474
-
475
- # Set a status on the object.
476
- sub_object.status = "new"
477
-
478
- # If the sub-object has no id, generate a temporary one and add it to the sub-object.
479
- unless sub_object[@options.id_attribute]? and sub_object[@options.id_attribute] isnt ''
480
- sub_object[@options.id_attribute] = @generate_id()
481
-
482
- # Add a delete method to the sub-object.
483
- sub_object.delete = =>
484
- object[sub_collection].delete(sub_object[@options.id_attribute])
485
-
486
- # Add an update method to the sub-object.
487
- sub_object.update = (attributes) =>
488
- object[sub_collection].update(attributes, sub_object[@options.id_attribute])
489
-
490
- # Add the object to the collection.
491
- object[sub_collection].push sub_object
492
-
493
- # If the root object was clean, flag it and increase the count of dirty objects.
494
- if object.status is "clean"
495
- object.status = "dirty"
496
- @dirty_object_count++
497
-
498
- # Store the collection.
499
- @store
500
-
501
- return sub_object
502
-
503
- # Delete functionality.
504
- object[sub_collection].delete = (id) =>
505
- # Don't bother if the vault is locked.
506
- if @locked
507
- @messages.errors.push 'Cannot delete sub-object, vault is locked.'
508
- return false
509
-
510
- # Remove the sub-object from its collection.
511
- for sub_object, index in object[sub_collection]
512
- if sub_object[@options.id_attribute] is id
513
- object[sub_collection].splice(index, 1)
514
-
515
- # If the root object was clean, flag it and increase the count of dirty objects.
516
- if object.status is "clean"
517
- object.status = "dirty"
518
- @dirty_object_count++
519
-
520
- # Store the collection.
521
- @store
522
-
523
- # Add a delete instance method for pre-existing objects.
524
- for sub_object, index in object[sub_collection]
525
- sub_object.delete = =>
526
- object[sub_collection].delete(sub_object[@options.id_attribute])
527
-
528
- # Update functionality.
529
- object[sub_collection].update = (attributes, id) =>
530
- # Don't bother if the vault is locked.
531
- if @locked
532
- @messages.errors.push 'Cannot update sub-object, vault is locked.'
533
- return false
534
-
535
- # Get the id of the sub-object from the attributes if it's not explicitly defined.
536
- id = attributes[@options.id_attribute] unless id?
537
-
538
- # Get the sub-object; return if it's undefined.
539
- sub_object = object[sub_collection].find(id)
540
- unless sub_object?
541
- @messages.errors.push 'Cannot update, sub-object not found.'
542
- return false
543
-
544
- # If the root object was clean, flag it and increase the count of dirty objects.
545
- if object.status is "clean"
546
- object.status = "dirty"
547
- @dirty_object_count++
548
-
549
- # Merge in the updated attributes, if they're specified and defined on the sub-object.
550
- if attributes?
551
- for attribute, value of attributes
552
- if sub_object[attribute]?
553
- sub_object[attribute] = value
554
-
555
- # Store the collection.
556
- @store
557
-
558
- # Add an update instance method for pre-existing objects.
559
- for sub_object in object[sub_collection]
560
- do (sub_object) =>
561
- sub_object.update = (attributes) =>
562
- object[sub_collection].update(attributes, sub_object[@options.id_attribute])
563
-
564
- return object
565
-
566
- # Return a copy of an object with vault-specific variables and functions removed.
567
- strip: (object) ->
568
- # Clone the object so that we don't strip the original.
569
- object_clone = @clone object
570
-
571
- # Remove the temporary id given to new objects.
572
- if object_clone.status is "new"
573
- delete object_clone[@options.id_attribute]
574
-
575
- # Remove vault object methods.
576
- delete object_clone.status
577
- delete object_clone.update
578
- delete object_clone.delete
579
- delete object_clone.destroy
580
- delete object_clone.save
581
-
582
- # Iterate through all of the sub-collections, and if present
583
- # strip them of their extended functionality.
584
- for sub_collection in @options.sub_collections
585
- if object_clone[sub_collection]?
586
- # Remove the sub-collection's methods.
587
- delete object_clone[sub_collection].find
588
- delete object_clone[sub_collection].add
589
- delete object_clone[sub_collection].delete
590
- delete object_clone[sub_collection].update
591
-
592
- # Iterate through and remove the extended instances' methods.
593
- for sub_object in object_clone[sub_collection]
594
- if sub_object.status is "new"
595
- delete sub_object[@options.id_attribute]
596
- delete sub_object.status
597
- delete sub_object.delete
598
- delete sub_object.update
599
- return object_clone
600
-
601
- # Clone (deep copy) an object.
602
- clone: (object) ->
603
- unless object? and typeof object is 'object'
604
- return object
605
-
606
- new_instance = new object.constructor()
607
-
608
- for key of object
609
- new_instance[key] = @clone object[key]
610
-
611
- return new_instance
612
-
613
- # Generate a new unique (within the set of generated ids) id.
614
- generate_id: ->
615
- until id_is_available
616
- # Generate a new id and assume it's available.
617
- id = new Date().getTime()
618
- id_is_available = true
619
-
620
- # Flag the new id as unavailable if it's taken.
621
- id_is_available = false for taken in @ids_in_use when id is taken
622
-
623
- # Store the new id so that it's not used again.
624
- @ids_in_use.push id
625
-
626
- return id
627
-
628
- # Attach the Vault class to the window so that it can be used by other scripts.
629
- window.Vault = this