easy_q 0.6.0
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/CHANGELOG.txt +0 -0
- data/Manifest.txt +14 -0
- data/README.txt +84 -0
- data/Rakefile +56 -0
- data/bin/easy_q +8 -0
- data/db/001_create_messages.rb +18 -0
- data/lib/easy_q/commands.rb +144 -0
- data/lib/easy_q/easy_q_service.rb +96 -0
- data/lib/easy_q/models/message.rb +12 -0
- data/lib/easy_q/version.rb +9 -0
- data/lib/easy_q.rb +1 -0
- data/setup.rb +1585 -0
- data/test/easy_q_test.rb +11 -0
- data/test/test_helper.rb +2 -0
- metadata +59 -0
data/CHANGELOG.txt
ADDED
File without changes
|
data/Manifest.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Rakefile
|
2
|
+
README.txt
|
3
|
+
CHANGELOG.txt
|
4
|
+
Manifest.txt
|
5
|
+
setup.rb
|
6
|
+
db/001_create_messages.rb
|
7
|
+
bin/easy_q
|
8
|
+
lib/easy_q/version.rb
|
9
|
+
lib/easy_q/commands.rb
|
10
|
+
lib/easy_q/easy_q_service.rb
|
11
|
+
lib/easy_q/models/message.rb
|
12
|
+
lib/easy_q.rb
|
13
|
+
test/test_helper.rb
|
14
|
+
test/easy_q_test.rb
|
data/README.txt
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
== EasyQ - A nice little message queue
|
2
|
+
|
3
|
+
|
4
|
+
EasyQ is my attempt to make a simple and uncomplicated message queue.
|
5
|
+
It requires an ActiveRecord compatible database. It isn't very fast,
|
6
|
+
and it doesn't support transactions and it only string messages.
|
7
|
+
It's not perfect but it works for me. I didn't need those things
|
8
|
+
for my purposes, and this was built for my purposes, but you're
|
9
|
+
welcome to use it any. Are you sold?
|
10
|
+
|
11
|
+
Oh ya...I made this on Windows and haven't tried it on *nix.
|
12
|
+
|
13
|
+
One more thing...I'm new to ruby, and I don't know anything about
|
14
|
+
RDoc. Deal.
|
15
|
+
|
16
|
+
Thanks to Assaf (reliable-msg) for the idea.
|
17
|
+
|
18
|
+
== Installation
|
19
|
+
|
20
|
+
gem install easy-q
|
21
|
+
|
22
|
+
== Setup
|
23
|
+
|
24
|
+
create a database on whatever db server your working with.
|
25
|
+
|
26
|
+
create a YAML config file. Call it whatever you want, and place it anywhere one the install machine.
|
27
|
+
|
28
|
+
sample config:
|
29
|
+
|
30
|
+
---
|
31
|
+
:database:
|
32
|
+
:adapter: mysql
|
33
|
+
:username: root
|
34
|
+
:host: localhost
|
35
|
+
:port: 3306
|
36
|
+
:password: password
|
37
|
+
:database: easy_q
|
38
|
+
:service:
|
39
|
+
:address: 0.0.0.0
|
40
|
+
:port: 2222
|
41
|
+
:acl:
|
42
|
+
- deny
|
43
|
+
- all
|
44
|
+
- allow
|
45
|
+
- localhost
|
46
|
+
- allow
|
47
|
+
- 127.0.0.1
|
48
|
+
|
49
|
+
|
50
|
+
== Usage
|
51
|
+
|
52
|
+
* Command Line Usage:
|
53
|
+
# installs the database schema
|
54
|
+
% easy_q install -c config.yaml
|
55
|
+
|
56
|
+
# starts a server
|
57
|
+
% easy_q start -c config.yaml
|
58
|
+
|
59
|
+
# stops a server
|
60
|
+
% easy_q stop -c config.yaml
|
61
|
+
|
62
|
+
# shows a list of queues and current message count for a server
|
63
|
+
% easy_q stats -c config.yaml
|
64
|
+
|
65
|
+
# peek at a few messages from the top or bottom of a queue on the server
|
66
|
+
% easy_q peek queue_name 10 top -c config.yaml
|
67
|
+
% easy_q peek queue_name 10 bottom -c config.yaml
|
68
|
+
|
69
|
+
* Pushing and Popping messages:
|
70
|
+
|
71
|
+
require 'drb'
|
72
|
+
|
73
|
+
o = DRbObject.new(nil,"druby://localhost:2222")
|
74
|
+
|
75
|
+
# push some messages into the queue
|
76
|
+
# :queue => 'name_of_queue' (required)
|
77
|
+
# :ttl => 1000 (seconds to live, not required)
|
78
|
+
# :body => 'hello!!' (the message body, bust be a String class)
|
79
|
+
|
80
|
+
100.times {o.push({:queue => "test", :body => Time.now.to_s})}
|
81
|
+
|
82
|
+
# get messages, returns nil or a hash
|
83
|
+
m = o.pop('text')
|
84
|
+
puts m[:body] if !m.nil?
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'hoe'
|
11
|
+
include FileUtils
|
12
|
+
require File.join(File.dirname(__FILE__), 'lib', 'easy_q', 'version')
|
13
|
+
Dir["#{File.dirname(__FILE__)}/lib/tasks/*.rake"].each { |ext| load ext }
|
14
|
+
AUTHOR = "Brandon" # can also be an array of Authors
|
15
|
+
EMAIL = "brandon@loudcity.net"
|
16
|
+
DESCRIPTION = "a simple little message queue"
|
17
|
+
GEM_NAME = "easy_q" # what ppl will type to install your gem
|
18
|
+
RUBYFORGE_PROJECT = "easy-q" # The unix name for your project
|
19
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
20
|
+
|
21
|
+
|
22
|
+
NAME = "easy_q"
|
23
|
+
REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
24
|
+
VERS = ENV['VERSION'] || (EasyQ::VERSION::STRING + (REV ? ".#{REV}" : ""))
|
25
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
26
|
+
RDOC_OPTS = ['--quiet', '--title', "easy_q documentation",
|
27
|
+
"--opname", "index.html",
|
28
|
+
"--line-numbers",
|
29
|
+
"--main", "README",
|
30
|
+
"--inline-source"]
|
31
|
+
|
32
|
+
class Hoe
|
33
|
+
def extra_deps
|
34
|
+
@extra_deps.reject { |x| Array(x).first == 'hoe' }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generate all the Rake tasks
|
39
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
40
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
41
|
+
p.author = AUTHOR
|
42
|
+
p.description = DESCRIPTION
|
43
|
+
p.email = EMAIL
|
44
|
+
p.summary = DESCRIPTION
|
45
|
+
p.url = HOMEPATH
|
46
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
47
|
+
p.test_globs = ["test/**/*_test.rb"]
|
48
|
+
p.clean_globs = CLEAN #An array of file patterns to delete on clean.
|
49
|
+
|
50
|
+
# == Optional
|
51
|
+
#p.changes - A description of the release's latest changes.
|
52
|
+
#p.extra_deps = ["uuid","activerecord"]
|
53
|
+
#p.spec_extras - A hash of extra values to set in the gemspec.
|
54
|
+
p.need_tar = false
|
55
|
+
p.need_zip = false
|
56
|
+
end
|
data/bin/easy_q
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateMessages < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :messages, :id => false do |t|
|
4
|
+
t.column :id, :string, :limit => 36, :null => false
|
5
|
+
t.column :queue, :string, :limit => 25, :null => false
|
6
|
+
t.column :ttl, :integer
|
7
|
+
t.column :body, :text
|
8
|
+
t.column :created_at, :datetime
|
9
|
+
end
|
10
|
+
|
11
|
+
execute "ALTER TABLE messages ADD PRIMARY KEY (id)"
|
12
|
+
add_index :messages, :queue
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :messages
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'yaml'
|
3
|
+
require 'drb'
|
4
|
+
require 'optparse'
|
5
|
+
require 'rdoc/usage'
|
6
|
+
require 'active_record'
|
7
|
+
|
8
|
+
module EasyQ
|
9
|
+
class Commands
|
10
|
+
|
11
|
+
USAGE = <<-EOF
|
12
|
+
usage: easy_q command [args] [options]
|
13
|
+
|
14
|
+
To see list of available commands and options
|
15
|
+
easy_q help
|
16
|
+
EOF
|
17
|
+
|
18
|
+
HELP = <<-EOF
|
19
|
+
usage: queues command [args] [options]
|
20
|
+
|
21
|
+
Reliable messaging queue manager, version #{VERSION}
|
22
|
+
|
23
|
+
Available commands:
|
24
|
+
|
25
|
+
help
|
26
|
+
Display this help message.
|
27
|
+
|
28
|
+
start
|
29
|
+
Start the easy_q as a standalone server
|
30
|
+
|
31
|
+
stop
|
32
|
+
Stop the easy_q server.
|
33
|
+
|
34
|
+
empty <queue>
|
35
|
+
Empty (delete all messages from) the named queue.
|
36
|
+
|
37
|
+
install -c config.yml
|
38
|
+
this creates the queue db schema
|
39
|
+
|
40
|
+
Available options:
|
41
|
+
|
42
|
+
-v --version
|
43
|
+
Show version number.
|
44
|
+
|
45
|
+
-c --config <path>
|
46
|
+
Points to the queue manager configuration file.
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
EOF
|
51
|
+
|
52
|
+
class InvalidUsage < Exception #:nodoc:
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
end
|
57
|
+
|
58
|
+
def run
|
59
|
+
#begin
|
60
|
+
config_file = nil
|
61
|
+
opts = OptionParser.new
|
62
|
+
opts.on("-c FILE", "--config FILE", String) { |value| config_file = value }
|
63
|
+
opts.on("-v", "--version") do
|
64
|
+
puts "Reliable messaging queue manager, version #{VERSION}"
|
65
|
+
exit
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on("-h", "--help") do
|
69
|
+
puts HELP
|
70
|
+
exit
|
71
|
+
end
|
72
|
+
|
73
|
+
args = opts.parse(ARGV)
|
74
|
+
|
75
|
+
raise InvalidUsage if args.length < 1
|
76
|
+
|
77
|
+
if config_file.nil?
|
78
|
+
puts "Specify a config file. -c settings.yml"
|
79
|
+
exit
|
80
|
+
end
|
81
|
+
|
82
|
+
@settings = YAML::load_file(config_file)
|
83
|
+
|
84
|
+
case args[0]
|
85
|
+
when 'help'
|
86
|
+
puts HELP
|
87
|
+
when 'install'
|
88
|
+
puts "installing..."
|
89
|
+
do_install
|
90
|
+
when 'start'
|
91
|
+
service = Service.new(@settings)
|
92
|
+
service.start
|
93
|
+
begin
|
94
|
+
while true
|
95
|
+
exit if service.shut_down
|
96
|
+
sleep 3
|
97
|
+
end
|
98
|
+
rescue Interrupt
|
99
|
+
puts exit
|
100
|
+
end
|
101
|
+
when 'stop'
|
102
|
+
service = DRbObject.new(nil, "druby://localhost:#{@settings[:service][:port]}")
|
103
|
+
service.stop
|
104
|
+
when 'stats'
|
105
|
+
service = DRbObject.new(nil, "druby://localhost:#{@settings[:service][:port]}")
|
106
|
+
service.stats.each do |q,c|
|
107
|
+
puts "#{q}: #{c}"
|
108
|
+
end
|
109
|
+
when 'peek'
|
110
|
+
queue = args[1]
|
111
|
+
number = args[2].to_i unless args[2].nil?
|
112
|
+
direction = args[3] unless args[3].nil?
|
113
|
+
service = DRbObject.new(nil, "druby://localhost:#{@settings[:service][:port]}")
|
114
|
+
service.peek(queue,number,direction).each do |m|
|
115
|
+
puts "####"
|
116
|
+
puts "id: #{m[:id]}"
|
117
|
+
puts "queue: #{m[:queue]}"
|
118
|
+
puts "ttl: #{m[:ttl]}"
|
119
|
+
puts "created_at #{m[:created_at]}"
|
120
|
+
puts "body: #{m[:body]}"
|
121
|
+
end
|
122
|
+
when 'empty'
|
123
|
+
queue = args[1]
|
124
|
+
service = DRbObject.new(nil, "druby://localhost:#{@settings[:service][:port]}")
|
125
|
+
service.empty(queue)
|
126
|
+
end
|
127
|
+
#rescue Exception => ex
|
128
|
+
# puts ex
|
129
|
+
#end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def do_install
|
134
|
+
@logger = Logger.new $stderr
|
135
|
+
ActiveRecord::Base.logger = @logger
|
136
|
+
ActiveRecord::Base.colorize_logging = false
|
137
|
+
|
138
|
+
ActiveRecord::Base.establish_connection(@settings)
|
139
|
+
|
140
|
+
ActiveRecord::Migrator.migrate("#{File.dirname(__FILE__)}/../../db/", nil)
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'drb'
|
2
|
+
require 'drb/acl'
|
3
|
+
require 'thread'
|
4
|
+
require 'yaml'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'uuid'
|
7
|
+
|
8
|
+
module EasyQ
|
9
|
+
class Service
|
10
|
+
attr_accessor :shut_down
|
11
|
+
|
12
|
+
def initialize(options={})
|
13
|
+
@shut_down = false
|
14
|
+
@settings = options
|
15
|
+
@mutex = Mutex.new
|
16
|
+
@logger = Logger.new $stderr
|
17
|
+
@cache = []
|
18
|
+
ActiveRecord::Base.logger = @logger
|
19
|
+
ActiveRecord::Base.colorize_logging = false
|
20
|
+
ActiveRecord::Base.establish_connection(options[:database])
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def start
|
25
|
+
@mutex.synchronize do
|
26
|
+
#to do - add code to check if service is already running
|
27
|
+
uri = "druby://#{@settings[:service][:address]}:#{@settings[:service][:port]}"
|
28
|
+
DRb.install_acl(ACL.new(@settings[:service][:acl],ACL::DENY_ALLOW))
|
29
|
+
@drb_service = DRb.start_service(uri, self)
|
30
|
+
puts "service started and listening at #{uri}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def stop
|
35
|
+
@mutex.synchronize do
|
36
|
+
@shut_down = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def stats
|
41
|
+
return Message.count(:group=>:queue)
|
42
|
+
end
|
43
|
+
|
44
|
+
def peek(queue_name, number = 10, direction = 'top')
|
45
|
+
return_value = []
|
46
|
+
case direction
|
47
|
+
when 'top'
|
48
|
+
results = Message.find(:all, :limit => number, :conditions => ["queue = ?",queue_name])
|
49
|
+
when 'bottom'
|
50
|
+
results = Message.find(:all, :limit => number, :conditions => ["queue = ?",queue_name], :order => :created_at)
|
51
|
+
end
|
52
|
+
results.each do |r|
|
53
|
+
return_value << r.to_hash
|
54
|
+
end
|
55
|
+
return return_value
|
56
|
+
end
|
57
|
+
|
58
|
+
def empty(queue_name)
|
59
|
+
Message.delete_all "queue = '#{queue_name}'"
|
60
|
+
return nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def push(options={})
|
64
|
+
raise Exception, "missing options[:queue]" if !options.key?(:queue)
|
65
|
+
raise Exception, "missing options[:body]" if !options.key?(:body)
|
66
|
+
|
67
|
+
if options.key?(:queue)
|
68
|
+
raise Exception, "options[:queue] cannot be empty" if options[:queue].empty?
|
69
|
+
end
|
70
|
+
|
71
|
+
if options.key?(:body)
|
72
|
+
raise Exception, "options[:body] cannot be empty" if options[:body].empty?
|
73
|
+
raise Exception, "options[:body] can only be a String" if options[:body].class.to_s != "String"
|
74
|
+
end
|
75
|
+
|
76
|
+
Message.create(options)
|
77
|
+
return nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def pop(queue_name)
|
81
|
+
@mutex.synchronize do
|
82
|
+
message = Message.find(:first,
|
83
|
+
:conditions => ["(queue = ? and (created_at + ttl) > ?) or (queue = ? and ttl is null)",
|
84
|
+
queue_name, Time.now, queue_name]
|
85
|
+
)
|
86
|
+
|
87
|
+
if !message.nil?
|
88
|
+
message.destroy
|
89
|
+
return message.to_hash
|
90
|
+
else
|
91
|
+
return nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/easy_q.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), 'easy_q/**/*.rb')].sort.each { |lib| require lib }
|