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 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
- == Dependencies:
5
+ == Sub packages:
6
6
 
7
- Hyperactive::Tree:: RBTree: http://raa.ruby-lang.org/project/ruby-rbtree
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
- == Sub packages:
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:: The base class itself, providing you with cached selectors and rejectors, along with cached finders for any number of attributes.
12
- Hyperactive::Tree:: A collection class that contains any number of Hyperactive::Records in a tree structure. Scales logarithmically, so use with care.
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
@@ -19,5 +19,5 @@ $: << File.dirname(__FILE__)
19
19
 
20
20
  require 'hyperactive/hooker'
21
21
  require 'hyperactive/record'
22
- require 'hyperactive/tree'
22
+ require 'hyperactive/hash'
23
23
  require 'hyperactive/list'
@@ -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
@@ -27,73 +27,148 @@ module Hyperactive
27
27
  module List
28
28
 
29
29
  #
30
- # A List element.
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
- attr_reader :first_element, :last_element, :size
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: As usual, dont call this. Use Head.get_instance instead.
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
- @size = 0
49
- @first_element = @last_element = nil
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
- @first_element.value
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
- @last_element.value
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 @first_element
71
- new_element = Element.get_instance
72
- new_element.value = v
73
- new_element.previous = @last_element
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
- @size += 1
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 @first_element
88
- new_element = Element.get_instance
89
- new_element.value = v
90
- new_element.next = @first_element
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
- @size += 1
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 = @last_element
107
- @last_element = element.previous
108
- @last_element.next = nil
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 = @first_element.value
113
- @first_element.destroy
114
- @first_element = @last_element = nil
187
+ v = self.first_element.value
188
+ self.first_element.destroy!
189
+ self.first_element = self.last_element = nil
115
190
  end
116
- @size -= 1
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 = @first_element
127
- @first_element = element.next
128
- @first_element.previous = nil
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 = @first_element.value
133
- @first_element.destroy
134
- @first_element = @last_element = nil
218
+ v = self.first_element.value
219
+ self.first_element.destroy!
220
+ self.first_element = self.last_element = nil
135
221
  end
136
- @size -= 1
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
- @first_element = @last_element = Element.get_instance
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