Skip to content

Commit

Permalink
implementing (loosly) the rest of the API and doing a gem version bump
Browse files Browse the repository at this point in the history
  • Loading branch information
leongersing committed Jun 3, 2011
1 parent 85fba6d commit 3ffded0
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 57 deletions.
27 changes: 27 additions & 0 deletions README.md
Expand Up @@ -38,6 +38,33 @@ is a hash of callbacks. Currently supported are :success and :failure. You can a


client.list_queues( :success => lambda {|queues| puts queues } ) client.list_queues( :success => lambda {|queues| puts queues } )


# Known Issues

At the moment, there is very little validation. So, it's important to validate your
data prior to placing submitting it to Amazon. Some bits that are important to the
success of sending a message (for example) are validated (signatures, keys, etc) but
items like Policies on set_queue_attributes are assumed "good" by the time you pass it in.

This is something that will be added in a future release... Stay Tuned or get your patch on. :)

# Changes

## 0.0.2
Implicit evaluation of expected actions
Base implementations of all SQS API methods.

## 0.0.1
Initial release. Only 3 endpoints supported.

# Long Term Goals
* Global Error Handling
* Include Message/AWS specific meta-data to response objects.
* Async support for other services in the AWS stack.
* Allow for drop-in alternatives to EventMachine

# Not interested in implementing...
* Backwards compatibility with past AWS APIs.

# Fork it. # Fork it.


We love to write and maintain code but there are only so many hours in a day! :) We encourage others to pick up where we left off and issue pull We love to write and maintain code but there are only so many hours in a day! :) We encourage others to pick up where we left off and issue pull
Expand Down
58 changes: 30 additions & 28 deletions lib/sqs.rb
Expand Up @@ -18,20 +18,26 @@


module SQS module SQS
include SQS::Utilities include SQS::Utilities

attr_accessor :aws_key, :aws_secret, :regions, :default_parameters, :post_options attr_accessor :aws_key, :aws_secret, :regions, :default_parameters, :post_options


def change_message_visibility(options={}) def change_message_visibility(options={})
raise "no Message specified" unless options[:message] raise "no Message specified" unless options[:message]
raise "no new visibility_timeout specified" unless options[:visibility_timeout] raise "no new visibility_timeout specified" unless options[:visibility_timeout]
options.merge!(:action => "ChangeMessageVisibility", options.merge!( :receipt_handle => options.delete(:message).receipt_handle,
:receipt_handle => options.delete(:message).receipt_handle, :visibility_timeout => options.delete(:visibility_timeout).to_i)
:visibility_timeout => options.delete(:visibility_timeout).to_i)
call_amazon(options) call_amazon(options)
end end


def set_queue_attributes(options={}) def set_queue_attributes(options={})
raise "Not Implemented Yet." raise "no target queue specified" unless options[:queue]
%w[ :visibility_timeout :policy :maximum_message_size :message_retention_period ].each do |attr_type|
if options[attr_type]
val = options.delete(attr_type)
options.merge! "Attribute.Name" => camelize(attr_type), "Attribute.Value" => val
end
end

call_amazon(options)
end end


def send_message(options={}) def send_message(options={})
Expand All @@ -52,7 +58,6 @@ def add_permission(options={})
end end


options.delete(:permissions) options.delete(:permissions)
options.merge!( :action => "AddPermission" )


call_amazon(options) call_amazon(options)
end end
Expand All @@ -67,7 +72,6 @@ def remove_permission(options={})
end end


options.delete(:permissions) options.delete(:permissions)
options.merge!( :action => "RemovePermission" )


call_amazon(options) call_amazon(options)
end end
Expand All @@ -76,7 +80,6 @@ def list_queues(options={})
prefix = options.delete(:prefix) prefix = options.delete(:prefix)
match = options.delete(:match) match = options.delete(:match)


options.merge!( :action => "ListQueues" )
options.merge!( :queue_name_prefix => encode(prefix) ) if prefix options.merge!( :queue_name_prefix => encode(prefix) ) if prefix


call_amazon(options) do |req| call_amazon(options) do |req|
Expand All @@ -89,33 +92,29 @@ def list_queues(options={})
def receive_message(options={}) def receive_message(options={})
raise "no target queue specified" unless options[:queue] raise "no target queue specified" unless options[:queue]
options = { :max_number_of_messages => 10 }.merge(options) options = { :max_number_of_messages => 10 }.merge(options)
options.merge!(:action => "ReceiveMessage")
call_amazon(options){ |req| SQSMessage.parse(req.response) } call_amazon(options){ |req| SQSMessage.parse(req.response) }
end end


def delete_message(options={}) def delete_message(options={})
raise "no Message specified" unless options[:message] raise "no Message specified" unless options[:message]
options.merge!(:action => "DeleteMessage", :receipt_handle => options.delete(:message).receipt_handle) options.merge!(:receipt_handle => options.delete(:message).receipt_handle)
call_amazon(options) call_amazon(options)
end end


def get_queue_attributes(options={}) def get_queue_attributes(options={})
raise "no target queue specified" unless options[:queue] raise "no target queue specified" unless options[:queue]
options = {:attribute_name => "All" }.merge(options) options = {:attribute_name => "All" }.merge(options)
options.merge!(:action => "GetQueueAttributes")
call_amazon(options){ |req| SQSAttributes.parse(req.response) } call_amazon(options){ |req| SQSAttributes.parse(req.response) }
end end


def delete_queue(options={}) def delete_queue(options={})
raise "no target queue specified" unless options[:queue] raise "no target queue specified" unless options[:queue]
options.merge!( :action => "DeleteQueue")
call_amazon(options) call_amazon(options)
end end


def create_queue(options={}) def create_queue(options={})
raise "no queue name specified" unless options[:queue_name] raise "no queue name specified" unless options[:queue_name]
options[:default_visibility_timeout] = 30 unless options[:default_visibility_timeout] options[:default_visibility_timeout] = 30 unless options[:default_visibility_timeout]
options.merge!( :action => "CreateQueue" )
call_amazon(options){ |req| SQSQueue.parse(req.response) } call_amazon(options){ |req| SQSQueue.parse(req.response) }
end end


Expand All @@ -125,21 +124,24 @@ def call_amazon(options)
endpoint = (options[:queue] != nil) ? options.delete(:queue).queue_url : "http://" << ( options.delete(:host) || region_host(:us_east) ) endpoint = (options[:queue] != nil) ? options.delete(:queue).queue_url : "http://" << ( options.delete(:host) || region_host(:us_east) )
callbacks = options.delete(:callbacks) || {:success=>nil, :failure =>nil } callbacks = options.delete(:callbacks) || {:success=>nil, :failure =>nil }


if( who_called_us = caller(0)[1] )
options = {:action => action_from_caller(who_called_us)}.merge(options)
end

options.amazonize_keys! options.amazonize_keys!

params = sign_params( endpoint, options ) params = sign_params( endpoint, options )
req = EM::HttpRequest.new("#{endpoint}?#{params}").get req = EM::HttpRequest.new("#{endpoint}?#{params}").get
req.callback do |req| req.callback do |req_ref|
if(req.response.to_s.match(/<ErrorResponse>/i)) if(req_ref.response.to_s.match(/<ErrorResponse>/i))
on_failure(req, callbacks) on_failure(req_ref, callbacks)
else else
result = req result = req_ref
result = yield req if block_given? result = yield req_ref if block_given?
callbacks[:success].call(result) if callbacks[:success] callbacks[:success].call(result) if callbacks[:success]
end end
end end
req.errback do |req| req.errback { |req_ref| on_failure(req_ref, callbacks) }
on_failure(req, callbacks)
end
end end


def on_failure(req, callbacks) def on_failure(req, callbacks)
Expand Down Expand Up @@ -169,7 +171,7 @@ def generate_signature(request_description)
end end


def encoding_exclusions def encoding_exclusions
/[^\w\d\-\_\.\~]/ /[^\w\d\-_\.~]/
end end


def encode(val) def encode(val)
Expand All @@ -181,17 +183,17 @@ def region_host(key)
end end


def regions def regions
@regions ||= Regions @regions ||= REGIONS
@regions @regions
end end


def default_paramters def default_paramters
@default_paramters ||= Parameters.merge("AWSAccessKeyId" => aws_key) @default_paramters ||= PARAMETERS.merge("AWSAccessKeyId" => aws_key)
@default_paramters.merge("Expires" => (Time.now+(60*30)).utc.iso8601) @default_paramters.merge("Expires" => (Time.now+(60*30)).utc.iso8601)
end end


def post_options def post_options
@post_options ||= PostOptions @post_options ||= POSTOPTIONS
@post_options @post_options
end end


Expand All @@ -212,7 +214,7 @@ def log(msg)
logger.error(log_msg.join("\n")) logger.error(log_msg.join("\n"))
end end


Regions = { REGIONS = {
:us_east => { :name => "US-East (Northern Virginia) Region", :uri => "sqs.us-east-1.amazonaws.com"}, :us_east => { :name => "US-East (Northern Virginia) Region", :uri => "sqs.us-east-1.amazonaws.com"},
:us_west => { :name => "US-West (Northern California) Region", :uri => "sqs.us-west-1.amazonaws.com"}, :us_west => { :name => "US-West (Northern California) Region", :uri => "sqs.us-west-1.amazonaws.com"},
:eu => { :name => "EU (Ireland) Region", :uri => "sqs.eu-west-1.amazonaws.com"}, :eu => { :name => "EU (Ireland) Region", :uri => "sqs.eu-west-1.amazonaws.com"},
Expand All @@ -221,13 +223,13 @@ def log(msg)
} }




Parameters = { PARAMETERS = {
"Version" => "2009-02-01", "Version" => "2009-02-01",
"SignatureVersion"=>"2", "SignatureVersion"=>"2",
"SignatureMethod"=>"HmacSHA256", "SignatureMethod"=>"HmacSHA256",
} }


PostOptions = { POSTOPTIONS = {
"Content-Type" => "application/x-www-form-urlencoded" "Content-Type" => "application/x-www-form-urlencoded"
} }
end end
31 changes: 14 additions & 17 deletions lib/sqs_permission.rb
@@ -1,30 +1,27 @@
module SQS module SQS
module Permissions module Permissions
All = "*" def self.all
SendMessage = "SendMessage" "*"
ReceiveMessage = "ReceiveMessage"
DeleteMessage = "DeleteMessage"
ChangeMessageVisibility = "ChangeMessageVisibility"
GetQueueAttributes = "GetQueueAttributes"

def send_message
SendMessage
end end


def receive_message def self.send_message
ReceiveMessage "SendMessage"
end end


def delete_message def self.receive_message
DeleteMessage "ReceiveMessage"
end end


def change_message_visibility def self.delete_message
ChangeMessageVisibility "DeleteMessage"
end end


def get_queue_attributes def self.change_message_visibility
GetQueueAttributes "ChangeMessageVisibility"
end

def self.get_queue_attributes
"GetQueueAttributes"
end end
end end
end end
Expand Down
6 changes: 5 additions & 1 deletion lib/sqs_utilities.rb
Expand Up @@ -4,7 +4,11 @@
module SQS module SQS
module Utilities module Utilities


def camelize(str, first_letter_in_uppercase = true) def action_from_caller(first_element_in_caller)
camelize(first_element_in_caller.scan(/\`(\w+)\'/).flatten.first)
end

def camelize(str)
str.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } str.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
end end


Expand Down
2 changes: 2 additions & 0 deletions spec/fixtures/set_queue_attributes.xml
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<SetQueueAttributesResponse><ResponseMetadata><RequestId>e5cca473-4fc0-4198-a451-8abb94d02c75</RequestId></ResponseMetadata></SetQueueAttributesResponse>
4 changes: 2 additions & 2 deletions spec/spec_helper.rb
Expand Up @@ -28,11 +28,11 @@ def initialize *args
end end


def get *args def get *args
return self self
end end


def post *args def post *args
return self self
end end


def callback &block def callback &block
Expand Down
2 changes: 1 addition & 1 deletion spec/sqs_attributes_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper' require 'spec_helper'


describe "SQSAttributes" do describe "SQSAttributes" do
describe ".parse" do context ".parse" do
let(:attributes_obj) { SQSAttributes.parse(xml_fixture(:queue_attributes)) } let(:attributes_obj) { SQSAttributes.parse(xml_fixture(:queue_attributes)) }


it "returns SQSAttributes objects" do it "returns SQSAttributes objects" do
Expand Down
2 changes: 1 addition & 1 deletion spec/sqs_message_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper' require 'spec_helper'


describe "SQSMessage" do describe "SQSMessage" do
describe ".parse" do context ".parse" do
let(:messages) { SQSMessage.parse(xml_fixture(:receive_message)) } let(:messages) { SQSMessage.parse(xml_fixture(:receive_message)) }


it "returns SQSMessage objects" do it "returns SQSMessage objects" do
Expand Down
2 changes: 1 addition & 1 deletion spec/sqs_queue_spec.rb
Expand Up @@ -2,7 +2,7 @@
require 'sqs_message' require 'sqs_message'


describe "SQSQueue" do describe "SQSQueue" do
describe ".parse" do context ".parse" do
let(:queues) { SQSQueue.parse(xml_fixture(:list_queues)) } let(:queues) { SQSQueue.parse(xml_fixture(:list_queues)) }


it "returns SQSQueue objects" do it "returns SQSQueue objects" do
Expand Down
21 changes: 16 additions & 5 deletions spec/sqs_spec.rb
Expand Up @@ -48,7 +48,7 @@ def initialize
context "calls Amazon Endpoints asynchronously to" do context "calls Amazon Endpoints asynchronously to" do


it "change_message_visibility" do it "change_message_visibility" do
mock_obj = mock(); mock_obj = mock()
mock_obj.expects(:call).once mock_obj.expects(:call).once
client.change_message_visibility( client.change_message_visibility(
:queue => queue, :queue => queue,
Expand All @@ -59,7 +59,17 @@ def initialize
EM::HttpRequest.succeed(EM::MockResponse.new(xml_fixture(:change_message_visibility))) EM::HttpRequest.succeed(EM::MockResponse.new(xml_fixture(:change_message_visibility)))
end end


it "set_queue_attributes" it "set_queue_attributes" do
mock_obj = mock()
mock_obj.expects(:call).once
client.set_queue_attributes(
:queue => queue,
:visibility_timeout => Time.now.to_i + (30*60),
:callbacks => { :success => mock_obj }
)
EM::HttpRequest.succeed(EM::MockResponse.new(xml_fixture(:set_queue_attributes)))
end

context "putting a message on the queue" do context "putting a message on the queue" do
it "send_message with SQSMessage object" do it "send_message with SQSMessage object" do
msg = SQSMessage.new msg = SQSMessage.new
Expand All @@ -76,6 +86,7 @@ def initialize
) )
EM::HttpRequest.succeed(EM::MockResponse.new(xml_fixture(:send_message))) EM::HttpRequest.succeed(EM::MockResponse.new(xml_fixture(:send_message)))
end end

it "send_message with message body" do it "send_message with message body" do
client.send_message( client.send_message(
:queue => queue, :queue => queue,
Expand Down Expand Up @@ -120,11 +131,11 @@ def initialize
let(:permissions) do let(:permissions) do
leon = SQSPermission.new leon = SQSPermission.new
leon.aws_account_id = "a12digitcode" leon.aws_account_id = "a12digitcode"
leon.permission = SQS::Permissions::All leon.permission = SQS::Permissions.all


john = SQSPermission.new john = SQSPermission.new
john.aws_account_id = "b12digitcode" john.aws_account_id = "b12digitcode"
john.permission = SQS::Permissions::SendMessage john.permission = SQS::Permissions.send_message


[leon, john] [leon, john]
end end
Expand Down Expand Up @@ -171,7 +182,7 @@ def initialize
end end


it "delete a message from the queue" do it "delete a message from the queue" do
mock_obj = mock(); mock_obj = mock()
mock_obj.expects(:call).once mock_obj.expects(:call).once
client.delete_message( client.delete_message(
:queue => queue, :queue => queue,
Expand Down
12 changes: 12 additions & 0 deletions spec/sqs_utilities_spec.rb
Expand Up @@ -8,4 +8,16 @@
hash.amazonize_keys! hash.amazonize_keys!
hash["QueueName"].should == "foo" hash["QueueName"].should == "foo"
end end

it "infers an amazonized action from the calling context" do
def this_would_be_the_same_as_an_action_name
sample_target
end

def sample_target
action_from_caller(caller(0)[1]).should == "ThisWouldBeTheSameAsAnActionName"
end

this_would_be_the_same_as_an_action_name
end
end end

0 comments on commit 3ffded0

Please sign in to comment.