jsvd-blackboard 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +41 -0
- data/README +62 -0
- data/Rakefile +37 -0
- data/blackboard.gemspec +30 -0
- data/lib/blackboard.rb +143 -0
- data/spec/blackboard_spec.rb +374 -0
- data/test/blackboard_test.rb +8 -0
- data/test/test_helper.rb +3 -0
- metadata +71 -0
data/ChangeLog
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
== 0.2.3 / 2009-01-16
|
2
|
+
|
3
|
+
* Fixed subsubfolders
|
4
|
+
|
5
|
+
== 0.2.2 / 2009-01-15
|
6
|
+
|
7
|
+
* Removed overriden Folder#keys. Now Hash#keys is called.
|
8
|
+
|
9
|
+
== 0.2.1 / 2009-01-15
|
10
|
+
|
11
|
+
* Fixed Folder#_update
|
12
|
+
|
13
|
+
== 0.2.0 / 2009-01-15
|
14
|
+
|
15
|
+
* New fluent interface
|
16
|
+
|
17
|
+
== 0.1.4 / 2008-12-15
|
18
|
+
|
19
|
+
* Imposed upper bound on ttl to #seconds in 30 days
|
20
|
+
|
21
|
+
== 0.1.3 / 2008-12-12
|
22
|
+
|
23
|
+
* Blackboard will only replace Data objects if new is..newer
|
24
|
+
|
25
|
+
== 0.1.2 / 2008-12-11
|
26
|
+
|
27
|
+
* TTL now configured per-folder (or defaults to BB's generic ttl)
|
28
|
+
|
29
|
+
== 0.1.1 / 2008-12-09
|
30
|
+
|
31
|
+
* Improved get method error handling
|
32
|
+
|
33
|
+
== 0.1.0 / 2008-12-09
|
34
|
+
|
35
|
+
* Added expiration times
|
36
|
+
* Added folders and data objects
|
37
|
+
* Added one memcache instance per folder
|
38
|
+
|
39
|
+
== 0.0.1 / 2008-11-28
|
40
|
+
|
41
|
+
* initial release
|
data/README
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
= blackboard
|
3
|
+
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
BlackBoard provides a folder-layer to memcache-client. A BlackBoard is created with a static structure of folders and items.
|
8
|
+
|
9
|
+
bb = Pulso::BlackBoard.new :ttl => 2 do
|
10
|
+
folder :folder1, [:name1] do
|
11
|
+
folder :folder1, [:name1, :name2]
|
12
|
+
folder :folder2, [:name2]
|
13
|
+
folder :folder3, [:name2]
|
14
|
+
end
|
15
|
+
|
16
|
+
It is aimed at quickly writing and reading, usually attached to EventMachine, for example.
|
17
|
+
Objects written to the blackboard must answer to :timestamp, for they will expire.
|
18
|
+
|
19
|
+
class TestObject
|
20
|
+
attr_reader :timestamp
|
21
|
+
def initialize; @timestamp = Time.now; end
|
22
|
+
end
|
23
|
+
|
24
|
+
obj = TestObject.new
|
25
|
+
|
26
|
+
To write an item from a folder:
|
27
|
+
|
28
|
+
bb.folder1.folder1.name1 = obj
|
29
|
+
|
30
|
+
To retrieve an item from a folder
|
31
|
+
|
32
|
+
obj = bb.folder1.folder1.name1
|
33
|
+
|
34
|
+
To retrive all items in a folder:
|
35
|
+
|
36
|
+
items = bb.folder1
|
37
|
+
obj = items[:folder1][:name1]
|
38
|
+
|
39
|
+
== Installation
|
40
|
+
|
41
|
+
=== Archive Installation
|
42
|
+
|
43
|
+
rake install
|
44
|
+
|
45
|
+
=== Gem Installation
|
46
|
+
|
47
|
+
gem install blackboard
|
48
|
+
|
49
|
+
|
50
|
+
== Features/Problems
|
51
|
+
|
52
|
+
Must include Pulso module to use.
|
53
|
+
Fully specced.
|
54
|
+
|
55
|
+
== Synopsis
|
56
|
+
|
57
|
+
|
58
|
+
== Copyright
|
59
|
+
|
60
|
+
Author:: João Duarte <jsvduarte@gmail.com>
|
61
|
+
Copyright:: Copyright (c) 2008 jsvd
|
62
|
+
License::
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'rake/contrib/sshpublisher'
|
10
|
+
require 'spec/rake/spectask'
|
11
|
+
require 'fileutils'
|
12
|
+
require 'metric_fu'
|
13
|
+
include FileUtils
|
14
|
+
|
15
|
+
MetricFu::CHURN_OPTIONS = {:scm => :git}
|
16
|
+
MetricFu::DIRECTORIES_TO_FLOG = ['lib']
|
17
|
+
MetricFu::SAIKURO_OPTIONS = {"--input_directory" => 'lib'}
|
18
|
+
|
19
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
20
|
+
task :default => [:test]
|
21
|
+
#task :package => [:clean]
|
22
|
+
|
23
|
+
Rake::TestTask.new("test") do |t|
|
24
|
+
t.libs << "test"
|
25
|
+
t.pattern = "test/**/*_test.rb"
|
26
|
+
t.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
desc "Run all examples with RCov"
|
31
|
+
Spec::Rake::SpecTask.new('examples_with_rcov') do |t|
|
32
|
+
t.spec_files = FileList['spec/**/*.rb']
|
33
|
+
t.rcov = true
|
34
|
+
t.rcov_opts = ['--exclude', 'examples']
|
35
|
+
end
|
36
|
+
|
37
|
+
task :cruise => [ "metrics:flog", "metrics:churn", "metrics:coverage", "metrics:saikuro" ]
|
data/blackboard.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
spec = Gem::Specification.new do |s|
|
2
|
+
s.name = "blackboard"
|
3
|
+
s.version = "0.2.3"
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.has_rdoc = false
|
6
|
+
s.summary = ""
|
7
|
+
s.description = ""
|
8
|
+
s.author = "João Duarte"
|
9
|
+
s.email = "jsvduarte@gmail.com"
|
10
|
+
s.executables = %w( )
|
11
|
+
s.bindir = "bin"
|
12
|
+
s.require_path = "lib"
|
13
|
+
|
14
|
+
s.add_dependency('memcache-client', '>=1.4.0')
|
15
|
+
s.required_ruby_version = '>= 1.8.5'
|
16
|
+
|
17
|
+
s.files = %w(
|
18
|
+
test
|
19
|
+
test/test_helper.rb
|
20
|
+
test/blackboard_test.rb
|
21
|
+
lib
|
22
|
+
lib/blackboard.rb
|
23
|
+
README
|
24
|
+
spec
|
25
|
+
spec/blackboard_spec.rb
|
26
|
+
ChangeLog
|
27
|
+
Rakefile
|
28
|
+
blackboard.gemspec)
|
29
|
+
|
30
|
+
end
|
data/lib/blackboard.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# vim: expandtab : tabstop=2 : shiftwidth=2 : softtabstop=2
|
2
|
+
|
3
|
+
module Pulso
|
4
|
+
|
5
|
+
class Folder < Hash
|
6
|
+
|
7
|
+
attr_reader :name, :ttl
|
8
|
+
|
9
|
+
def initialize name, children, args = {}, &block
|
10
|
+
@name = name
|
11
|
+
|
12
|
+
@items = {}
|
13
|
+
@folders = []
|
14
|
+
@ttl = args[:ttl]
|
15
|
+
@cache = args[:cache]
|
16
|
+
|
17
|
+
raise ArgumentError, "Pulso::Folder.new should receive name, keys, cache and ttl" if @ttl.nil? || args[:cache].nil?
|
18
|
+
raise ArgumentError, "Pulso::Folder.new should not receive ttl bigger than #seconds in 30 days" if @ttl > 2592000
|
19
|
+
|
20
|
+
create_children children
|
21
|
+
|
22
|
+
instance_eval(&block) unless block.nil?
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing folder
|
27
|
+
raise BlackBoardError, "Folder #{folder} not found"
|
28
|
+
end
|
29
|
+
|
30
|
+
def _update
|
31
|
+
@items.keys.each do |k|
|
32
|
+
self[k] = get k
|
33
|
+
end
|
34
|
+
@folders.each {|f| self[f]._update }
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def folder name, keys, args = {}, &block
|
39
|
+
raise BlackBoardError, "Folder #{name} already exists" if self.has_key?(name)
|
40
|
+
ttl = args[:ttl]
|
41
|
+
ttl ||= @ttl
|
42
|
+
@folders << name
|
43
|
+
self[name] = Pulso::Folder.new "#{@name}.#{name}", keys, :cache => @cache, :ttl => ttl, &block
|
44
|
+
instance_eval %Q{def #{name}; self[:#{name}]._update ;self[:#{name}]; end}
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_children children
|
49
|
+
children.each do |child|
|
50
|
+
self[child] = nil
|
51
|
+
instance_eval %Q{
|
52
|
+
def #{child}=(object); add :#{child}, object; end
|
53
|
+
def #{child}; self[:#{child}] = get :#{child}; end}
|
54
|
+
@items[child] = Time.at 0
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def add name, object
|
59
|
+
obj_ttl = (@ttl - (Time.now - object.timestamp)).round
|
60
|
+
return unless obj_ttl > 0
|
61
|
+
return if object.timestamp < @items[name]
|
62
|
+
|
63
|
+
@items[name] = object.timestamp
|
64
|
+
obj = Pulso::Data.new(name, object)
|
65
|
+
@cache.set "#{@name}.#{name}", obj, obj_ttl
|
66
|
+
end
|
67
|
+
|
68
|
+
def get obj_name
|
69
|
+
raise BlackBoardError, "Key #{obj_name} doesn't exist in folder #{@name}." unless @items.has_key?(obj_name)
|
70
|
+
ret = @cache["#{@name}.#{obj_name}"]
|
71
|
+
return if ret.nil?
|
72
|
+
ret.data
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
class Data
|
78
|
+
|
79
|
+
attr_reader :name, :data, :timestamp
|
80
|
+
|
81
|
+
def initialize name, object
|
82
|
+
@name = name
|
83
|
+
raise BlackBoardError, "Object does not have timestamp" unless object.respond_to?(:timestamp)
|
84
|
+
@data = object
|
85
|
+
@timestamp = Time.now
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
class BlackBoard
|
91
|
+
|
92
|
+
require 'rubygems'
|
93
|
+
require 'memcache'
|
94
|
+
|
95
|
+
@cache = nil
|
96
|
+
|
97
|
+
attr_reader :folders
|
98
|
+
|
99
|
+
def initialize opts = {}, &block
|
100
|
+
@folders = {}
|
101
|
+
@ttl = opts[:ttl] || 60
|
102
|
+
raise ArgumentError, "Pulso::BlackBoard.new should not receive ttl bigger than #seconds in 30 days" if @ttl > 2592000
|
103
|
+
@servers = opts[:servers]
|
104
|
+
@servers ||= "127.0.0.1:11411"
|
105
|
+
@cache = MemCache.new(@servers, :namespace => 'blackboard')
|
106
|
+
instance_eval(&block) unless block.nil?
|
107
|
+
end
|
108
|
+
|
109
|
+
def active?
|
110
|
+
@cache.active?
|
111
|
+
end
|
112
|
+
|
113
|
+
def has_folders?
|
114
|
+
!@folders.empty?
|
115
|
+
end
|
116
|
+
|
117
|
+
def empty?
|
118
|
+
@cache.stats.inject(0) {|sum, server| sum + server.last["curr_items"]} == 0
|
119
|
+
end
|
120
|
+
|
121
|
+
def folder name, keys, args = {}, &block
|
122
|
+
raise BlackBoardError, "Folder #{name} already exists" if @folders.has_key?(name)
|
123
|
+
ttl = args[:ttl]
|
124
|
+
ttl ||= @ttl
|
125
|
+
instance_eval %Q{def #{name}; @folders[:#{name}]._update; @folders[:#{name}]; end}
|
126
|
+
@folders[name] = Pulso::Folder.new name, keys, :cache => @cache, :ttl => ttl, &block
|
127
|
+
end
|
128
|
+
|
129
|
+
def clean
|
130
|
+
@cache.flush_all
|
131
|
+
end
|
132
|
+
|
133
|
+
def method_missing folder
|
134
|
+
raise BlackBoardError, "Folder #{folder} not found"
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
class BlackBoardError < RuntimeError
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
@@ -0,0 +1,374 @@
|
|
1
|
+
require 'spec'
|
2
|
+
require 'lib/blackboard'
|
3
|
+
|
4
|
+
class TestObject
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@timestamp = Time.now
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :color
|
11
|
+
attr_reader :timestamp
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Pulso::Folder do
|
15
|
+
|
16
|
+
before :each do
|
17
|
+
@cache = MemCache.new("127.0.0.1:11411", :namespace => 'blackboard')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be created with a name, servers and ttl" do
|
21
|
+
f = nil
|
22
|
+
lambda { f = Pulso::Folder.new }.should raise_error ArgumentError
|
23
|
+
lambda { f = Pulso::Folder.new :folder1, [], :cache => @cache }.should raise_error ArgumentError
|
24
|
+
lambda { f = Pulso::Folder.new :folder1, [], :ttl => 20 }.should raise_error ArgumentError
|
25
|
+
lambda { f = Pulso::Folder.new :folder1, [], :cache => @cache, :ttl => 20 }.should_not raise_error ArgumentError
|
26
|
+
f.name.should == :folder1
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should complain if ttl is bigger than seconds in 30 days" do
|
30
|
+
lambda { Pulso::Folder.new :folder1, [], :cache => @cache, :ttl => 30*24*3600+1 }.should raise_error ArgumentError
|
31
|
+
lambda { Pulso::Folder.new :folder1, [], :cache => @cache, :ttl => 30*24*3600 }.should_not raise_error ArgumentError
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not complain when creating subfolders" do
|
35
|
+
lambda {
|
36
|
+
Pulso::Folder.new :folder1, [:name1, :name2], :cache => @cache, :ttl => 30*24*3600 do
|
37
|
+
folder :folder2, [:name4, :name5], :ttl => 30*24*3600
|
38
|
+
end
|
39
|
+
}.should_not raise_error ArgumentError
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return a kind of Hash" do
|
43
|
+
f = Pulso::Folder.new :folder1, [:name1], :cache => @cache, :ttl => 20
|
44
|
+
f.should be_a_kind_of Hash
|
45
|
+
f.should == { :name1 => nil }
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should respond to folder name method" do
|
49
|
+
`memcached -d -p 11411 -P /tmp/memcached-test.pid`
|
50
|
+
k = Pulso::Folder.new :folder1, [:name1, :name2], :cache => @cache, :ttl => 30*24*3600 do
|
51
|
+
folder :folder2, [:name1]
|
52
|
+
end
|
53
|
+
lambda { k.folder2 }.should_not raise_error
|
54
|
+
k.folder2.should == { :name1 => nil }
|
55
|
+
`killall memcached`
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe Pulso::Data do
|
61
|
+
|
62
|
+
it "should be initialized with a name and an object that responds to :timestamp" do
|
63
|
+
lambda { Pulso::Data.new }.should raise_error
|
64
|
+
lambda { Pulso::Data.new :name1, Object.new }.should raise_error Pulso::BlackBoardError
|
65
|
+
obj = TestObject.new
|
66
|
+
data = nil
|
67
|
+
lambda { data = Pulso::Data.new :name1, obj }.should_not raise_error Pulso::BlackBoardError
|
68
|
+
data.name.should == :name1
|
69
|
+
data.data.should == obj
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should have a timestamp" do
|
73
|
+
Pulso::Data.new(:name1, TestObject.new).timestamp.should be_close Time.now,1
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
describe Pulso::BlackBoard do
|
79
|
+
|
80
|
+
before :all do
|
81
|
+
`memcached -d -p 11411 -P /tmp/memcached-test.pid`
|
82
|
+
@blackboard = Pulso::BlackBoard.new :ttl => 2 do
|
83
|
+
folder :folder1, [:name1, :name2]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "(default)" do
|
88
|
+
|
89
|
+
it "should be active?" do
|
90
|
+
@blackboard.should be_active
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should have folders" do
|
94
|
+
@blackboard.should have_folders
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should complain if ttl is bigger than seconds in 30 days" do
|
98
|
+
lambda { Pulso::BlackBoard.new :ttl => 30*24*3600+1 }.should raise_error ArgumentError
|
99
|
+
lambda { Pulso::BlackBoard.new :ttl => 30*24*3600 }.should_not raise_error ArgumentError
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "(empty)" do
|
105
|
+
|
106
|
+
it { @blackboard.should be_empty }
|
107
|
+
|
108
|
+
it "should have folders after adding one" do
|
109
|
+
bb = Pulso::BlackBoard.new do
|
110
|
+
folder :folder1, [:name1, :name2]
|
111
|
+
end
|
112
|
+
bb.should have_folders
|
113
|
+
bb.folders.keys.should include(:folder1)
|
114
|
+
end
|
115
|
+
|
116
|
+
# TODO improve by regexp matching
|
117
|
+
it "should complain when retrieving from inexistant folder" do
|
118
|
+
lambda { @blackboard.folder2.name1 }.should raise_error Pulso::BlackBoardError
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should return nil when retrieving known data key from folder" do
|
122
|
+
@blackboard.folder1.name2.should be_nil
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should complain when retrieving unknown data key from folder" do
|
126
|
+
lambda { @blackboard.folder1.name5 }.should raise_error Pulso::BlackBoardError
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "(non-empty)" do
|
132
|
+
|
133
|
+
before :all do
|
134
|
+
@blackboard = Pulso::BlackBoard.new :ttl => 2 do
|
135
|
+
folder :folder1, [:name1, :name2, :name3]
|
136
|
+
folder :folder2, [:name4, :name5, :name6]
|
137
|
+
end
|
138
|
+
@blackboard.folders.keys.should include(:folder1)
|
139
|
+
end
|
140
|
+
|
141
|
+
before :each do
|
142
|
+
@obj = TestObject.new
|
143
|
+
@obj.color = :green
|
144
|
+
@blackboard.folder1.name2 = @obj
|
145
|
+
@obj = TestObject.new
|
146
|
+
@obj.color = :black
|
147
|
+
@blackboard.folder1.name3 = @obj
|
148
|
+
@obj = TestObject.new
|
149
|
+
@obj.color = :blue
|
150
|
+
@blackboard.folder1.name1 = @obj
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should not be empty" do
|
154
|
+
@blackboard.should have_folders
|
155
|
+
obj = TestObject.new
|
156
|
+
obj.color = :green
|
157
|
+
@blackboard.folder1.name2 = obj
|
158
|
+
@blackboard.should_not be_empty
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should be able to retrieve object from a folder" do
|
162
|
+
obj = @blackboard.folder1.name1
|
163
|
+
obj.color.should == :blue
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should be empty after cleaning" do
|
167
|
+
@blackboard.clean
|
168
|
+
@blackboard.should be_empty
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should not return nil when retrieving object whose time-to-live was not exceeded" do
|
172
|
+
`sleep 1`
|
173
|
+
@blackboard.folder1.name1.should_not be_nil
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should return nil when retrieving object whose time-to-live was exceeded" do
|
177
|
+
@blackboard.folder1.name1.should_not be_nil
|
178
|
+
`sleep 2`
|
179
|
+
@blackboard.folder1.name1.should be_nil
|
180
|
+
@blackboard.folder1.name1 = @obj # already expired
|
181
|
+
@blackboard.folder1.name1.should be_nil
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should be possible to retrieve all data from a folder" do
|
185
|
+
ret = @blackboard.folder1
|
186
|
+
ret.name1.color.should == :blue
|
187
|
+
ret.name2.color.should == :green
|
188
|
+
ret.name3.color.should == :black
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should timestamp the BlackBoard::Data object with current time when adding" do
|
192
|
+
obj = TestObject.new
|
193
|
+
obj.color = :blue
|
194
|
+
@blackboard.folder1.name1 = obj
|
195
|
+
@blackboard.folder1.name1.timestamp.should be_close Time.now, 0.2
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should keep a Data object if new one is older" do
|
199
|
+
obj1 = TestObject.new
|
200
|
+
obj1.color = :blue
|
201
|
+
`sleep 1`
|
202
|
+
obj2 = TestObject.new
|
203
|
+
obj2.color = :green
|
204
|
+
@blackboard.folder2.name5 = obj2
|
205
|
+
@blackboard.folder2.name5 = obj1
|
206
|
+
obj = @blackboard.folder2.name5
|
207
|
+
obj.color.should == :green
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should replace a Data object if new one is newer" do
|
211
|
+
obj1 = TestObject.new
|
212
|
+
obj1.color = :blue
|
213
|
+
`sleep 1`
|
214
|
+
obj2 = TestObject.new
|
215
|
+
obj2.color = :green
|
216
|
+
|
217
|
+
@blackboard.folder1.name2 = obj1
|
218
|
+
obj = @blackboard.folder1.name2
|
219
|
+
obj.color.should == :blue
|
220
|
+
|
221
|
+
@blackboard.folder1.name2 = obj2
|
222
|
+
obj = @blackboard.folder1.name2
|
223
|
+
obj.color.should == :green
|
224
|
+
end
|
225
|
+
|
226
|
+
after :each do
|
227
|
+
@blackboard.clean
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe "(with subfolders)" do
|
232
|
+
|
233
|
+
it "should allow subfolders" do
|
234
|
+
|
235
|
+
lambda {
|
236
|
+
@blackboard = Pulso::BlackBoard.new :ttl => 2 do
|
237
|
+
folder :folder1, [:name1, :name2, :name3] do
|
238
|
+
folder :folder2, [:name4, :name5]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
}.should_not raise_error
|
242
|
+
|
243
|
+
@blackboard.folder1.should == { :name1 => nil, :name2 => nil, :name3 => nil, :folder2 => { :name4 => nil, :name5 => nil } }
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should be possible to write to a subfolder" do
|
248
|
+
|
249
|
+
@blackboard = Pulso::BlackBoard.new :ttl => 2 do
|
250
|
+
folder :folder1, [:name1, :name2, :name3] do
|
251
|
+
folder :folder2, [:name4, :name5]
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
obj = TestObject.new
|
256
|
+
obj.color = :green
|
257
|
+
|
258
|
+
lambda { @blackboard.folder1.folder2.name5 = obj }.should_not raise_error
|
259
|
+
|
260
|
+
ret = @blackboard.folder1.folder2.name5
|
261
|
+
ret.color.should == :green
|
262
|
+
|
263
|
+
ret = @blackboard.folder1.folder2
|
264
|
+
ret[:name4].should be_nil
|
265
|
+
ret[:name5].color.should == :green
|
266
|
+
|
267
|
+
lambda { @blackboard.folder1.folder2.name5 = obj }.should_not raise_error
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
it "should allow different ttl for subfolders" do
|
272
|
+
@blackboard = Pulso::BlackBoard.new :ttl => 2 do
|
273
|
+
folder :folder1, [:name1], :ttl => 1
|
274
|
+
folder :folder2, [:name2], :ttl => 2
|
275
|
+
end
|
276
|
+
obj = TestObject.new
|
277
|
+
obj.color = :green
|
278
|
+
@blackboard.folder1.name1 = obj
|
279
|
+
@blackboard.folder2.name2 = obj
|
280
|
+
@blackboard.folder1.name1.should_not be_nil
|
281
|
+
@blackboard.folder2.name2.should_not be_nil
|
282
|
+
`sleep 1`
|
283
|
+
@blackboard.folder1.name1.should be_nil
|
284
|
+
@blackboard.folder2.name2.should_not be_nil
|
285
|
+
`sleep 1`
|
286
|
+
@blackboard.folder1.name1.should be_nil
|
287
|
+
@blackboard.folder2.name2.should be_nil
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should allow different ttl between folder and subfolder" do
|
291
|
+
@blackboard = Pulso::BlackBoard.new :ttl => 2 do
|
292
|
+
folder :folder1, [:name1], :ttl => 1 do
|
293
|
+
folder :folder2, [:name2], :ttl => 2
|
294
|
+
end
|
295
|
+
end
|
296
|
+
obj = TestObject.new
|
297
|
+
obj.color = :green
|
298
|
+
@blackboard.folder1.name1 = obj
|
299
|
+
@blackboard.folder1.folder2.name2 = obj
|
300
|
+
|
301
|
+
@blackboard.folder1.name1.should_not be_nil
|
302
|
+
@blackboard.folder1.folder2.name2.should_not be_nil
|
303
|
+
`sleep 1`
|
304
|
+
@blackboard.folder1.name1.should be_nil
|
305
|
+
@blackboard.folder1.folder2.name2.should_not be_nil
|
306
|
+
`sleep 1`
|
307
|
+
@blackboard.folder1.name1.should be_nil
|
308
|
+
@blackboard.folder1.folder2.name2.should be_nil
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should propagate tll to subfolders " do
|
312
|
+
@blackboard = Pulso::BlackBoard.new :ttl => 2 do
|
313
|
+
folder :folder1, [:name1], :ttl => 1 do
|
314
|
+
folder :folder2, [:name2]
|
315
|
+
end
|
316
|
+
end
|
317
|
+
obj = TestObject.new
|
318
|
+
obj.color = :green
|
319
|
+
@blackboard.folder1.name1 = obj
|
320
|
+
@blackboard.folder1.folder2.name2 = obj
|
321
|
+
|
322
|
+
@blackboard.folder1.name1.should_not be_nil
|
323
|
+
@blackboard.folder1.folder2.name2.should_not be_nil
|
324
|
+
`sleep 1`
|
325
|
+
@blackboard.folder1.name1.should be_nil
|
326
|
+
@blackboard.folder1.folder2.name2.should be_nil
|
327
|
+
`sleep 1`
|
328
|
+
@blackboard.folder1.name1.should be_nil
|
329
|
+
@blackboard.folder1.folder2.name2.should be_nil
|
330
|
+
end
|
331
|
+
|
332
|
+
it "should support writing to two elements with same name on different folders" do
|
333
|
+
@blackboard = Pulso::BlackBoard.new :ttl => 10 do
|
334
|
+
folder :folder1, [:name1]
|
335
|
+
folder :folder2, [:name1]
|
336
|
+
end
|
337
|
+
obj = TestObject.new
|
338
|
+
obj.color = :green
|
339
|
+
@blackboard.folder1.name1 = obj
|
340
|
+
obj = TestObject.new
|
341
|
+
obj.color = :blue
|
342
|
+
@blackboard.folder2.name1 = obj
|
343
|
+
|
344
|
+
@blackboard.folder1.name1.color.should == :green
|
345
|
+
@blackboard.folder2.name1.color.should == :blue
|
346
|
+
end
|
347
|
+
|
348
|
+
it "should not complain when creating sub sub folders" do
|
349
|
+
lambda { @blackboard = Pulso::BlackBoard.new :ttl => 10 do
|
350
|
+
folder :folder1, [:name1] do
|
351
|
+
folder :folder1, [:name1] do
|
352
|
+
folder :folder1, [:name1] do
|
353
|
+
folder :folder1, [:name1] do
|
354
|
+
folder :folder1, [:name1] do
|
355
|
+
folder :folder1, [:name1]
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end }.should_not raise_error
|
362
|
+
obj = TestObject.new
|
363
|
+
obj.color = :green
|
364
|
+
@blackboard.folder1.folder1.folder1.folder1.folder1.folder1.name1 = obj
|
365
|
+
@blackboard.folder1.folder1.folder1.folder1.folder1.folder1.name1.color.should == :green
|
366
|
+
end
|
367
|
+
|
368
|
+
end
|
369
|
+
|
370
|
+
after :all do
|
371
|
+
`killall memcached`
|
372
|
+
end
|
373
|
+
|
374
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jsvd-blackboard
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Jo\xC3\xA3o Duarte"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-01-11 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: memcache-client
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.4.0
|
23
|
+
version:
|
24
|
+
description: ""
|
25
|
+
email: jsvduarte@gmail.com
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files: []
|
31
|
+
|
32
|
+
files:
|
33
|
+
- test
|
34
|
+
- test/test_helper.rb
|
35
|
+
- test/blackboard_test.rb
|
36
|
+
- lib
|
37
|
+
- lib/blackboard.rb
|
38
|
+
- README
|
39
|
+
- spec
|
40
|
+
- spec/blackboard_spec.rb
|
41
|
+
- ChangeLog
|
42
|
+
- Rakefile
|
43
|
+
- blackboard.gemspec
|
44
|
+
has_rdoc: false
|
45
|
+
homepage:
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.8.5
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.2.0
|
67
|
+
signing_key:
|
68
|
+
specification_version: 2
|
69
|
+
summary: ""
|
70
|
+
test_files: []
|
71
|
+
|