hyperactive 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README +9 -3
- data/TODO +4 -0
- data/lib/hyperactive/hash.rb +14 -6
- data/lib/hyperactive/hooker.rb +157 -47
- data/lib/hyperactive/index.rb +210 -0
- data/lib/hyperactive/list.rb +8 -0
- data/lib/hyperactive/record.rb +20 -407
- data/lib/hyperactive/transactions.rb +154 -0
- data/lib/hyperactive.rb +2 -0
- data/tests/hash_benchmark.rb +5 -5
- data/tests/hash_test.rb +5 -5
- data/tests/list_benchmark.rb +3 -3
- data/tests/list_test.rb +6 -6
- data/tests/record_test.rb +8 -8
- metadata +5 -2
data/README
CHANGED
@@ -7,15 +7,21 @@ It uses archipelago for persistence, and is meaningful only in an environment wh
|
|
7
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
8
|
Hyperactive::Hash:: A collection class that contains any number of Hyperactive::Records in a hash like structure.
|
9
9
|
Hyperactive::List:: A collection class that contains any number of Hyperactive::Records in a list like structure.
|
10
|
+
Hyperactive::Index:: A module included into Hyperactive::Record that adds simple indexing capabilities of the <b>find_by_...</b> type.
|
11
|
+
Hyperactive::Transactions:: A module included into Hyperactive::Record that adds a few simplifications in handling transactions properly.
|
10
12
|
|
11
13
|
== Usage:
|
12
14
|
|
13
15
|
To use Hyperactive in the simplest way, just run the script/services.rb script from the Archipelago
|
14
16
|
distribution to get a few services running, then subclass Hyperactive::Record::Bass and create
|
15
|
-
objects with <b>MyBassSubclass.get_instance</b
|
16
|
-
|
17
|
+
objects with <b>MyBassSubclass.get_instance</b>. They will be automagically stored in the network,
|
18
|
+
and fetchable via their <b>instance.record_id</b>.
|
17
19
|
|
18
|
-
|
20
|
+
You can also use <b>new</b> of course, but beware that this will not store the object persistently
|
21
|
+
<b>or</b> give you a proxy. Both of these things are good for you, so use get_instance unless you know
|
22
|
+
what you are doing.
|
23
|
+
|
24
|
+
By loading Hyperactive::Record you will automatically define Hyperactive::CAPTAIN which will
|
19
25
|
be an Archipelago::Pirate::Captain that is your interface to the distributed database.
|
20
26
|
|
21
27
|
== Examples:
|
data/TODO
ADDED
data/lib/hyperactive/hash.rb
CHANGED
@@ -62,6 +62,7 @@ module Hyperactive
|
|
62
62
|
# NB: Remember to call create on the new instance or use Head.get_instance to get that done automatically.
|
63
63
|
#
|
64
64
|
def initialize
|
65
|
+
super
|
65
66
|
self.list = nil
|
66
67
|
end
|
67
68
|
|
@@ -72,11 +73,18 @@ module Hyperactive
|
|
72
73
|
self.list.size
|
73
74
|
end
|
74
75
|
|
76
|
+
#
|
77
|
+
# Return whether this Hash is empty.
|
78
|
+
#
|
79
|
+
def empty?
|
80
|
+
self.list.empty?
|
81
|
+
end
|
82
|
+
|
75
83
|
#
|
76
84
|
# Return the value for +key+.
|
77
85
|
#
|
78
86
|
def [](key)
|
79
|
-
element = Hyperactive::
|
87
|
+
element = Hyperactive::CAPTAIN[my_key_for(key), @transaction]
|
80
88
|
if element
|
81
89
|
return element.value
|
82
90
|
else
|
@@ -88,7 +96,7 @@ module Hyperactive
|
|
88
96
|
# Returns whether +key+ is included in this Hash.
|
89
97
|
#
|
90
98
|
def include?(key)
|
91
|
-
Hyperactive::
|
99
|
+
Hyperactive::CAPTAIN.include?(my_key_for(key), @transaction)
|
92
100
|
end
|
93
101
|
|
94
102
|
#
|
@@ -97,13 +105,13 @@ module Hyperactive
|
|
97
105
|
def []=(key, value)
|
98
106
|
self.list = Hyperactive::List::Head.get_instance_with_transaction(@transaction) unless self.list
|
99
107
|
|
100
|
-
if (element = Hyperactive::
|
108
|
+
if (element = Hyperactive::CAPTAIN[my_key_for(key), @transaction])
|
101
109
|
element.value = value
|
102
110
|
else
|
103
111
|
element = Element.get_instance_with_transaction(@transaction, key, value, nil)
|
104
112
|
self.list << element
|
105
113
|
element.list_element = self.list.last_element
|
106
|
-
Hyperactive::
|
114
|
+
Hyperactive::CAPTAIN[my_key_for(key), @transaction] = element
|
107
115
|
end
|
108
116
|
end
|
109
117
|
|
@@ -115,9 +123,9 @@ module Hyperactive
|
|
115
123
|
|
116
124
|
return_value = nil
|
117
125
|
|
118
|
-
element = Hyperactive::
|
126
|
+
element = Hyperactive::CAPTAIN[my_key_for(key), @transaction]
|
119
127
|
if element
|
120
|
-
Hyperactive::
|
128
|
+
Hyperactive::CAPTAIN.delete(my_key_for(key), @transaction)
|
121
129
|
self.list.unlink!(element.list_element)
|
122
130
|
return_value = element.value
|
123
131
|
element.destroy!
|
data/lib/hyperactive/hooker.rb
CHANGED
@@ -19,61 +19,171 @@
|
|
19
19
|
module Hyperactive
|
20
20
|
|
21
21
|
#
|
22
|
-
# A
|
22
|
+
# A container of utility hook methods.
|
23
23
|
#
|
24
24
|
module Hooker
|
25
25
|
|
26
26
|
#
|
27
|
-
#
|
28
|
-
# calls the rest of the +hooks+ with +argument+ as argument
|
29
|
-
# and after that yields to the given +block+.
|
27
|
+
# Something that uses hooks a lot, include if you want support for various types of hooks.
|
30
28
|
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
# def update_timestamp(important_stuff_instance)
|
56
|
-
# important_stuff_instance.timestamp = Time.now
|
57
|
-
# Hyperactive::Hooker.call_with_hooks("changed at #{Time.now}",
|
58
|
-
# important_stuff_instance.method(:validate_and_notify)) do
|
59
|
-
# important_stuff_instance.do_save
|
60
|
-
# end
|
61
|
-
# end
|
62
|
-
# end
|
63
|
-
#
|
64
|
-
def call_with_hooks(argument, *hooks, &block)
|
65
|
-
if hooks.empty?
|
66
|
-
yield
|
67
|
-
else
|
68
|
-
first = hooks.first
|
69
|
-
rest = hooks[1..-1]
|
70
|
-
first.call(argument) do
|
71
|
-
call_with_hooks(argument, *rest, &block)
|
29
|
+
module Pimp
|
30
|
+
|
31
|
+
#
|
32
|
+
# Upon inclusion, our +base+ class will be given a set of hook
|
33
|
+
# arrays, and then get extended by ClassMethods.
|
34
|
+
#
|
35
|
+
def self.append_features(base)
|
36
|
+
super
|
37
|
+
base.instance_variable_set(:@create_hooks, [])
|
38
|
+
base.instance_variable_set(:@destroy_hooks, [])
|
39
|
+
base.instance_variable_set(:@save_hooks, [])
|
40
|
+
base.instance_variable_set(:@load_hooks, [])
|
41
|
+
base.extend(ClassMethods)
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# This will allow us to wrap any write of us to persistent storage
|
46
|
+
# in the @save_hooks as long as the Archipelago::Hashish provider
|
47
|
+
# supports it. See Archipelago::Hashish::BerkeleyHashish for an example
|
48
|
+
# of Hashish providers that do this.
|
49
|
+
#
|
50
|
+
def save_hook(old_value, &block)
|
51
|
+
self.class.with_hooks(:instance => self, :arguments => [old_value], :hooks => self.class.save_hooks) do
|
52
|
+
yield
|
72
53
|
end
|
73
54
|
end
|
74
|
-
|
55
|
+
|
56
|
+
#
|
57
|
+
# This will allow us to wrap any load of us from persistent storage
|
58
|
+
# in the @@load_hooks as long as the Archipelago::Hashish provider
|
59
|
+
# supports it. See Archipelago::Hashish::BerkeleyHashish for an example
|
60
|
+
# of Hashish providers that do this.
|
61
|
+
#
|
62
|
+
def load_hook(&block)
|
63
|
+
self.class.with_hooks(:instance => self, :hooks => self.class.load_hooks) do
|
64
|
+
yield
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
75
68
|
|
76
|
-
|
69
|
+
#
|
70
|
+
# The class methods of a Pimp.
|
71
|
+
#
|
72
|
+
module ClassMethods
|
73
|
+
|
74
|
+
#
|
75
|
+
# Will wrap a block within nested calls to given hooks.
|
76
|
+
#
|
77
|
+
# Parameters:
|
78
|
+
# * <i>:instance</i>: An instance to send methods to, defaults to self
|
79
|
+
# * <i>:arguments</i>: Extra arguments to send to whatever we call, defaults to self
|
80
|
+
# * <i>:hooks</i>: An Array of hook objects. They can be either Strings or Symbols, or
|
81
|
+
# whatever else can be given to <b>respond_to?</b>, <b>or</b> they can be anything
|
82
|
+
# that <b>respond_to?(:call)</b>. If the hook is something that
|
83
|
+
# <i>respond_to?(:call)</i> it will be <i>call</i>ed and given
|
84
|
+
# the the <i>:instance</i> and *<i>:arguments</i> and the provided +block+.
|
85
|
+
# Else the <i>:instance</i>.respond_to?(<i>the given hook</i>)
|
86
|
+
# must be true, and that <i>:instance</i> will be sent the <i>given hook</i> along with the <i>:arguments</i> and
|
87
|
+
# a block that does the rest of the hooks.
|
88
|
+
#
|
89
|
+
def with_hooks(options = {}, &block)
|
90
|
+
instance = options[:instance] || self
|
91
|
+
arguments = options[:arguments] || []
|
92
|
+
hooks = options[:hooks] || []
|
93
|
+
|
94
|
+
if hooks.empty?
|
95
|
+
yield
|
96
|
+
else
|
97
|
+
first = hooks.first
|
98
|
+
rest = hooks[1..-1]
|
99
|
+
if first.respond_to?(:call)
|
100
|
+
first.call(instance, *arguments) do
|
101
|
+
with_hooks(:instance => instance, :hooks => rest, :arguments => arguments, &block)
|
102
|
+
end
|
103
|
+
elsif instance.respond_to?(first)
|
104
|
+
instance.send(first, *arguments) do
|
105
|
+
with_hooks(:instance => instance, :hooks => rest, :arguments => arguments, &block)
|
106
|
+
end
|
107
|
+
else
|
108
|
+
raise "You can only send in hooks that are Symbols in the given instance or call'able objects"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Upon inheritance, our +subclass+ will be given copies of our hooks.
|
115
|
+
#
|
116
|
+
def inherited(subclass)
|
117
|
+
subclass.instance_variable_set(:@create_hooks, @create_hooks.clone)
|
118
|
+
subclass.instance_variable_set(:@destroy_hooks, @destroy_hooks.clone)
|
119
|
+
subclass.instance_variable_set(:@save_hooks, @save_hooks.clone)
|
120
|
+
subclass.instance_variable_set(:@load_hooks, @load_hooks.clone)
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Return our create_hooks, which can then be treated as any old Array.
|
125
|
+
#
|
126
|
+
# These must be <b>call</b>able objects with an arity of 1
|
127
|
+
# that will be sent the instance about to be created (initial
|
128
|
+
# insertion into the database system) that take a block argument.
|
129
|
+
#
|
130
|
+
# The block argument will be a Proc that actually injects the
|
131
|
+
# instance into the database system.
|
132
|
+
#
|
133
|
+
# Use this to preprocess, validate and/or postprocess your
|
134
|
+
# instances upon creation.
|
135
|
+
#
|
136
|
+
attr_reader :create_hooks
|
137
|
+
|
138
|
+
#
|
139
|
+
# Return our destroy_hooks, which can then be treated as any old Array.
|
140
|
+
#
|
141
|
+
# These must be <b>call</b>able objects with an arity of 1
|
142
|
+
# that will be sent the instance about to be destroyed (removal
|
143
|
+
# from the database system) that take a block argument.
|
144
|
+
#
|
145
|
+
# The block argument will be a Proc that actually removes the
|
146
|
+
# instance from the database system.
|
147
|
+
#
|
148
|
+
# Use this to preprocess, validate and/or postprocess your
|
149
|
+
# instances upon destruction.
|
150
|
+
#
|
151
|
+
attr_reader :destroy_hooks
|
152
|
+
|
153
|
+
#
|
154
|
+
# Return our load_hooks, which can then be treated as any old Array.
|
155
|
+
#
|
156
|
+
# These must be <b>call</b>able objects with an arity of 1
|
157
|
+
# that will be sent the instance about to be loaded (insertion
|
158
|
+
# into the live hash of the database in question) that take a block argument.
|
159
|
+
#
|
160
|
+
# The block argument will be a Proc that actually puts the
|
161
|
+
# instance into the live hash.
|
162
|
+
#
|
163
|
+
# Use this to preprocess, validate and/or postprocess your
|
164
|
+
# instances upon loading.
|
165
|
+
#
|
166
|
+
attr_reader :load_hooks
|
167
|
+
|
168
|
+
#
|
169
|
+
# Return our save_hooks, which can then be treated as any old Array.
|
170
|
+
#
|
171
|
+
# These must be <b>call</b>able objects with an arity of 1
|
172
|
+
# that will be sent [the old version, the new version] of the
|
173
|
+
# instance about to be saved (storage into the database system)
|
174
|
+
# along with a block argument.
|
175
|
+
#
|
176
|
+
# The block argument will be a Proc that actually saves the
|
177
|
+
# instance into the database system.
|
178
|
+
#
|
179
|
+
# Use this to preprocess, validate and/or postprocess your
|
180
|
+
# instances upon saving.
|
181
|
+
#
|
182
|
+
attr_reader :save_hooks
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
77
187
|
|
78
188
|
end
|
79
189
|
|
@@ -0,0 +1,210 @@
|
|
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
|
+
module Hyperactive
|
19
|
+
|
20
|
+
module Index
|
21
|
+
|
22
|
+
#
|
23
|
+
# A tiny <b>call</b>able class that saves stuff in
|
24
|
+
# indexes depending on certain attributes.
|
25
|
+
#
|
26
|
+
class IndexBuilder
|
27
|
+
#
|
28
|
+
# Get the key for the given attributes and values.
|
29
|
+
#
|
30
|
+
def self.get_key(klass, attributes, values)
|
31
|
+
"Hyperactive::IndexBuilder::#{klass}::#{attributes.join(",")}::#{values.join(",")}"
|
32
|
+
end
|
33
|
+
#
|
34
|
+
# Initialize an IndexBuilder giving it an array of +attributes+
|
35
|
+
# that will be indexed.
|
36
|
+
#
|
37
|
+
def initialize(klass, attributes)
|
38
|
+
@klass = klass
|
39
|
+
@attributes = attributes
|
40
|
+
end
|
41
|
+
#
|
42
|
+
# Get the key for the given +record+.
|
43
|
+
#
|
44
|
+
def get_key_for(record)
|
45
|
+
values = @attributes.collect do |att|
|
46
|
+
if record.respond_to?(att)
|
47
|
+
record.send(att)
|
48
|
+
else
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
key = self.class.get_key(@klass, @attributes, values)
|
53
|
+
end
|
54
|
+
#
|
55
|
+
# Call this IndexBuilder and pass it a +block+.
|
56
|
+
#
|
57
|
+
# If the +argument+ is an Array then we know we are a save hook,
|
58
|
+
# otherwise we are a destroy hook.
|
59
|
+
#
|
60
|
+
def call(*arguments, &block)
|
61
|
+
yield
|
62
|
+
|
63
|
+
#
|
64
|
+
# If the argument is an Array (of old value, new value)
|
65
|
+
# then we are a save hook, otherwise a destroy hook.
|
66
|
+
#
|
67
|
+
if arguments.size > 1
|
68
|
+
record = arguments.first
|
69
|
+
old_record = arguments.last
|
70
|
+
old_key = get_key_for(old_record)
|
71
|
+
new_key = get_key_for(record)
|
72
|
+
if old_key != new_key
|
73
|
+
(CAPTAIN[old_key] ||= Hyperactive::Hash::Head.get_instance_with_transaction(record.transaction)).delete(record.record_id)
|
74
|
+
(CAPTAIN[new_key] ||= Hyperactive::Hash::Head.get_instance_with_transaction(record.transaction))[record.record_id] = record
|
75
|
+
end
|
76
|
+
else
|
77
|
+
record = arguments.first
|
78
|
+
(CAPTAIN[get_key_for(record)] ||= Hyperactive::Hash::Head.get_instance_with_transaction(record.transaction)).delete(record.record_id)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# A tiny <b>call</b>able class that saves stuff inside containers
|
85
|
+
# if they match certain criteria.
|
86
|
+
#
|
87
|
+
class MatchSaver
|
88
|
+
#
|
89
|
+
# Initialize this MatchSaver with a +key+, a <b>call</b>able +matcher+
|
90
|
+
# and a +mode+ (:select, :reject, :delete_if_match or :delete_unless_match).
|
91
|
+
#
|
92
|
+
def initialize(key, matcher, mode)
|
93
|
+
@key = key
|
94
|
+
@matcher = matcher
|
95
|
+
@mode = mode
|
96
|
+
end
|
97
|
+
#
|
98
|
+
# Depending on <i>@mode</i> and return value of <i>@matcher</i>.call
|
99
|
+
# may save record in the Hash-like container named <i>@key</i> in
|
100
|
+
# the main database after having yielded to +block+.
|
101
|
+
#
|
102
|
+
def call(*arguments, &block)
|
103
|
+
yield
|
104
|
+
|
105
|
+
record = arguments.first
|
106
|
+
|
107
|
+
case @mode
|
108
|
+
when :select
|
109
|
+
if @matcher.call(record)
|
110
|
+
CAPTAIN[@key, record.transaction][record.record_id] = record
|
111
|
+
else
|
112
|
+
CAPTAIN[@key, record.transaction].delete(record.record_id)
|
113
|
+
end
|
114
|
+
when :reject
|
115
|
+
if @matcher.call(record)
|
116
|
+
CAPTAIN[@key, record.transaction].delete(record.record_id)
|
117
|
+
else
|
118
|
+
CAPTAIN[@key, record.transaction][record.record_id] = record
|
119
|
+
end
|
120
|
+
when :delete_if_match
|
121
|
+
if @matcher.call(record)
|
122
|
+
CAPTAIN[@key, record.transaction].delete(record.record_id)
|
123
|
+
end
|
124
|
+
when :delete_unless_match
|
125
|
+
unless @matcher.call(record)
|
126
|
+
CAPTAIN[@key, record.transaction].delete(record.record_id)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
module Indexable
|
133
|
+
|
134
|
+
def self.append_features(base)
|
135
|
+
super
|
136
|
+
base.extend(ClassMethods)
|
137
|
+
end
|
138
|
+
|
139
|
+
module ClassMethods
|
140
|
+
#
|
141
|
+
# Create an index for this class.
|
142
|
+
#
|
143
|
+
# Will create a method find_by_#{attributes.join("_and_")} for this
|
144
|
+
# class that will return what you expect.
|
145
|
+
#
|
146
|
+
def index_by(*attributes)
|
147
|
+
klass = self
|
148
|
+
self.class.class_eval do
|
149
|
+
define_method("find_by_#{attributes.join("_and_")}") do |*args|
|
150
|
+
CAPTAIN[IndexBuilder.get_key(klass, attributes, args)]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
index_builder = IndexBuilder.new(self, attributes)
|
154
|
+
self.save_hooks << index_builder
|
155
|
+
self.destroy_hooks << index_builder
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# Will define a method called +name+ that will include all
|
160
|
+
# existing instances of this class that when yielded to the +block+
|
161
|
+
# returns true. Will only return instances saved after this selector
|
162
|
+
# is defined.
|
163
|
+
#
|
164
|
+
def select(name, &block) #:yields: instance
|
165
|
+
key = collection_key(name)
|
166
|
+
CAPTAIN[key] ||= Hyperactive::Hash::Head.get_instance
|
167
|
+
self.class.class_eval do
|
168
|
+
define_method(name) do
|
169
|
+
CAPTAIN[key]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
self.save_hooks << MatchSaver.new(key, block, :select)
|
173
|
+
self.destroy_hooks << MatchSaver.new(key, block, :delete_if_match)
|
174
|
+
end
|
175
|
+
|
176
|
+
#
|
177
|
+
# Will define a method called +name+ that will include all
|
178
|
+
# existing instances of this class that when yielded to +block+
|
179
|
+
# does not return true. Will only return instances saved after this
|
180
|
+
# rejector is defined.
|
181
|
+
#
|
182
|
+
def reject(name, &block) #:yields: instance
|
183
|
+
key = collection_key(name)
|
184
|
+
CAPTAIN[key] ||= Hyperactive::Hash::Head.get_instance
|
185
|
+
self.class.class_eval do
|
186
|
+
define_method(name) do
|
187
|
+
CAPTAIN[key]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
self.save_hooks << MatchSaver.new(key, block, :reject)
|
191
|
+
self.destroy_hooks << MatchSaver.new(key, block, :delete_unless_match)
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
#
|
197
|
+
# The key used to store the collection with the given +sym+ as name.
|
198
|
+
#
|
199
|
+
def collection_key(sym)
|
200
|
+
"Hyperactive::Index::Indexable::collection_key::#{sym}"
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
data/lib/hyperactive/list.rb
CHANGED
@@ -70,6 +70,7 @@ module Hyperactive
|
|
70
70
|
# NB: Remember to call create on the new instance or use Head.get_instance to get that done automatically.
|
71
71
|
#
|
72
72
|
def initialize
|
73
|
+
super
|
73
74
|
self.size = 0
|
74
75
|
self.first_element = self.last_element = nil
|
75
76
|
end
|
@@ -102,6 +103,13 @@ module Hyperactive
|
|
102
103
|
element.destroy!
|
103
104
|
end
|
104
105
|
|
106
|
+
#
|
107
|
+
# Return whether this List is empty.
|
108
|
+
#
|
109
|
+
def empty?
|
110
|
+
return self.size == 0
|
111
|
+
end
|
112
|
+
|
105
113
|
#
|
106
114
|
# Remove all elements from this List::Head.
|
107
115
|
#
|