azure 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. data/Gemfile +3 -0
  2. data/Gemfile.lock +36 -0
  3. data/README.md +3 -0
  4. data/Rakefile +81 -0
  5. data/azure.gemspec +20 -9
  6. data/lib/azure.rb +4 -0
  7. data/lib/azure/atom.rb +170 -0
  8. data/lib/azure/auth.rb +29 -0
  9. data/lib/azure/blobs.rb +620 -0
  10. data/lib/azure/blobs/blob.rb +360 -0
  11. data/lib/azure/blobs/container.rb +209 -0
  12. data/lib/azure/blobs/service.rb +396 -0
  13. data/lib/azure/blobs/shared_access_signature.rb +84 -0
  14. data/lib/azure/blobs/uri.rb +60 -0
  15. data/lib/azure/configuration.rb +121 -0
  16. data/lib/azure/core/auth/shared_key.rb +95 -0
  17. data/lib/azure/core/auth/shared_key_lite.rb +34 -0
  18. data/lib/azure/core/collection.rb +118 -0
  19. data/lib/azure/core/service.rb +43 -0
  20. data/lib/azure/core/signer.rb +32 -0
  21. data/lib/azure/core/utils/interval.rb +97 -0
  22. data/lib/azure/core/utils/queryable.rb +74 -0
  23. data/lib/azure/core/utils/storage_service_properties.rb +83 -0
  24. data/lib/azure/core/utils/string.rb +59 -0
  25. data/lib/azure/error.rb +72 -0
  26. data/lib/azure/queues.rb +272 -0
  27. data/lib/azure/queues/message.rb +174 -0
  28. data/lib/azure/queues/queue.rb +187 -0
  29. data/lib/azure/queues/service.rb +263 -0
  30. data/lib/azure/queues/service_properties.rb +152 -0
  31. data/lib/azure/queues/uri.rb +78 -0
  32. data/lib/azure/request.rb +102 -0
  33. data/lib/azure/response.rb +93 -0
  34. data/lib/azure/service_bus.rb +4 -0
  35. data/lib/azure/service_bus/auth/authorizer.rb +22 -0
  36. data/lib/azure/service_bus/auth/uri.rb +52 -0
  37. data/lib/azure/service_bus/auth/wrap.rb +37 -0
  38. data/lib/azure/service_bus/auth/wrap_service.rb +76 -0
  39. data/lib/azure/service_bus/auth/wrap_token.rb +45 -0
  40. data/lib/azure/service_bus/auth/wrap_token_manager.rb +46 -0
  41. data/lib/azure/service_bus/brokered_message.rb +139 -0
  42. data/lib/azure/service_bus/brokered_message_serializer.rb +113 -0
  43. data/lib/azure/service_bus/queues.rb +194 -0
  44. data/lib/azure/service_bus/queues/queue.rb +100 -0
  45. data/lib/azure/service_bus/queues/queue_serializer.rb +51 -0
  46. data/lib/azure/service_bus/queues/service.rb +154 -0
  47. data/lib/azure/service_bus/queues/uri.rb +80 -0
  48. data/lib/azure/service_bus/rules.rb +110 -0
  49. data/lib/azure/service_bus/rules/rule.rb +97 -0
  50. data/lib/azure/service_bus/rules/service.rb +122 -0
  51. data/lib/azure/service_bus/rules/uri.rb +39 -0
  52. data/lib/azure/service_bus/service_bus_service.rb +22 -0
  53. data/lib/azure/service_bus/subscriptions.rb +170 -0
  54. data/lib/azure/service_bus/subscriptions/service.rb +133 -0
  55. data/lib/azure/service_bus/subscriptions/subscription.rb +164 -0
  56. data/lib/azure/service_bus/subscriptions/subscription_serializer.rb +74 -0
  57. data/lib/azure/service_bus/subscriptions/uri.rb +71 -0
  58. data/lib/azure/service_bus/topics.rb +120 -0
  59. data/lib/azure/service_bus/topics/service.rb +98 -0
  60. data/lib/azure/service_bus/topics/topic.rb +122 -0
  61. data/lib/azure/service_bus/topics/topic_serializer.rb +44 -0
  62. data/lib/azure/service_bus/topics/uri.rb +58 -0
  63. data/lib/azure/service_runtime/client/goal_state_pipe_monitor.rb +21 -0
  64. data/lib/azure/service_runtime/client/goal_state_protocol.rb +18 -0
  65. data/lib/azure/service_runtime/client/runtime_client.rb +135 -0
  66. data/lib/azure/service_runtime/deployment.rb +24 -0
  67. data/lib/azure/service_runtime/local_resource.rb +15 -0
  68. data/lib/azure/service_runtime/role.rb +17 -0
  69. data/lib/azure/service_runtime/role_environment.rb +206 -0
  70. data/lib/azure/service_runtime/role_environment_change.rb +32 -0
  71. data/lib/azure/service_runtime/role_instance.rb +35 -0
  72. data/lib/azure/service_runtime/role_instance_endpoint.rb +14 -0
  73. data/lib/azure/tables.rb +215 -0
  74. data/lib/azure/tables/auth/shared_key.rb +71 -0
  75. data/lib/azure/tables/auth/shared_key_lite.rb +30 -0
  76. data/lib/azure/tables/entities_collection.rb +66 -0
  77. data/lib/azure/tables/entity.rb +127 -0
  78. data/lib/azure/tables/service.rb +211 -0
  79. data/lib/azure/tables/table.rb +129 -0
  80. data/lib/azure/tables/tables_collection.rb +62 -0
  81. data/lib/azure/tables/types.rb +65 -0
  82. data/lib/azure/tables/uri.rb +62 -0
  83. data/test/fixtures/32px-fulls-black.jpg +0 -0
  84. data/test/fixtures/all_containers.xml +20 -0
  85. data/test/fixtures/all_tables.xml +22 -0
  86. data/test/fixtures/create_table_response_entry.xml +15 -0
  87. data/test/fixtures/error.xml +5 -0
  88. data/test/fixtures/insert_entity_response_entry.xml +25 -0
  89. data/test/fixtures/messages.xml +12 -0
  90. data/test/fixtures/query_entities_empty_response.xml +7 -0
  91. data/test/fixtures/query_entities_response.xml +45 -0
  92. data/test/fixtures/queue_service_properties.xml +22 -0
  93. data/test/fixtures/queue_service_properties_original.xml +19 -0
  94. data/test/fixtures/queues.xml +16 -0
  95. data/test/fixtures/sb_default_create_queue_response.xml +23 -0
  96. data/test/fixtures/sb_default_create_topic_response.xml +18 -0
  97. data/test/fixtures/sb_get_access_token_response.txt +1 -0
  98. data/test/fixtures/sb_queues_runtime_peek_message_response_headers.txt +9 -0
  99. data/test/integration/blobs/auth_test.rb +19 -0
  100. data/test/integration/blobs/blob_test.rb +61 -0
  101. data/test/integration/blobs/clear_page_range_test.rb +19 -0
  102. data/test/integration/blobs/copy_test.rb +33 -0
  103. data/test/integration/blobs/create_blobs_test.rb +51 -0
  104. data/test/integration/blobs/create_container_test.rb +13 -0
  105. data/test/integration/blobs/create_snapshot_test.rb +17 -0
  106. data/test/integration/blobs/delete_blob_snapshots_test.rb +19 -0
  107. data/test/integration/blobs/delete_blobs_test.rb +25 -0
  108. data/test/integration/blobs/delete_container_test.rb +24 -0
  109. data/test/integration/blobs/delete_snapshot_test.rb +17 -0
  110. data/test/integration/blobs/get_blob_snapshot_test.rb +18 -0
  111. data/test/integration/blobs/get_blobs_test.rb +31 -0
  112. data/test/integration/blobs/get_page_range_test.rb +19 -0
  113. data/test/integration/blobs/list_blobs_test.rb +39 -0
  114. data/test/integration/blobs/list_containers_test.rb +28 -0
  115. data/test/integration/blobs/manage_blob_leases_test.rb +45 -0
  116. data/test/integration/blobs/manage_blob_metadata_test.rb +51 -0
  117. data/test/integration/blobs/manage_blob_properties_test.rb +25 -0
  118. data/test/integration/blobs/manage_blob_service_properties_test.rb +38 -0
  119. data/test/integration/blobs/manage_container_metadata_test.rb +46 -0
  120. data/test/integration/blobs/manage_container_permissions_test.rb +17 -0
  121. data/test/integration/blobs/update_page_range_test.rb +20 -0
  122. data/test/integration/queues/clear_messages_test.rb +22 -0
  123. data/test/integration/queues/create_queue_test.rb +13 -0
  124. data/test/integration/queues/delete_message_test.rb +42 -0
  125. data/test/integration/queues/delete_queue_test.rb +24 -0
  126. data/test/integration/queues/get_messages_test.rb +39 -0
  127. data/test/integration/queues/list_queues_test.rb +43 -0
  128. data/test/integration/queues/manage_queue_metadata_test.rb +45 -0
  129. data/test/integration/queues/manage_queue_service_properties_test.rb +27 -0
  130. data/test/integration/queues/peek_messages_test.rb +55 -0
  131. data/test/integration/queues/put_message_test.rb +31 -0
  132. data/test/integration/queues/update_message_test.rb +46 -0
  133. data/test/integration/service_bus/auth_test.rb +18 -0
  134. data/test/integration/service_bus/queues/create_queue_test.rb +25 -0
  135. data/test/integration/service_bus/queues/delete_message_from_queue_test.rb +29 -0
  136. data/test/integration/service_bus/queues/delete_queue_test.rb +25 -0
  137. data/test/integration/service_bus/queues/get_queue_test.rb +23 -0
  138. data/test/integration/service_bus/queues/list_queues_test.rb +39 -0
  139. data/test/integration/service_bus/queues/peek_message_from_queue_test.rb +34 -0
  140. data/test/integration/service_bus/queues/read_and_delete_message_from_queue_test.rb +31 -0
  141. data/test/integration/service_bus/queues/send_message_to_queue_test.rb +22 -0
  142. data/test/integration/service_bus/queues/unlock_message_from_queue_test.rb +36 -0
  143. data/test/integration/service_bus/rules/create_rule_test.rb +19 -0
  144. data/test/integration/service_bus/rules/delete_rule_test.rb +17 -0
  145. data/test/integration/service_bus/rules/get_rule_test.rb +21 -0
  146. data/test/integration/service_bus/rules/list_rules_test.rb +24 -0
  147. data/test/integration/service_bus/rules/rule_test.rb +16 -0
  148. data/test/integration/service_bus/subscriptions/create_subscription_test.rb +25 -0
  149. data/test/integration/service_bus/subscriptions/delete_message_from_subscription_test.rb +31 -0
  150. data/test/integration/service_bus/subscriptions/delete_subscription_test.rb +30 -0
  151. data/test/integration/service_bus/subscriptions/fetch_subscription_test.rb +28 -0
  152. data/test/integration/service_bus/subscriptions/list_subscriptions_test.rb +23 -0
  153. data/test/integration/service_bus/subscriptions/peek_lock_message_from_subscription_test.rb +42 -0
  154. data/test/integration/service_bus/subscriptions/read_delete_message_from_subscription_test.rb +36 -0
  155. data/test/integration/service_bus/subscriptions/subscription_test.rb +31 -0
  156. data/test/integration/service_bus/subscriptions/unlock_message_from_subscription_test.rb +43 -0
  157. data/test/integration/service_bus/topics/create_topic_test.rb +25 -0
  158. data/test/integration/service_bus/topics/delete_topic_test.rb +25 -0
  159. data/test/integration/service_bus/topics/get_topic_test.rb +23 -0
  160. data/test/integration/service_bus/topics/list_topics_test.rb +39 -0
  161. data/test/integration/service_bus/topics/send_message_to_topic_test.rb +23 -0
  162. data/test/integration/tables/auth_test.rb +29 -0
  163. data/test/integration/tables/creating_tables_test.rb +16 -0
  164. data/test/integration/tables/delete_entity_test.rb +39 -0
  165. data/test/integration/tables/deleting_table_test.rb +22 -0
  166. data/test/integration/tables/insert_entity_test.rb +23 -0
  167. data/test/integration/tables/merge_entity_test.rb +28 -0
  168. data/test/integration/tables/query_entities_test.rb +131 -0
  169. data/test/integration/tables/query_tables_test.rb +63 -0
  170. data/test/integration/tables/update_entity_test.rb +54 -0
  171. data/test/integration/test_helper.rb +14 -0
  172. data/test/support/blobs.rb +12 -0
  173. data/test/support/env.rb +5 -0
  174. data/test/support/fixtures.rb +22 -0
  175. data/test/support/stubs.rb +28 -0
  176. data/test/support/table_names.rb +44 -0
  177. data/test/test_helper.rb +10 -0
  178. data/test/unit/atom_test.rb +58 -0
  179. data/test/unit/auth_test.rb +24 -0
  180. data/test/unit/blobs/blob_test.rb +5 -0
  181. data/test/unit/blobs/container_test.rb +67 -0
  182. data/test/unit/blobs/service_test.rb +17 -0
  183. data/test/unit/blobs/shared_access_signature_test.rb +66 -0
  184. data/test/unit/blobs_test.rb +156 -0
  185. data/test/unit/core/service_test.rb +57 -0
  186. data/test/unit/core/utils/interval_test.rb +70 -0
  187. data/test/unit/core/utils/queryable_test.rb +69 -0
  188. data/test/unit/core/utils/storage_service_properties_test.rb +66 -0
  189. data/test/unit/error_test.rb +39 -0
  190. data/test/unit/queues/message_test.rb +40 -0
  191. data/test/unit/queues/queue_test.rb +64 -0
  192. data/test/unit/queues/service_properties.rb +35 -0
  193. data/test/unit/request_test.rb +38 -0
  194. data/test/unit/response_test.rb +43 -0
  195. data/test/unit/service_bus/auth/authorizer_test.rb +27 -0
  196. data/test/unit/service_bus/auth/wrap_token_test.rb +28 -0
  197. data/test/unit/service_bus/queues/queue_test.rb +38 -0
  198. data/test/unit/service_bus/topics/topic_test.rb +33 -0
  199. data/test/unit/service_runtime/data/goalstate.xml +9 -0
  200. data/test/unit/service_runtime/data/roleenvironmentdata.xml +29 -0
  201. data/test/unit/service_runtime/data/runtime.xml +6 -0
  202. data/test/unit/service_runtime/role_environment_test.rb +144 -0
  203. data/test/unit/tables/auth/shared_key_lite_test.rb +39 -0
  204. data/test/unit/tables/auth/shared_key_test.rb +45 -0
  205. data/test/unit/tables/entities_collection_test.rb +39 -0
  206. data/test/unit/tables/entity_test.rb +72 -0
  207. data/test/unit/tables/table_test.rb +57 -0
  208. data/test/unit/tables_test.rb +302 -0
  209. data/test/unit/types_test.rb +67 -0
  210. metadata +310 -47
  211. data/LICENSE +0 -0
  212. data/README +0 -0
@@ -0,0 +1,32 @@
1
+ require "openssl"
2
+ require "base64"
3
+ require "azure/configuration"
4
+
5
+ module Azure
6
+ module Core
7
+ # Public: Utility class to sign strings with HMAC-256 and then encode the
8
+ # signed string using Base64.
9
+ class Signer
10
+ # The Azure account's access key.
11
+ attr :access_key
12
+
13
+ # Public: Initialize the Signer.
14
+ #
15
+ # access_key - The Azure access_key encoded in Base64. Defaults to the one
16
+ # in the global configuration.
17
+ def initialize(access_key=Azure.config.access_key)
18
+ @access_key = Base64.strict_decode64(access_key)
19
+ end
20
+
21
+ # Public: Generate an HMAC signature.
22
+ #
23
+ # body - The string to sign.
24
+ #
25
+ # Returns a Base64 String signed with HMAC.
26
+ def sign(body)
27
+ signed = OpenSSL::HMAC.digest("sha256", access_key, body)
28
+ Base64.strict_encode64(signed)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,97 @@
1
+ require "delegate"
2
+
3
+ module Azure
4
+ module Core
5
+ module Utils
6
+ # Public: Helper class to decorate a numeric duration so it can be output
7
+ # as an ISO8601-compliant Duration. (This class only implements a subset
8
+ # of the ISO8601 standard, since it's what Microsoft appears to be using.)
9
+ #
10
+ # Examples
11
+ #
12
+ # # Initialize an Interval from a Number, or use .try_convert to be
13
+ # # intelligent:
14
+ #
15
+ # Interval.new(10) #=> PT10S
16
+ # Interval.try_convert(10) #=> PT10S
17
+ # Interval.try_convert("PT10S") #=> PT10S
18
+ # Interval.try_convert(nil) #=> nil
19
+ class Interval < SimpleDelegator
20
+
21
+ # Public: Attempt to convert an object into an Interval.
22
+ #
23
+ # object - An object that might be converted into an Interval.
24
+ #
25
+ # Returns an Interval or nil.
26
+ def self.try_convert(object)
27
+ if object.respond_to?(:to_interval)
28
+ object.to_interval
29
+ elsif object.respond_to?(:to_int)
30
+ new(object)
31
+ elsif object.respond_to?(:to_str)
32
+ parse(object)
33
+ else
34
+ nil
35
+ end
36
+ end
37
+
38
+ # Public: Parse a String into an Interval.
39
+ #
40
+ # string - A String in the Interval format.
41
+ #
42
+ # Returns an Interval.
43
+ def self.parse(string)
44
+ re = /
45
+ P (?<d>[\d]+D)? # match days
46
+ (?:
47
+ T (?<h>[\d]+H)? # match hours
48
+ (?<m>[\d]+M)? # match minutes
49
+ (?<s>[\d\.]+S)? # match seconds
50
+ )?
51
+ /x
52
+
53
+ match = re.match(string)
54
+
55
+ return nil if match.nil?
56
+
57
+ days = match[:d].to_i
58
+ hours = match[:h].to_i
59
+ minutes = match[:m].to_i
60
+ seconds = match[:s].to_f
61
+
62
+ new(seconds + minutes * 60 + hours * 3600 + days * 86400)
63
+ end
64
+
65
+ # Public: Return this amount of seconds formatted as an interval.
66
+ #
67
+ # Returns a String.
68
+ def to_s
69
+ days = to_i / 86400
70
+ hours = (to_i % 86400) / 3600
71
+ minutes = (to_i % 3600) / 60
72
+ seconds = (self % 60)
73
+
74
+ days = "%<d>s" % {
75
+ d: days.zero? ? nil : "#{days}D"
76
+ }
77
+
78
+ time = "%<h>s%<m>s%<s>s" % {
79
+ h: hours.zero? ? nil : "#{hours}H",
80
+ m: minutes.zero? ? nil : "#{minutes}M",
81
+ s: nonzero? && seconds.zero? ? nil : "#{seconds}S"
82
+ }
83
+
84
+ "P#{days}" + (time.empty? ? "" : "T#{time}")
85
+ end
86
+ alias_method :inspect, :to_s
87
+
88
+ # Public: Convert this object into an interval.
89
+ #
90
+ # Returns self.
91
+ def to_interval
92
+ self
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,74 @@
1
+ require "uri"
2
+
3
+ module Azure
4
+ module Core
5
+ module Utils
6
+ # Convenience module to build query strings.
7
+ module Queryable
8
+ # Public: Build a query from a options hash.
9
+ #
10
+ # options - A hash of options.
11
+ # :partition_key - Only get entities with this PartitionKey
12
+ # (optional, but :row_key must also be
13
+ # included).
14
+ # :row_key - Only get entities with this RowKey
15
+ # (optional, but :partition_key must also
16
+ # be included).
17
+ # :select - An array with property names. Only these
18
+ # properties are provided from the
19
+ # returned returned set of entities.
20
+ # :filter - A string to filter results.
21
+ # :top - Returns only the top n results from the set.
22
+ # :skip - Skips the first n results from the set.
23
+ #
24
+ # Returns a query string
25
+ def build_query(options)
26
+ select = options.fetch(:select, nil)
27
+ select = "$select=#{select.join(",")}" if select
28
+
29
+ filter = options.fetch(:filter, nil)
30
+ filter = "$filter=#{filter}" if filter
31
+
32
+ top = options.fetch(:top, nil)
33
+ top = "$top=#{top}" if top
34
+
35
+ skip = options.fetch(:skip, nil)
36
+ skip = "$skip=#{skip}" if skip
37
+
38
+ next_partition_key = options.fetch("NextPartitionKey", nil)
39
+ next_partition_key = "NextPartitionKey=#{next_partition_key}" if next_partition_key
40
+
41
+ next_row_key = options.fetch("NextRowKey", nil)
42
+ next_row_key = "NextRowKey=#{next_row_key}" if next_row_key
43
+
44
+ next_table_name = options.fetch("NextTableName", nil)
45
+ next_table_name = "NextTableName=#{next_table_name}" if next_table_name
46
+
47
+ query = [next_table_name, next_partition_key, next_row_key,
48
+ select, filter, top, skip].compact.join("&")
49
+
50
+ query != "" ? ::URI.encode(query) : nil
51
+ end
52
+
53
+ # Public: Build a Hash with translated keys from a options hash.
54
+ #
55
+ # options - A hash of options.
56
+ # :top - Returns only the top n results from the set.
57
+ # :skip - Skips the first n results from the set.
58
+ #
59
+ # Returns a query string
60
+ def translate_options_hash(options)
61
+ hash = {}
62
+
63
+ skip = options.fetch(:skip, nil)
64
+ hash["$skip"] = skip if skip
65
+
66
+ top = options.fetch(:top, nil)
67
+ hash["$top"] = top if top
68
+
69
+ hash
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,83 @@
1
+ require 'nokogiri'
2
+
3
+ module Azure
4
+ module Core
5
+ module Utils
6
+ class StorageServiceProperties
7
+
8
+ REQUIRED_ELEMENTS = {
9
+ "Version" => true,
10
+ "Delete" => true,
11
+ "Read" => true,
12
+ "Write" => true,
13
+ "Enabled" => true,
14
+ "RetentionPolicy / Enabled" => true,
15
+ "RetentionPolicy / Days" => true,
16
+ "IncludeAPIs" => "Metrics"
17
+ }
18
+
19
+ def self.hash_to_xml(hash)
20
+ doc = Nokogiri::XML::Document.new
21
+ doc.encoding = "utf-8"
22
+ root = Nokogiri::XML::Node.new("StorageServiceProperties", doc)
23
+ iterate_hash(hash, root, doc)
24
+ doc << root
25
+ validate(doc)
26
+ doc
27
+ end
28
+
29
+ def self.hash_to_xml_string(hash)
30
+ hash_to_xml(hash).to_xml
31
+ end
32
+
33
+ def self.xml_string_to_hash(xml)
34
+ doc = Nokogiri::XML.parse(xml)
35
+ iterate_node(doc)["StorageServiceProperties"]
36
+ end
37
+
38
+ private
39
+
40
+ def self.iterate_node(node)
41
+ node.children.each_with_object({}) do |node, hash|
42
+
43
+ next if !node.element?
44
+
45
+ if node.children.size == 1
46
+ hash[node.name] = node.text
47
+ else
48
+ hash[node.name] = iterate_node(node)
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.iterate_hash(hash, root, doc)
54
+ hash.each do |key, value|
55
+ if value.is_a?(Hash)
56
+ node = Nokogiri::XML::Node.new(key, doc)
57
+ iterate_hash(value, node, doc)
58
+ root << node
59
+ else
60
+ node = Nokogiri::XML::Node.new(key, doc)
61
+ node.content = value
62
+ root << node
63
+ end
64
+ end
65
+ end
66
+
67
+ def self.validate(doc)
68
+ missing_keys = []
69
+
70
+ REQUIRED_ELEMENTS.each do |key,value|
71
+ if value == true
72
+ missing_keys << key if (doc / key).empty?
73
+ elsif (doc / value).empty? || (doc / key).empty?
74
+ missing_keys << key
75
+ end
76
+ end
77
+
78
+ raise StandardError, "StorageServicePropertiesMissing", missing_keys if !missing_keys.empty?
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,59 @@
1
+ module Azure
2
+ module Core
3
+ module Utils
4
+ # Convenience module to build query strings.
5
+ module String
6
+ # The following methods have been borrowed (stolen?) from ActiveSupport
7
+
8
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
9
+ # is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
10
+ #
11
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
12
+ #
13
+ # Examples:
14
+ # "active_model".camelize # => "ActiveModel"
15
+ # "active_model".camelize(:lower) # => "activeModel"
16
+ # "active_model/errors".camelize # => "ActiveModel::Errors"
17
+ # "active_model/errors".camelize(:lower) # => "activeModel::Errors"
18
+ #
19
+ # As a rule of thumb you can think of +camelize+ as the inverse of +underscore+,
20
+ # though there are cases where that does not hold:
21
+ #
22
+ # "SSLError".underscore.camelize # => "SslError"
23
+ def camelize(term, uppercase_first_letter = true)
24
+ string = term.to_s
25
+ acronyms = {}
26
+ if uppercase_first_letter
27
+ string = string.sub(/^[a-z\d]*/) { acronyms[$&] || $&.capitalize }
28
+ else
29
+ string = string.sub(/^(?:(?=a)b(?=\b|[A-Z_])|\w)/) { $&.downcase }
30
+ end
31
+ string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
32
+ end
33
+
34
+ # Makes an underscored, lowercase form from the expression in the string.
35
+ #
36
+ # Changes '::' to '/' to convert namespaces to paths.
37
+ #
38
+ # Examples:
39
+ # "ActiveModel".underscore # => "active_model"
40
+ # "ActiveModel::Errors".underscore # => "active_model/errors"
41
+ #
42
+ # As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
43
+ # though there are cases where that does not hold:
44
+ #
45
+ # "SSLError".underscore.camelize # => "SslError"
46
+ def underscore(camel_cased_word)
47
+ word = camel_cased_word.to_s.dup
48
+ word.gsub!(/::/, '/')
49
+ word.gsub!(/(?:([A-Za-z\d])|^)((?=a)b)(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
50
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
51
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
52
+ word.tr!("-", "_")
53
+ word.downcase!
54
+ word
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,72 @@
1
+ require "xml"
2
+
3
+ module Azure
4
+ # Public: Superclass for errors generated from this library, so people can
5
+ # just rescue this for generic error handling.
6
+ class Error < StandardError; end
7
+
8
+ # Public: FileUploadError
9
+ class FileUploadError < ::Azure::Error; end
10
+
11
+ # Public: Class for handling all HTTP response errors.
12
+ class HTTPError < ::Azure::Error
13
+ # Public: The HTTP status code of this error.
14
+ #
15
+ # Returns a Fixnum.
16
+ attr :code
17
+
18
+ # Public: The type of error, as described by the Azure documentation.
19
+ #
20
+ # Returns a String.
21
+ attr :type
22
+
23
+ # Public: The English description of this error.
24
+ #
25
+ # Returns a String.
26
+ attr :description
27
+
28
+ # Public: Initialize an error.
29
+ #
30
+ # http_response - An Azure::Response.
31
+ def initialize(http_response)
32
+ @http_response = http_response
33
+ @code = http_response.code
34
+ parse_response
35
+ super("#{type} (#{code}): #{description}")
36
+ end
37
+
38
+ # Extract the relevant information from the response's body. If the response
39
+ # body is not an XML, we return an 'Unknown' error with the entire body as
40
+ # the description.
41
+ #
42
+ # Returns nothing.
43
+ def parse_response
44
+ if @http_response.body.include?("<")
45
+ document = XML::Parser.string(@http_response.body).parse
46
+
47
+ # FIXME: For some reason document.find_first("code") (or "//code", etc.)
48
+ # and document.find_first("message") return nil, while this works. It
49
+ # makes no sense that this works and that doesn't. Oh well.
50
+ document.root.children.each do |child|
51
+ @type = child.content if child.name == "code"
52
+ @description = child.content if child.name == "message"
53
+ end
54
+ else
55
+ @type = "Unknown"
56
+ @description = @http_response.body.strip
57
+ end
58
+ end
59
+ end
60
+
61
+ # Public: Mixin that gives a few convenience methods for handling errors in
62
+ # domain objects.
63
+ module ErrorHandler
64
+ # Public: Get/Set the current error in this object.
65
+ attr_accessor :error
66
+
67
+ # Public: Check if this object doesn't have any errors.
68
+ def valid?
69
+ error.nil?
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,272 @@
1
+ require "nokogiri"
2
+ require "azure/queues/service"
3
+ require "azure/queues/queue"
4
+ require "azure/queues/message"
5
+ require "azure/queues/service_properties"
6
+ require "azure/queues/uri"
7
+
8
+ module Azure
9
+ module Queues
10
+ # Public: This operation gets the properties of a storage account’s Queue Service.
11
+ # If the operation is unsuccessful the resulting object will have the error
12
+ # accessible through the #error method.
13
+ #
14
+ # options - Options for this query (default: {}):
15
+ # :timeout - (optional) timeout for the request in seconds
16
+ # service - The backend service to implement this (optional).
17
+ #
18
+ # Returns an Azure::Queues::ServiceProperties object
19
+ def self.get_service_properties(options = {}, service=Azure::Queues::Services::GetServiceProperties.new)
20
+ response = service.call(options)
21
+
22
+ if response.success?
23
+ result = Nokogiri::XML(response.body)
24
+ node = result.xpath('//StorageServiceProperties').first
25
+ ServiceProperties.from_node(node)
26
+ else
27
+ ServiceProperties.from_error(response.error)
28
+ end
29
+ end
30
+
31
+ # Public: This operation sets the properties of a storage account’s Queue Service.
32
+ # If the operation is unsuccessful the resulting object will have the error
33
+ # accessible through the #error method.
34
+ #
35
+ # properties - an Azure::Queues::ServiceProperties object
36
+ # options - Options for this query (default: {}):
37
+ # :timeout - (optional) timeout for the request in seconds
38
+ # service - The backend service to implement this (optional).
39
+ #
40
+ # Returns an Azure::Queues::ServiceProperties object
41
+ def self.set_service_properties(properties, options = {}, service=Azure::Queues::Services::SetServiceProperties.new)
42
+ response = service.call(properties, options)
43
+ properties.error = response.error unless response.success?
44
+ response.success?
45
+ end
46
+
47
+ # Public: This operation lists all of the queues in a given storage account.
48
+ # GET http://myaccount.queue.core.windows.net?comp=list
49
+ #
50
+ # options - Options for this query (default: {}):
51
+ # :prefix - Filters the results to return only queues with names that begin with the specified prefix.
52
+ # :marker - A string value that identifies the portion of the list to be returned with the next list operation.
53
+ # :maxresults - Specifies the maximum number of queues to return
54
+ # :include_metadata - Include this parameter to specify that the container's metadata be returned as part of the response body.
55
+ # service - The backend service to implement this (optional).
56
+ #
57
+ # Returns an Array of Query elements.
58
+ def self.all(options = {}, service=Azure::Queues::Services::ListQueues.new)
59
+ response = service.call(options)
60
+
61
+ if response.success?
62
+ result = Nokogiri::XML(response.body)
63
+ result.xpath('//Queues/Queue').map {|node| Queue.from_node(node) }
64
+ else
65
+ []
66
+ end
67
+ end
68
+
69
+ # Public: Try to create a new queue. If the operation is unsuccessful,
70
+ # the resulting Queue object will have the error accessible through the
71
+ # #error method.
72
+ #
73
+ # name - The name of the container.
74
+ # metadata - User defined metadata for this container (optional).
75
+ # service - The backend service to implement this (optional).
76
+ #
77
+ # Returns a Queue.
78
+ def self.create(name, metadata={}, service=Azure::Queues::Services::CreateQueue.new)
79
+ response = service.call(name, metadata)
80
+
81
+ if response.success?
82
+ Queue.new(name)
83
+ else
84
+ Queue.from_error(response.error)
85
+ end
86
+ end
87
+
88
+ # Public: Delete a Queue from the server. If the delete operation fails,
89
+ # the queue is invalidated. Otherwise it's frozen.
90
+ #
91
+ # queue - An Azure::Queues::Queue.
92
+ # service - The backend service to implement this (optional).
93
+ #
94
+ # Returns true|false to indicate success.
95
+ def self.delete(queue, service=Azure::Queues::Services::DeleteQueue.new)
96
+ response = service.call(queue.name)
97
+
98
+ if response.success?
99
+ queue.freeze
100
+ else
101
+ queue.error = response.error
102
+ end
103
+
104
+ response.success?
105
+ end
106
+
107
+ # Public: Obtain the queue metadata. This updates the queue and
108
+ # changes the metadata internally if successful. If the operation fails, the
109
+ # queue's metadata does not change, and the queue is invalidated.
110
+ #
111
+ # queue - An Azure::Queues::Queue.
112
+ # service - The backend service to implement this (optional).
113
+ #
114
+ # Returns a Hash.
115
+ def self.load_metadata(queue, service=Azure::Queues::Services::GetMetadata.new)
116
+ response = service.call(queue.name)
117
+
118
+ if response.success?
119
+ queue.extract_metadata(response.headers)
120
+ else
121
+ queue.error = response.error
122
+ {}
123
+ end
124
+ end
125
+
126
+ # Public: Save the current metadata of a queue in the storage service.
127
+ # If the operation fails, the queue is invalidated.
128
+ #
129
+ # queue - An Azure::Queues::Queue.
130
+ # service - The backend service to implement this (optional).
131
+ #
132
+ # Returns true|false to indicate success.
133
+ def self.save_metadata(queue, service=Azure::Queues::Services::SetMetadata.new)
134
+ response = service.call(queue.name, queue.metadata)
135
+ queue.error = response.error unless response.success?
136
+ response.success?
137
+ end
138
+
139
+ ## MESSAGES
140
+
141
+ # Public: Try to add a new message to the back of the message queue. If the
142
+ # operation is unsuccessful, the message is invalidated.
143
+ #
144
+ # queue - An Azure::Queues::Queue.
145
+ # message - An Azure::Queues::Message.
146
+ # options - A Hash of options for this operation.
147
+ # :visibilitytimeout - Time in seconds until the message becomes
148
+ # visible in the queue (default: 0).
149
+ # :messagettl - Time-to-live interval for the message in
150
+ # seconds. Can go up to 7 days (the
151
+ # default).
152
+ # service - The backend service to implement this (optional).
153
+ #
154
+ # Returns the message.
155
+ def self.put_message(queue, message, options = {}, service=Azure::Queues::Services::PutMessage.new)
156
+ response = service.call(queue.name, message.text, options)
157
+
158
+ if response.success?
159
+ message.queue = queue
160
+ else
161
+ message.error = response.error
162
+ end
163
+
164
+ message
165
+ end
166
+
167
+ # Public: Get one or more messages from the front of the queue.
168
+ #
169
+ # FIXME: Handle errors.
170
+ #
171
+ # queue - An Azure::Queues::Queue.
172
+ # options - A Hash of options for this operation (optional):
173
+ # :visibilitytimeout - Update the returned messages'
174
+ # visibilitytimeout to this number (at least
175
+ # 1, the default).
176
+ # :numofmessages - A number between 1 and 32 of messages to
177
+ # return (default: 1).
178
+ # service - The backend service to implement this (optional).
179
+ #
180
+ # Returns an Array of Messages.
181
+ def self.get_messages(queue, options = {}, service = Azure::Queues::Services::GetMessages.new)
182
+ response = service.call(queue.name, options)
183
+
184
+ # FIXME: need error handling
185
+ document = Nokogiri::XML(response.body)
186
+ (document / "//QueueMessagesList/QueueMessage").map do |node|
187
+ Message.from_node(node) { |m| m.queue = queue }
188
+ end
189
+ end
190
+
191
+ # Public: Peek at one or more messages from the front of the queue.
192
+ #
193
+ # queue - The message queue
194
+ # options - A Hash of options for this operation (optional):
195
+ # :numofmessages - A number between 1 and 32 of messages to return
196
+ # (default: 1).
197
+ # service - The backend service to implement this (optional).
198
+ #
199
+ # Returns an Array of Messages.
200
+ def self.peek_messages(queue, options = {}, service = Azure::Queues::Services::PeekMessages.new)
201
+ response = service.call(queue.name, options)
202
+
203
+ # FIXME: need error handling
204
+ document = Nokogiri::XML(response.body)
205
+ (document / "//QueueMessagesList/QueueMessage").map do |node|
206
+ Message.from_node(node) { |m| m.queue = queue }
207
+ end
208
+ end
209
+
210
+ # Public: Delete the specified message from the queue. If the operation
211
+ # fails, the message is invalidated.
212
+ #
213
+ # message - An Azure::Queues::Message.
214
+ # service - The backend service to implement this (optional).
215
+ #
216
+ # Returns true|false to indicate success.
217
+ def self.delete_message(message, service = Azure::Queues::Services::DeleteMessage.new)
218
+ response = service.call(
219
+ message.queue.name,
220
+ message.id,
221
+ message.pop_receipt
222
+ )
223
+
224
+ if response.success?
225
+ message.freeze
226
+ else
227
+ message.error = response.error
228
+ end
229
+
230
+ response.success?
231
+ end
232
+
233
+ # Public: Delete all messages from the queue. If the operation fails, the
234
+ # queue is invalidated.
235
+ #
236
+ # queue - The message queue.
237
+ # service - The backend service to implement this (optional).
238
+ #
239
+ # Returns true|false to indicate success.
240
+ def self.clear_messages(queue, service = Azure::Queues::Services::ClearMessages.new)
241
+ response = service.call(queue.name)
242
+ queue.error = response.error unless response.success?
243
+ response.success?
244
+ end
245
+
246
+ # Public: Update the specified message. If the operation fails, the message
247
+ # is invalidated.
248
+ #
249
+ # message - An Azure::Queues::Message.
250
+ # service - The backend service to implement this (optional).
251
+ #
252
+ # Returns true|false to indicate success.
253
+ def self.update_message(message, service=Azure::Queues::Services::UpdateMessage.new)
254
+ response = service.call(
255
+ message.queue.name,
256
+ message.id,
257
+ message.text,
258
+ message.visibility_timeout,
259
+ message.pop_receipt
260
+ )
261
+
262
+ if response.success?
263
+ message.time_next_visible = response.headers['x-ms-time-next-visible']
264
+ message.pop_receipt = response.headers['x-ms-popreceipt']
265
+ else
266
+ message.error = response.error
267
+ end
268
+
269
+ response.success?
270
+ end
271
+ end
272
+ end