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.
- data/.gitignore +9 -9
- data/CHANGELOG.rdoc +72 -72
- data/Gemfile +4 -4
- data/Gemfile.lock +46 -40
- data/LICENSE +18 -18
- data/README.rdoc +310 -310
- data/lib/waz-blobs.rb +4 -4
- data/lib/waz-queues.rb +6 -6
- data/lib/waz-storage.rb +39 -39
- data/lib/waz-tables.rb +4 -4
- data/lib/waz/blobs/blob_object.rb +122 -122
- data/lib/waz/blobs/container.rb +172 -161
- data/lib/waz/blobs/exceptions.rb +10 -10
- data/lib/waz/blobs/service.rb +181 -156
- data/lib/waz/queues/exceptions.rb +28 -28
- data/lib/waz/queues/message.rb +64 -64
- data/lib/waz/queues/queue.rb +164 -164
- data/lib/waz/queues/service.rb +105 -105
- data/lib/waz/storage/base.rb +70 -70
- data/lib/waz/storage/exceptions.rb +33 -33
- data/lib/waz/storage/validation_rules.rb +25 -25
- data/lib/waz/tables/edm_type_helper.rb +44 -44
- data/lib/waz/tables/exceptions.rb +44 -44
- data/lib/waz/tables/service.rb +178 -178
- data/lib/waz/tables/table.rb +74 -74
- data/lib/waz/tables/table_array.rb +10 -10
- data/rakefile +8 -21
- data/{tests → spec}/configuration.rb +22 -22
- data/{tests/waz/blobs/blob_object_test.rb → spec/waz/blobs/blob_object_spec.rb} +80 -80
- data/{tests/waz/blobs/container_test.rb → spec/waz/blobs/container_spec.rb} +175 -162
- data/{tests/waz/blobs/service_test.rb → spec/waz/blobs/service_spec.rb} +336 -282
- data/{tests/waz/queues/message_test.rb → spec/waz/queues/message_spec.rb} +32 -32
- data/{tests/waz/queues/queue_test.rb → spec/waz/queues/queue_spec.rb} +205 -205
- data/{tests/waz/queues/service_test.rb → spec/waz/queues/service_spec.rb} +298 -298
- data/{tests → spec}/waz/storage/base_tests.rb +81 -81
- data/{tests/waz/storage/shared_key_core_service_test.rb → spec/waz/storage/shared_key_core_service_spec.rb} +141 -141
- data/{tests/waz/tables/service_test.rb → spec/waz/tables/service_spec.rb} +613 -613
- data/{tests/waz/tables/table_test.rb → spec/waz/tables/table_spec.rb} +97 -97
- data/waz-storage.gemspec +29 -27
- metadata +47 -26
data/lib/waz-blobs.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
$:.unshift(File.dirname(__FILE__))
|
2
|
-
require 'waz-storage'
|
3
|
-
# Application Files (massive include)
|
4
|
-
app_files = File.expand_path(File.join(File.dirname(__FILE__), 'waz','blobs', '*.rb'))
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
require 'waz-storage'
|
3
|
+
# Application Files (massive include)
|
4
|
+
app_files = File.expand_path(File.join(File.dirname(__FILE__), 'waz','blobs', '*.rb'))
|
5
5
|
Dir[app_files].each(&method(:load))
|
data/lib/waz-queues.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
$:.unshift(File.dirname(__FILE__))
|
2
|
-
require 'waz-storage'
|
3
|
-
# Application Files (massive include)
|
4
|
-
app_files = File.expand_path(File.join(File.dirname(__FILE__), 'waz','queues', '*.rb'))
|
5
|
-
Dir[app_files].each(&method(:load))
|
6
|
-
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
require 'waz-storage'
|
3
|
+
# Application Files (massive include)
|
4
|
+
app_files = File.expand_path(File.join(File.dirname(__FILE__), 'waz','queues', '*.rb'))
|
5
|
+
Dir[app_files].each(&method(:load))
|
6
|
+
|
data/lib/waz-storage.rb
CHANGED
@@ -1,39 +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
|
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
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
$:.unshift(File.dirname(__FILE__))
|
2
|
-
require 'waz-storage'
|
3
|
-
# Application Files (massive include)
|
4
|
-
app_files = File.expand_path(File.join(File.dirname(__FILE__), 'waz', 'tables', '*.rb'))
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
require 'waz-storage'
|
3
|
+
# Application Files (massive include)
|
4
|
+
app_files = File.expand_path(File.join(File.dirname(__FILE__), 'waz', 'tables', '*.rb'))
|
5
5
|
Dir[app_files].each(&method(:load))
|
@@ -1,122 +1,122 @@
|
|
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, :railsetag
|
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
|
-
self.railsetag = options[:railsetag]
|
53
|
-
end
|
54
|
-
|
55
|
-
# Returns the blob properties from Windows Azure. This properties always come as HTTP Headers and they include standard headers like
|
56
|
-
# <em>Content-Type</em>, <em>Content-Length</em>, etc. combined with custom properties like with <em>x-ms-meta-Name</em>.
|
57
|
-
def metadata
|
58
|
-
self.class.service_instance.get_blob_properties(path)
|
59
|
-
end
|
60
|
-
|
61
|
-
# Returns the actual blob content, this method is specially used to avoid retrieving the whole blob
|
62
|
-
# while iterating and retrieving the blob collection from the Container.
|
63
|
-
def value
|
64
|
-
@value ||= self.class.service_instance.get_blob(path)
|
65
|
-
end
|
66
|
-
|
67
|
-
# Assigns the given value to the blob content. It also stores a local copy of it in order to avoid round trips
|
68
|
-
# on scenarios when you do Save and Display on the same context.
|
69
|
-
def value=(new_value)
|
70
|
-
raise WAZ::Blobs::InvalidOperation if self.snapshot_date
|
71
|
-
self.class.service_instance.put_blob(path, new_value, content_type, metadata)
|
72
|
-
@value = new_value
|
73
|
-
end
|
74
|
-
|
75
|
-
# Stores the blob properties. Those properties are sent as HTTP Headers it's really important that you name your custom
|
76
|
-
# properties with the <em>x-ms-meta</em> prefix, if not the won't be persisted by the Windows Azure Blob Storage API.
|
77
|
-
def put_properties!(properties = {})
|
78
|
-
raise WAZ::Blobs::InvalidOperation if self.snapshot_date
|
79
|
-
self.class.service_instance.set_blob_properties(path, properties)
|
80
|
-
end
|
81
|
-
|
82
|
-
# Stores blob metadata. User metadata must be prefixed with 'x-ms-meta-'. The advantage of this over put_properties
|
83
|
-
# is that it only affect user_metadata and doesn't overwrite any system values, like 'content_type'.
|
84
|
-
def put_metadata!(metadata = {})
|
85
|
-
self.class.service_instance.set_blob_metadata(path, metadata)
|
86
|
-
end
|
87
|
-
|
88
|
-
# Removes the blob from the container.
|
89
|
-
def destroy!
|
90
|
-
self.class.service_instance.delete_blob(path)
|
91
|
-
end
|
92
|
-
|
93
|
-
# Copies the blob to the destination and returns
|
94
|
-
# the copied blob instance.
|
95
|
-
#
|
96
|
-
# destination should be formed as "container/blob"
|
97
|
-
def copy(destination)
|
98
|
-
self.class.service_instance.copy_blob(self.path, destination)
|
99
|
-
properties = self.class.service_instance.get_blob_properties(destination)
|
100
|
-
return BlobObject.new(:name => destination,
|
101
|
-
:url => self.class.service_instance.generate_request_uri(destination),
|
102
|
-
:content_type => properties[:content_type])
|
103
|
-
end
|
104
|
-
|
105
|
-
# Creates and returns a read-only snapshot of a blob as it looked like in time.
|
106
|
-
def snapshot
|
107
|
-
date = self.class.service_instance.snapshot_blob(self.path)
|
108
|
-
properties = self.class.service_instance.get_blob_properties(self.path)
|
109
|
-
return BlobObject.new(:name => self.name,
|
110
|
-
:url => self.class.service_instance.generate_request_uri(self.path) + "?snapshot=#{date}",
|
111
|
-
:content_type => properties[:content_type],
|
112
|
-
:snapshot_date => date)
|
113
|
-
end
|
114
|
-
|
115
|
-
# Returns the blob path. This is specially important when simulating containers inside containers
|
116
|
-
# by enabling the API to point to the appropiated resource.
|
117
|
-
def path
|
118
|
-
url.gsub(/https?:\/\/[^\/]+\//i, '').scan(/([^&]+)/i).first().first()
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
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, :railsetag
|
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
|
+
self.railsetag = options[:railsetag]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the blob properties from Windows Azure. This properties always come as HTTP Headers and they include standard headers like
|
56
|
+
# <em>Content-Type</em>, <em>Content-Length</em>, etc. combined with custom properties like with <em>x-ms-meta-Name</em>.
|
57
|
+
def metadata
|
58
|
+
self.class.service_instance.get_blob_properties(path)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the actual blob content, this method is specially used to avoid retrieving the whole blob
|
62
|
+
# while iterating and retrieving the blob collection from the Container.
|
63
|
+
def value
|
64
|
+
@value ||= self.class.service_instance.get_blob(path)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Assigns the given value to the blob content. It also stores a local copy of it in order to avoid round trips
|
68
|
+
# on scenarios when you do Save and Display on the same context.
|
69
|
+
def value=(new_value)
|
70
|
+
raise WAZ::Blobs::InvalidOperation if self.snapshot_date
|
71
|
+
self.class.service_instance.put_blob(path, new_value, content_type, metadata)
|
72
|
+
@value = new_value
|
73
|
+
end
|
74
|
+
|
75
|
+
# Stores the blob properties. Those properties are sent as HTTP Headers it's really important that you name your custom
|
76
|
+
# properties with the <em>x-ms-meta</em> prefix, if not the won't be persisted by the Windows Azure Blob Storage API.
|
77
|
+
def put_properties!(properties = {})
|
78
|
+
raise WAZ::Blobs::InvalidOperation if self.snapshot_date
|
79
|
+
self.class.service_instance.set_blob_properties(path, properties)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Stores blob metadata. User metadata must be prefixed with 'x-ms-meta-'. The advantage of this over put_properties
|
83
|
+
# is that it only affect user_metadata and doesn't overwrite any system values, like 'content_type'.
|
84
|
+
def put_metadata!(metadata = {})
|
85
|
+
self.class.service_instance.set_blob_metadata(path, metadata)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Removes the blob from the container.
|
89
|
+
def destroy!
|
90
|
+
self.class.service_instance.delete_blob(path)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Copies the blob to the destination and returns
|
94
|
+
# the copied blob instance.
|
95
|
+
#
|
96
|
+
# destination should be formed as "container/blob"
|
97
|
+
def copy(destination)
|
98
|
+
self.class.service_instance.copy_blob(self.path, destination)
|
99
|
+
properties = self.class.service_instance.get_blob_properties(destination)
|
100
|
+
return BlobObject.new(:name => destination,
|
101
|
+
:url => self.class.service_instance.generate_request_uri(destination),
|
102
|
+
:content_type => properties[:content_type])
|
103
|
+
end
|
104
|
+
|
105
|
+
# Creates and returns a read-only snapshot of a blob as it looked like in time.
|
106
|
+
def snapshot
|
107
|
+
date = self.class.service_instance.snapshot_blob(self.path)
|
108
|
+
properties = self.class.service_instance.get_blob_properties(self.path)
|
109
|
+
return BlobObject.new(:name => self.name,
|
110
|
+
:url => self.class.service_instance.generate_request_uri(self.path) + "?snapshot=#{date}",
|
111
|
+
:content_type => properties[:content_type],
|
112
|
+
:snapshot_date => date)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns the blob path. This is specially important when simulating containers inside containers
|
116
|
+
# by enabling the API to point to the appropiated resource.
|
117
|
+
def path
|
118
|
+
url.gsub(/https?:\/\/[^\/]+\//i, '').scan(/([^&]+)/i).first().first()
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/waz/blobs/container.rb
CHANGED
@@ -1,161 +1,172 @@
|
|
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
|
-
#
|
88
|
-
#
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
#
|
94
|
-
def
|
95
|
-
self.class.service_instance.
|
96
|
-
end
|
97
|
-
|
98
|
-
#
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
# the
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
#
|
125
|
-
# the required <em>content_type</em>. The
|
126
|
-
#
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
+
# Returns statistics of the container.
|
88
|
+
#
|
89
|
+
# @param [Hash] options
|
90
|
+
# @option options [String] :maxresults max blobs(5,000 at most)
|
91
|
+
# @option options [String] :marker marker of a page("2!80!MDAwMDE0***********--")
|
92
|
+
#
|
93
|
+
# @return [Hash] {:size => Integer, :files => Integer, :marker => String}
|
94
|
+
def statistics(options)
|
95
|
+
self.class.service_instance.statistics(self.name, options)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Adds metadata for the container.Those properties are sent as HTTP Headers it's really important that you name your custom
|
99
|
+
# properties with the <em>x-ms-meta</em> prefix, if not the won't be persisted by the Windows Azure Blob Storage API.
|
100
|
+
def put_properties!(properties = {})
|
101
|
+
self.class.service_instance.set_container_properties(self.name, properties)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Removes the container from the current account.
|
105
|
+
def destroy!
|
106
|
+
self.class.service_instance.delete_container(self.name)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Retuns a value indicating whether the container is public accessible (i.e. from a Web Browser) or not.
|
110
|
+
def public_access?
|
111
|
+
self.class.service_instance.get_container_acl(self.name)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Sets a value indicating whether the container is public accessible (i.e. from a Web Browser) or not.
|
115
|
+
def public_access=(value)
|
116
|
+
self.class.service_instance.set_container_acl(self.name, value)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns a list of blobs (WAZ::Blobs::BlobObject) contained on the current container.
|
120
|
+
def blobs
|
121
|
+
self.class.service_instance.list_blobs(name).map { |blob| WAZ::Blobs::BlobObject.new(blob) }
|
122
|
+
end
|
123
|
+
|
124
|
+
# Stores a blob on the container with under the given name, with the given content and
|
125
|
+
# the required <em>content_type</em>. <strong>The <em>options</em> parameters if provided
|
126
|
+
# will set the default metadata properties for the blob</strong>.
|
127
|
+
def store(blob_name, payload, content_type, options = {})
|
128
|
+
blob_name.gsub!(%r{^/}, '')
|
129
|
+
self.class.service_instance.put_blob("#{self.name}/#{blob_name}", payload, content_type, options)
|
130
|
+
return BlobObject.new(:name => blob_name,
|
131
|
+
:url => self.class.service_instance.generate_request_uri("#{self.name}/#{blob_name}"),
|
132
|
+
:content_type => content_type)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Uploads the contents of a stream to the specified blob within this container, using
|
136
|
+
# the required <em>content_type</em>. The stream will be uploaded in blocks of size
|
137
|
+
# <em>block_size</em> bytes, which defaults to four megabytes. <strong>The <em>options</em>
|
138
|
+
# parameter, if provided, will set the default metadata properties for the blob</strong>.
|
139
|
+
def upload(blob_name, stream, content_type, options = {}, block_size = 4 * 2**20)
|
140
|
+
blob_name.gsub!(%r{^/}, '')
|
141
|
+
path = "#{self.name}/#{blob_name}"
|
142
|
+
n = 0
|
143
|
+
until stream.eof?
|
144
|
+
self.class.service_instance.put_block path, Base64.encode64('%064d' % n), stream.read(block_size)
|
145
|
+
n += 1
|
146
|
+
end
|
147
|
+
self.class.service_instance.put_block_list path, (0...n).map{|id| Base64.encode64('%064d' % id)}, content_type, options
|
148
|
+
return BlobObject.new(:name => blob_name, :url => self.class.service_instance.generate_request_uri("#{self.name}/#{blob_name}"), :content_type => content_type)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Retrieves the blob by the given name. If the blob isn't found on the current container
|
152
|
+
# it will return nil instead of throwing an exception.
|
153
|
+
def [](blob_name)
|
154
|
+
begin
|
155
|
+
blob_name.gsub!(%r{^/}, '')
|
156
|
+
properties = self.class.service_instance.get_blob_properties("#{self.name}/#{blob_name}")
|
157
|
+
return BlobObject.new(:name => blob_name,
|
158
|
+
:url => self.class.service_instance.generate_request_uri("#{self.name}/#{blob_name}"),
|
159
|
+
:content_type => properties[:content_type],
|
160
|
+
:railsetag => properties[:x_ms_meta_railsetag])
|
161
|
+
rescue RestClient::ResourceNotFound
|
162
|
+
return nil
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
class BlobSecurity
|
167
|
+
Container = 'container'
|
168
|
+
Blob = 'blob'
|
169
|
+
Private = ''
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|