azure 0.0.0 → 0.1.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.
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