sparqcode-waz-storage 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|