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.
Files changed (53) hide show
  1. data/.autotest +41 -0
  2. data/.gemtest +1 -0
  3. data/ADAPTER_API.rdoc +97 -0
  4. data/HISTORY.rdoc +9 -0
  5. data/Manifest.txt +52 -0
  6. data/README.rdoc +156 -0
  7. data/Rakefile +246 -0
  8. data/lib/qup.rb +48 -0
  9. data/lib/qup/adapter.rb +57 -0
  10. data/lib/qup/adapter/kestrel.rb +56 -0
  11. data/lib/qup/adapter/kestrel/destination.rb +54 -0
  12. data/lib/qup/adapter/kestrel/queue.rb +101 -0
  13. data/lib/qup/adapter/kestrel/topic.rb +68 -0
  14. data/lib/qup/adapter/maildir.rb +57 -0
  15. data/lib/qup/adapter/maildir/queue.rb +123 -0
  16. data/lib/qup/adapter/maildir/topic.rb +85 -0
  17. data/lib/qup/adapter/redis.rb +55 -0
  18. data/lib/qup/adapter/redis/connection.rb +32 -0
  19. data/lib/qup/adapter/redis/queue.rb +97 -0
  20. data/lib/qup/adapter/redis/topic.rb +76 -0
  21. data/lib/qup/consumer.rb +42 -0
  22. data/lib/qup/message.rb +18 -0
  23. data/lib/qup/producer.rb +28 -0
  24. data/lib/qup/publisher.rb +30 -0
  25. data/lib/qup/queue_api.rb +124 -0
  26. data/lib/qup/session.rb +111 -0
  27. data/lib/qup/subscriber.rb +28 -0
  28. data/lib/qup/topic_api.rb +92 -0
  29. data/spec/qup/adapter/kestrel/queue_spec.rb +9 -0
  30. data/spec/qup/adapter/kestrel/topic_spec.rb +9 -0
  31. data/spec/qup/adapter/kestrel_context.rb +8 -0
  32. data/spec/qup/adapter/kestrel_spec.rb +8 -0
  33. data/spec/qup/adapter/maildir/queue_spec.rb +9 -0
  34. data/spec/qup/adapter/maildir/topic_spec.rb +9 -0
  35. data/spec/qup/adapter/maildir_context.rb +10 -0
  36. data/spec/qup/adapter/maildir_spec.rb +8 -0
  37. data/spec/qup/adapter/redis/queue_spec.rb +9 -0
  38. data/spec/qup/adapter/redis/topic_spec.rb +9 -0
  39. data/spec/qup/adapter/redis_context.rb +6 -0
  40. data/spec/qup/adapter/redis_spec.rb +8 -0
  41. data/spec/qup/adapter_spec.rb +28 -0
  42. data/spec/qup/consumer_spec.rb +40 -0
  43. data/spec/qup/message_spec.rb +13 -0
  44. data/spec/qup/producer_spec.rb +18 -0
  45. data/spec/qup/queue_api_spec.rb +21 -0
  46. data/spec/qup/session_spec.rb +81 -0
  47. data/spec/qup/shared_adapter_examples.rb +29 -0
  48. data/spec/qup/shared_queue_examples.rb +71 -0
  49. data/spec/qup/shared_topic_examples.rb +57 -0
  50. data/spec/qup/topic_api_spec.rb +21 -0
  51. data/spec/qup_spec.rb +37 -0
  52. data/spec/spec_helper.rb +26 -0
  53. metadata +281 -0
@@ -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
+
@@ -0,0 +1 @@
1
+
@@ -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
+
@@ -0,0 +1,9 @@
1
+ = Changelog
2
+
3
+ == Version 1.0.0 - 2012-03-10
4
+
5
+ * Initial public release
6
+
7
+ == Version 1.1.0 - 2012-03-12
8
+
9
+ * Addition of a Redis Adapter (thanks aniero)
@@ -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
@@ -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.
@@ -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
+ }