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