removing rAtom dependency
This commit is contained in:
Родитель
aaa44fda2b
Коммит
fdd5326dca
38
README.md
38
README.md
|
@ -1,3 +1,37 @@
|
|||
Run "rake" to run all tests.
|
||||
# Azure Ruby SDK
|
||||
|
||||
Run "gem build azure.gemspec" to build gem.
|
||||
## Generating documentation
|
||||
|
||||
Run the following command:
|
||||
|
||||
rake doc
|
||||
|
||||
This will generate the API documentation in the `./doc` directory.
|
||||
|
||||
## Running tests
|
||||
|
||||
In order to run the tests, run `rake`.
|
||||
|
||||
This will run all the unit tests, and then attempt to run the integration tests,
|
||||
which need a real azure server running.
|
||||
|
||||
In order for the integration tests to run, you need the following ENV variables:
|
||||
|
||||
* `AZURE_ACCOUNT_NAME`: The name of the storage account you're using.
|
||||
- If testing against the emulator, this must be `devstoreaccount1`
|
||||
* `AZURE_ACCESS_KEY`: The Base64-encoded Access Key for your storage account.
|
||||
- If testing against the emulator, this must be
|
||||
`Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==`
|
||||
* `AZURE_BLOB_HOST`: Pointing to the server running the Azure platform.
|
||||
- If testing against the real thing: `http://<account-name>.blob.core.windows.net`
|
||||
- If testing against the emulator: `http://localhost:10000/<account-name>`
|
||||
* `AZURE_QUEUE_HOST`: Pointing to the server running the Azure platform.
|
||||
- If testing against the real thing: `http://<account-name>.queue.core.windows.net`
|
||||
- If testing against the emulator: `http://localhost:10001/<account-name>`
|
||||
* `AZURE_TABLE_HOST`: Pointing to the server running the Azure platform.
|
||||
- If testing against the real thing: `http://<account-name>.table.core.windows.net`
|
||||
- If testing against the emulator: `http://localhost:10002/<account-name>`
|
||||
* `AZURE_ACS_NAMESPACE`: a ServiceBus management namespace.
|
||||
* `AZURE_SB_ACCESS_KEY`: The Base64-encoded Access Key for your ServiceBus
|
||||
namespace.
|
||||
* `AZURE_SB_ISSUER`: The name of the issuer for the ServiceBus. This should be `owner`
|
||||
|
|
44
Rakefile
44
Rakefile
|
@ -1,6 +1,10 @@
|
|||
require "rake/testtask"
|
||||
require "rubygems/package_task"
|
||||
|
||||
task :doc do
|
||||
system "yard --plugin yard-tomdoc -o doc/ -"
|
||||
end
|
||||
|
||||
gem_spec = eval(File.read("./azure.gemspec"))
|
||||
Gem::PackageTask.new(gem_spec) do |pkg|
|
||||
pkg.need_zip = false
|
||||
|
@ -8,6 +12,21 @@ Gem::PackageTask.new(gem_spec) do |pkg|
|
|||
end
|
||||
|
||||
namespace :test do
|
||||
task :require_environment do
|
||||
unset_environment = [
|
||||
ENV.fetch("AZURE_ACCOUNT_NAME", nil),
|
||||
ENV.fetch("AZURE_ACCESS_KEY", nil),
|
||||
ENV.fetch("AZURE_TABLE_HOST", nil),
|
||||
ENV.fetch("AZURE_BLOB_HOST", nil),
|
||||
ENV.fetch("AZURE_QUEUE_HOST", nil),
|
||||
ENV.fetch("AZURE_ACS_NAMESPACE", nil),
|
||||
ENV.fetch("AZURE_SB_ACCESS_KEY", nil),
|
||||
ENV.fetch("AZURE_SB_ISSUER", nil)
|
||||
].include?(nil)
|
||||
|
||||
abort "[ABORTING] Configure your environment to run the integration tests" if unset_environment
|
||||
end
|
||||
|
||||
Rake::TestTask.new :unit do |t|
|
||||
t.pattern = "test/unit/**/*_test.rb"
|
||||
t.verbose = true
|
||||
|
@ -20,6 +39,8 @@ namespace :test do
|
|||
t.libs = ["lib", "test"]
|
||||
end
|
||||
|
||||
task :integration => :require_environment
|
||||
|
||||
namespace :integration do
|
||||
def component_task(component)
|
||||
Rake::TestTask.new component do |t|
|
||||
|
@ -27,32 +48,17 @@ namespace :test do
|
|||
t.verbose = true
|
||||
t.libs = ["lib", "test"]
|
||||
end
|
||||
|
||||
task component => "test:require_environment"
|
||||
end
|
||||
|
||||
component_task :tables
|
||||
component_task :blobs
|
||||
component_task :queues
|
||||
component_task :service_bus
|
||||
|
||||
task :conditionally do
|
||||
name = ENV.fetch("AZURE_ACCOUNT_NAME", nil)
|
||||
key = ENV.fetch("AZURE_ACCESS_KEY", nil)
|
||||
t_host = ENV.fetch("AZURE_TABLE_HOST", nil)
|
||||
b_host = ENV.fetch("AZURE_BLOB_HOST", nil)
|
||||
q_host = ENV.fetch("AZURE_QUEUE_HOST", nil)
|
||||
acs_namespace = ENV.fetch("AZURE_ACS_NAMESPACE", nil)
|
||||
sb_access_key = ENV.fetch("AZURE_SB_ACCESS_KEY", nil)
|
||||
sb_issuer = ENV.fetch("AZURE_SB_ISSUER", nil)
|
||||
|
||||
if name && key && t_host && b_host && q_host && acs_namespace && sb_access_key && sb_issuer
|
||||
Rake::Task["test:integration"].invoke
|
||||
else
|
||||
warn "[WARNING] Configure your environment to run the integration tests"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Rake::TestTask.new :cleanup do |t|
|
||||
task :cleanup => :require_environment do
|
||||
$:.unshift "lib"
|
||||
require 'azure'
|
||||
|
||||
|
@ -76,6 +82,6 @@ namespace :test do
|
|||
end
|
||||
end
|
||||
|
||||
task :test => ["test:unit", "test:integration:conditionally"]
|
||||
task :test => ["test:unit", "test:integration"]
|
||||
|
||||
task default: :test
|
||||
|
|
|
@ -2,22 +2,20 @@ require "date"
|
|||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "azure"
|
||||
s.version = "0.1.1"
|
||||
s.date = Date.today.iso8601
|
||||
s.version = "0.1.0"
|
||||
|
||||
s.authors = ["AppFog","Microsoft"]
|
||||
s.authors = ["Microsoft"]
|
||||
s.email = "azure@microsoft.com"
|
||||
s.description = "Services and ruby SDKs to access the Windows Azure platform."
|
||||
s.summary = "Implementation of several Windows Azure SDKs in ruby."
|
||||
s.homepage = "http://azure.com"
|
||||
s.files = `git ls-files | grep -v "^examples/"`.split("\n")
|
||||
s.files = `git ls-files`.split("\n")
|
||||
|
||||
s.add_runtime_dependency("uuid", "~> 2.0")
|
||||
s.add_runtime_dependency("ratom", "~> 0.6")
|
||||
s.add_runtime_dependency("nokogiri", "~> 1.5")
|
||||
s.add_runtime_dependency("mime-types", "~> 1.0")
|
||||
|
||||
s.add_development_dependency("rake")
|
||||
s.add_development_dependency("minitest", "~> 3.0")
|
||||
s.add_development_dependency("em-minitest-spec")
|
||||
s.add_development_dependency("yard")
|
||||
s.add_development_dependency("yard-tomdoc")
|
||||
end
|
||||
|
|
|
@ -1,170 +1,173 @@
|
|||
require "atom"
|
||||
require "time"
|
||||
require "delegate"
|
||||
require "nokogiri"
|
||||
require "azure/tables/types"
|
||||
|
||||
module Azure
|
||||
# Collection of XML::Node extensions for generating Atom feeds.
|
||||
# Public: The Atom module includes functionality to generate and parse Atom
|
||||
# feeds and entries.
|
||||
module Atom
|
||||
Entry = ::Atom::Entry
|
||||
Feed = ::Atom::Feed
|
||||
|
||||
# Generates a Data Property, making sure that it's in the correct namespace
|
||||
# (dataservices).
|
||||
class Property < XML::Node
|
||||
|
||||
# Public: Set up the property.
|
||||
# Convenience module so abstract the logic of generating XML. The objects
|
||||
# that include this module must implement #as_xml, such that it returns a
|
||||
# Nokogiri::XML::Node.
|
||||
module Serializable
|
||||
# Public: Convert this object into XML.
|
||||
#
|
||||
# name - The property name, without the namespace qualification.
|
||||
# value - The property's value.
|
||||
def initialize(name, value)
|
||||
super("d:#{name}")
|
||||
self << value
|
||||
end
|
||||
|
||||
# Public: Set the property's value, and sets the content type based on the
|
||||
# value's type.
|
||||
#
|
||||
# value - The value of the property.
|
||||
#
|
||||
# Returns self.
|
||||
def <<(value)
|
||||
self["m:type"] = Azure::Tables::Types.type_of(value)
|
||||
super(value)
|
||||
end
|
||||
|
||||
def to_xml(*)
|
||||
self
|
||||
# Returns a String.
|
||||
def to_xml
|
||||
as_xml.to_xml
|
||||
end
|
||||
end
|
||||
|
||||
# Represent a list of properties in the proper namespace
|
||||
# (dataservices/metadata).
|
||||
class PropertyList < XML::Node
|
||||
include ::Atom::Xml::Parseable
|
||||
# Public: An Atom Entry corresponds to a representation of a single object.
|
||||
#
|
||||
# In order to parse an entry's XML into a more manageable object, call
|
||||
# Entry.parse.
|
||||
#
|
||||
# In order to generate an XML string from an entry, call #to_xml (included
|
||||
# from Serializable).
|
||||
class Entry
|
||||
include Serializable
|
||||
|
||||
add_extension_namespace "d", "http://schemas.microsoft.com/ado/2007/08/dataservices"
|
||||
|
||||
# Public: Initialize the property list.
|
||||
# Public: Parses a string of XML, returning a new Entry.
|
||||
#
|
||||
# Yields the PropertyList.
|
||||
def initialize(o=nil)
|
||||
super("m:properties")
|
||||
# xml - A String of XML data.
|
||||
#
|
||||
# Returns an Atom::Entry.
|
||||
def self.parse(xml)
|
||||
doc = Nokogiri::XML(xml)
|
||||
new do |entry|
|
||||
entry.id = (doc % "id").text
|
||||
entry.updated = Time.parse((doc % "updated").text)
|
||||
entry.content = (doc % "content").inner_html
|
||||
yield entry, doc if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
if o && o.is_a?(LibXML::XML::Reader)
|
||||
o.node.children.each do |node|
|
||||
self << node.copy(true) unless node.blank?
|
||||
# Public: Initializes the Entry.
|
||||
#
|
||||
# Yields the new Entry.
|
||||
def initialize
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
# Public: Get/Set the Entry's id.
|
||||
attr_accessor :id
|
||||
|
||||
# Public: Get/Set the Entry's content.
|
||||
attr_accessor :content
|
||||
|
||||
# Public: Get the Entry's updated-at Time (defaults to now).
|
||||
def updated
|
||||
@updated || Time.now
|
||||
end
|
||||
|
||||
# Public: Set the Entry's updated-at Time.
|
||||
attr_writer :updated
|
||||
|
||||
# Convert this object into an XML node that can be serialized.
|
||||
#
|
||||
# xml - A Nokogiri::XML::Builder to use as the parent node (optional).
|
||||
#
|
||||
# Returns a Nokogiri::XML::Node.
|
||||
def as_xml(xml=Nokogiri::XML::Builder.new)
|
||||
as_xml = ->(obj, parent) do
|
||||
if obj.respond_to?(:as_xml)
|
||||
obj.as_xml(parent)
|
||||
else
|
||||
obj.to_s
|
||||
end
|
||||
end
|
||||
|
||||
yield self if block_given?
|
||||
end
|
||||
xml.entry("xmlns" => "http://www.w3.org/2005/Atom",
|
||||
"xmlns:m" => "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata",
|
||||
"xmlns:d" => "http://schemas.microsoft.com/ado/2007/08/dataservices") do |xml|
|
||||
xml.id(self.id)
|
||||
xml.updated(self.updated.xmlschema)
|
||||
xml.title
|
||||
xml.author do |xml|
|
||||
xml.name
|
||||
end
|
||||
|
||||
# Public: Add several properties at the same time.
|
||||
#
|
||||
# properties - A Hash of property name => property value pairs.
|
||||
#
|
||||
# Returns the passed properties.
|
||||
def merge(properties)
|
||||
properties.each do |name, value|
|
||||
self[name] = value
|
||||
if content.respond_to?(:as_xml)
|
||||
xml.content("type" => "application/xml") do |xml|
|
||||
content.as_xml(xml)
|
||||
end
|
||||
else
|
||||
xml.content(content, "type" => "application/xml")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Public: Add a property to this list. This will internally store a
|
||||
# Atom::Nodes::Property object.
|
||||
#
|
||||
# property - The name of the property to be included.
|
||||
# value - The value of the property.
|
||||
#
|
||||
# Returns nothing.
|
||||
def []=(property, value)
|
||||
self << Property.new(property, value)
|
||||
end
|
||||
|
||||
def to_xml(*)
|
||||
self
|
||||
xml
|
||||
end
|
||||
end
|
||||
|
||||
# Represent an entry's <content/> tag, ensuring it has the correct content
|
||||
# type and that it conforms to the Atom::Content interface so it can be used
|
||||
# seamlessly with Atom::Entry objects.
|
||||
class Content < XML::Node
|
||||
include ::Atom::Xml::Parseable
|
||||
# Public: An Atom Feed is a list of Entries.
|
||||
#
|
||||
# In order to parse a feed's XML into a more manageable object, call
|
||||
# Feed.parse.
|
||||
#
|
||||
# In order to generate an XML string from a feed, call #to_xml (included
|
||||
# from Serializable).
|
||||
class Feed
|
||||
include Serializable
|
||||
|
||||
add_extension_namespace "m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
|
||||
# Get the Array of entries in this feed.
|
||||
attr :entries
|
||||
|
||||
element "m:properties", class: Azure::Atom::PropertyList
|
||||
|
||||
# Public: Initialize the content node.
|
||||
# Public: Parses a string of XML, returning a new Feed.
|
||||
#
|
||||
# Yields self.
|
||||
def initialize(o=nil)
|
||||
super("content")
|
||||
self["type"] = "application/xml"
|
||||
|
||||
if o && o.is_a?(LibXML::XML::Reader)
|
||||
o.read_inner_xml
|
||||
o.read
|
||||
parse o
|
||||
# xml - A String of XML data.
|
||||
#
|
||||
# Returns an Atom::Feed.
|
||||
def self.parse(xml, entry_parser=Entry)
|
||||
doc = Nokogiri::XML(xml)
|
||||
new do |feed|
|
||||
feed.id = (doc % "id").text
|
||||
feed.updated = Time.parse((doc % "updated").text)
|
||||
(doc / "entry").each do |entry|
|
||||
feed.entries << entry_parser.parse(entry.to_xml)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Public: Initialize the Feed.
|
||||
#
|
||||
# Yields the Feed.
|
||||
def initialize
|
||||
@entries = []
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
# Public: Cast this object into something Atom::Entry can understand as
|
||||
# content. By returning itself (as a Node) the Entry won't try to escape
|
||||
# this as CDATA.
|
||||
# Public: Get/Set the Feed's id.
|
||||
attr_accessor :id
|
||||
|
||||
# Public: Get the Feed's updated-at Time (defaults to now).
|
||||
def updated
|
||||
@updated || Time.now
|
||||
end
|
||||
|
||||
# Public: Set the Feed's updated-at Time.
|
||||
attr_writer :updated
|
||||
|
||||
# Convert this object into an XML node that can be serialized.
|
||||
#
|
||||
# Returns self.
|
||||
def to_xml(*)
|
||||
self
|
||||
# xml - A Nokogiri::XML::Builder to use as the parent node (optional).
|
||||
#
|
||||
# Returns a Nokogiri::XML::Node.
|
||||
def as_xml(xml=Nokogiri::XML::Builder.new)
|
||||
xml.entry("xmlns" => "http://www.w3.org/2005/Atom",
|
||||
"xmlns:m" => "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata",
|
||||
"xmlns:d" => "http://schemas.microsoft.com/ado/2007/08/dataservices") do |xml|
|
||||
xml.id(self.id)
|
||||
xml.updated(self.updated.xmlschema)
|
||||
xml.title
|
||||
entries.each do |entry|
|
||||
entry.as_xml(xml)
|
||||
end
|
||||
end
|
||||
xml
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# FIXME: The rAtom gem doesn't play well when you extend your classes, raising
|
||||
# weird errors, so we're monkeypatching their classes. This *sucks*.
|
||||
module Atom
|
||||
# Public: An Atom feed, that understands the Microsoft ADO namespaces for Data
|
||||
# services.
|
||||
class Feed
|
||||
add_extension_namespace "d", "http://schemas.microsoft.com/ado/2007/08/dataservices"
|
||||
add_extension_namespace "m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
|
||||
end
|
||||
|
||||
# Public: An Atom entry, that understands the Microsoft ADO namespaces for Data
|
||||
# services.
|
||||
class Entry
|
||||
add_extension_namespace "d", "http://schemas.microsoft.com/ado/2007/08/dataservices"
|
||||
add_extension_namespace "m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
|
||||
|
||||
element "content", class: Azure::Atom::Content
|
||||
|
||||
# Public: Generate a list of data properties to include in this Entry's
|
||||
# content.
|
||||
# FIXME: This method isn't very confident as getter, use content.m_properties instead.
|
||||
#
|
||||
# Yields a Nodes::PropertyList.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# entry.properties do |props|
|
||||
# props["one"] = 1
|
||||
# props["two"] = 2
|
||||
# end
|
||||
#
|
||||
# Returns the XML node for entry's <m_properties/>.
|
||||
def properties(&block)
|
||||
if !@content
|
||||
@content = Azure::Atom::Content.new
|
||||
@m_properties = Azure::Atom::PropertyList.new
|
||||
@content << @m_properties
|
||||
end
|
||||
|
||||
yield @m_properties if block_given?
|
||||
|
||||
@m_properties
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -181,6 +181,8 @@ module Azure
|
|||
def self.get_messages(queue, options = {}, service = Azure::Queues::Services::GetMessages.new)
|
||||
response = service.call(queue.name, options)
|
||||
|
||||
options[:visibility_timeout] = options.fetch(:visibility_timeout, 1)
|
||||
|
||||
# FIXME: need error handling
|
||||
document = Nokogiri::XML(response.body)
|
||||
(document / "//QueueMessagesList/QueueMessage").map do |node|
|
||||
|
|
|
@ -3,7 +3,7 @@ require "azure/tables/table"
|
|||
require "azure/tables/entity"
|
||||
require "azure/tables/entities_collection"
|
||||
require "azure/tables/tables_collection"
|
||||
require "azure/atom"
|
||||
require "azure/tables/atom"
|
||||
|
||||
module Azure
|
||||
module Tables
|
||||
|
@ -15,7 +15,7 @@ module Azure
|
|||
# Returns an Array of Table elements.
|
||||
def self.all(query = {}, service=Azure::Tables::Services::QueryTables.new)
|
||||
response = service.call(query)
|
||||
feed = Atom::Feed.load_feed(response.body)
|
||||
feed = Atom::Feed.parse(response.body)
|
||||
collection = Azure::Tables::TablesCollection.from_entries(feed.entries, query)
|
||||
|
||||
collection.continuation_token(
|
||||
|
@ -36,7 +36,7 @@ module Azure
|
|||
response = service.call(name)
|
||||
|
||||
if response.success?
|
||||
Table.from_entry(Atom::Entry.load_entry(response.body))
|
||||
Table.from_entry(Atom::Entry.parse(response.body))
|
||||
else
|
||||
Table.from_error(response.error)
|
||||
end
|
||||
|
@ -54,7 +54,7 @@ module Azure
|
|||
response = service.call(name)
|
||||
|
||||
if response.success?
|
||||
Table.from_entry(Atom::Entry.load_entry(response.body))
|
||||
Table.from_entry(Atom::Entry.parse(response.body))
|
||||
else
|
||||
Table.from_error(response.error)
|
||||
end
|
||||
|
@ -85,7 +85,7 @@ module Azure
|
|||
|
||||
if response.success?
|
||||
entity.reset(
|
||||
Entity.from_entry(Azure::Atom::Entry.load_entry(response.body))
|
||||
Entity.from_entry(Atom::Entry.parse(response.body))
|
||||
)
|
||||
entity.etag = response.headers["etag"]
|
||||
else
|
||||
|
@ -181,7 +181,7 @@ module Azure
|
|||
response = service.call(table.name, query)
|
||||
|
||||
if response.success?
|
||||
Entity.from_entry(Azure::Atom::Entry.load_entry(response.body))
|
||||
Entity.from_entry(Atom::Entry.parse(response.body))
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
@ -199,7 +199,7 @@ module Azure
|
|||
response = service.call(table.name, query)
|
||||
|
||||
if response.success?
|
||||
feed = Atom::Feed.load_feed(response.body)
|
||||
feed = Atom::Feed.parse(response.body)
|
||||
collection = Azure::Tables::EntitiesCollection.from_entries(table, feed.entries, query)
|
||||
collection.continuation_token(
|
||||
response.headers["x-ms-continuation-nextpartitionkey"],
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
require "azure/atom"
|
||||
|
||||
module Azure
|
||||
module Tables
|
||||
# Specific group of extensions to simplify working with entries as used in
|
||||
# the Table Service. See Azure::Atom for more information.
|
||||
module Atom
|
||||
# A table's or entity's entry has a list of properties that represent the
|
||||
# object in question.
|
||||
class Entry < Azure::Atom::Entry
|
||||
def self.parse(xml) # :nodoc:
|
||||
super(xml) do |entry, doc|
|
||||
doc.remove_namespaces!
|
||||
props = doc % "properties"
|
||||
entry.properties.merge(PropertyList.parse(props.to_xml))
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(*) # :nodoc:
|
||||
@properties = PropertyList.new
|
||||
super
|
||||
end
|
||||
|
||||
# Public: Get the list of properties from this Entry.
|
||||
#
|
||||
# Yields the list of properties.
|
||||
#
|
||||
# Returns the list of properties.
|
||||
def properties
|
||||
yield @properties if block_given?
|
||||
@properties
|
||||
end
|
||||
|
||||
# Public: Get the content of this entry. Defaults to its properties.
|
||||
def content
|
||||
@content ||= properties
|
||||
end
|
||||
end
|
||||
|
||||
class Feed < Azure::Atom::Feed # :nodoc:
|
||||
def self.parse(xml, entry_parser=Tables::Atom::Entry)
|
||||
super(xml, entry_parser)
|
||||
end
|
||||
end
|
||||
|
||||
# A PropertyList is a hash-like object that groups the meta-properties
|
||||
# than an Entry can have.
|
||||
#
|
||||
# It represents an <m:properties/> tag.
|
||||
class PropertyList
|
||||
include Azure::Atom::Serializable
|
||||
include Enumerable
|
||||
|
||||
# Public: Parses a string of XML, returning a new PropertyList.
|
||||
#
|
||||
# xml - A String of XML data.
|
||||
#
|
||||
# Returns a Tables::Atom::PropertyList.
|
||||
def self.parse(xml)
|
||||
doc = Nokogiri::XML(xml)
|
||||
doc.remove_namespaces!
|
||||
new do |list|
|
||||
doc.root.children.reject(&:text?).each do |property|
|
||||
p = Property.parse(property.to_xml)
|
||||
list[property.name] = p
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Public: Initialize the PropertyList.
|
||||
#
|
||||
# Yields the new PropertyList.
|
||||
def initialize
|
||||
@properties = {}
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
# Public: Iterate over every property.
|
||||
#
|
||||
# Yields each name/value pair.
|
||||
#
|
||||
# Returns the list of properties.
|
||||
def each
|
||||
@properties.each do |name, property|
|
||||
yield name, property.value
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Public: Access the value of a property, cast into the corresponding
|
||||
# type. See Azure::Table::Types.
|
||||
#
|
||||
# name - The name of the property.
|
||||
#
|
||||
# Returns the property's value.
|
||||
def [](name)
|
||||
@properties[name].value
|
||||
end
|
||||
|
||||
# Public: Set the value of a property.
|
||||
#
|
||||
# name - The name of the property.
|
||||
# value - The object with this property's value.
|
||||
#
|
||||
# Returns the property's value.
|
||||
def []=(name, value)
|
||||
value = Property.new(name, value) unless value.respond_to?(:value)
|
||||
@properties[name] = value
|
||||
end
|
||||
|
||||
# Public: Merge a hash of name/value pairs into this list of properties.
|
||||
#
|
||||
# props - A Hash.
|
||||
#
|
||||
# Returns the passed Hash.
|
||||
def merge(props)
|
||||
props.each do |name, value|
|
||||
self[name] = value
|
||||
end
|
||||
end
|
||||
|
||||
# Public: Get the size of the PropertyList.
|
||||
#
|
||||
# Returns a Fixnum.
|
||||
def size
|
||||
@properties.size
|
||||
end
|
||||
|
||||
# Convert this object into an XML node that can be serialized.
|
||||
#
|
||||
# xml - A Nokogiri::XML::Builder to use as the parent node (optional).
|
||||
#
|
||||
# Returns a Nokogiri::XML::Node.
|
||||
def as_xml(xml=Nokogiri::XML::Builder.new)
|
||||
xml.send("m:properties") do |xml|
|
||||
@properties.values.each do |property|
|
||||
property.as_xml(xml)
|
||||
end
|
||||
end
|
||||
|
||||
xml
|
||||
end
|
||||
end
|
||||
|
||||
# A Property represents a single field of information. It has a name, a
|
||||
# value, and a type (inferred from the value using Azure::Tables::Types).
|
||||
class Property
|
||||
include Azure::Atom::Serializable
|
||||
|
||||
# Public: Parses a string of XML, returning a new PropertyList.
|
||||
#
|
||||
# xml - A String of XML data.
|
||||
#
|
||||
# Returns a Tables::Atom::PropertyList.
|
||||
def self.parse(xml)
|
||||
doc = Nokogiri::XML(xml)
|
||||
doc.remove_namespaces!
|
||||
prop = doc.root
|
||||
value = Azure::Tables::Types.cast(prop.text, prop["type"])
|
||||
new(prop.name, value)
|
||||
end
|
||||
|
||||
# Get the property's name.
|
||||
attr :name
|
||||
|
||||
# Get the property's value.
|
||||
attr :value
|
||||
|
||||
# Public: Initialize the Property.
|
||||
#
|
||||
# name - The property's name.
|
||||
# value - The property's value.
|
||||
def initialize(name, value)
|
||||
@name = name
|
||||
@value = value
|
||||
end
|
||||
|
||||
# Public: Get the type of this property. See Azure::Tables::Types.
|
||||
#
|
||||
# Returns a String.
|
||||
def type
|
||||
Azure::Tables::Types.type_of(value)
|
||||
end
|
||||
|
||||
# Convert this object into an XML node that can be serialized.
|
||||
#
|
||||
# xml - A Nokogiri::XML::Builder to use as the parent node (optional).
|
||||
#
|
||||
# Returns a Nokogiri::XML::Node.
|
||||
def as_xml(xml=Nokogiri::XML::Builder.new)
|
||||
xml.send("d:#{name}", value, "m:type" => type)
|
||||
xml
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,4 +1,3 @@
|
|||
require "azure/tables/types"
|
||||
require "azure/tables"
|
||||
require "azure/error"
|
||||
|
||||
|
@ -13,7 +12,7 @@ module Azure
|
|||
|
||||
# Public: Returns an Entity from an Atom::Entry object.
|
||||
#
|
||||
# entry - Atom::Entry object representing the Entity.
|
||||
# entry - Tables::Atom::Entry object representing the Entity.
|
||||
#
|
||||
# Returns Azure::Entity
|
||||
def self.from_entry(entry)
|
||||
|
@ -21,13 +20,8 @@ module Azure
|
|||
|
||||
entity.url = URI(entry.id)
|
||||
|
||||
entry.content.m_properties.each do |property_node|
|
||||
value = Types.cast(
|
||||
property_node.content,
|
||||
property_node.attributes["type"]
|
||||
)
|
||||
|
||||
entity[property_node.name] = value
|
||||
entry.properties.each do |name, value|
|
||||
entity[name] = value
|
||||
end
|
||||
|
||||
entity
|
||||
|
|
|
@ -3,7 +3,7 @@ require "azure/core/utils/queryable"
|
|||
require "azure/tables/auth/shared_key"
|
||||
require "azure/tables/auth/shared_key_lite"
|
||||
require "azure/tables/uri"
|
||||
require "azure/atom"
|
||||
require "azure/tables/atom"
|
||||
|
||||
module Azure
|
||||
module Tables
|
||||
|
@ -59,7 +59,7 @@ module Azure
|
|||
#
|
||||
# Returns a Response.
|
||||
def call(table_name)
|
||||
body = Atom::Entry.new do |entry|
|
||||
body = Tables::Atom::Entry.new do |entry|
|
||||
entry.properties["TableName"] = table_name
|
||||
end
|
||||
|
||||
|
@ -89,7 +89,7 @@ module Azure
|
|||
#
|
||||
# Returns a Response.
|
||||
def call(table_name, attributes)
|
||||
body = Atom::Entry.new do |entry|
|
||||
body = Tables::Atom::Entry.new do |entry|
|
||||
entry.updated = Time.now.utc
|
||||
entry.properties.merge(attributes)
|
||||
end
|
||||
|
@ -116,7 +116,7 @@ module Azure
|
|||
attributes.fetch("RowKey")
|
||||
)
|
||||
|
||||
body = Atom::Entry.new do |entry|
|
||||
body = Tables::Atom::Entry.new do |entry|
|
||||
entry.id = uri
|
||||
entry.updated = Time.now.utc
|
||||
entry.properties.merge(attributes)
|
||||
|
@ -139,7 +139,7 @@ module Azure
|
|||
#
|
||||
# Returns a Response.
|
||||
def call(uri, attributes, etag)
|
||||
body = Atom::Entry.new do |entry|
|
||||
body = Tables::Atom::Entry.new do |entry|
|
||||
entry.id = uri
|
||||
entry.updated = Time.now.utc
|
||||
entry.properties.merge(attributes)
|
||||
|
|
|
@ -25,7 +25,7 @@ module Azure
|
|||
#
|
||||
# Returns a Table.
|
||||
def self.from_entry(entry)
|
||||
name = entry.content.m_properties.children.first.content
|
||||
name = entry.properties["TableName"]
|
||||
new(name, ::URI.parse(entry.id))
|
||||
end
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@ module Azure
|
|||
Integer(serialized)
|
||||
when "Edm.Boolean"
|
||||
/true/i === serialized
|
||||
when "Edm.Guid"
|
||||
GUID.new(serialized.to_s)
|
||||
else
|
||||
serialized.to_s
|
||||
end
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
class NameGenerator
|
||||
def initialize(&cleanup_proc)
|
||||
@cleanup_proc = cleanup_proc
|
||||
@names = []
|
||||
end
|
||||
|
||||
def name
|
||||
alpha = ("a".."z").to_a
|
||||
name = 10.times.map { alpha[Random.rand(alpha.size)]}.join
|
||||
@names << name
|
||||
name
|
||||
end
|
||||
|
||||
def clean
|
||||
@names.reject! do |name|
|
||||
@cleanup_proc.call(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
TableNameHelper = NameGenerator.new do |name|
|
||||
table = Azure::Tables::Table.new(name)
|
||||
Azure::Tables.delete(table)
|
||||
end
|
||||
|
||||
ContainerNameHelper = NameGenerator.new do |name|
|
||||
container = Azure::Blobs::Container.new(name)
|
||||
container.delete
|
||||
end
|
||||
|
||||
QueueNameHelper = NameGenerator.new do |name|
|
||||
queue = Azure::Queues::Queue.new(name)
|
||||
queue.delete
|
||||
end
|
||||
|
||||
ServiceBusQueueNameHelper = NameGenerator.new do |name|
|
||||
queue = Azure::ServiceBus::Queues::Queue.new(name)
|
||||
queue.delete
|
||||
end
|
||||
|
||||
ServiceBusTopicNameHelper = NameGenerator.new do |name|
|
||||
topic = Azure::ServiceBus::Topics::Topic.new(name)
|
||||
topic.delete
|
||||
end
|
|
@ -1,58 +1,112 @@
|
|||
require "test_helper"
|
||||
require "azure/atom"
|
||||
|
||||
describe "Generating Atom entries with property lists" do
|
||||
it "lists the properties in the node" do
|
||||
entry = Atom::Entry.new do |entry|
|
||||
entry.properties do |props|
|
||||
props["Prop1Name"] = "Prop1Value"
|
||||
props["Prop2Name"] = "Prop2Value"
|
||||
end
|
||||
end
|
||||
|
||||
entry.properties.first.name.must_equal "d:Prop1Name"
|
||||
entry.properties.first.content.must_equal "Prop1Value"
|
||||
|
||||
entry.properties.last.name.must_equal "d:Prop2Name"
|
||||
entry.properties.last.content.must_equal "Prop2Value"
|
||||
describe "Parsing Atom" do
|
||||
let :entry_xml do
|
||||
<<-XML
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<entry xml:base="http://myaccount.table.core.windows.net/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
|
||||
<id>http://myaccount.table.core.windows.net/Tables('mytable')</id>
|
||||
<title type="text"></title>
|
||||
<updated>2009-01-04T17:18:54.7062347Z</updated>
|
||||
<author>
|
||||
<name />
|
||||
</author>
|
||||
<link rel="edit" title="Tables" href="Tables('mytable')" />
|
||||
<category term="myaccount.Tables" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
|
||||
<content type="application/xml">
|
||||
<m:properties>
|
||||
<d:TableName>mytable</d:TableName>
|
||||
</m:properties>
|
||||
</content>
|
||||
</entry>
|
||||
XML
|
||||
end
|
||||
|
||||
it "can bulk-update a property list" do
|
||||
entry = Atom::Entry.new do |entry|
|
||||
entry.properties.merge(a: 1, b: 2, c: 3)
|
||||
end
|
||||
|
||||
doc = XML::Parser.string(entry.to_xml).parse
|
||||
doc.find("//d:a[text() = '1']").wont_be_empty
|
||||
doc.find("//d:b[text() = '2']").wont_be_empty
|
||||
doc.find("//d:c[text() = '3']").wont_be_empty
|
||||
let :feed_xml do
|
||||
<<-XML
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<feed xml:base="http://myaccount.table.core.windows.net/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
|
||||
<id>http://myaccount.table.core.windows.net/Tables</id>
|
||||
<title type="text">Tables</title>
|
||||
<updated>2009-01-04T17:18:54.7062347Z</updated>
|
||||
<link rel="self" title="Tables" href="Tables" />
|
||||
<entry>
|
||||
<id>http://myaccount.table.core.windows.net/Tables('mytable')</id>
|
||||
<title type="text"></title>
|
||||
<updated>2009-01-04T17:18:54.7062347Z</updated>
|
||||
<author>
|
||||
<name />
|
||||
</author>
|
||||
<link rel="edit" title="Tables" href="Tables('mytable')" />
|
||||
<category term="myaccount.Tables" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
|
||||
<content type="application/xml">
|
||||
<m:properties>
|
||||
<d:TableName>mytable</d:TableName>
|
||||
</m:properties>
|
||||
</content>
|
||||
</entry>
|
||||
</feed>
|
||||
XML
|
||||
end
|
||||
|
||||
it "can set properties in several ways" do
|
||||
entry = Atom::Entry.new do |entry|
|
||||
entry.properties["a"] = 1
|
||||
entry.properties.merge(b: 2, c: 3)
|
||||
entry.properties do |props|
|
||||
props["d"] = 4
|
||||
end
|
||||
end
|
||||
|
||||
doc = XML::Parser.string(entry.to_xml).parse
|
||||
doc.find("//d:a[text() = '1']").wont_be_empty
|
||||
doc.find("//d:b[text() = '2']").wont_be_empty
|
||||
doc.find("//d:c[text() = '3']").wont_be_empty
|
||||
doc.find("//d:d[text() = '4']").wont_be_empty
|
||||
let :feeds_entry_xml do
|
||||
(Nokogiri::XML(feed_xml) % "entry").to_xml
|
||||
end
|
||||
|
||||
it "generates properties with the given data type" do
|
||||
node = Azure::Atom::Property.new("name", "value")
|
||||
node["m:type"].must_equal "Edm.String"
|
||||
it "parses an entry correctly" do
|
||||
entry = Azure::Atom::Entry.parse(entry_xml)
|
||||
entry.id.must_equal "http://myaccount.table.core.windows.net/Tables('mytable')"
|
||||
entry.updated.must_equal Time.parse("2009-01-04T17:18:54.7062347Z")
|
||||
entry.content.must_match /d:TableName/
|
||||
end
|
||||
|
||||
it "generates properties with the given data name" do
|
||||
node = Azure::Atom::Property.new("firstName", "value")
|
||||
node.name.must_equal "d:firstName"
|
||||
node = Azure::Atom::Property.new(:firstName, "value")
|
||||
node.name.must_equal "d:firstName"
|
||||
it "parses a feed correctly" do
|
||||
feed = Azure::Atom::Feed.parse(feed_xml)
|
||||
feed.id.must_equal "http://myaccount.table.core.windows.net/Tables"
|
||||
feed.updated.must_equal Time.parse("2009-01-04T17:18:54.7062347Z")
|
||||
feed.entries.size.must_equal 1
|
||||
|
||||
entry = feed.entries.first
|
||||
entry.id.must_equal "http://myaccount.table.core.windows.net/Tables('mytable')"
|
||||
end
|
||||
|
||||
it "chooses the entry parser when parsing feeds" do
|
||||
entry = double()
|
||||
parser = MiniTest::Mock.new
|
||||
parser.expect(:parse, entry, [feeds_entry_xml])
|
||||
|
||||
feed = Azure::Atom::Feed.parse(feed_xml, parser)
|
||||
|
||||
parser.verify
|
||||
end
|
||||
end
|
||||
|
||||
describe "Generating Atom" do
|
||||
class ComplexContent
|
||||
def as_xml(xml)
|
||||
xml.foo("bar")
|
||||
xml
|
||||
end
|
||||
end
|
||||
|
||||
it "includes the content of the entry when it's a string" do
|
||||
entry = Azure::Atom::Entry.new do |entry|
|
||||
entry.content = "FooBar"
|
||||
end
|
||||
|
||||
doc = Nokogiri::XML(entry.to_xml)
|
||||
content = doc % "content"
|
||||
content.text.must_equal "FooBar"
|
||||
end
|
||||
|
||||
it "includes the content of the entry when it's an XML structure" do
|
||||
entry = Azure::Atom::Entry.new do |entry|
|
||||
entry.content = ComplexContent.new
|
||||
end
|
||||
|
||||
doc = Nokogiri::XML(entry.to_xml)
|
||||
foo_in_content = doc % "content > foo"
|
||||
foo_in_content.text.must_equal "bar"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
require "test_helper"
|
||||
require "azure/tables/atom"
|
||||
|
||||
describe "Generating Atom entries with property lists" do
|
||||
it "can set the properties one by one" do
|
||||
entry = Azure::Tables::Atom::Entry.new do |entry|
|
||||
entry.properties["name1"] = "value"
|
||||
entry.properties["name2"] = "value"
|
||||
end
|
||||
entry.properties.size.must_equal 2
|
||||
end
|
||||
|
||||
it "can set the properties by merging a hash" do
|
||||
entry = Azure::Tables::Atom::Entry.new do |entry|
|
||||
entry.properties.merge("name1" => "value", "name2" => "value")
|
||||
end
|
||||
entry.properties.size.must_equal 2
|
||||
end
|
||||
|
||||
it "overrides a previous property with the same name" do
|
||||
entry = Azure::Tables::Atom::Entry.new do |entry|
|
||||
entry.properties["name"] = "value1"
|
||||
entry.properties["name"] = "value2"
|
||||
end
|
||||
|
||||
entry.properties.size.must_equal 1
|
||||
entry.properties["name"].must_equal "value2"
|
||||
end
|
||||
|
||||
it "can pass the properties as a block" do
|
||||
entry = Azure::Tables::Atom::Entry.new do |entry|
|
||||
entry.properties do |props|
|
||||
props["Prop1Name"] = "Prop1Value"
|
||||
props["Prop2Name"] = "Prop2Value"
|
||||
end
|
||||
end
|
||||
entry.properties.size.must_equal 2
|
||||
end
|
||||
|
||||
it "can be serialized to xml" do
|
||||
entry = Azure::Tables::Atom::Entry.new do |entry|
|
||||
entry.properties do |props|
|
||||
props["name"] = "value"
|
||||
end
|
||||
end
|
||||
|
||||
doc = Nokogiri::XML(entry.to_xml)
|
||||
prop = doc.xpath("//d:name", doc.collect_namespaces).first
|
||||
prop.text.must_equal "value"
|
||||
end
|
||||
end
|
||||
|
||||
describe "Parsing property lists" do
|
||||
let :property_list do
|
||||
<<-XML
|
||||
<m:properties xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
|
||||
<d:Foo>bar</d:Foo>
|
||||
<d:Bar>baz</d:Bar>
|
||||
<d:Number m:type="Edm.Int32">20</d:Number>
|
||||
</m:properties>
|
||||
XML
|
||||
end
|
||||
|
||||
it "can parse a simple property list" do
|
||||
list = Azure::Tables::Atom::PropertyList.parse(property_list)
|
||||
list["Foo"].must_equal "bar"
|
||||
list["Bar"].must_equal "baz"
|
||||
end
|
||||
|
||||
it "will cast properties to the corresponding class" do
|
||||
list = Azure::Tables::Atom::PropertyList.parse(property_list)
|
||||
list["Number"].must_equal 20
|
||||
end
|
||||
end
|
|
@ -11,7 +11,7 @@ describe Azure::Tables::EntitiesCollection do
|
|||
|
||||
before do
|
||||
@table = Azure::Tables::Table.new("table_name")
|
||||
entries = Azure::Atom::Feed.load_feed(Fixtures[:query_entities_response]).entries
|
||||
entries = Azure::Tables::Atom::Feed.parse(Fixtures[:query_entities_response]).entries
|
||||
@collection = Azure::Tables::EntitiesCollection.from_entries(@table, entries, {}, service)
|
||||
end
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'azure/tables/entity'
|
|||
|
||||
describe Azure::Tables::Entity do
|
||||
before do
|
||||
@entry = Azure::Atom::Entry.load_entry(Fixtures[:insert_entity_response_entry])
|
||||
@entry = Azure::Tables::Atom::Entry.parse(Fixtures[:insert_entity_response_entry])
|
||||
end
|
||||
|
||||
it "should be able to instantiate an entity from an xml entry" do
|
||||
|
|
|
@ -3,7 +3,7 @@ require "azure/tables/table"
|
|||
|
||||
describe Azure::Tables::Table do
|
||||
def entry
|
||||
Atom::Entry.load_entry(Fixtures[:create_table_response_entry])
|
||||
Azure::Tables::Atom::Entry.parse(Fixtures[:create_table_response_entry])
|
||||
end
|
||||
|
||||
it "can be instantiated from an entry" do
|
||||
|
|
Загрузка…
Ссылка в новой задаче