qup 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }