waz-storage 0.5.81 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []