You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by mf...@redhat.com on 2011/08/12 14:42:55 UTC
[PATCH core] Rabbit: Added initial support for sub-collections
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/sinatra/rabbit.rb | 93 ++++++++++++++-
server/server.rb | 263 ++++++++++++++++++++++-------------------
2 files changed, 228 insertions(+), 128 deletions(-)
diff --git a/server/lib/sinatra/rabbit.rb b/server/lib/sinatra/rabbit.rb
index 70691d5..fc7ed00 100644
--- a/server/lib/sinatra/rabbit.rb
+++ b/server/lib/sinatra/rabbit.rb
@@ -56,7 +56,8 @@ module Sinatra
def initialize(coll, name, opts, &block)
@name = name.to_sym
opts = STANDARD[@name].merge(opts) if standard?
- @collection = coll
+ @path_generator = opts[:path_generator]
+ @collection, @standard = coll, opts[:standard]
raise "No method for operation #{name}" unless opts[:method]
@method = opts[:method].to_sym
@member = opts[:member]
@@ -71,7 +72,7 @@ module Sinatra
end
def standard?
- STANDARD.keys.include?(name)
+ STANDARD.keys.include?(name) || @standard
end
def form?
@@ -120,8 +121,17 @@ module Sinatra
end
end
+ def member?
+ if standard?
+ @member || STANDARD[name][:member]
+ else
+ @member
+ end
+ end
+
def path(args = {})
- if @member
+ return @path_generator.call(self) if @path_generator
+ if member?
if standard?
"#{@collection.name}/:id"
else
@@ -137,6 +147,11 @@ module Sinatra
end
def generate
+ # NOTE: To debug all generated routes uncomment this line.
+ # It will print out all defined routes by Rabbit to the console
+ #
+ # puts "#{@method.to_s.upcase}(#{name}): #{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/#{path}"
+
::Sinatra::Application.send(@method, "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/#{path}", {}, &@control)
# Set up some Rails-like URL helpers
if name == :index
@@ -184,12 +199,12 @@ module Sinatra
end
class Collection
- attr_reader :name, :operations
+ attr_reader :name, :operations, :subcollections
def initialize(name, &block)
@name = name
@description = ""
- @operations = {}
+ @operations, @subcollections = {}, {}
@global = false
instance_eval(&block) if block_given?
generate_documentation
@@ -197,6 +212,10 @@ module Sinatra
generate_options
end
+ def subcollection?
+ self.class == SubCollection
+ end
+
# Set/Return description for collection
# If first parameter is not present, full description will be
# returned.
@@ -271,13 +290,21 @@ module Sinatra
@operations[name] = Operation.new(self, name, opts, &block)
end
+ def subcollection(name, opts={}, &block)
+ if subcollections.keys.include?(name)
+ raise DuplicateOperationException::new(500, "DuplicateSubcollection", "Subcollection #{name} is already defined", [])
+ end
+ subcollections[name] = SubCollection.new(self, name, opts, &block)
+ subcollections[name].generate
+ end
+
def generate
operations.values.reject { |op| op.member }.each { |o| o.generate }
operations.values.select { |op| op.member }.each { |o| o.generate }
app = ::Sinatra::Application
collname = name # Work around Ruby's weird scoping/capture
app.send(:define_method, "#{name.to_s.singularize}_url") do |id|
- api_url_for "#{collname}/#{id}", :full
+ api_url_for "#{collname}/#{id}", :full
end
if index_op = operations[:index]
app.send(:define_method, "#{name}_url") do
@@ -293,6 +320,60 @@ module Sinatra
end
end
+ class SubCollection < Collection
+
+ attr_accessor :parent
+
+ def initialize(parent, name, opts={}, &block)
+ self.parent = parent
+ super(name, &block)
+ end
+
+ def operation(name, opts = {}, &block)
+ if @operations.keys.include?(name)
+ raise DuplicateOperationException::new(500, "DuplicateOperation", "Operation #{name} is already defined", [])
+ end
+ # Preserve self as local variable to workaround Ruby namespace
+ # weirdness
+ c = self
+ path_generator = Proc.new do |obj|
+ if obj.member?
+ if obj.standard?
+ "#{parent.name}/:#{parent.name.to_s.singularize}/:#{c.name.to_s.singularize}"
+ else
+ "#{parent.name}/:#{parent.name.to_s.singularize}/:#{c.name.to_s.singularize}/#{name}"
+ end
+ else
+ if obj.form?
+ "#{parent.name}/:id/:#{parent.name.to_s.singularize}/#{obj.name}"
+ else
+ "#{parent.name}/:#{parent.name.to_s.singularize}"
+ end
+ end
+ end
+ opts.merge!({
+ :path_generator => path_generator
+ })
+ @operations[name] = Operation.new(self, name, opts, &block)
+ end
+
+ def generate
+ operations.values.reject { |op| op.member }.each { |o| o.generate }
+ operations.values.select { |op| op.member }.each { |o| o.generate }
+ app = ::Sinatra::Application
+ collname = name # Work around Ruby's weird scoping/capture
+ app.send(:define_method, "#{parent.name.to_s}_#{name.to_s.singularize}_url") do |id, subid|
+ api_url_for "#{collname}/#{id}/#{subid}", :full
+ end
+ if index_op = operations[:index]
+ app.send(:define_method, "#{parent.name.to_s}_#{name}_url") do
+ api_url_for index_op.path.gsub(/\/\?$/,''), :full
+ end
+ end
+ end
+
+ end
+
def collections
@collections ||= {}
end
diff --git a/server/server.rb b/server/server.rb
index 51802e1..641127e 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -743,140 +743,159 @@ collection :keys do
end
-#create a new blob using PUT - streams through deltacloud
-put "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket/:blob" do
- if(env["BLOB_SUCCESS"]) #ie got a 200ok after putting blob
- content_type = env["CONTENT_TYPE"]
- content_type ||= ""
- @blob = driver.blob(credentials, {:id => params[:blob],
- 'bucket' => params[:bucket]})
- respond_to do |format|
- format.xml { haml :"blobs/show" }
- format.html { haml :"blobs/show" }
- format.json { convert_to_json(:blob, @blob) }
- end
- elsif(env["BLOB_FAIL"])
- report_error(500) #OK?
- else # small blobs - < 112kb dont hit the streaming monkey patch - use 'normal' create_blob
- # also, if running under webrick don't hit the streaming patch (Thin specific)
- bucket_id = params[:bucket]
- blob_id = params[:blob]
- temp_file = Tempfile.new("temp_blob_file")
- temp_file.write(env['rack.input'].read)
- temp_file.flush
- content_type = env['CONTENT_TYPE'] || ""
- blob_data = {:tempfile => temp_file, :type => content_type}
- user_meta = BlobHelper::extract_blob_metadata_hash(request.env)
- @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
- temp_file.delete
- respond_to do |format|
- format.xml { haml :"blobs/show" }
- format.html { haml :"blobs/show"}
- end
- end
-end
+collection :buckets do
+ description "Cloud Storage buckets - aka buckets|directories|folders"
-#create a new blob using html interface - NON STREAMING (i.e. browser POST http form data)
-post "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket" do
- bucket_id = params[:bucket]
- blob_id = params['blob']
- blob_data = params['blob_data']
- user_meta = {}
- #metadata from params (i.e., passed by http form post, e.g. browser)
- max = params[:meta_params]
- if(max)
- (1..max.to_i).each do |i|
- key = params[:"meta_name#{i}"]
- key = "HTTP_X_Deltacloud_Blobmeta_#{key}"
- value = params[:"meta_value#{i}"]
- user_meta[key] = value
+ subcollection :blobs do
+ description "Blobs associated with given bucket"
+
+ operation :show do
+ description "Display blob"
+ control do
+ @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
+ if @blob
+ respond_to do |format|
+ format.xml { haml :"blobs/show" }
+ format.html { haml :"blobs/show" }
+ format.json { convert_to_json(:blob, @blob) }
+ end
+ else
+ report_error(404)
+ end
+ end
end
- end
- @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
- respond_to do |format|
- format.xml { haml :"blobs/show" }
- format.html { haml :"blobs/show"}
- end
-end
-#delete a blob
-delete "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket/:blob" do
- bucket_id = params[:bucket]
- blob_id = params[:blob]
- driver.delete_blob(credentials, bucket_id, blob_id)
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(bucket_url(bucket_id)) }
- end
-end
+ operation :create do
+ description "Create new blob"
+ control do
+ bucket_id = params[:bucket]
+ blob_id = params['blob']
+ blob_data = params['blob_data']
+ user_meta = {}
+ #metadata from params (i.e., passed by http form post, e.g. browser)
+ max = params[:meta_params]
+ if(max)
+ (1..max.to_i).each do |i|
+ key = params[:"meta_name#{i}"]
+ key = "HTTP_X_Deltacloud_Blobmeta_#{key}"
+ value = params[:"meta_value#{i}"]
+ user_meta[key] = value
+ end
+ end
+ @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
+ respond_to do |format|
+ format.xml { haml :"blobs/show" }
+ format.html { haml :"blobs/show"}
+ end
+ end
+ end
-#get blob metadata
-head "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket/:blob" do
- @blob_id = params[:blob]
- @blob_metadata = driver.blob_metadata(credentials, {:id => params[:blob], 'bucket' => params[:bucket]})
- if @blob_metadata
- @blob_metadata.each do |k,v|
- headers["X-Deltacloud-Blobmeta-#{k}"] = v
+ operation :destroy do
+ description "Destroy bucket"
+ control do
+ bucket_id = params[:bucket]
+ blob_id = params[:blob]
+ driver.delete_blob(credentials, bucket_id, blob_id)
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(bucket_url(bucket_id)) }
+ end
end
- status 204
- respond_to do |format|
- format.xml
- format.json
+ end
+
+ operation :stream, :member => true, :standard => true, :method => :put do
+ description "Get blob metadata"
+ control do
+ if(env["BLOB_SUCCESS"]) #ie got a 200ok after putting blob
+ content_type = env["CONTENT_TYPE"]
+ content_type ||= ""
+ @blob = driver.blob(credentials, {:id => params[:blob],
+ 'bucket' => params[:bucket]})
+ respond_to do |format|
+ format.xml { haml :"blobs/show" }
+ format.html { haml :"blobs/show" }
+ format.json { convert_to_json(:blob, @blob) }
+ end
+ elsif(env["BLOB_FAIL"])
+ report_error(500) #OK?
+ else # small blobs - < 112kb dont hit the streaming monkey patch - use 'normal' create_blob
+ # also, if running under webrick don't hit the streaming patch (Thin specific)
+ bucket_id = params[:bucket]
+ blob_id = params[:blob]
+ temp_file = Tempfile.new("temp_blob_file")
+ temp_file.write(env['rack.input'].read)
+ temp_file.flush
+ content_type = env['CONTENT_TYPE'] || ""
+ blob_data = {:tempfile => temp_file, :type => content_type}
+ user_meta = BlobHelper::extract_blob_metadata_hash(request.env)
+ @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
+ temp_file.delete
+ respond_to do |format|
+ format.xml { haml :"blobs/show" }
+ format.html { haml :"blobs/show"}
+ end
+ end
end
- else
- report_error(404)
- end
-end
+ end
-#update blob metadata
-post "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket/:blob" do
- meta_hash = BlobHelper::extract_blob_metadata_hash(request.env)
- success = driver.update_blob_metadata(credentials, {'bucket'=>params[:bucket], :id =>params[:blob], 'meta_hash' => meta_hash})
- if(success)
- meta_hash.each do |k,v|
- headers["X-Deltacloud-Blobmeta-#{k}"] = v
+ operation :metadata, :member => true, :standard => true, :method => :head do
+ description "Get blob metadata"
+ control do
+ @blob_id = params[:blob]
+ @blob_metadata = driver.blob_metadata(credentials, {:id => params[:blob], 'bucket' => params[:bucket]})
+ if @blob_metadata
+ @blob_metadata.each do |k,v|
+ headers["X-Deltacloud-Blobmeta-#{k}"] = v
+ end
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ end
+ else
+ report_error(404)
+ end
+ end
end
- status 204
- respond_to do |format|
- format.xml
- format.json
+
+ operation :update, :member => true, :method => :post do
+ description "Update blob metadata"
+ control do
+ meta_hash = BlobHelper::extract_blob_metadata_hash(request.env)
+ success = driver.update_blob_metadata(credentials, {'bucket'=>params[:bucket], :id =>params[:blob], 'meta_hash' => meta_hash})
+ if(success)
+ meta_hash.each do |k,v|
+ headers["X-Deltacloud-Blobmeta-#{k}"] = v
+ end
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ end
+ else
+ report_error(404) #FIXME is this the right error code?
+ end
+ end
end
- else
- report_error(404) #FIXME is this the right error code?
- end
-end
-#Get a particular blob's particulars (not actual blob data)
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket/:blob" do
- @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
- if @blob
- respond_to do |format|
- format.xml { haml :"blobs/show" }
- format.html { haml :"blobs/show" }
- format.json { convert_to_json(:blob, @blob) }
+ operation :content, :member => true, :method => :get do
+ description "Download blob content"
+ control do
+ @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
+ if @blob
+ params['content_length'] = @blob.content_length
+ params['content_type'] = @blob.content_type
+ params['content_disposition'] = "attachment; filename=#{@blob.id}"
+ BlobStream.call(env, credentials, params)
+ else
+ report_error(404)
+ end
end
- else
- report_error(404)
- end
-end
+ end
-#get the content of a particular blob
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket/:blob/content" do
- @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
- if @blob
- params['content_length'] = @blob.content_length
- params['content_type'] = @blob.content_type
- params['content_disposition'] = "attachment; filename=#{@blob.id}"
- BlobStream.call(env, credentials, params)
- else
- report_error(404)
end
-end
-
-collection :buckets do
- description "Cloud Storage buckets - aka buckets|directories|folders"
operation :new do
description "A form to create a new bucket resource"
--
1.7.4.1