wave_box 0.4.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/.gitignore +6 -0
- data/Gemfile +16 -0
- data/Guardfile +14 -0
- data/README.md +71 -0
- data/Rakefile +1 -0
- data/lib/wave_box/box.rb +60 -0
- data/lib/wave_box/generate_wave.rb +86 -0
- data/lib/wave_box/receive_wave.rb +73 -0
- data/lib/wave_box/version.rb +3 -0
- data/lib/wave_box/wave.rb +0 -0
- data/lib/wave_box.rb +5 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/wave_box/box_spec.rb +89 -0
- data/spec/wave_box/generate_wave_spec.rb +278 -0
- data/spec/wave_box/receive_wave_spec.rb +75 -0
- data/wave_box.gemspec +25 -0
- metadata +78 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in wavebox.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'redis'
|
7
|
+
|
8
|
+
group :development do
|
9
|
+
gem 'guard'
|
10
|
+
gem 'guard-minitest'
|
11
|
+
gem 'rb-inotify', :require => false
|
12
|
+
gem 'rb-fsevent', :require => false
|
13
|
+
gem 'rb-fchange', :require => false
|
14
|
+
gem 'growl'
|
15
|
+
gem 'mock_redis'
|
16
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'minitest', :colour => true do
|
5
|
+
# with Minitest::Unit
|
6
|
+
watch(%r|^test/test_(.*)\.rb|)
|
7
|
+
watch(%r|^lib/(.*)([^/]+)\.rb|) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
|
8
|
+
watch(%r|^test/test_helper\.rb|) { "test" }
|
9
|
+
|
10
|
+
# with Minitest::Spec
|
11
|
+
watch(%r|^spec/(.*)_spec\.rb|)
|
12
|
+
watch(%r|^lib/(.*)\.rb|) { |m| "spec/#{m[1]}_spec.rb" }
|
13
|
+
watch(%r|^spec/spec_helper\.rb|) { "spec" }
|
14
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# WaveBox
|
2
|
+
|
3
|
+
A redis-based messaging system
|
4
|
+
|
5
|
+
inspired by [Tumblr Staircar](http://engineering.tumblr.com/post/7819252942/staircar-redis-powered-notifications)
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
* Easy to use
|
10
|
+
* Works well with redis presharding
|
11
|
+
* Simple memory usage control
|
12
|
+
* Tested in production environment
|
13
|
+
|
14
|
+
## Example
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
require 'wave_box'
|
18
|
+
|
19
|
+
class User
|
20
|
+
include ::WaveBox::GenerateWave
|
21
|
+
include ::WaveBox::ReceiveWave
|
22
|
+
|
23
|
+
can_generate_wave :name => "message",
|
24
|
+
:redis => :wave_redis_instance,
|
25
|
+
# waves with timestamp older than expire will be discarded
|
26
|
+
:expire => 60 * 60 * 24 * 7, # One week
|
27
|
+
# only store last 20 waves
|
28
|
+
:max_size => 20,
|
29
|
+
:id => :wave_box_id
|
30
|
+
|
31
|
+
can_receive_wave :name => "message",
|
32
|
+
:redis => :wave_redis_instance,
|
33
|
+
:expire => 60 * 60 * 24 * 7, # One week
|
34
|
+
:max_size => 20,
|
35
|
+
:id => :wave_box_id
|
36
|
+
|
37
|
+
def wave_redis_instance
|
38
|
+
Redis.new # return a redis instance
|
39
|
+
end
|
40
|
+
|
41
|
+
def wave_box_id
|
42
|
+
self.id
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
sender = User.new
|
47
|
+
# => #<User:0x007f85631b9a50>
|
48
|
+
receiver = User.new
|
49
|
+
# => #<User:0x007f85631cf940>
|
50
|
+
|
51
|
+
# Send a wave from sender to receiver
|
52
|
+
sender.generate_message "hi", receiver
|
53
|
+
|
54
|
+
# Find all message waves in sender's outbox
|
55
|
+
sender.generated_message_after(0)
|
56
|
+
# => ["hi"]
|
57
|
+
|
58
|
+
# Find all message waves in receiver's inbox
|
59
|
+
receiver.received_message_after(0)
|
60
|
+
# => ["hi"]
|
61
|
+
```
|
62
|
+
|
63
|
+
## Install
|
64
|
+
|
65
|
+
```
|
66
|
+
gem install wave_box
|
67
|
+
```
|
68
|
+
|
69
|
+
## License
|
70
|
+
|
71
|
+
the [MIT License](http://www.opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/wave_box/box.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module WaveBox
|
2
|
+
class Box
|
3
|
+
attr_reader :expire, :max_size
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
@options = options
|
7
|
+
@expire = options[:expire]
|
8
|
+
@max_size = options[:max_size]
|
9
|
+
@redis = options[:redis]
|
10
|
+
@key = options[:key]
|
11
|
+
@encode = options[:encode]
|
12
|
+
end
|
13
|
+
|
14
|
+
def push(value, time = Time.now)
|
15
|
+
if @encode == false
|
16
|
+
v = value
|
17
|
+
else
|
18
|
+
v = encode(value)
|
19
|
+
end
|
20
|
+
@redis.zadd(@key, time.to_i, v)
|
21
|
+
|
22
|
+
truncate!
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
@redis.zcard @key
|
27
|
+
end
|
28
|
+
|
29
|
+
def after(time)
|
30
|
+
items = @redis.zrangebyscore( @key, "#{time.to_i}", "+inf")
|
31
|
+
|
32
|
+
if @encode == false
|
33
|
+
items
|
34
|
+
else
|
35
|
+
items.map { |x| decode(x) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# TODO: Current encode/decode solution sucks,
|
42
|
+
# it create too much storage overhead
|
43
|
+
def encode(str)
|
44
|
+
"#{str}:#{Time.now.to_i}:#{box_counter}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def decode(str)
|
48
|
+
str.split(':')[0..-3].join(':')
|
49
|
+
end
|
50
|
+
|
51
|
+
def box_counter
|
52
|
+
@redis.incr("#{@key}:counter")
|
53
|
+
end
|
54
|
+
|
55
|
+
def truncate!
|
56
|
+
@redis.zremrangebyscore( @key, 0, (Time.now - @expire).to_f ) if @expire
|
57
|
+
@redis.zremrangebyrank( @key, 0, -1*(@max_size + 1) ) if @max_size
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module WaveBox
|
2
|
+
module GenerateWave
|
3
|
+
def method_missing(method, *args, &block)
|
4
|
+
if method.to_s =~ /generated_(\w+)_after/
|
5
|
+
generated_after( $1, *args )
|
6
|
+
elsif method.to_s =~ /generate_(\w+)/
|
7
|
+
generate( $1, *args)
|
8
|
+
else
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# method missing with good manner
|
14
|
+
def respond_to_missing?(method, *)
|
15
|
+
(method =~ /generated_(\w+)_after/) || (method =~ /generate_(\w+)/) || super
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate(name, wave, receiver, time = Time.now, options = {})
|
19
|
+
send("#{name}_outbox").push wave, time unless options[:outbox] == false
|
20
|
+
|
21
|
+
if receiver.respond_to? :each
|
22
|
+
receiver.each do |rec|
|
23
|
+
rec.receive(name, wave, time)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
receiver.receive(name, wave, time)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def generated_after(name, time)
|
31
|
+
send("#{name}_outbox").after(time)
|
32
|
+
end
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
def can_generate_wave(config)
|
36
|
+
raise ArgumentError, "Missing redis" unless config[:redis]
|
37
|
+
raise ArgumentError, "Missing id lambda" unless config[:id]
|
38
|
+
raise ArgumentError, "Missing wave name" unless config[:name]
|
39
|
+
|
40
|
+
name = config[:name]
|
41
|
+
[:redis, :expire, :max_size, :encode].each do |c|
|
42
|
+
define_method "#{name}_outbox_#{c}" do config[c] end
|
43
|
+
end
|
44
|
+
|
45
|
+
if config[:redis].is_a? Symbol
|
46
|
+
class_eval <<-RUBY
|
47
|
+
def #{name}_outbox_redis_instance
|
48
|
+
send( "#{config[:redis]}" )
|
49
|
+
end
|
50
|
+
RUBY
|
51
|
+
else
|
52
|
+
define_method "#{name}_outbox_redis_instance" do config[:redis] end
|
53
|
+
end
|
54
|
+
|
55
|
+
if config[:id].is_a? Symbol
|
56
|
+
class_eval <<-RUBY
|
57
|
+
def #{name}_outbox_id
|
58
|
+
send("#{config[:id]}")
|
59
|
+
end
|
60
|
+
RUBY
|
61
|
+
else
|
62
|
+
define_method "#{name}_outbox_id" do config[:redis] end
|
63
|
+
end
|
64
|
+
|
65
|
+
define_method "#{name}_outbox_key" do "wave:#{name}:outbox:#{send("#{name}_outbox_id")}" end
|
66
|
+
|
67
|
+
class_eval <<-RUBY
|
68
|
+
def #{name}_outbox
|
69
|
+
@#{name}_outbox ||= WaveBox::Box.new({
|
70
|
+
:encode => #{name}_outbox_encode,
|
71
|
+
:redis => #{name}_outbox_redis_instance,
|
72
|
+
:key => #{name}_outbox_key,
|
73
|
+
:expire => #{name}_outbox_expire,
|
74
|
+
:max_size => #{name}_outbox_max_size})
|
75
|
+
end
|
76
|
+
RUBY
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.included(host)
|
82
|
+
host.extend ClassMethods
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module WaveBox
|
2
|
+
module ReceiveWave
|
3
|
+
def method_missing(method, *args, &block)
|
4
|
+
if method.to_s =~ /received_(\w+)_after/
|
5
|
+
received_after( $1, *args )
|
6
|
+
elsif method.to_s =~ /receive_(\w+)/
|
7
|
+
receive( $1, *args)
|
8
|
+
else
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def receive(name, wave, time = Time.now)
|
14
|
+
send("#{name}_inbox").push(wave, time)
|
15
|
+
end
|
16
|
+
|
17
|
+
def received_after(name, time)
|
18
|
+
send("#{name}_inbox").after(time)
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def can_receive_wave(config)
|
23
|
+
raise ArgumentError, "Missing redis config" unless config[:redis]
|
24
|
+
raise ArgumentError, "Missing id lambda" unless config[:id]
|
25
|
+
raise ArgumentError, "Missing wave name" unless config[:name]
|
26
|
+
|
27
|
+
name = config[:name]
|
28
|
+
|
29
|
+
[:expire, :max_size, :encode].each do |c|
|
30
|
+
define_method "#{name}_inbox_#{c}" do config[c] end
|
31
|
+
end
|
32
|
+
|
33
|
+
if config[:redis].is_a? Symbol
|
34
|
+
class_eval <<-RUBY
|
35
|
+
def #{name}_inbox_redis_instance
|
36
|
+
send("#{config[:redis]}")
|
37
|
+
end
|
38
|
+
RUBY
|
39
|
+
else
|
40
|
+
define_method "#{name}_inbox_redis_instance" do config[:redis] end
|
41
|
+
end
|
42
|
+
|
43
|
+
if config[:id].is_a? Symbol
|
44
|
+
class_eval <<-RUBY
|
45
|
+
def #{name}_inbox_id
|
46
|
+
send("#{config[:id]}")
|
47
|
+
end
|
48
|
+
RUBY
|
49
|
+
else
|
50
|
+
define_method "#{name}_inbox_id" do config[:redis] end
|
51
|
+
end
|
52
|
+
|
53
|
+
define_method "#{name}_inbox_key" do "wave:#{name}:inbox:#{send("#{name}_inbox_id")}" end
|
54
|
+
|
55
|
+
class_eval <<-RUBY
|
56
|
+
def #{name}_inbox
|
57
|
+
@#{name}_inbox ||= WaveBox::Box.new({
|
58
|
+
:redis => #{name}_inbox_redis_instance,
|
59
|
+
:key => #{name}_inbox_key,
|
60
|
+
:encode => #{name}_inbox_encode,
|
61
|
+
:expire => #{name}_inbox_expire,
|
62
|
+
:max_size => #{name}_inbox_max_size})
|
63
|
+
end
|
64
|
+
RUBY
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.included(host)
|
69
|
+
host.extend ClassMethods
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
File without changes
|
data/lib/wave_box.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'mock_redis'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe WaveBox::Box do
|
5
|
+
describe "A box without limit" do
|
6
|
+
before do
|
7
|
+
@box = WaveBox::Box.new :redis => MockRedis.new,
|
8
|
+
:key => "test_key"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should save pushed item" do
|
12
|
+
@box.push(1)
|
13
|
+
|
14
|
+
@box.size.must_equal 1
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should return pushed items based on a timestamp" do
|
18
|
+
@box.push("foo")
|
19
|
+
|
20
|
+
@box.after(0).size.must_equal 1
|
21
|
+
@box.after(0)[0].must_equal "foo"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should able to handle two identical items in a single box" do
|
25
|
+
@box.push("foo")
|
26
|
+
@box.push("foo")
|
27
|
+
|
28
|
+
@box.after(0).size.must_equal 2
|
29
|
+
@box.after(0).each { |x| x.must_equal "foo" }
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should not truncate any item" do
|
33
|
+
100.times { @box.push "foo" }
|
34
|
+
|
35
|
+
@box.size.must_equal 100
|
36
|
+
|
37
|
+
@box.push "bar", Time.now - 10000
|
38
|
+
@box.after(0)[0].must_equal "bar"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "A box with limit" do
|
43
|
+
before do
|
44
|
+
@max_size = 10
|
45
|
+
@expire = 10*60
|
46
|
+
@box_with_limit = WaveBox::Box.new({
|
47
|
+
:redis => MockRedis.new,
|
48
|
+
:key => "test_key",
|
49
|
+
:expire => @expire,
|
50
|
+
:max_size => @max_size })
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should know its limit" do
|
54
|
+
@box_with_limit.max_size.must_equal @max_size
|
55
|
+
@box_with_limit.expire.must_equal @expire
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should truncate items over max size limit" do
|
59
|
+
20.times { @box_with_limit.push("foo") }
|
60
|
+
|
61
|
+
@box_with_limit.size.must_equal @max_size
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should truncate items over expire time" do
|
65
|
+
@box_with_limit.push "foo", Time.now - (@expire + 10)
|
66
|
+
|
67
|
+
@box_with_limit.size.must_equal 0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "A box can be set to not encode items" do
|
72
|
+
before do
|
73
|
+
@box = WaveBox::Box.new({
|
74
|
+
:redis => MockRedis.new,
|
75
|
+
:key => "test_key",
|
76
|
+
:expire => 10*60,
|
77
|
+
:max_size => 10,
|
78
|
+
:encode => false})
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should not be able to handle two identical items in a single box" do
|
82
|
+
@box.push("foo")
|
83
|
+
@box.push("foo")
|
84
|
+
|
85
|
+
@box.after(0).size.must_equal 1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "mock_redis"
|
3
|
+
|
4
|
+
describe WaveBox::GenerateWave do
|
5
|
+
it "should raise an ArgumentError if no wave name is given" do
|
6
|
+
lambda do
|
7
|
+
class A
|
8
|
+
include WaveBox::GenerateWave
|
9
|
+
|
10
|
+
can_generate_wave :id => :box_id,
|
11
|
+
:redis => MockRedis.new
|
12
|
+
end
|
13
|
+
end.must_raise ArgumentError
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should raise an argument error if no redis config is given" do
|
17
|
+
lambda do
|
18
|
+
class A
|
19
|
+
include WaveBox::GenerateWave
|
20
|
+
|
21
|
+
can_generate_wave :id => :box_id,
|
22
|
+
:name => "message"
|
23
|
+
end
|
24
|
+
end.must_raise ArgumentError
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should raise an ArgumentError if no box_id config is given" do
|
28
|
+
lambda do
|
29
|
+
class A
|
30
|
+
include WaveBox::GenerateWave
|
31
|
+
|
32
|
+
can_generate_wave :redis => MockRedis.new,
|
33
|
+
:name => "message"
|
34
|
+
end
|
35
|
+
end.must_raise ArgumentError
|
36
|
+
end
|
37
|
+
|
38
|
+
it "Can accept a symbol as redis parameter" do
|
39
|
+
class Sender
|
40
|
+
include WaveBox::GenerateWave
|
41
|
+
|
42
|
+
can_generate_wave :name => "message",
|
43
|
+
:redis => :dynamic_instance,
|
44
|
+
:expire => 60*10,
|
45
|
+
:max_size => 10,
|
46
|
+
# You have to specify a box id which
|
47
|
+
# is unique among all receiver
|
48
|
+
:id => :box_id
|
49
|
+
|
50
|
+
def dynamic_instance
|
51
|
+
return MockRedis.new
|
52
|
+
end
|
53
|
+
|
54
|
+
def box_id
|
55
|
+
object_id
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Receiver
|
60
|
+
include WaveBox::ReceiveWave
|
61
|
+
|
62
|
+
can_receive_wave :name => "message",
|
63
|
+
:redis => :dynamic_instance,
|
64
|
+
:expire => 60*10,
|
65
|
+
:max_size => 10,
|
66
|
+
# You have to specify a box id which
|
67
|
+
# is unique among all receiver
|
68
|
+
:id => :box_id
|
69
|
+
def dynamic_instance
|
70
|
+
return MockRedis.new
|
71
|
+
end
|
72
|
+
|
73
|
+
def box_id
|
74
|
+
object_id
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
s = Sender.new
|
79
|
+
r = Receiver.new
|
80
|
+
|
81
|
+
s.message_outbox.wont_be_nil
|
82
|
+
s.generate_message "foo", r
|
83
|
+
|
84
|
+
s.generated_message_after(0).size.must_equal 1
|
85
|
+
r.received_message_after(0).size.must_equal 1
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "A normal generator usage" do
|
89
|
+
before do
|
90
|
+
class User
|
91
|
+
include WaveBox::GenerateWave
|
92
|
+
|
93
|
+
can_generate_wave :name => "message",
|
94
|
+
:redis => MockRedis.new,
|
95
|
+
:expire => 60*10,
|
96
|
+
:max_size => 10,
|
97
|
+
# You have to specify a box id which
|
98
|
+
# is unique among all receiver
|
99
|
+
:id => :box_id
|
100
|
+
|
101
|
+
def box_id
|
102
|
+
object_id
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
@user = User.new
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should have an outbox with name" do
|
110
|
+
@user.message_outbox.wont_be_nil
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should save the box id lambda and call it when needed" do
|
114
|
+
@user.message_outbox_id.must_equal @user.object_id
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "Sending wave" do
|
118
|
+
before do
|
119
|
+
class Receiver
|
120
|
+
include WaveBox::ReceiveWave
|
121
|
+
|
122
|
+
can_receive_wave :name => "message",
|
123
|
+
:redis => MockRedis.new,
|
124
|
+
:expire => 60*10,
|
125
|
+
:max_size => 10,
|
126
|
+
# You have to specify a box id which
|
127
|
+
# is unique among all receiver
|
128
|
+
:id => :box_id
|
129
|
+
|
130
|
+
def box_id
|
131
|
+
object_id
|
132
|
+
end
|
133
|
+
end
|
134
|
+
@receiver = Receiver.new
|
135
|
+
@wave = "foo"
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should be able to send wave to a receiver" do
|
139
|
+
@user.generate "message", @wave, @receiver
|
140
|
+
|
141
|
+
@user.message_outbox.size.must_equal 1
|
142
|
+
@user.message_outbox.after(0)[0].must_equal @wave
|
143
|
+
|
144
|
+
@receiver.message_inbox.size.must_equal 1
|
145
|
+
@receiver.message_inbox.after(0)[0].must_equal @wave
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should be able to send multiple waves to a receiver and record them
|
149
|
+
in the outbox" do
|
150
|
+
n = 10
|
151
|
+
n.times do |i|
|
152
|
+
@user.generate "message", @wave, @receiver
|
153
|
+
|
154
|
+
@user.message_outbox.size.must_equal i+1
|
155
|
+
@receiver.message_inbox.size.must_equal i+1
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should be able to save options in its outbox" do
|
160
|
+
@user.generate "message", @wave, @receiver, Time.now - 10000
|
161
|
+
@user.message_outbox.after(0).size.must_equal 0
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should be able to save options in the receiver's inbox" do
|
165
|
+
@user.generate "message", @wave, @receiver, Time.now - 10000
|
166
|
+
# This wave will be truncated
|
167
|
+
@receiver.message_inbox.after(0).size.must_equal 0
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should have a helper to retrieve outbox after..." do
|
171
|
+
@user.generated_after("message", 0).must_equal @user.message_outbox.after(0)
|
172
|
+
end
|
173
|
+
|
174
|
+
it "can be customized to not save a backup in its outbox in a sepcify call" do
|
175
|
+
@user.generate "message", @wave, @receiver, Time.now, :outbox => false
|
176
|
+
|
177
|
+
@user.message_outbox.after(0).size.must_equal 0
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should utilize method_missing to provide some much better api" do
|
181
|
+
@user.generate_message @wave, @receiver
|
182
|
+
|
183
|
+
@user.generated_message_after(0).size.must_equal 1
|
184
|
+
|
185
|
+
@receiver.message_inbox.size.must_equal 1
|
186
|
+
@receiver.message_inbox.after(0)[0].must_equal @wave
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should implement method missing politely" do
|
190
|
+
@user.respond_to?(:generate_message).must_equal true
|
191
|
+
@user.respond_to?(:generated_message_after).must_equal true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
describe "USAGE: can generate multiple waves in a single class" do
|
198
|
+
before do
|
199
|
+
class MultipleSender
|
200
|
+
include WaveBox::GenerateWave
|
201
|
+
|
202
|
+
can_generate_wave :name => "message",
|
203
|
+
:redis => MockRedis.new,
|
204
|
+
:expire => 60*10,
|
205
|
+
:max_size => 10,
|
206
|
+
# You have to specify a box id which
|
207
|
+
# is unique among all receiver
|
208
|
+
:id => :box_id
|
209
|
+
|
210
|
+
can_generate_wave :name => "stone",
|
211
|
+
:redis => MockRedis.new,
|
212
|
+
:expire => 60*10,
|
213
|
+
:max_size => 10,
|
214
|
+
# You have to specify a box id which
|
215
|
+
# is unique among all receiver
|
216
|
+
:id => :box_id
|
217
|
+
|
218
|
+
|
219
|
+
def box_id
|
220
|
+
object_id
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class MultipleReceiver
|
225
|
+
include WaveBox::ReceiveWave
|
226
|
+
|
227
|
+
can_receive_wave :name => "message",
|
228
|
+
:redis => MockRedis.new,
|
229
|
+
:expire => 60*10,
|
230
|
+
:max_size => 10,
|
231
|
+
# You have to specify a box id which
|
232
|
+
# is unique among all receiver
|
233
|
+
:id => :box_id
|
234
|
+
|
235
|
+
can_receive_wave :name => "stone",
|
236
|
+
:redis => MockRedis.new,
|
237
|
+
:expire => 60*10,
|
238
|
+
:max_size => 10,
|
239
|
+
# You have to specify a box id which
|
240
|
+
# is unique among all receiver
|
241
|
+
:id => :box_id
|
242
|
+
|
243
|
+
|
244
|
+
def box_id
|
245
|
+
object_id
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
@sender = MultipleSender.new
|
250
|
+
@receiver = MultipleReceiver.new
|
251
|
+
end
|
252
|
+
|
253
|
+
it "Should have multiple outbox" do
|
254
|
+
@sender.message_outbox.wont_be_nil
|
255
|
+
@sender.stone_outbox.wont_be_nil
|
256
|
+
end
|
257
|
+
|
258
|
+
it "Should have multiple outbox key" do
|
259
|
+
@sender.message_outbox_key.wont_equal @sender.stone_outbox_key
|
260
|
+
end
|
261
|
+
|
262
|
+
it "Should be able to send multiple type of waves without
|
263
|
+
conflicting each other" do
|
264
|
+
@sender.generate_message "foo", @receiver
|
265
|
+
@sender.generated_message_after(0).size.must_equal 1
|
266
|
+
@receiver.received_after("message", 0).size.must_equal 1
|
267
|
+
|
268
|
+
@sender.generate_stone "bar", @receiver
|
269
|
+
|
270
|
+
@sender.generated_message_after(0).size.must_equal 1
|
271
|
+
@sender.generated_stone_after(0).size.must_equal 1
|
272
|
+
|
273
|
+
@receiver.received_message_after(0).size.must_equal 1
|
274
|
+
@receiver.received_stone_after(0).size.must_equal 1
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require 'mock_redis'
|
3
|
+
|
4
|
+
describe WaveBox::ReceiveWave do
|
5
|
+
it "should raise an argument error if no name given" do
|
6
|
+
lambda do
|
7
|
+
class A
|
8
|
+
include WaveBox::ReceiveWave
|
9
|
+
|
10
|
+
can_receive_wave :id => :box_id,
|
11
|
+
:redis => MockRedis.new
|
12
|
+
end
|
13
|
+
end.must_raise ArgumentError
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should raise an argument error if no redis config is given" do
|
17
|
+
lambda do
|
18
|
+
class A
|
19
|
+
include WaveBox::ReceiveWave
|
20
|
+
|
21
|
+
can_receive_wave :id => :box_id,
|
22
|
+
:name => "message"
|
23
|
+
end
|
24
|
+
end.must_raise ArgumentError
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should raise an ArgumentError if no box_id config is given" do
|
28
|
+
lambda do
|
29
|
+
class A
|
30
|
+
include WaveBox::ReceiveWave
|
31
|
+
|
32
|
+
can_receive_wave :redis => :box_id,
|
33
|
+
:name => "message"
|
34
|
+
end
|
35
|
+
end.must_raise ArgumentError
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "A normal receiver usage" do
|
39
|
+
before do
|
40
|
+
class User
|
41
|
+
include WaveBox::ReceiveWave
|
42
|
+
|
43
|
+
can_receive_wave :name => "message",
|
44
|
+
:redis => MockRedis.new,
|
45
|
+
:expire => 60*10,
|
46
|
+
:max_size => 10,
|
47
|
+
# You have to specify a box id which
|
48
|
+
# is unique among all receiver
|
49
|
+
:id => :box_id
|
50
|
+
|
51
|
+
def box_id
|
52
|
+
object_id
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@user = User.new
|
57
|
+
@wave = "foo"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should have an inbox" do
|
61
|
+
@user.message_inbox.wont_be_nil
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should be able to receive wave and save it to its inbox" do
|
65
|
+
@user.receive("message", @wave)
|
66
|
+
|
67
|
+
@user.message_inbox.size.must_equal 1
|
68
|
+
@user.message_inbox.after(0)[0].must_equal @wave
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should have a helper to retrieve inbox after..." do
|
72
|
+
@user.received_message_after(0).must_equal @user.message_inbox.after(0)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/wave_box.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "wave_box/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "wave_box"
|
7
|
+
s.version = WaveBox::VERSION
|
8
|
+
s.authors = ["Poga Po"]
|
9
|
+
s.email = ["poga.bahamut@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Redis-based Push-style messaging}
|
12
|
+
s.description = %q{A simple push style messaging based on redis}
|
13
|
+
|
14
|
+
s.rubyforge_project = "wavebox"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.licenses = "MIT"
|
21
|
+
|
22
|
+
# specify any dependencies here; for example:
|
23
|
+
# s.add_development_dependency "rspec"
|
24
|
+
s.add_runtime_dependency "redis"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wave_box
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Poga Po
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redis
|
16
|
+
requirement: &70280405090200 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70280405090200
|
25
|
+
description: A simple push style messaging based on redis
|
26
|
+
email:
|
27
|
+
- poga.bahamut@gmail.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
34
|
+
- Guardfile
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- lib/wave_box.rb
|
38
|
+
- lib/wave_box/box.rb
|
39
|
+
- lib/wave_box/generate_wave.rb
|
40
|
+
- lib/wave_box/receive_wave.rb
|
41
|
+
- lib/wave_box/version.rb
|
42
|
+
- lib/wave_box/wave.rb
|
43
|
+
- spec/spec_helper.rb
|
44
|
+
- spec/wave_box/box_spec.rb
|
45
|
+
- spec/wave_box/generate_wave_spec.rb
|
46
|
+
- spec/wave_box/receive_wave_spec.rb
|
47
|
+
- wave_box.gemspec
|
48
|
+
homepage: ''
|
49
|
+
licenses:
|
50
|
+
- MIT
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project: wavebox
|
69
|
+
rubygems_version: 1.8.10
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Redis-based Push-style messaging
|
73
|
+
test_files:
|
74
|
+
- spec/spec_helper.rb
|
75
|
+
- spec/wave_box/box_spec.rb
|
76
|
+
- spec/wave_box/generate_wave_spec.rb
|
77
|
+
- spec/wave_box/receive_wave_spec.rb
|
78
|
+
has_rdoc:
|