fake_sqs 0.0.2

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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fake_sqs.gemspec
4
+ gemspec
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 iain
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # Fake SQS [![Build Status](https://secure.travis-ci.org/iain/fake_sqs.png)](http://travis-ci.org/iain/fake_sqs)
2
+
3
+ Inspired by [Fake DynamoDB] [fake_dynamo], this is an AWS SQS compatible
4
+ message queue that can be ran locally. This makes it ideal for integration
5
+ testing, just like you would have a local database running. Fake SQS doesn't
6
+ persist anything, not even the queues themselves. You'll have to create the
7
+ queues everytime you start it.
8
+
9
+ This implementation is **not complete** yet.
10
+
11
+ Done so far are:
12
+
13
+ * Creating queues
14
+ * Deleting queues
15
+ * Listing queues (with prefixes)
16
+ * Get queue url via the name
17
+ * Send messages (and in batch)
18
+ * Receive messages (and in batch)
19
+ * Deleting messages (and in batch)
20
+
21
+ Certain bits are left off on purpose, to make it easier to work with, such as:
22
+
23
+ * No checking on access keys or signatures
24
+ * No 60 second delay between deleting a queue and recreating it.
25
+ * No visibility timeouts (see below about special hooks)
26
+
27
+ Other parts are just not done yet:
28
+
29
+ * Permissions
30
+ * Changing queue attributes
31
+ * Changing message visibility
32
+ * Error handling
33
+
34
+ So, actually, just the basics are implemented at this point.
35
+
36
+ ## Usage
37
+
38
+ To install:
39
+
40
+ ```
41
+ $ gem install fake_sqs
42
+ ```
43
+
44
+ To start:
45
+
46
+ ```
47
+ $ fake_sqs
48
+ ```
49
+
50
+ To configure, see the options in the help:
51
+
52
+ ```
53
+ $ fake_sqs --help
54
+ ```
55
+
56
+ This is an example of how to configure the official [aws-sdk gem] [aws-sdk], to
57
+ let it talk to Fake SQS.
58
+
59
+ ``` ruby
60
+ AWS.config(
61
+ :use_ssl => false,
62
+ :sqs_endpoint => "localhost",
63
+ :sqs_port => 4567,
64
+ :access_key_id => "access key id",
65
+ :secret_access_key => "secret access key"
66
+ )
67
+ ```
68
+
69
+ If you have the configuration options for other libraries, please give them to
70
+ me.
71
+
72
+ To reset the entire server, during tests for example, send a DELETE request to
73
+ the server. For example:
74
+
75
+ ```
76
+ $ curl -X DELETE http://localhost:4567/
77
+ ```
78
+
79
+ Within SQS, after receiving, messages will be available again automatically
80
+ after a certain time. While this is not implemented (for now at least), you can
81
+ trigger this behavior at at will, with a PUT request.
82
+
83
+ ```
84
+ $ curl -X PUT http://localhost:4567/
85
+ ```
86
+
87
+
88
+ [fake_dynamo]: https://github.com/ananthakumaran/fake_dynamo
89
+ [aws-sdk]: https://github.com/amazonwebservices/aws-sdk-for-ruby
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/fake_sqs ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fake_sqs'
4
+ require 'fake_sqs/web_interface'
5
+ require 'optparse'
6
+
7
+ app = FakeSQS::WebInterface
8
+
9
+ parser = OptionParser.new do |o|
10
+
11
+ o.on "-p", "--port PORT", Integer, "Port to use (default: 4567)" do |port|
12
+ app.set :port, port
13
+ end
14
+
15
+ o.on "-o", "--bind HOST", "Host to bind to (default: 0.0.0.0)" do |bind|
16
+ app.set :bind, bind
17
+ end
18
+
19
+ o.on "-s", "--server SERVER", ['thin', 'mongrel', 'webrick'], "Server to use: thin, mongrel or webrick" do |server|
20
+ app.set :server, server
21
+ end
22
+
23
+ o.on "-v", "--verbose", "Shows input parameters and output XML" do
24
+ require 'fake_sqs/show_output'
25
+ app.use FakeSQS::ShowOutput
26
+ end
27
+
28
+ o.on_tail "--version", "Shows the version" do
29
+ puts "fake_sqs version #{FakeSQS::VERSION}"
30
+ exit
31
+ end
32
+
33
+ o.on_tail "-h", "--help", "Shows this help page" do
34
+ puts o
35
+ exit
36
+ end
37
+
38
+ end
39
+
40
+ parser.parse!
41
+ app.run!
data/fake_sqs.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fake_sqs/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "fake_sqs"
8
+ gem.version = FakeSQS::VERSION
9
+ gem.authors = ["iain"]
10
+ gem.email = ["iain@iain.nl"]
11
+ gem.description = %q{Provides a fake SQS server that you can run locally to test against}
12
+ gem.summary = %q{Provides a fake SQS server that you can run locally to test against}
13
+ gem.homepage = "https://github.com/iain/fake_sqs"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "sinatra"
21
+ gem.add_dependency "builder"
22
+
23
+ gem.add_development_dependency "rspec"
24
+ gem.add_development_dependency "rake"
25
+ gem.add_development_dependency "aws-sdk"
26
+ gem.add_development_dependency "webmock"
27
+ gem.add_development_dependency "faraday"
28
+ gem.add_development_dependency "thin"
29
+ gem.add_development_dependency "verbose_hash_fetch"
30
+ gem.add_development_dependency "activesupport"
31
+
32
+ end
data/lib/fake_sqs.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'fake_sqs/version'
2
+ require 'fake_sqs/server'
3
+ require 'fake_sqs/queues'
4
+ require 'fake_sqs/responder'
5
+ require 'fake_sqs/queue'
6
+ require 'fake_sqs/queue_factory'
7
+ require 'fake_sqs/message'
8
+
9
+ module FakeSQS
10
+
11
+ def self.server(options = {})
12
+ Server.new(options.merge(queues: queues, responder: responder))
13
+ end
14
+
15
+ def self.queues
16
+ Queues.new(queue_factory: queue_factory)
17
+ end
18
+
19
+ def self.responder
20
+ Responder.new
21
+ end
22
+
23
+ def self.queue_factory
24
+ QueueFactory.new(message_factory: message_factory, queue: queue)
25
+ end
26
+
27
+ def self.message_factory
28
+ Message
29
+ end
30
+
31
+ def self.queue
32
+ Queue
33
+ end
34
+
35
+ end
@@ -0,0 +1,21 @@
1
+ require 'securerandom'
2
+
3
+ module FakeSQS
4
+ class Message
5
+
6
+ attr_reader :body
7
+
8
+ def initialize(options = {})
9
+ @body = options.fetch("MessageBody")
10
+ end
11
+
12
+ def id
13
+ @id ||= SecureRandom.uuid
14
+ end
15
+
16
+ def md5
17
+ @md5 ||= Digest::MD5.hexdigest(body)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,67 @@
1
+ require 'securerandom'
2
+
3
+ module FakeSQS
4
+
5
+ ReadCountOutOfRange = Class.new(RuntimeError)
6
+
7
+ class Queue
8
+
9
+ attr_reader :name, :messages, :message_factory, :messages_in_flight
10
+
11
+ def initialize(options = {})
12
+ @name = options.fetch("QueueName")
13
+ @message_factory = options.fetch(:message_factory)
14
+ reset
15
+ end
16
+
17
+ def send_message(options = {})
18
+ message = message_factory.new(options)
19
+ messages << message
20
+ message
21
+ end
22
+
23
+ def receive_message(options = {})
24
+ amount = Integer options.fetch("MaxNumberOfMessages") { "1" }
25
+
26
+ fail ReadCountOutOfRange, amount if amount > 10
27
+
28
+ return {} if messages.empty?
29
+
30
+ result = {}
31
+
32
+ actual_amount = amount > size ? size : amount
33
+
34
+ actual_amount.times do
35
+ message = messages.delete_at(rand(size))
36
+ receipt = generate_receipt
37
+ messages_in_flight[receipt] = message
38
+ result[receipt] = message
39
+ end
40
+
41
+ result
42
+ end
43
+
44
+ def delete_message(receipt)
45
+ message = messages_in_flight.delete(receipt)
46
+ end
47
+
48
+ def reset
49
+ @messages = []
50
+ @messages_in_flight = {}
51
+ end
52
+
53
+ def expire
54
+ @messages += messages_in_flight.values
55
+ @messages_in_flight = {}
56
+ end
57
+
58
+ def size
59
+ messages.size
60
+ end
61
+
62
+ def generate_receipt
63
+ SecureRandom.hex
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,16 @@
1
+ module FakeSQS
2
+ class QueueFactory
3
+
4
+ attr_reader :message_factory, :queue
5
+
6
+ def initialize(options = {})
7
+ @message_factory = options.fetch(:message_factory)
8
+ @queue = options.fetch(:queue)
9
+ end
10
+
11
+ def new(options)
12
+ queue.new(options.merge(:message_factory => message_factory))
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,57 @@
1
+ module FakeSQS
2
+
3
+ QueueNameExists = Class.new(RuntimeError)
4
+ NonExistentQueue = Class.new(RuntimeError)
5
+
6
+ class Queues
7
+
8
+ attr_reader :queues, :queue_factory
9
+
10
+ def initialize(options = {})
11
+ @queue_factory = options.fetch(:queue_factory)
12
+ reset
13
+ end
14
+
15
+ def create(name, options = {})
16
+ if queues[name]
17
+ fail QueueNameExists, name
18
+ else
19
+ queue = queue_factory.new(options)
20
+ queues[name] = queue
21
+ end
22
+ end
23
+
24
+ def delete(name, options = {})
25
+ if queues[name]
26
+ queues.delete(name)
27
+ else
28
+ fail NonExistentQueue, name
29
+ end
30
+ end
31
+
32
+ def list(options = {})
33
+ if (prefix = options["QueueNamePrefix"])
34
+ queues.select { |name, queue| name =~ /^#{prefix}/ }.values
35
+ else
36
+ queues.values
37
+ end
38
+ end
39
+
40
+ def get(name, options = {})
41
+ if queues[name]
42
+ queues[name]
43
+ else
44
+ fail NonExistentQueue, name
45
+ end
46
+ end
47
+
48
+ def reset
49
+ @queues = {}
50
+ end
51
+
52
+ def expire
53
+ queues.each { |name, queue| queue.expire }
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,22 @@
1
+ require 'builder'
2
+ require 'securerandom'
3
+
4
+ module FakeSQS
5
+ class Responder
6
+
7
+ def call(name, &block)
8
+ xml = Builder::XmlMarkup.new(:indent => 4)
9
+ xml.tag! "#{name}Response" do
10
+ if block
11
+ xml.tag! "#{name}Result" do
12
+ yield xml
13
+ end
14
+ end
15
+ xml.ResponseMetadata do
16
+ xml.RequestId SecureRandom.uuid
17
+ end
18
+ end
19
+ end
20
+
21
+ end
22
+ end