lib/azure/table/batch.rb in azure-0.6.0 vs lib/azure/table/batch.rb in azure-0.6.1
- old
+ new
@@ -1,330 +1,330 @@
-#-------------------------------------------------------------------------
-# # Copyright (c) Microsoft and contributors. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#--------------------------------------------------------------------------
-require 'uuid'
-
-require 'azure/table/serialization'
-require 'azure/table/table_service'
-require 'azure/table/batch_response'
-require 'azure/core/http/http_error'
-
-module Azure
- module Table
- # Represents a batch of table operations.
- #
- # Example usage (block syntax):
- #
- # results = Batch.new "table", "partition" do
- # insert "row1", {"meta"=>"data"}
- # insert "row2", {"meta"=>"data"}
- # end.execute
- #
- # which is equivalent to (fluent syntax):
- #
- # results = Batch.new("table", "partition")
- # .insert("row1", {"meta"=>"data"})
- # .insert("row2", {"meta"=>"data"})
- # .execute
- #
- # which is equivalent to (as class):
- #
- # svc = TableSerice.new
- #
- # batch = Batch.new "table", "partition"
- # batch.insert "row1", {"meta"=>"data"}
- # batch.insert "row2", {"meta"=>"data"}
- #
- # results = svc.execute_batch batch
- #
- class Batch
- def initialize(table, partition, &block)
- @table = table
- @partition = partition
- @operations = []
- @entity_keys = []
- @table_service = Azure::Table::TableService.new
- uuid = UUID.new
- @batch_id = "batch_" + uuid.generate
- @changeset_id = "changeset_" + uuid.generate
-
- self.instance_eval(&block) if block_given?
- end
-
- private
- attr_reader :table
- attr_reader :partition
- attr_reader :table_service
-
- attr_accessor :operations
- attr_accessor :entity_keys
- attr_accessor :changeset_id
-
- public
- attr_accessor :batch_id
-
- protected
- def execute
- @table_service.execute_batch(self)
- end
-
- protected
- class ResponseWrapper
- def initialize(hash)
- @hash = hash
- end
-
- def uri
- @hash[:uri]
- end
-
- def status_code
- @hash[:status_code].to_i
- end
-
- def body
- @hash[:body]
- end
- end
-
- protected
- def add_operation(method, uri, body=nil, headers=nil)
- op = {
- :method => method,
- :uri => uri,
- :body => body,
- :headers => headers
- }
- operations.push op
- end
-
- protected
- def check_entity_key(key)
- raise ArgumentError, "Only allowed to perform a single operation per entity, and there is already a operation registered in this batch for the key: #{key}." if entity_keys.include? key
- entity_keys.push key
- end
-
- public
- def parse_response(response)
- responses = BatchResponse.parse response.body
- new_responses = []
-
- (0..responses.length-1).each { |index|
- operation = operations[index]
- response = responses[index]
-
- if response[:status_code].to_i > 299
- # failed
- error = Azure::Core::Http::HTTPError.new(ResponseWrapper.new(response.merge({:uri=>operation[:uri]})))
- error.description = response[:message] if (error.description || '').strip == ''
- raise error
- else
- # success
- case operation[:method]
- when :post
- # entity from body
- result = Azure::Table::Serialization.hash_from_entry_xml(response[:body])
-
- entity = Azure::Table::Entity.new
- entity.table = table
- entity.updated = result[:updated]
- entity.etag = response[:headers]["etag"] || result[:etag]
- entity.properties = result[:properties]
-
- new_responses.push entity
- when :put, :merge
- # etag from headers
- new_responses.push response[:headers]["etag"]
- when :delete
- # true
- new_responses.push nil
- end
- end
- }
-
- new_responses
- end
-
- public
- def to_body
- body = ""
- body.define_singleton_method(:add_line) do |a| self << (a||nil) + "\n" end
-
- body.add_line "--#{batch_id}"
- body.add_line "Content-Type: multipart/mixed; boundary=#{changeset_id}"
- body.add_line ""
-
- content_id = 1
- operations.each { |op|
- body.add_line "--#{changeset_id}"
- body.add_line "Content-Type: application/http"
- body.add_line "Content-Transfer-Encoding: binary"
- body.add_line ""
- body.add_line "#{op[:method].to_s.upcase} #{op[:uri]} HTTP/1.1"
- body.add_line "Content-ID: #{content_id}"
-
- if op[:headers]
- op[:headers].each { |k,v|
- body.add_line "#{k}: #{v}"
- }
- end
-
- if op[:body]
- body.add_line "Content-Type: application/atom+xml;type=entry"
- body.add_line "Content-Length: #{op[:body].bytesize.to_s}"
- body.add_line ""
- body.add_line op[:body]
- else
- body.add_line ""
- end
-
- content_id += 1
- }
- body.add_line "--#{changeset_id}--"
- body.add_line "--#{batch_id}--"
- end
-
- # Public: Inserts new entity to the table.
- #
- # ==== Attributes
- #
- # * +row_key+ - String. The row key
- # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
- #
- # See http://msdn.microsoft.com/en-us/library/windowsazure/dd179433
- public
- def insert(row_key, entity_values)
- check_entity_key(row_key)
-
- body = Azure::Table::Serialization.hash_to_entry_xml({
- "PartitionKey" => partition,
- "RowKey" => row_key
- }.merge(entity_values) ).to_xml
-
- add_operation(:post, @table_service.entities_uri(table), body)
- self
- end
-
- # Public: Updates an existing entity in a table. The Update Entity operation replaces
- # the entire entity and can be used to remove properties.
- #
- # ==== Attributes
- #
- # * +row_key+ - String. The row key
- # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
- # * +options+ - Hash. Optional parameters.
- #
- # ==== Options
- #
- # Accepted key/value pairs in options parameter are:
- # * :if_match - String. A matching condition which is required for update (optional, Default="*")
- # * :create_if_not_exists - Boolean. If true, and partition_key and row_key do not reference and existing entity,
- # that entity will be inserted. If false, the operation will fail. (optional, Default=false)
- #
- # See http://msdn.microsoft.com/en-us/library/windowsazure/dd179427
- public
- def update(row_key, entity_values, options={})
- check_entity_key(row_key)
-
- uri = @table_service.entities_uri(table, partition, row_key)
-
- headers = {}
- headers["If-Match"] = options[:if_match] || "*" unless options[:create_if_not_exists]
-
- body = Azure::Table::Serialization.hash_to_entry_xml(entity_values).to_xml
-
- add_operation(:put, uri, body, headers)
- self
- end
-
- # Public: Updates an existing entity by updating the entity's properties. This operation
- # does not replace the existing entity, as the update_entity operation does.
- #
- # ==== Attributes
- #
- # * +row_key+ - String. The row key
- # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
- # * +options+ - Hash. Optional parameters.
- #
- # ==== Options
- #
- # Accepted key/value pairs in options parameter are:
- # * +if_match+ - String. A matching condition which is required for update (optional, Default="*")
- # * +create_if_not_exists+ - Boolean. If true, and partition_key and row_key do not reference and existing entity,
- # that entity will be inserted. If false, the operation will fail. (optional, Default=false)
- #
- # See http://msdn.microsoft.com/en-us/library/windowsazure/dd179392
- public
- def merge(row_key, entity_values, options={})
- check_entity_key(row_key)
-
- uri = @table_service.entities_uri(table, partition, row_key)
-
- headers = {}
- headers["If-Match"] = options[:if_match] || "*" unless options[:create_if_not_exists]
-
- body = Azure::Table::Serialization.hash_to_entry_xml(entity_values).to_xml
-
- add_operation(:merge, uri, body, headers)
- self
- end
-
- # Public: Inserts or updates an existing entity within a table by merging new property values into the entity.
- #
- # ==== Attributes
- #
- # * +row_key+ - String. The row key
- # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
- #
- # See http://msdn.microsoft.com/en-us/library/windowsazure/hh452241
- public
- def insert_or_merge(row_key, entity_values)
- merge(row_key, entity_values, { :create_if_not_exists => true })
- self
- end
-
- # Public: Inserts or updates a new entity into a table.
- #
- # ==== Attributes
- #
- # * +row_key+ - String. The row key
- # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
- #
- # See http://msdn.microsoft.com/en-us/library/windowsazure/hh452242
- public
- def insert_or_replace(row_key, entity_values)
- update(row_key, entity_values, { :create_if_not_exists => true })
- self
- end
-
- # Public: Deletes an existing entity in the table.
- #
- # ==== Attributes
- #
- # * +row_key+ - String. The row key
- # * +options+ - Hash. Optional parameters.
- #
- # ==== Options
- #
- # Accepted key/value pairs in options parameter are:
- # * +if_match+ - String. A matching condition which is required for update (optional, Default="*")
- #
- # See http://msdn.microsoft.com/en-us/library/windowsazure/dd135727
- public
- def delete(row_key, options={})
- add_operation(:delete, @table_service.entities_uri(table, partition, row_key), nil, {"If-Match"=> options[:if_match] || "*"})
- self
- end
- end
- end
+#-------------------------------------------------------------------------
+# # Copyright (c) Microsoft and contributors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#--------------------------------------------------------------------------
+require 'uuid'
+
+require 'azure/table/serialization'
+require 'azure/table/table_service'
+require 'azure/table/batch_response'
+require 'azure/core/http/http_error'
+
+module Azure
+ module Table
+ # Represents a batch of table operations.
+ #
+ # Example usage (block syntax):
+ #
+ # results = Batch.new "table", "partition" do
+ # insert "row1", {"meta"=>"data"}
+ # insert "row2", {"meta"=>"data"}
+ # end.execute
+ #
+ # which is equivalent to (fluent syntax):
+ #
+ # results = Batch.new("table", "partition")
+ # .insert("row1", {"meta"=>"data"})
+ # .insert("row2", {"meta"=>"data"})
+ # .execute
+ #
+ # which is equivalent to (as class):
+ #
+ # svc = TableSerice.new
+ #
+ # batch = Batch.new "table", "partition"
+ # batch.insert "row1", {"meta"=>"data"}
+ # batch.insert "row2", {"meta"=>"data"}
+ #
+ # results = svc.execute_batch batch
+ #
+ class Batch
+ def initialize(table, partition, &block)
+ @table = table
+ @partition = partition
+ @operations = []
+ @entity_keys = []
+ @table_service = Azure::Table::TableService.new
+ uuid = UUID.new
+ @batch_id = "batch_" + uuid.generate
+ @changeset_id = "changeset_" + uuid.generate
+
+ self.instance_eval(&block) if block_given?
+ end
+
+ private
+ attr_reader :table
+ attr_reader :partition
+ attr_reader :table_service
+
+ attr_accessor :operations
+ attr_accessor :entity_keys
+ attr_accessor :changeset_id
+
+ public
+ attr_accessor :batch_id
+
+ protected
+ def execute
+ @table_service.execute_batch(self)
+ end
+
+ protected
+ class ResponseWrapper
+ def initialize(hash)
+ @hash = hash
+ end
+
+ def uri
+ @hash[:uri]
+ end
+
+ def status_code
+ @hash[:status_code].to_i
+ end
+
+ def body
+ @hash[:body]
+ end
+ end
+
+ protected
+ def add_operation(method, uri, body=nil, headers=nil)
+ op = {
+ :method => method,
+ :uri => uri,
+ :body => body,
+ :headers => headers
+ }
+ operations.push op
+ end
+
+ protected
+ def check_entity_key(key)
+ raise ArgumentError, "Only allowed to perform a single operation per entity, and there is already a operation registered in this batch for the key: #{key}." if entity_keys.include? key
+ entity_keys.push key
+ end
+
+ public
+ def parse_response(response)
+ responses = BatchResponse.parse response.body
+ new_responses = []
+
+ (0..responses.length-1).each { |index|
+ operation = operations[index]
+ response = responses[index]
+
+ if response[:status_code].to_i > 299
+ # failed
+ error = Azure::Core::Http::HTTPError.new(ResponseWrapper.new(response.merge({:uri=>operation[:uri]})))
+ error.description = response[:message] if (error.description || '').strip == ''
+ raise error
+ else
+ # success
+ case operation[:method]
+ when :post
+ # entity from body
+ result = Azure::Table::Serialization.hash_from_entry_xml(response[:body])
+
+ entity = Azure::Table::Entity.new
+ entity.table = table
+ entity.updated = result[:updated]
+ entity.etag = response[:headers]["etag"] || result[:etag]
+ entity.properties = result[:properties]
+
+ new_responses.push entity
+ when :put, :merge
+ # etag from headers
+ new_responses.push response[:headers]["etag"]
+ when :delete
+ # true
+ new_responses.push nil
+ end
+ end
+ }
+
+ new_responses
+ end
+
+ public
+ def to_body
+ body = ""
+ body.define_singleton_method(:add_line) do |a| self << (a||nil) + "\n" end
+
+ body.add_line "--#{batch_id}"
+ body.add_line "Content-Type: multipart/mixed; boundary=#{changeset_id}"
+ body.add_line ""
+
+ content_id = 1
+ operations.each { |op|
+ body.add_line "--#{changeset_id}"
+ body.add_line "Content-Type: application/http"
+ body.add_line "Content-Transfer-Encoding: binary"
+ body.add_line ""
+ body.add_line "#{op[:method].to_s.upcase} #{op[:uri]} HTTP/1.1"
+ body.add_line "Content-ID: #{content_id}"
+
+ if op[:headers]
+ op[:headers].each { |k,v|
+ body.add_line "#{k}: #{v}"
+ }
+ end
+
+ if op[:body]
+ body.add_line "Content-Type: application/atom+xml;type=entry"
+ body.add_line "Content-Length: #{op[:body].bytesize.to_s}"
+ body.add_line ""
+ body.add_line op[:body]
+ else
+ body.add_line ""
+ end
+
+ content_id += 1
+ }
+ body.add_line "--#{changeset_id}--"
+ body.add_line "--#{batch_id}--"
+ end
+
+ # Public: Inserts new entity to the table.
+ #
+ # ==== Attributes
+ #
+ # * +row_key+ - String. The row key
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
+ #
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/dd179433
+ public
+ def insert(row_key, entity_values)
+ check_entity_key(row_key)
+
+ body = Azure::Table::Serialization.hash_to_entry_xml({
+ "PartitionKey" => partition,
+ "RowKey" => row_key
+ }.merge(entity_values) ).to_xml
+
+ add_operation(:post, @table_service.entities_uri(table), body)
+ self
+ end
+
+ # Public: Updates an existing entity in a table. The Update Entity operation replaces
+ # the entire entity and can be used to remove properties.
+ #
+ # ==== Attributes
+ #
+ # * +row_key+ - String. The row key
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
+ # * +options+ - Hash. Optional parameters.
+ #
+ # ==== Options
+ #
+ # Accepted key/value pairs in options parameter are:
+ # * :if_match - String. A matching condition which is required for update (optional, Default="*")
+ # * :create_if_not_exists - Boolean. If true, and partition_key and row_key do not reference and existing entity,
+ # that entity will be inserted. If false, the operation will fail. (optional, Default=false)
+ #
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/dd179427
+ public
+ def update(row_key, entity_values, options={})
+ check_entity_key(row_key)
+
+ uri = @table_service.entities_uri(table, partition, row_key)
+
+ headers = {}
+ headers["If-Match"] = options[:if_match] || "*" unless options[:create_if_not_exists]
+
+ body = Azure::Table::Serialization.hash_to_entry_xml(entity_values).to_xml
+
+ add_operation(:put, uri, body, headers)
+ self
+ end
+
+ # Public: Updates an existing entity by updating the entity's properties. This operation
+ # does not replace the existing entity, as the update_entity operation does.
+ #
+ # ==== Attributes
+ #
+ # * +row_key+ - String. The row key
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
+ # * +options+ - Hash. Optional parameters.
+ #
+ # ==== Options
+ #
+ # Accepted key/value pairs in options parameter are:
+ # * +if_match+ - String. A matching condition which is required for update (optional, Default="*")
+ # * +create_if_not_exists+ - Boolean. If true, and partition_key and row_key do not reference and existing entity,
+ # that entity will be inserted. If false, the operation will fail. (optional, Default=false)
+ #
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/dd179392
+ public
+ def merge(row_key, entity_values, options={})
+ check_entity_key(row_key)
+
+ uri = @table_service.entities_uri(table, partition, row_key)
+
+ headers = {}
+ headers["If-Match"] = options[:if_match] || "*" unless options[:create_if_not_exists]
+
+ body = Azure::Table::Serialization.hash_to_entry_xml(entity_values).to_xml
+
+ add_operation(:merge, uri, body, headers)
+ self
+ end
+
+ # Public: Inserts or updates an existing entity within a table by merging new property values into the entity.
+ #
+ # ==== Attributes
+ #
+ # * +row_key+ - String. The row key
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
+ #
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/hh452241
+ public
+ def insert_or_merge(row_key, entity_values)
+ merge(row_key, entity_values, { :create_if_not_exists => true })
+ self
+ end
+
+ # Public: Inserts or updates a new entity into a table.
+ #
+ # ==== Attributes
+ #
+ # * +row_key+ - String. The row key
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
+ #
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/hh452242
+ public
+ def insert_or_replace(row_key, entity_values)
+ update(row_key, entity_values, { :create_if_not_exists => true })
+ self
+ end
+
+ # Public: Deletes an existing entity in the table.
+ #
+ # ==== Attributes
+ #
+ # * +row_key+ - String. The row key
+ # * +options+ - Hash. Optional parameters.
+ #
+ # ==== Options
+ #
+ # Accepted key/value pairs in options parameter are:
+ # * +if_match+ - String. A matching condition which is required for update (optional, Default="*")
+ #
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/dd135727
+ public
+ def delete(row_key, options={})
+ add_operation(:delete, @table_service.entities_uri(table, partition, row_key), nil, {"If-Match"=> options[:if_match] || "*"})
+ self
+ end
+ end
+ end
end
\ No newline at end of file