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.
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