waz-storage 1.2.0 → 1.3.0

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.
Files changed (40) hide show
  1. data/.gitignore +9 -9
  2. data/CHANGELOG.rdoc +72 -72
  3. data/Gemfile +4 -4
  4. data/Gemfile.lock +46 -40
  5. data/LICENSE +18 -18
  6. data/README.rdoc +310 -310
  7. data/lib/waz-blobs.rb +4 -4
  8. data/lib/waz-queues.rb +6 -6
  9. data/lib/waz-storage.rb +39 -39
  10. data/lib/waz-tables.rb +4 -4
  11. data/lib/waz/blobs/blob_object.rb +122 -122
  12. data/lib/waz/blobs/container.rb +172 -161
  13. data/lib/waz/blobs/exceptions.rb +10 -10
  14. data/lib/waz/blobs/service.rb +181 -156
  15. data/lib/waz/queues/exceptions.rb +28 -28
  16. data/lib/waz/queues/message.rb +64 -64
  17. data/lib/waz/queues/queue.rb +164 -164
  18. data/lib/waz/queues/service.rb +105 -105
  19. data/lib/waz/storage/base.rb +70 -70
  20. data/lib/waz/storage/exceptions.rb +33 -33
  21. data/lib/waz/storage/validation_rules.rb +25 -25
  22. data/lib/waz/tables/edm_type_helper.rb +44 -44
  23. data/lib/waz/tables/exceptions.rb +44 -44
  24. data/lib/waz/tables/service.rb +178 -178
  25. data/lib/waz/tables/table.rb +74 -74
  26. data/lib/waz/tables/table_array.rb +10 -10
  27. data/rakefile +8 -21
  28. data/{tests → spec}/configuration.rb +22 -22
  29. data/{tests/waz/blobs/blob_object_test.rb → spec/waz/blobs/blob_object_spec.rb} +80 -80
  30. data/{tests/waz/blobs/container_test.rb → spec/waz/blobs/container_spec.rb} +175 -162
  31. data/{tests/waz/blobs/service_test.rb → spec/waz/blobs/service_spec.rb} +336 -282
  32. data/{tests/waz/queues/message_test.rb → spec/waz/queues/message_spec.rb} +32 -32
  33. data/{tests/waz/queues/queue_test.rb → spec/waz/queues/queue_spec.rb} +205 -205
  34. data/{tests/waz/queues/service_test.rb → spec/waz/queues/service_spec.rb} +298 -298
  35. data/{tests → spec}/waz/storage/base_tests.rb +81 -81
  36. data/{tests/waz/storage/shared_key_core_service_test.rb → spec/waz/storage/shared_key_core_service_spec.rb} +141 -141
  37. data/{tests/waz/tables/service_test.rb → spec/waz/tables/service_spec.rb} +613 -613
  38. data/{tests/waz/tables/table_test.rb → spec/waz/tables/table_spec.rb} +97 -97
  39. data/waz-storage.gemspec +29 -27
  40. metadata +47 -26
data/.gitignore CHANGED
@@ -1,9 +1,9 @@
1
- tests/waz/func/*.rb
2
- coverage
3
- pkg/*
4
- rdoc/*
5
- sdk/*
6
- sdk
7
- .rvmrc
8
- vendor/
9
- .bundle
1
+ tests/waz/func/*.rb
2
+ coverage
3
+ pkg/*
4
+ rdoc/*
5
+ sdk/*
6
+ sdk
7
+ .rvmrc
8
+ vendor/
9
+ .bundle
data/CHANGELOG.rdoc CHANGED
@@ -1,72 +1,72 @@
1
- = Revision History for Windows Azure Storage for Ruby
2
- === v1.1 - (smarx)
3
- - Upload from stream, plus XML/URI escaping in various places, and a few other minor fixes
4
- - Add upload method to container
5
- - Add put_block_list to support upload
6
- - Fix message canonicalization of ?comp= parameters for versions prior to 2009-09-19 (seems put_block was broken)
7
- - Unescape query string parameters when constructing signatures (needed for put_block, since some base64-encoded names include non-URI-friendly characters)
8
- - XML-escape property values for table entities (needed to insert things containing &, <, etc.)
9
- - Remove Unicode characters from rakefile (was breaking something on Windows)
10
- - Add tests for put_block_list and upload
11
- - Fix tests for content type (typo, = instead of .should ==)
12
- - Fix tests using RestClient.beautify_headers (expects an array, not a scalar)
13
-
14
- === v1.0.6 - (hermes.logicalbricks)
15
- - Update tests to rspec 2.5.0
16
- - Allow authorization using only SharedAccessSignature
17
-
18
- === v1.0.5 - (tomconte)
19
- - Fixed for list_blobs as it wasn't fully merged to 2009-09-19. Now it works without 403 exceptions
20
-
21
- === v1.0.3 - (Johnny Halife)
22
- - Merged with sriramk fix for loading path issues on some environments
23
-
24
- === v1.0.2 - (Johnny Halife)
25
- - Completed Blobs API migration to 2009-09-19, _fully supporting_ what third-party tools does (e.g. Cerebrata) [thanks percent20]
26
-
27
- === v1.0.1 - (Johnny halife)
28
- - Added syntax's sugar for ensuring a queue (get or create)
29
-
30
- === v1.0 - (Juan Pablo Garcia Dalolla)
31
- - Added support for table service to query, get_one, insert, update, merge and delete entities.
32
- - Added support for running against the Storage Developement Fabriq shipped with Microsoft SDK.
33
- - Added signature support for Tables service according to msdn.microsoft.com/en-us/library/dd179428.aspx
34
- - Added support to enumerate, create, and delete tables on give storage account.
35
- - Improved support for stacked connection management.
36
-
37
- === v0.5.81 - (Johnny Halife)
38
- - When simulating a container using a forward slash starting the blob name it crashed with 404, now it's fixed.
39
-
40
- === v0.5.8 - (Johnny Halife)
41
- - Fixed an issue on the Queues Documentation (thanks @ezequielm)
42
- - Added _naming validation for Containers_ according to http://msdn.microsoft.com/en-us/library/dd135715.aspx (thanks @panosjee)
43
- - Added _naming validation for Queues_ according to http://msdn.microsoft.com/en-us/library/dd179349.aspx (thanks @panosjee)
44
- - Added _support for Blob snapshots_
45
- - Added support for rest-client 1.3.0. (thanks @jpgd)
46
-
47
- === v0.5.7 - (Johnny Halife)
48
- - Fixes a critical bug on URL management that some time prevents message from being deleted, caused by a missing encoding on the URL parameters.
49
-
50
- === v0.5.6 - (Johnny Halife)
51
- - Added signature support for 2009-09-19 Version of the Storage API
52
- - Queues API has been migrated to the 2009-09-19 Version of the Storage API
53
- - Added a new parameter for listing queues with metadata
54
- - Added support for DequeueCount on messages being retrieved from the Queue
55
- - <strong>Known Issue</strong>: Creating a queue multiple times with same metadata throws 409.
56
-
57
- === v0.5.5 - (Johnny Halife)
58
- - Added a Change Log
59
- - Added support for copy-blob [WAZ::Blobs::BlobObject.copy("container/blob_name")]
60
- - Refactored out the whole request logic to an _execute_ method on the SharedKeyCoreService
61
- - Added transparent support for block management (users don't know if their blob is being uploaded chunked or not)
62
- - Added support for a blob to list its blocks
63
-
64
- === v0.5.4 - Sat Oct 17 19:35:48 2009 -0300 (Johnny Halife)
65
- - Fixed method name for String.start_with? (previously starts_with?)
66
-
67
- === v0.5.3 - Sat Oct 17 18:20:51 2009 -0300 (Johnny Halife)
68
- - Added the stack-based contextual connection handling (WAZ::Storage::Base.establish_connection {|block| }
69
- - Added string extensions for non rails environments (and work to work on heroku) String.starts_with?
70
-
71
- === v0.5.2 - Thu Oct 15 22:08:01 2009 -0300 (Johnny Halife)
72
- - Initial Version
1
+ = Revision History for Windows Azure Storage for Ruby
2
+ === v1.1 - (smarx)
3
+ - Upload from stream, plus XML/URI escaping in various places, and a few other minor fixes
4
+ - Add upload method to container
5
+ - Add put_block_list to support upload
6
+ - Fix message canonicalization of ?comp= parameters for versions prior to 2009-09-19 (seems put_block was broken)
7
+ - Unescape query string parameters when constructing signatures (needed for put_block, since some base64-encoded names include non-URI-friendly characters)
8
+ - XML-escape property values for table entities (needed to insert things containing &, <, etc.)
9
+ - Remove Unicode characters from rakefile (was breaking something on Windows)
10
+ - Add tests for put_block_list and upload
11
+ - Fix tests for content type (typo, = instead of .should ==)
12
+ - Fix tests using RestClient.beautify_headers (expects an array, not a scalar)
13
+
14
+ === v1.0.6 - (hermes.logicalbricks)
15
+ - Update tests to rspec 2.5.0
16
+ - Allow authorization using only SharedAccessSignature
17
+
18
+ === v1.0.5 - (tomconte)
19
+ - Fixed for list_blobs as it wasn't fully merged to 2009-09-19. Now it works without 403 exceptions
20
+
21
+ === v1.0.3 - (Johnny Halife)
22
+ - Merged with sriramk fix for loading path issues on some environments
23
+
24
+ === v1.0.2 - (Johnny Halife)
25
+ - Completed Blobs API migration to 2009-09-19, _fully supporting_ what third-party tools does (e.g. Cerebrata) [thanks percent20]
26
+
27
+ === v1.0.1 - (Johnny halife)
28
+ - Added syntax's sugar for ensuring a queue (get or create)
29
+
30
+ === v1.0 - (Juan Pablo Garcia Dalolla)
31
+ - Added support for table service to query, get_one, insert, update, merge and delete entities.
32
+ - Added support for running against the Storage Developement Fabriq shipped with Microsoft SDK.
33
+ - Added signature support for Tables service according to msdn.microsoft.com/en-us/library/dd179428.aspx
34
+ - Added support to enumerate, create, and delete tables on give storage account.
35
+ - Improved support for stacked connection management.
36
+
37
+ === v0.5.81 - (Johnny Halife)
38
+ - When simulating a container using a forward slash starting the blob name it crashed with 404, now it's fixed.
39
+
40
+ === v0.5.8 - (Johnny Halife)
41
+ - Fixed an issue on the Queues Documentation (thanks @ezequielm)
42
+ - Added _naming validation for Containers_ according to http://msdn.microsoft.com/en-us/library/dd135715.aspx (thanks @panosjee)
43
+ - Added _naming validation for Queues_ according to http://msdn.microsoft.com/en-us/library/dd179349.aspx (thanks @panosjee)
44
+ - Added _support for Blob snapshots_
45
+ - Added support for rest-client 1.3.0. (thanks @jpgd)
46
+
47
+ === v0.5.7 - (Johnny Halife)
48
+ - Fixes a critical bug on URL management that some time prevents message from being deleted, caused by a missing encoding on the URL parameters.
49
+
50
+ === v0.5.6 - (Johnny Halife)
51
+ - Added signature support for 2009-09-19 Version of the Storage API
52
+ - Queues API has been migrated to the 2009-09-19 Version of the Storage API
53
+ - Added a new parameter for listing queues with metadata
54
+ - Added support for DequeueCount on messages being retrieved from the Queue
55
+ - <strong>Known Issue</strong>: Creating a queue multiple times with same metadata throws 409.
56
+
57
+ === v0.5.5 - (Johnny Halife)
58
+ - Added a Change Log
59
+ - Added support for copy-blob [WAZ::Blobs::BlobObject.copy("container/blob_name")]
60
+ - Refactored out the whole request logic to an _execute_ method on the SharedKeyCoreService
61
+ - Added transparent support for block management (users don't know if their blob is being uploaded chunked or not)
62
+ - Added support for a blob to list its blocks
63
+
64
+ === v0.5.4 - Sat Oct 17 19:35:48 2009 -0300 (Johnny Halife)
65
+ - Fixed method name for String.start_with? (previously starts_with?)
66
+
67
+ === v0.5.3 - Sat Oct 17 18:20:51 2009 -0300 (Johnny Halife)
68
+ - Added the stack-based contextual connection handling (WAZ::Storage::Base.establish_connection {|block| }
69
+ - Added string extensions for non rails environments (and work to work on heroku) String.starts_with?
70
+
71
+ === v0.5.2 - Thu Oct 15 22:08:01 2009 -0300 (Johnny Halife)
72
+ - Initial Version
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in waz-storage.gemspec
4
- gemspec
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in waz-storage.gemspec
4
+ gemspec
data/Gemfile.lock CHANGED
@@ -1,40 +1,46 @@
1
- PATH
2
- remote: .
3
- specs:
4
- waz-storage (1.1.2)
5
- rest-client
6
- ruby-hmac
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- diff-lcs (1.1.3)
12
- metaclass (0.0.1)
13
- mime-types (1.19)
14
- mocha (0.10.5)
15
- metaclass (~> 0.0.1)
16
- multi_json (1.5.0)
17
- rest-client (1.6.7)
18
- mime-types (>= 1.16)
19
- rspec (2.9.0)
20
- rspec-core (~> 2.9.0)
21
- rspec-expectations (~> 2.9.0)
22
- rspec-mocks (~> 2.9.0)
23
- rspec-core (2.9.0)
24
- rspec-expectations (2.9.1)
25
- diff-lcs (~> 1.1.3)
26
- rspec-mocks (2.9.0)
27
- ruby-hmac (0.4.0)
28
- simplecov (0.7.1)
29
- multi_json (~> 1.0)
30
- simplecov-html (~> 0.7.1)
31
- simplecov-html (0.7.1)
32
-
33
- PLATFORMS
34
- ruby
35
-
36
- DEPENDENCIES
37
- mocha
38
- rspec
39
- simplecov
40
- waz-storage!
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ waz-storage (1.2.0)
5
+ rest-client
6
+ ruby-hmac
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ diff-lcs (1.1.3)
12
+ json (1.7.5)
13
+ metaclass (0.0.1)
14
+ mime-types (1.19)
15
+ mocha (0.10.5)
16
+ metaclass (~> 0.0.1)
17
+ multi_json (1.5.0)
18
+ rake (10.0.2)
19
+ rdoc (3.12)
20
+ json (~> 1.4)
21
+ rest-client (1.6.7)
22
+ mime-types (>= 1.16)
23
+ rspec (2.9.0)
24
+ rspec-core (~> 2.9.0)
25
+ rspec-expectations (~> 2.9.0)
26
+ rspec-mocks (~> 2.9.0)
27
+ rspec-core (2.9.0)
28
+ rspec-expectations (2.9.1)
29
+ diff-lcs (~> 1.1.3)
30
+ rspec-mocks (2.9.0)
31
+ ruby-hmac (0.4.0)
32
+ simplecov (0.7.1)
33
+ multi_json (~> 1.0)
34
+ simplecov-html (~> 0.7.1)
35
+ simplecov-html (0.7.1)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ mocha
42
+ rake
43
+ rdoc
44
+ rspec
45
+ simplecov
46
+ waz-storage!
data/LICENSE CHANGED
@@ -1,19 +1,19 @@
1
- Copyright (c) 2009 Johnny G. Halife
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining a copy
4
- of this software and associated documentation files (the "Software"), to deal
5
- in the Software without restriction, including without limitation the rights
6
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- copies of the Software, and to permit persons to whom the Software is
8
- furnished to do so, subject to the following conditions:
9
-
10
- The above copyright notice and this permission notice shall be included in
11
- all copies or substantial portions of the Software.
12
-
13
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1
+ Copyright (c) 2009 Johnny G. Halife
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  THE SOFTWARE.
data/README.rdoc CHANGED
@@ -1,310 +1,310 @@
1
- = Windows Azure Storage library — simple gem for accessing WAZ‘s Storage REST API
2
- A simple implementation of Windows Azure Storage API for Ruby, inspired by the S3 gems and self experience of dealing with queues. The major
3
- goal of the whole gem is to enable ruby developers [like me =)] to leverage Windows Azure Storage features and have another option
4
- for cloud storage.
5
-
6
- The whole gem is implemented based on Microsoft's specs from the communication and underlying service description and protocol (REST). The API
7
- is for ruby developers built by a ruby developer. I'm trying to follow idioms, patterns and fluent type of doing APIs on Ruby.
8
-
9
- This work isn't related at all with StorageClient Sample shipped with Microsoft SDK and written in .NET, the whole API is based on my own understanding,
10
- experience and values of elegance and ruby development.
11
-
12
- Full documentation for the gem is available at http://waz-storage.heroku.com
13
-
14
- === How does this differ from <em>waz-queues</em> and <em>waz-blobs</em> work?
15
- Well, this is a sum up of the whole experience of writing those gems and getting them to work together to simplify
16
- end user experience. Although there're some breaking changes, it's pretty backward compatible with existing gems.
17
-
18
- === What's new on v1.1.4?
19
- - Fixes an issue with Double Encoding (thanks Phil Ibis!)
20
-
21
- === What's new on v1.1.2?
22
-
23
- The main difference with the original library is the adding of the method "railsetag".
24
- This method add to any blob object an "x_ms_meta_railsetag" header, its value is calculated as the rails etag way (Digest::MD5.hexdigest(file))
25
-
26
- By doing so we can easily know if a locale copy of a file is up-to-date on Azure CDN.
27
- This is very much used in the "waz-sync" gem which purpose is to sync file between local storage and Azure.
28
-
29
- === What's new on the v1.1 version? [thanks to smarx]
30
- - Upload from stream, plus XML/URI escaping in various places, and a few other minor fixes
31
- - Add upload method to container
32
- - Add put_block_list to support upload
33
- - Fix message canonicalization of ?comp= parameters for versions prior to 2009-09-19 (seems put_block was broken)
34
- - Unescape query string parameters when constructing signatures (needed for put_block, since some base64-encoded names include non-URI-friendly characters)
35
- - XML-escape property values for table entities (needed to insert things containing &, <, etc.)
36
- - Remove Unicode characters from rakefile (was breaking something on Windows)
37
- - Add tests for put_block_list and upload
38
- - Fix tests for content type (typo, = instead of .should ==)
39
- - Fix tests using RestClient.beautify_headers (expects an array, not a scalar)
40
-
41
- === What's new on the v1.0.6 version? [thanks to hermes.logicalbricks]
42
- - Update tests to rspec 2.5.0
43
- - Allow authorization using only SharedAccessSignature
44
-
45
- === What's new on the version version?
46
- - Fixed for list_blobs as it wasn't fully merged to 2009-09-19. Now it works without 403 exceptions [Thanks tomconte]
47
-
48
- === What's new on the v1.0.3 version?
49
- - Merged with sriramk fix for loading path issues on some environments
50
-
51
- === What's new on the 1.0.2 version?
52
- - Completed Blobs API migration to 2009-09-19, _fully supporting_ what third-party tools does (e.g. Cerebrata) [thanks percent20]
53
-
54
- === What's new on the 1.0.1 version?
55
- - Added Syntax's sugar for ensuring a Queue (get or create)
56
-
57
- === What's new on the 1.0 version?
58
- - Added support for table service to query, get_one, insert, update, merge and delete entities.
59
- - Added support for running against the Storage Developement Fabriq shipped with Microsoft SDK.
60
- - Added signature support for Tables service according to msdn.microsoft.com/en-us/library/dd179428.aspx
61
- - Added support to enumerate, create, and delete tables on give storage account.
62
- - Improved support for stacked connection management.
63
-
64
- === What's new on the 0.5.81 version?
65
- - When simulating a container using a forward slash starting the blob name it crashed with 404, now it's fixed.
66
-
67
- === What's new on the 0.5.8 version?
68
- - Fixed an issue on the Queues Documentation (thanks @ezequielm)
69
- - Added <b>naming validation for Containers</b> according to http://msdn.microsoft.com/en-us/library/dd135715.aspx (thanks @panosjee)
70
- - Added <b>naming validation for Queues</b> according to http://msdn.microsoft.com/en-us/library/dd179349.aspx (thanks @panosjee)
71
- - Added <b>support for Blob snapshots</b>
72
- - Added support for rest-client 1.3.0. (thanks @jpgd)
73
-
74
- === What's new on the 0.5.7 version?
75
- - Fixes a critical bug on URL management that some time prevents messages from being deleted, caused by a missing encoding on the URL parameters.
76
-
77
- === What's new on the 0.5.6 version?
78
- - Added new <b>shared key authentication support for 2009-09-19 Version of the Storage API</b>
79
- - Queues API has been migrated to the <b>2009-09-19 Version of the Storage API</b>
80
- - Added a new parameter for <b>listing queues with metadata</b>
81
- - Added support for <b>DequeueCount</b> on messages being retrieved from the Queue
82
- - <b>Known Issue</b>: Creating a queue multiple times with same metadata throws 409.
83
-
84
- == Getting started
85
- sudo gem install waz-storage --source http://gemcutter.org
86
-
87
- == Basic Configuration
88
- One of the major changes from the waz-queues and waz-blobs APIs was the ability to set a single storage account (99% of the cases) to be
89
- used on your code just once. Since Windows Azure Storage Services are all related to a single account that includes Tables, Queues and Blobs,
90
- I've changed the API in order to make ease that administration by requiring you to set the configuration just once:
91
-
92
- require 'waz-queues' #=> if you want to use queues
93
- require 'waz-blobs' #=> if you want to use blobs (or you can include both for using both)
94
-
95
- WAZ::Storage::Base.establish_connection!(:account_name => account_name,
96
- :access_key => access_key)
97
-
98
- As you can see you, the way to get started it pretty simple, just include the establish_connection! call on your application bootstrapper, for example
99
- if you are doing a sinatra-rb application it can be
100
-
101
- configure :production do
102
- # here is were you hook up with WAZ Storage Stuff.
103
- end
104
-
105
- That's all you need to get started and running your Windows Azure Code on a sinatra application.
106
-
107
- == Usage: Queues
108
- Windows Azure Queues are extremely powerful for asyc programming, they are very useful when you don’t need "always consistent"
109
- data operations allowing you to enqueue and process on background. It’s important to mention that being WAZ-Queues a buffered
110
- message system, you should design for idempotent operations given the fact that there’s no guarantee that a message can be repeated.
111
-
112
- The implementation of the Windows Azure Queues REST API available online at http://msdn.microsoft.com/en-us/library/dd179363.aspx is fully
113
- covered here.
114
-
115
- Here's a quick getting started of the usage of the API:
116
-
117
- WAZ::Storage::Base.establish_connection!(:account_name => account_name,
118
- :access_key => access_key)
119
-
120
- # NEW! Now you can do ensure and it will return the queue, first trying to retrieve it
121
- # and then if it doesn't exist, will created it
122
- queue = WAZ::Queues::Queue.ensure('my-queue')
123
-
124
- # excepts that the metadata for the queue changes this method behaves as PUT (create/replace)
125
- # remarks: it performs a validation whether metadata changed or not (if changed HTTP 409 conflict)
126
- queue = WAZ::Queues::Queue.create('my-queue')
127
-
128
- 10.times do |m|
129
- # enqueue a receives string. Message content can be anything up to 8KB
130
- # you can serialize and send anything that serializes to UTF-8 string (JSON, XML, etc)
131
- queue.enqueue!("message##{m}")
132
- end
133
-
134
- while(queue.size > 0) do
135
- # Since WAZ implements the peek lock pattern we are locking messages (not dequeuing)
136
- # it has two parameters how many messages and for how long they are locked
137
- messages = queue.lock(10)
138
-
139
- puts "dequeued message: #{messages.size}"
140
-
141
- # deletes the message from the queue so other clients do not pick it after
142
- # visibility time out expires
143
- messages.each {|m| m.destroy!}
144
- end
145
-
146
- It's pretty intuitive, but full documentation (RDoc) is available for the API for further reference.
147
-
148
- == Usage: Blobs
149
- The blobs implementation inside this gem is fully compliant with the spec available at http://msdn.microsoft.com/en-us/library/dd135733.aspx. The Windows
150
- Azure Blobs REST API isn't fully covered here (see TODO's for more information). It's pretty usable and stable right now,
151
- I've been doing lot of testing around and it works seamlessly with the current Windows Azure implementation.
152
-
153
- require 'waz-blobs'
154
-
155
- WAZ::Storage::Base.establish_connection!(:account_name => account_name,
156
- :access_key => access_key)
157
-
158
- # creates a container
159
- container = WAZ::Blobs::Container.create('my-container')
160
-
161
- # stores a blob with custom properties (metadata)
162
- blob = container.store('my_blob.txt',
163
- 'this is the content of my blob',
164
- 'plain/text',
165
- {:x_ms_meta_Custom_Property => "custom_value" })
166
-
167
- # return a specific blob from a container
168
- blob = container['my_blob.txt']
169
-
170
- # retrieves a blob value
171
- blob.value
172
-
173
- It's pretty intuitive, but full documentation (RDoc) is available for the API for further reference.
174
-
175
- === Using Blob with Shared Acess Signature
176
- This feature allow to access a blob only with the information of the blob's name, the container, and the Shared Access Signature. If you don't have permission to perform an action (for example: list the blobs, create a new blob or something like that) you will receive an exception. If you want more details about this: http://msdn.microsoft.com/en-us/library/ee395415.aspx
177
-
178
- require 'waz-blobs'
179
-
180
- WAZ::Storage::Base.establish_connection!(:account_name=> 'mysharedblob',
181
- :use_sas_auth_only=> true,
182
- :sharedaccesssignature =>"?se=XXXXXX&sr=c&si=XXXXX&sig=XXXXXXXXXXXXXXXXXXXXX")
183
-
184
- container = WAZ::Blobs::Container.new(:name=>'container')
185
-
186
- blob = container.store('myfile.txt',File.read('fileofthecontent.txt'),'plain/text')
187
-
188
- The feature was only tested storing a blob.
189
-
190
- == Usage: Tables
191
- The tables implementation inside this gem is fully compliant with the spec available at http://msdn.microsoft.com/en-us/library/dd179423.aspx. The Windows
192
- Azure Tables REST API is fully covered here.
193
-
194
- require 'waz-storage'
195
- require 'waz-tables'
196
-
197
- WAZ::Storage::Base.establish_connection!(:account_name => account_name,
198
- :access_key => access_key)
199
-
200
- # creates a new table
201
- table = WAZ::Tables::Table.create('my-table')
202
-
203
- # list available tables
204
- # (returns a maximum of 1,000 items at one time (more details here: http://msdn.microsoft.com/en-us/library/dd135718.aspx)
205
- tables = WAZ::Tables::Table.list
206
-
207
- # more tables
208
- WAZ::Tables::Table.list(tables.continuation_token)
209
-
210
- # get a specific table, returning nil when the specified table is not found
211
- my_table = WAZ::Tables::Table.find('my-table')
212
-
213
- # table properties
214
- my_table.name
215
- my_table.url
216
-
217
- # delete a table
218
- my_table.destroy!
219
-
220
- # Entity operations using the Table service
221
-
222
- # get the existing service instance
223
- service = WAZ::Tables::Table.service_instance
224
-
225
- # define a new entity
226
- entity = { :address => 'Mountain View',
227
- :age => 23,
228
- :amount_due => 200.23,
229
- :binary_data => File.open(__FILE__),
230
- :customer_code => 'aaaaaaaa-bbbb-cccc-dddd-aaaabbbbcccc',
231
- :customer_since => Time.now.utc,
232
- :is_active => true,
233
- :num_of_orders => 255,
234
- :partition_key => 'customer',
235
- :row_key => "myRowKey#{rand(2000000).to_s}",
236
- :Timestamp => Time.now.utc }
237
-
238
- # inserts a new entity
239
- service.insert_entity('customer_table', entity)
240
-
241
- # retrieves all entities from a table
242
- # (returns a maximum of 1,000 items at one time (more details here: http://msdn.microsoft.com/en-us/library/dd135718.aspx)
243
- # Remarks on development storage it retrieves all items instead the first 1,000
244
- entities = service.query('customer_table')
245
-
246
- # retrieves more entities providing the obtained continuation_token
247
- service.query('customer_table', {:continuation_token => entities.continuation_token} )
248
-
249
- # retrieves all records that match with the specified query but only the first fifteen rows
250
- service.query('customer_table', {:expression => "(PartitionKey eq 'customer') and (Age eq 23)", :top => 15} )
251
-
252
- # get an existing entity by its partion_key and row_key
253
- entity = service.get_entity('customer_table', 'customer', 'rowKey1')
254
-
255
- # updates an entity
256
- entity[:age] = 90
257
- service.update_entity('customer_table', new_entity)
258
-
259
- # merges an entity (more details here: http://msdn.microsoft.com/en-us/library/dd179392.aspx)
260
- entity[:age] = 20
261
- service.merge_entity('customer_table', new_entity)
262
-
263
- # deletes an entity
264
- service.delete_entity('customer_table', 'customer', 'rowKey1')
265
-
266
- It's pretty intuitive, but full documentation (RDoc) is available for the API for further reference.
267
-
268
- == Usage: Contextual Connection Handling
269
- Sometimes while you are building a web application you may require handling different storage account but contextualized. The sample
270
- that comes to my mind is something like a Storage Explorer or Account Monitor for WAZ.
271
-
272
- That is why? I've added a new way of handling a stack-based contextual connection handling. The usage is pretty simple:
273
-
274
- WAZ::Storage::Base.establish_connection(options) do
275
- container = WAZ::Blobs::Container.find('container-name')
276
- blob = container['blob-name']
277
- blob.destroy!
278
- end
279
-
280
- As it is described on the example above, there's a new way of establishing a connection and use it on a given block. The whole implementation
281
- is stack based, and will let you scope your context for some rare cases where you have another account.
282
-
283
- *Disclaimer*: Moving objects across context isn't contemplated yet, and if you try to do changes among scopes you will get
284
- to some wired Windows Azure Errors regarding objects that may not exist.
285
-
286
- === Remarks
287
- Windows Azure Storage API works flawlessly from Heroku and custom ruby hosting deployments on EC2, as far as I tested it. You can leverage the storage
288
- services without the need of having to write the application on .NET or hosting your application on Windows Azure.
289
-
290
- The documentation and implementation exposed here is for the pre-release version and is subject to change on the future.
291
-
292
- === TODO's
293
- As far as users start using it, I'll be building a backlog and probably handling a wish-list of features, but right now I've the following
294
- TODO's already enqueued for further releases of the waz-storage API.
295
-
296
- -Generate a sample application to better show the usage.
297
-
298
- The things listed above do not represent any sort of priority, or the order they are going to be tackled. It's just a list.
299
-
300
- == Meta
301
-
302
- Written by Johnny G. Halife (johnny.halife at me dot com)
303
-
304
- contributed by: Ezequiel Morito (http://twitter.com/ezequielm), Juan Pablo Garcia (http://twitter.com/jpgd), Steve Marx (http://twitter.com/smarx), G. Montard (http://github.com/gmontard)
305
-
306
- Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
307
-
308
- http://waz-storage.heroku.com
309
-
310
- http://github.com/johnnyhalife/waz-storage
1
+ = Windows Azure Storage library — simple gem for accessing WAZ‘s Storage REST API
2
+ A simple implementation of Windows Azure Storage API for Ruby, inspired by the S3 gems and self experience of dealing with queues. The major
3
+ goal of the whole gem is to enable ruby developers [like me =)] to leverage Windows Azure Storage features and have another option
4
+ for cloud storage.
5
+
6
+ The whole gem is implemented based on Microsoft's specs from the communication and underlying service description and protocol (REST). The API
7
+ is for ruby developers built by a ruby developer. I'm trying to follow idioms, patterns and fluent type of doing APIs on Ruby.
8
+
9
+ This work isn't related at all with StorageClient Sample shipped with Microsoft SDK and written in .NET, the whole API is based on my own understanding,
10
+ experience and values of elegance and ruby development.
11
+
12
+ Full documentation for the gem is available at http://waz-storage.heroku.com
13
+
14
+ === How does this differ from <em>waz-queues</em> and <em>waz-blobs</em> work?
15
+ Well, this is a sum up of the whole experience of writing those gems and getting them to work together to simplify
16
+ end user experience. Although there're some breaking changes, it's pretty backward compatible with existing gems.
17
+
18
+ === What's new on v1.1.4?
19
+ - Fixes an issue with Double Encoding (thanks Phil Ibis!)
20
+
21
+ === What's new on v1.1.2?
22
+
23
+ The main difference with the original library is the adding of the method "railsetag".
24
+ This method add to any blob object an "x_ms_meta_railsetag" header, its value is calculated as the rails etag way (Digest::MD5.hexdigest(file))
25
+
26
+ By doing so we can easily know if a locale copy of a file is up-to-date on Azure CDN.
27
+ This is very much used in the "waz-sync" gem which purpose is to sync file between local storage and Azure.
28
+
29
+ === What's new on the v1.1 version? [thanks to smarx]
30
+ - Upload from stream, plus XML/URI escaping in various places, and a few other minor fixes
31
+ - Add upload method to container
32
+ - Add put_block_list to support upload
33
+ - Fix message canonicalization of ?comp= parameters for versions prior to 2009-09-19 (seems put_block was broken)
34
+ - Unescape query string parameters when constructing signatures (needed for put_block, since some base64-encoded names include non-URI-friendly characters)
35
+ - XML-escape property values for table entities (needed to insert things containing &, <, etc.)
36
+ - Remove Unicode characters from rakefile (was breaking something on Windows)
37
+ - Add tests for put_block_list and upload
38
+ - Fix tests for content type (typo, = instead of .should ==)
39
+ - Fix tests using RestClient.beautify_headers (expects an array, not a scalar)
40
+
41
+ === What's new on the v1.0.6 version? [thanks to hermes.logicalbricks]
42
+ - Update tests to rspec 2.5.0
43
+ - Allow authorization using only SharedAccessSignature
44
+
45
+ === What's new on the version version?
46
+ - Fixed for list_blobs as it wasn't fully merged to 2009-09-19. Now it works without 403 exceptions [Thanks tomconte]
47
+
48
+ === What's new on the v1.0.3 version?
49
+ - Merged with sriramk fix for loading path issues on some environments
50
+
51
+ === What's new on the 1.0.2 version?
52
+ - Completed Blobs API migration to 2009-09-19, _fully supporting_ what third-party tools does (e.g. Cerebrata) [thanks percent20]
53
+
54
+ === What's new on the 1.0.1 version?
55
+ - Added Syntax's sugar for ensuring a Queue (get or create)
56
+
57
+ === What's new on the 1.0 version?
58
+ - Added support for table service to query, get_one, insert, update, merge and delete entities.
59
+ - Added support for running against the Storage Developement Fabriq shipped with Microsoft SDK.
60
+ - Added signature support for Tables service according to msdn.microsoft.com/en-us/library/dd179428.aspx
61
+ - Added support to enumerate, create, and delete tables on give storage account.
62
+ - Improved support for stacked connection management.
63
+
64
+ === What's new on the 0.5.81 version?
65
+ - When simulating a container using a forward slash starting the blob name it crashed with 404, now it's fixed.
66
+
67
+ === What's new on the 0.5.8 version?
68
+ - Fixed an issue on the Queues Documentation (thanks @ezequielm)
69
+ - Added <b>naming validation for Containers</b> according to http://msdn.microsoft.com/en-us/library/dd135715.aspx (thanks @panosjee)
70
+ - Added <b>naming validation for Queues</b> according to http://msdn.microsoft.com/en-us/library/dd179349.aspx (thanks @panosjee)
71
+ - Added <b>support for Blob snapshots</b>
72
+ - Added support for rest-client 1.3.0. (thanks @jpgd)
73
+
74
+ === What's new on the 0.5.7 version?
75
+ - Fixes a critical bug on URL management that some time prevents messages from being deleted, caused by a missing encoding on the URL parameters.
76
+
77
+ === What's new on the 0.5.6 version?
78
+ - Added new <b>shared key authentication support for 2009-09-19 Version of the Storage API</b>
79
+ - Queues API has been migrated to the <b>2009-09-19 Version of the Storage API</b>
80
+ - Added a new parameter for <b>listing queues with metadata</b>
81
+ - Added support for <b>DequeueCount</b> on messages being retrieved from the Queue
82
+ - <b>Known Issue</b>: Creating a queue multiple times with same metadata throws 409.
83
+
84
+ == Getting started
85
+ sudo gem install waz-storage --source http://gemcutter.org
86
+
87
+ == Basic Configuration
88
+ One of the major changes from the waz-queues and waz-blobs APIs was the ability to set a single storage account (99% of the cases) to be
89
+ used on your code just once. Since Windows Azure Storage Services are all related to a single account that includes Tables, Queues and Blobs,
90
+ I've changed the API in order to make ease that administration by requiring you to set the configuration just once:
91
+
92
+ require 'waz-queues' #=> if you want to use queues
93
+ require 'waz-blobs' #=> if you want to use blobs (or you can include both for using both)
94
+
95
+ WAZ::Storage::Base.establish_connection!(:account_name => account_name,
96
+ :access_key => access_key)
97
+
98
+ As you can see you, the way to get started it pretty simple, just include the establish_connection! call on your application bootstrapper, for example
99
+ if you are doing a sinatra-rb application it can be
100
+
101
+ configure :production do
102
+ # here is were you hook up with WAZ Storage Stuff.
103
+ end
104
+
105
+ That's all you need to get started and running your Windows Azure Code on a sinatra application.
106
+
107
+ == Usage: Queues
108
+ Windows Azure Queues are extremely powerful for asyc programming, they are very useful when you don’t need "always consistent"
109
+ data operations allowing you to enqueue and process on background. It’s important to mention that being WAZ-Queues a buffered
110
+ message system, you should design for idempotent operations given the fact that there’s no guarantee that a message can be repeated.
111
+
112
+ The implementation of the Windows Azure Queues REST API available online at http://msdn.microsoft.com/en-us/library/dd179363.aspx is fully
113
+ covered here.
114
+
115
+ Here's a quick getting started of the usage of the API:
116
+
117
+ WAZ::Storage::Base.establish_connection!(:account_name => account_name,
118
+ :access_key => access_key)
119
+
120
+ # NEW! Now you can do ensure and it will return the queue, first trying to retrieve it
121
+ # and then if it doesn't exist, will created it
122
+ queue = WAZ::Queues::Queue.ensure('my-queue')
123
+
124
+ # excepts that the metadata for the queue changes this method behaves as PUT (create/replace)
125
+ # remarks: it performs a validation whether metadata changed or not (if changed HTTP 409 conflict)
126
+ queue = WAZ::Queues::Queue.create('my-queue')
127
+
128
+ 10.times do |m|
129
+ # enqueue a receives string. Message content can be anything up to 8KB
130
+ # you can serialize and send anything that serializes to UTF-8 string (JSON, XML, etc)
131
+ queue.enqueue!("message##{m}")
132
+ end
133
+
134
+ while(queue.size > 0) do
135
+ # Since WAZ implements the peek lock pattern we are locking messages (not dequeuing)
136
+ # it has two parameters how many messages and for how long they are locked
137
+ messages = queue.lock(10)
138
+
139
+ puts "dequeued message: #{messages.size}"
140
+
141
+ # deletes the message from the queue so other clients do not pick it after
142
+ # visibility time out expires
143
+ messages.each {|m| m.destroy!}
144
+ end
145
+
146
+ It's pretty intuitive, but full documentation (RDoc) is available for the API for further reference.
147
+
148
+ == Usage: Blobs
149
+ The blobs implementation inside this gem is fully compliant with the spec available at http://msdn.microsoft.com/en-us/library/dd135733.aspx. The Windows
150
+ Azure Blobs REST API isn't fully covered here (see TODO's for more information). It's pretty usable and stable right now,
151
+ I've been doing lot of testing around and it works seamlessly with the current Windows Azure implementation.
152
+
153
+ require 'waz-blobs'
154
+
155
+ WAZ::Storage::Base.establish_connection!(:account_name => account_name,
156
+ :access_key => access_key)
157
+
158
+ # creates a container
159
+ container = WAZ::Blobs::Container.create('my-container')
160
+
161
+ # stores a blob with custom properties (metadata)
162
+ blob = container.store('my_blob.txt',
163
+ 'this is the content of my blob',
164
+ 'plain/text',
165
+ {:x_ms_meta_Custom_Property => "custom_value" })
166
+
167
+ # return a specific blob from a container
168
+ blob = container['my_blob.txt']
169
+
170
+ # retrieves a blob value
171
+ blob.value
172
+
173
+ It's pretty intuitive, but full documentation (RDoc) is available for the API for further reference.
174
+
175
+ === Using Blob with Shared Acess Signature
176
+ This feature allow to access a blob only with the information of the blob's name, the container, and the Shared Access Signature. If you don't have permission to perform an action (for example: list the blobs, create a new blob or something like that) you will receive an exception. If you want more details about this: http://msdn.microsoft.com/en-us/library/ee395415.aspx
177
+
178
+ require 'waz-blobs'
179
+
180
+ WAZ::Storage::Base.establish_connection!(:account_name=> 'mysharedblob',
181
+ :use_sas_auth_only=> true,
182
+ :sharedaccesssignature =>"?se=XXXXXX&sr=c&si=XXXXX&sig=XXXXXXXXXXXXXXXXXXXXX")
183
+
184
+ container = WAZ::Blobs::Container.new(:name=>'container')
185
+
186
+ blob = container.store('myfile.txt',File.read('fileofthecontent.txt'),'plain/text')
187
+
188
+ The feature was only tested storing a blob.
189
+
190
+ == Usage: Tables
191
+ The tables implementation inside this gem is fully compliant with the spec available at http://msdn.microsoft.com/en-us/library/dd179423.aspx. The Windows
192
+ Azure Tables REST API is fully covered here.
193
+
194
+ require 'waz-storage'
195
+ require 'waz-tables'
196
+
197
+ WAZ::Storage::Base.establish_connection!(:account_name => account_name,
198
+ :access_key => access_key)
199
+
200
+ # creates a new table
201
+ table = WAZ::Tables::Table.create('my-table')
202
+
203
+ # list available tables
204
+ # (returns a maximum of 1,000 items at one time (more details here: http://msdn.microsoft.com/en-us/library/dd135718.aspx)
205
+ tables = WAZ::Tables::Table.list
206
+
207
+ # more tables
208
+ WAZ::Tables::Table.list(tables.continuation_token)
209
+
210
+ # get a specific table, returning nil when the specified table is not found
211
+ my_table = WAZ::Tables::Table.find('my-table')
212
+
213
+ # table properties
214
+ my_table.name
215
+ my_table.url
216
+
217
+ # delete a table
218
+ my_table.destroy!
219
+
220
+ # Entity operations using the Table service
221
+
222
+ # get the existing service instance
223
+ service = WAZ::Tables::Table.service_instance
224
+
225
+ # define a new entity
226
+ entity = { :address => 'Mountain View',
227
+ :age => 23,
228
+ :amount_due => 200.23,
229
+ :binary_data => File.open(__FILE__),
230
+ :customer_code => 'aaaaaaaa-bbbb-cccc-dddd-aaaabbbbcccc',
231
+ :customer_since => Time.now.utc,
232
+ :is_active => true,
233
+ :num_of_orders => 255,
234
+ :partition_key => 'customer',
235
+ :row_key => "myRowKey#{rand(2000000).to_s}",
236
+ :Timestamp => Time.now.utc }
237
+
238
+ # inserts a new entity
239
+ service.insert_entity('customer_table', entity)
240
+
241
+ # retrieves all entities from a table
242
+ # (returns a maximum of 1,000 items at one time (more details here: http://msdn.microsoft.com/en-us/library/dd135718.aspx)
243
+ # Remarks on development storage it retrieves all items instead the first 1,000
244
+ entities = service.query('customer_table')
245
+
246
+ # retrieves more entities providing the obtained continuation_token
247
+ service.query('customer_table', {:continuation_token => entities.continuation_token} )
248
+
249
+ # retrieves all records that match with the specified query but only the first fifteen rows
250
+ service.query('customer_table', {:expression => "(PartitionKey eq 'customer') and (Age eq 23)", :top => 15} )
251
+
252
+ # get an existing entity by its partion_key and row_key
253
+ entity = service.get_entity('customer_table', 'customer', 'rowKey1')
254
+
255
+ # updates an entity
256
+ entity[:age] = 90
257
+ service.update_entity('customer_table', new_entity)
258
+
259
+ # merges an entity (more details here: http://msdn.microsoft.com/en-us/library/dd179392.aspx)
260
+ entity[:age] = 20
261
+ service.merge_entity('customer_table', new_entity)
262
+
263
+ # deletes an entity
264
+ service.delete_entity('customer_table', 'customer', 'rowKey1')
265
+
266
+ It's pretty intuitive, but full documentation (RDoc) is available for the API for further reference.
267
+
268
+ == Usage: Contextual Connection Handling
269
+ Sometimes while you are building a web application you may require handling different storage account but contextualized. The sample
270
+ that comes to my mind is something like a Storage Explorer or Account Monitor for WAZ.
271
+
272
+ That is why? I've added a new way of handling a stack-based contextual connection handling. The usage is pretty simple:
273
+
274
+ WAZ::Storage::Base.establish_connection(options) do
275
+ container = WAZ::Blobs::Container.find('container-name')
276
+ blob = container['blob-name']
277
+ blob.destroy!
278
+ end
279
+
280
+ As it is described on the example above, there's a new way of establishing a connection and use it on a given block. The whole implementation
281
+ is stack based, and will let you scope your context for some rare cases where you have another account.
282
+
283
+ *Disclaimer*: Moving objects across context isn't contemplated yet, and if you try to do changes among scopes you will get
284
+ to some wired Windows Azure Errors regarding objects that may not exist.
285
+
286
+ === Remarks
287
+ Windows Azure Storage API works flawlessly from Heroku and custom ruby hosting deployments on EC2, as far as I tested it. You can leverage the storage
288
+ services without the need of having to write the application on .NET or hosting your application on Windows Azure.
289
+
290
+ The documentation and implementation exposed here is for the pre-release version and is subject to change on the future.
291
+
292
+ === TODO's
293
+ As far as users start using it, I'll be building a backlog and probably handling a wish-list of features, but right now I've the following
294
+ TODO's already enqueued for further releases of the waz-storage API.
295
+
296
+ -Generate a sample application to better show the usage.
297
+
298
+ The things listed above do not represent any sort of priority, or the order they are going to be tackled. It's just a list.
299
+
300
+ == Meta
301
+
302
+ Written by Johnny G. Halife (johnny.halife at me dot com)
303
+
304
+ contributed by: Ezequiel Morito (http://twitter.com/ezequielm), Juan Pablo Garcia (http://twitter.com/jpgd), Steve Marx (http://twitter.com/smarx), G. Montard (http://github.com/gmontard)
305
+
306
+ Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
307
+
308
+ http://waz-storage.heroku.com
309
+
310
+ http://github.com/johnnyhalife/waz-storage