waz-storage 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. data/.gitignore +9 -9
  2. data/CHANGELOG.rdoc +72 -72
  3. data/Gemfile +4 -4
  4. data/Gemfile.lock +46 -40
  5. data/LICENSE +18 -18
  6. data/README.rdoc +310 -310
  7. data/lib/waz-blobs.rb +4 -4
  8. data/lib/waz-queues.rb +6 -6
  9. data/lib/waz-storage.rb +39 -39
  10. data/lib/waz-tables.rb +4 -4
  11. data/lib/waz/blobs/blob_object.rb +122 -122
  12. data/lib/waz/blobs/container.rb +172 -161
  13. data/lib/waz/blobs/exceptions.rb +10 -10
  14. data/lib/waz/blobs/service.rb +181 -156
  15. data/lib/waz/queues/exceptions.rb +28 -28
  16. data/lib/waz/queues/message.rb +64 -64
  17. data/lib/waz/queues/queue.rb +164 -164
  18. data/lib/waz/queues/service.rb +105 -105
  19. data/lib/waz/storage/base.rb +70 -70
  20. data/lib/waz/storage/exceptions.rb +33 -33
  21. data/lib/waz/storage/validation_rules.rb +25 -25
  22. data/lib/waz/tables/edm_type_helper.rb +44 -44
  23. data/lib/waz/tables/exceptions.rb +44 -44
  24. data/lib/waz/tables/service.rb +178 -178
  25. data/lib/waz/tables/table.rb +74 -74
  26. data/lib/waz/tables/table_array.rb +10 -10
  27. data/rakefile +8 -21
  28. data/{tests → spec}/configuration.rb +22 -22
  29. data/{tests/waz/blobs/blob_object_test.rb → spec/waz/blobs/blob_object_spec.rb} +80 -80
  30. data/{tests/waz/blobs/container_test.rb → spec/waz/blobs/container_spec.rb} +175 -162
  31. data/{tests/waz/blobs/service_test.rb → spec/waz/blobs/service_spec.rb} +336 -282
  32. data/{tests/waz/queues/message_test.rb → spec/waz/queues/message_spec.rb} +32 -32
  33. data/{tests/waz/queues/queue_test.rb → spec/waz/queues/queue_spec.rb} +205 -205
  34. data/{tests/waz/queues/service_test.rb → spec/waz/queues/service_spec.rb} +298 -298
  35. data/{tests → spec}/waz/storage/base_tests.rb +81 -81
  36. data/{tests/waz/storage/shared_key_core_service_test.rb → spec/waz/storage/shared_key_core_service_spec.rb} +141 -141
  37. data/{tests/waz/tables/service_test.rb → spec/waz/tables/service_spec.rb} +613 -613
  38. data/{tests/waz/tables/table_test.rb → spec/waz/tables/table_spec.rb} +97 -97
  39. data/waz-storage.gemspec +29 -27
  40. metadata +47 -26
data/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
@@ -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
- # 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
- :railsetag => properties[:x_ms_meta_railsetag])
150
- rescue RestClient::ResourceNotFound
151
- return nil
152
- end
153
- end
154
- end
155
- class BlobSecurity
156
- Container = 'container'
157
- Blob = 'blob'
158
- Private = ''
159
- end
160
- end
161
- end
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