qup 1.1.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/.autotest +41 -0
- data/.gemtest +1 -0
- data/ADAPTER_API.rdoc +97 -0
- data/HISTORY.rdoc +9 -0
- data/Manifest.txt +52 -0
- data/README.rdoc +156 -0
- data/Rakefile +246 -0
- data/lib/qup.rb +48 -0
- data/lib/qup/adapter.rb +57 -0
- data/lib/qup/adapter/kestrel.rb +56 -0
- data/lib/qup/adapter/kestrel/destination.rb +54 -0
- data/lib/qup/adapter/kestrel/queue.rb +101 -0
- data/lib/qup/adapter/kestrel/topic.rb +68 -0
- data/lib/qup/adapter/maildir.rb +57 -0
- data/lib/qup/adapter/maildir/queue.rb +123 -0
- data/lib/qup/adapter/maildir/topic.rb +85 -0
- data/lib/qup/adapter/redis.rb +55 -0
- data/lib/qup/adapter/redis/connection.rb +32 -0
- data/lib/qup/adapter/redis/queue.rb +97 -0
- data/lib/qup/adapter/redis/topic.rb +76 -0
- data/lib/qup/consumer.rb +42 -0
- data/lib/qup/message.rb +18 -0
- data/lib/qup/producer.rb +28 -0
- data/lib/qup/publisher.rb +30 -0
- data/lib/qup/queue_api.rb +124 -0
- data/lib/qup/session.rb +111 -0
- data/lib/qup/subscriber.rb +28 -0
- data/lib/qup/topic_api.rb +92 -0
- data/spec/qup/adapter/kestrel/queue_spec.rb +9 -0
- data/spec/qup/adapter/kestrel/topic_spec.rb +9 -0
- data/spec/qup/adapter/kestrel_context.rb +8 -0
- data/spec/qup/adapter/kestrel_spec.rb +8 -0
- data/spec/qup/adapter/maildir/queue_spec.rb +9 -0
- data/spec/qup/adapter/maildir/topic_spec.rb +9 -0
- data/spec/qup/adapter/maildir_context.rb +10 -0
- data/spec/qup/adapter/maildir_spec.rb +8 -0
- data/spec/qup/adapter/redis/queue_spec.rb +9 -0
- data/spec/qup/adapter/redis/topic_spec.rb +9 -0
- data/spec/qup/adapter/redis_context.rb +6 -0
- data/spec/qup/adapter/redis_spec.rb +8 -0
- data/spec/qup/adapter_spec.rb +28 -0
- data/spec/qup/consumer_spec.rb +40 -0
- data/spec/qup/message_spec.rb +13 -0
- data/spec/qup/producer_spec.rb +18 -0
- data/spec/qup/queue_api_spec.rb +21 -0
- data/spec/qup/session_spec.rb +81 -0
- data/spec/qup/shared_adapter_examples.rb +29 -0
- data/spec/qup/shared_queue_examples.rb +71 -0
- data/spec/qup/shared_topic_examples.rb +57 -0
- data/spec/qup/topic_api_spec.rb +21 -0
- data/spec/qup_spec.rb +37 -0
- data/spec/spec_helper.rb +26 -0
- metadata +281 -0
data/.autotest
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# vim: syntax=ruby
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
# vim: ft=ruby
|
5
|
+
|
6
|
+
Autotest.add_hook :initialize do |at|
|
7
|
+
|
8
|
+
at.libs = %w[lib spec].join(File::PATH_SEPARATOR)
|
9
|
+
at.testlib = 'rspec'
|
10
|
+
|
11
|
+
at.add_exception 'coverage.info'
|
12
|
+
at.add_exception 'coverage'
|
13
|
+
at.add_exception '.git'
|
14
|
+
|
15
|
+
at.clear_mappings
|
16
|
+
|
17
|
+
# file in /spec -> run it
|
18
|
+
at.add_mapping(%r|^spec/.*_spec\.rb$|) do |filename, _|
|
19
|
+
filename
|
20
|
+
end
|
21
|
+
|
22
|
+
# file in /lib -> run test in /spec
|
23
|
+
at.add_mapping(%r|^lib/(.*)\.rb$|) do |_, match|
|
24
|
+
at.files_matching %r|^spec/#{match[1]}_spec.rb|
|
25
|
+
end
|
26
|
+
|
27
|
+
at.add_mapping(%r|^spec/spec_helper\.rb|) do
|
28
|
+
at.files_matching( %r|^spec/.*_spec\.rb| )
|
29
|
+
end
|
30
|
+
|
31
|
+
# Make the class map for things in lib
|
32
|
+
class_map = {}
|
33
|
+
sep = File::SEPARATOR
|
34
|
+
Dir.glob("lib/**/*.rb").each do |f|
|
35
|
+
c = f.sub(/lib#{sep}/,'').sub(/\.rb$/,'').split sep
|
36
|
+
c = c.map { |path| path.split(/_|(\d+)/).map { |seq| seq.capitalize }.join }
|
37
|
+
class_map[c.join("::")] = f
|
38
|
+
end
|
39
|
+
at.extra_class_map = class_map
|
40
|
+
end
|
41
|
+
|
data/.gemtest
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/ADAPTER_API.rdoc
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
= Adding an Adapter
|
2
|
+
|
3
|
+
Adding an Adapter to Qup should take the implementation of 3 classes
|
4
|
+
|
5
|
+
* Adapter - Inherit from Qup::Adapter
|
6
|
+
* Queue - Implement Qup::QueueAPI
|
7
|
+
* Topic - Implement Qup::TopicAPI
|
8
|
+
|
9
|
+
The rest of the system uses the defined interfaces in the above 3
|
10
|
+
Classes/Modules to implement the rest of the behavior.
|
11
|
+
|
12
|
+
Qup ships with 2 adapters already and you should use those as a guideline for
|
13
|
+
implementing your own Adapter. Feel free to {open an
|
14
|
+
issue on Github}[https://github.com/copiousfreetime/qup/issues] with me to
|
15
|
+
create a new adaptor, or send me a pull request.
|
16
|
+
|
17
|
+
== Adapter
|
18
|
+
|
19
|
+
The Qup::Adapter class is how the Adapter gets loaded and is the entry point
|
20
|
+
that Qup will use to use the backend.
|
21
|
+
|
22
|
+
The Adapter API is laid out in Qup::Adapter.
|
23
|
+
|
24
|
+
The Adapter class you implement must have Qup::Adapter as its parent class so that
|
25
|
+
you can call the 'register' class method to register your adapter. Your adapter
|
26
|
+
will be invoked when a URI with a scheme equivalent to your registration key is
|
27
|
+
used.
|
28
|
+
|
29
|
+
The other instance methods are an Internal API that the Qup::Session object will
|
30
|
+
use to interface with your Adapter. These methods should not be used by an end
|
31
|
+
user of the library, they are solely for use by the Qup library.
|
32
|
+
|
33
|
+
* Qup::Adapter#close - close the Adapter for further use
|
34
|
+
* Qup::Adapter#closed? - is the Adapter closed
|
35
|
+
* Qup::Adapter#queue - create an object that implements the QueueAPI
|
36
|
+
* Qup::Adapter#topic - create an object that implements the TopicAPI
|
37
|
+
|
38
|
+
See Qup::Adapter::Maildir or Qup::Adapter::Kestrel
|
39
|
+
|
40
|
+
=== Example
|
41
|
+
|
42
|
+
class Qup::Adapter::MyBackend < Qup::Adapter
|
43
|
+
register :mybackend
|
44
|
+
end
|
45
|
+
|
46
|
+
session = Qup::Session.new( 'mybackend://...' )
|
47
|
+
|
48
|
+
|
49
|
+
== Qup::Adapter::MyBackend::Queue
|
50
|
+
|
51
|
+
The Queue class is a point-to-point Messaging implementation, typically used for
|
52
|
+
worker queues. You should create a 'Qup::Adapter::MyBackend' and have it
|
53
|
+
'include Qup:QueueAPI'. These are the methods that are defined for Queue
|
54
|
+
objects.
|
55
|
+
|
56
|
+
When Qup::Adapter::MyBackend#queue is invoked, it should return an instance of
|
57
|
+
the object that implements Qup::QueueAPI.
|
58
|
+
|
59
|
+
See Qup::Adapter::Maildir::Queue or Qup::Adapter::Kestrel::Queue
|
60
|
+
|
61
|
+
=== Public API used by end users of the system
|
62
|
+
|
63
|
+
* Qup::QueueAPI#depth - How many Messages are currently on the Queue
|
64
|
+
* Qup::QueueAPI#destroy - Remove the Queue from the System if possible
|
65
|
+
* Qup::QueueAPI#flush - Remove all Messages from the Queue
|
66
|
+
* Qup::QueueAPI#name - The String Name of the Queue
|
67
|
+
|
68
|
+
=== Internal API used by the Qup library to implement the higher level patterns
|
69
|
+
|
70
|
+
* Qup::QueueAPI#acknowledge - Tell the System that you have completed processing a Message
|
71
|
+
* Qup::QueueAPI#consume - Take a message off of the Queue
|
72
|
+
* Qup::QueueAPI#produce - Put a message onto the Queue
|
73
|
+
|
74
|
+
|
75
|
+
== Qup::Adapter::MyBackend::Topic
|
76
|
+
|
77
|
+
The Topic class is a fanout or pub/sub Messaging implementation, typically
|
78
|
+
used to alert or send the same message from one publisher to many subscribers.
|
79
|
+
This API is defined in Qup::TopicAPI and all these methods must be implemented.
|
80
|
+
|
81
|
+
When Qup::Adapter::MyBackend#topic is invoked, it should return an instance of
|
82
|
+
the object that implements Qup::TopicAPI.
|
83
|
+
|
84
|
+
See Qup::Adapter::Maildir::Topic or Qup::Adapter::Kestrel::Topic
|
85
|
+
|
86
|
+
=== Public API used by end users of the system
|
87
|
+
|
88
|
+
* Qup::TopicAPI#destroy - Remove the Topic from the System if possible
|
89
|
+
* Qup::TopicAPI#name - The String Name of the Topic
|
90
|
+
* Qup::TopicAPI#publisher - Create a new Publisher to the Topic
|
91
|
+
* Qup::TopicAPI#subscriber - Create a new Subscriber to the Topic
|
92
|
+
* Qup::TopicAPI#subscriber_count - The number of Subscribers on the Topic
|
93
|
+
|
94
|
+
=== Internal API used by the Qup library to implement the higher level patterns
|
95
|
+
|
96
|
+
* Qup::TopicAPI#publish - Send a Message to all the Subscribers
|
97
|
+
|
data/HISTORY.rdoc
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
.autotest
|
2
|
+
.gemtest
|
3
|
+
ADAPTER_API.rdoc
|
4
|
+
HISTORY.rdoc
|
5
|
+
Manifest.txt
|
6
|
+
README.rdoc
|
7
|
+
Rakefile
|
8
|
+
lib/qup.rb
|
9
|
+
lib/qup/adapter.rb
|
10
|
+
lib/qup/adapter/kestrel.rb
|
11
|
+
lib/qup/adapter/kestrel/destination.rb
|
12
|
+
lib/qup/adapter/kestrel/queue.rb
|
13
|
+
lib/qup/adapter/kestrel/topic.rb
|
14
|
+
lib/qup/adapter/maildir.rb
|
15
|
+
lib/qup/adapter/maildir/queue.rb
|
16
|
+
lib/qup/adapter/maildir/topic.rb
|
17
|
+
lib/qup/adapter/redis.rb
|
18
|
+
lib/qup/adapter/redis/connection.rb
|
19
|
+
lib/qup/adapter/redis/queue.rb
|
20
|
+
lib/qup/adapter/redis/topic.rb
|
21
|
+
lib/qup/consumer.rb
|
22
|
+
lib/qup/message.rb
|
23
|
+
lib/qup/producer.rb
|
24
|
+
lib/qup/publisher.rb
|
25
|
+
lib/qup/queue_api.rb
|
26
|
+
lib/qup/session.rb
|
27
|
+
lib/qup/subscriber.rb
|
28
|
+
lib/qup/topic_api.rb
|
29
|
+
spec/qup/adapter/kestrel/queue_spec.rb
|
30
|
+
spec/qup/adapter/kestrel/topic_spec.rb
|
31
|
+
spec/qup/adapter/kestrel_context.rb
|
32
|
+
spec/qup/adapter/kestrel_spec.rb
|
33
|
+
spec/qup/adapter/maildir/queue_spec.rb
|
34
|
+
spec/qup/adapter/maildir/topic_spec.rb
|
35
|
+
spec/qup/adapter/maildir_context.rb
|
36
|
+
spec/qup/adapter/maildir_spec.rb
|
37
|
+
spec/qup/adapter/redis/queue_spec.rb
|
38
|
+
spec/qup/adapter/redis/topic_spec.rb
|
39
|
+
spec/qup/adapter/redis_context.rb
|
40
|
+
spec/qup/adapter/redis_spec.rb
|
41
|
+
spec/qup/adapter_spec.rb
|
42
|
+
spec/qup/consumer_spec.rb
|
43
|
+
spec/qup/message_spec.rb
|
44
|
+
spec/qup/producer_spec.rb
|
45
|
+
spec/qup/queue_api_spec.rb
|
46
|
+
spec/qup/session_spec.rb
|
47
|
+
spec/qup/shared_adapter_examples.rb
|
48
|
+
spec/qup/shared_queue_examples.rb
|
49
|
+
spec/qup/shared_topic_examples.rb
|
50
|
+
spec/qup/topic_api_spec.rb
|
51
|
+
spec/qup_spec.rb
|
52
|
+
spec/spec_helper.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
= qup - Queue Up
|
2
|
+
|
3
|
+
* http://github.com/copiousfreetime/qup
|
4
|
+
|
5
|
+
== DESCRIPTION
|
6
|
+
|
7
|
+
Qup is a generalized API for Message Queue and Publish/Subscribe messaging
|
8
|
+
patterns with the ability to plug in an appropriate messaging infrastructure
|
9
|
+
based upon your needs.
|
10
|
+
|
11
|
+
Qup ships with support for {Kestrel}[https://github.com/robey/kestrel] and a
|
12
|
+
filesystem infrastructure based on {Maildir}[https://rubygems.org/gems/maildir].
|
13
|
+
Additional Adapters will be developed as needs arise. {Please submit an
|
14
|
+
Issue}[https://github.com/copiousfreetime/qup/issues] to have a new Adapter
|
15
|
+
created. Pull requests gladly accepted.
|
16
|
+
|
17
|
+
== FEATURES
|
18
|
+
|
19
|
+
Qup provides an abstract implementation of two common messaging patterns.
|
20
|
+
|
21
|
+
[Basic Message Queue]
|
22
|
+
|
23
|
+
Examples of a basic message queue are {Work/Task
|
24
|
+
Queues}[http://www.rabbitmq.com/tutorials/tutorial-two-python.html], {JMS
|
25
|
+
Queue}[http://docs.oracle.com/javaee/6/api/javax/jms/Queue.html], or {Amazon
|
26
|
+
SQS}[http://aws.amazon.com/sqs/]. This is a pattern where one or more
|
27
|
+
Producers puts Messages on a Queue and one or more Consumers received those
|
28
|
+
Messages. Each Message is delivered only 1 time to a Consumer.
|
29
|
+
|
30
|
+
[Publish/Subscribe]
|
31
|
+
|
32
|
+
{Wikipedia Article on Pub/Sub
|
33
|
+
pattern}[http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern].
|
34
|
+
|
35
|
+
Qup implements a Topic based system, where Publishers send Messages on a Topic
|
36
|
+
and all Subscribers to that topic each receive their own copy of the message.
|
37
|
+
|
38
|
+
Qup assumes that the messaging systems it has adapters for provided durable and
|
39
|
+
acknowledgeable messaging.
|
40
|
+
|
41
|
+
[Durability]
|
42
|
+
|
43
|
+
When message is sent to the messaging system by Qup, the message is persisted to
|
44
|
+
disk.
|
45
|
+
|
46
|
+
[Acknowledgeable Messages]
|
47
|
+
|
48
|
+
When a Consumer receives a Message, and then processes it, Qup assumes that
|
49
|
+
the messaging infrastructure requires that the Message be positively
|
50
|
+
acknowledged. In other words, if the Consumer does not acknowledge the message
|
51
|
+
then the messages infrastructure will put the Message back onto the Queue.
|
52
|
+
|
53
|
+
== SYNOPSIS
|
54
|
+
|
55
|
+
=== Basic Message Queue
|
56
|
+
|
57
|
+
session = Qup::Session.new( "maildir:///tmp/test-queue" )
|
58
|
+
queue = session.queue( 'basic-messaging' )
|
59
|
+
producer = queue.producer
|
60
|
+
|
61
|
+
consumer_1 = queue.consumer
|
62
|
+
consumer_2 = queue.consumer
|
63
|
+
|
64
|
+
producer.produce( 'message_1' )
|
65
|
+
producer.produce( 'message_2' )
|
66
|
+
|
67
|
+
message_1 = consumer_1.consume
|
68
|
+
puts message_1.data # => 'message_1'
|
69
|
+
consumer_1.acknowledge( message_1 )
|
70
|
+
|
71
|
+
consumer_2.consume do |message_2|
|
72
|
+
puts message_2.data # => 'message_2'
|
73
|
+
end # auto acknowledged at the end of the block
|
74
|
+
|
75
|
+
=== Publish/Subscribe
|
76
|
+
|
77
|
+
session = Qup::Session.new( "kestrel://messaging.example.com:22133" )
|
78
|
+
topic = session.topic( 'topic-messaging' )
|
79
|
+
publisher = topic.publisher
|
80
|
+
|
81
|
+
subscribers = []
|
82
|
+
3.times do |n|
|
83
|
+
subscribers << topic.subscriber( "subscriber-#{n}" )
|
84
|
+
end
|
85
|
+
|
86
|
+
publisher.publish( 'a fine message on a topic' )
|
87
|
+
|
88
|
+
subscribers.each do |sub|
|
89
|
+
sub.consume do |msg|
|
90
|
+
puts msg.data # => 'a fine message on a topic'
|
91
|
+
end # auto acknowledge an end of block
|
92
|
+
end
|
93
|
+
|
94
|
+
== REQUIREMENTS
|
95
|
+
|
96
|
+
Depending on the backend messaging system you want to use, you'll need to
|
97
|
+
install additional gems. At the current moment, these are the supported
|
98
|
+
messaging backends.
|
99
|
+
|
100
|
+
* Qup::Adapter::Maildir - built in and uses the 'maildir' gem
|
101
|
+
* Qup::Adapter::Kestrel - uses the 'kestrel-client' gem
|
102
|
+
|
103
|
+
== INSTALL
|
104
|
+
|
105
|
+
* gem install qup
|
106
|
+
|
107
|
+
== DEVELOPERS
|
108
|
+
|
109
|
+
After checking out the source, run:
|
110
|
+
|
111
|
+
$ rake develop
|
112
|
+
|
113
|
+
This task will install any missing dependencies. You may then run:
|
114
|
+
|
115
|
+
$ rake test
|
116
|
+
|
117
|
+
Other tasks are viewable with
|
118
|
+
|
119
|
+
$ rake -T
|
120
|
+
|
121
|
+
=== Kestrel
|
122
|
+
|
123
|
+
To run the Kestrel tests you will need:
|
124
|
+
|
125
|
+
* gem install kestrel-client
|
126
|
+
* A Kestrel server running on <tt>localhost:22133</tt>
|
127
|
+
|
128
|
+
You can download Kestrel from http://robey.github.com/kestrel/ and then run the
|
129
|
+
+scripts/devel.sh+ command and you will have a default Kestrel server running on
|
130
|
+
<tt>localhost:22133</tt>. This will be enough to run the kestrel tests.
|
131
|
+
|
132
|
+
|
133
|
+
== LICENSE
|
134
|
+
|
135
|
+
(The ISC LICENSE)
|
136
|
+
|
137
|
+
Copyright (c) 2012 Jeremy Hinegardner
|
138
|
+
|
139
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
140
|
+
a copy of this software and associated documentation files (the
|
141
|
+
'Software'), to deal in the Software without restriction, including
|
142
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
143
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
144
|
+
permit persons to whom the Software is furnished to do so, subject to
|
145
|
+
the following conditions:
|
146
|
+
|
147
|
+
The above copyright notice and this permission notice shall be
|
148
|
+
included in all copies or substantial portions of the Software.
|
149
|
+
|
150
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
151
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
152
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
153
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
154
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
155
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
156
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
# vim: syntax=ruby
|
2
|
+
|
3
|
+
This.name = "qup"
|
4
|
+
This.author = "Jeremy Hinegardner"
|
5
|
+
This.email = "jeremy@copiousfreetime.org"
|
6
|
+
This.homepage = "http://github.com/copiousfreetime/#{ This.name }"
|
7
|
+
This.version = Util.version
|
8
|
+
|
9
|
+
#------------------------------------------------------------------------------
|
10
|
+
# If you want to Develop on qup just run 'rake develop' and you'll have all you
|
11
|
+
# need to get going. If you want to use bundler for development, then run
|
12
|
+
# 'rake develop:using_bundler'
|
13
|
+
#------------------------------------------------------------------------------
|
14
|
+
namespace :develop do
|
15
|
+
task :default do
|
16
|
+
require 'rubygems/dependency_installer'
|
17
|
+
installer = Gem::DependencyInstaller.new
|
18
|
+
|
19
|
+
puts "Installing gem depedencies needed for development"
|
20
|
+
This.gemspec.dependencies.each do |dep|
|
21
|
+
if dep.matching_specs.empty? then
|
22
|
+
puts "Installing : #{dep}"
|
23
|
+
installer.install dep
|
24
|
+
else
|
25
|
+
puts "Skipping : #{dep} -> already installed #{dep.matching_specs.first.full_name}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
puts "\n\nNow run 'rake test'"
|
29
|
+
end
|
30
|
+
|
31
|
+
file 'Gemfile' => :gemspec do
|
32
|
+
File.open( "Gemfile", "w+" ) do |f|
|
33
|
+
f.puts 'source :rubygems'
|
34
|
+
f.puts 'gemspec'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Create a bundler Gemfile"
|
39
|
+
task :using_bundler => 'Gemfile' do
|
40
|
+
puts "Now you can 'bundle'"
|
41
|
+
end
|
42
|
+
CLOBBER << FileList['Gemfile*']
|
43
|
+
end
|
44
|
+
desc "Boostrap development"
|
45
|
+
task :develop => "develop:default"
|
46
|
+
|
47
|
+
#------------------------------------------------------------------------------
|
48
|
+
# RSpec
|
49
|
+
#------------------------------------------------------------------------------
|
50
|
+
begin
|
51
|
+
require 'rspec/core/rake_task'
|
52
|
+
RSpec::Core::RakeTask.new( :test ) do |t|
|
53
|
+
t.ruby_opts = %w[ -w ]
|
54
|
+
t.rspec_opts = %w[ --color --format documentation ]
|
55
|
+
end
|
56
|
+
task :default => :test
|
57
|
+
rescue LoadError
|
58
|
+
Util.task_warning( 'test' )
|
59
|
+
end
|
60
|
+
|
61
|
+
#------------------------------------------------------------------------------
|
62
|
+
# RDoc
|
63
|
+
#------------------------------------------------------------------------------
|
64
|
+
begin
|
65
|
+
require 'rdoc/task'
|
66
|
+
RDoc::Task.new do |t|
|
67
|
+
t.markup = 'tomdoc'
|
68
|
+
t.rdoc_dir = 'doc'
|
69
|
+
t.main = 'README.rdoc'
|
70
|
+
t.title = "#{This.name} #{This.version}"
|
71
|
+
t.rdoc_files.include( '*.rdoc', 'lib/**/*.rb' )
|
72
|
+
end
|
73
|
+
rescue LoadError
|
74
|
+
Util.task_warning( 'rdoc' )
|
75
|
+
end
|
76
|
+
|
77
|
+
#------------------------------------------------------------------------------
|
78
|
+
# Coverage
|
79
|
+
#------------------------------------------------------------------------------
|
80
|
+
begin
|
81
|
+
require 'rcov/rcovtask'
|
82
|
+
Rcov::RcovTask.new do |t|
|
83
|
+
t.libs << 'spec'
|
84
|
+
t.pattern = 'spec/**/*_spec.rb'
|
85
|
+
t.verbose = true
|
86
|
+
t.rcov_opts << "-x ^/" # remove all the global files
|
87
|
+
t.rcov_opts << "--sort coverage" # so we see the worst files at the top
|
88
|
+
end
|
89
|
+
rescue LoadError
|
90
|
+
Util.task_warning( 'rcov' )
|
91
|
+
end
|
92
|
+
|
93
|
+
#------------------------------------------------------------------------------
|
94
|
+
# Manifest - most of this is from Hoe
|
95
|
+
#------------------------------------------------------------------------------
|
96
|
+
namespace 'manifest' do
|
97
|
+
desc "Check the manifest"
|
98
|
+
task :check => :clean do
|
99
|
+
files = FileList["**/*", ".*"].exclude( This.exclude_from_manifest ).to_a.sort
|
100
|
+
files = files.select{ |f| File.file?( f ) }
|
101
|
+
|
102
|
+
tmp = "Manifest.tmp"
|
103
|
+
File.open( tmp, 'w' ) do |f|
|
104
|
+
f.puts files.join("\n")
|
105
|
+
end
|
106
|
+
|
107
|
+
begin
|
108
|
+
sh "diff -du Manifest.txt #{tmp}"
|
109
|
+
ensure
|
110
|
+
rm tmp
|
111
|
+
end
|
112
|
+
puts "Manifest looks good"
|
113
|
+
end
|
114
|
+
|
115
|
+
desc "Generate the manifest"
|
116
|
+
task :generate => :clean do
|
117
|
+
files = %x[ git ls-files ].split("\n").sort
|
118
|
+
files.reject! { |f| f =~ This.exclude_from_manifest }
|
119
|
+
File.open( "Manifest.txt", "w" ) do |f|
|
120
|
+
f.puts files.join("\n")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
#------------------------------------------------------------------------------
|
126
|
+
# Gem Specification
|
127
|
+
#------------------------------------------------------------------------------
|
128
|
+
This.gemspec = Gem::Specification.new do |spec|
|
129
|
+
spec.name = This.name
|
130
|
+
spec.version = This.version
|
131
|
+
spec.author = This.author
|
132
|
+
spec.email = This.email
|
133
|
+
spec.homepage = This.homepage
|
134
|
+
|
135
|
+
spec.summary = This.summary
|
136
|
+
spec.description = This.description
|
137
|
+
|
138
|
+
spec.files = This.manifest
|
139
|
+
spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
|
140
|
+
spec.test_files = spec.files.grep(/^spec/)
|
141
|
+
|
142
|
+
spec.extra_rdoc_files += spec.files.grep(/(txt|rdoc)$/)
|
143
|
+
spec.rdoc_options = [ "--main" , 'README.rdoc',
|
144
|
+
"--markup", "tomdoc" ]
|
145
|
+
|
146
|
+
# The Runtime Dependencies
|
147
|
+
spec.add_runtime_dependency( 'maildir', '~> 2.0.0' )
|
148
|
+
|
149
|
+
# Additional functionality if used
|
150
|
+
spec.add_development_dependency( 'kestrel-client' , '~> 0.7.1' )
|
151
|
+
spec.add_development_dependency( 'redis' , '~> 2.2.2' )
|
152
|
+
spec.add_development_dependency( 'SystemTimer' , '~> 1.2.3' )
|
153
|
+
|
154
|
+
# The Development Dependencies
|
155
|
+
spec.add_development_dependency( 'rake' , '~> 0.9.2.2')
|
156
|
+
spec.add_development_dependency( 'rcov' , '~> 1.0.0' )
|
157
|
+
spec.add_development_dependency( 'rspec' , '~> 2.8.0' )
|
158
|
+
spec.add_development_dependency( 'rdoc' , '~> 3.12' )
|
159
|
+
|
160
|
+
end
|
161
|
+
This.gemspec_file = "#{This.name}.gemspec"
|
162
|
+
|
163
|
+
desc "Build the #{This.name}.gemspec file"
|
164
|
+
task :gemspec do
|
165
|
+
File.open( This.gemspec_file, "wb+" ) do |f|
|
166
|
+
f.write This.gemspec.to_ruby
|
167
|
+
end
|
168
|
+
end
|
169
|
+
CLOBBER << This.gemspec_file
|
170
|
+
|
171
|
+
require 'rubygems/package_task'
|
172
|
+
Gem::PackageTask.new( This.gemspec ) do
|
173
|
+
# nothing
|
174
|
+
end
|
175
|
+
|
176
|
+
#------------------------------------------------------------------------------
|
177
|
+
# Release
|
178
|
+
#------------------------------------------------------------------------------
|
179
|
+
task :release_check do
|
180
|
+
unless `git branch` =~ /^\* master$/
|
181
|
+
abort "You must be on the master branch to release!"
|
182
|
+
end
|
183
|
+
unless `git status` =~ /^nothing to commit/m
|
184
|
+
abort "Nope, sorry, you have unfinished business"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
desc "Create tag v#{This.version}, build and push #{This.gemspec.full_name} to rubygems.org"
|
189
|
+
task :release => [ :release_check, 'manifest:check', :gem ] do
|
190
|
+
sh "git commit --allow-empty -a -m 'Release #{This.version}'"
|
191
|
+
sh "git tag -a -m 'v#{This.version}' v#{This.version}"
|
192
|
+
sh "git push origin master"
|
193
|
+
sh "git push origin v#{This.version}"
|
194
|
+
sh "gem push pkg/#{This.gemspec.full_name}.gem"
|
195
|
+
end
|
196
|
+
|
197
|
+
#------------------------------------------------------------------------------
|
198
|
+
# Rakefile Support
|
199
|
+
#------------------------------------------------------------------------------
|
200
|
+
BEGIN {
|
201
|
+
|
202
|
+
require 'ostruct'
|
203
|
+
require 'rake/clean'
|
204
|
+
require 'rubygems' unless defined? Gem
|
205
|
+
|
206
|
+
module Util
|
207
|
+
def self.version
|
208
|
+
line = File.read( "lib/#{ This.name }.rb" )[/^\s*VERSION\s*=\s*.*/]
|
209
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
210
|
+
end
|
211
|
+
|
212
|
+
# Partition an rdoc file into sections and return the text of the section
|
213
|
+
# as an array of paragraphs
|
214
|
+
def self.section_of( file, section_name )
|
215
|
+
re = /^=+ (.*)$/
|
216
|
+
parts = File.read( file ).split( re )[1..-1]
|
217
|
+
parts.map! { |p| p.strip }
|
218
|
+
|
219
|
+
sections = Hash.new
|
220
|
+
Hash[*parts].each do |k,v|
|
221
|
+
sections[k] = v.split("\n\n")
|
222
|
+
end
|
223
|
+
return sections[section_name]
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.task_warning( task )
|
227
|
+
warn "WARNING: '#{task}' tasks are not defined. Please run 'rake develop'"
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.read_manifest
|
231
|
+
abort "You need a Manifest.txt" unless File.readable?( "Manifest.txt" )
|
232
|
+
File.readlines( "Manifest.txt" ).map { |l| l.strip }
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Hold all the metadata about this project
|
237
|
+
This = OpenStruct.new
|
238
|
+
desc = Util.section_of( 'README.rdoc', 'DESCRIPTION')
|
239
|
+
This.summary = desc.first
|
240
|
+
This.description = desc.join("\n\n")
|
241
|
+
|
242
|
+
|
243
|
+
This.exclude_from_manifest = %r/tmp$|\.(git|DS_Store)|^(doc|coverage|pkg)|\.gemspec$|\.swp$|\.jar|\.rvmrc$|~$/
|
244
|
+
This.manifest = Util.read_manifest
|
245
|
+
|
246
|
+
}
|