waz-storage 0.5.81 → 1.0.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.
@@ -1,3 +1,5 @@
1
+ require 'net/http'
2
+
1
3
  $:.unshift(File.dirname(__FILE__))
2
4
  require 'waz/storage/base'
3
5
  require 'waz/storage/core_service'
@@ -14,4 +16,16 @@ unless String.method_defined? :start_with?
14
16
  self[0, prefix.length] == prefix
15
17
  end
16
18
  end
19
+ end
20
+
21
+ # The Merge method is not defined in the RFC 2616
22
+ # and it's required to Merge entities in Windows Azure
23
+ module Net
24
+ class HTTP < Protocol
25
+ class Merge < HTTPRequest
26
+ METHOD = 'MERGE'
27
+ REQUEST_HAS_BODY = true
28
+ RESPONSE_HAS_BODY = false
29
+ end
30
+ end
17
31
  end
@@ -0,0 +1,20 @@
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
+
9
+ $:.unshift(File.dirname(__FILE__))
10
+ require 'waz-storage'
11
+ require 'waz/tables/exceptions'
12
+ require 'waz/tables/table'
13
+ require 'waz/tables/table_array'
14
+ require 'waz/tables/service'
15
+ require 'waz/tables/edm_type_helper'
16
+
17
+ # extendes the Symbol class to assign a type to an entity field
18
+ class Symbol
19
+ attr_accessor :edm_type
20
+ end
@@ -31,7 +31,7 @@ module WAZ
31
31
  # from the default_connection on WAZ::Storage::Base initialized thru establish_connection!
32
32
  def service_instance
33
33
  options = WAZ::Storage::Base.default_connection.merge(:type_of_service => "blob")
34
- (@service_instances ||= {})[:account_name] ||= Service.new(options)
34
+ (@service_instances ||= {})[options[:account_name]] ||= Service.new(options)
35
35
  end
36
36
  end
37
37
 
@@ -66,7 +66,7 @@ module WAZ
66
66
  # from the default_connection on WAZ::Storage::Base initialized thru establish_connection!
67
67
  def service_instance
68
68
  options = WAZ::Storage::Base.default_connection.merge(:type_of_service => "blob")
69
- (@service_instances ||= {})[:account_name] ||= Service.new(options)
69
+ (@service_instances ||= {})[options[:account_name]] ||= Service.new(options)
70
70
  end
71
71
  end
72
72
 
@@ -29,7 +29,7 @@ module WAZ
29
29
  # from the default_connection on WAZ::Storage::Base initialized thru establish_connection!
30
30
  def service_instance
31
31
  options = WAZ::Storage::Base.default_connection.merge(:type_of_service => "queue")
32
- (@service_instances ||= {})[:account_name] ||= Service.new(options)
32
+ (@service_instances ||= {})[options[:account_name]] ||= Service.new(options)
33
33
  end
34
34
  end
35
35
 
@@ -76,7 +76,7 @@ module WAZ
76
76
  # from the default_connection on WAZ::Storage::Base initialized thru establish_connection!
77
77
  def service_instance
78
78
  options = WAZ::Storage::Base.default_connection.merge(:type_of_service => "queue")
79
- (@service_instances ||= {})[:account_name] ||= Service.new(options)
79
+ (@service_instances ||= {})[options[:account_name]] ||= Service.new(options)
80
80
  end
81
81
  end
82
82
 
@@ -3,15 +3,17 @@ module WAZ
3
3
  # This module is imported by the specific services that use Shared Key authentication profile. On the current implementation
4
4
  # this module is imported from WAZ::Queues::Service and WAZ::Blobs::Service.
5
5
  module SharedKeyCoreService
6
- attr_accessor :account_name, :access_key, :use_ssl, :base_url, :type_of_service
6
+ attr_accessor :account_name, :access_key, :use_ssl, :base_url, :type_of_service, :use_devenv
7
7
 
8
8
  # Creates an instance of the implementor service (internally used by the API).
9
9
  def initialize(options = {})
10
10
  self.account_name = options[:account_name]
11
11
  self.access_key = options[:access_key]
12
+ self.type_of_service = options[:type_of_service]
12
13
  self.use_ssl = options[:use_ssl] or false
13
- self.type_of_service = options[:type_of_service]
14
- self.base_url = "#{options[:type_of_service] or "blobs"}.#{options[:base_url] or "core.windows.net"}"
14
+ self.use_devenv = !!options[:use_devenv]
15
+ self.base_url = "#{options[:type_of_service] or "blobs"}.#{options[:base_url] or "core.windows.net"}" unless self.use_devenv
16
+ self.base_url ||= (options[:base_url] or "core.windows.net")
15
17
  end
16
18
 
17
19
  # Generates a request based on Adam Wiggings' rest-client, including all the required headers
@@ -32,7 +34,8 @@ module WAZ
32
34
  def generate_request_uri(path = nil, options = {})
33
35
  protocol = use_ssl ? "https" : "http"
34
36
  query_params = options.keys.sort{ |a, b| a.to_s <=> b.to_s}.map{ |k| "#{k.to_s.gsub(/_/, '')}=#{CGI.escape(options[k].to_s)}"}.join("&") unless options.nil? or options.empty?
35
- uri = "#{protocol}://#{account_name}.#{base_url}#{(path or "").start_with?("/") ? "" : "/"}#{(path or "")}"
37
+ uri = "#{protocol}://#{base_url}/#{account_name}#{(path or "").start_with?("/") ? "" : "/"}#{(path or "")}" if !self.use_devenv.nil? and self.use_devenv
38
+ uri ||= "#{protocol}://#{account_name}.#{base_url}#{(path or "").start_with?("/") ? "" : "/"}#{(path or "")}"
36
39
  uri << "?#{query_params}" if query_params
37
40
  return uri
38
41
  end
@@ -101,7 +104,7 @@ module WAZ
101
104
  # Generates a Windows Azure Storage call, it internally calls url generation method
102
105
  # and the request generation message.
103
106
  def execute(verb, path, query = {}, headers = {}, payload = nil)
104
- url = generate_request_uri(path, query)
107
+ url = generate_request_uri(path, query)
105
108
  request = generate_request(verb, url, headers, payload)
106
109
  request.execute()
107
110
  end
@@ -11,6 +11,15 @@ module WAZ
11
11
  def valid_name?(name)
12
12
  name =~ /^[a-z0-9][a-z0-9\-]{1,}[^-]$/ && name.length < 64
13
13
  end
14
+
15
+ # Validates that the Table name given matches with the requirements of Windows Azure.
16
+ #
17
+ # -Table names must start with at least one lower / upper character.
18
+ # -Table names can have character or any digit starting from the second position.
19
+ # -Table names must be from 3 through 63 characters long.
20
+ def valid_table_name?(name)
21
+ name =~ /^([a-z]|[A-Z]){1}([a-z]|[A-Z]|\d){2,62}$/
22
+ end
14
23
  end
15
24
  end
16
25
  end
@@ -1,9 +1,9 @@
1
1
  module WAZ
2
2
  module Storage
3
3
  module VERSION #:nodoc:
4
- MAJOR = '0'
5
- MINOR = '5'
6
- TINY = '81'
4
+ MAJOR = '1'
5
+ MINOR = '0'
6
+ TINY = '0'
7
7
  end
8
8
 
9
9
  Version = [VERSION::MAJOR, VERSION::MINOR, VERSION::TINY].compact * '.'
@@ -0,0 +1,45 @@
1
+ module WAZ
2
+ module Tables
3
+ class EdmTypeHelper
4
+ class << self
5
+ def parse_from(item)
6
+ return nil if !item.attributes['m:null'].nil? and item.attributes['m:null'] == 'true'
7
+ case item.attributes['m:type']
8
+ when 'Edm.Int16', 'Edm.Int32', 'Edm.Int64'
9
+ item.text.to_i
10
+ when 'Edm.Single', 'Edm.Double'
11
+ item.text.to_f
12
+ when 'Edm.Boolean'
13
+ item.text == 'true'
14
+ when 'Edm.DateTime'
15
+ Time.parse(item.text)
16
+ when 'Edm.Binary'
17
+ StringIO.new(Base64.decode64(item.text))
18
+ else
19
+ item.text
20
+ end
21
+ end
22
+
23
+ def parse_to(item)
24
+ case item.class.name
25
+ when 'String'
26
+ [item, 'Edm.String']
27
+ when 'Fixnum'
28
+ [item, 'Edm.Int32']
29
+ when 'Float'
30
+ [item, 'Edm.Double']
31
+ when 'TrueClass', 'FalseClass'
32
+ [item, 'Edm.Boolean']
33
+ when 'Time'
34
+ [item.iso8601, 'Edm.DateTime']
35
+ when 'File', 'StringIO'
36
+ item.pos = 0
37
+ [Base64.encode64(item.read), 'Edm.Binary']
38
+ else
39
+ [item, 'Edm.String']
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,45 @@
1
+ module WAZ
2
+ module Tables
3
+ # This exception is raised while trying to create table that already exists.
4
+ class TableAlreadyExists < WAZ::Storage::StorageException
5
+ def initialize(name)
6
+ super("The table #{name} already exists on your account.")
7
+ end
8
+ end
9
+
10
+ # This exception is raised while trying to delete an unexisting table.
11
+ class TableDoesNotExist < WAZ::Storage::StorageException
12
+ def initialize(name)
13
+ super("The specified table #{name} does not exist.")
14
+ end
15
+ end
16
+
17
+ # This exception is raised when an invalid table name is provided.
18
+ class InvalidTableName < WAZ::Storage::StorageException
19
+ def initialize(name)
20
+ super("The table name #{name} is invalid, it must start with at least one lower/upper characted, must be from 3 through 63 characters long and can have character or any digit starting from the second position")
21
+ end
22
+ end
23
+
24
+ # This exception is raised when provided more than the 252 properties allowed by the Rest API.
25
+ class TooManyProperties < WAZ::Storage::StorageException
26
+ def initialize(total)
27
+ super("The entity contains more properties than allowed (252). The entity has #{total} properties.")
28
+ end
29
+ end
30
+
31
+ # This exception is raised when the specified entity already exists.
32
+ class EntityAlreadyExists < WAZ::Storage::StorageException
33
+ def initialize(row_key)
34
+ super("The specified entity already exists. RowKey: #{row_key}")
35
+ end
36
+ end
37
+
38
+ # This exception is raised while trying to delete an unexisting entity.
39
+ class EntityDoesNotExist < WAZ::Storage::StorageException
40
+ def initialize(key)
41
+ super("The specified entity with #{key} does not exist.")
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,178 @@
1
+ module WAZ
2
+ module Tables
3
+ # This is internally used by the waz-tables part of the gem and it exposes the Windows Azure Blob API REST methods
4
+ # implementation. You can use this class to perform an specific operation that isn't provided by the current API.
5
+ class Service
6
+ include WAZ::Storage::SharedKeyCoreService
7
+
8
+ DATASERVICES_NAMESPACE = "http://schemas.microsoft.com/ado/2007/08/dataservices"
9
+ DATASERVICES_METADATA_NAMESPACE = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
10
+
11
+ # Creates a table on the current Windows Azure Storage account.
12
+ def create_table(table_name)
13
+ raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
14
+
15
+ payload = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" \
16
+ "<entry xmlns:d=\"#{DATASERVICES_NAMESPACE}\" xmlns:m=\"#{DATASERVICES_METADATA_NAMESPACE}\" xmlns=\"http://www.w3.org/2005/Atom\">" \
17
+ "<title /><updated>#{Time.now.utc.iso8601}</updated><author><name/></author><id/>" \
18
+ "<content type=\"application/xml\"><m:properties><d:TableName>#{table_name}</d:TableName></m:properties></content></entry>"
19
+
20
+ begin
21
+ execute :post, 'Tables', {}, default_headers, payload
22
+ return {:name => table_name, :url => "#{self.base_url}/Tables('#{table_name}"}
23
+ rescue RestClient::RequestFailed
24
+ raise WAZ::Tables::TableAlreadyExists, table_name if $!.http_code == 409
25
+ end
26
+ end
27
+
28
+ # Delete a table on the current Windows Azure Storage account.
29
+ def delete_table(table_name)
30
+ raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
31
+ begin
32
+ execute :delete, "Tables('#{table_name}')", {}, default_headers
33
+ rescue RestClient::ResourceNotFound
34
+ raise WAZ::Tables::TableDoesNotExist, table_name if $!.http_code == 404
35
+ end
36
+ end
37
+
38
+ # Lists all existing tables on the current storage account.
39
+ # remove Content-Type if it's not working
40
+ def list_tables(next_table_name = nil)
41
+ query = { 'NextTableName' => next_table_name } unless next_table_name.nil?
42
+ content = execute :get, "Tables", query ||= {}, default_headers
43
+
44
+ doc = REXML::Document.new(content)
45
+ tables = REXML::XPath.each(doc, '/feed/entry').map do |item|
46
+ { :name => REXML::XPath.first(item.elements['content'], "m:properties/d:TableName", {"m" => DATASERVICES_METADATA_NAMESPACE, "d" => DATASERVICES_NAMESPACE}).text,
47
+ :url => REXML::XPath.first(item, "id").text }
48
+ end
49
+
50
+ return tables, content.headers[:x_ms_continuation_nexttablename]
51
+ end
52
+
53
+ # Retrieves an existing table on the current storage account.
54
+ # remove Content-Type if it's not working
55
+ def get_table(table_name)
56
+ raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
57
+
58
+ begin
59
+ content = execute :get, "Tables('#{table_name}')", {}, default_headers
60
+ doc = REXML::Document.new(content)
61
+ item = REXML::XPath.first(doc, "entry")
62
+ return { :name => REXML::XPath.first(item.elements['content'], "m:properties/d:TableName", {"m" => DATASERVICES_METADATA_NAMESPACE, "d" => DATASERVICES_NAMESPACE}).text,
63
+ :url => REXML::XPath.first(item, "id").text }
64
+ rescue RestClient::ResourceNotFound
65
+ raise WAZ::Tables::TableDoesNotExist, table_name if $!.http_code == 404
66
+ end
67
+ end
68
+
69
+ # Insert a new entity on the provided table for the current storage account
70
+ # TODO: catch all api errors as described on Table Service Error Codes on MSDN (http://msdn.microsoft.com/en-us/library/dd179438.aspx)
71
+ def insert_entity(table_name, entity)
72
+ raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
73
+ raise WAZ::Tables::TooManyProperties, entity.length if entity.length > 252
74
+
75
+ begin
76
+ response = execute(:post, table_name, {}, default_headers, generate_payload(table_name, entity))
77
+ return parse_response(response)
78
+ rescue RestClient::RequestFailed
79
+ raise WAZ::Tables::EntityAlreadyExists, entity[:row_key] if $!.http_code == 409 and $!.response.body.include?('EntityAlreadyExists')
80
+ end
81
+ end
82
+
83
+ # Update an existing entity on the current storage account.
84
+ # TODO: handle specific errors
85
+ def update_entity(table_name, entity)
86
+ raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
87
+ response = execute(:put, "#{table_name}(PartitionKey='#{entity[:partition_key]}',RowKey='#{entity[:row_key]}')", {}, default_headers.merge({'If-Match' => '*'}) , generate_payload(table_name, entity))
88
+ return parse_response(response)
89
+ end
90
+
91
+ # Merge an existing entity on the current storage account.
92
+ # The Merge Entity operation updates an existing entity by updating the entity's properties.
93
+ # This operation does not replace the existing entity, as the Update Entity operation does
94
+ # TODO: handle specific errors
95
+ def merge_entity(table_name, entity)
96
+ raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
97
+ response = execute(:merge, "#{table_name}(PartitionKey='#{entity[:partition_key]}',RowKey='#{entity[:row_key]}')", {}, default_headers.merge({'If-Match' => '*'}), generate_payload(table_name, entity))
98
+ return parse_response(response)
99
+ end
100
+
101
+ # Delete an existing entity in a table.
102
+ def delete_entity(table_name, partition_key, row_key)
103
+ raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
104
+
105
+ begin
106
+ execute :delete, "#{table_name}(PartitionKey='#{partition_key}',RowKey='#{row_key}')", {}, default_headers.merge({'If-Match' => '*'})
107
+ rescue RestClient::ResourceNotFound
108
+ raise WAZ::Tables::TableDoesNotExist, table_name if $!.http_code == 404 and $!.response.body.include?('TableNotFound')
109
+ raise WAZ::Tables::EntityDoesNotExist, "(PartitionKey='#{partition_key}',RowKey='#{row_key}')" if $!.http_code == 404
110
+ end
111
+ end
112
+
113
+ # Retrieves an existing entity on the current storage account.
114
+ # TODO: handle specific errors
115
+ def get_entity(table_name, partition_key, row_key)
116
+ raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
117
+ response = execute(:get, "#{table_name}(PartitionKey='#{partition_key}',RowKey='#{row_key}')", {}, default_headers)
118
+ return parse_response(response)
119
+ end
120
+
121
+ # Retrieves a set of entities on the current storage account for a given query.
122
+ # When the :top => n is passed it returns only the first n rows that match with the query
123
+ # Optional parameters:
124
+ # * :headers a hash containing the request headers
125
+ # * :expression the filter query that will be executed against the table (see http://msdn.microsoft.com/en-us/library/dd179421.aspx for more information),
126
+ # * :top limits the amount of fields for this query.
127
+ # * :continuation_token the hash obtained when you perform a query that has more than 1000 records or exceeds the allowed timeout (see http://msdn.microsoft.com/en-us/library/dd135718.aspx)
128
+ def query(table_name, options = {})
129
+ raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
130
+ query = {'$filter' => (options[:expression] or '') }
131
+ query.merge!({ '$top' => options[:top] }) unless options[:top].nil?
132
+ query.merge!(options[:continuation_token]) unless options[:continuation_token].nil?
133
+ response = execute :get, "#{table_name}()", query, default_headers
134
+ continuation_token = {'NextPartitionKey' => response.headers[:x_ms_continuation_nextpartitionkey], 'NextRowKey' => response.headers[:x_ms_continuation_nextrowkey]}
135
+ parse_response(response, continuation_token)
136
+ end
137
+
138
+ private
139
+ def generate_payload(table_name, entity)
140
+ payload = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" \
141
+ "<entry xmlns:d=\"#{DATASERVICES_NAMESPACE}\" xmlns:m=\"#{DATASERVICES_METADATA_NAMESPACE}\" xmlns=\"http://www.w3.org/2005/Atom\">" \
142
+ "<id>#{generate_request_uri "#{table_name}"}(PartitionKey='#{entity[:partition_key]}',RowKey='#{entity[:row_key]}')</id>" \
143
+ "<title /><updated>#{Time.now.utc.iso8601}</updated><author><name /></author><link rel=\"edit\" title=\"#{table_name}\" href=\"#{table_name}(PartitionKey='#{entity[:partition_key]}',RowKey='#{entity[:row_key]}')\" />" \
144
+ "<content type=\"application/xml\"><m:properties>"
145
+
146
+ entity.sort_by { |k| k.to_s }.each do |k,v|
147
+ value, type = EdmTypeHelper.parse_to(v)[0].to_s, EdmTypeHelper.parse_to(v)[1].to_s
148
+ payload << (!v.nil? ? "<d:#{k.to_s} m:type=\"#{k.edm_type || type}\">#{value}</d:#{k.to_s}>" : "<d:#{k.to_s} m:type=\"#{k.edm_type || type}\" m:null=\"true\" />") unless k.eql?(:partition_key) or k.eql?(:row_key)
149
+ end
150
+
151
+ payload << "<d:PartitionKey>#{entity[:partition_key]}</d:PartitionKey>" \
152
+ "<d:RowKey>#{entity[:row_key]}</d:RowKey>" \
153
+ "</m:properties></content></entry>"
154
+ return payload
155
+ end
156
+
157
+ def parse_response(response, continuation_token = nil)
158
+ doc = REXML::Document.new(response)
159
+ entities = REXML::XPath.each(doc, '//entry').map do |entry|
160
+ fields = REXML::XPath.each(entry.elements['content'], 'm:properties/*', {"m" => DATASERVICES_METADATA_NAMESPACE}).map do |f|
161
+ { f.name.gsub(/PartitionKey/i, 'partition_key').gsub(/RowKey/i, 'row_key').to_sym => EdmTypeHelper.parse_from(f) }
162
+ end
163
+ Hash[*fields.collect {|h| h.to_a}.flatten]
164
+ end
165
+ entities = WAZ::Tables::TableArray.new(entities)
166
+ entities.continuation_token = continuation_token
167
+ return (REXML::XPath.first(doc, '/feed')) ? entities : entities.first
168
+ end
169
+
170
+ def default_headers
171
+ { 'Date' => Time.new.httpdate,
172
+ 'Content-Type' => 'application/atom+xml',
173
+ 'DataServiceVersion' => '1.0;NetFx',
174
+ 'MaxDataServiceVersion' => '1.0;NetFx' }
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,75 @@
1
+ module WAZ
2
+ module Tables
3
+ # This class represents a Table on Windows Azure Tables API. These are the methods implemented from Microsoft's API description
4
+ # available on MSDN at http://msdn.microsoft.com/en-us/library/dd179423.aspx
5
+ #
6
+ # # list available tables
7
+ # tables = WAZ::Tables::Table.list
8
+ #
9
+ # # list more tables
10
+ # WAZ::Tables::Table.list(tables.continuation_token)
11
+ #
12
+ # # get a specific table
13
+ # my_table = WAZ::Tables::Table.find('my-table')
14
+ #
15
+ # # delete table
16
+ # my_table.destroy!
17
+ #
18
+ # # create a new table
19
+ # WAZ::Tables::Table.create('new-table')
20
+ #
21
+ class Table
22
+ class << self
23
+ INVALID_TABLE_ERROR_MESSAGE = "must start with at least one lower/upper characted, can have character or any digit starting from the second position, must be from 3 through 63 characters long"
24
+
25
+ # Finds a table by name. It will return nil if no table was found.
26
+ def find(table_name)
27
+ raise WAZ::Storage::InvalidParameterValue, {:name => table_name, :values => [INVALID_TABLE_ERROR_MESSAGE]} unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
28
+ begin
29
+ WAZ::Tables::Table.new(service_instance.get_table(table_name))
30
+ rescue WAZ::Tables::TableDoesNotExist
31
+ return nil
32
+ end
33
+ end
34
+
35
+ # Returns an array of the existing tables (WAZ::Tables::Table) on the current
36
+ # Windows Azure Storage account.
37
+ def list(continuation_token = {})
38
+ table_list, next_table_name = service_instance.list_tables(continuation_token['NextTableName'])
39
+ tables = TableArray.new(table_list.map { |table| WAZ::Tables::Table.new({ :name => table[:name], :url => table[:url] }) })
40
+ tables.continuation_token = {'NextTableName' => next_table_name} unless next_table_name.nil?
41
+ return tables
42
+ end
43
+
44
+ # Creates a table on the current account.
45
+ def create(table_name)
46
+ raise WAZ::Storage::InvalidParameterValue, {:name => table_name, :values => [INVALID_TABLE_ERROR_MESSAGE]} unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
47
+ WAZ::Tables::Table.new(service_instance.create_table(table_name))
48
+ end
49
+
50
+ # This method is internally used by this class. It's the way we keep a single instance of the
51
+ # service that wraps the calls the Windows Azure Tables API. It's initialized with the values
52
+ # from the default_connection on WAZ::Storage::Base initialized thru establish_connection!
53
+ def service_instance
54
+ options = WAZ::Storage::Base.default_connection.merge(:type_of_service => "table")
55
+ (@service_instances ||= {})[options[:account_name]] ||= Service.new(options)
56
+ end
57
+ end
58
+
59
+ attr_accessor :name, :url
60
+
61
+ def initialize(options = {})
62
+ raise WAZ::Storage::InvalidOption, :name unless options.keys.include?(:name) and !options[:name].empty?
63
+ raise WAZ::Storage::InvalidOption, :url unless options.keys.include?(:url) and !options[:url].empty?
64
+ raise WAZ::Storage::InvalidParameterValue, {:name => options[:name], :values => [INVALID_TABLE_ERROR_MESSAGE]} unless WAZ::Storage::ValidationRules.valid_table_name?(options[:name])
65
+ self.name = options[:name]
66
+ self.url = options[:url]
67
+ end
68
+
69
+ # Removes the table from the current account.
70
+ def destroy!
71
+ self.class.service_instance.delete_table(self.name)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,11 @@
1
+ module WAZ
2
+ module Tables
3
+ class TableArray < Array
4
+ attr_accessor :continuation_token
5
+
6
+ def initialize(array)
7
+ super(array)
8
+ end
9
+ end
10
+ end
11
+ end
data/rakefile CHANGED
@@ -5,10 +5,9 @@ require 'rake/gempackagetask'
5
5
  require 'rake/rdoctask'
6
6
  require 'lib/waz-storage'
7
7
 
8
-
9
8
  namespace :test do
10
9
  Spec::Rake::SpecTask.new('run_with_rcov') do |t|
11
- t.spec_files = FileList['tests/waz/queues/*.rb', 'tests/waz/blobs/*.rb', 'tests/waz/storage/*.rb']
10
+ t.spec_files = FileList['tests/waz/queues/*.rb', 'tests/waz/blobs/*.rb', 'tests/waz/tables/*.rb', 'tests/waz/storage/*.rb']
12
11
  t.rcov = true
13
12
  t.rcov_opts = ['--text-report', '--exclude', "exclude.*/.gem,test,Library,#{ENV['GEM_HOME']}", '--sort', 'coverage' ]
14
13
  t.spec_opts = ['-cfn']
@@ -49,4 +48,4 @@ namespace :docs do
49
48
  t.rdoc_files.include('README.rdoc')
50
49
  t.rdoc_files.include('lib/**/*.rb')
51
50
  end
52
- end
51
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waz-storage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.81
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johnny G. Halife
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-03 00:00:00 -03:00
12
+ date: 2010-02-04 00:00:00 -03:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -55,9 +55,15 @@ files:
55
55
  - lib/waz/storage/exceptions.rb
56
56
  - lib/waz/storage/validation_rules.rb
57
57
  - lib/waz/storage/version.rb
58
+ - lib/waz/tables/edm_type_helper.rb
59
+ - lib/waz/tables/exceptions.rb
60
+ - lib/waz/tables/service.rb
61
+ - lib/waz/tables/table.rb
62
+ - lib/waz/tables/table_array.rb
58
63
  - lib/waz-blobs.rb
59
64
  - lib/waz-queues.rb
60
65
  - lib/waz-storage.rb
66
+ - lib/waz-tables.rb
61
67
  has_rdoc: true
62
68
  homepage: http://waz-storage.heroku.com
63
69
  licenses: []