sparqcode-waz-storage 1.1.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 +7 -0
- data/CHANGELOG.rdoc +72 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +36 -0
- data/LICENSE +19 -0
- data/README.rdoc +299 -0
- data/lib/waz-blobs.rb +5 -0
- data/lib/waz-queues.rb +6 -0
- data/lib/waz-storage.rb +39 -0
- data/lib/waz-tables.rb +5 -0
- data/lib/waz/blobs/blob_object.rb +121 -0
- data/lib/waz/blobs/container.rb +160 -0
- data/lib/waz/blobs/exceptions.rb +11 -0
- data/lib/waz/blobs/service.rb +156 -0
- data/lib/waz/queues/exceptions.rb +29 -0
- data/lib/waz/queues/message.rb +65 -0
- data/lib/waz/queues/queue.rb +165 -0
- data/lib/waz/queues/service.rb +106 -0
- data/lib/waz/storage/base.rb +70 -0
- data/lib/waz/storage/core_service.rb +122 -0
- data/lib/waz/storage/exceptions.rb +33 -0
- data/lib/waz/storage/validation_rules.rb +26 -0
- data/lib/waz/storage/version.rb +11 -0
- data/lib/waz/tables/edm_type_helper.rb +45 -0
- data/lib/waz/tables/exceptions.rb +45 -0
- data/lib/waz/tables/service.rb +178 -0
- data/lib/waz/tables/table.rb +75 -0
- data/lib/waz/tables/table_array.rb +11 -0
- data/rakefile +23 -0
- data/tests/configuration.rb +14 -0
- data/tests/waz/blobs/blob_object_test.rb +80 -0
- data/tests/waz/blobs/container_test.rb +162 -0
- data/tests/waz/blobs/service_test.rb +282 -0
- data/tests/waz/queues/message_test.rb +33 -0
- data/tests/waz/queues/queue_test.rb +206 -0
- data/tests/waz/queues/service_test.rb +299 -0
- data/tests/waz/storage/base_tests.rb +81 -0
- data/tests/waz/storage/shared_key_core_service_test.rb +142 -0
- data/tests/waz/tables/service_test.rb +614 -0
- data/tests/waz/tables/table_test.rb +98 -0
- data/waz-storage.gemspec +29 -0
- metadata +187 -0
data/lib/waz-queues.rb
ADDED
data/lib/waz-storage.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
require 'cgi'
|
|
3
|
+
require 'base64'
|
|
4
|
+
require 'rexml/document'
|
|
5
|
+
require 'rexml/xpath'
|
|
6
|
+
require 'restclient'
|
|
7
|
+
require 'hmac-sha2'
|
|
8
|
+
require 'net/http'
|
|
9
|
+
|
|
10
|
+
app_files = File.expand_path(File.join(File.dirname(__FILE__), 'waz', 'storage', '*.rb'))
|
|
11
|
+
Dir[app_files].each(&method(:load))
|
|
12
|
+
|
|
13
|
+
# It will depende on which version of Ruby (or if you have Rails)
|
|
14
|
+
# but this method is required so we will add it the String class.
|
|
15
|
+
unless String.method_defined? :start_with?
|
|
16
|
+
class String
|
|
17
|
+
def start_with?(prefix)
|
|
18
|
+
prefix = prefix.to_s
|
|
19
|
+
self[0, prefix.length] == prefix
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The Merge method is not defined in the RFC 2616
|
|
25
|
+
# and it's required to Merge entities in Windows Azure
|
|
26
|
+
module Net
|
|
27
|
+
class HTTP < Protocol
|
|
28
|
+
class Merge < HTTPRequest
|
|
29
|
+
METHOD = 'MERGE'
|
|
30
|
+
REQUEST_HAS_BODY = true
|
|
31
|
+
RESPONSE_HAS_BODY = false
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# extendes the Symbol class to assign a type to an entity field
|
|
37
|
+
class Symbol
|
|
38
|
+
attr_accessor :edm_type
|
|
39
|
+
end
|
data/lib/waz-tables.rb
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module WAZ
|
|
2
|
+
module Blobs
|
|
3
|
+
# This class is used to model the Blob inside Windows Azure Blobs the usage
|
|
4
|
+
# it's pretty simple, here you can see a couple of samples. These are the implemented methods of the blob API up to now.
|
|
5
|
+
# The basics are implemented although blocks management is not yet completed, I consider that the API is pretty usable since
|
|
6
|
+
# it covers the basics
|
|
7
|
+
#
|
|
8
|
+
# # retrieve blob name, uri and content-type
|
|
9
|
+
# blob.name
|
|
10
|
+
# blob.url
|
|
11
|
+
# blob.content_type
|
|
12
|
+
#
|
|
13
|
+
# # retrieve blob value
|
|
14
|
+
# blob.value #=> lazy loaded payload of the blob
|
|
15
|
+
#
|
|
16
|
+
# # retrieve blob metadata (+ properties)
|
|
17
|
+
# blob.metadata #=> hash containing beautified metadata (:x_ms_meta_name)
|
|
18
|
+
#
|
|
19
|
+
# # put blob metadata
|
|
20
|
+
# blob.put_properties(:x_ms_meta_MyProperty => "my value")
|
|
21
|
+
#
|
|
22
|
+
# # update value
|
|
23
|
+
# blob.value = "my new value" #=> this will update the blob content on WAZ
|
|
24
|
+
#
|
|
25
|
+
# *REMARKS*: This class is not meant to be manually instanciated it's basicaly an internal
|
|
26
|
+
# representation returned by the WAZ::Blobs::Container.
|
|
27
|
+
class BlobObject
|
|
28
|
+
class << self
|
|
29
|
+
# This method is internally used by this class. It's the way we keep a single instance of the
|
|
30
|
+
# service that wraps the calls the Windows Azure Blobs API. It's initialized with the values
|
|
31
|
+
# from the default_connection on WAZ::Storage::Base initialized thru establish_connection!
|
|
32
|
+
def service_instance
|
|
33
|
+
options = WAZ::Storage::Base.default_connection.merge(:type_of_service => "blob")
|
|
34
|
+
(@service_instances ||= {})[options[:account_name]] ||= Service.new(options)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
attr_accessor :name, :url, :content_type, :snapshot_date
|
|
39
|
+
|
|
40
|
+
# Creates a new instance of the Blob object. This constructor is internally used by the Container
|
|
41
|
+
# it's initialized thru a hash that's received as parameter. It has the following requirements:
|
|
42
|
+
# <em>:name</em> which is the blob name (usually the file name), <em>:url</em> that is the url of the blob (used to download or access it via browser)
|
|
43
|
+
# and <em>:content_type</em> which is the content type of the blob and is a required parameter by the Azure API
|
|
44
|
+
def initialize(options = {})
|
|
45
|
+
raise WAZ::Storage::InvalidOption, :name unless options.keys.include?(:name) and !options[:name].empty?
|
|
46
|
+
raise WAZ::Storage::InvalidOption, :url unless options.keys.include?(:url) and !options[:url].empty?
|
|
47
|
+
raise WAZ::Storage::InvalidOption, :content_type unless options.keys.include?(:content_type) and !options[:content_type].empty?
|
|
48
|
+
self.name = options[:name]
|
|
49
|
+
self.url = options[:url]
|
|
50
|
+
self.content_type = options[:content_type]
|
|
51
|
+
self.snapshot_date = options[:snapshot_date]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns the blob properties from Windows Azure. This properties always come as HTTP Headers and they include standard headers like
|
|
55
|
+
# <em>Content-Type</em>, <em>Content-Length</em>, etc. combined with custom properties like with <em>x-ms-meta-Name</em>.
|
|
56
|
+
def metadata
|
|
57
|
+
self.class.service_instance.get_blob_properties(path)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns the actual blob content, this method is specially used to avoid retrieving the whole blob
|
|
61
|
+
# while iterating and retrieving the blob collection from the Container.
|
|
62
|
+
def value
|
|
63
|
+
@value ||= self.class.service_instance.get_blob(path)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Assigns the given value to the blob content. It also stores a local copy of it in order to avoid round trips
|
|
67
|
+
# on scenarios when you do Save and Display on the same context.
|
|
68
|
+
def value=(new_value)
|
|
69
|
+
raise WAZ::Blobs::InvalidOperation if self.snapshot_date
|
|
70
|
+
self.class.service_instance.put_blob(path, new_value, content_type, metadata)
|
|
71
|
+
@value = new_value
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Stores the blob properties. Those properties are sent as HTTP Headers it's really important that you name your custom
|
|
75
|
+
# properties with the <em>x-ms-meta</em> prefix, if not the won't be persisted by the Windows Azure Blob Storage API.
|
|
76
|
+
def put_properties!(properties = {})
|
|
77
|
+
raise WAZ::Blobs::InvalidOperation if self.snapshot_date
|
|
78
|
+
self.class.service_instance.set_blob_properties(path, properties)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Stores blob metadata. User metadata must be prefixed with 'x-ms-meta-'. The advantage of this over put_properties
|
|
82
|
+
# is that it only affect user_metadata and doesn't overwrite any system values, like 'content_type'.
|
|
83
|
+
def put_metadata!(metadata = {})
|
|
84
|
+
self.class.service_instance.set_blob_metadata(path, metadata)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Removes the blob from the container.
|
|
88
|
+
def destroy!
|
|
89
|
+
self.class.service_instance.delete_blob(path)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Copies the blob to the destination and returns
|
|
93
|
+
# the copied blob instance.
|
|
94
|
+
#
|
|
95
|
+
# destination should be formed as "container/blob"
|
|
96
|
+
def copy(destination)
|
|
97
|
+
self.class.service_instance.copy_blob(self.path, destination)
|
|
98
|
+
properties = self.class.service_instance.get_blob_properties(destination)
|
|
99
|
+
return BlobObject.new(:name => destination,
|
|
100
|
+
:url => self.class.service_instance.generate_request_uri(destination),
|
|
101
|
+
:content_type => properties[:content_type])
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Creates and returns a read-only snapshot of a blob as it looked like in time.
|
|
105
|
+
def snapshot
|
|
106
|
+
date = self.class.service_instance.snapshot_blob(self.path)
|
|
107
|
+
properties = self.class.service_instance.get_blob_properties(self.path)
|
|
108
|
+
return BlobObject.new(:name => self.name,
|
|
109
|
+
:url => self.class.service_instance.generate_request_uri(self.path) + "?snapshot=#{date}",
|
|
110
|
+
:content_type => properties[:content_type],
|
|
111
|
+
:snapshot_date => date)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Returns the blob path. This is specially important when simulating containers inside containers
|
|
115
|
+
# by enabling the API to point to the appropiated resource.
|
|
116
|
+
def path
|
|
117
|
+
url.gsub(/https?:\/\/[^\/]+\//i, '').scan(/([^&]+)/i).first().first()
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
module WAZ
|
|
2
|
+
module Blobs
|
|
3
|
+
# This class is used to model the Container inside Windows Azure Blobs the usage
|
|
4
|
+
# it's pretty simple, here you can see a couple of samples. Here you can find Microsoft's REST API description available on MSDN
|
|
5
|
+
# at http://msdn.microsoft.com/en-us/library/dd179361.aspx
|
|
6
|
+
#
|
|
7
|
+
# # list available containers
|
|
8
|
+
# WAZ::Blobs::Container.list
|
|
9
|
+
#
|
|
10
|
+
# # create a container
|
|
11
|
+
# WAZ::Blobs::Container.create('my-container')
|
|
12
|
+
#
|
|
13
|
+
# # get a specific container
|
|
14
|
+
# my_container = WAZ::Blobs::Container.find('my-container')
|
|
15
|
+
#
|
|
16
|
+
# # get container properties (including default headers)
|
|
17
|
+
# my_container.metadata #=> hash containing beautified metadata (:x_ms_meta_name)
|
|
18
|
+
#
|
|
19
|
+
# # set container properties (should follow x-ms-meta to be persisted)
|
|
20
|
+
# my_container.put_properties(:x_ms_meta_MyProperty => "my value")
|
|
21
|
+
#
|
|
22
|
+
# # get a the value indicating whether the container is public or not
|
|
23
|
+
# my_container.public_access? #=> true or false based on x-ms-prop-publicaccess
|
|
24
|
+
#
|
|
25
|
+
# # set a value indicating whether the container is public or not
|
|
26
|
+
# my_container.public_access = false
|
|
27
|
+
#
|
|
28
|
+
# # delete container
|
|
29
|
+
# my_container.destroy!
|
|
30
|
+
#
|
|
31
|
+
# # store a blob on the given container
|
|
32
|
+
# my_container.store('my-blob', blob_content, 'application/xml')
|
|
33
|
+
#
|
|
34
|
+
# # retrieve a particular blob from a container
|
|
35
|
+
# my_container['my-blob']
|
|
36
|
+
#
|
|
37
|
+
# # retrieve a blob list from a container
|
|
38
|
+
# my_container.blobs #=> WAZ::Blobs::BlobObject collection
|
|
39
|
+
#
|
|
40
|
+
class Container
|
|
41
|
+
class << self
|
|
42
|
+
# Creates a new container with the given name.
|
|
43
|
+
def create(name)
|
|
44
|
+
raise WAZ::Storage::InvalidParameterValue, {:name => "name", :values => ["lower letters, numbers or - (hypen), and must not start or end with - (hyphen)"]} unless WAZ::Storage::ValidationRules.valid_name?(name)
|
|
45
|
+
service_instance.create_container(name)
|
|
46
|
+
return Container.new(:name => name)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Finds a container by name. It will return nil if no container was found.
|
|
50
|
+
def find(name)
|
|
51
|
+
begin
|
|
52
|
+
properties = service_instance.get_container_properties(name)
|
|
53
|
+
return Container.new(properties.merge(:name => name))
|
|
54
|
+
rescue RestClient::ResourceNotFound
|
|
55
|
+
return nil
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns all the containers on the given account.
|
|
60
|
+
def list(options = {})
|
|
61
|
+
service_instance.list_containers(options).map { |container| Container.new(container) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# This method is internally used by this class. It's the way we keep a single instance of the
|
|
65
|
+
# service that wraps the calls the Windows Azure Blobs API. It's initialized with the values
|
|
66
|
+
# from the default_connection on WAZ::Storage::Base initialized thru establish_connection!
|
|
67
|
+
def service_instance
|
|
68
|
+
options = WAZ::Storage::Base.default_connection.merge(:type_of_service => "blob")
|
|
69
|
+
(@service_instances ||= {})[options[:account_name]] ||= Service.new(options)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
attr_accessor :name
|
|
74
|
+
|
|
75
|
+
# Creates a new instance of the WAZ::Blobs::Container. This class isn't intended for external use
|
|
76
|
+
# to access or create a container you should use the class methods provided like list, create, or find.
|
|
77
|
+
def initialize(options = {})
|
|
78
|
+
raise WAZ::Storage::InvalidOption, :name unless options.keys.include?(:name) and !options[:name].empty?
|
|
79
|
+
self.name = options[:name]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Returns the container metadata.
|
|
83
|
+
def metadata
|
|
84
|
+
self.class.service_instance.get_container_properties(self.name)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Adds metadata for the container.Those properties are sent as HTTP Headers it's really important that you name your custom
|
|
88
|
+
# properties with the <em>x-ms-meta</em> prefix, if not the won't be persisted by the Windows Azure Blob Storage API.
|
|
89
|
+
def put_properties!(properties = {})
|
|
90
|
+
self.class.service_instance.set_container_properties(self.name, properties)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Removes the container from the current account.
|
|
94
|
+
def destroy!
|
|
95
|
+
self.class.service_instance.delete_container(self.name)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Retuns a value indicating whether the container is public accessible (i.e. from a Web Browser) or not.
|
|
99
|
+
def public_access?
|
|
100
|
+
self.class.service_instance.get_container_acl(self.name)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Sets a value indicating whether the container is public accessible (i.e. from a Web Browser) or not.
|
|
104
|
+
def public_access=(value)
|
|
105
|
+
self.class.service_instance.set_container_acl(self.name, value)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Returns a list of blobs (WAZ::Blobs::BlobObject) contained on the current container.
|
|
109
|
+
def blobs
|
|
110
|
+
self.class.service_instance.list_blobs(name).map { |blob| WAZ::Blobs::BlobObject.new(blob) }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Stores a blob on the container with under the given name, with the given content and
|
|
114
|
+
# the required <em>content_type</em>. <strong>The <em>options</em> parameters if provided
|
|
115
|
+
# will set the default metadata properties for the blob</strong>.
|
|
116
|
+
def store(blob_name, payload, content_type, options = {})
|
|
117
|
+
blob_name.gsub!(%r{^/}, '')
|
|
118
|
+
self.class.service_instance.put_blob("#{self.name}/#{blob_name}", payload, content_type, options)
|
|
119
|
+
return BlobObject.new(:name => blob_name,
|
|
120
|
+
:url => self.class.service_instance.generate_request_uri("#{self.name}/#{blob_name}"),
|
|
121
|
+
:content_type => content_type)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Uploads the contents of a stream to the specified blob within this container, using
|
|
125
|
+
# the required <em>content_type</em>. The stream will be uploaded in blocks of size
|
|
126
|
+
# <em>block_size</em> bytes, which defaults to four megabytes. <strong>The <em>options</em>
|
|
127
|
+
# parameter, if provided, will set the default metadata properties for the blob</strong>.
|
|
128
|
+
def upload(blob_name, stream, content_type, options = {}, block_size = 4 * 2**20)
|
|
129
|
+
blob_name.gsub!(%r{^/}, '')
|
|
130
|
+
path = "#{self.name}/#{blob_name}"
|
|
131
|
+
n = 0
|
|
132
|
+
until stream.eof?
|
|
133
|
+
self.class.service_instance.put_block path, Base64.encode64('%064d' % n), stream.read(block_size)
|
|
134
|
+
n += 1
|
|
135
|
+
end
|
|
136
|
+
self.class.service_instance.put_block_list path, (0...n).map{|id| Base64.encode64('%064d' % id)}, content_type, options
|
|
137
|
+
return BlobObject.new(:name => blob_name, :url => self.class.service_instance.generate_request_uri("#{self.name}/#{blob_name}"), :content_type => content_type)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Retrieves the blob by the given name. If the blob isn't found on the current container
|
|
141
|
+
# it will return nil instead of throwing an exception.
|
|
142
|
+
def [](blob_name)
|
|
143
|
+
begin
|
|
144
|
+
blob_name.gsub!(%r{^/}, '')
|
|
145
|
+
properties = self.class.service_instance.get_blob_properties("#{self.name}/#{blob_name}")
|
|
146
|
+
return BlobObject.new(:name => blob_name,
|
|
147
|
+
:url => self.class.service_instance.generate_request_uri("#{self.name}/#{blob_name}"),
|
|
148
|
+
:content_type => properties[:content_type])
|
|
149
|
+
rescue RestClient::ResourceNotFound
|
|
150
|
+
return nil
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
class BlobSecurity
|
|
155
|
+
Container = 'container'
|
|
156
|
+
Blob = 'blob'
|
|
157
|
+
Private = ''
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module WAZ
|
|
2
|
+
module Blobs
|
|
3
|
+
# This exception is raised when the user tries to perform an operation over a snapshoted blob. Since
|
|
4
|
+
# Snapshots are read-only copies of the original blob they cannot be modified
|
|
5
|
+
class InvalidOperation < WAZ::Storage::StorageException
|
|
6
|
+
def initialize()
|
|
7
|
+
super("A snapshoted blob cannot be modified.")
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
module WAZ
|
|
2
|
+
module Blobs
|
|
3
|
+
# This is internally used by the waz-blobs part of the gem and it exposes the Windows Azure Blob API REST methods
|
|
4
|
+
# implementation. You can use this class to perform an specific operation that isn't provided by the current API.
|
|
5
|
+
class Service
|
|
6
|
+
include WAZ::Storage::SharedKeyCoreService
|
|
7
|
+
|
|
8
|
+
# Creates a container on the current Windows Azure Storage account.
|
|
9
|
+
def create_container(container_name)
|
|
10
|
+
execute :put, container_name, {:restype => 'container'}, {:x_ms_version => '2009-09-19'}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Retrieves all the properties existing on the container.
|
|
14
|
+
def get_container_properties(container_name)
|
|
15
|
+
execute(:get, container_name, {:restype => 'container'}, {:x_ms_version => '2009-09-19'}).headers
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Set the container properties (metadata).
|
|
19
|
+
#
|
|
20
|
+
# Remember that custom properties should be named as :x_ms_meta_{propertyName} in order
|
|
21
|
+
# to have Windows Azure to persist them.
|
|
22
|
+
def set_container_properties(container_name, properties = {})
|
|
23
|
+
execute :put, container_name, { :restype => 'container', :comp => 'metadata' }, properties.merge!({:x_ms_version => '2009-09-19'})
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Retrieves the value of the :x_ms_prop_publicaccess header from the
|
|
27
|
+
# container properties indicating whether the container is publicly
|
|
28
|
+
# accessible or not.
|
|
29
|
+
def get_container_acl(container_name)
|
|
30
|
+
headers = execute(:get, container_name, { :restype => 'container', :comp => 'acl' }, {:x_ms_version => '2009-09-19'}).headers
|
|
31
|
+
headers[:x_ms_blob_public_access]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Sets the value of the :x_ms_prop_publicaccess header from the
|
|
35
|
+
# container properties indicating whether the container is publicly
|
|
36
|
+
# accessible or not.
|
|
37
|
+
#
|
|
38
|
+
# Default is _false_
|
|
39
|
+
def set_container_acl(container_name, public_available = WAZ::Blobs::BlobSecurity::Private)
|
|
40
|
+
publicity = {:x_ms_version => '2009-09-19' }
|
|
41
|
+
publicity[:x_ms_blob_public_access] = public_available unless public_available == WAZ::Blobs::BlobSecurity::Private
|
|
42
|
+
execute :put, container_name, { :restype => 'container', :comp => 'acl' }, publicity
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Lists all the containers existing on the current storage account.
|
|
46
|
+
def list_containers(options = {})
|
|
47
|
+
content = execute(:get, nil, options.merge(:comp => 'list'))
|
|
48
|
+
doc = REXML::Document.new(content)
|
|
49
|
+
containers = []
|
|
50
|
+
REXML::XPath.each(doc, '//Container/') do |item|
|
|
51
|
+
containers << { :name => REXML::XPath.first(item, "Name").text,
|
|
52
|
+
:url => REXML::XPath.first(item, "Url").text,
|
|
53
|
+
:last_modified => REXML::XPath.first(item, "LastModified").text}
|
|
54
|
+
end
|
|
55
|
+
return containers
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Deletes the given container from the Windows Azure Storage account.
|
|
59
|
+
def delete_container(container_name)
|
|
60
|
+
execute :delete, container_name, {:restype => 'container'}, {:x_ms_version => '2009-09-19'}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Lists all the blobs inside the given container.
|
|
64
|
+
def list_blobs(container_name)
|
|
65
|
+
content = execute(:get, container_name, { :restype => 'container', :comp => 'list'}, {:x_ms_version => '2009-09-19'})
|
|
66
|
+
doc = REXML::Document.new(content)
|
|
67
|
+
containers = []
|
|
68
|
+
REXML::XPath.each(doc, '//Blob/') do |item|
|
|
69
|
+
containers << { :name => REXML::XPath.first(item, "Name").text,
|
|
70
|
+
:url => REXML::XPath.first(item, "Url").text,
|
|
71
|
+
:content_type => REXML::XPath.first(item.elements["Properties"], "Content-Type").text }
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
return containers
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Stores a blob on the given container.
|
|
78
|
+
#
|
|
79
|
+
# Remarks path and payload are just text.
|
|
80
|
+
#
|
|
81
|
+
# content_type is required by the blobs api, but on this method is defaulted to "application/octect-stream"
|
|
82
|
+
#
|
|
83
|
+
# metadata is a hash that stores all the properties that you want to add to the blob when creating it.
|
|
84
|
+
def put_blob(path, payload, content_type = "application/octet-stream", metadata = {})
|
|
85
|
+
default_headers = {"Content-Type" => content_type, :x_ms_version => "2009-09-19", :x_ms_blob_type => "BlockBlob"}
|
|
86
|
+
execute :put, path, nil, metadata.merge(default_headers), payload
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Commits a list of blocks to the given blob.
|
|
90
|
+
#
|
|
91
|
+
# blockids is a list of valid, already-uploaded block IDs (base64-encoded)
|
|
92
|
+
#
|
|
93
|
+
# content_type is required by the blobs api, but on this method is defaulted to "application/octect-stream"
|
|
94
|
+
#
|
|
95
|
+
# metadata is a hash that stores all the properties that you want to add to the blob when creating it.
|
|
96
|
+
def put_block_list(path, blockids, content_type = "application/octet-stream", metadata = {})
|
|
97
|
+
default_headers = {"Content-Type" => content_type, :x_ms_version => "2009-09-19"}
|
|
98
|
+
execute :put, path, { :comp => 'blocklist' }, metadata.merge(default_headers), '<?xml version="1.0" encoding="utf-8"?><BlockList>' + blockids.map {|id| "<Latest>#{id.rstrip}</Latest>"}.join + '</BlockList>'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Retrieves a blob (content + headers) from the current path.
|
|
102
|
+
def get_blob(path, options = {})
|
|
103
|
+
execute :get, path, options, {:x_ms_version => "2009-09-19"}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Deletes the blob existing on the current path.
|
|
107
|
+
def delete_blob(path)
|
|
108
|
+
execute :delete, path, nil, {:x_ms_version => "2009-09-19"}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Retrieves the properties associated with the blob at the given path.
|
|
112
|
+
def get_blob_properties(path, options = {})
|
|
113
|
+
execute(:head, path, options, {:x_ms_version => "2009-09-19"}).headers
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Sets the properties (metadata) associated to the blob at given path.
|
|
117
|
+
def set_blob_properties(path, properties ={})
|
|
118
|
+
execute :put, path, { :comp => 'properties' }, properties.merge({:x_ms_version => "2009-09-19"})
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Set user defined metadata - overwrites any previous metadata key:value pairs
|
|
122
|
+
def set_blob_metadata(path, metadata = {})
|
|
123
|
+
execute :put, path, { :comp => 'metadata' }, metadata.merge({:x_ms_version => "2009-09-19"})
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Copies a blob within the same account (not necessarily to the same container)
|
|
127
|
+
def copy_blob(source_path, dest_path)
|
|
128
|
+
execute :put, dest_path, nil, { :x_ms_version => "2009-09-19", :x_ms_copy_source => canonicalize_message(source_path) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Adds a block to the block list of the given blob
|
|
132
|
+
def put_block(path, identifier, payload)
|
|
133
|
+
execute :put, path, { :comp => 'block', :blockid => identifier }, {'Content-Type' => "application/octet-stream"}, payload
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Retrieves the list of blocks associated with a single blob. The list is filtered (or not) by type of blob
|
|
137
|
+
def list_blocks(path, block_list_type = 'all')
|
|
138
|
+
raise WAZ::Storage::InvalidParameterValue , {:name => :blocklisttype, :values => ['all', 'uncommitted', 'committed']} unless (block_list_type or "") =~ /all|committed|uncommitted/i
|
|
139
|
+
content = execute(:get, path, {:comp => 'blocklist'}.merge(:blocklisttype => block_list_type.downcase), { :x_ms_version => "2009-04-14" })
|
|
140
|
+
doc = REXML::Document.new(content)
|
|
141
|
+
blocks = []
|
|
142
|
+
REXML::XPath.each(doc, '//Block/') do |item|
|
|
143
|
+
blocks << { :name => REXML::XPath.first(item, "Name").text,
|
|
144
|
+
:size => REXML::XPath.first(item, "Size").text,
|
|
145
|
+
:committed => item.parent.name == "CommittedBlocks" }
|
|
146
|
+
end
|
|
147
|
+
return blocks
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Creates a read-only snapshot of a blob as it looked like in time.
|
|
151
|
+
def snapshot_blob(path)
|
|
152
|
+
execute(:put, path, { :comp => 'snapshot' }, {:x_ms_version => "2009-09-19"}).headers[:x_ms_snapshot]
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|