maildir-queue 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Aaron Suggs
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ = maildir-queue
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Aaron Suggs. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "maildir-queue"
8
+ gem.summary = %Q{A simple queue API with a maildir backend.}
9
+ gem.description = %Q{A simple queue API with a maildir backend. Also includes an HTTP API}
10
+ gem.email = "aaron@ktheory.com"
11
+ gem.homepage = "http://github.com/ktheory/maildir-queue"
12
+ gem.authors = ["Aaron Suggs"]
13
+ gem.add_development_dependency "shoulda", ">= 0"
14
+ gem.add_development_dependency "rack-test", ">= 0"
15
+ gem.add_development_dependency "ktheory-fakefs", ">= 0"
16
+ gem.add_dependency "maildir", ">= 0.3.0"
17
+ gem.add_dependency "sinatra", ">= 0.0.0"
18
+ gem.add_dependency "json", ">= 0.0.0"
19
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
20
+ end
21
+ Jeweler::GemcutterTasks.new
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
24
+ end
25
+
26
+ require 'rake/testtask'
27
+ Rake::TestTask.new(:test) do |test|
28
+ test.libs << 'lib' << 'test'
29
+ test.pattern = 'test/**/test_*.rb'
30
+ test.verbose = true
31
+ end
32
+
33
+ begin
34
+ require 'rcov/rcovtask'
35
+ Rcov::RcovTask.new do |test|
36
+ test.libs << 'test'
37
+ test.pattern = 'test/**/test_*.rb'
38
+ test.verbose = true
39
+ end
40
+ rescue LoadError
41
+ task :rcov do
42
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
43
+ end
44
+ end
45
+
46
+ task :test => :check_dependencies
47
+
48
+ task :default => :test
49
+
50
+ require 'rake/rdoctask'
51
+ Rake::RDocTask.new do |rdoc|
52
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "maildir-queue #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,5 @@
1
+ # A rackup file suitable for thin, passenger, unicorn, etc.
2
+
3
+ require "maildir/web_queue"
4
+ Maildir::WebQueue.path = "/tmp/my_maildir"
5
+ run Maildir::WebQueue
@@ -0,0 +1,60 @@
1
+ require 'maildir'
2
+ class Maildir::Queue < Maildir
3
+ # How many times to retry getting a key.
4
+ # Default is -1 (infinite retries)
5
+ SHIFT_RETRIES = -1
6
+
7
+ # Adds a new message to the queue. Returns a Maildir::Message object
8
+ def push(data)
9
+ add(data)
10
+ end
11
+
12
+ # Finds a new message and marks it as being processed (i.e. moves message
13
+ # from new to cur). Returns message if successful; nil if there are no
14
+ # pending messages.
15
+ def shift
16
+ retries = 0
17
+ begin
18
+ # Get a pending message
19
+ message = list(:new, :limit => 1).first
20
+ return nil if message.nil?
21
+
22
+ # Move the message from new to cur
23
+ if message.process
24
+ return message
25
+ else
26
+ raise Errno::ENOENT
27
+ end
28
+ rescue Errno::ENOENT
29
+ # Either message.process failed. Retry.
30
+ if SHIFT_RETRIES < 0 || retries < SHIFT_RETRIES
31
+ retries += 1
32
+ retry
33
+ else
34
+ # After several failures, act as if there are no pending messages
35
+ return nil
36
+ end
37
+ end
38
+ end
39
+
40
+ # Returns messages in cur that haven't been modified since +time+
41
+ def stalled_messages(time)
42
+ list(:cur).select do |message|
43
+ (mtime = message.mtime) && mtime < time
44
+ end
45
+ end
46
+
47
+ # Returns a count of requeued messages
48
+ def requeue_stalled_messages(time)
49
+ stalled_messages(time).inject(0) do |count, message|
50
+ begin
51
+ push(message.data)
52
+ message.destroy
53
+ count + 1
54
+ rescue Errno::ENOENT
55
+ # Could not read message.data
56
+ count
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,74 @@
1
+ require "maildir/queue"
2
+ require "sinatra/base"
3
+ require "json"
4
+
5
+ class Maildir::WebQueue < Sinatra::Base
6
+
7
+ KEY_VALIDATOR = /^cur\/\d{10}\.\w+(\.\w+)+:2,(\w+)?$/
8
+
9
+ def self.path=(path)
10
+ @@queue = Maildir::Queue.new(path)
11
+ end
12
+
13
+ def queue
14
+ @@queue
15
+ end
16
+
17
+ # Test that key is well-formed. If not, return 403 Forbidden error.
18
+ def sanitize(key)
19
+ # E.g. cur/1263444769.M975543P58179Q11.gnt.local:2,
20
+ unless key.match(KEY_VALIDATOR)
21
+ content_type "text/plain"
22
+ halt 403, "Malformed key: #{key}"
23
+ end
24
+ end
25
+
26
+ # Return a 204 No Content response
27
+ def no_content
28
+ halt 204, ""
29
+ end
30
+
31
+ # Check the server status
32
+ get "/status" do
33
+ content_type "application/json"
34
+ {"new" => queue.list(:new).size,"cur" => queue.list(:cur).size}.to_json
35
+ end
36
+
37
+ # Create a new message. Requires params[:data]
38
+ # Returns the message's key as json
39
+ post "/message" do
40
+ halt 400, "Must specify data parameter" unless params[:data]
41
+ message = queue.push(params[:data])
42
+ content_type "application/json"
43
+ message.key.to_json
44
+ end
45
+
46
+ # Shift a new message off the queue
47
+ get "/message" do
48
+ message = queue.shift
49
+ content_type "application/json"
50
+ if message
51
+ {"key" => message.key, "data" => message.data}.to_json
52
+ else
53
+ not_found "No pending messages".to_json
54
+ end
55
+ end
56
+
57
+ # Delete a message from the queue
58
+ delete "/message/*" do |key|
59
+ sanitize(key)
60
+ queue.delete(key)
61
+ no_content
62
+ end
63
+
64
+ # # Update the timestamps on a message
65
+ # post "/message/touch/*" do |key|
66
+ # sanitize(key)
67
+ # if queue.get(key).utime(Time.now, Time.now)
68
+ # no_content
69
+ # else
70
+ # not_found "Key #{key} does not exist"
71
+ # end
72
+ # end
73
+ end
74
+
@@ -0,0 +1,73 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{maildir-queue}
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Aaron Suggs"]
12
+ s.date = %q{2010-01-15}
13
+ s.description = %q{A simple queue API with a maildir backend. Also includes an HTTP API}
14
+ s.email = %q{aaron@ktheory.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "examples/config.ru",
27
+ "lib/maildir/queue.rb",
28
+ "lib/maildir/web_queue.rb",
29
+ "maildir-queue.gemspec",
30
+ "test/helper.rb",
31
+ "test/test_maildir_queue.rb",
32
+ "test/test_maildir_web_queue.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/ktheory/maildir-queue}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.5}
38
+ s.summary = %q{A simple queue API with a maildir backend.}
39
+ s.test_files = [
40
+ "test/helper.rb",
41
+ "test/test_maildir_queue.rb",
42
+ "test/test_maildir_web_queue.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
51
+ s.add_development_dependency(%q<rack-test>, [">= 0"])
52
+ s.add_development_dependency(%q<ktheory-fakefs>, [">= 0"])
53
+ s.add_runtime_dependency(%q<maildir>, [">= 0.3.0"])
54
+ s.add_runtime_dependency(%q<sinatra>, [">= 0.0.0"])
55
+ s.add_runtime_dependency(%q<json>, [">= 0.0.0"])
56
+ else
57
+ s.add_dependency(%q<shoulda>, [">= 0"])
58
+ s.add_dependency(%q<rack-test>, [">= 0"])
59
+ s.add_dependency(%q<ktheory-fakefs>, [">= 0"])
60
+ s.add_dependency(%q<maildir>, [">= 0.3.0"])
61
+ s.add_dependency(%q<sinatra>, [">= 0.0.0"])
62
+ s.add_dependency(%q<json>, [">= 0.0.0"])
63
+ end
64
+ else
65
+ s.add_dependency(%q<shoulda>, [">= 0"])
66
+ s.add_dependency(%q<rack-test>, [">= 0"])
67
+ s.add_dependency(%q<ktheory-fakefs>, [">= 0"])
68
+ s.add_dependency(%q<maildir>, [">= 0.3.0"])
69
+ s.add_dependency(%q<sinatra>, [">= 0.0.0"])
70
+ s.add_dependency(%q<json>, [">= 0.0.0"])
71
+ end
72
+ end
73
+
data/test/helper.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'test/unit'
2
+ require 'shoulda'
3
+ require 'rack/test'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'maildir/queue'
8
+ require 'maildir/web_queue'
9
+
10
+ # Require 'ktheory-fakefs' until issues 28, 29, and 30 are resolved in
11
+ # defunkt/fakefs. See http://github.com/defunkt/fakefs/issues
12
+ gem "ktheory-fakefs"
13
+ require 'fakefs'
14
+
15
+
16
+ # Create a reusable MaildirQueue that's cleaned up when the tests are done
17
+ def temp_queue
18
+ Maildir::Queue.new("/tmp/maildir_queue_test")
19
+ end
@@ -0,0 +1,47 @@
1
+ require 'helper'
2
+ class TestMaildirQueue < Test::Unit::TestCase
3
+
4
+ context "A maildir queue" do
5
+
6
+ setup { FakeFS::FileSystem.clear }
7
+
8
+ should "shift messages" do
9
+ temp_queue.add("1")
10
+ temp_queue.add("2")
11
+ message = temp_queue.shift
12
+ assert_equal "1", message.data
13
+ end
14
+
15
+ should "put shifted messages in cur" do
16
+ temp_queue.add("")
17
+ message = temp_queue.shift
18
+ assert_equal :cur, message.dir
19
+ end
20
+
21
+ should "list stalled messages" do
22
+ temp_queue.add("")
23
+ message = temp_queue.shift
24
+ mtime = Time.now - 100
25
+ message.utime(mtime, mtime)
26
+ assert temp_queue.stalled_messages(Time.now - 50).include?(message)
27
+ end
28
+
29
+ should "requeue stalled messages" do
30
+ data = "my data"
31
+ temp_queue.add(data)
32
+ stalled_message = temp_queue.shift
33
+ mtime = Time.now - 100
34
+ stalled_message.utime(mtime, mtime)
35
+ temp_queue.requeue_stalled_messages(Time.now - 50)
36
+ assert_equal data, temp_queue.list(:new).first.data
37
+ end
38
+
39
+ should "return the number of requeued stalled messages" do
40
+ temp_queue.add("")
41
+ stalled_message = temp_queue.shift
42
+ mtime = Time.now - 100
43
+ stalled_message.utime(mtime, mtime)
44
+ assert_equal 1, temp_queue.requeue_stalled_messages(Time.now - 50)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,85 @@
1
+ require 'helper'
2
+ class TestMaildirWebQueue < Test::Unit::TestCase
3
+ include Rack::Test::Methods
4
+
5
+ def app
6
+ Maildir::WebQueue.path = temp_queue.path
7
+ Maildir::WebQueue
8
+ end
9
+
10
+ context "The webcontroller" do
11
+
12
+ setup do
13
+ FakeFS::FileSystem.clear
14
+ @data = "my message"
15
+ end
16
+
17
+ should "have a status" do
18
+ get "/status"
19
+ assert last_response.ok?
20
+ end
21
+
22
+ should "accept posted messages" do
23
+ post "/message", :data => @data
24
+ assert last_response.ok?
25
+ end
26
+
27
+ should "add posted messages to the queue" do
28
+ post "/message", :data => @data
29
+ assert_equal 1, temp_queue.list(:new).size
30
+ end
31
+
32
+ should "save messages with the right data" do
33
+ post "/message", :data => @data
34
+ assert_equal @data, temp_queue.list(:new).first.data
35
+ end
36
+
37
+ should "return 404 when no pending messages" do
38
+ get "/message"
39
+ assert_equal 404, last_response.status
40
+ end
41
+
42
+ should "return successfully when messages are pending" do
43
+ post "/message", :data => @data
44
+ get "/message"
45
+ assert last_response.ok?
46
+ end
47
+
48
+ should "return a well-formed key when messages are pending" do
49
+ post "/message", :data => @data
50
+ get "/message"
51
+ assert_match Maildir::WebQueue::KEY_VALIDATOR, JSON.parse(last_response.body)["key"]
52
+ end
53
+
54
+ should "return message data when messages are pending" do
55
+ post "/message", :data => @data
56
+ get "/message"
57
+ assert_equal @data, JSON.parse(last_response.body)["data"]
58
+ end
59
+
60
+ end
61
+
62
+ # Test the Maildir::WebQueue::KEY_VALIDATOR
63
+ bad_keys = [ "/etc/passwd", "cur/../../etc/password",
64
+ "cur/123456789.M975543P58179Q11.host:2,",
65
+ "cur/1234567890.M975543P58179Q11:2,",
66
+ "cur/1234567890.M975543P58179Q11.host"
67
+ ]
68
+ bad_keys.each_with_index do |key, i|
69
+ define_method "test_bad_key_#{i}" do
70
+ delete "/message/#{key}"
71
+ assert_equal 403, last_response.status, "Key: #{key}"
72
+ end
73
+ end
74
+
75
+ good_keys = [ "cur/1234567890.M975543P58179Q11.host:2,",
76
+ "cur/1234567890.abc123.really.long.domain.co.uk:2,",
77
+ "cur/1234567890.M975543P58179Q11.host:2,FRS"
78
+ ]
79
+ good_keys.each_with_index do |key, i|
80
+ define_method "test_good_key_#{i}" do
81
+ delete "/message/#{key}"
82
+ assert last_response.successful?
83
+ end
84
+ end
85
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maildir-queue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Suggs
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-15 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: shoulda
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rack-test
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: ktheory-fakefs
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: maildir
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.3.0
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: sinatra
57
+ type: :runtime
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.0.0
64
+ version:
65
+ - !ruby/object:Gem::Dependency
66
+ name: json
67
+ type: :runtime
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.0.0
74
+ version:
75
+ description: A simple queue API with a maildir backend. Also includes an HTTP API
76
+ email: aaron@ktheory.com
77
+ executables: []
78
+
79
+ extensions: []
80
+
81
+ extra_rdoc_files:
82
+ - LICENSE
83
+ - README.rdoc
84
+ files:
85
+ - .document
86
+ - .gitignore
87
+ - LICENSE
88
+ - README.rdoc
89
+ - Rakefile
90
+ - VERSION
91
+ - examples/config.ru
92
+ - lib/maildir/queue.rb
93
+ - lib/maildir/web_queue.rb
94
+ - maildir-queue.gemspec
95
+ - test/helper.rb
96
+ - test/test_maildir_queue.rb
97
+ - test/test_maildir_web_queue.rb
98
+ has_rdoc: true
99
+ homepage: http://github.com/ktheory/maildir-queue
100
+ licenses: []
101
+
102
+ post_install_message:
103
+ rdoc_options:
104
+ - --charset=UTF-8
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: "0"
112
+ version:
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: "0"
118
+ version:
119
+ requirements: []
120
+
121
+ rubyforge_project:
122
+ rubygems_version: 1.3.5
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: A simple queue API with a maildir backend.
126
+ test_files:
127
+ - test/helper.rb
128
+ - test/test_maildir_queue.rb
129
+ - test/test_maildir_web_queue.rb