hyperactive 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README +12 -5
- data/lib/hyperactive.rb +1 -1
- data/lib/hyperactive/hash.rb +172 -0
- data/lib/hyperactive/list.rb +128 -43
- data/lib/hyperactive/record.rb +159 -41
- data/tests/{tree_benchmark.rb → hash_benchmark.rb} +6 -6
- data/tests/{tree_test.rb → hash_test.rb} +17 -16
- data/tests/list_benchmark.rb +48 -0
- data/tests/list_test.rb +69 -11
- data/tests/record_test.rb +25 -31
- metadata +7 -6
- data/lib/hyperactive/tree.rb +0 -241
data/README
CHANGED
@@ -2,14 +2,21 @@
|
|
2
2
|
|
3
3
|
It uses archipelago for persistence, and is meaningful only in an environment where the server process doesnt restart at each request. This means that cgi environment is not really an option.
|
4
4
|
|
5
|
-
==
|
5
|
+
== Sub packages:
|
6
6
|
|
7
|
-
Hyperactive::
|
7
|
+
Hyperactive::Record:: The base class package itself, providing you with cached selectors and rejectors, along with cached finders for any number of attributes.
|
8
|
+
Hyperactive::Hash:: A collection class that contains any number of Hyperactive::Records in a hash like structure.
|
9
|
+
Hyperactive::List:: A collection class that contains any number of Hyperactive::Records in a list like structure.
|
8
10
|
|
9
|
-
==
|
11
|
+
== Usage:
|
12
|
+
|
13
|
+
To use Hyperactive in the simplest way, just run the script/services.rb script from the Archipelago
|
14
|
+
distribution to get a few services running, then subclass Hyperactive::Record::Bass and create
|
15
|
+
objects with <b>MyBassSubclass.get_instance</b> (not <b>new</b>!). They will be automagically
|
16
|
+
stored in the network, and fetchable via their <b>instance.record_id</b>.
|
10
17
|
|
11
|
-
Hyperactive::Record
|
12
|
-
|
18
|
+
By loading Hyperactive::Record you will automatically define Hyperactive::Record::CAPTAIN which will
|
19
|
+
be an Archipelago::Pirate::Captain that is your interface to the distributed database.
|
13
20
|
|
14
21
|
== Examples:
|
15
22
|
|
data/lib/hyperactive.rb
CHANGED
@@ -0,0 +1,172 @@
|
|
1
|
+
# Archipelago - a distributed computing toolkit for ruby
|
2
|
+
# Copyright (C) 2006 Martin Kihlgren <zond at troja dot ath dot cx>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
|
19
|
+
require 'rubygems'
|
20
|
+
require 'archipelago'
|
21
|
+
require 'digest/sha1'
|
22
|
+
|
23
|
+
module Hyperactive
|
24
|
+
|
25
|
+
#
|
26
|
+
# The package containing the Hash class that provides any
|
27
|
+
# kind of index for your Hyperactive classes.
|
28
|
+
#
|
29
|
+
# Is supposed to be constantly scaling, but preliminary benchmarks show some
|
30
|
+
# problems with that assumption?
|
31
|
+
#
|
32
|
+
module Hash
|
33
|
+
|
34
|
+
#
|
35
|
+
# A wrapper class that knows what key, value and list_element belong together.
|
36
|
+
#
|
37
|
+
class Element < Hyperactive::Record::Bass
|
38
|
+
attr_accessor :key, :value, :list_element
|
39
|
+
#
|
40
|
+
# Initialize a new Hash::Element with given +key+, +value+ and +list_element+.
|
41
|
+
#
|
42
|
+
def initialize(key, value, list_element)
|
43
|
+
self.key = key
|
44
|
+
self.value = value
|
45
|
+
self.list_element = list_element
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# A class suitable for storing large and often-changing datasets in
|
51
|
+
# an Archipelago environment.
|
52
|
+
#
|
53
|
+
class Head < Hyperactive::Record::Bass
|
54
|
+
|
55
|
+
attr_accessor :list
|
56
|
+
|
57
|
+
include Archipelago::Current::ThreadedCollection
|
58
|
+
|
59
|
+
#
|
60
|
+
# Initialize a Hash::Head.
|
61
|
+
#
|
62
|
+
# NB: Remember to call create on the new instance or use Head.get_instance to get that done automatically.
|
63
|
+
#
|
64
|
+
def initialize
|
65
|
+
self.list = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Return the size of this Hash.
|
70
|
+
#
|
71
|
+
def size
|
72
|
+
self.list.size
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Return the value for +key+.
|
77
|
+
#
|
78
|
+
def [](key)
|
79
|
+
element = Hyperactive::Record::CAPTAIN[my_key_for(key), @transaction]
|
80
|
+
if element
|
81
|
+
return element.value
|
82
|
+
else
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Returns whether +key+ is included in this Hash.
|
89
|
+
#
|
90
|
+
def include?(key)
|
91
|
+
Hyperactive::Record::CAPTAIN.include?(my_key_for(key), @transaction)
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Insert +value+ under +key+ in this Hash.
|
96
|
+
#
|
97
|
+
def []=(key, value)
|
98
|
+
self.list = Hyperactive::List::Head.get_instance_with_transaction(@transaction) unless self.list
|
99
|
+
|
100
|
+
if (element = Hyperactive::Record::CAPTAIN[my_key_for(key), @transaction])
|
101
|
+
element.value = value
|
102
|
+
else
|
103
|
+
element = Element.get_instance_with_transaction(@transaction, key, value, nil)
|
104
|
+
self.list << element
|
105
|
+
element.list_element = self.list.last_element
|
106
|
+
Hyperactive::Record::CAPTAIN[my_key_for(key), @transaction] = element
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Delete +key+ from this Hash.
|
112
|
+
#
|
113
|
+
def delete(key)
|
114
|
+
self.list = Hyperactive::List::Head.get_instance_with_transaction(@transaction) unless self.list
|
115
|
+
|
116
|
+
return_value = nil
|
117
|
+
|
118
|
+
element = Hyperactive::Record::CAPTAIN[my_key_for(key), @transaction]
|
119
|
+
if element
|
120
|
+
Hyperactive::Record::CAPTAIN.delete(my_key_for(key), @transaction)
|
121
|
+
self.list.unlink!(element.list_element)
|
122
|
+
return_value = element.value
|
123
|
+
element.destroy!
|
124
|
+
end
|
125
|
+
return return_value
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Will yield to +block+ once for each key/value pair in this Hash.
|
130
|
+
#
|
131
|
+
def each(&block)
|
132
|
+
self.list = Hyperactive::List::Head.get_instance_with_transaction(@transaction) unless self.list
|
133
|
+
|
134
|
+
self.list.each do |element|
|
135
|
+
yield([element.key, element.value])
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Remove all key/value pairs from this Hash.
|
141
|
+
#
|
142
|
+
def clear!
|
143
|
+
self.list = Hyperactive::List::Head.get_instance_with_transaction(@transaction) unless self.list
|
144
|
+
|
145
|
+
self.list.t_each do |element|
|
146
|
+
element.destroy!
|
147
|
+
end
|
148
|
+
self.list.clear!
|
149
|
+
end
|
150
|
+
|
151
|
+
#
|
152
|
+
# Clear everything from this Tree and destroy it.
|
153
|
+
#
|
154
|
+
def destroy!
|
155
|
+
self.clear!
|
156
|
+
super
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
#
|
162
|
+
# Get my private key for a given +key+.
|
163
|
+
#
|
164
|
+
def my_key_for(key)
|
165
|
+
Digest::SHA1.hexdigest("#{Marshal.dump(key)}#{self.record_id}")
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
data/lib/hyperactive/list.rb
CHANGED
@@ -27,73 +27,148 @@ module Hyperactive
|
|
27
27
|
module List
|
28
28
|
|
29
29
|
#
|
30
|
-
# A List
|
30
|
+
# A wrapper class that knows its previous and next List::Elements as well as its value and the id of its list.
|
31
31
|
#
|
32
32
|
class Element < Hyperactive::Record::Bass
|
33
|
-
attr_accessor :previous, :next, :value
|
33
|
+
attr_accessor :previous, :next, :value, :list_id
|
34
|
+
|
35
|
+
include Archipelago::Current::ThreadedCollection
|
36
|
+
|
37
|
+
#
|
38
|
+
# Initialize this List::Element with given +previous_element+, +next_element+, +value+ and +list_id+.
|
39
|
+
#
|
40
|
+
def initialize(previous_element, next_element, value, list_id)
|
41
|
+
self.previous = previous_element
|
42
|
+
self.next = next_element
|
43
|
+
self.value = value
|
44
|
+
self.list_id = list_id
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Yield to +block+ once for this and each following element.
|
49
|
+
#
|
50
|
+
def each(&block)
|
51
|
+
element = self
|
52
|
+
while element
|
53
|
+
yield(element)
|
54
|
+
element = self.next
|
55
|
+
end
|
56
|
+
end
|
34
57
|
end
|
35
58
|
|
36
59
|
#
|
37
60
|
# A List head.
|
38
61
|
#
|
39
62
|
class Head < Hyperactive::Record::Bass
|
40
|
-
|
63
|
+
attr_accessor :first_element, :last_element, :size
|
64
|
+
|
65
|
+
include Archipelago::Current::ThreadedCollection
|
41
66
|
|
42
67
|
#
|
43
|
-
# Create a Head.
|
68
|
+
# Create a List::Head. This is in essence the linked list.
|
44
69
|
#
|
45
|
-
# NB:
|
70
|
+
# NB: Remember to call create on the new instance or use Head.get_instance to get that done automatically.
|
46
71
|
#
|
47
72
|
def initialize
|
48
|
-
|
49
|
-
|
73
|
+
self.size = 0
|
74
|
+
self.first_element = self.last_element = nil
|
50
75
|
end
|
51
76
|
|
77
|
+
#
|
78
|
+
# Unlinks the given +element+ and reconnects the List around it.
|
79
|
+
#
|
80
|
+
def unlink!(element)
|
81
|
+
raise "#{element} is not a part of #{self}" unless element.list_id == @record_id
|
82
|
+
|
83
|
+
if size > 1
|
84
|
+
if element == self.first_element
|
85
|
+
self.first_element = element.next
|
86
|
+
self.first_element.previous = nil
|
87
|
+
elsif element == self.last_element
|
88
|
+
self.last_element = element.previous
|
89
|
+
self.last_element.next = nil
|
90
|
+
else
|
91
|
+
element.previous.next = element.next
|
92
|
+
element.next.previous = element.previous
|
93
|
+
end
|
94
|
+
else
|
95
|
+
if element == self.first_element
|
96
|
+
self.first_element = self.last_element = nil
|
97
|
+
else
|
98
|
+
raise "#{element} is not a part of #{self} even though it claims to be"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
self.size -= 1
|
102
|
+
element.destroy!
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Remove all elements from this List::Head.
|
107
|
+
#
|
108
|
+
def clear!
|
109
|
+
self.first_element.t_each do |element|
|
110
|
+
element.destroy!
|
111
|
+
end
|
112
|
+
self.first_element = self.last_element = nil
|
113
|
+
self.size = 0
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Destroys this list and all its elements.
|
118
|
+
#
|
119
|
+
def destroy!
|
120
|
+
self.clear!
|
121
|
+
super
|
122
|
+
end
|
123
|
+
|
52
124
|
#
|
53
125
|
# Return the first value of the list.
|
54
126
|
#
|
55
127
|
def first
|
56
|
-
|
128
|
+
if self.first_element
|
129
|
+
self.first_element.value
|
130
|
+
else
|
131
|
+
nil
|
132
|
+
end
|
57
133
|
end
|
58
|
-
|
134
|
+
|
59
135
|
#
|
60
136
|
# Return the last value of the list.
|
61
137
|
#
|
62
138
|
def last
|
63
|
-
|
139
|
+
if self.last_element
|
140
|
+
self.last_element.value
|
141
|
+
else
|
142
|
+
end
|
64
143
|
end
|
65
144
|
|
66
145
|
#
|
67
146
|
# Push +v+ onto the end of this list.
|
68
147
|
#
|
69
148
|
def <<(v)
|
70
|
-
if
|
71
|
-
new_element = Element.
|
72
|
-
|
73
|
-
|
74
|
-
@last_element.next = new_element
|
75
|
-
@last_element = new_element
|
149
|
+
if self.first_element
|
150
|
+
new_element = Element.get_instance_with_transaction(@transaction, self.last_element, nil, v, @record_id)
|
151
|
+
self.last_element.next = new_element
|
152
|
+
self.last_element = new_element
|
76
153
|
else
|
77
154
|
start(v)
|
78
155
|
end
|
79
|
-
|
156
|
+
self.size += 1
|
80
157
|
return v
|
81
158
|
end
|
82
|
-
|
159
|
+
|
83
160
|
#
|
84
161
|
# Push +v+ onto the beginning of this list.
|
85
162
|
#
|
86
163
|
def unshift(v)
|
87
|
-
if
|
88
|
-
new_element = Element.
|
89
|
-
|
90
|
-
|
91
|
-
@first_element.previous = new_element
|
92
|
-
@first_element = new_element
|
164
|
+
if self.first_element
|
165
|
+
new_element = Element.get_instance_with_transaction(@transaction, nil, self.first_element, v, @record_id)
|
166
|
+
self.first_element.previous = new_element
|
167
|
+
self.first_element = new_element
|
93
168
|
else
|
94
169
|
start(v)
|
95
170
|
end
|
96
|
-
|
171
|
+
self.size += 1
|
97
172
|
return v
|
98
173
|
end
|
99
174
|
|
@@ -103,37 +178,48 @@ module Hyperactive
|
|
103
178
|
def pop
|
104
179
|
v = nil
|
105
180
|
if size > 1
|
106
|
-
element =
|
107
|
-
|
108
|
-
|
181
|
+
element = self.last_element
|
182
|
+
self.last_element = element.previous
|
183
|
+
self.last_element.next = nil
|
109
184
|
v = element.value
|
110
|
-
element.destroy
|
185
|
+
element.destroy!
|
111
186
|
else
|
112
|
-
v =
|
113
|
-
|
114
|
-
|
187
|
+
v = self.first_element.value
|
188
|
+
self.first_element.destroy!
|
189
|
+
self.first_element = self.last_element = nil
|
115
190
|
end
|
116
|
-
|
191
|
+
self.size -= 1
|
117
192
|
return v
|
118
193
|
end
|
119
194
|
|
195
|
+
#
|
196
|
+
# Yield to +block+ once for each value in this list.
|
197
|
+
#
|
198
|
+
def each(&block)
|
199
|
+
element = self.first_element
|
200
|
+
while element
|
201
|
+
yield(element.value)
|
202
|
+
element = element.next
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
120
206
|
#
|
121
207
|
# Remove the first value from this list and return it.
|
122
208
|
#
|
123
209
|
def shift
|
124
210
|
v = nil
|
125
211
|
if size > 1
|
126
|
-
element =
|
127
|
-
|
128
|
-
|
212
|
+
element = self.first_element
|
213
|
+
self.first_element = element.next
|
214
|
+
self.first_element.previous = nil
|
129
215
|
v = element.value
|
130
|
-
element.destroy
|
216
|
+
element.destroy!
|
131
217
|
else
|
132
|
-
v =
|
133
|
-
|
134
|
-
|
218
|
+
v = self.first_element.value
|
219
|
+
self.first_element.destroy!
|
220
|
+
self.first_element = self.last_element = nil
|
135
221
|
end
|
136
|
-
|
222
|
+
self.size -= 1
|
137
223
|
return v
|
138
224
|
end
|
139
225
|
|
@@ -143,8 +229,7 @@ module Hyperactive
|
|
143
229
|
# Start this list with +v+ when it has no other values.
|
144
230
|
#
|
145
231
|
def start(v)
|
146
|
-
|
147
|
-
@first_element.value = v
|
232
|
+
self.first_element = self.last_element = Element.get_instance_with_transaction(@transaction, nil, nil, v, @record_id)
|
148
233
|
end
|
149
234
|
|
150
235
|
end
|